|
| 1 | +package com.ysm.converter; |
| 2 | + |
| 3 | +import com.google.gson.JsonArray; |
| 4 | +import com.google.gson.JsonElement; |
| 5 | +import com.google.gson.JsonObject; |
| 6 | +import com.google.gson.JsonParser; |
| 7 | + |
| 8 | +import java.io.IOException; |
| 9 | +import java.nio.charset.StandardCharsets; |
| 10 | +import java.nio.file.Files; |
| 11 | +import java.nio.file.Path; |
| 12 | +import java.nio.file.StandardCopyOption; |
| 13 | + |
| 14 | +/** |
| 15 | + * Post-processes a Blockbench .bbmodel for ModelEngine compatibility. |
| 16 | + * |
| 17 | + * <p>Actions performed: |
| 18 | + * <ul> |
| 19 | + * <li>Rename bones that start with "hit_" → ModelEngine hitbox bone</li> |
| 20 | + * <li>Copy textures to the model's texture folder</li> |
| 21 | + * <li>Strip non-ModelEngine animation controllers</li> |
| 22 | + * <li>Write a basic ModelEngine model configuration</li> |
| 23 | + * </ul> |
| 24 | + */ |
| 25 | +public final class BlockbenchToModelEngine { |
| 26 | + |
| 27 | + private BlockbenchToModelEngine() {} |
| 28 | + |
| 29 | + public static void convert(Path bbmodelPath, Path outputDir) throws IOException { |
| 30 | + Files.createDirectories(outputDir); |
| 31 | + |
| 32 | + String raw = Files.readString(bbmodelPath, StandardCharsets.UTF_8); |
| 33 | + JsonObject model = JsonParser.parseString(raw).getAsJsonObject(); |
| 34 | + |
| 35 | + // Copy the processed .bbmodel alongside |
| 36 | + Path destBbmodel = outputDir.resolve(bbmodelPath.getFileName()); |
| 37 | + Files.copy(bbmodelPath, destBbmodel, StandardCopyOption.REPLACE_EXISTING); |
| 38 | + |
| 39 | + // Flag hitbox bones |
| 40 | + if (model.has("outliner")) { |
| 41 | + flagModelEngineBones(model.getAsJsonArray("outliner")); |
| 42 | + } |
| 43 | + |
| 44 | + // Write the augmented .bbmodel back |
| 45 | + Files.writeString(destBbmodel, model.toString(), StandardCharsets.UTF_8); |
| 46 | + |
| 47 | + // Write a skeleton ModelEngine model.yml |
| 48 | + String modelName = bbmodelPath.getFileName().toString().replace(".bbmodel", ""); |
| 49 | + writeModelEngineConfig(outputDir, modelName); |
| 50 | + } |
| 51 | + |
| 52 | + private static void flagModelEngineBones(JsonArray outliner) { |
| 53 | + for (JsonElement el : outliner) { |
| 54 | + if (!el.isJsonObject()) continue; |
| 55 | + JsonObject group = el.getAsJsonObject(); |
| 56 | + String name = group.has("name") ? group.get("name").getAsString() : ""; |
| 57 | + if (name.toLowerCase().startsWith("hit_")) { |
| 58 | + group.addProperty("model_engine_hitbox", true); |
| 59 | + } |
| 60 | + if (name.equalsIgnoreCase("mount") || name.equalsIgnoreCase("seat")) { |
| 61 | + group.addProperty("model_engine_seat", true); |
| 62 | + } |
| 63 | + if (group.has("children")) { |
| 64 | + flagModelEngineBones(group.getAsJsonArray("children")); |
| 65 | + } |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + private static void writeModelEngineConfig(Path outputDir, String modelName) throws IOException { |
| 70 | + String yml = """ |
| 71 | + # ModelEngine model configuration for %s |
| 72 | + model: |
| 73 | + id: %s |
| 74 | + type: GENERIC |
| 75 | + scale: 1.0 |
| 76 | + # Hitbox, seat, and nametag bones are auto-detected from .bbmodel. |
| 77 | + # Adjust mountHeight and other settings as needed. |
| 78 | + """.formatted(modelName, modelName); |
| 79 | + |
| 80 | + Files.writeString(outputDir.resolve("model.yml"), yml, StandardCharsets.UTF_8); |
| 81 | + } |
| 82 | +} |
0 commit comments