Discussions

Scene

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

ParameterTypeDescription
fromQueryObject | StringThe 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

ParameterTypeDescription
idStringSearch for a node that matches the given id.
parentBooleanReturn the parent of the node id provided. This requires using the id option.
childStringReturn the first child of the node id provided. This requires using the id option.
includeParentBooleanIf true, the query will also try to match on from. The player API sets this to true by default.
shallowBooleanIf true, the query will not search the children's children.
skipModelsBooleanIf true, the query will not descend into Model type nodes.
hierarchicalBooleanIf true, the query will also descend proxies and model references. This requires the scene-graph to be evaluated.

Query Filter

ParameterTypeDescription
nameString | RegExpThe name of the node you are searching for must match the given name. Can use wildcard "*".
typeString | String[]Specify that the node type must be one of the types listed below. Exact matches only.
tagsArray<String | RegExp>The node must have at least one tag matching one of the given tags. Can use wildcard "*".
hasPlugStringThe node must have the given plug type, from the list below.

Operator Filter

ParameterTypeDescription
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

ParameterTypeDescription
evalNodeBooleanIf true, the result will be the evaluated node. Can be combined with property to get the evaluated node's property.
evalPlugStringIf true, the result will be the evaluated plug. Can be combined with property to get the evaluated plug's property.
plugString- 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.
operatorIndexNumberIf set, return the operator at that index in the array of operators on a given plug.
propertyString- 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([player.scene.findNode({name: /Sphere\$/}), 'name']);
>> "Sphere$"
api.scene.get([player.scene.findNode({name: /Sphere\^/}), 'name']);
>> "Sphere^"
api.scene.get([player.scene.findNode({name: /Sphere\+/}), 'name']);
>> "Sphere+"
api.scene.get([player.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([player.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([player.scene.findNode({name: "Sphere\"\""}), 'name']);
>> "Sphere\""

api.scene.get([player.scene.findNode({name: "Sphere\\^"}), 'name']);
>> "Sphere^"

api.scene.get([player.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)
ParemeterTypeDescription
parentIdStringID of the new parent
childIdsArrayIDs of the children we're updating
indexIntegerOptional 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)
ParameterTypeDescription
nodeIdStringThe id of the node receiving the operator
plugTypeStringThe plug that is receiving the operator
operatorTypeStringThe type of operator being added
operatorObjectObjectThe 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)
ParameterTypeDescription
nodeIdStringThe node that will be aligned.
alignNodeIdStringThe node nodeId will align with.
options.anchorNodeIdStringOptional - the child of nodeId that will be aligned with alignNodeId. If left undefined, nodeId itself will be aligned to alignNodeId.
options.withRotationBooleanDefault 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)
ParameterTypeDescription
nodeIdsArray || QueryObjectIds of the nodes to merge, or query that selects those meshes
mergedNameStringName of the resulting merged mesh
options.throwOnWarningsBooleanIf the command has warnings, throw an error. Defaults to true.

Scene Graph Sandbox

🚧

Warning

Test your methods here using player.scene instead of api.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.