JavaFX 3d : Weird clipping or false z-buffer i guess

158 Views Asked by At

So, I'm working on my little project for perlin-noise generated meshes and all works fine, except the rendering. I don't know what I am doing wrong. From the one side (looking in +z direction) everything seems totally fine, but from the other side (looking in -z direction) it just looks weird.

Demonstration Video: (Sorry for the bad quality)
https://drive.google.com/file/d/1q7tsSzVr4VVw4Tr5Y-WNTdlO5wo6DoKg/view?usp=sharing

Main Class:

package ch.imgajeed.world_generator

import javafx.application.Application
import javafx.beans.property.SimpleDoubleProperty
import javafx.scene.DepthTest
import javafx.scene.Group
import javafx.scene.PerspectiveCamera
import javafx.scene.Scene
import javafx.scene.input.ScrollEvent
import javafx.scene.paint.Color
import javafx.scene.shape.DrawMode
import javafx.scene.transform.Rotate
import javafx.stage.Stage

const val SCREEN_WIDTH = 1000.0
const val SCREEN_HEIGHT = 800.0

var screenInFocus = true

class Main : Application() {

    var anchorX = 0.0
    var anchorY = 0.0
    var anchorAngleX = 0.0
    var anchorAngleY = 0.0
    var angleX = SimpleDoubleProperty(20.0)
    var angleY = SimpleDoubleProperty(90.0)

    override fun start(stage: Stage) {
        stage.title = "World Generator"
        setupRenderer(stage)

        stage.show()
    }

    private fun setupRenderer(stage: Stage) {
        // val heightList = pngToHeightList("src/main/resources/ch/imgajeed/world_generator/Test-Height-Map.png", 0.5f)
        val generator = MapGenerator(resolution = 1000)
        generator.modifiers.add(PerlinNoise(0.0,300f, 0.5f))
        generator.modifiers.add(PerlinNoise(1.0,200f, 5f))
        generator.modifiers.add(PerlinNoise(2.0,100f, 2f))
        generator.modifiers.add(PerlinNoise(3.0,50f, 5f))
        generator.modifiers.add(PerlinNoise(4.0,10f, 15f))
        generator.modifiers.add(PerlinNoise(5.0,1f, 150f))
        generator.modifiers.add(PerlinNoise(6.0,0.1f, 250f))
        val heightList = generator.generate()

        val terrain3d = Terrain3d(300f, 300f, 1)

        val obj = terrain3d.generateMesh(heightList,true, false, 4)
        obj.drawMode = DrawMode.FILL

        val group = Group()
        group.children.add(obj)
        group.depthTest = DepthTest.ENABLE

        val camera = PerspectiveCamera()

        val scene = Scene(group, SCREEN_WIDTH, SCREEN_HEIGHT)
        scene.fill = Color.BLACK
        scene.camera = camera

        camera.translateX = -(SCREEN_WIDTH / 2)
        camera.translateY = -(SCREEN_HEIGHT / 2)
        camera.translateZ = 0.0

        camera.nearClip = 0.00000001
        camera.farClip = 1000.0

        initMouseController(group, scene, stage)

        stage.scene = scene
    }

    private fun initMouseController(group: Group, scene: Scene, stage: Stage) {
        val rotatorX = Rotate(0.0, Rotate.X_AXIS)
        val rotatorY = Rotate(0.0, Rotate.Y_AXIS)
        rotatorX.angleProperty().bind(angleX)
        rotatorY.angleProperty().bind(angleY)

        group.transforms.addAll(rotatorX, rotatorY)

        scene.setOnMousePressed { event ->
            anchorX = event.sceneX
            anchorY = event.sceneY
            anchorAngleX = angleX.get()
            anchorAngleY = angleY.get()
        }

        scene.setOnMouseDragged { event ->
            angleX.set(anchorAngleX - (anchorY - event.sceneY))
            angleY.set(anchorAngleY + anchorX - event.sceneX)

            if (angleX.get() < 1) {
                angleX.set(1.0)
            }
        }

        stage.addEventHandler(ScrollEvent.SCROLL) { event ->
            val movement = event.deltaY
            group.translateZ -= movement
        }
    }
}

fun main() {
    Application.launch(Main::class.java)
}

Terrain Class:

package ch.imgajeed.world_generator

import javafx.scene.shape.MeshView
import javafx.scene.shape.TriangleMesh
import java.awt.Color
import java.io.File
import javax.imageio.ImageIO

class Terrain3d(var width: Float = 5f, var height: Float = 5f, var resolution: Int = 5) {
    fun generateMesh(heightList: ArrayList<ArrayList<Float>>, hlr: Boolean = false, doubleSided: Boolean = false, divider: Int = 1): MeshView {
        if (heightList.size != heightList[0].size) {
            error("HeightList size must be a square.")
        }

        if (hlr) {
            resolution = heightList.size / divider
        }

        if (heightList.size / divider != resolution || heightList[0].size / divider != resolution) {
            error("Size of HeightList and resolution does not match. If you want to use the HeightLists resolution set hlr to true.")
        }

        val vertices = arrayListOf<Float>()
        val texCoords = arrayListOf<Float>()
        val faces = arrayListOf<Int>()

        val tileWidth = (width / resolution)
        val tileHeight = (height / resolution)

        for (x in 0 until resolution) {
            for (y in 0 until resolution) {
                vertices.addAll(
                    arrayListOf(
                        x * tileWidth - width / 2f, -heightList[y * divider][x * divider], y * tileHeight - height / 2f
                    )
                )
                texCoords.addAll(
                    arrayListOf(
                        (x.toFloat() / resolution.toFloat()), (y.toFloat() / resolution.toFloat())
                    )
                )
            }
        }

        var faceCountA = 0
        var faceCountB = 0

        for (i in 0 until vertices.size / 3) {
            val ps = vertices.size / 3

            val a1: Int = i
            val a2: Int = i + resolution
            val a3: Int = i + 1

            val a1OK = ((a1 + 1) % resolution != 0) && a1 < ps - resolution
            val a2OK = a2 < ps
            val a3OK = a3 < ps

            if (a1OK && a2OK && a3OK) {
                faces.add(a1)
                faces.add(a1)
                faces.add(a2)
                faces.add(a2)
                faces.add(a3)
                faces.add(a3)

                if (doubleSided) {
                    // other side
                    faces.add(a3)
                    faces.add(a3)
                    faces.add(a2)
                    faces.add(a2)
                    faces.add(a1)
                    faces.add(a1)
                }

                faceCountA += 1
            }


            val b1: Int = i
            val b2: Int = i - resolution
            val b3: Int = i - 1

            val b1OK = b1 > resolution && b1 % resolution != 0
            val b2OK = b2 > 0 && b2 < ps - resolution
            val b3OK = b3 > 0 && b3 < ps

            if (b1OK && b2OK && b3OK) {
                faces.add(b1)
                faces.add(b1)
                faces.add(b2)
                faces.add(b2)
                faces.add(b3)
                faces.add(b3)

                if (doubleSided) {
                    // other side
                    faces.add(b3)
                    faces.add(b3)
                    faces.add(b2)
                    faces.add(b2)
                    faces.add(b1)
                    faces.add(b1)
                }

                faceCountB += 1
            }
        }

        println("Resolution: $resolution")
        println("Vertices: ${vertices.size}")
        println("TexCoords: ${texCoords.size}")
        println("Faces: ${faces.size}")

        val mesh = TriangleMesh()
        for (vertex in vertices) {
            mesh.points.addAll(vertex)
        }
        for (coord in texCoords) {
            mesh.texCoords.addAll(coord)
        }
        for (face in faces) {
            mesh.faces.addAll(face)
        }

        mesh.normals.addAll(0f)
        mesh.normals.addAll(-1f)
        mesh.normals.addAll(0f)

        val meshView = MeshView()
        meshView.mesh = mesh

        println("Terrain mesh created")


        return meshView
    }
}

fun pngToHeightList(png: String, scaler: Float = 1f): ArrayList<ArrayList<Float>> {
    val img = ImageIO.read(File(png))

    val heightList = arrayListOf<ArrayList<Float>>()

    for (x in 0 until img.width) {
        val line = arrayListOf<Float>()
        for (y in 0 until img.height) {
            val color = Color(img.getRGB(x, y))
            val graylevel: Float = color.red.toFloat() * scaler
            line.add(graylevel)
        }
        heightList.add(line)
    }

   return heightList
}

Does someone have an idea to fix this problem? (Project is written in Kotlin)

1

There are 1 best solutions below

1
ImGajeed76 On

Fixed it.

Just had to change this line

camera.nearClip = 0.00000001

to this

camera.nearClip = 0.01

and now all works fine.