home   previous   next  contents

Shout3D™ 2.5 - User Guide


Using and Creating Custom Nodes

Understanding Custom Nodes

Nodes are the elements of the 3D scene graphs, such as Transforms, Materials, Lights and IndexedFaceSets. They are readily identifiable in a .wrl or .s3d scene file because their initial letter is capitalized and their fields are listed within curly braces. The fields of a node parameterize that node. For example, the specific values in the translation field of a Transform node determine the position of objects subject to that Transform.

When a scene file is loaded into a Shout3D viewer, Java class objects are created for each node in the scene. In the Shout3D Java class hierarchy, all such classes are derived from the Node class.

Just as you can extend the Shout3DApplet and Shout3DPanel classes to create projects with custom interactivity, you can also create new classes by extending existing Shout3D Nodes. (In this context, the term Shout3D Nodes means both the Node class and any of its subclasses.) For example, you can create cameras with custom features by extending the Viewpoint node. Creating custom nodes allows you to expand your Shout3D class library, and you can use the new nodes whenever you need them.

Custom nodes are easy to use. Once created, you can add them to a scene file by simply typing them in. They will be instantiated when the scene is loaded into the Shout3D viewer, just like the standard nodes.

IMPORTANT NOTE: The discussion of custom nodes here is high-level and assumes a working knowledge of object-oriented programming in the Java language. For a more complete discussion of this subject, with extensive project tutorials, refer to the Shout Book.

PostRenderEffects

Your first exposure to custom nodes is most likely to occur in the context of PostRenderEffects. The Shout3D Class library contains a Node class named PostRenderEffect which enables you to perform 2D post-processing on the rendered image after each render. This is an extremely powerful tool that can be used for antialiasing, color filter effects, and drawing text and 2D graphics on top of the 3D image.

The PostRenderEffect node is an abstract class that provides no functionality and cannot be used directly. It is a base class that is used to create subclasses by overriding its existing methods and adding others. The Panorama and StillCamProgAntialias nodes in the standard Shout3D class library are derived from PostRenderEffect. But you will commonly need to create PostRenderEffect custom nodes to meet the needs of specific projects. Most of the nodes provided in the custom_nodes directory of your Shout3D installation are derived from PostRenderEffect. You can learn a great deal by consulting the source code for these classes.

A Simple Custom Node

Let's look at a very simple custom node to get the idea.

Let's say we need a PostRenderEffect that will display the number of times that the user has clicked the mouse. The following class definition will do the job.


package custom_nodes;

import shout3d.core.*;
import shout3d.*;
import java.awt.*;


public class CountDisplay extends PostRenderEffect implements DeviceObserver {

   boolean initialized = false;  
   int count = 0; 
   Font labelFont =  new Font("SansSerif", Font.BOLD, 16);
      
   //constructor   
   public CountDisplay (){
   }

   //to handle mouse input
   public boolean onDeviceInput(DeviceInput di, Object userData){
      MouseInput mi = (MouseInput)di;
      switch (mi.which){
      
         case MouseInput.DOWN:
            count++;
            return true;
            
      }
      return false;
   }

   //called after each render;
   //overriden from super class
   public void filter(Graphics g, int surface_pixel_bits[], float z_buffer[], int deviceWidth, int deviceHeight){
      
	//register to receive mouse events
      if (!initialized) {
         getViewer().getDeviceListener().addDeviceObserver(this, "MouseInput", null);
         initialized = true;
      }
      
      g.setFont(labelFont);  
      g.setColor(java.awt.Color.red);
      String countString = ""+count;
      g.drawString(countString, 30, 30);
   }
   
}

The filter() method, which is inherited from the PostRenderEffect class, is called after each render. It is passed a Java AWT Graphics object, which can be used to perform 2D drawing functions. Note how the DeviceObserver interface is implemented here to process mouse events. The node, as a DeviceObserver, must be registered to receive mouse events through its associated viewer.

Once the CountDisplay source code is compiled, the .class file is placed in the custom_nodes directory. To add a CountDisplay to any scene, you simply type the node into the scene file as:

CountDisplay {}

To see the CountDisplay custom node in action, look at the counterTest demo. Open counterTest.s3d from the models\postRenderEffectsTest directory in a text editor to see the CountDisplay node added to the scene.

Examples of Custom Nodes

Your Shout3D installation comes with a number of custom nodes that will give you many ideas about how custom nodes can be used and how to create them.

A Parametric Pyramid

Paramatric geometric objects enable you create simple, regular geometric forms "on the fly" by setting parameters. For example, a sphere can be defined entirely by its radius. The standard Shout3D class library provides nodes for a number of parametric geometric objects, including Sphere, Box, Cone and Cylinder nodes. All of these are created by extension of the IndexedFaceSet node – a basic polygonal mesh description from VRML.

You'll find a similarly designed parametric Pyramid offered as a custom node, complete with source code, in the custom_nodes directory. When you look at the source code, you'll notice that the geometry is created by scaling a normalized version of the mesh in the width, height and depth dimensions. Any change in either of these dimensions causes the geometry to update. This node makes use of the FieldObserver interface. When any of the registered fields (width, height or depth) is changed, the onFieldChange() method is called – here triggering a recalculation of the vertices.

Check out two demos implementing this node. onePyramid.html simply displays the following scene graph in an ExamineApplet. The Pyramid node is used in the geometry field of a Shape node, as is highlighted here in boldface.


DEF PYRAMID Transform {
   rotation 0 1 0 .79
   children Shape {
      appearance Appearance {
	   material Material {
             diffuseColor .6 .6 1 
         }
      }
      geometry Pyramid {
         width 4
         height 2
         depth 1
      }
   }
 } 

pyramidClickStretch.html is more sophisticated, using a custom applet and panel class pair. The panel class instantiates three Pyramid objects directly, without a scene file, and uses horizontal and vertical mouse drags to interactively modify the height, width and depth dimensions of the selected object. Consult PyramidTestPanel.java in the applets directory.

A Dynamic Texture

The GradientTexture custom node extends the PixelBasedTexture class to produce a procedurally generated gradient bitmap. You determine the pixel dimensions of the images and any number of colors. The colors are interpolated from top to bottom. The resultant bitmap can be used wherever a Texture node is used.

For example, you can create a gradient background for a scene file as follows:

Background {
   texture  DEF GRADIENT GradientTexture {
      width 300
      height 300
      gradientColors [ 1 1 0, 1 0 0, 0 0 1 ]
   }
}

The background will appear like this:

Review the source code for the GradientTexture custom node in the custom_nodes directory to see how pixel values are computed and set. Note how, as with the Pyramid custom node, the FieldObserver interface is implemented. This allows the gradient to be recomputed dynamically, as color values are changed at runtime. Check out the fourGradientTextures demo to see an animated gradient used as texture map. The color values on the box are modulated by a CoordinateInterpolator node.

A Color Filter Effect

We've previously looked at using a PostRenderEffect custom node to perform 2D drawing on top of the 3D scene. But a PostRenderEffect node is passed the array of all the rendered pixels, allowing you to write procedures for altering these colors. The InvertSceneEffect custom node in your custom_nodes directory demonstrates an effect typical of a Photoshop filter. If you look at the source code in InvertSceneEffect.java, you'll see how the color values of each pixel are inverted. Take a look a the invertSceneEffect.html demo to see the effect.

Node Search Paths

The standard practice is to compile custom nodes in the custom_nodes package, and to place the compiled classes in the codebase\custom_nodes directory. That way, the class file is certain to be found in the scene loading process.

However, as you develop a rich library of custom nodes you may wish to organize them more conveniently. For example, as many custom nodes are uniquely associated with a specific scene, you may wish to store these nodes in the same directory as the model file. To do this, you must:

The pyramidInSpecialDir demo provides an example. It uses the same Pyramid custom node discussed previously in this section of the User Guide, but renamed as Pyramid2. To be able to store this Pyramid2 node in the same directory as the associated scene file - models\nodeSearchPathTests\onePyramid2.s3d - the node must be compiled with the following package statement:

package models.nodeSearchPathTests;

The <APPLET> tag in the HTML file has a nodeSearchPath parameter. If it is not listed, it uses a default path that looks in the shout3d.core, shout3d.sound and custom nodes directories for node class files. To make sure that other directories are included in the search as well, they can be added after using the DEFAULT keyword (without spaces).

<param name="nodeSearchPath" value="DEFAULT;models.nodeSearchPathTests">

Review the following files (under the Shout3D_runtime directory) in a text editor to understand the entire process:


Copyright© 1999-2001, Shout3D LLC.