VRML Generation Message

[Slightly formatted message seen on sony-cp / DKS .. thanx Justin !]
From: Justin Couch 
Subject: Re: [sony-cp] Use of JAVA-API
Time to do a bit of self promotion :) I have a set of classes called JVerge that replicate the VRML 2.0 nodes as individual java classes. The current version does not work with community place, but I expect to be releasing an update within the next 24hrs that will. The URL is http://www.vlc.com.au/JVerge/. If you want to dig into the VRML code yourself then attached is a little tutorial that I posted not more than an hour ago to the VRML development list. It is a tutorial on using the createVrmlFromX calls within a script. It does not teach you how to write the Java scripts, just how to use these two methods.
-------------------------------------------------------------------
Justin Couch                            The Virtual Light Company
justin@vlc.com.au                  http://www.vlc.com.au/~justin/
-------------------------------------------------------------------
"Look through the lens, and the light breaks down into many lights.
 Turn it or move it, and a new set of arrangements appears... is it
 a single light or many lights, lights that one must know how to
 distinguish, recognise and appreciate? Is it one light with many
 frames or one frame for many lights?"      -Suocomandante Marcos 
-------------------------------------------------------------------

A simple CreateVrmlFromX Lesson:

CreateVrmlFromString.
--------------------

Used to create a piece of VRML on the fly without referencing any
external source.

To use simply create a string of the VRML that you want to add. For
example we want to have a green box. The normal VRML file format for
this is

Shape {
  appearance Appearance {
    material Material {
      emissiveColor 0 1 0
    }
  }
  geometry Box {}
}

So now you place this into a Java string:

String green_box = "Shape { " +
                     "appearance Appearance { " + 
                        "material Material { " +
                           "emissiveColor 0 1 0 " +
                        "} " +
                     "} " +
                     "geometry Box {} " +
                   "}";

Then you need to turn this from string to VRML. Do this using the
createVrmlFromString method of the Browser class. This method returns an
array of BaseNodes (if using scripts). For a script you can then write

Browser b = getBrowser();   // this is part of the Script class

BaseNode[] vrml_box = b.createVrmlFromString(green_box);

This has now created a representation of the green box in java. But it
has not yet been added to the scene, so you can't see it. Now you must
add it to the scene. The only way that you can do this is to have a
reference to a grouping node passed into the script. This means we need
to have it included in the VRML script definition. For example:

#VRML V2.0 utf8

# This is the group that you will be adding the box to.
DEF a_group Group {}

DEF my_script Script {
  url "myclass.class"
  field SFNode parentNode USE a_group
}

Now you need to write the script class. The Script handles everything.
So:

// a script class

import vrml.*;
import vrml.field.*;
import vrml.node.*;

public class myclass
{
    private SFNode the_parent;
    private Browser b;

    public void initialize()
    {
        // get hold of the node reference
        the_parent = (SFNode)getField("parentNode");
        b = getBrowser();
    }

    public void processEvent(Event e)
    {
       // now in response to some input we want to create the new box:
       createNode();
    }

    private void createNode()
    {
        String green_box = "Shape { " +
                              "appearance Appearance { " + 
                                "material Material { " +
                                   "emissiveColor 0 1 0 " +
                                "} " +
                              "} " +
                              "geometry Box {} " +
                           "}";

        BaseNode[] vrml_box = b.createVrmlFromString(green_box);

        // Now we need to add it to the existing scene.
        // first need to get the eventIn of the Group
        MFNode add_kids = (MFNode)the_parent.getEventIn("addChildren");

        // now we can add this to the Node directly
        add_kids.setValue(vrml_box);
    }
}

Done. That is it. All you need to do is fill in the blanks with
something to trigger the add in the first place.

CreateVrmlFromURL
-----------------

Used to dynamically add external files to the current scene without the
limitations of the Inline node.

The URL system is very similar to that used to create VRML content from
a string. THe major difference is that instead of returning a node and
adding that to the scene you actually get the information back through
an event. That event must be an MFNode eventIn.

Lets look at our problem again. The box is now moved out to a separate
VRML file (called box.wrl).

#VRML V2.0 utf8
Shape {
  appearance Appearance {
    material Material {
      emissiveColor 0 1 0
    }
  }
  geometry Box {}
}


CreateVrmlFromURL takes three arguments: The first is an array of
strings. This array is a list of URLs that can be retrieved. Like the
rest of the nodes that use URL strings, this refers to the urls in
decreasing order of preference.

The second argument is a BaseNode (Node for EAI) reference. This must be
a pre-existing, legal VRML node reference. This node could be anything,
including a node previously created with a createVrmlFromString call,
that has not been added to the scene yet. You can use anything, so long
as it is not null. Normally this is either the script itself (refered to
using a "this" reference) or a Grouping node.

The third argument is a string. This is the name of the particular
eventIn (or exposedField) that you want to send the new nodes to. If you
had a Grouping node you could pick either the addChildren,
removeChildren or children fields of the node as an example.

Lets look back at the example. Let's say that we will add the node
directly to the Group node that we have a reference to (the_parent). The
script code now becomes:

// a script class

import vrml.*;
import vrml.field.*;
import vrml.node.*;

public class myclass
{
    private SFNode the_parent;
    private Browser b;

    public void initialize()
    {
        // get hold of the node reference
        the_parent = (SFNode)getField("parentNode");
        b = getBrowser();
    }

    public void processEvent(Event e)
    {
       // now in response to some input we want to create the new box:
       createNode();
    }

    private void createNode()
    {
        b.createVrmlFromURL("box.wrl", the_parent, "addChildren");
    }
}


As you can see, there is a bit less code to do it this way.

An alternative is that we can send the information back to the script
itself for later addition to the scene. We might want to do some other
processing of it for example. To use the script instead of the node
directly, some extra changes are needed.

Firstly, the script now needs an MFNode eventIn:

DEF my_script Script {
  url "myclass.class"
  field SFNode parentNode USE a_group
  eventIn MFNode set_newChildren
}

Now we need to handle that in the script. This is an extra eventIn with
some values, which are the nodes to add to the system. Using this, we
can go back to the original script and use this as the event trigger.
ie:

// a script class

import vrml.*;
import vrml.field.*;
import vrml.node.*;

public class myclass
{
    private SFNode the_parent;
    private Browser b;

    public void initialize()
    {
        // get hold of the node reference
        the_parent = (SFNode)getField("parentNode");
        b = getBrowser();
    }

    public void processEvent(Event e)
    {
       if(e.getName().equals("set_newChildren")
           nodesReady((ConstMFNode)(e.getValue())
       // now in response to some otherinput we want to create
       // the new box:
       else
           createNode();
    }

    private void createNode()
    {
        b.createVrmlFromURL("box.wrl", the_parent, "addChildren");
    }

    private voud nodesReady(ConstMFNode nodes)
    {
        BaseNode[] node_list = new BaseNode[nodes.getSize()];

        nodes.getValue(node_list);

        // Now we need to add it to the existing scene.
        // first need to get the eventIn of the Group
        MFNode add_kids = (MFNode)the_parent.getEventIn("addChildren");

        // now we can add this to the Node directly
        add_kids.setValue(vrml_box);

    }
}

There is a lot of extra code needed to do it this way. Notice how we
need to first create an array of nodes before passing them to the
getValue method. getValue then fills the array with the list of nodes
which we can then pass on to the Group node a couple of lines later.


Well, that's it. All you need to know about the createVrmlFromX methods
and how to use them within script nodes. If I'm feeling energetic I'll
write one relating to use within the EAI at sometime in the not too
distant future.

As always, if you have questions don't hesitate to email me. I'm doing a
lot of travel recently so I might take a few days to respond to any
queries.

D.K.S.