diff --git a/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt b/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt index cc4e8e6a9..abb1498ee 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/RGBColor.kt @@ -27,6 +27,12 @@ class RGBColor(val rgba: Int) : ChatCode, TextFormattable { constructor(red: Double, green: Double, blue: Double, alpha: Double = 1.0) : this(red.toFloat(), green.toFloat(), blue.toFloat(), alpha.toFloat()) + val argb: Int + get() = (alpha shl 24) or (red shl 16) or (green shl 8) or blue + + val abgr: Int + get() = (alpha shl 24) or (blue shl 16) or (green shl 8) or red + val alpha: @IntRange(from = 0L, to = 255L) Int get() = rgba and 0xFF diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt b/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt index 7cc15b194..1e8703357 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/main/MainErosController.kt @@ -29,6 +29,7 @@ import de.bixilon.minosoft.terminal.RunConfiguration import de.bixilon.minosoft.util.KUtil.toResourceLocation import de.bixilon.minosoft.util.ShutdownManager import de.bixilon.minosoft.util.task.pool.DefaultThreadPool +import javafx.application.Platform import javafx.fxml.FXML import javafx.scene.control.Label import javafx.scene.image.ImageView @@ -139,7 +140,7 @@ class MainErosController : JavaFXWindowController() { try { account.verify() } catch (exception: Throwable) { - activity = ErosMainActivities.ACCOUNT + Platform.runLater { activity = ErosMainActivities.ACCOUNT } // ToDo: Show account window and do account error handling } onSuccess(account) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt index d6f940648..e1f5c3ea7 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/RenderWindow.kt @@ -39,6 +39,7 @@ import de.bixilon.minosoft.gui.rendering.stats.AbstractRenderStats import de.bixilon.minosoft.gui.rendering.system.base.IntegratedBufferTypes import de.bixilon.minosoft.gui.rendering.system.base.PolygonModes import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem +import de.bixilon.minosoft.gui.rendering.system.base.phases.RenderPhases import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem import de.bixilon.minosoft.gui.rendering.system.window.BaseWindow import de.bixilon.minosoft.gui.rendering.system.window.GLFWWindow @@ -309,13 +310,17 @@ class RenderWindow( val rendererList = rendererMap.values for (renderer in rendererList) { - renderer.update() + renderer.prepareDraw() } + for (renderer in rendererList) { - renderer.draw() - } - for (renderer in rendererList) { - renderer.postDraw() + for (phase in RenderPhases.VALUES) { + if (!phase.type.java.isAssignableFrom(renderer::class.java)) { + continue + } + phase.invokeSetup(renderer) + phase.invokeDraw(renderer) + } } renderSystem.reset() // Reset to enable depth mask, etc again diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt index c215c75f6..c0ef8d7a3 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/Renderer.kt @@ -13,15 +13,15 @@ package de.bixilon.minosoft.gui.rendering +import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem + interface Renderer { + val renderWindow: RenderWindow + val renderSystem: RenderSystem fun init() = Unit fun postInit() = Unit - fun update() = Unit - - fun draw() = Unit - - fun postDraw() = Unit + fun prepareDraw() = Unit } 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 967b22c3b..78dc597f2 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 @@ -35,6 +35,10 @@ import de.bixilon.minosoft.gui.rendering.block.renderable.BlockLikeRenderContext import de.bixilon.minosoft.gui.rendering.input.camera.Frustum import de.bixilon.minosoft.gui.rendering.modding.events.FrustumChangeEvent import de.bixilon.minosoft.gui.rendering.modding.events.RenderingStateChangeEvent +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.phases.TranslucentDrawable +import de.bixilon.minosoft.gui.rendering.system.base.phases.TransparentDrawable import de.bixilon.minosoft.gui.rendering.system.base.shader.Shader import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager import de.bixilon.minosoft.gui.rendering.util.VecUtil.chunkPosition @@ -59,12 +63,14 @@ import glm_.vec3.Vec3i class WorldRenderer( private val connection: PlayConnection, - val renderWindow: RenderWindow, -) : Renderer { + override val renderWindow: RenderWindow, +) : Renderer, OpaqueDrawable, TransparentDrawable, TranslucentDrawable { + override val renderSystem: RenderSystem = renderWindow.renderSystem private val world: World = connection.world private val waterBlock = connection.registries.blockRegistry[ResourceLocation("minecraft:water")].nullCast() - private val chunkShader: Shader = renderWindow.renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "world")) + private val chunkShader: Shader = renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "world")) + private val transparentShader: Shader = renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "world")) private val lightMap = LightMap(connection) val allChunkSections: SynchronizedMap> = synchronizedMapOf() @@ -121,8 +127,12 @@ class WorldRenderer( } } - if (meshCollection.transparentSectionArrayMesh!!.data.isEmpty) { - meshCollection.transparentSectionArrayMesh = null + if (meshCollection.translucentMesh!!.data.isEmpty) { + meshCollection.translucentMesh = null + } + + if (meshCollection.transparentMesh!!.data.isEmpty) { + meshCollection.transparentMesh = null } return meshCollection } @@ -187,13 +197,18 @@ class WorldRenderer( } override fun postInit() { - chunkShader.load() lightMap.init() - + chunkShader.load() renderWindow.textureManager.staticTextures.use(chunkShader) renderWindow.textureManager.staticTextures.animator.use(chunkShader) lightMap.use(chunkShader) + transparentShader.defines[Shader.TRANSPARENT_DEFINE] = "" + transparentShader.load() + renderWindow.textureManager.staticTextures.use(transparentShader) + renderWindow.textureManager.staticTextures.animator.use(transparentShader) + lightMap.use(transparentShader) + for (blockState in allBlocks!!) { for (model in blockState.renderers) { model.postInit() @@ -202,29 +217,46 @@ class WorldRenderer( allBlocks = null } - override fun update() { + override fun prepareDraw() { lastVisibleChunks = visibleChunks.toSynchronizedMap() lightMap.update() } - override fun draw() { - renderWindow.renderSystem.reset() + override fun setupOpaque() { + super.setupOpaque() chunkShader.use() + } + override fun drawOpaque() { for (map in lastVisibleChunks.values) { for (mesh in map.values) { - mesh.opaqueSectionArrayMesh.draw() + mesh.opaqueMesh.draw() } } } - override fun postDraw() { - renderWindow.renderSystem.reset(depthMask = false) - chunkShader.use() + override fun setupTransparent() { + super.setupTransparent() + transparentShader.use() + } + override fun drawTransparent() { for (map in lastVisibleChunks.values) { for (mesh in map.values) { - mesh.transparentSectionArrayMesh?.draw() + mesh.transparentMesh?.draw() + } + } + } + + override fun setupTranslucent() { + super.setupTranslucent() + chunkShader.use() + } + + override fun drawTranslucent() { + for (map in lastVisibleChunks.values) { + for (mesh in map.values) { + mesh.translucentMesh?.draw() } } } @@ -337,23 +369,33 @@ class WorldRenderer( val sectionMap = allChunkSections.getOrPut(chunkPosition) { synchronizedMapOf() } sectionMap[index]?.let { - it.opaqueSectionArrayMesh.unload() + it.opaqueMesh.unload() meshes-- - triangles -= it.opaqueSectionArrayMesh.vertices + triangles -= it.opaqueMesh.vertices - it.transparentSectionArrayMesh?.let { + it.translucentMesh?.let { + it.unload() + meshes-- + triangles -= it.vertices + } + it.transparentMesh?.let { it.unload() meshes-- triangles -= it.vertices } } - meshCollection.opaqueSectionArrayMesh.let { + meshCollection.opaqueMesh.let { it.load() meshes++ triangles += it.vertices } - meshCollection.transparentSectionArrayMesh?.let { + meshCollection.translucentMesh?.let { + it.load() + meshes++ + triangles += it.vertices + } + meshCollection.transparentMesh?.let { it.load() meshes++ triangles += it.vertices @@ -418,12 +460,17 @@ class WorldRenderer( private fun unloadMeshes(meshes: Collection) { renderWindow.assertOnRenderThread() for (meshCollection in meshes) { - meshCollection.opaqueSectionArrayMesh.let { + meshCollection.opaqueMesh.let { it.unload() this.meshes-- triangles -= it.vertices } - meshCollection.transparentSectionArrayMesh?.let { + meshCollection.translucentMesh?.let { + it.unload() + this.meshes-- + triangles -= it.vertices + } + meshCollection.transparentMesh?.let { it.unload() this.meshes-- triangles -= it.vertices diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/chunk/ChunkBorderRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/chunk/ChunkBorderRenderer.kt index 8dc9fc2ac..3bafb714f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/chunk/ChunkBorderRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/chunk/ChunkBorderRenderer.kt @@ -19,6 +19,8 @@ import de.bixilon.minosoft.gui.rendering.RenderConstants 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.system.base.RenderSystem +import de.bixilon.minosoft.gui.rendering.system.base.phases.OpaqueDrawable import de.bixilon.minosoft.gui.rendering.util.mesh.LineMesh import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -27,13 +29,14 @@ import glm_.vec3.Vec3 class ChunkBorderRenderer( val connection: PlayConnection, - val renderWindow: RenderWindow, -) : Renderer { + override val renderWindow: RenderWindow, +) : Renderer, OpaqueDrawable { + override val renderSystem: RenderSystem = renderWindow.renderSystem private var lastChunkPosition: Vec2i? = null private var lastMesh: LineMesh? = null - private fun prepare() { + override fun prepareDraw() { val chunkPosition = renderWindow.connection.player.positionInfo.chunkPosition if (chunkPosition == lastChunkPosition && lastMesh != null) { return @@ -109,11 +112,13 @@ class ChunkBorderRenderer( this.lastChunkPosition = chunkPosition } - override fun draw() { - prepare() - val mesh = lastMesh ?: return + override fun setupOpaque() { renderWindow.renderSystem.reset(faceCulling = false) renderWindow.shaderManager.genericColorShader.use() + } + + override fun drawOpaque() { + val mesh = lastMesh ?: return mesh.draw() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshCollection.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshCollection.kt index a02481b94..69e5ab11c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshCollection.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/mesh/ChunkSectionMeshCollection.kt @@ -17,8 +17,9 @@ import de.bixilon.minosoft.gui.rendering.RenderWindow class ChunkSectionMeshCollection( renderWindow: RenderWindow, - val opaqueSectionArrayMesh: ChunkSectionArrayMesh = ChunkSectionArrayMesh(renderWindow), - var transparentSectionArrayMesh: ChunkSectionArrayMesh? = ChunkSectionArrayMesh(renderWindow), + val opaqueMesh: ChunkSectionArrayMesh = ChunkSectionArrayMesh(renderWindow), + var translucentMesh: ChunkSectionArrayMesh? = ChunkSectionArrayMesh(renderWindow), + var transparentMesh: ChunkSectionArrayMesh? = ChunkSectionArrayMesh(renderWindow), ) { var lowestBlockHeight = 0 var highestBlockHeight = 0 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/outline/BlockOutlineRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/outline/BlockOutlineRenderer.kt index 9745e1048..a38f281b2 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/outline/BlockOutlineRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/outline/BlockOutlineRenderer.kt @@ -23,6 +23,7 @@ import de.bixilon.minosoft.gui.rendering.Renderer import de.bixilon.minosoft.gui.rendering.RendererBuilder import de.bixilon.minosoft.gui.rendering.input.camera.hit.BlockRaycastHit import de.bixilon.minosoft.gui.rendering.system.base.DepthFunctions +import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem 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 @@ -32,13 +33,15 @@ import glm_.vec3.Vec3i class BlockOutlineRenderer( val connection: PlayConnection, - val renderWindow: RenderWindow, + override val renderWindow: RenderWindow, ) : Renderer { + override val renderSystem: RenderSystem = renderWindow.renderSystem private var currentOutlinePosition: Vec3i? = null private var currentOutlineBlockState: BlockState? = null private var currentMesh: LineMesh? = null + private fun drawMesh() { val currentMesh = currentMesh ?: return renderWindow.renderSystem.reset(faceCulling = false) @@ -47,6 +50,7 @@ class BlockOutlineRenderer( } renderWindow.shaderManager.genericColorShader.use() currentMesh.draw() + draw() } private fun unload() { @@ -57,7 +61,8 @@ class BlockOutlineRenderer( this.currentOutlineBlockState = null } - override fun draw() { + @Deprecated("TODO") + private fun draw() { val raycastHit = renderWindow.inputHandler.camera.target.nullCast() var currentMesh = currentMesh diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/block/renderable/block/ElementRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/block/renderable/block/ElementRenderer.kt index a243d4392..dce59a574 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/block/renderable/block/ElementRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/block/renderable/block/ElementRenderer.kt @@ -168,11 +168,11 @@ class ElementRenderer( val DRAW_OFFSET = Vec3(+0.5f, +0.5f, +0.5f) - fun getMesh(meshCollection: ChunkSectionMeshCollection, textureTransparencies: TextureTransparencies): ChunkSectionArrayMesh { - return if (textureTransparencies == TextureTransparencies.TRANSLUCENT) { - meshCollection.transparentSectionArrayMesh!! - } else { - meshCollection.opaqueSectionArrayMesh + fun getMesh(meshCollection: ChunkSectionMeshCollection, transparency: TextureTransparencies): ChunkSectionArrayMesh { + return when (transparency) { + TextureTransparencies.OPAQUE -> meshCollection.opaqueMesh + TextureTransparencies.TRANSPARENT -> meshCollection.transparentMesh!! + TextureTransparencies.TRANSLUCENT -> meshCollection.translucentMesh!! } } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entity/EntityHitBoxRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entity/EntityHitBoxRenderer.kt index 5423bdb43..400d96578 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entity/EntityHitBoxRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entity/EntityHitBoxRenderer.kt @@ -22,6 +22,8 @@ import de.bixilon.minosoft.gui.rendering.Renderer import de.bixilon.minosoft.gui.rendering.RendererBuilder import de.bixilon.minosoft.gui.rendering.modding.events.FrustumChangeEvent 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.OpaqueDrawable import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh import de.bixilon.minosoft.modding.event.events.EntityDestroyEvent import de.bixilon.minosoft.modding.event.events.EntitySpawnEvent @@ -33,8 +35,9 @@ import de.bixilon.minosoft.util.collections.SynchronizedMap class EntityHitBoxRenderer( val connection: PlayConnection, - val renderWindow: RenderWindow, -) : Renderer { + override val renderWindow: RenderWindow, +) : Renderer, OpaqueDrawable { + override val renderSystem: RenderSystem = renderWindow.renderSystem private val meshes: SynchronizedMap = synchronizedMapOf() @@ -102,13 +105,15 @@ class EntityHitBoxRenderer( } } - override fun draw() { - renderWindow.renderSystem.reset(faceCulling = false) + override fun setupOpaque() { + renderWindow.renderSystem.reset(faceCulling = false) // ToDo? if (Minosoft.config.config.game.entities.hitBox.disableZBuffer) { renderWindow.renderSystem.depth = DepthFunctions.ALWAYS } renderWindow.shaderManager.genericColorShader.use() + } + override fun drawOpaque() { fun draw(mesh: EntityHitBoxMesh?) { mesh ?: return diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt index 1cf99ddd6..ae7fd2564 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt @@ -39,6 +39,8 @@ import de.bixilon.minosoft.gui.rendering.gui.hud.elements.title.TitleHUDElement import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent import de.bixilon.minosoft.gui.rendering.system.base.IntegratedBufferTypes +import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem +import de.bixilon.minosoft.gui.rendering.system.base.phases.OtherDrawable import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -53,8 +55,9 @@ import glm_.vec2.Vec2i class HUDRenderer( val connection: PlayConnection, - val renderWindow: RenderWindow, -) : Renderer { + override val renderWindow: RenderWindow, +) : Renderer, OtherDrawable { + override val renderSystem: RenderSystem = renderWindow.renderSystem val shader = renderWindow.renderSystem.createShader("minosoft:hud".toResourceLocation()) private lateinit var mesh: GUIMesh var scaledSize: Vec2i = renderWindow.window.size @@ -138,7 +141,9 @@ class HUDRenderer( } } - override fun postDraw() { + override fun setupOther() = Unit + + override fun drawOther() { if (!enabled) { return } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt index e9c4754a9..c7aad869a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/ParticleRenderer.kt @@ -20,6 +20,9 @@ import de.bixilon.minosoft.gui.rendering.Renderer import de.bixilon.minosoft.gui.rendering.RendererBuilder import de.bixilon.minosoft.gui.rendering.modding.events.CameraMatrixChangeEvent import de.bixilon.minosoft.gui.rendering.particle.types.Particle +import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem +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.shader.Shader import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionStateChangeEvent import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker @@ -37,11 +40,15 @@ import glm_.vec3.Vec3 class ParticleRenderer( private val connection: PlayConnection, - val renderWindow: RenderWindow, -) : Renderer { - private val particleShader: Shader = renderWindow.renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "particle")) - private var particleMesh = ParticleMesh(renderWindow, 0) - private var transparentParticleMesh = ParticleMesh(renderWindow, 0) + override val renderWindow: RenderWindow, +) : Renderer, TransparentDrawable, TranslucentDrawable { + override val renderSystem: RenderSystem = renderWindow.renderSystem + private val transparentShader: Shader = renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "particle")) + private val translucentShader: Shader = renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "particle")) + + // There is no opaque mesh because it is simply not needed (every particle has transparency) + private var transparentMesh = ParticleMesh(renderWindow, 0) + private var translucentMesh = ParticleMesh(renderWindow, 0) private var particles: MutableSet = mutableSetOf() private var particleQueue: MutableSet = mutableSetOf() @@ -55,13 +62,22 @@ class ParticleRenderer( override fun init() { connection.registerEvent(CallbackEventInvoker.of { renderWindow.queue += { - particleShader.use().setMat4("uViewProjectionMatrix", Mat4(it.viewProjectionMatrix)) - particleShader.use().setVec3("uCameraRight", Vec3(it.viewMatrix[0][0], it.viewMatrix[1][0], it.viewMatrix[2][0])) - particleShader.use().setVec3("uCameraUp", Vec3(it.viewMatrix[0][1], it.viewMatrix[1][1], it.viewMatrix[2][1])) + fun applyToShader(shader: Shader) { + shader.apply { + use() + setMat4("uViewProjectionMatrix", Mat4(it.viewProjectionMatrix)) + setVec3("uCameraRight", Vec3(it.viewMatrix[0][0], it.viewMatrix[1][0], it.viewMatrix[2][0])) + setVec3("uCameraUp", Vec3(it.viewMatrix[0][1], it.viewMatrix[1][1], it.viewMatrix[2][1])) + } + } + + applyToShader(transparentShader) + applyToShader(translucentShader) } }) - particleMesh.load() - transparentParticleMesh.load() + + transparentMesh.load() + translucentMesh.load() connection.registries.particleTypeRegistry.forEachItem { for (resourceLocation in it.textures) { renderWindow.textureManager.staticTextures.createTexture(resourceLocation) @@ -72,9 +88,15 @@ class ParticleRenderer( } override fun postInit() { - particleShader.load() - renderWindow.textureManager.staticTextures.use(particleShader) - renderWindow.textureManager.staticTextures.animator.use(particleShader) + transparentShader.defines[Shader.TRANSPARENT_DEFINE] = "" + transparentShader.load() + renderWindow.textureManager.staticTextures.use(transparentShader) + renderWindow.textureManager.staticTextures.animator.use(transparentShader) + + translucentShader.load() + renderWindow.textureManager.staticTextures.use(translucentShader) + renderWindow.textureManager.staticTextures.animator.use(translucentShader) + connection.world.particleRenderer = this @@ -109,15 +131,16 @@ class ParticleRenderer( add(particle) } - override fun update() { - particleMesh.unload() - transparentParticleMesh.unload() + override fun prepareDraw() { + transparentMesh.unload() + translucentMesh.unload() val toRemove: MutableSet = mutableSetOf() - particleMesh = ParticleMesh(renderWindow, particles.size + particleQueue.size) - transparentParticleMesh = ParticleMesh(renderWindow, 500) + transparentMesh = ParticleMesh(renderWindow, 500) + translucentMesh = ParticleMesh(renderWindow, particles.size + particleQueue.size) + synchronized(particles) { synchronized(particleQueue) { @@ -131,27 +154,34 @@ class ParticleRenderer( toRemove += particle continue } - particle.addVertex(transparentParticleMesh, particleMesh, time) + particle.addVertex(transparentMesh, translucentMesh, time) } particles -= toRemove } - particleMesh.load() - transparentParticleMesh.load() + transparentMesh.load() + translucentMesh.load() } - override fun draw() { - renderWindow.renderSystem.reset() - particleShader.use() - particleMesh.draw() + override fun setupTransparent() { + super.setupTransparent() + transparentShader.use() } - override fun postDraw() { - renderWindow.renderSystem.reset(depthMask = false) - particleShader.use() - transparentParticleMesh.draw() + override fun drawTransparent() { + transparentMesh.draw() } + override fun setupTranslucent() { + super.setupTranslucent() + translucentShader.use() + } + + override fun drawTranslucent() { + translucentMesh.draw() + } + + companion object : RendererBuilder { override val RESOURCE_LOCATION = ResourceLocation("minosoft:particle") diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/Particle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/Particle.kt index ae0d0c2b4..b2ce34cb4 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/Particle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/Particle.kt @@ -190,7 +190,7 @@ abstract class Particle( } } - abstract fun addVertex(transparentMesh: ParticleMesh, particleMesh: ParticleMesh, time: Long) + abstract fun addVertex(transparentMesh: ParticleMesh, particleMesh: ParticleMesh, time:Long) companion object { private const val MAGIC_VELOCITY_CONSTANT = 0.4 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt index 54502e936..d11c449d1 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt @@ -25,13 +25,11 @@ abstract class TextureParticle(connection: PlayConnection, position: Vec3d, velo abstract val texture: AbstractTexture? - override fun addVertex(transparentMesh: ParticleMesh, particleMesh: ParticleMesh, time: Long) { - texture?.let { - if (it.transparency == TextureTransparencies.TRANSLUCENT || color.alpha != 255) { - transparentMesh - } else { - particleMesh - }.addVertex(getCameraPosition(time), scale, it, color) - } + override fun addVertex(transparentMesh: ParticleMesh, translucentMesh: ParticleMesh, time:Long) { + val texture = texture ?: return + when { + texture.transparency == TextureTransparencies.TRANSLUCENT || color.alpha != 255 -> translucentMesh + else -> transparentMesh + }.addVertex(getCameraPosition(time), scale, texture, color) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt index 118f35468..9a29c22ce 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt @@ -25,13 +25,11 @@ abstract class AdvancedTextureParticle(connection: PlayConnection, position: Vec var minUV: Vec2 = Vec2(0.0f, 0.0f) var maxUV: Vec2 = Vec2(1.0f, 1.0f) - override fun addVertex(transparentMesh: ParticleMesh, particleMesh: ParticleMesh, time: Long) { - texture?.let { - if (it.transparency == TextureTransparencies.TRANSLUCENT || color.alpha != 255) { - transparentMesh - } else { - particleMesh - }.addVertex(getCameraPosition(time), scale, it, color, minUV, maxUV) - } + override fun addVertex(transparentMesh: ParticleMesh, translucentMesh: ParticleMesh, time:Long) { + val texture = texture ?: return + when { + texture.transparency == TextureTransparencies.TRANSLUCENT || color.alpha != 255 -> translucentMesh + else -> transparentMesh + }.addVertex(getCameraPosition(time), scale, texture, color, minUV, maxUV) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/SkyRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/SkyRenderer.kt index 762c3fc56..1929afd87 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sky/SkyRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sky/SkyRenderer.kt @@ -24,6 +24,8 @@ import de.bixilon.minosoft.gui.rendering.RendererBuilder import de.bixilon.minosoft.gui.rendering.modding.events.CameraMatrixChangeEvent import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions 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.CustomDrawable import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh import de.bixilon.minosoft.gui.rendering.util.mesh.SimpleTextureMesh @@ -39,10 +41,11 @@ import glm_.vec3.Vec3d class SkyRenderer( private val connection: PlayConnection, - val renderWindow: RenderWindow, -) : Renderer { - private val skyboxShader = renderWindow.renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "sky/skybox")) - private val skySunShader = renderWindow.renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "sky/sun")) + override val renderWindow: RenderWindow, +) : Renderer, CustomDrawable { + override val renderSystem: RenderSystem = renderWindow.renderSystem + private val skyboxShader = renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "sky/skybox")) + private val skySunShader = renderSystem.createShader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "sky/sun")) private val skyboxMesh = SkyboxMesh(renderWindow) private var skySunMesh = SimpleTextureMesh(renderWindow) private lateinit var sunTexture: AbstractTexture @@ -140,7 +143,7 @@ class SkyRenderer( skyboxMesh.draw() } - override fun draw() { + override fun drawCustom() { renderWindow.renderSystem.reset(depth = DepthFunctions.LESS_OR_EQUAL) drawSkybox() drawSun() diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/CustomDrawable.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/CustomDrawable.kt new file mode 100644 index 000000000..14fa1100d --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/CustomDrawable.kt @@ -0,0 +1,8 @@ +package de.bixilon.minosoft.gui.rendering.system.base.phases + +import de.bixilon.minosoft.gui.rendering.Renderer + +interface CustomDrawable : Renderer { + + fun drawCustom() +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/OpaqueDrawable.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/OpaqueDrawable.kt new file mode 100644 index 000000000..1d287eaf2 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/OpaqueDrawable.kt @@ -0,0 +1,12 @@ +package de.bixilon.minosoft.gui.rendering.system.base.phases + +import de.bixilon.minosoft.gui.rendering.Renderer + +interface OpaqueDrawable : Renderer { + + fun setupOpaque() { + renderSystem.reset(blending = false) + } + + fun drawOpaque() +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/OtherDrawable.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/OtherDrawable.kt new file mode 100644 index 000000000..8e274f64b --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/OtherDrawable.kt @@ -0,0 +1,12 @@ +package de.bixilon.minosoft.gui.rendering.system.base.phases + +import de.bixilon.minosoft.gui.rendering.Renderer + +interface OtherDrawable : Renderer { + + fun setupOther() { + renderSystem.reset() + } + + fun drawOther() +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/RenderPhases.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/RenderPhases.kt new file mode 100644 index 000000000..4ebb99b77 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/RenderPhases.kt @@ -0,0 +1,44 @@ +/* + * 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.system.base.phases + +import de.bixilon.minosoft.gui.rendering.Renderer +import de.bixilon.minosoft.util.KUtil.unsafeCast +import kotlin.reflect.KClass + +class RenderPhases( + val type: KClass, + val setup: (T) -> Unit, + val draw: (T) -> Unit, +) { + + fun invokeSetup(renderer: Renderer) { + setup.invoke(renderer.unsafeCast()) + } + + fun invokeDraw(renderer: Renderer) { + draw.invoke(renderer.unsafeCast()) + } + + companion object { + val OTHER = RenderPhases(OtherDrawable::class, { it.setupOther() }, { it.drawOther() }) + val CUSTOM = RenderPhases(CustomDrawable::class, { }, { it.drawCustom() }) + val OPAQUE = RenderPhases(OpaqueDrawable::class, { it.setupOpaque() }, { it.drawOpaque() }) + val TRANSPARENT = RenderPhases(TransparentDrawable::class, { it.setupTransparent() }, { it.drawTransparent() }) + val TRANSLUCENT = RenderPhases(TranslucentDrawable::class, { it.setupTranslucent() }, { it.drawTranslucent() }) + + + val VALUES = arrayOf(OTHER, CUSTOM, OPAQUE, TRANSPARENT, TRANSLUCENT) + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/TranslucentDrawable.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/TranslucentDrawable.kt new file mode 100644 index 000000000..d87dcf7cf --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/TranslucentDrawable.kt @@ -0,0 +1,14 @@ +package de.bixilon.minosoft.gui.rendering.system.base.phases + +import de.bixilon.minosoft.gui.rendering.Renderer +import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions + +interface TranslucentDrawable : Renderer { + + fun setupTranslucent() { + renderSystem.reset(depthMask = false) // ToDo: This is just a translucent workaround + renderSystem.setBlendFunc(BlendingFunctions.SOURCE_ALPHA, BlendingFunctions.ONE_MINUS_SOURCE_ALPHA, BlendingFunctions.ONE, BlendingFunctions.ONE_MINUS_SOURCE_ALPHA) + } + + fun drawTranslucent() +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/TransparentDrawable.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/TransparentDrawable.kt new file mode 100644 index 000000000..dfc4c8df4 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/phases/TransparentDrawable.kt @@ -0,0 +1,12 @@ +package de.bixilon.minosoft.gui.rendering.system.base.phases + +import de.bixilon.minosoft.gui.rendering.Renderer + +interface TransparentDrawable : Renderer { + + fun setupTransparent() { + renderSystem.reset(blending = false) + } + + fun drawTransparent() +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/shader/Shader.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/shader/Shader.kt index e37cece2b..afed581b2 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/shader/Shader.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/shader/Shader.kt @@ -30,6 +30,7 @@ interface Shader { val renderWindow: RenderWindow val resourceLocation: ResourceLocation val uniforms: List + val defines: MutableMap val log: String @@ -74,6 +75,7 @@ interface Shader { } companion object { + const val TRANSPARENT_DEFINE = "TRANSPARENT" val DEFAULT_DEFINES: Map Any?> = mapOf( "ANIMATED_TEXTURE_COUNT" to { max(it.textureManager.staticTextures.animator.size, 1) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/SpriteAnimator.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/SpriteAnimator.kt index c4835ace3..999b41109 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/SpriteAnimator.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/SpriteAnimator.kt @@ -23,5 +23,5 @@ interface SpriteAnimator { fun draw() - fun use(shader: Shader, bufferName: String = "uAnimationBuffer") + fun use(shader: Shader, bufferName: String = "uSpriteBuffer") } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/AbstractTexture.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/AbstractTexture.kt index 1209dd9c0..6dc6a551b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/AbstractTexture.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/AbstractTexture.kt @@ -15,11 +15,11 @@ package de.bixilon.minosoft.gui.rendering.system.base.texture.texture import de.bixilon.minosoft.data.assets.AssetsManager import de.bixilon.minosoft.data.registries.ResourceLocation -import de.bixilon.minosoft.data.text.RGBColor import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureArray import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties +import example.jonathan2520.SRGBAverager import glm_.vec2.Vec2 import glm_.vec2.Vec2i import org.lwjgl.BufferUtils @@ -42,119 +42,54 @@ interface AbstractTexture { fun load(assetsManager: AssetsManager) - fun generateMipMaps(): Array> { - val ret: MutableList> = mutableListOf() - var lastBuffer = data!! - var lastSize = size - for (i in 0 until OpenGLTextureArray.MAX_MIPMAP_LEVELS) { - val size = Vec2i(size.x shr i, size.y shr i) - if (i != 0 && size.x != 0 && size.y != 0) { - lastBuffer = generateMipmap(lastBuffer, lastSize, size) - lastSize = size + fun generateMipMaps(): Array { + val images: MutableList = mutableListOf() + + var data = data!! + + images += data + + for (i in 1 until OpenGLTextureArray.MAX_MIPMAP_LEVELS) { + val mipMapSize = Vec2i(size.x shr i, size.y shr i) + if (mipMapSize.x <= 0 || mipMapSize.y <= 0) { + break } - ret += Pair(size, lastBuffer) + data = generateMipmap(data, Vec2i(size.x shr (i - 1), size.y shr (i - 1))) + images += data } - return ret.toTypedArray() + return images.toTypedArray() } + private fun generateMipmap(origin: ByteBuffer, oldSize: Vec2i): ByteBuffer { + // No Vec2i: performance reasons + val oldSizeX = oldSize.x + val newSizeX = oldSizeX shr 1 - private fun ByteBuffer.getRGB(start: Int): RGBColor { - return RGBColor(get(start), get(start + 1), get(start + 2), get(start + 3)) - } - - private fun ByteBuffer.setRGB(start: Int, color: RGBColor) { - put(start, color.red.toByte()) - put(start + 1, color.green.toByte()) - put(start + 2, color.blue.toByte()) - put(start + 3, color.alpha.toByte()) - } - - @Deprecated(message = "This is garbage, will be improved soon...") - private fun generateMipmap(biggerBuffer: ByteBuffer, oldSize: Vec2i, newSize: Vec2i): ByteBuffer { - val sizeFactor = oldSize / newSize - val buffer = BufferUtils.createByteBuffer(biggerBuffer.capacity() shr 1) + val buffer = BufferUtils.createByteBuffer(origin.capacity() shr 1) buffer.limit(buffer.capacity()) - fun getRGB(x: Int, y: Int): RGBColor { - return biggerBuffer.getRGB((y * oldSize.x + x) * 4) + fun getRGB(x: Int, y: Int): Int { + return origin.getInt((y * oldSizeX + x) * 4) } - fun setRGB(x: Int, y: Int, color: RGBColor) { - buffer.setRGB((y * newSize.x + x) * 4, color) + fun setRGB(x: Int, y: Int, color: Int) { + buffer.putInt((y * newSizeX + x) * 4, color) } - for (y in 0 until newSize.y) { - for (x in 0 until newSize.x) { + for (y in 0 until (oldSize.y shr 1)) { + for (x in 0 until newSizeX) { + val xOffset = x * 2 + val yOffset = y * 2 - // check what is the most used transparency - val transparencyPixelCount = IntArray(TextureTransparencies.VALUES.size) - for (mixY in 0 until sizeFactor.y) { - for (mixX in 0 until sizeFactor.x) { - val color = getRGB(x * sizeFactor.x + mixX, y * sizeFactor.y + mixY) - when (color.alpha) { - 255 -> transparencyPixelCount[TextureTransparencies.OPAQUE.ordinal]++ - 0 -> transparencyPixelCount[TextureTransparencies.TRANSPARENT.ordinal]++ - else -> transparencyPixelCount[TextureTransparencies.TRANSLUCENT.ordinal]++ - } - } - } - var largest = 0 - for (count in transparencyPixelCount) { - if (count > largest) { - largest = count - } - } - var transparency: TextureTransparencies = TextureTransparencies.OPAQUE - for ((index, count) in transparencyPixelCount.withIndex()) { - if (count >= largest) { - transparency = TextureTransparencies[index] - break - } - } + val output = SRGBAverager.average( + getRGB(xOffset + 0, yOffset + 0), + getRGB(xOffset + 1, yOffset + 0), + getRGB(xOffset + 0, yOffset + 1), + getRGB(xOffset + 1, yOffset + 1), + ) - var count = 0 - var red = 0 - var green = 0 - var blue = 0 - var alpha = 0 - - // make magic for the most used transparency - for (mixY in 0 until sizeFactor.y) { - for (mixX in 0 until sizeFactor.x) { - val color = getRGB(x * sizeFactor.x + mixX, y * sizeFactor.y + mixY) - when (transparency) { - TextureTransparencies.OPAQUE -> { - if (color.alpha != 0xFF) { - continue - } - red += color.red - green += color.green - blue += color.blue - alpha += color.alpha - count++ - } - TextureTransparencies.TRANSPARENT -> { - } - TextureTransparencies.TRANSLUCENT -> { - red += color.red - green += color.green - blue += color.blue - alpha += color.alpha - count++ - } - } - } - } - - - - - - if (count == 0) { - count++ - } - setRGB(x, y, RGBColor(red / count, green / count, blue / count, alpha / count)) + setRGB(x, y, output) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLShader.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLShader.kt index e36d82870..adbaa7a78 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLShader.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/OpenGLShader.kt @@ -40,7 +40,7 @@ class OpenGLShader( ) : Shader { override var loaded: Boolean = false private set - val defines: MutableMap = mutableMapOf() + override val defines: MutableMap = mutableMapOf() private var shader = -1 override var uniforms: MutableList = mutableListOf() private set diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt index 66dfcfdc9..73aca05e8 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt @@ -137,20 +137,21 @@ class OpenGLTextureArray( glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT) // glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST) - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST) + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, MAX_MIPMAP_LEVELS - 1) - for (i in 0 until MAX_MIPMAP_LEVELS) { - glTexImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA, resolution shr i, resolution shr i, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) + for (level in 0 until MAX_MIPMAP_LEVELS) { + glTexImage3D(GL_TEXTURE_2D_ARRAY, level, GL_RGBA, resolution shr level, resolution shr level, textures.size, 0, GL_RGBA, GL_UNSIGNED_BYTE, null as ByteBuffer?) } for (texture in textures) { val mipMaps = texture.generateMipMaps() val renderData = texture.renderData as OpenGLTextureData - for ((mipMapLevel, data) in mipMaps.withIndex()) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, mipMapLevel, 0, 0, renderData.index, data.first.x, data.first.y, mipMapLevel + 1, GL_RGBA, GL_UNSIGNED_BYTE, data.second) + for ((level, data) in mipMaps.withIndex()) { + val size = texture.size shr level + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, level, 0, 0, renderData.index, size.x, size.y, level + 1, GL_RGBA, GL_UNSIGNED_BYTE, data) } texture.data = null @@ -192,7 +193,7 @@ class OpenGLTextureArray( companion object { - val TEXTURE_RESOLUTION_ID_MAP = arrayOf(16, 32, 64, 128, 256, 512, 1024) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split + val TEXTURE_RESOLUTION_ID_MAP = intArrayOf(16, 32, 64, 128, 256, 512, 1024) // A 12x12 texture will be saved in texture id 0 (in 0 are only 16x16 textures). Animated textures get split const val TEXTURE_MAX_RESOLUTION = 1024 const val MAX_MIPMAP_LEVELS = 5 } diff --git a/src/main/java/de/bixilon/minosoft/util/Pair.kt b/src/main/java/de/bixilon/minosoft/util/Pair.kt index 60a85a970..8ddeeac6e 100644 --- a/src/main/java/de/bixilon/minosoft/util/Pair.kt +++ b/src/main/java/de/bixilon/minosoft/util/Pair.kt @@ -12,6 +12,7 @@ */ package de.bixilon.minosoft.util +@Deprecated("Java only") data class Pair( val key: K, val value: V, diff --git a/src/main/java/example/jonathan2520/SRGBAverager.java b/src/main/java/example/jonathan2520/SRGBAverager.java new file mode 100644 index 000000000..019e246e1 --- /dev/null +++ b/src/main/java/example/jonathan2520/SRGBAverager.java @@ -0,0 +1,74 @@ +// Source: https://bugs.mojang.com/browse/MC-114265 + +// Averaging of texels for mipmap generation. + +package example.jonathan2520; + +public class SRGBAverager { + private static final SRGBTable SRGB = new SRGBTable(); + + public static int average(int c0, int c1, int c2, int c3) { + if ((((c0 | c1 | c2 | c3) ^ (c0 & c1 & c2 & c3)) & 0xff000000) == 0) { + // Alpha values are all equal. Simplifies computation somewhat. It's + // also a reasonable fallback when all alpha values are zero, in + // which case the resulting color would normally be undefined. + // Defining it like this allows code that uses invisible colors for + // whatever reason to work. Note that Minecraft's original code + // would set the color to black; this is added functionality. + + float r = SRGB.decode(c0 & 0xff) + + SRGB.decode(c1 & 0xff) + + SRGB.decode(c2 & 0xff) + + SRGB.decode(c3 & 0xff); + + float g = SRGB.decode(c0 >> 8 & 0xff) + + SRGB.decode(c1 >> 8 & 0xff) + + SRGB.decode(c2 >> 8 & 0xff) + + SRGB.decode(c3 >> 8 & 0xff); + + float b = SRGB.decode(c0 >> 16 & 0xff) + + SRGB.decode(c1 >> 16 & 0xff) + + SRGB.decode(c2 >> 16 & 0xff) + + SRGB.decode(c3 >> 16 & 0xff); + + return SRGB.encode(0.25F * r) + | SRGB.encode(0.25F * g) << 8 + | SRGB.encode(0.25F * b) << 16 + | c0 & 0xff000000; + } else { + // The general case. Well-defined if at least one alpha value is + // not zero. If you do try to process all zeros, you get + // r = g = b = a = 0 which will NaN out in the division and produce + // invisible black. You could remove the other case if you're okay + // with that, but mind that producing or consuming a NaN causes an + // extremely slow exception handler to be run on many CPUs. + + float a0 = c0 >>> 24; + float a1 = c1 >>> 24; + float a2 = c2 >>> 24; + float a3 = c3 >>> 24; + + float r = a0 * SRGB.decode(c0 & 0xff) + + a1 * SRGB.decode(c1 & 0xff) + + a2 * SRGB.decode(c2 & 0xff) + + a3 * SRGB.decode(c3 & 0xff); + + float g = a0 * SRGB.decode(c0 >> 8 & 0xff) + + a1 * SRGB.decode(c1 >> 8 & 0xff) + + a2 * SRGB.decode(c2 >> 8 & 0xff) + + a3 * SRGB.decode(c3 >> 8 & 0xff); + + float b = a0 * SRGB.decode(c0 >> 16 & 0xff) + + a1 * SRGB.decode(c1 >> 16 & 0xff) + + a2 * SRGB.decode(c2 >> 16 & 0xff) + + a3 * SRGB.decode(c3 >> 16 & 0xff); + + float a = a0 + a1 + a2 + a3; + + return SRGB.encode(r / a) + | SRGB.encode(g / a) << 8 + | SRGB.encode(b / a) << 16 + | (int) (0.25F * a + 0.5F) << 24; + } + } +} diff --git a/src/main/java/example/jonathan2520/SRGBCalculator.java b/src/main/java/example/jonathan2520/SRGBCalculator.java new file mode 100644 index 000000000..a2d6a2e47 --- /dev/null +++ b/src/main/java/example/jonathan2520/SRGBCalculator.java @@ -0,0 +1,58 @@ +// Source: https://bugs.mojang.com/browse/MC-114265 + +// Offers very precise sRGB encoding and decoding. + +// The actual values defining sRGB are alpha = 0.055 and gamma = 2.4. + +// This class works directly from that definition to take advantage of all +// available precision, unlike pre-rounded constants like 12.92 that cause a +// comparatively humongous discontinuity at a point that should be of +// differentiability class C^1. + +// Stored values are chosen to speed up bulk conversion somewhat. + +package example.jonathan2520; + +public class SRGBCalculator { + private final double decode_threshold; + private final double decode_slope; + private final double decode_multiplier; + private final double decode_addend; + private final double decode_exponent; + private final double encode_threshold; + private final double encode_slope; + private final double encode_multiplier; + private final double encode_addend; + private final double encode_exponent; + + public SRGBCalculator(double gamma, double alpha) { + encode_multiplier = alpha + 1.0; + decode_multiplier = 1.0 / encode_multiplier; + encode_addend = -alpha; + decode_addend = decode_multiplier * alpha; + encode_exponent = 1.0 / gamma; + decode_exponent = gamma; + decode_threshold = alpha / (gamma - 1.0); + encode_threshold = Math.pow(gamma * decode_threshold * decode_multiplier, gamma); + encode_slope = decode_threshold / encode_threshold; + decode_slope = encode_threshold / decode_threshold; + } + + public SRGBCalculator() { + this(2.4, 0.055); + } + + public double decode(double x) { + if (x < decode_threshold) + return decode_slope * x; + else + return Math.pow(x * decode_multiplier + decode_addend, decode_exponent); + } + + public double encode(double x) { + if (x < encode_threshold) + return encode_slope * x; + else + return Math.pow(x, encode_exponent) * encode_multiplier + encode_addend; + } +} diff --git a/src/main/java/example/jonathan2520/SRGBTable.java b/src/main/java/example/jonathan2520/SRGBTable.java new file mode 100644 index 000000000..cad1fbbff --- /dev/null +++ b/src/main/java/example/jonathan2520/SRGBTable.java @@ -0,0 +1,73 @@ +// Source: https://bugs.mojang.com/browse/MC-114265 + +// Offers fast sRGB encoding and decoding. + +// Decoding is a straightforward table look-up. + +// Encoding is a little more sophisticated. It's an exact conversion using about +// 4 kB of look-up tables that's also still quick. It relies on the fact that +// thresholds that would round to the next value have a minimum spacing of about +// 0.0003. That means that any range of 0.0003 contains at most one threshold. +// The to_int table contains the smaller value in the range. The threshold table +// contains the threshold above which the value should be one greater. + +// The minimum scale value that maintains proper spacing is 255 * encode_slope +// or about 3295.4. You can get away with a little bit less like 3200, taking +// advantage of the alignment of thresholds, but it's not really worth it. + +package example.jonathan2520; + +public class SRGBTable { + private final float scale; + private final float[] to_float; + private final float[] threshold; + private final byte[] to_int; + + public SRGBTable() { + this(new SRGBCalculator(), 3295.5F); + } + + public SRGBTable(SRGBCalculator calculator, float scale) { + this.scale = scale; + to_float = new float[256]; + threshold = new float[256]; + to_int = new byte[(int) scale + 1]; + for (int i = 0; i < 255; ++i) { + to_float[i] = (float) calculator.decode(i / 255.0); + double dthresh = calculator.decode((i + 0.5) / 255.0); + float fthresh = (float) dthresh; + if (fthresh >= dthresh) + fthresh = Math.nextAfter(fthresh, -1); + threshold[i] = fthresh; + } + to_float[255] = 1; + threshold[255] = Float.POSITIVE_INFINITY; + int offset = 0; + for (int i = 0; i < 255; ++i) { + int up_to = (int) (threshold[i] * scale); + build_to_int_table(offset, up_to, (byte) i); + offset = up_to + 1; + } + build_to_int_table(offset, (int) scale, (byte) 255); + } + + private void build_to_int_table(int offset, int up_to, byte value) { + if (offset > up_to) + throw new IllegalArgumentException("scale is too small"); + while (offset <= up_to) + to_int[offset++] = value; + } + + // x in [0, 255] + public float decode(int x) { + return to_float[x]; + } + + // x in about [-0.0003, 1.00015]: tolerates rounding error on top of [0, 1] + public int encode(float x) { + int index = to_int[(int) (x * scale)] & 0xff; + if (x > threshold[index]) + ++index; + return index; + } +} diff --git a/src/main/resources/assets/minosoft/rendering/shader/includes/postprocessing/fragment.glsl b/src/main/resources/assets/minosoft/rendering/shader/includes/postprocessing/fragment.glsl index c99daeaaa..af16f3d5e 100644 --- a/src/main/resources/assets/minosoft/rendering/shader/includes/postprocessing/fragment.glsl +++ b/src/main/resources/assets/minosoft/rendering/shader/includes/postprocessing/fragment.glsl @@ -41,7 +41,7 @@ void main() { float fogFactor = getFogFactor(distance(uCameraPosition, finVertexPosition)); if (fogFactor != 1.0f) { - outColor = vec4(mix(uFogColor.rgb, outColor.rgb, fogFactor), outColor.a); + foutColor = vec4(mix(uFogColor.rgb, foutColor.rgb, fogFactor), foutColor.a); }; #endif } diff --git a/src/main/resources/assets/minosoft/rendering/shader/particle/particle.fsh b/src/main/resources/assets/minosoft/rendering/shader/particle/particle.fsh index 012cc3909..1de936da9 100644 --- a/src/main/resources/assets/minosoft/rendering/shader/particle/particle.fsh +++ b/src/main/resources/assets/minosoft/rendering/shader/particle/particle.fsh @@ -33,14 +33,26 @@ void main() { if (finInterpolation == 0.0f) { foutColor = texelColor1 * finTintColor; + #ifdef TRANSPARENT + if (foutColor.a < 0.3f){ + discard; + } + #endif return; } - vec4 texelColor2 = getTexture(finTextureIndex2, finTextureCoordinates2); + vec4 texelColor2 = getTexture(finTextureIndex2, finTextureCoordinates2); if (texelColor2.a == 0.0f) { discard; } foutColor = mix(texelColor1, texelColor2, finInterpolation) * finTintColor; + + + #ifdef TRANSPARENT + if (foutColor.a < 0.3f){ + discard; + } + #endif } diff --git a/src/main/resources/assets/minosoft/rendering/shader/sky/sun/sky_sun.fsh b/src/main/resources/assets/minosoft/rendering/shader/sky/sun/sky_sun.fsh index ebda5c585..ba6a0267a 100644 --- a/src/main/resources/assets/minosoft/rendering/shader/sky/sun/sky_sun.fsh +++ b/src/main/resources/assets/minosoft/rendering/shader/sky/sun/sky_sun.fsh @@ -28,6 +28,6 @@ void main() { if (finTintColor.a == 1.0f && texelColor.a == 0) { discard; } - // outColor = vec4(0.0f, 1.0f, 1.0f, 1.0f); + // foutColor = vec4(0.0f, 1.0f, 1.0f, 1.0f); foutColor = texelColor * finTintColor; } diff --git a/src/main/resources/assets/minosoft/rendering/shader/world/world.fsh b/src/main/resources/assets/minosoft/rendering/shader/world/world.fsh index ee405e536..b38ff93f7 100644 --- a/src/main/resources/assets/minosoft/rendering/shader/world/world.fsh +++ b/src/main/resources/assets/minosoft/rendering/shader/world/world.fsh @@ -13,7 +13,7 @@ #version 330 core -out vec4 outColor; +out vec4 foutColor; flat in uint finTextureIndex1; in vec3 finTextureCoordinates1; @@ -34,17 +34,28 @@ void work() { } if (finInterpolation == 0.0f) { - outColor = firstTexelColor * finTintColor; + foutColor = firstTexelColor * finTintColor; + #ifdef TRANSPARENT + if (foutColor.a < 0.3f){ + discard; + } + #endif return; } - vec4 secondTexelColor = getTexture(finTextureIndex2, finTextureCoordinates2); + vec4 secondTexelColor = getTexture(finTextureIndex2, finTextureCoordinates2); if (secondTexelColor.a == 0.0f) { discard; } - outColor = mix(firstTexelColor, secondTexelColor, finInterpolation) * finTintColor; + foutColor = mix(firstTexelColor, secondTexelColor, finInterpolation) * finTintColor; + + #ifdef TRANSPARENT + if (foutColor.a < 0.3f) { + discard; + } + #endif } -#include "minosoft:postprocessing/fragment" + #include "minosoft:postprocessing/fragment"