Merge pull request #1978 from RainWarrior/model-rotation

Custom transformations in forge blockstate json.
This commit is contained in:
Fry 2015-06-30 22:14:31 +03:00
commit 20bf76464a
13 changed files with 538 additions and 52 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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));
}
}

View File

@ -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;
}
/**

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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"
}

View File

@ -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"
}]
}
}