From 6930fa378a355708fc7f4d05e5de3cb8402e5ddb Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 24 Mar 2021 18:24:23 +0100 Subject: [PATCH] rendering: transparency improvements (2 meshes) --- .../rendering/chunk/ChunkMeshCollection.kt | 22 ++++++ .../gui/rendering/chunk/SectionArrayMesh.kt | 2 - .../gui/rendering/chunk/WorldRenderer.kt | 75 +++++++++++++------ .../models/renderable/BlockRenderInterface.kt | 4 +- .../chunk/models/renderable/BlockRenderer.kt | 6 +- .../models/renderable/ElementRenderer.kt | 15 +++- .../chunk/models/renderable/FluidRenderer.kt | 9 ++- 7 files changed, 99 insertions(+), 34 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMeshCollection.kt diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMeshCollection.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMeshCollection.kt new file mode 100644 index 000000000..5fc36db7a --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/ChunkMeshCollection.kt @@ -0,0 +1,22 @@ +/* + * 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.chunk + +data class ChunkMeshCollection( + val opaqueSectionArrayMesh: SectionArrayMesh = SectionArrayMesh(), + var transparentSectionArrayMesh: SectionArrayMesh? = SectionArrayMesh(), +) { + var lowestBlockHeight = 0 + var highestBlockHeight = 0 +} 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 0f0772f1d..171bfe171 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 @@ -25,8 +25,6 @@ import org.lwjgl.opengl.GL20.glEnableVertexAttribArray import org.lwjgl.opengl.GL20.glVertexAttribPointer class SectionArrayMesh : Mesh(initialCacheSize = 100000) { - var lowestBlockHeight = 0 - var highestBlockHeight = 0 fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?, lightLevel: Int = 14) { val data = data!! 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 d84b80f9a..e7f2db85a 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 @@ -37,8 +37,8 @@ class WorldRenderer( val renderWindow: RenderWindow, ) : Renderer { lateinit var chunkShader: Shader - val allChunkSections = ConcurrentHashMap>() - val visibleChunks = ConcurrentHashMap>() + val allChunkSections = ConcurrentHashMap>() + val visibleChunks = ConcurrentHashMap>() val queuedChunks: MutableSet = mutableSetOf() var meshes = 0 @@ -46,7 +46,7 @@ class WorldRenderer( var triangles = 0 private set - private fun prepareSections(chunkPosition: ChunkPosition, sections: Map): SectionArrayMesh { + private fun prepareSections(chunkPosition: ChunkPosition, sections: Map): ChunkMeshCollection { // val stopwatch = Stopwatch() check(sections.isNotEmpty()) { "Illegal argument!" } @@ -56,7 +56,7 @@ class WorldRenderer( val chunk = world.getChunk(chunkPosition) ?: error("Chunk in world is null at $chunkPosition?") val dimensionSupports3dBiomes = connection.player.world.dimension?.supports3DBiomes ?: false - val mesh = SectionArrayMesh() + val meshCollection = ChunkMeshCollection() for ((sectionHeight, section) in sections) { for ((index, blockInfo) in section.blocks.withIndex()) { @@ -88,12 +88,16 @@ class WorldRenderer( blockInfo.tintColor?.let { tintColor = it } } - blockInfo.getBlockRenderer(blockPosition).render(blockInfo, world.worldLightAccessor, tintColor, blockPosition, mesh, neighborBlocks, world) + blockInfo.getBlockRenderer(blockPosition).render(blockInfo, world.worldLightAccessor, tintColor, blockPosition, meshCollection, neighborBlocks, world) } } + if (meshCollection.transparentSectionArrayMesh!!.trianglesCount == 0) { + meshCollection.transparentSectionArrayMesh = null + } + // stopwatch.labPrint() - return mesh + return meshCollection } override fun init() { @@ -127,7 +131,13 @@ class WorldRenderer( for ((_, map) in visibleChunks) { for ((_, mesh) in map) { - mesh.draw() + mesh.opaqueSectionArrayMesh.draw() + } + } + + for ((_, map) in visibleChunks) { + for ((_, mesh) in map) { + mesh.transparentSectionArrayMesh?.draw() } } } @@ -214,7 +224,7 @@ class WorldRenderer( return } Minosoft.THREAD_POOL.execute { - val mesh = prepareSections(chunkPosition, sections) + val meshCollection = prepareSections(chunkPosition, sections) var lowestBlockHeight = 0 var highestBlockHeight = 0 @@ -232,27 +242,41 @@ class WorldRenderer( val index = getSectionIndex(highestBlockHeight) - mesh.lowestBlockHeight = lowestBlockHeight - mesh.highestBlockHeight = highestBlockHeight + meshCollection.lowestBlockHeight = lowestBlockHeight + meshCollection.highestBlockHeight = highestBlockHeight renderWindow.renderQueue.add { val sectionMap = allChunkSections.getOrPut(chunkPosition, { ConcurrentHashMap() }) sectionMap[index]?.let { - it.unload() + it.opaqueSectionArrayMesh.unload() meshes-- - triangles -= it.trianglesCount + triangles -= it.opaqueSectionArrayMesh.trianglesCount + + it.transparentSectionArrayMesh?.let { + it.unload() + meshes-- + triangles -= it.trianglesCount + } } - mesh.load() - meshes++ - triangles += mesh.trianglesCount + meshCollection.opaqueSectionArrayMesh.let { + it.load() + meshes++ + triangles += it.trianglesCount + } + meshCollection.transparentSectionArrayMesh?.let { + it.load() + meshes++ + triangles += it.trianglesCount + } - sectionMap[index] = mesh + + sectionMap[index] = meshCollection if (renderWindow.camera.frustum.containsChunk(chunkPosition, lowestBlockHeight, highestBlockHeight)) { - visibleChunks.getOrPut(chunkPosition, { ConcurrentHashMap() })[index] = mesh + visibleChunks.getOrPut(chunkPosition, { ConcurrentHashMap() })[index] = meshCollection } else { visibleChunks[chunkPosition]?.remove(index) } @@ -289,10 +313,17 @@ class WorldRenderer( } renderWindow.renderQueue.add { allChunkSections[chunkPosition]?.let { - for ((_, mesh) in it) { - mesh.unload() - meshes-- - triangles -= mesh.trianglesCount + for ((_, meshCollection) in it) { + meshCollection.opaqueSectionArrayMesh.let { + it.unload() + meshes-- + triangles -= it.trianglesCount + } + meshCollection.transparentSectionArrayMesh?.let { + it.unload() + meshes-- + triangles -= it.trianglesCount + } } allChunkSections.remove(chunkPosition) visibleChunks.remove(chunkPosition) @@ -314,7 +345,7 @@ class WorldRenderer( fun recalculateVisibleChunks() { visibleChunks.clear() for ((chunkLocation, indexMap) in allChunkSections) { - val visibleIndexMap: ConcurrentHashMap = ConcurrentHashMap() + val visibleIndexMap: ConcurrentHashMap = ConcurrentHashMap() for ((index, mesh) in indexMap) { if (renderWindow.camera.frustum.containsChunk(chunkLocation, mesh.lowestBlockHeight, mesh.highestBlockHeight)) { visibleIndexMap[index] = mesh diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt index 2af7bdb2f..4ef9ce508 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderInterface.kt @@ -6,14 +6,14 @@ import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.light.LightAccessor -import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh +import de.bixilon.minosoft.gui.rendering.chunk.ChunkMeshCollection import de.bixilon.minosoft.gui.rendering.textures.Texture interface BlockRenderInterface { val fullFaceDirections: Array val transparentFaces: Array - fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array, world: World) + fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, meshCollection: ChunkMeshCollection, neighbourBlocks: Array, world: World) fun resolveTextures(indexed: MutableList, textureMap: MutableMap) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt index 86f472610..769763022 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/BlockRenderer.kt @@ -21,7 +21,7 @@ import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.light.LightAccessor -import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh +import de.bixilon.minosoft.gui.rendering.chunk.ChunkMeshCollection import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel import de.bixilon.minosoft.gui.rendering.textures.Texture import de.bixilon.minosoft.gui.rendering.textures.TextureTransparencies @@ -92,7 +92,7 @@ class BlockRenderer: BlockRenderInterface { } } - override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array, world: World) { + override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, meshCollection: ChunkMeshCollection, neighbourBlocks: Array, world: World) { val modelMatrix = Mat4().translate(position.toVec3()) for (direction in Directions.DIRECTIONS) { @@ -112,7 +112,7 @@ class BlockRenderer: BlockRenderInterface { if (neighbourBlockFullFace && cullFace) { continue } - element.render(tintColor, position, lightAccessor, textureMapping, modelMatrix, direction, mesh) + element.render(tintColor, position, lightAccessor, textureMapping, modelMatrix, direction, meshCollection) } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt index 8bd7db2f3..70f16356f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/ElementRenderer.kt @@ -20,11 +20,13 @@ import de.bixilon.minosoft.data.Directions import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.light.LightAccessor +import de.bixilon.minosoft.gui.rendering.chunk.ChunkMeshCollection import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace import de.bixilon.minosoft.gui.rendering.textures.Texture +import de.bixilon.minosoft.gui.rendering.textures.TextureTransparencies import de.bixilon.minosoft.gui.rendering.util.VecUtil import glm_.Java.Companion.glm import glm_.mat4x4.Mat4 @@ -62,7 +64,7 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo } - fun render(tintColor: RGBColor?, position: BlockPosition, lightAccessor: LightAccessor, textureMapping: MutableMap, modelMatrix: Mat4, direction: Directions, mesh: SectionArrayMesh) { + fun render(tintColor: RGBColor?, position: BlockPosition, lightAccessor: LightAccessor, textureMapping: MutableMap, modelMatrix: Mat4, direction: Directions, meshCollection: ChunkMeshCollection) { val realDirection = directionMapping.inverse()[direction]!! val face = faces[realDirection] ?: return // Not our face @@ -74,6 +76,8 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]]) + val mesh = getMesh(meshCollection, texture.transparency) + fun createQuad(drawPositions: Array, texturePositions: Array) { for (vertex in DRAW_ODER) { val input = Vec4(drawPositions[vertex.first], 1.0f) @@ -91,6 +95,7 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo ) } } + val texturePositions = face.getTexturePositionArray(realDirection) createQuad(drawPositions, texturePositions) } @@ -181,6 +186,14 @@ class ElementRenderer(parent: BlockModelElement, val rotation: Vec3, uvLock: Boo setOf(POSITION_2, POSITION_4, POSITION_6, POSITION_8), setOf(POSITION_1, POSITION_3, POSITION_5, POSITION_7) ) + + fun getMesh(meshCollection: ChunkMeshCollection, textureTransparencies: TextureTransparencies): SectionArrayMesh { + return if (textureTransparencies == TextureTransparencies.SEMI_TRANSPARENT) { + meshCollection.transparentSectionArrayMesh!! + } else { + meshCollection.opaqueSectionArrayMesh + } + } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt index a5e9f435a..2607234fa 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/models/renderable/FluidRenderer.kt @@ -7,7 +7,7 @@ import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.light.LightAccessor -import de.bixilon.minosoft.gui.rendering.chunk.SectionArrayMesh +import de.bixilon.minosoft.gui.rendering.chunk.ChunkMeshCollection import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelElement import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace import de.bixilon.minosoft.gui.rendering.textures.Texture @@ -28,7 +28,7 @@ class FluidRenderer( private var still: Texture? = null private var flowing: Texture? = null - override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array, world: World) { + override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, meshCollection: ChunkMeshCollection, neighbourBlocks: Array, world: World) { val modelMatrix = Mat4().translate(position.toVec3()) val lightLevel = lightAccessor.getLightLevel(position) val heights = calculateHeights(neighbourBlocks, blockState, world, position) @@ -56,7 +56,7 @@ class FluidRenderer( face.rotate(angle) val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[direction.ordinal] val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]]) - createQuad(drawPositions, face.getTexturePositionArray(direction), texture, modelMatrix, mesh, tintColor, lightLevel) + createQuad(drawPositions, face.getTexturePositionArray(direction), texture, modelMatrix, meshCollection, tintColor, lightLevel) } } @@ -103,7 +103,8 @@ class FluidRenderer( return heights.toSet().size != 1 // liquid is flowing, if not all of the heights are the same } - private fun createQuad(drawPositions: Array, texturePositions: Array, texture: Texture, modelMatrix: Mat4, mesh: SectionArrayMesh, tintColor: RGBColor?, lightLevel: Int) { + private fun createQuad(drawPositions: Array, texturePositions: Array, texture: Texture, modelMatrix: Mat4, meshCollection: ChunkMeshCollection, tintColor: RGBColor?, lightLevel: Int) { + val mesh = ElementRenderer.getMesh(meshCollection, texture.transparency) for (vertex in ElementRenderer.DRAW_ODER) { val input = Vec4(drawPositions[vertex.first], 1.0f) val output = modelMatrix * input