package framework.gameMain; import javax.media.j3d.Appearance; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DistanceLOD; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.IndexedTriangleArray; import javax.media.j3d.Material; import javax.media.j3d.Shape3D; import javax.media.j3d.Switch; import javax.media.j3d.TexCoordGeneration; import javax.media.j3d.Texture; import javax.media.j3d.TransparencyAttributes; import javax.vecmath.Color3f; import javax.vecmath.Color4f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import javax.vecmath.Vector4f; import com.sun.j3d.utils.image.TextureLoader; import framework.animation.Animation3D; import framework.animation.PartAnimation; import framework.model3D.BumpMapGenerator; import framework.model3D.FresnelsReflectionMapGenerator; import framework.model3D.Object3D; import framework.model3D.Position3D; import framework.model3D.ReflectionMapGenerator; /** * 水面を表現するオブジェクト。 * 波の動きはバンプマッピングをスクロールさせて表現している。 * 反射ではフレネル効果を加味している。 * Java3Dでは、頂点間の反射ベクトルは線形補間でしか求めていないため、 * 精度を上げるために、LODを使って水面の視点に近い部分はメッシュ分割を細かくしている。 * @author Nitta * */ public class Water extends Animatable { private static int DEFAULT_FIRST_LEVEL_MESH_SIZE = 25; private static int DEFAULT_SECOND_LEVEL_MESH_SIZE = 1; private static int DEFAULT_THIRD_LEVEL_MESH_SIZE = 40; public Water(double minX, double minZ, double maxX, double maxZ, double height, boolean bUseLOD) { this(minX, minZ, maxX, maxZ, height, 0.5f, bUseLOD); } public Water(double minX, double minZ, double maxX, double maxZ, double height, float transparency, boolean bUseLOD) { this(minX, minZ, maxX, maxZ, height, transparency, 0.3f, bUseLOD); } public Water(double minX, double minZ, double maxX, double maxZ, double height, float transparency, float reflection, boolean bUseLOD) { this(minX, minZ, maxX, maxZ, height, transparency, reflection, new Color3f(0.2f, 0.5f, 1.0f), bUseLOD); } public Water(double minX, double minZ, double maxX, double maxZ, double height, float transparency, float reflection, Color3f waterColor, boolean bUseLOD) { this(minX, minZ, maxX, maxZ, height, transparency, reflection, new Color3f(0.2f, 0.5f, 1.0f), DEFAULT_FIRST_LEVEL_MESH_SIZE, DEFAULT_SECOND_LEVEL_MESH_SIZE, DEFAULT_THIRD_LEVEL_MESH_SIZE, bUseLOD); } public Water(double minX, double minZ, double maxX, double maxZ, double height, float transparency, float reflection, int firstLevelMeshSize, int secondLevelMeshSize, int thirdLevelMeshSize, boolean bUseLOD) { this(minX, minZ, maxX, maxZ, height, transparency, reflection, new Color3f(0.2f, 0.5f, 1.0f), firstLevelMeshSize, secondLevelMeshSize, thirdLevelMeshSize, bUseLOD); } public Water(double minX, double minZ, double maxX, double maxZ, double height, float transparency, float reflection, Color3f waterColor, int firstLevelMeshSize, int secondLevelMeshSize, int thirdLevelMeshSize, boolean bUseLOD) { super(null, null); int firstLevelMeshSizeX = firstLevelMeshSize; int firstLevelMeshSizeZ = firstLevelMeshSize; int secondLevelMeshSizeX = secondLevelMeshSize; int secondLevelMeshSizeZ = secondLevelMeshSize; int thirdLevelMeshSizeX = thirdLevelMeshSize; int thirdLevelMeshSizeZ = thirdLevelMeshSize; // 水面のジオメトリの作成 IndexedQuadArray coarseWaterGeometry = new IndexedQuadArray((secondLevelMeshSizeX + 1) * (secondLevelMeshSizeZ + 1), IndexedQuadArray.COORDINATES | IndexedQuadArray.NORMALS, 4 * secondLevelMeshSizeX * secondLevelMeshSizeZ); IndexedQuadArray fineWaterGeometry = new IndexedQuadArray((secondLevelMeshSizeX * thirdLevelMeshSizeX + 1) * (secondLevelMeshSizeZ * thirdLevelMeshSizeZ + 1), IndexedQuadArray.COORDINATES | IndexedQuadArray.NORMALS, 4 * secondLevelMeshSizeX * thirdLevelMeshSizeX * secondLevelMeshSizeZ * thirdLevelMeshSizeZ); double unitSizeX1 = (maxX - minX) / firstLevelMeshSizeX; double unitSizeZ1 = (maxZ - minZ) / firstLevelMeshSizeZ; double unitSizeX2 = unitSizeX1 / secondLevelMeshSizeX; double unitSizeZ2 = unitSizeZ1 / secondLevelMeshSizeZ; double unitSizeX3 = unitSizeX2 / thirdLevelMeshSizeX; double unitSizeZ3 = unitSizeZ2 / thirdLevelMeshSizeZ; double baseX = -(maxX - minX) / firstLevelMeshSizeX / 2.0; double baseZ = -(maxZ - minZ) / firstLevelMeshSizeZ / 2.0; // 第二、第三レベルは LOD で切り替え for (int z2 = 0; z2 < secondLevelMeshSizeZ + 1; z2++) { for (int x2 = 0; x2 < secondLevelMeshSizeX + 1; x2++) { int indexX2 = x2; int indexZ2 = z2; int index20 = indexZ2 * secondLevelMeshSizeX + indexX2; int index21 = indexZ2 * (secondLevelMeshSizeX + 1) + indexX2; int index22 = (indexZ2 + 1) * (secondLevelMeshSizeX + 1) + indexX2; double xx2 = baseX + unitSizeX2 * indexX2; double zz2 = baseZ + unitSizeZ2 * indexZ2; coarseWaterGeometry.setCoordinate(index21, new Point3d(xx2, 0.0, zz2)); coarseWaterGeometry.setNormal(index21, new Vector3f(0.0f, 1.0f, 0.0f)); if (indexX2 < secondLevelMeshSizeX && indexZ2 < secondLevelMeshSizeZ) { coarseWaterGeometry.setCoordinateIndices(index20 * 4, new int[]{index22, index22 + 1, index21 + 1, index21}); if (bUseLOD) { for (int z3 = 0; z3 < thirdLevelMeshSizeZ + 1; z3++) { for (int x3 = 0; x3 < thirdLevelMeshSizeX + 1; x3++) { int indexX3 = x2 * thirdLevelMeshSizeX + x3; int indexZ3 = z2 * thirdLevelMeshSizeZ + z3; int index30 = indexZ3 * (secondLevelMeshSizeX * thirdLevelMeshSizeX) + indexX3; int index31 = indexZ3 * (secondLevelMeshSizeX * thirdLevelMeshSizeX + 1) + indexX3; int index32 = (indexZ3 + 1) * (secondLevelMeshSizeX * thirdLevelMeshSizeX + 1) + indexX3; double xx3 = baseX + unitSizeX3 * indexX3; double zz3 = baseZ + unitSizeZ3 * indexZ3; fineWaterGeometry.setCoordinate(index31, new Point3d(xx3, 0.0, zz3)); fineWaterGeometry.setNormal(index31, new Vector3f(0.0f, 1.0f, 0.0f)); if (indexX3 < secondLevelMeshSizeX * thirdLevelMeshSizeX && indexZ3 < secondLevelMeshSizeZ * thirdLevelMeshSizeZ) { fineWaterGeometry.setCoordinateIndices(index30 * 4, new int[]{index32, index32 + 1, index31 + 1, index31}); } } } } } } } // 第一レベルは Object3D の集合で構成 Object3D children[] = new Object3D[firstLevelMeshSizeX * firstLevelMeshSizeZ]; TextureLoader loaderWater = new TextureLoader("data\\texture\\waternormalMap.jpg", TextureLoader.BY_REFERENCE, null); Texture textureWater = loaderWater.getTexture(); // 水面の表面属性の作成(全体で共有する) Appearance a = new Appearance(); a.setCapability(Appearance.ALLOW_TEXTURE_UNIT_STATE_READ); a.setCapability(Appearance.ALLOW_TEXTURE_UNIT_STATE_WRITE); Material m1 = new Material(); m1.setDiffuseColor(waterColor); // 水の色 m1.setSpecularColor(0.0f, 0.0f, 0.0f); a.setMaterial(m1); ColoringAttributes ca = new ColoringAttributes(); ca.setShadeModel(ColoringAttributes.NICEST); a.setColoringAttributes(ca); TransparencyAttributes ta = new TransparencyAttributes(); ta.setTransparency(transparency); // 透明度 ta.setTransparencyMode(TransparencyAttributes.BLENDED); a.setTransparencyAttributes(ta); // 拡張表面属性(全体で共有する) TexCoordGeneration tcg = new TexCoordGeneration(TexCoordGeneration.OBJECT_LINEAR, TexCoordGeneration.TEXTURE_COORDINATE_3); tcg.setPlaneS(new Vector4f(0.1f, 0.0f, 0.0f, 0.0f)); tcg.setPlaneT(new Vector4f(0.0f, 0.0f, 0.1f, 0.0f)); BumpMapGenerator bumpMapGenerator = new BumpMapGenerator(textureWater, tcg, true); // テクスチャユニットを1つ使用 FresnelsReflectionMapGenerator fresnelsReflectionMapGenerator = new FresnelsReflectionMapGenerator(new Color4f(reflection, reflection, reflection, 1.0f), true); // テクスチャユニットを2つ使用 for (int z1 = 0; z1 < firstLevelMeshSizeZ; z1++) { for (int x1 = 0; x1 < firstLevelMeshSizeX; x1++) { double xx1 = minX + (maxX - minX) / firstLevelMeshSizeX * x1; double zz1 = minZ + (maxZ - minZ) / firstLevelMeshSizeZ * z1; Object3D waterSurfaceUnit; if (bUseLOD) { DistanceLOD lod = new DistanceLOD(); Switch surfaceSwitch = new Switch(); surfaceSwitch.addChild(new Shape3D(fineWaterGeometry, a)); surfaceSwitch.addChild(new Shape3D(coarseWaterGeometry, a)); lod.addSwitch(surfaceSwitch); lod.setDistance(0, unitSizeX1 / 2.0); lod.setPosition(new Point3f(0.0f, 0.0f, 0.0f)); waterSurfaceUnit = new Object3D("waterUnit", lod); } else { waterSurfaceUnit = new Object3D("waterUnit", new Shape3D(coarseWaterGeometry, a)); } waterSurfaceUnit.apply(new Position3D(xx1, height, zz1), false); // 拡張表面属性を設定 waterSurfaceUnit.setBumpMapping(bumpMapGenerator); waterSurfaceUnit.setReflectionMapping(fresnelsReflectionMapGenerator); children[z1 * firstLevelMeshSizeX + x1] = waterSurfaceUnit; } } // 全体オブジェクトを構成 Object3D waterSurface = new Object3D("water", children); body = waterSurface; // アニメーションの作成 Animation3D waveAnimation = new Animation3D(); PartAnimation pa = new PartAnimation("waterUnit", 0); pa.addTexture(0L, new Position3D(0.0, 0.0, 0.0)); pa.addTexture(10000L, new Position3D(1.0, -1.0, 0.0)); waveAnimation.addPartAnimation(pa); animation = waveAnimation; } @Override public void onEndAnimation() { } }