3

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)

ImGajeed76
  • 61
  • 4
  • 2
    your scene instance does not have zbuffer enabled `val scene = Scene(group, SCREEN_WIDTH, SCREEN_HEIGHT)` that constructor (node,width,height ) does not add zbuffer and antialiasing . you need `Scene(Parent root, double width, double height, boolean depthBuffer, SceneAntialiasing antiAliasing)` – Giovanni Contreras Jul 16 '22 at 01:14
  • 2
    `val scene = Scene(group, SCREEN_WIDTH, SCREEN_HEIGHT,true,SceneAntialiasing.BALANCED)` in your case – Giovanni Contreras Jul 16 '22 at 01:17
  • Thanks for the help, but the problem continues to occur. – ImGajeed76 Jul 16 '22 at 08:48
  • After some thinking, I don't think it's because of the z-buffer. Because the Object is rotating, not the camera. – ImGajeed76 Jul 16 '22 at 21:31
  • 1
    The camera doesn’t need to rotate for the z buffer to come into play, only some polygons need to occluded by other polygons, which would appear to be the case with your sample. You may have additional issues besides the z buffer issue, I don’t know. – jewelsea Jul 18 '22 at 00:17

1 Answers1

3

Fixed it.

Just had to change this line

camera.nearClip = 0.00000001

to this

camera.nearClip = 0.01

and now all works fine.

ImGajeed76
  • 61
  • 4