mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-14 01:48:04 -04:00
wip: world rendering
This commit is contained in:
parent
821a849e25
commit
20c5901fea
@ -23,20 +23,26 @@ import de.bixilon.minosoft.gui.rendering.Renderer
|
||||
import de.bixilon.minosoft.gui.rendering.RendererBuilder
|
||||
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.CullSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.block.preparer.GenericSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.block.preparer.GreedySectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.input.camera.Frustum
|
||||
import de.bixilon.minosoft.gui.rendering.modding.events.FrustumChangeEvent
|
||||
import de.bixilon.minosoft.gui.rendering.models.ModelLoader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.phases.OpaqueDrawable
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.phases.TranslucentDrawable
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.phases.TransparentDrawable
|
||||
import de.bixilon.minosoft.modding.event.events.ChunkDataChangeEvent
|
||||
import de.bixilon.minosoft.modding.event.events.ChunkUnloadEvent
|
||||
import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
|
||||
import de.bixilon.minosoft.util.collections.SynchronizedMap
|
||||
import glm_.vec2.Vec2i
|
||||
import java.io.FileInputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.random.Random
|
||||
|
||||
class WorldRenderer(
|
||||
private val connection: PlayConnection,
|
||||
@ -48,11 +54,14 @@ class WorldRenderer(
|
||||
private val world: World = connection.world
|
||||
private val sectionPreparer: AbstractSectionPreparer = GenericSectionPreparer(renderWindow)
|
||||
private val lightMap = LightMap(connection)
|
||||
private lateinit var mesh: ChunkSectionMeshes
|
||||
private val meshes: SynchronizedMap<Vec2i, SynchronizedMap<Int, ChunkSectionMeshes>> = synchronizedMapOf()
|
||||
private var visibleMeshes: MutableSet<ChunkSectionMeshes> = mutableSetOf() // ToDo: Split in opaque, transparent, translucent meshes
|
||||
|
||||
private val culledPreparer = GenericSectionPreparer(renderWindow, CullSectionPreparer(renderWindow))
|
||||
private val greedyPreparer = GenericSectionPreparer(renderWindow, GreedySectionPreparer(renderWindow))
|
||||
|
||||
val visibleSize: Int
|
||||
get() = visibleMeshes.size
|
||||
val preparedSize: Int
|
||||
get() = visibleMeshes.size
|
||||
|
||||
override fun init() {
|
||||
val asset = Resources.getAssetVersionByVersion(connection.version)
|
||||
@ -75,37 +84,49 @@ class WorldRenderer(
|
||||
renderWindow.textureManager.staticTextures.animator.use(transparentShader)
|
||||
lightMap.use(transparentShader)
|
||||
|
||||
connection.registerEvent(CallbackEventInvoker.of<FrustumChangeEvent> { onFrustumChange(it.frustum) })
|
||||
|
||||
val random = Random(0L)
|
||||
val blockState1 = connection.registries.blockRegistry["grass_block"]?.defaultState
|
||||
val blockState2 = connection.registries.blockRegistry["diamond_block"]!!.defaultState//.withProperties(BlockProperties.MULTIPART_SOUTH to MultipartDirectionParser.SIDE, BlockProperties.MULTIPART_NORTH to MultipartDirectionParser.SIDE, BlockProperties.MULTIPART_EAST to MultipartDirectionParser.SIDE, BlockProperties.MULTIPART_WEST to MultipartDirectionParser.SIDE)
|
||||
val section = ChunkSection(Array(4096) {
|
||||
when (random.nextInt(3)) {
|
||||
1 -> blockState1
|
||||
2 -> blockState2
|
||||
else -> null
|
||||
|
||||
connection.registerEvent(CallbackEventInvoker.of<ChunkDataChangeEvent> {
|
||||
val sections = it.chunk.sections ?: return@of
|
||||
for ((sectionHeight, section) in sections) {
|
||||
prepareSection(it.chunkPosition, sectionHeight, section)
|
||||
}
|
||||
})
|
||||
//val section = ChunkSection(Array(4096) { if (it < 1) blockState else null })
|
||||
|
||||
mesh = sectionPreparer.prepare(section)
|
||||
connection.registerEvent(CallbackEventInvoker.of<ChunkUnloadEvent> {
|
||||
val meshes = this.meshes.remove(it.chunkPosition)?.values ?: return@of
|
||||
|
||||
for (i in 0 until 1000)
|
||||
mesh = sectionPreparer.prepare(section)
|
||||
renderWindow.queue += {
|
||||
for (mesh in meshes) {
|
||||
mesh.unload()
|
||||
this.visibleMeshes -= mesh
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
Log.log(LogMessageType.OTHER, LogLevels.WARN){"Culling now..."}
|
||||
|
||||
val culledMesh = culledPreparer.prepare(section)
|
||||
for (i in 0 until 1000){
|
||||
culledPreparer.prepare(section)
|
||||
@Synchronized
|
||||
private fun prepareSection(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection? = world[chunkPosition]?.sections?.get(sectionHeight)) {
|
||||
if (section == null) {
|
||||
return
|
||||
}
|
||||
val greedyMesh = greedyPreparer.prepare(section)
|
||||
|
||||
Log.log(LogMessageType.OTHER,LogLevels.INFO){"Culling has ${culledMesh.data.size / ChunkSectionMesh.SectionArrayMeshStruct.FLOATS_PER_VERTEX}, greedy meshed has ${greedyMesh.data.size / ChunkSectionMesh.SectionArrayMeshStruct.FLOATS_PER_VERTEX}."}
|
||||
val mesh = sectionPreparer.prepare(chunkPosition, sectionHeight, section, arrayOfNulls(6))
|
||||
|
||||
*/
|
||||
mesh.load()
|
||||
val meshes = this.meshes.getOrPut(chunkPosition) { synchronizedMapOf() }
|
||||
val currentMesh = meshes.remove(sectionHeight)
|
||||
|
||||
renderWindow.queue += {
|
||||
if (currentMesh != null) {
|
||||
currentMesh.unload()
|
||||
this.visibleMeshes -= currentMesh
|
||||
}
|
||||
|
||||
mesh.load()
|
||||
meshes[sectionHeight] = mesh
|
||||
this.visibleMeshes += mesh
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupOpaque() {
|
||||
@ -114,7 +135,9 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
override fun drawOpaque() {
|
||||
mesh.opaqueMesh?.draw()
|
||||
for (mesh in visibleMeshes) {
|
||||
mesh.opaqueMesh?.draw()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupTranslucent() {
|
||||
@ -123,7 +146,9 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
override fun drawTranslucent() {
|
||||
mesh.translucentMesh?.draw()
|
||||
for (mesh in visibleMeshes) {
|
||||
mesh.translucentMesh?.draw()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupTransparent() {
|
||||
@ -132,7 +157,25 @@ class WorldRenderer(
|
||||
}
|
||||
|
||||
override fun drawTransparent() {
|
||||
mesh.transparentMesh?.draw()
|
||||
for (mesh in visibleMeshes) {
|
||||
mesh.transparentMesh?.draw()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFrustumChange(frustum: Frustum) {
|
||||
val visible: MutableSet<ChunkSectionMeshes> = mutableSetOf()
|
||||
|
||||
// ToDo
|
||||
for ((chunkPosition, meshes) in this.meshes.toSynchronizedMap()) {
|
||||
for ((sectionHeight, mesh) in meshes) {
|
||||
if (!frustum.containsChunk(chunkPosition, sectionHeight, mesh.minPosition, mesh.maxPosition)) {
|
||||
continue
|
||||
}
|
||||
visible += mesh
|
||||
}
|
||||
}
|
||||
|
||||
this.visibleMeshes = visible
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,6 +55,13 @@ class ChunkSectionMeshes(
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun unload() {
|
||||
opaqueMesh?.unload()
|
||||
translucentMesh?.unload()
|
||||
transparentMesh?.unload()
|
||||
}
|
||||
|
||||
fun addBlock(x: Int, y: Int, z: Int) {
|
||||
if (x < minPosition.x) {
|
||||
minPosition.x = x
|
||||
|
@ -2,9 +2,10 @@ package de.bixilon.minosoft.gui.rendering.block.preparer
|
||||
|
||||
import de.bixilon.minosoft.data.world.ChunkSection
|
||||
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMeshes
|
||||
import glm_.vec2.Vec2i
|
||||
|
||||
interface AbstractSectionPreparer {
|
||||
|
||||
fun prepare(section: ChunkSection): ChunkSectionMeshes
|
||||
fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>): ChunkSectionMeshes
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow
|
||||
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMeshes
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import glm_.vec2.Vec2i
|
||||
import glm_.vec3.Vec3i
|
||||
import java.util.*
|
||||
|
||||
@ -14,53 +15,61 @@ class CullSectionPreparer(
|
||||
val renderWindow: RenderWindow,
|
||||
) : AbstractSectionPreparer {
|
||||
|
||||
override fun prepare(section: ChunkSection): ChunkSectionMeshes {
|
||||
override fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>): ChunkSectionMeshes {
|
||||
val mesh = ChunkSectionMeshes(renderWindow)
|
||||
val random = Random(0L)
|
||||
|
||||
val blocks = section.blocks
|
||||
var block: BlockState?
|
||||
val neighbours: Array<BlockState?> = arrayOfNulls(Directions.SIZE)
|
||||
val neighbourBlocks: Array<BlockState?> = arrayOfNulls(Directions.SIZE)
|
||||
|
||||
val offsetX = chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X
|
||||
val offsetY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y
|
||||
val offsetZ = chunkPosition.y * ProtocolDefinition.SECTION_WIDTH_Z
|
||||
|
||||
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
|
||||
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
|
||||
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
||||
block = section.blocks[ChunkSection.getIndex(x, y, z)]
|
||||
block = blocks[ChunkSection.getIndex(x, y, z)]
|
||||
val model = block?.model ?: continue
|
||||
|
||||
// ToDo: Chunk borders
|
||||
neighbours[Directions.DOWN.ordinal] = if (y == 0) {
|
||||
null
|
||||
neighbourBlocks[Directions.DOWN.ordinal] = if (y == 0) {
|
||||
neighbours[Directions.DOWN.ordinal]?.blocks?.get(ChunkSection.getIndex(x, ProtocolDefinition.SECTION_MAX_Y, z))
|
||||
} else {
|
||||
section.blocks[ChunkSection.getIndex(x, y - 1, z)]
|
||||
blocks[ChunkSection.getIndex(x, y - 1, z)]
|
||||
}
|
||||
neighbours[Directions.UP.ordinal] = if (y == ProtocolDefinition.SECTION_MAX_Y) {
|
||||
null
|
||||
neighbourBlocks[Directions.UP.ordinal] = if (y == ProtocolDefinition.SECTION_MAX_Y) {
|
||||
neighbours[Directions.UP.ordinal]?.blocks?.get(ChunkSection.getIndex(x, 0, z))
|
||||
} else {
|
||||
section.blocks[ChunkSection.getIndex(x, y + 1, z)]
|
||||
}
|
||||
neighbours[Directions.NORTH.ordinal] = if (z == 0) {
|
||||
null
|
||||
} else {
|
||||
section.blocks[ChunkSection.getIndex(x, y, z - 1)]
|
||||
}
|
||||
neighbours[Directions.SOUTH.ordinal] = if (z == ProtocolDefinition.SECTION_MAX_Z) {
|
||||
null
|
||||
} else {
|
||||
section.blocks[ChunkSection.getIndex(x, y, z + 1)]
|
||||
}
|
||||
neighbours[Directions.WEST.ordinal] = if (x == 0) {
|
||||
null
|
||||
} else {
|
||||
section.blocks[ChunkSection.getIndex(x - 1, y, z)]
|
||||
}
|
||||
neighbours[Directions.EAST.ordinal] = if (x == ProtocolDefinition.SECTION_MAX_X) {
|
||||
null
|
||||
} else {
|
||||
section.blocks[ChunkSection.getIndex(x + 1, y, z)]
|
||||
blocks[ChunkSection.getIndex(x, y + 1, z)]
|
||||
}
|
||||
|
||||
random.setSeed(VecUtil.generatePositionHash(x, y, z))
|
||||
val rendered = model.singleRender(Vec3i(x, y, z), mesh, random, neighbours, 0xFF, floatArrayOf(1.0f, 1.0f, 1.0f, 1.0f))
|
||||
neighbourBlocks[Directions.NORTH.ordinal] = if (z == 0) {
|
||||
neighbours[Directions.NORTH.ordinal]?.blocks?.get(ChunkSection.getIndex(x, y, ProtocolDefinition.SECTION_MAX_Z))
|
||||
} else {
|
||||
blocks[ChunkSection.getIndex(x, y, z - 1)]
|
||||
}
|
||||
neighbourBlocks[Directions.SOUTH.ordinal] = if (z == ProtocolDefinition.SECTION_MAX_Z) {
|
||||
neighbours[Directions.NORTH.ordinal]?.blocks?.get(ChunkSection.getIndex(x, y, 0))
|
||||
} else {
|
||||
blocks[ChunkSection.getIndex(x, y, z + 1)]
|
||||
}
|
||||
|
||||
neighbourBlocks[Directions.WEST.ordinal] = if (x == 0) {
|
||||
neighbours[Directions.WEST.ordinal]?.blocks?.get(ChunkSection.getIndex(ProtocolDefinition.SECTION_MAX_X, y, z))
|
||||
} else {
|
||||
blocks[ChunkSection.getIndex(x - 1, y, z)]
|
||||
}
|
||||
neighbourBlocks[Directions.EAST.ordinal] = if (x == ProtocolDefinition.SECTION_MAX_X) {
|
||||
neighbours[Directions.WEST.ordinal]?.blocks?.get(ChunkSection.getIndex(0, y, z))
|
||||
} else {
|
||||
blocks[ChunkSection.getIndex(x + 1, y, z)]
|
||||
}
|
||||
|
||||
val position = Vec3i(offsetX + x, offsetY + y, offsetZ + z)
|
||||
random.setSeed(VecUtil.generatePositionHash(position.x, position.y, position.z))
|
||||
val rendered = model.singleRender(position, mesh, random, neighbourBlocks, 0xFF, floatArrayOf(1.0f, 1.0f, 1.0f, 1.0f))
|
||||
if (rendered) {
|
||||
mesh.addBlock(x, y, z)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMeshes
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
import glm_.vec2.Vec2i
|
||||
|
||||
|
||||
class GenericSectionPreparer(
|
||||
@ -26,10 +27,10 @@ class GenericSectionPreparer(
|
||||
private val preparer: AbstractSectionPreparer = CullSectionPreparer(renderWindow),
|
||||
) : AbstractSectionPreparer {
|
||||
|
||||
override fun prepare(section: ChunkSection): ChunkSectionMeshes {
|
||||
override fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>): ChunkSectionMeshes {
|
||||
val startTime = System.nanoTime()
|
||||
|
||||
val mesh = preparer.prepare(section)
|
||||
val mesh = preparer.prepare(chunkPosition, sectionHeight, section, neighbours)
|
||||
|
||||
val time = System.nanoTime()
|
||||
val delta = time - startTime
|
||||
|
@ -22,6 +22,7 @@ import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMeshes
|
||||
import de.bixilon.minosoft.gui.rendering.models.baked.block.GreedyBakedBlockModel
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.SECTION_SIZE
|
||||
import de.bixilon.minosoft.util.KUtil.decide
|
||||
import glm_.vec2.Vec2i
|
||||
import glm_.vec3.Vec3i
|
||||
import java.util.*
|
||||
|
||||
@ -36,7 +37,7 @@ class GreedySectionPreparer(
|
||||
|
||||
|
||||
// base taken from https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
|
||||
override fun prepare(section: ChunkSection): ChunkSectionMeshes {
|
||||
override fun prepare(chunkPosition: Vec2i, sectionHeight: Int, section: ChunkSection, neighbours: Array<ChunkSection?>): ChunkSectionMeshes {
|
||||
val mesh = ChunkSectionMesh(renderWindow)
|
||||
|
||||
val random = Random(0L)
|
||||
|
@ -87,7 +87,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 {
|
||||
// ToDo: layout += AutoTextElement(hudRenderer, 1) { "C v=${visibleChunks.size}, p=${allChunkSections.size}, q=${queuedChunks.size}, t=${connection.world.chunks.size}" }
|
||||
layout += AutoTextElement(hudRenderer, 1) { "C v=${visibleSize}, p=${preparedSize}, q=-1, t=${connection.world.chunks.size}" }
|
||||
}
|
||||
layout += AutoTextElement(hudRenderer, 1) { "E t=${connection.world.entities.size}" }
|
||||
|
||||
|
@ -17,8 +17,6 @@ package de.bixilon.minosoft.gui.rendering.input.camera
|
||||
import de.bixilon.minosoft.data.registries.AABB
|
||||
import de.bixilon.minosoft.gui.rendering.RenderConstants
|
||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.of
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.util.KUtil
|
||||
import de.bixilon.minosoft.util.KUtil.get
|
||||
import de.bixilon.minosoft.util.enum.ValuesEnum
|
||||
@ -163,11 +161,10 @@ class Frustum(private val camera: Camera) {
|
||||
return true
|
||||
}
|
||||
|
||||
fun containsChunk(chunkPosition: Vec2i, lowestBlockHeight: Int, highestBlockHeight: Int): Boolean {
|
||||
val from = Vec3i.of(chunkPosition, 0, Vec3i.EMPTY)
|
||||
from.y = lowestBlockHeight
|
||||
val to = Vec3(from.x + ProtocolDefinition.SECTION_WIDTH_X, highestBlockHeight, from.z + ProtocolDefinition.SECTION_WIDTH_Z)
|
||||
return containsRegion(Vec3(from), to)
|
||||
fun containsChunk(chunkPosition: Vec2i, sectionHeight: Int, minPosition: Vec3i, maxPosition: Vec3i): Boolean {
|
||||
val from = Vec3i.of(chunkPosition, sectionHeight, minPosition)
|
||||
val to = Vec3i.of(chunkPosition, sectionHeight, maxPosition)
|
||||
return containsRegion(Vec3(from), Vec3(to))
|
||||
}
|
||||
|
||||
fun containsAABB(aabb: AABB): Boolean {
|
||||
|
Loading…
x
Reference in New Issue
Block a user