Backlog/extras/doomplayer/src/doom.zig

454 lines
12 KiB
Zig

allocator: std.mem.Allocator,
doomSampler: *gpu.GPUTexture = undefined,
gpuTransferBuffer: *gpu.GPUTransferBuffer = undefined,
texture: *gpu.GPUTexture = undefined,
width: u32,
height: u32,
inputEnabled: bool = false,
sensitivity: f32 = 1.0,
fn log(comptime fmt: []const u8, args: anytype) void {
core.engine_log("[Doomplayer] " ++ fmt, args);
}
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.width = 320,
.height = 200,
};
log("initialized", .{});
try self.setup();
self.sensitivity = core.configVar(f32, "doomplayer.sensitivity", 1.0);
gDoom = self;
return self;
}
pub fn setup(self: *@This()) !void {
try self.createTextures();
try backlog.platform.context().addSDLProcessFunction(processFunc);
c.doom_set_resolution(320, 200);
c.doom_set_default_int("key_up", c.DOOM_KEY_W);
c.doom_set_default_int("key_down", c.DOOM_KEY_S);
c.doom_set_default_int("key_strafeleft", c.DOOM_KEY_A);
c.doom_set_default_int("key_straferight", c.DOOM_KEY_D);
c.doom_set_default_int("key_use", c.DOOM_KEY_E);
c.doom_set_default_int("mouse_move", 0); // Mouse will not move forward
c.doom_init(1, @constCast(@ptrCast(@alignCast(&.{"doom"}))), 8);
}
fn sdlToDoomButton(button: c_uint) c_uint {
switch (button) {
sdl3_c.SDL_BUTTON_LEFT => {
return c.DOOM_LEFT_BUTTON;
},
sdl3_c.SDL_BUTTON_RIGHT => {
return c.DOOM_RIGHT_BUTTON;
},
sdl3_c.SDL_BUTTON_MIDDLE => {
return c.DOOM_MIDDLE_BUTTON;
},
else => {},
}
return 0;
}
fn sdlToDoomScancode(sdlScancode: c_uint) c_int {
switch (sdlScancode) {
sdl3_c.SDL_SCANCODE_TAB => {
return c.DOOM_KEY_TAB;
},
sdl3_c.SDL_SCANCODE_RETURN => {
return c.DOOM_KEY_ENTER;
},
sdl3_c.SDL_SCANCODE_ESCAPE => {
return c.DOOM_KEY_ESCAPE;
},
sdl3_c.SDL_SCANCODE_SPACE => {
return c.DOOM_KEY_SPACE;
},
sdl3_c.SDL_SCANCODE_APOSTROPHE => {
return c.DOOM_KEY_APOSTROPHE;
},
sdl3_c.SDL_SCANCODE_KP_MULTIPLY => {
return c.DOOM_KEY_MULTIPLY;
},
sdl3_c.SDL_SCANCODE_COMMA => {
return c.DOOM_KEY_COMMA;
},
sdl3_c.SDL_SCANCODE_MINUS => {
return c.DOOM_KEY_MINUS;
},
sdl3_c.SDL_SCANCODE_PERIOD => {
return c.DOOM_KEY_PERIOD;
},
sdl3_c.SDL_SCANCODE_SLASH => {
return c.DOOM_KEY_SLASH;
},
sdl3_c.SDL_SCANCODE_0 => {
return c.DOOM_KEY_0;
},
sdl3_c.SDL_SCANCODE_1 => {
return c.DOOM_KEY_1;
},
sdl3_c.SDL_SCANCODE_2 => {
return c.DOOM_KEY_2;
},
sdl3_c.SDL_SCANCODE_3 => {
return c.DOOM_KEY_3;
},
sdl3_c.SDL_SCANCODE_4 => {
return c.DOOM_KEY_4;
},
sdl3_c.SDL_SCANCODE_5 => {
return c.DOOM_KEY_5;
},
sdl3_c.SDL_SCANCODE_6 => {
return c.DOOM_KEY_6;
},
sdl3_c.SDL_SCANCODE_7 => {
return c.DOOM_KEY_7;
},
sdl3_c.SDL_SCANCODE_8 => {
return c.DOOM_KEY_8;
},
sdl3_c.SDL_SCANCODE_9 => {
return c.DOOM_KEY_9;
},
sdl3_c.SDL_SCANCODE_SEMICOLON => {
return c.DOOM_KEY_SEMICOLON;
},
sdl3_c.SDL_SCANCODE_EQUALS => {
return c.DOOM_KEY_EQUALS;
},
sdl3_c.SDL_SCANCODE_LEFTBRACKET => {
return c.DOOM_KEY_LEFT_BRACKET;
},
sdl3_c.SDL_SCANCODE_RIGHTBRACKET => {
return c.DOOM_KEY_RIGHT_BRACKET;
},
sdl3_c.SDL_SCANCODE_A => {
return c.DOOM_KEY_A;
},
sdl3_c.SDL_SCANCODE_B => {
return c.DOOM_KEY_B;
},
sdl3_c.SDL_SCANCODE_C => {
return c.DOOM_KEY_C;
},
sdl3_c.SDL_SCANCODE_D => {
return c.DOOM_KEY_D;
},
sdl3_c.SDL_SCANCODE_E => {
return c.DOOM_KEY_E;
},
sdl3_c.SDL_SCANCODE_F => {
return c.DOOM_KEY_F;
},
sdl3_c.SDL_SCANCODE_G => {
return c.DOOM_KEY_G;
},
sdl3_c.SDL_SCANCODE_H => {
return c.DOOM_KEY_H;
},
sdl3_c.SDL_SCANCODE_I => {
return c.DOOM_KEY_I;
},
sdl3_c.SDL_SCANCODE_J => {
return c.DOOM_KEY_J;
},
sdl3_c.SDL_SCANCODE_K => {
return c.DOOM_KEY_K;
},
sdl3_c.SDL_SCANCODE_L => {
return c.DOOM_KEY_L;
},
sdl3_c.SDL_SCANCODE_M => {
return c.DOOM_KEY_M;
},
sdl3_c.SDL_SCANCODE_N => {
return c.DOOM_KEY_N;
},
sdl3_c.SDL_SCANCODE_O => {
return c.DOOM_KEY_O;
},
sdl3_c.SDL_SCANCODE_P => {
return c.DOOM_KEY_P;
},
sdl3_c.SDL_SCANCODE_Q => {
return c.DOOM_KEY_Q;
},
sdl3_c.SDL_SCANCODE_R => {
return c.DOOM_KEY_R;
},
sdl3_c.SDL_SCANCODE_S => {
return c.DOOM_KEY_S;
},
sdl3_c.SDL_SCANCODE_T => {
return c.DOOM_KEY_T;
},
sdl3_c.SDL_SCANCODE_U => {
return c.DOOM_KEY_U;
},
sdl3_c.SDL_SCANCODE_V => {
return c.DOOM_KEY_V;
},
sdl3_c.SDL_SCANCODE_W => {
return c.DOOM_KEY_W;
},
sdl3_c.SDL_SCANCODE_X => {
return c.DOOM_KEY_X;
},
sdl3_c.SDL_SCANCODE_Y => {
return c.DOOM_KEY_Y;
},
sdl3_c.SDL_SCANCODE_Z => {
return c.DOOM_KEY_Z;
},
sdl3_c.SDL_SCANCODE_BACKSPACE => {
return c.DOOM_KEY_BACKSPACE;
},
sdl3_c.SDL_SCANCODE_LCTRL => {},
sdl3_c.SDL_SCANCODE_RCTRL => {
return c.DOOM_KEY_CTRL;
},
sdl3_c.SDL_SCANCODE_LEFT => {
return c.DOOM_KEY_LEFT_ARROW;
},
sdl3_c.SDL_SCANCODE_UP => {
return c.DOOM_KEY_UP_ARROW;
},
sdl3_c.SDL_SCANCODE_RIGHT => {
return c.DOOM_KEY_RIGHT_ARROW;
},
sdl3_c.SDL_SCANCODE_DOWN => {
return c.DOOM_KEY_DOWN_ARROW;
},
sdl3_c.SDL_SCANCODE_LSHIFT, sdl3_c.SDL_SCANCODE_RSHIFT => {
return c.DOOM_KEY_SHIFT;
},
sdl3_c.SDL_SCANCODE_LALT => {},
sdl3_c.SDL_SCANCODE_RALT => {
return c.DOOM_KEY_ALT;
},
sdl3_c.SDL_SCANCODE_F1 => {
return c.DOOM_KEY_F1;
},
sdl3_c.SDL_SCANCODE_F2 => {
return c.DOOM_KEY_F2;
},
sdl3_c.SDL_SCANCODE_F3 => {
return c.DOOM_KEY_F3;
},
sdl3_c.SDL_SCANCODE_F4 => {
return c.DOOM_KEY_F4;
},
sdl3_c.SDL_SCANCODE_F5 => {
return c.DOOM_KEY_F5;
},
sdl3_c.SDL_SCANCODE_F6 => {
return c.DOOM_KEY_F6;
},
sdl3_c.SDL_SCANCODE_F7 => {
return c.DOOM_KEY_F7;
},
sdl3_c.SDL_SCANCODE_F8 => {
return c.DOOM_KEY_F8;
},
sdl3_c.SDL_SCANCODE_F9 => {
return c.DOOM_KEY_F9;
},
sdl3_c.SDL_SCANCODE_F10 => {
return c.DOOM_KEY_F10;
},
sdl3_c.SDL_SCANCODE_F11 => {
return c.DOOM_KEY_F11;
},
sdl3_c.SDL_SCANCODE_F12 => {
return c.DOOM_KEY_F12;
},
sdl3_c.SDL_SCANCODE_PAUSE => {
return c.DOOM_KEY_PAUSE;
},
else => {
return c.DOOM_KEY_UNKNOWN;
},
}
return c.DOOM_KEY_UNKNOWN;
}
var gDoom: *@This() = undefined;
pub fn processFunc(e: *sdl3.Event) void {
if (!gDoom.inputEnabled) {
return;
}
switch (e.type) {
else => {},
sdl3_c.SDL_EVENT_MOUSE_MOTION => {
c.doom_mouse_move(@intFromFloat(e.motion.xrel * 4 * gDoom.sensitivity), @intFromFloat(e.motion.yrel * 4 * gDoom.sensitivity));
},
sdl3_c.SDL_EVENT_MOUSE_BUTTON_DOWN => {
c.doom_button_down(sdlToDoomButton(e.button.button));
},
sdl3_c.SDL_EVENT_MOUSE_BUTTON_UP => {
c.doom_button_up(sdlToDoomButton(e.button.button));
},
sdl3_c.SDL_EVENT_KEY_DOWN => {
if (!e.key.repeat) {
c.doom_key_down(sdlToDoomScancode(e.key.scancode));
}
},
sdl3_c.SDL_EVENT_KEY_UP => {
if (!e.key.repeat) {
c.doom_key_up(sdlToDoomScancode(e.key.scancode));
}
},
}
}
fn getFrameLength(self: @This()) c_int {
return self.width * self.height;
}
fn acquireVideoFrameBuffer(self: *@This()) []u8 {
var slice: []u8 = undefined;
slice.ptr = rend.context().device.mapGPUTransferBuffer(self.gpuTransferBuffer, false);
slice.len = @intCast(self.width * self.width * 4);
return slice;
}
fn releaseAndUploadFrameBuffer(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
const copyPass = cmd.beginGPUCopyPass();
copyPass.uploadToGPUTexture(
&.{
.transfer_buffer = self.gpuTransferBuffer,
.offset = 0,
.pixels_per_row = self.width,
.rows_per_layer = self.height,
},
&std.mem.zeroInit(gpu.GPUTextureRegion, .{
.texture = self.texture,
.w = self.width,
.h = self.height,
.d = 1,
}),
true,
);
copyPass.endGPUCopyPass();
}
fn createTextureInner(width: u32, height: u32) !*gpu.GPUTexture {
const ctx = rend.context();
const gpuTexture = ctx.device.createGPUTexture(&std.mem.zeroInit(gpu.GPUTextureCreateInfo, .{
.type = .texturetype2d,
.format = .textureformatR8g8b8a8Unorm,
.usage = .{ .textureusageSampler = true },
.width = @as(u32, @intCast(width)),
.height = @as(u32, @intCast(height)),
.layer_count_or_depth = 1,
.num_levels = 1,
}));
return gpuTexture;
}
pub fn createTextures(self: *@This()) !void {
self.texture = try createTextureInner(self.width, self.height);
// create transfer Buffer
self.gpuTransferBuffer = rend.context().device.createGPUTransferBuffer(&.{
.usage = .transferbufferusageUpload,
.size = @as(u32, @intCast(self.width * self.height * 4)),
.props = 0,
});
}
pub fn tick(self: *@This(), dt: f64) void {
_ = dt;
c.doom_update();
const frame = self.acquireVideoFrameBuffer();
const doomFb = c.doom_get_framebuffer(4);
var i: usize = 0;
while (i < self.width * self.height * 4) : (i += 1) {
frame[i] = doomFb[i];
}
rend.context().device.unmapGPUTransferBuffer(self.gpuTransferBuffer);
const cmd = rend.context().device.acquireGPUCommandBuffer();
self.releaseAndUploadFrameBuffer(cmd);
_ = cmd.submitGPUCommandBuffer();
if (self.inputEnabled) {
if (ig.begin("doom settings", null, .{})) {
_ = ig.sliderFloat("sensitivity", &self.sensitivity, 0.1, 20, null, .{});
ig.end();
}
}
}
fn bindVideoTextures(p: ?*anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
const ctx = rend.context();
const renderpass: *gpu.GPURenderPass = ctx.state.pass.?;
renderpass.bindGPUFragmentSamplers(0, &.{ .sampler = ctx.blockySampler, .texture = self.texture }, 1);
}
// creates a video player entity
pub fn createEntity(self: *@This()) !core.Entity {
const entity = try core.createEntity();
const scene = entity.addComponent(core.Scene).?;
const size = 5.0;
const h: f32 = @floatFromInt(self.height);
const w: f32 = @floatFromInt(self.width);
scene.setScale(size, size, size * (h / w));
scene.setRotation(core.Rotation.eulerX(core.radians(90.0)));
scene.setPosition(.{ .z = 30 });
if (entity.addComponent(rend.MeshComponent)) |mesh| {
mesh.cmrf = bindVideoTextures;
mesh.cmrf_ctx = self;
mesh.textureMode.fullbright = true;
mesh.textureMode.noSrgb = true;
mesh.setMesh("m_plane");
}
return entity;
}
pub fn destroy(self: *@This()) void {
core.engine_logs("[Videoplayer] destroying player");
self.allocator.destroy(self);
}
const std = @import("std");
const backlog = @import("Backlog");
const core = backlog.core;
const rend = backlog.rend;
const sdl3 = backlog.platform.windowing.sdl3;
const sdl3_c = sdl3.c;
const c = @import("c.zig").c;
const gpu = rend.renderer.gpu;
const ig = backlog.imgui.api;