From 7bd34f85fa804a72a46e374218282c55b48b09d3 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sat, 13 Mar 2021 18:00:15 +0100 Subject: [PATCH] rendering: improve frustum culling code, cull chunks at y axis --- .../game/controls/KeyBindingsGameConfig.kt | 4 +- .../bixilon/minosoft/gui/rendering/Camera.kt | 15 ++++-- .../minosoft/gui/rendering/RenderConstants.kt | 2 +- .../minosoft/gui/rendering/RenderWindow.kt | 6 +-- .../minosoft/gui/rendering/chunk/Frustum.kt | 21 +++++---- .../gui/rendering/chunk/SectionArrayMesh.kt | 2 + .../gui/rendering/chunk/WorldRenderer.kt | 46 ++++++++++++++----- 7 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/config/config/game/controls/KeyBindingsGameConfig.kt b/src/main/java/de/bixilon/minosoft/config/config/game/controls/KeyBindingsGameConfig.kt index 1082bbb56..200f6907a 100644 --- a/src/main/java/de/bixilon/minosoft/config/config/game/controls/KeyBindingsGameConfig.kt +++ b/src/main/java/de/bixilon/minosoft/config/config/game/controls/KeyBindingsGameConfig.kt @@ -47,7 +47,7 @@ object KeyBindingsNames { val TOGGLE_DEBUG_SCREEN = ResourceLocation("minosoft:toggle_debug_screen") val DEBUG_CLEAR_CHUNK_CACHE = ResourceLocation("minosoft:debug_clear_chunk_cache") - val DEBUG_POLYGEN = ResourceLocation("minosoft:debug_polygen") + val DEBUG_POLYGON = ResourceLocation("minosoft:debug_polygon") val DEBUG_MOUSE_CATCH = ResourceLocation("minosoft:debug_mouse_catch") val WHEN_IN_GAME = ResourceLocation("minosoft:in_game") @@ -124,7 +124,7 @@ object KeyBindingsNames { ), mutableSetOf(mutableSetOf(WHEN_IN_GAME)) ), - DEBUG_POLYGEN to KeyBinding( + DEBUG_POLYGON to KeyBinding( mutableMapOf( KeyAction.MODIFIER to mutableSetOf(KeyCodes.KEY_F4), KeyAction.PRESS to mutableSetOf(KeyCodes.KEY_P) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt index 30afc58f7..538181174 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Camera.kt @@ -39,7 +39,7 @@ import kotlin.math.sin class Camera( val connection: Connection, var fov: Float, - private val renderWindow: RenderWindow, + val renderWindow: RenderWindow, ) { private var mouseSensitivity = Minosoft.getConfig().config.game.camera.moseSensitivity private var movementSpeed = 7 @@ -73,8 +73,12 @@ class Camera( var inChunkSectionPosition: InChunkSectionPosition = InChunkSectionPosition(0, 0, 0) private set - private var screenHeight = 0 - private var screenWidth = 0 + val frustum: Frustum = Frustum(this) + + var screenHeight = 0 + private set + var screenWidth = 0 + private set private val shaders: MutableSet = mutableSetOf() private var keyForwardDown = false @@ -214,7 +218,10 @@ class Camera( // recalculate sky color for current biome val blockPosition = Position(cameraPosition).toBlockPosition() renderWindow.setSkyColor(connection.player.world.getChunk(blockPosition.getChunkPosition())?.biomeAccessor?.getBiome(blockPosition, connection.player.world.dimension?.supports3DBiomes ?: false)?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR) - connection.renderer.renderWindow.worldRenderer.recalculateFrustum(Frustum(this)) + + frustum.recalculate() + renderWindow.worldRenderer.recalculateVisibleChunks() + connection.player.world.dimension?.hasSkyLight?.let { if (it) { renderWindow.setSkyColor(currentBiome?.skyColor ?: RenderConstants.DEFAULT_SKY_COLOR) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt index 602a98589..309189fa5 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderConstants.kt @@ -43,7 +43,7 @@ object RenderConstants { const val TEXT_LINE_PADDING = 0 - const val CHUNK_SECTIONS_PER_MESH = 8 + const val CHUNK_SECTIONS_PER_MESH = 4 const val MAXIMUM_CALLS_PER_FRAME = 10 } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt index 4f938dfb5..c51a79fdb 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -224,7 +224,7 @@ class RenderWindow( // Make the OpenGL context current glfwMakeContextCurrent(windowId) // Enable v-sync - glfwSwapInterval(0) + glfwSwapInterval(1) // Make the window visible @@ -309,14 +309,14 @@ class RenderWindow( } private fun registerGlobalKeyCombinations() { - registerKeyCallback(KeyBindingsNames.DEBUG_POLYGEN) { _: KeyCodes, _: KeyAction -> + registerKeyCallback(KeyBindingsNames.DEBUG_POLYGON) { _: KeyCodes, _: KeyAction -> polygonEnabled = !polygonEnabled glPolygonMode(GL_FRONT_AND_BACK, if (polygonEnabled) { GL_LINE } else { GL_FILL }) - sendDebugMessage("Toggled polygen mode!") + sendDebugMessage("Toggled polygon mode!") } registerKeyCallback(KeyBindingsNames.DEBUG_MOUSE_CATCH) { _: KeyCodes, _: KeyAction -> mouseCatch = !mouseCatch diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/Frustum.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/Frustum.kt index b6c7fb3a6..1d0fa03e4 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/Frustum.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/Frustum.kt @@ -3,7 +3,6 @@ package de.bixilon.minosoft.gui.rendering.chunk import de.bixilon.minosoft.data.world.ChunkPosition import de.bixilon.minosoft.gui.rendering.Camera import de.bixilon.minosoft.gui.rendering.util.VecUtil -import de.bixilon.minosoft.protocol.network.Connection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import glm_.glm import glm_.vec3.Vec3 @@ -13,7 +12,15 @@ class Frustum(private val camera: Camera) { camera.cameraFront.normalize(), ) + init { + recalculate() + } + + fun recalculate() { + normals.clear() + normals.add(camera.cameraFront.normalize()) + calculateSideNormals() calculateVerticalNormals() } @@ -28,7 +35,7 @@ class Frustum(private val camera: Camera) { } private fun calculateVerticalNormals() { - val aspect = camera.connection.renderer.renderWindow.screenHeight.toFloat() / camera.connection.renderer.renderWindow.screenWidth.toFloat() + val aspect = camera.screenHeight.toFloat() / camera.screenWidth.toFloat() val angle = glm.radians(camera.fov * aspect - 90f) val sin = glm.sin(angle) val cos = glm.cos(angle) @@ -63,12 +70,10 @@ class Frustum(private val camera: Camera) { return true } - fun containsChunk(chunkPosition: ChunkPosition, connection: Connection): Boolean { - val dimension = connection.player.world.dimension!! - val from = Vec3(chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X, dimension.minY, chunkPosition.z * ProtocolDefinition.SECTION_WIDTH_Z) - val to = from + Vec3(ProtocolDefinition.SECTION_WIDTH_X, dimension.logicalHeight, ProtocolDefinition.SECTION_WIDTH_Z) - val frustum = Frustum(connection.renderer.renderWindow.camera) - return frustum.containsRegion(from, to) + fun containsChunk(chunkPosition: ChunkPosition, lowestBlockHeight: Int, highestBlockHeight: Int): Boolean { + val from = Vec3(chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X, lowestBlockHeight, chunkPosition.z * ProtocolDefinition.SECTION_WIDTH_Z) + val to = from + Vec3(ProtocolDefinition.SECTION_WIDTH_X, highestBlockHeight, ProtocolDefinition.SECTION_WIDTH_Z) + return containsRegion(from, to) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/SectionArrayMesh.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/SectionArrayMesh.kt index d0540bcf1..0931ba13f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/SectionArrayMesh.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/SectionArrayMesh.kt @@ -24,6 +24,8 @@ import org.lwjgl.opengl.GL20.glEnableVertexAttribArray import org.lwjgl.opengl.GL20.glVertexAttribPointer class SectionArrayMesh : Mesh() { + var lowestBlockHeight = 0 + var highestBlockHeight = 0 fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?, lightLevel: Int = 14) { data.add(position.x) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt index 33ee263ad..bfc0c4a9f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/WorldRenderer.kt @@ -39,7 +39,6 @@ class WorldRenderer( lateinit var chunkShader: Shader val allChunkSections = ConcurrentHashMap>() val visibleChunks = ConcurrentHashMap>() - private lateinit var frustum: Frustum private var currentTick = 0 // for animation usage private var lastTickIncrementTime = 0L val queuedChunks: MutableSet = mutableSetOf() @@ -220,13 +219,33 @@ class WorldRenderer( Minosoft.THREAD_POOL.execute { val mesh = prepareSections(chunkPosition, sections) - var sectionMap = allChunkSections[chunkPosition] - if (sectionMap == null) { - sectionMap = ConcurrentHashMap() - allChunkSections[chunkPosition] = sectionMap + var lowestBlockHeight = 0 + var highestBlockHeight = 0 + for ((sectionHeight, _) in sections) { + if (sectionHeight < lowestBlockHeight) { + lowestBlockHeight = sectionHeight + } + if (sectionHeight > highestBlockHeight) { + highestBlockHeight = sectionHeight + } + } + val index = getSectionIndex(lowestBlockHeight) + + lowestBlockHeight *= ProtocolDefinition.SECTION_HEIGHT_Y + highestBlockHeight = highestBlockHeight * ProtocolDefinition.SECTION_HEIGHT_Y + ProtocolDefinition.SECTION_MAX_Y + + mesh.lowestBlockHeight = lowestBlockHeight + mesh.highestBlockHeight = highestBlockHeight + + + val sectionMap = allChunkSections[chunkPosition] ?: let { + val map: ConcurrentHashMap = ConcurrentHashMap() + allChunkSections[chunkPosition] = map + map } - if (frustum.containsChunk(chunkPosition, connection)) { + + if (renderWindow.camera.frustum.containsChunk(chunkPosition, lowestBlockHeight, highestBlockHeight)) { visibleChunks[chunkPosition] = sectionMap } @@ -302,12 +321,17 @@ class WorldRenderer( prepareWorld(connection.player.world) } - fun recalculateFrustum(frustum: Frustum) { + fun recalculateVisibleChunks() { visibleChunks.clear() - this.frustum = frustum - for ((chunkLocation, sectionMap) in allChunkSections.entries) { - if (frustum.containsChunk(chunkLocation, connection)) { - visibleChunks[chunkLocation] = sectionMap + for ((chunkLocation, indexMap) in allChunkSections) { + val visibleIndexMap: ConcurrentHashMap = ConcurrentHashMap() + for ((index, mesh) in indexMap) { + if (renderWindow.camera.frustum.containsChunk(chunkLocation, mesh.lowestBlockHeight, mesh.highestBlockHeight)) { + visibleIndexMap[index] = mesh + } + } + if (visibleIndexMap.isNotEmpty()) { + visibleChunks[chunkLocation] = visibleIndexMap } } }