Post

Creating New Node Types

Motivation

You can create new node types that encapsulate:

  • Shapes
  • Sensors
  • Interpolators
  • Scripts
  • anything else …

This creates high-level nodes:

  • Robots, menus, new shapes, etc.

Syntax: PROTO

A PROTO statement declares a new node type (a prototype):

  • name - the new node type name
  • fields, inputs, and outputs - interface to the prototype

XML Encoding

1
2
3
4
5
6
7
8
9
<ProtoDeclare name='BouncingBall'>
  <ProtoInterface>
    <field accessType='inputOutput' type='SFTime' name='cycleInterval' value='1'/>
    <field accessType='inputOutput' type='SFFloat' name='bounceHeight' value='1'/>
  </ProtoInterface>
  <ProtoBody>
    <!-- ... -->
  </ProtoBody>
</ProtoDeclare>

Classic Encoding

1
2
3
4
5
6
7
PROTO BouncingBall [
  inputOutput SFTime  cycleInterval 1.0
  inputOutput SFFloat bounceHeight  1.0
]
{
  ...
}

Defining prototype bodies

PROTO defines:

  • body - nodes and routes for the new node type

XML Encoding

1
2
3
4
5
6
7
8
9
10
<ProtoDeclare name='BouncingBall'>
  <ProtoInterface>
    <!-- ... -->
  </ProtoInterface>
  <ProtoBody>
    <Transform>
      <!-- children ... -->
    </Transform>
  </ProtoBody>
</ProtoDeclare>

Classic Encoding

1
2
3
4
5
6
7
8
9
PROTO BouncingBall [
  ...
]
{
  Transform {
    children [ ... ]
  }
  ROUTE ...
}

Syntax: IS

The IS syntax connects a prototype interface field, input, or output to the body:

  • Like an assignment statement
  • Assigns interface field or input to body
  • Assigns body outputs to interface

Interface items connected by IS need not have the same name as an item in the body, but often do:

XML Encoding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<ProtoDeclare name='BouncingBall'>
  <ProtoInterface>
    <field accessType='inputOutput' type='SFTime' name='cycleInterval' value='1'/>
    <field accessType='inputOutput' type='SFFloat' name='bounceHeight' value='1'/>
  </ProtoInterface>
  <ProtoBody>
    <!-- ... -->
    <TimeSensor DEF='Clock' ... >
      <IS>
        <connect nodeField='cycleInterval' protoField='cycleInterval'/>
        <!-- ... -->
      </IS>
    </TimeSensor>
    <!-- ... -->
  </ProtoBody>
</ProtoDeclare>

Classic Encoding

1
2
3
4
5
6
7
8
9
10
11
12
PROTO BouncingBall [
  inputOutput SFTime  cycleInterval 1.0
  inputOutput SFFloat bounceHeight  1.0
]
{
  ...
  DEF Clock TimeSensor {
    cycleInterval IS cycleInterval
    ...
  }
  ...
}

Using IS

May IS to …

InterfaceinitializeOnlyinputOnlyoutputOnlyinputOutput
initializeOnlyyesnonoyes
inputOnlynoyesnoyes
outputOnlynonoyesyes
inputOutputnononoyes

Using prototyped nodes

The new node type can be used like any other type.

XML Encoding

1
2
3
4
5
6
7
8
9
<!-- Official Syntax -->
<ProtoInstance name='BouncingBall'>
  <fieldValue name='cycleInterval' value='2'/>
  <fieldValue name='bounceHeight' value='3'/>
</ProtoInstance>
<!-- Short Syntax -->
<BouncingBall
    cycleInterval='2'
    bounceHeight='3'/>

Classic Encoding

1
2
3
4
BouncingBall {
  cycleInterval 2.0
  bounceHeight  3.0
}

Controlling usage rules

Recall that node use must be appropriate for the context:

  • A Shape node specifies shape, not color
  • A Material node specifies color, not shape
  • A Box node specifies geometry, not shape or color

The context for a new node type depends upon the first node in the PROTO body.

For example, if the first node is a geometry node:

  • The prototype creates a new geometry node type

The new node type can be used wherever the first node of the prototype body can be used.

  • In XML the default value of the »containerField« attribute of a ProtoInstance element is »children«. Change this attribute to whatever value you need.

XML Encoding

1
2
3
4
5
6
7
8
9
10
<Shape>
  <!-- Official Syntax -->
  <ProtoInstance name='Torus' containerField='geometry'>
    <!-- ... -->
  </ProtoInstance>
</Shape>
<Shape>
  <!-- Short Syntax -->
  <Torus containerField='geometry' .../>
</Shape>

A sample prototype use

Create a BouncingBall node type that:

  • Builds a beachball
    • Creates an animation clock
    • Using a PROTO field to select the cycle interval
  • Bounces the beachball
    • Using the bouncing ball program script
    • Using a PROTO field to select the bounce height

Fields needed:

  • Bounce height
  • Cycle interval

XML Encoding

1
2
3
4
5
6
7
8
9
<ProtoDeclare name='BouncingBall'>
  <ProtoInterface>
    <field accessType='inputOutput' type='SFTime' name='cycleInterval' value='1'/>
    <field accessType='inputOutput' type='SFFloat' name='bounceHeight' value='1'/>
  </ProtoInterface>
  <ProtoBody>
    <!-- ... -->
  </ProtoBody>
</ProtoDeclare>

Classic Encoding

1
2
3
4
5
6
7
PROTO BouncingBall [
  inputOutput SFTime  cycleInterval 1.0
  inputOutput SFFloat bounceHeight 1.0
]
{
  ...
}

Inputs and outputs needed:

  • None - a TimeSensor node is built in to the new node

Body needed:

  • A ball shape inside a transform
  • An animation clock
  • A bouncing ball program script
  • Routes connecting it all together

XML Encoding

1
2
3
4
5
6
7
8
9
10
11
12
13
<ProtoDeclare name='BouncingBall'>
  <ProtoInterface>
    <!-- ... -->
  </ProtoInterface>
  <ProtoBody>
    <Transform DEF='Ball'>
      <Shape><!-- ... --></Shape>
    </Transform>
    <TimeSensor DEF='Clock' ... />
    <Script DEF='Bouncer' ... />
    <ROUTE ... />
  </ProtoBody>
</ProtoDeclare>

Classic Encoding

1
2
3
4
5
6
7
8
9
10
11
12
13
PROTO BouncingBall [
  ...
]
{
  DEF Ball Transform {
    children [
      Shape { ... }
    ]
  }
  DEF Clock   TimeSensor { ... }
  DEF Bouncer Script { ... }
  ROUTE ...
}

Changing a prototype

If you change a prototype, all uses of that prototype change as well

  • Prototypes enable world modularity
  • Large worlds make heavy use of prototypes

For the BouncingBall prototype, adding a shadow to the prototype makes all balls have a shadow.

Syntax: EXTERNPROTO

Prototypes are typically in a separate external file, referenced by an EXTERNPROTO

  • name, fields, events - as from PROTO, minus initial values
  • url - the URL of the prototype file
  • #name - name of PROTO in file

XML Encoding

1
2
3
4
<ExternProtoDeclare name='BouncingBall' url='"bounce.x3dv#BouncingBall", "bounce.x3d#BouncingBall"'>
  <field accessType='inputOutput' type='SFTime' name='cycleInterval'/>
  <field accessType='inputOutput' type='SFFloat' name='bounceHeight'/>
</ExternProtoDeclare>

Classic Encoding

1
2
3
4
5
6
7
8
EXTERNPROTO BouncingBall [
  inputOutput SFTime  cycleInterval
  inputOutput SFFloat bounceHeight
]
[
  "bounce.x3dv#BouncingBall",
  "bounce.x3d#BouncingBall"
]

Summary

  • PROTO declares a new node type and defines its node body
  • EXTERNPROTO declares a new node type, specified by URL
This post is licensed under CC BY 4.0 by the author.