diff --git a/common/src/main/java/com/leisuretimedock/jsonem/Jsonem.java b/common/src/main/java/com/leisuretimedock/jsonem/JsonEm.java similarity index 51% rename from common/src/main/java/com/leisuretimedock/jsonem/Jsonem.java rename to common/src/main/java/com/leisuretimedock/jsonem/JsonEm.java index dd2120a..5bbb2cf 100644 --- a/common/src/main/java/com/leisuretimedock/jsonem/Jsonem.java +++ b/common/src/main/java/com/leisuretimedock/jsonem/JsonEm.java @@ -1,8 +1,11 @@ package com.leisuretimedock.jsonem; -public final class Jsonem { - public static final String MOD_ID = "jsonem"; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +public final class JsonEm { + public static final String MOD_ID = "jsonem"; + public static final Logger LOG = LoggerFactory.getLogger(JsonEm.class); public static void init() { // Write common init code here. } diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java new file mode 100644 index 0000000..1101a76 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/DilationAccess.java @@ -0,0 +1,17 @@ +package com.leisuretimedock.jsonem.mixin; + +import net.minecraft.client.model.geom.builders.CubeDeformation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(CubeDeformation.class) +public interface DilationAccess { + @Accessor("growX") + float jsonem$growX(); + + @Accessor("growY") + float jsonem$growY(); + + @Accessor("growZ") + float jsonem$growZ(); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java new file mode 100644 index 0000000..7da2fe4 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelLoaderMixin.java @@ -0,0 +1,26 @@ +package com.leisuretimedock.jsonem.mixin; + +import com.leisuretimedock.jsonem.util.JsonEntityModelUtil; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.HashMap; +import java.util.Map; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.server.packs.resources.ResourceManager; + +@Mixin(EntityModelSet.class) +public class EntityModelLoaderMixin { + @Shadow private Map roots; + + @Inject(method = "onResourceManagerReload", at = @At("TAIL")) + private void jsonem$loadJsonEntityModels(ResourceManager manager, CallbackInfo ci) { + this.roots = new HashMap<>(this.roots); + JsonEntityModelUtil.loadModels(manager, this.roots); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java new file mode 100644 index 0000000..c606ea9 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/EntityModelsMixin.java @@ -0,0 +1,33 @@ +package com.leisuretimedock.jsonem.mixin; + +import com.google.common.collect.ImmutableMap; +import com.leisuretimedock.jsonem.JsonEm; +import com.leisuretimedock.jsonem.platform.Services; +import com.leisuretimedock.jsonem.util.JsonEntityModelUtil; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.io.IOException; +import java.util.Map; +import net.minecraft.client.model.geom.LayerDefinitions; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; + +@Mixin(LayerDefinitions.class) +public class EntityModelsMixin { + @Inject(method = "createRoots", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;"), locals = LocalCapture.CAPTURE_FAILSOFT) + private static void jsonem$dumpModels(CallbackInfoReturnable> cir, ImmutableMap.Builder layers) { + if (Services.PLATFORM.shouldDumpModels()) { + layers.build().forEach((layer, data) -> { + try { + JsonEntityModelUtil.dump(layer, data); + } catch (IOException e) { + JsonEm.LOG.error("Error",e); + } + }); + } + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java new file mode 100644 index 0000000..1aa5548 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelCuboidDataAccess.java @@ -0,0 +1,45 @@ +package com.leisuretimedock.jsonem.mixin; + +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.Set; +import net.minecraft.client.model.geom.builders.CubeDefinition; +import net.minecraft.client.model.geom.builders.CubeDeformation; +import net.minecraft.client.model.geom.builders.UVPair; +import net.minecraft.core.Direction; + +@Mixin(CubeDefinition.class) +public interface ModelCuboidDataAccess { + @Accessor("comment") + String jsonem$comment(); + + @Accessor("origin") + Vector3f jsonem$origin(); + + @Accessor("dimensions") + Vector3f jsonem$dimensions(); + + @Accessor("grow") + CubeDeformation jsonem$grow(); + + @Accessor("mirror") + boolean jsonem$mirror(); + + @Accessor("texCoord") + UVPair jsonem$uv(); + + @Accessor("texScale") + UVPair jsonem$uvScale(); + + @Accessor("visibleFaces") + Set jsonem$faces(); + + @Invoker("") + static CubeDefinition jsonem$create(@Nullable String name, float textureX, float textureY, float offsetX, float offsetY, float offsetZ, float sizeX, float sizeY, float sizeZ, CubeDeformation extra, boolean mirror, float textureScaleX, float textureScaleY, Set directions) { + throw new AssertionError("mixin broke"); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java new file mode 100644 index 0000000..a2a56e1 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/ModelPartDataAccess.java @@ -0,0 +1,28 @@ +package com.leisuretimedock.jsonem.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; +import java.util.Map; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.CubeDefinition; +import net.minecraft.client.model.geom.builders.PartDefinition; + +@Mixin(PartDefinition.class) +public interface ModelPartDataAccess { + @Accessor("cubes") + List jsonem$cubes(); + + @Accessor("partPose") + PartPose jsonem$transform(); + + @Accessor("children") + Map jsonem$children(); + + @Invoker("") + static PartDefinition create(List cuboids, PartPose rotation) { + throw new AssertionError("mixin broke"); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java new file mode 100644 index 0000000..fb7c73f --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TextureDimensionsAccess.java @@ -0,0 +1,14 @@ +package com.leisuretimedock.jsonem.mixin; + +import net.minecraft.client.model.geom.builders.MaterialDefinition; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MaterialDefinition.class) +public interface TextureDimensionsAccess { + @Accessor("xTexSize") + int jsonem$width(); + + @Accessor("yTexSize") + int jsonem$height(); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java new file mode 100644 index 0000000..73d9e8d --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/mixin/TexturedModelDataAccess.java @@ -0,0 +1,22 @@ +package com.leisuretimedock.jsonem.mixin; + +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MaterialDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(LayerDefinition.class) +public interface TexturedModelDataAccess { + @Accessor("mesh") + MeshDefinition jsonem$root(); + + @Accessor("material") + MaterialDefinition jsonem$texture(); + + @Invoker("") + static LayerDefinition create(MeshDefinition data, MaterialDefinition dimensions) { + throw new AssertionError("mixin broke"); + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java b/common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java new file mode 100644 index 0000000..a198a0f --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/platform/IPlatFormHelper.java @@ -0,0 +1,5 @@ +package com.leisuretimedock.jsonem.platform; + +public interface IPlatFormHelper { + boolean shouldDumpModels(); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java b/common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java new file mode 100644 index 0000000..511e18c --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/platform/Services.java @@ -0,0 +1,16 @@ +package com.leisuretimedock.jsonem.platform; + +import com.leisuretimedock.jsonem.JsonEm; + +import java.util.ServiceLoader; + +public class Services { + public static final IPlatFormHelper PLATFORM = load(IPlatFormHelper.class); + public static T load(Class clazz) { + final T loadedService = ServiceLoader.load(clazz) + .findFirst() + .orElseThrow(() -> new RuntimeException("Could not find service for " + clazz.getSimpleName())); + JsonEm.LOG.debug("Loaded {} service for {}", loadedService, clazz); + return loadedService; + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java b/common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java new file mode 100644 index 0000000..5a61ce2 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/serialization/JsonEMCodecs.java @@ -0,0 +1,123 @@ +package com.leisuretimedock.jsonem.serialization; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.leisuretimedock.jsonem.mixin.DilationAccess; +import com.leisuretimedock.jsonem.mixin.ModelCuboidDataAccess; +import com.leisuretimedock.jsonem.mixin.ModelPartDataAccess; +import com.leisuretimedock.jsonem.mixin.TextureDimensionsAccess; +import com.leisuretimedock.jsonem.mixin.TexturedModelDataAccess; +import com.leisuretimedock.jsonem.util.Vector2fComparable; +import org.joml.Vector3f; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import net.minecraft.Util; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.CubeDefinition; +import net.minecraft.client.model.geom.builders.CubeDeformation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MaterialDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; +import net.minecraft.client.model.geom.builders.PartDefinition; +import net.minecraft.client.model.geom.builders.UVPair; +import net.minecraft.core.Direction; +import net.minecraft.util.ExtraCodecs; + +public class JsonEMCodecs { + private static final Set ALL_DIRECTIONS = EnumSet.allOf(Direction.class); + + public static final Codec VECTOR2F = Codec.FLOAT.listOf().comapFlatMap((vec) -> + Util.fixedSize(vec, 2).map((arr) -> new Vector2fComparable(arr.get(0), arr.get(1))), + (vec) -> ImmutableList.of(vec.u(), vec.v()) + ); + + public static final Codec TEXTURE_DIMENSIONS = RecordCodecBuilder.create((instance) -> + instance.group( + Codec.INT.fieldOf("width").forGetter(obj -> ((TextureDimensionsAccess) obj).jsonem$width()), + Codec.INT.fieldOf("height").forGetter(obj -> ((TextureDimensionsAccess) obj).jsonem$height()) + ).apply(instance, MaterialDefinition::new) + ); + + public static final Codec MODEL_TRANSFORM = RecordCodecBuilder.create((instance) -> + instance.group( + ExtraCodecs.VECTOR3F.optionalFieldOf("origin", new Vector3f(0)).forGetter(obj -> new Vector3f(obj.x, obj.y, obj.z)), + ExtraCodecs.VECTOR3F.optionalFieldOf("rotation", new Vector3f(0)).forGetter(obj -> new Vector3f(obj.xRot, obj.yRot, obj.zRot)) + ).apply(instance, (origin, rot) -> PartPose.offsetAndRotation(origin.x(), origin.y(), origin.z(), rot.x(), rot.y(), rot.z())) + ); + + public static final Codec DILATION = ExtraCodecs.VECTOR3F.xmap( + vec -> new CubeDeformation(vec.x(), vec.y(), vec.z()), + dil -> new Vector3f( + ((DilationAccess) dil).jsonem$growX(), + ((DilationAccess) dil).jsonem$growY(), + ((DilationAccess) dil).jsonem$growZ()) + ); + + private static CubeDefinition createCuboidData(Optional name, Vector3f offset, Vector3f dimensions, CubeDeformation dilation, boolean mirror, UVPair uv, UVPair uvSize, Optional> faces) { + return ModelCuboidDataAccess.jsonem$create(name.orElse(null), + uv.u(), uv.v(), + offset.x(), offset.y(), offset.z(), + dimensions.x(), dimensions.y(), dimensions.z(), + dilation, mirror, + uvSize.u(), uvSize.v(), + faces.map(Set::copyOf).orElse(ALL_DIRECTIONS)); + } + + // If the set has all faces, return empty + private static Optional> optionalFaceList(Set faces) { + for (Direction direction : Direction.values()) { + if (!faces.contains(direction)) { + return Optional.of(List.copyOf(faces)); + } + } + + return Optional.empty(); + } + + private static final UVPair DEFAULT_UV_SCALE = new Vector2fComparable(1.0f, 1.0f); + + public static final Codec MODEL_CUBOID_DATA = RecordCodecBuilder.create((instance) -> + instance.group( + Codec.STRING.optionalFieldOf("name").forGetter(obj -> Optional.ofNullable(((ModelCuboidDataAccess) (Object) obj).jsonem$comment())), + ExtraCodecs.VECTOR3F.fieldOf("offset").forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$origin()), + ExtraCodecs.VECTOR3F.fieldOf("dimensions").forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$dimensions()), + DILATION.optionalFieldOf("dilation", CubeDeformation.NONE).forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$grow()), + Codec.BOOL.optionalFieldOf("mirror", false).forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$mirror()), + VECTOR2F.fieldOf("uv").forGetter(obj -> ((ModelCuboidDataAccess)(Object)obj).jsonem$uv()), + VECTOR2F.optionalFieldOf("uv_scale", DEFAULT_UV_SCALE).forGetter(obj -> Vector2fComparable.of(((ModelCuboidDataAccess)(Object)obj).jsonem$uvScale())), + Codec.list(Direction.CODEC).optionalFieldOf("faces").forGetter(obj -> optionalFaceList(((ModelCuboidDataAccess)(Object)obj).jsonem$faces())) + ).apply(instance, JsonEMCodecs::createCuboidData) + ); + + private static Codec createPartDataCodec() { + return RecordCodecBuilder.create((instance) -> + instance.group( + MODEL_TRANSFORM.optionalFieldOf("transform", PartPose.ZERO).forGetter(obj -> ((ModelPartDataAccess) obj).jsonem$transform()), + Codec.list(MODEL_CUBOID_DATA).fieldOf("cuboids").forGetter(obj -> ((ModelPartDataAccess) obj).jsonem$cubes()), + LazyTypeUnboundedMapCodec.of(Codec.STRING, JsonEMCodecs::createPartDataCodec).optionalFieldOf("children", new HashMap<>()).forGetter(obj -> ((ModelPartDataAccess) obj).jsonem$children()) + ).apply(instance, (transform, cuboids, children) -> { + var data = ModelPartDataAccess.create(cuboids, transform); + ((ModelPartDataAccess) data).jsonem$children().putAll(children); + return data; + }) + ); + } + + public static final Codec MODEL_PART_DATA = createPartDataCodec(); + + public static final Codec TEXTURED_MODEL_DATA = RecordCodecBuilder.create((instance) -> + instance.group( + TEXTURE_DIMENSIONS.fieldOf("texture").forGetter(obj -> ((TexturedModelDataAccess) obj).jsonem$texture()), + Codec.unboundedMap(Codec.STRING, MODEL_PART_DATA).fieldOf("bones").forGetter(obj -> ((ModelPartDataAccess) ((TexturedModelDataAccess) obj).jsonem$root().getRoot()).jsonem$children()) + ).apply(instance, (texture, bones) -> { + var data = new MeshDefinition(); + ((ModelPartDataAccess) data.getRoot()).jsonem$children().putAll(bones); + return TexturedModelDataAccess.create(data, texture); + }) + ); +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java b/common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java new file mode 100644 index 0000000..98f749c --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/serialization/LazyTypeUnboundedMapCodec.java @@ -0,0 +1,51 @@ +package com.leisuretimedock.jsonem.serialization; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.codecs.BaseMapCodec; + +import java.util.Map; +import java.util.function.Supplier; + +public class LazyTypeUnboundedMapCodec implements BaseMapCodec, Codec> { + private final Codec keyCodec; + private final Supplier> elementCodecProvider; + + private Codec elementCodec; + + public LazyTypeUnboundedMapCodec(Codec keyCodec, Supplier> elementCodecProvider) { + this.keyCodec = keyCodec; + this.elementCodecProvider = elementCodecProvider; + } + + public static LazyTypeUnboundedMapCodec of(Codec keyCodec, Supplier> elementCodecProvider) { + return new LazyTypeUnboundedMapCodec<>(keyCodec, elementCodecProvider); + } + + @Override + public DataResult, T>> decode(DynamicOps ops, T input) { + return ops.getMap(input).setLifecycle(Lifecycle.stable()).flatMap(map -> decode(ops, map)).map(r -> Pair.of(r, input)); + } + + @Override + public DataResult encode(Map input, DynamicOps ops, T prefix) { + return encode(input, ops, ops.mapBuilder()).build(prefix); + } + + @Override + public Codec keyCodec() { + return keyCodec; + } + + @Override + public Codec elementCodec() { + if (elementCodec == null) { + elementCodec = elementCodecProvider.get(); + } + + return elementCodec; + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java b/common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java new file mode 100644 index 0000000..9ee53ed --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/util/JsonEntityModelUtil.java @@ -0,0 +1,76 @@ +package com.leisuretimedock.jsonem.util; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.leisuretimedock.jsonem.JsonEm; +import com.leisuretimedock.jsonem.serialization.JsonEMCodecs; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.JsonOps; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import org.jetbrains.annotations.ApiStatus; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; + +public class JsonEntityModelUtil { + public static Path DUMP_DIR; + public static final Gson GSON = new Gson(); + + public static Optional readJson(InputStream data) { + JsonElement json = GSON.fromJson(GSON.newJsonReader(new InputStreamReader(data)), JsonObject.class); + + return JsonEMCodecs.TEXTURED_MODEL_DATA.decode(JsonOps.INSTANCE, json).result().map(Pair::getFirst); + } + + public static void loadModels(ResourceManager manager, Map models) { + ModelLayers.getKnownLocations().forEach(layer -> { + var modelLoc = new ResourceLocation(layer.getModel().getNamespace(), "models/entity/"+layer.getModel().getPath()+"/"+layer.getLayer()+".json"); + + var res = manager.getResource(modelLoc); + + if (res.isPresent()) { + try { + try (var in = res.get().open()) { + var data = JsonEntityModelUtil.readJson(in); + data.ifPresent(model -> models.put(layer, model)); + } + } catch (IOException e) { + JsonEm.LOG.error("Error: {}",e.getMessage()); + } + } + }); + } + + public static void dump(ModelLayerLocation layer, LayerDefinition data) throws IOException { + if (!Files.exists(DUMP_DIR)) { + Files.createDirectories(DUMP_DIR); + } + + var modelResult = JsonEMCodecs.TEXTURED_MODEL_DATA.encode(data, JsonOps.INSTANCE, new JsonObject()); + var modelFolder = DUMP_DIR.resolve("assets").resolve(layer.getModel().getNamespace()).resolve("models").resolve("entity").resolve(layer.getModel().getPath()); + var modelFile = modelFolder.resolve(layer.getLayer()+".json"); + + if (!Files.exists(modelFolder)) { + Files.createDirectories(modelFolder); + } + + var element = modelResult.get().left(); + if (element.isPresent()) { + var writer = GSON.newJsonWriter(Files.newBufferedWriter(modelFile)); + writer.setIndent(" "); + GSON.toJson(element.get(), writer); + + writer.close(); + } + } +} diff --git a/common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java b/common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java new file mode 100644 index 0000000..7724a45 --- /dev/null +++ b/common/src/main/java/com/leisuretimedock/jsonem/util/Vector2fComparable.java @@ -0,0 +1,24 @@ +package com.leisuretimedock.jsonem.util; + +import net.minecraft.client.model.geom.builders.UVPair; + +/** + * Implementation of {@code equals()} on {@link UVPair} + */ +public class Vector2fComparable extends UVPair { + public Vector2fComparable(float x, float y) { + super(x, y); + } + + public static Vector2fComparable of(UVPair vec) { + return new Vector2fComparable(vec.u(), vec.v()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UVPair vec) { + return this.u() == vec.u() && this.v() == vec.v(); + } + return super.equals(obj); + } +} diff --git a/common/src/main/resources/jsonem.mixins.json b/common/src/main/resources/jsonem.mixins.json index 596aa72..ab68579 100644 --- a/common/src/main/resources/jsonem.mixins.json +++ b/common/src/main/resources/jsonem.mixins.json @@ -4,6 +4,12 @@ "compatibilityLevel": "JAVA_17", "minVersion": "0.8", "client": [ + "DilationAccess", + "EntityModelLoaderMixin", + "EntityModelsMixin", + "ModelPartDataAccess", + "TextureDimensionsAccess", + "TexturedModelDataAccess" ], "mixins": [ ], diff --git a/forge/src/main/resources/pack.mcmeta b/common/src/main/resources/pack.mcmeta similarity index 100% rename from forge/src/main/resources/pack.mcmeta rename to common/src/main/resources/pack.mcmeta diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java new file mode 100644 index 0000000..8f27530 --- /dev/null +++ b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonEmFabric.java @@ -0,0 +1,23 @@ +package com.leisuretimedock.jsonem.fabric; + +import com.leisuretimedock.jsonem.fabric.config.JsonEmConfig; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; + + +public class JsonEmFabric implements ClientModInitializer { + + public static final JsonEmConfig CONFIG = new JsonEmConfig(); + + @Override + public void onInitializeClient() { + CONFIG.load(); + } + + public static void registerModelLayer(ModelLayerLocation layer) { + EntityModelLayerRegistry.registerModelLayer(layer, () -> LayerDefinition.create(new MeshDefinition(), 32, 32)); + } +} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java deleted file mode 100644 index 7735d40..0000000 --- a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/JsonemFabric.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.leisuretimedock.jsonem.fabric; - -import com.leisuretimedock.jsonem.Jsonem; -import net.fabricmc.api.ModInitializer; - -public final class JsonemFabric implements ModInitializer { - @Override - public void onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. - - // Run our common setup. - Jsonem.init(); - } -} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java deleted file mode 100644 index 978c72b..0000000 --- a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/client/JsonemFabricClient.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.leisuretimedock.jsonem.fabric.client; - -import net.fabricmc.api.ClientModInitializer; - -public final class JsonemFabricClient implements ClientModInitializer { - @Override - public void onInitializeClient() { - // This entrypoint is suitable for setting up client-specific logic, such as rendering. - } -} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java new file mode 100644 index 0000000..1497d03 --- /dev/null +++ b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/config/JsonEmConfig.java @@ -0,0 +1,49 @@ +package com.leisuretimedock.jsonem.fabric.config; + +import com.leisuretimedock.jsonem.JsonEm; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +public final class JsonEmConfig { + public static final Path FILE = FabricLoader.getInstance().getConfigDir().resolve("jsonem.properties"); + + public final Properties values = new Properties(); + + private static void defaultValues(Properties values) { + values.setProperty("dump_models", "false"); + } + + public void load() { + try { + if (!Files.exists(FILE)) { + save(); + } + + this.values.clear(); + try (var in = Files.newInputStream(FILE)) { + this.values.load(in); + } + } catch (IOException ex) { + JsonEm.LOG.error("Error loading config file for Json Entity Models", ex); + } + } + + public void save() { + try { + if (!Files.exists(FILE)) { + Files.createFile(FILE); + } + + defaultValues(this.values); + try (var out = Files.newOutputStream(FILE)) { + this.values.store(out, null); + } + } catch (IOException ex) { + JsonEm.LOG.error("Error saving config file for Json Entity Models", ex); + } + } +} diff --git a/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java new file mode 100644 index 0000000..d758269 --- /dev/null +++ b/fabric/src/main/java/com/leisuretimedock/jsonem/fabric/platform/FabricPlatformHelper.java @@ -0,0 +1,11 @@ +package com.leisuretimedock.jsonem.fabric.platform; + +import com.leisuretimedock.jsonem.fabric.JsonEmFabric; +import com.leisuretimedock.jsonem.platform.IPlatFormHelper; + +public class FabricPlatformHelper implements IPlatFormHelper { + @Override + public boolean shouldDumpModels() { + return "true".equals(JsonEmFabric.CONFIG.values.getProperty("dump_models")); + } +} diff --git a/fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper b/fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper new file mode 100644 index 0000000..bab57b4 --- /dev/null +++ b/fabric/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper @@ -0,0 +1 @@ +com.leisuretimedock.jsonem.fabric.platform.FabricPlatformHelper \ No newline at end of file diff --git a/fabric/src/main/resources/assets/jsonem/icon.png b/fabric/src/main/resources/assets/jsonem/icon.png new file mode 100644 index 0000000..54c2a1f Binary files /dev/null and b/fabric/src/main/resources/assets/jsonem/icon.png differ diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 5145b6f..a81ee2f 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -15,11 +15,8 @@ "icon": "assets/jsonem/icon.png", "environment": "*", "entrypoints": { - "main": [ - "com.leisuretimedock.jsonem.fabric.JsonemFabric" - ], "client": [ - "com.leisuretimedock.jsonem.fabric.client.JsonemFabricClient" + "com.leisuretimedock.jsonem.fabric.JsonEmFabric" ] }, "mixins": [ diff --git a/forge/build.gradle b/forge/build.gradle index af89834..1bce9fc 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -18,6 +18,7 @@ configurations { canBeResolved = true canBeConsumed = false } + shadowCommon compileClasspath.extendsFrom common runtimeClasspath.extendsFrom common developmentForge.extendsFrom common @@ -31,12 +32,11 @@ configurations { } dependencies { + compileOnly project(":common") forge "net.minecraftforge:forge:$rootProject.forge_version" modImplementation "dev.architectury:architectury-forge:$rootProject.architectury_api_version" - common(project(path: ':common', configuration: 'namedElements')) { transitive false } - shadowBundle project(path: ':common', configuration: 'transformProductionForge') } processResources { diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java new file mode 100644 index 0000000..1acea9b --- /dev/null +++ b/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonEmForge.java @@ -0,0 +1,21 @@ +package com.leisuretimedock.jsonem.forge; + +import com.leisuretimedock.jsonem.JsonEm; +import com.leisuretimedock.jsonem.forge.config.JsonEmConfig; +import dev.architectury.platform.forge.EventBuses; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; + +@Mod(JsonEm.MOD_ID) +public final class JsonEmForge { + public JsonEmForge(IEventBus eventBus) { + // Submit our event bus to let Architectury API register our content on the right time. + EventBuses.registerModEventBus(JsonEm.MOD_ID, eventBus); + //noinspection removal + ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, JsonEmConfig.spec, "jsonem.toml"); + // Run our common setup. + JsonEm.init(); + } +} diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java deleted file mode 100644 index 4e5f15f..0000000 --- a/forge/src/main/java/com/leisuretimedock/jsonem/forge/JsonemForge.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.leisuretimedock.jsonem.forge; - -import com.leisuretimedock.jsonem.Jsonem; -import dev.architectury.platform.forge.EventBuses; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; - -@Mod(Jsonem.MOD_ID) -public final class JsonemForge { - @SuppressWarnings("removal") - public JsonemForge() { - // Submit our event bus to let Architectury API register our content on the right time. - EventBuses.registerModEventBus(Jsonem.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); - - // Run our common setup. - Jsonem.init(); - } -} diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java new file mode 100644 index 0000000..f8539ed --- /dev/null +++ b/forge/src/main/java/com/leisuretimedock/jsonem/forge/config/JsonEmConfig.java @@ -0,0 +1,20 @@ +package com.leisuretimedock.jsonem.forge.config; + +import net.minecraftforge.common.ForgeConfigSpec; +import org.apache.commons.lang3.tuple.Pair; + +public class JsonEmConfig { + public static final ForgeConfigSpec spec; + public static final JsonEmConfig INSTANCE; + public final ForgeConfigSpec.BooleanValue DUMP_MODELS; + public JsonEmConfig(ForgeConfigSpec.Builder builder) { + builder.push("jsonem"); + DUMP_MODELS = builder.comment("Whether to dump models").define("dump_models", false); + builder.pop(); + } + static { + Pair config = new ForgeConfigSpec.Builder().configure(JsonEmConfig::new); + spec = config.getRight(); + INSTANCE = config.getLeft(); + } +} diff --git a/forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java b/forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java new file mode 100644 index 0000000..bf0ae75 --- /dev/null +++ b/forge/src/main/java/com/leisuretimedock/jsonem/forge/platform/ForgePlatformHelper.java @@ -0,0 +1,11 @@ +package com.leisuretimedock.jsonem.forge.platform; + +import com.leisuretimedock.jsonem.forge.config.JsonEmConfig; +import com.leisuretimedock.jsonem.platform.IPlatFormHelper; + +public class ForgePlatformHelper implements IPlatFormHelper { + @Override + public boolean shouldDumpModels() { + return JsonEmConfig.INSTANCE.DUMP_MODELS.get(); + } +} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index 0ba0e0d..842f504 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -11,7 +11,7 @@ authors = "FoundationGames, R3944Realms" description = ''' Migrate from Fabric 1.20.1 ''' -#logoFile = "" +logoFile = "assets/jsonem/icon.png" [[dependencies.jsonem]] modId = "forge" diff --git a/forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper b/forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper new file mode 100644 index 0000000..d5ed12c --- /dev/null +++ b/forge/src/main/resources/META-INF/services/com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper @@ -0,0 +1 @@ +com.leisuretimedock.jsonem.forge.platform.ForgePlatformHelper \ No newline at end of file diff --git a/forge/src/main/resources/assets/jsonem/icon.png b/forge/src/main/resources/assets/jsonem/icon.png new file mode 100644 index 0000000..54c2a1f Binary files /dev/null and b/forge/src/main/resources/assets/jsonem/icon.png differ diff --git a/settings.gradle b/settings.gradle index 1b9240f..fadf72c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,3 +12,7 @@ rootProject.name = 'jsonem' include 'common' include 'fabric' include 'forge' + +project(':common').projectDir = file('common') +project(':forge').projectDir = file('forge') +project(':fabric').projectDir = file('fabric')