rendering: transparency improvements (2 meshes)

This commit is contained in:
Bixilon 2021-03-24 18:24:23 +01:00
parent c31115b858
commit 6930fa378a
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
7 changed files with 99 additions and 34 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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
}

View File

@ -25,8 +25,6 @@ import org.lwjgl.opengl.GL20.glEnableVertexAttribArray
import org.lwjgl.opengl.GL20.glVertexAttribPointer import org.lwjgl.opengl.GL20.glVertexAttribPointer
class SectionArrayMesh : Mesh(initialCacheSize = 100000) { class SectionArrayMesh : Mesh(initialCacheSize = 100000) {
var lowestBlockHeight = 0
var highestBlockHeight = 0
fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?, lightLevel: Int = 14) { fun addVertex(position: Vec3, textureCoordinates: Vec2, texture: Texture, tintColor: RGBColor?, lightLevel: Int = 14) {
val data = data!! val data = data!!

View File

@ -37,8 +37,8 @@ class WorldRenderer(
val renderWindow: RenderWindow, val renderWindow: RenderWindow,
) : Renderer { ) : Renderer {
lateinit var chunkShader: Shader lateinit var chunkShader: Shader
val allChunkSections = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>() val allChunkSections = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, ChunkMeshCollection>>()
val visibleChunks = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>() val visibleChunks = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, ChunkMeshCollection>>()
val queuedChunks: MutableSet<ChunkPosition> = mutableSetOf() val queuedChunks: MutableSet<ChunkPosition> = mutableSetOf()
var meshes = 0 var meshes = 0
@ -46,7 +46,7 @@ class WorldRenderer(
var triangles = 0 var triangles = 0
private set private set
private fun prepareSections(chunkPosition: ChunkPosition, sections: Map<Int, ChunkSection>): SectionArrayMesh { private fun prepareSections(chunkPosition: ChunkPosition, sections: Map<Int, ChunkSection>): ChunkMeshCollection {
// val stopwatch = Stopwatch() // val stopwatch = Stopwatch()
check(sections.isNotEmpty()) { "Illegal argument!" } 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 chunk = world.getChunk(chunkPosition) ?: error("Chunk in world is null at $chunkPosition?")
val dimensionSupports3dBiomes = connection.player.world.dimension?.supports3DBiomes ?: false val dimensionSupports3dBiomes = connection.player.world.dimension?.supports3DBiomes ?: false
val mesh = SectionArrayMesh() val meshCollection = ChunkMeshCollection()
for ((sectionHeight, section) in sections) { for ((sectionHeight, section) in sections) {
for ((index, blockInfo) in section.blocks.withIndex()) { for ((index, blockInfo) in section.blocks.withIndex()) {
@ -88,12 +88,16 @@ class WorldRenderer(
blockInfo.tintColor?.let { tintColor = it } 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() // stopwatch.labPrint()
return mesh return meshCollection
} }
override fun init() { override fun init() {
@ -127,7 +131,13 @@ class WorldRenderer(
for ((_, map) in visibleChunks) { for ((_, map) in visibleChunks) {
for ((_, mesh) in map) { 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 return
} }
Minosoft.THREAD_POOL.execute { Minosoft.THREAD_POOL.execute {
val mesh = prepareSections(chunkPosition, sections) val meshCollection = prepareSections(chunkPosition, sections)
var lowestBlockHeight = 0 var lowestBlockHeight = 0
var highestBlockHeight = 0 var highestBlockHeight = 0
@ -232,27 +242,41 @@ class WorldRenderer(
val index = getSectionIndex(highestBlockHeight) val index = getSectionIndex(highestBlockHeight)
mesh.lowestBlockHeight = lowestBlockHeight meshCollection.lowestBlockHeight = lowestBlockHeight
mesh.highestBlockHeight = highestBlockHeight meshCollection.highestBlockHeight = highestBlockHeight
renderWindow.renderQueue.add { renderWindow.renderQueue.add {
val sectionMap = allChunkSections.getOrPut(chunkPosition, { ConcurrentHashMap() }) val sectionMap = allChunkSections.getOrPut(chunkPosition, { ConcurrentHashMap() })
sectionMap[index]?.let { sectionMap[index]?.let {
it.unload() it.opaqueSectionArrayMesh.unload()
meshes-- meshes--
triangles -= it.trianglesCount triangles -= it.opaqueSectionArrayMesh.trianglesCount
it.transparentSectionArrayMesh?.let {
it.unload()
meshes--
triangles -= it.trianglesCount
}
} }
mesh.load() meshCollection.opaqueSectionArrayMesh.let {
meshes++ it.load()
triangles += mesh.trianglesCount 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)) { if (renderWindow.camera.frustum.containsChunk(chunkPosition, lowestBlockHeight, highestBlockHeight)) {
visibleChunks.getOrPut(chunkPosition, { ConcurrentHashMap() })[index] = mesh visibleChunks.getOrPut(chunkPosition, { ConcurrentHashMap() })[index] = meshCollection
} else { } else {
visibleChunks[chunkPosition]?.remove(index) visibleChunks[chunkPosition]?.remove(index)
} }
@ -289,10 +313,17 @@ class WorldRenderer(
} }
renderWindow.renderQueue.add { renderWindow.renderQueue.add {
allChunkSections[chunkPosition]?.let { allChunkSections[chunkPosition]?.let {
for ((_, mesh) in it) { for ((_, meshCollection) in it) {
mesh.unload() meshCollection.opaqueSectionArrayMesh.let {
meshes-- it.unload()
triangles -= mesh.trianglesCount meshes--
triangles -= it.trianglesCount
}
meshCollection.transparentSectionArrayMesh?.let {
it.unload()
meshes--
triangles -= it.trianglesCount
}
} }
allChunkSections.remove(chunkPosition) allChunkSections.remove(chunkPosition)
visibleChunks.remove(chunkPosition) visibleChunks.remove(chunkPosition)
@ -314,7 +345,7 @@ class WorldRenderer(
fun recalculateVisibleChunks() { fun recalculateVisibleChunks() {
visibleChunks.clear() visibleChunks.clear()
for ((chunkLocation, indexMap) in allChunkSections) { for ((chunkLocation, indexMap) in allChunkSections) {
val visibleIndexMap: ConcurrentHashMap<Int, SectionArrayMesh> = ConcurrentHashMap() val visibleIndexMap: ConcurrentHashMap<Int, ChunkMeshCollection> = ConcurrentHashMap()
for ((index, mesh) in indexMap) { for ((index, mesh) in indexMap) {
if (renderWindow.camera.frustum.containsChunk(chunkLocation, mesh.lowestBlockHeight, mesh.highestBlockHeight)) { if (renderWindow.camera.frustum.containsChunk(chunkLocation, mesh.lowestBlockHeight, mesh.highestBlockHeight)) {
visibleIndexMap[index] = mesh visibleIndexMap[index] = mesh

View File

@ -6,14 +6,14 @@ import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.light.LightAccessor 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 import de.bixilon.minosoft.gui.rendering.textures.Texture
interface BlockRenderInterface { interface BlockRenderInterface {
val fullFaceDirections: Array<Directions?> val fullFaceDirections: Array<Directions?>
val transparentFaces: Array<Directions?> val transparentFaces: Array<Directions?>
fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array<BlockState?>, world: World) fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, meshCollection: ChunkMeshCollection, neighbourBlocks: Array<BlockState?>, world: World)
fun resolveTextures(indexed: MutableList<Texture>, textureMap: MutableMap<String, Texture>) fun resolveTextures(indexed: MutableList<Texture>, textureMap: MutableMap<String, Texture>)

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.light.LightAccessor 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.chunk.models.loading.BlockModel
import de.bixilon.minosoft.gui.rendering.textures.Texture import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureTransparencies 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<BlockState?>, world: World) { override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, meshCollection: ChunkMeshCollection, neighbourBlocks: Array<BlockState?>, world: World) {
val modelMatrix = Mat4().translate(position.toVec3()) val modelMatrix = Mat4().translate(position.toVec3())
for (direction in Directions.DIRECTIONS) { for (direction in Directions.DIRECTIONS) {
@ -112,7 +112,7 @@ class BlockRenderer: BlockRenderInterface {
if (neighbourBlockFullFace && cullFace) { if (neighbourBlockFullFace && cullFace) {
continue continue
} }
element.render(tintColor, position, lightAccessor, textureMapping, modelMatrix, direction, mesh) element.render(tintColor, position, lightAccessor, textureMapping, modelMatrix, direction, meshCollection)
} }
} }
} }

View File

@ -20,11 +20,13 @@ import de.bixilon.minosoft.data.Directions
import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.light.LightAccessor 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.SectionArrayMesh
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModel 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.BlockModelElement
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace 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.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureTransparencies
import de.bixilon.minosoft.gui.rendering.util.VecUtil import de.bixilon.minosoft.gui.rendering.util.VecUtil
import glm_.Java.Companion.glm import glm_.Java.Companion.glm
import glm_.mat4x4.Mat4 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<String, Texture>, modelMatrix: Mat4, direction: Directions, mesh: SectionArrayMesh) { fun render(tintColor: RGBColor?, position: BlockPosition, lightAccessor: LightAccessor, textureMapping: MutableMap<String, Texture>, modelMatrix: Mat4, direction: Directions, meshCollection: ChunkMeshCollection) {
val realDirection = directionMapping.inverse()[direction]!! val realDirection = directionMapping.inverse()[direction]!!
val face = faces[realDirection] ?: return // Not our face 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 drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]])
val mesh = getMesh(meshCollection, texture.transparency)
fun createQuad(drawPositions: Array<Vec3>, texturePositions: Array<Vec2?>) { fun createQuad(drawPositions: Array<Vec3>, texturePositions: Array<Vec2?>) {
for (vertex in DRAW_ODER) { for (vertex in DRAW_ODER) {
val input = Vec4(drawPositions[vertex.first], 1.0f) 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) val texturePositions = face.getTexturePositionArray(realDirection)
createQuad(drawPositions, texturePositions) 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_2, POSITION_4, POSITION_6, POSITION_8),
setOf(POSITION_1, POSITION_3, POSITION_5, POSITION_7) 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
}
}
} }
} }

View File

@ -7,7 +7,7 @@ import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.data.world.BlockPosition import de.bixilon.minosoft.data.world.BlockPosition
import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.light.LightAccessor 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.BlockModelElement
import de.bixilon.minosoft.gui.rendering.chunk.models.loading.BlockModelFace 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.Texture
@ -28,7 +28,7 @@ class FluidRenderer(
private var still: Texture? = null private var still: Texture? = null
private var flowing: Texture? = null private var flowing: Texture? = null
override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, mesh: SectionArrayMesh, neighbourBlocks: Array<BlockState?>, world: World) { override fun render(blockState: BlockState, lightAccessor: LightAccessor, tintColor: RGBColor?, position: BlockPosition, meshCollection: ChunkMeshCollection, neighbourBlocks: Array<BlockState?>, world: World) {
val modelMatrix = Mat4().translate(position.toVec3()) val modelMatrix = Mat4().translate(position.toVec3())
val lightLevel = lightAccessor.getLightLevel(position) val lightLevel = lightAccessor.getLightLevel(position)
val heights = calculateHeights(neighbourBlocks, blockState, world, position) val heights = calculateHeights(neighbourBlocks, blockState, world, position)
@ -56,7 +56,7 @@ class FluidRenderer(
face.rotate(angle) face.rotate(angle)
val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[direction.ordinal] val positionTemplate = BlockModelElement.FACE_POSITION_MAP_TEMPLATE[direction.ordinal]
val drawPositions = arrayOf(positions[positionTemplate[0]], positions[positionTemplate[1]], positions[positionTemplate[2]], positions[positionTemplate[3]]) 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 return heights.toSet().size != 1 // liquid is flowing, if not all of the heights are the same
} }
private fun createQuad(drawPositions: Array<Vec3>, texturePositions: Array<Vec2?>, texture: Texture, modelMatrix: Mat4, mesh: SectionArrayMesh, tintColor: RGBColor?, lightLevel: Int) { private fun createQuad(drawPositions: Array<Vec3>, texturePositions: Array<Vec2?>, texture: Texture, modelMatrix: Mat4, meshCollection: ChunkMeshCollection, tintColor: RGBColor?, lightLevel: Int) {
val mesh = ElementRenderer.getMesh(meshCollection, texture.transparency)
for (vertex in ElementRenderer.DRAW_ODER) { for (vertex in ElementRenderer.DRAW_ODER) {
val input = Vec4(drawPositions[vertex.first], 1.0f) val input = Vec4(drawPositions[vertex.first], 1.0f)
val output = modelMatrix * input val output = modelMatrix * input