diff --git a/src/itemdrop.zig b/src/itemdrop.zig index cae696a4..67e8a154 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -519,6 +519,29 @@ pub const ClientItemDropManager = struct { // MARK: ClientItemDropManager } }; +// Going to handle item animations and other things like - bobbing, interpolation, movement reactions +pub const ItemDisplayManager = struct { // MARK: ItemDisplayManager + pub var showItem: bool = true; + var cameraFollow: Vec3f = @splat(0); + var cameraFollowVel: Vec3f = @splat(0); + const damping: Vec3f = @splat(130); + + pub fn update(deltaTime: f64) void { + if(deltaTime == 0) return; + const dt: f32 = @floatCast(deltaTime); + + var playerVel: Vec3f = .{@floatCast((game.Player.super.vel[2]*0.009 + game.Player.eyeVel[2]*0.0075)), 0, 0}; + playerVel = vec.clampMag(playerVel, 0.32); + + // TODO: add *smooth* item sway + const n1: Vec3f = cameraFollowVel - (cameraFollow - playerVel)*damping*damping*@as(Vec3f, @splat(dt)); + const n2: Vec3f = @as(Vec3f, @splat(1)) + damping*@as(Vec3f, @splat(dt)); + cameraFollowVel = n1/(n2*n2); + + cameraFollow += cameraFollowVel*@as(Vec3f, @splat(dt)); + } +}; + pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer var itemShader: graphics.Shader = undefined; var itemUniforms: struct { @@ -539,7 +562,6 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer } = undefined; var itemModelSSBO: graphics.SSBO = undefined; - var modelData: main.List(u32) = undefined; var freeSlots: main.List(*ItemVoxelModel) = undefined; @@ -659,8 +681,7 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer return voxelModels.findOrCreate(compareObject, ItemVoxelModel.init, null); } - pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) void { - game.world.?.itemDrops.updateInterpolationData(); + fn bindCommonUniforms(projMatrix: Mat4f, viewMatrix: Mat4f, ambientLight: Vec3f, time: u32) void { itemShader.bind(); c.glUniform1i(itemUniforms.texture_sampler, 0); c.glUniform1i(itemUniforms.emissionSampler, 1); @@ -670,8 +691,32 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer c.glUniform1i(itemUniforms.time, @as(u31, @truncate(time))); c.glUniformMatrix4fv(itemUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix)); c.glUniform3fv(itemUniforms.ambientLight, 1, @ptrCast(&ambientLight)); - c.glUniformMatrix4fv(itemUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix)); + c.glUniformMatrix4fv(itemUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix)); c.glUniform1f(itemUniforms.contrast, 0.12); + } + + fn bindLightUniform(light: [6]u8, ambientLight: Vec3f) void { + c.glUniform3fv(itemUniforms.ambientLight, 1, @ptrCast(&@max( + ambientLight*@as(Vec3f, @as(Vec3f, @floatFromInt(Vec3i{light[0], light[1], light[2]}))/@as(Vec3f, @splat(255))), + @as(Vec3f, @floatFromInt(Vec3i{light[3], light[4], light[5]}))/@as(Vec3f, @splat(255)), + ))); + } + + fn bindModelUniforms(modelIndex: u31, blockType: u16) void { + c.glUniform1i(itemUniforms.modelIndex, modelIndex); + c.glUniform1i(itemUniforms.block, blockType); + } + + fn drawItem(vertices: u31, modelMatrix: Mat4f) void { + c.glUniformMatrix4fv(itemUniforms.modelMatrix, 1, c.GL_TRUE, @ptrCast(&modelMatrix)); + c.glBindVertexArray(main.renderer.chunk_meshing.vao); + c.glDrawElements(c.GL_TRIANGLES, vertices, c.GL_UNSIGNED_INT, null); + } + + pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) void { + game.world.?.itemDrops.updateInterpolationData(); + + bindCommonUniforms(projMatrix, game.camera.viewMatrix, ambientLight, time); const itemDrops = &game.world.?.itemDrops.super; for(itemDrops.indices[0..itemDrops.size]) |i| { if(itemDrops.list.items(.itemStack)[i].item) |item| { @@ -679,25 +724,21 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer const rot = itemDrops.list.items(.rot)[i]; const blockPos: Vec3i = @intFromFloat(@floor(pos)); const light: [6]u8 = main.renderer.mesh_storage.getLight(blockPos[0], blockPos[1], blockPos[2]) orelse @splat(0); - c.glUniform3fv(itemUniforms.ambientLight, 1, @ptrCast(&@max( - ambientLight*@as(Vec3f, @as(Vec3f, @floatFromInt(Vec3i{light[0], light[1], light[2]}))/@as(Vec3f, @splat(255))), - @as(Vec3f, @floatFromInt(Vec3i{light[3], light[4], light[5]}))/@as(Vec3f, @splat(255)), - ))); + bindLightUniform(light, ambientLight); pos -= playerPos; const model = getModel(item); - c.glUniform1i(itemUniforms.modelIndex, model.index); var vertices: u31 = 36; var scale: f32 = 0.3; + var blockType: u16 = 0; if(item == .baseItem and item.baseItem.block != null and item.baseItem.image.imageData.ptr == graphics.Image.defaultImage.imageData.ptr) { - const blockType = item.baseItem.block.?; - c.glUniform1i(itemUniforms.block, blockType); + blockType = item.baseItem.block.?; vertices = model.len/2*6; } else { - c.glUniform1i(itemUniforms.block, 0); scale = 0.5; } + bindModelUniforms(model.index, blockType); var modelMatrix = Mat4f.translation(@floatCast(pos)); modelMatrix = modelMatrix.mul(Mat4f.rotationX(-rot[0])); @@ -705,11 +746,108 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-rot[2])); modelMatrix = modelMatrix.mul(Mat4f.scale(@splat(scale))); modelMatrix = modelMatrix.mul(Mat4f.translation(@splat(-0.5))); - c.glUniformMatrix4fv(itemUniforms.modelMatrix, 1, c.GL_TRUE, @ptrCast(&modelMatrix)); - - c.glBindVertexArray(main.renderer.chunk_meshing.vao); - c.glDrawElements(c.GL_TRIANGLES, vertices, c.GL_UNSIGNED_INT, null); + drawItem(vertices, modelMatrix); } } } + + inline fn getIndex(x: u8, y: u8, z: u8) u32 { + return (z*4) + (y*2) + (x); + } + + inline fn blendColors(a: [6]f32, b: [6]f32, t: f32) [6]f32 { + var result: [6]f32 = .{0, 0, 0, 0, 0, 0}; + inline for(0..6) |i| { + result[i] = std.math.lerp(a[i], b[i], t); + } + return result; + } + + pub fn renderDisplayItems(ambientLight: Vec3f, playerPos: Vec3d, time: u32) void { + if(!ItemDisplayManager.showItem) return; + + const projMatrix: Mat4f = Mat4f.perspective(std.math.degreesToRadians(65), @as(f32, @floatFromInt(main.renderer.lastWidth))/@as(f32, @floatFromInt(main.renderer.lastHeight)), 0.01, 3); + const viewMatrix = Mat4f.identity(); + bindCommonUniforms(projMatrix, viewMatrix, ambientLight, time); + + const selectedItem = game.Player.inventory.getItem(game.Player.selectedSlot); + if(selectedItem) |item| { + var pos: Vec3d = Vec3d{0, 0, 0}; + const rot: Vec3f = ItemDisplayManager.cameraFollow; + + const lightPos = @as(Vec3f, @floatCast(playerPos)) - @as(Vec3f, @splat(0.5)); + const blockPos: Vec3i = @intFromFloat(@floor(lightPos)); + const localBlockPos = lightPos - @as(Vec3f, @floatFromInt(blockPos)); + + var samples: [8][6]f32 = @splat(@splat(0)); + inline for(0..2) |z| { + inline for(0..2) |y| { + inline for(0..2) |x| { + const light: [6]u8 = main.renderer.mesh_storage.getLight( + blockPos[0] +% @as(i32, @intCast(x)), + blockPos[1] +% @as(i32, @intCast(y)), + blockPos[2] +% @as(i32, @intCast(z)), + ) orelse @splat(0); + + inline for(0..6) |i| { + samples[getIndex(x, y, z)][i] = @as(f32, @floatFromInt(light[i])); + } + } + } + } + + inline for(0..2) |y| { + inline for(0..2) |x| { + samples[getIndex(x, y, 0)] = blendColors(samples[getIndex(x, y, 0)], samples[getIndex(x, y, 1)], localBlockPos[2]); + } + } + + inline for(0..2) |x| { + samples[getIndex(x, 0, 0)] = blendColors(samples[getIndex(x, 0, 0)], samples[getIndex(x, 1, 0)], localBlockPos[1]); + } + + var result: [6]u8 = .{0, 0, 0, 0, 0, 0}; + inline for(0..6) |i| { + const val = std.math.lerp(samples[getIndex(0, 0, 0)][i], samples[getIndex(1, 0, 0)][i], localBlockPos[0]); + result[i] = @as(u8, @intFromFloat(@floor(val))); + } + + bindLightUniform(result, ambientLight); + + const model = getModel(item); + var vertices: u31 = 36; + + const isBlock: bool = item == .baseItem and item.baseItem.block != null and item.baseItem.image.imageData.ptr == graphics.Image.defaultImage.imageData.ptr; + var scale: f32 = 0; + var blockType: u16 = 0; + if(isBlock) { + blockType = item.baseItem.block.?; + vertices = model.len/2*6; + scale = 0.3; + pos = Vec3d{0.4, 0.55, -0.32}; + } else { + scale = 0.57; + pos = Vec3d{0.4, 0.65, -0.3}; + } + bindModelUniforms(model.index, blockType); + + var modelMatrix = Mat4f.rotationZ(-rot[2]); + modelMatrix = modelMatrix.mul(Mat4f.rotationY(-rot[1])); + modelMatrix = modelMatrix.mul(Mat4f.rotationX(-rot[0])); + modelMatrix = modelMatrix.mul(Mat4f.translation(@floatCast(pos))); + if(!isBlock) { + if(item == .tool) { + modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-std.math.pi*0.47)); + modelMatrix = modelMatrix.mul(Mat4f.rotationY(std.math.pi*0.25)); + } else { + modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-std.math.pi*0.45)); + } + } else { + modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-std.math.pi*0.2)); + } + modelMatrix = modelMatrix.mul(Mat4f.scale(@splat(scale))); + modelMatrix = modelMatrix.mul(Mat4f.translation(@splat(-0.5))); + drawItem(vertices, modelMatrix); + } + } }; diff --git a/src/main.zig b/src/main.zig index 2929fe9c..61a3804b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -307,11 +307,17 @@ fn openCommand() void { } fn takeBackgroundImageFn() void { if(game.world == null) return; + const showItem = itemdrop.ItemDisplayManager.showItem; + itemdrop.ItemDisplayManager.showItem = false; renderer.MenuBackGround.takeBackgroundImage(); + itemdrop.ItemDisplayManager.showItem = showItem; } fn toggleHideGui() void { gui.hideGui = !gui.hideGui; } +fn toggleHideDisplayItem() void { + itemdrop.ItemDisplayManager.showItem = !itemdrop.ItemDisplayManager.showItem; +} fn toggleDebugOverlay() void { gui.toggleWindow("debug"); } @@ -416,6 +422,7 @@ pub const KeyBoard = struct { // MARK: KeyBoard .{.name = "cameraDown", .gamepadAxis = .{.axis = c.GLFW_GAMEPAD_AXIS_RIGHT_Y, .positive = true}}, // debug: .{.name = "hideMenu", .key = c.GLFW_KEY_F1, .pressAction = &toggleHideGui}, + .{.name = "hideDisplayItem", .key = c.GLFW_KEY_F2, .pressAction = &toggleHideDisplayItem}, .{.name = "debugOverlay", .key = c.GLFW_KEY_F3, .pressAction = &toggleDebugOverlay}, .{.name = "performanceOverlay", .key = c.GLFW_KEY_F4, .pressAction = &togglePerformanceOverlay}, .{.name = "gpuPerformanceOverlay", .key = c.GLFW_KEY_F5, .pressAction = &toggleGPUPerformanceOverlay}, @@ -693,7 +700,7 @@ pub fn main() void { // MARK: main() if(!isHidden) { c.glEnable(c.GL_CULL_FACE); c.glEnable(c.GL_DEPTH_TEST); - renderer.render(game.Player.getEyePosBlocking()); + renderer.render(game.Player.getEyePosBlocking(), deltaTime); // Render the GUI gui.windowlist.gpu_performance_measuring.startQuery(.gui); c.glDisable(c.GL_CULL_FACE); diff --git a/src/renderer.zig b/src/renderer.zig index b73982bd..f16ce7b5 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -114,8 +114,8 @@ fn initReflectionCubeMap() void { var worldFrameBuffer: graphics.FrameBuffer = undefined; -var lastWidth: u31 = 0; -var lastHeight: u31 = 0; +pub var lastWidth: u31 = 0; +pub var lastHeight: u31 = 0; var lastFov: f32 = 0; pub fn updateViewport(width: u31, height: u31, fov: f32) void { lastWidth = @intFromFloat(@as(f32, @floatFromInt(width))*main.settings.resolutionScale); @@ -126,7 +126,7 @@ pub fn updateViewport(width: u31, height: u31, fov: f32) void { worldFrameBuffer.unbind(); } -pub fn render(playerPosition: Vec3d) void { +pub fn render(playerPosition: Vec3d, deltaTime: f64) void { // TODO: player bobbing if(game.world) |world| { // TODO: Handle colors and sun position in the world. @@ -137,6 +137,7 @@ pub fn render(playerPosition: Vec3d) void { const skyColor = vec.xyz(world.clearColor); game.fog.skyColor = skyColor; + itemdrop.ItemDisplayManager.update(deltaTime); renderWorld(world, ambient, skyColor, playerPosition); const startTime = std.time.milliTimestamp(); mesh_storage.updateMeshes(startTime + maximumMeshTime); @@ -250,6 +251,11 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo c.glDepthMask(c.GL_TRUE); c.glDepthFunc(c.GL_LESS); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); + + c.glDepthRange(0, 0.001); + itemdrop.ItemDropRenderer.renderDisplayItems(ambientLight, playerPos, time); + c.glDepthRange(0.001, 1); + chunk_meshing.endRender(); worldFrameBuffer.bindTexture(c.GL_TEXTURE3); @@ -579,7 +585,7 @@ pub const MenuBackGround = struct { // Draw to frame buffer. buffer.bind(); c.glClear(c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT | c.GL_COLOR_BUFFER_BIT); - main.renderer.render(game.Player.getEyePosBlocking()); + main.renderer.render(game.Player.getEyePosBlocking(), 0); // Copy the pixels directly from OpenGL buffer.bind(); c.glReadPixels(0, 0, size, size, c.GL_RGBA, c.GL_UNSIGNED_BYTE, pixels.ptr); diff --git a/src/vec.zig b/src/vec.zig index 460d2975..a4390879 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -38,6 +38,14 @@ pub fn normalize(self: anytype) @TypeOf(self) { return self/@as(@TypeOf(self), @splat(length(self))); } +pub fn clampMag(self: anytype, maxMag: @typeInfo(@TypeOf(self)).vector.child) @TypeOf(self) { + if(lengthSquare(self) > maxMag*maxMag) { + return normalize(self)*@as(@TypeOf(self), @splat(maxMag)); + } + + return self; +} + pub fn cross(self: anytype, other: @TypeOf(self)) @TypeOf(self) { if(@typeInfo(@TypeOf(self)).vector.len != 3) @compileError("Only available for vectors of length 3."); return @TypeOf(self){