mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
Add player item display (#1109)
* extract item model storage from itemdrop renderer to its own struct * render player held item * render display item on top of everything * add custom projection matrix that ignores fov settings * fixed item display position * change indentation from spaces to tabs * improved item display, added interpolation and hiding * fix formatting * cleanup * cleanup, made item movement * localize item display projection matrix * fix formatting * add lighting interpolation, remove item sway * fix formatting * switch to trilinear interpolation * fix light interpolation * fix formatting * small tweaks * small tweaks, move item hiding to f2 button * some rendering tweaks * fix item clipping * resolve conflict
This commit is contained in:
parent
4a8c0a6327
commit
f8210eb7b4
170
src/itemdrop.zig
170
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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){
|
||||
|
Loading…
x
Reference in New Issue
Block a user