This module is a high level scene graph manipulation and query library.
Description
This module is only intended to be used within custom script actions inside asset configurator rules. For this purpose, the Scene API needs to be referenced with api.scene
.
The Scene API is not intended to be used in either Item logic or front-end code, where the evaluation of the scene graph happens a little bit differently. Please keep Item logic only for product logic, while 3D logic should reside only on Assets. Spreading 3D logic across Items and the front-end makes it more difficult to understand and maintain an implementation.
If you would like to test these methods with the examples provided, please use the provided testing sandbox at the bottom of the page.
You can also test these methods in the browser console while you have a ThreeKit asset's page open, using playerApi.scene
instead of api.scene
. For example: playerApi.scene.getAll({name: 'coolerMesh'})
.
For testing in the console of a page with an embedded player, you need to use the embedded player
object instead, such as player.scene
.
The Query Object
Each of the methods listed in this document allows for a query object to be passed as a parameter. You can use an object with the following key:value
pairs.
{
//Query Source
from: 'QueryObject | String', // The ID of the node whose children we query
//Query Controls
id: 'String', //Matches the node with the exact id
parent: 'Boolean', //Return the parent of the node
child: 'String', //Return the first child of the node provided
includeParent: 'Boolean', //If true, the query will also try to match on `from`
shallow: 'Boolean', //If true, the query will not search the children's children
skipModels: 'Boolean', //If true, the query will not descent into `Model` type nodes
hierarchical: 'Boolean', //If true, the query will also descend proxies and models
//Query Filter
name: 'String | RegExp', // The name of the node you are querying for
type: 'String | String[]', //The node type must be one of the `type` listed below
tags: 'Array<String | RegExp>',
hasPlug: 'String', //The node must have the listed plug from the list below.
//Operator Filter
properties: '{ [key: String]: any }', //Operator properties
//Query Result
evalNode: 'Boolean', //If true, the result will be the evaluated node
evalPlug: 'String', //If true, the result will be the evaluated plug
plug: 'String', // Plug you are querying for
operatorIndex: 'Number', //Return the operator at the provided index
property: 'String', // Several uses. See description
}
or you can use an array that defines the path to your query.
[from, plugs, plugName, index, property]
Option Details
Query Source
Parameter | Type | Description |
---|---|---|
from | QueryObject | String | The node whose children we query. For simplicity's sake, the player API automatically sets from: api.instanceId .When embedding a configurator, the ThreeKit player automatically creates new asset instances in the browser local memory. This is because an asset can be used multiple times, and the player needs to be able to distinguish between each instance of an asset where it gets referenced. The api.instanceId provides the reference to this new temporary asset created by the player for the current session. |
Query Controls
Parameter | Type | Description |
---|---|---|
id | String | Search for a node that matches the given id . |
parent | Boolean | Return the parent of the node id provided. This requires using the id option. |
child | String | Return the first child of the node id provided. This requires using the id option. |
includeParent | Boolean | If true, the query will also try to match on from . The player API sets this to true by default. |
shallow | Boolean | If true, the query will not search the children's children. |
skipModels | Boolean | If true, the query will not descend into Model type nodes. |
hierarchical | Boolean | If true, the query will also descend proxies and model references. This requires the scene-graph to be evaluated. |
Query Filter
Parameter | Type | Description |
---|---|---|
name | String | RegExp | The name of the node you are searching for must match the given name . Can use wildcard "*". |
type | String | String[] | Specify that the node type must be one of the types listed below. Exact matches only. |
tags | Array<String | RegExp> | The node must have at least one tag matching one of the given tags . Can use wildcard "*". |
hasPlug | String | The node must have the given plug type, from the list below. |
Operator Filter
Parameter | Type | Description |
---|---|---|
properties | { [key: String]: any } | The operator must match the given template. Only works for number, string, and boolean properties. Example: properties: { angle: 120, shift: true, mode:"parallel" } |
Query Result
Parameter | Type | Description |
---|---|---|
evalNode | Boolean | If true, the result will be the evaluated node. Can be combined with property to get the evaluated node's property . |
evalPlug | String | If true, the result will be the evaluated plug. Can be combined with property to get the evaluated plug's property. |
plug | String | - If set, the result will be the first matching operator of the plug . - If set, it will also act as a filter like hasPlug . - Can be combined with property to get the operator's property. - If combined with property , but there is no operatorIndex , the operator must have the given property . |
operatorIndex | Number | If set, return the operator at that index in the array of operators on a given plug. |
property | String | - Can be used with evalNode to get an evaluated property. - Can be used with evalPlug to get an evaluated plug property. - Can be used on its own to get the node's "name" or "type". - Can be used with plug to get the operator's property. |
Node Types
{ 'Scene', 'Objects', 'MaterialLibrary', 'PolyMesh', 'Light', 'Camera',
'Null', 'Model', 'Material', 'Image', 'Pass', 'Renderer',
'Bone', 'Annotation', 'Measurement', 'Sprite', 'Shape', 'Helper',
'Vrscene', 'Item', 'Upload', 'Group', 'Layer', 'ImageLayer',
'Composite', 'ShadowPlane', 'Vector', 'Font', 'Video', 'Lut',
'Vfb', 'Stage', 'VrayMesh', 'VrayLight', 'VraySky', 'LayoutContainer',
'Connector' }
Plug Types
{ 'Annotation', 'Camera', 'Connector', 'Environment', 'Font',
'Group', 'Image', 'LayoutContainer', 'Light', 'Lut',
'Material', 'Measurement', 'Null', 'Objects', 'Physics',
'Player', 'PolyMesh', 'PostEffect', 'Properties', 'Proxy',
'RenderSettings', 'Selection', 'ShadowPlane', 'Shape', 'Sprite',
'Timeline', 'Transform', 'Vector', 'Vfb', 'Video',
'VrayLight', 'VrayMesh', 'VraySky', 'Vrscene' }
Example
{
name: 'Cooler',
plug: 'Transform',
property: 'rotation',
}
The Cooler -> Transform -> rotation object above will accomplish the same thing as the array below. You can find this path array using the find method.
['54f3a9f3-3ed1-4486-b5e7-9025fa0d9559', 'plugs', 'Transform', 0, 'rotation']
Using Regex
When querying a node by name, you may use a string or regular expression object. If you’re querying a node whose name has special characters, it’s recommended that you use a RegEx object for simplicity. Below are some examples of using a RegEx object to query nodes with various special characters in their names.
api.scene.get([api.scene.findNode({name: /Sphere\$/}), 'name']);
>> "Sphere$"
api.scene.get([api.scene.findNode({name: /Sphere\^/}), 'name']);
>> "Sphere^"
api.scene.get([api.scene.findNode({name: /Sphere\+/}), 'name']);
>> "Sphere+"
api.scene.get([api.scene.findNode({name: /Sphere\"/}), 'name']);
>> "Sphere\""
You can also use strings to query nodes by name, but these strings are not taken literally. Some of their special characters are replaced, and then the string is compiled into a RegEx in the form /^ReplacedString$/i
. “ReplacedString" being the string that had some of its special characters replaced.
(
and )
are escaped automatically, thus they are treated literally. You can’t do a ReGex “Group” via a string, you must use an actual RegEx object to achieve groups
*
is converted to the regex (.*)
, thus behaving like a simple wildcard, the same as if you use *
in a Google search
If the string starts or ends with a double-quote, "
, it is removed. Thus, if you’re node name is something like "Ugly" Fabric, your string must be:
player.scene.get([player.scene.findNode({name: "\"\"Ugly\" Fabric"}), 'name'])
Here are the above examples implemented via strings:
// note, the name of the node ended with a space, not the "+" sign
api.scene.get([api.scene.findNode({name: "Sphere\\+ "}), 'name']);
>> "Sphere+ "
// first and last character are stripped if they are double-quotes, so I need to put 2
api.scene.get([api.scene.findNode({name: "Sphere\"\""}), 'name']);
>> "Sphere\""
api.scene.get([api.scene.findNode({name: "Sphere\\^"}), 'name']);
>> "Sphere^"
api.scene.get([api.scene.findNode({name: "Sphere\\$"}), 'name']);
>> "Sphere$"
Get All
getAll
method will return object(s) of { nodeId: value }
for all values that match the query.
Finds all nodes:
api.scene.getAll(null || QueryObject)
Examples
You query for and get all matching values for something as simple as the node's name:
api.scene.getAll({name: 'coolerMesh'})
{
"a5628847-6af9-4bf8-a50a-de97a4388a31":{
"id":"a5628847-6af9-4bf8-a50a-de97a4388a31",
"name":"coolerMesh",
"type":"PolyMesh",
"plugs":{
"PolyMesh":[],
"Transform":[],
"Properties":[],
"Material":[]
},
"children":[],
"parent":"f508e33a-d5b2-439d-ad8d-57ead4851a99",
"sceneId":"88dd117b-3678-4aa5-870a-c82ae380f8a3",
"_v":3
}
}
You can provide additional parameters to your query:
api.scene.getAll({
name: 'coolerMesh',
plug: 'Material',
property: 'defaultColor'
})
{
"cf1a718a-5968-4b8a-bbf6-b5b660cc9cb5":
{
"r":0.6901960784313725,
"g":0.6901960784313725,
"b":0.6901960784313725
}
}
Get
get
method will return the value matching the query. An empty query will return the top level node.
api.scene.get(QueryObject)
Examples
api.scene.get({name: 'coolerMesh'})
{
"id":"27f9ae9c-8f42-4ba9-9671-f87f926a9a07",
"name":"coolerMesh",
"type":"PolyMesh",
"plugs":{
"PolyMesh":[],
"Transform":[],
"Properties":[],
"Material":[]
},
"children":[
],
"parent":"9035187b-7dba-40a7-8830-d03dc3fde1f8",
"sceneId":"6403ef24-5a4e-4043-9309-488607af8941",
"_v":3
}
api.scene.get({
name: 'coolerMesh',
plug: 'Material',
property: 'reference'
})
'ff335b7a-92fc-484d-a242-ff01e7c5391f'
Find
find
method will locate the first matching query & return its path. An empty query will return the top-level node's ID.
api.scene.find(QueryObject)
Examples
api.scene.find({name: 'Cooler'})
['54f3a9f3-3ed1-4486-b5e7-9025fa0d9559']
api.scene.find({
name: 'Cooler',
plug: 'Transform',
property: 'rotation'
})
['54f3a9f3-3ed1-4486-b5e7-9025fa0d9559', 'plugs', 'Transform', 0, 'rotation']
Find Node
findNode
method will locate the first matching node & returns its ID. An empty query will return the top-level node's ID.
api.scene.findNode(QueryObject)
Example
api.scene.find({type: 'PolyMesh'})
or
api.scene.find({name: 'coolerMesh'})
Although the queries are different, they will resolve to the same node because the coolerMesh
is the first node with the type of PolyMesh
:
['961841c9-6b92-4ea2-bdca-6cfc25ecc768']
Filter
filter
method will return an array of all matching paths to a query.
api.scene.filter(QueryObject)
Example
api.scene.filter({
type: 'PolyMesh',
plug: 'Transform',
property: 'rotation'
})
[
["391c3593-4c8e-4635-987f-a1a2394aecda","plugs","Transform",0,"rotation"],
["f823a979-4d67-446d-bb1f-177404c75e62","plugs","Transform",0,"rotation"],
["3e5081d3-4a60-42af-8797-aa7cff4cb7bb","plugs","Transform",0,"rotation"],
["558b6438-6ef5-45be-b676-314a48810248","plugs","Transform",0,"rotation"],
["d5abeac3-5fb7-4002-9927-32ce2bdf6094","plugs","Transform",0,"rotation"],
["0f58121c-d7ec-4ed4-949a-71b076184742","plugs","Transform",0,"rotation"],
["1f3a95cf-7ba5-46f2-9f0b-6ab305a36b7a","plugs","Transform",0,"rotation"],
["de5fcbe9-228b-4c69-9e7e-de440484d7d4","plugs","Transform",0,"rotation"],
["40e6255b-fd14-47da-929e-c6646d5923b6","plugs","Transform",0,"rotation"],
["4457cd9a-41ff-4708-994a-1c9a8ce3ab63","plugs","Transform",0,"rotation"]
]
Filter Nodes
filterNodes
method returns an array of all node IDs that match the query.
api.scene.filterNodes(QueryObject)
Example
api.scene.filterNodes({type: 'PolyMesh'})
["5ed10727-5dbc-429f-b0f1-b566157fa71a", "2cf1a3f8-8529-4bec-8d24-7978eac41520", ...]
Set
set
method will set some value at the first query match. This method will accept two parameters: QueryObject
and value
. The value parameter must follow the format of the property being set (object, string, boolean, etc.).
api.scene.set(QueryObject, value)
Example
api.scene.set(
{
name: "meterArrow_GEO",
plug: 'Transform',
property: 'rotation'
},
{x: 0,y: 50, z: 0}) // rotation property is an object with x, y, z values
{
"path": [
"45a82fe7-de94-413e-9639-fb9928a4706f",
"plugs",
"Transform",
0,
"rotation"
],
"value": {
"x": 0,
"y": 50,
"z": 0
}
}
This particular example is subtle, zoom in on the meter to see it:
Set All
setAll
method will set some value for each of the query matches.
api.scene.setAll(QueryObject, value)
Example
api.scene.setAll(
{
from: editingAssetId,
plug: 'Transform',
property: 'translation'
}, {x: 3, y: 4, z: 5})
[{…}, {…}, {…}, {…}, …]
Reparent
reparent
method will set a new parent for all of the children in the array.
api.scene.reparent(parentId, childIds, index)
Paremeter | Type | Description |
---|---|---|
parentId | String | ID of the new parent |
childIds | Array | IDs of the children we're updating |
index | Integer | Optional child index in the new parent |
Example
let parentId = '3352d9bb-9841-465a-a872-d3bf3c74b42a'; // ID of the new parent
let childIds = ['f4732ef7-4477-48d4-94fa-34e211e51c23']; // IDs of the children you are updating
let index; // Index of the optional child index in the new parent
api.scene.reparent(parentId, childIds, index)
{
parentId: "b35e489e-17b2-406e-b9eb-5d46bd21a737",
childIds: Array[1],
index: 0
}
Add Node
addNode
method will add a node to the scene.
api.scene.addNode(Node)
Example
api.scene.addNode(
{
name: "My Node",
type: "PolyMesh"
},"3352d9bb-9841-465a-a872-d3bf3c74b42a" // ID of the parent node for the new node
)
"32f1ea99-bfb9-4831-9f7f-46ad07c99a51"
Delete Node
deleteNode
method will delete a node from a scene.
api.scene.deleteNode(NodeID)
Example
api.scene.deleteNode("52fa1c1b-2995-4980-b8f0-aa3158bc3c9c")
{
"value": [
"52fa1c1b-2995-4980-b8f0-aa3158bc3c9c"
],
"type": "Node",
"path": [
"0b13c621-4ba7-42a0-8770-be44afdb4c79",
"children"
]
}
Add Operator
addOperator
will add an operator to a node.
api.scene.addOperator(nodeId, plugType, operatorType, operatorObject)
Parameter | Type | Description |
---|---|---|
nodeId | String | The id of the node receiving the operator |
plugType | String | The plug that is receiving the operator |
operatorType | String | The type of operator being added |
operatorObject | Object | The details of the operator |
Example
let nodeId = "53f3ff02-8151-46ce-8831-d3e91285e024";
let plugType = "Material";
let operatorType = "Falloff";
let operatorObject = {color: {"r":0.5725490196078431,"g":0.09803921568627451,"b":0.09803921568627451}, strength: 1};
api.scene.addOperator(nodeId, plugType, operatorType, operatorObject)
Add Attribute
addAttribute
method will add a configurator attribute.
api.scene.addAttribute(Attribute)
Example
api.scene.addAttribute({
type: "String",
name: "Attribute Name",
values: ['Hey there', 'other'],
defaultValue: 'Hey there'
})
Fetch
fetch
method will fetch an asset and make it available for querying sceneGraph data.
api.scene.fetch(AssetID)
Example
api.scene.fetch(NodeId)
"a11fb954c6956a6e72a0c3bdbd2eb94bca94c5a7"
Align Node
alignNode
method will move a provided nodeId
to align with another.
api.scene.alignNode(nodeId, alignNodeId, options)
Parameter | Type | Description |
---|---|---|
nodeId | String | The node that will be aligned. |
alignNodeId | String | The node nodeId will align with. |
options.anchorNodeId | String | Optional - the child of nodeId that will be aligned with alignNodeId. If left undefined, nodeId itself will be aligned to alignNodeId. |
options.withRotation | Boolean | Default is false. When set to true, both position and rotation of alignNodeId and anchorNodeId will be same after alignment. |
Example
let nodeId = "d42068b1-8d2f-4c08-9935-68bb8de66bf6";
let alignNodeId = "143527fc-5f14-44de-ab35-48fa6099eb22";
let options = {anchorNodeId: "73988328-944f-4f8f-8fe9-5f65108a4950", withRotation: true};
api.scene.alignNode(nodeId, alignNodeId, options)
Merge Meshes
mergeMeshes
method will merge the meshes specified by nodeIds
, and creates a single mesh with the name specified by mergedName
api.scene.mergeMeshes(nodeIds, mergedName, options)
Parameter | Type | Description |
---|---|---|
nodeIds | Array || QueryObject | Ids of the nodes to merge, or query that selects those meshes |
mergedName | String | Name of the resulting merged mesh |
options.throwOnWarnings | Boolean | If the command has warnings, throw an error. Defaults to true. |
Scene Graph Sandbox
Warning
Test your methods here using
player.scene
instead ofapi.scene
.
IDs returned from these calls will be different than what is in the examples. SceneGraph IDs are generated at loadtime and do not persist.