VRMLを使ってみよう(オブジェクトの外部からの読み込み→PROTOによる定義)

前回、VRMLを使ってみよう(座標系本番verの作成、オブジェクトの外部ファイル読み込み) - erythritolは甘味料のとおりInlineを使って外部ファイルからのオブジェクトデータの読み込みを行った。
これにより、オブジェクト形状を定義するファイルと、オブジェクトの動作を定義するファイルとを分けて作ることが可能になった。
しかしこの方法では、オブジェクト定義のVRML内で定義されたDEF文は使えない。
そうなると、動作中にある特定のオブジェクトのみ色を変える、というようなことはできなくなってしまう。

PROTO文・EXTERNPROTO文の利用

Inlineが、外部のVRMLファイルで定義されたVRML空間自体を読み込むのに対し、PROTO文は独自のノードを作成することができる。
このノードに対する独自のフィールド(=パラメータ)も定義できるので、たとえば
形は一定だが色は変化しうる物体
を作るなら、色を定義するフィールドを作成しておき、ノードの形状を定義する部分ではIS文を利用してこのフィールドを引用しておけば、フィールドの値をいじることでその都度違う色で同じ形状の物体を作ることができるのでは、と考えた。

そもそも、フィールドの種類について

フィールドにはfield, eventIn, eventOut, exposedFieldというのがある。

  • field : 固定値(初期値を与えられる)
  • eventIn, eventOut : イベントで受け渡されるパラメータ
  • exposedField : field, eventIn, eventOut全ての性質を持つ = 初期値を与えられるうえ、イベントでの受け渡しも可能

つまり、ROUTEなどを使って値を変化させるときに

  • eventIn, exposedField : set_* やフィールド名で値を与えることができる
  • eventOut, exposedField : *_changed, is* やフィールド名で値が発生する
PROTO・EXTERNPROTOの実践

昨日と同じ例題を、以下のように書き換えてみた。

PROTOの方

#VRML V2.0 utf8
#右腕PROTO  filename=RUpperArm.wrl

PROTO RUpperArm
   [ exposedField SFVec3f objscale 1 1 1 ]
{
# PROTOここから

Transform {
   children [

      # ローカル座標原点
      # 回転・移動はしないがオブジェクトのサイズに合わせて適度にリスケール

      Transform {
         scale 0.1 0.1 0.1
         children [
            Inline {
               url ["localcoord.wrl"]
            }
         ]
      }

      # ZX平面で押し出した図形を、あたかもXY平面で押し出したかのように変換
      Transform {
         rotation 0 1 0 1.57
         children [
            Transform {
               rotation 0 0 1 1.57
               scale IS objscale
               children[
                  # ここからが本番
                  Shape {
                     appearance Appearance {
                        material Material {
                           diffuseColor 0.8 0.8 0.5
                        }
                     }
                     geometry Extrusion {
                        crossSection [0 0.7, 0.7 0.5, 1 0, 0.7 -0.3, 
                                      0 -0.5, -0.7 -0.3, -1 0, -0.7 0.5, 
                                      0 0.7 ]
                        spine [0 -1 0, 0 0 0]
                        scale [0.2 0.2  0.3 0.3]
                        solid TRUE
                        beginCap TRUE
                        endCap TRUE
                     }
                  }
               ]
            }
         ]
      }
   ]
}

# PROTOここまで

}

EXTERNPROTOの方

#VRML V2.0 utf8
#Inlineの練習  filename=testexternprot.wrl

EXTERNPROTO RUpperArm 
   [ exposedField SFVec3f objscale ]
   [ "RUpperArm.wrl" ]

DEF WORLD Transform {
   # 世界座標系の位置向き x軸が奥行き、y軸が横、z軸が高さになるように変換
   # ここは固定にしておく
   translation 0 0 0
   rotation -1 -1 -1 2.09
   children [
      Inline {
         url ["localcoord.wrl"]
      }
      Transform {
         # 世界座標原点との位置を変えるために今だけ移動、後で消すぜ
         translation 0 1 1.5
         children[
            Transform {
               children[
                  Transform {
                     children[
                        RUpperArm { objscale 0.1 0.1 0.1 }
                     ]
                  }
               ]
            }
         ]
      }
   ]
}

これで、無事に呼び出されて表示されることを確認した。
上記のサンプルだと、3次元座標のサイズは変えずに立体物のサイズだけ変えることが可能。

ちなみに、日本語でコメント文を書いている部分が悪さをしているのか、上手く動かないことがあった。
解決法はコメント文の後ろに1行余白を与えたり色々といじっているうちに何故か動いたが…。
文頭にutf8と書いてあるのに実際にはSJISで保存しているのが原因かもしれない、と不安に思う。
ちなみに、Textノードで日本語を表示するにはutf8で保存する必要があるが、そうすると他では日本語が使えなくなる、という記述をhttp://www.ceres.dti.ne.jp/~kekenken/main/3d/script/04_2_sample.htm:Title=みかけた
でもUTF-8で保存したら開けなかったんだよなー、と複雑な気分。

色も変えられるようにしてみる

さっきはscaleのみ外部から与えたが、同様に色も変えてみる。
PROTO内で、exposedFieldとして色の変数(ここではshouldercolor)を定義しておくことにより、ROUTEではここの値を変えることができる(いつもはset_*と書いているところに、このフィールド名自体を書く)

サンプルは以下。

PROTO側

#VRML V2.0 utf8
#Right Upper Arm PROTO  filename=RUpperArm.wrl

PROTO RUpperArm
   [ exposedField SFColor shouldercolor 1.0 1.0 0.8
     exposedField SFVec3f objscale 1 1 1 ]
{
# PROTO ここから

Transform {
   children [

      # ローカル座標系

      Transform {
         scale 0.1 0.1 0.1
         children [
            Inline {
               url ["localcoord.wrl"]
            }
         ]
      }

      # ZX平面の押し出し -> XY平面の押し出しに変える

      Transform {
         rotation 0 1 0 1.57
         children [
            Transform {
               rotation 0 0 1 1.57
               scale IS objscale
               children[

                  # main shape

                  Shape {
                     appearance Appearance {
                        material Material {
                           diffuseColor IS shouldercolor
                        }
                     }
                     geometry Extrusion {
                        crossSection [0 0.7, 0.7 0.5, 1 0, 0.7 -0.3, 
                                      0 -0.5, -0.7 -0.3, -1 0, -0.7 0.5, 
                                      0 0.7 ]
                        spine [0 -1 0, 0 0 0]
                        scale [0.2 0.2  0.3 0.3]
                        solid TRUE
                        beginCap TRUE
                        endCap TRUE
                     }
                  }
               ]
            }
         ]
      }
   ]
}

# PROTO ここまで

}

EXTERNPROTO側

#VRML V2.0 utf8
#Inlineの練習  filename=testexternproto2.wrl

EXTERNPROTO RUpperArm 
   [ exposedField SFColor shouldercolor
     exposedField SFVec3f objscale ]
   [ "RUpperArm.wrl" ]

DEF WORLD Transform {

   # Zが縦になるように座標変換
   # ここは固定で必ずつける
   translation 0 0 0
   rotation -1 -1 -1 2.09
   # 固定でつけるところここまで

   children [
      Inline {
         url ["localcoord.wrl"]
      }
      Transform {

         # 世界座標系との差分 
         translation 0 1 1.5

         children[
            Transform {
               children[
                  Transform {
                     children[
                        DEF Rua RUpperArm {
                           objscale 0.5 0.5 0.5 
                        }
                     ]
                  }
               ]
            }
         ]
      }
   ]
}

DEF TS TimeSensor {
   cycleInterval 1
   loop TRUE
}

DEF CI ColorInterpolator {
   key [0, 1]
   keyValue [ 0.8 0.8 0.5, 1 0 0 ]
}

ROUTE TS.fraction_changed TO CI.set_fraction
ROUTE CI.value_changed TO Rua.shouldercolor

これで、着色も自由につけられる!という確信が持てた。