C# Script | A content node that can host a piece of C# source code in order to extend Ventuz with custom functionality. | |
VB Script | A content node that can host a piece of Visual Basic source code in order to extend Ventuz with custom functionality. |
Although some fairly complex scenes can be created using the existing Node functionality, there are certain effects that cannot be achieved by simply using Nodes and Bindings. For other cases, it is simply easier to add a small piece of code to the Ventuz scene that performs additional computations than implementing that functionality via nodes.
Each Script node can be given a number of input and output properties by the author via the Custom Model mechanism. Each of those properties is reflected in the script source code, thus providing a connection between the source code and the Ventuz scene.
Upon loading a scene, each script node has to compile its source code into executable operations. A large number of scripts in a Scene can increase the load time of that Scene quite substantially!
By double-clicking on a script node, the Script Editor is opened. The dock window on the left shows the input and output properties added to this node by the author of the script. By pressing the add button, new properties for float values, strings, methods and so on can be created. The dock window also contains a tab for showing the references to assemblies (i.e. DLLs) that can be used in the script.
Adding additional references is not supported yet. At this stage, the reference window is only for listing the assemblies available within the script code.
The main area of the Script Editor is of course assigned to the source code. The editor offers many comforts of professional IDEs like context highlighting and auto-completion. While changing the code, the Script Editor tries to detect syntax errors on the fly and will list them in the Error List at the bottom of the Script Editor.
The code can also be explicitly compiled by pressing the Compile button in the menu bar at the top of the Script Editor. In order to be able to execute a script, the code has to compile without errors.
The Script Editor usually does a fairly good job of detecting problems. However, there are errors that the syntax highlighter will not be able to detect but the compiler will. Do not assume that just because the syntax highlighter does not detect any errors there are none!
By pressing Save & Close, the changes will be saved, the code compiled and the editor closed.
When a script node is dragged to the Content Editor, a source code skeleton which contains all mandatory member functions is generated automatically. The following will discuss the C# script code only, the Visual Basic version being exactly the same except for the obvious language differences.
using System; using Ventuz.Kernel; public class Script : ScriptBase, System.IDisposable { // This member indicates whether the Generate() method has to return true or false. // If the Output values of this script change, Generate() has to return true to // trigger a validation of the Nodes that are bound to the Output values of this script. private bool changed; // This Method is called if the component is loaded/created public Script() { } // This Method is called if the component is unloaded/disposed public virtual void Dispose() { } // This Method is called if an input property has changed its value public override void Validate() { } // This Method is called every time before a frame is rendered public override bool Generate() { // return true, if output values have been changed if (changed) { changed = false; return true; } return false; } }
For the content of the following discussion, it is assumed that the reader is familiarly with the concepts of Bindings and Validation as well as with the C# programming language itself.
Any reader proficient in C# should be familiar with the concepts of construction and disposing. The constructor (i.e. Script()) is called when the node is created (usually as part of loading the scene) while Dispose() is called when the node is destroyed (usually as part of closing the scene). It is the authors responsibility to de-allocate unmanaged resources and close critical resources like file handles or net sockets in the Dispose method. For more information, please refer to the MSDN documentation on garbage collection in general and disposing in particular.
The Validate() and Generate() methods however directly mirror the way the standard Ventuz nodes are implemented internally. Since re-performing a nodes functionality every frame is too expensive for realtime applications (imagine a mesh node allocating its memory and recomputing vertex positions every frame), a node should perform as much of its computations only as a reaction to input properties changing. As long as the input properties keep the same values, there is little need to do any computation. One exception are nodes like the Mover node which react on time progressing, another one are nodes which react on external sources of information.
To support this "caching" mechanism, the scripts Validate() method is called whenever one of the input properties changes. This is the place to perform the majority of computation and update the output values or store the results in internal variables.
In contrast, the Generate() method is called every frame, therefore computation should be kept to a minimum. There is however one very important task the Generate() has to perform and that is to cause nodes bound to the scripts output properties to validate themselves. Assigning a value to a scripts output property will only change the value but not trigger the bound nodes Validate() method. Unless the Generate() method returns true, bound nodes will not be invalidated.
The values of output properties can be updated at any place in the script including the Validate() method. However, properties bound to those outputs are only notified of the change when the Generate() method returns true.
Obviously, the Generate() method should only return true if and only if the values output properties have really changed, for very much the same performance considerations as above. The script skeleton therefore has declared a Boolean called changed. If the Validate() method updates output properties, it simply should set changed to true and the default implementation of Generate() will do the right thing.
Input and Output Properties that have been added to the Script node via the Custom Model dialog are accessible from the script code by their (case-sensitive) names. If an input property Amount and an output property Sum has been added, the script code might look like this:
public override void Validate() { // Read value of input property float len1 = this.Amount; // Do some arbitrary logic or code (here we check if the // input variable is larger than 12) if ( len1 > 12.0f ) { // Assign a new value to the output. this.Sum = len1 * 10; // Set changed to true so Generate will return true during // it's next call. changed = true; } }
Adding an Input Method property to the script and then double-click on the entry will generate a method declaration with the name of the property with an added prefix On. If an Input Method Reset is added the corresponding method in the script code is called OnReset(). To trigger Events that have been added via Custom Model the name of the Event must be called from the script code. To trigger the Event Activate, the script code should look like this:
public override bool Generate() { //... some arbitrary code before the call Activate(); //... some arbitrary code after the call }
Note that Methods and Events can receive and send integer arguments so e.g. the Activate event code can look like this: Activate(12).
While it is possible to use external DLLs or dynamically load .net assemblies via C# language constructs (P/Invoke; .Net Reflection), the Script Editor is currently not really designed to support this. While a number of customers have successfully extended scripts to integrate external databases or devices, the whole process requires expert knowledge of the .net environment and is error-prone to say the least.
Extending scripting in Ventuz to this level of generality is planned for the future but at this time script nodes should be treated as containers for complex function logic rather than C#/VB backdoor to extend Ventuz.