Add movement and camera rotation.

This commit is contained in:
IntegratedQuantum 2022-09-17 10:50:24 +02:00
parent d532848262
commit c742b5ed74
4 changed files with 224 additions and 46 deletions

View File

@ -1,8 +1,11 @@
const std = @import("std");
const assets = @import("assets.zig");
const main = @import("main.zig");
const keyboard = &main.keyboard;
const vec = @import("vec.zig");
const Vec3f = vec.Vec3f;
const Vec3d = vec.Vec3d;
const Mat4f = vec.Mat4f;
const graphics = @import("graphics.zig");
const Fog = graphics.Fog;
@ -30,6 +33,9 @@ pub const camera = struct {
}
};
pub var playerPos: Vec3d = Vec3d{.x=0, .y=0, .z=0};
pub var isFlying: bool = true;
pub var blockPalette: *assets.BlockPalette = undefined;
pub const World = u1; // TODO
pub var testWorld: World = 0;
@ -38,4 +44,53 @@ pub var world: ?*World = &testWorld;
pub var projectionMatrix: Mat4f = Mat4f.identity();
pub var lodProjectionMatrix: Mat4f = Mat4f.identity();
pub var fog = Fog{.active = true, .color=.{.x=0, .y=1, .z=0.5}, .density=1.0/15.0/256.0};
pub var fog = Fog{.active = true, .color=.{.x=0, .y=1, .z=0.5}, .density=1.0/15.0/256.0};
pub fn update(deltaTime: f64) void {
var movement = Vec3d{.x=0, .y=0, .z=0};
var forward = Vec3d.rotateY(Vec3d{.x=0, .y=0, .z=-1}, -camera.rotation.y);
var right = Vec3d{.x=forward.z, .y=0, .z=-forward.x};
if(keyboard.forward.pressed) {
if(keyboard.sprint.pressed) {
if(isFlying) {
movement.addEqual(forward.mulScalar(64));
} else {
movement.addEqual(forward.mulScalar(8));
}
} else {
movement.addEqual(forward.mulScalar(4));
}
}
if(keyboard.backward.pressed) {
movement.addEqual(forward.mulScalar(-4));
}
if(keyboard.left.pressed) {
movement.addEqual(right.mulScalar(4));
}
if(keyboard.right.pressed) {
movement.addEqual(right.mulScalar(-4));
}
if(keyboard.jump.pressed) {
if(isFlying) {
if(keyboard.sprint.pressed) {
movement.y = 59.45;
} else {
movement.y = 5.45;
}
} else { // TODO: if (Cubyz.player.isOnGround())
movement.y = 5.45;
}
}
if(keyboard.fall.pressed) {
if(isFlying) {
if(keyboard.sprint.pressed) {
movement.y = -59.45;
} else {
movement.y = -5.45;
}
}
}
playerPos.addEqual(movement.mulScalar(deltaTime));
}

View File

@ -11,6 +11,7 @@ const settings = @import("settings.zig");
const utils = @import("utils.zig");
const Vec2f = @import("vec.zig").Vec2f;
const Vec3d = @import("vec.zig").Vec3d;
pub const c = @cImport ({
@cInclude("glad/glad.h");
@ -43,20 +44,55 @@ pub fn log(
nosuspend std.io.getStdErr().writer().print(color ++ format ++ "\x1b[0m\n", args) catch {};
}
const Key = struct {
pressed: bool = false,
key: c_int = c.GLFW_KEY_UNKNOWN,
scancode: c_int = 0,
releaseAction: ?*const fn() void = null,
};
pub var keyboard: struct {
forward: Key = Key{.key = c.GLFW_KEY_W},
left: Key = Key{.key = c.GLFW_KEY_A},
backward: Key = Key{.key = c.GLFW_KEY_S},
right: Key = Key{.key = c.GLFW_KEY_D},
sprint: Key = Key{.key = c.GLFW_KEY_LEFT_CONTROL},
jump: Key = Key{.key = c.GLFW_KEY_SPACE},
fall: Key = Key{.key = c.GLFW_KEY_LEFT_SHIFT},
fullscreen: Key = Key{.key = c.GLFW_KEY_F11, .releaseAction = &Window.toggleFullscreen},
} = .{};
pub const Window = struct {
var isFullscreen: bool = false;
pub var width: u31 = 1280;
pub var height: u31 = 720;
var window: *c.GLFWwindow = undefined;
pub var grabbed: bool = false;
const GLFWCallbacks = struct {
fn errorCallback(errorCode: c_int, description: [*c]const u8) callconv(.C) void {
std.log.err("GLFW Error({}): {s}", .{errorCode, description});
}
fn keyCallback(_: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.C) void {
std.log.info("Key pressed: {}, {}, {}, {}", .{key, scancode, action, mods});
if(key == c.GLFW_KEY_F11 and action == c.GLFW_RELEASE) {
toggleFullscreen();
if(action == c.GLFW_PRESS) {
inline for(@typeInfo(@TypeOf(keyboard)).Struct.fields) |field| {
if(key == @field(keyboard, field.name).key) {
if(key != c.GLFW_KEY_UNKNOWN or scancode == @field(keyboard, field.name).scancode) {
@field(keyboard, field.name).pressed = true;
}
}
}
} else if(action == c.GLFW_RELEASE) {
inline for(@typeInfo(@TypeOf(keyboard)).Struct.fields) |field| {
if(key == @field(keyboard, field.name).key) {
if(key != c.GLFW_KEY_UNKNOWN or scancode == @field(keyboard, field.name).scancode) {
@field(keyboard, field.name).pressed = false;
if(@field(keyboard, field.name).releaseAction) |releaseAction| {
releaseAction();
}
}
}
}
}
std.log.info("Key pressed: {}, {}, {}, {}", .{key, scancode, action, mods});
}
fn framebufferSize(_: ?*c.GLFWwindow, newWidth: c_int, newHeight: c_int) callconv(.C) void {
std.log.info("Framebuffer: {}, {}", .{newWidth, newHeight});
@ -64,6 +100,31 @@ pub const Window = struct {
height = @intCast(u31, newHeight);
renderer.updateViewport(width, height, settings.fov);
}
// Mouse deltas are averaged over multiple frames using a circular buffer:
const deltasLen: u2 = 3;
var deltas: [deltasLen]Vec2f = [_]Vec2f{Vec2f{.x=0, .y=0}} ** 3;
var deltaBufferPosition: u2 = 0;
var currentPos: Vec2f = Vec2f{.x=0, .y=0};
var ignoreDataAfterRecentGrab: bool = true;
fn cursorPosition(_: ?*c.GLFWwindow, x: f64, y: f64) callconv(.C) void {
const newPos = Vec2f {
.x = @floatCast(f32, x),
.y = @floatCast(f32, y),
};
if(grabbed and !ignoreDataAfterRecentGrab) {
deltas[deltaBufferPosition].addEqual(newPos.sub(currentPos).mulScalar(settings.mouseSensitivity));
var averagedDelta: Vec2f = Vec2f{.x=0, .y=0};
for(deltas) |delta| {
averagedDelta.addEqual(delta);
}
averagedDelta.divEqualScalar(deltasLen);
game.camera.moveRotation(averagedDelta.x*0.0089, averagedDelta.y*0.0089);
deltaBufferPosition = (deltaBufferPosition + 1)%deltasLen;
deltas[deltaBufferPosition] = Vec2f{.x=0, .y=0};
}
ignoreDataAfterRecentGrab = false;
currentPos = newPos;
}
fn glDebugOutput(_: c_uint, typ: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void {
if(typ == c.GL_DEBUG_TYPE_ERROR or typ == c.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR or typ == c.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR or typ == c.GL_DEBUG_TYPE_PORTABILITY or typ == c.GL_DEBUG_TYPE_PERFORMANCE) {
std.log.err("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]});
@ -72,6 +133,20 @@ pub const Window = struct {
}
};
pub fn setMouseGrabbed(grab: bool) void {
if(grabbed != grab) {
if(!grab) {
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_NORMAL);
} else {
c.glfwSetInputMode(window, c.GLFW_CURSOR, c.GLFW_CURSOR_DISABLED);
if (c.glfwRawMouseMotionSupported() != 0)
c.glfwSetInputMode(window, c.GLFW_RAW_MOUSE_MOTION, c.GLFW_TRUE);
GLFWCallbacks.ignoreDataAfterRecentGrab = true;
}
grabbed = grab;
}
}
fn init() !void {
_ = c.glfwSetErrorCallback(GLFWCallbacks.errorCallback);
@ -89,6 +164,7 @@ pub const Window = struct {
_ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback);
_ = c.glfwSetFramebufferSizeCallback(window, GLFWCallbacks.framebufferSize);
_ = c.glfwSetCursorPosCallback(window, GLFWCallbacks.cursorPosition);
c.glfwMakeContextCurrent(window);
@ -182,6 +258,8 @@ pub fn main() !void {
try network.Protocols.handShake.clientSide(conn2, "quanturmdoelvloper");
Window.setMouseGrabbed(true);
try assets.loadWorldAssets("serverAssets", game.blockPalette);
try blocks.meshes.generateTextureArray();
@ -190,6 +268,7 @@ pub fn main() !void {
c.glEnable(c.GL_BLEND);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);
Window.GLFWCallbacks.framebufferSize(null, Window.width, Window.height);
var lastTime = std.time.milliTimestamp();
while(c.glfwWindowShouldClose(Window.window) == 0) {
{ // Check opengl errors:
@ -198,14 +277,17 @@ pub fn main() !void {
std.log.err("Got opengl error: {}", .{err});
}
}
game.camera.moveRotation(0.01, 0);
c.glfwSwapBuffers(Window.window);
c.glfwPollEvents();
try renderer.RenderOctree.update(conn2, .{.x = 25, .y = 11, .z = -703}, 4, 2.0);
var newTime = std.time.milliTimestamp();
var deltaTime = @intToFloat(f64, newTime -% lastTime)/1000.0;
lastTime = newTime;
game.update(deltaTime);
try renderer.RenderOctree.update(conn2, game.playerPos, 4, 2.0);
{ // Render the game
c.glEnable(c.GL_CULL_FACE);
c.glEnable(c.GL_DEPTH_TEST);
try renderer.render(.{.x = 25, .y = 11, .z = -703});
try renderer.render(game.playerPos);
}
{ // Render the GUI

View File

@ -9,4 +9,6 @@ pub const highestLOD: u5 = 5;
pub var fov: f32 = 45;
pub var fov: f32 = 45;
pub var mouseSensitivity: f32 = 1;

View File

@ -8,45 +8,9 @@ pub const Vec3f = extern struct {// This one gets a bit of extra functionality f
x: f32,
y: f32,
z: f32,
pub usingnamespace Vec3RotationMath(@This(), f32);
pub usingnamespace Vec3SpecificMath(@This(), f32);
pub usingnamespace GenericVectorMath(@This(), f32);
pub fn rotateX(self: Vec3f, angle: f32) Vec3f {
const sin = @sin(angle);
const cos = @cos(angle); // TODO: Consider using sqrt here.
return Vec3f{
.x = self.x,
.y = self.y*cos - self.z*sin,
.z = self.y*sin + self.z*cos,
};
}
pub fn rotateY(self: Vec3f, angle: f32) Vec3f {
const sin = @sin(angle);
const cos = @cos(angle); // TODO: Consider using sqrt here.
return Vec3f{
.x = self.x*cos + self.z*sin,
.y = self.y,
.z = -self.x*sin + self.z*cos,
};
}
pub fn rotateZ(self: Vec3f, angle: f32) Vec3f {
const sin = @sin(angle);
const cos = @cos(angle); // TODO: Consider using sqrt here.
return Vec3f{
.x = self.x*cos - self.y*sin,
.y = self.x*sin + self.y*cos,
.z = self.z,
};
}
pub fn cross(self: @This(), other: @This()) @This() {
return @This() {
.x = self.y*other.z - self.z*other.y,
.y = self.z*other.x - self.x*other.z,
.z = self.x*other.y - self.y*other.x,
};
}
pub fn xyz(self: Vec4f) Vec3f {
return Vec3f{.x=self.x, .y=self.y, .z=self.z};
@ -57,6 +21,57 @@ pub const Vec4i = GenericVector4(i32);
pub const Vec4f = GenericVector4(f32);
pub const Vec4d = GenericVector4(f64);
fn Vec3RotationMath(comptime Vec: type, comptime T: type) type {
if(@typeInfo(Vec).Struct.fields.len == 3 and @typeInfo(T) == .Float) {
return struct{
pub fn rotateX(self: Vec, angle: T) Vec {
const sin = @sin(angle);
const cos = @cos(angle); // TODO: Consider using sqrt here.
return Vec{
.x = self.x,
.y = self.y*cos - self.z*sin,
.z = self.y*sin + self.z*cos,
};
}
pub fn rotateY(self: Vec, angle: T) Vec {
const sin = @sin(angle);
const cos = @cos(angle); // TODO: Consider using sqrt here.
return Vec{
.x = self.x*cos + self.z*sin,
.y = self.y,
.z = -self.x*sin + self.z*cos,
};
}
pub fn rotateZ(self: Vec, angle: T) Vec {
const sin = @sin(angle);
const cos = @cos(angle); // TODO: Consider using sqrt here.
return Vec{
.x = self.x*cos - self.y*sin,
.y = self.x*sin + self.y*cos,
.z = self.z,
};
}
};
} else {
return struct{};
}
}
fn Vec3SpecificMath(comptime Vec: type, comptime T: type) type {
_ = T;
return struct {
pub fn cross(self: Vec, other: Vec) Vec {
return Vec {
.x = self.y*other.z - self.z*other.y,
.y = self.z*other.x - self.x*other.z,
.z = self.x*other.y - self.y*other.x,
};
}
};
}
fn GenericVectorMath(comptime Vec: type, comptime T: type) type {
return struct {
pub fn add(self: Vec, other: Vec) Vec {
@ -103,6 +118,18 @@ fn GenericVectorMath(comptime Vec: type, comptime T: type) type {
}
}
pub fn divScalar(self: Vec, scalar: T) Vec {
if(@typeInfo(T) == .Float) {
var result: Vec = undefined;
inline for(@typeInfo(Vec).Struct.fields) |field| {
@field(result, field.name) = @field(self, field.name) / scalar;
}
return result;
} else {
@compileError("Not supported for integer types.");
}
}
pub fn minimum(self: Vec, other: Vec) Vec {
var result: Vec = undefined;
inline for(@typeInfo(Vec).Struct.fields) |field| {
@ -153,6 +180,16 @@ fn GenericVectorMath(comptime Vec: type, comptime T: type) type {
}
}
pub fn divEqualScalar(self: *Vec, scalar: T) void {
if(@typeInfo(T) == .Float) {
inline for(@typeInfo(Vec).Struct.fields) |field| {
@field(self, field.name) /= scalar;
}
} else {
@compileError("Not supported for integer types.");
}
}
pub fn dot(self: Vec, other: Vec) T {
var result: T = 0;
inline for(@typeInfo(Vec).Struct.fields) |field| {
@ -176,6 +213,8 @@ fn GenericVector3(comptime T: type) type {
x: T,
y: T,
z: T,
pub usingnamespace Vec3RotationMath(@This(), T);
pub usingnamespace Vec3SpecificMath(@This(), T);
pub usingnamespace GenericVectorMath(@This(), T);
};
}