package framework.model3D; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingBox; import javax.media.j3d.Shape3D; import javax.media.j3d.TriangleStripArray; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; /** * メッシュで構成された地形用のオブジェクト(衝突判定が高速化されている) * @author 新田直也 * */ public class Terrain3D extends BaseObject3D { static final int MESH_SIZE = 4; private Position3D origin; private double sizeX = 0; private double sizeZ = 0; private double heightMap[][] = null; private int meshX = 0; private int meshZ = 0; private TriangleStripArray triStripAttay = null; public Terrain3D(Position3D origin, double sizeX, double sizeZ, double heightMap[][], Appearance a) { super(); this.origin = origin; this.sizeX = sizeX; this.sizeZ = sizeZ; this.heightMap = heightMap; meshX = heightMap[0].length; meshZ = heightMap.length; int stripVertexCounts[] = new int[meshZ - 1]; for (int n = 0; n < meshZ - 1; n++) { stripVertexCounts[n] = meshX * 2; } triStripAttay = new TriangleStripArray(meshX * 2 * (meshZ - 1), TriangleStripArray.COORDINATES | TriangleStripArray.NORMALS, stripVertexCounts); int index = 0; for (int z = 0; z < meshZ; z++) { for (int x = 0; x < meshX; x++) { // メッシュごとに三角形を2つづつ生成 if (z < meshZ - 1) { // 頂点座標の設定 // 頂点の法線ベクトルの設定 triStripAttay.setCoordinate(index, new Point3d(origin.getX() + sizeX * (double)x, origin.getY() + heightMap[z][x], origin.getZ() + sizeZ * (double)z)); Vector3f normal = calcNormal(z, x); normal.normalize(); triStripAttay.setNormal(index, normal); triStripAttay.setCoordinate(index + 1, new Point3d(origin.getX() + sizeX * (double)x, origin.getY() + heightMap[z + 1][x], origin.getZ() + sizeZ * (double)(z + 1))); normal = calcNormal(z + 1, x); normal.normalize(); triStripAttay.setNormal(index + 1, normal); index += 2; } } } Appearance a2 = (Appearance)a.cloneNodeComponent(true); a2.setCapability(Appearance.ALLOW_MATERIAL_READ); a2.setCapability(Appearance.ALLOW_TEXTURE_READ); a2.setCapability(Appearance.ALLOW_TEXTURE_WRITE); a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_READ); a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE); Shape3D shape = new Shape3D(triStripAttay, a2); shape.setCapability(Shape3D.ALLOW_APPEARANCE_READ); shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); center.addChild(shape); } private Vector3f calcNormal(int z, int x) { Vector3f normal = new Vector3f(); if (x < meshX - 1) { Vector3f xp = new Vector3f((float)sizeX, (float)(heightMap[z][x+1] - heightMap[z][x]), 0.0f); if (z < meshZ - 1) { Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); Vector3f v1 = new Vector3f(); v1.cross(zp, xp); v1.normalize(); normal.add(v1); } if (z > 0) { Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); Vector3f v1 = new Vector3f(); v1.cross(xp, zm); v1.normalize(); normal.add(v1); } } if (x > 0) { Vector3f xm = new Vector3f(-(float)sizeX, (float)(heightMap[z][x-1] - heightMap[z][x]), 0.0f); if (z < meshZ - 1) { Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); Vector3f v1 = new Vector3f(); v1.cross(xm, zp); v1.normalize(); normal.add(v1); } if (z > 0) { Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); Vector3f v1 = new Vector3f(); v1.cross(zm, xm); v1.normalize(); normal.add(v1); } } return normal; } /** * 衝突判定用のボリューム(ポリゴンと粗い判定用の直方体)を取得する * @return 衝突判定用のボリューム列 */ public BoundingSurface[] getBoundingSurfaces() { if (boundingSurfaces == null) { if (triStripAttay == null) return null; double coordinate1[] = new double[3]; double coordinate2[] = new double[3]; double coordinate3[] = new double[3]; double coordinate4[] = new double[3]; // GeometryArrayの習得 if (triStripAttay instanceof TriangleStripArray) { // IndexedTriangleStripArray の場合 // 8 x 8 メッシュ単位でまとめる BoundingSurface surfaces[] = new BoundingSurface[((meshX + MESH_SIZE - 2) / MESH_SIZE) * ((meshZ + MESH_SIZE - 2) / MESH_SIZE)]; int n = 0; for (int j = 0; j < meshZ - 1; j += MESH_SIZE) { for (int i = 0; i < meshX - 1; i += MESH_SIZE) { BoundingSurface parent = new BoundingSurface(); double lowY = 1.0; double highY = -1.0; for (int j2 = 0; j + j2 < meshZ - 1 && j2 < MESH_SIZE; j2++) { for (int i2 = 0; i + i2 < meshX - 1 && i2 < MESH_SIZE; i2++) { int index = (j + j2) * meshX * 2 + (i + i2) * 2; triStripAttay.getCoordinates(index, coordinate1); triStripAttay.getCoordinates(index + 1, coordinate2); triStripAttay.getCoordinates(index + 2, coordinate3); triStripAttay.getCoordinates(index + 3, coordinate4); if (lowY > highY) { lowY = highY = coordinate1[1]; } else { if (lowY > coordinate1[1]) { lowY = coordinate1[1]; } else if (highY < coordinate1[1]) { highY = coordinate1[1]; } if (lowY > coordinate2[1]) { lowY = coordinate2[1]; } else if (highY < coordinate2[1]) { highY = coordinate2[1]; } if (lowY > coordinate3[1]) { lowY = coordinate3[1]; } else if (highY < coordinate3[1]) { highY = coordinate3[1]; } if (lowY > coordinate4[1]) { lowY = coordinate4[1]; } else if (highY < coordinate4[1]) { highY = coordinate4[1]; } } // 1 x 1 メッシュ内のBoundingSurface Vector3d v1 = new Vector3d(coordinate1); Vector3d v2 = new Vector3d(coordinate2); Vector3d v3 = new Vector3d(coordinate3); Vector3d v4 = new Vector3d(coordinate4); BoundingSurface bSurface = new BoundingSurface(); bSurface.addVertex((Vector3d)v1.clone()); //1つめの三角形 bSurface.addVertex((Vector3d)v2.clone()); bSurface.addVertex((Vector3d)v3.clone()); bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); parent.addChild(bSurface, false); bSurface = new BoundingSurface(); bSurface.addVertex((Vector3d)v2.clone()); //2つめの三角形 bSurface.addVertex((Vector3d)v4.clone()); bSurface.addVertex((Vector3d)v3.clone()); bSurface.setBounds(createBoundingPolytope(v2, v4, v3)); parent.addChild(bSurface, true); } } // 8 x 8 メッシュ単位のBoundingSurface triStripAttay.getCoordinates(j * meshX * 2 + i * 2, coordinate1); parent.setBounds(new BoundingBox(new Point3d(coordinate1[0], lowY, coordinate1[2]), new Point3d(coordinate4[0], highY, coordinate4[2]))); surfaces[n] = parent; n++; } } boundingSurfaces = surfaces; } else { return null; } } return boundingSurfaces; } public double getHeight(double x, double z) { int i = (int)((x - origin.getX()) / sizeX); int j = (int)((z - origin.getZ()) / sizeZ); if (i >= meshX || i < 0 || j >= meshZ || j < 0) return 0.0; int index = j * meshX * 2 + i * 2; double coordinate1[] = new double[3]; double coordinate2[] = new double[3]; double coordinate3[] = new double[3]; double coordinate4[] = new double[3]; triStripAttay.getCoordinates(index, coordinate1); triStripAttay.getCoordinates(index + 1, coordinate2); triStripAttay.getCoordinates(index + 2, coordinate3); triStripAttay.getCoordinates(index + 3, coordinate4); Vector3d v1 = new Vector3d(coordinate1); Vector3d v2 = new Vector3d(coordinate2); Vector3d v3 = new Vector3d(coordinate3); Vector3d v4 = new Vector3d(coordinate4); Vector3d p1 = new Vector3d(x, 0.0, z); Vector3d p2 = new Vector3d(x, 1.0, z); double x2 = x - (double)i * sizeX - origin.getX(); double z2 = z - (double)j * sizeZ - origin.getZ(); if (x2 < GeometryUtility.TOLERANCE || (meshZ - z2) / x2 > meshZ / meshX) { // 1つめの三角形を通る場合 Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v1, v2, v3), p1, p2); return crossPoint.getY(); } else { // 2つめの三角形を通る場合 Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v2, v4, v3), p1, p2); return crossPoint.getY(); } } } // // ********** IndexedTriangleStripArray を使うバージョン ********** // (IndexedTriangleStripArrayを用いるとメモリを節約できるが、シェーディングができないので注意) // //public class Terrain3D extends BaseObject3D { // static final int MESH_SIZE = 8; // private Position3D origin; // private double sizeX = 0; // private double sizeZ = 0; // private double heightMap[][] = null; // private int meshX = 0; // private int meshZ = 0; // private IndexedTriangleStripArray triStripAttay = null; // // public Terrain3D(Position3D origin, double sizeX, double sizeZ, double heightMap[][], Appearance a) { // super(); // this.origin = origin; // this.sizeX = sizeX; // this.sizeZ = sizeZ; // this.heightMap = heightMap; // meshX = heightMap[0].length; // meshZ = heightMap.length; // // int stripVertexCounts[] = new int[meshZ - 1]; // for (int n = 0; n < meshZ - 1; n++) { // stripVertexCounts[n] = meshX * 2; // } // triStripAttay = new IndexedTriangleStripArray(meshX * meshZ, // IndexedTriangleStripArray.COORDINATES | IndexedTriangleStripArray.NORMALS, // meshX * 2 * (meshZ - 1), // stripVertexCounts); // int index = 0, index2 = 0, i1, i2; // for (int z = 0; z < meshZ; z++) { // for (int x = 0; x < meshX; x++) { // // 頂点座標の設定 // triStripAttay.setCoordinate(index, // new Point3d(origin.getX() + sizeX * (double)x, // origin.getY() + heightMap[z][x], // origin.getZ() + sizeZ * (double)z)); // // 頂点の法線ベクトルの設定 // Vector3f normal = new Vector3f(); // if (x < meshX - 1) { // Vector3f xp = new Vector3f((float)sizeX, (float)(heightMap[z][x+1] - heightMap[z][x]), 0.0f); // if (z < meshZ - 1) { // Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); // Vector3f v1 = new Vector3f(); // v1.cross(zp, xp); // v1.normalize(); // normal.add(v1); // } // if (z > 0) { // Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); // Vector3f v1 = new Vector3f(); // v1.cross(xp, zm); // v1.normalize(); // normal.add(v1); // } // } // if (x > 0) { // Vector3f xm = new Vector3f(-(float)sizeX, (float)(heightMap[z][x-1] - heightMap[z][x]), 0.0f); // if (z < meshZ - 1) { // Vector3f zp = new Vector3f(0.0f, (float)(heightMap[z+1][x] - heightMap[z][x]), (float)sizeZ); // Vector3f v1 = new Vector3f(); // v1.cross(xm, zp); // v1.normalize(); // normal.add(v1); // } // if (z > 0) { // Vector3f zm = new Vector3f(0.0f, (float)(heightMap[z-1][x] - heightMap[z][x]), -(float)sizeZ); // Vector3f v1 = new Vector3f(); // v1.cross(zm, xm); // v1.normalize(); // normal.add(v1); // } // } // normal.normalize(); // triStripAttay.setNormal(index, normal); // index++; // // メッシュごとに三角形を2つづつ生成 // if (z < meshZ - 1) { // i1 = z * meshX + x; // i2 = (z + 1) * meshX + x; // triStripAttay.setCoordinateIndices(index2, new int[]{i1, i2}); // index2 += 2; // } // } // } // Appearance a2 = (Appearance)a.cloneNodeComponent(true); // a2.setCapability(Appearance.ALLOW_MATERIAL_READ); // a2.setCapability(Appearance.ALLOW_TEXTURE_READ); // a2.setCapability(Appearance.ALLOW_TEXTURE_WRITE); // a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_READ); // a2.setCapability(Appearance.ALLOW_TEXTURE_ATTRIBUTES_WRITE); // Shape3D shape = new Shape3D(triStripAttay, a2); // shape.setCapability(Shape3D.ALLOW_APPEARANCE_READ); // shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE); // center.addChild(shape); // } // // public BoundingSurface[] getBoundingSurfaces() { // if (boundingSurfaces == null) { // if (triStripAttay == null) return null; // double coordinate1[] = new double[3]; // double coordinate2[] = new double[3]; // double coordinate3[] = new double[3]; // double coordinate4[] = new double[3]; // // // GeometryArrayの習得 // if (triStripAttay instanceof IndexedTriangleStripArray) { // // IndexedTriangleStripArray の場合 // // 8 x 8 メッシュ単位でまとめる // BoundingSurface surfaces[] = new BoundingSurface[((meshX + MESH_SIZE - 2) / MESH_SIZE) * ((meshZ + MESH_SIZE - 2) / MESH_SIZE)]; // int n = 0; // for (int j = 0; j < meshZ - 1; j += MESH_SIZE) { // for (int i = 0; i < meshX - 1; i += MESH_SIZE) { // BoundingSurface parent = new BoundingSurface(); // double lowY = 1.0; // double highY = -1.0; // for (int j2 = 0; j + j2 < meshZ - 1 && j2 < MESH_SIZE; j2++) { // for (int i2 = 0; i + i2 < meshX - 1 && i2 < MESH_SIZE; i2++) { // int index = (j + j2) * meshX * 2 + (i + i2) * 2; // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); // if (lowY > highY) { // lowY = highY = coordinate1[1]; // } else { // if (lowY > coordinate1[1]) { // lowY = coordinate1[1]; // } else if (highY < coordinate1[1]) { // highY = coordinate1[1]; // } // if (lowY > coordinate2[1]) { // lowY = coordinate2[1]; // } else if (highY < coordinate2[1]) { // highY = coordinate2[1]; // } // if (lowY > coordinate3[1]) { // lowY = coordinate3[1]; // } else if (highY < coordinate3[1]) { // highY = coordinate3[1]; // } // if (lowY > coordinate4[1]) { // lowY = coordinate4[1]; // } else if (highY < coordinate4[1]) { // highY = coordinate4[1]; // } // } // // // 1 x 1 メッシュ内のBoundingSurface // Vector3d v1 = new Vector3d(coordinate1); // Vector3d v2 = new Vector3d(coordinate2); // Vector3d v3 = new Vector3d(coordinate3); // Vector3d v4 = new Vector3d(coordinate4); // BoundingSurface bSurface = new BoundingSurface(); // bSurface.addVertex((Vector3d)v1.clone()); //1つめの三角形 // bSurface.addVertex((Vector3d)v2.clone()); // bSurface.addVertex((Vector3d)v3.clone()); // bSurface.setBounds(createBoundingPolytope(v1, v2, v3)); // parent.addChild(bSurface, false); // // bSurface = new BoundingSurface(); // bSurface.addVertex((Vector3d)v2.clone()); //2つめの三角形 // bSurface.addVertex((Vector3d)v4.clone()); // bSurface.addVertex((Vector3d)v3.clone()); // bSurface.setBounds(createBoundingPolytope(v2, v4, v3)); // parent.addChild(bSurface, true); // } // } // // 8 x 8 メッシュ単位のBoundingSurface // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(j * meshX * 2 + i * 2), coordinate1); // parent.setBounds(new BoundingBox(new Point3d(coordinate1[0], lowY, coordinate1[2]), // new Point3d(coordinate4[0], highY, coordinate4[2]))); // surfaces[n] = parent; // n++; // } // } // boundingSurfaces = surfaces; // } else { // return null; // } // } // return boundingSurfaces; // } // // public double getHeight(double x, double z) { // int i = (int)((x - origin.getX()) / sizeX); // int j = (int)((z - origin.getZ()) / sizeZ); // if (i >= meshX || i < 0 || j >= meshZ || j < 0) return 0.0; // // int index = j * meshX * 2 + i * 2; // double coordinate1[] = new double[3]; // double coordinate2[] = new double[3]; // double coordinate3[] = new double[3]; // double coordinate4[] = new double[3]; // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index), coordinate1); // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+1), coordinate2); // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+2), coordinate3); // triStripAttay.getCoordinates(triStripAttay.getCoordinateIndex(index+3), coordinate4); // Vector3d v1 = new Vector3d(coordinate1); // Vector3d v2 = new Vector3d(coordinate2); // Vector3d v3 = new Vector3d(coordinate3); // Vector3d v4 = new Vector3d(coordinate4); // Vector3d p1 = new Vector3d(x, 0.0, z); // Vector3d p2 = new Vector3d(x, 1.0, z); // // double x2 = x - (double)i * sizeX - origin.getX(); // double z2 = z - (double)j * sizeZ - origin.getZ(); // if (x2 < GeometryUtility.TOLERANCE || (meshZ - z2) / x2 > meshZ / meshX) { // // 1つめの三角形を通る場合 // Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v1, v2, v3), p1, p2); // return crossPoint.getY(); // } else { // // 2つめの三角形を通る場合 // Vector3d crossPoint = GeometryUtility.intersect(GeometryUtility.createPlane(v2, v4, v3), p1, p2); // return crossPoint.getY(); // } // } //}