Add camera movement

This commit is contained in:
johni0702
2015-11-07 17:43:02 +01:00
parent 6c93b7f0c6
commit c2686f5d34
11 changed files with 445 additions and 284 deletions

View File

@@ -1,276 +0,0 @@
package com.replaymod.replay;
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
import eu.crushedpixel.replaymod.replay.LesserDataWatcher;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.entity.Entity;
import net.minecraft.stats.StatFileWriter;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import org.lwjgl.Sys;
import java.util.UUID;
public class CameraEntity extends EntityPlayerSP {
public static final double SPEED_CHANGE = 0.5;
public static final double LOWER_SPEED = 2;
public static final double UPPER_SPEED = 20;
private static double MAX_SPEED = 10;
private static double THRESHOLD = MAX_SPEED / 20;
private static double DECAY = MAX_SPEED/3;
public static void modifyCameraSpeed(boolean increase) {
setCameraMaximumSpeed(getCameraMaximumSpeed() + (increase ? 1 : -1) * SPEED_CHANGE);
}
public static void setCameraMaximumSpeed(double maxSpeed) {
if(maxSpeed < LOWER_SPEED || maxSpeed > UPPER_SPEED) return;
MAX_SPEED = maxSpeed;
THRESHOLD = MAX_SPEED / 20;
DECAY = 5;
}
public static double getCameraMaximumSpeed() {
return MAX_SPEED;
}
private Vec3 direction;
private Vec3 dirBefore;
private double motion;
public float roll;
private long lastCall = 0;
private boolean speedup = false;
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatFileWriter statFileWriter) {
super(mcIn, worldIn, netHandlerPlayClient, statFileWriter);
}
//frac = time since last tick
public void updateMovement() {
long frac = Sys.getTime() - lastCall;
if(frac == 0) return;
double decFac = Math.max(0, 1 - (DECAY * (frac / 1000D)));
if(speedup) {
if(motion < THRESHOLD) motion = THRESHOLD;
motion /= decFac;
} else {
motion *= decFac;
}
motion = Math.min(motion, MAX_SPEED);
lastCall = Sys.getTime();
if(direction == null || motion < THRESHOLD) {
return;
}
Vec3 movement = direction.normalize();
double factor = motion * (frac / 1000D);
moveRelative(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor);
}
public void speedUp() {
speedup = true;
}
public void stopSpeedUp() {
speedup = false;
}
public void setMovement(MoveDirection dir) {
switch(dir) {
case BACKWARD:
direction = this.getVectorForRotation(-rotationPitch, rotationYaw - 180);
break;
case DOWN:
direction = this.getVectorForRotation(90, 0);
break;
case FORWARD:
direction = this.getVectorForRotation(rotationPitch, rotationYaw);
break;
case LEFT:
direction = this.getVectorForRotation(0, rotationYaw - 90);
break;
case RIGHT:
direction = this.getVectorForRotation(0, rotationYaw + 90);
break;
case UP:
direction = this.getVectorForRotation(-90, 0);
break;
}
Vec3 dbf = direction;
if(dirBefore != null) {
direction = dirBefore.normalize().add(direction);
}
dirBefore = dbf;
updateMovement();
}
public void moveAbsolute(AdvancedPosition pos) {
this.moveAbsolute(pos.getX(), pos.getY(), pos.getZ());
rotationPitch = prevRotationPitch = (float)pos.getPitch();
rotationYaw = prevRotationYaw = (float)pos.getYaw();
roll = (float) pos.getRoll();
updateBoundingBox();
}
public void moveAbsolute(double x, double y, double z) {
this.lastTickPosX = this.prevPosX = this.posX = x;
this.lastTickPosY = this.prevPosY = this.posY = y;
this.lastTickPosZ = this.prevPosZ = this.posZ = z;
updateBoundingBox();
}
public void moveRelative(double x, double y, double z) {
this.lastTickPosX = this.prevPosX = this.posX = this.posX + x;
this.lastTickPosY = this.prevPosY = this.posY = this.posY + y;
this.lastTickPosZ = this.prevPosZ = this.posZ = this.posZ + z;
updateBoundingBox();
}
public void movePath(AdvancedPosition pos) {
this.prevRotationPitch = this.rotationPitch = (float)pos.getPitch();
this.prevRotationYaw = this.rotationYaw = (float)pos.getYaw();
this.roll = (float) pos.getRoll();
this.lastTickPosX = this.prevPosX = this.posX = pos.getX();
this.lastTickPosY = this.prevPosY = this.posY = pos.getY();
this.lastTickPosZ = this.prevPosZ = this.posZ = pos.getZ();
updateBoundingBox();
}
private void updateBoundingBox() {
this.setEntityBoundingBox(new AxisAlignedBB(posX - width / 2, posY, posZ - width / 2,
posX + width / 2, posY + height, posZ + width / 2));
}
protected void updatePos(Entity to) {
prevPosX = to.prevPosX;
prevPosY = to.prevPosY;
prevPosZ = to.prevPosZ;
prevRotationYaw = to.prevRotationYaw;
prevRotationPitch = to.prevRotationPitch;
posX = to.posX;
posY = to.posY;
posZ = to.posZ;
rotationYaw = to.rotationYaw;
rotationPitch = to.rotationPitch;
}
@Override
protected void entityInit() {
this.dataWatcher = new LesserDataWatcher(this);
this.setSize(0.6f, 1.8f);
updateBoundingBox();
}
@Override
public void setAngles(float yaw, float pitch) {
this.rotationYaw = (float) ((double) this.rotationYaw + (double) yaw * 0.15D);
this.rotationPitch = (float) ((double) this.rotationPitch - (double) pitch * 0.15D);
this.rotationPitch = MathHelper.clamp_float(this.rotationPitch, -90.0F, 90.0F);
this.prevRotationPitch = this.rotationPitch;
this.prevRotationYawHead = this.rotationYawHead = this.prevRotationYaw = this.rotationYaw;
}
@Override
public void onUpdate() {
Entity view = mc.getRenderViewEntity();
if (view != null) {
UUID spectating = ReplayModReplay.instance.getReplayHandler().spectating;
if (spectating != null && (view.getUniqueID() != spectating || view.worldObj != worldObj)) {
view = worldObj.getPlayerEntityByUUID(spectating);
if (view != null) {
mc.setRenderViewEntity(view);
} else {
mc.setRenderViewEntity(this);
return;
}
}
if (view != this) {
updatePos(view);
}
}
}
@Override
public void preparePlayerToSpawn() {
if (mc.theWorld != null) {
worldObj = mc.theWorld;
}
super.preparePlayerToSpawn();
}
@Override
public boolean isEntityInsideOpaqueBlock() {
return false;
}
@Override
public boolean isInsideOfMaterial(Material materialIn) {
return false;
}
@Override
public boolean isInLava() {
return false;
}
@Override
public boolean isInWater() {
return false;
}
@Override
public boolean canBePushed() {
return false;
}
@Override
protected void createRunningParticles() {}
@Override
public boolean canBeCollidedWith() {
return false;
}
@Override
public boolean isSpectator() {
return true;
}
public enum MoveDirection {
UP, DOWN, LEFT, RIGHT, FORWARD, BACKWARD
}
@Override
public MovingObjectPosition rayTrace(double p_174822_1_, float p_174822_3_) {
MovingObjectPosition pos = super.rayTrace(p_174822_1_, 1f);
if(pos != null && pos.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
pos.typeOfHit = MovingObjectPosition.MovingObjectType.MISS;
}
return pos;
}
}

View File

@@ -1,6 +1,8 @@
package com.replaymod.replay;
import com.replaymod.core.utils.WrappedTimer;
import com.replaymod.replay.camera.CameraController;
import com.replaymod.replay.camera.CameraEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.settings.GameSettings;
@@ -64,7 +66,21 @@ public class InputReplayTimer extends WrappedTimer {
int wheel = Mouse.getEventDWheel();
if (wheel != 0) {
// TODO: Update camera movement speed
ReplayHandler replayHandler = mod.getReplayHandler();
if (replayHandler != null) {
CameraEntity cameraEntity = replayHandler.getCameraEntity();
if (cameraEntity != null) {
CameraController controller = cameraEntity.getCameraController();
while (wheel > 0) {
controller.increaseSpeed();
wheel--;
}
while (wheel < 0) {
controller.decreaseSpeed();
wheel++;
}
}
}
}
if (mc.currentScreen == null) {

View File

@@ -2,6 +2,7 @@ package com.replaymod.replay;
import com.google.common.base.Preconditions;
import com.mojang.authlib.GameProfile;
import com.replaymod.replay.camera.CameraEntity;
import com.replaymod.replay.events.ReplayCloseEvent;
import com.replaymod.replay.events.ReplayOpenEvent;
import com.replaymod.replay.gui.overlay.GuiReplayOverlay;
@@ -72,7 +73,7 @@ public class ReplayHandler {
*/
private AdvancedPosition targetCameraPosition;
protected UUID spectating;
private UUID spectating;
public ReplayHandler(ReplayFile replayFile, boolean asyncMode) throws IOException {
Preconditions.checkState(mc.isCallingFromMinecraftThread(), "Must be called from Minecraft thread.");
@@ -212,7 +213,7 @@ public class ReplayHandler {
if (mc.getRenderViewEntity() != e) {
mc.setRenderViewEntity(e);
cameraEntity.updatePos(e);
cameraEntity.setCameraPosRot(e);
}
}
@@ -240,6 +241,10 @@ public class ReplayHandler {
return mc.thePlayer instanceof CameraEntity ? (CameraEntity) mc.thePlayer : null;
}
public UUID getSpectatedUUID() {
return spectating;
}
public void setTargetPosition(AdvancedPosition pos) {
targetCameraPosition = pos;
}
@@ -247,7 +252,7 @@ public class ReplayHandler {
public void moveCameraToTargetPosition() {
CameraEntity cam = getCameraEntity();
if (cam != null && targetCameraPosition != null) {
cam.moveAbsolute(targetCameraPosition);
cam.setCameraPosRot(targetCameraPosition);
}
}

View File

@@ -3,6 +3,7 @@ package com.replaymod.replay;
import com.google.common.base.Preconditions;
import com.google.common.io.Files;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.replaymod.replay.camera.CameraEntity;
import de.johni0702.replaystudio.replay.ReplayFile;
import eu.crushedpixel.replaymod.holders.PacketData;
import com.replaymod.core.utils.Restrictions;
@@ -425,7 +426,7 @@ public class ReplaySender extends ChannelInboundHandlerAdapter {
}
CameraEntity cent = replayHandler.getCameraEntity();
cent.moveAbsolute(ppl.func_148932_c(), ppl.func_148928_d(), ppl.func_148933_e());
cent.setCameraPosition(ppl.func_148932_c(), ppl.func_148928_d(), ppl.func_148933_e());
return null;
}
}.call();

View File

@@ -0,0 +1,8 @@
package com.replaymod.replay.camera;
public interface CameraController {
void update(float partialTicksPassed);
void increaseSpeed();
void decreaseSpeed();
}

View File

@@ -0,0 +1,217 @@
package com.replaymod.replay.camera;
import com.replaymod.replay.ReplayModReplay;
import eu.crushedpixel.replaymod.holders.AdvancedPosition;
import lombok.Getter;
import lombok.Setter;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.entity.Entity;
import net.minecraft.stats.StatFileWriter;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import java.util.UUID;
/**
* The camera entity used as the main player entity during replay viewing.
* During a replay {@link Minecraft#thePlayer} should be an instance of this class.
* Camera movement is controlled by a separate {@link CameraController}.
*/
public class CameraEntity extends EntityPlayerSP {
/**
* Roll of this camera in degrees.
*/
public float roll;
@Getter
@Setter
private CameraController cameraController;
private long lastControllerUpdate = System.currentTimeMillis();
public CameraEntity(Minecraft mcIn, World worldIn, NetHandlerPlayClient netHandlerPlayClient, StatFileWriter statFileWriter) {
super(mcIn, worldIn, netHandlerPlayClient, statFileWriter);
FMLCommonHandler.instance().bus().register(this);
cameraController = new VanillaCameraController(mc, this);
}
/**
* Moves the camera by the specified delta.
* @param x Delta in X direction
* @param y Delta in Y direction
* @param z Delta in Z direction
*/
public void moveCamera(double x, double y, double z) {
setCameraPosition(posX + x, posY + y, posZ + z);
}
/**
* Set the camera position.
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate
*/
public void setCameraPosition(double x, double y, double z) {
this.lastTickPosX = this.prevPosX = this.posX = x;
this.lastTickPosY = this.prevPosY = this.posY = y;
this.lastTickPosZ = this.prevPosZ = this.posZ = z;
updateBoundingBox();
}
/**
* Sets the camera rotation.
* @param yaw Yaw in degrees
* @param pitch Pitch in degrees
* @param roll Roll in degrees
*/
public void setCameraRotation(float yaw, float pitch, float roll) {
this.prevRotationYaw = this.rotationYaw = yaw;
this.prevRotationPitch = this.rotationPitch = pitch;
this.roll = roll;
}
/**
* Sets the camera position and rotation to that of the specified AdvancedPosition
* @param pos The position and rotation to set
*/
public void setCameraPosRot(AdvancedPosition pos) {
setCameraRotation((float) pos.getYaw(), (float) pos.getPitch(), (float) pos.getRoll());
setCameraPosition(pos.getX(), pos.getY(), pos.getZ());
}
/**
* Sets the camera position and rotation to that of the specified entity.
* @param to The entity whose position to copy
*/
public void setCameraPosRot(Entity to) {
prevPosX = to.prevPosX;
prevPosY = to.prevPosY;
prevPosZ = to.prevPosZ;
prevRotationYaw = to.prevRotationYaw;
prevRotationPitch = to.prevRotationPitch;
posX = to.posX;
posY = to.posY;
posZ = to.posZ;
rotationYaw = to.rotationYaw;
rotationPitch = to.rotationPitch;
lastTickPosX = to.lastTickPosX;
lastTickPosY = to.lastTickPosY;
lastTickPosZ = to.lastTickPosZ;
updateBoundingBox();
}
private void updateBoundingBox() {
setEntityBoundingBox(new AxisAlignedBB(
posX - width / 2, posY, posZ - width / 2,
posX + width / 2, posY + height, posZ + width / 2));
}
@Override
public void onUpdate() {
Entity view = mc.getRenderViewEntity();
if (view != null) {
// Make sure we're always spectating the right entity
// This is important if the spectated player respawns as their
// entity is recreated and we have to spectate a new entity
UUID spectating = ReplayModReplay.instance.getReplayHandler().getSpectatedUUID();
if (spectating != null && (view.getUniqueID() != spectating || view.worldObj != worldObj)) {
view = worldObj.getPlayerEntityByUUID(spectating);
if (view != null) {
mc.setRenderViewEntity(view);
} else {
mc.setRenderViewEntity(this);
return;
}
}
// Move cmera to their position so when we exit the first person view
// we don't jump back to where we entered it
if (view != this) {
setCameraPosRot(view);
}
}
}
@Override
public void preparePlayerToSpawn() {
// Make sure our world is up-to-date in case of world changes
if (mc.theWorld != null) {
worldObj = mc.theWorld;
}
super.preparePlayerToSpawn();
}
@Override
public boolean isEntityInsideOpaqueBlock() {
return false; // Make sure no suffocation overlay is rendered
}
@Override
public boolean isInsideOfMaterial(Material materialIn) {
return false; // Make sure no overlays are rendered
}
@Override
public boolean isInLava() {
return false; // Make sure no lava overlay is rendered
}
@Override
public boolean isInWater() {
return false; // Make sure no water overlay is rendered
}
@Override
public boolean canBePushed() {
return false; // We are in full control of ourselves
}
@Override
protected void createRunningParticles() {
// We do not produce any particles, we are a camera
}
@Override
public boolean canBeCollidedWith() {
return false; // We are a camera, we cannot collide
}
@Override
public boolean isSpectator() {
return true; // Make sure we're treated as spectator
}
@Override
public MovingObjectPosition rayTrace(double p_174822_1_, float p_174822_3_) {
MovingObjectPosition pos = super.rayTrace(p_174822_1_, 1f);
// Make sure we can never look at blocks (-> no outline)
if(pos != null && pos.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
pos.typeOfHit = MovingObjectPosition.MovingObjectType.MISS;
}
return pos;
}
@Override
public void setDead() {
super.setDead();
FMLCommonHandler.instance().bus().unregister(this);
}
@SubscribeEvent
public void onRenderUpdate(TickEvent.RenderTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
long now = System.currentTimeMillis();
long timePassed = now - lastControllerUpdate;
cameraController.update(timePassed / 50f);
lastControllerUpdate = now;
}
}
}

View File

@@ -0,0 +1,116 @@
package com.replaymod.replay.camera;
public class ClassicCameraController implements CameraController {
@Override
public void update(float partialTicksPassed) {
// TODO
}
@Override
public void increaseSpeed() {
}
@Override
public void decreaseSpeed() {
}
// public static final double SPEED_CHANGE = 0.5;
// public static final double LOWER_SPEED = 2;
// public static final double UPPER_SPEED = 20;
//
// private static double MAX_SPEED = 10;
// private static double THRESHOLD = MAX_SPEED / 20;
// private static double DECAY = MAX_SPEED/3;
//
// public static void modifyCameraSpeed(boolean increase) {
// setCameraMaximumSpeed(getCameraMaximumSpeed() + (increase ? 1 : -1) * SPEED_CHANGE);
// }
//
// public static void setCameraMaximumSpeed(double maxSpeed) {
// if(maxSpeed < LOWER_SPEED || maxSpeed > UPPER_SPEED) return;
// MAX_SPEED = maxSpeed;
// THRESHOLD = MAX_SPEED / 20;
// DECAY = 5;
// }
//
// public static double getCameraMaximumSpeed() {
// return MAX_SPEED;
// }
//
// private Vec3 direction;
// private Vec3 dirBefore;
// private double motion;
// private long lastCall = 0;
//
// private boolean speedup = false;
//
// //frac = time since last tick
// public void updateMovement() {
//
// long frac = Sys.getTime() - lastCall;
//
// if(frac == 0) return;
//
// double decFac = Math.max(0, 1 - (DECAY * (frac / 1000D)));
//
// if(speedup) {
// if(motion < THRESHOLD) motion = THRESHOLD;
// motion /= decFac;
// } else {
// motion *= decFac;
// }
//
// motion = Math.min(motion, MAX_SPEED);
//
// lastCall = Sys.getTime();
//
// if(direction == null || motion < THRESHOLD) {
// return;
// }
//
// Vec3 movement = direction.normalize();
// double factor = motion * (frac / 1000D);
//
// moveCamera(movement.xCoord * factor, movement.yCoord * factor, movement.zCoord * factor);
// }
//
// public void setMovement(MoveDirection dir) {
// switch(dir) {
// case BACKWARD:
// direction = this.getVectorForRotation(-rotationPitch, rotationYaw - 180);
// break;
// case DOWN:
// direction = this.getVectorForRotation(90, 0);
// break;
// case FORWARD:
// direction = this.getVectorForRotation(rotationPitch, rotationYaw);
// break;
// case LEFT:
// direction = this.getVectorForRotation(0, rotationYaw - 90);
// break;
// case RIGHT:
// direction = this.getVectorForRotation(0, rotationYaw + 90);
// break;
// case UP:
// direction = this.getVectorForRotation(-90, 0);
// break;
// }
//
// Vec3 dbf = direction;
//
// if(dirBefore != null) {
// direction = dirBefore.normalize().add(direction);
// }
//
// dirBefore = dbf;
//
// updateMovement();
// }
//
// public enum MoveDirection {
// UP, DOWN, LEFT, RIGHT, FORWARD, BACKWARD
// }
}

View File

@@ -0,0 +1,74 @@
package com.replaymod.replay.camera;
import com.sun.javafx.geom.Vec3d;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.settings.KeyBinding;
/**
* Camera controller performing vanilla creative-like camera movements.
*/
public class VanillaCameraController implements CameraController {
private static final int MAX_SPEED = 1000;
private static final int MIN_SPEED = -1000;
private static final Vec3d[] DIRECTIONS = new Vec3d[]{
new Vec3d(0, 0, 1), new Vec3d(0, 0, -1), new Vec3d(1, 0, 0), new Vec3d(-1, 0, 0),
new Vec3d(0, 1, 0), new Vec3d(0, -1, 0),
};
private final KeyBinding[] bindings = new KeyBinding[6];
private final CameraEntity camera;
private int speed;
public VanillaCameraController(Minecraft mc, CameraEntity camera) {
this.camera = camera;
GameSettings gameSettings = mc.gameSettings;
this.bindings[0] = gameSettings.keyBindForward;
this.bindings[1] = gameSettings.keyBindBack;
this.bindings[2] = gameSettings.keyBindLeft;
this.bindings[3] = gameSettings.keyBindRight;
this.bindings[4] = gameSettings.keyBindJump;
this.bindings[5] = gameSettings.keyBindSneak;
}
@Override
public void update(float partialTicksPassed) {
if (partialTicksPassed == 0) return;
Vec3d direction = new Vec3d(0, 0, 0);
for (int i = 0; i < 6; i++) { // First, get movement direction depending on keys pressed
if (bindings[i].isKeyDown()) {
direction.add(DIRECTIONS[i]);
}
}
if (direction.length() == 0) return;
direction.normalize(); // Normalize, so we don't move quicker if we hold down multiple keys
double yawRadians = Math.toRadians(camera.rotationYaw);
double yawSin = Math.sin(yawRadians), yawCos = Math.cos(yawRadians);
// Rotate by yaw
direction.set(
direction.x * yawCos - direction.z * yawSin,
direction.y,
direction.x * yawSin + direction.z * yawCos
);
// Adjust for current speed
// We transform speed to blocks per second: x->2^(x/300)
direction.mul(Math.pow(2, speed / 300d));
// Adjust for time passed
direction.mul(partialTicksPassed / 20);
// Actually move
camera.moveCamera(direction.x, direction.y, direction.z);
}
@Override
public void increaseSpeed() {
speed = Math.min(MAX_SPEED, speed + 1);
}
@Override
public void decreaseSpeed() {
speed = Math.max(MIN_SPEED, speed - 1);
}
}