javajavafxmeshterrainjavafx-3d

How to merge multiple 3D objects as a single Mesh in order to remove a visual glitch?


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!


Solution

  • 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.