wip: multipart models

This commit is contained in:
Bixilon 2021-11-10 13:07:21 +01:00
parent 81dd2f7535
commit 486da0eda1
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 199 additions and 34 deletions

View File

@ -134,11 +134,11 @@ enum class BlockProperties {
companion object { companion object {
private val PROPERTIES: Map<String, List<BlockProperties>> = run { val PROPERTIES: Map<String, List<BlockProperties>> = run {
val map: MutableMap<String, MutableList<BlockProperties>> = mutableMapOf() val map: MutableMap<String, MutableList<BlockProperties>> = mutableMapOf()
for (value in values()) { for (value in values()) {
val list = map.getOrPut(value.group, { mutableListOf() }) val list = map.getOrPut(value.group) { mutableListOf() }
list.add(value) list.add(value)
} }

View File

@ -73,8 +73,9 @@ class WorldRenderer(
val random = Random(0L) val random = Random(0L)
val blockState1 = connection.registries.blockRegistry["end_portal_frame"]?.defaultState val blockState1 = connection.registries.blockRegistry["end_portal_frame"]?.defaultState
val blockState2 = connection.registries.blockRegistry["carved_pumpkin"]?.defaultState val blockState2 = connection.registries.blockRegistry["oak_fence"]!!.defaultState//.withProperties(BlockProperties.FACING to Directions.SOUTH)
val section = ChunkSection(Array(4096) { val section = ChunkSection(Array(4096) {
if (it < 256) return@Array blockState2 else return@Array null
when (random.nextInt(3)) { when (random.nextInt(3)) {
1 -> blockState2 1 -> blockState2
2 -> blockState2 2 -> blockState2

View File

@ -0,0 +1,37 @@
/*
* 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 <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.baked
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel
import glm_.vec3.Vec3i
import java.util.*
class MultipartBakedModel(
val models: Array<BakedBlockModel>,
) : BakedBlockModel {
override fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int {
return 0xFF
}
override fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, neighbours: Array<BlockState?>, light: Int, ambientLight: IntArray) {
for (model in models) {
model.singleRender(position, mesh, random, neighbours, light, ambientLight)
}
}
}

View File

@ -17,7 +17,6 @@ import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.world.light.LightAccessor import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.gui.rendering.models.FaceSize
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel
import glm_.vec3.Vec3i import glm_.vec3.Vec3i
import java.util.* import java.util.*
@ -52,10 +51,6 @@ class WeightedBakedModel(
throw IllegalStateException("Could not find a model: This should never happen!") throw IllegalStateException("Could not find a model: This should never happen!")
} }
override fun getFaceSize(direction: Directions, random: Random): Array<FaceSize> {
return getModel(random).getFaceSize(direction, random)
}
override fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int { override fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int {
return getModel(random).getLight(position, random, side, lightAccessor) return getModel(random).getLight(position, random, side, lightAccessor)
} }

View File

@ -17,15 +17,12 @@ import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.world.light.LightAccessor import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.gui.rendering.models.FaceSize
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
import glm_.vec3.Vec3i import glm_.vec3.Vec3i
import java.util.* import java.util.*
interface BakedBlockModel : BakedModel { interface BakedBlockModel : BakedModel {
fun getFaceSize(direction: Directions, random: Random): Array<FaceSize>
// ToDo: Tint // ToDo: Tint
fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, neighbours: Array<BlockState?>, light: Int, ambientLight: IntArray) fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, neighbours: Array<BlockState?>, light: Int, ambientLight: IntArray)

View File

@ -17,7 +17,6 @@ import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.BlockState import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.world.light.LightAccessor import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.gui.rendering.models.FaceSize
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.toVec3 import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.toVec3
import glm_.vec3.Vec3i import glm_.vec3.Vec3i
import java.util.* import java.util.*
@ -28,16 +27,12 @@ class BakedBlockStateModel(
override val canGreedyMesh: Boolean = true override val canGreedyMesh: Boolean = true
override val greedyMeshableFaces: BooleanArray = booleanArrayOf(true, false, true, true, true, true) override val greedyMeshableFaces: BooleanArray = booleanArrayOf(true, false, true, true, true, true)
override fun getFaceSize(direction: Directions, random: Random): Array<FaceSize> {
return arrayOf() // ToDo
}
override fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, neighbours: Array<BlockState?>, light: Int, ambientLight: IntArray) { override fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, neighbours: Array<BlockState?>, light: Int, ambientLight: IntArray) {
val floatPosition = position.toVec3() val floatPosition = position.toVec3()
for ((index, direction) in faces.withIndex()) { for ((index, direction) in faces.withIndex()) {
val neighbour = neighbours[index] val neighbour = neighbours[index]
if (neighbour != null) { if (neighbour != null) {
continue // continue
} }
for (face in direction) { for (face in direction) {
face.singleRender(floatPosition, mesh, neighbour, light, ambientLight) face.singleRender(floatPosition, mesh, neighbour, light, ambientLight)

View File

@ -0,0 +1,122 @@
/*
* 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 <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.unbaked.block
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.blocks.BlockState
import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.compoundCast
class MultipartRootModel(
private val conditions: MutableMap<MutableSet<Set<Map<BlockProperties, Any>>>, Set<UnbakedBlockStateModel>>,
) : RootModel {
private fun Set<Map<BlockProperties, Any>>.matches(blockState: BlockState): Boolean {
var matches = false
for (propertyMap in this) {
var singleMatches = false
for ((property, value) in propertyMap) {
if (blockState.properties[property] == value) {
singleMatches = true
break
}
}
if (singleMatches) {
matches = true
break
}
}
return matches
}
private fun MutableSet<Set<Map<BlockProperties, Any>>>.matchesAny(blockState: BlockState): Boolean {
var matches = true
for (or in this) {
if (!or.matches(blockState)) {
matches = false
break
}
}
return matches
}
override fun getModelForState(blockState: BlockState): UnbakedModel {
val models: MutableSet<UnbakedBlockStateModel> = mutableSetOf()
for ((condition, apply) in conditions) {
if (condition.matchesAny(blockState)) {
models += apply
}
}
return UnbakedMultipartModel(models)
}
companion object {
private fun getCondition(data: MutableMap<String, Any>): MutableSet<Map<BlockProperties, Any>> {
val singleCondition: MutableSet<Map<BlockProperties, Any>> = mutableSetOf()
for ((propertyName, value) in data) {
val properties: MutableMap<BlockProperties, Any> = mutableMapOf()
for (propertyValue in value.toString().split("|")) {
properties += BlockProperties.parseProperty(propertyName, propertyValue)
}
singleCondition += properties
}
return singleCondition
}
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: List<Any>): MultipartRootModel {
val conditions: MutableMap<MutableSet<Set<Map<BlockProperties, Any>>>, Set<UnbakedBlockStateModel>> = mutableMapOf()
for (modelData in data) {
check(modelData is Map<*, *>)
val condition: MutableSet<Set<Map<BlockProperties, Any>>> = mutableSetOf()
val applyData = modelData["apply"]!!
val apply: MutableSet<UnbakedBlockStateModel> = mutableSetOf()
if (applyData is Map<*, *>) {
apply += UnbakedBlockStateModel(models, applyData.unsafeCast())
} else if (applyData is List<*>) {
for (applyModelData in applyData) {
apply += UnbakedBlockStateModel(models, applyModelData.unsafeCast())
}
}
modelData["when"]?.compoundCast()?.let {
val or = it["OR"]
if (or is List<*>) {
for (orData in or) {
condition += getCondition(orData.unsafeCast())
}
return@let
}
condition += getCondition(it)
}
conditions[condition] = apply
}
return MultipartRootModel(conditions)
}
}
}

View File

@ -28,10 +28,8 @@ interface RootModel {
val variants = data["variants"] val variants = data["variants"]
val multipart = data["multipart"] val multipart = data["multipart"]
return when { return when {
// ToDo: Single?
variants != null -> SimpleRootModel(models, variants.unsafeCast()) variants != null -> SimpleRootModel(models, variants.unsafeCast())
// ToDo: multipart != null -> MultipartUnbakedBlockStateModel(models, multipart.unsafeCast()) multipart != null -> MultipartRootModel(models, multipart.unsafeCast())
multipart != null -> null
else -> TODO("Don't know what type of block state model to choose: $data") else -> TODO("Don't know what type of block state model to choose: $data")
} }
} }

View File

@ -38,21 +38,9 @@ import glm_.vec2.Vec2
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import glm_.vec3.Vec3 import glm_.vec3.Vec3
import glm_.vec3.swizzle.xz import glm_.vec3.swizzle.xz
import kotlin.collections.Map
import kotlin.collections.MutableList
import kotlin.collections.MutableMap
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
import kotlin.collections.drop
import kotlin.collections.iterator
import kotlin.collections.mutableListOf
import kotlin.collections.mutableMapOf
import kotlin.collections.plus
import kotlin.collections.plusAssign
import kotlin.collections.set import kotlin.collections.set
import kotlin.collections.take
import kotlin.collections.toTypedArray
import kotlin.collections.withIndex
data class UnbakedBlockStateModel( data class UnbakedBlockStateModel(
val model: UnbakedBlockModel, val model: UnbakedBlockModel,
@ -114,7 +102,7 @@ data class UnbakedBlockStateModel(
direction = Directions.byDirection(Vec3(face.direction.vectorf).apply { rotateAssign(rad) }) direction = Directions.byDirection(Vec3(face.direction.vectorf).apply { rotateAssign(rad) })
for ((index, position) in positions.withIndex()) { for ((index, position) in positions.withIndex()) {
positions[index] = Vec3(position).apply { rotateAssign(rad) } positions[index] = Vec3(position).apply { rotateAssign(rad, true) }
} }
} }

View File

@ -0,0 +1,24 @@
package de.bixilon.minosoft.gui.rendering.models.unbaked.block
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.models.baked.MultipartBakedModel
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.util.KUtil.unsafeCast
class UnbakedMultipartModel(
val models: Set<UnbakedBlockStateModel>,
) : UnbakedModel {
override fun bake(renderWindow: RenderWindow): BakedModel {
val baked: Array<BakedBlockModel?> = arrayOfNulls(this.models.size)
var index = 0
for (model in this.models) {
baked[index++] = model.bake(renderWindow)
}
return MultipartBakedModel(baked.unsafeCast())
}
}

View File

@ -59,9 +59,17 @@ object Vec3Util {
} }
} }
fun Vec3.rotateAssign(rotation: Vec2) { fun Vec3.rotateAssign(rotation: Vec2, centerBlock: Boolean = false) {
if (centerBlock) {
this -= 0.5f
}
rotateAssign(rotation.y, Axes.Y) rotateAssign(rotation.y, Axes.Y)
rotateAssign(rotation.x, Axes.X) rotateAssign(rotation.x, Axes.X)
if (centerBlock) {
this += 0.5f
}
} }
fun Vec3.rotateAssign(angle: Float, axis: Axes, origin: Vec3, rescale: Boolean) { fun Vec3.rotateAssign(angle: Float, axis: Axes, origin: Vec3, rescale: Boolean) {