Merge pull request #1978 from RainWarrior/model-rotation
Custom transformations in forge blockstate json.
This commit is contained in:
commit
20bf76464a
13 changed files with 538 additions and 52 deletions
|
@ -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;
|
||||
|
|
|
@ -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<ModelBlockDefinition.Variant> 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<String, String> textures;
|
||||
private final ResourceLocation model;
|
||||
private final ImmutableMap<String, String> customData;
|
||||
|
||||
public SubModel(ModelRotation rotation, boolean uvLock, ImmutableMap<String, String> textures, ResourceLocation model, ImmutableMap<String, String> customData)
|
||||
public SubModel(IModelState state, boolean uvLock, ImmutableMap<String, String> textures, ResourceLocation model, ImmutableMap<String, String> 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<String, String> getTextures() { return textures; }
|
||||
public ResourceLocation getModelLocation() { return model; }
|
||||
|
@ -126,13 +126,15 @@ public class BlockStateLoader
|
|||
private final ImmutableMap<String, String> textures;
|
||||
private final ImmutableMap<String, SubModel> parts;
|
||||
private final ImmutableMap<String, String> customData;
|
||||
private final IModelState state;
|
||||
|
||||
public ForgeVariant(ResourceLocation model, ModelRotation rotation, boolean uvLock, int weight, ImmutableMap<String, String> textures, ImmutableMap<String, SubModel> parts, ImmutableMap<String, String> customData)
|
||||
public ForgeVariant(ResourceLocation model, IModelState state, boolean uvLock, int weight, ImmutableMap<String, String> textures, ImmutableMap<String, SubModel> parts, ImmutableMap<String, String> 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<String, String> textureMap, ImmutableMap<String, String> 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<String, Pair<IModel, IModelState>> models = ImmutableMap.builder();
|
||||
for (Entry<String, SubModel> 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
|
||||
|
|
|
@ -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<ModelRotation> rotation = part.rotation;
|
||||
Optional<IModelState> 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<ModelRotation> rotation = Optional.absent();
|
||||
private Optional<IModelState> state = Optional.absent();
|
||||
private Optional<Boolean> uvLock = Optional.absent();
|
||||
private Optional<Integer> weight = Optional.absent();
|
||||
private Map<String, String> 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.<IModelState>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.<IModelState>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.<IModelState>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.<IModelState>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.<IModelState>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.<IModelState>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<TransformType, IModelState> 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<ModelRotation> getRotation() { return rotation; }
|
||||
public Optional<IModelState> getState() { return state; }
|
||||
public Optional<Boolean> getUvLock() { return uvLock; }
|
||||
public Optional<Integer> getWeight() { return weight; }
|
||||
public ImmutableMap<String, String> getTextures() { return ImmutableMap.copyOf(textures); }
|
||||
public ImmutableMap<String, List<ForgeBlockStateV1.Variant>> getSubmodels() { return ImmutableMap.copyOf(submodels); }
|
||||
public ImmutableMap<String, String> getCustomData() { return ImmutableMap.copyOf(customData); }
|
||||
}
|
||||
}
|
||||
|
||||
public static class TRSRDeserializer implements JsonDeserializer<TRSRTransformation>
|
||||
{
|
||||
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<String, JsonElement> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,21 +9,32 @@ import com.google.common.collect.ImmutableMap;
|
|||
*/
|
||||
public class MapModelState implements IModelState
|
||||
{
|
||||
private final ImmutableMap<IModelPart, TRSRTransformation> map;
|
||||
private final TRSRTransformation def;
|
||||
private final ImmutableMap<IModelPart, IModelState> map;
|
||||
private final IModelState def;
|
||||
|
||||
public MapModelState(Map<IModelPart, TRSRTransformation> map)
|
||||
public MapModelState(Map<IModelPart, ? extends IModelState> map)
|
||||
{
|
||||
this(map, TRSRTransformation.identity());
|
||||
}
|
||||
|
||||
public MapModelState(Map<IModelPart, TRSRTransformation> map, TRSRTransformation def)
|
||||
public MapModelState(Map<IModelPart, ? extends IModelState> map, TRSRTransformation def)
|
||||
{
|
||||
this.map = ImmutableMap.copyOf(map);
|
||||
this(map, (IModelState)def);
|
||||
}
|
||||
|
||||
public MapModelState(Map<IModelPart, ? extends IModelState> map, IModelState def)
|
||||
{
|
||||
this.map = ImmutableMap.<IModelPart, IModelState>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);
|
||||
|
|
|
@ -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<IModelPart, TRSRTransformation> builder = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<IModelPart, IModelState> builder = ImmutableMap.builder();
|
||||
for (Variant v : (List<Variant>)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<ResourceLocation, TextureAtlasSprite> 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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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<String, IFlexibleBakedModel> mapBuilder = ImmutableMap.builder();
|
||||
|
||||
|
@ -199,7 +199,7 @@ public class MultiModel implements IModel
|
|||
@Override
|
||||
public IModelState getDefaultState()
|
||||
{
|
||||
return ModelRotation.X0_Y0;
|
||||
return baseState;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<IBakedModel, Matrix4f> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue