From cef0c8632155d44ed359ae2663c041fbac58f4c7 Mon Sep 17 00:00:00 2001 From: boybook Date: Mon, 19 Feb 2024 00:45:44 +0800 Subject: [PATCH] Entity register and more complex bedrock geometry render and bake. --- .../easecation/bedrockloader/BedrockLoader.kt | 18 +- .../bedrockloader/BedrockLoaderClient.kt | 16 + .../BedrockResourceReloadListener.kt | 2 - .../block/component/BlockComponents.kt | 2 +- .../block/component/ComponentGeometry.kt | 2 +- .../definition/BlockResourceDefinition.kt | 2 +- .../EntityRenderControllerDefinition.kt | 67 +++++ .../definition/EntityResourceDefinition.kt | 9 +- .../bedrock/definition/GeometryDefinition.kt | 126 ++++---- .../definition/TerrainTextureDefinition.kt | 4 +- .../bedrockloader/bedrock/typealias.kt | 6 + .../bedrockloader/block/BlockDataDriven.kt | 3 +- .../deserializer/BedrockBehaviorContext.kt | 17 ++ .../BedrockBehaviorDeserializer.kt | 40 +++ .../deserializer/BedrockPackContext.kt | 10 + .../deserializer/BedrockResourceContext.kt | 37 +++ .../BedrockResourceDeserializer.kt | 83 ++++++ .../deserializer/PackDeserializer.kt | 9 + .../bedrockloader/entity/EntityDataDriven.kt | 55 ++++ .../java/definition/JavaModelDefinition.kt | 6 +- .../loader/BedrockAddonLoader.kt | 150 ---------- .../loader/BedrockAddonsLoader.kt | 116 ++++++++ .../loader/BedrockAddonsRegistry.kt | 25 ++ .../loader/BedrockBehaviorContext.kt | 10 - .../loader/BedrockBehaviorPackLoader.kt | 28 +- .../loader/BedrockPackContext.kt | 6 - .../loader/BedrockResourceContext.kt | 14 - .../loader/BedrockResourcePackLoader.kt | 239 ++++++++++++--- .../render/BedrockGeometryModel.kt | 135 +++++++++ .../render/BedrockModelProvider.kt | 15 + .../bedrockloader/render/BedrockRenderUtil.kt | 74 +++++ .../render/MeshBuilderVertexConsumer.kt | 145 +++++++++ .../RenderControllerWithClientEntity.kt | 44 +++ .../render/VertexIndexedVertexConsumer.kt | 9 + .../bedrockloader/render/model/Dilation.kt | 29 ++ .../bedrockloader/render/model/Model.kt | 35 +++ .../render/model/ModelCuboidData.kt | 23 ++ .../bedrockloader/render/model/ModelData.kt | 14 + .../bedrockloader/render/model/ModelPart.kt | 281 ++++++++++++++++++ .../render/model/ModelPartBuilder.kt | 82 +++++ .../render/model/ModelPartData.kt | 48 +++ .../render/model/ModelTransform.kt | 26 ++ .../bedrockloader/render/model/ModelUtil.kt | 22 ++ .../render/model/TextureDimensions.kt | 14 + .../render/model/TexturedModelData.kt | 21 ++ .../renderer/EntityDataDrivenRenderer.kt | 47 +++ .../easecation/bedrockloader/util/GsonUtil.kt | 3 + src/main/resources/fabric.mod.json | 6 + 48 files changed, 1857 insertions(+), 318 deletions(-) create mode 100644 src/main/kotlin/net/easecation/bedrockloader/BedrockLoaderClient.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityRenderControllerDefinition.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/bedrock/typealias.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorContext.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorDeserializer.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockPackContext.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceContext.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceDeserializer.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/deserializer/PackDeserializer.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/entity/EntityDataDriven.kt delete mode 100644 src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonLoader.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsLoader.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsRegistry.kt delete mode 100644 src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorContext.kt delete mode 100644 src/main/kotlin/net/easecation/bedrockloader/loader/BedrockPackContext.kt delete mode 100644 src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourceContext.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/BedrockGeometryModel.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/BedrockModelProvider.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/BedrockRenderUtil.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/MeshBuilderVertexConsumer.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/RenderControllerWithClientEntity.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/VertexIndexedVertexConsumer.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/Dilation.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/Model.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelCuboidData.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelData.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPart.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartBuilder.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartData.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelTransform.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/ModelUtil.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/TextureDimensions.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/model/TexturedModelData.kt create mode 100644 src/main/kotlin/net/easecation/bedrockloader/render/renderer/EntityDataDrivenRenderer.kt diff --git a/src/main/kotlin/net/easecation/bedrockloader/BedrockLoader.kt b/src/main/kotlin/net/easecation/bedrockloader/BedrockLoader.kt index 37f5b42..34d2b79 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/BedrockLoader.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/BedrockLoader.kt @@ -1,6 +1,7 @@ package net.easecation.bedrockloader -import net.easecation.bedrockloader.loader.BedrockAddonLoader +import net.easecation.bedrockloader.loader.BedrockAddonsLoader +import net.easecation.bedrockloader.loader.BedrockAddonsRegistry import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder import net.fabricmc.loader.api.FabricLoader @@ -24,23 +25,30 @@ object BedrockLoader : ModInitializer { logger.info("Initializing BedrockLoader...") logger.info("Loading bedrock addons...") - BedrockAddonLoader.load() + BedrockAddonsLoader.load() // group var iconItem = Items.BONE_BLOCK - if (BedrockAddonLoader.registeredItems.isNotEmpty()) { - iconItem = BedrockAddonLoader.registeredItems.entries.first().value + if (BedrockAddonsRegistry.items.isNotEmpty()) { + iconItem = BedrockAddonsRegistry.items.values.first() } FabricItemGroupBuilder.create(Identifier("bedrock-loader", "bedrock-loader")) .icon { ItemStack(iconItem) } .appendItems { stacks -> - BedrockAddonLoader.registeredItems.forEach { (_, item) -> + BedrockAddonsRegistry.items.forEach { (_, item) -> stacks.add(ItemStack(item)) } } .build() logger.info("BedrockLoader initialized!") + + // 测试 + test() + } + + private fun test() { + } fun getGameDir(): File { diff --git a/src/main/kotlin/net/easecation/bedrockloader/BedrockLoaderClient.kt b/src/main/kotlin/net/easecation/bedrockloader/BedrockLoaderClient.kt new file mode 100644 index 0000000..c25f39a --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/BedrockLoaderClient.kt @@ -0,0 +1,16 @@ +package net.easecation.bedrockloader + +import net.easecation.bedrockloader.render.BedrockModelProvider +import net.fabricmc.api.ClientModInitializer +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment +import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry + +@Environment(EnvType.CLIENT) +object BedrockLoaderClient : ClientModInitializer { + + override fun onInitializeClient() { + ModelLoadingRegistry.INSTANCE.registerResourceProvider { rm -> BedrockModelProvider } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/BedrockResourceReloadListener.kt b/src/main/kotlin/net/easecation/bedrockloader/BedrockResourceReloadListener.kt index f820281..b411559 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/BedrockResourceReloadListener.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/BedrockResourceReloadListener.kt @@ -1,8 +1,6 @@ package net.easecation.bedrockloader import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener -import net.fabricmc.fabric.impl.resource.loader.FabricModResourcePack -import net.fabricmc.fabric.mixin.resource.loader.NamespaceResourceManagerAccessor import net.minecraft.resource.ResourceManager import net.minecraft.util.Identifier diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/BlockComponents.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/BlockComponents.kt index d2d3b67..1e43661 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/BlockComponents.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/BlockComponents.kt @@ -3,7 +3,7 @@ package net.easecation.bedrockloader.bedrock.block.component import com.google.gson.annotations.SerializedName data class BlockComponents( - @SerializedName("minecraft:material_instances") val minecraftMaterialInstances: List?, + @SerializedName("minecraft:material_instances") val minecraftMaterialInstances: ComponentMaterialInstances?, @SerializedName("minecraft:collision_box") val minecraftCollisionBox: ComponentCollisionBox?, @SerializedName("minecraft:selection_box") val minecraftSelectionBox: ComponentSelectionBox?, @SerializedName("minecraft:display_name") val minecraftDisplayName: String?, diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/ComponentGeometry.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/ComponentGeometry.kt index 2f08313..9f86cf7 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/ComponentGeometry.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/block/component/ComponentGeometry.kt @@ -9,7 +9,7 @@ import java.lang.reflect.Type sealed class ComponentGeometry : IBlockComponent { - data class ComponentGeometrySimple(val value: String) : ComponentGeometry() + data class ComponentGeometrySimple(val identifier: String) : ComponentGeometry() data class ComponentGeometryFull(val identifier: String, val bone_visibility: Map? diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/BlockResourceDefinition.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/BlockResourceDefinition.kt index ce83f04..702a661 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/BlockResourceDefinition.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/BlockResourceDefinition.kt @@ -13,7 +13,7 @@ data class BlockResourceDefinition( ) { data class Block( - val textures: Textures, + val textures: Textures?, val carried_textures: String?, val sound: String? ) diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityRenderControllerDefinition.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityRenderControllerDefinition.kt new file mode 100644 index 0000000..0cd0025 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityRenderControllerDefinition.kt @@ -0,0 +1,67 @@ +package net.easecation.bedrockloader.bedrock.definition + +import com.google.gson.annotations.SerializedName +import net.minecraft.util.Identifier + +data class EntityRenderControllerDefinition( + @SerializedName("format_version") val formatVersion: String, + @SerializedName("render_controllers") val renderControllers: Map +) { + + data class RenderController( + val arrays: Arrays?, + val color: Color?, + val filter_lighting: Boolean?, + val geometry: String, + val ignore_lighting: Boolean?, + val is_hurt_color: IsHurtColor?, + val light_color_multiplier: Any?, + val materials: List>, + val on_fire_color: OnFireColor?, + val overlay_color: OverlayColor?, + val part_visibility: List>?, + val rebuild_animation_matrices: Boolean?, + val textures: List?, + val uv_anim: UvAnim? + ) + + data class Arrays( + val geometries: Map>?, + val materials: Map>?, + val textures: Map>? + ) + + data class Color( + val r: Any?, + val g: Any?, + val b: Any?, + val a: Any? + ) + + data class IsHurtColor( + val r: Any?, + val g: Any?, + val b: Any?, + val a: Any? + ) + + data class OnFireColor( + val r: Any?, + val g: Any?, + val b: Any?, + val a: Any? + ) + + data class OverlayColor( + val r: Any?, + val g: Any?, + val b: Any?, + val a: Any? + ) + + data class UvAnim( + val offset: List, + val scale: List + ) + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityResourceDefinition.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityResourceDefinition.kt index 3256a97..bfe0c7a 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityResourceDefinition.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/EntityResourceDefinition.kt @@ -1,6 +1,7 @@ package net.easecation.bedrockloader.bedrock.definition import com.google.gson.annotations.SerializedName +import net.minecraft.util.Identifier data class EntityResourceDefinition( @SerializedName("format_version") val formatVersion: String, @@ -8,22 +9,22 @@ data class EntityResourceDefinition( ) { data class ClientEntity( - val description: Description + val description: ClientEntityDescription ) - data class Description( + data class ClientEntityDescription( val animations: Map?, val enable_attachables: Boolean?, val geometry: Map?, val queryable_geometry: String?, val hide_armor: Boolean?, val held_item_ignores_lighting: Boolean?, - val identifier: String, + val identifier: Identifier, val materials: Map?, val min_engine_version: String?, val particle_effects: Map?, val particle_emitters: Map?, - val render_controllers: List?, + val render_controllers: List?, val scripts: Scripts?, val sound_effects: Map?, val spawn_egg: SpawnEgg?, diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/GeometryDefinition.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/GeometryDefinition.kt index fb15f4a..c4af710 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/GeometryDefinition.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/GeometryDefinition.kt @@ -1,9 +1,15 @@ package net.easecation.bedrockloader.bedrock.definition +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.annotations.SerializedName +import java.lang.reflect.Type + data class GeometryDefinition( val debug: Boolean?, val format_version: String, - val minecraft: Map> + @SerializedName("minecraft:geometry") val geometry: List ) { data class Model( @@ -16,82 +22,76 @@ data class GeometryDefinition( val identifier: String, val texture_width: Int?, val texture_height: Int?, - val visible_bounds_offset: List?, - val visible_bounds_width: Int?, - val visible_bounds_height: Int? + val visible_bounds_offset: List?, + val visible_bounds_width: Float?, + val visible_bounds_height: Float? ) data class Bone( val binding: String?, val cubes: List?, val debug: Boolean?, - val inflate: Int?, + val inflate: Float?, val locators: Map?, val mirror: Boolean?, val name: String, val parent: String?, - val pivot: List?, + val pivot: List?, val poly_mesh: PolyMesh?, val render_group_id: Int?, - val rotation: List?, + val rotation: List?, val texture_meshes: List? ) data class Cube( - val inflate: Int?, + val inflate: Float?, val mirror: Boolean?, - val origin: List?, - val pivot: List?, + val origin: List?, + val pivot: List?, val reset: Boolean?, - val rotation: List?, - val size: List?, + val rotation: List?, + val size: List?, val uv: Uv? ) - data class Uv( - val north: North?, - val south: South?, - val east: East?, - val west: West?, - val up: Up?, - val down: Down? - ) - - data class North( - val uv: List?, - val uv_size: List?, - val material_instance: String? - ) - - data class South( - val uv: List?, - val uv_size: List?, - val material_instance: String? - ) - - data class East( - val uv: List?, - val uv_size: List?, - val material_instance: String? - ) - - data class West( - val uv: List?, - val uv_size: List?, - val material_instance: String? - ) - - data class Up( - val uv: List?, - val uv_size: List?, - val material_instance: String? - ) - - data class Down( - val uv: List?, - val uv_size: List?, - val material_instance: String? - ) + sealed class Uv { + data class UvBox( + val uv: List?, + ) : Uv() + + data class UvPerFace( + val north: UvPerFaceData?, + val south: UvPerFaceData?, + val east: UvPerFaceData?, + val west: UvPerFaceData?, + val up: UvPerFaceData?, + val down: UvPerFaceData? + ) : Uv() { + data class UvPerFaceData( + val uv: List?, + val uv_size: List?, + val material_instance: String? + ) + } + + class Deserializer : JsonDeserializer { + override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Uv { + if (json.isJsonObject) { + val obj = json.asJsonObject + return UvPerFace( + obj["north"]?.let { context.deserialize(it, UvPerFace.UvPerFaceData::class.java) }, + obj["south"]?.let { context.deserialize(it, UvPerFace.UvPerFaceData::class.java) }, + obj["east"]?.let { context.deserialize(it, UvPerFace.UvPerFaceData::class.java) }, + obj["west"]?.let { context.deserialize(it, UvPerFace.UvPerFaceData::class.java) }, + obj["up"]?.let { context.deserialize(it, UvPerFace.UvPerFaceData::class.java) }, + obj["down"]?.let { context.deserialize(it, UvPerFace.UvPerFaceData::class.java) } + ) + } else { + return UvBox(json.asJsonArray.map { it.asInt }) + } + } + } + } data class Locator( val offset: List?, @@ -101,17 +101,17 @@ data class GeometryDefinition( data class PolyMesh( val normalized_uvs: Boolean?, - val normals: List>?, - val polys: List>?, - val positions: List>?, + val normals: List>?, + val polys: List>?, + val positions: List>?, val uvs: List>? ) data class TextureMeshes( - val local_pivot: List?, - val position: List?, - val rotation: List?, - val scale: List?, + val local_pivot: List?, + val position: List?, + val rotation: List?, + val scale: List?, val texture: String ) diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/TerrainTextureDefinition.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/TerrainTextureDefinition.kt index ad38bff..cb7602e 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/TerrainTextureDefinition.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/definition/TerrainTextureDefinition.kt @@ -1,5 +1,7 @@ package net.easecation.bedrockloader.bedrock.definition +import net.easecation.bedrockloader.bedrock.BedrockTexturePath + data class TerrainTextureDefinition( val resource_pack_name: String?, val texture_name: String?, @@ -8,6 +10,6 @@ data class TerrainTextureDefinition( val texture_data: Map ) { data class TextureData( - val textures: String + val textures: BedrockTexturePath ) } diff --git a/src/main/kotlin/net/easecation/bedrockloader/bedrock/typealias.kt b/src/main/kotlin/net/easecation/bedrockloader/bedrock/typealias.kt new file mode 100644 index 0000000..78185be --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/bedrock/typealias.kt @@ -0,0 +1,6 @@ +package net.easecation.bedrockloader.bedrock + +import net.minecraft.util.Identifier + +typealias BedrockTexturePath = String // 不包含命名空间的路径(如textures/block/stone) +typealias JavaTexturePath = Identifier // 包含命名空间,不包含textures的路径(如minecraft:block/stone) \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/block/BlockDataDriven.kt b/src/main/kotlin/net/easecation/bedrockloader/block/BlockDataDriven.kt index 2825ffb..40a43db 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/block/BlockDataDriven.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/block/BlockDataDriven.kt @@ -22,12 +22,13 @@ class BlockDataDriven private constructor(val identifier: Identifier, val compon } private fun calculateSettings(components: BlockComponents): Settings { - val settings = FabricBlockSettings.of(Material.METAL).hardness(4.0f) + val settings = FabricBlockSettings.of(Material.METAL).hardness(4.0f) // TODO hardness components.minecraftCollisionBox?.let { if (it.size.all { e -> e == 0f }) { settings.collidable(false) } } + // TODO SelectionBox components.minecraftLightEmission?.let { settings.luminance(it) } diff --git a/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorContext.kt b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorContext.kt new file mode 100644 index 0000000..5fcb570 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorContext.kt @@ -0,0 +1,17 @@ +package net.easecation.bedrockloader.deserializer + +import net.easecation.bedrockloader.bedrock.definition.BlockBehaviourDefinition +import net.easecation.bedrockloader.bedrock.definition.EntityBehaviourDefinition +import net.minecraft.util.Identifier + +class BedrockBehaviorContext { + + val blocks: MutableMap = mutableMapOf() + val entities: MutableMap = mutableMapOf() + + fun putAll(other: BedrockBehaviorContext) { + blocks.putAll(other.blocks) + entities.putAll(other.entities) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorDeserializer.kt b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorDeserializer.kt new file mode 100644 index 0000000..89964d6 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockBehaviorDeserializer.kt @@ -0,0 +1,40 @@ +package net.easecation.bedrockloader.deserializer + +import net.easecation.bedrockloader.bedrock.definition.BlockBehaviourDefinition +import net.easecation.bedrockloader.bedrock.definition.EntityBehaviourDefinition +import net.easecation.bedrockloader.util.GsonUtil +import net.minecraft.util.Identifier +import java.io.InputStreamReader +import java.util.zip.ZipFile + +object BedrockBehaviorDeserializer : PackDeserializer { + + override fun deserialize(file: ZipFile): BedrockBehaviorContext { + val context = BedrockBehaviorContext() + + file.use { zip -> + val entries = zip.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + val name = entry.name + if (name.startsWith("entities/") && name.endsWith(".json")) { + zip.getInputStream(entry).use { stream -> + val entityBehaviourDefinition: EntityBehaviourDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), EntityBehaviourDefinition::class.java) + val entityBehaviour: EntityBehaviourDefinition.EntityBehaviour = entityBehaviourDefinition.minecraftEntity + val identifier: Identifier = entityBehaviour.description.identifier + context.entities[identifier] = entityBehaviour + } + } else if (name.startsWith("blocks/") && name.endsWith(".json")) { + zip.getInputStream(entry).use { stream -> + val blockBehaviourDefinition: BlockBehaviourDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), BlockBehaviourDefinition::class.java) + val blockBehaviour: BlockBehaviourDefinition.BlockBehaviour = blockBehaviourDefinition.minecraftBlock + val identifier: Identifier = blockBehaviour.description.identifier + context.blocks[identifier] = blockBehaviour + } + } + } + } + return context + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockPackContext.kt b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockPackContext.kt new file mode 100644 index 0000000..0a77754 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockPackContext.kt @@ -0,0 +1,10 @@ +package net.easecation.bedrockloader.deserializer + +/** + * Mixed all content of loaded bedrock pack + * 混合了所有加载的基岩材质包和行为包的内容,用于loader的交叉读取 + */ +data class BedrockPackContext( + val resource: BedrockResourceContext = BedrockResourceContext(), + val behavior: BedrockBehaviorContext = BedrockBehaviorContext() +) diff --git a/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceContext.kt b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceContext.kt new file mode 100644 index 0000000..283c5d1 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceContext.kt @@ -0,0 +1,37 @@ +package net.easecation.bedrockloader.deserializer + +import net.easecation.bedrockloader.BedrockLoader +import net.easecation.bedrockloader.bedrock.BedrockTexturePath +import net.easecation.bedrockloader.bedrock.JavaTexturePath +import net.easecation.bedrockloader.bedrock.data.TextureImage +import net.easecation.bedrockloader.bedrock.definition.* +import net.minecraft.util.Identifier + +class BedrockResourceContext { + + val terrainTexture: MutableMap = mutableMapOf() + val blocks: MutableMap = mutableMapOf() + val entities: MutableMap = mutableMapOf() + val geometries: MutableMap = mutableMapOf() + val renderControllers: MutableMap = mutableMapOf() + val textureImages: MutableMap = mutableMapOf() // 去除后缀的路径(如textures/block/stone) -> TextureImage(image, type(后缀类型)) + + fun putAll(other: BedrockResourceContext) { + terrainTexture.putAll(other.terrainTexture) + blocks.putAll(other.blocks) + entities.putAll(other.entities) + geometries.putAll(other.geometries) + renderControllers.putAll(other.renderControllers) + textureImages.putAll(other.textureImages) + } + + fun terrainTextureToJava(textureKey: String, namespace: String) : JavaTexturePath? { + val texture = terrainTexture[textureKey]?.textures + if (texture == null || !texture.contains("textures/")) { + BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: $textureKey") + return null + } + return Identifier(namespace, texture.replace("textures/", "")) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceDeserializer.kt b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceDeserializer.kt new file mode 100644 index 0000000..4d060d3 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/deserializer/BedrockResourceDeserializer.kt @@ -0,0 +1,83 @@ +package net.easecation.bedrockloader.deserializer + +import net.easecation.bedrockloader.BedrockLoader +import net.easecation.bedrockloader.bedrock.data.TextureImage +import net.easecation.bedrockloader.bedrock.definition.* +import net.easecation.bedrockloader.util.GsonUtil +import net.minecraft.util.Identifier +import java.io.InputStreamReader +import java.util.zip.ZipFile +import javax.imageio.ImageIO + +object BedrockResourceDeserializer : PackDeserializer { + + override fun deserialize(file: ZipFile): BedrockResourceContext { + val context = BedrockResourceContext() + file.use { zip -> + zip.getEntry("textures/terrain_texture.json")?.let { entry -> + zip.getInputStream(entry).use { stream -> + val terrainTextureDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), TerrainTextureDefinition::class.java) + context.terrainTexture.putAll(terrainTextureDefinition.texture_data) + } + } + zip.getEntry("blocks.json")?.let { entry -> + zip.getInputStream(entry).use { stream -> + val blockResourceDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), BlockResourceDefinition::class.java) + context.blocks.putAll(blockResourceDefinition.blocks) + } + } + val entries = zip.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + val name = entry.name + // texture + if (name.startsWith("textures/") && (name.endsWith(".png") || name.endsWith(".jpg"))) { + try { + val ext = name.substring(name.lastIndexOf('.') + 1) + val withoutExt = name.substring(0, name.lastIndexOf('.')) + context.textureImages[withoutExt] = TextureImage(ImageIO.read(zip.getInputStream(entry)), ext) + } catch (e: Exception) { + BedrockLoader.logger.error("Error parsing texture: $name", e) + } + } + // geometry + if (name.endsWith(".geo.json")) { + zip.getInputStream(entry).use { stream -> + try { + val geometryDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), GeometryDefinition::class.java) + for (model in geometryDefinition.geometry) { + context.geometries[model.description.identifier] = model + } + } catch (e: Exception) { + BedrockLoader.logger.error("Error parsing geometry: $name", e) + } + } + } + // entity + if (name.endsWith(".entity.json")) { + zip.getInputStream(entry).use { stream -> + try { + val entityResourceDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), EntityResourceDefinition::class.java) + context.entities.put(entityResourceDefinition.clientEntity.description.identifier, entityResourceDefinition.clientEntity) + } catch (e: Exception) { + BedrockLoader.logger.error("Error parsing client entity: $name", e) + } + } + } + // render controller + if (name.endsWith(".render_controllers.json") || name.endsWith(".render_controller.json")) { + zip.getInputStream(entry).use { stream -> + try { + val renderControllerDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), EntityRenderControllerDefinition::class.java) + context.renderControllers.putAll(renderControllerDefinition.renderControllers) + } catch (e: Exception) { + BedrockLoader.logger.error("Error parsing render controller: $name", e) + } + } + } + } + } + return context + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/deserializer/PackDeserializer.kt b/src/main/kotlin/net/easecation/bedrockloader/deserializer/PackDeserializer.kt new file mode 100644 index 0000000..fb6598e --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/deserializer/PackDeserializer.kt @@ -0,0 +1,9 @@ +package net.easecation.bedrockloader.deserializer + +import java.util.zip.ZipFile + +interface PackDeserializer { + + fun deserialize(file: ZipFile): T + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/entity/EntityDataDriven.kt b/src/main/kotlin/net/easecation/bedrockloader/entity/EntityDataDriven.kt new file mode 100644 index 0000000..36420ae --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/entity/EntityDataDriven.kt @@ -0,0 +1,55 @@ +package net.easecation.bedrockloader.entity + +import net.easecation.bedrockloader.bedrock.entity.components.EntityComponents +import net.easecation.bedrockloader.loader.BedrockAddonsRegistry +import net.fabricmc.fabric.api.`object`.builder.v1.entity.FabricEntityTypeBuilder +import net.minecraft.entity.EntityType +import net.minecraft.entity.EquipmentSlot +import net.minecraft.entity.SpawnGroup +import net.minecraft.entity.attribute.DefaultAttributeContainer +import net.minecraft.entity.mob.MobEntity +import net.minecraft.item.ItemStack +import net.minecraft.util.Arm +import net.minecraft.util.Identifier +import net.minecraft.world.World + +class EntityDataDriven( + val identifier: Identifier, + val components: EntityComponents, + entityType: EntityType?, + world: World? +) : MobEntity(entityType, world) { + + companion object { + fun buildEntityType(identifier: Identifier): EntityType { + val builder = FabricEntityTypeBuilder.create(SpawnGroup.CREATURE) { type, world -> + val components = BedrockAddonsRegistry.entityComponents[identifier] + ?: throw IllegalStateException("[EntityDataDriven] Entity $identifier has no components") + EntityDataDriven(identifier, components, type, world) + } + return builder.build() + } + fun buildEntityAttributes(components: EntityComponents): DefaultAttributeContainer.Builder { + val builder = MobEntity.createMobAttributes() + // TODO components + return builder + } + } + + override fun getArmorItems(): MutableIterable { + return mutableListOf() + } + + override fun equipStack(slot: EquipmentSlot?, stack: ItemStack?) { + } + + override fun getEquippedStack(slot: EquipmentSlot?): ItemStack { + return ItemStack.EMPTY + } + + override fun getMainArm(): Arm { + return Arm.RIGHT + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/java/definition/JavaModelDefinition.kt b/src/main/kotlin/net/easecation/bedrockloader/java/definition/JavaModelDefinition.kt index a43ca81..f83e4bb 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/java/definition/JavaModelDefinition.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/java/definition/JavaModelDefinition.kt @@ -1,6 +1,8 @@ package net.easecation.bedrockloader.java.definition +import net.minecraft.util.Identifier + data class JavaModelDefinition( - val parent: String? = null, - val textures: Map? = null + var parent: String? = null, + var textures: Map? = null ) diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonLoader.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonLoader.kt deleted file mode 100644 index 0cb1571..0000000 --- a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonLoader.kt +++ /dev/null @@ -1,150 +0,0 @@ -package net.easecation.bedrockloader.loader - -import net.easecation.bedrockloader.BedrockLoader -import net.easecation.bedrockloader.bedrock.data.TextureImage -import net.easecation.bedrockloader.bedrock.definition.BlockBehaviourDefinition -import net.easecation.bedrockloader.bedrock.pack.BedrockPack -import net.easecation.bedrockloader.bedrock.definition.BlockResourceDefinition -import net.easecation.bedrockloader.bedrock.definition.TerrainTextureDefinition -import net.easecation.bedrockloader.bedrock.pack.ZippedBedrockPack -import net.easecation.bedrockloader.util.GsonUtil -import net.minecraft.block.Block -import net.minecraft.item.Item -import net.minecraft.util.Identifier -import java.io.File -import java.io.InputStreamReader -import java.util.* -import java.util.zip.ZipFile -import javax.imageio.ImageIO - -object BedrockAddonLoader { - - private val resourcePackMap: MutableMap = HashMap() - - private val behaviourPackMap: MutableMap = HashMap() - - // private val registeredEntities: MutableMap = HashMap() - - val registeredItems: MutableMap = mutableMapOf() - val registeredBlocks: MutableMap = mutableMapOf() - - fun load() { - val dataFolder: File = BedrockLoader.getGameDir().resolve("config/bedrock-loader") - if (dataFolder.exists()) { - dataFolder.mkdirs() - } - // 从dataFolder中读取所有的zip文件 - val files = dataFolder.listFiles { dir: File?, name: String -> name.endsWith(".zip") || name.endsWith(".mcpack") } - if (files == null) return - if (files.isEmpty()) { - BedrockLoader.logger.warn("No bedrock pack found in " + dataFolder.absolutePath) - return - } - // 读取zip文件 - for (file in files) { - BedrockLoader.logger.info("Loading pack... " + file.name) - try { - val pack = ZippedBedrockPack(file) - if (pack.getPackType().equals("resources")) { - // 只添加到材质包管理器中 - resourcePackMap[pack.getPackId()!!] = pack - this.loadResourcePack(pack, file) - BedrockLoader.logger.info((("Loaded resource pack: " + pack.getPackName()) + "[" + pack.getPackId()) + "]") - } else if (pack.getPackType().equals("data")) { - // 行为包,需要读取内容 - behaviourPackMap[pack.getPackId()!!] = pack - this.loadBehaviourPack(pack, file) - BedrockLoader.logger.info((("Loaded behaviour pack: " + pack.getPackName()) + "[" + pack.getPackId()) + "]") - } - } catch (e: Exception) { - BedrockLoader.logger.warn("Failed to load pack " + file.name, e) - } - } - } - - private fun loadResourcePack(pack: BedrockPack?, file: File) { - if (!file.exists()) { - throw IllegalArgumentException("File not found: " + file.name) - } - val context = BedrockResourceContext() - ZipFile(file).use { zip -> - zip.getEntry("textures/terrain_texture.json")?.let { entry -> - zip.getInputStream(entry).use { stream -> - val terrainTextureDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), TerrainTextureDefinition::class.java) - context.terrainTexture = terrainTextureDefinition.texture_data - } - } - zip.getEntry("blocks.json")?.let { entry -> - zip.getInputStream(entry).use { stream -> - val blockResourceDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), BlockResourceDefinition::class.java) - context.blocks = blockResourceDefinition.blocks - } - } - val entries = zip.entries() - while (entries.hasMoreElements()) { - val entry = entries.nextElement() - val name = entry.name - if (name.startsWith("textures/") && (name.endsWith(".png") || name.endsWith(".jpg"))) { - val ext = name.substring(name.lastIndexOf('.') + 1) - val withoutExt = name.substring(0, name.lastIndexOf('.')) - context.textureImages[withoutExt] = TextureImage(ImageIO.read(zip.getInputStream(entry)), ext) - } - } - } - BedrockResourcePackLoader(BedrockLoader.getTmpResourceDir(), context).load() - } - - private fun loadBehaviourPack(pack: BedrockPack?, file: File) { - if (!file.exists()) { - throw IllegalArgumentException("File not found: " + file.name) - } - - val context = BedrockBehaviorContext() - - ZipFile(file).use { zip -> - val entries = zip.entries() - while (entries.hasMoreElements()) { - val entry = entries.nextElement() - val name = entry.name - /*if (name.startsWith("entities/") && name.endsWith(".json")) { - zip.getInputStream(entry).use { stream -> - val entityBehaviourFile: EntityBehaviourFile = GsonUtil.getGson().fromJson(InputStreamReader(stream), EntityBehaviourFile::class.java) - val entityBehaviour: EntityBehaviourFile.EntityBehaviour = entityBehaviourFile.minecraft_entity() - if (entityBehaviour != null) { - val identifier: String = entityBehaviour.description().identifier() - if (identifier != null) { - registeredEntities[identifier] = entityBehaviour - EntityDataDriven.ID_COMPONENTS_MAP.put(identifier, entityBehaviour.components()) - Entity.registerEntity( - identifier, identifier, - EntityDataDriven::class.java - ) { chunk, nbt -> - // 首次生成实体,写入identifier - if (!nbt.contains("identifier")) { - nbt.putString("identifier", identifier) - } - EntityDataDriven(chunk, nbt) - } - plugin.getLogger().info("[CustomEntity] 注册实体:$identifier") - } - } - } - } else */if (name.startsWith("blocks/") && name.endsWith(".json")) { - zip.getInputStream(entry).use { stream -> - val blockBehaviourDefinition: BlockBehaviourDefinition = GsonUtil.GSON.fromJson(InputStreamReader(stream), BlockBehaviourDefinition::class.java) - val blockBehaviour: BlockBehaviourDefinition.BlockBehaviour = blockBehaviourDefinition.minecraftBlock - val identifier: Identifier = blockBehaviour.description.identifier - // registeredBlocks[identifier] = blockBehaviour - context.blocks[identifier] = blockBehaviour - } - } - } - } - - val loader = BedrockBehaviorPackLoader(context) - loader.load() - - registeredBlocks.putAll(loader.registeredBlocks) - registeredItems.putAll(loader.registeredItems) - } -} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsLoader.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsLoader.kt new file mode 100644 index 0000000..68b3d77 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsLoader.kt @@ -0,0 +1,116 @@ +package net.easecation.bedrockloader.loader + +import com.google.common.io.Files +import net.easecation.bedrockloader.BedrockLoader +import net.easecation.bedrockloader.bedrock.pack.BedrockPack +import net.easecation.bedrockloader.bedrock.pack.ZippedBedrockPack +import net.easecation.bedrockloader.deserializer.BedrockBehaviorDeserializer +import net.easecation.bedrockloader.deserializer.BedrockPackContext +import net.easecation.bedrockloader.deserializer.BedrockResourceDeserializer +import org.apache.commons.io.FileUtils +import org.apache.commons.io.filefilter.TrueFileFilter +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.nio.file.attribute.FileTime +import java.util.* +import java.util.zip.Deflater +import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipOutputStream + +/** + * BedrockAddonLoader is the main class for loading bedrock packs. + * It will load all the bedrock packs from the data folder and load them into the game. + * + * 加载流程: + * 基岩版行为包/材质包 -> 解码器Deserializer(将来要做自动版本提升) -> 混合到同一个BedrockPackContext -> + * 材质包加载器(转换为临时java版材质包、准备资源Provider) -> 行为包加载器(注册方块和物品到java服务端) + */ +object BedrockAddonsLoader { + + private val resourcePackMap: MutableMap = HashMap() + private val behaviourPackMap: MutableMap = HashMap() + + val context = BedrockPackContext() + + fun load() { + val dataFolder: File = BedrockLoader.getGameDir().resolve("config/bedrock-loader") + if (dataFolder.exists()) { + dataFolder.mkdirs() + } + // 从dataFolder中读取所有的zip文件 + val files = dataFolder.listFiles { file: File, name: String -> (file.isDirectory && name != ".DS_Store") || name.endsWith(".zip") || name.endsWith(".mcpack") } + if (files == null) return + if (files.isEmpty()) { + BedrockLoader.logger.warn("No bedrock pack found in " + dataFolder.absolutePath) + return + } + // 读取zip文件 + for (file in files) { + try { + var f: File = file + if (file.isDirectory) { + loadDirectoryPack(file)?.let { f = it } ?: continue + } + val pack: BedrockPack = ZippedBedrockPack(f) + if (pack.getPackType().equals("resources")) { + // 只添加到材质包管理器中 + resourcePackMap[pack.getPackId()!!] = pack + context.resource.putAll(BedrockResourceDeserializer.deserialize(ZipFile(f))) + BedrockLoader.logger.info((("Reading resource pack: " + pack.getPackName()) + "[" + pack.getPackId()) + "]") + } else if (pack.getPackType().equals("data")) { + // 行为包,需要读取内容 + behaviourPackMap[pack.getPackId()!!] = pack + context.behavior.putAll(BedrockBehaviorDeserializer.deserialize(ZipFile(f))) + BedrockLoader.logger.info((("Reading behaviour pack: " + pack.getPackName()) + "[" + pack.getPackId()) + "]") + } + } catch (e: Exception) { + BedrockLoader.logger.warn("Failed to load pack " + file.name, e) + } + } + + // load resource pack + BedrockLoader.logger.info("Loading resource pack...") + BedrockResourcePackLoader(BedrockLoader.getTmpResourceDir(), context).load() + + // load behaviour pack + BedrockLoader.logger.info("Loading behaviour pack...") + val loader = BedrockBehaviorPackLoader(context) + loader.load() + + BedrockLoader.logger.info("Loading pack finished! ${BedrockAddonsRegistry.blocks.size} blocks, ${BedrockAddonsRegistry.items.size} items, ${BedrockAddonsRegistry.entities.size} entities") + } + + private fun loadDirectoryPack(directory: File): File? { + val manifestFile = File(directory, "manifest.json") + if (!manifestFile.exists() || !manifestFile.isFile) { + return null + } + + val tempFile: File + try { + tempFile = File.createTempFile("pack", ".zip") + tempFile.deleteOnExit() + + val time = FileTime.fromMillis(0) + ZipOutputStream(FileOutputStream(tempFile)).use { stream -> + stream.setLevel(Deflater.BEST_COMPRESSION) + val files: Collection = TreeSet(FileUtils.listFiles(directory, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) + for (file in files) { + val entry = ZipEntry(directory.toPath().relativize(file.toPath()).toString()) + .setCreationTime(time) + .setLastModifiedTime(time) + .setLastAccessTime(time) + stream.putNextEntry(entry) + stream.write(Files.toByteArray(file)) + stream.closeEntry() + } + } + } catch (e: IOException) { + throw RuntimeException("Unable to create temporary mcpack file", e) + } + return tempFile + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsRegistry.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsRegistry.kt new file mode 100644 index 0000000..42f57f2 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockAddonsRegistry.kt @@ -0,0 +1,25 @@ +package net.easecation.bedrockloader.loader + +import net.easecation.bedrockloader.bedrock.entity.components.EntityComponents +import net.easecation.bedrockloader.entity.EntityDataDriven +import net.easecation.bedrockloader.render.BedrockGeometryModel +import net.minecraft.block.Block +import net.minecraft.entity.EntityType +import net.minecraft.item.Item +import net.minecraft.util.Identifier +import net.minecraft.util.registry.Registry + +object BedrockAddonsRegistry { + + val items: MutableMap = mutableMapOf() + val blocks: MutableMap = mutableMapOf() + val entities: MutableMap> = mutableMapOf() + val entityComponents: MutableMap = mutableMapOf() + val models: MutableMap = mutableMapOf() + + fun getOrRegisterEntityType(id: Identifier): EntityType = + entities.getOrElse(id) { + Registry.register(Registry.ENTITY_TYPE, id, EntityDataDriven.buildEntityType(id)).also { entities[id] = it } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorContext.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorContext.kt deleted file mode 100644 index 095d94b..0000000 --- a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorContext.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.easecation.bedrockloader.loader - -import net.easecation.bedrockloader.bedrock.definition.BlockBehaviourDefinition -import net.minecraft.util.Identifier - -class BedrockBehaviorContext { - - val blocks: MutableMap = mutableMapOf() - -} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorPackLoader.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorPackLoader.kt index ec086f1..8062206 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorPackLoader.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockBehaviorPackLoader.kt @@ -1,28 +1,36 @@ package net.easecation.bedrockloader.loader import net.easecation.bedrockloader.block.BlockDataDriven +import net.easecation.bedrockloader.deserializer.BedrockPackContext +import net.easecation.bedrockloader.entity.EntityDataDriven import net.fabricmc.fabric.api.item.v1.FabricItemSettings -import net.minecraft.block.Block +import net.fabricmc.fabric.api.`object`.builder.v1.entity.FabricDefaultAttributeRegistry +import net.fabricmc.loader.api.FabricLoader import net.minecraft.item.BlockItem -import net.minecraft.item.Item -import net.minecraft.util.Identifier import net.minecraft.util.registry.Registry class BedrockBehaviorPackLoader( - val context: BedrockBehaviorContext + val context: BedrockPackContext ) { - val registeredItems: MutableMap = mutableMapOf() - val registeredBlocks: MutableMap = mutableMapOf() - fun load() { - context.blocks.forEach { (id, beh) -> + val env = FabricLoader.getInstance().environmentType + // Block + context.behavior.blocks.forEach { (id, beh) -> val block = BlockDataDriven.create(id, beh.components) Registry.register(Registry.BLOCK, id, block) - registeredBlocks[id] = block + BedrockAddonsRegistry.blocks[id] = block val item = BlockItem(block, FabricItemSettings()) Registry.register(Registry.ITEM, id, item) - registeredItems[id] = item + BedrockAddonsRegistry.items[id] = item + } + // Entity + context.behavior.entities.forEach { (id, beh) -> + // entity type + val entityType = BedrockAddonsRegistry.getOrRegisterEntityType(id) + BedrockAddonsRegistry.entityComponents[id] = beh.components + // entity attributes + FabricDefaultAttributeRegistry.register(entityType, EntityDataDriven.buildEntityAttributes(beh.components)) } } diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockPackContext.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockPackContext.kt deleted file mode 100644 index 11e0aa8..0000000 --- a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockPackContext.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.easecation.bedrockloader.loader - -data class BedrockPackContext( - val resource: BedrockResourceContext = BedrockResourceContext(), - val behavior: BedrockBehaviorContext = BedrockBehaviorContext() -) diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourceContext.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourceContext.kt deleted file mode 100644 index 0148657..0000000 --- a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourceContext.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.easecation.bedrockloader.loader - -import net.easecation.bedrockloader.bedrock.data.TextureImage -import net.easecation.bedrockloader.bedrock.definition.BlockResourceDefinition -import net.easecation.bedrockloader.bedrock.definition.TerrainTextureDefinition -import net.minecraft.util.Identifier - -class BedrockResourceContext { - - var terrainTexture: Map = mapOf() - var blocks: Map = mapOf() - val textureImages: MutableMap = mutableMapOf() - -} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourcePackLoader.kt b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourcePackLoader.kt index b789b10..9cc35ba 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourcePackLoader.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/loader/BedrockResourcePackLoader.kt @@ -1,11 +1,23 @@ package net.easecation.bedrockloader.loader import net.easecation.bedrockloader.BedrockLoader +import net.easecation.bedrockloader.bedrock.block.component.ComponentGeometry +import net.easecation.bedrockloader.bedrock.block.component.ComponentMaterialInstances +import net.easecation.bedrockloader.render.RenderControllerWithClientEntity import net.easecation.bedrockloader.bedrock.definition.BlockResourceDefinition +import net.easecation.bedrockloader.bedrock.definition.EntityResourceDefinition +import net.easecation.bedrockloader.deserializer.BedrockPackContext +import net.easecation.bedrockloader.entity.EntityDataDriven +import net.easecation.bedrockloader.render.renderer.EntityDataDrivenRenderer import net.easecation.bedrockloader.java.definition.JavaBlockStatesDefinition import net.easecation.bedrockloader.java.definition.JavaMCMeta import net.easecation.bedrockloader.java.definition.JavaModelDefinition +import net.easecation.bedrockloader.render.BedrockGeometryModel import net.easecation.bedrockloader.util.GsonUtil +import net.fabricmc.api.EnvType +import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry +import net.fabricmc.loader.api.FabricLoader +import net.minecraft.entity.EntityType import net.minecraft.util.Identifier import java.io.File import java.io.FileWriter @@ -13,11 +25,54 @@ import javax.imageio.ImageIO class BedrockResourcePackLoader( private val javaResDir: File, - private val context: BedrockResourceContext + private val context: BedrockPackContext ) { private val initedNamespaces = mutableSetOf() + fun load() { + val env = FabricLoader.getInstance().environmentType + this.init() + // Geometry + context.resource.geometries.forEach { (key, value) -> + BedrockAddonsRegistry.models[key] = BedrockGeometryModel(value) + } + // Blocks + for (block in context.resource.blocks) { + val identifier = block.key + val dir = namespaceDir(identifier.namespace) + createBlockTextures(identifier, block.value.textures) + createBlockModel( + dir.resolve("models/block/${identifier.path}.json"), + identifier, + block.value.textures, + context.behavior.blocks[identifier]?.components?.minecraftGeometry, + context.behavior.blocks[identifier]?.components?.minecraftMaterialInstances, + ) + createItemModel(dir.resolve("models/item/${identifier.path}.json"), identifier) + createBlockState( + dir.resolve("blockstates/${identifier.path}.json"), + identifier, + context.behavior.blocks[identifier]?.components?.minecraftGeometry, + context.behavior.blocks[identifier]?.components?.minecraftMaterialInstances) + } + // Entity + for (entity in context.resource.entities) { + val identifier = entity.key + val clientEntity = entity.value.description + val entityType = BedrockAddonsRegistry.getOrRegisterEntityType(identifier) + // textures + createEntityTextures(identifier, clientEntity) + // renderer + if (env == EnvType.CLIENT) { + registerRenderController(clientEntity, identifier, entityType) + } + } + } + + /** + * 初始化临时资源包文件夹 + */ private fun init() { javaResDir.deleteRecursively() javaResDir.mkdirs() @@ -45,6 +100,9 @@ class BedrockResourcePackLoader( assetsDir.mkdirs() } + /** + * 获取命名空间对应的资源包文件夹,如果不存在则创建 + */ private fun namespaceDir(namespace: String) : File { val namespaceDir = javaResDir.resolve("assets").resolve(namespace) if (!namespaceDir.exists()) { @@ -76,28 +134,36 @@ class BedrockResourcePackLoader( if (!texturesBlock.exists()) { texturesBlock.mkdirs() } + val texturesEntity = textures.resolve("entity") + if (!texturesEntity.exists()) { + texturesEntity.mkdirs() + } initedNamespaces.add(namespace) } return namespaceDir } - fun load() { - this.init() - // Blocks - for (block in context.blocks) { - val identifier = block.key - val dir = this.namespaceDir(identifier.namespace) - createBlockTextures(dir, block.value.textures) - createBlockModel(dir.resolve("models/block/${identifier.path}.json"), identifier, block.value.textures) - createItemModel(dir.resolve("models/item/${identifier.path}.json"), identifier) - createBlockState(dir.resolve("blockstates/${identifier.path}.json"), identifier) + /** + * 创建一个方块状态文件,内部包含model路径(附带命名空间) + * 如果方块带模型,则详见createBlockModel,在创建的方块模型中,继承与自定义基岩版geometry模型,从而调用自定义渲染器和烘焙器来渲染基岩版模型 + */ + private fun createBlockState(file: File, identifier: Identifier, geometry: ComponentGeometry?, materialInstances: ComponentMaterialInstances?) { + // TODO block state + var model = "${identifier.namespace}:block/${identifier.path}" + // 带有模型的方块情况:通过行为包定义模型和贴图 + when (geometry) { + is ComponentGeometry.ComponentGeometrySimple -> { + model = geometry.identifier + } + is ComponentGeometry.ComponentGeometryFull -> { + model = geometry.identifier + // TODO bone_visibility + } + else -> {} } - } - - private fun createBlockState(file: File, identifier: Identifier) { val blockState = JavaBlockStatesDefinition( variants = mapOf( - "" to JavaBlockStatesDefinition.Variant("${identifier.namespace}:block/${identifier.path}") + "" to JavaBlockStatesDefinition.Variant(model) ) ) FileWriter(file).use { writer -> @@ -105,15 +171,19 @@ class BedrockResourcePackLoader( } } - private fun createBlockTextures(namespaceDir: File, textures: BlockResourceDefinition.Textures) { + /** + * 根据方块材质包中的定义,创建一个方块贴图文件(附带命名空间) + */ + private fun createBlockTextures(identifier: Identifier, textures: BlockResourceDefinition.Textures?) { + val namespaceDir = this.namespaceDir(identifier.namespace) when (textures) { is BlockResourceDefinition.Textures.TexturesAllFace -> { - val texture = context.terrainTexture[textures.all]?.textures + val texture = context.resource.terrainTexture[textures.all]?.textures if (texture == null || !texture.contains("textures/")) { BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: ${textures.all}") return } - val bedrockTexture = context.textureImages[texture] + val bedrockTexture = context.resource.textureImages[texture] if (bedrockTexture == null) { BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: ${textures.all}") return @@ -134,12 +204,12 @@ class BedrockResourcePackLoader( ) for ((_, textureKey) in directions) { textureKey?.let { - val texture = context.terrainTexture[it]?.textures + val texture = context.resource.terrainTexture[it]?.textures if (texture == null || !texture.contains("textures/")) { BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: $it") return } - val bedrockTexture = context.textureImages[texture] + val bedrockTexture = context.resource.textureImages[texture] if (bedrockTexture == null) { BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: $it") return @@ -151,39 +221,68 @@ class BedrockResourcePackLoader( } } } + else -> {} } } - private fun createBlockModel(file: File, identifier: Identifier, textures: BlockResourceDefinition.Textures) { + /** + * 根据方块材质包和行为包中的定义,创建一个方块模型文件(如果设定了模型,则应用模型;否则应用正常方块模型) + */ + private fun createBlockModel(file: File, identifier: Identifier, textures: BlockResourceDefinition.Textures?, geometry: ComponentGeometry?, materialInstances: ComponentMaterialInstances?) { + val model = JavaModelDefinition() - fun addTextureToMap(texturesMap: MutableMap, direction: String, textureKey: String, identifier: Identifier) { - val texture = context.terrainTexture[textureKey]?.textures - if (texture == null || !texture.contains("textures/")) { - BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: $textureKey") - return + if (geometry != null) { + return + } + // 带有模型的方块情况:通过行为包定义模型和贴图 + when (geometry) { + is ComponentGeometry.ComponentGeometrySimple -> { + model.parent = geometry.identifier + } + is ComponentGeometry.ComponentGeometryFull -> { + model.parent = geometry.identifier + // TODO bone_visibility + } + else -> { + when (textures) { + is BlockResourceDefinition.Textures.TexturesAllFace -> { + model.parent = "block/cube_all" + } + is BlockResourceDefinition.Textures.TexturesMultiFace -> { + model.parent = "block/cube" + } + else -> {} + } } - texturesMap[direction] = texture.replace("textures/", "${identifier.namespace}:") } + // 通过行为包定义了贴图 + materialInstances?.forEach { (key, value) -> + if (key == "*") { + value.texture?.let { texture -> + val javaTexture = context.resource.terrainTextureToJava(texture, identifier.namespace) + if (javaTexture != null) { + model.textures = mapOf("all" to javaTexture) // TODO 不确定是什么key... + } + } + } else { + BedrockLoader.logger.info("[BedrockResourcePackLoader] Material instance $key -> $value is not supported yet.") + } + } + + // 普通方块情况:通过材质包的blocks.json定义贴图 when (textures) { is BlockResourceDefinition.Textures.TexturesAllFace -> { - val texture = context.terrainTexture[textures.all]?.textures + val texture = context.resource.terrainTexture[textures.all]?.textures if (texture == null || !texture.contains("textures/")) { BedrockLoader.logger.warn("[BedrockResourcePackLoader] Block texture not found: ${textures.all}") return } - val model = JavaModelDefinition( - parent = "block/cube_all", - textures = mapOf( - "all" to texture.replace("textures/", "${identifier.namespace}:") - ) - ) - FileWriter(file).use { writer -> - GsonUtil.GSON.toJson(model, writer) - } + context.resource.terrainTextureToJava(textures.all, identifier.namespace)?.let { model.textures = mapOf("all" to it) } } is BlockResourceDefinition.Textures.TexturesMultiFace -> { - val texturesMap = mutableMapOf() + val texturesMap = mutableMapOf() + val directions = mapOf( "up" to textures.up, "down" to textures.down, @@ -195,28 +294,74 @@ class BedrockResourcePackLoader( for ((direction, textureKey) in directions) { textureKey?.let { - addTextureToMap(texturesMap, direction, it, identifier) + val texture = context.resource.terrainTextureToJava(it, identifier.namespace) + if (texture != null) { + texturesMap[direction] = texture + } } } - val model = JavaModelDefinition( - parent = "block/cube", - textures = texturesMap - ) - FileWriter(file).use { writer -> - GsonUtil.GSON.toJson(model, writer) - } + model.textures = texturesMap } + else -> {} + } + + FileWriter(file).use { writer -> + GsonUtil.GSON.toJson(model, writer) } } + /** + * 直接创建一个继承于对应方块模型的物品模型 + */ private fun createItemModel(file: File, identifier: Identifier) { val model = JavaModelDefinition( - parent = "${identifier.namespace}:block/${identifier.path}" + parent = Identifier(identifier.namespace, "block/${identifier.path}").toString() ) FileWriter(file).use { writer -> GsonUtil.GSON.toJson(model, writer) } } + /** + * 从ClientEntity读取需要的贴图,然后将对应的贴图文件保存到java材质包中(对应命名空间) + */ + private fun createEntityTextures(identifier: Identifier, clientEntity: EntityResourceDefinition.ClientEntityDescription) { + val namespaceDir = this.namespaceDir(identifier.namespace) + clientEntity.textures?.forEach { (_, texture) -> + val bedrockTexture = context.resource.textureImages[texture] + if (bedrockTexture == null) { + BedrockLoader.logger.warn("[BedrockResourcePackLoader] Entity texture not found: $texture") + return + } + val file = namespaceDir.resolve(texture + "." + bedrockTexture.type.getExtension()) + file.parentFile.mkdirs() + bedrockTexture.image.let { image -> + ImageIO.write(image, file.extension, file) + } + } + } + + /** + * 从ClientEntity读取需要的渲染控制器,然后注册到Java版的渲染控制器中 + */ + private fun registerRenderController(clientEntity: EntityResourceDefinition.ClientEntityDescription, identifier: Identifier, entityType: EntityType) { + clientEntity.render_controllers?.let { controllers -> + if (controllers.isNotEmpty()) { + if (controllers.size > 1) { + BedrockLoader.logger.warn("[BedrockResourcePackLoader] Entity $identifier has more than one render controller, only the first one will be used.") + } + val controller = controllers[0] + val renderController = context.resource.renderControllers[controller] + if (renderController != null) { + val renderControllerInstance = RenderControllerWithClientEntity(renderController, clientEntity) + EntityRendererRegistry.register(entityType) { context -> EntityDataDrivenRenderer.create(context, renderControllerInstance, 0.5f) } + BedrockLoader.logger.debug("[BedrockResourcePackLoader] Entity {} render controller registered: {}", identifier, controller) + } else { + BedrockLoader.logger.warn("[BedrockResourcePackLoader] Entity $identifier render controller not found: $controller") + } + } + } + } + } \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/BedrockGeometryModel.kt b/src/main/kotlin/net/easecation/bedrockloader/render/BedrockGeometryModel.kt new file mode 100644 index 0000000..b85986a --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/BedrockGeometryModel.kt @@ -0,0 +1,135 @@ +package net.easecation.bedrockloader.render + +import net.easecation.bedrockloader.BedrockLoader +import net.easecation.bedrockloader.bedrock.definition.GeometryDefinition +import net.easecation.bedrockloader.entity.EntityDataDriven +import net.easecation.bedrockloader.render.model.ModelPart +import net.easecation.bedrockloader.render.model.TexturedModelData +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment +import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh +import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel +import net.minecraft.block.BlockState +import net.minecraft.client.render.VertexConsumer +import net.minecraft.client.render.entity.model.EntityModel +import net.minecraft.client.render.model.* +import net.minecraft.client.render.model.json.JsonUnbakedModel +import net.minecraft.client.render.model.json.ModelOverrideList +import net.minecraft.client.render.model.json.ModelTransformation +import net.minecraft.client.texture.Sprite +import net.minecraft.client.texture.SpriteAtlasTexture +import net.minecraft.client.util.SpriteIdentifier +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.item.ItemStack +import net.minecraft.util.Identifier +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.Direction +import net.minecraft.world.BlockRenderView +import java.util.* +import java.util.function.Supplier + + +@Environment(EnvType.CLIENT) +class BedrockGeometryModel( + private val bedrockModel: GeometryDefinition.Model, +) : EntityModel(), UnbakedModel, BakedModel, FabricBakedModel { + + private val DEFAULT_BLOCK_MODEL = Identifier("minecraft:block/block") + private val SPRITE_IDS = arrayOf(SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, Identifier("minecraft:block/furnace_front_on")), + SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, Identifier("minecraft:block/furnace_top")) + ) + private val SPRITES = arrayOfNulls(2) + + private var transformation: ModelTransformation? = null + private val modelPart: ModelPart = getTexturedModelData().createModel() + private var mesh: Mesh? = null + + private fun getTexturedModelData(): TexturedModelData { + if (bedrockModel.description.texture_width == null || bedrockModel.description.texture_height == null) throw IllegalStateException("[BedrockGeometryModel] Model has no texture size") + if (bedrockModel.bones == null) throw IllegalStateException("[BedrockGeometryModel] Model has no bones") + BedrockRenderUtil.bedrockBonesToJavaModelData(bedrockModel.bones).let { modelData -> + return TexturedModelData.of(modelData, bedrockModel.description.texture_width, bedrockModel.description.texture_height) + } + } + + override fun getModelDependencies(): Collection { + return emptyList() // 模型不依赖于其他模型。 + } + + override fun getTextureDependencies(unbakedModelGetter: java.util.function.Function?, unresolvedTextureReferences: MutableSet>?): MutableList { + val map = mutableListOf() + for (i in 0..1) { + map.add(SPRITE_IDS[i]) + } + return map // 本模型(以及其模型依赖,依赖的依赖,等)依赖的纹理。 TODO + } + + override fun bake(loader: ModelLoader, textureGetter: java.util.function.Function, rotationContainer: ModelBakeSettings?, modelId: Identifier?): BakedModel { + BedrockLoader.logger.info("Baking model... $modelId ${bedrockModel.description.identifier}") + // 加载默认方块模型 + val defaultBlockModel = loader.getOrLoadModel(DEFAULT_BLOCK_MODEL) as JsonUnbakedModel + // 获取 ModelTransformation + transformation = defaultBlockModel.transformations + // 获得sprites + for (i in 0..1) { + SPRITES[i] = textureGetter.apply(SPRITE_IDS[i]) + } + mesh = BedrockRenderUtil.bakeModelPartToMesh(modelPart) + return this + } + + override fun getQuads(state: BlockState?, face: Direction?, random: Random?): List { + return emptyList() // 不需要,因为我们使用的是 FabricBakedModel + } + + override fun useAmbientOcclusion(): Boolean { + return true // 环境光遮蔽:我们希望方块在有临近方块时显示阴影 + } + + override fun isBuiltin(): Boolean { + return false + } + + override fun hasDepth(): Boolean { + return false + } + + override fun isSideLit(): Boolean { + return true + } + + override fun getParticleSprite(): Sprite { + return SPRITES[1]!! // 方块被破坏时产生的颗粒,使用furnace_top + } + + override fun isVanillaAdapter(): Boolean { + return false // false 以触发 FabricBakedModel 渲染 + } + + override fun emitBlockQuads(blockView: BlockRenderView?, state: BlockState?, pos: BlockPos?, randomSupplier: Supplier?, context: net.fabricmc.fabric.api.renderer.v1.render.RenderContext?) { + context?.meshConsumer()?.accept(mesh) + } + + override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier?, context: net.fabricmc.fabric.api.renderer.v1.render.RenderContext) { + context.meshConsumer().accept(mesh); + } + + override fun getTransformation(): ModelTransformation { + return transformation!! + } + + override fun getOverrides(): ModelOverrideList { + return ModelOverrideList.EMPTY + } + + // EntityModel methods + + override fun render(matrices: MatrixStack, vertices: VertexConsumer, light: Int, overlay: Int, red: Float, green: Float, blue: Float, alpha: Float) { + modelPart.render(matrices, vertices, light, overlay, red, green, blue, alpha) + } + + override fun setAngles(entity: EntityDataDriven?, limbAngle: Float, limbDistance: Float, animationProgress: Float, headYaw: Float, headPitch: Float) { + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/BedrockModelProvider.kt b/src/main/kotlin/net/easecation/bedrockloader/render/BedrockModelProvider.kt new file mode 100644 index 0000000..44e4739 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/BedrockModelProvider.kt @@ -0,0 +1,15 @@ +package net.easecation.bedrockloader.render + +import net.easecation.bedrockloader.loader.BedrockAddonsRegistry +import net.fabricmc.fabric.api.client.model.ModelProviderContext +import net.fabricmc.fabric.api.client.model.ModelResourceProvider +import net.minecraft.client.render.model.UnbakedModel +import net.minecraft.util.Identifier + +object BedrockModelProvider : ModelResourceProvider { + + override fun loadModelResource(resourceId: Identifier, context: ModelProviderContext): UnbakedModel? { + return BedrockAddonsRegistry.models[resourceId.path]?.let { return it } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/BedrockRenderUtil.kt b/src/main/kotlin/net/easecation/bedrockloader/render/BedrockRenderUtil.kt new file mode 100644 index 0000000..fe0d350 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/BedrockRenderUtil.kt @@ -0,0 +1,74 @@ +package net.easecation.bedrockloader.render + +import net.easecation.bedrockloader.bedrock.definition.GeometryDefinition +import net.easecation.bedrockloader.render.model.* +import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh +import net.minecraft.client.util.math.MatrixStack + +object BedrockRenderUtil { + + fun bedrockBonesToJavaModelData(bones: List) : ModelData { + var boneCount = 0 + fun addBoneToModelData(bone: GeometryDefinition.Bone, parentPartData: ModelPartData) { + val pivotTransform = ModelTransform.of( + bone.pivot?.get(0) ?: 0f, + bone.pivot?.get(1) ?: 0f, + bone.pivot?.get(2) ?: 0f, + bone.rotation?.get(0) ?: 0f, + bone.rotation?.get(1) ?: 0f, + bone.rotation?.get(2) ?: 0f + ) + val bonePartData = parentPartData.addChild(bone.name, ModelPartBuilder.create().mirrored(bone.mirror == true), pivotTransform) + + bone.cubes?.forEach { cube -> + val cubeBuilder = ModelPartBuilder.create().mirrored(cube.mirror == true) + cube.uv?.let { + when (it) { + is GeometryDefinition.Uv.UvBox -> cubeBuilder.uv(it.uv?.get(0) ?: 0, it.uv?.get(1) ?: 0) + is GeometryDefinition.Uv.UvPerFace -> {} // TODO 暂不支持逐面UV + } + } + cube.origin?.let { cubeBuilder + .cuboid( + it[0], it[1], it[2], + cube.size?.get(0) ?: 0f, + cube.size?.get(1) ?: 0f, + cube.size?.get(2) ?: 0f + ) + } + bonePartData.addChild("cube${boneCount++}", cubeBuilder, + ModelTransform.of( + cube.pivot?.get(0) ?: 0f, cube.pivot?.get(1) ?: 0f, cube.pivot?.get(2) ?: 0f, + cube.rotation?.get(0) ?: 0f, cube.rotation?.get(1) ?: 0f, cube.rotation?.get(2) ?: 0f + ) + ) + } + + bones.filter { it.parent == bone.name }.forEach { childBone -> + addBoneToModelData(childBone, bonePartData) + } + } + + val modelData = ModelData() + val rootPartData = modelData.root + for (bone in bones) { + if (bone.parent == null) { + addBoneToModelData(bone, rootPartData) + } + } + return modelData + } + + /** + * Converts a ModelPart to a Mesh using a MeshBuilder. + * @param modelPart The ModelPart to convert. + * @return The created Mesh. + */ + fun bakeModelPartToMesh(modelPart: ModelPart): Mesh { + val matrixStack = MatrixStack() + val vertices = MeshBuilderVertexConsumer() + modelPart.render(matrixStack, vertices, 1, 1) + return vertices.build() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/MeshBuilderVertexConsumer.kt b/src/main/kotlin/net/easecation/bedrockloader/render/MeshBuilderVertexConsumer.kt new file mode 100644 index 0000000..0b1b7f8 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/MeshBuilderVertexConsumer.kt @@ -0,0 +1,145 @@ +package net.easecation.bedrockloader.render + +import net.easecation.bedrockloader.BedrockLoader +import net.fabricmc.fabric.api.renderer.v1.Renderer +import net.fabricmc.fabric.api.renderer.v1.RendererAccess +import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh +import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder +import net.minecraft.client.render.VertexConsumer +import net.minecraft.client.render.VertexFormats +import net.minecraft.client.render.model.BakedQuad +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.util.math.Vec3f +import net.minecraft.util.math.Vector4f +import org.lwjgl.system.MemoryStack + +class MeshBuilderVertexConsumer : VertexIndexedVertexConsumer { + + private val renderer: Renderer = RendererAccess.INSTANCE.renderer!! + private val meshBuilder: MeshBuilder = renderer.meshBuilder() + private val emitter = meshBuilder.emitter + + private var vertexIndex = 0 + + override fun quad(matrixEntry: MatrixStack.Entry, quad: BakedQuad, brightnesses: FloatArray, red: Float, green: Float, blue: Float, lights: IntArray, overlay: Int, useQuadColorData: Boolean) { + val fs = floatArrayOf(brightnesses[0], brightnesses[1], brightnesses[2], brightnesses[3]) + val `is` = intArrayOf(lights[0], lights[1], lights[2], lights[3]) + val js = quad.vertexData + val vec3i = quad.face.vector + val vec3f = Vec3f(vec3i.x.toFloat(), vec3i.y.toFloat(), vec3i.z.toFloat()) + val matrix4f = matrixEntry.positionMatrix + vec3f.transform(matrixEntry.normalMatrix) + val i = 8 + val j = js.size / 8 + MemoryStack.stackPush().use { memoryStack -> + val byteBuffer = memoryStack.malloc(VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL.vertexSize) + val intBuffer = byteBuffer.asIntBuffer() + for (k in 0 until j) { + var q: Float + var p: Float + var o: Float + var n: Float + var m: Float + intBuffer.clear() + intBuffer.put(js, k * 8, 8) + val f = byteBuffer.getFloat(0) + val g = byteBuffer.getFloat(4) + val h = byteBuffer.getFloat(8) + if (useQuadColorData) { + val l = (byteBuffer[12].toInt() and 0xFF).toFloat() / 255.0f + m = (byteBuffer[13].toInt() and 0xFF).toFloat() / 255.0f + n = (byteBuffer[14].toInt() and 0xFF).toFloat() / 255.0f + o = l * fs[k] * red + p = m * fs[k] * green + q = n * fs[k] * blue + } else { + o = fs[k] * red + p = fs[k] * green + q = fs[k] * blue + } + val r = `is`[k] + m = byteBuffer.getFloat(16) + n = byteBuffer.getFloat(20) + val vector4f = Vector4f(f, g, h, 1.0f) + vector4f.transform(matrix4f) + emitter.pos(k, vector4f.x, vector4f.y, vector4f.z) + // emitter.spriteBake(k, ) // TODO texture + emitter.spriteColor(k, -1, -1, -1, -1) + emitter.spriteColor(k, (o * 255).toInt(), (p * 255).toInt(), (q * 255).toInt(), 255) + emitter.normal(k, vec3f) + emitter.lightmap(k, r) + this.vertex(vector4f.x, vector4f.y, vector4f.z, o, p, q, 1.0f, m, n, overlay, r, vec3f.x, vec3f.y, vec3f.z) + emitter.emit() + } + } + } + + override fun vertex(x: Double, y: Double, z: Double): VertexConsumer { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.vertex($x, $y, $z)") + emitter.pos(vertexIndex, x.toFloat(), y.toFloat(), z.toFloat()) + return this + } + + override fun color(red: Int, green: Int, blue: Int, alpha: Int): VertexConsumer { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.color($red, $green, $blue, $alpha)") + emitter.spriteColor(0, red, green, blue, alpha) + return this + } + + override fun texture(u: Float, v: Float): VertexConsumer { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.texture($u, $v)") + emitter.sprite(vertexIndex, 0, u, v) + return this + } + + override fun overlay(u: Int, v: Int): VertexConsumer { + BedrockLoader.logger.info("MeshBuilderVertexConsumer.overlay($u, $v)") + return this + } + + override fun overlay(overlay: Int): VertexConsumer { + BedrockLoader.logger.info("MeshBuilderVertexConsumer.overlay($overlay)") + return this + } + + override fun light(u: Int, v: Int): VertexConsumer { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.light($u, $v)") + emitter.lightmap(vertexIndex, v) + return this + } + + override fun light(light: Int): VertexConsumer { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.light($light)") + emitter.lightmap(vertexIndex, light) + return this + } + + override fun normal(x: Float, y: Float, z: Float): VertexConsumer { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.normal($x, $y, $z)") + emitter.normal(vertexIndex, x, y, z) + return this + } + + override fun next() { + // BedrockLoader.logger.info("MeshBuilderVertexConsumer.next()") + emitter.emit() + } + + override fun vertexIndex(index: Int) { + vertexIndex = index + } + + override fun fixedColor(red: Int, green: Int, blue: Int, alpha: Int) { + BedrockLoader.logger.info("MeshBuilderVertexConsumer.fixedColor($red, $green, $blue, $alpha)") + emitter.spriteColor(vertexIndex, red, green, blue, alpha) + } + + override fun unfixColor() { + BedrockLoader.logger.info("MeshBuilderVertexConsumer.unfixColor()") + emitter.spriteColor(vertexIndex, -1, -1, -1, -1) + } + + fun build(): Mesh { + return meshBuilder.build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/RenderControllerWithClientEntity.kt b/src/main/kotlin/net/easecation/bedrockloader/render/RenderControllerWithClientEntity.kt new file mode 100644 index 0000000..fe0d67d --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/RenderControllerWithClientEntity.kt @@ -0,0 +1,44 @@ +package net.easecation.bedrockloader.render + +import net.easecation.bedrockloader.bedrock.BedrockTexturePath +import net.easecation.bedrockloader.bedrock.definition.EntityRenderControllerDefinition +import net.easecation.bedrockloader.bedrock.definition.EntityResourceDefinition +import net.easecation.bedrockloader.entity.EntityDataDriven +import net.easecation.bedrockloader.loader.BedrockAddonsRegistry + +/** + * This is a combination of the bedrock RenderController and the bedrock ClientEntity render controller instance + * (because the render controller needs to be combined with the client entity to get the final rendered model and texture) + * 这是一个结合了基岩版的 渲染控制器 和 客户端实体 的动画控制器实例(因为渲染控制器需要结合客户端实体才能得到最终渲染的模型和材质) + * @param renderController 渲染控制器 + * @param clientEntity 客户端实体 + */ +class RenderControllerWithClientEntity( + private val renderController: EntityRenderControllerDefinition.RenderController, + private val clientEntity: EntityResourceDefinition.ClientEntityDescription +) { + + fun getModel() : BedrockGeometryModel? { + val rawGeometry = renderController.geometry + if (rawGeometry.startsWith("Geometry.") || rawGeometry.startsWith("geometry.")) { + val geo = rawGeometry.substring(9) + clientEntity.geometry?.get(geo)?.let { geometry -> + return BedrockAddonsRegistry.models[geometry] + } + } + return null + } + + fun getTextures(entity: EntityDataDriven) : List? { + // TODO 通过molang,结合entity中的数据来动态获取材质 + return renderController.textures?.map { + if (it.startsWith("Texture.") || it.startsWith("texture.")) { + val texture = it.substring(8) + clientEntity.textures?.get(texture) + } else { + null + } + }?.filterNotNull() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/VertexIndexedVertexConsumer.kt b/src/main/kotlin/net/easecation/bedrockloader/render/VertexIndexedVertexConsumer.kt new file mode 100644 index 0000000..05c8a0f --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/VertexIndexedVertexConsumer.kt @@ -0,0 +1,9 @@ +package net.easecation.bedrockloader.render + +import net.minecraft.client.render.VertexConsumer + +interface VertexIndexedVertexConsumer : VertexConsumer { + + fun vertexIndex(index: Int) + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/Dilation.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/Dilation.kt new file mode 100644 index 0000000..56d994c --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/Dilation.kt @@ -0,0 +1,29 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +/** + * @implNote This should be in same package as [ModelCuboidData] as + * its package private static fields are accessed by it. + */ +@Environment(value = EnvType.CLIENT) +class Dilation(val radiusX: Float, val radiusY: Float, val radiusZ: Float) { + constructor(radius: Float) : this(radius, radius, radius) + + fun add(radius: Float): Dilation { + return Dilation(this.radiusX + radius, this.radiusY + radius, this.radiusZ + radius) + } + + fun add(radiusX: Float, radiusY: Float, radiusZ: Float): Dilation { + return Dilation(this.radiusX + radiusX, this.radiusY + radiusY, this.radiusZ + radiusZ) + } + + companion object { + val NONE: Dilation = Dilation(0.0f) + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/Model.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/Model.kt new file mode 100644 index 0000000..09080a4 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/Model.kt @@ -0,0 +1,35 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment +import net.minecraft.client.render.RenderLayer +import net.minecraft.client.render.VertexConsumer +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.util.Identifier +import java.util.function.Function + +/** + * Represents a dynamic model which has its own render layers and custom rendering. + */ +@Environment(value = EnvType.CLIENT) +abstract class Model(protected val layerFactory: Function) { + /** + * {@return the render layer for the corresponding texture} + * + * @param texture the texture used for the render layer + */ + fun getLayer(texture: Identifier): RenderLayer { + return layerFactory.apply(texture) + } + + /** + * Renders the model. + * + * @param light the lightmap coordinates used for this model rendering + */ + abstract fun render(var1: MatrixStack?, var2: VertexConsumer?, var3: Int, var4: Int, var5: Float, var6: Float, var7: Float, var8: Float) +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelCuboidData.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelCuboidData.kt new file mode 100644 index 0000000..a535628 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelCuboidData.kt @@ -0,0 +1,23 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment +import net.minecraft.client.util.math.Vector2f +import net.minecraft.util.math.Vec3f + +@Environment(value = EnvType.CLIENT) +class ModelCuboidData(private val name: String?, textureX: Float, textureY: Float, offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float, extra: Dilation, private val mirror: Boolean, textureScaleX: Float, textureScaleY: Float) { + private val offset = Vec3f(offsetX, offsetY, offsetZ) + private val dimensions = Vec3f(sizeX, sizeY, sizeZ) + private val extraSize = extra + private val textureUV = Vector2f(textureX, textureY) + private val textureScale = Vector2f(textureScaleX, textureScaleY) + + fun createCuboid(textureWidth: Int, textureHeight: Int): ModelPart.Cuboid { + return ModelPart.Cuboid(textureUV.x.toInt(), textureUV.y.toInt(), offset.x, offset.y, offset.z, dimensions.x, dimensions.y, dimensions.z, extraSize.radiusX, extraSize.radiusY, extraSize.radiusZ, this.mirror, textureWidth.toFloat() * textureScale.x, textureHeight.toFloat() * textureScale.y) + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelData.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelData.kt new file mode 100644 index 0000000..5e0bcd6 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelData.kt @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import com.google.common.collect.ImmutableList +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +@Environment(value = EnvType.CLIENT) +class ModelData { + val root: ModelPartData = ModelPartData(ImmutableList.of(), ModelTransform.Companion.NONE) +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPart.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPart.kt new file mode 100644 index 0000000..7ffce16 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPart.kt @@ -0,0 +1,281 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.easecation.bedrockloader.render.VertexIndexedVertexConsumer +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment +import net.minecraft.client.render.VertexConsumer +import net.minecraft.client.util.math.MatrixStack +import net.minecraft.util.math.* +import java.util.* +import java.util.stream.Stream + +@Environment(value = EnvType.CLIENT) +class ModelPart(private val cuboids: List, private val children: Map) { + var pivotX: Float = 0f + var pivotY: Float = 0f + var pivotZ: Float = 0f + var pitch: Float = 0f + var yaw: Float = 0f + var roll: Float = 0f + var visible: Boolean = true + + var transform: ModelTransform + get() = ModelTransform.of(this.pivotX, this.pivotY, this.pivotZ, this.pitch, this.yaw, this.roll) + set(rotationData) { + this.pivotX = rotationData.pivotX + this.pivotY = rotationData.pivotY + this.pivotZ = rotationData.pivotZ + this.pitch = rotationData.pitch + this.yaw = rotationData.yaw + this.roll = rotationData.roll + } + + fun copyTransform(part: ModelPart) { + this.pitch = part.pitch + this.yaw = part.yaw + this.roll = part.roll + this.pivotX = part.pivotX + this.pivotY = part.pivotY + this.pivotZ = part.pivotZ + } + + fun getChild(name: String): ModelPart { + val modelPart = children[name] ?: throw NoSuchElementException("Can't find part $name") + return modelPart + } + + fun setPivot(x: Float, y: Float, z: Float) { + this.pivotX = x + this.pivotY = y + this.pivotZ = z + } + + fun setAngles(pitch: Float, yaw: Float, roll: Float) { + this.pitch = pitch + this.yaw = yaw + this.roll = roll + } + + @JvmOverloads + fun render(matrices: MatrixStack, vertices: VertexConsumer, light: Int, overlay: Int, red: Float = 1.0f, green: Float = 1.0f, blue: Float = 1.0f, alpha: Float = 1.0f) { + if (!this.visible) { + return + } + if (cuboids.isEmpty() && children.isEmpty()) { + return + } + matrices.push() + this.rotate(matrices) + this.renderCuboids(matrices.peek(), vertices, light, overlay, red, green, blue, alpha) + for (modelPart in children.values) { + modelPart.render(matrices, vertices, light, overlay, red, green, blue, alpha) + } + matrices.pop() + } + + fun forEachCuboid(matrices: MatrixStack, consumer: CuboidConsumer) { + this.forEachCuboid(matrices, consumer, "") + } + + private fun forEachCuboid(matrices: MatrixStack, consumer: CuboidConsumer, path: String) { + if (cuboids.isEmpty() && children.isEmpty()) { + return + } + matrices.push() + this.rotate(matrices) + val entry = matrices.peek() + for (i in cuboids.indices) { + consumer.accept(entry, path, i, cuboids[i]) + } + val string = "$path/" + children.forEach { (name: String, part: ModelPart) -> part.forEachCuboid(matrices, consumer, string + name) } + matrices.pop() + } + + fun rotate(matrices: MatrixStack) { + matrices.translate((this.pivotX / 16.0f).toDouble(), (this.pivotY / 16.0f).toDouble(), (this.pivotZ / 16.0f).toDouble()) + if (this.roll != 0.0f) { + matrices.multiply(Vec3f.POSITIVE_Z.getRadialQuaternion(this.roll)) + } + if (this.yaw != 0.0f) { + matrices.multiply(Vec3f.POSITIVE_Y.getRadialQuaternion(this.yaw)) + } + if (this.pitch != 0.0f) { + matrices.multiply(Vec3f.POSITIVE_X.getRadialQuaternion(this.pitch)) + } + } + + private fun renderCuboids(entry: MatrixStack.Entry, vertexConsumer: VertexConsumer, light: Int, overlay: Int, red: Float, green: Float, blue: Float, alpha: Float) { + for (cuboid in this.cuboids) { + cuboid.renderCuboid(entry, vertexConsumer, light, overlay, red, green, blue, alpha) + } + } + + private fun renderCuboids(entry: MatrixStack.Entry, vertexConsumer: VertexIndexedVertexConsumer, light: Int, overlay: Int, red: Float, green: Float, blue: Float, alpha: Float) { + for (cuboid in this.cuboids) { + cuboid.renderCuboid(entry, vertexConsumer, light, overlay, red, green, blue, alpha) + } + } + + fun getRandomCuboid(random: Random): Cuboid { + return cuboids[random.nextInt(cuboids.size)] + } + + val isEmpty: Boolean + get() = cuboids.isEmpty() + + fun traverse(): Stream { + return Stream.concat(Stream.of(this), children.values.stream().flatMap { obj: ModelPart -> obj.traverse() }) + } + + @Environment(value = EnvType.CLIENT) + fun interface CuboidConsumer { + fun accept(var1: MatrixStack.Entry?, var2: String?, var3: Int, var4: Cuboid?) + } + + @Environment(value = EnvType.CLIENT) + data class FaceUV( + val north: UVMapping, + val east: UVMapping, + val south: UVMapping, + val west: UVMapping, + val up: UVMapping, + val down: UVMapping + ) + + @Environment(value = EnvType.CLIENT) + data class UVMapping( + val uv: Pair, + val uvSize: Pair + ) + + @Environment(value = EnvType.CLIENT) + class Cuboid(u: Int, v: Int, x: Float, y: Float, z: Float, sizeX: Float, sizeY: Float, sizeZ: Float, extraX: Float, extraY: Float, extraZ: Float, mirror: Boolean, textureWidth: Float, textureHeight: Float) { + private val sides: Array + val minX: Float + val minY: Float + val minZ: Float + val maxX: Float + val maxY: Float + val maxZ: Float + + init { + this.minX = x - sizeX / 2 + this.minY = y + this.minZ = z - sizeZ / 2 + this.maxX = x + sizeX / 2 + this.maxY = y + sizeY + this.maxZ = z + sizeZ / 2 + this.sides = arrayOfNulls(6) + var adjustedMinX = this.minX - extraX + var adjustedMinY = this.minY - extraY + var adjustedMinZ = this.minZ - extraZ + var adjustedMaxX = this.maxX + extraX + var adjustedMaxY = this.maxY + extraY + var adjustedMaxZ = this.maxZ + extraZ + if (mirror) { + val tempX = adjustedMaxX + adjustedMaxX = adjustedMinX + adjustedMinX = tempX + } + val vertexBottomNW = Vertex(adjustedMinX, adjustedMinY, adjustedMinZ, 0.0f, 0.0f) + val vertexBottomNE = Vertex(adjustedMaxX, adjustedMinY, adjustedMinZ, 0.0f, 8.0f) + val vertexTopNE = Vertex(adjustedMaxX, adjustedMaxY, adjustedMinZ, 8.0f, 8.0f) + val vertexTopNW = Vertex(adjustedMinX, adjustedMaxY, adjustedMinZ, 8.0f, 0.0f) + val vertexBottomSW = Vertex(adjustedMinX, adjustedMinY, adjustedMaxZ, 0.0f, 0.0f) + val vertexBottomSE = Vertex(adjustedMaxX, adjustedMinY, adjustedMaxZ, 0.0f, 8.0f) + val vertexTopSE = Vertex(adjustedMaxX, adjustedMaxY, adjustedMaxZ, 8.0f, 8.0f) + val vertexTopSW = Vertex(adjustedMinX, adjustedMaxY, adjustedMaxZ, 8.0f, 0.0f) + + val textureOriginU = u.toFloat() + val textureEndUZ = textureOriginU + sizeZ + val textureEndUX = textureOriginU + sizeZ + sizeX + val textureDoubleEndUX = textureOriginU + sizeZ + sizeX + sizeX + val textureWrapU = textureOriginU + sizeZ + sizeX + sizeZ + val textureFullWrapU = textureOriginU + sizeZ + sizeX + sizeZ + sizeX + val textureOriginV = v.toFloat() + val textureEndVZ = textureOriginV + sizeZ + val textureEndVY = textureOriginV + sizeZ + sizeY + + sides[2] = Quad(arrayOf(vertexBottomSE, vertexBottomSW, vertexBottomNW, vertexBottomNE), textureEndUZ, textureOriginV, textureEndUX, textureEndVZ, textureWidth, textureHeight, mirror, Direction.DOWN) + sides[3] = Quad(arrayOf(vertexTopNE, vertexTopNW, vertexTopSW, vertexTopSE), textureEndUX, textureEndVZ, textureDoubleEndUX, textureOriginV, textureWidth, textureHeight, mirror, Direction.UP) + sides[1] = Quad(arrayOf(vertexBottomNW, vertexBottomSW, vertexTopSW, vertexTopNW), textureOriginU, textureEndVZ, textureEndUZ, textureEndVY, textureWidth, textureHeight, mirror, Direction.WEST) + sides[4] = Quad(arrayOf(vertexBottomNE, vertexBottomNW, vertexTopNW, vertexTopNE), textureEndUZ, textureEndVZ, textureEndUX, textureEndVY, textureWidth, textureHeight, mirror, Direction.NORTH) + sides[0] = Quad(arrayOf(vertexBottomSE, vertexBottomNE, vertexTopNE, vertexTopSE), textureEndUX, textureEndVZ, textureWrapU, textureEndVY, textureWidth, textureHeight, mirror, Direction.EAST) + sides[5] = Quad(arrayOf(vertexBottomSW, vertexBottomSE, vertexTopSE, vertexTopSW), textureWrapU, textureEndVZ, textureFullWrapU, textureEndVY, textureWidth, textureHeight, mirror, Direction.SOUTH) + } + + fun renderCuboid(entry: MatrixStack.Entry, vertexConsumer: VertexConsumer, light: Int, overlay: Int, red: Float, green: Float, blue: Float, alpha: Float) { + val transformMatrix4f = entry.positionMatrix // 变换矩阵 + val normalMatrix3f = entry.normalMatrix // 法相矩阵 + for (quad in this.sides) { + val directionUnitVector = quad!!.direction.copy() + directionUnitVector.transform(normalMatrix3f) + val directionUnitVectorX = directionUnitVector.x + val directionUnitVectorY = directionUnitVector.y + val directionUnitVectorZ = directionUnitVector.z + val vertices = quad.vertices + var vertexIndex = 0 + val verticesLength = vertices.size + while (vertexIndex < verticesLength) { + val vertex = vertices[vertexIndex] + val vertexPosX = vertex.pos.x / 16.0f + val vertexPosY = vertex.pos.y / 16.0f + val vertexPosZ = vertex.pos.z / 16.0f + val quaternion = Vector4f(vertexPosX, vertexPosY, vertexPosZ, 1.0f) // 普通坐标转为四元数的 + quaternion.transform(transformMatrix4f) + if (vertexConsumer is VertexIndexedVertexConsumer) { + vertexConsumer.vertexIndex(vertexIndex) + } + vertexConsumer.vertex( + quaternion.x, quaternion.y, quaternion.z, + red, green, blue, alpha, + vertex.u, vertex.v, + overlay, light, + directionUnitVectorX, directionUnitVectorY, directionUnitVectorZ + ) + vertexIndex++ + } + } + } + } + + @Environment(value = EnvType.CLIENT) + internal class Vertex(val pos: Vec3f, val u: Float, val v: Float) { + constructor(x: Float, y: Float, z: Float, u: Float, v: Float) : this(Vec3f(x, y, z), u, v) + + fun remap(u: Float, v: Float): Vertex { + return Vertex(this.pos, u, v) + } + } + + @Environment(value = EnvType.CLIENT) + internal class Quad(val vertices: Array, u1: Float, v1: Float, u2: Float, v2: Float, squishU: Float, squishV: Float, flip: Boolean, direction: Direction) { + val direction: Vec3f + + init { + val f = 0.0f / squishU + val g = 0.0f / squishV + vertices[0] = vertices[0].remap(u2 / squishU - f, v1 / squishV + g) + vertices[1] = vertices[1].remap(u1 / squishU + f, v1 / squishV + g) + vertices[2] = vertices[2].remap(u1 / squishU + f, v2 / squishV - g) + vertices[3] = vertices[3].remap(u2 / squishU - f, v2 / squishV - g) + if (flip) { + val i = vertices.size + for (j in 0 until i / 2) { + val vertex = vertices[j] + vertices[j] = vertices[i - 1 - j] + vertices[i - 1 - j] = vertex + } + } + this.direction = direction.unitVector + if (flip) { + this.direction.multiplyComponentwise(-1.0f, 1.0f, 1.0f) + } + } + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartBuilder.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartBuilder.kt new file mode 100644 index 0000000..cd5f9d4 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartBuilder.kt @@ -0,0 +1,82 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import com.google.common.collect.ImmutableList +import com.google.common.collect.Lists +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +@Environment(value = EnvType.CLIENT) +class ModelPartBuilder { + private val cuboidData: MutableList = Lists.newArrayList() + private var textureX = 0 + private var textureY = 0 + private var mirror = false + + fun uv(textureX: Int, textureY: Int): ModelPartBuilder { + this.textureX = textureX + this.textureY = textureY + return this + } + + @JvmOverloads + fun mirrored(mirror: Boolean = true): ModelPartBuilder { + this.mirror = mirror + return this + } + + fun cuboid(name: String?, offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Int, sizeY: Int, sizeZ: Int, extra: Dilation, textureX: Int, textureY: Int): ModelPartBuilder { + this.uv(textureX, textureY) + cuboidData.add(ModelCuboidData(name, this.textureX.toFloat(), this.textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX.toFloat(), sizeY.toFloat(), sizeZ.toFloat(), extra, this.mirror, 1.0f, 1.0f)) + return this + } + + fun cuboid(name: String?, offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Int, sizeY: Int, sizeZ: Int, textureX: Int, textureY: Int): ModelPartBuilder { + this.uv(textureX, textureY) + cuboidData.add(ModelCuboidData(name, this.textureX.toFloat(), this.textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX.toFloat(), sizeY.toFloat(), sizeZ.toFloat(), Dilation.Companion.NONE, this.mirror, 1.0f, 1.0f)) + return this + } + + fun cuboid(offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float): ModelPartBuilder { + cuboidData.add(ModelCuboidData(null, textureX.toFloat(), textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ, Dilation.Companion.NONE, this.mirror, 1.0f, 1.0f)) + return this + } + + fun cuboid(name: String?, offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float): ModelPartBuilder { + cuboidData.add(ModelCuboidData(name, textureX.toFloat(), textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ, Dilation.Companion.NONE, this.mirror, 1.0f, 1.0f)) + return this + } + + fun cuboid(name: String?, offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float, extra: Dilation): ModelPartBuilder { + cuboidData.add(ModelCuboidData(name, textureX.toFloat(), textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ, extra, this.mirror, 1.0f, 1.0f)) + return this + } + + fun cuboid(offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float, mirror: Boolean): ModelPartBuilder { + cuboidData.add(ModelCuboidData(null, textureX.toFloat(), textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ, Dilation.Companion.NONE, mirror, 1.0f, 1.0f)) + return this + } + + fun cuboid(offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float, extra: Dilation, textureScaleX: Float, textureScaleY: Float): ModelPartBuilder { + cuboidData.add(ModelCuboidData(null, textureX.toFloat(), textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ, extra, this.mirror, textureScaleX, textureScaleY)) + return this + } + + fun cuboid(offsetX: Float, offsetY: Float, offsetZ: Float, sizeX: Float, sizeY: Float, sizeZ: Float, extra: Dilation): ModelPartBuilder { + cuboidData.add(ModelCuboidData(null, textureX.toFloat(), textureY.toFloat(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ, extra, this.mirror, 1.0f, 1.0f)) + return this + } + + fun build(): List { + return ImmutableList.copyOf(this.cuboidData) + } + + companion object { + fun create(): ModelPartBuilder { + return ModelPartBuilder() + } + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartData.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartData.kt new file mode 100644 index 0000000..c2be3d8 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelPartData.kt @@ -0,0 +1,48 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import com.google.common.collect.ImmutableList +import com.google.common.collect.Maps +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment +import java.util.stream.Collectors + +@Environment(value = EnvType.CLIENT) +class ModelPartData internal constructor(private val cuboidData: List?, private val rotationData: ModelTransform) { + private val children: MutableMap = Maps.newHashMap() + + fun addChild(name: String, builder: ModelPartBuilder, rotationData: ModelTransform): ModelPartData { + val modelPartData = ModelPartData(builder.build(), rotationData) + val previousModelPartData = children.put(name, modelPartData) + if (previousModelPartData != null) { + modelPartData.children.putAll(previousModelPartData.children) + } + return modelPartData + } + + fun createPart(textureWidth: Int, textureHeight: Int): ModelPart { + val object2ObjectArrayMap = children.entries.stream() + .collect(Collectors.toMap( + { entry -> entry.key }, + { entry -> entry.value.createPart(textureWidth, textureHeight) }, + { modelPart, _ -> modelPart }, + { Object2ObjectArrayMap() } + )) + + val list = cuboidData!!.stream() + .map { modelCuboidData -> modelCuboidData!!.createCuboid(textureWidth, textureHeight) } + .collect(ImmutableList.toImmutableList()) + + val modelPart = ModelPart(list, object2ObjectArrayMap) + modelPart.transform = rotationData + return modelPart + } + + fun getChild(name: String): ModelPartData? { + return children[name] + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelTransform.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelTransform.kt new file mode 100644 index 0000000..28842dc --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelTransform.kt @@ -0,0 +1,26 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +@Environment(value = EnvType.CLIENT) +class ModelTransform private constructor(val pivotX: Float, val pivotY: Float, val pivotZ: Float, val pitch: Float, val yaw: Float, val roll: Float) { + companion object { + val NONE: ModelTransform = of(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) + fun pivot(pivotX: Float, pivotY: Float, pivotZ: Float): ModelTransform { + return of(pivotX, pivotY, pivotZ, 0.0f, 0.0f, 0.0f) + } + + fun rotation(pitch: Float, yaw: Float, roll: Float): ModelTransform { + return of(0.0f, 0.0f, 0.0f, pitch, yaw, roll) + } + + fun of(pivotX: Float, pivotY: Float, pivotZ: Float, pitch: Float, yaw: Float, roll: Float): ModelTransform { + return ModelTransform(pivotX, pivotY, pivotZ, pitch, yaw, roll) + } + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelUtil.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelUtil.kt new file mode 100644 index 0000000..4c71856 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/ModelUtil.kt @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +@Environment(value = EnvType.CLIENT) +object ModelUtil { + fun interpolateAngle(angle1: Float, angle2: Float, progress: Float): Float { + var f = angle2 - angle1 + while (f < (-Math.PI).toFloat()) { + f += Math.PI.toFloat() * 2 + } + while (f >= Math.PI.toFloat()) { + f -= Math.PI.toFloat() * 2 + } + return angle1 + progress * f + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/TextureDimensions.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/TextureDimensions.kt new file mode 100644 index 0000000..86ea7a0 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/TextureDimensions.kt @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +/** + * Internal class used by [TexturedModelData]. + */ +@Environment(value = EnvType.CLIENT) +class TextureDimensions(val width: Int, val height: Int) + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/model/TexturedModelData.kt b/src/main/kotlin/net/easecation/bedrockloader/render/model/TexturedModelData.kt new file mode 100644 index 0000000..0aa7e10 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/model/TexturedModelData.kt @@ -0,0 +1,21 @@ +/* + * Decompiled with CFR 0.2.1 (FabricMC 53fa44c9). + */ +package net.easecation.bedrockloader.render.model + +import net.fabricmc.api.EnvType +import net.fabricmc.api.Environment + +@Environment(value = EnvType.CLIENT) +class TexturedModelData private constructor(private val data: ModelData, private val dimensions: TextureDimensions) { + fun createModel(): ModelPart { + return data.root.createPart(dimensions.width, dimensions.height) + } + + companion object { + fun of(partData: ModelData, textureWidth: Int, textureHeight: Int): TexturedModelData { + return TexturedModelData(partData, TextureDimensions(textureWidth, textureHeight)) + } + } +} + diff --git a/src/main/kotlin/net/easecation/bedrockloader/render/renderer/EntityDataDrivenRenderer.kt b/src/main/kotlin/net/easecation/bedrockloader/render/renderer/EntityDataDrivenRenderer.kt new file mode 100644 index 0000000..0c70d40 --- /dev/null +++ b/src/main/kotlin/net/easecation/bedrockloader/render/renderer/EntityDataDrivenRenderer.kt @@ -0,0 +1,47 @@ +package net.easecation.bedrockloader.render.renderer + +import net.easecation.bedrockloader.BedrockLoader +import net.easecation.bedrockloader.bedrock.JavaTexturePath +import net.easecation.bedrockloader.render.RenderControllerWithClientEntity +import net.easecation.bedrockloader.entity.EntityDataDriven +import net.easecation.bedrockloader.render.BedrockGeometryModel +import net.minecraft.client.render.entity.EntityRendererFactory +import net.minecraft.client.render.entity.MobEntityRenderer +import net.minecraft.util.Identifier + +/** + * 实现了Java版的实体渲染器,通过RenderControllerWithClientEntity来获取实体的渲染信息 + */ +class EntityDataDrivenRenderer private constructor( + context: EntityRendererFactory.Context?, + private val renderControllerWithClientEntity: RenderControllerWithClientEntity, + entityModel: BedrockGeometryModel?, + shadowRadius: Float +) : MobEntityRenderer(context, entityModel, shadowRadius) { + + companion object { + + // 吐槽:java版是直接在这边就定下来Model了,导致没法像基岩版渲染控制器那样动态决定模型 + fun create( + context: EntityRendererFactory.Context?, + renderControllerWithClientEntity: RenderControllerWithClientEntity, + shadowRadius: Float + ): EntityDataDrivenRenderer { + val model = renderControllerWithClientEntity.getModel() + return EntityDataDrivenRenderer(context, renderControllerWithClientEntity, model, shadowRadius) + } + + } + + override fun getTexture(entity: EntityDataDriven): Identifier { + val texturesList = renderControllerWithClientEntity.getTextures(entity) + if (texturesList.isNullOrEmpty()) { + throw IllegalStateException("No textures found for entity ${entity.type}") + } else if (texturesList.size > 1) { + BedrockLoader.logger.warn("Multiple textures found for entity ${entity.type}, using the first one") + } + // 这边的texturesList中是基岩版的材质路径,需要转换为Java版的材质路径(补上后缀) + return Identifier(entity.identifier.namespace, texturesList[0] + ".png") + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/easecation/bedrockloader/util/GsonUtil.kt b/src/main/kotlin/net/easecation/bedrockloader/util/GsonUtil.kt index 4faea89..ecfdad6 100644 --- a/src/main/kotlin/net/easecation/bedrockloader/util/GsonUtil.kt +++ b/src/main/kotlin/net/easecation/bedrockloader/util/GsonUtil.kt @@ -6,6 +6,7 @@ import net.easecation.bedrockloader.bedrock.block.component.ComponentPlacementFi import net.easecation.bedrockloader.bedrock.block.component.ComponentSelectionBox import net.easecation.bedrockloader.bedrock.pack.SemVersion import net.easecation.bedrockloader.bedrock.definition.BlockResourceDefinition +import net.easecation.bedrockloader.bedrock.definition.GeometryDefinition import net.minecraft.util.Identifier import java.lang.reflect.Type import java.util.UUID @@ -25,6 +26,8 @@ object GsonUtil { .registerTypeAdapter(ComponentGeometry::class.java, ComponentGeometry.Deserializer()) .registerTypeAdapter(ComponentSelectionBox::class.java, ComponentSelectionBox.Deserializer()) .registerTypeAdapter(ComponentPlacementFilter::class.java, ComponentPlacementFilter.Deserializer()) + // geometry + .registerTypeAdapter(GeometryDefinition.Uv::class.java, GeometryDefinition.Uv.Deserializer()) .create() class UUIDSerializer : JsonSerializer, JsonDeserializer { diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e811cd6..97da932 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -20,6 +20,12 @@ "value": "net.easecation.bedrockloader.BedrockLoader", "adapter": "kotlin" } + ], + "client": [ + { + "value": "net.easecation.bedrockloader.BedrockLoaderClient", + "adapter": "kotlin" + } ] }, "mixins": [