From 2279af73a15b24ed2db613ec95d6b3a8ac375b6d Mon Sep 17 00:00:00 2001 From: Bixilon Date: Mon, 8 Nov 2021 13:41:47 +0100 Subject: [PATCH] normal cull section preparer --- .../minosoft/data/direction/Directions.kt | 1 + .../gui/rendering/block/WorldRenderer.kt | 28 +++++++- .../block/preparer/AbstractSectionPreparer.kt | 10 +++ .../block/preparer/CullSectionPreparer.kt | 70 +++++++++++++++++++ .../block/preparer/GenericSectionPreparer.kt | 40 +++++++++++ .../GreedySectionPreparer.kt} | 34 ++++----- .../unbaked/block/UnbakedBlockStateModel.kt | 2 +- .../minosoft/gui/rendering/util/VecUtil.kt | 2 +- 8 files changed, 163 insertions(+), 24 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/AbstractSectionPreparer.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/CullSectionPreparer.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GenericSectionPreparer.kt rename src/main/java/de/bixilon/minosoft/gui/rendering/block/{SectionPreparer.kt => preparer/GreedySectionPreparer.kt} (83%) diff --git a/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt b/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt index 5ebf63d9f..e0ca3e233 100644 --- a/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt +++ b/src/main/java/de/bixilon/minosoft/data/direction/Directions.kt @@ -101,6 +101,7 @@ enum class Directions( companion object : BlockPropertiesSerializer, ValuesEnum { + const val SIZE = 6 override val VALUES = values() override val NAME_MAP: Map = KUtil.getEnumValues(VALUES) val SIDES = arrayOf(NORTH, SOUTH, WEST, EAST) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt index d9fee4ba8..464604aa8 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/WorldRenderer.kt @@ -24,6 +24,10 @@ 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.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.models.ModelLoader import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem import de.bixilon.minosoft.gui.rendering.system.base.phases.OpaqueDrawable @@ -43,10 +47,13 @@ class WorldRenderer( override val renderSystem: RenderSystem = renderWindow.renderSystem private val shader = renderSystem.createShader("minosoft:world".toResourceLocation()) private val world: World = connection.world - private val sectionPreparer = SectionPreparer(renderWindow) + private val sectionPreparer: AbstractSectionPreparer = GenericSectionPreparer(renderWindow) private val lightMap = LightMap(connection) private lateinit var mesh: ChunkSectionMesh + private val culledPreparer = GenericSectionPreparer(renderWindow, CullSectionPreparer(renderWindow)) + private val greedyPreparer = GenericSectionPreparer(renderWindow, GreedySectionPreparer(renderWindow)) + override fun init() { val asset = Resources.getAssetVersionByVersion(connection.version) @@ -66,9 +73,24 @@ class WorldRenderer( val random = Random(0L) val blockState = connection.registries.blockRegistry["diamond_block"]?.defaultState - val chunk = ChunkSection(Array(4096) { if (random.nextBoolean()) blockState else null }) + val section = ChunkSection(Array(4096) { if (random.nextBoolean()) blockState else null }) + + mesh = sectionPreparer.prepare(section) + /* for (i in 0 until 1000) - mesh = sectionPreparer.prepare(chunk) + mesh = sectionPreparer.prepare(section) + + Log.log(LogMessageType.OTHER, LogLevels.WARN){"Culling now..."} + + val culledMesh = culledPreparer.prepare(section) + for (i in 0 until 1000){ + culledPreparer.prepare(section) + } + 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}."} + + */ mesh.load() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/AbstractSectionPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/AbstractSectionPreparer.kt new file mode 100644 index 000000000..971de29a1 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/AbstractSectionPreparer.kt @@ -0,0 +1,10 @@ +package de.bixilon.minosoft.gui.rendering.block.preparer + +import de.bixilon.minosoft.data.world.ChunkSection +import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh + +interface AbstractSectionPreparer { + + fun prepare(section: ChunkSection): ChunkSectionMesh + +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/CullSectionPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/CullSectionPreparer.kt new file mode 100644 index 000000000..7b806adf9 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/CullSectionPreparer.kt @@ -0,0 +1,70 @@ +package de.bixilon.minosoft.gui.rendering.block.preparer + +import de.bixilon.minosoft.data.direction.Directions +import de.bixilon.minosoft.data.registries.blocks.BlockState +import de.bixilon.minosoft.data.world.ChunkSection +import de.bixilon.minosoft.gui.rendering.RenderWindow +import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh +import de.bixilon.minosoft.gui.rendering.util.VecUtil +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import glm_.vec3.Vec3i +import java.util.* + +class CullSectionPreparer( + val renderWindow: RenderWindow, +) : AbstractSectionPreparer { + + override fun prepare(section: ChunkSection): ChunkSectionMesh { + val mesh = ChunkSectionMesh(renderWindow) + val random = Random(0L) + + var block: BlockState? + val neighbours: Array = arrayOfNulls(Directions.SIZE) + + 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)] + + // ToDo: Chunk borders + neighbours[Directions.DOWN.ordinal] = if (y == 0) { + null + } else { + section.blocks[ChunkSection.getIndex(x, y - 1, z)] + } + neighbours[Directions.UP.ordinal] = if (y == ProtocolDefinition.SECTION_MAX_Y) { + null + } 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)] + } + + random.setSeed(VecUtil.generatePositionHash(x, y, z)) + block?.model?.singleRender(Vec3i(x, y, z), mesh, random, neighbours, 0xFF, intArrayOf(0xF, 0xF, 0xF, 0xF)) + } + } + } + + + return mesh + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GenericSectionPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GenericSectionPreparer.kt new file mode 100644 index 000000000..6eaac372f --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GenericSectionPreparer.kt @@ -0,0 +1,40 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.block.preparer + +import de.bixilon.minosoft.data.world.ChunkSection +import de.bixilon.minosoft.gui.rendering.RenderWindow +import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh +import de.bixilon.minosoft.util.logging.Log +import de.bixilon.minosoft.util.logging.LogLevels +import de.bixilon.minosoft.util.logging.LogMessageType + + +class GenericSectionPreparer( + val renderWindow: RenderWindow, + private val preparer: AbstractSectionPreparer = GreedySectionPreparer(renderWindow), +) : AbstractSectionPreparer { + + override fun prepare(section: ChunkSection): ChunkSectionMesh { + val startTime = System.nanoTime() + + val mesh = preparer.prepare(section) + + val time = System.nanoTime() + val delta = time - startTime + Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Preparing took ${delta}ns, ${delta / 1000}µs, ${delta / 1000000}ms" } + + return mesh + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/SectionPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt similarity index 83% rename from src/main/java/de/bixilon/minosoft/gui/rendering/block/SectionPreparer.kt rename to src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt index 2e8894832..0f4ee832d 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/SectionPreparer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/preparer/GreedySectionPreparer.kt @@ -11,37 +11,38 @@ * This software is not affiliated with Mojang AB, the original developer of Minecraft. */ -package de.bixilon.minosoft.gui.rendering.block +package de.bixilon.minosoft.gui.rendering.block.preparer import de.bixilon.minosoft.data.direction.Directions +import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.world.ChunkSection import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh 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 de.bixilon.minosoft.util.logging.Log -import de.bixilon.minosoft.util.logging.LogLevels -import de.bixilon.minosoft.util.logging.LogMessageType import glm_.vec3.Vec3i import java.util.* -class SectionPreparer( +class GreedySectionPreparer( val renderWindow: RenderWindow, -) { +) : AbstractSectionPreparer { private fun renderNormal(position: Vec3i, section: ChunkSection, mesh: ChunkSectionMesh, random: Random) { // ToDo } - fun prepare(section: ChunkSection): ChunkSectionMesh { - val startTime = System.nanoTime() + override fun prepare(section: ChunkSection): ChunkSectionMesh { val mesh = ChunkSectionMesh(renderWindow) val random = Random(0L) + var currentBlock: BlockState? + var compareBlock: BlockState? + var start: Vec3i + var end: Vec3i for (direction in Directions.VALUES) { // Sweep over each axis (X, Y and Z) @@ -80,8 +81,8 @@ class SectionPreparer( n++ continue } - val currentBlock = if (position[axis] >= 0) section.blocks[ChunkSection.getIndex(position[0], position[1], position[2])] else null - val compareBlock = if (position[axis] < SECTION_SIZE - 1) section.blocks[ChunkSection.getIndex(position[0] + checkOffset[0], position[1] + checkOffset[1], position[2] + checkOffset[2])] else null + currentBlock = if (position[axis] >= 0) section.blocks[ChunkSection.getIndex(position[0], position[1], position[2])] else null + compareBlock = if (position[axis] < SECTION_SIZE - 1) section.blocks[ChunkSection.getIndex(position[0] + checkOffset[0], position[1] + checkOffset[1], position[2] + checkOffset[2])] else null // The mask is set to true if there is a visible face between two blocks, // i.e. both aren't empty and both aren't blocks @@ -150,14 +151,14 @@ class SectionPreparer( position[axis] -= offsetCheck } - val start = Vec3i(position[0], position[1], position[2]) + start = Vec3i(position[0], position[1], position[2]) - val end = Vec3i(position[0] + du[0] + dv[0], position[1] + du[1] + dv[1], position[2] + du[2] + dv[2]) + end = Vec3i(position[0] + du[0] + dv[0], position[1] + du[1] + dv[1], position[2] + du[2] + dv[2]) - val block = section.blocks[ChunkSection.getIndex(position[0], position[1], position[2])]!! - (block.model as GreedyBakedBlockModel).greedyRender(start, end, direction, mesh, 0xFF) + currentBlock = section.blocks[ChunkSection.getIndex(position[0], position[1], position[2])]!! + (currentBlock.model as GreedyBakedBlockModel).greedyRender(start, end, direction, mesh, 0xFF) if (!backFace) { @@ -188,11 +189,6 @@ class SectionPreparer( } } - - val time = System.nanoTime() - val delta = time - startTime - Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Preparing took ${delta}ns, ${delta / 1000}µs, ${delta / 1000000}ms" } - return mesh } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/unbaked/block/UnbakedBlockStateModel.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/unbaked/block/UnbakedBlockStateModel.kt index 06a1638b8..2919bd1e2 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/unbaked/block/UnbakedBlockStateModel.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/unbaked/block/UnbakedBlockStateModel.kt @@ -68,7 +68,7 @@ data class UnbakedBlockStateModel( } - val faces: Array> = Array(Directions.VALUES.size) { mutableListOf() } + val faces: Array> = Array(Directions.SIZE) { mutableListOf() } for (element in model.elements) { val rescale = element.rotation?.rescale ?: false diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt index 1a11f41bf..95d5fae2b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt @@ -279,7 +279,7 @@ object VecUtil { val Vec3d.empty: Boolean get() = this.length() < 0.001 - private fun generatePositionHash(x: Int, y: Int, z: Int): Long { + fun generatePositionHash(x: Int, y: Int, z: Int): Long { var hash = (x * 3129871L) xor z.toLong() * 116129781L xor y.toLong() hash = hash * hash * 42317861L + hash * 11L return hash shr 16