diff --git a/gradle.properties b/gradle.properties index 3d513740..ffe7551c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ fabric_version=0.110.2+1.21.4 maven_group = eu.pb4 -mod_version = 0.11.0 +mod_version = 0.11.1 minecraft_version_supported = ">=1.21.4-" diff --git a/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java b/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java index 431f8c5f..5a722b93 100644 --- a/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java +++ b/polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java @@ -14,6 +14,7 @@ import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; import eu.pb4.polymer.resourcepack.extras.api.ResourcePackExtras; import eu.pb4.polymer.resourcepack.extras.api.format.item.ItemAsset; +import eu.pb4.polymer.resourcepack.extras.api.format.model.Model; import eu.pb4.polymer.virtualentity.api.tracker.EntityTrackedData; import net.fabricmc.api.EnvType; import net.fabricmc.api.ModInitializer; @@ -500,14 +501,14 @@ public void onInitialize() { new Thread(() -> { var vanillaJar = PolymerCommonUtils.getClientJarRoot(); - var itemsBase = vanillaJar.resolve("/assets/minecraft/items/"); + var modelsBase = vanillaJar.resolve("/assets/minecraft/models/"); try { var value = new MutableInt(); var count = new MutableInt(); - Files.walk(itemsBase, 1).forEach(path -> { - if (path.equals(itemsBase)) { + Files.walk(itemsBase).forEach(path -> { + if (!path.toString().endsWith(".json")) { return; } count.increment(); @@ -520,6 +521,28 @@ public void onInitialize() { e.printStackTrace(); } }); + System.out.println("Parsed " + value + " out of " + count + " item assets!"); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + var value = new MutableInt(); + var count = new MutableInt(); + Files.walk(modelsBase).forEach(path -> { + if (!path.toString().endsWith(".json")) { + return; + } + count.increment(); + try { + var asset = Model.fromJson(Files.readString(path)); + //System.out.println(path + ">" + asset); + value.increment(); + } catch (Throwable e) { + System.err.println("Error while parsing file: " + path); + e.printStackTrace(); + } + }); System.out.println("Parsed " + value + " out of " + count + " models!"); } catch (IOException e) { e.printStackTrace(); diff --git a/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/GuiLight.java b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/GuiLight.java new file mode 100644 index 00000000..c549b5ef --- /dev/null +++ b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/GuiLight.java @@ -0,0 +1,20 @@ +package eu.pb4.polymer.resourcepack.extras.api.format.model; + +import com.mojang.serialization.Codec; +import net.minecraft.util.StringIdentifiable; + +public enum GuiLight implements StringIdentifiable { + SIDE("side"), + FRONT("front"); + + public static final Codec CODEC = StringIdentifiable.createCodec(GuiLight::values); + + private final String name; + private GuiLight(String name) { + this.name = name; + } + @Override + public String asString() { + return this.name; + } +} diff --git a/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/Model.java b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/Model.java new file mode 100644 index 00000000..205fafac --- /dev/null +++ b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/Model.java @@ -0,0 +1,57 @@ +package eu.pb4.polymer.resourcepack.extras.api.format.model; + +import com.google.gson.JsonParser; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.ModelTransformationMode; +import net.minecraft.util.Identifier; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public record Model(Optional parent, Optional> elements, Map textures, + Map display, + Optional guiLight, + boolean ambientOcclusion) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Identifier.CODEC.optionalFieldOf("parent").forGetter(Model::parent), + ModelElement.CODEC.listOf().optionalFieldOf("elements").forGetter(Model::elements), + Codec.unboundedMap(Codec.STRING, Codec.STRING).optionalFieldOf("textures", Map.of()).forGetter(Model::textures), + Codec.unboundedMap(ModelTransformationMode.CODEC, ModelTransformation.CODEC).optionalFieldOf("display", Map.of()).forGetter(Model::display), + GuiLight.CODEC.optionalFieldOf("gui_light").forGetter(Model::guiLight), + Codec.BOOL.optionalFieldOf("ambientocclusion", true).forGetter(Model::ambientOcclusion) + ).apply(instance, Model::new)); + + public Model(Optional parent, Optional> elements, Map textures, + Map display, + Optional guiLight) { + this(parent, elements, textures, display, guiLight, true); + } + + public Model(Optional parent, Optional> elements, Map textures, + Map display) { + this(parent, elements, textures, display, Optional.empty(), true); + } + + public Model(Optional parent, Optional> elements, Map textures) { + this(parent, elements, textures, Map.of(), Optional.empty(), true); + } + + public Model(Identifier parent, Map textures) { + this(Optional.of(parent), Optional.empty(), textures, Map.of(), Optional.empty(), true); + } + + public Model(List elements, Map textures) { + this(Optional.empty(), Optional.of(elements), textures, Map.of(), Optional.empty(), true); + } + + public String toJson() { + return CODEC.encodeStart(JsonOps.INSTANCE, this).getOrThrow().toString(); + } + + public static Model fromJson(String json) { + return CODEC.decode(JsonOps.INSTANCE, JsonParser.parseString(json)).getOrThrow().getFirst(); + } +} diff --git a/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/ModelElement.java b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/ModelElement.java new file mode 100644 index 00000000..f30f9e11 --- /dev/null +++ b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/ModelElement.java @@ -0,0 +1,84 @@ +package eu.pb4.polymer.resourcepack.extras.api.format.model; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.dynamic.Codecs; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public record ModelElement(Vec3d from, Vec3d to, Map faces, Optional rotation, + boolean shade, int lightEmission) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Vec3d.CODEC.fieldOf("from").forGetter(ModelElement::from), + Vec3d.CODEC.fieldOf("to").forGetter(ModelElement::to), + Codec.unboundedMap(Direction.CODEC, Face.CODEC).optionalFieldOf("faces", Map.of()).forGetter(ModelElement::faces), + Rotation.CODEC.optionalFieldOf("rotation").forGetter(ModelElement::rotation), + Codec.BOOL.optionalFieldOf("shade", true).forGetter(ModelElement::shade), + Codecs.rangedInt(0, 15).optionalFieldOf("light_emission", 0).forGetter(ModelElement::lightEmission) + ).apply(instance, ModelElement::new)); + + public ModelElement(Vec3d from, Vec3d to, Map faces, Optional rotation, + boolean shade) { + this(from, to, faces, rotation, shade, 0); + } + public ModelElement(Vec3d from, Vec3d to, Map faces, Optional rotation) { + this(from, to, faces, rotation, true, 0); + } + + public ModelElement(Vec3d from, Vec3d to, Map faces) { + this(from, to, faces, Optional.empty(), true, 0); + } + + public record Rotation(Vec3d origin, Direction.Axis axis, float angle, boolean rescale) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Vec3d.CODEC.optionalFieldOf("origin", Vec3d.ZERO).forGetter(Rotation::origin), + Direction.Axis.CODEC.fieldOf("axis").forGetter(Rotation::axis), + Codec.FLOAT.fieldOf("angle").forGetter(Rotation::angle), + Codec.BOOL.optionalFieldOf("rescale", false).forGetter(Rotation::rescale) + ).apply(instance, Rotation::new)); + + public Rotation(Vec3d origin, Direction.Axis axis, float angle) { + this(origin, axis, angle, false); + } + + public Rotation(Direction.Axis axis, float angle) { + this(Vec3d.ZERO, axis, angle, false); + } + } + + public record Face(List uv, String texture, Optional cullface, int rotation, int tintIndex) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.list(Codec.FLOAT, 4, 4).optionalFieldOf("uv", List.of()).forGetter(Face::uv), + Codec.STRING.optionalFieldOf("texture", "").forGetter(Face::texture), + Direction.CODEC.optionalFieldOf("cullface").forGetter(Face::cullface), + Codec.INT.optionalFieldOf("rotation", 0).forGetter(Face::rotation), + Codec.INT.optionalFieldOf("tintindex", -1).forGetter(Face::tintIndex) + ).apply(instance, Face::new)); + + public Face { + if (uv.size() != 4 && !uv.isEmpty()) { + throw new IllegalArgumentException("uv needs to have either 4 elements or be empty"); + } + } + + public Face(List uv, String texture, Optional cullface, int rotation) { + this(uv, texture, cullface, rotation, -1); + } + + public Face(List uv, String texture, Optional cullface) { + this(uv, texture, cullface, 0, -1); + } + + public Face(List uv, String texture) { + this(uv, texture, Optional.empty(), 0, -1); + } + + public Face(String texture) { + this(List.of(), texture, Optional.empty(), 0, -1); + } + } +} diff --git a/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/ModelTransformation.java b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/ModelTransformation.java new file mode 100644 index 00000000..403fc95a --- /dev/null +++ b/polymer-resource-pack-extras/src/main/java/eu/pb4/polymer/resourcepack/extras/api/format/model/ModelTransformation.java @@ -0,0 +1,13 @@ +package eu.pb4.polymer.resourcepack.extras.api.format.model; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.math.Vec3d; + +public record ModelTransformation(Vec3d rotation, Vec3d translation, Vec3d scale) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Vec3d.CODEC.optionalFieldOf("rotation", Vec3d.ZERO).forGetter(ModelTransformation::rotation), + Vec3d.CODEC.optionalFieldOf("translation", Vec3d.ZERO).forGetter(ModelTransformation::translation), + Vec3d.CODEC.optionalFieldOf("scale", Vec3d.ZERO).forGetter(ModelTransformation::scale) + ).apply(instance, ModelTransformation::new)); +}