- nested PROTOs
- ProtoCall inside ProtoCall
- ProtoCall inside PROTO definition
- PROTO definition inside ProtoCall
- PROTO definition inside PROTO definition
| further reading | more topics » |
| mjbWorld program | 3D theory |
3D physics |
3D maths |
3D programming | technology |
about site |
sitemap A-Z |
about |
white paper |
download program | user guide |
programmers guide | source |
bugs |
to do |
index |
architecture |
scenegraph |
user interface |
animation |
program structure |
data structure |
language specific |
| index | geometry | mjbModel | proto |
PROTOs are are a very efficient way to store information where there may be objects that are similar but only differ in a few respects. Examples of such objects might be, animals, plants, manufactured items, buildings, etc. These all tend to be repeated in the world with small differences. Rather than building each instance of these types the PROTO gives us a way to abstract out the common features.
The VRML standard explains that the PROTO statement defines a new node type in terms of already defined (built-in or prototyped) node types. Once defined, prototyped node types may be instantiated in the scene graph exactly like the built-in node types. For me though what is important is not just the ability to define new node types, but that this provides a very efficient and scalable way to store a big 3D model.
The PROTO concept is similar to a subroutine or a procedure call, but without any procedural code.
The PROTO definition object is defined only once, but where parameter values are different for different objects then the parameter value is - IS 'parameter name'.
The diagram below illustrates this, the tree structure on the left contains some small nodes. Each of these nodes only contains 4 parameters, so it is very small and easy to construct and understand. However, as far as the model is concerned, it is as if each of these PROTO calls were replaced by the PROTO definition on the right. This could be any arbitrary size. Most of its parameters will have fixed values, but where the value varies depending on the particular instance then the 'IS' keyword is used map the value, through the interface definition, back to the PROTO call.

Most renderers would work by replacing each PROTO instance with the full structure from the PROTO definition with the appreciate 'IS' values plugged in. However, since mjbWorld is an editor it needs to maintain the original structure. This also hat the advantage of not duplicating all the parameter values in the PROTO definition which are the same for each PROTO call. This does mean that when events are happening, that we need a more complex way of mapping parameter values for the PROTO. The following diagram illustrates how parameter values are mapped in and out of the PROTO when mjbWorld is running:

Most VRML readers would handle PROTOs by creating a complete new copy (instance) of the PROTO every time it occurs in the scenegraph. For instance, if the first node under the PROTO definition is a transformGroup, then when this scene graph is expanded out the tranformGroup under the PROTO definition is replicated everywhere in the scenegraph that the protoCall was (but with any IS values replaced by the values in the protoCall) as follows:
spParam
type:

So when mjbWorld encounters a PROTO and it needs to create a Java3D scenegraph to display in a 3D window, then the Java3D scenegraph will be expanded out as described above. However the internal representation in mjbWorld is not expanded out. This is because mjbWorld is an editor and needs to be able to read and create PROTOs and to save them as PROTOs. Also I think that the PROTO is a very efficient way to store scenegraph information.
Therefore mjbWorld needs to map its internal representation of the scenegraph to the Java3D scenegraph. This mapping of PROTO values is done in the program as follows:

If a parameter is read inside a PROTO and that parameter has IS value set, then the following sequence of code happens to return the appropriate value:
As an example of this, the above case shows render3d being called on a protoInstance, this results in render3d being called on all the nodes under the PROTO definition.
When get is called on a parameter with is[] non zero, then this links back to the original PROTOInstance as shown.
When transversing a scenegraph, say when rendering a scene, and we come to a PROTOInstance then we do not call the sbnodes on the PROTOInstance directly. Instead we call renderInstance3d on the PROTO. If there are any mfnodes or sfnodes with 'IS' values then we jump back to the PROTOInstance.

As an example of this, the above case shows render3d being called on a protoInstance.
When render3d is called on a parameter with is[] non zero, then this links back to the original PROTOInstance as shown.

MFNode or SFNode passes back control from PROTO definition to the node that called it

So when this is expanded out, each protoCall is replaced by a copy of the transformGroup under the PROTO definition, but the children of each instance of the transform group will be the children of the protoCall that called it.

For instance if I want lots of spherical objects of different size and colour I could define a PROTO as follows:
PROTO myBlob [field SFFloat.size 3.0 field SFColor.col 1 0 0] {
Shape {
appearance Appearance {
material Material {
diffuseColor IS col
}
}
geometry Sphere {
radius IS size
}
}
}
myBlob {size 8 col 1 0 1}
myBlob {size 10 col 0 1 1}
myBlob {size 12 col 1 1 0}
How do we implement this? In the case of a program which just displays the scene but does not edit. It is simple just expand out the PROTO calls as follows:
Shape {
appearance Appearance {
material Material {
diffuseColor 1 0 1
}
}
geometry Sphere {
radius 8
}
}
Shape {
appearance Appearance {
material Material {
diffuseColor 0 1 1
}
}
geometry Sphere {
radius 10
}
}
Shape {
appearance Appearance {
material Material {
diffuseColor 1 1 0
}
}
geometry Sphere {
radius 12
}
}
Since this program is an editor we cannot expand out the PROTO when loading the file. This is because we may need to edit the PROTO itself, and also when we save the file we want to keep the PROTO.
Therefore when we load the file we load the information into a set of beans (as described here). The PROTO definition is stored under a protoBean node and calls to the PROTO are stored as protoInstanceBean nodes.
Then then a 3D window is created as set of Java3D nodes has to be created. This is done by traversing the beans, and when a protoInstanceBean is encountered then a copy of the whole structure under the protoBean is created, with Java3D instances, replacing the IS keywords with the appropriate values.

|
metadata block
|
|
| see also: | |
| Correspondence about this page | |
|
Book Shop - Further reading. Where I can, I have put links to Amazon for books that are relevant to the subject, click on the appropriate country flag to get more details of the book or to buy it from them. |
|
|
Commercial Software Shop Where I can, I have put links to Amazon for commercial software, not directly related to this site, but related to the subject being discussed, click on the appropriate country flag to get more details of the software or to buy it from them. |
|
|
Can you help? Please send me any improvements to here. I would appreciate ideas to make the pages more useful including error correction, ideas for new pages, improvements to wording. It helps if you quote the full URL of the page. |
|
|
progam I am working on a project which uses these principles, if you would like to help me with this you are welcome to join in, here: |
|
This site may have errors. Don't use for critical systems.
Copyright (c) 1998-2008 Martin John Baker - All rights reserved.