instantreality 1.0

Direct scene manipulations with Javascript

Keywords:
script, javascript, dynamic
Author(s): Michael Zoellner
Date: 2009-01-01

Summary: This tutorial shows you how to create, manipulate, add and remove nodes via Javascript

Introduction

The previous tutorial (Implementing Script nodes in Javascript) shows the basic mechanisms to integrate your Script-node with inputOnly/OutputOnly-fields and ROUTEs. This fits well in the overall X3D-design where nodes usually do not manipulate the scene-graph structure. However, for some applications it is necessary to change the scene-graph directly. But be warned. If you access and manipulate the graph with your code all automatic event-processing mechanisms are gone. What is even more important is the following fact: The X3D Browser has to switch all runtime optimizations off for the current executionContext (e.g scene, inline or ProtoInstance) since the system can not know which part of the scene will be manipulated by your script. Therefore you have to mark Script-nodes which are allowed to manipulate the scene by setting the directOutput-field to true.

Warning: You have to set Script.directOutput to true if you directly manipulate the scene inside of your script-code

This simple example will create a small subtree via Javascript. The resulting tree will include a Box and a single Transform as child of a given Group.

Code: The resulting nodes

<Transform>
	<Shape>
		<Appearance>
			<Material />
		</Appearance>
		<Box />
	</Shape>
</Transform>
        

Basic Scene setup

We only need two nodes in the scene to get started. A Group node where we will put the dynamic objects in and a Script node that generates them.

Code: Basic Scene setup

<Group DEF='dynamic_group' />
<Script DEF='script' directOutput='true' />
	

Accessing exiting nodes

To start with our scene-changes we have to access at least a single node. There are two different ways to access exiting nodes within your script code. You can search for your node within the current executionContext using the Browser.currentScene.getNamedNode() method. This may take some milliseconds depending on how many named nodes you have in your executionContext.

Code: Searching for a existing node by name

var dynamic_group = Browser.currentScene.getNamedNode('dynamic_group');
	

The second method is to reference the node in a initializeOnly field.

Code: Referencing a existing node inside of the Script interface

<Group DEF='dynamic_group' />
<Script DEF='script' directOutput='true' >
	<field accessType='initializeOnly' name='dynamic_group' type='SFNode'>
		<Group USE='dynamic_group'/>
	</field>
</Script>
        

Use whatever method is more adequate for your application.

Creating nodes

Next we will create these nodes from inside out and add them as children to their parent node. There are again two methods. First you can use the ISO-standard X3D field-accessor to create and assign those fields. The other method uses some InstantReality-specific addChild() calls.

Code: Creating and adding nodes using standard methods

var box = Browser.currentScene.createNode("Box");

var material = Browser.currentScene.createNode("Material");
material.diffuseColor = new SFColor(1, 0.5, 0);
	
var appearance = Browser.currentScene.createNode("Appearance");
appearance.material = material;

var shape = Browser.currentScene.createNode("Shape");
shape.geometry = box;
shape.appearance = appearance;

var transform = Browser.currentScene.createNode("Transform");
transform.children = new MFNode(shape);

// finally add the transform node to the scene
dynamic_group.children[0] = transform;
        

The second method utilizes the intelligent type-system by not adding the new nodes to a specific field but to the parent-node. The system therefore tries to find the correct field and adds the child. In addition it calls the SFNode constructor directly. But remember: This works only in the InstantReality runtime.

Code: Creating and adding nodes using InstantReality extensions

var box = new SFNode("Box");

var material = new SFNode("Material");
material.diffuseColor = new SFColor(1, 0.5, 0);
					
var appearance = new SFNode("Appearance");
appearance.addChild(material);				

var shape = new SFNode("Shape");
shape.addChild (box, appearance);

// create a transform node and put the shape into it
var transform = new SFNode("Transform");
transform.addChild (shape);

// finally add the transform node to the scene
dynamic_group.addChild(transform);
        

The last line in both versions adds the new created transform-node to the given dynamic_group. Otherwise these nodes are not added to the scene and thus not visible.

Image: Dynamic Box

Manipulating nodes

Once the dynamic nodes are in the Scene we can read and manipulate the field values directly.

Code: Accessing node values

// printing the number of children
Browser.println( transform.children.length );

// printing the first child's translation
Browser.println( transform.translation );

// changing the transformation
transform.translation = new SFVec3f(1,0,0);

Removing nodes

One thing is important to know: You only can remove the node from a specific field or parent but you can not delete a node. It will be automatically deleted with the last reference. For the actual removal there are again different methods. For all nodes which have a children-field the safest method is to use the removeChildren field. This is independent of the actual position inside of the children container.

Code: Removing nodes via removeChildren-field

// remove transform
dynamic_group.removeChildren = new MFNode( transform );
        

Another save but again non-standard method is the removeChild() function. This works for all nodes and SFNode/MFNode-links but is a InstantReality specific extension.

Code: Removing nodes via removeChild() function

// remove transform
dynamic_group.removeChild ( transform );

SFNode fields you can set directly to null to remove the link

Code: Removing nodes in SFNode-fiels via null

// remove appearance
shape.appearance = null;

Adding and deleting Routes

In addition to the nodes you can also dynamically add and delete routes

Code: Adding and deleting routes to given nodes

Browser.addRoute( my_timeSensor, 'fraction_changed', my_interpolator, 'set_fraction');
Browser.deleteRoute( my_TimeSensor, 'fraction_changed', my_interpolator, 'set_fraction');	
	

You can even add and delete routes directly to the Script using the 'this' object

Code: Adding and deleting routes to the script node

Browser.addRoute( my_timeSensor, 'time', this, 'printTime');
        

This tutorial should give you a first idea how to manipulate your scene dynamically. For more inspiration look at the X3D specification and the online Javascript documentation.

Example Files

The first three example files are generating one Box and several Boxes at random positions. The next two are showing how to create dynamic nodes from Protos and advanced manipulation methods. The last one creates and connects a route directly to the script node.

Files: