Cubyz/src/main.zig
2023-01-30 17:23:53 +01:00

356 lines
12 KiB
Zig

const std = @import("std");
const assets = @import("assets.zig");
const blocks = @import("blocks.zig");
const chunk = @import("chunk.zig");
const entity = @import("entity.zig");
const game = @import("game.zig");
const graphics = @import("graphics.zig");
const itemdrop = @import("itemdrop.zig");
const items = @import("items.zig");
const models = @import("models.zig");
const network = @import("network.zig");
const renderer = @import("renderer.zig");
const rotation = @import("rotation.zig");
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");
@cInclude("GLFW/glfw3.h");
});
pub threadlocal var threadAllocator: std.mem.Allocator = undefined;
pub threadlocal var seed: u64 = undefined;
pub var globalAllocator: std.mem.Allocator = undefined;
pub var threadPool: utils.ThreadPool = undefined;
var logFile: std.fs.File = undefined;
// overwrite the log function:
pub const std_options = struct {
pub const log_level = .debug;
pub fn logFn(
comptime level: std.log.Level,
comptime _: @Type(.EnumLiteral),
comptime format: []const u8,
args: anytype,
) void {
const color = comptime switch (level) {
std.log.Level.err => "\x1b[31m",
std.log.Level.info => "",
std.log.Level.warn => "\x1b[33m",
std.log.Level.debug => "\x1b[37;44m",
};
std.debug.getStderrMutex().lock();
defer std.debug.getStderrMutex().unlock();
logFile.writer().print("[" ++ level.asText() ++ "]" ++ ": " ++ format ++ "\n", args) catch {};
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 {
_ = mods;
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();
}
}
}
}
}
}
fn framebufferSize(_: ?*c.GLFWwindow, newWidth: c_int, newHeight: c_int) callconv(.C) void {
std.log.info("Framebuffer: {}, {}", .{newWidth, newHeight});
width = @intCast(u31, newWidth);
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{0, 0}} ** 3;
var deltaBufferPosition: u2 = 0;
var currentPos: Vec2f = Vec2f{0, 0};
var ignoreDataAfterRecentGrab: bool = true;
fn cursorPosition(_: ?*c.GLFWwindow, x: f64, y: f64) callconv(.C) void {
const newPos = Vec2f {
@floatCast(f32, x),
@floatCast(f32, y),
};
if(grabbed and !ignoreDataAfterRecentGrab) {
deltas[deltaBufferPosition] += (newPos - currentPos)*@splat(2, settings.mouseSensitivity);
var averagedDelta: Vec2f = Vec2f{0, 0};
for(deltas) |delta| {
averagedDelta += delta;
}
averagedDelta /= @splat(2, @as(f32, deltasLen));
game.camera.moveRotation(averagedDelta[0]*0.0089, averagedDelta[1]*0.0089);
deltaBufferPosition = (deltaBufferPosition + 1)%deltasLen;
deltas[deltaBufferPosition] = Vec2f{0, 0};
}
ignoreDataAfterRecentGrab = false;
currentPos = newPos;
}
fn glDebugOutput(_: c_uint, _: c_uint, _: c_uint, severity: c_uint, length: c_int, message: [*c]const u8, _: ?*const anyopaque) callconv(.C) void {
if(severity == c.GL_DEBUG_SEVERITY_HIGH) {
std.log.err("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]});
} else if(severity == c.GL_DEBUG_SEVERITY_MEDIUM) {
std.log.warn("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]});
} else if(severity == c.GL_DEBUG_SEVERITY_LOW) {
std.log.info("OpenGL {}:{s}", .{severity, message[0..@intCast(usize, length)]});
}
}
};
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);
if(c.glfwInit() == 0) {
return error.GLFWFailed;
}
if(@import("builtin").mode == .Debug) {
c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, 1);
}
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 3);
window = c.glfwCreateWindow(width, height, "Cubyz", null, null) orelse return error.GLFWFailed;
_ = c.glfwSetKeyCallback(window, GLFWCallbacks.keyCallback);
_ = c.glfwSetFramebufferSizeCallback(window, GLFWCallbacks.framebufferSize);
_ = c.glfwSetCursorPosCallback(window, GLFWCallbacks.cursorPosition);
c.glfwMakeContextCurrent(window);
if(c.gladLoadGL() == 0) {
return error.GLADFailed;
}
c.glfwSwapInterval(0);
if(@import("builtin").mode == .Debug) {
c.glEnable(c.GL_DEBUG_OUTPUT);
c.glEnable(c.GL_DEBUG_OUTPUT_SYNCHRONOUS);
c.glDebugMessageCallback(GLFWCallbacks.glDebugOutput, null);
c.glDebugMessageControl(c.GL_DONT_CARE, c.GL_DONT_CARE, c.GL_DONT_CARE, 0, null, c.GL_TRUE);
}
}
fn deinit() void {
c.glfwDestroyWindow(window);
c.glfwTerminate();
}
var oldX: c_int = 0;
var oldY: c_int = 0;
var oldWidth: c_int = 0;
var oldHeight: c_int = 0;
pub fn toggleFullscreen() void {
isFullscreen = !isFullscreen;
if (isFullscreen) {
c.glfwGetWindowPos(window, &oldX, &oldY);
c.glfwGetWindowSize(window, &oldWidth, &oldHeight);
const monitor = c.glfwGetPrimaryMonitor();
if(monitor == null) {
isFullscreen = false;
return;
}
const vidMode = c.glfwGetVideoMode(monitor).?;
c.glfwSetWindowMonitor(window, monitor, 0, 0, vidMode[0].width, vidMode[0].height, c.GLFW_DONT_CARE);
} else {
c.glfwSetWindowMonitor(window, null, oldX, oldY, oldWidth, oldHeight, c.GLFW_DONT_CARE);
c.glfwSetWindowAttrib(window, c.GLFW_DECORATED, c.GLFW_TRUE);
}
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=false}){};
threadAllocator = gpa.allocator();
defer if(gpa.deinit()) {
@panic("Memory leak");
};
var global_gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=true}){};
globalAllocator = global_gpa.allocator();
defer if(global_gpa.deinit()) {
@panic("Memory leak");
};
// init logging.
try std.fs.cwd().makePath("logs");
logFile = std.fs.cwd().createFile("logs/latest.log", .{}) catch unreachable;
defer logFile.close();
var poolgpa = std.heap.GeneralPurposeAllocator(.{}){};
defer if(poolgpa.deinit()) {
@panic("Memory leak");
};
threadPool = try utils.ThreadPool.init(poolgpa.allocator(), 1 + ((std.Thread.getCpuCount() catch 4) -| 3));
defer threadPool.deinit();
try settings.init();
defer settings.deinit();
try Window.init();
defer Window.deinit();
try graphics.init();
defer graphics.deinit();
try rotation.init();
defer rotation.deinit();
try models.init();
defer models.deinit();
items.globalInit();
defer items.deinit();
try itemdrop.ItemDropRenderer.init();
defer itemdrop.ItemDropRenderer.deinit();
try assets.init();
defer assets.deinit();
blocks.meshes.init();
defer blocks.meshes.deinit();
try chunk.meshing.init();
defer chunk.meshing.deinit();
try renderer.init();
defer renderer.deinit();
network.init();
try renderer.RenderStructure.init();
defer renderer.RenderStructure.deinit();
try entity.ClientEntityManager.init();
defer entity.ClientEntityManager.deinit();
var manager = try network.ConnectionManager.init(12347, true);
defer manager.deinit();
try game.world.?.init(settings.lastUsedIPAddress, manager);
defer game.world.?.deinit();
Window.setMouseGrabbed(true);
try blocks.meshes.generateTextureArray();
c.glCullFace(c.GL_BACK);
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();
var buffer = try graphics.TextBuffer.init(threadAllocator, "Time to wrap some lines! a⃗ a⃗⃗ _a#ff0000⃗#ffff00⃗#00ff00⃗#00ffff⃗_#0000ff⃗#ff00ff⃗#000000 ⌬ __*italic*__ _**bold**_ ___***everything***___ #ff0000red#00ff00green#0000ffblue", .{}, true);
defer buffer.deinit();
var buffer2 = try graphics.TextBuffer.init(threadAllocator, "Time to wrap some lines! a⃗ a⃗⃗ _a#ff0000⃗#ffff00⃗#00ff00⃗#00ffff⃗_#0000ff⃗#ff00ff⃗#000000 ⌬ __*italic*__ _**bold**_ ___***everything***___ #ff0000red#00ff00green#0000ffblue", .{}, false);
defer buffer2.deinit();
var buffer3 = try graphics.TextBuffer.init(threadAllocator, "😀 😃 😄 😁 😆 😅 🤣 😂 🙂 🙃 🫠 😉 😊 😇 🥰 😍 🤩 😘 😗 ☺ 😚 😙 🥲 😋 😛 😜 🤪 😝 🤑 🤗 🤭 🫢 🫣 🤫 🤔 🫡 🤐 🤨 😐 😑 😶 🫥 😏 😒 🙄 😬 🤥 🫨 😌 😔 😪 🤤 😴 😷 🤒 🤕 🤢 🤮 🤧 🥵 🥶 🥴 😵 🤯 🤠 🥳 🥸 😎 🤓 🧐 😕 🫤 😟 🙁 😮 😯 😲 😳 🥺 🥹 😦 😧 😨 😰 😥 😢 😭 😱 😖 😣 😞 😓 😩 😫 🥱", .{}, false);
defer buffer3.deinit();
while(c.glfwWindowShouldClose(Window.window) == 0) {
{ // Check opengl errors:
const err = c.glGetError();
if(err != 0) {
std.log.err("Got opengl error: {}", .{err});
}
}
c.glfwSwapBuffers(Window.window);
c.glfwPollEvents();
var newTime = std.time.milliTimestamp();
var deltaTime = @intToFloat(f64, newTime -% lastTime)/1000.0;
lastTime = newTime;
try game.update(deltaTime);
{ // Render the game
c.glEnable(c.GL_CULL_FACE);
c.glEnable(c.GL_DEPTH_TEST);
try renderer.render(game.Player.getPosBlocking());
}
{ // Render the GUI
c.glDisable(c.GL_CULL_FACE);
c.glDisable(c.GL_DEPTH_TEST);
//const dim = try buffer2.calculateLineBreaks(32, 200);
//try buffer.render(100, 200, 32);
//graphics.Draw.setColor(0xff008000);
//graphics.Draw.rect(.{100, 400}, .{200, dim[1]});
//try buffer2.render(100, 400, 32);
//_ = try buffer3.calculateLineBreaks(32, 600);
//try buffer3.render(400, 400, 32);
//graphics.Draw.setColor(0xff0000ff);
//graphics.Draw.rect(Vec2f{.x = 100, .y = 100}, Vec2f{.x = 200, .y = 100});
//graphics.Draw.circle(Vec2f{.x = 200, .y = 200}, 59);
//graphics.Draw.setColor(0xffff00ff);
//graphics.Draw.line(Vec2f{.x = 0, .y = 0}, Vec2f{.x = 1920, .y = 1080});
}
}
}
test "abc" {
_ = @import("json.zig");
}