improve item rendering

This commit is contained in:
Moritz Zwerger 2023-11-14 20:46:19 +01:00
parent 442a06a6c9
commit b88680e75d
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
15 changed files with 191 additions and 28 deletions

View File

@ -44,7 +44,7 @@ Minosoft is an open source minecraft client, written from scratch in kotlin (and
### Features
- Blocks
- Entities (hitboxes and players for now)
- Entities
- Block entities (e.g. signs, chests)
- HUD and GUI (inventory, menus, ...)
- Particles

View File

@ -23,6 +23,7 @@ import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMeshCache
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.models.raw.display.ModelDisplay
import de.bixilon.minosoft.gui.rendering.models.util.CuboidUtil
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshOrder
@ -36,7 +37,7 @@ class BlockGUIConsumerTest {
val gui = GUIRenderer::class.java.allocate()
val consumer = GUIConsumer()
return BlockGUIConsumer(gui, Vec2(11, 12), consumer, null, Vec2(45))
return BlockGUIConsumer(gui, Vec2(11, 12), consumer, null, ModelDisplay.DEFAULT, Vec2(45))
}
private fun BlockGUIConsumer.assertVertices(vararg expected: Vec2) {

View File

@ -16,7 +16,6 @@ package de.bixilon.minosoft.gui.rendering
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.data.text.formatting.color.RGBColor.Companion.asColor
import de.bixilon.minosoft.util.KUtil
object RenderConstants {
val EXPERIENCE_BAR_LEVEL_COLOR = "#80ff20".asColor()
@ -47,7 +46,7 @@ object RenderConstants {
const val UV_ADD = 0.001f
const val DISABLE_GUI_CACHE = false
const val DISABLE_GUI_CACHE = true
const val OPENGL_DEBUG_MODE = false
const val DIRTY_BUFFER_UNBIND = true

View File

@ -310,7 +310,8 @@ class ChunkRenderer(
companion object : RendererBuilder<ChunkRenderer> {
override fun build(connection: PlayConnection, context: RenderContext): ChunkRenderer {
override fun build(connection: PlayConnection, context: RenderContext): ChunkRenderer? {
return null
return ChunkRenderer(connection, context)
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.entities.feature.item
import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.minosoft.data.container.stack.ItemStack
import de.bixilon.minosoft.gui.rendering.entities.feature.EntityRenderFeature
import de.bixilon.minosoft.gui.rendering.entities.feature.block.BlockMesh
import de.bixilon.minosoft.gui.rendering.entities.feature.block.BlockShader
import de.bixilon.minosoft.gui.rendering.entities.renderer.EntityRenderer
import de.bixilon.minosoft.gui.rendering.entities.visibility.EntityLayer
import de.bixilon.minosoft.gui.rendering.models.item.ItemRenderUtil.getModel
import de.bixilon.minosoft.gui.rendering.models.raw.display.DisplayPositions
import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.reset
import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateXAssign
import de.bixilon.minosoft.gui.rendering.util.mat.mat4.Mat4Util.translateZAssign
import de.bixilon.minosoft.gui.rendering.util.mesh.Mesh
open class ItemFeature(
renderer: EntityRenderer<*>,
stack: ItemStack?,
val display: DisplayPositions,
) : EntityRenderFeature(renderer) {
private var mesh: BlockMesh? = null
private var matrix = Mat4()
override var enabled: Boolean
get() = super.enabled && mesh != null
set(value) {
super.enabled = value
}
var stack: ItemStack? = stack
set(value) {
if (field == value) return
field = value
unload()
}
// TODO: observe stack
override val layer get() = EntityLayer.Translucent // TODO
override fun update(millis: Long, delta: Float) {
if (!super.enabled) return unload()
if (this.mesh == null) {
val stack = this.stack ?: return unload()
createMesh(stack)
}
updateMatrix()
}
private fun createMesh(stack: ItemStack) {
val model = stack.item.item.getModel(renderer.renderer.connection) ?: return
val mesh = BlockMesh(renderer.renderer.context)
val tint = renderer.renderer.context.tints.getItemTint(stack)
model.render(mesh, stack, tint)
this.mesh = mesh
}
private fun updateMatrix() {
this.matrix.reset()
this.matrix
.translateXAssign(-0.5f).translateZAssign(-0.5f)
// TODO: rotate?
this.matrix = renderer.matrix * matrix
}
override fun draw() {
val mesh = this.mesh ?: return
if (mesh.state != Mesh.MeshStates.LOADED) mesh.load()
renderer.renderer.context.system.reset(faceCulling = false)
val shader = renderer.renderer.features.block.shader
draw(mesh, shader)
}
protected open fun draw(mesh: BlockMesh, shader: BlockShader) {
shader.use()
shader.matrix = matrix
shader.tint = renderer.light.value
mesh.draw()
}
override fun unload() {
val mesh = this.mesh ?: return
this.mesh = null
renderer.renderer.queue += { mesh.unload() }
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.entities.renderer.item
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.minosoft.data.entities.entities.item.FallingBlockEntity
import de.bixilon.minosoft.data.registries.identified.Identified
import de.bixilon.minosoft.gui.rendering.entities.EntitiesRenderer
import de.bixilon.minosoft.gui.rendering.entities.factory.RegisteredEntityModelFactory
import de.bixilon.minosoft.gui.rendering.entities.feature.block.BlockFeature
import de.bixilon.minosoft.gui.rendering.entities.renderer.EntityRenderer
class ItemEntityRenderer(renderer: EntitiesRenderer, entity: FallingBlockEntity) : EntityRenderer<FallingBlockEntity>(renderer, entity) {
val block = BlockFeature(this, null).register()
init {
entity::blockState.observe(this, true) { block.state = it }
}
companion object : RegisteredEntityModelFactory<FallingBlockEntity>, Identified {
override val identifier get() = FallingBlockEntity.identifier
override fun create(renderer: EntitiesRenderer, entity: FallingBlockEntity) = ItemEntityRenderer(renderer, entity)
}
}

View File

@ -97,6 +97,7 @@ class GUIRenderer(
destinationRGB = BlendingFunctions.ONE_MINUS_SOURCE_ALPHA,
sourceAlpha = BlendingFunctions.ONE,
destinationAlpha = BlendingFunctions.ONE_MINUS_SOURCE_ALPHA,
faceCulling = false,
)
shader.use()
}

View File

@ -78,7 +78,7 @@ class RawItemElement(
val textureSize = size - 1
val item = stack.item.item
val model = item.getModel(guiRenderer)
val model = item.getModel(guiRenderer.connection)
if (model != null) {
val tints = context.tints.getItemTint(stack)
model.render(guiRenderer, offset, consumer, options, textureSize, stack, tints)

View File

@ -62,7 +62,8 @@ class BakedModel(
}
private fun render(mesh: BlockVertexConsumer, tints: IntArray?) {
for (faces in faces) {
for ((directionIndex, faces) in faces.withIndex()) {
// if(directionIndex == Directions.O_NORTH || directionIndex == Directions.O_UP || directionIndex == Directions.O_EAST)
for (face in faces) {
face.render(mesh, tints)
}

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.models.block.state.render
import de.bixilon.kotlinglm.GLM
import de.bixilon.kotlinglm.func.rad
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec4.Vec4
@ -25,6 +24,7 @@ import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.models.block.element.FaceVertexData
import de.bixilon.minosoft.gui.rendering.models.raw.display.ModelDisplay
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
class BlockGUIConsumer(
@ -32,6 +32,7 @@ class BlockGUIConsumer(
val offset: Vec2,
val consumer: GUIVertexConsumer,
val options: GUIVertexOptions?,
val display: ModelDisplay,
val size: Vec2,
) : BlockVertexConsumer {
override val order = consumer.order
@ -42,28 +43,32 @@ class BlockGUIConsumer(
override fun addQuad(offset: FloatArray, positions: FaceVertexData, uvData: FaceVertexData, textureId: Float, lightTint: Float) = Broken("Not chunk rendering")
override fun addQuad(positions: FaceVertexData, uvData: FaceVertexData, textureId: Float, lightTint: Float) {
val position = Vec3(0, 0, -1) // one block offset in north direction
val front = Vec3(0, 0, 1) // and directly looking onto the south side
// TODO: look from front (whatever that means) to the block in 45° angle from above
val position = Vec3(1.6f, 1.65f, 0.5f)// one block offset in north direction
// TODO: look from front
val front = Vec3(-0.5f, -0.3f, 0.5f).normalizeAssign() // EntityRotation(45.0f, 45.0f).front
val view = GLM.lookAt(position, position + front, CameraDefinition.CAMERA_UP_VEC3)
val projection = GLM.perspective(45.0f.rad, size.x / size.y, CameraDefinition.NEAR_PLANE, CameraDefinition.FAR_PLANE)
val size = size * 1.3f * 10.0f
// val projection = GLM.perspective(105.0f.rad, size.x / size.y, CameraDefinition.NEAR_PLANE, 3.0f)
val viewProjection = view * projection
val viewProjection = view //* projection
val tint = (lightTint.toBits() shl 8) or 0xFF
order.iterate { p, uv ->
gui.context.system.quadOrder.iterate { p, uv ->
val vertexOffset = p * Vec3.length
val uvOffset = uv * Vec2.length
val xyz = Vec4(positions[vertexOffset], positions[vertexOffset + 1], positions[vertexOffset + 2], 1.0f)
val a = viewProjection * xyz
val out = viewProjection * xyz
var x = (a.x * 0.5f) + 0.5f
var x = ((out.x * 0.5f))
x = (x * size.x) + offset.x
var y = (a.y * 0.5f) + 0.5f
y = (y * size.y) + offset.y
var y = ((out.y * 0.5f))
y = (-y * size.y) + offset.y
// TODO: depth
consumer.addVertex(x, y, textureId, uvData[uvOffset], uvData[uvOffset + 1], tint, options)
}

View File

@ -26,6 +26,8 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.cull.side.SideProperties
import de.bixilon.minosoft.gui.rendering.models.item.ItemRender
import de.bixilon.minosoft.gui.rendering.models.raw.display.DisplayPositions
import de.bixilon.minosoft.gui.rendering.models.raw.display.ModelDisplay
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import java.util.*
@ -37,7 +39,7 @@ interface BlockRender : ItemRender {
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) {
val consumer = BlockGUIConsumer(gui, offset, consumer, options, size)
val consumer = BlockGUIConsumer(gui, offset, consumer, options, getDisplay(DisplayPositions.GUI) ?: ModelDisplay.DEFAULT, size)
render(consumer, stack, tints)
}

View File

@ -14,14 +14,23 @@
package de.bixilon.minosoft.gui.rendering.models.item
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.minosoft.data.container.stack.ItemStack
import de.bixilon.minosoft.gui.rendering.chunk.mesh.BlockVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.models.raw.display.DisplayPositions
import de.bixilon.minosoft.gui.rendering.models.raw.display.ModelDisplay
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rad
interface ItemRender {
fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?)
fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?)
fun getDisplay(position: DisplayPositions): ModelDisplay? = when (position) { // TODO: remove hardcoded value
DisplayPositions.GUI -> ModelDisplay(Vec3(30, 225, 0).rad, scale = Vec3(0.625f))
else -> null
}
}

View File

@ -14,13 +14,13 @@
package de.bixilon.minosoft.gui.rendering.models.item
import de.bixilon.minosoft.data.registries.item.items.Item
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
object ItemRenderUtil {
@Deprecated("please let this be the last fucking hack in this game") // TODO
fun Item.getModel(gui: GUIRenderer): ItemRender? {
val block = gui.connection.registries.block[identifier]
fun Item.getModel(connection: PlayConnection): ItemRender? {
val block = connection.registries.block[identifier]
return block?.model ?: block?.states?.default?.model ?: model
}
}

View File

@ -27,6 +27,7 @@ enum class DisplayPositions(vararg names: String = arrayOf()) : AliasableEnum {
HEAD,
GROUND,
FIXED,
WORLD,
;
override val names: Array<String> = names.unsafeCast()

View File

@ -15,21 +15,24 @@ package de.bixilon.minosoft.gui.rendering.models.raw.display
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY_INSTANCE
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.ONE
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rad
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.toVec3
data class ModelDisplay(
val rotation: Vec3?,
val translation: Vec3?,
val scale: Vec3?,
val rotation: Vec3 = Vec3.EMPTY_INSTANCE,
val translation: Vec3 = Vec3.EMPTY_INSTANCE,
val scale: Vec3 = Vec3.ONE,
) {
companion object {
val DEFAULT = ModelDisplay()
fun deserialize(data: JsonObject): ModelDisplay {
return ModelDisplay(
rotation = data["rotation"]?.toVec3()?.rad,
translation = data["translation"]?.toVec3(),
scale = data["scale"]?.toVec3(),
rotation = data["rotation"]?.toVec3()?.rad ?: Vec3.EMPTY_INSTANCE,
translation = data["translation"]?.toVec3() ?: Vec3.EMPTY_INSTANCE,
scale = data["scale"]?.toVec3() ?: Vec3.ONE,
)
}
}