mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 17:37:58 -04:00
wip block item rendering
This commit is contained in:
parent
24ba71f323
commit
33ca98b07c
@ -17,6 +17,7 @@ import de.bixilon.kotlinglm.vec2.Vec2i
|
|||||||
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
|
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
|
||||||
import de.bixilon.kutil.observer.DataObserver
|
import de.bixilon.kutil.observer.DataObserver
|
||||||
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
|
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
|
||||||
|
import de.bixilon.kutil.reflection.ReflectionUtil.jvmField
|
||||||
import de.bixilon.minosoft.data.registries.blocks.light.LightProperties
|
import de.bixilon.minosoft.data.registries.blocks.light.LightProperties
|
||||||
import de.bixilon.minosoft.data.registries.blocks.light.OpaqueProperty
|
import de.bixilon.minosoft.data.registries.blocks.light.OpaqueProperty
|
||||||
import de.bixilon.minosoft.data.registries.blocks.settings.BlockSettings
|
import de.bixilon.minosoft.data.registries.blocks.settings.BlockSettings
|
||||||
|
@ -444,8 +444,9 @@ class SolidSectionMesherTest {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) = Broken()
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) = Broken()
|
||||||
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = Broken()
|
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = Broken()
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) = Broken()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,8 +458,9 @@ class SolidSectionMesherTest {
|
|||||||
|
|
||||||
private open class TestModel(val queue: TestQueue, val properties: SideProperties?) : BlockRender {
|
private open class TestModel(val queue: TestQueue, val properties: SideProperties?) : BlockRender {
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) = Broken()
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) = Broken()
|
||||||
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = Broken()
|
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = Broken()
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) = Broken()
|
||||||
override fun getProperties(direction: Directions): SideProperties? {
|
override fun getProperties(direction: Directions): SideProperties? {
|
||||||
return this.properties
|
return this.properties
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ class DummyComponentConsumer : GUIVertexConsumer {
|
|||||||
|
|
||||||
override val order: RenderOrder get() = RenderOrder(IntArray(0))
|
override val order: RenderOrder get() = RenderOrder(IntArray(0))
|
||||||
override fun addVertex(x: Float, y: Float, texture: ShaderTexture?, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?) = Broken()
|
override fun addVertex(x: Float, y: Float, texture: ShaderTexture?, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?) = Broken()
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) = Broken()
|
||||||
override fun addCache(cache: GUIMeshCache) = Broken()
|
override fun addCache(cache: GUIMeshCache) = Broken()
|
||||||
override fun ensureSize(size: Int) = Unit
|
override fun ensureSize(size: Int) = Unit
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
package de.bixilon.minosoft.gui.rendering.gui.mesh
|
package de.bixilon.minosoft.gui.rendering.gui.mesh
|
||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
|
import de.bixilon.kutil.exception.Broken
|
||||||
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
||||||
import de.bixilon.minosoft.gui.rendering.system.base.RenderOrder
|
import de.bixilon.minosoft.gui.rendering.system.base.RenderOrder
|
||||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
|
||||||
@ -36,6 +37,8 @@ open class DummyGUIVertexConsumer : GUIVertexConsumer {
|
|||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) = Broken()
|
||||||
|
|
||||||
override fun addChar(start: Vec2, end: Vec2, texture: Texture?, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
|
override fun addChar(start: Vec2, end: Vec2, texture: Texture?, uvStart: Vec2, uvEnd: Vec2, italic: Boolean, tint: RGBColor, options: GUIVertexOptions?) {
|
||||||
addChar(start, end, this.char++)
|
addChar(start, end, this.char++)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.models.block.state.render
|
||||||
|
|
||||||
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
|
import de.bixilon.kotlinglm.vec3.Vec3
|
||||||
|
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
||||||
|
import de.bixilon.kutil.exception.Broken
|
||||||
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
|
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
||||||
|
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.util.CuboidUtil
|
||||||
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
|
||||||
|
import de.bixilon.minosoft.gui.rendering.util.mesh.MeshOrder
|
||||||
|
import de.bixilon.minosoft.test.ITUtil.allocate
|
||||||
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
|
@Test(groups = ["models"])
|
||||||
|
class BlockGUIConsumerTest {
|
||||||
|
|
||||||
|
private fun create(): BlockGUIConsumer {
|
||||||
|
val gui = GUIRenderer::class.java.allocate()
|
||||||
|
val consumer = GUIConsumer()
|
||||||
|
|
||||||
|
return BlockGUIConsumer(gui, Vec2(11, 12), consumer, null, Vec2(45))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BlockGUIConsumer.assertVertices(vararg expected: Vec2) {
|
||||||
|
val consumer = this.consumer.unsafeCast<GUIConsumer>()
|
||||||
|
|
||||||
|
if (consumer.vertices.size != expected.size) throw AssertionError("Size mismatch")
|
||||||
|
|
||||||
|
for (index in expected.indices) {
|
||||||
|
val delta = expected[index] - consumer.vertices[index]
|
||||||
|
if (delta.length2() < 0.01f) continue
|
||||||
|
throw AssertionError("Vertex at index $index mismatched: Expected ${expected[index]}, but got ${consumer.vertices[index]}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun `south quad with offset and specific size`() {
|
||||||
|
val consumer = create()
|
||||||
|
val position = CuboidUtil.positions(Directions.SOUTH, Vec3(0, 0, 0), Vec3(1, 1, 1))
|
||||||
|
val uv = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f)
|
||||||
|
consumer.addQuad(position, uv, 0f, 0f)
|
||||||
|
|
||||||
|
consumer.assertVertices(Vec2()) // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GUIConsumer : GUIVertexConsumer {
|
||||||
|
override val order = MeshOrder.QUAD
|
||||||
|
val vertices: MutableList<Vec2> = mutableListOf()
|
||||||
|
|
||||||
|
override fun addVertex(x: Float, y: Float, texture: ShaderTexture?, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?) = Broken()
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) = Broken()
|
||||||
|
|
||||||
|
override fun addCache(cache: GUIMeshCache) = Broken()
|
||||||
|
override fun ensureSize(size: Int) = Unit
|
||||||
|
}
|
||||||
|
}
|
@ -72,6 +72,10 @@ class SignBlockEntityRenderer(
|
|||||||
state.model?.render(mesh, state, tints) // render wood part
|
state.model?.render(mesh, state, tints) // render wood part
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderText(state: BlockState, sign: SignBlockEntity, offset: FloatArray, mesh: ChunkMesh, light: Int) {
|
private fun renderText(state: BlockState, sign: SignBlockEntity, offset: FloatArray, mesh: ChunkMesh, light: Int) {
|
||||||
when (state.block) {
|
when (state.block) {
|
||||||
is StandingSignBlock -> renderStandingText(state.getRotation(), sign, offset, mesh, light)
|
is StandingSignBlock -> renderStandingText(state.getRotation(), sign, offset, mesh, light)
|
||||||
@ -123,7 +127,7 @@ class SignBlockEntityRenderer(
|
|||||||
this += 0.5f
|
this += 0.5f
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) = Unit
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) = Unit
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TEXT_PROPERTIES = TextRenderProperties(scale = 1.35f, allowNewLine = false, shadow = false, fallbackColor = ChatColors.BLACK)
|
private val TEXT_PROPERTIES = TextRenderProperties(scale = 1.35f, allowNewLine = false, shadow = false, fallbackColor = ChatColors.BLACK)
|
||||||
|
@ -39,6 +39,7 @@ class BillboardTextMesh(context: RenderContext) : Mesh(context, BillboardTextMes
|
|||||||
data.add(tint.rgba.buffer())
|
data.add(tint.rgba.buffer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) = Broken()
|
||||||
override fun addCache(cache: GUIMeshCache) = Broken("This is not a text only consumer!")
|
override fun addCache(cache: GUIMeshCache) = Broken("This is not a text only consumer!")
|
||||||
|
|
||||||
data class BillboardTextMeshStruct(
|
data class BillboardTextMeshStruct(
|
||||||
|
@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.font
|
|||||||
|
|
||||||
import de.bixilon.kotlinglm.mat4x4.Mat4
|
import de.bixilon.kotlinglm.mat4x4.Mat4
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
|
import de.bixilon.kutil.exception.Broken
|
||||||
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMesh
|
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMesh
|
||||||
import de.bixilon.minosoft.gui.rendering.font.renderer.component.ChatComponentRenderer
|
import de.bixilon.minosoft.gui.rendering.font.renderer.component.ChatComponentRenderer
|
||||||
@ -36,6 +37,7 @@ class WorldGUIConsumer(val mesh: ChunkMesh, val transform: Mat4, val light: Int)
|
|||||||
mesh.addVertex(transformed, this.uv, texture ?: whiteTexture.texture, tint.rgb, light)
|
mesh.addVertex(transformed, this.uv, texture ?: whiteTexture.texture, tint.rgb, light)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) = Broken()
|
||||||
override fun addCache(cache: GUIMeshCache) {
|
override fun addCache(cache: GUIMeshCache) {
|
||||||
throw IllegalStateException("This is not hud!")
|
throw IllegalStateException("This is not hud!")
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,8 @@ class RawItemElement(
|
|||||||
val item = stack.item.item
|
val item = stack.item.item
|
||||||
val model = item.model
|
val model = item.model
|
||||||
if (model != null) {
|
if (model != null) {
|
||||||
model.render(guiRenderer, offset, consumer, options, textureSize, stack)
|
val tints = context.tints.getItemTint(stack)
|
||||||
|
model.render(guiRenderer, offset, consumer, options, textureSize, stack, tints)
|
||||||
} else {
|
} else {
|
||||||
ColorElement(guiRenderer, textureSize, ChatColors.WHITE).render(offset, consumer, options)
|
ColorElement(guiRenderer, textureSize, ChatColors.WHITE).render(offset, consumer, options)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,10 @@ class GUIMesh(
|
|||||||
addVertex(data, halfSize, x, y, texture ?: whiteTexture.texture, u, v, tint, options)
|
addVertex(data, halfSize, x, y, texture ?: whiteTexture.texture, u, v, tint, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) {
|
||||||
|
addVertex(data, halfSize, x, y, textureId, u, v, tint, options)
|
||||||
|
}
|
||||||
|
|
||||||
override fun addCache(cache: GUIMeshCache) {
|
override fun addCache(cache: GUIMeshCache) {
|
||||||
data.add(cache.data)
|
data.add(cache.data)
|
||||||
}
|
}
|
||||||
@ -60,14 +64,18 @@ class GUIMesh(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addVertex(data: AbstractFloatList, halfSize: Vec2, x: Float, y: Float, texture: ShaderIdentifiable, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?) {
|
fun addVertex(data: AbstractFloatList, halfSize: Vec2, x: Float, y: Float, texture: ShaderIdentifiable, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?) {
|
||||||
|
addVertex(data, halfSize, x, y, texture.shaderId.buffer(), u, v, tint.rgba, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addVertex(data: AbstractFloatList, halfSize: Vec2, x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) {
|
||||||
val x = x / halfSize.x - 1.0f
|
val x = x / halfSize.x - 1.0f
|
||||||
val y = 1.0f - y / halfSize.y
|
val y = 1.0f - y / halfSize.y
|
||||||
|
|
||||||
|
|
||||||
var color = tint.rgba
|
var color = tint
|
||||||
|
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
options.tintColor?.let { color = tint.mix(it).rgba }
|
options.tintColor?.let { color = RGBColor(tint).mix(it).rgba }
|
||||||
|
|
||||||
if (options.alpha != 1.0f) {
|
if (options.alpha != 1.0f) {
|
||||||
val alpha = color and 0xFF
|
val alpha = color and 0xFF
|
||||||
@ -81,7 +89,7 @@ class GUIMesh(
|
|||||||
data.add(y)
|
data.add(y)
|
||||||
data.add(u)
|
data.add(u)
|
||||||
data.add(v)
|
data.add(v)
|
||||||
data.add(texture.shaderId.buffer())
|
data.add(textureId)
|
||||||
data.add(color.buffer())
|
data.add(color.buffer())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,10 @@ class GUIMeshCache(
|
|||||||
revision++
|
revision++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?) {
|
||||||
|
GUIMesh.addVertex(data, halfSize, x, y, textureId, u, v, tint, options)
|
||||||
|
}
|
||||||
|
|
||||||
override fun addCache(cache: GUIMeshCache) {
|
override fun addCache(cache: GUIMeshCache) {
|
||||||
data.add(cache.data)
|
data.add(cache.data)
|
||||||
revision++
|
revision++
|
||||||
|
@ -28,6 +28,7 @@ interface GUIVertexConsumer {
|
|||||||
|
|
||||||
fun addVertex(position: Vec2, texture: ShaderTexture?, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) = addVertex(position.x, position.y, texture, uv.x, uv.y, tint, options)
|
fun addVertex(position: Vec2, texture: ShaderTexture?, uv: Vec2, tint: RGBColor, options: GUIVertexOptions?) = addVertex(position.x, position.y, texture, uv.x, uv.y, tint, options)
|
||||||
fun addVertex(x: Float, y: Float, texture: ShaderTexture?, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?)
|
fun addVertex(x: Float, y: Float, texture: ShaderTexture?, u: Float, v: Float, tint: RGBColor, options: GUIVertexOptions?)
|
||||||
|
fun addVertex(x: Float, y: Float, textureId: Float, u: Float, v: Float, tint: Int, options: GUIVertexOptions?)
|
||||||
|
|
||||||
fun addQuad(start: Vec2, end: Vec2, texture: ShaderTexture?, uvStart: Vec2 = UV_START, uvEnd: Vec2 = UV_END, tint: RGBColor, options: GUIVertexOptions?) {
|
fun addQuad(start: Vec2, end: Vec2, texture: ShaderTexture?, uvStart: Vec2 = UV_START, uvEnd: Vec2 = UV_END, tint: RGBColor, options: GUIVertexOptions?) {
|
||||||
val start = start.array
|
val start = start.array
|
||||||
|
@ -36,7 +36,8 @@ class BlockModelPrototype(val model: DirectBlockModel) : BlockRender {
|
|||||||
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = prototype()
|
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = prototype()
|
||||||
override fun getParticleTexture(random: Random?, position: Vec3i) = prototype()
|
override fun getParticleTexture(random: Random?, position: Vec3i) = prototype()
|
||||||
override fun getProperties(direction: Directions) = prototype()
|
override fun getProperties(direction: Directions) = prototype()
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) = prototype()
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) = prototype()
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) = prototype()
|
||||||
|
|
||||||
|
|
||||||
private fun prototype(): Nothing = throw IllegalStateException("prototype")
|
private fun prototype(): Nothing = throw IllegalStateException("prototype")
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.models.block.state.baked
|
package de.bixilon.minosoft.gui.rendering.models.block.state.baked
|
||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
|
||||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
import de.bixilon.minosoft.data.container.stack.ItemStack
|
import de.bixilon.minosoft.data.container.stack.ItemStack
|
||||||
import de.bixilon.minosoft.data.direction.Directions
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
@ -21,10 +20,6 @@ import de.bixilon.minosoft.data.entities.block.BlockEntity
|
|||||||
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
||||||
import de.bixilon.minosoft.data.world.positions.BlockPosition
|
import de.bixilon.minosoft.data.world.positions.BlockPosition
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.mesh.BlockVertexConsumer
|
import de.bixilon.minosoft.gui.rendering.chunk.mesh.BlockVertexConsumer
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
|
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement
|
|
||||||
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.FaceCulling
|
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.cull.FaceCulling
|
||||||
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.cull.side.SideProperties
|
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.cull.side.SideProperties
|
||||||
import de.bixilon.minosoft.gui.rendering.models.block.state.render.BlockRender
|
import de.bixilon.minosoft.gui.rendering.models.block.state.render.BlockRender
|
||||||
@ -66,7 +61,7 @@ class BakedModel(
|
|||||||
return rendered
|
return rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) {
|
private fun render(mesh: BlockVertexConsumer, tints: IntArray?) {
|
||||||
for (faces in faces) {
|
for (faces in faces) {
|
||||||
for (face in faces) {
|
for (face in faces) {
|
||||||
face.render(mesh, tints)
|
face.render(mesh, tints)
|
||||||
@ -74,10 +69,6 @@ class BakedModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) {
|
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = render(mesh, tints)
|
||||||
val texture = particle ?: return
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) = render(mesh, tints)
|
||||||
ImageElement(gui, texture, size = size).render(offset, consumer, options)
|
|
||||||
|
|
||||||
// TODO: create projection/view matrix and look at the edge of the block. Then map the resulting 3d coordinates to 2d space
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -48,11 +48,11 @@ class BuiltModel(
|
|||||||
return rendered
|
return rendered
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) {
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) {
|
||||||
model.render(gui, offset, consumer, options, size, stack)
|
model.render(gui, offset, consumer, options, size, stack, tints)
|
||||||
|
|
||||||
for (dynamic in this.dynamic) {
|
for (dynamic in this.dynamic) {
|
||||||
dynamic.render(gui, offset, consumer, options, size, stack)
|
dynamic.render(gui, offset, consumer, options, size, stack, tints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +63,13 @@ class BuiltModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) {
|
||||||
|
model.render(mesh, stack, tints)
|
||||||
|
for (dynamic in this.dynamic) {
|
||||||
|
dynamic.render(mesh, stack, tints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getProperties(direction: Directions): SideProperties? {
|
override fun getProperties(direction: Directions): SideProperties? {
|
||||||
return model.getProperties(direction) // TODO: dynamic?
|
return model.getProperties(direction) // TODO: dynamic?
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.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
|
||||||
|
import de.bixilon.kutil.exception.Broken
|
||||||
|
import de.bixilon.minosoft.gui.rendering.camera.CameraDefinition
|
||||||
|
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.block.element.FaceVertexData
|
||||||
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.shader.ShaderTexture
|
||||||
|
|
||||||
|
class BlockGUIConsumer(
|
||||||
|
val gui: GUIRenderer,
|
||||||
|
val offset: Vec2,
|
||||||
|
val consumer: GUIVertexConsumer,
|
||||||
|
val options: GUIVertexOptions?,
|
||||||
|
val size: Vec2,
|
||||||
|
) : BlockVertexConsumer {
|
||||||
|
override val order = consumer.order
|
||||||
|
|
||||||
|
|
||||||
|
override fun addVertex(position: FloatArray, uv: Vec2, texture: ShaderTexture, tintColor: Int, light: Int) = Broken("Not chunk rendering")
|
||||||
|
override fun addVertex(x: Float, y: Float, z: Float, u: Float, v: Float, textureId: Float, lightTint: Float) = Broken("Not chunk rendering")
|
||||||
|
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 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 viewProjection = view * projection
|
||||||
|
|
||||||
|
val tint = (lightTint.toBits() shl 8) or 0xFF
|
||||||
|
|
||||||
|
order.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
|
||||||
|
|
||||||
|
var x = (a.x * 0.5f) + 0.5f
|
||||||
|
x = (x * size.x) + offset.x
|
||||||
|
var y = (a.y * 0.5f) + 0.5f
|
||||||
|
y = (y * size.y) + offset.y
|
||||||
|
|
||||||
|
consumer.addVertex(x, y, textureId, uvData[uvOffset], uvData[uvOffset + 1], tint, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// block renders from (in normal cases) from 0 to 1
|
||||||
|
// matrix should map those pixels into screen 2d space (offset until offset+size)
|
||||||
|
}
|
||||||
|
}
|
@ -13,12 +13,17 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.models.block.state.render
|
package de.bixilon.minosoft.gui.rendering.models.block.state.render
|
||||||
|
|
||||||
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
|
import de.bixilon.minosoft.data.container.stack.ItemStack
|
||||||
import de.bixilon.minosoft.data.direction.Directions
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
||||||
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
||||||
import de.bixilon.minosoft.data.world.positions.BlockPosition
|
import de.bixilon.minosoft.data.world.positions.BlockPosition
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.mesh.BlockVertexConsumer
|
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.block.state.baked.cull.side.SideProperties
|
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.item.ItemRender
|
||||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
||||||
@ -30,5 +35,12 @@ interface BlockRender : ItemRender {
|
|||||||
fun render(position: BlockPosition, offset: FloatArray, mesh: BlockVertexConsumer, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?, entity: BlockEntity?): Boolean
|
fun render(position: BlockPosition, offset: FloatArray, mesh: BlockVertexConsumer, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?, entity: BlockEntity?): Boolean
|
||||||
fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?)
|
fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
render(consumer, stack, tints)
|
||||||
|
}
|
||||||
|
|
||||||
fun getProperties(direction: Directions): SideProperties? = null
|
fun getProperties(direction: Directions): SideProperties? = null
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,8 @@ interface PickedBlockRender : BlockRender {
|
|||||||
fun pick(state: BlockState, neighbours: Array<BlockState?>): BlockRender?
|
fun pick(state: BlockState, neighbours: Array<BlockState?>): BlockRender?
|
||||||
|
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) {
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) {
|
||||||
default?.render(gui, offset, consumer, options, size, stack)
|
default?.render(gui, offset, consumer, options, size, stack, tints)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(position: BlockPosition, offset: FloatArray, mesh: BlockVertexConsumer, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?, entity: BlockEntity?): Boolean {
|
override fun render(position: BlockPosition, offset: FloatArray, mesh: BlockVertexConsumer, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?, entity: BlockEntity?): Boolean {
|
||||||
@ -46,6 +46,10 @@ interface PickedBlockRender : BlockRender {
|
|||||||
default?.render(mesh, state, tints)
|
default?.render(mesh, state, tints)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) {
|
||||||
|
default?.render(mesh, stack, tints)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getProperties(direction: Directions): SideProperties? {
|
override fun getProperties(direction: Directions): SideProperties? {
|
||||||
return default?.getProperties(direction) // both models should have the same properties
|
return default?.getProperties(direction) // both models should have the same properties
|
||||||
}
|
}
|
||||||
|
@ -66,14 +66,18 @@ class WeightedBlockRender(
|
|||||||
return getModel(random, position).render(position, offset, mesh, random, state, neighbours, light, tints, entity)
|
return getModel(random, position).render(position, offset, mesh, random, state, neighbours, light, tints, entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) {
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) {
|
||||||
models.first().model.render(gui, offset, consumer, options, size, stack)
|
models.first().model.render(gui, offset, consumer, options, size, stack, tints)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) {
|
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) {
|
||||||
models.first().model.render(mesh, state, tints)
|
models.first().model.render(mesh, state, tints)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) {
|
||||||
|
models.first().model.render(mesh, stack, tints)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
data class WeightedEntry(
|
data class WeightedEntry(
|
||||||
val weight: Int,
|
val weight: Int,
|
||||||
|
@ -28,6 +28,7 @@ import java.util.*
|
|||||||
interface PropertyOnlyBlockRender : BlockRender {
|
interface PropertyOnlyBlockRender : BlockRender {
|
||||||
override fun render(position: BlockPosition, offset: FloatArray, mesh: BlockVertexConsumer, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?, entity: BlockEntity?) = false
|
override fun render(position: BlockPosition, offset: FloatArray, mesh: BlockVertexConsumer, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?, entity: BlockEntity?) = false
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) = Unit
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) = Unit
|
||||||
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = Unit
|
override fun render(mesh: BlockVertexConsumer, state: BlockState, tints: IntArray?) = Unit
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) = Unit
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.models.item
|
|||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
import de.bixilon.minosoft.data.container.stack.ItemStack
|
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.GUIRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement
|
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||||
@ -23,7 +24,11 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
|||||||
|
|
||||||
class FlatItemRender(val texture: Texture) : ItemRender {
|
class FlatItemRender(val texture: Texture) : ItemRender {
|
||||||
|
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) {
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) {
|
||||||
ImageElement(gui, texture, size = size).render(offset, consumer, options)
|
ImageElement(gui, texture, size = size).render(offset, consumer, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) {
|
||||||
|
// TODO: render items in world space
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.models.item
|
|||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
import de.bixilon.minosoft.data.container.stack.ItemStack
|
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.GUIRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
||||||
@ -23,7 +24,8 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
|||||||
class ItemModelPrototype(
|
class ItemModelPrototype(
|
||||||
private var texture: Texture,
|
private var texture: Texture,
|
||||||
) : ItemRender {
|
) : ItemRender {
|
||||||
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack) = prototype()
|
override fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?) = prototype()
|
||||||
|
override fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?) = prototype()
|
||||||
|
|
||||||
|
|
||||||
private fun prototype(): Nothing = throw IllegalStateException("prototype")
|
private fun prototype(): Nothing = throw IllegalStateException("prototype")
|
||||||
|
@ -15,11 +15,13 @@ package de.bixilon.minosoft.gui.rendering.models.item
|
|||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
import de.bixilon.minosoft.data.container.stack.ItemStack
|
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.GUIRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
||||||
|
|
||||||
interface ItemRender {
|
interface ItemRender {
|
||||||
|
|
||||||
fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack)
|
fun render(gui: GUIRenderer, offset: Vec2, consumer: GUIVertexConsumer, options: GUIVertexOptions?, size: Vec2, stack: ItemStack, tints: IntArray?)
|
||||||
|
fun render(mesh: BlockVertexConsumer, stack: ItemStack, tints: IntArray?)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user