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
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!!

View File

@ -37,8 +37,8 @@ class WorldRenderer(
val renderWindow: RenderWindow,
) : Renderer {
lateinit var chunkShader: Shader
val allChunkSections = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>()
val visibleChunks = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>()
val allChunkSections = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, ChunkMeshCollection>>()
val visibleChunks = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, ChunkMeshCollection>>()
val queuedChunks: MutableSet<ChunkPosition> = mutableSetOf()
var meshes = 0
@ -46,7 +46,7 @@ class WorldRenderer(
var triangles = 0
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()
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<Int, SectionArrayMesh> = ConcurrentHashMap()
val visibleIndexMap: ConcurrentHashMap<Int, ChunkMeshCollection> = ConcurrentHashMap()
for ((index, mesh) in indexMap) {
if (renderWindow.camera.frustum.containsChunk(chunkLocation, mesh.lowestBlockHeight, mesh.highestBlockHeight)) {
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.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<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>)

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.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<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())
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)
}
}
}

View File

@ -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<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 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<Vec3>, texturePositions: Array<Vec2?>) {
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
}
}
}
}

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.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<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 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<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) {
val input = Vec4(drawPositions[vertex.first], 1.0f)
val output = modelMatrix * input