wip: model baking

This commit is contained in:
Bixilon 2021-11-05 00:20:13 +01:00
parent 596866c3ee
commit 8ac5e14ddf
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
31 changed files with 811 additions and 112 deletions

View File

@ -36,7 +36,7 @@ enum class Directions(
override val vectorf = Vec3(vector)
override val vectord = Vec3d(vector)
val axis: Axes get() = Axes.get(this)
val axis: Axes get() = Axes[this] // ToDo
lateinit var inverted: Directions
private set
@ -64,6 +64,17 @@ enum class Directions(
}
}
fun getPositions(from: Vec3, to: Vec3): Array<Vec3> {
return when (this) {
DOWN -> arrayOf(from, Vec3(to.x, from.y, from.y), Vec3(from.x, from.y, to.z), Vec3(to.x, from.y, to.z))
UP -> arrayOf(to, Vec3(to.x, to.y, from.y), Vec3(from.x, to.y, to.z), Vec3(from.x, to.y, from.z))
NORTH -> arrayOf(Vec3(to.x, to.y, from.y), Vec3(to.x, from.y, from.y), Vec3(from.x, to.y, from.z), from)
SOUTH -> arrayOf(Vec3(from.x, from.y, to.z), Vec3(to.x, from.y, to.y), Vec3(from.x, to.y, to.z), to)
WEST -> arrayOf(Vec3(from.x, to.y, to.z), Vec3(from.x, to.y, from.y), Vec3(from.x, from.y, to.z), from)
EAST -> arrayOf(Vec3(to.x, from.y, from.z), Vec3(to.x, to.y, from.y), Vec3(to.x, from.y, to.z), to)
}
}
companion object : BlockPropertiesSerializer, ValuesEnum<Directions> {
override val VALUES = values()

View File

@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.sounds.SoundEvent
import de.bixilon.minosoft.data.text.RGBColor
import de.bixilon.minosoft.gui.rendering.TintColorCalculator
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel
import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.unsafeCast
@ -45,6 +46,7 @@ data class BlockState(
val soundEventVolume: Float = 1.0f,
val soundEventPitch: Float = 1.0f,
) {
var model: BakedBlockModel? = null
override fun hashCode(): Int {
return Objects.hash(block, properties)

View File

@ -113,6 +113,10 @@ enum class BlockProperties {
ROTATION("rotation", IntBlockPropertiesSerializer),
ORIENTATION("orientation", Orientations),
// ToDo: used in models
MAP("map", BooleanBlockPropertiesSerializer),
;
val group: String

View File

@ -19,10 +19,10 @@ import java.util.*
enum class MultipartDirectionParser(
vararg val aliases: Any,
) {
NONE(false),
NONE(false, "false"),
LOW,
UP,
SIDE(true),
SIDE(true, "true"),
TALL,
;

View File

@ -13,16 +13,11 @@
package de.bixilon.minosoft.data.registries.blocks.properties.serializer
import de.bixilon.minosoft.util.KUtil.toBoolean
object BooleanBlockPropertiesSerializer : BlockPropertiesSerializer {
override fun deserialize(value: Any): Boolean {
if (value is Boolean) {
return value
}
return when (value) {
"true" -> true
"false" -> false
else -> throw IllegalArgumentException("Not a boolean: $value")
}
return value.toBoolean()
}
}

View File

@ -13,9 +13,11 @@
package de.bixilon.minosoft.data.registries.blocks.properties.serializer
import de.bixilon.minosoft.util.KUtil.toInt
object IntBlockPropertiesSerializer : BlockPropertiesSerializer {
override fun deserialize(value: Any): Int {
return value as Int
return value.toInt()
}
}

View File

@ -16,6 +16,9 @@ package de.bixilon.minosoft.gui.rendering.block
import de.bixilon.minosoft.data.world.ChunkSection
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import glm_.vec3.Vec3i
import java.util.*
class SectionPreparer(
val renderWindow: RenderWindow,
@ -25,6 +28,16 @@ class SectionPreparer(
fun prepare(section: ChunkSection): ChunkSectionMesh {
val mesh = ChunkSectionMesh(renderWindow)
for (x in 0 until ProtocolDefinition.SECTION_MAX_X) {
for (y in 0 until ProtocolDefinition.SECTION_MAX_Y) {
for (z in 0 until ProtocolDefinition.SECTION_MAX_Z) {
val block = section.blocks[ChunkSection.getIndex(x, y, z)]
block?.model?.singleRender(Vec3i(x, y, z), mesh, Random(0L), 0xFF, intArrayOf(0xF, 0xF, 0xF, 0xF))
}
}
}
return mesh

View File

@ -43,25 +43,30 @@ class WorldRenderer(
private val shader = renderSystem.createShader("minosoft:world".toResourceLocation())
private val world: World = connection.world
private val sectionPreparer = SectionPreparer(renderWindow)
private val lightMap = LightMap(connection)
private lateinit var mesh: ChunkSectionMesh
override fun init() {
val asset = Resources.getAssetVersionByVersion(connection.version)
val zip = ZipInputStream(GZIPInputStream(FileInputStream(AssetsUtil.getAssetDiskPath(asset.clientJarHash!!, true))))
val modelLoader = ModelLoader(zip)
val modelLoader = ModelLoader(zip, renderWindow)
modelLoader.load()
val dirt = connection.registries.blockRegistry["dirt"]?.defaultState
val chunk = ChunkSection(Array(4096) { dirt })
mesh = sectionPreparer.prepare(chunk)
mesh.load()
}
override fun postInit() {
lightMap.init()
shader.load()
renderWindow.textureManager.staticTextures.use(shader)
renderWindow.textureManager.staticTextures.animator.use(shader)
lightMap.use(shader)
val blockState = connection.registries.blockRegistry["dispenser"]?.defaultState
val chunk = ChunkSection(Array(4096) { if (it < 1) blockState else null })
mesh = sectionPreparer.prepare(chunk)
mesh.load()
}
override fun setupOpaque() {

View File

@ -0,0 +1,21 @@
/*
* 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
import glm_.vec3.Vec3
class FaceSize(
val start: Vec3,
val end: Vec3,
)

View File

@ -14,10 +14,13 @@
package de.bixilon.minosoft.gui.rendering.models
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.builtin.BuiltinModels
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedBlockModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedItemModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.block.RootModel
import de.bixilon.minosoft.util.KUtil.fromJson
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
@ -33,11 +36,15 @@ import java.util.zip.ZipInputStream
class ModelLoader(
val jar: ZipInputStream,
val renderWindow: RenderWindow,
) {
private val unbakedBlockModels: MutableMap<ResourceLocation, UnbakedModel> = BuiltinModels.BUILTIN_MODELS.toMutableMap()
private val unbakedBlockModels: MutableMap<ResourceLocation, GenericUnbakedModel> = BuiltinModels.BUILTIN_MODELS.toMutableMap()
private val unbakedBlockStateModels: MutableMap<ResourceLocation, RootModel> = mutableMapOf()
private val blockStateJsons: MutableMap<ResourceLocation, Map<String, Any>> = mutableMapOf()
private val modelJsons: MutableMap<ResourceLocation, Map<String, Any>> = mutableMapOf()
private val registry: Registries = renderWindow.connection.registries
private fun loadJsons() {
// ToDo: Integrate in assets manager
var entry: ZipEntry? = jar.nextEntry
@ -61,45 +68,77 @@ class ModelLoader(
}
}
fun load() {
loadJsons()
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Loaded ${blockStateJsons.size} block states and ${modelJsons.size} model jsons!" }
private fun cleanup() {
modelJsons.clear()
blockStateJsons.clear()
}
private fun loadBlockModel(name: ResourceLocation, data: Map<String, Any>? = null): GenericUnbakedModel {
unbakedBlockModels[name]?.let { return it.unsafeCast() }
val data = data ?: modelJsons[name] ?: error("Can not find json: $name")
fun loadBlockModel(name: ResourceLocation, json: Map<String, Any>? = null): UnbakedModel {
unbakedBlockModels[name]?.let { return it.unsafeCast() }
val data = json ?: modelJsons[name] ?: error("Can not find json: $name")
val parent = data["parent"]?.toResourceLocation()?.let { loadBlockModel(it) }
val parent = data["parent"]?.toResourceLocation()?.let { loadBlockModel(it) }
val model = UnbakedBlockModel(parent, data)
val model = UnbakedBlockModel(parent, data)
unbakedBlockModels[name] = model
return model
}
unbakedBlockModels[name] = model
return model
}
private fun loadItemModel(name: ResourceLocation, data: Map<String, Any>? = null): GenericUnbakedModel {
unbakedBlockModels[name]?.let { return it.unsafeCast() }
val data = data ?: modelJsons[name] ?: error("Can not find json: $name")
val parent = data["parent"]?.toResourceLocation()?.let { loadItemModel(it) }
fun loadItemModel(name: ResourceLocation, json: Map<String, Any>? = null): UnbakedModel {
unbakedBlockModels[name]?.let { return it.unsafeCast() }
val data = json ?: modelJsons[name] ?: error("Can not find json: $name")
val model = UnbakedItemModel(parent, data)
val parent = data["parent"]?.toResourceLocation()?.let { loadItemModel(it) }
unbakedBlockModels[name] = model
return model
}
val model = UnbakedItemModel(parent, data)
unbakedBlockModels[name] = model
return model
}
for ((name, json) in modelJsons) {
private fun loadModels() {
for ((name, data) in modelJsons) {
if (name.path.startsWith("block/")) {
loadBlockModel(name, json)
loadBlockModel(name, data)
} else if (name.path.startsWith("item/")) {
loadItemModel(name, json)
loadItemModel(name, data)
} else {
TODO("Unknown block model type: $name")
}
}
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Done loading models!" }
}
private fun loadBlockStates() {
for ((name, data) in blockStateJsons) {
val model = RootModel(unbakedBlockModels, data) ?: continue
unbakedBlockStateModels[name] = model
}
}
private fun bakeModels() {
for ((name, model) in unbakedBlockStateModels) {
val block = registry.blockRegistry[name] ?: continue
for (state in block.states) {
state.model = model.getModelForState(state).bake(renderWindow).unsafeCast()
}
}
}
fun load() {
loadJsons()
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Loaded ${blockStateJsons.size} block states and ${modelJsons.size} model jsons!" }
loadModels()
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Done loading unbaked models!" }
loadBlockStates()
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Done loading block states!" }
bakeModels()
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Done baking models!" }
cleanup()
}
}

View File

@ -0,0 +1,16 @@
/*
* 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
interface BakedModel

View File

@ -0,0 +1,65 @@
/*
* 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.world.light.LightAccessor
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 glm_.vec3.Vec3i
import java.util.*
import kotlin.math.abs
class WeightedBakedModel(
val models: Map<BakedBlockModel, Int>,
) : BakedBlockModel {
val totalWeight: Int
init {
var totalWeight = 0
for ((_, weight) in models) {
totalWeight += weight
}
this.totalWeight = totalWeight
}
private fun getModel(random: Random): BakedBlockModel {
val totalWeight = abs(random.nextLong() % totalWeight)
var weightLeft = totalWeight
for ((model, weight) in models) {
weightLeft -= weight
if (weightLeft < 0) {
return model
}
}
TODO("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 {
return getModel(random).getLight(position, random, side, lightAccessor)
}
override fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, light: Int, ambientLight: IntArray) {
getModel(random).singleRender(position, mesh, random, light, ambientLight)
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.block
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.light.LightAccessor
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 glm_.vec3.Vec3i
import java.util.*
interface BakedBlockModel : BakedModel {
fun getFaceSize(direction: Directions, random: Random): Array<FaceSize>
// ToDo: Tint
fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, light: Int, ambientLight: IntArray)
// ToDo: Get ambient light
fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int
}

View File

@ -0,0 +1,43 @@
/*
* 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.block
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.light.LightAccessor
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.gui.rendering.models.FaceSize
import glm_.vec3.Vec3i
import java.util.*
class BakedBlockStateModel(
val faces: Array<Array<BakedFace>>,
val sizes: Array<Array<FaceSize>>,
) : BakedBlockModel {
override fun getFaceSize(direction: Directions, random: Random): Array<FaceSize> {
return sizes[direction.ordinal]
}
override fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, random: Random, light: Int, ambientLight: IntArray) {
for (direction in faces) {
for (face in direction) {
face.singleRender(position, mesh, light, ambientLight)
}
}
}
override fun getLight(position: Vec3i, random: Random, side: Directions, lightAccessor: LightAccessor): Int {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.block
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import glm_.vec2.Vec2
import glm_.vec3.Vec3
import glm_.vec3.Vec3i
class BakedFace(
val positions: Array<Vec3>,
val uv: Array<Vec2>,
val shade: Boolean,
val tintIndex: Int,
val cullFace: Directions?,
val texture: AbstractTexture,
) {
fun singleRender(position: Vec3i, mesh: ChunkSectionMesh, light: Int, ambientLight: IntArray) {
val floatPosition = Vec3(position)
for (index in DRAW_ORDER) {
mesh.addVertex(positions[index] + floatPosition, uv[index], texture, null, light)
}
}
companion object {
private val DRAW_ORDER = intArrayOf(
0,
1,
3,
3,
2,
0,
)
}
}

View File

@ -0,0 +1,25 @@
/*
* 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.block
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.block.mesh.ChunkSectionMesh
import glm_.vec3.Vec3i
interface GreedyBakedBlockModel {
val canGreedyMesh: Boolean
// ToDo: Tint
fun greedyRender(start: Vec3i, end: Vec3i, side: Directions, mesh: ChunkSectionMesh, light: Int)
}

View File

@ -14,10 +14,10 @@
package de.bixilon.minosoft.gui.rendering.models.builtin
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
object BuiltinModels {
val BUILTIN_MODELS: Map<ResourceLocation, UnbakedModel> = mapOf(
val BUILTIN_MODELS: Map<ResourceLocation, GenericUnbakedModel> = mapOf(
UnbakedBlockEntityModel.RESOURCE_LOCATION to UnbakedBlockEntityModel,
UnbakedGeneratedModel.RESOURCE_LOCATION to UnbakedGeneratedModel,
)

View File

@ -15,11 +15,17 @@ package de.bixilon.minosoft.gui.rendering.models.builtin
import de.bixilon.minosoft.data.registries.CompanionResourceLocation
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.util.KUtil.toResourceLocation
@Deprecated("TODO")
object UnbakedBlockEntityModel : UnbakedModel(null, mapOf()), CompanionResourceLocation {
object UnbakedBlockEntityModel : GenericUnbakedModel(null, mapOf()), CompanionResourceLocation {
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:builtin/entity".toResourceLocation()
override fun bake(renderWindow: RenderWindow): BakedModel {
TODO("Not yet implemented")
}
}

View File

@ -15,11 +15,17 @@ package de.bixilon.minosoft.gui.rendering.models.builtin
import de.bixilon.minosoft.data.registries.CompanionResourceLocation
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.util.KUtil.toResourceLocation
@Deprecated("TODO")
object UnbakedGeneratedModel : UnbakedModel(null, mapOf()), CompanionResourceLocation {
object UnbakedGeneratedModel : GenericUnbakedModel(null, mapOf()), CompanionResourceLocation {
override val RESOURCE_LOCATION: ResourceLocation = "minecraft:builtin/generated".toResourceLocation()
override fun bake(renderWindow: RenderWindow): BakedModel {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,73 @@
/*
* 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
import de.bixilon.minosoft.gui.rendering.models.display.ModelDisplay
import de.bixilon.minosoft.gui.rendering.models.display.ModelDisplayPositions
import de.bixilon.minosoft.gui.rendering.models.unbaked.element.UnbakedElement
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.compoundCast
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
abstract class GenericUnbakedModel(
parent: GenericUnbakedModel?,
json: Map<String, Any>,
) : UnbakedModel {
val display: Map<ModelDisplayPositions, ModelDisplay>
val elements: Set<UnbakedElement>
init {
val display = parent?.display?.toMutableMap() ?: mutableMapOf()
json["display"]?.compoundCast()?.let {
for ((name, value) in it) {
display[ModelDisplayPositions[name]] = ModelDisplay(data = value.unsafeCast())
}
}
this.display = display
}
val textures: Map<String, String>
init {
val textures = parent?.textures?.toMutableMap() ?: mutableMapOf()
json["textures"]?.compoundCast()?.let {
for ((name, value) in it) {
textures[name] = value.toString()
}
}
this.textures = textures
}
init {
val elements: MutableSet<UnbakedElement>
val elementJson = json["elements"]?.listCast<Map<String, Any>>()
if (elementJson != null) {
elements = mutableSetOf()
for (element in elementJson) {
elements += UnbakedElement(data = element)
}
} else {
elements = parent?.elements?.toMutableSet() ?: mutableSetOf()
}
this.elements = elements
}
}

View File

@ -13,11 +13,17 @@
package de.bixilon.minosoft.gui.rendering.models.unbaked
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
import de.bixilon.minosoft.util.KUtil.toBoolean
class UnbakedBlockModel(
parent: UnbakedModel?,
parent: GenericUnbakedModel?,
json: Map<String, Any>,
) : UnbakedModel(parent, json) {
) : GenericUnbakedModel(parent, json) {
val ambientOcclusion: Boolean = json["ambientocclusion"]?.toBoolean() ?: parent?.let { return@let if (parent is UnbakedBlockModel) parent.ambientOcclusion else null } ?: true
override fun bake(renderWindow: RenderWindow): BakedModel {
return object : BakedModel {} // ToDo
}
}

View File

@ -13,13 +13,19 @@
package de.bixilon.minosoft.gui.rendering.models.unbaked
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.GUILights
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
class UnbakedItemModel(
parent: UnbakedModel?,
parent: GenericUnbakedModel?,
json: Map<String, Any>,
) : UnbakedModel(parent, json) {
) : GenericUnbakedModel(parent, json) {
val guiLight: GUILights = json["gui_light"]?.toString()?.let { GUILights[it] } ?: parent?.let { return@let if (parent is UnbakedItemModel) parent.guiLight else null } ?: GUILights.SIDE
// ToDo: Overrides (predicates)
override fun bake(renderWindow: RenderWindow): BakedModel {
return object : BakedModel {} // ToDo
}
}

View File

@ -13,56 +13,10 @@
package de.bixilon.minosoft.gui.rendering.models.unbaked
import de.bixilon.minosoft.gui.rendering.models.display.ModelDisplay
import de.bixilon.minosoft.gui.rendering.models.display.ModelDisplayPositions
import de.bixilon.minosoft.gui.rendering.models.unbaked.element.UnbakedElement
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.compoundCast
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
abstract class UnbakedModel(
parent: UnbakedModel?,
json: Map<String, Any>,
) : Model {
val display: Map<ModelDisplayPositions, ModelDisplay>
val elements: Set<UnbakedElement>
interface UnbakedModel : Model {
init {
val display = parent?.display?.toMutableMap() ?: mutableMapOf()
json["display"]?.compoundCast()?.let {
for ((name, value) in it) {
display[ModelDisplayPositions[name]] = ModelDisplay(data = value.unsafeCast())
}
}
this.display = display
}
val textures: Map<String, String>
init {
val textures = parent?.textures?.toMutableMap() ?: mutableMapOf()
json["textures"]?.compoundCast()?.let {
for ((name, value) in it) {
textures[name] = value.toString()
}
}
this.textures = textures
}
init {
val elements = parent?.elements?.toMutableSet() ?: mutableSetOf()
json["elements"]?.listCast<Map<String, Any>>()?.let {
for (element in it) {
elements += UnbakedElement(data = element)
}
}
this.elements = elements
}
fun bake(renderWindow: RenderWindow): BakedModel
}

View File

@ -0,0 +1,39 @@
/*
* 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.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.util.KUtil.unsafeCast
interface RootModel {
fun getModelForState(blockState: BlockState): UnbakedModel
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: Map<String, Any>): RootModel? {
val variants = data["variants"]
val multipart = data["multipart"]
return when {
// ToDo: Single?
variants != null -> SimpleRootModel(models, variants.unsafeCast())
// ToDo: multipart != null -> MultipartUnbakedBlockStateModel(models, multipart.unsafeCast())
multipart != null -> null
else -> TODO("Don't know what type of block state model to choose: $data")
}
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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
class SimpleRootModel(
private val conditions: Map<Map<BlockProperties, Any>, UnbakedModel>,
) : RootModel {
private fun Map<BlockProperties, Any>.matches(blockState: BlockState): Boolean {
for ((property, value) in this) {
blockState.properties[property]?.let {
if (value != it) {
return false
}
}
}
return true
}
override fun getModelForState(blockState: BlockState): UnbakedModel {
for ((condition, model) in conditions) {
if (condition.matches(blockState)) {
return model
}
}
TODO("Could not find model for $blockState")
}
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: Map<String, Any>): SimpleRootModel {
val conditions: MutableMap<Map<BlockProperties, Any>, UnbakedModel> = mutableMapOf()
for ((conditionString, value) in data) {
val condition: MutableMap<BlockProperties, Any> = mutableMapOf()
if (conditionString.isNotBlank()) {
for (pair in conditionString.split(",")) {
val (propertyName, propertyStringValue) = pair.split("=")
val (property, propertyValue) = BlockProperties.parseProperty(propertyName, propertyStringValue)
condition[property] = propertyValue
}
}
val model = when (value) {
is Map<*, *> -> UnbakedBlockStateModel(models, value.unsafeCast())
is List<*> -> WeightedUnbakedBlockStateModel(models, value.unsafeCast())
else -> TODO("Can not create model: $value")
}
conditions[condition] = model
}
return SimpleRootModel(conditions)
}
}
}

View File

@ -0,0 +1,124 @@
/*
* 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.direction.Directions
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderWindow
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.BakedBlockStateModel
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedFace
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedBlockModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.toVec3iN
import de.bixilon.minosoft.util.KUtil.toBoolean
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
import glm_.vec2.Vec2
import glm_.vec3.Vec3i
data class UnbakedBlockStateModel(
val model: UnbakedBlockModel,
val rotation: Vec3i?,
val uvLock: Boolean,
val weight: Int,
) : UnbakedModel {
override fun bake(renderWindow: RenderWindow): BakedBlockModel {
val textureArray = renderWindow.textureManager.staticTextures
val resolvedTextures: MutableMap<String, AbstractTexture> = mutableMapOf()
fun resolveTexture(key: String, value: String): AbstractTexture {
resolvedTextures[key]?.let { return it }
val variable = value.removePrefix("#")
var texture: AbstractTexture? = null
if (variable.length != value.length) {
// resolve variable first
texture = resolveTexture(variable, model.textures[variable]!!)
}
if (texture == null) {
texture = textureArray.createTexture(value.toResourceLocation().texture())
}
resolvedTextures[key] = texture
return texture
}
for ((key, value) in model.textures) {
resolveTexture(key, value)
}
val faces: Array<MutableList<BakedFace>> = Array(Directions.VALUES.size) { mutableListOf() }
val sizes: Array<MutableList<FaceSize>> = Array(Directions.VALUES.size) { mutableListOf() }
for (element in model.elements) {
for (face in element.faces) {
val texture = resolvedTextures[face.texture.removePrefix("#")]!! // ToDo: Allow direct texture names?
val positions = face.direction.getPositions(element.from, element.to)
val texturePositions = arrayOf(
face.uvStart,
Vec2(face.uvStart.x, face.uvEnd.y),
Vec2(face.uvEnd.x, face.uvStart.y),
face.uvEnd,
)
faces[face.direction.ordinal] += BakedFace(
positions = positions,
uv = texturePositions,
shade = element.shade,
tintIndex = face.tintIndex,
cullFace = face.cullFace,
texture = texture,
)
}
}
val finalFaces: Array<Array<BakedFace>?> = Array(faces.size) { null }
for ((index, faceArray) in faces.withIndex()) {
finalFaces[index] = faceArray.toTypedArray()
}
val finalSizes: Array<Array<FaceSize>?> = Array(sizes.size) { null }
for ((index, sizeArray) in sizes.withIndex()) {
finalSizes[index] = sizeArray.toTypedArray()
}
return BakedBlockStateModel(finalFaces.unsafeCast(), finalSizes.unsafeCast())
}
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: Map<String, Any>): UnbakedBlockStateModel {
return UnbakedBlockStateModel(
model = models[data["model"].toResourceLocation()].unsafeCast(),
rotation = data.toVec3iN(),
uvLock = data["uvlock"]?.toBoolean() ?: false,
weight = data["weight"]?.toInt() ?: 1,
)
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.models.baked.WeightedBakedModel
import de.bixilon.minosoft.gui.rendering.models.baked.block.BakedBlockModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.GenericUnbakedModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
class WeightedUnbakedBlockStateModel(
val models: List<UnbakedBlockStateModel>,
) : UnbakedModel {
override fun bake(renderWindow: RenderWindow): BakedModel {
val baked: MutableMap<BakedBlockModel, Int> = mutableMapOf()
for (model in models) {
baked[model.bake(renderWindow)] = model.weight
}
return WeightedBakedModel(baked)
}
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: List<Map<String, Any>>): WeightedUnbakedBlockStateModel {
val weightedModels: MutableList<UnbakedBlockStateModel> = mutableListOf()
for (entry in data) {
weightedModels += UnbakedBlockStateModel(models, entry)
}
return WeightedUnbakedBlockStateModel(weightedModels)
}
}
}

View File

@ -30,6 +30,8 @@ data class UnbakedElement(
) {
companion object {
const val BLOCK_RESOLUTION = 16.0f
operator fun invoke(data: Map<String, Any>): UnbakedElement {
val faces: MutableSet<UnbakedElementFace> = mutableSetOf()
@ -40,8 +42,8 @@ data class UnbakedElement(
}
return UnbakedElement(
from = data["from"].toVec3(),
to = data["to"].toVec3(),
from = data["from"].toVec3() / BLOCK_RESOLUTION,
to = data["to"].toVec3() / BLOCK_RESOLUTION,
rotation = data["rotation"]?.compoundCast()?.let { return@let UnbakedElementRotation(data = it) },
shade = data["shade"]?.toBoolean() ?: true,
faces = faces,

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.gui.rendering.models.unbaked.element
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.gui.rendering.models.unbaked.element.UnbakedElement.Companion.BLOCK_RESOLUTION
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
import glm_.vec2.Vec2
@ -30,8 +31,8 @@ data class UnbakedElementFace(
companion object {
operator fun invoke(direction: Directions, data: Map<String, Any>): UnbakedElementFace {
val uv = data["uv"]?.listCast<Number>()
val uvStart = Vec2(uv?.get(0) ?: 0.0f, uv?.get(2) ?: 0.0f)
val uvEnd = Vec2(uv?.get(1) ?: 16.0f, uv?.get(3) ?: 16.0f)
val uvStart = Vec2(uv?.get(0) ?: 0.0f, uv?.get(2) ?: 0.0f) / BLOCK_RESOLUTION
val uvEnd = Vec2(uv?.get(1) ?: 16.0f, uv?.get(3) ?: 16.0f) / BLOCK_RESOLUTION
return UnbakedElementFace(
direction = direction,

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.util.vec.vec3
import de.bixilon.minosoft.util.KUtil.toInt
import glm_.vec3.Vec3
import glm_.vec3.Vec3i
object Vec3iUtil {
@ -22,10 +21,10 @@ object Vec3iUtil {
val Vec3i.Companion.MIN: Vec3i
get() = Vec3i(Int.MIN_VALUE, Int.MIN_VALUE, Int.MIN_VALUE)
val Vec3.Companion.EMPTY: Vec3i
val Vec3i.Companion.EMPTY: Vec3i
get() = Vec3i(0, 0, 0)
val Vec3.Companion.MAX: Vec3i
val Vec3i.Companion.MAX: Vec3i
get() = Vec3i(Int.MAX_VALUE, Int.MAX_VALUE, Int.MAX_VALUE)
@ -37,4 +36,13 @@ object Vec3iUtil {
else -> default ?: throw IllegalArgumentException("Not a Vec3i: $this")
}
}
fun Any?.toVec3iN(default: Vec3i? = null): Vec3i? {
return when (this) {
is List<*> -> Vec3i(this[0].toInt(), this[1].toInt(), this[2].toInt())
is Map<*, *> -> Vec3i(this["x"]?.toInt() ?: 0.0f, this["y"]?.toInt() ?: 0.0f, this["z"]?.toInt() ?: 0.0f)
is Number -> Vec3i(this.toInt())
else -> default
}
}
}

View File

@ -465,4 +465,28 @@ object KUtil {
}
return null
}
fun Any?.autoType(): Any? {
if (this == null) {
return this
}
if (this is Number) {
return this
}
val string = this.toString()
if (string == "true") {
return true
}
if (string == "false") {
return false
}
// ToDo: Optimize
if (string.matches("\\d+".toRegex())) {
return string.toInt()
}
return string
}
}