java 3D学习笔记

Java3D 运行及开发环境的安装运行Java 3D for Windows (OpenGL Version) SDK for the JDK (includes Runtime) (java3d-1_3_1-windows-i586-opengl-sdk.exe, 6.43 MB) .exe

下载链接https://java3d.dev.java.net/binary-builds-old.html


  安装好Java3D的开发环境后,下面可以通过运行一个Java3D程序,验证一下安装是否成功。
启动eclipse,新建一个项目JAVA3D,然后再建立一个包song,然后在该项目下建立一个类Hello3d
源代码如下:
package song;

import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.geometry.ColorCube;
import javax.media.j3d.BranchGroup;
public class Hello3d { public Hello3d(){
 SimpleUniverse universe = new SimpleUniverse();
 BranchGroup group = new BranchGroup(); 
 group.addChild(new ColorCube(0.3)); 
 universe.getViewingPlatform().setNominalViewingTransform();
 universe.addBranchGraph(group); 
 }
public static void main( String[] args ) {
 new Hello3d();}
} // end of class Hello3d

运行后出现下图所示表示环境安装成功

hello1.BMP

以下是基础理论的介绍觉得这篇不错:

       Java3D用其自己定义的场景图和观察模式等技术构造了3D的上层结构,实现了在Java平台使用三维技术。本文在原理上着重介绍Java3D特有的两个重要概念:场景图(Scene Graph)、观察模式(View Model)。在接口使用上的介绍分为两部分:实例说明如何使用Java3D接口;说明如何将Java3D技术与Java原有的Web技术(JSP、Serverlet)相结合,在网页上实现三维显示。

关于Java3D

 

1.1 Java3D简介

Java3D API是Sun定义的用于实现3D显示的接口。3D技术是底层的显示技术,Java3D提供了基于Java的上层接口。Java3D把OpenGL和DirectX这些底层技术包装在Java接口中。这种全新的设计使3D技术变得不再繁琐并且可以加入到J2SE、J2EE的整套架构,这些特性保证了Java3D技术强大的扩展性。

JAVA3D建立在JAVA2(JAVA1.2)基础之上,JAVA语言的简单性使JAVA3D的推广有了可能。它实现了以下三维显示能够用到的功能:

  • 生成简单或复杂的形体(也可以调用现有的三维形体)
  • 使形体具有颜色、透明效果、贴图。
  • 在三维环境中生成灯光、移动灯光。
  • 具有行为的处理判断能力(键盘、鼠标、定时等)。
  • 生成雾、背景、声音。
  • 使形体变形、移动、生成三维动画。
  • 编写非常复杂的应用程序,用于各种领域如VR(虚拟现实)。

J2SE在其标准开发包中并不提供Java3D的API,Java3D是一个独立的可选组件,可以单独下载。Java3D现在(截止到2003年5月)提供的正式版本是1.3.0,可以在 http://java.sun.com/products/java-media/3D/download.html 下载该开发工具包。

Java3D 1.3有7个不同的可下载版本:

Java3D for Windows(DirectX version)SDK for JDK(include Runtime);
Java3D for Windows(OpenGL version)SDK for JDK(include Runtime);
Java3D for Solaris/SPARC Runtime for JDK(include Runtime);
Java3D for Windows(DirectX version)Runtime for JRE;
Java3D for Windows(OpenGL version)Runtime for JRE;
Java3D for Solaris/SPARC SDK for JRE;
Java3D for Solaris Runtime 64 bit support;

其中前三个版本是开发包。第4、5、6个版本是Java3D运行期支持程序包。最后一个是支持Solaris 64 bit操作平台运行Java3D的程序包。前三个的开发包包含了各自对应的运行期支持包。Java语言本身具有跨平台特性,无论使用上述哪个版本的开发包,概念和实现代码都会保持完全一致。这里我们使用Java3D for Windows(OpenGL version)SDK for JDK(include Runtime);版本作为我们讨论、实现Java3D的工具包。注意在安装此版本SDK前要保证下列环境:

Java 2 (Runtime or SDK) version 1.3.1 或更后的版本

OpenGL 1.1 或更后的版本,并且是Microsoft支持的显卡厂商

Windows NT 4.0 only: Service Pack 3 或更后的版本(Window2000、WindowXP)

1.2 Java3D与其他三维技术的比较

JAVA3D可应用在三维动画、三维游戏、机械CAD等多个领域。但作为三维显示实现技术,它并不是唯一选择而且是一个新面孔。在Java3D之前已经存在很多三维技术,这些三维技术在实现的技术、使用的语言以及适用的情况上各有不同,我们主要介绍与Java3D又密切关系的三种技术:OpenGL、DIRECT3D、VRML

OpenGL是业界最为流行也是支持最广泛的一个底层3D技术,几乎所有的显卡厂商都在底层实现了对OpenGL的支持和优化。OpenGL同时也定义了一系列接口用于编程实现三维应用程序,但是这些接口使用C(C++)语言实现并且很复杂。掌握针对OpenGL的编程技术需要花费大量时间精力。

DIRECT3D是Microsoft公司推出的三维图形编程API,它主要应用于三维游戏的编程。众多优秀的三维游戏都是由这个接口实现。与OpenGL一样,Direct3D的实现主要使用C++语言。

VRML2.0(VRML97)自1997年12月正式成为国际标准之后,在网络上得到了广泛的应用,这是一种比BASIC、JAVASCRIPT等还要简单的语言。脚本化的语句可以编写三维动画片、三维游戏、计算机三维辅助教学。它最大的优势在于可以嵌在网页中显示,但这种简单的语言功能较弱(如目前没有形体之间的碰撞检查功能),与JAVA语言等其它高级语言的连接较难掌握,因而逐渐被淹没在竞争激烈的网络三维技术中。

表1是Java3D与其它三维技术的比较图,可以从中直观的看出他们相互间的区别:

表1:3D技术对招表

技术实现层次开发技术(难度)扩展性最适合应用领域
Java3D中层(JVM)Java(较易)J2SE标准扩展(好)网上三维显示实现…
OpenGL底层(显卡)C/C++(难)各大厂商支持(较好)三维设计软件…
Direct3D底层(操作系统)C++(较难)Windows平台(差)三维游戏…
VRML上层(网页)标记语言(容易)安装插件支持(一般)网上虚拟现实…





回页首


Java3D的场景图结构

Java3D实际上是Java语言在三维图形领域的扩展,与Java一样,Java3D有纯粹的面向对象结构。Java3D的数据结构采用的是Scene Graphs Structure(场景图),就是一些具有方向性的不对称图形组成的树状结构(图1)。

我们在一个Java3D应用程序看到的逼真三维场景从程序的角度看来,实际就是由Java3D定义的一系列的对象,这些对象不是杂乱无序,对象之间也不是毫无关系。如果想让三维图像正常显示,必须在这两点上遵循Java3D场景图的规定。观察图1,Java3D场景图的树结构由各种各样的对象组成:

在图中出现的这些对象都实现了Java3D中有重要的意义的类,从逻辑上我们将它们分为三类:

  • 根节点(Root):Virtual Universe Object
  • 节点(Node):Local Object、Branch Group Nodes、Behavior Node、Shape3D Node…
  • 叶子节点(Leaf):Appearance、Geomery..


图1:在应用中的Java3D场景图

场景图中线和线的交汇点称为节点(Node),这些节点都是Java3D类的实例(Instance of Class),节点之间的线表示各个实例之间的关系。

  • Virtual Universe是根节点,每一个场景图的Virtual Universe是唯一的。
  • 在Virtual Universe下面是Locale节点,每个程序可以有一个或多个Locale,但同时只能有一个Locale处于显示状态,就好象一个三维世界非常大,有很多个景点,但我们同时只能在一个景点进行观察。Java3D允许从一个Locale跳到另一个Locale,不过绝大多数程序只有一个Locale。
  • 每一个Locale可以拥有多个BranchGroup节点。所有三维形体的其位置信息(Transform Group Nodes)都建立在BranchGroup节点之上。
  • TransformGroup Node用来设定Shape3D在Virtual Universe中的位置。
  • Spape3D Node是三维图形节点,这个节点的实体放映在最后的显示画面中,就是三维世界中的每个形体。包括正方体、球体以及任何形状和外观的三维形体。
  • 位于场景图最下层的是两个叶子节点:三维体的外观(Appearance)和几何信息(Geometry),这两个节点定义了一个三维体的显示效果。
  • View Platform位于图1的另一个分枝上,与前面所有描述三维体的性质的概念不同,View Platform和View都是用来定义观察者的信息。

上面所列的概念很多,但是对于建立一个简单的Java3D程序,我们至少需要了解三个概念:虚拟宇宙(Virtual Universe)、场景(Locale)、坐标系统。

2.1 虚拟宇宙(Virtual Universe)

在Java3D中,虚拟宇宙被定义为结合一系列对象的三维空间。虚拟宇宙被用作最大的聚集体表现单位,同时也可被看作一个数据库。不管是在物理空间还是逻辑内容,虚拟宇宙都可以很大。实际上在大多数情况下,一个虚拟宇宙就可以满足一个应用程序所有的需求。

虚拟宇宙是各自独立的个体,原因是在任何时候一个结点对象都不能在超过一个的虚拟宇宙中存在。同样的,在一个虚拟宇宙中的结点对象也不能在其他的虚拟宇宙中可见或者与其他的对象结合。

对于一个Java3D应用程序,必须定义一个虚拟宇宙才可以在这个"宇宙"中显示三维图像。

2.2 Java3D的坐标系统

默认情况下,Java3D的坐标系统是右旋的,用方位语义学来解释就是:正y方向是本地重力的上,正x方向是水平的右,正z是这对着观察者的方向。默认的单位是米。

双精度浮点、单精度浮点甚至是定点来表示的三维坐标都足够来表示和显示丰富的3D场景。不幸的是,场景不是真实世界,更不必说整个宇宙了。如果使用单精度坐标,有可能出现下列情景:

  1. 离原点仅有一百公里的距离,被描绘得相当量子化,所能达到的最好效果就是三分之一英寸,在实际应用中这样的精度比要求的粗糙的多。
  2. 如果要缩小到一个很小的尺寸(例如表现集成电路的大小),甚至在离原点很近的地方就会出现同坐标问题。

为了支持一个大型的邻接虚拟宇宙,Java3D选择了有256位的高分辨率坐标:

Java3D高分辨率坐标由三个256位的定点数组成,分别表示x、y、z。定点被固定在第128位,并且值1.0被定义为真实的1米。这个坐标系统足够用来描述一个超过几百万光年距离的宇宙,也可以定义小于一质子大小(小于一普朗克长度)的对象。

在Java3D中,高分辨率坐标仅仅用于将更加传统的浮点坐标系统嵌入更高分辨率的底层系统。用这种方法,可以创造出一个具有任意大小和规模的在视觉上无缝的虚拟宇宙,而且可以不必担心数字上的精度。(参看表2)

一个256位的定点数还具有能够直接表示几乎任何的合理适当的单精度浮点值。

Java3D用有符号的、两位补码的256位定点数字来表示高分标率坐标。尽管Java3D保持内部高分辨率坐标表示的不透明,但用户用有八个整型变量的数组来表示256位的坐标。Java3D把数组中从索引号由0到7分别看作高分辨率坐标的从高到底位上的数。第128位上是二进制的小数点,也可以说在索引号为3和4的整数之间。高分辨率坐标的1.0就是1米。

如果是"小"的虚拟宇宙(类似于相对比例的几百米),在虚拟宇宙对象下的(0.0,0.0,0.0)点建立一个带有高分辨率坐标的Locale作为根节点就足够使用了;装入程序在装入过程中能自动构建结点,而在高分辨率坐标下的点不需要任何外部文件的直接描述。

大一些的虚拟宇宙期待被构建为有如同计算机文件那样的层次,这意味着一个根宇宙要包含由外部文件引用的嵌入虚拟宇宙。就这样,文件引用的对象(用户指定的Java3D组或高分辨率结点)定义了被读入现存虚拟宇宙的数据的位置。

表2-1:Java 3D 高分辨率坐标

Java 3D 高分辨率坐标
2n MetersUnits
87.29Universe (20 billion light years)
69.68Galaxy (100000 light years)
53.07Light year
43.43Solar system diameter
23.60Earth diameter
10.65Mile
9.97Kilometer
0.00Meter
-19.93Micron
-33.22Angstrom
-115.57Planck length

2.3 场景(Locale)

为了支持大型虚拟宇宙,Java3D提出了"Locale"的概念。Locale把高分辨率坐标作为起源。把高分辨率坐标看作精确的定位,它在高分辨率坐标的影响范围之内使用精度较低的浮点坐标指定对象的位置。

一个Locale和与它结合的高分辨率坐标一起组成了在虚拟宇宙之下的一个表现层。所有虚拟宇宙包含一个或多个高分辨率Locale。而所有其他的对象都是附加在一个Locale上的。在整个体系中,高分辨率坐标扮演的是上层的仅供翻译的转换结点。例如,附加到一个特定Locale的所有对象的坐标都会与这个Locale位置的高分辨率坐标有关。(图2)


图2:高分辨率坐标指定场景

如果一个虚拟宇宙与传统的计算机图像的概念相近,给定的虚拟宇宙可能会变得太大。所以在通常情况下最好把一个场景图看作是一个高分辨率坐标场景的子结点。

构造一个三维场景,程序员必须运行一个Java3D程序。这个Java3D应用程序必须首先创建一个虚拟宇宙对象并且至少把一个Locale对象附加之上。然后,构建出需要的场景图像,它由一个分支组结点开始并且包括至少一个观察平台对象,而场景图就是附加于这个观察平台。当一个包含场景图的观察对象被附加于一个虚拟宇宙,Java3D的渲染循环就开始工作。这样,场景就会和它的观察对象一起被绘制在画布上。

2.4 编程实现一个三维世界

这一部分描述怎样调用VirtualUniverse、Locale和HiResCoord对象的编程接口实现建立一个完整的"三维世界"。注意,这个三维世界有原点、坐标,是实现三维显示程序的第一步。

  1. VirtualUniverse对象有下列构造函数:
    public VirtualUniverse()
    

    这个函数构造了一个新的VirtualUniverse对象,这个对象可以用来创建Locale对象。

  2. Locale对象有下列构建器:
    public Locale(VirtualUniverse universe)
    public Locale(VirtualUniverse universe, int x[], int y[], int  z[])
    public Locale(VirtualUniverse universe, HiResCoord hiRes)
    

    这三个构建器在指定的VirtualUniverse中创建了一个新的高分辨率Locale对象。其中第一个形成了一个在(0.0,0.0,0.0)的Locale对象。其他的两个构建器在指定的坐标上建立了Locale对象。在第二种形式里,参数x,y,z是含八个32位整数的数组,这些数组指定了各自的高分辨率坐标。

    HiResCoord对象定义了一个使用三个高分辨率坐标的点,而每一个坐标又由三个定点数组成。每个高分辨率坐标数共有256位,第128位是二进制小数点。Java3D使用长度为八的整数数组来定义或提取一个256位的坐标值。Java3D用数组内的第一个整数来表示高32位,最后一个整数来表示低32位。

  3. HiResCoord 对象有以下的构建函数:
    public HiResCoord(int x[], int y[], int z[])
    public HiResCoord(HiResCoord hc)
    public HiResCoord()
    

    第一个构造函数从输入的三个长度为八的整数数组生成高分辨率坐标。整数数组定义了与其同名坐标对象的值。第二个构造函数通过复制另外一个坐标创建一个新的坐标。第三个构造函数创建了一个值为(0.0,0.0,0.0)的坐标。

    所有Java3D程序都会首先建立VirtualUniverse和Locale对象,也就是说都会包含表3所示的代码。为了方便使用,Java3D为最原始的VirtualUniverse创建了几个子类:SimpleUniverse 、ConfiguredUniverse,这些子类保证了可以将三维图像轻易的在通过Canvas3D的对象在Applet或Frame中显示。其中最常用到的是SimpleUnivese对象,这个类位于包com.sun.j3d.utils.universe中。

    u = new SimpleUniverse(v,viewer);
    u.getViewingPlatform();
    ViewingPlatform viewingPlatform = u.getViewingPlatform();
    

    例1 初始化VirtualUniverse和Locale代码





回页首


Java3D的观察模式

Java3D引入了一种新的观察模式,这种模式使Java编写的显示效果符合"编写一次,随处运行"的原则。Java3D还把这种功能推广到显示设备或六等级自由度输入外部设备,例如跟踪摄像头。这种新的观察模式的"一次编写,随处观察"的特性意味着用Java3D观察模式编写的应用程序和Applet可以广泛应用于各种各样的显示设备。在不修改场景图的条件下,图像可以在包括标准电脑显示、多放射显示空间和安装摄像头设备的显示设备上被渲染。这也意味着在不需要修改场景图的情况下,同一个应用程序既能够渲染立体景象,还能通过摄像头的输入控制渲染过的观察图。

Java3D的观察模式通过完全分离虚拟和现实世界来实现这种多功能性。这种模式区分了以下两种情况:

  1. 一个应用程序通过控制观察平台的位置和方向在虚拟宇宙中对一个观察台对象(ViewPlatform)定位、定向和设定比例尺;
  2. 渲染器使用已知位置和方向计算出要使用的观察对象,对终端用户物理环境的描述确定用户在物理环境中的位置和方向。

3.1 为什么使用一个新的模式

在底层的编程接口中可以找到基于照相机的观察模式,开发者通过它可以控制所有渲染图的参数。它可以应付处理常规的应用程序,但是处理有更广阔的适应性的系统的时候就显得力不从心,这些系统包括:把整个世界作为一个单元装入和显示的观察器或浏览器、可供终端用户观察、操纵、显示、甚至与虚拟世界交互的系统。

基于照相机的观察模式仿效在虚拟世界中放置一个照相机而不是一个人。开发者必须持续重新配置一个照相机来模拟"在虚拟世界中有一个人"。

Java3D观察模式直接和跟踪摄像头结合。在有摄像头的情况下,用户会有好像他们是真实的存在在那个虚拟世界的错觉,而开发者可以不做任何附加的工作就可以为用户带来这种效果。

在没有摄像头并且只是用来表现一个单一的标准显示的情况下,Java3D观察模式表现得更像传统的基于照相机的观察模式,只是加上了能够产生立体透视图的功能。

在一个需要由物理环境规定一些观察参数的系统中,让应用程序来控制所有的观察参数并不合理。

例子就是:一个带有摄像头的显示设备可以用其光系统直接决定应用程序中的观察领域。不同的设备有不同的光系统,程序开发者硬绑定这样的参数或允许终端用户改变这样的参数都是不合理的。

另外一个例子是:一个可以由用户当前的头部位置自动计算出观察参数的系统。只有一个对世界的说明和一条预先定义的轨迹可能不会严密的定义一个终端对象的观察。对于有摄像头设备用户,他们可能会期待在沿着一条固定的路线前进的时候能够看到他们左右两旁的物体。就好像在一个游乐场中,游客乘坐观光车按照固定的路线参观游乐场,但是在这过程中,游客可以持续转动他们的头。

由于依靠终端用户的具体物理环境,观察的各个参数,尤其是观察和投影的基体变化很大。影响观察和投影基体的因素包括显示设备的物理尺寸,显示设备的安装方法(在用户的桌面或用户的头顶上),计算机是否知道用户的头在三维空间的位置,头顶装置真实的观察领域,显示设备上每平方英寸的像素数,还有其他类似的参数。

Java3D建立的观察模式完全可以满足上述所有的需求。

3.2 分离物理和虚拟

Java3D分离了虚拟环境和物理环境:应用程序在虚拟环境中按照一定关系放置对象,而用户存在在物理环境之中,看计算机显示器并操纵输入设备。

Java3D也定义了用户所在的物理世界和图像程序所在的虚拟世界之间最基本的通信。这种"物理到虚拟世界"的通信定义了一个单一的公共空间,在这个空间中用户的动作影响虚拟世界中的对象,而在虚拟世界中的任何活动都会影响最终用户的观察。

虚拟世界是虚拟对象存在的通用空间。虚拟世界的坐标系统相对于每个Locale对象的高分辨率坐标存在,它定义了所有附加于这个Locale的虚拟世界坐标原点。包含当前活动的观察平台对象的Local定义了用来绘图的虚拟世界坐标。Java3D最后把所有图像单元的坐标转换到通用的虚拟世界空间中。

物理世界就是指真实的世界。这是真实的用户存在和移动他(她)的头和手的空间。这也是使用任何物理追踪仪可以定义他们的局部坐标和几个标准的坐标系统被描述的空间。

物理世界是一个空间,而不是Java3D不同的程序执行实例之间的通用坐标系统。所以当两个不同的计算机在世界上两个不同的地方同时运行同一个程序的时候,Java3D中没有直接来描述它们在物理世界坐标系统中相对位置的机制。因为标准问题,当地的跟踪系统仅仅定义了特定的Java3D应用程序实例的物理坐标系统。

3.3 Java3D中用来定义观察的对象

Java3D通过几个对象来发布它的观察模式。特别是View对象和与它相关的组件对象:PhysicalBody对象、PhysicalEnvironment对象、Canvas3D对象、Screen3D对象。图3描述了View对象的中心角色和组件对象的辅助角色。

观察有关的对象都在图3中,它们起的作用如下:

ViewPlatform(观察平台):一个view用一个叶子结点来在场景图为自己定位。观察平台的起始结点指定了它的位置、方向和在虚拟世界中的比例尺。

View(观察):主要的观察对象包含了很多观察的状态。

Canvas3D:抽象窗口工具箱中画布对象的3D版本。它描绘了一个可以让Java3D在上面画图像的窗口。它包括了一个对Screen3D对象的引用和描述一个Canvas3D要用到的尺寸、形状和位置信息。

Screen3D:一个包含描述显示荧屏物理属性信息的对象。Java3D把显示荧屏信息分别放在单独的对象中,这样做可以防止在每一个Canvas3D对象中不同的显示屏幕信息共享一个屏幕。

PhysicalBody:一个包含刻度信息的对象,它描述了用户的物理身体。

PhysicalEnvironment:一个包含刻度信息的对象,它描述了物理世界。主要的信息描述了环境的六自由度硬件。


图3:View和它的组件对象以及它们的相互联系

这些对象一起描述观察的几何体胜于明白的提供观察或投影基体。Java3D的表现工具用这个信息来构造适合的观察和投影基体。这些观察对象的几何中心为产生一个观察提供了更大的弹性,这种弹性需要支持可以选择的显示配置。

3.4 ViewPlatform: 在虚拟世界中的位置

一个Viewplatform结点定义了一个坐标系统。这样,在虚拟世界中就有了一个有原点或参考点的参考系。观察平台是一个附加在观察对象的点并且作为决定描绘工具观察的基础。

图4表示了一个场景图的一部分,它包括一个观察平台结点。直接在观察平台之上的结点决定了它在虚拟世界中的位置和方向。应用程序和或行为通过修改直接在观察平台之上任何与TransformGroup结点结合的Tramsform3D对象可以在虚拟世界中任意移动VierPlatform。一个简单的应用程序可能直接在一个观察平台上定义一个TransformGroup结点。

一个虚拟宇宙可能有很多不同的观察平台,但是一个特定的View对象只能附加于一个单一的观察平台之上。这样,每个画在Canvas3D上的图画都是从一个单一的观察平台开始。


图4:包含观察平台的一部分场景图

3.5 如何在虚拟世界中移动

应用程序通过修改观察平台的上级TransformGroup在虚拟世界中航行。修改一个观察平台的位置和方向的应用程序的例子包括:浏览器、提供航行控制的阅读器、做建筑预设计的程序、甚至是搜寻和毁坏游戏。

控制观察平台对象能产生很有趣和有用的结果。我们可以定义一个简单的场景图,这个程序的目的是在窗口的正中画了一个对象并且绕自己的中心转动。

我们不管在中心的对象,而让ViewPlatform在虚拟世界中绕圈。如果形体结点包括一个地球模型,这个程序可能产生一个类似于绕地球的远航员观察对象。

如果在这个世界中加入更多的对象,这个场景图允许经由行为结点来浏览整个虚拟世界。


图5:一个由观察控制的简单场景图

应用程序和动作通过TransformGroup的可访问方法操纵它。这些方法允许应用程序得到和设置组结点的Transform3D对象。Transform3D结点有setTransform和getTransform两个方法。

3.6 加载于喜欢的地方

一个场景图可能包括多个观察平台对象。如果用户把一个观察对象从一个观察平台分离,然后把这个观察对象附加到另外一个不同的观察平台上。显示屏上的图像现在就要从新的观察平台上的观察点画图了。

在Java3D的绘图机制中,真实的观察由当前附加观察平台的观察附加策略决定。观察平台定义了设置和得到观察附加策略的方法:

public void setViewAttachPolicy(int policy)
public int getViewAttachPolicy()

这些方法设置和得到在虚拟世界策略中的共存中心。默认的附加策略是View.NOMINAL_HEAD。观察平台的附加观察策略决定了Java3D怎样在观察平台中放置出射点。这个策略可以是以下的几个值之一:

View.NOMINAL_HEAD:保证终端用户在物理世界名义上的眼睛位置对应于在虚拟世界中虚拟眼睛的位置。本质上,这个策略告诉Java3D要用同一种方法把虚拟出射点和观察平台原点及物理出射点和物理世界的原点相对放置。物理世界中出射点的方向和位置与原点的背离会产生相应的虚拟出射点的方向和位置在虚拟世界中的背离。

View.NOMINAL_FEET:保证终端用户的虚拟世界中的脚一直接触虚拟地面,这个策略告诉Java3D要以这种约束计算物理-虚拟世界的通信。为达到之一目的,Java3D通过移动终端用户的眼睛位置和物理高度。Java3D用在PhysicalBody对象中的nominalEyeHeightFromGround参数来执行这个计算。

View.NOMINAL_SCREEN:允许应用程序总是拥有一个和"感兴趣的点"保持"可观察"距离的出射点。这个策略也决定了Java3D计算"物理到虚拟世界"通信的方法。这个方法保证程序根据PhysicalBody对象定义nominalEyeOffsetFromNominalScreen参数来设置虚拟出射点与要表现的点之间的距离。

3.7 在三维世界中建立、移动观察点

形体移动的实现向来都是三维实现的难点和复杂之处,传统三维技术的实现多是注重模拟三维物体的真实移动。而Java3D除了提供传统的方案,还可以在一个存在的三维世界中移动一个观察点,借助观察点的移动模拟物体的移动。如同物理所学的切割磁力线发电,转子和静子本来就是一对可以互逆的对象,结果都是把动能转化为电能。例2的代码显示了在Virtual Universe中建立Viewer、ViewPlatForm、和如何通过绑定OrbitBehavior实现移动ViewPlatform。


例2 建立、移动观察点代码

setLayout(new BorderLayout());
Canvas3D c = new Canvas3D(null); 
add("Center", c); 
Viewer viewer = new Viewer(c);
Vector3d viewpoint = new Vector3d(0.0,0.0,2.41); 
//初始观察点位置        
Transform3D t = new Transform3D();   
t.set(viewpoint);        
ViewingPlatform v = new ViewingPlatform( );       
v.getViewPlatformTransform().setTransform(t);
u = new SimpleUniverse(v,viewer);        
u.getViewingPlatform();        
ViewingPlatform viewingPlatform = u.getViewingPlatform();
OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL);	  
BoundingSphere bounds =	 
new BoundiBoundingSpherengSphere(new Point3d(0.0, 0.0, 0.0), 100.0);	   
orbit.setSchedulingBounds(bounds);	  
viewingPlatform.setViewPlatformBehavior(orbit);       
….





回页首


将Java3D与Java平台技术的结合

Java3D可以很容易的与Java平台的其他技术相结合,如Applet、JSP、Serverlet、JDBC、EJB等。100%的纯Java实现是Java3D可以与如此多的Java平台技术结合的根本原因:

  1. 同是Java平台保证Java3D可以在Applet中实现;
  2. Applet使Java3D可以轻易的在网页中显示;
  3. JSP、Serverlet技术保证将动态网页技术用于Java3D显示;
  4. Serverlet本身就是J2EE平台的核心技术,这使得Java3D可以搭建于J2EE平台。更可以使用所有J2EE的其他技术:JDBC、EJB、JMS…

4.1 在网页上显示3D图形

Java3D一个最大的特性是可以使用Applet作为显示容器,例3和例4的代码分别显示了如何在Applet中显示3D图形和在网页文件中(HTML)嵌入该Applet。


例3 Applet实现Java3D

Public class HelloUniverse extends Applet { 
private SimpleUniverse u = null;   
public BranchGroup createSceneGraph() {	
BranchGroup objRoot = new BranchGroup();    
….	
return objRoot;  
}   
public void init() {
Canvas3D c = new Canvas3D(config);	
add("Center", c);   
….
u.addBranchGraph(scene);   
}  
….
}


例4 在网页嵌入显示3D Applet

<HTML>
<HEAD>
<TITLE>Hello, Universe!</TITLE>
</HEAD>
<BODY BGCOLOR="#000000">
<applet align=middle code="HelloUniverse.class" width=256 height=256>
</applet>
</BODY>
</HTML>

4.2 动态网页技术与Java3D

通过Jsp和Serverlet,可以使Java3D在网页中"动"起来。虽然Java3D本身就有三维动画的功能,但是这里的"动"指得是赋予了程序编写人员对Java3D动态的控制能力。改造上面的HelloUniverse,例5 的jsp代码可以实现控制旋转的正方体大小的功能。通过每次Random对象生成的随机数,立方体的大小也是随即改变,这段程执行的效果,如图6、7所示。


例5实现可以动态调整三位物体大小的jsp代码

<%@ page contentType="text/html; charset=GBK" %>
<HTML>
<HEAD>
<TITLE>Hello, Universe!</TITLE>
</HEAD>
<BODY BGCOLOR="#000000">
<%int i=300;%>
<%float j=i*(new java.util.Random()).nextFloat();%>
<applet align=middle code="HelloUniverse.class" width=<%=j%> height=<%=j%>>
</applet>
</BODY>
</HTML>


图6 Jsp显示效果1


图6 Jsp显示效果2

4.3 J2EE平台对Java3D的支持

上面的例子只是通过动态设定Applet大小来控制Java3D的显示,实际上可应通过更多的方法实现Jsp、Serverlet对Java3D显示效果的控制,甚至可以将Java3D置于J2EE平台的显示层,实现对EJB、JDBC的调用。

实现Java3D利用J2EE平台资源的方法很多,甚至可以直接在Java3D的实现类中直接调用EJB。但是从J2EE平台的设计模式出发,把对EJB调用放到Jsp中,而将返回的结果作为参数传入实现Java3D的Applet类中是一个更好的模式。具体代码见例6。


例6调用EJB作为Java3D参数代码

<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="javax.ejb.*" %>
<%@ page import="java.rmi.*"%>
<%@ page import="javax.rmi.PortableRemoteObject"%>
<%@ page import="javax.naming.*"%>
<%@ page import="java.util.Properties"%>
<HTML>
<HEAD>
<TITLE>Hello, Universe!</TITLE>
</HEAD>
<BODY BGCOLOR="#000000">
<% try{   
Properties properties = null;  
Context context = new InitialContext(properties);   
Object ref = context.lookup("Customer");   
EJBHome home= (EJBHome)PortableRemoteObject.narrow(ref,CustomerHome.class);  
CustomerHome home=(CustomerHome)home;    
Customer customer= home.findByPrimaryKey("1000000");   
String data = customer.getData();
}
catch(Exception e){e.printStackTrace();}
%>
<applet align=middle code="HelloUniverse.class" width=200 height=200>
<param name="Data" value=<%=data%>>
</applet>
</BODY>
</HTML>

上面的代码首先访问JNDI名为"Customer"的EJB,然后将返回值作为参数传入实现Java3D的Applet。

Java3D与J2EE是相互支持的关系:Java3D丰富、强化了J2EE的显示模式,使略显枯燥的J2EE客户端光鲜多彩;J2EE平台为Java3D提供了支持,功能强大的Server端处理能力为三维显示所需的复杂计算和大数据量提供了有力的支持。

我们可以想象下面的两幅场景:

  • 通讯卫星将全国所有道路、建筑信息录入大型数据库;EJB实现应用逻辑并将之部署到AppServer上;所有支持JVM的手机、PDA、车载GPS可以通过调用EJB显示与真实世界一模一样的周围环境。
  • 地震局根据实际勘测到的地表等高线信息绘制二维矢量图,存为FDX文件;将二维图像转为三维实现的复杂算法放到EJB中实现;Jsp页面调用EJB后可以在Appet上实现三维GIS的显示。

有了Java3D和Java,这一切都不只是梦想。



参考资料



作者简介

 

钟家豪
目前在中国广东从事金融领域的软件开发,主要致力于J2EE、XML、WebService、数据库方面的研究。对企业软件架构搭建、e-commerce以及企业EAI技术实现很感兴趣,可以通过 judson78@yahoo.com.cn 与他联系。


 

陈漠
目前致力于J2EE、关系数据库建模和系统测试方面的研究。对图像处理和三维显示技术有多年经验。可以通过 Maggie_c2008@hotmail.com与她联系。


下在为魔方源码比较复杂

//存为MoFang.java

import java.applet.Applet ;
import java.awt.*;
import com.sun.j3d.utils.applet.MainFrame ;
import java.awt.BorderLayout ;
import com.sun.j3d.utils.universe.SimpleUniverse ;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.behaviors.keyboard.*;
import com.sun.j3d.utils.picking.behaviors.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.image.TextureLoader ;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame.*;
import javax.swing.*;

class mySimpleUniverse extends Applet
{
    BranchGroup createSceneGraph(Canvas3D canvas)

    {


        //System.out.print("**1**");

        //创建变换组,无用的t3D
        Transform3D t3d=new Transform3D();
        TransformGroup trans=new TransformGroup(t3d);
        trans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        trans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        //创建分枝组
        BranchGroup objRoot=new BranchGroup();

        //测试
        //SomeShape3D.book3D( this, trans);
        SomeShape3D.addText3DDonghua(trans,"魔方",new Point3f(-7.0f,6.0f,6.0f),0.1f,new Color3f(1.0f,0.0f,0.0f),1);

        //初始化数据结构
        System.out.println("/n/n载入方块,并向变换组中加入每个方块的坐标系和方块...");
        for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
        for(int k=0;k<=2;k++)
        {
         int[] p;
         p=Position.getPxyzFromPositionAy(i,j,k,MoFang.positionArray);
            MoFang.blockArray[i][j][k]=new Block(i,j,k,p[0],p[1],p[2],trans,t3d,objRoot,this);
        }
        System.out.println("加入每个方块的坐标系和方块,完成./n");

        //创建大坐标轴,自动加到主坐标系
        SomeShape3D.zuoBiaoZhuBigXShape3D(trans);
        SomeShape3D.zuoBiaoZhuBigYShape3D(trans);
        SomeShape3D.zuoBiaoZhuBigZShape3D(trans);

        //创建边界对象
        BoundingSphere bounds=new BoundingSphere(new Point3d(0.0,0.0,0.0),100);

        //创建鼠标pick行为,加到分支组objRoot
        PickRotateBehavior pickRotate=new PickRotateBehavior(objRoot,canvas,bounds);
        PickTranslateBehavior pickTranslate=new PickTranslateBehavior(objRoot,canvas,bounds);
        PickZoomBehavior pickZoom=new PickZoomBehavior(objRoot,canvas,bounds);
        //objRoot.addChild(pickRotate);
        objRoot.addChild(pickTranslate);
        //objRoot.addChild(pickZoom);

        //创建鼠标旋转行为
        MouseRotate behavior=new MouseRotate();
        behavior.setTransformGroup(trans);
        behavior.setSchedulingBounds(bounds);

        //创建鼠标平移行为
        //MouseTranslate myMouseRotate=new MouseTranslate();
        //myMouseRotate.setTransformGroup(trans);
        //myMouseRotate.setSchedulingBounds(bounds);

        //创建鼠标缩放行为
        MouseZoom myMouseZoom=new MouseZoom();
        myMouseZoom.setTransformGroup(trans);
        myMouseZoom.setSchedulingBounds(bounds);

        //创建键盘默认行为
        KeyNavigatorBehavior keyNavBeh=new KeyNavigatorBehavior(trans);
        keyNavBeh.setSchedulingBounds(bounds);
        objRoot.addChild(keyNavBeh);

        //白色背景
        Background bg=new Background(new Color3f(0.0f,0.0f,0.0f));
        bg.setApplicationBounds(bounds);
        objRoot.addChild(bg);

        //创建带材质的背景
        //TextureLoader bgTexture=new TextureLoader("bg3.jpg",this);
        //Background bg=new Background(bgTexture.getImage());
        //bg.setApplicationBounds(bounds);
        //trans.addChild(shape1);//3D物体 加到 变换组
        //trans.addChild(shape2);//3D物体 加到 变换组
        objRoot.addChild(trans);
        //变换组 加到 分枝组
        objRoot.addChild(behavior);
        //鼠标行为  加到 分枝组
        //objRoot.addChild(myMouseRotate);
        //objRoot.addChild(myMouseZoom);
        //objRoot.addChild(bg);//背景 加到 分枝组
        //编译
        objRoot.compile();
        //回送创建好的带3D物体的分枝组
        return objRoot ;

    }
    mySimpleUniverse()
    {
        //创建带控制的画布
        GraphicsConfiguration config=SimpleUniverse.getPreferredConfiguration();
        Canvas3D c=new Canvas3D(config);
        //创建以画布为依托的简单场景图对象,没有多个Locale
        SimpleUniverse u=new SimpleUniverse(c);
        u.getViewingPlatform().setNominalViewingTransform();
        //创建分支组对象
        BranchGroup scene=createSceneGraph(c);
        //组装,分支组 对象加到 场景图
        u.addBranchGraph(scene);

        //带场景图的画布 加到 本applet中
        setLayout(new BorderLayout());
        add("Center",c);
    }

    //测试码
    //public static void main(String aregs[])
    //{new MainFrame(new mySimpleUniverse(),200,200);//加applet到应用程序界面
    //}
}

class SomeShape3D
{
    public static float zuoBiaoZhouSmallDingDian=0.09f;//小坐标顶点位置
    public static float zuoBiaoZhouSmallDingXi=0.02f;//小坐标顶点伞的半径
    public static float zuoBiaoZhouSmallDingChang=0.07f;//小坐标顶点伞的长度
    public static float zuoBiaoZhouSmallWeiDian=-0.09f;//小坐标尾巴的位置
    public static float zuoBiaoZhouBigDingDian=1.0f;//大坐标顶点位置
    public static float zuoBiaoZhouBigDingXi=0.04f;//大坐标顶点伞的半径
    public static float zuoBiaoZhouBigDingChang=0.8f;//大坐标顶点伞的长度
    public static float zuoBiaoZhouBigWeiDian=-1.0f;//大坐标尾巴的位置
    public static float fangKuaiBanJing=0.18f;//每个方块的半径
    public static void zuoBiaoZhuBigXShape3D(TransformGroup trans)
    {


        //创建大坐标轴对象

        int i ;
        float x1,x2,y1,y2,z1,z2 ;

         SomeShape3D.addText3DDonghua(trans,"X",new Point3f(zuoBiaoZhouBigDingDian*10,0.0f,0.0f),0.1f,Block.mianColor[0],0);

        Point3f[]vert=new Point3f[41];
        Color3f[]colors=new Color3f[41];
        for(i=0;i<27;i++)
        {
            if(i==0)
            {
                vert[i]=new Point3f(zuoBiaoZhouBigDingDian,0.0f,0.0f);
                colors[i]=Block.mianColor[0];
            }
            else
            {
                z1=(float)(zuoBiaoZhouBigDingXi*Math.cos(i*2*Math.PI/25));
                x1=zuoBiaoZhouBigDingChang;
                y1=(float)(zuoBiaoZhouBigDingXi*Math.sin(i*2*Math.PI/25));
                vert[i]=new Point3f(x1,y1,z1);
                colors[i]=Block.mianColor[0];
            }
        }

        for(i=0;i<14;i++)
        {
            if(i==0)
            {
                vert[27+i]=new Point3f(zuoBiaoZhouBigDingDian,0.0f,0.0f);
                colors[27+i]=Block.mianColor[0];
            }
            else
            {
                z1=(float)(0.01f*Math.cos(i*2*Math.PI/12));
                x1=zuoBiaoZhouBigWeiDian ;
                y1=(float)(0.01f*Math.sin(i*2*Math.PI/12));
                vert[27+i]=new Point3f(x1,y1,z1);
                colors[27+i]=Block.mianColor[1];
            }
        }
        int count[]=new int[2];
        count[0]=27 ;
        count[1]=14 ;

        TriangleFanArray tri=new TriangleFanArray(
        vert.length,
        TriangleFanArray.COORDINATES|TriangleStripArray.COLOR_3,
        count
        );
        tri.setCoordinates(0,vert);
        tri.setColors(0,colors);


        Appearance app=new Appearance();

        PolygonAttributes polyAttrib=new PolygonAttributes();
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        //polyAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        app.setPolygonAttributes(polyAttrib);

        Shape3D shape=new Shape3D(tri,app);
        System.out.print("zuoBiaoZhuBigX 创建 完成/n");

        trans.addChild(shape);


        //到这里,大坐标轴对象创建完成


    }
    public static   void zuoBiaoZhuBigYShape3D(TransformGroup trans)
    {


        //创建大坐标轴对象

        int i ;
        float x1,x2,y1,y2,z1,z2 ;

        SomeShape3D.addText3DDonghua(trans,"Y",new Point3f(-1.0f,zuoBiaoZhouBigDingDian*10,0.0f),0.1f,Block.mianColor[2],0);

        Point3f[]vert=new Point3f[41];
        Color3f[]colors=new Color3f[41];
        for(i=0;i<27;i++)
        {
            if(i==0)
            {
                vert[i]=new Point3f(0.0f,zuoBiaoZhouBigDingDian,0.0f);
                colors[i]=Block.mianColor[2];
            }
            else
            {
                x1=(float)(zuoBiaoZhouBigDingXi*Math.cos(i*2*Math.PI/25));
                y1=zuoBiaoZhouBigDingChang;
                z1=(float)(zuoBiaoZhouBigDingXi*Math.sin(i*2*Math.PI/25));
                vert[i]=new Point3f(x1,y1,z1);
                colors[i]=Block.mianColor[2];
            }
        }

        for(i=0;i<14;i++)
        {
            if(i==0)
            {
                vert[27+i]=new Point3f(0.0f,zuoBiaoZhouBigDingDian,0.0f);
                colors[27+i]=Block.mianColor[2];
            }
            else
            {
                x1=(float)(0.01f*Math.cos(i*2*Math.PI/12));
                y1=zuoBiaoZhouBigWeiDian ;
                z1=(float)(0.01f*Math.sin(i*2*Math.PI/12));
                vert[27+i]=new Point3f(x1,y1,z1);
                colors[27+i]=Block.mianColor[3];
            }
        }
        int count[]=new int[2];
        count[0]=27 ;
        count[1]=14 ;

        TriangleFanArray tri=new TriangleFanArray(
        vert.length,
        TriangleFanArray.COORDINATES|TriangleStripArray.COLOR_3,
        count
        );
        tri.setCoordinates(0,vert);
        tri.setColors(0,colors);
        Appearance app=new Appearance();

        PolygonAttributes polyAttrib=new PolygonAttributes();
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        //polyAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        app.setPolygonAttributes(polyAttrib);

        Shape3D shape=new Shape3D(tri,app);

        System.out.print("zuoBiaoZhuBigY 创建 完成/n");

        trans.addChild(shape);


        //到这里,大坐标轴对象创建完成


    }

    public static  void  zuoBiaoZhuBigZShape3D(TransformGroup trans)
    {


        //创建大坐标轴对象

        int i ;
        float x1,x2,y1,y2,z1,z2 ;
 SomeShape3D.addText3DDonghua(trans,"Z",new Point3f(-1.0f,0.0f,zuoBiaoZhouBigDingDian*10),0.1f,Block.mianColor[4],0);

        Point3f[]vert=new Point3f[41];
        Color3f[]colors=new Color3f[41];
        for(i=0;i<27;i++)
        {
            if(i==0)
            {
                vert[i]=new Point3f(0.0f,0.0f,zuoBiaoZhouBigDingDian);
                colors[i]=Block.mianColor[4];
            }
            else
            {
                y1=(float)(zuoBiaoZhouBigDingXi*Math.cos(i*2*Math.PI/25));
                z1=zuoBiaoZhouBigDingChang;
                x1=(float)(zuoBiaoZhouBigDingXi*Math.sin(i*2*Math.PI/25));
                vert[i]=new Point3f(x1,y1,z1);
                colors[i]=Block.mianColor[4];
            }
        }

        for(i=0;i<14;i++)
        {
            if(i==0)
            {
                vert[27+i]=new Point3f(0.0f,0.0f,zuoBiaoZhouBigDingDian);
                colors[27+i]=Block.mianColor[4];
            }
            else
            {
                y1=(float)(0.01f*Math.cos(i*2*Math.PI/12));
                z1=zuoBiaoZhouBigWeiDian ;
                x1=(float)(0.01f*Math.sin(i*2*Math.PI/12));
                vert[27+i]=new Point3f(x1,y1,z1);
                colors[27+i]=Block.mianColor[5];
            }
        }
        int count[]=new int[2];
        count[0]=27 ;
        count[1]=14 ;

        TriangleFanArray tri=new TriangleFanArray(
        vert.length,
        TriangleFanArray.COORDINATES|TriangleStripArray.COLOR_3,
        count
        );
        tri.setCoordinates(0,vert);
        tri.setColors(0,colors);
        Appearance app=new Appearance();

        PolygonAttributes polyAttrib=new PolygonAttributes();
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        //polyAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        app.setPolygonAttributes(polyAttrib);

        Shape3D shape=new Shape3D(tri,app);
        System.out.print("zuoBiaoZhuBigZ 创建 完成/n");

        trans.addChild(shape);
        //到这里,大坐标轴对象创建完成
    }
    public static  void  zuoBiaoZhuSmallXShape3D(TransformGroup trans)
    {
        //创建小坐标轴对象

        int i ;
        float x1,x2,y1,y2,z1,z2 ;

        Point3f[]vert=new Point3f[41];
        Color3f[]colors=new Color3f[41];
        for(i=0;i<27;i++)
        {
            if(i==0)
            {
                vert[i]=new Point3f(zuoBiaoZhouSmallDingDian,0.0f,0.0f);
                colors[i]=Block.mianColor[0];
            }
            else
            {
                z1=(float)(zuoBiaoZhouSmallDingXi*Math.cos(i*2*Math.PI/25));
                x1=zuoBiaoZhouSmallDingChang ;
                y1=(float)(zuoBiaoZhouSmallDingXi*Math.sin(i*2*Math.PI/25));
                vert[i]=new Point3f(x1,y1,z1);
                colors[i]=Block.mianColor[0];
            }
        }

        for(i=0;i<14;i++)
        {
            if(i==0)
            {
                vert[27+i]=new Point3f(zuoBiaoZhouSmallDingDian,0.0f,0.0f);
                colors[27+i]=Block.mianColor[0];
            }
            else
            {
                z1=(float)(0.005f*Math.cos(i*2*Math.PI/12));
                x1=zuoBiaoZhouSmallWeiDian ;
                y1=(float)(0.005f*Math.sin(i*2*Math.PI/12));
                vert[27+i]=new Point3f(x1,y1,z1);
                colors[27+i]=Block.mianColor[1];
            }
        }


        int count[]=new int[2];
        count[0]=27 ;
        count[1]=14 ;

        TriangleFanArray tri=new TriangleFanArray(
        vert.length,
        TriangleFanArray.COORDINATES|TriangleStripArray.COLOR_3,
        count
        );
        tri.setCoordinates(0,vert);
        tri.setColors(0,colors);
        Appearance app=new Appearance();

        PolygonAttributes polyAttrib=new PolygonAttributes();
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        //polyAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        app.setPolygonAttributes(polyAttrib);

        Shape3D shape=new Shape3D(tri,app);

        //System.out.print("zuoBiaoZhuSmallX 创建 完成");

        trans.addChild(shape);


        //到这里,小坐标轴对象创建完成


    }
    public static  void  zuoBiaoZhuSmallYShape3D(TransformGroup trans)
    {


        //创建小坐标轴对象

        int i ;
        float x1,x2,y1,y2,z1,z2 ;

        Point3f[]vert=new Point3f[41];
        Color3f[]colors=new Color3f[41];
        for(i=0;i<27;i++)
        {
            if(i==0)
            {
                vert[i]=new Point3f(0.0f,zuoBiaoZhouSmallDingDian,0.0f);
                colors[i]=Block.mianColor[2];
            }
            else
            {
                x1=(float)(zuoBiaoZhouSmallDingXi*Math.cos(i*2*Math.PI/25));
                y1=zuoBiaoZhouSmallDingChang ;
                z1=(float)(zuoBiaoZhouSmallDingXi*Math.sin(i*2*Math.PI/25));
                vert[i]=new Point3f(x1,y1,z1);
                colors[i]=Block.mianColor[2];
            }
        }

        for(i=0;i<14;i++)
        {
            if(i==0)
            {
                vert[27+i]=new Point3f(0.0f,zuoBiaoZhouSmallDingDian,0.0f);
                colors[27+i]=Block.mianColor[2];
            }
            else
            {
                x1=(float)(0.005f*Math.cos(i*2*Math.PI/12));
                y1=zuoBiaoZhouSmallWeiDian ;
                z1=(float)(0.005f*Math.sin(i*2*Math.PI/12));
                vert[27+i]=new Point3f(x1,y1,z1);
                colors[27+i]=Block.mianColor[3];
            }
        }
        int count[]=new int[2];
        count[0]=27 ;
        count[1]=14 ;

        TriangleFanArray tri=new TriangleFanArray(
        vert.length,
        TriangleFanArray.COORDINATES|TriangleStripArray.COLOR_3,
        count
        );
        tri.setCoordinates(0,vert);
        tri.setColors(0,colors);
        Appearance app=new Appearance();

        PolygonAttributes polyAttrib=new PolygonAttributes();
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        //polyAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        app.setPolygonAttributes(polyAttrib);

        Shape3D shape=new Shape3D(tri,app);
        //System.out.print("zuoBiaoZhuSmallY 创建 完成");

        trans.addChild(shape);


        //到这里,小坐标轴对象创建完成


    }

    public static  void  zuoBiaoZhuSmallZShape3D(TransformGroup trans)
    {


        //创建小坐标轴对象

        int i ;
        float x1,x2,y1,y2,z1,z2 ;

        Point3f[]vert=new Point3f[41];
        Color3f[]colors=new Color3f[41];
        for(i=0;i<27;i++)
        {
            if(i==0)
            {
                vert[i]=new Point3f(0.0f,0.0f,zuoBiaoZhouSmallDingDian);
                colors[i]=Block.mianColor[4];
            }
            else
            {
                y1=(float)(zuoBiaoZhouSmallDingXi*Math.cos(i*2*Math.PI/25));
                z1=zuoBiaoZhouSmallDingChang ;
                x1=(float)(zuoBiaoZhouSmallDingXi*Math.sin(i*2*Math.PI/25));
                vert[i]=new Point3f(x1,y1,z1);
                colors[i]=Block.mianColor[4];
            }
        }

        for(i=0;i<14;i++)
        {
            if(i==0)
            {
                vert[27+i]=new Point3f(0.0f,0.0f,zuoBiaoZhouSmallDingDian);
                colors[27+i]=Block.mianColor[4];
            }
            else
            {
                y1=(float)(0.005f*Math.cos(i*2*Math.PI/12));
                z1=zuoBiaoZhouSmallWeiDian ;
                x1=(float)(0.005f*Math.sin(i*2*Math.PI/12));
                vert[27+i]=new Point3f(x1,y1,z1);
                colors[27+i]=Block.mianColor[5];
            }
        }
        int count[]=new int[2];
        count[0]=27 ;
        count[1]=14 ;

        TriangleFanArray tri=new TriangleFanArray(
        vert.length,
        TriangleFanArray.COORDINATES|TriangleStripArray.COLOR_3,
        count
        );
        tri.setCoordinates(0,vert);
        tri.setColors(0,colors);


        Appearance app=new Appearance();

        PolygonAttributes polyAttrib=new PolygonAttributes();
        polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
        //polyAttrib.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        app.setPolygonAttributes(polyAttrib);

        Shape3D shape=new Shape3D(tri,app);

 

        //System.out.print("zuoBiaoZhuSmallZ 创建 完成");

        trans.addChild(shape);


        //到这里,小坐标轴对象创建完成


    }
       public static Shape3D shapeMaker(Component observer,String filename,Point3f[] p)
       {

       //用材质,四顶点数组创建一个四边面,需要applet对象observer


            //创建贴图和外观
            TextureLoader loader=new  TextureLoader(filename,observer);
            ImageComponent2D myImage=loader.getImage();
            Texture myTex=loader.getTexture();
            myTex.setImage(0,myImage);
            Appearance appear=new Appearance();
            appear.setTexture(myTex);

            //四边形对象
            //QuadArray tri=new QuadArray(dingdian.length,QuadArray.COORDINATES|QuadArray.COLOR_3|QuadArray.TEXTURE_COORDINATE_2);
QuadArray tri=new QuadArray(4,QuadArray.COORDINATES|QuadArray.TEXTURE_COORDINATE_2);//GeometryArray
            tri.setCoordinates(0,p);
            //tri.setColors(0,color);

            //给四边形对象配材质
            TexCoord2f texCoords=new TexCoord2f();//材质坐标
            texCoords.set(0.0f,1.0f);//取左下角
            tri.setTextureCoordinate(0,0,texCoords);//为左上角
            texCoords.set(0.0f,0.0f);//
            tri.setTextureCoordinate(0,1,texCoords);//
            texCoords.set(1.0f,0.0f);//
            tri.setTextureCoordinate(0,2,texCoords);//
            texCoords.set(1.0f,1.0f);//
            tri.setTextureCoordinate(0,3,texCoords);//

            Shape3D shape=new Shape3D(tri,appear);
        return shape ;

        //到这里,6个面对象创建完成
    }
    public static void addText3DDonghua(TransformGroup parentTrg,String textString,Point3f myPoint3f,float sl,Color3f ambientColor,int donghua)
    {
     //s1定scale,myPoint3f定位置,daxiao是大小
     //字的左下角默认左下角在中点,当tl=0.1时,要向左移10才到左端

     //自定义trg
        Transform3D trgtra=new Transform3D();
        TransformGroup trg=new TransformGroup(trgtra);
        trg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        trg.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        //trg.setCapability(TransformGroup.ENABLE_PICK_REPORTING);

     double tessellation=-0.0 ;
     String fontName="vadana" ;
        // Create the root of the branch graph
        // Create a Transformgroup to scale all objects so they
        // appear in the scene.
        TransformGroup objScale=new TransformGroup();
        Transform3D t3d=new Transform3D();
        // Assuming uniform size chars, set scale to fit string in view

        t3d.setScale(sl);

        objScale.setTransform(t3d);
        trg.addChild(objScale);

        // Create the transform group node and initialize it to the
        // identity.  Enable the TRANSFORM_WRITE capability so that
        // our behavior code can modify it at runtime.  Add it to the
        // root of the subgraph.
        TransformGroup objTrans=new TransformGroup();
        objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);

        objScale.addChild(objTrans);


        Font3D f3d ;
        if(tessellation>0.0)
        {
            f3d=new Font3D(new Font(fontName,Font.PLAIN,2),
            tessellation,
            new FontExtrusion());
        }
        else
        {
            f3d=new Font3D(new Font(fontName,Font.PLAIN,2),
            new FontExtrusion());
        }
        Text3D txt=new Text3D(f3d,textString,
        myPoint3f);
        Shape3D sh=new Shape3D();
        Appearance app=new Appearance();
        Material mm=new Material();
        mm.setLightingEnable(true);
        app.setMaterial(mm);
        sh.setGeometry(txt);
        sh.setAppearance(app);
        objTrans.addChild(sh);

        BoundingSphere bounds=
        new BoundingSphere(new Point3d(0.0,0.0,0.0),100.0);

        // Set up the ambient light

        AmbientLight ambientLightNode=new AmbientLight(ambientColor);
        ambientLightNode.setInfluencingBounds(bounds);
        trg.addChild(ambientLightNode);

        // Set up the directional lights
        Color3f light1Color=new Color3f(1.0f,1.0f,0.9f);
        Vector3f light1Direction=new Vector3f(1.0f,1.0f,1.0f);
        Color3f light2Color=new Color3f(1.0f,1.0f,0.9f);
        Vector3f light2Direction=new Vector3f(-1.0f,-1.0f,-1.0f);

        DirectionalLight light1
        =new DirectionalLight(light1Color,light1Direction);
        light1.setInfluencingBounds(bounds);
        trg.addChild(light1);

        DirectionalLight light2
        =new DirectionalLight(light2Color,light2Direction);
        light2.setInfluencingBounds(bounds);
        trg.addChild(light2);

if(donghua==1)
{
        //给trg(自定义),加上旋转插件
        Alpha alpha1=new Alpha(-1,Alpha.INCREASING_ENABLE|Alpha.DECREASING_ENABLE,0,0,5000,300,100000,5000,300,100000);
        RotationInterpolator myRoTate=new RotationInterpolator(alpha1,trg,trgtra,0.0f,(float)Math.PI*30);
myRoTate.setSchedulingBounds(bounds);
trg.addChild(myRoTate);
trgtra.rotZ(Math.PI/2);
trg.setTransform(trgtra);
System.out.println("/n文本 动画 方案:"+donghua);
}
parentTrg.addChild(trg);
    }

}
//由于java数组的局限,整个程序使用0,1,2空间,为了方便计算,计算时要进行坐标变换

public class MoFang
{
    //该类含魔方的数据表示,blockArray为计算单步必需的,Position为便于人的视觉(用于操作和
    //显示输出)而添加的辅助结构,实际上,我们可以总是从blockArray算出来,但计算量大得惊人
    public static Block[][][]blockArray=new Block[3][3][3];
    //设魔方27块
    public static Position[][][]positionArray=new Position[3][3][3];
    //魔方27个绝对位置

    //用于处理对魔方的操作,共27种操作
    //doType:'X','Y','Z'
    //Floor:-1,0,1
    //totateArg:90 180 -90
    public static void doIt(char doType,int Floor,int totateArg)
    {
        System.out.println("/n处理操作:"+doType);
        System.out.println("层数:"+(Floor-1));
        System.out.println("角度:"+totateArg+"/n");

                   if(!Block.closeDonghua)  //动画打开才等待
{

  while(Block.yunXingThread!=0)
  {System.out.print('.');

  }


}
        switch(doType)
        {
            case 'Z' :
            for(int i=0;i<=2;i++)
            for(int j=0;j<=2;j++)
            //for(int k=0;k<=2;k++)
            {
             if(Block.closeDonghua)//动画关闭才变换中间块
{
                  if(i==1&&j==1)
                {
                    continue ;
                }
}

                int changBlockX=positionArray[i][j][Floor].x ;
                int changBlockY=positionArray[i][j][Floor].y ;
                int changBlockZ=positionArray[i][j][Floor].z ;
                Block changBlock=blockArray[changBlockX][changBlockY][changBlockZ];
                changBlock.xyzChange('Z',totateArg);

            }

            break ;
            case 'Y' :
            for(int i=0;i<=2;i++)
            //for(int j=0;j<=2;j++)
            for(int k=0;k<=2;k++)
            {
                           if(Block.closeDonghua)
{
                if(i==1&&k==1)
                {
                    continue ;
                }
            }
                int changBlockX=positionArray[i][Floor][k].x ;
                int changBlockY=positionArray[i][Floor][k].y ;
                int changBlockZ=positionArray[i][Floor][k].z ;
                Block changBlock=blockArray[changBlockX][changBlockY][changBlockZ];
                changBlock.xyzChange('Y',totateArg);
            }
            break ;

            case 'X' :
            //for(int i=0;i<=2;i++)
            for(int j=0;j<=2;j++)
            for(int k=0;k<=2;k++)
            {
                          if(Block.closeDonghua)
{
                if(j==1&&k==1)
                {
                    continue ;
                }
 }
                int changBlockX=positionArray[Floor][j][k].x ;
                int changBlockY=positionArray[Floor][j][k].y ;
                int changBlockZ=positionArray[Floor][j][k].z ;
                Block changBlock=blockArray[changBlockX][changBlockY][changBlockZ];
                changBlock.xyzChange('X',totateArg);
            }
            break ;

            default :
            System.out.println("无效的操作");
        }

        someBlockNewToOld();
    }

    //开始人工智能计算解法
    //select:选择电脑的计算方案
    public static void autoStart(int select)
    {
    }

    //输出每个位置上的块号
    public static void showPosition()
    {
        System.out.println("/n每个位置上的块号:");
        for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
        for(int k=0;k<=2;k++)
        {
            //System.out.println("Block"+i+","+j+","+k+"    "+blockArray[i][j][k].x+","+blockArray[i][j][k].y+","+blockArray[i][j][k].z);
            System.out.println("Position:"+(i-1)+","+(j-1)+","+(k-1)+"  的块号是:  "+(positionArray[i][j][k].x-1)+","+(positionArray[i][j][k].y-1)+","+(positionArray[i][j][k].z-1));

        }
    }

    //把刚才记录的有变化(来了新的块)的位置 存储到 没个position的块号中
    public static void someBlockNewToOld()
    {
        for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
        for(int k=0;k<=2;k++)
        {
            if(positionArray[i][j][k].haveNew)
            {
                positionArray[i][j][k].newToOld();
            }
        }
    }

 public static void myWait()
 {
                      try
        {

            System.in.read();

                        System.in.read();
            //暂停
        }
        catch(Exception e)
        {
        }
 }

    //测试用代码
      public static   void someTest()
    {

      doIt('Y',0+1,90);
      //        //new MoFang().showPosition();
      MoFang.myWait();
       doIt('Y',0+1,-90);
      MoFang.myWait();
             // new MoFang().showPosition();

       doIt('Z',-1+1,-90);
                     //new MoFang().showPosition();
       MoFang.myWait();

       doIt('Z',-1+1,90);
       //      new MoFang().showPosition();
       //MoFang.myWait();
              //new MoFang().showPosition();

       doIt('X',1+1,90);
       MoFang.myWait();
               //new MoFang().showPosition();
       doIt('X',1+1,-90);
        // new MoFang().showPosition();
       MoFang.myWait();

        //showPosition();
    }


    //魔方自动随机变化多少条
       public static void  ranGet(int num)
    {
     char selectChar='E';//'X','Y','Z'
     int layer;//-1,0,1
     int jiaoDu=0;//90,-19,180

     for(int i=0;i<num;i++)
     {
      //选xyz
      int select=(int)((Math.random()*10)%3);
      if(select==0){selectChar='X';}
      if(select==1){selectChar='Y';}
      if(select==2){selectChar='Z';}

      //
      layer=(int)((Math.random()*10)%3);//0,1,2
      layer-=1;//-1,0,1

      //
      int jiao=(int)((Math.random()*10)%3);//0,1,2
      if(jiao==0){jiaoDu=90;}
      if(jiao==1){jiaoDu=-90;}
      if(jiao==2){jiaoDu=180-90;}
      System.out.println("/n*******************************/nRandom Generater:"+(i+1)+" of "+num);
      System.out.print(selectChar);
      System.out.print(","+layer+","+jiaoDu+"/n按任意键开始动画?/n");

      myWait();

      doIt(selectChar,layer+1,jiaoDu);
     }
    }
    //从三维图形界面输入操作参数
    public static void graphicStart()
    {
     JFrame myframe =new JFrame();
        myframe.setVisible(true);

    }
    public static void main(String[]args)
    {

     //初始化位置数组,也可以从文件中加载,其实应该把所有关键信息从文件载入,关闭时保存
        for(int i=0;i<=2;i++)
        for(int j=0;j<=2;j++)
        for(int k=0;k<=2;k++)
        {

            MoFang.positionArray[i][j][k]=new Position(i,j,k);
        }
        System.out.println("初始化位置数组完成.最初全在原位置/n");

       //showPosition();

        new MainFrame(new mySimpleUniverse(),200,200);
        //加applet到应用程序界面
  //someTest();
        //graphicStart();
  myWait();
       ranGet(30);
        //
        //
    }

}

class Block implements Runnable
{
    //计算已画的块数
    private static int whickBlockPainted=0 ;

    //计算生成的第几块
    private static int BlockCreated=0 ;

    //每面的颜色,排列为:+x,-x,+y,-y,+z,-z,no
    public static Color3f[] mianColor=
{new Color3f(1.0f,0.0f,0.0f),
new Color3f(0.0f,1.0f,0.0f),
new Color3f(0.0f,0.0f,1.0f),
new Color3f(1.0f,1.0f,0.0f),
new Color3f(1.0f,0.0f,1.0f),
new Color3f(0.0f,1.0f,1.0f),
new Color3f(0.2f,0.2f,0.2f)
};
    //每面的材质,排列为:+x,-x,+y,-y,+z,-z,no
    public static String[] mianImageFile=new String[7];

    //块偏移量
    private static float kuaiZhongXinWeizhi=0.4f ;

    //创建材质时要用的,仅用于他,他是一个applet对象
    Component observer;

    //该块的编号
    private int blockIdX ;
    private int blockIdY ;
    private int blockIdZ ;

    //该块的位置
    private int x ;
    private int y ;
    private int z ;

    //该块的坐标轴点,最初全为(1,1,1),表示与外坐标一致
    private MyPoint xvec;
    private MyPoint yvec;
    private MyPoint zvec;

    //position=new Verctor3f(0.0f,0.0f,0.0f);

    //该块的角度,限定为从-359到359,模360可以限定为一周,如果模180,则只能表示半个圆,不行
    //int anglex ;
    //int angley ;
    //int anglez ;

    //添加物品的变换组
    public TransformGroup transGroup ;
    public Transform3D trans ;


    //用于位置,角度变动的变换组
    public TransformGroup transGroupx ;
    public Transform3D transx ;
    public TransformGroup transGroupy ;
    public Transform3D transy ;
    public TransformGroup transGroupz ;
    public Transform3D transz ;
    public TransformGroup transGroupp ;
    public Transform3D transp ;

    //新的方块位置
 int[]  nexyz={0,0,0};

    //动画相关
    int totateArg;
    boolean canNew=false;
    char selectedC;
    Thread myThread;
 public static boolean closeDonghua=false ; //为true使用线程动画,为false直接调用


  private static int selectDonghuaId=0;//取ID变量,取值从0~8,每次从0开始取,正好9个,取完后变为10(每取一个自动加1),这时才让所有线程一起动
  private int myDonghuaId;
 private static int donghuaDelay=50 ; //绝对延迟,
 public static int whileDelay=20;//加快同步系统性能延迟,

 //公共控制变量
  public static int yunXingThread=0;//运行的线程数,启动线程时加1,退出时减一。可以控制主线程等待,直到为0时才开始做所有的xyzChange,否则等待

//动画时的多线程要用它,因为主线程仍在跑,x,y,z会变化,只能用它保存
            int yuanx;
            int yuany;
            int yuanz;

//当前位置x点的偏移
MyPoint chaxvec;
//当前位置y点的偏移
MyPoint chayvec;
//当前位置z点的偏移
MyPoint chazvec;


    //构造函数,给初值
    public Block(int i,int j,int k,int px,int py,int pz,TransformGroup parentTransGroup,Transform3D t3d,BranchGroup objRoot,Component obServer1)
    {

        blockIdX=i ;
        blockIdY=j ;
        blockIdZ=k ;


        x=px ;
        y=py ;
        z=pz ;

//计算轴向量 :块坐标加坐标偏移
xvec=new MyPoint((blockIdX-1)+1,(blockIdY-1),(blockIdZ-1));
yvec=new MyPoint((blockIdX-1),(blockIdY-1)+1 ,(blockIdZ-1));
zvec=new MyPoint((blockIdX-1),(blockIdY-1),(blockIdZ-1)+1 );
//System.out.println("轴点:"+(zoux)+(zouy)+(zouz));
//System.out.println("轴向量:"+(zoux-(x-1))+(zouy-(y-1))+(zouz-(z-1)));

        observer=obServer1;

        //anglex=0 ;
        //angley=0 ;
        //anglez=0 ;

        trans=new Transform3D();
        transGroup=new TransformGroup(trans);
        transGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        transGroup.setCapability(TransformGroup.ENABLE_PICK_REPORTING);

        transp=new Transform3D();
        transGroupp=new TransformGroup(transp);
        transGroupp.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transGroupp.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        //transGroupp.setCapability(TransformGroup.ENABLE_PICK_REPORTING);

        transx=new Transform3D();
        transGroupx=new TransformGroup(transx);
        transGroupx.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transGroupx.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        //transGroupx.setCapability(TransformGroup.ENABLE_PICK_REPORTING);

        transy=new Transform3D();
        transGroupy=new TransformGroup(transy);
        transGroupy.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transGroupy.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        //transGroupy.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
        transz=new Transform3D();
        transGroupz=new TransformGroup(transz);
        transGroupz.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        transGroupz.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        //transGroupz.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
        //用已知信息组装变换组,,
        //注意这里的顺序,先平移,再旋转,或先x,先y的顺序是不一样的,这里
        //必须是先在原位置自转好方向,再平移,这样,平移不会改变它的自转方向,
        //如果先平移,再转,转动会带动平移,刚才的平移无任何意义。
        //还要注意是先绕x转,再绕y转,再绕z转
        //换了顺序,是不一样的,例如x,y,z和z,y,x的结果不一样
        //但是,可以证明x,y和y,x的顺序无关性,当都只左转或右转90度时,很容易证明
        //如果有180度,可以分成两次90度操作
        parentTransGroup.addChild(transGroupp);
        transGroupp.addChild(transGroupz);
        transGroupz.addChild(transGroupy);
        transGroupy.addChild(transGroupx);
        transGroupx.addChild(transGroup);
        // 测试,直接加到根,没有鼠标功能
        //objRoot.addChild(transGroup);
        //System.out.println("/n/n/n处理第"+BlockCreated+"块");
        //System.out.println("块"+(blockIdX-1)+(blockIdY-1)+(blockIdZ-1)+"的坐标系添加完成");
        BlockCreated++;
        //if (BlockCreated==14)//||BlockCreated==3//||BlockCreated==2||BlockCreated==20||BlockCreated==16
        add3DCube(x-1,y-1,z-1,transGroup);
        //建小坐标轴
        SomeShape3D.zuoBiaoZhuSmallXShape3D(transGroup);
        SomeShape3D.zuoBiaoZhuSmallYShape3D(transGroup);
        SomeShape3D.zuoBiaoZhuSmallZShape3D(transGroup);
        //测试主变换组的平移
        //Shape3D shape1=SomeShape3D.FlowerShape3D();
        //parentTransGroup.addChild(shape1);
        //t3d.setTranslation(new Vector3f(0.0f,1.0f,0.0f));
        //parentTransGroup.setTransform(t3d);
        //测试子变换组的平移(不行,不起作用)
        //Shape3D shape2=SomeShape3D.SanShape3D();
        //transGroup.addChild(shape2);
        //trans.setTranslation(new Vector3f(0.0f,-1f,0.0f));
        //transGroup.setTransform(t3d);
        //创建鼠标行为
        //MouseRotate behavior1=new MouseRotate();
        //behavior1.setTransformGroup(transGroup);
        //behavior1.setSchedulingBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0),100));
        //objRoot.addChild(behavior1);//鼠标行为  加到 分枝组
        //设置初始值,显示输出
        float fx ;
        float fy ;
        float fz ;
        fx=(float)kuaiZhongXinWeizhi*(x-1);
        fy=(float)kuaiZhongXinWeizhi*(y-1);
        fz=(float)kuaiZhongXinWeizhi*(z-1);
        //变换量
        transx.rotX(Math.toRadians(0));//anglex
        transy.rotY(Math.toRadians(0));
        transz.rotZ(Math.toRadians(0));
        transp.setTranslation(new Vector3f(fx,fy,fz));
        //生效
        transGroupp.setTransform(transp);
        transGroupx.setTransform(transx);
        transGroupy.setTransform(transy);
        transGroupz.setTransform(transz);
    }
int[] jisuanNextXYZ(char doType,int totateArg,int oldx,int oldy,int oldz)
//需要变量x,y,z,doType,totateArg,输出返回到数组
{
//System.out.println("计算下一个点。。。");
//用于计算
int newz=0;
int newy=0;
int newx=0;
//返回时的标准格式
int[] nextXYZ={0,0,0};

//计算时接收调用外面函数的返回函数值
int[] myShunShiNext={0,0};
if(totateArg==0)
{
//当totateArg为0时,没有可用的if匹配,返回原位置
newx=oldx;
newy=oldy;
newz=oldz;
//返回结果
nextXYZ[0]=newx;
nextXYZ[1]=newy;
nextXYZ[2]=newz;
//System.out.println("/n变为位置:"+newx+newy+newz);
return nextXYZ;
}
//
//System.out.println("/n原位置:"+oldx+oldy+oldz);
//System.out.println("绕:"+doType+"转"+totateArg);

switch(doType)
{
case 'Z' :
            newz=oldz ;
if(totateArg==90)
           {myShunShiNext=quXiaYiGe(oldx,oldy,1);
}
else if (totateArg==180)
           {myShunShiNext=quXiaYiGe(oldx,oldy,2);
}
else if(totateArg==-90)
           {myShunShiNext=quXiaYiGe(oldx,oldy,3);
}
newx=myShunShiNext[0];
newy=myShunShiNext[1];
break;

case 'Y' :
            newy=oldy ;
if(totateArg==90)
           {myShunShiNext=quXiaYiGe(oldz,oldx,1);
}
else if(totateArg==180)
           {myShunShiNext=quXiaYiGe(oldz,oldx,2);
}
else if(totateArg==-90)
           {myShunShiNext=quXiaYiGe(oldz,oldx,3);
}
newz=myShunShiNext[0];
newx=myShunShiNext[1];
break;
case 'X' :
   newx=oldx ;
if(totateArg==90)
           {myShunShiNext=quXiaYiGe(oldy,oldz,1);
}
else if(totateArg==180)
           {myShunShiNext=quXiaYiGe(oldy,oldz,2);
}
else if(totateArg==-90)
           {myShunShiNext=quXiaYiGe(oldy,oldz,3);
}
newy=myShunShiNext[0];
newz=myShunShiNext[1];
break;
}
//返回结果
nextXYZ[0]=newx;
nextXYZ[1]=newy;
nextXYZ[2]=newz;
//System.out.println("/n变为位置:"+newx+newy+newz);
return nextXYZ;
}
boolean fangXiangCorrect(int argx,int argy,int argz)
{//用三个差向量判断自转
//System.out.println("方向判断。。。");
 //(blockIdX-1),(blockIdY-1),(blockIdZ-1)为初始点位置,分别加1得到三个初始向量
 //xvec,yvec,zvec为保存的变化
 //减去当前的方块位置,得到每个方向点当前向量chaxvec,chayvec,chazvec
//X点
int[] p1X=jisuanNextXYZ('X',argx,1,0,0);//原始x点便移
int[] p2X=jisuanNextXYZ('Y',argy,p1X[0],p1X[1],p1X[2]);
int[] p3X=jisuanNextXYZ('Z',argz,p2X[0],p2X[1],p2X[2]);//新的x点便移
//Y点
int[] p1Y=jisuanNextXYZ('X',argx,0,1,0);
int[] p2Y=jisuanNextXYZ('Y',argy,p1Y[0],p1Y[1],p1Y[2]);
int[] p3Y=jisuanNextXYZ('Z',argz,p2Y[0],p2Y[1],p2Y[2]);
//Z点
int[] p1Z=jisuanNextXYZ('X',argx,0,0,1);
int[] p2Z=jisuanNextXYZ('Y',argy,p1Z[0],p1Z[1],p1Z[2]);
int[] p3Z=jisuanNextXYZ('Z',argz,p2Z[0],p2Z[1],p2Z[2]);
 //System.out.println("坐标系:"+chaX+"   "+chaY+"   "+chaZ);
// 新的x点便移=当前位置x点的偏移
if(
((p3X[0]==chaxvec.x)&&(p3X[1]==chaxvec.y)&&(p3X[2]==chaxvec.z))&&
((p3Y[0]==chayvec.x)&&(p3Y[1]==chayvec.y)&&(p3Y[2]==chayvec.z))&&
((p3Z[0]==chazvec.x)&&(p3Z[1]==chazvec.y)&&(p3Z[2]==chazvec.z))
)
{System.out.println("坐标轴到位了");
return true;
}
else
{
 //System.out.println("坐标轴没到位");
return false;
}
}
boolean weiZhiCorrect(int aidx,int aidy,int aidz,int argx,int argy,int argz)
{
int[] p1=jisuanNextXYZ('X',argx,blockIdX-1,blockIdY-1,blockIdZ-1);
int[] p2=jisuanNextXYZ('Y',argy,p1[0],p1[1],p1[2]);
int[] p3=jisuanNextXYZ('Z',argz,p2[0],p2[1],p2[2]);
if((p3[0]==aidx)&&(p3[1]==aidy)&&(p3[2]==aidz))
{System.out.println("位置对了");
return true;
}
else
{
 System.out.println("位置不对");
return false;
}
}
//仅计算逆时针旋转时的坐标变化,当转逆时针-90度时,转化为3个逆时针90度
int[] quXiaYiGe(int num1,int num2,int n)
{
int[][] xiangXianZhi={{1,1},{-1,1},{-1,-1},{1,-1}}; //一,二,三,四,0,1,2,3
int[][] zouShangZhi={{1,0},{0,1},{-1,0},{0,-1}}; //
int[] result={0,0};
//System.out.println("旋转次数为:"+n);
//System.out.println(num1+" , "+num2+"旋转到:");
int temp=0;
for(int i=0;i<n;i++)
{
//循环一次转一次
if(num1==0&&num2==0)//
{
num1=0;
num2=0;
//System.out.println("0");
}
else if(num1>0&&num2>0)//一象限转到二象限
{
temp=num1;
num1=-num2;
num2=temp;
//System.out.println("1");
}
else if(num1<0&&num2>0)//二象限转到三象限
{
 temp=num1;
num1=-num2;
num2=temp;
System.out.println("2");
}
else if(num1<0&&num2<0)//三象限转到四象限
{
 temp=num1;
num1=-num2;
num2=temp;
System.out.println("3");
}
else if(num1>0&&num2<0)//四象限转到一象限
{
 temp=num1;
num1=-num2;
num2=temp;
//System.out.println("4");
}
else if(num1>0&&num2==0)//X上到Y上
{
 temp=num1;
num1=0;
num2=temp;
//System.out.println("5");
}
else if(num1==0&&num2>0)//Y上到X上
{
 temp=num1;
num1=-num2;
num2=0;
//System.out.println("6");
}
else if(num1<0&&num2==0)//
{
 temp=num1;
num1=0;
num2=temp;
//System.out.println("7");
}
else if(num1==0&&num2<0)//
{
 temp=num1;
num1=-num2;
num2=0;
//System.out.println("8");
}
else
{System.out.println("no");}
}
//System.out.println(num1+" , "+num2);
result[0]=num1;
result[1]=num2;
return result;
}
 public void run()
  {
   //System.out.println("我的动画开始了");
   //动画协调,取一个id,并放出令牌
myDonghuaId=selectDonghuaId++;
selectDonghuaId%=9;
yunXingThread++;
   switch(selectedC)
  {
   case 'Z':
   {startDonghuaZ();break;
   }
   case 'Y':
   {startDonghuaY();break;
   }
   case 'X':
   {startDonghuaX();break;
   }
  }
  //最终位置
  while(!canNew)//当canNew=true时,输出到3D界面,否则只计算
  {//System.out.println(" 我在等待计算出新值...");
  }
        //变换生效
        transGroupp.setTransform(transp);
        transGroupx.setTransform(transx);
        transGroupy.setTransform(transy);
        transGroupz.setTransform(transz);
  canNew=false;
  //System.out.println("我的动画完成了");
   yunXingThread--;
  }
 //用来从一个原坐标和结果坐标来 搜索 3个x,y,z顺序的旋转操作,返回到needRotate[3]中
//使用了int[] jisuanNextXYZ(char doType,int totateArg,int oldx,int oldy,int oldz)
int[] shouSuoXYZRotate(int oldx,int oldy,int oldz,int aidx,int aidy,int aidz)
{//搜索自转,不看位置,只看三个向量对不对
System.out.println("块原始位置:"+oldx+","+oldy+","+oldz);
System.out.println("块目标位置:"+aidx+","+aidy+","+aidz);
//存放结果
int needRotatex=0;
int needRotatey=0;
int needRotatez=0;
//转换返回
int[] needRotate={0,0,0};
int num=0;
wancheng:
for(int j=0;j<=3;j++)
{
int toArg=0;//j=3时取0
      if(j==1){toArg=90;}
      if(j==2){toArg=-90;}
      if(j==3){toArg=180;}
for(int jj=0;jj<=3;jj++)
{
int ttoArg=0;
      if(jj==1){ttoArg=90;}
      if(jj==2){ttoArg=-90;}
      if(jj==3){ttoArg=180;}
for(int jjj=0;jjj<=3;jjj++)
{
int tttoArg=0;
      if(jjj==1){tttoArg=90;}
      if(jjj==2){tttoArg=-90;}
      if(jjj==3){tttoArg=180;}
     //System.out.println("验证xyz旋转.."+toArg+"  "+ttoArg+"  "+tttoArg);
     // 这个打开 有的会找不到
//boolean myBoolean1=weiZhiCorrect(nexyz[0],nexyz[1],nexyz[2],toArg,ttoArg,tttoArg);
boolean myBoolean=fangXiangCorrect(toArg,ttoArg,tttoArg);
//MoFang.myWait();
if (myBoolean)
{
needRotatex=toArg;
needRotatey=ttoArg;
needRotatez=tttoArg;
needRotate[0]=needRotatex;
needRotate[1]=needRotatey;
needRotate[2]=needRotatez;
num++;
System.out.println("*****找到了x,y,z旋转即可*****"+toArg+" , "+ttoArg+" , "+tttoArg+"");
//return needRotate;
}
}
}
}
if(num==0)
{
System.out.println("没有适合的xyz旋转,不旋转..");
MoFang.myWait();
}
else if(num>=2)
{System.out.println("###########################找到了num="+num+"个");
}
return needRotate;
}
       void startDonghuaX()
  {
            //动画模块
            int chuJiao ;
            int oneTime ;
            int bianJiao ;
            int nowJiao ;
            //测试
            //totateArg=90;
            chuJiao=getChujiao(yuany,yuanz);
            if(totateArg==-90)
            oneTime=-15 ;
            else
            oneTime=15 ;
                //System.out.println("chuJiao="+chuJiao);
                //System.out.println("totateArg="+totateArg);
            //nowJiao=chuJiao+bianJiao ;
            //这是目前角度公式,可见,据坐标可以判断它的角度嘛
            for(bianJiao=0;bianJiao!=(totateArg+oneTime);bianJiao+=oneTime)
            {
            if(!closeDonghua)
            {
              while(myDonghuaId!=selectDonghuaId)//动画同步相关轮到自己时才执行,把令牌交给下一个
             {;//System.out.println("我是"+myDonghuaId+"号,而令牌现在是"+selectDonghuaId+"号,我要等...");
       try
         {
          //System.in.read();//暂停
               myThread.sleep(whileDelay);
           }
             catch(Exception e)
           {
            }
           }
          }
                //计算x,y和角度输出
                float fx ;
                float fy ;
                float fz ;
                //半径
                float r;
    if(yuany==0&&yuanz==0)
    r=0;//(x,y)不变,故不用计算,直接给(0,0),初角返回任何值都可;
    else
    {
    if(yuany==0||yuanz==0)
    r=1.0f;
    else
    r=1.414f;
             }
                nowJiao=chuJiao+bianJiao ;
                //使用初角直为了计算当前(x,y)位置,当前jiaodu1与他无关,只与老角有关
                //(-1,-1)距离原点为根2,约1.732
                //0.3指定了1点在0.3,-0.3处
                fy=(float)kuaiZhongXinWeizhi*r*(float)Math.cos(Math.PI*nowJiao/180);
                fz=(float)kuaiZhongXinWeizhi*r*(float)Math.sin(Math.PI*nowJiao/180);
                fx=(float)kuaiZhongXinWeizhi*(yuanx);
                //z坐标不变,这里的z早减过了1
        //计算出了全部数据,OK,开始刷新
        //用变换量
        //transz.rotZ(Math.toRadians(anglez));
        //transy.rotY(Math.toRadians(angley));
        //transx.rotX(Math.toRadians(bianJiao+oldJiaoDu));
        transp.setTranslation(new Vector3f(fx,fy,fz));
        //生效
        //transGroupz.setTransform(transz);
        //transGroupy.setTransform(transy);
        //transGroupx.setTransform(transx);
        transGroupp.setTransform(transp);
     //停不了,只能使用多线程或定时
                //System.out.println("bianJiao="+bianJiao);
                try
              {
                //System.in.read();//暂停
                //
                myThread.sleep(donghuaDelay);
             }
              catch(Exception e)
            {
             }
                           //在这里才放出令牌
             selectDonghuaId++;
     selectDonghuaId%=9;
            }
 }
   void startDonghuaY()
  {
             //动画模块
            int chuJiao ;
            int oneTime ;
            int bianJiao ;
            int nowJiao ;
            //测试
            //totateArg=90;
            chuJiao=getChujiao(yuanz,yuanx);
            if(totateArg==-90)
            oneTime=-15 ;
            else
            oneTime=15 ;
                //System.out.println("chuJiao="+chuJiao);
                //System.out.println("totateArg="+totateArg);
            //nowJiao=chuJiao+bianJiao ;
            //这是目前角度公式,可见,据坐标可以判断它的角度嘛
            for(bianJiao=0;bianJiao!=(totateArg+oneTime);bianJiao+=oneTime)
            {
            if(!closeDonghua)
            {
              while(myDonghuaId!=selectDonghuaId)//动画同步相关轮到自己时才执行,把令牌交给下一个
             {;//System.out.println("我是"+myDonghuaId+"号,而令牌现在是"+selectDonghuaId+"号,我要等...");
       try
         {
          //System.in.read();//暂停
               myThread.sleep(whileDelay);
           }
             catch(Exception e)
           {
            }
           }
          }
                //计算x,y和角度输出
                float fx ;
                float fy ;
                float fz ;
                //半径
                float r;
    if(yuanz==0&&yuanx==0)
    r=0;//(x,y)不变,故不用计算,直接给(0,0),初角返回任何值都可;
    else
    {
    if(yuanz==0||yuanx==0)
    r=1.0f;
    else
    r=1.414f;
             }
                nowJiao=chuJiao+bianJiao ;
                //使用初角直为了计算当前(x,y)位置,当前jiaodu1与他无关,只与老角有关
                //(-1,-1)距离原点为根2,约1.732
                //0.3指定了1点在0.3,-0.3处
                fz=(float)kuaiZhongXinWeizhi*r*(float)Math.cos(Math.PI*nowJiao/180);
                fx=(float)kuaiZhongXinWeizhi*r*(float)Math.sin(Math.PI*nowJiao/180);
                fy=(float)kuaiZhongXinWeizhi*(yuany);
                //z坐标不变,这里的z早减过了1
        //计算出了全部数据,OK,开始刷新
        //用变换量
        //transz.rotZ(Math.toRadians(anglez));
        //transy.rotY(Math.toRadians(bianJiao+oldJiaoDu));
        //transx.rotX(Math.toRadians(anglex));
        transp.setTranslation(new Vector3f(fx,fy,fz));
        //生效
        //transGroupz.setTransform(transz);
        //transGroupy.setTransform(transy);
        //transGroupx.setTransform(transx);
        transGroupp.setTransform(transp);
    //停不了,只能使用多线程或定时
                //System.out.println("bianJiao="+bianJiao);
                 try
              {
                //System.in.read();//暂停
                //
                myThread.sleep(donghuaDelay);
             }
              catch(Exception e)
            {
             }
                           //在这里才放出令牌
             selectDonghuaId++;
     selectDonghuaId%=9;
            }
 }
   void startDonghuaZ()
  {
            //动画模块
            int chuJiao ;
            int oneTime ;
            int bianJiao ;
            int nowJiao ;
            //测试
            //totateArg=90;
            chuJiao=getChujiao(yuanx,yuany);
            if(totateArg==-90)
            oneTime=-15 ;
            else
            oneTime=15 ;
                //System.out.println("chuJiao="+chuJiao);
                //System.out.println("totateArg="+totateArg);
            //nowJiao=chuJiao+bianJiao ;
            //这是目前角度公式,可见,据坐标可以判断它的角度嘛
            for(bianJiao=0;bianJiao!=(totateArg+oneTime);bianJiao+=oneTime)
            {
            if(!closeDonghua)
            {
              while(myDonghuaId!=selectDonghuaId)//动画同步相关轮到自己时才执行,把令牌交给下一个
             {;//System.out.println("我是"+myDonghuaId+"号,而令牌现在是"+selectDonghuaId+"号,我要等...");
       try
         {
          //System.in.read();//暂停
               myThread.sleep(whileDelay);
           }
             catch(Exception e)
           {
            }
           }
          }
                //计算x,y和角度输出
                float fx ;
                float fy ;
                float fz ;
                //半径
                float r;
    if(yuanx==0&&yuany==0)
    r=0;//(x,y)不变,故不用计算,直接给(0,0),初角返回任何值都可;
    else
    {
    if(yuanx==0||yuany==0)
    r=1.0f;
    else
    r=1.414f;
             }
                nowJiao=chuJiao+bianJiao ;
                //使用初角直为了计算当前(x,y)位置,当前jiaodu1与他无关,只与老角有关
                //(-1,-1)距离原点为根2,约1.732
                //0.3指定了1点在0.3,-0.3处
                fx=(float)kuaiZhongXinWeizhi*r*(float)Math.cos(Math.PI*nowJiao/180);
                fy=(float)kuaiZhongXinWeizhi*r*(float)Math.sin(Math.PI*nowJiao/180);
                fz=(float)kuaiZhongXinWeizhi*(yuanz);
                //z坐标不变,这里的z早减过了1
        //计算出了全部数据,OK,开始刷新
        //用变换量
        //transz.rotZ(Math.toRadians());
        //transy.rotY(Math.toRadians());
        //transx.rotX(Math.toRadians());
        transp.setTranslation(new Vector3f(fx,fy,fz));
        //生效
        //transGroupz.setTransform(transz);
        //transGroupy.setTransform(transy);
        //transGroupx.setTransform(transx);
        transGroupp.setTransform(transp);
    //停不了,只能使用多线程或定时

                //System.out.println("bianJiao="+bianJiao);
                 try
              {
                //System.in.read();//暂停
                //
                myThread.sleep(donghuaDelay);
             }
              catch(Exception e)
            {
             }
                           //在这里才放出令牌
             selectDonghuaId++;
     selectDonghuaId%=9;

            }
  }
    //center点仅用来计算颜色
    void add3DCube(int centerx,int centery,int centerz,TransformGroup myTransGroup)
    {
        //System.out.println("正在画该块.....");
        //颜色数据结构
        int[]compare=new int[6];
        compare[0]=centerx ;
        //x
        compare[1]=centerx ;
        //x
        compare[2]=centery ;
        //y
        compare[3]=centery ;
        //y
        compare[4]=centerz ;
        //z
        compare[5]=centerz ;
        //z
        int[]compareWith=new int[6];
        compareWith[0]=1 ;
        compareWith[1]=-1 ;
        compareWith[2]=1 ;
        compareWith[3]=-1 ;
        compareWith[4]=1 ;
        compareWith[5]=-1 ;
        Color3f presentMianColor ;
        //面图
        String presentImageFile;
        mianImageFile[0]="IMG//coverRight.jpg";
        mianImageFile[1]="IMG//coverLeft.jpg";
        mianImageFile[2]="IMG//coverUp.jpg";
        mianImageFile[3]="IMG//coverDown.jpg";
        mianImageFile[4]="IMG//coverFront.jpg";
        mianImageFile[5]="IMG//coverBehind.jpg";
        mianImageFile[6]="IMG//coverCenter.jpg";
        //点数据结构
        Vector3f mianxin=new Vector3f();
        Vector3f[]mianxinpianyi=new Vector3f[6];
        mianxinpianyi[0]=new Vector3f(1,0,0);
        mianxinpianyi[1]=new Vector3f(-1,0,0);
        mianxinpianyi[2]=new Vector3f(0,1,0);
        mianxinpianyi[3]=new Vector3f(0,-1,0);
        mianxinpianyi[4]=new Vector3f(0,0,1);
        mianxinpianyi[5]=new Vector3f(0,0,-1);
        Vector3f[]dingdianPianyiX=new Vector3f[4];
        dingdianPianyiX[0]=new Vector3f(0.0f,1.0f,1.0f);
        dingdianPianyiX[1]=new Vector3f(0.0f,-1.0f,1.0f);
        dingdianPianyiX[2]=new Vector3f(0.0f,-1.0f,-1.0f);
        dingdianPianyiX[3]=new Vector3f(0.0f,1.0f,-1.0f);
        Vector3f[]dingdianPianyiY=new Vector3f[4];
        dingdianPianyiY[0]=new Vector3f(1.0f,0.0f,1.0f);
        dingdianPianyiY[1]=new Vector3f(1.0f,0.0f,-1.0f);
        dingdianPianyiY[2]=new Vector3f(-1.0f,0.0f,-1.0f);
        dingdianPianyiY[3]=new Vector3f(-1.0f,0.0f,1.0f);
        Vector3f[]dingdianPianyiZ=new Vector3f[4];
        dingdianPianyiZ[0]=new Vector3f(1.0f,1.0f,0.0f);
        dingdianPianyiZ[1]=new Vector3f(-1.0f,1.0f,0.0f);
        dingdianPianyiZ[2]=new Vector3f(-1.0f,-1.0f,0.0f);
        dingdianPianyiZ[3]=new Vector3f(1.0f,-1.0f,0.0f);
        //通过for,集合到三个数组
        Point3f[][] vert=new Point3f[6][4];
        Color3f[] color=new Color3f[6];
        String[] imageFile=new String[6];
        for(int i=0;i<=5;i++)
        {
            //计算该面 颜色和贴图
            if(compare[i]==compareWith[i])
            {
             presentMianColor=mianColor[i];
             presentImageFile=mianImageFile[i];
         }
            else
            {
                presentMianColor=mianColor[6];
                presentImageFile=mianImageFile[6];
                //如果颜色为白色,不画该面
                //continue则跳过不画
                //continue ;
            }
            try
            {
                //System.in.read();//暂停
            }
            catch(Exception e)
            {
            }
            //计算该面 面心
            mianxin.x=mianxinpianyi[i].x ;
            mianxin.y=mianxinpianyi[i].y ;
            mianxin.z=mianxinpianyi[i].z ;
            //计算该面 四个点
            Vector3f[]dingdian=new Vector3f[4];
            for(int j=0;j<=3;j++)
            {
                dingdian[j]=new Vector3f();
                if(i==0||i==1)
                {
                    dingdian[j].x=mianxin.x+dingdianPianyiX[j].x ;
                    dingdian[j].y=mianxin.y+dingdianPianyiX[j].y ;
                    dingdian[j].z=mianxin.z+dingdianPianyiX[j].z ;
                }
                else if(i==2||i==3)
                {
                    dingdian[j].x=mianxin.x+dingdianPianyiY[j].x ;
                    dingdian[j].y=mianxin.y+dingdianPianyiY[j].y ;
                    dingdian[j].z=mianxin.z+dingdianPianyiY[j].z ;
                }
                else if(i==4||i==5)
                {
                    dingdian[j].x=mianxin.x+dingdianPianyiZ[j].x ;
                    dingdian[j].y=mianxin.y+dingdianPianyiZ[j].y ;
                    dingdian[j].z=mianxin.z+dingdianPianyiZ[j].z ;
                }
            }
            //用顶点和颜色画 该面
            //建面方法一,把vector3D对象传进去,在里面转化为float数组
   //Shape3D shape=SomeShape3D.mian1of6CubeShape3D(observer,dingdian,presentImageFile,presentMianColor);
            //建面方法二,把vector3D在这里转化为point3f数组,再传进去,转换更简单
           Point3f[] vert1=new Point3f[4];
           Point3f[] vert2=new Point3f[4];
           //4个点的信息
           for(int k=0;k<=3;k++)
            {
      vert1[k]=new Point3f(SomeShape3D.fangKuaiBanJing*dingdian[k].x ,SomeShape3D.fangKuaiBanJing*dingdian[k].y,SomeShape3D.fangKuaiBanJing*dingdian[k].z);
 vert2[3-k]=new Point3f(SomeShape3D.fangKuaiBanJing*dingdian[k].x ,SomeShape3D.fangKuaiBanJing*dingdian[k].y,SomeShape3D.fangKuaiBanJing*dingdian[k].z);
         }
         Shape3D shape1=SomeShape3D.shapeMaker(observer,presentImageFile,vert1);
         Shape3D shape2=SomeShape3D.shapeMaker(observer,presentImageFile,vert2);
            //两方法结果一样
            //消失现象 可以避免,原因是各面衔接点不重合,dingdian[k].x乘上SomeShape3D.fangKuaiBanJing后有数据问题
            //挂到自己的坐标系
      myTransGroup.addChild(shape1);
      myTransGroup.addChild(shape2);
            //测试3

         // for(int j=0;j<=3;j++)
          //  {
          // vert[i][j]=new Point3f(SomeShape3D.fangKuaiBanJing*dingdian[j].x ,SomeShape3D.fangKuaiBanJing*dingdian[j].y,SomeShape3D.fangKuaiBanJing*dingdian[j].z);
   // }
       //color[i]=presentMianColor;
          // imageFile[i]=presentImageFile;
        }
                    //测试3
           // SomeShape3D.box3D(observer,myTransGroup,vert,color,imageFile);
        //System.out.println("第"+whickBlockPainted+"块完毕。");
        System.out.print('.');
        whickBlockPainted++;
    }
    //该块改变位置
    //Floor:0,1,2
    //totateArg:90 180 -90
    //为了方便计算,先平移(坐标全减1),变换后,再平移还原(坐标全加1)
    //该块改变位置
    //Floor:0,1,2
    //totateArg:90 180 -90
    //为了方便计算,先平移(坐标全减1),变换后,再平移还原(坐标全加1)
    void xyzChange(char doType,int mYtotateArg)
    {
        //
        totateArg=mYtotateArg;
        //在处理过程中,原坐标先平移
        x-=1 ;
        y-=1 ;
        z-=1 ;
//动画预处理
yuanx=x;
yuany=y;
yuanz=z;

if(closeDonghua)
{//startDonghuaX();
}
else
//换为用线程执行
{
selectedC=doType;
myThread=new Thread(this,"Rotate");
myThread.start();
}
System.out.println("新轴点x计算并保存。。。");
  //新轴点计算并保存
//System.out.println("。。。"+xvec.x+xvec.y+xvec.z);
        int[] newvecx=jisuanNextXYZ(doType,totateArg,xvec.x,xvec.y,xvec.z);
//System.out.println("。。。");
        xvec.x=newvecx[0];
        xvec.y=newvecx[1];
        xvec.z=newvecx[2];
System.out.println("新轴点y计算并保存。。。");
        int[] newvecy=jisuanNextXYZ(doType,totateArg,yvec.x,yvec.y,yvec.z);
        yvec.x=newvecy[0];
        yvec.y=newvecy[1];
        yvec.z=newvecy[2];
        int[] newvecz=jisuanNextXYZ(doType,totateArg,zvec.x,zvec.y,zvec.z);
        zvec.x=newvecz[0];
        zvec.y=newvecz[1];
        zvec.z=newvecz[2];
System.out.println("新坐标计算并保存。。。");
        //新坐标计算
        nexyz=jisuanNextXYZ(doType,totateArg,x,y,z)  ;
        //输出处理结果
        System.out.println("/n块"+(blockIdX-1)+(blockIdY-1)+(blockIdZ-1)+"从"+x+y+z+"  变到  "+nexyz[0]+nexyz[1]+nexyz[2]);
       //新坐标保存
        x=nexyz[0]+1;
        y=nexyz[1]+1;
        z=nexyz[2]+1;
System.out.println("搜寻旋转角度量。。。");
  //搜寻旋转角度量
//当前位置x点的偏移
chaxvec=new MyPoint(xvec.x-nexyz[0],xvec.y-nexyz[1],xvec.z-nexyz[2]);
//当前位置y点的偏移
chayvec=new MyPoint(yvec.x-nexyz[0],yvec.y-nexyz[1],yvec.z-nexyz[2]);
//当前位置z点的偏移
chazvec=new MyPoint(zvec.x-nexyz[0],zvec.y-nexyz[1],zvec.z-nexyz[2]);
System.out.println("---------x点的偏移"+(xvec.x-nexyz[0])+"  "+(xvec.y-nexyz[1])+"  "+(xvec.z-nexyz[2]));
System.out.println("---------y点的偏移"+(yvec.x-nexyz[0])+"  "+(yvec.y-nexyz[1])+"  "+(yvec.z-nexyz[2]));
System.out.println("---------z点的偏移"+(zvec.x-nexyz[0])+"  "+(zvec.y-nexyz[1])+"  "+(zvec.z-nexyz[2]));
//MoFang.myWait();
  int[] xZJD=shouSuoXYZRotate(yuanx,yuany,yuanz,nexyz[0],nexyz[1],nexyz[2]);
        //重新在3D界面中输出
        //1,计算坐标
        float fx ;
        float fy ;
        float fz ;
        fx=(float)kuaiZhongXinWeizhi*(x-1);
        fy=(float)kuaiZhongXinWeizhi*(y-1);
        fz=(float)kuaiZhongXinWeizhi*(z-1);
        //变换量
        transp.setTranslation(new Vector3f(fx,fy,fz));
//MoFang.myWait();
        transx.rotX(Math.toRadians(xZJD[0]));//anglex
        transy.rotY(Math.toRadians(xZJD[1]));
        transz.rotZ(Math.toRadians(xZJD[2]));
        //生效
 if(closeDonghua)
{        //变换生效
        transGroupx.setTransform(transx);
        transGroupy.setTransform(transy);
        transGroupz.setTransform(transz);
        transGroupp.setTransform(transp);
}
        //不刷新,在动画后刷新,可以输出了,但在动画后
  canNew=true;
        //同时更改positionArray
        MoFang.positionArray[(nexyz[0]+1)][(nexyz[1]+1)][(nexyz[2]+1)].setBlockId(blockIdX,blockIdY,blockIdZ);
    }
    int getChujiao(int xyzQian,int xyzHou)
    {
     int value=0;
        if((xyzQian==-1)&&(xyzHou==-1))
        {
            value= 3*(-1)*45 ;
        }
        else if((xyzQian==-1)&&(xyzHou==1))
        {
            value= 5*(-1)*45 ;
        }
        else if((xyzQian==1)&&(xyzHou==-1))
        {
            value= 1*(-1)*45 ;
        }
        else if((xyzQian==1)&&(xyzHou==1))
        {
            value= 7*(-1)*45 ;
        }
        else if((xyzQian==0)&&(xyzHou==-1))
        {
            value= 2*(-1)*45 ;
        }
        else if((xyzQian==0)&&(xyzHou==1))
        {
            value= 6*(-1)*45 ;
        }
        else if((xyzQian==-1)&&(xyzHou==0))
        {
            value= 4*(-1)*45 ;
        }
        else if((xyzQian==1)&&(xyzHou==0))
        {
            value= 0*(-1)*45 ;
        }
        else if((xyzQian==0)&&(xyzHou==0))
        System.out.println("中间块,返回任意初角");
        else
        System.out.println("参数错误x="+xyzQian+"  y="+xyzHou);
        return value;
    }
}
class Position
{
    int x ;
    int y ;
    int z ;
    int newx ;
    int newy ;
    int newz ;
    boolean haveNew=false ;
    public Position(int i,int j,int k)
    {
        x=i ;
        y=j ;
        z=k ;
    }
    void setBlockId(int blockIdX,int blockIdY,int blockIdZ)
    {
        newx=blockIdX ;
        newy=blockIdY ;
        newz=blockIdZ ;
        haveNew=true ;
        //只要设为true,马上会执行newToOld,使他回到false
    }
    void newToOld()
    {
        x=newx ;
        y=newy ;
        z=newz ;

        haveNew=false ;
        //保证了每一次操作后全为false
    }
    //得到某块u位置
  public static int[] getPxyzFromPositionAy(int x,int y,int z,Position[][][] positionArray)
  {
   int[] p={0,0,0};

   for(int i=0;i<=2;i++)
   for(int j=0;j<=2;j++)
   for(int k=0;k<=2;k++)
   {
    if ((positionArray[i][j][k].x==x)&&(positionArray[i][j][k].y==y)&&(positionArray[i][j][k].z==z))
    {
     p[0]=i;
     p[1]=j;
     p[2]=k;

     return p;
    }
   }
       System.out.println("该方块没有对应的位置");
     return p;
   //这里不该返回任何东西,该抛出异常
  }
}
//Point3f,Vector3f不好用,我自定义的三维变量
class MyPoint
{int x;
int y;
int z;
//构造函数一
MyPoint(int getx,int gety,int getz)
{
 x=getx;
 y=gety;
 z=getz;
}
//构造函数二,转化float为int
MyPoint(float getx,float gety,float getz)
{
 x=(int)getx;
 y=(int)gety;
 z=(int)getz;
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值