The tag manager is responsible for altering node and material data, such as the node visibility, transformation and color information. This functionality might sound very similar to the Parameter system, however the tag manager comes with some advantages.
NOTE: The tag manager works for NODES and MATERIALS alike, but for the sake of simplicity the main part of this documentation is about NODES, as this will be the main use case of the tag manager. MATERIALS are covered in a dedicated chapter at the end of this page.
The tag manager of course only works, if tags are set on the desired nodes. Have a read in the Babylon.js docs to get familiar with this feature in general.
Most common 3d tools are able to export tags along with it's nodes. If the viewer is used in a Combeenation configurator, you can also adapt the tags in the babylon asset editor.
Altering certain node data is as easy as calling setTagParameterValue. The viewer will extract all nodes that share the desired tag value and alters these nodes according to the given parameter and value.
import { Parameter } from '@combeenation/3d-viewer';
// shows all nodes that have the tag "wheel" set
viewer.tagManager.setTagParameterValue('wheel', Parameter.VISIBLE, true);
It's also possible to set multiple tag values simultaniously, using setParameterValues
// show nodes with tags "seat" and "wheel" and also move "wheel" nodes to a different position
viewer.tagManager.setParameterValues([
{ tagName: 'seat', parameterName: Parameter.VISIBLE, value: true },
{ tagName: 'wheel', parameterName: Parameter.VISIBLE, value: true },
{ tagName: 'wheel', parameterName: Parameter.POSITION, value: '(1, 0, 0)' },
]);
Similar to the legacy parameter system, there are so-called "built-in parameters" that are already implemented by the system. These are:
For example the parameter handler for the visibility will just call the Babylon.js function setEnabled for each node that is affected by the given tag value.
It's also worth noting that the parameters will not be applied to sub childs of tagged nodes. In this way the user has full control over the node states and can use the node tree structure to its advantage. If a sub child needs special treatment just set an according tag.
Since some scenarios can not be handled with built-in parameters, the user has the possibility to create custom parameter observers as well. This can be done by using the setParameterObserver function.
import { Texture } from '@combeenation/3d-viewer';
import { setMaterialTexture } from '@combeenation/3d-viewer/dist/lib-cjs/api/util/babylonHelper';
viewer.tagManager.setParameterObserver('CustomTextureParam', async ({ nodes, newValue }) => {
// create the texture from the input value
const texture = new Texture(/** @type {string} */ (newValue));
// go through each node and set the created texture
nodes.forEach(node => setMaterialTexture(node, texture));
return true;
});
The parameter observer will be triggered with setTagParameterValue
or setParameterValues
, just like it can be done with built-in parameters.
viewer.tagManager.setTagParameterValue('wheel', 'CustomTextureParam', 'https://some-texture-url...');
setParameterObserver
is called a second time for the same parameter, the first observer will be removed.
In this way you can also overwrite built-in parameter observers.
In most cases you want to startup the viewer with a certain parameter set, so that the viewer can already take the values into account when bootstrapping.
Therefore the bootstrap function can now be enhanced with an initial parameter set on the tagManagerParameterValues
input.
Keep in mind to register your custom parameter observers before the bootstrap
function, so that the initial tag values can be considered immediatly.
const viewer = new Viewer(canvas, createSpec());
viewer.tagManager.setParameterObserver('CustomTextureParam', async ({ nodes, newValue }) => {
const texture = new Texture(/** @type {string} */ (newValue));
nodes.forEach(node => setMaterialTexture(node, texture));
return true;
});
// provide initial tag values in the same format as for `setParameterValues` function
await viewer.bootstrap([
{ parameterName: 'CustomTextureParam', value: 'https://some-texture-url...', tagName: 'wheel' }
]);
Per default, tags are cloned along with all nodes inside the variant when calling the clone function. This may be the desired behaviour in some cases, if you want to treat original and cloned nodes as a combined group.
In other cases you may want to have dedicated groups for each clone, which require the tags of the clone to be renamed.
This can be done with the new tagMapping
input of the clone function.
This input requires an object with the original tag name as key
and the new tag name as value
.
await viewer.variantInstances.clone(
'WheelOrig',
'WheelClone',
// leave legacy parameters empty when cloning the variant
{},
// rename `wheel` tag to `wheelCloned` for nodes inside the cloned instance
{
wheel: 'wheelCloned',
}
);
// only the cloned wheel should be moved to (5, 0, 0)
await viewer.tagManager.setTagParameterValue('wheelCloned', Parameter.POSITION, '(5, 0, 0)');
Whenever tag values are set (eg: setTagParameterValue
, setParameterValues
), the tag values are persisted in a dedicated store.
If new nodes are loaded into the scene, the values from the tag store are applied on the nodes immediatly, so that node data is synced with the
already existing nodes in the scene.
Let's make an example:
wheel
is available and visible in the scenesetTagParameterValue('wheel', Parameter.VISIBLE, false)
to hide the nodewheel
setwheel
tag is false
!In some cases you will probably set a tag only on one node, for example when handling the visibility of a whole node tree branch by controlling the parent node. In this case the tag doesn't really make sense, because nothing has to be grouped. However the tag is required to be able to use the tag manager though.
For this use case parameter values can be set on nodes directly, using the nodeName
instead of a tagName
.
viewer.tagManager.setTagParameterValue('tagNameWheel', Parameter.VISIBLE, true);
// vs
viewer.tagManager.setNodeParameterValue('nodeNameWheel', Parameter.VISIBLE, true);
viewer.tagManager.setParameterValues([
{ tagName: 'tagNameWheel', parameterName: Parameter.VISIBLE, value: true },
// vs
{ nodeName: 'nodeNameWheel', parameterName: Parameter.VISIBLE, value: true },
]);
Accessing nodes directly instead of working with corresponding tags has the advantage, that the 3d models don't have to be extended with tags. In this case most 3d models can be used directly, without having to be modified by 3d artists or with the tag editor on the Combeenation platform.
Working with tags is certainly preferred when parameters should be applied on multiple nodes. In this way the parameter has to be set just once instead of having to go through all affected nodes.
Furthermore tags can be forwarded to cloned variants. If you want to set the same parameter values on the clone and the original variant, you can do this by accessing the dedicated tag of the original variant. This however is not possible for nodes, as node names always have to be unique and therefore the cloned variant will have different node names than the original one.
Whilst it is no hard restriction of Babylon.js, we strive for unique naming of nodes, in order to avoid confusion and undesired side effects. Therefore each time a node is created in the scene by the viewer, a certain naming strategy is used. Nodes are created by the viewer in the following scenarios:
The first time a node gets added, the original name will not be altered! In this way it's easier for the user to recognize the original node structure from the 3d model.
However if a node is added multiple times by creating additional Spec elements and variant instances or by using the clone function, a name clash would occur and therefore the new name is optionally suffixed with the element and variant instance name.
If required, you can also adapt this strategy by overwriting the nodeNamingStrategyHandler. You'll receive the variant, variant instance and variantParameterizable (aka element) as payload and can build your desired node name strategy accordingly.
In some cases you want the cloned instance to have the exact same tags as the original one. In this way the nodes of the original and cloned instance can be treated as one group. In other cases you want the cloned instance to have different tags, so that the clone can be adjusted seperately.
However you can achieve both results by using the tagMapping
parameter of the clone function.
If the parameter is left empty, the tags from the old instance are copied onto the clone.
If you want to have renamed tags, you can set up the tagMapping
object in the following way.
{
wheelTag: 'clonedWheelTag',
seatTag: 'clonedSeatTag',
}
In this way all nodes that had the tags wheelTag
and seatTag
will have new tags clonedWheelTag
and clonedSeatTag
after cloning.
It's also worth mentioning that the tag manager state of the original tags will also be copied. In this case the clone looks exactly the same than the original instance initially. However the two instances will be altered differently, depending on the dedicated node and tag parameters, that will be applied later on.
The tag manager also works for materials, since tags can also be set on material objects in Babylon.js. Simliar to nodes, there are 3 ways to set a material parameter:
// set single material parameter
viewer.tagManager.setMaterialParameterValue('matWindow', Parameter.COLOR, '#DD0060');
// set material parameter via associated tag
// => tag 'windowTag' has to be available on a material
viewer.tagManager.setTagParameterValue('windowTag', Parameter.COLOR, '#DD0060');
// set parameter in bulk call
viewer.tagManager.setParameterValues([
{ materialName: 'matWindow', parameterName: Parameter.COLOR, value: '#DD0060' },
{ tagName: 'seat', parameterName: Parameter.VISIBLE, value: true },
{ nodeName: 'wheel', parameterName: Parameter.VISIBLE, value: false },
]);
As shown in the example above, it is possible to mix different "subjects" in the setParameterValues
bulk call.
The tag manager automatically applies the parameter value to the dedicated subject (material, node or tag).
Furthermore it is also possible to add the same tag to a node and a material. Whilst this sounds a bit theoretic at first glance, it can be used to advantage in combination with custom observers, where you can apply different logic to node tags and material tags as shown in the example below.
The following material parameters are supported:
The tag manager will set the corresponding properties on the material object. Parameter values will also be stored if the target material is not available in the scene yet. If the material gets added, the tag manager state will be synced onto this material.
Materials can also be targeted in custom observers, as shown in the following example.
viewer.tagManager.setParameterObserver('CustomTextureParam', async ({ materials, newValue }) => {
const texture = new Texture(/** @type {string} */ (newValue));
materials.forEach(material => material.albedoTexture = texture);
return true;
});
If the parameter change is triggered by a tag, the observer may contain nodes and materials at the same time.
viewer.tagManager.setParameterObserver('CustomTextureParam', async ({ nodes, materials, newValue }) => {
const texture = new Texture(/** @type {string} */ (newValue));
// set texture on targeted nodes
nodes.forEach(node => setMaterialTexture(node, texture));
// set texture on targeted material directly
materials.forEach(material => material.albedoTexture = texture);
return true;
});
// called by
viewer.tagManager.setTagParameterValue('textureTag', 'CustomTextureParam', 'someTextureUrl');
Generated using TypeDoc