integrate model loader in assets manager, fix animation crash

This commit is contained in:
Bixilon 2021-12-13 10:10:06 +01:00
parent f6e964a643
commit 25a89cc8f7
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
19 changed files with 71 additions and 119 deletions

View File

@ -19,7 +19,7 @@ import de.bixilon.minosoft.util.CountUpAndDownLatch
import java.io.FileNotFoundException
import java.io.InputStream
interface AssetsManager : Iterable<Map.Entry<ResourceLocation, ByteArray>> {
interface AssetsManager {
/**
* All namespaces this assets-manager can provide

View File

@ -63,10 +63,6 @@ class DirectoryAssetsManager(
loaded = false
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {
TODO("Not yet implemented")
}
override fun get(path: ResourceLocation): InputStream {
if (path !in assets) {
throw FileNotFoundException("Can not find asset $path")

View File

@ -44,8 +44,4 @@ abstract class FileAssetsManager : AssetsManager {
assets.clear()
loaded = false
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {
return assets.iterator()
}
}

View File

@ -124,10 +124,6 @@ class JarAssetsManager(
loaded = false
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {
TODO("Not yet implemented")
}
companion object {
private val REQUIRED_FILE_PREFIXES = arrayOf(
"blockstates/",
@ -137,7 +133,7 @@ class JarAssetsManager(
"particles/",
"texts/",
"textures/",
"recipes/"
"recipes/",
)
}
}

View File

@ -127,10 +127,6 @@ class IndexAssetsManager(
loaded = true
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {
TODO("Not yet implemented")
}
override fun unload() {
assets.clear()
loaded = false

View File

@ -85,8 +85,4 @@ class PriorityAssetsManager(
}
}
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {
TODO("Not yet implemented")
}
}

View File

@ -73,7 +73,7 @@ object FileUtil {
return builder.toString()
}
fun InputStream.readJsonObject(close: Boolean = true): Map<String, Any?> {
fun InputStream.readJsonObject(close: Boolean = true): Map<String, Any> {
try {
return Jackson.MAPPER.readValue(this, Jackson.JSON_MAP_TYPE)
} finally {

View File

@ -92,6 +92,10 @@ open class ResourceLocation(
}
}
fun prefix(prefix: String): ResourceLocation {
return ResourceLocation(namespace, prefix + path)
}
override fun compareTo(other: ResourceLocation): Int {
return hashCode() - other.hashCode()
}

View File

@ -107,8 +107,6 @@ data class BlockState(
getProperties(it)
} ?: mutableMapOf()
val material = registries.materialRegistry[ResourceLocation(data["material"].unsafeCast())]!!

View File

@ -13,7 +13,9 @@
package de.bixilon.minosoft.gui.rendering.models
import de.bixilon.minosoft.assets.util.FileUtil.readJsonObject
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.blocks.types.Block
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.models.builtin.BuiltinModels
@ -23,49 +25,45 @@ import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedItemModel
import de.bixilon.minosoft.gui.rendering.models.unbaked.block.RootModel
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.json.Jackson
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
class ModelLoader(
val data: Map<String, ByteArray>,
val renderWindow: RenderWindow,
) {
private val assetsManager = renderWindow.connection.assetsManager
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
for ((entryName, data) in data) {
if (!entryName.startsWith("assets/minecraft/models/") && !entryName.startsWith("assets/minecraft/blockstates/")) {
continue
}
val name = entryName.removePrefix("assets/minecraft/").removeSuffix(".json")
val json: Map<String, Any> = Jackson.MAPPER.readValue(String(data), Jackson.JSON_MAP_TYPE)
if (name.startsWith("models/")) {
modelJsons[name.removePrefix("models/").toResourceLocation()] = json
} else {
blockStateJsons[name.removePrefix("blockstates/").toResourceLocation()] = json
}
}
}
private fun cleanup() {
unbakedBlockModels.clear()
unbakedBlockStateModels.clear()
modelJsons.clear()
blockStateJsons.clear()
}
private fun loadBlockModel(name: ResourceLocation, data: Map<String, Any>? = null): GenericUnbakedModel {
private fun ResourceLocation.model(): ResourceLocation {
return ResourceLocation(this.namespace, "models/" + this.path + ".json")
}
private fun ResourceLocation.blockState(): ResourceLocation {
return ResourceLocation(this.namespace, "blockstates/" + this.path + ".json")
}
private fun loadBlockStates(block: Block) {
val blockStateJson = assetsManager[block.resourceLocation.blockState()].readJsonObject()
val model = RootModel(this, blockStateJson) ?: return
for (state in block.states) {
state.blockModel = model.getModelForState(state).bake(renderWindow).unsafeCast()
}
}
fun loadBlockModel(name: ResourceLocation): GenericUnbakedModel {
unbakedBlockModels[name]?.let { return it.unsafeCast() }
val data = data ?: modelJsons[name] ?: error("Can not find json: $name")
val data = assetsManager[name.model()].readJsonObject()
val parent = data["parent"]?.toResourceLocation()?.let { loadBlockModel(it) }
@ -75,9 +73,9 @@ class ModelLoader(
return model
}
private fun loadItemModel(name: ResourceLocation, data: Map<String, Any>? = null): GenericUnbakedModel {
fun loadItemModel(name: ResourceLocation): GenericUnbakedModel {
unbakedBlockModels[name]?.let { return it.unsafeCast() }
val data = data ?: modelJsons[name] ?: error("Can not find json: $name")
val data = assetsManager[name.model()].readJsonObject()
val parent = data["parent"]?.toResourceLocation()?.let { loadItemModel(it) }
@ -87,48 +85,18 @@ class ModelLoader(
return model
}
private fun loadModels() {
for ((name, data) in modelJsons) {
if (name.path.startsWith("block/")) {
loadBlockModel(name, data)
} else if (name.path.startsWith("item/")) {
loadItemModel(name, data)
} else {
TODO("Unknown block model type: $name")
}
}
}
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.blockModel = 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()
// ToDo: Optimize performance
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Loading block models..." }
registry.blockRegistry.forEachItem {
loadBlockStates(it)
}
Log.log(LogMessageType.VERSION_LOADING, LogLevels.VERBOSE) { "Loading item models..." }
registry.itemRegistry.forEachItem {
loadItemModel(it.resourceLocation.prefix("item/"))
}
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

@ -13,10 +13,9 @@
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.ModelLoader
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
@ -90,7 +89,7 @@ class MultipartRootModel(
return condition
}
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: List<Any>): MultipartRootModel {
operator fun invoke(modelLoader: ModelLoader, data: List<Any>): MultipartRootModel {
val conditions: MutableMap<MutableSet<Map<BlockProperties, Set<Any>>>, MutableSet<UnbakedBlockStateModel>> = mutableMapOf()
@ -100,10 +99,10 @@ class MultipartRootModel(
val applyData = modelData["apply"]!!
val apply: MutableSet<UnbakedBlockStateModel> = mutableSetOf()
if (applyData is Map<*, *>) {
apply += UnbakedBlockStateModel(models, applyData.unsafeCast())
apply += UnbakedBlockStateModel(modelLoader, applyData.unsafeCast())
} else if (applyData is List<*>) {
for (applyModelData in applyData) {
apply += UnbakedBlockStateModel(models, applyModelData.unsafeCast())
apply += UnbakedBlockStateModel(modelLoader, applyModelData.unsafeCast())
}
}

View File

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

View File

@ -13,10 +13,9 @@
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.ModelLoader
import de.bixilon.minosoft.gui.rendering.models.unbaked.UnbakedModel
import de.bixilon.minosoft.util.KUtil.unsafeCast
@ -45,7 +44,7 @@ class SimpleRootModel(
}
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: Map<String, Any>): SimpleRootModel {
operator fun invoke(modelLoader: ModelLoader, data: Map<String, Any>): SimpleRootModel {
val conditions: MutableMap<Map<BlockProperties, Any>, UnbakedModel> = mutableMapOf()
@ -63,8 +62,8 @@ class SimpleRootModel(
}
val model = when (value) {
is Map<*, *> -> UnbakedBlockStateModel(models, value.unsafeCast())
is List<*> -> WeightedUnbakedBlockStateModel(models, value.unsafeCast())
is Map<*, *> -> UnbakedBlockStateModel(modelLoader, value.unsafeCast())
is List<*> -> WeightedUnbakedBlockStateModel(modelLoader, value.unsafeCast())
else -> TODO("Can not create model: $value")
}

View File

@ -15,13 +15,12 @@ package de.bixilon.minosoft.gui.rendering.models.unbaked.block
import de.bixilon.minosoft.data.Axes
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.ModelLoader
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.properties.AbstractFaceProperties
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
@ -181,9 +180,9 @@ data class UnbakedBlockStateModel(
}
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: Map<String, Any>): UnbakedBlockStateModel {
operator fun invoke(modeLoader: ModelLoader, data: Map<String, Any>): UnbakedBlockStateModel {
return UnbakedBlockStateModel(
model = models[data["model"].toResourceLocation()].unsafeCast(),
model = modeLoader.loadBlockModel(data["model"].toResourceLocation()).unsafeCast(),
rotation = data.toVec2iN(),
uvLock = data["uvlock"]?.toBoolean() ?: false,
weight = data["weight"]?.toInt() ?: 1,

View File

@ -13,12 +13,11 @@
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.ModelLoader
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(
@ -36,11 +35,11 @@ class WeightedUnbakedBlockStateModel(
}
companion object {
operator fun invoke(models: Map<ResourceLocation, GenericUnbakedModel>, data: List<Map<String, Any>>): WeightedUnbakedBlockStateModel {
operator fun invoke(modelLoader: ModelLoader, data: List<Map<String, Any>>): WeightedUnbakedBlockStateModel {
val weightedModels: MutableList<UnbakedBlockStateModel> = mutableListOf()
for (entry in data) {
weightedModels += UnbakedBlockStateModel(models, entry)
weightedModels += UnbakedBlockStateModel(modelLoader, entry)
}
return WeightedUnbakedBlockStateModel(weightedModels)

View File

@ -34,12 +34,19 @@ data class UnbakedElementFace(
val uvStart = uv?.let { Vec2(it[0], it[1]) / BLOCK_RESOLUTION } ?: fallbackUvStart
val uvEnd = uv?.let { Vec2(it[2], it[3]) / BLOCK_RESOLUTION } ?: fallbackUvEnd
val cullFace = data["cullface"]?.toString()?.let {
if (it == "none") {
return@let null
}
return@let Directions[it]
}
return UnbakedElementFace(
direction = direction,
uvStart = uvStart,
uvEnd = uvEnd,
texture = data["texture"].toString(),
cullFace = data["cullface"]?.toString()?.let { return@let Directions[it] },
cullFace = cullFace,
rotation = data["rotation"]?.toInt() ?: 0,
tintIndex = data["tintindex"]?.toInt() ?: -1,
)

View File

@ -41,7 +41,7 @@ data class TextureAnimation(
private fun getNextIndex(): Int {
return if (currentFrameIndex == animationProperties.frames.size - 1) {
1
0
} else {
currentFrameIndex + 1
}

View File

@ -131,7 +131,7 @@ class WorldRenderer(
override fun init() {
val asset = AssetsVersionProperties[connection.version]!!
val zip = FileUtil.readFile(FileAssetsUtil.getPath(asset.jarAssetsHash)).readArchive()
val modelLoader = ModelLoader(zip, renderWindow)
val modelLoader = ModelLoader(renderWindow)
modelLoader.load()
connection.registries.fluidRegistry.forEachItem {