I've been doing a lot of voxel terrain generation with JavaFX 3D. For some reason, whenever I generate a landscape, the right side of the landscape will always have these weird black lines. I've tried changing the PerspectiveCamera
's near and far clip values, but they seem to have no effect. In case you need it, my near clip value is set to 0.1
and my far clip value is set to 100000.0
.
Here is a picture of my program, as you can see, there are clear visual glitches present mainly on the right side of the landscape.
I'm pretty sure the visual glitch is caused by the fact that each cube is its own object. So in the image above, I create 22500 cube objects and placed them with adjusted colors to form the landscape.
Is there a way to create a massive Mesh to replace my large amounts of cube objects, while still retaining the colors?
Thanks for all your help!
Merging cubes
With the use of ScatterMesh
from the FXyz library you can merge all the individual meshes from your cube objects (I take each cube is a Box
), into one single big triangle mesh, while keeping their unique color.
Given that you have a set of 3D coordinates, each one defining a cube, and a given color per coordinate (cube), this small code snippet demonstrates how to do it. It is based on the use of CUBE
markers that will create a CuboidMesh
per each point in the scatter plot.
List<Double> coordinates = Arrays.asList(x0,y0,z0,...); // n points
List<Color> colors = Arrays.asList(new Color(),...); // n colors
// create org.fxyz3d.geometry.Point3D for each cube with x,y,z,f (index of color)
List<Point3D> cubes = new ArrayList<>();
AtomicInteger i = new AtomicInteger();
colors.stream()
.forEach(c -> cubes.add(new Point3D(coordinates.get(i.getAndIncrement()),
coordinates.get(i.getAndIncrement()),
coordinates.get(i.getAndIncrement()),
colors.indexOf(c))));
// create scatterMesh from cubes, each cube with a size of 20 pixels
ScatterMesh scatterMesh = new ScatterMesh(cubes, 20);
// apply `CUBE` marker
scatterMesh.setMarker(MarkerFactory.Marker.CUBE);
// apply same `f` value to all 8 vertices of each cube
scatterMesh.setFunctionData(cubes.stream()
.flatMap(p -> Collections.nCopies(8, p.f).stream()).collect(Collectors.toList()));
// Apply texture based on the list of colors
scatter.setTextureModeVertices3D(new Palette.ListColorPalette(colors), p -> p.f);
With this approach you will have as a result a single mesh. Accessing the cubes can be done with PickResult
and some logic to find the cube from the given coordinates.
Another option that could simplify identifying cubes of a given terrain height is to use a ScatterMesh
per given height (that will have the same color):
List<ScatterMesh> scatterMeshList = new ArrayList<>();
// Collect different heights
List<Float> heights = cubes.stream()
.map(b -> b.z)
.distinct()
.collect(Collectors.toList());
scatterMeshList = heights.stream()
.map(h -> {
List<Point3D> cubesPerH = cubes.stream()
.filter(p -> p.z == h)
.collect(Collectors.toList());
int colorIndex = (int)cubesPerH.get(0).f;
ScatterMesh scatterMesh = new ScatterMesh(cubesPerH, 20);
scatterMesh.setMarker(MarkerFactory.Marker.CUBE);
scatterMesh.setTextureModeNone(colors.get(colorIndex));
return scatterMesh;
})
.collect(Collectors.toList());
Artifacts
Having a single or a small number of scatter meshes instead of hundreds or thousands of cube meshes is obviously better in terms of performance.
However, this might not solve the issue with the artifacts that appear when rendering a JavaFX 3D shape with a given texture. This is a known issue, but I didn't find it filed though, so it should definitely be reported.