Implemented interpolation of TRSR transformations; B3D: added interpolation capabilities to B3DState, animated TESR example in ModelAnimationTest (pure TESR right now, no separation inside the example model between the static and dynamic parts right now).

This commit is contained in:
RainWarrior 2015-11-16 06:50:54 +03:00
parent 1246f1a791
commit f9024d2eb3
4 changed files with 717 additions and 106 deletions

View File

@ -3,6 +3,8 @@ package net.minecraftforge.client.model;
import javax.vecmath.Matrix3f;
import javax.vecmath.Matrix4f;
import javax.vecmath.Quat4f;
import javax.vecmath.Tuple3f;
import javax.vecmath.Tuple4f;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;
@ -181,7 +183,6 @@ public class TRSRTransformation implements IModelState, ITransformation
{
Matrix4f res = new Matrix4f(), t = new Matrix4f();
res.setIdentity();
if(translation != null) res.setTranslation(translation);
if(leftRot != null)
{
t.set(leftRot);
@ -200,6 +201,7 @@ public class TRSRTransformation implements IModelState, ITransformation
t.set(rightRot);
res.mul(t);
}
if(translation != null) res.setTranslation(translation);
return res;
}
@ -221,7 +223,8 @@ public class TRSRTransformation implements IModelState, ITransformation
b.set(m);
b.mul(t);
sortSingularValues(b, v);
// FIXME: this doesn't work correctly for some reason; not crucial, so disabling for now; investigate in the future.
//sortSingularValues(b, v);
Pair<Float, Float> p;
@ -281,7 +284,7 @@ public class TRSRTransformation implements IModelState, ITransformation
return f;
}
private static final float eps = 1e-7f;
private static final float eps = 1e-6f;
private static final float g = 3f + 2f * (float)Math.sqrt(2);
private static final float cs = (float)Math.cos(Math.PI / 8);
private static final float ss = (float)Math.sin(Math.PI / 8);
@ -365,50 +368,59 @@ public class TRSRTransformation implements IModelState, ITransformation
Quat4f qt = new Quat4f(), ret = new Quat4f(0, 0, 0, 1);
Pair<Float, Float> p;
// 01
p = approxGivensQuat(m.m00, .5f * (m.m01 + m.m10), m.m11);
qt.set(0, 0, p.getLeft(), p.getRight());
//qt.normalize();
ret.mul(qt);
//t.set(qt);
t.setIdentity();
t.m00 = qt.w * qt.w - qt.z * qt.z;
t.m11 = t.m00;
t.m10 = 2 * qt.z * qt.w;
t.m01 = -t.m10;
t.m22 = qt.w * qt.w + qt.z * qt.z;
m.mul(m, t);
t.transpose();
m.mul(t, m);
if(m.m01 * m.m01 + m.m10 * m.m10 > eps)
{
p = approxGivensQuat(m.m00, .5f * (m.m01 + m.m10), m.m11);
qt.set(0, 0, p.getLeft(), p.getRight());
//qt.normalize();
ret.mul(qt);
//t.set(qt);
t.setIdentity();
t.m00 = qt.w * qt.w - qt.z * qt.z;
t.m11 = t.m00;
t.m10 = 2 * qt.z * qt.w;
t.m01 = -t.m10;
t.m22 = qt.w * qt.w + qt.z * qt.z;
m.mul(m, t);
t.transpose();
m.mul(t, m);
}
// 02
p = approxGivensQuat(m.m00, .5f * (m.m02 + m.m20), m.m22);
qt.set(0, -p.getLeft(), 0, p.getRight());
//qt.normalize();
ret.mul(qt);
//t.set(qt);
t.setIdentity();
t.m00 = qt.w * qt.w - qt.y * qt.y;
t.m22 = t.m00;
t.m20 = -2 * qt.y * qt.w;
t.m02 = -t.m20;
t.m11 = qt.w * qt.w + qt.y * qt.y;
m.mul(m, t);
t.transpose();
m.mul(t, m);
if(m.m02 * m.m02 + m.m20 * m.m20 > eps)
{
p = approxGivensQuat(m.m00, .5f * (m.m02 + m.m20), m.m22);
qt.set(0, -p.getLeft(), 0, p.getRight());
//qt.normalize();
ret.mul(qt);
//t.set(qt);
t.setIdentity();
t.m00 = qt.w * qt.w - qt.y * qt.y;
t.m22 = t.m00;
t.m20 = -2 * qt.y * qt.w;
t.m02 = -t.m20;
t.m11 = qt.w * qt.w + qt.y * qt.y;
m.mul(m, t);
t.transpose();
m.mul(t, m);
}
// 12
p = approxGivensQuat(m.m11, .5f * (m.m12 + m.m21), m.m22);
qt.set(p.getLeft(), 0, 0, p.getRight());
//qt.normalize();
ret.mul(qt);
//t.set(qt);
t.setIdentity();
t.m11 = qt.w * qt.w - qt.x * qt.x;
t.m22 = t.m11;
t.m21 = 2 * qt.x * qt.w;
t.m12 = -t.m21;
t.m00 = qt.w * qt.w + qt.x * qt.x;
m.mul(m, t);
t.transpose();
m.mul(t, m);
if(m.m12 * m.m12 + m.m21 * m.m21 > eps)
{
p = approxGivensQuat(m.m11, .5f * (m.m12 + m.m21), m.m22);
qt.set(p.getLeft(), 0, 0, p.getRight());
//qt.normalize();
ret.mul(qt);
//t.set(qt);
t.setIdentity();
t.m11 = qt.w * qt.w - qt.x * qt.x;
t.m22 = t.m11;
t.m21 = 2 * qt.x * qt.w;
t.m12 = -t.m21;
t.m00 = qt.w * qt.w + qt.x * qt.x;
m.mul(m, t);
t.transpose();
m.mul(t, m);
}
return ret;
}
@ -566,4 +578,35 @@ public class TRSRTransformation implements IModelState, ITransformation
else if (!matrix.equals(other.matrix)) return false;
return true;
}
public static Vector3f lerp(Tuple3f from, Tuple3f to, float progress)
{
Vector3f res = new Vector3f(from);
res.interpolate(from, to, progress);
return res;
}
public static Vector4f lerp(Tuple4f from, Tuple4f to, float progress)
{
Vector4f res = new Vector4f(from);
res.interpolate(from, to, progress);
return res;
}
public static Quat4f slerp(Quat4f from, Quat4f to, float progress)
{
Quat4f res = new Quat4f();
res.interpolate(from, to, progress);
return res;
}
public TRSRTransformation slerp(TRSRTransformation that, float progress)
{
return new TRSRTransformation(
lerp(this.getTranslation(), that.getTranslation(), progress),
slerp(this.getLeftRot(), that.getLeftRot(), progress),
lerp(this.getScale(), that.getScale(), progress),
slerp(this.getRightRot(), that.getRightRot(), progress)
);
}
}

View File

@ -25,6 +25,7 @@ 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.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IFlexibleBakedModel;
@ -65,6 +66,9 @@ 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.ImmutableSet;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
/*
* Loader for Blitz3D models.
@ -138,16 +142,19 @@ public class B3DLoader implements ICustomModelLoader
FMLLog.severe("No mesh named %s in model %s, skipping", mesh, modelLocation);
return ModelLoaderRegistry.getMissingModel();
}
return new Wrapper(modelLocation, model.getTextures(), model.getMeshes().get(mesh));
return new ModelWrapper(modelLocation, model, ImmutableSet.of(mesh), 1);
}
if(!(model.getRoot().getKind() instanceof Mesh))
{
FMLLog.severe("No root mesh in model %s and no mesh name in location, skipping", modelLocation);
return ModelLoaderRegistry.getMissingModel();
return new ModelWrapper(modelLocation, model, ImmutableSet.<String>of(), 1);
}
return new Wrapper(modelLocation, model.getTextures(), (Node<Mesh>)model.getRoot());
return new ModelWrapper(modelLocation, model, ImmutableSet.of(((Node<Mesh>)model.getRoot()).getName()), 1);
}
/**
* @deprecated Use json or IModelCustomData.process
*/
@Deprecated
public static class B3DMeshLocation extends ResourceLocation
{
public final String mesh;
@ -192,17 +199,31 @@ public class B3DLoader implements ICustomModelLoader
{
private final Animation animation;
private final int frame;
private final int nextFrame;
private final float progress;
private final IModelState parent;
public B3DState(Animation animation, int frame)
{
this(animation, frame, null);
this(animation, frame, frame, 0);
}
public B3DState(Animation animation, int frame, IModelState parent)
{
this(animation, frame, frame, 0, parent);
}
public B3DState(Animation animation, int frame, int nextFrame, float progress)
{
this(animation, frame, nextFrame, progress, null);
}
public B3DState(Animation animation, int frame, int nextFrame, float progress, IModelState parent)
{
this.animation = animation;
this.frame = frame;
this.nextFrame = nextFrame;
this.progress = MathHelper.clamp_float(progress, 0, 1);
this.parent = getParent(parent);
}
@ -223,17 +244,47 @@ public class B3DLoader implements ICustomModelLoader
return frame;
}
public int getNextFrame()
{
return nextFrame;
}
public float getProgress()
{
return progress;
}
public IModelState getParent()
{
return parent;
}
public TRSRTransformation apply(IModelPart part)
{
if(!(part instanceof PartWrapper<?>))
{
throw new IllegalArgumentException("B3DState can only be applied to b3d models");
}
Node<?> node = ((PartWrapper<?>)part).getNode();
TRSRTransformation nodeTransform;
if(progress < 1e-5 || frame == nextFrame)
{
nodeTransform = getNodeMatrix(node, frame);
}
else if(progress > 1 - 1e-5)
{
nodeTransform = getNodeMatrix(node, nextFrame);
}
else
{
nodeTransform = getNodeMatrix(node, frame);
nodeTransform = nodeTransform.slerp(getNodeMatrix(node, nextFrame), progress);
}
if(parent != null)
{
return parent.apply(part).compose(getNodeMatrix(((PartWrapper<?>)part).getNode()));
return parent.apply(part).compose(nodeTransform);
}
return getNodeMatrix(((PartWrapper<?>)part).getNode());
return nodeTransform;
}
private static LoadingCache<Triple<Animation, Node<?>, Integer>, TRSRTransformation> cache = CacheBuilder.newBuilder()
@ -252,6 +303,11 @@ public class B3DLoader implements ICustomModelLoader
return cache.getUnchecked(Triple.<Animation, Node<?>, Integer>of(animation, node, frame));
}
public TRSRTransformation getNodeMatrix(Node<?> node, int frame)
{
return cache.getUnchecked(Triple.<Animation, Node<?>, Integer>of(animation, node, frame));
}
public static TRSRTransformation getNodeMatrix(Animation animation, Node<?> node, int frame)
{
TRSRTransformation ret = TRSRTransformation.identity();
@ -326,20 +382,36 @@ public class B3DLoader implements ICustomModelLoader
}
}
/**
* @deprecated Use ModelWrapper, this will be removed in 1.9
*/
@Deprecated
public static class Wrapper extends PartWrapper<Mesh> implements IRetexturableModel, IModelCustomData
{
private final ResourceLocation location;
private final ImmutableSet<String> meshes;
private final ImmutableMap<String, ResourceLocation> textures;
public Wrapper(ResourceLocation location, List<Texture> textures, B3DModel.Node<Mesh> mesh)
{
this(location, buildTextures(textures), mesh);
this(location, ImmutableSet.<String>of(), buildTextures(textures), mesh);
}
public Wrapper(ResourceLocation location, ImmutableMap<String, ResourceLocation> textures, B3DModel.Node<Mesh> mesh)
{
this(location, ImmutableSet.<String>of(), textures, mesh);
}
public Wrapper(ResourceLocation location, ImmutableSet<String> meshes, List<Texture> textures, B3DModel.Node<Mesh> mesh)
{
this(location, meshes, buildTextures(textures), mesh);
}
public Wrapper(ResourceLocation location, ImmutableSet<String> meshes, ImmutableMap<String, ResourceLocation> textures, B3DModel.Node<Mesh> mesh)
{
super(mesh);
this.location = location;
this.meshes = meshes;
this.textures = textures;
}
@ -398,7 +470,7 @@ public class B3DLoader implements ICustomModelLoader
}
}
builder.put("missingno", missing);
return new BakedWrapper(this, state, format, builder.build());
return new BakedWrapper(getNode(), state, format, meshes, builder.build());
}
public B3DState getDefaultState()
@ -465,29 +537,211 @@ public class B3DLoader implements ICustomModelLoader
@Override
public IModel process(ImmutableMap<String, String> customData)
{
// TODO keyframe
return null;
return this;
}
}
public static class ModelWrapper implements IRetexturableModel, IModelCustomData
{
private final ResourceLocation modelLocation;
private final B3DModel model;
private final ImmutableSet<String> meshes;
private final ImmutableMap<String, ResourceLocation> textures;
private final int defaultKey;
public ModelWrapper(ResourceLocation modelLocation, B3DModel model, ImmutableSet<String> meshes, int defaultKey)
{
this(modelLocation, model, meshes, defaultKey, buildTextures(model.getTextures()));
}
public ModelWrapper(ResourceLocation modelLocation, B3DModel model, ImmutableSet<String> meshes, int defaultKey, ImmutableMap<String, ResourceLocation> textures)
{
this.modelLocation = modelLocation;
this.model = model;
this.meshes = meshes;
this.textures = textures;
this.defaultKey = defaultKey;
}
private static ImmutableMap<String, ResourceLocation> buildTextures(List<Texture> textures)
{
ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
for(Texture t : textures)
{
String path = t.getPath();
String location = getLocation(path);
if(!location.startsWith("#")) location = "#" + location;
builder.put(path, new ResourceLocation(location));
}
return builder.build();
}
private static String getLocation(String path)
{
if(path.endsWith(".png")) path = path.substring(0, path.length() - ".png".length());
return path;
}
@Override
public Collection<ResourceLocation> getDependencies()
{
return Collections.emptyList();
}
@Override
public Collection<ResourceLocation> getTextures()
{
return Collections2.filter(textures.values(), new Predicate<ResourceLocation>()
{
public boolean apply(ResourceLocation loc)
{
return !loc.getResourcePath().startsWith("#");
}
});
}
@Override
public IFlexibleBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter)
{
ImmutableMap.Builder<String, TextureAtlasSprite> builder = ImmutableMap.builder();
TextureAtlasSprite missing = bakedTextureGetter.apply(new ResourceLocation("missingno"));
for(Map.Entry<String, ResourceLocation> e : textures.entrySet())
{
if(e.getValue().getResourcePath().startsWith("#"))
{
FMLLog.severe("unresolved texture '%s' for b3d model '%s'", e.getValue().getResourcePath(), modelLocation);
builder.put(e.getKey(), missing);
}
else
{
builder.put(e.getKey(), bakedTextureGetter.apply(e.getValue()));
}
}
builder.put("missingno", missing);
return new BakedWrapper(model.getRoot(), state, format, meshes, builder.build());
}
@Override
public IModelState getDefaultState()
{
return new B3DState(model.getRoot().getAnimation(), defaultKey, defaultKey, 0);
}
@Override
public IModel retexture(ImmutableMap<String, String> textures)
{
ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
for(Map.Entry<String, ResourceLocation> e : this.textures.entrySet())
{
String path = e.getKey();
String loc = getLocation(path);
if(textures.containsKey(loc))
{
String newLoc = textures.get(loc);
if(newLoc == null) newLoc = getLocation(path);
builder.put(e.getKey(), new ResourceLocation(newLoc));
}
else
{
builder.put(e);
}
}
return new ModelWrapper(modelLocation, model, meshes, defaultKey, builder.build());
}
@Override
public IModel process(ImmutableMap<String, String> data)
{
if(data.containsKey("mesh"))
{
JsonElement e = new JsonParser().parse(data.get("mesh"));
if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isString())
{
return new ModelWrapper(modelLocation, model, ImmutableSet.of(e.getAsString()), defaultKey, textures);
}
else if (e.isJsonArray())
{
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for(JsonElement s : e.getAsJsonArray())
{
if(s.isJsonPrimitive() && s.getAsJsonPrimitive().isString())
{
builder.add(s.getAsString());
}
else
{
FMLLog.severe("unknown mesh definition '%s' in array for b3d model '%s'", s.toString(), modelLocation);
return this;
}
}
return new ModelWrapper(modelLocation, model, builder.build(), defaultKey, textures);
}
else
{
FMLLog.severe("unknown mesh definition '%s' for b3d model '%s'", e.toString(), modelLocation);
return this;
}
}
if(data.containsKey("key"))
{
JsonElement e = new JsonParser().parse(data.get("key"));
if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isNumber())
{
return new ModelWrapper(modelLocation, model, meshes, e.getAsNumber().intValue(), textures);
}
else
{
FMLLog.severe("unknown keyframe definition '%s' for b3d model '%s'", e.toString(), modelLocation);
return this;
}
}
return this;
}
}
private static class BakedWrapper implements IFlexibleBakedModel, ISmartBlockModel, ISmartItemModel, IPerspectiveAwareModel
{
private final B3DLoader.Wrapper model;
private final Node<?> node;
private final IModelState state;
private final VertexFormat format;
private final ImmutableSet<String> meshes;
private final ImmutableMap<String, TextureAtlasSprite> textures;
private final LoadingCache<Integer, BakedWrapper> cache;
private ImmutableList<BakedQuad> quads;
private static final int BYTES_IN_INT = Integer.SIZE / Byte.SIZE;
private static final int VERTICES_IN_QUAD = 4;
public BakedWrapper(B3DLoader.Wrapper model, IModelState state, VertexFormat format, ImmutableMap<String, TextureAtlasSprite> textures)
public BakedWrapper(Node<?> node, IModelState state, VertexFormat format, ImmutableSet<String> meshes, ImmutableMap<String, TextureAtlasSprite> textures)
{
this.model = model;
this(node, state, format, meshes, textures, CacheBuilder.newBuilder()
.maximumSize(128)
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new CacheLoader<Integer, BakedWrapper>()
{
public BakedWrapper load(Integer frame) throws Exception
{
IModelState parent = state;
Animation newAnimation = null;
if(parent instanceof B3DState)
{
B3DState ps = (B3DState)parent;
parent = ps.getParent();
}
if(newAnimation == null)
{
newAnimation = node.getAnimation();
}
return new BakedWrapper(node, new B3DState(newAnimation, frame, frame, 0, parent), format, meshes, textures);
}
}));
}
public BakedWrapper(Node<?> node, IModelState state, VertexFormat format, ImmutableSet<String> meshes, ImmutableMap<String, TextureAtlasSprite> textures, LoadingCache<Integer, BakedWrapper> cache)
{
this.node = node;
this.state = state;
this.format = format;
this.meshes = meshes;
this.textures = textures;
this.cache = cache;
}
public List<BakedQuad> getFaceQuads(EnumFacing side)
@ -495,47 +749,43 @@ public class B3DLoader implements ICustomModelLoader
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
public List<BakedQuad> getGeneralQuads()
{
if(quads == null)
{
Node<Mesh> mesh = model.getNode();
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
for(Node<?> child : mesh.getNodes().values())
for(Node<?> child : node.getNodes().values())
{
if(child.getKind() instanceof Mesh)
{
Node<Mesh> childMesh = (Node<Mesh>)child;
builder.addAll(new BakedWrapper(new B3DLoader.Wrapper(model.getLocation(), model.getTextureMap(), childMesh), state, format, textures).getGeneralQuads());
}
builder.addAll(new BakedWrapper(child, state, format, meshes, textures).getGeneralQuads());
}
mesh.getKind().getWeightMap();
Collection<Face> faces = mesh.getKind().getFaces();
faces = mesh.getKind().bake(new Function<Node<?>, Matrix4f>()
if(node.getKind() instanceof Mesh && meshes.contains(node.getName()))
{
// gets transformation in global space
public Matrix4f apply(Node<?> node)
Mesh mesh = (Mesh)node.getKind();
Collection<Face> faces = mesh.bake(new Function<Node<?>, Matrix4f>()
{
return state.apply(PartWrapper.create(node)).getMatrix();
// gets transformation in global space
public Matrix4f apply(Node<?> node)
{
return state.apply(PartWrapper.create(node)).getMatrix();
}
});
for(Face f : faces)
{
UnpackedBakedQuad.Builder quadBuilder = new UnpackedBakedQuad.Builder(format);
quadBuilder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
quadBuilder.setQuadColored();
List<Texture> textures = null;
if(f.getBrush() != null) textures = f.getBrush().getTextures();
TextureAtlasSprite sprite;
if(textures == null || textures.isEmpty()) sprite = this.textures.get("missingno");
else if(textures.get(0) == B3DModel.Texture.White) sprite = ModelLoader.White.instance;
else sprite = this.textures.get(textures.get(0).getPath());
putVertexData(quadBuilder, f.getV1(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV2(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
builder.add(quadBuilder.build());
}
});
for(Face f : faces)
{
UnpackedBakedQuad.Builder quadBuilder = new UnpackedBakedQuad.Builder(format);
quadBuilder.setQuadOrientation(EnumFacing.getFacingFromVector(f.getNormal().x, f.getNormal().y, f.getNormal().z));
quadBuilder.setQuadColored();
List<Texture> textures = null;
if(f.getBrush() != null) textures = f.getBrush().getTextures();
TextureAtlasSprite sprite;
if(textures == null || textures.isEmpty()) sprite = this.textures.get("missingno");
else if(textures.get(0) == B3DModel.Texture.White) sprite = ModelLoader.White.instance;
else sprite = this.textures.get(textures.get(0).getPath());
putVertexData(quadBuilder, f.getV1(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV2(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
putVertexData(quadBuilder, f.getV3(), f.getNormal(), sprite);
builder.add(quadBuilder.build());
}
quads = builder.build();
}
@ -632,24 +882,30 @@ public class B3DLoader implements ICustomModelLoader
B3DState s = exState.getValue(B3DFrameProperty.instance);
if(s != null)
{
return getCachedModel(s.getFrame());
//return getCachedModel(s.getFrame());
IModelState parent = this.state;
Animation newAnimation = s.getAnimation();
if(parent instanceof B3DState)
{
B3DState ps = (B3DState)parent;
parent = ps.getParent();
}
if(newAnimation == null)
{
newAnimation = node.getAnimation();
}
if(s.getFrame() == s.getNextFrame())
{
return cache.getUnchecked(s.getFrame());
}
B3DState newState = new B3DState(newAnimation, s.getFrame(), s.getNextFrame(), s.getProgress(), parent);
return new BakedWrapper(node, newState, format, meshes, textures);
}
}
}
return this;
}
private final Map<Integer, BakedWrapper> cache = new HashMap<Integer, BakedWrapper>();
public BakedWrapper getCachedModel(int frame)
{
if(!cache.containsKey(frame))
{
cache.put(frame, new BakedWrapper(model, new B3DState(model.getNode().getAnimation(), frame, state), format, textures));
}
return cache.get(frame);
}
public VertexFormat getFormat()
{
return format;
@ -665,7 +921,7 @@ public class B3DLoader implements ICustomModelLoader
{
if(state instanceof IPerspectiveState)
{
return Pair.of((IBakedModel)this, TRSRTransformation.blockCornerToCenter(((IPerspectiveState)state).forPerspective(cameraTransformType).apply(model)).getMatrix());
return Pair.of((IBakedModel)this, TRSRTransformation.blockCornerToCenter(((IPerspectiveState)state).forPerspective(cameraTransformType).apply(PartWrapper.create(node))).getMatrix());
}
return Pair.of((IBakedModel)this, null);
}

View File

@ -0,0 +1,286 @@
package net.minecraftforge.debug;
import net.minecraft.block.Block;
import net.minecraft.block.BlockPistonBase;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyDirection;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.resources.model.IBakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.server.gui.IUpdatePlayerListBox;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import net.minecraftforge.client.model.ISmartBlockModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.b3d.B3DLoader;
import net.minecraftforge.client.model.b3d.B3DLoader.B3DFrameProperty;
import net.minecraftforge.client.model.b3d.B3DLoader.B3DState;
import net.minecraftforge.common.property.ExtendedBlockState;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.registry.GameRegistry;
import org.lwjgl.opengl.GL11;
@Mod(modid = ModelAnimationDebug.MODID, version = ModelAnimationDebug.VERSION)
public class ModelAnimationDebug
{
public static final String MODID = "forgedebugmodelanimation";
public static final String VERSION = "0.0";
public static String blockName = "test_animation_block";
public static final PropertyDirection FACING = PropertyDirection.create("facing");
@SidedProxy(serverSide = "net.minecraftforge.debug.ModelAnimationDebug$CommonProxy", clientSide = "net.minecraftforge.debug.ModelAnimationDebug$ClientProxy")
public static CommonProxy proxy;
public static class CommonProxy
{
public void preInit(FMLPreInitializationEvent event)
{
B3DLoader.instance.addDomain(MODID);
GameRegistry.registerBlock(new Block(Material.wood)
{
{
setCreativeTab(CreativeTabs.tabBlock);
setUnlocalizedName(MODID + "." + blockName);
}
@Override
public ExtendedBlockState createBlockState()
{
return new ExtendedBlockState(this, new IProperty[]{ FACING }, new IUnlistedProperty[]{ B3DFrameProperty.instance });
}
@Override
public int getRenderType() { return -1; }
@Override
public boolean isOpaqueCube() { return false; }
@Override
public boolean isFullCube() { return false; }
@Override
public IBlockState onBlockPlaced(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
{
return this.getDefaultState().withProperty(FACING, BlockPistonBase.getFacingFromEntity(world, pos, placer));
}
@Override
public IBlockState getStateFromMeta(int meta) {
return getDefaultState().withProperty(FACING, EnumFacing.getFront(meta));
}
@Override
public int getMetaFromState(IBlockState state) {
return ((EnumFacing)state.getValue(FACING)).getIndex();
}
@Override
public boolean hasTileEntity(IBlockState state) {
return true;
}
@Override
public TileEntity createTileEntity(World world, IBlockState state) {
return new Chest(state);
}
/*@Override
public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) {
TileEntity te = world.getTileEntity(pos);
if(te instanceof Chest && state instanceof IExtendedBlockState)
{
return ((Chest)te).getState((IExtendedBlockState)state);
}
return super.getExtendedState(state, world, pos);
}*/
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumFacing side, float hitX, float hitY, float hitZ)
{
if(world.isRemote)
{
TileEntity te = world.getTileEntity(pos);
if(te instanceof Chest)
{
((Chest)te).click();
}
}
return false;
}
}, blockName);
}
public void init(FMLInitializationEvent event) {}
}
public static class ClientProxy extends CommonProxy
{
@Override
public void preInit(FMLPreInitializationEvent event)
{
super.preInit(event);
ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(GameRegistry.findBlock(MODID, blockName)), 0, new ModelResourceLocation(MODID.toLowerCase() + ":" + blockName, "inventory"));
}
@Override
public void init(FMLInitializationEvent event)
{
super.init(event);
ClientRegistry.bindTileEntitySpecialRenderer(Chest.class, ChestRenderer.instance);
}
}
@EventHandler
public void preInit(FMLPreInitializationEvent event) { proxy.preInit(event); }
@EventHandler
public void init(FMLInitializationEvent event) { proxy.init(event); }
private static class Chest extends TileEntity implements IUpdatePlayerListBox
{
private final int minFrame = 1;
private final int maxFrame = 10;
private int tick = minFrame;
private boolean opening = false;
private boolean closing = false;
public Chest(IBlockState state) {
}
/*public IExtendedBlockState getState(IExtendedBlockState state) {
return state.withProperty(B3DFrameProperty.instance, curState);
}*/
public void click()
{
if(opening || tick == maxFrame)
{
opening = false;
closing = true;
return;
}
if(closing || tick == minFrame)
{
closing = false;
opening = true;
return;
}
opening = true;
}
@Override
public void update()
{
if(opening)
{
tick++;
if(tick >= maxFrame)
{
tick = maxFrame;
opening = false;
}
}
if(closing)
{
tick--;
if(tick <= minFrame)
{
tick = minFrame;
closing = false;
}
}
}
public int getCurFrame()
{
return tick;
}
public int getNextFrame()
{
if(opening) return Math.min(tick + 1, maxFrame);
if(closing) return Math.max(tick - 1, minFrame);
return tick;
}
}
private static class ChestRenderer extends TileEntitySpecialRenderer
{
public static ChestRenderer instance = new ChestRenderer();
private ChestRenderer() {}
private final BlockRendererDispatcher blockRenderer = Minecraft.getMinecraft().getBlockRendererDispatcher();
public void renderTileEntityAt(TileEntity te, double x, double y, double z, float partialTick, int breakStage)
{
IBlockState state = te.getWorld().getBlockState(te.getPos());
IBakedModel model = this.blockRenderer.getModelFromBlockState(state, te.getWorld(), te.getPos());
if(state instanceof IExtendedBlockState)
{
IExtendedBlockState exState = (IExtendedBlockState)state;
if(exState.getUnlistedNames().contains(B3DFrameProperty.instance))
{
exState = exState.withProperty(B3DFrameProperty.instance, new B3DState(null, ((Chest)te).getCurFrame(), ((Chest)te).getNextFrame(), partialTick));
if(model instanceof ISmartBlockModel)
{
model = ((ISmartBlockModel)model).handleBlockState(exState);
}
}
}
Tessellator tessellator = Tessellator.getInstance();
WorldRenderer worldrenderer = tessellator.getWorldRenderer();
this.bindTexture(TextureMap.locationBlocksTexture);
RenderHelper.disableStandardItemLighting();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.enableBlend();
GlStateManager.disableCull();
if (Minecraft.isAmbientOcclusionEnabled())
{
GlStateManager.shadeModel(GL11.GL_SMOOTH);
}
else
{
GlStateManager.shadeModel(GL11.GL_FLAT);
}
worldrenderer.startDrawingQuads();
worldrenderer.setVertexFormat(DefaultVertexFormats.BLOCK);
worldrenderer.setTranslation(x - te.getPos().getX(), y - te.getPos().getY(), z - te.getPos().getZ());
this.blockRenderer.getBlockModelRenderer().renderModel(te.getWorld(), model, state, te.getPos(), worldrenderer, false);
worldrenderer.setTranslation(0, 0, 0);
tessellator.draw();
RenderHelper.enableStandardItemLighting();
}
}
}

View File

@ -0,0 +1,26 @@
{
"forge_marker": 1,
"defaults": {
"textures": {
"#chest": "entity/chest/normal"
},
"model": "forgedebugmodelloaderregistry:chest.b3d",
"custom": {
"mesh": ["Cube.001"]
}
},
"variants": {
"normal": [{}],
"inventory": [{
"transform": "forge:default-block"
}],
"facing": {
"down": { "transform": { "rotation": { "x": 90 } } },
"up": { "transform": { "rotation": { "x": 270 } } },
"north": { "transform": { "rotation": { "y": 180 } } },
"south": { "transform": "identity" },
"west": { "transform": { "rotation": { "y": 90 } } },
"east": { "transform": { "rotation": { "y": 270 } } }
}
}
}