Realign all nametags towards the camera when rendering 360° frames
This commit is contained in:
@@ -12,6 +12,7 @@ public class LoadingPlugin implements IFMLLoadingPlugin {
|
||||
CameraRollCT.class.getName(),
|
||||
ForceChunkLoadingCT.class.getName(),
|
||||
EntityRendererCT.class.getName(),
|
||||
RenderManagerCT.class.getName(),
|
||||
SoundManagerCT.class.getName(),
|
||||
EnchantmentTimerCT.class.getName()
|
||||
};
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package eu.crushedpixel.replaymod.coremod;
|
||||
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.util.ListIterator;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* Transforms the RenderManager class:
|
||||
* - Adds a new field 'CubicEntityRenderer hook'
|
||||
* - Injects CubicEntityRenderer.beforeEntityRender(dx, dy, dz) call at beginning of 'doRenderEntity'
|
||||
*/
|
||||
public class RenderManagerCT implements IClassTransformer {
|
||||
|
||||
private static final String HOOK = "eu.crushedpixel.replaymod.video.entity.CubicEntityRenderer";
|
||||
private static final String HOOK_JVM = HOOK.replace('.', '/');
|
||||
private static final String HOOK_TYPE = "L" + HOOK_JVM + ";";
|
||||
private static final String RENDER_MANAGER = "net.minecraft.client.renderer.entity.RenderManager";
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] bytes) {
|
||||
if (RENDER_MANAGER.equals(transformedName)) {
|
||||
if (name.equals(transformedName)) {
|
||||
return transformRenderManager(bytes, "doRenderEntity", "(Lnet/minecraft/entity/Entity;DDDFFZ)Z");
|
||||
} else {
|
||||
return transformRenderManager(bytes, "a", "(Lwv;DDDFFZ)Z");
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private byte[] transformRenderManager(byte[] bytes, String name_doRenderEntity, String desc_doRenderEntity) {
|
||||
ClassReader classReader = new ClassReader(bytes);
|
||||
ClassNode classNode = new ClassNode();
|
||||
classReader.accept(classNode, 0);
|
||||
|
||||
// Add field
|
||||
classNode.visitField(ACC_PUBLIC, "hook", HOOK_TYPE, null, null);
|
||||
|
||||
// Find method
|
||||
MethodNode doRenderEntity = null;
|
||||
for (MethodNode m : classNode.methods) {
|
||||
if (name_doRenderEntity.equals(m.name) && desc_doRenderEntity.equals(m.desc)) {
|
||||
doRenderEntity = m;
|
||||
}
|
||||
}
|
||||
if (doRenderEntity == null) {
|
||||
throw new NoSuchMethodError("doRenderEntity");
|
||||
}
|
||||
|
||||
injectDoRenderEntity(classNode, doRenderEntity);
|
||||
|
||||
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
classNode.accept(classWriter);
|
||||
return classWriter.toByteArray();
|
||||
}
|
||||
|
||||
private MethodNode injectDoRenderEntity(ClassNode classNode, MethodNode method) {
|
||||
ListIterator<AbstractInsnNode> list = method.instructions.iterator();
|
||||
list.add(new VarInsnNode(ALOAD, 0));
|
||||
list.add(new FieldInsnNode(GETFIELD, classNode.name, "hook", HOOK_TYPE));
|
||||
LabelNode labelNull = new LabelNode();
|
||||
list.add(new JumpInsnNode(IFNULL, labelNull));
|
||||
|
||||
list.add(new VarInsnNode(ALOAD, 0)); // this
|
||||
list.add(new FieldInsnNode(GETFIELD, classNode.name, "hook", HOOK_TYPE));
|
||||
list.add(new VarInsnNode(DLOAD, 2)); // x
|
||||
list.add(new VarInsnNode(DLOAD, 4)); // y
|
||||
list.add(new VarInsnNode(DLOAD, 6)); // z
|
||||
list.add(new MethodInsnNode(INVOKEVIRTUAL, HOOK_JVM, "beforeEntityRender", "(DDD)V", false));
|
||||
|
||||
list.add(labelNull);
|
||||
list.add(new FrameNode(F_SAME, 0, null, 0, null));
|
||||
|
||||
return method;
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,12 @@ import eu.crushedpixel.replaymod.settings.RenderOptions;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class CubicEntityRenderer extends CustomEntityRenderer {
|
||||
|
||||
public enum Direction {
|
||||
@@ -39,6 +42,28 @@ public class CubicEntityRenderer extends CustomEntityRenderer {
|
||||
public CubicEntityRenderer(RenderOptions options, int frameSize, boolean stable) {
|
||||
super(options, frameSize, frameSize);
|
||||
this.stable = stable;
|
||||
|
||||
try {
|
||||
Field hookField = RenderManager.class.getField("hook");
|
||||
hookField.set(mc.getRenderManager(), this);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new Error(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
super.cleanup();
|
||||
try {
|
||||
Field hookField = RenderManager.class.getField("hook");
|
||||
hookField.set(mc.getRenderManager(), null);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new Error(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrame(int id) {
|
||||
@@ -144,4 +169,12 @@ public class CubicEntityRenderer extends CustomEntityRenderer {
|
||||
|
||||
super.renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // Called by ASM
|
||||
public void beforeEntityRender(double dx, double dy, double dz) {
|
||||
double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz));
|
||||
double yaw = -Math.atan2(dx, dz);
|
||||
mc.getRenderManager().playerViewX = (float) Math.toDegrees(pitch);
|
||||
mc.getRenderManager().playerViewY = (float) Math.toDegrees(yaw);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user