java3D 简介

java3D
一。JAVA3D的作用:

JAVA3D可用在三维动画、三维游戏、机械CAD等领域。

1.可以用来编写三维形体,但和VRML不同,JAVA3D没有基本形体,不过我们可以利用
JAVA3D所带的UTILITY生成一些基本形体如立方体、球、圆锥等,我们也可以直接调用
一些软件如ALIAS、LIGHTWARE、3DS MAX生成的形体,也可以直接调用VRML2.0生成的
形体。

2.可以和VRML一样,使形体带有颜色、贴图。

3.可以产生形体的运动、变化,动态地改变观测点的位置及视角。

4.可以具有交互作用,如点击形体时会使程序发出一个信号从而产生一定的变化。

5.可以充分利用JAVA语言的强大功能,编写出复杂的三维应用程序。

6.JAVA3D具有VRML所没有的形体碰撞检查功能(这也是本人目前中意JAVA3D的原因)。
 
二。OPENGL、VRML、DIRECT3D、JAVA3D的比较
由于OPENGL的跨平台特性,许多人利用OPENGL编写三维应用程序,不过对于一个非
计算专业的人员来说,利用OPENGL编写出复杂的三维应用程序是比较困难的,且不说C
(C++)语言的掌握需要花费大量时间精力,当我们需要处理复杂问题的时候,我们不得
不自己完成大量非常繁琐的工作。当然,对于编程高手来说,OPENGL是他们发挥才能的
非常好的工具。
VRML2.0(VRML97)自1997年12月正式成为国际标准之后,在网络上得到了广泛的应
用,编写VRML程序非常方法(VRML语言可以说比BASIC、JAVASCRIPT等语言还要简
单),同时可以编写三维动画片、三维游戏、用于计算机辅助教学,因而其应用前景非
常广阔尤其适合在中国推广应用。不过由于VRML语言功能目前还不是很强(如目前没有
形体之间的碰撞检查功能),与JAVA语言等其它高级语言的连接较难掌握,因而失去了
一些计算机高手的宠爱。但我们认为,我们可以让大学里的文理科学生利用VRML编写多媒
体应用程序,让学生很快地对编写程序感兴趣,从而使国内的计算机水平得到提高。
DIRECT3D是Microsoft公司推出的三维图形编程API,它主要应用于三维游戏的编程,
目前相关的学习资料难于获得,由于它一般需要VC等编程工具进行编程,需要编程人员
具有较高的C++等高级语言的编程功底,因而难以普及。
JAVA3D是建立在JAVA2(JAVA1.2)基础之上的,JAVA语言的简单性使JAVA3D的推广
有了可能。OPENGL和JAVA3D之间的比较可以看成汇编语言与C语言之间的比较,一个是低
级的,一个是高级的(也许这样比较不太恰当)。JAVA3D给我们编写三维应用程序提供
了一个非常完善的API,它可以帮助我们:
生成简单或复杂的形体(也可以直接调用现有的三维形体)
使形体具有颜色、透明效果、贴图。
可以在三维环境中生成灯光、移动灯光。
可以具有行为(Behavior)的处理判断能力(键盘、鼠标、定时等)
可以生成雾、背景、声音等。
可以使形体变形、移动、生成三维动画。
可以编写非常复杂的应用程序,用于各种领域如VR。

在编写JAVA3D程序之前,我们需要了解一些概念,完成一些准备工作。

一. JAVA3D的数据结构
JAVA3D实际上是JAVA语言在三维图形领域的扩展,JAVA3D的编程和JAVA一样,
是面向对象的编程。
JAVA3D的数据结构采用的是Scene Graphs Structure(场景图),这一灵活
的树状结构与显示列表多少有些相似之处,但运用起来更耐用(More Robust)。
JAVA3D的场景图是DAG(Directed-acyclic Graph),即具有方向性的不对称图形。
场景图中有许多线和线的交汇点,交汇点称为节点(Node),不管什么节点,
它都是JAVA3D类的实例(Instance of Class),线(Arc)表示实例之间的关系。
在JAVA3D的场景图中,最底层(根部)的节点是Virtual Universe,每一个
场景图只能有一个Virtual Universe。
在Virtual Universe上面,就是Locale节点,每个程序可以有一个或多个
Locale,但同时只能有一个Locale处于显示状态,就好象一个三维世界非常大,
有很多个景点,但我们同时只能在一个景点进行观察。当然我们可以从一个景点
跳到另一个景点,不过绝大多数程序只有一个Locale。
每一个Locale上面拥有一个到多个BranchGroup节点。我们知道,要想建立
我们的三维应用环境,我们必须建立所需要的形体(Shape),给出形体的外观
(Appearance)及几何信息(Geometry),再把它们摆放在合适的位置,
这些形体及其摆放位置都建立在BranchGroup节点之上,摆放位置通过另一个节点
TransformGroup来设定。另外,我们在安放好三维形体之后,还需要设定具体的
观察位置,我们暂时用View Platform代替,它也是建立在TransformGroup节点
之上的。
下面我们用一个示意图来说明上面我们介绍的JAVA3D的场景图概念。

Virtual Universe
|
|----------------------------------|
| |
Locale Locale
|
|
----------------+-----------------
| | | |
| | | |
BG BG BG BG (BG--BranchGroup)
| | | |
| | | | (S---Shape)
S TG TG TG (TG--TransformGroup)
----+--- | | |
| | | | |
A G S S View Platform
| |
| | (A---Appearance)
----+---- ----+---- (G---Geometry)
| | | |
| | | |
A G A G

二. 如何编写JAVA3D源程序
用自己喜爱的文本编辑工具编辑源程序,和其它JAVA程序
一样,程序后缀为JAVA。

三. 如何运行JAVA3D源程序
用JAVAC编译源程序,生成class文件。根据文件的类型,
选择用JAVA或APPLETVIEWER运行程序。
JAVA3D程序可以为APPLICATION程序,也可以为APPLET程序,
因而JAVA3D程序也可以摆放在网页上,当然这时候我们必须在浏
览器上做一些设置工作(以后再介绍)。

四. 一个最简单的JAVA3D源程序。
下面我们介绍一个最简单的JAVA3D源程序,进而介绍JAVA3D
为我们提供的各种功能。
程序是一个JAVA的APPLET类型的程序,它的作用是显示一个
红色的圆锥,仅此而已。
名为SimpleCone.java。
//SimpleCone.java

import java.applet.Applet;
import java.awt.BorderLayout;
//import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class SimpleCone extends Applet{

public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();

TransformGroup objTrans = new TransformGroup();
objRoot.addChild(objTrans);

Appearance app = new Appearance();
Material material = new Material();
material.setEmissiveColor(new Color3f(1.0f,0.0f,0.0f));
app.setMaterial(material);
Cone cone=new Cone(.5f,1.0f,1,app);
objTrans.addChild(cone);

objRoot.compile();
return objRoot;
}

public SimpleCone() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

// public static void main(String[] args) {
// new MainFrame(new SimpleCone(), 256, 256);
// }
}

//end of Simple.java
在运行applet程序时,我们需要编写一个HTML文件:
<HTML>
<BODY>
<APPLET code=SimpleCone.class width=200 height=150>
</APPLET>
</BODY>
</HTML>
先用javac将JAVA3D源程序编译成class文件,再用
appletviewer运行HTML文件。
虽然程序是applet程序,但我们也可以将其变成
application程序,这时我们只需将程序头尾的四个注释行
的注释符号去掉即可,这时我们可以用java来运行它:
java SimpleCone

JAVA3D程序也是JAVA程序,因而我们首先必须对JAVA有所了解,并能
编写简单的JAVA程序,了解JAVA编程的基本概念,关于JAVA语言的相关知
识,我们在这里就不一一介绍了,国内这方面的书籍非常丰富。

一. SimpleCone 程序分析
1. SimpleCone.java及其对应的VRML程序
上一节中,我们给出了SimpleCone这个非常简单的JAVA3D程序,下面
我们来解读这个程序,进而介绍JAVA3D所提供的API的内容。
第三节中,我们对JAVA3D的场景图结构作了一个简单的介绍,从场景图
我们可以看出,JAVA3D的场景图和VRML的场景图非常相似,因而我们可以编
写类似的VRML程序出来,与JAVA3D程序进行比较。
SimpleCone程序只是显示一个红色的圆锥,相类似的VRML程序为:
//SimpleCone.wrl

#VRML V2.0 utf8
Shape{
geometry Cone{ bottomRadius .5 height 1.0}
appearance Appearance{material Material{emissiveColor 1 0 0}}
}

//end of SimpleCone.wrl

可以看到,与JAVA3D程序相比,VRML程序要简单的多,并且可以直接在
IE和Netscape浏览器上运行,而JAVA3D则要复杂的多。不过几乎所有的VRML
程序都可以用JAVA3D编写出来,而稍微复杂一点的JAVA3D的程序则基本上无
法用VRML语言完成。因为VRML几乎可以看成是一个三维计算机图形格式,而
JAVA3D则是一个高级计算机语言。
在上面的VRML程序中,只定义了一个三维形体及其材质,背景、灯光、
位置等均为缺省值。
同样地,SimpleCone.java程序中,我们也只定义了三维形体及其材质,
没有背景、灯光等,处在缺省的位置上(坐标系原点)。
JAVA3D和VRML一样,采用的是一个符合右手螺旋法则的三维坐标系。正
X轴向右,正Y轴向上,正Z轴朝外(从计算机屏幕指向我们)。

2. JAVA3D(API)中的类
JAVA3D是SUN公司为我们提供的一个API,里面包含了几乎所有我们所需
要的编写JAVA三维多媒体应用程序的基本的类及方法。我们在编程时,只
需调用所需要的类及方法,就可以快速地编写出复杂的三维多媒体应用程
序。可以说,我们所做的工作只是学会应用JAVA3D的各种类(Objects)及方
法。
JAVA3D为我们提供了100多个存放于javax.media.j3d程序包中的类,它
们被称为JAVA3D的核心类,除了核心包中的类以外,JAVA3D还提供了一些其
它程序包,其中一个重要的包是com.sun.j3d.utils包(Utility)。
JAVA3D所提供的Utility并不是JAVA3D编译环境的核心组成部分,我们可以
不用它,不过使用它们会大大提高我们的程序编写效率。JAVA3D为我们提供的
Utility会不断增加,例如有可能增加处理NURBS曲线的Utility。目前,JAVA3D
为我们提供了四组Utility,它们是:
用于调用其它三维图形格式如ALIAS图形格式的content loader
用于构造场景图的scene graph construction aids
用于建立一些基本体如圆锥、球的geometry classes
一些其它方便我们编程的convenience utilities
除了JAVA3D的核心包及Utility包之外,每个JAVA3D程序还必需用到下面
两个包:java.awt和javax.vecmath。java.awt包用来定义一个显示用的窗口,
而javax.vecmath包则是用来处理调用程序所需要的定义矢量计算所用的类,
处理定义三维形体及其位置时,我们需要用到点、矢量、矩阵及其它一些数学
对象,它也是JAVA3D所提供的一个包,目前它在JAVA3D的编译环境中,今后则
可能会成为JAVA1.2的核心组成部分。

根据其作用,JAVA3D所提供的类主要有两种类型:Node、NodeComponent
Node类,它含有Group及Leaf两个子类:
Group类 (用于将形体等按一定的组合方式组合在一起),
类似于VRML的组节点。
Leaf 类 (如Light、Sound、Background、shape3d、
Appearance、Texture及其属性等,还有
ViewPlatform、Sensor、Behavior、Morph、
Link等),类似与VRML的相应节点,是JAVA3D
场景图的重要组成部分。
NodeComponent类,用于表示Node的属性,它并不是JAVA3D场景图
的组成部分,而是被JAVA3D场景图所引用,如某
一个颜色可以被多个形体所引用。

3. SimpleCone.java程序import语句部分的介绍
根据JAVA3D所提供的类,按照面向对象的编程方式,我们可以编写出三维
环境中所需要的各个对象。编写JAVA3D程序的关键是学会应用JAVA3D所提供的
各个类,生成自己所需要的对象。
下面我们来看一下SimpleCone.java里的每一个import语句。
我们知道,java程序中。凡是利用到的外部的类均用import语句调用,我
们首先介绍一下程序中的import语句。
第一个import语句表示本程序是Applet程序。
第二个import语句表示窗口环境采用BorderLayout管理方式。
第三个import语句语句在去掉//后就可以使程序既为applet
也为application,为此,使用了JAVA3D所附带的一个Utility,这是一个名
叫Jef Poskanzer的人所编写的类。
第四个import语句表示调用生成Cone的一个Utility,这是因为,JAVA3D
和VRML不同,VRML有几个基本几何体的节点语句,但JAVA3D的核心部分一个
基本体也没有定义,但JAVA3D所附带的Utility为我们提供了一些事先编好的
基本体,我们可以调用这些基本体。
第五个import语句表示我们调用一些设置基本的观测位置所用的类,如
SimpleUniverse、Viewer、ViewingPlatform等,利用它们,我们可以很方便
地构造JAVA3D场景图底部的VirtualUniverse、Locale及其上的View分支,进
而使精力主要用在建模及复杂问题的处理上面,当然它们也都是JAVA3D所附带
的Utility。
第六个import语句表示调用程序所需要的JAVA3D核心类,因而所有JAVA3D
程序都必须带有这一语句,只有这样才能在JDK1.2环境中编译执行JAVA3D程序。
第七个import语句表示调用程序所需要的定义矢量计算所用的类。

3. SimpleCone.java程序的组成
SimpleCone.java程序主要由三个部分组成:
createSceneGraph方法的定义
SimpleCone的构造函数
用于生成application应用程序的main函数

4. createSceneGraph方法的定义
我们首先来看一下createSceneGraph方法的定义部分。通过第三讲的
JAVA3D场景图的简单示意图,我们知道,为了编写出一个JAVA3D应用程序,
我们必须编写出一个拥有三维形体的内容分支,即一个分支组,一个
BranchGroup。我们将我们所需要的形体及其材质定义在里面,由于一般
形体会摆放在三维空间的某一个位置,因而我们还需要先在BranchGroup
分支之上建立一个可用于几何变换用的TransformGroup,即几何变换分支
组,再将形体及其材质作为TransformGroup的一个分支给出,当然程序中
如果将形体摆放在坐标原点,也可以不给出一个TransformGroup,如下面
的SimpleCone1程序运行起来和SimpleCone结果完全一样,它没有一个
TransformGroup对象:

//SimpleCone1.java
import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class SimpleCone1 extends Applet{

public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();

Appearance app = new Appearance();
Material material = new Material();
material.setEmissiveColor(new Color3f(1.0f,0.0f,0.0f));
app.setMaterial(material);
Cone cone=new Cone(.5f,1.0f,1,app);
objRoot.addChild(cone);

objRoot.compile();
return objRoot;
}

public SimpleCone1() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new SimpleCone1(), 256, 256);
}
}
//end of SimpleCone1.java

JAVA3D有三种运行方式,一种是Immediate Mode,一种是
Retained Mode,一种是Compiled-Retained Mode。
SimpleCone程序中的
objRoot.compile();
表示程序为Compiled-Retained Mode,在这种模式下,JAVA3D对程序
进行了优化,程序的运行运行速度最快。不过在一些场合,如形体在
程序运行过程中才生成,则不能Compile形体,这说明优化是有条件的。
注意,JAVA3D程序没有“开始渲染”这一步骤,当一个View被有
效地放入一个Virtual Universe,JAVA3D运行环境就开始不停地渲染
JAVA3D场景图中的三维形体,从而使屏幕上显示出三维图形。

5. SimpleCone的构造函数
SimpleCone的构造函数的作用为
首先设置一个BorderLayout。
生成一个名为c的Canvas--Canvas3D继承了JDK1.2中的Canvas类,
从而构造了一个图形环境。
将c放入BorderLayout的中心位置。
生成一个场景图分支scene,里面定义了形体及其材质(红色)。
借用JAVA3D的一个Utility,生成了场景图的Virtual Universe及
Locale、Viewer,和VRML程序有些不同,在缺省情况下,
JAVA3D的观察点为位于(0 , 0 , 2.41),而VRML的观察点
位于(0 , 0 , 10),因而形体在VRML中看起来比较小,
而在JAVA3D中看起来比较大。我们利用这个Utility生成的
这个u使我们不必考虑生成场景图中的观测分支,不过它也
有缺点,即我们不能在Virtual Universe中拥有多个View,
因而也就不能从一个View跳到另一个View。

6. 用于生成application应用程序的main方法
为了使我们的程序既可以为applet程序,又可以为application
程序,我们可以通过编写一个main方法,利用import语句提供的
MainFrame类,生成一个MainFrame对象,从而使程序变成为
application程序。MainFrame是JAVA3D为我们提供的一个非常有用
的Utility。

介绍了一个简单的JAVA3D程序后,我们开始学习JAVA3D的编程
技术。首席我们介绍三维基本形体的生成。
和VRML不同,JAVA3D没有基本形体类,因而在程序中无法直接
生成大量应用的基本形体,如BOX、CONE、SPHERE等。我们可以通过
复杂的编程生成这些基本形体,也可以直接调用JAVA3D为我们提供的
geometry classes,利用它生成程序所需要的BOX、COLORCUBE、CONE、
SPHERE、CYLINDER。下面介绍这些基本体的生成方法。

一. BOX
UTILITY里BOX的构造函数有:
1. Box()
生成一个各边尺寸均为2的BOX,要说明的是,BOX、COLORCUBE、
SPHERE的坐标原点均在其中心点,CONE、CYLINDER的则在其轴
线的中点上。

2. Box(float xdim, float ydim, Appearance ap)
生成一个给定尺寸、给定外观属性的BOX
例:Box(.5f, .6f, .4f, myApp)

3. Box(float xdim, float ydim, float zdim, int primflags,
Appearance ap)
生成一个有特定说明的BOX,例如:
Box(.4f,.6f,.3f,Primitive.ENABLE_APPEARANCE_MODIFY, ap)
表示程序在运行时可以改变其外观属性。
我们可以在程序中使用的primflags种类可以在JAVA3D所附带提
供的UTILITY里的Primitive.java中获得。

二. COLORCUBE
UTILITY里COLORCUBE的构造函数有:
1. ColorCube()
生成一个边长均为2的COLORCUBE

2. ColorCube(double scale)
将边长均为2的COLORCUBE按比例放大缩小。

三. CONE
UTILITY里CONE的构造函数有:
1. public Cone()
生成一个底半径为1,高为2的CONE。

2. Cone (float radius, float height)

3. Cone (float radius, float height, int primflags,
Appearance ap)

4. Cone(float radius, float height, int primflags,
int xdivision, int ydivision, Appearance ap)
这里,xdivision、ydivision可用来表示圆锥的显示是高精度
的显示,或是底精度的显示,缺省时的中等精度时
xdivision = 15; ydivision = 1; 我们可利用这两个参数来
改变显示的效果,使显示圆锥的三角片更多或更少些。

四. SPHERE
UTILITY里SPHERE的构造函数有:
1. Sphere()
生成一个半径为1的SPHERE。

2. Sphere (float radius)

3. Sphere (float radius, Appearance ap)

4. Sphere(float radius, int primflags, Appearance ap)

5. Sphere(float radius, int primflags, int divisions)

6. Sphere(float radius, int primflags, int divisions,
Appearance ap)
这里,divisions的作用和圆锥的xdivision、ydivision相似。

五. CYLINDER
UTILITY里CYLINDER的构造函数有:
1. Cylinder()
生成一个底半径为1,高为2的CYLINDER。

2. Cylinder (float radius, float height)

3. Cylinder (float radius, float height, Appearance ap)

4. Cylinder (float radius, float height, int primflags,
Appearance ap)

5. Cylinder(float radius, float height, int primflags,
int xdivision, int ydivision, Appearance ap)

有了这些基本体的构造函数,我们就可以按SimpleCone.java
程序同样的方法,编写出生成BOX、COLORCUBE、CONE、SPHERE、

JAVA3D编程过程中,我们经常要编写一些点、线、面,JAVA3D所提供
的API中有许多这方面的对象,下面我们开始一一介绍它们的使用方法。

一. 点的生成
我们先用VRML编写一个带有不同颜色的六个点的程序。
//Point.wrl ----观测点在 (0 0 10)

#VRML V2.0 utf8
Shape {
geometry PointSet {
coord Coordinate {
point [.8 .8 .0, -.8, .8 0, .5 0 0,
-.5 0 0, -.8 -.8 0, .8 -.8 0]}
color Color{
color [ .0 .5 1., .5 .0 1, 0 .8 .2,
1 0 .3, 0 1 .3, .3 .8 0 ]}
}}

#end of Point.wrl
由程序可知,VRML程序中的点非常小,且无法变大。

下面我们改用JAVA3D编写同样的程序,不过由于观测
点不同,观测效果有差异,VRML程序中的点比较集中,JAVA3D
程序中的点比较分散,程序如下:
//Point1.java -----观测点在( 0 0 2.41 )

import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Point1 extends Applet {

public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();

float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,0.2f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};
Shape3D shape = new Shape3D();
PointArray point = new PointArray(6, PointArray.COORDINATES
|PointArray.COLOR_3);
point.setCoordinates(0,vert);
point.setColors(0,color);
shape.setGeometry(point);

objRoot.addChild(shape);
objRoot.compile();
return objRoot;
}

public Point1() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Point1(), 400,400);
}
}

//end of Point1.java

我们来分析一下上面的Point1.java。
我们知道,编写JAVA3D程序实际上是编写一个特定的场景图,
给出了场景图中带有形体及其属性的一个分支(BranchGrou)和
表示观察位置等数据的另一个分支(View Platform)。一般来说,
表示观测位置的分支可以用JAVA3D的UTILITY来完成,因而我们可
以看到,在Point1.java中,构造函数Point1和前面介绍的
SimpleCone.java的构造函数SimpleCone内容完全一样。两个程序
的不同之处在于形体构造及处理分支,即createSceneGraph方法的
定义。
我们来看一下Point1.java的createScendGraph方法的定义。
在这个方法里,程序先定义了一个分支objRoot,然后用数组
的形式定义了六个顶点坐标vert和六种颜色color,再用PointArray
定义了一组点point,并将顶点坐标及颜色赋值给point,由于JAVA3D
中的PointArray点是Shape3D的子类,它不能直接放入一个BranchGroup,
因而我们还要先定义一个Shape3D对象shape,再将point赋予shape,
这样point就可以放入BranchGroup类型的对象objRoot中了。

二. PointArray、IndexedPointArray介绍
JAVA3D提供的API中,可用于生成Point的对象有:
PointArray
IndexedPointArray

1. PointArray
PointArray的构造函数为:
PointArray( int vertexCount, int vertexFormat );
这里,vertexCount表示应生成的点的数目,
vertexFormat表示所需要的顶点的格式。

点、线、面几何体所需要的顶点的格式有:
COORDINATES 顶点坐标数组
NORMALS 顶点法向数组
COLOR_3 不带alpha值的颜色数组
COLOR_4 带alpha值的颜色数组
TEXTURE_COORDINATE_2 二维纹理坐标数组
TEXTURE_COORDINATE_3 三维纹理坐标数组
Point1.java程序用到了COORDINATES和COLOR_3。

2. IndexedPointArray
IndexedPointArray的构造函数为:
IndexedPointArray( int vertexCount, int vertexFormat,
int indexCount );
利用本函数,我们可以从众多的点中,选择特定的点来显示。
这里,vertexCount表示顶点坐标数组所提供的点的总个数,
indexCount表示最终应生成的点的个数。

三. 20像素大小的点的生成
JAVA3D可以生成任意大小的点,并且可以使点为方点或圆点。
下面的程序生成了一个20像素大小的程序。
//Point2.java
import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Point2 extends Applet {

public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();

float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};
Shape3D shape = new Shape3D();
PointArray point = new PointArray(6, PointArray.COORDINATES
|PointArray.COLOR_3);
point.setCoordinates(0,vert);
point.setColors(0,color);
PointAttributes pa = new PointAttributes();
pa.setPointSize(20.0f);

pa.setPointAntialiasingEnable(true);
//不加这一行,点的显示效果为正方形
//加了这一行,点的显示效果为圆形

Appearance ap = new Appearance();
ap.setPointAttributes(pa);

shape.setGeometry(point);
shape.setAppearance(ap);
objRoot.addChild(shape);
objRoot.compile();
return objRoot;
}

public Point2() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Point2(), 400,400);
}
}

//end of Point2.java

四. IndexedPointArray编写的点
下面的程序中,我们用IndexedPointArray生成了四个点。

//Point3.java

import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Point3 extends Applet {

public BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();

float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};
Shape3D shape = new Shape3D();

int[] index={ 0 , 2 , 3 , 4 };
int VertexCount=4;
IndexedPointArray point = new IndexedPointArray(6,
IndexedPointArray.COORDINATES|
IndexedPointArray.COLOR_3,
VertexCount);
point.setCoordinates(0,vert);
point.setColors(0,color);
point.setCoordinateIndices(0,index);
point.setColorIndices(0,index);
PointAttributes pa = new PointAttributes();
pa.setPointSize(20.0f);
pa.setPointAntialiasingEnable(true);
Appearance ap = new Appearance();
ap.setPointAttributes(pa);

shape.setGeometry(point);
shape.setAppearance(ap);
objRoot.addChild(shape);
objRoot.compile();
return objRoot;
}

public Point3() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Point3(), 400,400);
}
}

//end of Point3.java

通过上面的程序,我们来看一下IndexedPointArray
的应用方法。
在定义一个point实例后,我们要给出顶点坐标数组及
对应各个顶点的颜色数组,按下标给出我们的顶点及颜色
的具体选择方案。从而得以从众多的点中,选择特定的点来
显示并给定颜色。通过setPointSize、setPointAntialiasingEnable
的设定,使显示的点拥有一定的大小及良好的显示效果。

---1--- ---0---

---3--- ---2---

---4--- ---5---

  程序Point3.java中,我们只选用了六个点中的0、2、3、4
四个点。

五. 主程序比较简洁的程序Point4.java
前面几个程序,所有的内容均放置在一个程序中,这对于
阅读程序来说,增添了一些困难。一般来说,一个具体的例子通常
由几个JAVA3D程序来完成,一般是将形体生成部分划为单独的一个
子程序,下面我们将上面的Point3.java分成两个程序:子程序
myShape.java用来生成点,主程序Point4.java完成其它设置任务
并调用myShape.java,我们设定两个程序均位于同一个子目录下。
//pointShape.java

import javax.media.j3d.*;

public class pointShape extends Shape3D {

private float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};

public pointShape() {

int[] index={ 0 , 2 , 3 , 4 };
int VertexCount=4;
IndexedPointArray point = new IndexedPointArray(6,
IndexedPointArray.COORDINATES|
IndexedPointArray.COLOR_3,
VertexCount);
point.setCoordinates(0,vert);
point.setColors(0,color);
point.setCoordinateIndices(0,index);
point.setColorIndices(0,index);
PointAttributes pa = new PointAttributes();
pa.setPointSize(20.0f);
pa.setPointAntialiasingEnable(true);
Appearance ap = new Appearance();
ap.setPointAttributes(pa);
this.setGeometry(point);
this.setAppearance(ap);
}
}

//end of pointShape.java
--------------------------------------------
//Point4.java
import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Point4 extends Applet {

private BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
Shape3D shape = new pointShape();
objRoot.addChild(shape);
objRoot.compile();
return objRoot;
}

public Point4() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Point4(), 400,400);
}
}

//end of Point4.java

六. 能够旋转的点
前面介绍的JAVA3D程序,显示的内容是静止的,且看不出立体的
效果,为此,我们使程序中的点绕着Y轴旋转,这样就可以看到具有
立体效果的点了,当然,其它形体也可以按同样的方法编程,程序调
用了上面给出的pointShape.java。
//Point5.java

import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Point5 extends Applet {

private BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
objRoot.addChild(createObject());
objRoot.compile();
return objRoot;
}

private Group createObject() {
Transform3D t = new Transform3D();
TransformGroup objTrans = new TransformGroup(t);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

Shape3D shape = new pointShape();
objTrans.addChild(shape);

Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
0, 0,
4000, 0, 0,
0, 0, 0);
RotationInterpolator rotator =
new RotationInterpolator(rotationAlpha, objTrans, yAxis,
0.0f, (float) Math.PI*2.0f);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,0.0,0.0), 50.0);
rotator.setSchedulingBounds(bounds);
objTrans.addChild(rotator);

return objTrans;
}

public Point5() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Point5(), 400,400);
}
}
//end of Point5.java

在Point4.java的objRoot里,放置的是一个Shape3D对象,
而在Point5.java的objRoot里,放置的是一个Group对象。在
生成对象的createObject() 方法里,为了使得形体能够产生
旋转运动,我们首先建立了一个TransformGroup对象和一个
Transform3D对象,通过Capability参数的设置,表示在程序
运行时,objTrans能够进行几何变换,并将形体放入objTrans。
JAVA3D之所以能够使形体运动,是因为JAVA3D拥有类似于
VRML的时间传感器节点的Alpha对象,和类似于VRML的内插器节
点的各种Interpolator对象,它们在由BoundingSphere等对象
所设定的范围内在特定的时间内进行几何坐标变化,因而使形
体产生运动变化的效果。
本程序中,Alpha给出了一个4秒钟的循环变化时间周期;
RotationInterpolator规定了形体每4秒钟绕着Y轴旋转
一周。BoundingSphere表示所有距离坐标原点50米之内的形体
均可以旋转运动,而在这范围之外的所有形体均不产生运动。
和Point5.java相类似的VRML程序如下,VRML里的点不能够
改变大小:
//Point5.wrl

#VRML V2.0 utf8
DEF T Transform{
children Shape {
geometry PointSet {
coord Coordinate {
point [.8 .8 .0, -.8, .8 0, .5 0 0,
-.5 0 0, -.8 -.8 0, .8 -.8 0]}
color Color{
color [ .0 .5 1., .5 .0 1, 0 .8 .2,
1 0 .3, 0 1 .3, .3 .8 0 ]}
}}}

DEF TS TimeSensor{
cycleInterval 4
loop TRUE}

DEF OI OrientationInterpolator{
key [0 .25 .5 .75 1]
keyValue [0 1 0 1, 0 1 0 1.57, 0 1 0 3.14
0 1 0 4.71 0 1 0 6.28]}

ROUTE TS.fraction TO OI.fraction
ROUTE OI.value TO T.rotation

# end of Point5.wrl
CYLINDER的JAVA3D程序来。

我们可以利用JAVA3D的一些对象,生成各种直线。
可以生成直线的对象有:
1. LineArray
LineArray(int vertexCount, int vertexFormat)

2. LineStripArray
LineStripArray(int vertexCount ,int vertexFormat,
int[] stripVertexCounts )

3. IndexedLineArray
IndexedLineArray(int vertexCount, int vertexFormat,
int indexCount )

4. IndexedLineStripArray
IndexedLineStripArray( int vertexCount, int vertexFormat,
int indexCount, int stripIndexCounts[])

一. 利用LineArray生成直线
LineArray对象的定义如下:
LineArray(int vertexCount, int vertexFormat)
这里:
vertexCount表示顶点的个数(必须为偶数)
vertexFormat表示顶点的格式(第七讲有介绍)

由下面的程序我们得知,Line1.java和前面介绍的
Point4.java几乎完全一样,lineShape1.java和
pointShape.java也相差不多。运行Line1.java我们获得
了三条直线,由此得知,LineArray的作用是生成多条直线,
顶点坐标数组的每一对数据构成一条直线。
在编写LineArray生成的直线时,要注意,顶点及颜色
的个数必须相等且为偶数,此数目必须赋值于vertexCount,也即
程序中的vertexCount必须为偶数且不能少于顶点的个数。

-------------------------- 第一条

---------------- 第二条

-------------------------- 第三条

我们可以根据各种不同的情况,生成不同的直线,如
给定宽度的直线、虚线等。相应的的方法有:
setLineWidth(float lineWidth)
setLinePattern(int linePattern)
setLineAntialiasingEnable(boolean state)
对于线型linePattern有以下数据可选:
int PATTERN_SOLID
int PATTERN_DASH
int PATTERN_DOT
int PATTERN_DASH_DOT
这些内容对所有种类的直线都有效。

前面我们利用PointArray生成了六个点,这里,我们
将前面的pointShape.java稍微变化一下,则同样的六个点生
成了三条直线,所用的两个程序为:
//lineShape1.java

import javax.media.j3d.*;

public class lineShape1 extends Shape3D {

private float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};

public lineShape1() {
LineArray line = new LineArray(6,
LineArray.COORDINATES|LineArray.COLOR_3);
line.setCoordinates(0,vert);
line.setColors(0,color);
LineAttributes la = new LineAttributes();
la.setLineWidth(5.0f);
la.setLineAntialiasingEnable(true);
Appearance ap = new Appearance();
ap.setLineAttributes(la);
this.setGeometry(line);
this.setAppearance(ap);
}
}
//end of lineShape1.java
------------------------------------
//Line1.java ---using LineArray object

import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Line1 extends Applet {

private BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
Shape3D shape = new lineShape1();
objRoot.addChild(shape);
objRoot.compile();
return objRoot;
}

public Line1() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Line1(), 400,400);
}
}

//end of Line1.java

二. 利用LineStripArray生成直线
LineStripArray可用来生成多条折线段
LineStripArray对象的定义如下:
LineStripArray(int vertexCount ,int vertexFormat,
int[] stripVertexCounts )
这里:
vertexCount表示顶点的个数(必须为偶数)
vertexFormat表示顶点的格式(第七讲有介绍)
stripVertexCounts为一数组,数组里的每一个数值表示
每条折线段所拥有的顶点数目。
下面我们利用lineShape1.java同样的顶点坐标数组及
颜色数组,用LineStripArray对象生成直线。程序也是两个:
lineShape2.java、Line2.java,并使生成的直线绕着Y轴旋转,
直线线型为虚线,线宽为30个像素。
//lineShape2.java

import javax.media.j3d.*;

public class lineShape2 extends Shape3D {
int StripCount[] = new int[1];

private float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};

public lineShape2() {
StripCount[0] = 6;

LineStripArray line = new LineStripArray(6,
LineStripArray.COORDINATES|
LineStripArray.COLOR_3,StripCount);
line.setCoordinates(0,vert);
line.setColors(0,color);
LineAttributes la = new LineAttributes();
la.setLineWidth(30.0f);
la.setLineAntialiasingEnable(true);
la.setLinePattern(LineAttributes.PATTERN_DASH);
Appearance ap = new Appearance();
ap.setLineAttributes(la);
this.setGeometry(line);
this.setAppearance(ap);
}
}

//end of lineShape2.java
-----------------------------------------
//Line2.java

import java.applet.Applet;
import java.awt.BorderLayout;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Line2 extends Applet {

private BranchGroup createSceneGraph() {
BranchGroup objRoot = new BranchGroup();
objRoot.addChild(createObject());
objRoot.compile();
return objRoot;
}

private Group createObject() {
Transform3D t = new Transform3D();
TransformGroup objTrans = new TransformGroup(t);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

Shape3D shape = new lineShape2();
objTrans.addChild(shape);

Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
0, 0,
4000, 0, 0,
0, 0, 0);
RotationInterpolator rotator =
new RotationInterpolator(rotationAlpha, objTrans, yAxis,
0.0f, (float) Math.PI*2.0f);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,0.0,0.0), 50.0);
rotator.setSchedulingBounds(bounds);
objTrans.addChild(rotator);

return objTrans;
}

public Line2() {
setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null);
add("Center", c);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(c);
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}

public static void main(String[] args) {
new MainFrame(new Line2(), 400,400);
}
}

//end of Line2.java

由上可知,Line2.java这个程序和Point5.java几乎没有
什么差别,除了类的名字于调用的外部程序名不同之外,其余
完全相同。
lineShape1.java和lineShape2.java相差不大,
lineShape2.java多了一个StripCount数组,它可以用来生成
多个折线段,下面的lineShape3.java程序就将Line2.java生成
的一条折线段分成了两条折线段:0、1、2三个点构成了一个折
线段,3、4、5构成了另一条折线段,每个折线段的顶点数目就
构成了数组StripCount,StripCount数组的大小等于折线段的
数目。
//lineShape3.java
import javax.media.j3d.*;

public class lineShape3 extends Shape3D {
int StripCount[] = new int[2];

private float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};

public lineShape3() {
StripCount[0] = 3;
StripCount[1] = 3;

LineStripArray line = new LineStripArray(6,
LineStripArray.COORDINATES|
LineStripArray.COLOR_3,StripCount);
line.setCoordinates(0,vert);
line.setColors(0,color);
LineAttributes la = new LineAttributes();
la.setLineWidth(30.0f);
la.setLineAntialiasingEnable(true);
la.setLinePattern(LineAttributes.PATTERN_DASH);
Appearance ap = new Appearance();
ap.setLineAttributes(la);
this.setGeometry(line);
this.setAppearance(ap);
}
}

//end of lineShape3.java

将lineShape3.java生成的绕Y轴旋转的形体用VRML
程序表示的结果为:

#VRML V2.0 utf8
DEF T Transform{
children Shape {
geometry IndexedLineSet {
coord Coordinate {
point [.8 .8 .0, -.8, .8 0, .5 0 0,
-.5 0 0, -.8 -.8 0, .8 -.8 0]}
coordIndex [0 1 2 -1, 3 4 5 ]
# 两个折线段
color Color{
color [ .0 .5 1., .5 .0 1, 0 .8 .2,
1 0 .3, 0 1 .3, .3 .8 0 ]}
}}}

DEF TS TimeSensor{
cycleInterval 4
loop TRUE}

DEF OI OrientationInterpolator{
key [0 .25 .5 .75 1]
keyValue [0 1 0 1, 0 1 0 1.57, 0 1 0 3.14
0 1 0 4.71 0 1 0 6.28]}

ROUTE TS.fraction TO OI.fraction
ROUTE OI.value TO T.rotation
#end of lineShape3.wrl

三. 利用IndexedLineArray生成直线
IndexedLineArray对象的定义为:
IndexedLineArray(int vertexCount, int vertexFormat,
int indexCount )
这里:
vertexCount表示顶点数组里顶点的个数
vertexFormat表示顶点的格式(第七讲有介绍)
indexCount表示选用的顶点个数,如果一个点用了
几次,则要把几次加进去

在上一节里我们介绍了利用IndexedPoint生成点
的程序,和IndexedPoint相类似,我们可以利用
IndexedLineArray生成直线段。
下面的lineShape4.java利用了IndexedLineArray
从六个点中挑选了3个点,生成了2条直线。
从程序中我们可以看到,下标为0的点使用了两次,
但生成的是两条线,因而参数VertexCount应为4,即
此处的VertexCount的数值应为直线条数的两倍。
//lineShape4.java

import javax.media.j3d.*;

public class lineShape4 extends Shape3D {
int[] index={ 1, 0, 0 , 3, };
int VertexCount=4;

private float vert[] = {
.8f, 0.8f,0.0f,
-0.8f, 0.8f,0.0f,
0.5f, 0.0f,0.0f,
-0.5f, 0.0f,0.0f,
-0.8f,-0.8f,0.0f,
0.8f,-0.8f,0.0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
};

public lineShape4() {

IndexedLineArray line = new IndexedLineArray(6,
IndexedLineArray.COORDINATES|
IndexedLineArray.COLOR_3,VertexCount);
line.setCoordinates(0,vert);
line.setColors(0,color);
line.setCoordinateIndices(0,index);
line.setColorIndices(0,index);
LineAttributes la = new LineAttributes();
la.setLineWidth(30.0f);
la.setLineAntialiasingEnable(true);
la.setLinePattern(LineAttributes.PATTERN_DASH);
Appearance ap = new Appearance();
ap.setLineAttributes(la);
this.setGeometry(line);
this.setAppearance(ap);
}
}

//end of lineShape4.java

将lineShape4.java翻译成VRML的相应程序为:

#VRML V2.0 utf8
DEF T Transform{
children Shape {
geometry IndexedLineSet {
coord Coordinate {
point [.8 .8 .0, -.8, .8 0, .5 0 0,
-.5 0 0, -.8 -.8 0, .8 -.8 0]}
coordIndex [1 0 -1 0 3]
color Color{
color [ .0 .5 1., .5 .0 1, 0 .8 .2,
1 0 .3, 0 1 .3, .3 .8 0 ]}
}}}

DEF TS TimeSensor{
cycleInterval 4
loop TRUE}

DEF OI OrientationInterpolator{
key [0 .25 .5 .75 1]
keyValue [0 1 0 1, 0 1 0 1.57, 0 1 0 3.14
0 1 0 4.71 0 1 0 6.28]}

ROUTE TS.fraction TO OI.fraction
ROUTE OI.value TO T.rotation


四. 利用IndexedLineStripArray生成直线
IndexedLineStripArray对象的定义如下:
IndexedLineStripArray( int vertexCount, int vertexFormat,
int indexCount, int stripIndexCounts[])
这里:
vertexCount表示顶点数组里顶点的个数
vertexFormat表示顶点的格式(第七讲有介绍)
indexCount表示选用的顶点的个数
stripIndexCounts为一数组,数组里的每一个数值表示
每条折线段所拥有的顶点数目。
下面的程序里,我们给出10个顶点,

--0-- --1--

--2-- --3--

--4-- --5--

--6-- --7--

--8-- --9--

然后我们用IndexedLineStripArray生成三个折线段,第一个
折线段为:0 1 3 2,第二个折线段为3、5、4,第三个折线段为
6、7、8、6,最后一个点没有用到。所有的直线宽度为30像数。
这时我们只用了10个点中的9个点,但有2个点用了两次,因而程序
中的vertexCount为11,
程序如下:
//lineShape5.java
import javax.media.j3d.*;

public class lineShape5 extends Shape3D {
int StripCount[] = new int[3];
int[] index={ 0 , 1 , 3 , 2 , 3 , 5 ,
4 , 6 , 7 , 8 , 6 } ;
int vertexCount = 11;
private float vert[] = {
-.3f , .8f , .0f,
.3f , .8f , .0f,
-.3f , .4f , .0f,
.3f , .4f , .0f,
-.3f , .0f , .0f,
.3f , .0f , .0f,
-.3f , -.4f , .0f,
.3f , -.4f , .0f,
-.3f , -.8f , .0f,
.3f , -.8f , .0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f
};

public lineShape5() {
StripCount[0] = 4;
StripCount[1] = 3;
StripCount[2] = 4;
IndexedLineStripArray line = new IndexedLineStripArray(10 ,
IndexedLineStripArray.COORDINATES|
IndexedLineStripArray.COLOR_3, vertexCount , StripCount);
line.setCoordinates(0,vert);
line.setColors(0,color);
line.setCoordinateIndices(0,index);
line.setColorIndices(0,index);
LineAttributes la = new LineAttributes();
la.setLineWidth(30.0f);
la.setLineAntialiasingEnable(true);
la.setLinePattern(LineAttributes.PATTERN_DASH);
Appearance ap = new Appearance();
ap.setLineAttributes(la);
this.setGeometry(line);
this.setAppearance(ap);
}
}

一. 生成平面的对象及其定义
JAVA3D可通过编程显示出面来,面有两种:三角形和四边形,
相应的对象为Triangle和Quad。
JAVA3D用于生成平面的对象有:
1. TriangleArray
TriangleArray (int vertexCount, int vertexFormat )

2. QuadArray
QuadArray (int vertexCount, int vertexFormat )

3. TriangleStripArray
TriangleStripArray ( int vertexCount , int vertexFormat,
int[] stripVertexCounts )

4. TriangleFanArray
TriangleFanArray ( int vertexCount ,int vetexFormat,
int[] stripVertexCounts )

5. IndexedTriangleArray
IndexedTriangleArray (int vertexCount , int vertexFormat,
int indexCount)

6. IndexedQuadArray
IndexedQuadArray (int vertexCount , int vertexFormat,
int indexCount )

7. IndexedTriangleStripArray
IndexedTriangleStripArray( int vertexCount, int vertexFormat,
int indexCount, int stripIndexCounts[])

8. IndexedTriangleFanArray
IndexedTriangleFanArray ( int vertexCount, int vertexFormat,
int indexCount, int stripIndexCounts[])

二. TriangleArray生成的面
和前面介绍的PointArray、LineArray一样,面也可以用
TriangleArray来生成,利用它可以生成三角片面我们先看一下TriangleArray的定义:
TriangleArray (int vertexCount, int vertexFormat )
这里:
vertexCount表示顶点的个数(必须为三的倍数)
vertexFormat表示顶点的格式(第七讲有介绍)
下面我们看一个利用TriangleArray的例子,例子里有九个点。

--1-- --4-- --7--

--0-- --3-- --6--

--2-- --5-- --8--

//triShape1.java
import javax.media.j3d.*;

public class triShape1 extends Shape3D {

private float vert[] = {
-.8f , .0f ,0.0f,
-.4f , .8f ,0.0f,
-.4f , -.8f,0.0f,
-.2f , .0f ,0.0f,
0.2f , .8f ,0.0f,
0.2f , -.8f,0.0f,
0.4f , .0f ,0.0f,
0.8f , .8f ,0.0f,
0.8f , -.8f,0.0f,
};

private float color[] = {
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
1.0f,0.0f,0.3f,
0.0f,1.0f,0.3f,
0.3f,0.8f,0.0f,
0.0f,0.5f,1.0f,
0.5f,0.0f,1.0f,
0.0f,0.8f,2.0f,
};

public triShape1() {
TriangleArray tri = new TriangleArray(9,
TriangleArray.COORDINATES|TriangleArray.COLOR_3);
tri.setCoordinates(0,vert);
tri.setColors(0,color);
this.setGeometry(tri);
}
}

//end of triShape1.java

从程序运行结果可以得知,TriangleArray将一个顶点数组的
每三个数组合在一个,生成一个面,因而vertexCount的点数必须
为三的倍数。triShape1.java显示的结果是三个三角形构成的面。
由于对每一个顶点都定义了颜色,因而程序显示的是色彩变化的
三角形,且只能从正面看到,反面看不到,这和VRML程序生成面
的效果完全一样。在VRML程序中,为了使一个面的正反方向都可见,
可以在IndexedFaceSet节点中将solid字段设为FALSE。
下面是和triShape1.java相对应的VRML程序,为了使面的正反
都可见,我们将IndexedFaceSet的solid字段设为FALSE。

#VRML V2.0 utf8
DEF T Transform{
children Shape {
geometry IndexedFaceSet {
solid FALSE
coord Coordinate {
point [ -.8 , .0 ,0.0,
-.4 , .8 ,0.0,
-.4 , -.8,0.0,
-.2 , .0 ,0.0,
0.2 , .8 ,0.0,
0.2 , -.8,0.0,
0.4 , .0 ,0.0,
0.8 , .8 ,0.0,
0.8 , -.8,0.0,]}
coordIndex [0 1 2 -1 3 4 5 -1 6 7 8]
color Color{
color [ 0.0,0.5,1.0,
0.5,0.0,1.0,
0.0,0.8,2.0,
1.0,0.0,0.3,
0.0,1.0,0.3,
0.3,0.8,0.0
0.0,0.5,1.0,
0.5,0.0,1.0,
0.0,0.8,2.0,]}
}}}

DEF TS TimeSensor{
cycleInterval 4
loop TRUE}

DEF OI OrientationInterpolator{
key [0 .25 .5 .75 1]
keyValue [0 1 0 1, 0 1 0 1.57, 0 1 0 3.14
0 1 0 4.71 0 1 0 6.28]}

ROUTE TS.fraction TO OI.fraction
ROUTE OI.value TO T.rotation

如何将triShape1.java显示的内容从色彩变化及单面显示
变为每一个面只有一种颜色及双面显示?
VRML程序中我们可以将IndexedFaceSet节点的
colorPerVertex设为FALSE,并提供一个
colorIndex字段以挑选颜色数组中的颜色。
在JAVA3D程序中我们有一个笨办法处理颜色问题,即将
每一个面的三个点的颜色均设定为相同的数值,因为
TriangleArray对象没有
setColorIndices(0,index)
这一方法,故不能从颜色组中选用颜色。
至于双面显示问题,我们可以通过设定PolygonAttributes
对象的setCullFace(int cullFace)方法来获得,cullFace可以为:
CULL_NONE
CULL_FRONT
CULL_BACK
通过选择其中之一,来获得前面、后面及双面的显示效果,缺省
值为只显示前面。
另外,我们还可以通过设定PolygonAttributes对象的
setPolygonMode(int polygonMode)方法使面用顶点或端线来代替。
polygonMode有下列三种选择:
POLYGON_POINT
POLYGON_LINE
POLYGON_FILL
缺省值为POLYGON_FILL。
triShape2.java显示的是前后都可以看得见的面,每一个面
都有唯一的颜色。
//triShape2.java

import javax.media.j3d.*;

public class triShape2 extends Shape3D {
private float vert[] = {
-.8f , .0f ,0.0f,
-.4f , .8f ,0.0f,
-.4f , -.8f,0.0f,
-.2f , .0f ,0.0f,
0.2f , .8f ,0.0f,
0.2f , -.8f,0.0f,
0.4f , .0f ,0.0f,
0.8f , .8f ,0.0f,
0.8f , -.8f,0.0f,
};

private float color[] = {
1.0f,0.0f,0.0f,
1.0f,0.0f,0.0f,
1.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,
0.0f,1.0f,0.0f,
0.0f,1.0f,0.0f,
0.0f,0.0f,1.0f,
0.0f,0.0f,1.0f,
0.0f,0.0f,1.0f,
};

public triShape2() {
TriangleArray tri = new TriangleArray(9,
TriangleArray.COORDINATES|TriangleArray.COLOR_3);
tri.setCoordinates(0,vert);
tri.setColors(0,color);

PolygonAttributes pa = new PolygonAttributes();
pa.setCullFace(PolygonAttributes.CULL_NONE);

//pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
// 增加这一行会使面由线代替

Appearance ap = new Appearance();
ap.setPolygonAttributes(pa);
this.setGeometry(tri);
this.setAppearance(ap);
}
}

三. QuadArray生成的面
QuadArray对象的定义如下:
QuadArray (int vertexCount, int vertexFormat )
这里,每一个参数的含义都和TriangleArray里的一样。
QuadArray可用来生成平面,构成平面的顶点的数目必须为
4的倍数。
下面我们来看一个利用QuadArray的例子,例子里有8个点,
程序运行后生成了两个长方形面。
//quadShape1.java

import javax.media.j3d.*;

public class quadShape1 extends Shape3D {
private float vert[] = {
-.6f , .8f ,0.0f,
-.6f , -.8f,0.0f,
-0.2f , -.6f,0.0f,
-0.2f , .6f ,0.0f,
0.2f , .8f ,0.0f,
0.6f , .8f, 0.0f,
0.6f , -.8f, 0.0f,
0.2f , -.8f,0.5f,
};

private float color[] = {
1.0f,0.5f,0.0f,
1.0f,0.0f,0.5f,
1.0f,0.8f,0.0f,
5.0f,1.0f,0.0f,
0.0f,1.0f,0.5f,
0.9f,1.0f,0.0f,
0.5f,0.0f,1.0f,
0.0f,0.5f,1.0f,
};

public quadShape1() {
QuadArray tri = new QuadArray(8,
QuadArray.COORDINATES|QuadArray.COLOR_3);
tri.setCoordinates(0,vert);
tri.setColors(0,color);

this.setGeometry(tri);
}
}
//end of quadShape1.java
程序的双面显示问题及单一颜色问题的处理同上面的
triShape2.java。
编写QuadArray应用程序时,我们要注意几个问题。
首先是四点应当共面,如果不共面,程序仍然可以编译
运行,但显示的内容为两个三角面。
其次是四个点组成的面不应有凹点,这有点象VRML程序中
的Extrusion、IndexedFaceSet里的情况,当然,在VRML程序中
四个点构成的面可以有凹点,这时只需要在相应的节点内加上
一个参数:
convex TRUE
而在JAVA3D程序中,如果QuadArray生成的面有凹点时,
程序的显示结果会不正确,例如,当我们将quadShape里的顶点
坐标换为:
private float vert[] = {
-.6f , .8f , 0.0f,
-.6f , -.8f , 0.0f,
-0.2f, -.6f , 0.0f,
-0.5f, .0f , 0.0f,
0.2f , .8f , 0.0f,
0.6f , .8f , 0.0f,
0.6f , -.8f , 0.0f,
0.2f , -.8f , 0.5f,
};
时,显示的结果不确定,正面时是一个形状,转到反面时是
另一个形状。
最后一个问题是QuadArray所利用的每四个点,其坐标
位置除了要共面、凸点,四个点的旋转方向也要注意,如果
点的旋转方向是逆时针的话,其正面朝外,反之则朝内。当
我们将顶点的坐标换成下面的数据时,我们可以清楚地得出
这一结论。
private float vert[] = {
-.6f , .8f ,0.0f,
-.6f , -.8f ,0.0f,
-0.2f , -.4f ,0.0f,
-0.2f , .4f ,0.0f,
0.2f , .8f ,0.0f,
0.6f , .8f ,0.0f,
0.6f , -.8f ,0.0f,
0.2f , -.8f ,0.5f,
};


四. TriangleStripArray生成的面
TriangleStripArray对象的定义为:
TriangleStripArray ( int vertexCount , int vertexFormat,
int[] stripVertexCounts )
利用TriangleStripArray对象,我们可以生成多组三角片面,
对于每一组三角片面来说,它的头三个点生成一个面,从第四个点
开始,每一个点都和前两个点生成一个新的面。
下面的程序中,我们利用一组点,生成了两组三角片面。程序
中,顶点数组的个数为11,头一组用了7个顶点,生成了5个相连的
三角片面,后一组用了5个顶点,生成了三个相连的三角片面。
//triShape3.java
import javax.media.j3d.*;

public class triShape3 extends Shape3D {
int StripCount[] = new int[2];
private float vert[] = {
-.6f , .8f , 0.0f,
-.6f , -.8f, 0.2f,
-0.2f , .5f, -.2f,
-0.2f , -.5f , 0.2f,
0.0f , -.5f, -.2f,
0.0f , .5f , .2f,
0.2f , .0f, .0f,
0.2f , .8f , 0.3f,
0.2f , -.8f, -0.3f,
0.6f , .8f, 0.0f,
0.6f , -.8f, 0.5f,
0.8f , 0.0f , .3f
};

private float color[] = {
1.0f,0.5f,0.0f,
1.0f,0.0f,0.5f,
1.0f,0.8f,0.0f,
5.0f,1.0f,0.0f,
0.0f,1.0f,0.5f,
0.9f,1.0f,0.0f,
0.5f,0.0f,1.0f,
0.0f,0.5f,1.0f,
1.0f,0.5f,0.0f,
1.0f,0.0f,0.5f,
1.0f,0.8f,0.0f,
};

public triShape3() {
StripCount[0] = 7;
StripCount[1] = 5;
TriangleStripArray tri = new TriangleStripArray(12,
TriangleStripArray.COORDINATES|
TriangleStripArray.COLOR_3 , StripCount);
tri.setCoordinates(0,vert);
tri.setColors(0,color);
PolygonAttributes pa = new PolygonAttributes();
pa.setCullFace(PolygonAttributes.CULL_NONE);
Appearance ap = new Appearance();
ap.setPolygonAttributes(pa);
this.setGeometry(tri);
this.setAppearance(ap);
this.setGeometry(tri);
}
}
//end of triShape3.java

五. TriangleFanArray生成的面
TriangleFanArray对象的定义为:
TriangleFanArray ( int vertexCount ,int vetexFormat,
int[] stripVertexCounts )
利用TriangleFanArray对象,我们可以生成多组三角片面,每组
三角片面占用一定数量的顶点,每个组在生成三角片面时,头三个顶
点构成一个三角片面,其余的顶点和前面的顶点及每组第一个顶点生成
一个三角片面。下面的triShape4.java程序中,我们生成了两组三角
片面,头5个点生成了三个相连的三角片面,后6个点生成了四个相连的
三角片面。形状就像两把扇子,一大一小。
//triShape4.java

import javax.media.j3d.*;

public class triShape4 extends Shape3D {
int FanCount[] = new int[2];
private float vert[] = {
0.0f , 0.0f , 0.0f,
-0.3f , 0.3f , 0.02f,
-0.1f , 0.4f , -0.02f,
0.1f , 0.4f , 0.02f,
0.3f, 0.3f , -0.02f,
0.0f, -0.8f , 0.0f,
-0.6f, -0.2f, 0.02f,
-0.3f, -0.1f , -0.02f,
.0f, -0.05f, 0.02f,
.3f, -0.1f, -0.02f,
.6f, -0.2f, 0.02f
};

private float color[] = {
1.0f,0.5f,0.0f,
1.0f,0.0f,0.5f,
1.0f,0.8f,0.0f,
5.0f,1.0f,0.0f,
0.0f,1.0f,0.5f,
0.9f,1.0f,0.0f,
0.5f,0.0f,1.0f,
0.0f,0.5f,1.0f,
1.0f,0.5f,0.0f,
1.0f,0.0f,0.5f,
};

public triShape4() {
FanCount[0] = 5;
FanCount[1] = 6;
TriangleFanArray tri = new TriangleFanArray(11,
TriangleFanArray.COORDINATES|
TriangleFanArray.COLOR_3 , FanCount);
tri.setCoordinates(0,vert);
tri.setColors(0,color);
PolygonAttributes pa = new PolygonAttributes();
pa.setCullFace(PolygonAttributes.CULL_NONE);
Appearance ap = new Appearance();
ap.setPolygonAttributes(pa);
this.setGeometry(tri);
this.setAppearance(ap);
this.setGeometry(tri);
}
}
//end of triShape4.java

//end of lineShape5.java 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值