About the X3D/VRML ROUTE statement
About the X3D/VRML ROUTE statement
I have heard items like:
" Tracking ROUTEs is tedious tasks and error-prone. Routes have as many detractors as fans, and more when experienced HTML Javascripters first pick up VRML, They may look at ROUTE and say,"What the heck is this?"
Imagine a complex highly structured hierarchy of many nodes, each with native eventOuts, eventIns, and even inputOutputs. Imagine these nodes with nothing to tell them who they can talk and listen to. Imagine being able to configure these collections of connections in a highly dynamic way that is essentially self-documenting. Fan out folks, and also Fan in...:) Give the Route a chance to breath. They are so obvious in their necessity and simplicity. And the Nodes really need 'em.
When the experienced regex or DOM script programmer looks at VRML/X3D and says: "What the heck are those ROUTE thingies" you don't need to be a 'fan' of the Route statement to add a listener and see the need to help them understand how the Node employs the Route.
True, it may be possible to create and deliver very complex X3D/VRML without using the Route statement, and in fact, that is the way 'they/us' probably learned to to program, But, it may not be the programmer-friendly or even browser-friendly way to create and deliver interactive Web 3D.
It is not uncommon that the first "Route-like' programming 'they/us' ever used was the equal sign and even then used that as an assignment operator, not as a Route. Then, onward and upward to building a real node and then actually adding a live field to that node and building an event listener for that field of that node so that if that field is activated there is a target node that has a live field that can be activated to process the event and maybe pass it on to another node. Now, to import/export raw characters and data structures between live distributed web applications. After mastering all this, including XML and ECMAScript/Java DOM and maybe ActionScript, they/us come to X3D expecting order sanity and evolution. And they/us find out that you gotta use ROUTE?
So, since this may be intense, it still more than likely will be better in the long-term to just go ahead and explain some facts about the Nodes and the Routes. Besides, you can leverage this learning experience because there are several other examples where X3D Nodes and X3D Statements get along together real nice.
As another besides, at the high end, please recall that the list of ROUTE statements of a particular SAI context of the X3D scene is intended to be a live list, like others possibly encountered, like maybe in the DOM. So, for X3D, at the beginning of a potential next event cascade to figure out what needs to be done to produce the next frame, this list constitutes an instantaneous map of possible event flow needed to produce that next frame. Of course the list may be changed by events of the current cascade. This is the intent in the conformant X3D browser and should be a tool to be employed by the efficient scene content developer. (Please see Add/Delete Route SAI interfaces.)
Some detailed facts about the Route and its relationship to the Node.
We can generally discuss a Node with field content that may be a Node, and how one node's eventOut may get connected with another node's eventIn for the purpose of information exchange using the X3D VRML Route statement.
In short, to connect an eventOut with an EventIn, you write an X3D statement like:
Route From DEFNode1.fieldName To DEFNode2.fieldName
The exact characters used for the Route depends upon the encoding, but that is it. The important items are the "From" where you define the named node's eventOut field of interest, and the "To" where you define the target named node's field. At runtime, if the "From" field produces an output, then the event data will be received at the "To" field.
So this works universally. Basic example is if you need a sensor to activate a timer, then you rig a sensor and a timer and whatever the timer is going to control, then you connect the sensor to the timer by a Route statement that says: From Sensor.isActive To Timer.enabled and the timer can run once or continuously while the sensor is active.
Node has fields, each with a declared data type and access type. Event is passed between nodes using Route statement which names the From Node1.eventOut and the To Node2.eventIn. Where necessary, supporting user code including event utilities and script is responsible for data type conversion to match the initial eventOut dataType to the target EventIn dataType.
In a vernacular: "The gozinta gotta typematch the gozinta."
Now back in the early middle/meta ages, since the ascent of 'scripting' to generate live code, a machine that generated real time interactive 3D using the relatively rich VRML structuring needed all the speed and convenience and memory management they could get. If there are a lot of nodes that have live fields and not all of them will be activated to produce every frame, then of course it is most simple to demand the programmer to predeclare paths of event flow, and even to predeclare paths of no event flow. In the former, this helps to optimize complex simulation because the machine can know more. Just because a Node happens to contain an eventOut, there is need to actually provide any service if there are no eventIns that care. In the latter, this may help the machine to tell you that no changes were expected for that group.
So OK, that is all very nice and self contained. The Route statement has performed the detail of defining sources and sinks for events in a very dynamic way and human-readable way.
Everything works fine when we can pass events of the same data type between different nodes. But if the data types are not the same, or need some processing, We first peruse the X3D event utilities and if we can't find something there, or the processing is too complicated, we can use a script.
The Script is essentially a Sensor node which can receive and send events. Like any other node, events are passed from a SensorNode.eventOut to a ScriptNode.eventIn using a Route. Likewise, as expected, events are passed from a ScriptNode.eventOut to AnotherNode.eventIn using a Route. In the X3D standard event system the eventOuts are sent in sequence at the end of Script execution.
This is just the way you want it. Known things are going to happen at known times. There is great opportunity for debug because the scene can literally document and simulate itself.
But wait. Using Routes, the script can only get access to fields of other nodes. What if I want to get complete access to another node and its fields directly. Without using Route?
Well, please first check your design and look for another, perhaps better integrated and structured solution that is certainly available.
However, there is always using the familiar DOM-style SAI getNamedNode('AnotherNode'); interface in the body of the script.
Or, you may use a Script directOut declaration. In this usage case, you can provide: inputOutput SFNode 'myNodeInTheScript' USE 'AnotherNode' in the script declaration to get a connection to AnotherNode. Now you may access the fields directly using: MyNodeInTheScript.fieldName in the body of the script.
In the second case, check your event timing because it may be possible that directOut event(s) may not be synchronized with the cascade that produced the current script eventIn. Although the X3D standard may be interpreted to predict standardized event cascade processing in a Script node and between other scripts and nodes involved in a cascade, this is potentially application-dependant just because it is hard to implement correctly.
So it seems to me that there is actually a preferred way:
Use Routes to define event flow between all nodes, even the Script nodes.
And, you may: Use SAI getNamedNode(); to work on nodes.
and/or Use directOut to manipulate the node.
or everything at once.
EXAMPLE 1: Accessing Script Fields
DEF SomeNode Transform { ... } Script DEF SomeScriptNode { inputOnly SFVec3f pos outputOnly SFVec3f someField url "ecmascript: function pos(value) { someField = value; } " } ROUTE SomeOtherNode.someSFVec3FField TO SomeScriptNode.pos ROUTE SomeScriptNode.someField TO SomeNode.trans
Example 1 receives a routed event from SomeOtherNode.someSFVec3FField then generates an event that is routed to the Transform node SomeNode.translation. The time stamp of the input and output events is the same, the output is ready when script execution completes, and the result is evaluated as part of the current cascade.
EXAMPLE 2: Accessing fields of other nodes
DEF SomeNode Transform { } Script DEF SomeScriptNode{ inputOnly SFVec3f pos initializeOnly SFNode node USE SomeNode directOutput TRUE url "ecmascript: function pos(value) { node.translation = value; } " } ROUTE SomeOtherNode.someSFVec3FField TO SomeScriptNode.pos
Example 2 receives a routed event from SomeOtherNode.someSFVec3FField then sends a set_translation event to the Transform node. Because directOutput is TRUE and the target node is included in the declaration by USE, the event is sent directly to SomeNode.translation through the direct action of the script rather than through a ROUTE. This event is processed separate from any existing or pending event cascade and shall not initiate a new cascade.
Since there are so many ways to play, you will probably have to make a choice in how to do the job. When in doubt always choose the simplest solution? How you do it may not make a difference from one browser to another, or it might. Ask your favorite browser maker if their product cares. I think most applications should be better performing and easier to maintain when directOuts are refactored to use Route and to eliminate many scripts by good use of X3D event utilities.
It should be reassuring that there are several ways to do it, if luck is happening, your choice will run everywhere!
Also look at some other X3D Statements like Import and Export.