通常,我們可以將一個場景裏的不同物體寫在不同的wrl文件中,最後在主場景wrl文件裏將各個物體通過“Inline”節點導入,形成一個完整場景。
這種方式對於用戶交互和動畫比較少並且形式單純的方式比較合適,但是在一些複雜情況下,用“Inline”節點是無法實現的。其原因在於“Inline”節點沒有用於交互的輸入輸出事件。
舉一個最簡單的例子。場景中有兩個物體A和B,A具有插值動畫,可以旋轉,B具有一個touchSensor(觸摸感應器)。
現在要求實現:
1.A物體的繪製和動畫代碼寫在a.wrl文件中,B物體的繪製和觸發器代碼寫在b.wrl文件中(這樣做的目的是爲了實現前面描述的好處。)
2.鼠標點擊物體B後,觸發A物體的動畫。
顯然,使用“Inline”節點是無法同時滿足上面兩個條件的。
但是兩個條件在VRML中並非無法滿足。
這個時候,我們需要使用PROTO節點和EXTERNPROTO進行原型聲明和外部引用。下面對代碼編寫進行詳細說明。
首先,將a.wrl和b.wrl的完整代碼貼上來:
a.wrl:
- #VRML V2.0 utf8
- #Cosmo Worlds V2.0
- PROTO KfaPositionInterpolator [
- eventIn SFFloat set_fraction
- eventOut SFVec3f value_changed
- exposedField MFFloat key 0
- exposedField MFInt32 keyTypes 2
- exposedField MFVec3f keyValue 0 0 0
- field MFFloat authorKey 0
- field MFVec3f authorKeyValue 0 0 0
- ]
- {
- PositionInterpolator {
- key IS key
- set_fraction IS set_fraction
- keyValue IS keyValue
- value_changed IS value_changed
- }
- }
- PROTO KfaAnimation [
- field SFFloat framesPerSecond 10
- field SFFloat zoom 1
- field SFBool snap TRUE
- field SFBool viewInFrames TRUE
- field SFBool showEmptyFieldLines FALSE
- field SFFloat playRangeStart 0
- field SFFloat playRangeEnd 1
- field SFBool usePlayRange TRUE
- field SFNode timeSensor NULL
- field MFNode fieldInterps []
- field MFNode actors []
- ]
- {
- Group {
- }
- }
- PROTO AnimationInA [
- eventIn SFTime TouchTime
- ]
- {
- DEF _0 Transform {
- children [
- Shape {
- appearance Appearance {
- material Material {
- }
- }
- geometry Box {
- }
- }
- DEF UnnamedAnimation0 KfaAnimation {
- framesPerSecond 25
- snap TRUE
- viewInFrames TRUE
- playRangeStart 0
- playRangeEnd 1
- timeSensor DEF UnnamedAnimation0Time TimeSensor {
- startTime IS TouchTime
- cycleInterval 4
- }
- actors USE _0
- fieldInterps [
- DEF UnnamedTransformScaleInterp KfaPositionInterpolator {
- key [ 0, 0.01, 0.02, 0.03,
- 0.04, 0.05, 0.06, 0.07,
- 0.08, 0.09, 0.1, 0.11,
- 0.12, 0.13, 0.14, 0.15,
- 0.16, 0.17, 0.18, 0.19,
- 0.2, 0.21, 0.22, 0.23,
- 0.24, 0.25, 0.26, 0.27,
- 0.28, 0.29, 0.3, 0.31,
- 0.32, 0.33, 0.34, 0.35,
- 0.36, 0.37, 0.38, 0.39,
- 0.4, 0.41, 0.42, 0.43,
- 0.44, 0.45, 0.46, 0.47,
- 0.48, 0.49, 0.5, 0.51,
- 0.52, 0.53, 0.54, 0.55,
- 0.56, 0.57, 0.58, 0.59,
- 0.6, 0.61, 0.62, 0.63,
- 0.64, 0.65, 0.66, 0.67,
- 0.68, 0.69, 0.7, 0.71,
- 0.72, 0.73, 0.74, 0.75,
- 0.76, 0.77, 0.779999, 0.789999,
- 0.799999, 0.809999, 0.819999, 0.829999,
- 0.839999, 0.849999, 0.859999, 0.869999,
- 0.879999, 0.889999, 0.899999, 0.909999,
- 0.919999, 0.929999, 0.939999, 0.949999,
- 0.959999, 0.969999, 0.979999, 0.989999,
- 1 ]
- keyTypes [ 2, 2, 2 ]
- authorKey [ 0, 0.25, 1 ]
- keyValue [ 1 1 1,
- 1.05987 1.05987 1.05987,
- 1.12379 1.12379 1.12379,
- 1.19122 1.19122 1.19122,
- 1.26161 1.26161 1.26161,
- 1.33439 1.33439 1.33439,
- 1.40902 1.40902 1.40902,
- 1.48493 1.48493 1.48493,
- 1.56159 1.56159 1.56159,
- 1.63843 1.63843 1.63843,
- 1.7149 1.7149 1.7149,
- 1.79045 1.79045 1.79045,
- 1.86452 1.86452 1.86452,
- 1.93657 1.93657 1.93657,
- 2.00603 2.00603 2.00603,
- 2.07235 2.07235 2.07235,
- 2.13498 2.13498 2.13498,
- 2.19338 2.19338 2.19338,
- 2.24697 2.24697 2.24697,
- 2.29521 2.29521 2.29521,
- 2.33756 2.33756 2.33756,
- 2.37344 2.37344 2.37344,
- 2.40231 2.40231 2.40231,
- 2.42362 2.42362 2.42362,
- 2.43681 2.43681 2.43681,
- 2.44133 2.44133 2.44133,
- 2.44082 2.44082 2.44082,
- 2.43931 2.43931 2.43931,
- 2.43681 2.43681 2.43681,
- 2.43335 2.43335 2.43335,
- 2.42895 2.42895 2.42895,
- 2.42362 2.42362 2.42362,
- 2.41739 2.41739 2.41739,
- 2.41028 2.41028 2.41028,
- 2.40231 2.40231 2.40231,
- 2.3935 2.3935 2.3935,
- 2.38387 2.38387 2.38387,
- 2.37344 2.37344 2.37344,
- 2.36223 2.36223 2.36223,
- 2.35026 2.35026 2.35026,
- 2.33756 2.33756 2.33756,
- 2.32413 2.32413 2.32413,
- 2.31001 2.31001 2.31001,
- 2.29521 2.29521 2.29521,
- 2.27976 2.27976 2.27976,
- 2.26367 2.26367 2.26367,
- 2.24697 2.24697 2.24697,
- 2.22967 2.22967 2.22967,
- 2.2118 2.2118 2.2118,
- 2.19338 2.19338 2.19338,
- 2.17442 2.17442 2.17442,
- 2.15495 2.15495 2.15495,
- 2.13499 2.13499 2.13499,
- 2.11455 2.11455 2.11455,
- 2.09367 2.09367 2.09367,
- 2.07235 2.07235 2.07235,
- 2.05063 2.05063 2.05063,
- 2.02851 2.02851 2.02851,
- 2.00603 2.00603 2.00603,
- 1.98319 1.98319 1.98319,
- 1.96003 1.96003 1.96003,
- 1.93657 1.93657 1.93657,
- 1.91281 1.91281 1.91281,
- 1.88879 1.88879 1.88879,
- 1.86452 1.86452 1.86452,
- 1.84003 1.84003 1.84003,
- 1.81533 1.81533 1.81533,
- 1.79045 1.79045 1.79045,
- 1.7654 1.7654 1.7654,
- 1.74021 1.74021 1.74021,
- 1.7149 1.7149 1.7149,
- 1.68949 1.68949 1.68949,
- 1.66399 1.66399 1.66399,
- 1.63843 1.63843 1.63843,
- 1.61283 1.61283 1.61283,
- 1.58721 1.58721 1.58721,
- 1.56159 1.56159 1.56159,
- 1.53599 1.53599 1.53599,
- 1.51043 1.51043 1.51043,
- 1.48493 1.48493 1.48493,
- 1.45952 1.45952 1.45952,
- 1.43421 1.43421 1.43421,
- 1.40902 1.40902 1.40902,
- 1.38397 1.38397 1.38397,
- 1.35909 1.35909 1.35909,
- 1.33439 1.33439 1.33439,
- 1.3099 1.3099 1.3099,
- 1.28563 1.28563 1.28563,
- 1.26161 1.26161 1.26161,
- 1.23785 1.23785 1.23785,
- 1.21439 1.21439 1.21439,
- 1.19123 1.19123 1.19123,
- 1.16839 1.16839 1.16839,
- 1.14591 1.14591 1.14591,
- 1.12379 1.12379 1.12379,
- 1.10207 1.10207 1.10207,
- 1.08075 1.08075 1.08075,
- 1.05987 1.05987 1.05987,
- 1.03943 1.03943 1.03943,
- 1.01947 1.01947 1.01947,
- 1 1 1 ]
- authorKeyValue [ 1 1 1,
- 2.44133 2.44133 2.44133,
- 1 1 1 ]
- }
- ]
- }
- ]
- translation 4 1 0
- rotation 0 1 0 3.14159
- scale 1 1 1
- }
- ROUTE UnnamedAnimation0Time.fraction_changed TO UnnamedTransformScaleInterp.set_fraction
- ROUTE UnnamedTransformScaleInterp.value_changed TO _0.set_scale
- }
b.wrl:
- #VRML V2.0 utf8
- #Cosmo Worlds V2.0
- Transform {
- children [
- DEF TS TouchSensor {}
- Shape {
- appearance Appearance {
- material Material {
- }
- }
- geometry Sphere {
- }
- }
- ]
- translation -3 1 0
- }
- EXTERNPROTO AnimationInB [
- eventIn SFTime TouchTime
- ]
- "a.wrl#AnimationInA"
- DEF animation AnimationInB {}
- ROUTE TS.touchTime TO animation.TouchTime
分析一下。代碼很簡單,a.wrl中,畫了一個正方體,並使用一個TimeSensor和PositionInterpolator實現插值動畫。b.wrl中,畫了一個球體,並在球題上安裝了一個touchSensor作爲鼠標點擊的感應器。
注意,在a.wrl文件中,我們將A物體的繪製和動畫代碼使用PROTO括起來,使它成爲一個原型(相當於一個自定義的節點。如果不知原型爲何物,請參閱任意關於VRML的教材)。
原型的域中,我們加入一個時間型入事件:
- eventIn SFTime TouchTime
接下來,在時間感應器TimeSensor中,我們這樣寫:
- timeSensor DEF UnnamedAnimation0Time TimeSensor {
- startTime IS TouchTime
- cycleInterval 4
- }
最後,注意動畫的路由:
- ROUTE UnnamedAnimation0Time.fraction_changed TO UnnamedTransformScaleInterp.set_fraction
- ROUTE UnnamedTransformScaleInterp.value_changed TO _0.set_scale
好了,再來分析b.wrl。
b.wrl在繪製完一個球體後,使用:
- EXTERNPROTO Animation [
- eventIn SFTime TouchTime
- ]
- "1.wrl#Animation"
然後,我們使用
- DEF animation Animation {}
最後,我們生成路由:
- ROUTE TS.touchTime TO animation.TouchTime
大功告成!
說了這麼多,代碼雖然簡單,但是也有不少(其實主要的代碼量來自於插值動畫的中間值,真正的功能代碼非常少),下面進行一下總結:
思路其實十分簡單。用戶點擊球體後,相當於觸發了球體上的touchSensor,產生一個時間型的事件,將此事件通過ROUTE TS.touchTime TO animation.TouchTime,傳遞給我們定義的原型AnimationInB,從而傳到了所引用的外部原型AnimationInA中(也就是說,傳到了a.wrl文件中),而由於AnimationInA的TimeSensor中有一個“startTime IS TouchTime”,所以這個時間型事件最終傳到了時間觸發器TimeSensor裏,觸發了動畫。
原型定義PROTO和外部原型引用EXTERNPROTO是一對非常有用的節點,人和我們自己寫的擴展節點都要用到他們。靈活使用不僅能夠使我們的VRML場景更加豐富多彩,還能提高效率,減少代碼複寫率等。
希望本文能夠起到一個拋磚引玉的作用。
最後,給出運行效果截圖: