From fe036d72d8bb47f571cad6747df93f37ac3a4b42 Mon Sep 17 00:00:00 2001 From: CrushedPixel Date: Sun, 31 May 2015 13:54:22 +0200 Subject: [PATCH] Added Spectator Keyframes and handles them properly in both normal and rendered Replay Paths --- .../gui/overlay/GuiReplayOverlay.java | 4 +- .../replaymod/holders/Position.java | 5 + .../replaymod/holders/PositionKeyframe.java | 16 ++- .../replaymod/renderer/SpectatorRenderer.java | 97 ++++++++++--------- .../replaymod/replay/ReplayHandler.java | 9 +- .../replaymod/replay/ReplayProcess.java | 43 +++++--- .../replaymod/video/VideoRenderer.java | 34 +++++-- .../video/entity/CustomEntityRenderer.java | 6 ++ 8 files changed, 143 insertions(+), 71 deletions(-) diff --git a/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java b/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java index 4ae1f306..df058ad4 100755 --- a/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java +++ b/src/main/java/eu/crushedpixel/replaymod/gui/overlay/GuiReplayOverlay.java @@ -214,7 +214,9 @@ public class GuiReplayOverlay extends Gui { if(cam != null) { Position position = new Position(cam.posX, cam.posY, cam.posZ, cam.rotationPitch, cam.rotationYaw % 360, ReplayHandler.getCameraTilt()); - ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), position)); + + if(ReplayHandler.isCamera()) ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), position)); + else ReplayHandler.addKeyframe(new PositionKeyframe(ReplayHandler.getRealTimelineCursor(), cam.getEntityId())); } } } diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/Position.java b/src/main/java/eu/crushedpixel/replaymod/holders/Position.java index 99bd4747..94b4a89a 100755 --- a/src/main/java/eu/crushedpixel/replaymod/holders/Position.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/Position.java @@ -1,5 +1,6 @@ package eu.crushedpixel.replaymod.holders; +import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -8,6 +9,10 @@ public class Position { private double x, y, z; private float pitch, yaw, roll; + public Position(int entityID) { + this(Minecraft.getMinecraft().theWorld.getEntityByID(entityID)); + } + public Position(Entity e) { this.x = e.posX; this.y = e.posY; diff --git a/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java b/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java index 0abe9594..91d80eb7 100755 --- a/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java +++ b/src/main/java/eu/crushedpixel/replaymod/holders/PositionKeyframe.java @@ -5,10 +5,11 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; public class PositionKeyframe extends Keyframe { private Position position; + private Integer spectatedEntityID = null; @Override public Object clone() { - return new PositionKeyframe(this.getRealTimestamp(), this.getPosition()); + return new PositionKeyframe(this.getRealTimestamp(), this.getPosition(), this.getSpectatedEntityID()); } public PositionKeyframe(int realTime, Position position) { @@ -16,12 +17,24 @@ public class PositionKeyframe extends Keyframe { this.position = position; } + public PositionKeyframe(int realTime, Position position, int spectatedEntityID) { + super(realTime); + this.position = position; + this.spectatedEntityID = spectatedEntityID; + } + + public PositionKeyframe(int realTime, int spectatedEntityID) { + this(realTime, new Position(spectatedEntityID), spectatedEntityID); + } + public Position getPosition() { return position; } public void setPosition(Position position) { this.position = position; } + public Integer getSpectatedEntityID() { return spectatedEntityID; } + @Override public boolean equals(Object o2) { if(o2 == null) return false; @@ -35,6 +48,7 @@ public class PositionKeyframe extends Keyframe { return new HashCodeBuilder(17, 37) .append(getPosition()) .append(getRealTimestamp()) + .append(getSpectatedEntityID()) .toHashCode(); } } \ No newline at end of file diff --git a/src/main/java/eu/crushedpixel/replaymod/renderer/SpectatorRenderer.java b/src/main/java/eu/crushedpixel/replaymod/renderer/SpectatorRenderer.java index ccdb9d04..f4e3164e 100644 --- a/src/main/java/eu/crushedpixel/replaymod/renderer/SpectatorRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/renderer/SpectatorRenderer.java @@ -36,6 +36,55 @@ public class SpectatorRenderer { return (EntityPlayer)current; } + public void renderSpectatorHand(EntityPlayer entityPlayer, float partialTicks, int renderPass) { + + GlStateManager.matrixMode(GL11.GL_PROJECTION); + GlStateManager.loadIdentity(); + + float f1 = 0.07F; + + if(this.mc.gameSettings.anaglyph) { + GlStateManager.translate((float)(-(renderPass * 2 - 1)) * f1, 0.0F, 0.0F); + } + + Project.gluPerspective(mc.entityRenderer.getFOVModifier(partialTicks, false), + (float) this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, mc.entityRenderer.farPlaneDistance * 2.0F); + + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + GlStateManager.loadIdentity(); + + if(this.mc.gameSettings.anaglyph) { + GlStateManager.translate((float)(renderPass * 2 - 1) * 0.1F, 0.0F, 0.0F); + } + + GlStateManager.pushMatrix(); + mc.entityRenderer.hurtCameraEffect(partialTicks); + + if(this.mc.gameSettings.viewBobbing) { + mc.entityRenderer.setupViewBobbing(partialTicks); + } + + boolean sleeping = this.mc.getRenderViewEntity() instanceof EntityLivingBase && + ((EntityLivingBase)this.mc.getRenderViewEntity()).isPlayerSleeping(); + + if(this.mc.gameSettings.thirdPersonView == 0 && !sleeping && !this.mc.gameSettings.hideGUI) { + mc.entityRenderer.enableLightmap(); + renderItemInFirstPerson(partialTicks); + mc.entityRenderer.disableLightmap(); + } + + GlStateManager.popMatrix(); + + if(this.mc.gameSettings.thirdPersonView == 0 && !sleeping) { + renderOverlays(partialTicks, entityPlayer); + mc.entityRenderer.hurtCameraEffect(partialTicks); + } + + if(this.mc.gameSettings.viewBobbing) { + mc.entityRenderer.setupViewBobbing(partialTicks); + } + } + @SubscribeEvent public void renderHandEvent(RenderHandEvent event) { if(!ReplayHandler.isInReplay() || ReplayHandler.isCamera()) { @@ -45,54 +94,8 @@ public class SpectatorRenderer { EntityPlayer entityPlayer = getSpectatedPlayer(); if(entityPlayer == null) return; + renderSpectatorHand(entityPlayer, event.partialTicks, event.renderPass); event.setCanceled(true); - - GlStateManager.matrixMode(GL11.GL_PROJECTION); - GlStateManager.loadIdentity(); - - float f1 = 0.07F; - - if(this.mc.gameSettings.anaglyph) { - GlStateManager.translate((float)(-(event.renderPass * 2 - 1)) * f1, 0.0F, 0.0F); - } - - Project.gluPerspective(mc.entityRenderer.getFOVModifier(event.partialTicks, false), - (float)this.mc.displayWidth / (float) this.mc.displayHeight, 0.05F, mc.entityRenderer.farPlaneDistance * 2.0F); - - GlStateManager.matrixMode(GL11.GL_MODELVIEW); - GlStateManager.loadIdentity(); - - if(this.mc.gameSettings.anaglyph) { - GlStateManager.translate((float)(event.renderPass * 2 - 1) * 0.1F, 0.0F, 0.0F); - } - - GlStateManager.pushMatrix(); - mc.entityRenderer.hurtCameraEffect(event.partialTicks); - - if(this.mc.gameSettings.viewBobbing) { - mc.entityRenderer.setupViewBobbing(event.partialTicks); - } - - boolean sleeping = this.mc.getRenderViewEntity() instanceof EntityLivingBase && - ((EntityLivingBase)this.mc.getRenderViewEntity()).isPlayerSleeping(); - - if(this.mc.gameSettings.thirdPersonView == 0 && !sleeping && !this.mc.gameSettings.hideGUI) { - mc.entityRenderer.enableLightmap(); - renderItemInFirstPerson(event.partialTicks); - mc.entityRenderer.disableLightmap(); - } - - GlStateManager.popMatrix(); - - if(this.mc.gameSettings.thirdPersonView == 0 && !sleeping) { - renderOverlays(event.partialTicks, entityPlayer); - mc.entityRenderer.hurtCameraEffect(event.partialTicks); - } - - if(this.mc.gameSettings.viewBobbing) { - mc.entityRenderer.setupViewBobbing(event.partialTicks); - } - } public void renderItemInFirstPerson(float partialTicks) { diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java index 7f76f6e6..f8a45801 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayHandler.java @@ -71,8 +71,13 @@ public class ReplayHandler { } public static void spectateEntity(Entity e) { - currentEntity = e; - mc.setRenderViewEntity(currentEntity); + if(e == null) { + spectateCamera(); + } + else { + currentEntity = e; + mc.setRenderViewEntity(currentEntity); + } } public static void spectateCamera() { diff --git a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java index a2d80152..c90dfdbc 100755 --- a/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java +++ b/src/main/java/eu/crushedpixel/replaymod/replay/ReplayProcess.java @@ -221,6 +221,18 @@ public class ReplayProcess { PositionKeyframe lastPos = ReplayHandler.getPreviousPositionKeyframe(curRealReplayTime); PositionKeyframe nextPos = ReplayHandler.getNextPositionKeyframe(curRealReplayTime); + + boolean spectating = false; + + //if it's between two spectator keyframes sharing the same entity, spectate this entity + if(lastPos != null && nextPos != null) { + if(lastPos.getSpectatedEntityID() != null && nextPos.getSpectatedEntityID() != null) { + if(lastPos.getSpectatedEntityID().equals(nextPos.getSpectatedEntityID())) { + spectating = true; + } + } + } + ReplayHandler.setRealTimelineCursor(curRealReplayTime); int lastPosStamp = 0; @@ -292,17 +304,27 @@ public class ReplayProcess { float splinePos = ((float) ReplayHandler.getKeyframeIndex(lastPos) + currentPosStepPerc) / (float) (posCount - 1); float timePos = ((float) ReplayHandler.getKeyframeIndex(lastTime) + currentTimeStepPerc) / (float) (timeCount - 1); - Position pos = null; - if(posCount > 1) { - if(!linear) { - pos = motionSpline.getPoint(Math.max(0, Math.min(1, splinePos))); + if(!spectating) { + ReplayHandler.spectateCamera(); + Position pos = null; + if(posCount > 1) { + if(!linear) { + pos = motionSpline.getPoint(Math.max(0, Math.min(1, splinePos))); + } else { + pos = motionLinear.getPoint(Math.max(0, Math.min(1, splinePos))); + } } else { - pos = motionLinear.getPoint(Math.max(0, Math.min(1, splinePos))); + if(posCount == 1) { + pos = ReplayHandler.getFirstPositionKeyframe().getPosition(); + } + } + + if(pos != null) { + ReplayHandler.setCameraTilt(pos.getRoll()); + ReplayHandler.getCameraEntity().movePath(pos); } } else { - if(posCount == 1) { - pos = ReplayHandler.getFirstPositionKeyframe().getPosition(); - } + ReplayHandler.spectateEntity(mc.theWorld.getEntityByID(lastPos.getSpectatedEntityID())); } Integer curTimestamp = null; @@ -310,11 +332,6 @@ public class ReplayProcess { curTimestamp = timeLinear.getPoint(Math.max(0, Math.min(1, timePos))); } - if(pos != null) { - ReplayHandler.setCameraTilt(pos.getRoll()); - ReplayHandler.getCameraEntity().movePath(pos); - } - if(!isVideoRecording()) ReplayMod.replaySender.setReplaySpeed(curSpeed); //if(curSpeed > 0) lastSpeed = curSpeed; diff --git a/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java b/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java index d70cd7ff..029cf53f 100644 --- a/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/video/VideoRenderer.java @@ -198,12 +198,13 @@ public class VideoRenderer { Position pos; PositionKeyframe lastPos = ReplayHandler.getPreviousPositionKeyframe(videoTime); + PositionKeyframe nextPos = null; if (movement == null || lastPos == null) { // Stay at one position, no movement pos = ReplayHandler.getNextPositionKeyframe(-1).getPosition(); } else { // Position interpolation - PositionKeyframe nextPos = ReplayHandler.getNextPositionKeyframe(videoTime); + nextPos = ReplayHandler.getNextPositionKeyframe(videoTime); int lastPosStamp = lastPos.getRealTimestamp(); int nextPosStamp = (nextPos == null ? lastPos : nextPos).getRealTimestamp(); @@ -215,13 +216,32 @@ public class VideoRenderer { float totalPct = (ReplayHandler.getKeyframeIndex(lastPos) + diffPct) / (posCount-1); pos = movement.getPoint(Math.max(0, Math.min(1, totalPct))); } - ReplayHandler.setCameraTilt(pos.getRoll()); - ReplayHandler.getCameraEntity().movePath(pos); - // Make sure we're spectating the camera entity - // We do not use .spectateCamera() as that method sets the position of the camera to the previous - // entity, overriding our calculations - ReplayHandler.spectateEntity(ReplayHandler.getCameraEntity()); + boolean spectating = false; + + //if it's between two spectator keyframes sharing the same entity, spectate this entity + if(lastPos != null && nextPos != null) { + if(lastPos.getSpectatedEntityID() != null && nextPos.getSpectatedEntityID() != null) { + if(lastPos.getSpectatedEntityID().equals(nextPos.getSpectatedEntityID())) { + spectating = true; + } + } + } + + if(!spectating) { + // Make sure we're spectating the camera entity + // We do not use .spectateCamera() as that method sets the position of the camera to the previous + // entity, overriding our calculations + ReplayHandler.spectateEntity(ReplayHandler.getCameraEntity()); + + if(pos != null) { + ReplayHandler.setCameraTilt(pos.getRoll()); + ReplayHandler.getCameraEntity().movePath(pos); + } + } else { + ReplayHandler.spectateEntity(mc.theWorld.getEntityByID(lastPos.getSpectatedEntityID())); + } + } private void updateTime(Timer timer, int framesDone) { diff --git a/src/main/java/eu/crushedpixel/replaymod/video/entity/CustomEntityRenderer.java b/src/main/java/eu/crushedpixel/replaymod/video/entity/CustomEntityRenderer.java index bebf1dae..fa54086b 100644 --- a/src/main/java/eu/crushedpixel/replaymod/video/entity/CustomEntityRenderer.java +++ b/src/main/java/eu/crushedpixel/replaymod/video/entity/CustomEntityRenderer.java @@ -1,5 +1,7 @@ package eu.crushedpixel.replaymod.video.entity; +import eu.crushedpixel.replaymod.ReplayMod; +import eu.crushedpixel.replaymod.replay.ReplayHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.EffectRenderer; import net.minecraft.client.renderer.*; @@ -7,6 +9,7 @@ import net.minecraft.client.renderer.culling.ClippingHelperImpl; import net.minecraft.client.renderer.culling.ICamera; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.EnumWorldBlockLayer; import net.minecraft.util.MathHelper; @@ -172,6 +175,9 @@ public abstract class CustomEntityRenderer { } net.minecraftforge.client.ForgeHooksClient.dispatchRenderLast(renderglobal, partialTicks); + + if(!ReplayHandler.isCamera() && ReplayHandler.getCurrentEntity() instanceof EntityPlayer) + ReplayMod.spectatorRenderer.renderSpectatorHand((EntityPlayer)ReplayHandler.getCurrentEntity(), partialTicks, renderPass); } protected void setupCameraTransform(float partialTicks) {