mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 01:16:46 -04:00
world-renderer: split visible meshes, sort them
This commit is contained in:
parent
dcb058c960
commit
611f55fd82
@ -48,7 +48,7 @@ object RenderConstants {
|
||||
const val FRUSTUM_CULLING_ENABLED = true
|
||||
const val SHOW_FPS_IN_WINDOW_TITLE = true
|
||||
|
||||
const val MAXIMUM_QUEUE_TIME_PER_FRAME = 100L
|
||||
const val MAXIMUM_QUEUE_TIME_PER_FRAME = 20L
|
||||
|
||||
const val DISABLE_LIGHTING = false
|
||||
|
||||
|
@ -22,6 +22,7 @@ import de.bixilon.minosoft.data.world.World
|
||||
import de.bixilon.minosoft.gui.rendering.RenderWindow
|
||||
import de.bixilon.minosoft.gui.rendering.Renderer
|
||||
import de.bixilon.minosoft.gui.rendering.RendererBuilder
|
||||
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
|
||||
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMeshes
|
||||
import de.bixilon.minosoft.gui.rendering.block.preparer.AbstractSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.block.preparer.GenericSectionPreparer
|
||||
@ -67,19 +68,20 @@ class WorldRenderer(
|
||||
private val sectionPreparer: AbstractSectionPreparer = GenericSectionPreparer(renderWindow)
|
||||
private val lightMap = LightMap(connection)
|
||||
private val meshes: SynchronizedMap<Vec2i, SynchronizedMap<Int, ChunkSectionMeshes>> = synchronizedMapOf() // all prepared (and up to date) meshes
|
||||
private var visibleMeshes: MutableSet<ChunkSectionMeshes> = mutableSetOf() // ToDo: Split in opaque, transparent, translucent meshes and sort (opaque and transparent front to back, translucent back to front)
|
||||
private var incomplete: MutableSet<Vec2i> = synchronizedSetOf() // Queue of chunk positions that can not be rendered yet (data not complete or neighbours not completed yet)
|
||||
private var queue: MutableMap<Vec2i, MutableSet<Int>> = synchronizedMapOf() // Chunk sections that can be prepared or have changed, but are not required to get rendered yet (i.e. culled chunks)
|
||||
|
||||
private var visibleOpaque: MutableList<ChunkSectionMesh> = mutableListOf()
|
||||
private var visibleTranslucent: MutableList<ChunkSectionMesh> = mutableListOf()
|
||||
private var visibleTransparent: MutableList<ChunkSectionMesh> = mutableListOf()
|
||||
|
||||
val visibleSize: Int
|
||||
get() = visibleMeshes.size
|
||||
val preparedSize: Int
|
||||
get() = meshes.size
|
||||
val queuedSize: Int
|
||||
get() = queue.size
|
||||
val incompleteSize: Int
|
||||
get() = incomplete.size
|
||||
|
||||
val visibleOpaqueSize: Int by visibleOpaque::size
|
||||
val visibleTranslucentSize: Int by visibleTranslucent::size
|
||||
val visibleTransparentSize: Int by visibleTransparent::size
|
||||
val preparedSize: Int by meshes::size
|
||||
val queuedSize: Int by queue::size
|
||||
val incompleteSize: Int by incomplete::size
|
||||
|
||||
override fun init() {
|
||||
val asset = Resources.getAssetVersionByVersion(connection.version)
|
||||
@ -136,17 +138,29 @@ class WorldRenderer(
|
||||
incomplete += neighbourPosition
|
||||
}
|
||||
val meshes = this.meshes.remove(chunkPosition) ?: return
|
||||
visibleMeshes -= meshes.values
|
||||
if (meshes.isEmpty()) {
|
||||
return
|
||||
}
|
||||
renderWindow.queue += {
|
||||
for (mesh in meshes.values) {
|
||||
removeMesh(mesh)
|
||||
mesh.unload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeMesh(mesh: ChunkSectionMeshes) {
|
||||
mesh.opaqueMesh?.let { visibleOpaque -= it }
|
||||
mesh.translucentMesh?.let { visibleTranslucent -= it }
|
||||
mesh.transparentMesh?.let { visibleTransparent -= it }
|
||||
}
|
||||
|
||||
private fun addMesh(mesh: ChunkSectionMeshes) {
|
||||
mesh.opaqueMesh?.let { visibleOpaque += it }
|
||||
mesh.translucentMesh?.let { visibleTranslucent += it }
|
||||
mesh.transparentMesh?.let { visibleTransparent += it }
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All 8 fully loaded neighbour chunks or null
|
||||
*/
|
||||
@ -186,7 +200,7 @@ class WorldRenderer(
|
||||
neighbourChunks[3].sections!![sectionHeight],
|
||||
neighbourChunks[4].sections!![sectionHeight],
|
||||
neighbourChunks[1].sections!![sectionHeight],
|
||||
neighbourChunks[7].sections!![sectionHeight],
|
||||
neighbourChunks[6].sections!![sectionHeight],
|
||||
)
|
||||
}
|
||||
|
||||
@ -253,7 +267,7 @@ class WorldRenderer(
|
||||
if (previousMesh != null && !visible) {
|
||||
meshes.remove(sectionHeight)
|
||||
renderWindow.queue += {
|
||||
visibleMeshes -= previousMesh
|
||||
removeMesh(previousMesh)
|
||||
previousMesh.unload()
|
||||
}
|
||||
}
|
||||
@ -281,18 +295,17 @@ class WorldRenderer(
|
||||
private fun prepareSection(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>, meshes: SynchronizedMap<Int, ChunkSectionMeshes> = this.meshes.getOrPut(chunkPosition) { synchronizedMapOf() }) {
|
||||
val mesh = sectionPreparer.prepare(chunkPosition, sectionHeight, section, neighbours)
|
||||
|
||||
val currentMesh = meshes.remove(sectionHeight)
|
||||
val previousMesh = meshes.remove(sectionHeight)
|
||||
|
||||
renderWindow.queue += {
|
||||
if (currentMesh != null) {
|
||||
currentMesh.unload()
|
||||
this.visibleMeshes -= currentMesh
|
||||
if (previousMesh != null) {
|
||||
removeMesh(previousMesh)
|
||||
}
|
||||
|
||||
mesh.load()
|
||||
meshes[sectionHeight] = mesh
|
||||
if (isChunkVisible(chunkPosition, sectionHeight, mesh.minPosition, mesh.maxPosition)) {
|
||||
this.visibleMeshes += mesh
|
||||
addMesh(mesh)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -303,8 +316,8 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
override fun drawOpaque() {
|
||||
for (mesh in visibleMeshes) {
|
||||
mesh.opaqueMesh?.draw()
|
||||
for (mesh in visibleOpaque) {
|
||||
mesh.draw()
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,8 +327,8 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
override fun drawTranslucent() {
|
||||
for (mesh in visibleMeshes) {
|
||||
mesh.translucentMesh?.draw()
|
||||
for (mesh in visibleTranslucent) {
|
||||
mesh.draw()
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,8 +338,8 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
override fun drawTransparent() {
|
||||
for (mesh in visibleMeshes) {
|
||||
mesh.transparentMesh?.draw()
|
||||
for (mesh in visibleTransparent) {
|
||||
mesh.draw()
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,14 +349,18 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
private fun onFrustumChange() {
|
||||
val visible: MutableSet<ChunkSectionMeshes> = mutableSetOf()
|
||||
val visibleOpaque: MutableList<ChunkSectionMesh> = mutableListOf()
|
||||
val visibleTranslucent: MutableList<ChunkSectionMesh> = mutableListOf()
|
||||
val visibleTransparent: MutableList<ChunkSectionMesh> = mutableListOf()
|
||||
|
||||
for ((chunkPosition, meshes) in this.meshes.toSynchronizedMap()) {
|
||||
for ((sectionHeight, mesh) in meshes) {
|
||||
if (!isChunkVisible(chunkPosition, sectionHeight, mesh.minPosition, mesh.maxPosition)) {
|
||||
continue
|
||||
}
|
||||
visible += mesh
|
||||
mesh.opaqueMesh?.let { visibleOpaque += it }
|
||||
mesh.translucentMesh?.let { visibleTranslucent += it }
|
||||
mesh.transparentMesh?.let { visibleTransparent += it }
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,8 +376,16 @@ class WorldRenderer(
|
||||
updateSection(chunkPosition, sectionHeight, chunk, neighbours.unsafeCast(), meshes)
|
||||
}
|
||||
}
|
||||
val cameraPositionLength = connection.player.cameraPosition.length2()
|
||||
|
||||
this.visibleMeshes = visible
|
||||
visibleOpaque.sortBy { it.centerLength - cameraPositionLength }
|
||||
this.visibleOpaque = visibleOpaque
|
||||
|
||||
visibleTranslucent.sortBy { cameraPositionLength - it.centerLength }
|
||||
this.visibleTranslucent = visibleTranslucent
|
||||
|
||||
visibleTransparent.sortBy { it.centerLength - cameraPositionLength }
|
||||
this.visibleTransparent = visibleTransparent
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
|
||||
import glm_.vec2.Vec2
|
||||
import glm_.vec3.Vec3
|
||||
|
||||
class ChunkSectionMesh(renderWindow: RenderWindow) : Mesh(renderWindow, SectionArrayMeshStruct, PrimitiveTypes.QUAD, initialCacheSize = 200000) {
|
||||
class ChunkSectionMesh(renderWindow: RenderWindow, initialCacheSize: Int, val centerLength: Double) : Mesh(renderWindow, SectionArrayMeshStruct, PrimitiveTypes.QUAD, initialCacheSize = initialCacheSize) {
|
||||
|
||||
fun addVertex(position: FloatArray, uv: Vec2, texture: AbstractTexture, tintColor: Int, light: Int) {
|
||||
val transformedUV = texture.renderData?.transformUV(uv) ?: uv
|
||||
|
@ -14,16 +14,22 @@
|
||||
package de.bixilon.minosoft.gui.rendering.block.mesh
|
||||
|
||||
import de.bixilon.minosoft.gui.rendering.RenderWindow
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
|
||||
import glm_.vec2.Vec2i
|
||||
import glm_.vec3.Vec3d
|
||||
import glm_.vec3.Vec3i
|
||||
|
||||
class ChunkSectionMeshes(
|
||||
renderWindow: RenderWindow,
|
||||
chunkPosition: Vec2i,
|
||||
sectionHeight: Int,
|
||||
) {
|
||||
var opaqueMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow)
|
||||
private val centerLength = Vec3d(Vec3i.of(chunkPosition, sectionHeight, Vec3i(8, 8, 8))).length2()
|
||||
var opaqueMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 200000, centerLength)
|
||||
private set
|
||||
var translucentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow)
|
||||
var translucentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 100000, centerLength)
|
||||
private set
|
||||
var transparentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow)
|
||||
var transparentMesh: ChunkSectionMesh? = ChunkSectionMesh(renderWindow, 100000, centerLength)
|
||||
private set
|
||||
|
||||
// used for frustum culling
|
||||
|
@ -16,7 +16,7 @@ class CullSectionPreparer(
|
||||
) : AbstractSectionPreparer {
|
||||
|
||||
override fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>): ChunkSectionMeshes {
|
||||
val mesh = ChunkSectionMeshes(renderWindow)
|
||||
val mesh = ChunkSectionMeshes(renderWindow, chunkPosition, sectionHeight)
|
||||
val random = Random(0L)
|
||||
|
||||
val blocks = section.blocks
|
||||
|
@ -37,8 +37,9 @@ class GreedySectionPreparer(
|
||||
|
||||
|
||||
// base taken from https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
|
||||
@Deprecated("TODO")
|
||||
override fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>): ChunkSectionMeshes {
|
||||
val mesh = ChunkSectionMesh(renderWindow)
|
||||
val mesh = ChunkSectionMesh(renderWindow, 20000, 0.0)
|
||||
|
||||
val random = Random(0L)
|
||||
|
||||
|
@ -85,7 +85,7 @@ class DebugHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<GridLayout>
|
||||
layout += TextElement(hudRenderer, TextComponent(RunConfiguration.VERSION_STRING, ChatColors.RED))
|
||||
layout += AutoTextElement(hudRenderer, 1) { "FPS ${renderWindow.renderStats.smoothAvgFPS.round10}" }
|
||||
renderWindow[WorldRenderer]?.apply {
|
||||
layout += AutoTextElement(hudRenderer, 1) { "C v=$visibleSize, p=$preparedSize, q=$queuedSize, i=$incompleteSize, t=${connection.world.chunks.size}" }
|
||||
layout += AutoTextElement(hudRenderer, 1) { "C vO=$visibleOpaqueSize, vP=$visibleTransparentSize, vL=$visibleTranslucentSize, p=$preparedSize, q=$queuedSize, i=$incompleteSize, t=${connection.world.chunks.size}" }
|
||||
}
|
||||
layout += AutoTextElement(hudRenderer, 1) { "E t=${connection.world.entities.size}" }
|
||||
|
||||
|
@ -59,7 +59,7 @@ class Camera(
|
||||
val renderWindow: RenderWindow,
|
||||
) {
|
||||
var fogColor = Previous(ChatColors.GREEN)
|
||||
var fogStart = 1000.0f
|
||||
var fogStart = 100.0f
|
||||
private var mouseSensitivity = Minosoft.config.config.game.controls.moseSensitivity
|
||||
|
||||
@Deprecated("", ReplaceWith("connection.player"))
|
||||
|
Loading…
x
Reference in New Issue
Block a user