Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase FOV when moving fast (Issue#716) #812

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
21 changes: 21 additions & 0 deletions src/game.zig
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ pub const Player = struct { // MARK: Player
pub var eyeVel: Vec3d = .{0, 0, 0};
pub var eyeCoyote: f64 = 0;
pub var eyeStep: @Vector(3, bool) = .{false, false, false};
pub var speedMod: f32 = 0.0;
pub var id: u32 = 0;
pub var gamemode: Atomic(Gamemode) = .init(.creative);
pub var isFlying: Atomic(bool) = .init(false);
Expand Down Expand Up @@ -401,6 +402,12 @@ pub const Player = struct { // MARK: Player
return eyeCoyote;
}

pub fn getSpeedModifierBlocking() f32 {
mutex.lock();
defer mutex.unlock();
return speedMod;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just make fovMod atomic?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Why does it have to be atomic, when other, similar, variables (like eyeVel or eyePos) are not atomic either?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's only ever used by the update and render functions, then you are right, it doesn't need to be thread safe.

pub fn setGamemode(newGamemode: Gamemode) void {
gamemode.store(newGamemode, .monotonic);

Expand Down Expand Up @@ -894,6 +901,20 @@ pub fn update(deltaTime: f64) void { // MARK: update()
Player.eyeVel[i] = firstTerm.mul(c_3.negate().subScalar(frictionCoefficient).mulScalar(0.5)).add(secondTerm.mul((c_3.subScalar(frictionCoefficient)).mulScalar(0.5))).val[0];
Player.eyePos[i] += firstTerm.add(secondTerm).addScalar(a/k).val[0];
}

// Calculate our FOV modifier based on our current speed.
{
const x : f32 = @as(f32, @floatCast(movementSpeed));
const speedModGoal : f32 = @max(@log10((x*x)-15)/1.8, 0);
const speedModDir: f32 = if(Player.speedMod < speedModGoal) 1 else -1;
const speedModNext: f32 = Player.speedMod + (speedModDir * @as(f32, @floatCast(deltaTime)) * 5.0);

if((Player.speedMod < speedModGoal and speedModNext < speedModGoal) or (Player.speedMod > speedModGoal and speedModNext > speedModGoal)) {
Player.speedMod = speedModNext;
} else {
Player.speedMod = speedModGoal;
}
}
}

const time = std.time.milliTimestamp();
Expand Down
10 changes: 10 additions & 0 deletions src/gui/windows/graphics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ fn fovFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const
return std.fmt.allocPrint(allocator.allocator, "#ffffffField Of View: {d:.0}°", .{value}) catch unreachable;
}

fn fovSpeedAddCallback(newValue: f32) void {
settings.speedFovAdd = newValue;
settings.save();
}

fn fovSpeedAddFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffFoV Speed Widening: {d:.0}°", .{value}) catch unreachable;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not happy with the naming here. How do other games call this option?

Also the naming is inconsistent: The function is called fovSpeedAdd, the option is called speedFovAdd and the user sees FoV Speed Widening.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not happy with the naming here. How do other games call this option?

Also the naming is inconsistent: The function is called fovSpeedAdd, the option is called speedFovAdd and the user sees FoV Speed Widening.

Good point. I'll check what other games do as nothing comes to mind right now.

Not sure why I skipped over the naming inconsistencies. Will fix, thanks!

What do you think of the points brought up by Ikabod-kee? I imagine you have the final say, right? :D


fn lodDistanceFormatter(allocator: main.utils.NeverFailingAllocator, value: f32) []const u8 {
return std.fmt.allocPrint(allocator.allocator, "#ffffffOpaque leaves distance: {d:.0}", .{@round(value)}) catch unreachable;
}
Expand Down Expand Up @@ -118,6 +127,7 @@ pub fn onOpen() void {
list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffLeaves Quality (TODO: requires reload): ", "{}", &leavesQualities, settings.leavesQuality - leavesQualities[0], &leavesQualityCallback));
list.add(ContinuousSlider.init(.{0, 0}, 128, 50.0, 400.0, settings.@"lod0.5Distance", &lodDistanceCallback, &lodDistanceFormatter));
list.add(ContinuousSlider.init(.{0, 0}, 128, 40.0, 120.0, settings.fov, &fovCallback, &fovFormatter));
list.add(ContinuousSlider.init(.{ 0, 0 }, 128, 0, 30, settings.speedFovAdd, &fovSpeedAddCallback, &fovSpeedAddFormatter));
list.add(CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback));
list.add(CheckBox.init(.{0, 0}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback));
list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffAnisotropic Filtering: ", "{}x", &anisotropy, switch(settings.anisotropicFiltering) {1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4, else => 2}, &anisotropicFilteringCallback));
Expand Down
4 changes: 4 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,10 @@ pub fn main() void { // MARK: main()
if(!isHidden) {
c.glEnable(c.GL_CULL_FACE);
c.glEnable(c.GL_DEPTH_TEST);

const fov: f32 = if (game.world != null) settings.fov + (game.Player.getSpeedModifierBlocking() * settings.speedFovAdd) else 70.0;
renderer.updateFOV(fov);

renderer.render(game.Player.getEyePosBlocking());
// Render the GUI
gui.windowlist.gpu_performance_measuring.startQuery(.gui);
Expand Down
11 changes: 8 additions & 3 deletions src/renderer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,14 @@ var lastFov: f32 = 0;
pub fn updateViewport(width: u31, height: u31, fov: f32) void {
IntegratedQuantum marked this conversation as resolved.
Show resolved Hide resolved
lastWidth = @intFromFloat(@as(f32, @floatFromInt(width))*main.settings.resolutionScale);
lastHeight = @intFromFloat(@as(f32, @floatFromInt(height))*main.settings.resolutionScale);
updateFOV(fov);
worldFrameBuffer.updateSize(lastWidth, lastHeight, c.GL_RGB16F);
worldFrameBuffer.unbind();
}

pub fn updateFOV(fov: f32) void {
lastFov = fov;
game.projectionMatrix = Mat4f.perspective(std.math.degreesToRadians(fov), @as(f32, @floatFromInt(lastWidth))/@as(f32, @floatFromInt(lastHeight)), zNear, zFar);
worldFrameBuffer.updateSize(lastWidth, lastHeight, c.GL_RGB16F);
worldFrameBuffer.unbind();
}

pub fn render(playerPosition: Vec3d) void {
Expand Down Expand Up @@ -527,9 +531,10 @@ pub const MenuBackGround = struct {

const oldResolutionScale = main.settings.resolutionScale;
main.settings.resolutionScale = 1;
const fovPrev: f32 = lastFov;
updateViewport(size, size, 90.0);
main.settings.resolutionScale = oldResolutionScale;
defer updateViewport(Window.width, Window.height, settings.fov);
defer updateViewport(Window.width, Window.height, fovPrev);

var buffer: graphics.FrameBuffer = undefined;
buffer.init(true, c.GL_NEAREST, c.GL_REPEAT);
Expand Down
1 change: 1 addition & 0 deletions src/settings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub var anisotropicFiltering: u8 = 4.0;
pub var fpsCap: ?u32 = null;

pub var fov: f32 = 70;
pub var speedFovAdd: f32 = 15;

pub var mouseSensitivity: f32 = 1;
pub var controllerSensitivity: f32 = 1;
Expand Down
Loading