wighted block state model/apply

This commit is contained in:
Bixilon 2023-03-25 15:44:47 +01:00
parent 955bf59ace
commit 0a435274b5
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 131 additions and 34 deletions

View File

@ -76,7 +76,6 @@ class BlockStateApplyTest {
x = 0, x = 0,
y = 0, y = 0,
uvLock = false, uvLock = false,
weight = 1,
)) ))
} }

View File

@ -17,14 +17,14 @@ import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.world.entities.BlockEntityRenderer import de.bixilon.minosoft.gui.rendering.world.entities.BlockEntityRenderer
import de.bixilon.minosoft.gui.rendering.world.entities.MeshedBlockEntityRenderer import de.bixilon.minosoft.gui.rendering.world.entities.MeshedEntityRenderer
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
abstract class MeshedBlockEntity(connection: PlayConnection) : BlockEntity(connection) { abstract class MeshedBlockEntity(connection: PlayConnection) : BlockEntity(connection) {
override fun getRenderer(context: RenderContext, blockState: BlockState, blockPosition: Vec3i, light: Int): MeshedBlockEntityRenderer<*>? { override fun getRenderer(context: RenderContext, blockState: BlockState, blockPosition: Vec3i, light: Int): MeshedEntityRenderer<*> {
var renderer = this.renderer var renderer = this.renderer
if (renderer is MeshedBlockEntityRenderer<*> && renderer.blockState == blockState) { if (renderer is MeshedEntityRenderer && renderer.blockState == blockState) {
return renderer return renderer
} }
renderer = createMeshedRenderer(context, blockState, blockPosition) renderer = createMeshedRenderer(context, blockState, blockPosition)
@ -36,5 +36,5 @@ abstract class MeshedBlockEntity(connection: PlayConnection) : BlockEntity(conne
throw IllegalAccessException() throw IllegalAccessException()
} }
abstract fun createMeshedRenderer(context: RenderContext, blockState: BlockState, blockPosition: Vec3i): MeshedBlockEntityRenderer<*> abstract fun createMeshedRenderer(context: RenderContext, blockState: BlockState, blockPosition: Vec3i): MeshedEntityRenderer<*>
} }

View File

@ -15,17 +15,17 @@ package de.bixilon.minosoft.gui.rendering.models.block.state.apply
import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.json.JsonUtil.asJsonObject import de.bixilon.kutil.json.JsonUtil.asJsonObject
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakedModel import de.bixilon.minosoft.gui.rendering.models.block.state.render.BlockRender
import de.bixilon.minosoft.gui.rendering.models.loader.BlockLoader import de.bixilon.minosoft.gui.rendering.models.loader.BlockLoader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
interface BlockStateApply { interface BlockStateApply {
fun bake(textures: TextureManager): BakedModel? fun bake(textures: TextureManager): BlockRender?
companion object { companion object {
fun deserialize(loader: BlockLoader, data: Any): BlockStateApply { fun deserialize(loader: BlockLoader, data: Any): BlockStateApply? {
if (data is Map<*, *>) return SingleBlockStateApply.deserialize(loader, data.asJsonObject()) if (data is Map<*, *>) return SingleBlockStateApply.deserialize(loader, data.asJsonObject())
if (data is List<*>) return WeightedBlockStateApply.deserialize(loader, data.unsafeCast()) if (data is List<*>) return WeightedBlockStateApply.deserialize(loader, data.unsafeCast())

View File

@ -34,7 +34,6 @@ import java.util.*
data class SingleBlockStateApply( data class SingleBlockStateApply(
val model: BlockModel, val model: BlockModel,
val uvLock: Boolean = false, val uvLock: Boolean = false,
val weight: Int = 1,
val x: Int = 0, val x: Int = 0,
val y: Int = 0, val y: Int = 0,
) : BlockStateApply { ) : BlockStateApply {
@ -84,11 +83,10 @@ data class SingleBlockStateApply(
fun deserialize(model: BlockModel, data: JsonObject): SingleBlockStateApply { fun deserialize(model: BlockModel, data: JsonObject): SingleBlockStateApply {
val uvLock = data["uvlock"]?.toBoolean() ?: false val uvLock = data["uvlock"]?.toBoolean() ?: false
val weight = data["weight"]?.toInt() ?: 1
val x = data["x"]?.toInt()?.rotation() ?: 0 val x = data["x"]?.toInt()?.rotation() ?: 0
val y = data["y"]?.toInt()?.rotation() ?: 0 val y = data["y"]?.toInt()?.rotation() ?: 0
return SingleBlockStateApply(model, uvLock, weight, x, y) return SingleBlockStateApply(model, uvLock, x, y)
} }
fun deserialize(loader: BlockLoader, data: JsonObject): SingleBlockStateApply { fun deserialize(loader: BlockLoader, data: JsonObject): SingleBlockStateApply {

View File

@ -13,26 +13,48 @@
package de.bixilon.minosoft.gui.rendering.models.block.state.apply package de.bixilon.minosoft.gui.rendering.models.block.state.apply
import de.bixilon.kutil.array.ArrayUtil.cast
import de.bixilon.kutil.json.JsonObject import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakedModel import de.bixilon.kutil.primitive.IntUtil.toInt
import de.bixilon.minosoft.gui.rendering.models.block.state.render.WeightedBlockRender
import de.bixilon.minosoft.gui.rendering.models.loader.BlockLoader import de.bixilon.minosoft.gui.rendering.models.loader.BlockLoader
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureManager
data class WeightedBlockStateApply( data class WeightedBlockStateApply(
val models: List<SingleBlockStateApply> val models: List<WeightedApply>
) : BlockStateApply { ) : BlockStateApply {
override fun bake(textures: TextureManager): BakedModel { override fun bake(textures: TextureManager): WeightedBlockRender? {
TODO("Not yet implemented") val baked: Array<WeightedBlockRender.WeightedEntry?> = arrayOfNulls(models.size)
var totalWeight = 0
for ((index, entry) in models.withIndex()) {
val model = entry.apply.bake(textures) ?: continue
baked[index] = WeightedBlockRender.WeightedEntry(entry.weight, model)
totalWeight += entry.weight
} }
if (totalWeight == 0) return null
return WeightedBlockRender(baked.cast(), totalWeight)
}
data class WeightedApply(
val weight: Int,
val apply: SingleBlockStateApply,
)
companion object { companion object {
fun deserialize(loader: BlockLoader, data: List<JsonObject>): WeightedBlockStateApply { fun deserialize(loader: BlockLoader, data: List<JsonObject>): WeightedBlockStateApply? {
val models: MutableList<SingleBlockStateApply> = mutableListOf() if (data.isEmpty()) return null
val models: MutableList<WeightedApply> = mutableListOf()
for (entry in data) { for (entry in data) {
models += SingleBlockStateApply.deserialize(loader, entry) var weight = entry["weight"]?.toInt() ?: 1
if (weight < 0) weight = 1
val apply = SingleBlockStateApply.deserialize(loader, entry)
models += WeightedApply(weight, apply)
} }
return WeightedBlockStateApply(models) return WeightedBlockStateApply(models)

View File

@ -28,9 +28,9 @@ class BakedModel(
val particle: AbstractTexture?, val particle: AbstractTexture?,
) : BlockRender { ) : BlockRender {
override fun getParticleTexture(random: Random, position: Vec3i) = particle override fun getParticleTexture(random: Random?, position: Vec3i) = particle
override fun render(position: BlockPosition, mesh: WorldMesh, random: Random, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean { override fun render(position: BlockPosition, mesh: WorldMesh, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean {
val float = position.toVec3 val float = position.toVec3
val array = float.array val array = float.array

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
import java.util.* import java.util.*
interface BlockRender { interface BlockRender {
fun getParticleTexture(random: Random, position: Vec3i): AbstractTexture? = null fun getParticleTexture(random: Random?, position: Vec3i): AbstractTexture? = null
fun render(position: BlockPosition, mesh: WorldMesh, random: Random, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean fun render(position: BlockPosition, mesh: WorldMesh, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean
} }

View File

@ -0,0 +1,62 @@
/*
* 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.vec3.Vec3i
import de.bixilon.kutil.exception.Broken
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.BlockPositionUtil.positionHash
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
import java.util.*
import kotlin.math.abs
class WeightedBlockRender(
val models: Array<WeightedEntry>,
val totalWeight: Int,
) : BlockRender {
private fun getModel(random: Random?): BlockRender {
if (random == null) return models.first().model
var weightLeft = abs(random.nextLong() % totalWeight)
for ((weight, model) in models) {
weightLeft -= weight
if (weightLeft >= 0) continue
return model
}
Broken("Could not find a model: This should never happen!")
}
override fun getParticleTexture(random: Random?, position: Vec3i): AbstractTexture? {
random?.setSeed(position.positionHash)
return getModel(random).getParticleTexture(random, position)
}
override fun render(position: BlockPosition, mesh: WorldMesh, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean {
return getModel(random).render(position, mesh, random, state, neighbours, light, tints)
}
data class WeightedEntry(
val weight: Int,
val model: BakedModel,
)
}

View File

@ -43,7 +43,7 @@ interface VariantBlockModel : DirectBlockModel {
for ((variant, entry) in data) { for ((variant, entry) in data) {
val apply = BlockStateApply.deserialize(loader, entry.asJsonObject()) val apply = BlockStateApply.deserialize(loader, entry.asJsonObject()) ?: continue
if (variant == "") { if (variant == "") {
// no further conditions // no further conditions
return SingleVariantBlockModel(apply) return SingleVariantBlockModel(apply)
@ -51,6 +51,8 @@ interface VariantBlockModel : DirectBlockModel {
variants[parseVariant(variant)] = apply variants[parseVariant(variant)] = apply
} }
if (variants.isEmpty()) return null
return PropertyVariantBlockModel(variants) return PropertyVariantBlockModel(variants)
} }
} }

View File

@ -0,0 +1,19 @@
/*
* 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.world.entities
import de.bixilon.minosoft.data.entities.block.BlockEntity
import de.bixilon.minosoft.gui.rendering.models.block.state.render.BlockRender
interface MeshedEntityRenderer<E : BlockEntity> : BlockEntityRenderer<E>, BlockRender

View File

@ -32,10 +32,9 @@ import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.font.Font import de.bixilon.minosoft.gui.rendering.font.Font
import de.bixilon.minosoft.gui.rendering.font.renderer.ChatComponentRenderer import de.bixilon.minosoft.gui.rendering.font.renderer.ChatComponentRenderer
import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement.Companion.BLOCK_SIZE import de.bixilon.minosoft.gui.rendering.models.block.element.ModelElement.Companion.BLOCK_SIZE
import de.bixilon.minosoft.gui.rendering.models.block.state.render.BlockRender
import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3 import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotateAssign import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.rotateAssign
import de.bixilon.minosoft.gui.rendering.world.entities.BlockEntityRenderer import de.bixilon.minosoft.gui.rendering.world.entities.MeshedEntityRenderer
import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh import de.bixilon.minosoft.gui.rendering.world.mesh.SingleWorldMesh
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
import de.bixilon.minosoft.gui.rendering.world.preparer.cull.SolidCullSectionPreparer.Companion.SELF_LIGHT_INDEX import de.bixilon.minosoft.gui.rendering.world.preparer.cull.SolidCullSectionPreparer.Companion.SELF_LIGHT_INDEX
@ -45,7 +44,7 @@ class SignBlockEntityRenderer(
val sign: SignBlockEntity, val sign: SignBlockEntity,
val context: RenderContext, val context: RenderContext,
override val blockState: BlockState, override val blockState: BlockState,
) : BlockEntityRenderer<SignBlockEntity>, BlockRender { ) : MeshedEntityRenderer<SignBlockEntity> {
override val enabled: Boolean get() = false override val enabled: Boolean get() = false
private fun getRotation(): Float { private fun getRotation(): Float {
@ -54,7 +53,7 @@ class SignBlockEntityRenderer(
return rotation * 22.5f return rotation * 22.5f
} }
override fun render(position: Vec3i, mesh: WorldMesh, random: Random, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean { override fun render(position: Vec3i, mesh: WorldMesh, random: Random?, state: BlockState, neighbours: Array<BlockState?>, light: ByteArray, tints: IntArray?): Boolean {
val block = this.blockState.block val block = this.blockState.block
if (block is StandingSignBlock) { if (block is StandingSignBlock) {
renderStandingText(position, mesh, light[SELF_LIGHT_INDEX].toInt()) renderStandingText(position, mesh, light[SELF_LIGHT_INDEX].toInt())

View File

@ -56,10 +56,9 @@ class SolidCullSectionPreparer(
} }
override fun prepareSolid(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: Array<Chunk>, neighbours: Array<ChunkSection?>, mesh: WorldMesh) { override fun prepareSolid(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: Array<Chunk>, neighbours: Array<ChunkSection?>, mesh: WorldMesh) {
val random = Random(0L) val random = if (profile.antiMoirePattern) Random(0L) else null
val randomness = profile.antiMoirePattern
val isLowestSection = sectionHeight == chunk.minSection val isLowestSection = sectionHeight == chunk.minSection
val isHighestSection = sectionHeight == chunk.maxSection val isHighestSection = sectionHeight == chunk.maxSection
val blocks = section.blocks val blocks = section.blocks
@ -132,11 +131,8 @@ class SolidCullSectionPreparer(
light[O_UP] = (light[O_UP].toInt() or 0xF0).toByte() light[O_UP] = (light[O_UP].toInt() or 0xF0).toByte()
} }
if (randomness) { random?.setSeed(position.positionHash)
random.setSeed(position.positionHash)
} else {
random.setSeed(0L)
}
val tints = tintColorCalculator.getAverageBlockTint(chunk, neighbourChunks, state, x, y, z) val tints = tintColorCalculator.getAverageBlockTint(chunk, neighbourChunks, state, x, y, z)
var rendered = model.render(position, mesh, random, state, neighbourBlocks, light, tints) var rendered = model.render(position, mesh, random, state, neighbourBlocks, light, tints)