使用 JavaFX 3D 创建坐标网格最实用的方法是什么?
What is the most practical way to create coordinate grid with JavaFX 3D?
我想用 JavaFX 创建一个 3D 演示应用程序来可视化 3D 中点的移动 space,首先我需要设置一个坐标网格以供视觉参考。不幸的是,我找不到像这张图片中这样的网格的示例代码:
有谁知道创建类似的东西最实用的方法是什么?
已经有一些解决方案了。
FXyz3D library has a CubeWorld
class,正好给你一个参考网格。
使用起来相当简单。只需从 JCenter 导入 'org.fxyz3d:fxyz3d:0.3.0'
依赖项并使用它:
CubeWorld cubeWorld = new CubeWorld(5000, 500, true);
Sphere sphere = new Sphere(100);
sphere.setMaterial(new PhongMaterial(Color.FIREBRICK));
sphere.getTransforms().add(new Translate(100, 200, 300));
Scene scene = new Scene(new Group(cubeWorld, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
如您所见,解决方案是基于对每个面使用 2D 矩形,并使用 3D 圆柱体创建网格线。它有很好的功能(比如自闪电或根据相机的正面不显示网格),但它的节点非常密集(上面的示例有 168 个节点)。
还有其他使用较少节点的解决方案。比如这个sample,正好也和Leap Motion有关,我就用了一个TriangleMesh
.
这是一个简单的解决方案,而且只有两个网格。但是,您看到的是三角形,而不是正方形。
所以让我们尝试摆脱三角形。为此,我将使用 PolygonMesh
,就像在另一个 , based on the 3DViewer project that is available at the OpenJFX repository, contains already a PolygonalMesh 实现中一样,它允许每个面有任意数量的点,因此任何多边形都可以是面。
这将为您提供基于方形面的平面网格:
private PolygonMesh createQuadrilateralMesh(float width, float height, int subDivX, int subDivY) {
final float minX = - width / 2f;
final float minY = - height / 2f;
final float maxX = width / 2f;
final float maxY = height / 2f;
final int pointSize = 3;
final int texCoordSize = 2;
// 4 point indices and 4 texCoord indices per face
final int faceSize = 8;
int numDivX = subDivX + 1;
int numVerts = (subDivY + 1) * numDivX;
float points[] = new float[numVerts * pointSize];
float texCoords[] = new float[numVerts * texCoordSize];
int faceCount = subDivX * subDivY;
int faces[][] = new int[faceCount][faceSize];
// Create points and texCoords
for (int y = 0; y <= subDivY; y++) {
float dy = (float) y / subDivY;
double fy = (1 - dy) * minY + dy * maxY;
for (int x = 0; x <= subDivX; x++) {
float dx = (float) x / subDivX;
double fx = (1 - dx) * minX + dx * maxX;
int index = y * numDivX * pointSize + (x * pointSize);
points[index] = (float) fx;
points[index + 1] = (float) fy;
points[index + 2] = 0.0f;
index = y * numDivX * texCoordSize + (x * texCoordSize);
texCoords[index] = dx;
texCoords[index + 1] = dy;
}
}
// Create faces
int index = 0;
for (int y = 0; y < subDivY; y++) {
for (int x = 0; x < subDivX; x++) {
int p00 = y * numDivX + x;
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
int tc00 = y * numDivX + x;
int tc01 = tc00 + 1;
int tc10 = tc00 + numDivX;
int tc11 = tc10 + 1;
faces[index][0] = p00;
faces[index][1] = tc00;
faces[index][2] = p10;
faces[index][3] = tc10;
faces[index][4] = p11;
faces[index][5] = tc11;
faces[index][6] = p01;
faces[index++][7] = tc01;
}
}
int[] smooth = new int[faceCount];
PolygonMesh mesh = new PolygonMesh(points, texCoords, faces);
mesh.getFaceSmoothingGroups().addAll(smooth);
return mesh;
}
因此您可以使用其中的 2 个或 3 个来创建这样的坐标系:
public Group createGrid(float size, float delta) {
if (delta < 1) {
delta = 1;
}
final PolygonMesh plane = createQuadrilateralMesh(size, size, (int) (size / delta), (int) (size / delta));
final PolygonMesh plane2 = createQuadrilateralMesh(size, size, (int) (size / delta / 5), (int) (size / delta / 5));
PolygonMeshView meshViewXY = new PolygonMeshView(plane);
meshViewXY.setDrawMode(DrawMode.LINE);
meshViewXY.setCullFace(CullFace.NONE);
PolygonMeshView meshViewXZ = new PolygonMeshView(plane);
meshViewXZ.setDrawMode(DrawMode.LINE);
meshViewXZ.setCullFace(CullFace.NONE);
meshViewXZ.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ = new PolygonMeshView(plane);
meshViewYZ.setDrawMode(DrawMode.LINE);
meshViewYZ.setCullFace(CullFace.NONE);
meshViewYZ.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
PolygonMeshView meshViewXY2 = new PolygonMeshView(plane2);
meshViewXY2.setDrawMode(DrawMode.LINE);
meshViewXY2.setCullFace(CullFace.NONE);
meshViewXY2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
PolygonMeshView meshViewXZ2 = new PolygonMeshView(plane2);
meshViewXZ2.setDrawMode(DrawMode.LINE);
meshViewXZ2.setCullFace(CullFace.NONE);
meshViewXZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewXZ2.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ2 = new PolygonMeshView(plane2);
meshViewYZ2.setDrawMode(DrawMode.LINE);
meshViewYZ2.setCullFace(CullFace.NONE);
meshViewYZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewYZ2.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
return new Group(meshViewXY, meshViewXY2, meshViewXZ, meshViewXZ2 /*, meshViewYZ, meshViewYZ2 */);
}
请注意,我已经复制了平面以每 5 行模拟一个更宽的笔划。
最后添加坐标轴:
public Group getAxes(double scale) {
Cylinder axisX = new Cylinder(1, 200);
axisX.getTransforms().addAll(new Rotate(90, Rotate.Z_AXIS), new Translate(0, -100, 0));
axisX.setMaterial(new PhongMaterial(Color.RED));
Cylinder axisY = new Cylinder(1, 200);
axisY.getTransforms().add(new Translate(0, 100, 0));
axisY.setMaterial(new PhongMaterial(Color.GREEN));
Cylinder axisZ = new Cylinder(1, 200);
axisZ.setMaterial(new PhongMaterial(Color.BLUE));
axisZ.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS), new Translate(0, 100, 0));
Group group = new Group(axisX, axisY, axisZ);
group.getTransforms().add(new Scale(scale, scale, scale));
return group;
}
现在你有:
final Group axes = getAxes(0.5);
final Group grid = createGrid(200, 10);
final Sphere sphere = new Sphere(5);
sphere.getTransforms().add(new Translate(20, 15, 40));
Scene scene = new Scene(new Group(axes, grid, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
本次样本的节点总数为14。
当然,还可以添加标签等诸多功能进行改进。
我想用 JavaFX 创建一个 3D 演示应用程序来可视化 3D 中点的移动 space,首先我需要设置一个坐标网格以供视觉参考。不幸的是,我找不到像这张图片中这样的网格的示例代码:
有谁知道创建类似的东西最实用的方法是什么?
已经有一些解决方案了。
FXyz3D library has a CubeWorld
class,正好给你一个参考网格。
使用起来相当简单。只需从 JCenter 导入 'org.fxyz3d:fxyz3d:0.3.0'
依赖项并使用它:
CubeWorld cubeWorld = new CubeWorld(5000, 500, true);
Sphere sphere = new Sphere(100);
sphere.setMaterial(new PhongMaterial(Color.FIREBRICK));
sphere.getTransforms().add(new Translate(100, 200, 300));
Scene scene = new Scene(new Group(cubeWorld, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
如您所见,解决方案是基于对每个面使用 2D 矩形,并使用 3D 圆柱体创建网格线。它有很好的功能(比如自闪电或根据相机的正面不显示网格),但它的节点非常密集(上面的示例有 168 个节点)。
还有其他使用较少节点的解决方案。比如这个sample,正好也和Leap Motion有关,我就用了一个TriangleMesh
.
这是一个简单的解决方案,而且只有两个网格。但是,您看到的是三角形,而不是正方形。
所以让我们尝试摆脱三角形。为此,我将使用 PolygonMesh
,就像在另一个
这将为您提供基于方形面的平面网格:
private PolygonMesh createQuadrilateralMesh(float width, float height, int subDivX, int subDivY) {
final float minX = - width / 2f;
final float minY = - height / 2f;
final float maxX = width / 2f;
final float maxY = height / 2f;
final int pointSize = 3;
final int texCoordSize = 2;
// 4 point indices and 4 texCoord indices per face
final int faceSize = 8;
int numDivX = subDivX + 1;
int numVerts = (subDivY + 1) * numDivX;
float points[] = new float[numVerts * pointSize];
float texCoords[] = new float[numVerts * texCoordSize];
int faceCount = subDivX * subDivY;
int faces[][] = new int[faceCount][faceSize];
// Create points and texCoords
for (int y = 0; y <= subDivY; y++) {
float dy = (float) y / subDivY;
double fy = (1 - dy) * minY + dy * maxY;
for (int x = 0; x <= subDivX; x++) {
float dx = (float) x / subDivX;
double fx = (1 - dx) * minX + dx * maxX;
int index = y * numDivX * pointSize + (x * pointSize);
points[index] = (float) fx;
points[index + 1] = (float) fy;
points[index + 2] = 0.0f;
index = y * numDivX * texCoordSize + (x * texCoordSize);
texCoords[index] = dx;
texCoords[index + 1] = dy;
}
}
// Create faces
int index = 0;
for (int y = 0; y < subDivY; y++) {
for (int x = 0; x < subDivX; x++) {
int p00 = y * numDivX + x;
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
int tc00 = y * numDivX + x;
int tc01 = tc00 + 1;
int tc10 = tc00 + numDivX;
int tc11 = tc10 + 1;
faces[index][0] = p00;
faces[index][1] = tc00;
faces[index][2] = p10;
faces[index][3] = tc10;
faces[index][4] = p11;
faces[index][5] = tc11;
faces[index][6] = p01;
faces[index++][7] = tc01;
}
}
int[] smooth = new int[faceCount];
PolygonMesh mesh = new PolygonMesh(points, texCoords, faces);
mesh.getFaceSmoothingGroups().addAll(smooth);
return mesh;
}
因此您可以使用其中的 2 个或 3 个来创建这样的坐标系:
public Group createGrid(float size, float delta) {
if (delta < 1) {
delta = 1;
}
final PolygonMesh plane = createQuadrilateralMesh(size, size, (int) (size / delta), (int) (size / delta));
final PolygonMesh plane2 = createQuadrilateralMesh(size, size, (int) (size / delta / 5), (int) (size / delta / 5));
PolygonMeshView meshViewXY = new PolygonMeshView(plane);
meshViewXY.setDrawMode(DrawMode.LINE);
meshViewXY.setCullFace(CullFace.NONE);
PolygonMeshView meshViewXZ = new PolygonMeshView(plane);
meshViewXZ.setDrawMode(DrawMode.LINE);
meshViewXZ.setCullFace(CullFace.NONE);
meshViewXZ.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ = new PolygonMeshView(plane);
meshViewYZ.setDrawMode(DrawMode.LINE);
meshViewYZ.setCullFace(CullFace.NONE);
meshViewYZ.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
PolygonMeshView meshViewXY2 = new PolygonMeshView(plane2);
meshViewXY2.setDrawMode(DrawMode.LINE);
meshViewXY2.setCullFace(CullFace.NONE);
meshViewXY2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
PolygonMeshView meshViewXZ2 = new PolygonMeshView(plane2);
meshViewXZ2.setDrawMode(DrawMode.LINE);
meshViewXZ2.setCullFace(CullFace.NONE);
meshViewXZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewXZ2.getTransforms().add(new Rotate(90, Rotate.X_AXIS));
PolygonMeshView meshViewYZ2 = new PolygonMeshView(plane2);
meshViewYZ2.setDrawMode(DrawMode.LINE);
meshViewYZ2.setCullFace(CullFace.NONE);
meshViewYZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
meshViewYZ2.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));
return new Group(meshViewXY, meshViewXY2, meshViewXZ, meshViewXZ2 /*, meshViewYZ, meshViewYZ2 */);
}
请注意,我已经复制了平面以每 5 行模拟一个更宽的笔划。
最后添加坐标轴:
public Group getAxes(double scale) {
Cylinder axisX = new Cylinder(1, 200);
axisX.getTransforms().addAll(new Rotate(90, Rotate.Z_AXIS), new Translate(0, -100, 0));
axisX.setMaterial(new PhongMaterial(Color.RED));
Cylinder axisY = new Cylinder(1, 200);
axisY.getTransforms().add(new Translate(0, 100, 0));
axisY.setMaterial(new PhongMaterial(Color.GREEN));
Cylinder axisZ = new Cylinder(1, 200);
axisZ.setMaterial(new PhongMaterial(Color.BLUE));
axisZ.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS), new Translate(0, 100, 0));
Group group = new Group(axisX, axisY, axisZ);
group.getTransforms().add(new Scale(scale, scale, scale));
return group;
}
现在你有:
final Group axes = getAxes(0.5);
final Group grid = createGrid(200, 10);
final Sphere sphere = new Sphere(5);
sphere.getTransforms().add(new Translate(20, 15, 40));
Scene scene = new Scene(new Group(axes, grid, sphere), 800, 800, true, SceneAntialiasing.BALANCED);
本次样本的节点总数为14。
当然,还可以添加标签等诸多功能进行改进。