This article describes how to migrate between different major versions of Reveal and from the old @cognite/3d-viewer package.
Migrating from Reveal 1.x
There are some important differences between version 1 and version 2 of @cognite/reveal which might require changes in the build configuration and code. The most important changes are:
- The node styling/filtering API has been reworked and will require changes. See below for more information.
- ThreeJS and @types/three is included in the package, so it's no longer necessary to include this in the application you are using Reveal from if you are not using ThreeJS directly.
Cognite3DViewer.getIntersectionFromPixelis now async.
Migrating to the new node styling API
Before reading this chapter it's recommended to read through the introduction to the new styling and filtering API.
In the 2.0 of release of Reveal, the filtering and styling API changed drastically - breaking existing functionality. Migrating to version 2 will most likely require code changes, but these should often be quite limited and lead to a simplification of the code base along with an increase in filtering- and styling performance.
In previous versions of Reveal, styling was done by a set of functions for explicitly applying styling to a node identified by a tree index (and optionally all its children).
- Visibility -
hideNodeByTreeIndex(),showNodeByTreeIndex(),hideAllNodes()andshowAllNodes() - Color override -
setNodeColorByTreeIndex(),setAllNodeColors(),resetNodeColorByTreeIndex()andresetAllNodeColors() - Ghost nodes -
ghostNodeByTreeIndex(),unghostNodeByTreeIndex(),ghostAllNodes()andunghostAllNodes() - Highlighting -
selectNodeByTreeIndex(),deselectNodeByTreeIndex()anddeselectAllNodes()
This allowed flexible styling of nodes, but required many individual calls to update each node, causing degraded performance. The new API instead requires a set of styles which nodes are assigned to. It is preferable not to have too many styles - both in terms of performance and to avoid a cluttered view for the end user.
The previous styling options maps quite directly to the new API. Visibility is controlled by NodeAppearance.visible,
color override by NodeAppearance.color and ghost mode by NodeAppearance.renderGhosted. However, there is no
direct mapping between what was previously called 'selection' or 'highlighting', this is now a combination of
NodeAppearance.color, NodeAppearance.renderInFront and NodeAppearance.outlineColor. This means that the new API
introduces additional flexibility - e.g. an object can have an outline, but not be rendered in front of other objects. For
convenience, the new API comes with a set of default styles available through DefaultNodeAppearance.
A commonly used functionality is to apply a default style to all nodes and to style a few selected nodes with a different
style to make them stand out. Before, this could be achieved using a ghostAllNodes, hideAllNodes and setAllNodeColors to
apply the default styling, and a combination of the different per-node styling functions mentioned above to style the
nodes that should stand out. In Reveal 2, this is replaced by Cognite3DModel.setDefaultNodeAppearance which is a style that will be applied to any node that is not styled by another styled set.
Using TreeIndexNodeCollection to migrate existing filtering logic
In general, it is recommended to use the specialized NodeCollection-implementations for best performance. However,
it is possible to use TreeIndexNodeCollection as a migration step and simple use cases. This allows the use of previously implemented logic for
populating the set based on some application specific logic.
As an example, consider an application that has some logic for only showing objects that are part of the 'EA'-function. In previous versions this could be achieved by doing:
model.hideAllNodes();
sdk.revisions3D.list3DNodes(model.modelId, model.revisionId,
{
limit: 1000,
properties: { PDMS: { Function: 'EA' } }
})
.autoPagingEach(node => {
for (let i = 0; i < node.subtreeSize; i++) {
model.showNodeByTreeIndex(node.treeIndex + i);
}
});
Now, the same can be implemented using:
Note that the styling is set up before any nodes actually are fetched and that the set is populated asynchronously.
This example could easily have been migrated using TreeIndexNodeCollection, but in
other cases this might not be as straight forward as shown.
A note on applyToAllChildren to style all descendants of a node
In previous versions, all styling functions accepted an optional applyToAllChildren-argument.
When this was true, Reveal would apply the styling to all descendants. When migrating
to the new version, adding the root tree index of the subtree to TreeIndexNodeCollection will not have
the same result. When you also have a subtreeSize available, use TreeIndexNodeCollection.addRange with
new NumericRange(treeIndex, subtreeSize) to add the full subtree. If you don't know the
subtreeSize, Cognite3DModel.getSubtreeIndices() can be used to get the full range of tree indices.
// In Reveal 1.x:
// model.hideNodeByTreeIndex(subtreeRootTreeIndex, true);
// ❌ Wrong - will only affect a single tree index, not all descendants
const indexSet = new IndexSet();
indexSet.add(subtreeRootIndex);
const nodes = new TreeIndexNodeCollection(indexSet);
model.assignStyledNodeCollection(nodes, { visible: false });
// ✅ Correct - all descendants are affected
const indexSet = new IndexSet();
indexSet.addRange(await model.getSubtreeIndices(subtreeRootIndex));
const nodes = new TreeIndexNodeCollection(indexSet);
model.assignStyledNodeCollection(nodes, { visible: false });
Migrating from @cognite/3d‑viewer
This section describes how to migrate from @cognite/3d-viewer to Reveal version 1. It doesn't take into account changes
between Reveal versions 1 and 2.
@cognite/3d-viewer is an older version of Reveal that has similar functionality to this package. However, some important differences caused us to switch the package name to @cognite/reveal.
@cognite/3d-viewerdownloads all geometry when a model is loaded. This causes higher initial loading time, but potentially increases the performance once the data has been loaded. It also limits the size of the models that can be loaded. The new version,@cognite/reveal, is based on "streaming geometry" which downloads necessary geometry within a budget as the camera moved. This enables support for very complex 3D models and reduces the initial loading time.@cognite/3d-viewersupports styling 3D nodes by node ID. In@cognite/revealthe recommended approach is to use tree indices instead. See Styling nodes for more information. For an introduction to the differences between these concepts, see Concepts. The new component supports mapping between the two identifiers when necessary.@cognite/3d-viewersupports photogrammetry models through textures while@cognite/revealsupports photogrammetry through point clouds. This is due to the heavy processing load textures places on processing.@cognite/revealsupports styling a 3D node and all it's children in one operation. In@cognite/3d-viewerit's necessary to iterate over all children and manually apply the same change to all nodes.@cognite/revealsupports point cloud models. This is not supported in@cognite/3d-viewer.@cognite/revealrequires new output file formats. See Preparing models below for details.
API changes
The APIs are very similar and the functionality provided in @cognite/reveal should feel familiar. There are several operations supported in @cognite/3d-viewer which isn't supported by @cognite/reveal, but these have been replaced by alternatives that should provide similar functionality.
Styling nodes
In @cognite/3d-viewer, there are several functions for manipulating the styling of 3D nodes. These functions identify nodes by their node IDs. In @cognite/reveal similar functions exist, but these identify the nodes by their tree indices. It's therefor necessary to migrate to the new functions.
Coloring nodes
Cognite3DModel.setNodeColor(nodeId, r, g, b)has been replaced bysetNodeColorByTreeIndex(treeIndex, r, g, b, applyToChildren?)Cognite3DModel.resetNodeColor(nodeId)has been replaced byresetNodeColorByTreeIndex(treeIndex, applyToChildren?)Cognite3DModel.getNodeColor(nodeId)is no longer supportedCognite3DModel.resetAllNodeColors()has been added to the API
The new functions identify nodes by tree index rather than node ID. It also supports applying the same color to all children of the identified node. Note that the functions identifying nodes by node ID will work, but will be slower and use is not recommended.
Selecting/highlighting nodes
Cognite3DModel.selectNode(nodeId)has been replaced byselectNodeByTreeIndex(treeIndex, applyToChildren?)Cognite3DModel.deselectNode(nodeId)has been replaced bydeselectNodeByTreeIndex(treeIndex, applyToChildren?)Cognite3DModel.deselectAllNodes()works equally in both components
The new functions identify nodes by tree index rather than node ID. It also supports selecting/highlighting all children of the identified node. Note that the functions identifying nodes by node ID will work, but will be slower and use is not recommended.
Controlling node visibility
Cognite3DModel.showNode(nodeId)has been replaced byshowNodeByTreeIndex(treeIndex, applyToChildren?)Cognite3DModel.hideNode(nodeId)has been replaced byhideNodeByTreeIndex(treeIndex, applyToChildren?)Cognite3DModel.showAllNodes()works equally in both componentsCognite3DModel.hideAllNodes(makeGray?)doesn't support settingmakeGrayany longer
The new functions identify nodes by tree index rather than node ID. It also supports showing/hiding all children of the identified node. Note that the functions identifying nodes by node ID will work, but will be slower and use is not recommended.
Node visitor functions
Reveal has functions for iterating over nodes, either all nodes in a 3D model or a subtree of a parent node. Notice that all functions are async, and the action that you pass is not applied immediately. A single model can have millions of nodes. That's why the iteration functions call the passed action step by step. You can see it in action in our node visiting example where colors are applied gradually to nodes.
Cognite3DModel.iterateNodes(action: (nodeId, treeIndex) => void): Promise<boolean>has been replaced byiterateNodesByTreeIndex(action: (treeIndex) => void): Promise<void>. Returns promise which resolves once iteration has done.Cognite3DModel.iterateSubtree(nodeId, action: (nodeId, treeIndex) => void, treeIndex?, subtreeSize?)has been replaced byiterateSubtreeByTreeIndex(treeIndex, action: (treeIndex) => void): Promise<number>. Returns promise which resolves once iteration has done.
Accessing node information and mapping between node IDs and tree indices
Reveal supports accessing information about 3D nodes through the Cognite SDK. However, the SDK identifies nodes by node ID, not tree index. It might be necessary to map between the two concepts. The following functions provide such functionality:
async Cognite3DModel.mapNodeIdToTreeIndex(nodeId)async Cognite3DModel.mapNodeIdsToTreeIndices(nodeIds)async Cognite3DModel.mapTreeIndexToNodeId(treeIndex)async Cognite3DModel.mapTreeIndicesToNodeIds(treeIndices)
Note that the operation requires communication with CDF servers and should be kept at a minimum. It's recommended to use the functions that accepts a list of nodes to map whenever possible. Batching requests is recommended to achieve the best performance.
Access to node information such as name, bounding box and properties/attributes is done through the 3D revision API, specifically:
list3DNodesfor retrieving a set of nodes using a filterlist3DNodeAncestorsfor retrieving ancestors of a specific noderetrieve3DNodesfor retrieving a set of nodes identified by their node ids
The API for accessing node information has not changed @cognite/3d-viewer, but since Reveal now uses tree indices explicit mapping tree index to node ID is necessary.
Working with asset mappings
3D Asset Mappings enables mapping 3D nodes to assets. A mapping is a link from assetId to nodeId, so to use these with Reveal it's necessary to map these to tree indices.
Ray picking and intersection for handling click events
In @cognite/3d-viewer Cognite3dViewer.getIntersectionFromPixel optionally accepts a model-argument to restrict the result to a single model. Support for this has been removed
in @cognite/reveal. Previously getIntersectionFromPixel would return a struct with both nodeId and treeIndex. Now this has been changed to only include treeIndex (to
determine nodeId use async Cognite3DModel.mapTreeIndexToNodeId(treeIndex)).
Other differences
There are a few other noticeable changes from @cognite/3d-viewer and @cognite/reveal:
@cognite/3d-viewersupports local caching to reduce the time to load previously opened 3D models. Currently, this is not supported by@cognite/reveal, but the need for such functionality is reduced by adding streaming capabilities.- In
@cognite/3d-viewerCognite3DViewer.addModel(...)will always return aCognite3DModel. In@cognite/revealthis function might also return aCognitePointCloudModel. To explicitly add a CAD model or point cloud model useCognite3DViewer.addCadModel(...)orCognite3DViewer.addPointCloudModel(...) Cognite3DViewer.loadCameraFromModel(...)] has been added for loading camera settings from CDF when available.- Due to the way
@cognite/revealstreams data, theOnProgressDatais no longer exported, and theaddModelfunction no longer accepts anonProgressparameter. Because of this theonCompleteoption inCognite3DViewer.addModelhas been deprecated. To monitor loading activity, use theonLoading-callback provided as an option when constructingCognite3DViewer. Cognite3DViewer.getCamera()can now be used to access theTHREE.Cameraof the viewer. Note that this camera shouldn't be modified.Cognite3DViewer.addModelno longer supports optionslocalPath,orthographicCameraandonComplete. Point clouds currently don't supportgeometryFilter.- Textures are not supported by
@cognite/reveal. Models can be loaded, but textures are not applied to the geometry. @cognite/revealsupports several anti-alias techniques which might improve visual quality at some performance cost. To use these new modes, see theantiAliasingHint-option provided in theCognite3dViewer-constructor.
Preparing models
@cognite/reveal requires new output file formats which means that 3D models that have been converted before June 2020 might need reconversion. To determine if a model needs reconversion, the listModelRevisionOutputs API endpoint can be used with URL parameter format=reveal-directory. This can be done using the SDK, e.g.:
// Switch these with your project/model identifier
const project = 'publicdata';
const modelId = 4715379429968321;
const revisionId = 5688854005909501;
// Prepare a SDK client and authenticate
const sdk = new CogniteClient({
appId: 'cognite.reveal.docs.IsModelCompatible',
});
await sdk.loginWithOAuth({ type: 'CDF_OAUTH', options: { project }}); // With Cognite authentication flow
await sdk.authenticate();
// Request a list of formats
const url = `https://api.cognitedata.com/api/v1/projects/${project}/3d/models/${modelId}/revisions/${revisionId}/outputs`;
const response = await sdk.get(url, { params: { format: 'reveal-directory' } });
// Determine if there's compatible outputs
const hasCompatibleOutputs = response.data.items.length > 0;
alert(`Is model compatible with @cognite/reveal: ${hasCompatibleOutputs}`);
Or by using curl (requires a Cognite API key or access token):
> curl "https://api.cognitedata.com/api/v1/projects/$PROJECT/3d/models/$MODELID/revisions/$REVISIONID/outputs?format=reveal-directory" --header 'api-key: $APIKEY'
{
"items": [
{
"format": "reveal-directory",
"version": 8,
"blobId": 42723775736403107
}
]
}
where PROJECT, MODELID, REVISIONID and APIKEY must be set in the environment variables. If the model isn't compatible the response will be an empty list, i.e.
{
"items": []
}
If the model isn't compatible with @cognite/reveal, it must be reprocessed. This can be done by uploading a new revision in Cognite Data Fusion. When uploading a new revision, asset mappings must be recreated. If the node hierarchy in the new revision is identical to the previous model node IDs should be identical and the asset mappings can be copied from one revision to the other by using the get3DMappings and create3DMappings API endpoints.
Another alternative is to use the experimental 'reprocess' endpoint which generates new model outputs for a 3D model and is less intrusive than uploading a new revision. Since this endpoint is experimental it's not publicly exposed in the API yet. Please contact lars.moastuen@cognite.com if you want to explore this option.
Common issues
TypeError: e.applyMatrix4 is not a function
If you experience TypeError: e.applyMatrix4 is not a function after migrating from
@cognite/3d-viewer to @cognite/reveal, the problem is usually caused by missing or
outdated ThreeJS.
To fix this issue, install or update ThreeJS using the
three-package. This should match the version
used by @cognite/reveal.