wip render layers

This should reduce opaque/translucent artefacts, remove the obscure TransparentDrawable, ... interfaces, allow more (and dynamic!) render "phases", reduce gpu calls and draw opaque before transparent (performance gain)
This commit is contained in:
Moritz Zwerger 2023-07-26 00:44:02 +02:00
parent 6803f5f698
commit 2770e04bf6
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
20 changed files with 284 additions and 268 deletions

View File

@ -37,13 +37,14 @@ import de.bixilon.minosoft.gui.rendering.chunk.shader.ChunkTextShader
import de.bixilon.minosoft.gui.rendering.chunk.util.ChunkRendererChangeListener
import de.bixilon.minosoft.gui.rendering.events.VisibilityGraphChangeEvent
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
import de.bixilon.minosoft.gui.rendering.system.base.DepthFunctions
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.base.RenderingCapabilities
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.gui.rendering.system.base.layer.OpaqueLayer
import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.layer.TransparentLayer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.blockPosition
@ -55,7 +56,8 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
class ChunkRenderer(
val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, OpaqueDrawable, TranslucentDrawable, TransparentDrawable {
) : WorldRenderer {
override val layers = LayerSettings()
private val profile = connection.profiles.block
override val renderSystem: RenderSystem = context.system
val visibilityGraph = context.camera.visibilityGraph
@ -88,6 +90,13 @@ class ChunkRenderer(
var cameraChunkPosition = Vec2i.EMPTY
var cameraSectionHeight = 0
override fun registerLayers() {
layers.register(OpaqueLayer, shader, this::drawBlocksOpaque) { visible.opaque.isEmpty() }
layers.register(TransparentLayer, transparentShader, this::drawBlocksTransparent) { visible.transparent.isEmpty() }
layers.register(TransparentLayer, transparentShader, this::drawBlocksTranslucent) { visible.translucent.isEmpty() }
layers.register(TextLayer, textShader, this::drawText) { visible.text.isEmpty() }
layers.register(BlockEntitiesLayer, shader, this::drawBlockEntities) { visible.blockEntities.isEmpty() }
}
override fun init(latch: AbstractLatch) {
context.models.load(latch)
@ -218,52 +227,36 @@ class ChunkRenderer(
loadingQueue.work()
}
override fun setupOpaque() {
super.setupOpaque()
shader.use()
}
override fun drawOpaque() {
private fun drawBlocksOpaque() {
for (mesh in visible.opaque) {
mesh.draw()
}
}
context.system.depth = DepthFunctions.LESS_OR_EQUAL
for (blockEntity in visible.blockEntities) {
blockEntity.draw(context)
private fun drawBlocksTransparent() {
for (mesh in visible.transparent) {
mesh.draw()
}
}
override fun setupTranslucent() {
super.setupTranslucent()
shader.use()
}
override fun drawTranslucent() {
private fun drawBlocksTranslucent() {
for (mesh in visible.translucent) {
mesh.draw()
}
}
override fun setupTransparent() {
super.setupTransparent()
transparentShader.use()
}
override fun drawTransparent() {
for (mesh in visible.transparent) {
mesh.draw()
}
context.system.depth = DepthFunctions.LESS_OR_EQUAL
context.system[RenderingCapabilities.POLYGON_OFFSET] = true
context.system.polygonOffset(-2.5f, -2.5f)
textShader.use()
private fun drawText() {
for (mesh in visible.text) {
mesh.draw()
}
}
private fun drawBlockEntities() {
for (blockEntity in visible.blockEntities) {
blockEntity.draw(context)
}
}
private fun onFrustumChange() {
var sortQueue = false
val cameraPosition = Vec3(connection.player.renderInfo.eyePosition - context.camera.offset.offset)
@ -307,6 +300,16 @@ class ChunkRenderer(
}
private object TextLayer : RenderLayer {
override val settings = RenderSettings(blending = true, depth = DepthFunctions.LESS_OR_EQUAL, polygonOffset = true, polygonOffsetFactor = -2.5f, polygonOffsetUnit = -2.5f)
override val priority: Int get() = 1500
}
private object BlockEntitiesLayer : RenderLayer {
override val settings = RenderSettings(depth = DepthFunctions.LESS_OR_EQUAL)
override val priority: Int get() = 500
}
companion object : RendererBuilder<ChunkRenderer> {
override fun build(connection: PlayConnection, context: RenderContext): ChunkRenderer {

View File

@ -24,11 +24,13 @@ import de.bixilon.minosoft.data.world.border.WorldBorderState
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.phases.SkipAll
import de.bixilon.minosoft.gui.rendering.system.base.phases.TranslucentDrawable
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
@ -37,7 +39,8 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
class WorldBorderRenderer(
override val context: RenderContext,
) : WorldRenderer, AsyncRenderer, TranslucentDrawable, SkipAll {
) : WorldRenderer, AsyncRenderer, SkipAll {
override val layers = LayerSettings()
override val renderSystem: RenderSystem = context.system
private val shader = renderSystem.createShader(minosoft("world/border")) { WorldBorderShader(it) }
private var borderMesh: WorldBorderMesh? = null
@ -48,6 +51,10 @@ class WorldBorderRenderer(
get() = border.getDistanceTo(context.connection.player.physics.position) > MAX_DISTANCE
private var reload = false
override fun registerLayers() {
layers.register(WorldBorderLayer, shader, this::draw) { this.borderMesh == null }
}
override fun init(latch: AbstractLatch) {
shader.native.defines["MAX_DISTANCE"] = MAX_DISTANCE
shader.load()
@ -106,9 +113,18 @@ class WorldBorderRenderer(
this.reload = false
}
override fun setupTranslucent() {
private fun draw() {
val mesh = this.borderMesh ?: return
renderSystem.reset(
update()
if (mesh.state == Mesh.MeshStates.PREPARING) {
mesh.load()
}
mesh.draw()
}
private object WorldBorderLayer : RenderLayer {
override val settings = RenderSettings(
blending = true,
sourceRGB = BlendingFunctions.SOURCE_ALPHA,
destinationRGB = BlendingFunctions.ONE,
@ -119,16 +135,7 @@ class WorldBorderRenderer(
polygonOffsetFactor = -3.0f,
polygonOffsetUnit = -3.0f,
)
shader.use()
update()
if (mesh.state == Mesh.MeshStates.PREPARING) {
mesh.load()
}
}
override fun drawTranslucent() {
borderMesh?.draw()
override val priority get() = 3000
}
companion object : RendererBuilder<WorldBorderRenderer> {

View File

@ -27,9 +27,11 @@ import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.renderer.MeshSwapper
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
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.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.util.mesh.LineMesh
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.blockPosition
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.EMPTY
@ -43,7 +45,8 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
class ChunkBorderRenderer(
val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, AsyncRenderer, OpaqueDrawable, MeshSwapper {
) : WorldRenderer, AsyncRenderer, MeshSwapper {
override val layers = LayerSettings()
private val profile = connection.profiles.rendering
override val renderSystem: RenderSystem = context.system
private var offset = Vec3i.EMPTY
@ -55,8 +58,9 @@ class ChunkBorderRenderer(
override var nextMesh: LineMesh? = null
override var unload = false
override val skipOpaque: Boolean
get() = mesh == null || !profile.chunkBorder.enabled
override fun registerLayers() {
layers.register(ChunkBorderLayer, context.shaders.genericColorShader, this::draw) { mesh == null || !profile.chunkBorder.enabled }
}
override fun init(latch: AbstractLatch) {
context.input.bindings.register(
@ -189,17 +193,17 @@ class ChunkBorderRenderer(
}
}
override fun setupOpaque() {
context.system.reset(
private fun draw() {
mesh?.draw()
}
private object ChunkBorderLayer : RenderLayer {
override val settings = RenderSettings(
polygonOffset = true,
polygonOffsetFactor = -1.0f,
polygonOffsetUnit = -2.0f,
)
context.shaders.genericColorShader.use()
}
override fun drawOpaque() {
mesh?.draw()
override val priority get() = 1500
}

View File

@ -30,10 +30,12 @@ import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.renderer.MeshSwapper
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
import de.bixilon.minosoft.gui.rendering.system.base.DepthFunctions
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.base.phases.OtherDrawable
import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.util.VecUtil.getWorldOffset
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3d
import de.bixilon.minosoft.gui.rendering.util.mesh.LineMesh
@ -42,7 +44,8 @@ import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
class BlockOutlineRenderer(
val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, AsyncRenderer, OtherDrawable, MeshSwapper {
) : WorldRenderer, AsyncRenderer, MeshSwapper {
override val layers = LayerSettings()
private val profile = connection.profiles.block.outline
override val renderSystem: RenderSystem = context.system
@ -50,8 +53,6 @@ class BlockOutlineRenderer(
private var state: BlockState? = null
override var mesh: LineMesh? = null
override val skipOther: Boolean
get() = mesh == null
/**
* Unloads the current mesh and creates a new one
@ -62,6 +63,10 @@ class BlockOutlineRenderer(
override var nextMesh: LineMesh? = null
override var unload: Boolean = false
override fun registerLayers() {
layers.register(BlockOutlineLayer, context.shaders.genericColorShader, this::draw) { this.mesh == null }
}
override fun init(latch: AbstractLatch) {
this.profile::enabled.observe(this) { reload = true }
this.profile::collisions.observe(this) { reload = true }
@ -69,22 +74,12 @@ class BlockOutlineRenderer(
this.profile::collisionColor.observe(this) { reload = true }
}
override fun drawOther() {
private fun draw() {
val mesh = mesh ?: return
mesh.draw()
}
override fun setupOther() {
context.system.reset(
polygonOffset = true,
polygonOffsetFactor = -3.0f,
polygonOffsetUnit = -3.0f,
)
if (profile.showThroughWalls) {
context.system.depth = DepthFunctions.ALWAYS
}
context.shaders.genericColorShader.use()
mesh.draw()
}
override fun postPrepareDraw() {
@ -149,6 +144,14 @@ class BlockOutlineRenderer(
this.reload = false
}
private object BlockOutlineLayer : RenderLayer {
override val settings = RenderSettings(
polygonOffset = true,
polygonOffsetFactor = -3.0f,
polygonOffsetUnit = -3.0f,
)
override val priority get() = 1500
}
companion object : RendererBuilder<BlockOutlineRenderer> {

View File

@ -33,9 +33,11 @@ import de.bixilon.minosoft.gui.rendering.entity.models.EntityModel
import de.bixilon.minosoft.gui.rendering.entity.models.minecraft.player.LocalPlayerModel
import de.bixilon.minosoft.gui.rendering.events.VisibilityGraphChangeEvent
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
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.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.modding.event.events.EntityDestroyEvent
import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent
import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen
@ -46,7 +48,8 @@ import java.util.concurrent.atomic.AtomicInteger
class EntityRenderer(
val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, OpaqueDrawable {
) : WorldRenderer {
override val layers = LayerSettings()
override val renderSystem: RenderSystem = context.system
val profile = connection.profiles.entity
val visibilityGraph = context.camera.visibilityGraph
@ -62,6 +65,10 @@ class EntityRenderer(
private var reset = false
override fun registerLayers() {
layers.register(EntityLayer, null, this::draw) { visibleCount <= 0 }
}
override fun init(latch: AbstractLatch) {
connection.events.listen<EntitySpawnEvent> { event ->
if (event.entity is LocalPlayerEntity) return@listen
@ -134,11 +141,7 @@ class EntityRenderer(
models.lock.release()
}
override fun setupOpaque() {
context.system.reset(faceCulling = false)
}
override fun drawOpaque() {
private fun draw() {
// ToDo: Probably more transparent
models.lock.acquire()
for (model in models.unsafe.values) {
@ -156,6 +159,10 @@ class EntityRenderer(
models.lock.release()
}
private object EntityLayer : RenderLayer {
override val settings = RenderSettings(faceCulling = false)
override val priority: Int get() = 1000
}
companion object : RendererBuilder<EntityRenderer> {
private val HITBOX_TOGGLE_KEY_COMBINATION = minosoft("toggle_hitboxes")

View File

@ -28,10 +28,10 @@ import de.bixilon.minosoft.gui.rendering.gui.gui.popper.PopperManager
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDManager
import de.bixilon.minosoft.gui.rendering.gui.input.ModifierKeys
import de.bixilon.minosoft.gui.rendering.input.InputHandler
import de.bixilon.minosoft.gui.rendering.renderer.drawable.Drawable
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.gui.rendering.system.base.phases.OtherDrawable
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen
@ -42,7 +42,7 @@ import de.bixilon.minosoft.util.delegate.RenderingDelegate.observeRendering
class GUIRenderer(
val connection: PlayConnection,
override val context: RenderContext,
) : AsyncRenderer, InputHandler, OtherDrawable {
) : AsyncRenderer, InputHandler, Drawable {
private val profile = connection.profiles.gui
override val renderSystem = context.system
var scaledSize: Vec2 by observed(Vec2(context.window.size))
@ -130,7 +130,7 @@ class GUIRenderer(
dragged.drawAsync()
}
override fun drawOther() {
override fun draw() {
hud.draw()
gui.draw()
popper.draw()

View File

@ -27,11 +27,12 @@ import de.bixilon.minosoft.gui.rendering.events.CameraMatrixChangeEvent
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.base.layer.TranslucentLayer
import de.bixilon.minosoft.gui.rendering.system.base.layer.TransparentLayer
import de.bixilon.minosoft.gui.rendering.system.base.phases.SkipAll
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.listener.CallbackEventListener.Companion.listen
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnectionStates
@ -44,7 +45,8 @@ import de.bixilon.minosoft.util.collections.floats.BufferedArrayFloatList
class ParticleRenderer(
private val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, AsyncRenderer, TransparentDrawable, TranslucentDrawable, SkipAll, AbstractParticleRenderer {
) : WorldRenderer, AsyncRenderer, SkipAll, AbstractParticleRenderer {
override val layers = LayerSettings()
override val renderSystem: RenderSystem = context.system
private val profile = connection.profiles.particle
private val transparentShader = renderSystem.createShader(minosoft("particle")) { ParticleShader(it, true) }
@ -100,6 +102,11 @@ class ParticleRenderer(
val size: Int
get() = particles.size
override fun registerLayers() {
layers.register(TransparentLayer, transparentShader, this::drawTransparent)
layers.register(TranslucentLayer, translucentShader, this::drawTranslucent)
}
override fun init(latch: AbstractLatch) {
profile::maxAmount.observe(this, true) { maxAmount = minOf(it, MAXIMUM_AMOUNT) }
profile::enabled.observe(this, true) { enabled = it }
@ -242,21 +249,11 @@ class ParticleRenderer(
translucentMesh.load()
}
override fun setupTransparent() {
super.setupTransparent()
transparentShader.use()
}
override fun drawTransparent() {
private fun drawTransparent() {
transparentMesh.draw()
}
override fun setupTranslucent() {
super.setupTranslucent()
translucentShader.use()
}
override fun drawTranslucent() {
private fun drawTranslucent() {
translucentMesh.draw()
}

View File

@ -25,7 +25,6 @@ import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.system.base.PolygonModes
import de.bixilon.minosoft.gui.rendering.system.base.phases.PostDrawable
import de.bixilon.minosoft.gui.rendering.system.base.phases.PreDrawable
import de.bixilon.minosoft.gui.rendering.system.base.phases.RenderPhases
import de.bixilon.minosoft.gui.rendering.system.base.phases.SkipAll
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels

View File

@ -0,0 +1,24 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.renderer.renderer.world
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer
class LayerSettings {
fun register(layer: RenderLayer, shader: Shader?, renderer: () -> Unit, skip: (() -> Boolean)? = null) {
TODO()
}
}

View File

@ -11,13 +11,17 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.renderer.renderer
package de.bixilon.minosoft.gui.rendering.renderer.renderer.world
import de.bixilon.minosoft.gui.rendering.framebuffer.IntegratedFramebuffer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
/**
* A renderer that renders in world space (world framebuffer)
*/
interface WorldRenderer : Renderer {
override val framebuffer: IntegratedFramebuffer? get() = context.framebuffer.world
val layers: LayerSettings
fun registerLayers()
}

View File

@ -27,11 +27,12 @@ import de.bixilon.minosoft.data.world.weather.WorldWeather
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
import de.bixilon.minosoft.gui.rendering.renderer.renderer.WorldRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings
import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer
import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.base.RenderingCapabilities
import de.bixilon.minosoft.gui.rendering.system.base.phases.OpaqueDrawable
import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateLinear
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
@ -45,11 +46,12 @@ class CloudRenderer(
private val sky: SkyRenderer,
val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, OpaqueDrawable, AsyncRenderer {
) : WorldRenderer, AsyncRenderer {
override val layers = LayerSettings()
override val renderSystem: RenderSystem = context.system
val shader = renderSystem.createShader(minosoft("sky/clouds")) { CloudShader(it) }
val matrix = CloudMatrix()
private val layers: MutableList<CloudLayer> = mutableListOf()
private val cloudLayers: MutableList<CloudLayer> = mutableListOf()
private var position = Vec2i(Int.MIN_VALUE)
private var color: Vec3 = Vec3.EMPTY
private var maxDistance = 0.0f
@ -65,9 +67,10 @@ class CloudRenderer(
private var reset = false
override val skipOpaque: Boolean
get() = !sky.effects.clouds || !sky.profile.clouds.enabled || connection.profiles.block.viewDistance < 3 || layers.isEmpty()
override fun registerLayers() {
layers.register(CloudRenderLayer, shader, this::draw) { !sky.effects.clouds || !sky.profile.clouds.enabled || connection.profiles.block.viewDistance < 3 || cloudLayers.isEmpty() }
}
override fun asyncInit(latch: AbstractLatch) {
matrix.load(connection.assetsManager)
@ -86,7 +89,7 @@ class CloudRenderer(
override fun postInit(latch: AbstractLatch) {
shader.load()
sky.profile.clouds::movement.observe(this, instant = true) {
for (layer in layers) {
for (layer in cloudLayers) {
layer.movement = it
}
}
@ -98,7 +101,7 @@ class CloudRenderer(
}
// reset clouds
position = Vec2i(Int.MIN_VALUE)
for ((index, layer) in this.layers.withIndex()) {
for ((index, layer) in this.cloudLayers.withIndex()) {
layer.height = getCloudHeight(index)
}
}
@ -112,12 +115,12 @@ class CloudRenderer(
}
private fun updateLayers(layers: Int) {
while (layers < this.layers.size) {
toUnload += this.layers.removeLast()
while (layers < this.cloudLayers.size) {
toUnload += this.cloudLayers.removeLast()
}
for (index in this.layers.size until layers) {
for (index in this.cloudLayers.size until layers) {
val layer = CloudLayer(sky, this, index, getCloudHeight(index))
this.layers += layer
this.cloudLayers += layer
}
}
@ -130,7 +133,7 @@ class CloudRenderer(
updateLayers(nextLayers)
reset = false
}
if (layers.size != nextLayers) {
if (cloudLayers.size != nextLayers) {
updateLayers(nextLayers)
}
@ -139,7 +142,7 @@ class CloudRenderer(
this.delta = delta / 1000.0f
this.time = time
for (layer in layers) {
for (layer in cloudLayers) {
layer.prepareAsync()
}
}
@ -151,16 +154,11 @@ class CloudRenderer(
if (!sky.effects.clouds) {
return
}
for (layer in layers) {
for (layer in cloudLayers) {
layer.prepare()
}
}
override fun setupOpaque() {
super.setupOpaque()
renderSystem.disable(RenderingCapabilities.FACE_CULLING)
}
private fun calculateDay(progress: Float): Vec3 {
return interpolateLinear((abs(progress - 0.5f) * 2).pow(2), DAY_COLOR, DAY_COLOR * 0.8f)
}
@ -239,8 +237,7 @@ class CloudRenderer(
shader.yOffset = yOffset
}
override fun drawOpaque() {
shader.use()
private fun draw() {
val color = calculateCloudsColor()
if (color != this.color) {
shader.cloudsColor = Vec4(color, 1.0f)
@ -249,11 +246,16 @@ class CloudRenderer(
setYOffset()
for (layer in layers) {
for (layer in cloudLayers) {
layer.draw()
}
}
private object CloudRenderLayer : RenderLayer {
override val settings = RenderSettings(faceCulling = false)
override val priority get() = 3000
}
companion object : RendererBuilder<CloudRenderer> {
private val RAIN_COLOR = Vec3(0.31f, 0.35f, 0.40f)
private val SUNRISE_COLOR = Vec3(0.85f, 0.68f, 0.36f)

View File

@ -24,6 +24,7 @@ import de.bixilon.minosoft.gui.rendering.system.base.buffer.uniform.FloatUniform
import de.bixilon.minosoft.gui.rendering.system.base.buffer.uniform.IntUniformBuffer
import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.FloatVertexBuffer
import de.bixilon.minosoft.gui.rendering.system.base.buffer.vertex.PrimitiveTypes
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshStruct
@ -59,16 +60,21 @@ interface RenderSystem {
polygonOffsetFactor: Float = 0.0f,
polygonOffsetUnit: Float = 0.0f,
) {
setBlendFunction(sourceRGB, destinationRGB, sourceAlpha, destinationAlpha)
this[RenderingCapabilities.DEPTH_TEST] = depthTest
this[RenderingCapabilities.BLENDING] = blending
this[RenderingCapabilities.FACE_CULLING] = faceCulling
this[RenderingCapabilities.POLYGON_OFFSET] = polygonOffset
this.depth = depth
this.depthMask = depthMask
this.clearColor = clearColor
val settings = RenderSettings(depthTest, blending, faceCulling, polygonOffset, depthMask, sourceRGB, destinationRGB, sourceAlpha, destinationAlpha, depth, clearColor, polygonOffsetFactor, polygonOffsetUnit)
this.set(settings)
}
fun set(settings: RenderSettings) {
setBlendFunction(settings.sourceRGB, settings.destinationRGB, settings.sourceAlpha, settings.destinationAlpha)
this[RenderingCapabilities.DEPTH_TEST] = settings.depthTest
this[RenderingCapabilities.BLENDING] = settings.blending
this[RenderingCapabilities.FACE_CULLING] = settings.faceCulling
this[RenderingCapabilities.POLYGON_OFFSET] = settings.polygonOffset
this.depth = settings.depth
this.depthMask = settings.depthMask
this.clearColor = settings.clearColor
shader = null
polygonOffset(polygonOffsetFactor, polygonOffsetUnit)
polygonOffset(settings.polygonOffsetFactor, settings.polygonOffsetUnit)
}
fun enable(capability: RenderingCapabilities)

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 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.
*
@ -11,17 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.phases
package de.bixilon.minosoft.gui.rendering.system.base.layer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.settings.DefaultSettings
interface OtherDrawable : Renderer {
val skipOther: Boolean
get() = false
fun setupOther() {
renderSystem.reset()
}
fun drawOther()
object OpaqueLayer : RenderLayer {
override val settings get() = DefaultSettings.OPAQUE
override val priority: Int get() = 0
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 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.
*
@ -11,13 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.phases
package de.bixilon.minosoft.gui.rendering.system.base.layer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
interface CustomDrawable : Renderer {
val skipCustom: Boolean
get() = false
fun drawCustom()
interface RenderLayer {
val settings: RenderSettings
val priority: Int
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 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.
*
@ -11,17 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.phases
package de.bixilon.minosoft.gui.rendering.system.base.layer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.settings.DefaultSettings
interface OpaqueDrawable : Renderer {
val skipOpaque: Boolean
get() = false
fun setupOpaque() {
renderSystem.reset()
}
fun drawOpaque()
object TranslucentLayer : RenderLayer {
override val settings get() = DefaultSettings.TRANSLUCENT
override val priority: Int get() = 2000
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 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.
*
@ -11,17 +11,11 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.system.base.phases
package de.bixilon.minosoft.gui.rendering.system.base.layer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.settings.DefaultSettings
interface TransparentDrawable : Renderer {
val skipTransparent: Boolean
get() = false
fun setupTransparent() {
renderSystem.reset(blending = true)
}
fun drawTransparent()
object TransparentLayer : RenderLayer {
override val settings get() = DefaultSettings.TRANSPARENT
override val priority: Int get() = 1000
}

View File

@ -1,49 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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.system.base.phases
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
import kotlin.reflect.KClass
class RenderPhases<T : Renderer>(
val type: KClass<T>,
val skip: (T) -> Boolean,
val setup: (T) -> Unit,
val draw: (T) -> Unit,
) {
fun invokeSkip(renderer: Renderer): Boolean {
return skip.invoke(renderer.unsafeCast())
}
fun invokeSetup(renderer: Renderer) {
setup.invoke(renderer.unsafeCast())
}
fun invokeDraw(renderer: Renderer) {
draw.invoke(renderer.unsafeCast())
}
companion object {
val OTHER = RenderPhases(OtherDrawable::class, { it.skipOther }, { it.setupOther() }, { it.drawOther() })
val CUSTOM = RenderPhases(CustomDrawable::class, { it.skipCustom }, { }, { it.drawCustom() })
val OPAQUE = RenderPhases(OpaqueDrawable::class, { it.skipOpaque }, { it.setupOpaque() }, { it.drawOpaque() })
val TRANSPARENT = RenderPhases(TransparentDrawable::class, { it.skipTransparent }, { it.setupTransparent() }, { it.drawTransparent() })
val TRANSLUCENT = RenderPhases(TranslucentDrawable::class, { it.skipTranslucent }, { it.setupTranslucent() }, { it.drawTranslucent() })
val VALUES = arrayOf(TRANSPARENT, OTHER, CUSTOM, OPAQUE, TRANSLUCENT)
}
}

View File

@ -1,34 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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.system.base.phases
import de.bixilon.minosoft.gui.rendering.renderer.renderer.Renderer
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
interface TranslucentDrawable : Renderer {
val skipTranslucent: Boolean
get() = false
fun setupTranslucent() {
renderSystem.reset(
blending = true,
sourceRGB = BlendingFunctions.SOURCE_ALPHA,
destinationRGB = BlendingFunctions.ONE_MINUS_SOURCE_ALPHA,
sourceAlpha = BlendingFunctions.SOURCE_ALPHA,
destinationAlpha = BlendingFunctions.DESTINATION_ALPHA,
)
}
fun drawTranslucent()
}

View File

@ -0,0 +1,28 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.system.base.settings
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
object DefaultSettings {
val OPAQUE = RenderSettings()
val TRANSPARENT = RenderSettings(blending = true)
val TRANSLUCENT = RenderSettings(
blending = true,
sourceRGB = BlendingFunctions.SOURCE_ALPHA,
destinationRGB = BlendingFunctions.ONE_MINUS_SOURCE_ALPHA,
sourceAlpha = BlendingFunctions.SOURCE_ALPHA,
destinationAlpha = BlendingFunctions.DESTINATION_ALPHA,
)
}

View File

@ -0,0 +1,35 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.system.base.settings
import de.bixilon.minosoft.data.text.formatting.color.Colors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.gui.rendering.system.base.DepthFunctions
data class RenderSettings(
val depthTest: Boolean = true,
val blending: Boolean = false,
val faceCulling: Boolean = true,
val polygonOffset: Boolean = false,
val depthMask: Boolean = true,
val sourceRGB: BlendingFunctions = BlendingFunctions.ONE,
val destinationRGB: BlendingFunctions = BlendingFunctions.ONE_MINUS_SOURCE_ALPHA,
val sourceAlpha: BlendingFunctions = BlendingFunctions.ONE,
val destinationAlpha: BlendingFunctions = BlendingFunctions.ZERO,
val depth: DepthFunctions = DepthFunctions.LESS,
val clearColor: RGBColor = Colors.TRANSPARENT,
val polygonOffsetFactor: Float = 0.0f,
val polygonOffsetUnit: Float = 0.0f,
)