虚拟现实建模语言(virtual reality modeling language 即vrml)和java语言都是 可执行的代码“技术,通过网络传输的只是一个很小的描述文件(指令性数据集),客户端通过本地的vrml浏览器、java虚拟机交互地访问这些文件并解释执行。将java与vrml结合,能够很好地实现网络多媒体课件技术:使用vrml进行场景构造,内嵌用java编写的程序设计实现对象的编程行为。这使vrml在网上创建充满真实感的三维虚拟世界,为用户提供一种自然的、身临其境的体验方式,包括交互性、动态效果、连续性及参与感,而这将是未来网络教学的主要形式之一。
1、 vrml简单交互
vrml简单交互,即vrml空间中各节点间数据交互。vrml有统一的数据传输接口和完善的属性集合和事件集合,可接收传感器节点发送过来的数值,并根据所对应的接口属性域设置提取数据、改变自身的属性值、触发事件进而改变自己在空间中的位置、外型和显示效果,还可以将处理后的数据送到输出域,传递给其他模型节点。各节点通过接口域的相互路由(route to)实现数据传输,路由的映射关系自由,但接口域的数据规格必须严格一致。
2、vrml扩展交互
vrml本身交互机制是无法构建一个大型多媒体课件的,因为vrml本身不能完成普通程序设计中的转折、分支、循环等基本特征。因此实际系统设计中,可利用java语言提供的强大网络编程能力,编写与远程系统通信和共享能力处理等功能的应用程序。java对vrml的所有支持都通过附加的封装类实现。通过这些类,java程序就能够访问vrml场景、接受和发送事件、从页面上得到vrml对象等,实现对vrml场景的完全控制。[2]根据访问方式的不同,又可分为:脚本编程接口sai(script authoring interface) 交互与外部编程接口eai(eai external authoring interface) 交互。
2.1脚本编程接口sai交互
sai交互即使用事件机制和路由利用script节点实现vrml与 java(或javascript)的交互。首先,通过eventin将事件传至script节点中的脚本;其次,在script节点中的脚本中调用相应的java类(或javascript)进行处理;最后,通过eventout将结果送回到vrml场景以实现动画或交互。
vrml的script节点是一种控制传感器和内插器的节点,通过它可以定义和改变场景中对象的外观和行为。script节点的url域可以包含一段javascript程序或者是链接的一个javaclass.script节点可放置在场景的任何地方,用def命名它,从而完成事件的删除成传递。script节点的字段是由用户延伸的,到达这些字段的字段会自动移到和script节点相关程序中。另外,script节点也能够通过编写合适程序脚本,动态生成(改变)三维造型并绑定合适的感应器和内插器。
2.1. 1java通过script类(vrml.node包)与script结点建立联系,控制vrml结点
首先,定义script节点中的各域域值,设置路由(route)使vrml中的其它节点与script节点联系起来。当与script节点有联系的节点发生事件时,事件通过路由传给script节点的eventin域,script节点则通过url域中指定的地址找到java类。
然后,java类开始工作,最先执行的是initialize()方法:在initialize()方法中必须获得script节点中eventin或field域的值,或者把java中的值返回给script中的eventout域。script类提供的方法有: getfield(fieldname); geteventin(eventinname); geteventout(eventoutname); 作为参数的fieldname、eventinname、eventoutname要与script节点中定义的名称相符合,对应的field类的子类(vrml.filed包)与script节点中定义的各种type类型一致。它提供getvalue()和setvalue()方法。
再则,通过script类提供的processevent(event p0)或processevents(int p0, event[] p1)方法来捕获script节点传递的事件。processevent和processevents方法是所有外部事件公共的输入点。java程序通过替代这两个方法对事件做出相应的处理。vrml包中event类的getname()方法返回事件名称(该名称与script节点中发生该事件的eventinname相同),确定发生的事件,从而做出相应的处理;event类的getvalue()方法则返回一个constfield类的实例 (它是script节点中发生该事件的eventin域),constfield类是field类的子类,它没有setvalue()方法,因而只能取出script节点中eventin域的值,而不能修改。
最后,所有事件处理完后,程序就调用script类中的eventsprocessed()方法,在这儿可以加入用户想要实现的动画、交互等各种效果。script类中的shutdown()方法实现离开vrml文件的方式。
通过script节点,能java程序处理过的数据返回给vrml文件。通过初始化时geteventout()得到的变量的setvalue()方法将值传递给script节点的eventout域,再通过路由传递给vrml中的各类节点,完成整个通信过程。
2.1.2 java通过script节点和node类(vrml.node包)直接修改vrml节点
通过script节点和路由,同时又必须在script节点的eventout域设置控制点,java程序才能改变vrml文件中节点值。当有大量数据需要传送时,这种给每个数据设置一个eventout域加一个路由的方法显然是相当麻烦的。因此java,提出直接修改节点的方法。
java直接修改场景中的节点,仍然要用到script节点。首先在定义script节点时,将eventout域改为field域,fieldtype定义为sfnode,具体格式如下:
“field sfnode fieldname use nodename”。 “nodename”为要修改的节点名称(在vrml中由def语句命名),产生一个指向名为nodename节点的指针。执行初始化时通过getfield()方法得到相应的sfnode类的一个实例,通过getvalue()方法得到该类的值,这里它的值恰好是名称为nodename的node类的实例,然后通过node类提供的方法直接修改节点。getexposedfield(string p0)、geteventin(string p0)方法,分别得到节点的field和eventin域,并把结果作为field类;再通过各field类的子类提供的方法读取和设置值,将设置结果直接返回节点,不用再设置路由,从而达到了直接修改节点的目的。
node类还提供了geteventout(string p0)方法,用它可以得到节点的eventout域,与前两个方法不同,它得到的结果是一个不能修改的field类,即field类的子类constfield类。 使用改进后的方法,可以有效的缩减代码长度,节省vrml文件的计算时间,提高vrml场景的生成速度。
2.1.3 java通过script节点和browser类(vrml包)直接创建vrml场景
设置group节点的addchindren和removechindren的值来可添加和删除具体节点,但还无法操作路由。当有大量不同的并相互关联的节点要通过java在vrml中创建时,工作量是难以想象的。为了满足这一要求,script类继承basenode(vrml包)类,basenode类中定义了两个方法:getbrowser()和gettype()。getbrowser()方法将会返回basenode类所处的浏览器。browser类存在于vrml包中,它提供了一系列方法来对整个vrml文件进行控制。 如:
(1)、添加和删除路由:addroute(sfnode fromnode,sfstring fromeventout,sfnode tonode,sfstring toeventin),deleteroute(sfnode fromnode,sfstring fromeventout,sfnode tonode,sfstring toeventin)
(2)、直接创建vrml:createvrmlfromstring(sfstring vrmlsyntax)(把要创建的节点语法以字符串的方式作为参数写入)
(3)、在vrml中建立相应节点:createvrmlfromurl(mfstring url,sfnode node,sfstring event)(该方法的提供节点语法的参数是放在url地址所指出的vrml文件中,浏览器通过url找到要加入的节点,把它加入到node参数指定的节点中event参数指定的eventin域中)
(4)、用指定的节点来替换整个vrml文件:replaceworld(mfnode nodes)
用url指定的另一个vrml文件来替换当前的文件:loadurl(mfstring url,mfstring parameter)。
script类执行initialize()方法时,得到浏览器实例,然后processevent(event e)方法执行时使用上述方法建立、修改vrml场景。 通过改进,不仅vrml文件代码长度缩减,java程序代码也大大减少,进一步提高了vrml场景的生成速度。
2.2外部编程接口(eai)交互
sai交互vrml离不开script节点和script类(vrml.node包)。为了实现通信功能编写的java类都必须继承自script类。但java不支持多继承,如果用applet操控vrml场景, java程序要继承script类的方法相当复杂。特别是要把vrml文件与applet集成在同一个html页面时,用script类来实现颇为困难,而且程序代码冗长、可扩展性差。
vrml97标准提供的eai定义了外部环境怎样通过vrml现存的事件驱动模式来访问vrml内部的节点的一系列方法,实现了applet与vrml场景的通信。eai包含浏览器脚本接口的全部功能,例如查询浏览器的状态,增加或删除路由,生成新的节点等功能。
java主要使用browser和node两个类(vrml.external包),以及其它一些如处理eventin域和eventout域的各个属性类(eventin*、eventout*)和处理例外的各种*exception类完成对eai的描述。
browser类提供了三个方法:getbrowser(applet papplet)、getbrowser(applet papplet,string framename,int index)和getnode(string name)方法。前两个方法是静态方法,直接在applet类中使用这两个方法得到applet类和vrml所在的浏览器类的实例。getnode()方法可以直接得到vrml文件的具体节点(node类)的实例,用inline语句嵌入在vrml文件中的各个节点是用getnode()方法是无法得到的。这里的node类与方法一的node类是完全不同的,它用自己的geteventin()和geteventout()方法来得到vrml节点的各个域。然后使用返回的event*的实例的getvalue()和setvalue()方法来控制具体的值。eventin*类仅提供了设置值的setvalue()方法;eventout*类相对复杂,不仅提供了getvalue()方法来获得当前值,还提供了监听eventout域的接口??eventoutobserver,随时监听vrml节点中是否有新的eventout事件发生,一旦有新事件发生,通过替代该接口提供的callback(eventout value,double timestamp,object data)方法响应事件。
通过browser和node类,可以抛弃script节点和script类甚至路由而现实对vrml文件的各种节点即node类的各个实例直接操控。但getbrowser()方法的参数必须是applet的实例,因而该方法只能通过applet来控制vrml.
3.总结
vrml与其它实现三维场景的技术手段(如opengl,cult3d,3dsmax,viewpoint)相比,具有语法简单、三维建模功能强大、便于网上发布等优点。但由于vrml本身仅仅是一种标准,不可能满足各行各业的所有需要。所以,高级交互功能的实现必须借助于java等功能强大的高级语言。这样,可以有效地弥补vrml本身的一些不足(例如逻辑判断、文件操作、键盘输入、精确控制场景等),进而完善与html等其它媒体的交互,也可实现复杂的虚拟环境系统网络课件。