Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/cam72cam/mod/ModCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import cam72cam.mod.event.ClientEvents;
import cam72cam.mod.gui.GuiRegistry;
import cam72cam.mod.input.Mouse;
import cam72cam.mod.render.SmoothFloat;
import cam72cam.mod.net.Packet;
import cam72cam.mod.net.PacketDirection;
import cam72cam.mod.render.BlockRender;
Expand Down Expand Up @@ -290,6 +291,7 @@ public void clientEvent(ModEvent event) {
});
BlockRender.onPostColorSetup();
ClientEvents.fireReload();
ClientEvents.TICK.subscribe(SmoothFloat::onClientTick);
break;
}

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/cam72cam/mod/event/ClientEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import cam72cam.mod.gui.helpers.GUIHelpers;
import cam72cam.mod.input.Mouse;
import cam72cam.mod.math.Vec3d;
import cam72cam.mod.render.CameraUtils;
import cam72cam.mod.render.EntityRenderer;
import cam72cam.mod.render.GlobalRender;
import cam72cam.mod.render.opengl.CustomTexture;
Expand Down Expand Up @@ -212,6 +213,15 @@ public static void onRenderMouseover(DrawBlockHighlightEvent event) {
RENDER_MOUSEOVER.execute(x -> x.accept(event.getPartialTicks()));
}

@SubscribeEvent
public static void onCameraSetup(EntityViewRenderEvent.CameraSetup event) {
CameraUtils.applyTranslation(event);
}

public static void onFOVSetup(EntityViewRenderEvent.FOVModifier event) {
CameraUtils.applyFov(event);
}

@SubscribeEvent
public static void onSoundLoad(SoundLoadEvent event) {
SOUND_LOAD.execute(x -> x.accept(event));
Expand Down
148 changes: 148 additions & 0 deletions src/main/java/cam72cam/mod/render/CameraUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package cam72cam.mod.render;

import cam72cam.mod.MinecraftClient;
import cam72cam.mod.math.Vec3d;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraftforge.client.event.EntityViewRenderEvent;

import java.util.*;

public class CameraUtils {
private static final List<Controller> controllers = new ArrayList<>();
private static float cameraRoll;
private static boolean cameraColliding;

public static Perspective getPerspective() {
if (MinecraftClient.isReady()) {
switch (Minecraft.getMinecraft().gameSettings.thirdPersonView) {
case 0:
return Perspective.FIRST_PERSON;
case 1:
return Perspective.THIRD_PERSON;
case 2:
return Perspective.THIRD_PERSON_INVERTED;
}
}

return Perspective.FIRST_PERSON;
}

public static void setPerspective(Perspective perspective) {
if (MinecraftClient.isReady()) {
switch (perspective) {
case FIRST_PERSON:
Minecraft.getMinecraft().gameSettings.thirdPersonView = 0;
return;
case THIRD_PERSON:
Minecraft.getMinecraft().gameSettings.thirdPersonView = 1;
return;
case THIRD_PERSON_INVERTED:
Minecraft.getMinecraft().gameSettings.thirdPersonView = 2;
}
}
}

public static float getFov() {
return Minecraft.getMinecraft().gameSettings.fovSetting;
}

/** Get global position of the player's eyes (with partialTicks taken into account) */
public static Vec3d getCameraPos(float partialTicks) {
net.minecraft.entity.Entity playerRender = Minecraft.getMinecraft().getRenderViewEntity();
double d0 = playerRender.lastTickPosX + (playerRender.posX - playerRender.lastTickPosX) * partialTicks;
double d1 = playerRender.lastTickPosY + (playerRender.posY - playerRender.lastTickPosY) * partialTicks;
double d2 = playerRender.lastTickPosZ + (playerRender.posZ - playerRender.lastTickPosZ) * partialTicks;
return new Vec3d(d0, d1, d2);
}

public static float getCameraYaw(float partialTicks) {
net.minecraft.entity.Entity playerRender = Minecraft.getMinecraft().getRenderViewEntity();
return playerRender.prevRotationYaw + (playerRender.rotationYaw - playerRender.prevRotationYaw) * partialTicks;
}

public static float getCameraPitch(float partialTicks) {
net.minecraft.entity.Entity playerRender = Minecraft.getMinecraft().getRenderViewEntity();
return playerRender.prevRotationPitch + (playerRender.rotationPitch - playerRender.prevRotationPitch) * partialTicks;
}

public static float getCameraRoll() {
return cameraRoll;
}

public static Controller newController(Perspective... activeIn) {
return new Controller(Arrays.asList(activeIn));
}

public static void applyTranslation(EntityViewRenderEvent.CameraSetup event) {
cameraRoll = event.getRoll();

float x = 0, y = 0, z = 0;
float yaw = 0, pitch = 0, roll = 0;

float partialTicks = (float) event.getRenderPartialTicks();

Perspective perspective = getPerspective();
for (Controller controller : controllers) {
if (!controller.perspectives.contains(perspective))
continue;
x += controller.xOffset.getValue(partialTicks);
y += controller.yOffset.getValue(partialTicks);
z += controller.zOffset.getValue(partialTicks);
yaw += controller.yawOffset.getValue(partialTicks);
pitch += controller.pitchOffset.getValue(partialTicks);
roll += controller.rollOffset.getValue(partialTicks);
}

GlStateManager.translate(x, y, z);
event.setYaw(yaw);
event.setPitch(pitch);
event.setRoll(roll);
}

public static void applyFov(EntityViewRenderEvent.FOVModifier event) {
float fov = 0;
for (Controller controller : controllers) {
fov += controller.fovOffset.getValue((float) event.getRenderPartialTicks());
}
event.setFOV(fov);
}

public enum Perspective {
FIRST_PERSON,
THIRD_PERSON,
THIRD_PERSON_INVERTED,
}

public static class Controller {
//+X is left, +Y is top, +Z is back
public final SmoothFloat xOffset;
public final SmoothFloat yOffset;
public final SmoothFloat zOffset;

//Roll is the first to be applied, then pitch, then yaw
public final SmoothFloat rollOffset;
public final SmoothFloat pitchOffset;
public final SmoothFloat yawOffset;

public final SmoothFloat fovOffset;

final List<Perspective> perspectives;

private Controller(List<Perspective> perspectives) {
this.xOffset = new SmoothFloat(0);
this.yOffset = new SmoothFloat(0);
this.zOffset = new SmoothFloat(0);

this.yawOffset = new SmoothFloat(0);
this.pitchOffset = new SmoothFloat(0);
this.rollOffset = new SmoothFloat(0);

this.fovOffset = new SmoothFloat(0);

this.perspectives = perspectives;

controllers.add(this);
}
}
}
6 changes: 5 additions & 1 deletion src/main/java/cam72cam/mod/render/GlobalRender.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ public static boolean isTransparentPass() {
return MinecraftForgeClient.getRenderPass() != 0;
}

/** Get global position of the player's eyes (with partialTicks taken into account) */
/**
* Get global position of the player's eyes (with partialTicks taken into account)
* @deprecated use the one in CameraUtils
* */
@Deprecated
public static Vec3d getCameraPos(float partialTicks) {
net.minecraft.entity.Entity playerRender = Minecraft.getMinecraft().getRenderViewEntity();
double d0 = playerRender.lastTickPosX + (playerRender.posX - playerRender.lastTickPosX) * partialTicks;
Expand Down
169 changes: 169 additions & 0 deletions src/main/java/cam72cam/mod/render/SmoothFloat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package cam72cam.mod.render;

import net.minecraft.util.math.MathHelper;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import java.lang.ref.WeakReference;
import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;

/**
* A class for smoothing input values with Hermite function (3t^2 - 2t^3)
*/
@SideOnly(Side.CLIENT)
public class SmoothFloat {
private static final Deque<WeakReference<SmoothFloat>> instances = new ConcurrentLinkedDeque<>();

private float currentValue;
private float currentVelocity;

private float startValue;
private float startVelocity;

private float targetValue;
private float targetVelocity;

private float durationTicks;
private float elapsedTicks;
private boolean active;

public SmoothFloat(float value) {
this.currentValue = value;
this.currentVelocity = 0.0f;

this.startValue = value;
this.startVelocity = 0.0f;

this.targetValue = value;
this.targetVelocity = 0.0f;

this.durationTicks = 0.0f;
this.elapsedTicks = 0.0f;
this.active = false;

instances.add(new WeakReference<>(this));
}

public float getValue(float partialTicks) {
if (!active || durationTicks <= 0.0f) {
return currentValue;
}

float t = MathHelper.clamp((elapsedTicks + MathHelper.clamp(partialTicks, 0, 1)) / durationTicks, 0, 1);
return evaluatePosition(t);
}

public float getTargetValue() {
return targetValue;
}

public void setNewValue(float newValue, float expectedTicks) {
if (expectedTicks <= 0.0f) {
//If we want to set it instantly
currentValue = newValue;
currentVelocity = 0.0f;

startValue = newValue;
startVelocity = 0.0f;

targetValue = newValue;
targetVelocity = 0.0f;

durationTicks = 0.0f;
elapsedTicks = 0.0f;
active = false;
return;
}

//Or use current velocity
float v = getCurrentVelocityAtTime(elapsedTicks, durationTicks);

currentValue = getValue(0.0f);

startValue = currentValue;
startVelocity = v;

targetValue = newValue;
targetVelocity = 0.0f;

durationTicks = expectedTicks;
elapsedTicks = 0.0f;
active = true;
}

private void tick() {
if (!active || durationTicks <= 0.0f) {
return;
}

elapsedTicks += 1.0f;

if (elapsedTicks >= durationTicks) {
currentValue = targetValue;
currentVelocity = targetVelocity;
active = false;
return;
}

float t = MathHelper.clamp(elapsedTicks / durationTicks, 0, 1);
currentValue = evaluatePosition(t);
currentVelocity = evaluateVelocity(t) / durationTicks;
}

public static void onClientTick() {
Iterator<WeakReference<SmoothFloat>> it = instances.iterator();
while (it.hasNext()) {
WeakReference<SmoothFloat> ref = it.next();
SmoothFloat instance = ref.get();
if (instance != null) {
instance.tick();
} else {
it.remove();
}
}
}

private float evaluatePosition(float t) {
t = MathHelper.clamp(t, 0, 1);

float t2 = t * t;
float t3 = t2 * t;

float h00 = 2.0f * t3 - 3.0f * t2 + 1.0f;
float h10 = t3 - 2.0f * t2 + t;
float h01 = -2.0f * t3 + 3.0f * t2;
float h11 = t3 - t2;

return h00 * startValue
+ h10 * startVelocity * durationTicks
+ h01 * targetValue
+ h11 * targetVelocity * durationTicks;
}

private float evaluateVelocity(float t) {
t = MathHelper.clamp(t, 0, 1);

float t2 = t * t;

float dh00 = 6.0f * t2 - 6.0f * t;
float dh10 = 3.0f * t2 - 4.0f * t + 1.0f;
float dh01 = -6.0f * t2 + 6.0f * t;
float dh11 = 3.0f * t2 - 2.0f * t;

return dh00 * startValue
+ dh10 * startVelocity * durationTicks
+ dh01 * targetValue
+ dh11 * targetVelocity * durationTicks;
}

private float getCurrentVelocityAtTime(float elapsedTicks, float durationTicks) {
if (!active || durationTicks <= 0.0f) {
return currentVelocity;
}

float t = MathHelper.clamp(elapsedTicks / durationTicks, 0, 1);
return evaluateVelocity(t) / durationTicks;
}
}