diff --git a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java.patch b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java.patch index 0e859a434..3e878fba9 100644 --- a/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java.patch +++ b/patches/minecraft/net/minecraft/client/renderer/block/model/ModelBlockDefinition.java.patch @@ -9,3 +9,21 @@ } public ModelBlockDefinition(Collection p_i46221_1_) +@@ -178,11 +178,17 @@ + return this.field_178437_a; + } + ++ @Deprecated + public ModelRotation func_178432_b() + { + return this.field_178435_b; + } + ++ public net.minecraftforge.client.model.IModelState getState() ++ { ++ return this.field_178435_b; ++ } ++ + public boolean func_178433_c() + { + return this.field_178436_c; diff --git a/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java b/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java index ea00197f3..a69a2c377 100644 --- a/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java +++ b/src/main/java/net/minecraftforge/client/model/BlockStateLoader.java @@ -32,6 +32,7 @@ public class BlockStateLoader private static final Gson GSON = (new GsonBuilder()) .registerTypeAdapter(ForgeBlockStateV1.class, ForgeBlockStateV1.Deserializer.INSTANCE) .registerTypeAdapter(ForgeBlockStateV1.Variant.class, ForgeBlockStateV1.Variant.Deserializer.INSTANCE) + .registerTypeAdapter(TRSRTransformation.class, ForgeBlockStateV1.TRSRDeserializer.INSTANCE) .create(); /** * Loads a BlockStates json file. @@ -66,14 +67,13 @@ public class BlockStateLoader List mcVars = Lists.newArrayList(); for (ForgeBlockStateV1.Variant var : entry.getValue()) { - ModelRotation rot = var.getRotation().or(ModelRotation.X0_Y0); boolean uvLock = var.getUvLock().or(false); int weight = var.getWeight().or(1); - if (var.getModel() != null && var.getSubmodels().size() == 0 && var.getTextures().size() == 0 && var.getCustomData().size() == 0) - mcVars.add(new ModelBlockDefinition.Variant(var.getModel(), rot, uvLock, weight)); + if (var.getModel() != null && var.getSubmodels().size() == 0 && var.getTextures().size() == 0 && var.getCustomData().size() == 0 && var.getState().orNull() instanceof ModelRotation) + mcVars.add(new ModelBlockDefinition.Variant(var.getModel(), (ModelRotation)var.getState().get(), uvLock, weight)); else - mcVars.add(new ForgeVariant(var.getModel(), rot, uvLock, weight, var.getTextures(), var.getOnlyPartsVariant(), var.getCustomData())); + mcVars.add(new ForgeVariant(var.getModel(), var.getState().or(TRSRTransformation.identity()), uvLock, weight, var.getTextures(), var.getOnlyPartsVariant(), var.getCustomData())); } variants.add(new ModelBlockDefinition.Variants(entry.getKey(), mcVars)); } @@ -99,22 +99,22 @@ public class BlockStateLoader //This is here specifically so that we do not have a hard reference to ForgeBlockStateV1.Variant in ForgeVariant public static class SubModel { - private final ModelRotation rotation; + private final IModelState state; private final boolean uvLock; private final ImmutableMap textures; private final ResourceLocation model; private final ImmutableMap customData; - public SubModel(ModelRotation rotation, boolean uvLock, ImmutableMap textures, ResourceLocation model, ImmutableMap customData) + public SubModel(IModelState state, boolean uvLock, ImmutableMap textures, ResourceLocation model, ImmutableMap customData) { - this.rotation = rotation; + this.state = state; this.uvLock = uvLock; this.textures = textures; this.model = model; this.customData = customData; } - public ModelRotation getRotation() { return rotation; } + public IModelState getState() { return state; } public boolean isUVLock() { return uvLock; } public ImmutableMap getTextures() { return textures; } public ResourceLocation getModelLocation() { return model; } @@ -126,13 +126,15 @@ public class BlockStateLoader private final ImmutableMap textures; private final ImmutableMap parts; private final ImmutableMap customData; + private final IModelState state; - public ForgeVariant(ResourceLocation model, ModelRotation rotation, boolean uvLock, int weight, ImmutableMap textures, ImmutableMap parts, ImmutableMap customData) + public ForgeVariant(ResourceLocation model, IModelState state, boolean uvLock, int weight, ImmutableMap textures, ImmutableMap parts, ImmutableMap customData) { - super(model == null ? new ResourceLocation("builtin/missing") : model, rotation, uvLock, weight); + super(model == null ? new ResourceLocation("builtin/missing") : model, state instanceof ModelRotation ? (ModelRotation)state : ModelRotation.X0_Y0, uvLock, weight); this.textures = textures; this.parts = parts; this.customData = customData; + this.state = state; } protected IModel runModelHooks(IModel base, ImmutableMap textureMap, ImmutableMap customData) @@ -176,17 +178,12 @@ public class BlockStateLoader // Apply rotation of base model to submodels. // If baseRot is non-null, then that rotation will be applied instead of the base model's rotation. // This is used to allow replacing base model with a submodel when there is no base model for a variant. - ModelRotation baseRot = getRotation(); + IModelState baseTr = getState(); ImmutableMap.Builder> models = ImmutableMap.builder(); for (Entry entry : parts.entrySet()) { SubModel part = entry.getValue(); - Matrix4f matrix = new Matrix4f(baseRot.getMatrix()); - matrix.mul(part.getRotation().getMatrix()); - IModelState partState = new TRSRTransformation(matrix); - if (part.isUVLock()) partState = new ModelLoader.UVLock(partState); - IModel model = null; try { @@ -198,10 +195,19 @@ public class BlockStateLoader model = loader.getMissingModel(); // Will make it look weird, but that is good. } + IModelState partState = new ModelStateComposition(baseTr, part.getState()); + if (part.isUVLock()) partState = new ModelLoader.UVLock(partState); + models.put(entry.getKey(), Pair.of(runModelHooks(model, part.getTextures(), part.getCustomData()), partState)); } - return new MultiModel(hasBase ? base : null, baseRot, models.build()); + return new MultiModel(hasBase ? base : null, baseTr, models.build()); + } + + @Override + public IModelState getState() + { + return state; } @Override diff --git a/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java b/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java index 49715bd34..e7fde7459 100644 --- a/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java +++ b/src/main/java/net/minecraftforge/client/model/ForgeBlockStateV1.java @@ -4,11 +4,19 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import javax.vecmath.AxisAngle4d; +import javax.vecmath.AxisAngle4f; +import javax.vecmath.Matrix4f; +import javax.vecmath.Quat4f; +import javax.vecmath.Vector3f; + +import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; import net.minecraft.client.resources.model.ModelRotation; import net.minecraft.util.JsonUtils; import net.minecraft.util.ResourceLocation; @@ -22,6 +30,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -131,10 +140,10 @@ public class ForgeBlockStateV1 extends Marker ForgeBlockStateV1.Variant part = partList.get(0); // Must keep old rotation for the part, because the base variant's rotation is applied to the parts already. - Optional rotation = part.rotation; + Optional state = part.state; part.sync(v); part.simpleSubmodels.clear(); - part.rotation = rotation; + part.state = state; } } @@ -253,7 +262,7 @@ public class ForgeBlockStateV1 extends Marker private ResourceLocation model = null; private boolean modelSet = false; - private Optional rotation = Optional.absent(); + private Optional state = Optional.absent(); private Optional uvLock = Optional.absent(); private Optional weight = Optional.absent(); private Map textures = Maps.newHashMap(); @@ -270,7 +279,7 @@ public class ForgeBlockStateV1 extends Marker { this.model = other.model; this.modelSet = other.modelSet; - this.rotation = other.rotation; + this.state = other.state; this.uvLock = other.uvLock; this.weight = other.weight; this.textures.putAll(other.textures); @@ -289,7 +298,7 @@ public class ForgeBlockStateV1 extends Marker this.model = parent.model; this.modelSet = parent.modelSet; } - if (!this.rotation.isPresent()) this.rotation = parent.rotation; + if (!this.state.isPresent()) this.state = parent.state; if (!this.uvLock.isPresent()) this.uvLock = parent.uvLock; if (!this.weight.isPresent()) this.weight = parent.weight; @@ -346,7 +355,7 @@ public class ForgeBlockStateV1 extends Marker protected SubModel asGenericSubModel() { - return new SubModel(rotation.or(ModelRotation.X0_Y0), uvLock.or(false), getTextures(), model, getCustomData()); + return new SubModel(state.or(TRSRTransformation.identity()), uvLock.or(false), getTextures(), model, getCustomData()); } /** @@ -430,11 +439,132 @@ public class ForgeBlockStateV1 extends Marker { // Load rotation values. int x = JsonUtils.getJsonObjectIntegerFieldValueOrDefault(json, "x", 0); int y = JsonUtils.getJsonObjectIntegerFieldValueOrDefault(json, "y", 0); - ret.rotation = Optional.of(ModelRotation.getModelRotation(x, y)); - if (ret.rotation == null) + ret.state = Optional.of(new TRSRTransformation(ModelRotation.getModelRotation(x, y))); + if (!ret.state.isPresent()) throw new JsonParseException("Invalid BlockModelRotation x: " + x + " y: " + y); } + if (json.has("transform")) + { + if (json.get("transform").isJsonPrimitive() && json.get("transform").getAsJsonPrimitive().isString()) + { + String transform = json.get("transform").getAsString(); + // Note: these strings might change to a full-blown resource locations in the future, and move from here to some json somewhere + if (transform.equals("identity")) + { + ret.state = Optional.of(TRSRTransformation.identity()); + } + else if (transform.equals("forge:default-block")) + { + IModelState thirdperson = TRSRTransformation.blockCenterToCorner(new TRSRTransformation( + new Vector3f(0, 1.5f / 16, -2.75f / 16), + TRSRTransformation.quatFromYXZDegrees(new Vector3f(10, -45, 170)), + new Vector3f(0.375f, 0.375f, 0.375f), + null)); + ret.state = Optional.of(new IPerspectiveState.Impl(TRSRTransformation.identity(), ImmutableMap.of(TransformType.THIRD_PERSON, thirdperson))); + } + else if (transform.equals("forge:default-item")) + { + IModelState thirdperson = TRSRTransformation.blockCenterToCorner(new TRSRTransformation( + new Vector3f(0, 1f / 16, -3f / 16), + TRSRTransformation.quatFromYXZDegrees(new Vector3f(-90, 0, 0)), + new Vector3f(0.55f, 0.55f, 0.55f), + null)); + IModelState firstperson = TRSRTransformation.blockCenterToCorner(new TRSRTransformation( + new Vector3f(0, 4f / 16, 2f / 16), + TRSRTransformation.quatFromYXZDegrees(new Vector3f(0, -135, 25)), + new Vector3f(1.7f, 1.7f, 1.7f), + null)); + ret.state = Optional.of(new IPerspectiveState.Impl(TRSRTransformation.identity(), ImmutableMap.of(TransformType.THIRD_PERSON, thirdperson, TransformType.FIRST_PERSON, firstperson))); + } + else if (transform.equals("forge:default-tool")) + { + IModelState thirdperson = TRSRTransformation.blockCenterToCorner(new TRSRTransformation( + new Vector3f(0, 1.25f / 16, -3.5f / 16), + TRSRTransformation.quatFromYXZDegrees(new Vector3f(0, 90, -35)), + new Vector3f(0.85f, 0.85f, 0.85f), + null)); + IModelState firstperson = TRSRTransformation.blockCenterToCorner(new TRSRTransformation( + new Vector3f(0, 4f / 16, 2f / 16), + TRSRTransformation.quatFromYXZDegrees(new Vector3f(0, -135, 25)), + new Vector3f(1.7f, 1.7f, 1.7f), + null)); + ret.state = Optional.of(new IPerspectiveState.Impl(TRSRTransformation.identity(), ImmutableMap.of(TransformType.THIRD_PERSON, thirdperson, TransformType.FIRST_PERSON, firstperson))); + } + else + { + throw new JsonParseException("transform: unknown default string: " + transform); + } + } + else if (!json.get("transform").isJsonObject()) + { + try + { + TRSRTransformation base = context.deserialize(json.get("transform"), TRSRTransformation.class); + ret.state = Optional.of(TRSRTransformation.blockCenterToCorner(base)); + } + catch (JsonParseException e) + { + throw new JsonParseException("transform: expected a string, object or valid base transformation, got: " + json.get("transform")); + } + } + else + { + JsonObject transform = json.get("transform").getAsJsonObject(); + EnumMap transforms = Maps.newEnumMap(TransformType.class); + if(transform.has("thirdperson")) + { + TRSRTransformation t = context.deserialize(transform.get("thirdperson"), TRSRTransformation.class); + transform.remove("thirdperson"); + transforms.put(TransformType.THIRD_PERSON, TRSRTransformation.blockCenterToCorner(t)); + } + if(transform.has("firstperson")) + { + TRSRTransformation t = context.deserialize(transform.get("firstperson"), TRSRTransformation.class); + transform.remove("firstperson"); + transforms.put(TransformType.FIRST_PERSON, TRSRTransformation.blockCenterToCorner(t)); + } + if(transform.has("gui")) + { + TRSRTransformation t = context.deserialize(transform.get("gui"), TRSRTransformation.class); + transform.remove("gui"); + transforms.put(TransformType.GUI, TRSRTransformation.blockCenterToCorner(t)); + } + if(transform.has("head")) + { + TRSRTransformation t = context.deserialize(transform.get("head"), TRSRTransformation.class); + transform.remove("head"); + transforms.put(TransformType.HEAD, TRSRTransformation.blockCenterToCorner(t)); + } + int k = transform.entrySet().size(); + if(transform.has("matrix")) k--; + if(transform.has("translation")) k--; + if(transform.has("rotation")) k--; + if(transform.has("scale")) k--; + if(transform.has("post-rotation")) k--; + if(k > 0) + { + throw new JsonParseException("transform: allowed keys: 'thirdperson', 'firstperson', 'gui', 'head', 'matrix', 'translation', 'rotation', 'scale', 'post-rotation'"); + } + TRSRTransformation base = TRSRTransformation.identity(); + if(!transform.entrySet().isEmpty()) + { + base = context.deserialize(transform, TRSRTransformation.class); + base = TRSRTransformation.blockCenterToCorner(base); + } + IModelState state; + if(transforms.isEmpty()) + { + state = base; + } + else + { + state = new IPerspectiveState.Impl(base, Maps.immutableEnumMap(transforms)); + } + ret.state = Optional.of(state); + } + } + if (json.has("uvlock")) { // Load uvlock. ret.uvLock = Optional.of(JsonUtils.getJsonObjectBooleanFieldValue(json, "uvlock")); @@ -518,11 +648,194 @@ public class ForgeBlockStateV1 extends Marker public ResourceLocation getModel() { return model; } public boolean isModelSet() { return modelSet; } - public Optional getRotation() { return rotation; } + public Optional getState() { return state; } public Optional getUvLock() { return uvLock; } public Optional getWeight() { return weight; } public ImmutableMap getTextures() { return ImmutableMap.copyOf(textures); } public ImmutableMap> getSubmodels() { return ImmutableMap.copyOf(submodels); } public ImmutableMap getCustomData() { return ImmutableMap.copyOf(customData); } } -} \ No newline at end of file + + public static class TRSRDeserializer implements JsonDeserializer + { + public static TRSRDeserializer INSTANCE = new TRSRDeserializer(); + + public TRSRTransformation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException + { + if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString()) + { + String transform = json.getAsString(); + if(transform.equals("identity")) + { + return TRSRTransformation.identity(); + } + else + { + throw new JsonParseException("TRSR: unknown default string: " + transform); + } + } + if (json.isJsonArray()) + { + // direct matrix array + return new TRSRTransformation(parseMatrix(json)); + } + if (!json.isJsonObject()) throw new JsonParseException("TRSR: expected array or object, got: " + json); + JsonObject obj = json.getAsJsonObject(); + TRSRTransformation ret; + if (obj.has("matrix")) + { + // matrix as a sole key + ret = new TRSRTransformation(parseMatrix(obj.get("matrix"))); + obj.remove("matrix"); + if (obj.entrySet().size() != 0) + { + throw new JsonParseException("TRSR: can't combine matrix and other keys"); + } + return ret; + } + Vector3f translation = null; + Quat4f leftRot = null; + Vector3f scale = null; + Quat4f rightRot = null; + if (obj.has("translation")) + { + translation = new Vector3f(parseFloatArray(obj.get("translation"), 3, "Translation")); + obj.remove("translation"); + } + if (obj.has("rotation")) + { + leftRot = parseRotation(obj.get("rotation")); + obj.remove("rotation"); + } + if (obj.has("scale")) + { + if(!obj.get("scale").isJsonArray()) + { + try + { + float s = obj.get("scale").getAsNumber().floatValue(); + scale = new Vector3f(s, s, s); + } + catch (ClassCastException ex) + { + throw new JsonParseException("TRSR scale: expected number or array, got: " + obj.get("scale")); + } + } + else + { + scale = new Vector3f(parseFloatArray(obj.get("scale"), 3, "Scale")); + } + obj.remove("scale"); + } + if (obj.has("post-rotation")) + { + rightRot = parseRotation(obj.get("post-rotation")); + obj.remove("post-rotation"); + } + if (!obj.entrySet().isEmpty()) throw new JsonParseException("TRSR: can either have single 'matrix' key, or a combination of 'translation', 'rotation', 'scale', 'post-rotation'"); + return new TRSRTransformation(translation, leftRot, scale, rightRot); + } + + public static Matrix4f parseMatrix(JsonElement e) + { + if (!e.isJsonArray()) throw new JsonParseException("Matrix: expected an array, got: " + e); + JsonArray m = e.getAsJsonArray(); + if (m.size() != 3) throw new JsonParseException("Matrix: expected an array of length 3, got: " + m.size()); + Matrix4f ret = new Matrix4f(); + for (int i = 0; i < 3; i++) + { + if (!m.get(i).isJsonArray()) throw new JsonParseException("Matrix row: expected an array, got: " + m.get(i)); + JsonArray r = m.get(i).getAsJsonArray(); + if (r.size() != 4) throw new JsonParseException("Matrix row: expected an array of length 4, got: " + r.size()); + for (int j = 0; j < 4; j++) + { + try + { + ret.setElement(i, j, r.get(j).getAsNumber().floatValue()); + } + catch (ClassCastException ex) + { + throw new JsonParseException("Matrix element: expected number, got: " + r.get(j)); + } + } + } + return ret; + } + + public static float[] parseFloatArray(JsonElement e, int length, String prefix) + { + if (!e.isJsonArray()) throw new JsonParseException(prefix + ": expected an array, got: " + e); + JsonArray t = e.getAsJsonArray(); + if (t.size() != length) throw new JsonParseException(prefix + ": expected an array of length " + length + ", got: " + t.size()); + float[] ret = new float[length]; + for (int i = 0; i < length; i++) + { + try + { + ret[i] = t.get(i).getAsNumber().floatValue(); + } + catch (ClassCastException ex) + { + throw new JsonParseException(prefix + " element: expected number, got: " + t.get(i)); + } + } + return ret; + } + + public static Quat4f parseAxisRotation(JsonElement e) + { + if (!e.isJsonObject()) throw new JsonParseException("Axis rotation: object expected, got: " + e); + JsonObject obj = e.getAsJsonObject(); + if (obj.entrySet().size() != 1) throw new JsonParseException("Axis rotation: expected single axis object, got: " + e); + Map.Entry entry = obj.entrySet().iterator().next(); + Quat4f ret = new Quat4f(); + try + { + if (entry.getKey().equals("x")) + { + ret.set(new AxisAngle4d(1, 0, 0, Math.toRadians(entry.getValue().getAsNumber().floatValue()))); + } + else if (entry.getKey().equals("y")) + { + ret.set(new AxisAngle4d(0, 1, 0, Math.toRadians(entry.getValue().getAsNumber().floatValue()))); + } + else if (entry.getKey().equals("z")) + { + ret.set(new AxisAngle4d(0, 0, 1, Math.toRadians(entry.getValue().getAsNumber().floatValue()))); + } + else throw new JsonParseException("Axis rotation: expected single axis key, got: " + entry.getKey()); + } + catch(ClassCastException ex) + { + throw new JsonParseException("Axis rotation value: expected number, got: " + entry.getValue()); + } + return ret; + } + + public static Quat4f parseRotation(JsonElement e) + { + if (e.isJsonArray()) + { + if (e.getAsJsonArray().get(0).isJsonObject()) + { + Quat4f ret = new Quat4f(0, 0, 0, 1); + for (JsonElement a : e.getAsJsonArray()) + { + ret.mul(parseAxisRotation(a)); + } + return ret; + } + else + { + // quaternion + return new Quat4f(parseFloatArray(e, 4, "Rotation")); + } + } + else if (e.isJsonObject()) + { + return parseAxisRotation(e); + } + else throw new JsonParseException("Rotation: expected array or object, got: " + e); + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/IPerspectiveState.java b/src/main/java/net/minecraftforge/client/model/IPerspectiveState.java index 5d030ec88..1dbeb77f2 100644 --- a/src/main/java/net/minecraftforge/client/model/IPerspectiveState.java +++ b/src/main/java/net/minecraftforge/client/model/IPerspectiveState.java @@ -5,6 +5,7 @@ import java.util.Map; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; @@ -56,5 +57,11 @@ public interface IPerspectiveState extends IModelState if(state == null) state = TRSRTransformation.identity(); return state; } + + @Override + public String toString() + { + return Objects.toStringHelper(this.getClass()).add("parent", parent).add("states", states).toString(); + } } } diff --git a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java index 20d1255d2..c2eaf5350 100644 --- a/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java +++ b/src/main/java/net/minecraftforge/client/model/ItemLayerModel.java @@ -146,7 +146,7 @@ public class ItemLayerModel implements IRetexturableModel { { TRSRTransformation tr = transforms.get(cameraTransformType); Matrix4f mat = null; - if(tr != null && tr != TRSRTransformation.identity()) mat = tr.getMatrix(); + if(tr != null && tr != TRSRTransformation.identity()) mat = tr.blockCornerToCenter(tr).getMatrix(); return Pair.of((IBakedModel)this, mat); } } diff --git a/src/main/java/net/minecraftforge/client/model/MapModelState.java b/src/main/java/net/minecraftforge/client/model/MapModelState.java index 72c874e0b..66f86f7b2 100644 --- a/src/main/java/net/minecraftforge/client/model/MapModelState.java +++ b/src/main/java/net/minecraftforge/client/model/MapModelState.java @@ -9,21 +9,32 @@ import com.google.common.collect.ImmutableMap; */ public class MapModelState implements IModelState { - private final ImmutableMap map; - private final TRSRTransformation def; + private final ImmutableMap map; + private final IModelState def; - public MapModelState(Map map) + public MapModelState(Map map) { this(map, TRSRTransformation.identity()); } - public MapModelState(Map map, TRSRTransformation def) + public MapModelState(Map map, TRSRTransformation def) { - this.map = ImmutableMap.copyOf(map); + this(map, (IModelState)def); + } + + public MapModelState(Map map, IModelState def) + { + this.map = ImmutableMap.copyOf(map); this.def = def; } public TRSRTransformation apply(IModelPart part) + { + if(!map.containsKey(part)) return def.apply(part); + return map.get(part).apply(part); + } + + public IModelState getState(IModelPart part) { if(!map.containsKey(part)) return def; return map.get(part); diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index a84862a84..7efdcff3a 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -322,7 +322,11 @@ public class ModelLoader extends ModelBakery ModelBlock model = this.model; if(model == null) return getMissingModel().bake(state, format, bakedTextureGetter); ItemCameraTransforms transforms = new ItemCameraTransforms(model.getThirdPersonTransform(), model.getFirstPersonTransform(), model.getHeadTransform(), model.getInGuiTransform()); - if(hasItemModel(model)) return new ItemLayerModel(model).bake(new IPerspectiveState.Impl(state, transforms), format, bakedTextureGetter); + if(hasItemModel(model)) + { + IPerspectiveState perState = state instanceof IPerspectiveState ? (IPerspectiveState)state : new IPerspectiveState.Impl(state, transforms); + return new ItemLayerModel(model).bake(perState, format, bakedTextureGetter); + } if(isCustomRenderer(model)) return new IFlexibleBakedModel.Wrapper(new BuiltInModel(transforms), format); return bakeNormal(model, state.apply(this), format, bakedTextureGetter, state instanceof UVLock); } @@ -475,7 +479,7 @@ public class ModelLoader extends ModelBakery public WeightedRandomModel(ModelResourceLocation parent, Variants variants) { this.variants = variants.getVariants(); - ImmutableMap.Builder builder = ImmutableMap.builder(); + ImmutableMap.Builder builder = ImmutableMap.builder(); for (Variant v : (List)variants.getVariants()) { ResourceLocation loc = v.getModelLocation(); @@ -505,14 +509,14 @@ public class ModelLoader extends ModelBakery model = new WeightedPartWrapper(model); models.add(model); - builder.put(model, new TRSRTransformation(v.getRotation())); + builder.put(model, v.getState()); } if (models.size() == 0) //If all variants are missing, add one with the missing model and default rotation. { IModel missing = getMissingModel(); models.add(missing); - builder.put(missing, new TRSRTransformation(ModelRotation.X0_Y0)); + builder.put(missing, TRSRTransformation.identity()); } defaultState = new MapModelState(builder.build()); @@ -535,6 +539,15 @@ public class ModelLoader extends ModelBakery return state; } + private IModelState getState(IModelState state, IModelPart part) + { + if(state instanceof MapModelState) + { + return ((MapModelState)state).getState(part); + } + return state; + } + public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function bakedTextureGetter) { if(!Attributes.moreSpecific(format, Attributes.DEFAULT_BAKED_FORMAT)) @@ -545,14 +558,14 @@ public class ModelLoader extends ModelBakery { Variant v = variants.get(0); IModel model = models.get(0); - return model.bake(addUV(v.isUvLocked(), state.apply(model)), format, bakedTextureGetter); + return model.bake(addUV(v.isUvLocked(), getState(state, model)), format, bakedTextureGetter); } WeightedBakedModel.Builder builder = new WeightedBakedModel.Builder(); for(int i = 0; i < variants.size(); i++) { IModel model = models.get(i); Variant v = variants.get(i); - builder.add(model.bake(addUV(v.isUvLocked(), state.apply(model)), format, bakedTextureGetter), variants.get(i).getWeight()); + builder.add(model.bake(addUV(v.isUvLocked(), getState(state, model)), format, bakedTextureGetter), variants.get(i).getWeight()); } return new FlexibleWeightedBakedModel(builder.build(), Attributes.DEFAULT_BAKED_FORMAT); } diff --git a/src/main/java/net/minecraftforge/client/model/ModelStateComposition.java b/src/main/java/net/minecraftforge/client/model/ModelStateComposition.java new file mode 100644 index 000000000..fb4dbd223 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/ModelStateComposition.java @@ -0,0 +1,18 @@ +package net.minecraftforge.client.model; + +public class ModelStateComposition implements IModelState +{ + private final IModelState first; + private final IModelState second; + + public ModelStateComposition(IModelState first, IModelState second) + { + this.first = first; + this.second = second; + } + + public TRSRTransformation apply(IModelPart part) + { + return first.apply(part).compose(second.apply(part)); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/MultiModel.java b/src/main/java/net/minecraftforge/client/model/MultiModel.java index c11572652..207eb0124 100644 --- a/src/main/java/net/minecraftforge/client/model/MultiModel.java +++ b/src/main/java/net/minecraftforge/client/model/MultiModel.java @@ -183,7 +183,7 @@ public class MultiModel implements IModel IFlexibleBakedModel bakedBase = null; if (base != null) - bakedBase = base.bake(baseState, format, bakedTextureGetter); + bakedBase = base.bake(state, format, bakedTextureGetter); ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); @@ -199,7 +199,7 @@ public class MultiModel implements IModel @Override public IModelState getDefaultState() { - return ModelRotation.X0_Y0; + return baseState; } /** diff --git a/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java b/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java index ea53d8fd0..b55d41ff1 100644 --- a/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java +++ b/src/main/java/net/minecraftforge/client/model/TRSRTransformation.java @@ -14,6 +14,8 @@ import net.minecraft.util.Vec3i; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; +import com.google.common.base.Objects; + /* * Interpolation-friendly affine transformation. * If created with matrix, should successfully decompose it to a composition @@ -53,7 +55,13 @@ public class TRSRTransformation implements IModelState, ITransformation public TRSRTransformation(ItemTransformVec3f transform) { - this(transform.translation, quatFromYXZDegrees(transform.rotation), transform.scale, null); + this(getMatrix(transform)); + } + + public static Matrix4f getMatrix(ItemTransformVec3f transform) + { + TRSRTransformation ret = new TRSRTransformation(transform.translation, quatFromYXZDegrees(transform.rotation), transform.scale, null); + return blockCenterToCorner(ret).getMatrix(); } public TRSRTransformation(ModelRotation rotation) @@ -467,4 +475,45 @@ public class TRSRTransformation implements IModelState, ITransformation // FIXME check if this is good enough return vertexIndex; } + + @Override + public String toString() + { + genCheck(); + return Objects.toStringHelper(this.getClass()) + .add("matrix", matrix) + .add("translation", translation) + .add("leftRot", leftRot) + .add("scale", scale) + .add("rightRot", rightRot) + .toString(); + } + + /** + * convert transformation from assuming center-block system to corner-block system + */ + public static TRSRTransformation blockCenterToCorner(TRSRTransformation transform) + { + Matrix4f ret = new Matrix4f(transform.getMatrix()), tmp = new Matrix4f(); + tmp.setIdentity(); + tmp.m03 = tmp.m13 = tmp.m23 = .5f; + ret.mul(tmp, ret); + tmp.m03 = tmp.m13 = tmp.m23 = -.5f; + ret.mul(tmp); + return new TRSRTransformation(ret); + } + + /** + * convert transformation from assuming corner-block system to center-block system + */ + public static TRSRTransformation blockCornerToCenter(TRSRTransformation transform) + { + Matrix4f ret = new Matrix4f(transform.getMatrix()), tmp = new Matrix4f(); + tmp.setIdentity(); + tmp.m03 = tmp.m13 = tmp.m23 = -.5f; + ret.mul(tmp, ret); + tmp.m03 = tmp.m13 = tmp.m23 = .5f; + ret.mul(tmp); + return new TRSRTransformation(ret); + } } diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java index f19ea021c..fa107a45e 100644 --- a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java @@ -17,11 +17,13 @@ import javax.vecmath.Matrix4f; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.resources.IResource; import net.minecraft.client.resources.IResourceManager; +import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; @@ -33,6 +35,8 @@ import net.minecraftforge.client.model.IModel; import net.minecraftforge.client.model.IModelCustomData; import net.minecraftforge.client.model.IModelPart; import net.minecraftforge.client.model.IModelState; +import net.minecraftforge.client.model.IPerspectiveAwareModel; +import net.minecraftforge.client.model.IPerspectiveState; import net.minecraftforge.client.model.IRetexturableModel; import net.minecraftforge.client.model.ISmartBlockModel; import net.minecraftforge.client.model.ISmartItemModel; @@ -54,7 +58,6 @@ import net.minecraftforge.fml.common.FMLLog; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; -import org.apache.logging.log4j.Level; import org.lwjgl.BufferUtils; import com.google.common.base.Function; @@ -65,7 +68,6 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; import com.google.common.collect.Multimap; /* @@ -194,11 +196,25 @@ public class B3DLoader implements ICustomModelLoader { private final Animation animation; private final int frame; + private final IModelState parent; public B3DState(Animation animation, int frame) + { + this(animation, frame, null); + } + + public B3DState(Animation animation, int frame, IModelState parent) { this.animation = animation; this.frame = frame; + this.parent = getParent(parent); + } + + private IModelState getParent(IModelState parent) + { + if (parent == null) return null; + else if (parent instanceof B3DState) return ((B3DState)parent).parent; + return parent; } public Animation getAnimation() @@ -217,6 +233,10 @@ public class B3DLoader implements ICustomModelLoader { throw new IllegalArgumentException("B3DState can only be applied to b3d models"); } + if(parent != null) + { + return parent.apply(part).compose(getNodeMatrix(((PartWrapper)part).getNode())); + } return getNodeMatrix(((PartWrapper)part).getNode()); } @@ -452,7 +472,7 @@ public class B3DLoader implements ICustomModelLoader } } - private static class BakedWrapper implements IFlexibleBakedModel, ISmartBlockModel, ISmartItemModel + private static class BakedWrapper implements IFlexibleBakedModel, ISmartBlockModel, ISmartItemModel, IPerspectiveAwareModel { private final B3DLoader.Wrapper model; private final IModelState state; @@ -638,7 +658,7 @@ public class B3DLoader implements ICustomModelLoader { if(!cache.containsKey(frame)) { - cache.put(frame, new BakedWrapper(model, new B3DState(model.getNode().getAnimation(), frame), format, textures)); + cache.put(frame, new BakedWrapper(model, new B3DState(model.getNode().getAnimation(), frame, state), format, textures)); } return cache.get(frame); } @@ -653,5 +673,14 @@ public class B3DLoader implements ICustomModelLoader // TODO specify how to get B3DState from ItemStack return this; } + + public Pair handlePerspective(TransformType cameraTransformType) + { + if(state instanceof IPerspectiveState) + { + return Pair.of((IBakedModel)this, TRSRTransformation.blockCornerToCenter(((IPerspectiveState)state).forPerspective(cameraTransformType).apply(model)).getMatrix()); + } + return Pair.of((IBakedModel)this, null); + } } } diff --git a/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json b/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json index 99e0306c6..4fcba2edd 100644 --- a/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json +++ b/src/test/resources/assets/forgedebugitemlayermodel/blockstates/TestItem.json @@ -3,6 +3,19 @@ "variants": { "inventory": { "model": "forge:item-layer", + "transform": "forge:default-tool", + /*"transform": { + "thirdperson": { + "translation": [ 0, 0.078125, -0.21875 ], + "rotation": [ {"y": -90}, {"z": -35} ], + "scale": 0.85 + }, + "firstperson": { + "translation": [ 0, 0.25, 0.125 ], + "rotation": [ {"y": 135}, {"z": 25} ], + "scale": 1.7 + } + },*/ "textures": { "layer0": "items/diamond_pickaxe" } diff --git a/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json b/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json index c6db8d825..4b73e67cc 100644 --- a/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json +++ b/src/test/resources/assets/forgedebugmodelloaderregistry/blockstates/CustomModelBlock.json @@ -8,11 +8,20 @@ "model": "forgedebugmodelloaderregistry:chest.b3d" }, "variants": { - "normal": { - "dummy": "" - }, - "inventory": { - "dummy": "" - } + "normal": [{ + "transform": { + "rotation": {"y": 45} + } + }], + "inventory": [{ + /*"transform": { + "thirdperson": { + "translation": [ 0, 0.09375, -0.171875 ], + "rotation": [ {"y": -45}, {"x": 10}, {"z": 170} ], + "scale": 0.375 + } + }*/ + "transform": "forge:default-block" + }] } }