NodeReference Class
Inherits from: | Node |
A node reference is a node that refers to an external .node asset on the disk, which is obtained by exporting a node from the world. The world can contain several node references referring to the same .node file.
The .node file usually contains a pre-fabricated node (or a hierarchy of nodes) with links to all the materials, properties and physical bodies that are required for its rendering and behaviour. All changes that are made for the node reference via UnigineEditor are saved into this file. When editing the node reference via code, implement saving changes into the .node file.
Node references should be used to propagate a lot of identical objects repeated in the world: unlike regular nodes, reference nodes are loaded into the world faster because of the internal cache usage. It will also make it easier to apply the same changes to all repeated objects.
Node reference supports nesting, i.e. a node reference can include other node references which is helpful for implementing complex instancing solutions.
A NodeReference instance is a Posessor of the nodes stored by it, so it is responsible for loading and deleting them. You can detach the node from the NodeReference instance to make it managed by the World.
Automatic Unpacking#
The Automatic Unpacking mode defines where the content of NodeReferences is placed in the world nodes hierarchy when it is loaded at run time (at the world startup or when a new NodeReference is created). When enabled, the content of node references is extracted to simplify hierarchy management. This is a global setting for all worlds used in your project.
- Auto-unpacking is disabled in C++ and UnigineScript only projects by default for backward compatibility.
- Auto-unpacking is enabled in C# projects by default.
Use engine.world.isUnpackNodeReferences() to check if auto-unpacking is enabled in your project.
You can toggle the auto-unpacking mode by calling engine.world.setUnpackNodeReferences() in the system script before a world is loaded:
#include <core/unigine.h>
int init()
{
// enable automatic unpacking
engine.world.setUnpackNodeReferences(1);
return 1;
}
The auto-unpacking mode affects the relation between the transformations of the NodeReference and the nodes stored by it.
Automatic Unpacking Disabled#
If auto-unpacking is disabled (by default in C++ / UnigineScript only projects), the content of NodeReference nodes is added to the root of the world nodes hierarchy and can be accessed only as references.
#include <core/unigine.h>
int init() {
// create a NodeReference instance
NodeReference nodeRef = new NodeReference("unigine_project/nodes/node_reference_0.node");
// get the reference node
Node group = nodeRef.getReference();
// find the desired node
Node node_2 = group.getChild(group.findChild("Node_2"));
return 1;
}
Automatic Unpacking Enabled#
When automatic unpacking of node references is enabled (by default in C# projects), the content of NodeReference nodes is extracted to simplify hierarchy management. It is done only at run time and does not affect your *.world and *.node files.
The heirarchy of NodeReference's contents is attached to it as an immediate child, so you can access it in a straightforward manner:
#include <core/unigine.h>
int init() {
// create a NodeReference instance
NodeReference nodeRef = new NodeReference("nodes/node_reference_0.node");
// get the root node of the pre-fabricated hierarchy
Node group = nodeRef.getChild(0);
// find the desired node among all descendants
Node node_2 = nodeRef.findNode("Node_2", 1);
return 1;
}
Node Reference Transformations#
Local transformation of the root node stored by the NodeReference is not necessarily identity, it can be displaced relative to the NodeReference position, for example.
If automatic unpacking of node references is enabled, the transformations of nodes are simply subject to parent-child relation. However, if automatic unpacking was disabled (i.e. the node content was added to the root of world nodes hierarchy), matrix hierarchy is used for the content of the NodeReference. Thus, when moving the NodeReference, the root node of its content also moves relatively to this NodeReference. At that, the local transformation matrix of the root reference node changes.
This relation of transformations can be destroyed by detaching the reference node from the NodeReference instance. Keep note that the unpacked detached node will remain child of the NodeReference and its tranformation matrix will still be affected by the NodeReference transformation. In this case you may need to unparent the detached node by using setParent() method.
Internal Nodes Copy#
Each Node Reference has its internal copy of nodes loaded from the .node asset and therefore this allows applying unique changes to each instance of identical Node References separately at runtime (adding, deleting, and changing the nodes inside of a Node Reference).
Other Node Reference instances will not be updated at runtime when changes are applied to a single instance. If you want to propagate these changes at runtime from one instance of a Node Reference to all the other Node Reference in the world that are linked to the same .node asset on disk, you should save changes to the asset and reload all Node References. Automatic runtime reloading is available.
See Also#
- Article on Nodes that lists the main differences between the regular nodes and reference nodes
- Article on Node Reference
Creating a Node Reference#
To create a NodeReference, you should specify a path to the existing .node file. For example, you can export a node into a .node file and then create a NodeReference by using it:
#include <core/unigine.h>
int init() {
// create a mesh
Mesh mesh = new Mesh();
mesh.addBoxSurface("box_0", vec3(1.0f));
// create a dynamic mesh by using the mesh
ObjectMeshDynamic dynamic = new ObjectMeshDynamic(mesh);
// export the dynamic mesh into a .node file
engine.world.saveNode("unigine_project/nodes/node_reference_0.node", dynamic);
// create a node reference
NodeReference nodeRef = new NodeReference("unigine_project/nodes/node_reference_0.node");
return 1;
}
Also you can cast a Node instance to the NodeReference. However, such type casting is possible only if the Node type is NODE_REFERENCE. For example:
#include <core/unigine.h>
void nodeCast(Node node) {
// check if the node is a NodeReference
if (node.getType() == NODE_REFERENCE)
{
// cast the node to the NodeReference
NodeReference nodeRef = node_cast(node);
// set a name for the NodeReference
nodeRef.setName("NodeReference_casted");
}
}
int init() {
// create a NodeReference from the file
NodeReference nodeRef_0 = new NodeReference("unigine_project/nodes/node_reference_0.node");
// set a name
nodeRef_0.setName("NodeReference_0");
// save changes into the .world file
engine.console.run("world_save");
// get the added NodeReference as a Node
Node node = engine.world.getNodeByName("NodeReference_0");
// cast the obtained Node to a NodeReference
nodeCast(node);
return 1;
}
In the result, the NodeReference_0 node will be converted to the NodeReference_casted node.
Editing a Node Reference#
Editing a NodeReference includes:
- Changing the path to the referenced .node file.
- Editing the node stored by the reference.
To access the node to which the NodeReference refers, use the getReference() method. To save the modified reference node to the same .node asset, use engine.world.saveNode() method as follows:
#include <core/unigine.h>
int init() {
// create a NodeReference instance
NodeReference nodeRef = new NodeReference("unigine_project/nodes/node_reference_0.node");
// set a name
nodeRef.setName("NodeReference_0");
// get a node stored by the reference and check if it is an ObjectMeshDynamic
if (nodeRef.getReference().getType() == NODE_OBJECT_MESH_DYNAMIC) {
// cast the referenced node to the ObjectMeshDynamic type
ObjectMeshDynamic dynamic = node_cast(nodeRef.getReference());
// save changes on the referenced node into the .node file
engine.world.saveNode(nodeRef.getNodePath(), dynamic);
}
return 1;
}
In the result, the source .node file, to which the NodeReference refers, will be updated and the NodeReference_0 node will refer to the updated node with the material assigned. If you want all NodeReferences that refer to the modifed .node asset to update automatically, you should enable automatic reloading of NodeReferences by calling the setAutoReloadNodeReferences() method preliminarily.
NodeReference Class
Members
static NodeReference ( string name ) #
Constructor. Creates a new object that references a node from a given file.Arguments
- string name - Path to a *.node file.
string getNodePath ( ) #
Returns the path to the referenced *.node file.// create an NodeReference instance
NodeReference nodeRef = new NodeReference("unigine_project/nodes/reference_0.node");
// ...
// return the path to the reference node
log.message("The referenced node is: %s\n",nodeRef.getNodePath());
Return value
Path to the referenced *.node file.Node getReference ( ) #
Returns the node stored by the reference. The method should be used when you need to change the referenced node. If getReference() has returned a NodeDummy instance, it means that several nodes of the same hierarchy level has been converted into the NodeReference.// create a NodeReference instance
NodeReference nodeRef = new NodeReference("unigine_project/nodes/single_nref.node");
// get a node stored by the reference
Node node = nodeRef.getReference();
// if the node is a NodeDummy
if (node.getType() == NODE_DUMMY){
// print the type name of each child node of the root node stored by the reference
forloop(int i = 0; node.getNumChildren();1) {
log.message("%d: %s\n",i,node.getChild(i).getTypeName());
}
}
Return value
Node instance.static int canBeReference ( string name, Node node ) #
Returns a value indicating if the hierarchy of the given node does not contain a node reference with the given name.Arguments
- string name - Node reference name.
- Node node - Node to check.
Return value
1 if the hierarchy of the given node does not contain a node reference with the given name; otherwise, 0.Node detachReference ( ) #
Returns the node stored by the reference and releases this node of ownership so it is no longer owned and referred to by the NodeReference. The node is managed by the World.// create a new NodeReference instance
NodeReference nodeRef = new NodeReference("unigine_project/nodes/node_reference_0.node");
// perform something
// ...
// get the node stored by the reference and release NodeReference ownership of the node
Node node = nodeRef.detachReference();