2

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!

Gamer818
  • 102
  • 11
  • 1
    Have you enabled the depth buffer? This looks like the stuff that is further from the camera gets rendered on top of stuff that is closer... – fabian Dec 23 '19 at 11:31
  • @fabian After enabling the depth buffer in the `Scene`'s constructor, the result is still the same. – Gamer818 Dec 23 '19 at 11:43
  • 1
    Check FXyz library https://github.com/FXyz/FXyz, there is a [`MeshHelper`](https://github.com/FXyz/FXyz/blob/master/FXyz-Core/src/main/java/org/fxyz3d/shapes/primitives/helper/MeshHelper.java) utility class used for merging meshes. Each cube could keep the color data, so that can be used later on to easily generate the texture mapping. – José Pereda Dec 23 '19 at 13:30
  • @JoséPereda So I would have each cube be a TriangleMesh instead of a Box, right? What about setting the color data? If possible, may you give me an example? – Gamer818 Dec 23 '19 at 13:34
  • 1
    Each cube will be a `TriangleMesh` (you can use the `CuboidMesh` for instance), as access to the mesh data is required (Box doesn't export it). Basically all you need per voxel is the 3D coordinates of its center and one float value that can be mapped to a palette of colors (and the size of the voxel of course). Is your project OSS? – José Pereda Dec 23 '19 at 13:46
  • @JoséPereda I'm sorry but I don't know what OSS means. Anyhow, I've imported the library and changed all my boxes into CuboidMeshes and changed the color of the meshes using material. If I merge my current meshes into a single mesh, will the colors remain the same, or do I have to use the float value thing that you mentioned? – Gamer818 Dec 23 '19 at 13:49
  • 1
    I asked if your project was open source, so I could help you with it (and maybe you will be also willing to contribute to FXyz). Otherwise I'll try to add an answer on how to manage the texture when I have the time. – José Pereda Dec 23 '19 at 13:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/204735/discussion-between-gamer818-and-jose-pereda). – Gamer818 Dec 23 '19 at 13:53

1 Answers1

5

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.

José Pereda
  • 44,311
  • 7
  • 104
  • 132