Motivation
A program script implements the Script node using values from the interface:
- The script responds to inputs and sends outputs
A program script can be written in JavaScript, ECMAScript, and other languages
- JavaScript is easier to program
- ECMAScript is essentially JavaScript
Declaring a program script interface
For a JavaScript program script, typically give the script in the Script node's url field:
XML Encoding
1 2 3 4 5 6 7 8 9 |
<Script DEF='Bouncer'> <field accessType='initializeOnly' type='SFFloat' name='bounceHeight' value='3'/> <field accessType='inputOnly' type='SFFloat' name='set_fraction'/> <field accessType='outputOnly' type='SFVec3f' name='value_changed'/> <field accessType='inputOutput' type='SFBool' name='enabled' value='true'/> <![CDATA[ecmascript: ... ]]> </Script> |
Classic Encoding
1 2 3 4 5 6 7 |
DEF Bouncer Script { initializeOnly SFFloat bounceHeight 3.0 inputOnly SFFloat set_fraction outputOnly SFVec3f value_changed inputOutput SFBool enabled TRUE url "ecmascript: ..." } |
Initializing a program script
The optional initialize function is called when the script is loaded:
1 2 3 4 |
function initialize () { ... } |
Initialization occurs when:
- the Script node is created (typically when the browser loads the world)
Shutting down a program script
The optional shutdown function is called when the script is unloaded:
1 2 3 4 |
function shutdown () { ... } |
Shutdown occurs when:
- the Script node is deleted
- the browser loads a new world
Accessing outputs from ECMAScript
Each interface output field is a ECMAScript variable
- Read a variable to access the last output field value
- Write a variable to send an event on the output field
1 2 3 4 5 |
if (!enabled) return; lastval = value_changed[0]; // get last event value_changed[0] = newval; // send new event |
A sample JavaScript script
Create a Bouncing ball interpolator that computes a gravity-like vertical bouncing motion from a fractional time input.
Nodes needed:
XML Encoding
1 2 3 4 5 6 7 |
<Transform DEF='Ball'> <!-- children ... --> </Transform> <TimeSensor DEF='Clock' ... /> <Script DEF='Bouncer ... /> |
Classic Encoding
1 2 3 4 5 6 7 8 9 10 11 |
DEF Ball Transform { children [ ... ] } DEF Clock TimeSensor { ... } DEF Bouncer Script { ... } |
Script fields needed:
- Bounce height
XML Encoding
1 2 3 4 |
<Script DEF='Bouncer'> <field accessType='initializeOnly' type='SFFloat' name='bounceHeight' value='3'/> <!-- ... --> </Script> |
Classic Encoding
1 2 3 4 |
DEF Bouncer Script { initializeOnly SFFloat bounceHeight 3.0 ... } |
Inputs and outputs needed:
- Fractional time input
- Position value output
- Enabled input/output
XML Encoding
1 2 3 4 5 6 7 |
<Script DEF='Bouncer'> <!-- ... --> <field accessType='inputOnly' type='SFFloat' name='set_fraction'/> <field accessType='outputOnly' type='SFVec3f' name='value_changed'/> <field accessType='inputOutput' type='SFBool' name='enabled' value='true'/> <!-- ... --> </Script> |
Classic Encoding
1 2 3 4 5 6 7 |
DEF Bouncer Script { ... inputOnly SFFloat set_fraction outputOnly SFVec3f value_changed inputOutput SFBool enabled TRUE ... } |
Initialization and shutdown actions needed:
- None - all work done in input function
Event processing actions needed:
- set_fraction input function
- No need for eventsProcessed function
XML Encoding
1 2 3 4 5 6 7 8 9 |
<Script DEF='Bouncer'> <!-- ... --> <![CDATA[ecmascript: function set_fraction (fraction, time) { ... } ]]> </Script> |
Classic Encoding
1 2 3 4 5 6 7 8 9 |
DEF Bouncer Script { ... url "ecmascript: function set_fraction (fraction, time) { ... } " } |
Calculations needed:
- Compute new ball position
- Send new position event
Use a ball position equation roughly based upon Physics:
- See comments in the X3D file for the derivation of the equation
XML Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<Script DEF='Bouncer'> <field accessType='initializeOnly' type='SFFloat' name='bounceHeight' value='3'/> <field accessType='inputOnly' type='SFFloat' name='set_fraction'/> <field accessType='outputOnly' type='SFVec3f' name='value_changed'/> <field accessType='inputOutput' type='SFBool' name='enabled' value='true'/> <![CDATA[ecmascript: function set_fraction (fraction, time) { if (!enabled) return; var y = 4.0 * bounceHeight * fraction * (1.0 - fraction); value_changed [0] = 0.0; value_changed [1] = y; value_changed [2] = 0.0; } ]]> </Script> |
Classic Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
DEF Bouncer Script { initializeOnly SFFloat bounceHeight 3 inputOnly SFFloat set_fraction outputOnly SFVec3f value_changed inputOutput SFBool enabled TRUE url "ecmascript: function set_fraction (fraction, time) { if (!enabled) return; var y = 4.0 * bounceHeight * fraction * (1.0 - fraction); value_changed [0] = 0.0; value_changed [1] = y; value_changed [2] = 0.0; } " } |
Routes needed:
- Clock into script's set_fraction
- Script's value_changed into transform
XML Encoding
1 2 |
<ROUTE fromNode='Clock' fromField='fraction_changed' toNode='Bouncer' toField='set_fraction'/> <ROUTE fromNode='Bouncer' fromField='value_changed' toNode='Ball' toField='set_translation'/> |
Classic Encoding
1 2 |
ROUTE Clock.fraction_changed TO Bouncer.set_fraction ROUTE Bouncer.value_changed TO Ball.set_translation |
XML Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.3//EN" "http://www.web3d.org/specifications/x3d-3.3.dtd"> <X3D profile='Full' version='3.3' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='http://www.web3d.org/specifications/x3d-3.3.xsd'> <Scene> <Transform DEF='Ball'> <Shape> <Appearance> <Material ambientIntensity='0.5' diffuseColor='1 1 1' specularColor='0.7 0.7 0.7' shininess='0.4'/> <ImageTexture url='"beach.jpg"'/> <TextureTransform scale='2 1'/> </Appearance> <Sphere/> </Shape> </Transform> <TimeSensor DEF='Clock' cycleInterval='2' loop='true' startTime='1'/> <Script DEF='Bouncer'> <field accessType='initializeOnly' type='SFFloat' name='bounceHeight' value='3'/> <field accessType='inputOnly' type='SFFloat' name='set_fraction'/> <field accessType='outputOnly' type='SFVec3f' name='value_changed'/> <field accessType='inputOutput' type='SFBool' name='enabled' value='true'/> <![CDATA[ecmascript: function set_fraction (fraction, time) { if (!enabled) return; var y = 4.0 * bounceHeight * fraction * (1.0 - fraction); value_changed [0] = 0.0; value_changed [1] = y; value_changed [2] = 0.0; } ]]> </Script> <ROUTE fromNode='Clock' fromField='fraction_changed' toNode='Bouncer' toField='set_fraction'/> <ROUTE fromNode='Bouncer' fromField='value_changed' toNode='Ball' toField='set_translation'/> </Scene> </X3D> |
Classic Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#X3D V3.3 utf8 Titania V4.3.0 DEF Ball Transform { children [ Shape { appearance Appearance { material Material { ambientIntensity 0.5 diffuseColor 1.0 1.0 1.0 specularColor 0.7 0.7 0.7 shininess 0.4 } texture ImageTexture { url "beach.jpg" } textureTransform TextureTransform { scale 2.0 1.0 } } geometry Sphere { } } ] } DEF Clock TimeSensor { cycleInterval 2.0 startTime 1.0 stopTime 0.0 loop TRUE } DEF Bouncer Script { initializeOnly SFFloat bounceHeight 3.0 inputOnly SFFloat set_fraction outputOnly SFVec3f value_changed inputOutput SFBool enabled TRUE url "ecmascript: function set_fraction (fraction, time) { if (!enabled) return; var y = 4.0 * bounceHeight * fraction * (1.0 - fraction); value_changed [0] = 0.0; value_changed [1] = y; value_changed [2] = 0.0; } " } ROUTE Clock.fraction_changed TO Bouncer.set_fraction ROUTE Bouncer.value_changed TO Ball.set_translation |
Building a toggle switch
A toggle script turns on at 1st touch, off at 2nd:
- A TouchSensor node can supply touch events
XML Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<Script DEF='Toggle'> <field accessType='inputOutput' type='SFBool' name='on' value='true'/> <field accessType='inputOnly' type='SFBool' name='set_active'/> <field accessType='outputOnly' type='SFBool' name='on_changed'/> <![CDATA[ecmascript: function set_active (value, time) { if (value == false) return; if (on == TRUE) on = FALSE; else on = TRUE; on_changed = on; } ]]> </Script> |
Classic Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
DEF Toggle Script { inputOutput SFBool on TRUE inputOnly SFBool set_active outputOnly SFBool on_changed url "ecmascript: function set_active (value, time) { if (value == false) return; if (on == TRUE) on = FALSE; else on = TRUE; on_changed = on; } " } |
Using a toggle switch
Use the toggle switch to make a lamp turn on and off
XML Encoding
1 2 3 4 5 6 7 |
<TouchSensor DEF='LightSwitch'/> <SpotLight DEF='LampLight' ... /> <Script DEF='Toggle' ... /> <ROUTE fromNode='LightSwitch' fromField='isActive' toNode='Toggle' toField='set_active'/> <ROUTE fromNode='Toggle' fromField='on_changed' toNode='LampLight' toField='set_on'/> |
Classic Encoding
1 2 3 4 5 6 7 |
DEF LightSwitch TouchSensor { } DEF LampLight SpotLight { ... } DEF Toggle Script { ... } ROUTE LightSwitch.isActive TO Toggle.set_active ROUTE Toggle.on_changed TO LampLight.set_on |
Building a color selector
The lamp on and off, but the light bulb doesn't change color!
A color selector script sends an on color on a TRUE input, and an off color on a FALSE input.
XML Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<Script DEF='ColorSelector'> <field accessType='initializeOnly' type='SFColor' name='onColor' value='1 1 1'/> <field accessType='initializeOnly' type='SFColor' name='offColor'/> <field accessType='inputOnly' type='SFBool' name='set_selection'/> <field accessType='outputOnly' type='SFColor' name='color_changed'/> <![CDATA[ecmascript: function set_selection (value, time) { if (value == true) color_changed = onColor; else color_changed = offColor; } ]]> </Script> |
Classic Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
DEF ColorSelector Script { initializeOnly SFColor onColor 1.0 1.0 1.0 initializeOnly SFColor offColor 0.0 0.0 0.0 inputOnly SFBool set_selection outputOnly SFColor color_changed url "ecmascript: function set_selection (value, time) { if (value == true) color_changed = onColor; else color_changed = offColor; } " } |
Using a color selector
XML Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<TouchSensor DEF='LightSwitch'/> <SpotLight DEF='LampLight' ... /> <Material DEF='BulbMaterial' ... /> <Script DEF='Toggle' ... /> <Script DEF='ColorSelector' ... /> <ROUTE fromNode='LightSwitch' fromField='isActive' toNode='Toggle' toField='set_active'/> <ROUTE fromNode='Toggle' fromField='on_changed' toNode='LampLight' toField='set_on'/> <ROUTE fromNode='Toggle' fromField='on_changed' toNode='ColorSelector' toField='set_selection'/> <ROUTE fromNode='ColorSelector' fromField='color_changed' toNode='BulbMaterial' toField='set_emissiveColor'/> |
Classic Encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DEF LightSwitch TouchSensor { } DEF LampLight SpotLight { ... } DEF BulbMaterial Material { ... } DEF Toggle Script { ... } DEF ColorSelector Script { ... } ROUTE LightSwitch.isActive TO Toggle.set_active ROUTE Toggle.on_changed TO LampLight.set_on ROUTE Toggle.on_changed TO ColorSelector.set_selection ROUTE ColorSelector.color_changed TO BulbMaterial.set_emissiveColor |