From 8a4081cf9c2227d5e6f3a0eda1776a46f72ef191 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:18:40 +0800 Subject: [PATCH] feat: player rendering (#27) * refactor(player): extract player rendering into PlayerRenderer class Move player rendering code from Renderer to new PlayerRenderer class. Add normal and tangent vertex attributes for improved per-pixel lighting. * feat(player-renderer): add shadow rendering and include local player data * feat(gameplay): add player yaw/pitch synchronization and rendering * refactor(player_renderer): split rendering into per-body-part VAOs and VBOs Move `PlayerVertex` struct to header and replace single VAO/VBO with arrays of 6 parts. Implement head pitch rotation in `render()` and `shadow_render()` by applying a separate model matrix for the head (index 0). Remove old `vao()` and `sum()` accessors. Adjust model translation for consistency. * feat(player): add texture mapping to player model using skin * fix(player-renderer): correct player rendering rotation and texture coordinates * feat(gameplay): add player gait system with limb animation * fix(player): update leg UVs and remove yaw wrap Update left and right leg UV coordinates to align with updated player texture. Remove yaw modulo operation to allow unrestricted yaw rotation beyond 360 degrees. * feat(camera): add third-person perspective and walk animation - Introduce Perspective enum (FIRST_PERSON, THIRD_PERSON_BACK, THIRD_PERSON_FRONT) and m_front member for camera direction. - Handle F5 key to cycle perspectives. - Add angle and walk_time to ClientPlayer for walking animation. - Update player rendering to skip local player only in first person and fix rotation sign. - Remove redundant player info reporting; update player data with animation angle. * fix(camera): replace magic number with constant and fix missing break * fix(client-player): stop sprinting on forward key release * feat: add camera collision and sphere collision for world * fix(skin): update player001 texture * refactor(gameplay): improve thread safety and remove debug logging * refactor(shaders): extract shadow and normal logic into include files and add lighting to player shaders * chore(shader): remove unused uniforms cameraPos and specularStrength * fix: inlcude header --- assets/shaders/block_f_shader.glsl | 250 +----------- .../shaders/depth_player_fragment_shader.glsl | 7 + assets/shaders/depth_player_shader.glsl | 15 + assets/shaders/normal.glsl | 10 + assets/shaders/player_f_shader.glsl | 45 ++- assets/shaders/player_v_shader.glsl | 14 +- assets/shaders/shadow.glsl | 238 +++++++++++ assets/texture/skin/player001.png | Bin 0 -> 535 bytes include/Cubed/camera.hpp | 12 + include/Cubed/gameplay/client_player.hpp | 34 +- include/Cubed/gameplay/client_world.hpp | 37 +- include/Cubed/gameplay/packet.hpp | 6 +- include/Cubed/gameplay/player.hpp | 21 + include/Cubed/gameplay/server_player.hpp | 14 +- include/Cubed/gameplay/server_world.hpp | 5 +- include/Cubed/player_renderer.hpp | 32 ++ include/Cubed/renderer.hpp | 12 +- include/Cubed/texture_manager.hpp | 4 + src/CMakeLists.txt | 1 + src/app.cpp | 5 + src/camera.cpp | 69 +++- src/dev_panel.cpp | 10 +- src/gameplay/client_player.cpp | 78 ++-- src/gameplay/client_world.cpp | 126 +++++- src/gameplay/server_player.cpp | 6 + src/gameplay/server_world.cpp | 29 +- src/gameplay/session.cpp | 8 +- src/player_renderer.cpp | 375 ++++++++++++++++++ src/proto/player/player.proto | 8 +- src/renderer.cpp | 64 +-- src/texture_manager.cpp | 30 ++ 31 files changed, 1194 insertions(+), 371 deletions(-) create mode 100644 assets/shaders/depth_player_fragment_shader.glsl create mode 100644 assets/shaders/depth_player_shader.glsl create mode 100644 assets/shaders/normal.glsl create mode 100644 assets/shaders/shadow.glsl create mode 100644 assets/texture/skin/player001.png create mode 100644 include/Cubed/gameplay/player.hpp create mode 100644 include/Cubed/player_renderer.hpp create mode 100644 src/player_renderer.cpp diff --git a/assets/shaders/block_f_shader.glsl b/assets/shaders/block_f_shader.glsl index b3d28d8..488de41 100644 --- a/assets/shaders/block_f_shader.glsl +++ b/assets/shaders/block_f_shader.glsl @@ -29,254 +29,8 @@ uniform bool flipY; uniform int renderDistance; uniform vec3 skyColor; -const vec2 poissonDisk32[32] = vec2[]( - vec2(-0.975402, -0.071138), - vec2(-0.920347, -0.411420), - vec2(-0.883908, 0.217872), - vec2(-0.815442, -0.879125), - vec2(-0.775043, 0.543896), - vec2(-0.698126, -0.227570), - vec2(-0.682433, 0.801894), - vec2(-0.563905, 0.021517), - vec2(-0.443233, -0.975116), - vec2(-0.412231, 0.361307), - vec2(-0.264969, -0.418930), - vec2(-0.241888, 0.997065), - vec2(-0.094184, -0.929389), - vec2(-0.019101, 0.680997), - vec2( 0.143832, -0.141008), - vec2( 0.199841, 0.786414), - - vec2( 0.344959, 0.293878), - vec2( 0.443233, -0.475115), - vec2( 0.537430, -0.473734), - vec2( 0.589349, 0.569135), - vec2( 0.674281, -0.178897), - vec2( 0.791975, 0.190902), - vec2( 0.815442, 0.879125), - vec2( 0.896420, -0.613392), - vec2( 0.945586, -0.768907), - vec2( 0.974844, 0.756484), - vec2(-0.814100, 0.914376), - vec2(-0.382775, 0.276768), - vec2(-0.915886, 0.457714), - vec2( 0.537800, 0.912200), - vec2(-0.620000, -0.650000), - vec2( 0.120000, -0.780000) -); - -const vec2 poissonDisk16[16] = vec2[]( - vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), - vec2(-0.09418410, -0.92938870), vec2(0.34495938, 0.29387760), - vec2(-0.91588581, 0.45771432), vec2(-0.81544232, -0.87912464), - vec2(-0.38277543, 0.27676845), vec2(0.97484398, 0.75648379), - vec2(0.44323325, -0.97511554), vec2(0.53742981, -0.47373420), - vec2(-0.26496911, -0.41893023), vec2(0.79197514, 0.19090188), - vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590), - vec2(0.19984126, 0.78641367), vec2(0.14383161, -0.14100790) -); -const vec2 poissonDisk8[8] = vec2[]( - vec2( 0.1440, 0.7659), vec2(-0.5761, 0.4479), - vec2(-0.3220, -0.6058), vec2( 0.5693, -0.4048), - vec2(-0.1276, 0.1657), vec2(-0.0649, -0.0165), - vec2( 0.2773, -0.0305), vec2(-0.1134, -0.2122) -); -uniform int samples; -float random(vec3 seed) { - return fract(sin(dot(seed, vec3(12.9898,78.233,45.5432))) * 43758.5453); -} - -float FindBlocker(vec2 uv, - float zReceiver, - vec2 texelSize, - float bias, - float lightSizeUV) -{ - float avgDepth = 0.0; - int blockers = 0; - - float searchRadius = lightSizeUV * 0.5; - - for(int i = 0; i < samples; i++) - { - vec2 offset; - if (samples == 32) { - offset = - poissonDisk32[i] - * searchRadius - * texelSize; - } else if (samples == 16) { - offset = - poissonDisk16[i] - * searchRadius - * texelSize; - } else if (samples == 8) { - offset = - poissonDisk8[i] - * searchRadius - * texelSize; - } else { - offset = - poissonDisk32[i] - * searchRadius - * texelSize; - } - float depth = - texture(shadowMap, uv + offset).r; - - if(depth < zReceiver - bias) - { - avgDepth += depth; - blockers++; - } - } - - if(blockers == 0) - return -1.0; - - return avgDepth / blockers; -} - -float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir) -{ - - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - - projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x < 0.0 || projCoords.x > 1.0 || - projCoords.y < 0.0 || projCoords.y > 1.0 || - projCoords.z < 0.0 || projCoords.z > 1.0) { - return 0.0; - } - float currentDepth = projCoords.z; - vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); - float shadow = 0.0; - - float bias = - clamp( - 0.001 * (1.0 - dot(norm, lightDir)), - 0.0003, - 0.003 - ); - - if (shadowMode == 0) { - vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0; - float angle = random(seed) * 6.2831853;; // 2*PI - float s = sin(angle), c = cos(angle); - mat2 rot = mat2(c, -s, s, c); - //float radius = 0.7; - float radius = mix(1.0, 4.0, currentDepth); - - for (int i = 0; i < samples; ++i) { - vec2 offset; - if (samples == 32) { - offset = rot * poissonDisk32[i] * radius * texelSize; - } else if (samples == 16) { - offset = rot * poissonDisk16[i] * radius * texelSize; - } else if (samples == 8) { - offset = rot * poissonDisk8[i] * radius * texelSize; - } else { - offset = rot * poissonDisk32[i] * radius * texelSize; - } - float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; - shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); - } - shadow /= float(samples); - } else if (shadowMode == 1) { - for (int x = -1; x <= 1; ++x) { - for (int y = -1; y <= 1; ++y) { - vec2 offset = vec2(x, y) * texelSize; - float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; - shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); - } - } - shadow /= 9.0; - } else if (shadowMode == 2) { - // pcf off - float pcfDepth = - texture(shadowMap, projCoords.xy).r; - - shadow = - currentDepth - bias > pcfDepth - ? 1.0 - : 0.0; - } else if (shadowMode == 3) { - float avgBlockerDepth = - FindBlocker( - projCoords.xy, - currentDepth, - texelSize, - bias, - lightSizeUV - ); - - if(avgBlockerDepth < 0.0) - { - return 0.0; - } - - vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0; - float angle = random(seed) * 6.2831853;; // 2*PI - float s = sin(angle), c = cos(angle); - mat2 rot = mat2(c, -s, s, c); - /* - float penumbraRatio = (currentDepth - avgBlockerDepth); - float radius = clamp( - penumbraRatio * lightSizeUV, - minRadius, - maxRadius - ); - */ - float radius = - mix( - minRadius, - maxRadius, - smoothstep( - 0.0, - 0.05, - currentDepth - avgBlockerDepth - ) - ); - for (int i = 0; i < samples; ++i) { - vec2 offset; - if (samples == 32) { - offset = rot * poissonDisk32[i] * radius * texelSize; - } else if (samples == 16) { - offset = rot * poissonDisk16[i] * radius * texelSize; - } else if (samples == 8) { - offset = rot * poissonDisk8[i] * radius * texelSize; - } else { - offset = rot * poissonDisk32[i] * radius * texelSize; - } - float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; - shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); - } - shadow /= float(samples); - - } else { - float pcfDepth = - texture(shadowMap, projCoords.xy).r; - - shadow = - currentDepth - bias > pcfDepth - ? 1.0 - : 0.0; - } - - - return shadow; -} - -vec3 calcNewNormal() { - mat3 TBN = mat3(normalize(tangent), normalize(bitangent), normalize(normal)); - vec3 retrievedNormal = texture(normMap, vec3(tc, tex_layer)).xyz; - retrievedNormal = retrievedNormal * 2.0 - 1.0; - if (flipY) { - retrievedNormal.y = -retrievedNormal.y; - } - vec3 newNormal = TBN * retrievedNormal; - return normalize(newNormal); -} +#include "shadow.glsl" +#include "normal.glsl" void main(void) { vec4 objectColor = texture(samp, vec3(tc, tex_layer)); diff --git a/assets/shaders/depth_player_fragment_shader.glsl b/assets/shaders/depth_player_fragment_shader.glsl new file mode 100644 index 0000000..91820f3 --- /dev/null +++ b/assets/shaders/depth_player_fragment_shader.glsl @@ -0,0 +1,7 @@ +#version 460 + +in vec2 tc; + +void main() { + +} \ No newline at end of file diff --git a/assets/shaders/depth_player_shader.glsl b/assets/shaders/depth_player_shader.glsl new file mode 100644 index 0000000..2de921a --- /dev/null +++ b/assets/shaders/depth_player_shader.glsl @@ -0,0 +1,15 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; + +uniform mat4 lightSpaceMatrix; +uniform mat4 modelMatrix; + +out vec2 tc; +flat out int tex_layer; + +void main() { + tc = texCoord; + gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/normal.glsl b/assets/shaders/normal.glsl new file mode 100644 index 0000000..f68726e --- /dev/null +++ b/assets/shaders/normal.glsl @@ -0,0 +1,10 @@ +vec3 calcNewNormal() { + mat3 TBN = mat3(normalize(tangent), normalize(bitangent), normalize(normal)); + vec3 retrievedNormal = texture(normMap, vec3(tc, tex_layer)).xyz; + retrievedNormal = retrievedNormal * 2.0 - 1.0; + if (flipY) { + retrievedNormal.y = -retrievedNormal.y; + } + vec3 newNormal = TBN * retrievedNormal; + return normalize(newNormal); +} \ No newline at end of file diff --git a/assets/shaders/player_f_shader.glsl b/assets/shaders/player_f_shader.glsl index 68c9da0..6266dbc 100644 --- a/assets/shaders/player_f_shader.glsl +++ b/assets/shaders/player_f_shader.glsl @@ -1,8 +1,47 @@ #version 460 -out vec4 color; in vec2 tc; +in vec3 normal; +in vec3 vert_pos; +in vec4 FragPosLightSpace; +out vec4 color; + +layout (binding = 0) uniform sampler2D shadowMap; +layout (binding = 1) uniform sampler2D samp; + +uniform float ambientStrength; +uniform vec3 sunlightColor; +uniform vec3 ambientColor; +uniform vec3 sunlightDir; +uniform bool shader_on; +uniform int shadowMode; +uniform float lightSizeUV; +uniform float minRadius; +uniform float maxRadius; + +#include "shadow.glsl" void main() { - color = vec4(0.0, 0.0, 1.0, 1.0); -} \ No newline at end of file + vec4 objectColor = texture(samp, tc); + + if (!shader_on) { + color = objectColor; + return; + } + + vec3 lightDir = normalize(-sunlightDir); + + vec3 norm = normalize(normal); + + vec3 ambient = ambientStrength * ambientColor; + + float diff = max(dot(norm, lightDir), 0.0); + + vec3 diffuse = diff * sunlightColor; + + float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); + //float shadow = 0.0; + //color = vec4(vec3(shadow),1); + //color = vec4(vec3(diff),1); + color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb, objectColor.a); +} diff --git a/assets/shaders/player_v_shader.glsl b/assets/shaders/player_v_shader.glsl index 2bf71d5..d42b89a 100644 --- a/assets/shaders/player_v_shader.glsl +++ b/assets/shaders/player_v_shader.glsl @@ -2,14 +2,26 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec2 texCoord; +layout (location = 2) in vec3 aNormal; +layout (location = 3) in vec3 aTangent; uniform mat4 mv_matrix; uniform mat4 proj_matrix; - +uniform mat4 norm_matrix; +uniform mat4 lightSpaceMatrix; +uniform mat4 modelMatrix; +out vec4 FragPosLightSpace; +out vec3 normal; out vec2 tc; +out vec3 vert_pos; + void main() { + vec4 worldPos = modelMatrix * vec4(pos, 1.0); + FragPosLightSpace = lightSpaceMatrix * worldPos; vec4 viewPos = mv_matrix * vec4(pos, 1.0); tc = texCoord; + vert_pos = pos; + normal = normalize(mat3(norm_matrix) * aNormal); gl_Position = proj_matrix * viewPos; } \ No newline at end of file diff --git a/assets/shaders/shadow.glsl b/assets/shaders/shadow.glsl new file mode 100644 index 0000000..92a7bed --- /dev/null +++ b/assets/shaders/shadow.glsl @@ -0,0 +1,238 @@ +const vec2 poissonDisk32[32] = vec2[]( + vec2(-0.975402, -0.071138), + vec2(-0.920347, -0.411420), + vec2(-0.883908, 0.217872), + vec2(-0.815442, -0.879125), + vec2(-0.775043, 0.543896), + vec2(-0.698126, -0.227570), + vec2(-0.682433, 0.801894), + vec2(-0.563905, 0.021517), + vec2(-0.443233, -0.975116), + vec2(-0.412231, 0.361307), + vec2(-0.264969, -0.418930), + vec2(-0.241888, 0.997065), + vec2(-0.094184, -0.929389), + vec2(-0.019101, 0.680997), + vec2( 0.143832, -0.141008), + vec2( 0.199841, 0.786414), + + vec2( 0.344959, 0.293878), + vec2( 0.443233, -0.475115), + vec2( 0.537430, -0.473734), + vec2( 0.589349, 0.569135), + vec2( 0.674281, -0.178897), + vec2( 0.791975, 0.190902), + vec2( 0.815442, 0.879125), + vec2( 0.896420, -0.613392), + vec2( 0.945586, -0.768907), + vec2( 0.974844, 0.756484), + vec2(-0.814100, 0.914376), + vec2(-0.382775, 0.276768), + vec2(-0.915886, 0.457714), + vec2( 0.537800, 0.912200), + vec2(-0.620000, -0.650000), + vec2( 0.120000, -0.780000) +); + +const vec2 poissonDisk16[16] = vec2[]( + vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), + vec2(-0.09418410, -0.92938870), vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), vec2(0.14383161, -0.14100790) +); +const vec2 poissonDisk8[8] = vec2[]( + vec2( 0.1440, 0.7659), vec2(-0.5761, 0.4479), + vec2(-0.3220, -0.6058), vec2( 0.5693, -0.4048), + vec2(-0.1276, 0.1657), vec2(-0.0649, -0.0165), + vec2( 0.2773, -0.0305), vec2(-0.1134, -0.2122) +); +uniform int samples; +float random(vec3 seed) { + return fract(sin(dot(seed, vec3(12.9898,78.233,45.5432))) * 43758.5453); +} + +float FindBlocker(vec2 uv, + float zReceiver, + vec2 texelSize, + float bias, + float lightSizeUV) +{ + float avgDepth = 0.0; + int blockers = 0; + + float searchRadius = lightSizeUV * 0.5; + + for(int i = 0; i < samples; i++) + { + vec2 offset; + if (samples == 32) { + offset = + poissonDisk32[i] + * searchRadius + * texelSize; + } else if (samples == 16) { + offset = + poissonDisk16[i] + * searchRadius + * texelSize; + } else if (samples == 8) { + offset = + poissonDisk8[i] + * searchRadius + * texelSize; + } else { + offset = + poissonDisk32[i] + * searchRadius + * texelSize; + } + float depth = + texture(shadowMap, uv + offset).r; + + if(depth < zReceiver - bias) + { + avgDepth += depth; + blockers++; + } + } + + if(blockers == 0) + return -1.0; + + return avgDepth / blockers; +} + +float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir) +{ + + vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; + + projCoords = projCoords * 0.5 + 0.5; + if (projCoords.x < 0.0 || projCoords.x > 1.0 || + projCoords.y < 0.0 || projCoords.y > 1.0 || + projCoords.z < 0.0 || projCoords.z > 1.0) { + return 0.0; + } + float currentDepth = projCoords.z; + vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0)); + float shadow = 0.0; + + float bias = + clamp( + 0.001 * (1.0 - dot(norm, lightDir)), + 0.0003, + 0.003 + ); + + if (shadowMode == 0) { + vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0; + float angle = random(seed) * 6.2831853;; // 2*PI + float s = sin(angle), c = cos(angle); + mat2 rot = mat2(c, -s, s, c); + //float radius = 0.7; + float radius = mix(1.0, 4.0, currentDepth); + + for (int i = 0; i < samples; ++i) { + vec2 offset; + if (samples == 32) { + offset = rot * poissonDisk32[i] * radius * texelSize; + } else if (samples == 16) { + offset = rot * poissonDisk16[i] * radius * texelSize; + } else if (samples == 8) { + offset = rot * poissonDisk8[i] * radius * texelSize; + } else { + offset = rot * poissonDisk32[i] * radius * texelSize; + } + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + shadow /= float(samples); + } else if (shadowMode == 1) { + for (int x = -1; x <= 1; ++x) { + for (int y = -1; y <= 1; ++y) { + vec2 offset = vec2(x, y) * texelSize; + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + } + shadow /= 9.0; + } else if (shadowMode == 2) { + // pcf off + float pcfDepth = + texture(shadowMap, projCoords.xy).r; + + shadow = + currentDepth - bias > pcfDepth + ? 1.0 + : 0.0; + } else if (shadowMode == 3) { + float avgBlockerDepth = + FindBlocker( + projCoords.xy, + currentDepth, + texelSize, + bias, + lightSizeUV + ); + + if(avgBlockerDepth < 0.0) + { + return 0.0; + } + + vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0; + float angle = random(seed) * 6.2831853;; // 2*PI + float s = sin(angle), c = cos(angle); + mat2 rot = mat2(c, -s, s, c); + /* + float penumbraRatio = (currentDepth - avgBlockerDepth); + float radius = clamp( + penumbraRatio * lightSizeUV, + minRadius, + maxRadius + ); + */ + float radius = + mix( + minRadius, + maxRadius, + smoothstep( + 0.0, + 0.05, + currentDepth - avgBlockerDepth + ) + ); + for (int i = 0; i < samples; ++i) { + vec2 offset; + if (samples == 32) { + offset = rot * poissonDisk32[i] * radius * texelSize; + } else if (samples == 16) { + offset = rot * poissonDisk16[i] * radius * texelSize; + } else if (samples == 8) { + offset = rot * poissonDisk8[i] * radius * texelSize; + } else { + offset = rot * poissonDisk32[i] * radius * texelSize; + } + float pcfDepth = texture(shadowMap, projCoords.xy + offset).r; + shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0); + } + shadow /= float(samples); + + } else { + float pcfDepth = + texture(shadowMap, projCoords.xy).r; + + shadow = + currentDepth - bias > pcfDepth + ? 1.0 + : 0.0; + } + + + return shadow; +} + diff --git a/assets/texture/skin/player001.png b/assets/texture/skin/player001.png new file mode 100644 index 0000000000000000000000000000000000000000..e9fb9e43159a1cef07eddaf23e6e9ffc9d387c1f GIT binary patch literal 535 zcmV+y0_gpTP)Px$(Md!>RCt{2nz3%eFc5}+TOOEMso)7%=**LJU_fQ$RVp!pI&|O_`U1?b^a-e7 zWvn_Nr3*pSgxJnuE|KqBLhR(v-Nk30BNTuPHRiCIm-e@Z6GhQCWe;f&Cr$(M>x!V< z`zXv|H7|73*Liq4T30l;0o-WZc!N!Tt08FI$V2Yjd=%$N&~`YL5n%EksW@z-#)~XF z4)y?mkLM=Ycd)D>>&6$KU(h^ zAB0MEK<+$F>C)sNsL^^Cv=_K^-m^Vw4pRvp0Yy%hwbvafg}DvjMgqWX05=+^b>F6_ zt*qVES69?{kwyI^_Wr7By$l*^t#4!3{>Q*NBUGQdSCBb?+W>Bo4?yr1D(bMBla?u0DH>a!ih7xD?^>%bXT5*t zFVpQk<*)Ps$Xh=f==; z7qRszm{z}w->>iUg8>Aeml0c^O7bb7?(kRHsLLR=)OC^k2oMB85ClOG1VIo4K@fz* Z_yeqf @@ -11,7 +12,7 @@ #include #include namespace Cubed { -enum class Gait { WALK = 0, RUN }; + class ClientWorld; class ClientPlayer { public: @@ -25,7 +26,7 @@ public: static AABB get_aabb(const glm::vec3& pos); const glm::vec3& get_front() const; - const Gait& get_gait() const; + Gait get_gait() const; const std::optional& get_look_block_pos() const; // thread safe glm::vec3 get_player_pos() const; @@ -50,17 +51,25 @@ public: unsigned place_block() const; - Gait& gait(); + void set_gait(Gait gait); GameMode& game_mode(); const ClientWorld& get_world() const; void set_uuid(std::string_view uuid); - const std::string& get_uuid() const; + std::string get_uuid() const; const std::string& get_name() const; void init(std::string_view name); + float yaw() const; + float pitch() const; + float& angle(); + float& walk_time(); + bool ray_cast(const glm::vec3& start, const glm::vec3& dir, + glm::ivec3& block_pos, glm::vec3& normal, + float distance = 4.0f); + private: using enum GameMode; float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED; @@ -70,8 +79,8 @@ private: float m_g = DEFAULT_G; constexpr static float MAX_SPACE_ON_TIME = 0.3f; - float m_yaw = 0.0f; - float m_pitch = 0.0f; + std::atomic m_yaw = 0.0f; + std::atomic m_pitch = 0.0f; float m_sensitivity = 0.15f; @@ -88,6 +97,9 @@ private: unsigned m_place_block = 1; + bool m_moving = false; + bool m_sprinting = false; + glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 move_distance{0.0f, 0.0f, 0.0f}; // player is tow block tall, the pos is the lower pos @@ -99,20 +111,21 @@ private: glm::vec3 m_right{0, 0, 0}; static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f}; - Gait m_gait = Gait::WALK; + std::atomic m_gait = Gait::STOP; MoveState m_move_state{}; GameMode m_game_mode = CREATIVE; std::optional m_look_block = std::nullopt; std::string m_name{}; + mutable std::shared_mutex m_uuid_mutex; std::string m_uuid; ClientWorld& m_world; + float m_angle{0.0f}; + float m_walk_time{0.0f}; + mutable std::shared_mutex m_player_pos_mutex; mutable std::shared_mutex m_chunk_pos_mutex; ChunkPosSet m_player_chunk_pos_set; - bool ray_cast(const glm::vec3& start, const glm::vec3& dir, - glm::ivec3& block_pos, glm::vec3& normal, - float distance = 4.0f); void update_direction(); void update_lookup_block(); @@ -121,5 +134,6 @@ private: void update_y_move(glm::vec3& player_pos); void update_z_move(glm::vec3& player_pos); void update_player_chunk(); + Gait compute_gait() const; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index e236a70..ca1679f 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -14,15 +14,28 @@ #include namespace Cubed { -struct RemotePlayerInfo { +struct PlayerInfo { std::string name; + std::string uuid; glm::vec3 render_pos; glm::vec3 target_pos; + float render_yaw; + float yaw; + float render_pitch; + float pitch; + Gait gait; + float angle = 0.0f; + float walk_time = 0.0f; }; -struct RemotePlayerRenderData { +struct PlayerRenderData { std::string name; + std::string uuid; glm::vec3 render_pos; + float yaw; + float pitch; + Gait gait; + float angle; }; class ClientWorld { @@ -34,6 +47,7 @@ public: void update(float delta_time); const std::optional& get_look_block_pos() const; ClientPlayer& get_player(); + const ClientPlayer& get_player() const; int get_block(const glm::ivec3& block_pos) const; bool is_solid(const glm::ivec3& block_pos) const; bool can_pass_block(const glm::ivec3& block_pos) const; @@ -65,13 +79,17 @@ public: void request_chunk(); std::vector& planes(); const std::vector& render_snapshots() const; - const std::vector& render_player_data() const; + const std::vector& render_player_data() const; + std::vector& render_player_data(); + glm::vec3 sunlight_dir() const; + bool sphere_collide_world(glm::vec3 center, float radius) const; void receive_chunk(std::vector data, PacketHeader header); void request_exit(); bool is_receive_exit(); int chunk_size() const; static AABB get_block_aabb(const glm::ivec3& pos); + template void register_timer(std::string_view id, TickType threshold, Fn&& f) { m_timers.emplace(std::piecewise_construct, @@ -86,21 +104,20 @@ private: ChunkPos::TBBHash>; using ChunkPosSet = absl::flat_hash_set; using ChunkPosVector = std::vector; - using OtherPlayerHashMap = - std::unordered_map; + using OtherPlayerHashMap = std::unordered_map; using chunk_acc = ChunkHashMap::accessor; using chunk_cacc = ChunkHashMap::const_accessor; static constexpr int WORLD_EXIT_TIMEOUT = 200; static constexpr int MAX_UPLOAD_CHUNK_SUM = 16; ClientPlayer m_player; - OtherPlayerHashMap m_other_players; + OtherPlayerHashMap m_player_info; ChunkHashMap m_chunks; std::vector m_planes; std::jthread m_client_thread; std::mutex m_delete_vbo_mutex; std::mutex m_delete_vao_mutex; - mutable std::shared_mutex m_other_players_mutex; + mutable std::shared_mutex m_player_info_mutex; tbb::concurrent_queue> m_pending_upload_queue; tbb::concurrent_queue m_dirty_chunk_queue; @@ -110,7 +127,7 @@ private: std::deque m_dirty_queue; std::vector m_render_snapshots; - std::vector m_render_player_data; + std::vector m_render_player_data; tbb::concurrent_unordered_map m_timers; std::atomic m_game_running{false}; std::atomic m_receive_exit{false}; @@ -127,9 +144,7 @@ private: void client_run(std::stop_token token); - void set_player_pos(); - - void report_player_pos(); + void report_player_info(); void set_block(const glm::ivec3& pos, unsigned id); diff --git a/include/Cubed/gameplay/packet.hpp b/include/Cubed/gameplay/packet.hpp index a87fa17..93db2c5 100644 --- a/include/Cubed/gameplay/packet.hpp +++ b/include/Cubed/gameplay/packet.hpp @@ -52,7 +52,7 @@ enum class PacketEnum : uint16_t { LOGOUT_REQ = 1003, LOGOUT_RSP = 1004, PLAYER_INFO = 2001, - PLAYER_POS = 2002, + C2S_PLAYER_INFO = 2002, PLAYER_INFO_RSP = 2003, CHUNK_DATA_REQ = 3001, CHUNK_DATA_RSP = 3002, @@ -87,8 +87,8 @@ template <> constexpr uint16_t get_packet_id() { template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::PLAYER_INFO); } -template <> constexpr uint16_t get_packet_id() { - return std::to_underlying(PacketEnum::PLAYER_POS); +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::C2S_PLAYER_INFO); } template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::PLAYER_INFO_RSP); diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/player.hpp new file mode 100644 index 0000000..09deb0b --- /dev/null +++ b/include/Cubed/gameplay/player.hpp @@ -0,0 +1,21 @@ +#pragma once +#include +#include +namespace Cubed { +enum class Gait { STOP = 0, WALK = 1, RUN = 2 }; +constexpr int get_gait_id(Gait gait) { return std::to_underlying(gait); } + +inline Gait get_gait_from_id(int id) { + switch (id) { + case std::to_underlying(Gait::STOP): + return Gait::STOP; + case std::to_underlying(Gait::WALK): + return Gait::WALK; + case std::to_underlying(Gait::RUN): + return Gait::RUN; + default: + throw std::runtime_error("Unknown Gait"); + } +} + +} // namespace Cubed diff --git a/include/Cubed/gameplay/server_player.hpp b/include/Cubed/gameplay/server_player.hpp index db7aa3e..c98ee92 100644 --- a/include/Cubed/gameplay/server_player.hpp +++ b/include/Cubed/gameplay/server_player.hpp @@ -1,6 +1,7 @@ #pragma once #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/game_time.hpp" +#include "Cubed/gameplay/player.hpp" #include #include @@ -38,6 +39,14 @@ public: const ChunkPosSet& get_chunk_pos_set() const; ChunkPosSet& get_chunk_pos_set(); + void set_yaw(float yaw); + void set_pitch(float pitch); + float yaw() const; + float pitch() const; + + Gait gait() const; + void set_gait(Gait gait); + private: static constexpr TickType TIMEOUT = 200; std::string m_name; @@ -45,9 +54,12 @@ private: glm::vec3 m_pos{0.0f}; ServerWorld& m_world; ChunkPos m_last_chunk_pos{0, 0}; - std::shared_ptr m_session; + std::atomic> m_session; std::atomic m_last_gametick{0}; std::atomic m_chunk_task_id{0}; + std::atomic m_yaw{0.0f}; + std::atomic m_pitch{0.0f}; + std::atomic m_gait; mutable std::shared_mutex m_chunk_pos_mutex; ChunkPosSet m_player_chunk_pos_set; }; diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 0baea37..4fc2461 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -3,6 +3,7 @@ #include "Cubed/gameplay/cave_carver.hpp" #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/game_time.hpp" +#include "Cubed/gameplay/packet.hpp" // IWYU pragma: keep #include "Cubed/gameplay/river_worm.hpp" #include "Cubed/gameplay/server_chunk.hpp" #include "Cubed/gameplay/server_player.hpp" @@ -69,7 +70,7 @@ public: bool set_block(const glm::ivec3& block_pos, unsigned id); - void sync_player_pos(const std::string& uuid, float x, float y, float z); + void sync_player_pos(const C2S_PlayerInfo& rsp); void handle_player_login(const std::string& player_name, std::shared_ptr session); glm::vec3 get_player_pos(const std::string& uuid) const; @@ -136,7 +137,7 @@ private: std::atomic m_gen_pool_threads{0}; std::atomic m_net_pool_threads{0}; std::atomic m_max_threads{1}; - + std::atomic m_player_sum{0}; std::atomic m_game_ticks{0}; std::atomic m_day_tick{6000}; std::atomic m_tick_running{true}; diff --git a/include/Cubed/player_renderer.hpp b/include/Cubed/player_renderer.hpp new file mode 100644 index 0000000..4d2cc85 --- /dev/null +++ b/include/Cubed/player_renderer.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "Cubed/shader.hpp" + +#include +#include + +namespace Cubed { +class Renderer; +class PlayerRenderer { +public: + static constexpr int BODY_PART_NUM = 6; + PlayerRenderer(Renderer& renderer); + ~PlayerRenderer(); + void init(); + void render(const Shader& shader); + void shadow_render(const Shader& shader, glm::mat4& light_matrix); + +private: + struct PlayerVertex { + + float x = 0.0f, y = 0.0f, z = 0.0f; + float s = 0.0f, t = 0.0f; + float nx = 0.0f, ny = 0.0f, nz = 0.0f; + float tx = 0.0f, ty = 0.0f, tz = 0.0f; + }; + Renderer& m_renderer; + std::array m_vao; + std::array m_vbo; + bool m_inited{false}; + std::array, BODY_PART_NUM> m_vertices; +}; +} // namespace Cubed diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index 91b1eb6..5604e72 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -1,6 +1,7 @@ #pragma once #include "Cubed/constants.hpp" +#include "Cubed/player_renderer.hpp" #include "Cubed/primitive_data.hpp" #include "Cubed/shader.hpp" #include "Cubed/ui/text.hpp" @@ -50,6 +51,14 @@ public: float& underwater_fog_density(); float& water_density(); + const Camera& camera() const; + const ClientWorld& world() const; + ClientWorld& world(); + const glm::mat4& proj_mat() const; + const TextureManager& texture_mamger() const; + + float delta_time() const; + private: struct ParallelLight { glm::vec3 sundir; // direction from sun to vertex @@ -92,7 +101,7 @@ private: DevPanel& m_dev_panel; const TextureManager& m_texture_manager; ClientWorld& m_world; - + PlayerRenderer m_player_renderer; bool m_discard_tranparent = true; bool m_shader_on = true; bool m_water_perturb = true; @@ -174,7 +183,6 @@ private: 2 - outline vao 3 - ui vao 4 - text vao - 5 - player vao */ std::vector m_vao; std::vector m_ui; diff --git a/include/Cubed/texture_manager.hpp b/include/Cubed/texture_manager.hpp index 7383378..130b92d 100644 --- a/include/Cubed/texture_manager.hpp +++ b/include/Cubed/texture_manager.hpp @@ -16,6 +16,8 @@ private: GLuint m_normal_texture_array = 0; GLfloat m_max_aniso = 0.0f; + GLuint m_skin = 0; + int m_aniso = 1; std::vector m_item_textures; @@ -30,6 +32,7 @@ private: void init_block(); void init_ui(); void init_block_status(); + void init_skin(); void hot_reload(); public: @@ -43,6 +46,7 @@ public: GLuint get_ui_array() const; GLuint get_pbr_texture() const; const std::vector& item_textures() const; + GLuint get_skin() const; // Must call after MapTable::init_map() and glfwMakeContextCurrent(window); void init_texture(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2b3a0a9..03322e7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,4 +43,5 @@ target_sources(${PROJECT_NAME} gameplay/client_player.cpp gameplay/session.cpp gameplay/network_client.cpp + player_renderer.cpp ) \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index 0929dba..9a1ff82 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -199,6 +199,11 @@ void App::key_callback(GLFWwindow* window, int key, int scancode, int action, app->m_camera.reset_camera(); } break; + case GLFW_KEY_F5: + if (action == GLFW_PRESS) { + app->m_camera.change_perspective(); + } + break; } app->m_client_world.get_player().update_player_move_state(key, action); diff --git a/src/camera.cpp b/src/camera.cpp index 74d4e7a..3903883 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -4,6 +4,10 @@ #include "Cubed/gameplay/client_world.hpp" #include "Cubed/tools/cubed_assert.hpp" +namespace { +constexpr float DISTANCE = 4.0f; + +} // namespace namespace Cubed { Camera::Camera() {} @@ -12,7 +16,28 @@ void Camera::update_move_camera() { ASSERT_MSG(m_player, "nullptr"); auto pos = m_player->get_player_pos(); // pos.y need to add 1.6f to center - m_camera_pos = glm::vec3(pos.x, pos.y + 1.6f, pos.z); + static constexpr float PLAYER_EYE_OFFSET = 1.6f; + glm::vec3 eye = glm::vec3(pos.x, pos.y + PLAYER_EYE_OFFSET, pos.z); + auto forward = m_player->get_front(); + m_front = forward; + switch (m_perspective) { + case Perspective::FIRST_PERSON: + m_camera_pos = eye; + break; + case Perspective::THIRD_PERSON_BACK: { + constexpr float CAMERA_RADIUS = 0.2f; + + glm::vec3 target = eye - forward * DISTANCE; + m_camera_pos = camera_collision(eye, target, CAMERA_RADIUS); + } break; + case Perspective::THIRD_PERSON_FRONT: { + m_front = -forward; + constexpr float CAMERA_RADIUS = 0.2f; + + glm::vec3 target = eye + forward * DISTANCE; + m_camera_pos = camera_collision(eye, target, CAMERA_RADIUS); + } break; + } glm::ivec3 block_pos = glm::floor(m_camera_pos); auto& world = m_player->get_world(); if (world.get_block_tpye(block_pos) == 7) { @@ -52,7 +77,7 @@ void Camera::update_cursor_position_camera(double xpos, double ypos) { const glm::mat4 Camera::get_camera_lookat() const { ASSERT_MSG(m_player, "nullptr"); - return glm::lookAt(m_camera_pos, m_camera_pos + m_player->get_front(), + return glm::lookAt(m_camera_pos, m_camera_pos + m_front, glm::vec3(0.0f, 1.0f, 0.0f)); } @@ -60,6 +85,44 @@ const glm::vec3& Camera::get_camera_pos() const { return m_camera_pos; } bool Camera::is_under_water() const { return m_under_water; } -glm::vec3 Camera::get_camera_front() const { return m_player->get_front(); } +glm::vec3 Camera::get_camera_front() const { return m_front; } + +void Camera::change_perspective() { + switch (m_perspective) { + case Perspective::FIRST_PERSON: + m_perspective = Perspective::THIRD_PERSON_BACK; + break; + case Perspective::THIRD_PERSON_BACK: + m_perspective = Perspective::THIRD_PERSON_FRONT; + break; + case Perspective::THIRD_PERSON_FRONT: + m_perspective = Perspective::FIRST_PERSON; + break; + } +} +bool Camera::is_first_person() const { + return m_perspective == Perspective::FIRST_PERSON; +} + +glm::vec3 Camera::camera_collision(glm::vec3 start, glm::vec3 end, + float radius) { + constexpr float STEP = 0.05f; + + glm::vec3 last = start; + + glm::vec3 dir = glm::normalize(end - start); + float len = glm::length(end - start); + + for (float t = 0.0f; t <= len; t += STEP) { + glm::vec3 p = start + dir * t; + + if (m_player->get_world().sphere_collide_world(p, radius)) + return last; + + last = p; + } + + return end; +} } // namespace Cubed diff --git a/src/dev_panel.cpp b/src/dev_panel.cpp index a646159..9ad24e3 100644 --- a/src/dev_panel.cpp +++ b/src/dev_panel.cpp @@ -572,9 +572,9 @@ void DevPanel::show_player_tab_item() { if (ImGui::Combo("Gait", &m_player_profile.gait, GAITS, IM_ARRAYSIZE(GAITS))) { if (m_player_profile.gait == 0) { - m_player->gait() = Gait::WALK; + m_player->set_gait(Gait::WALK); } else if (m_player_profile.gait == 1) { - m_player->gait() = Gait::RUN; + m_player->set_gait(Gait::RUN); } else { ASSERT_MSG(false, "Unknown Gait"); } @@ -613,11 +613,11 @@ void DevPanel::show_player_tab_item() { m_player->deceleration() = DEFAULT_DECELERATION; m_player->g() = DEFAULT_G; m_player->change_mode(GameMode::CREATIVE); - m_player->gait() = Gait::WALK; + m_player->set_gait(Gait::WALK); m_player_profile.game_mode = 0; m_player_profile.gait = 0; } - if (m_player->gait() == Gait::WALK) { + if (m_player->get_gait() == Gait::WALK) { m_player_profile.gait = 0; } else { m_player_profile.gait = 1; @@ -753,7 +753,7 @@ void DevPanel::update_player_profile() { ASSERT(false); return; } - m_player_profile.gait = std::to_underlying(m_player->gait()); + m_player_profile.gait = std::to_underlying(m_player->get_gait()); m_player_profile.game_mode = std::to_underlying(m_player->game_mode()); } diff --git a/src/gameplay/client_player.cpp b/src/gameplay/client_player.cpp index 595e44e..da46ecc 100644 --- a/src/gameplay/client_player.cpp +++ b/src/gameplay/client_player.cpp @@ -20,7 +20,7 @@ AABB ClientPlayer::get_aabb(const glm::vec3& pos) { } const glm::vec3& ClientPlayer::get_front() const { return m_front; } -const Gait& ClientPlayer::get_gait() const { return m_gait; } +Gait ClientPlayer::get_gait() const { return m_gait.load(); } const std::optional& ClientPlayer::get_look_block_pos() const { return m_look_block; @@ -111,10 +111,9 @@ void ClientPlayer::change_mode(GameMode mode) { Logger::info("Change GameMode to {}", to_str(mode)); if (mode == CREATIVE) { is_fly = false; - m_gait = Gait::WALK; + m_max_speed = m_max_walk_speed; } else if (mode == SPECTATOR) { is_fly = true; - m_gait = Gait::RUN; m_max_speed = m_max_run_speed; } } @@ -128,7 +127,7 @@ void ClientPlayer::set_player_pos(const glm::vec3& pos) { m_player_pos = pos; } void ClientPlayer::set_place_block(unsigned id) { m_place_block = id; } void ClientPlayer::update(float delta_time) { - + m_gait = compute_gait(); update_move(delta_time); update_lookup_block(); @@ -145,36 +144,42 @@ void ClientPlayer::update_player_move_state(int key, int action) { case GLFW_KEY_W: if (action == GLFW_PRESS) { m_move_state.forward = true; + m_moving = true; } if (action == GLFW_RELEASE) { m_move_state.forward = false; - if (m_game_mode != SPECTATOR) { - m_gait = Gait::WALK; - } + m_moving = false; + m_sprinting = false; } break; case GLFW_KEY_S: if (action == GLFW_PRESS) { m_move_state.back = true; + m_moving = true; } if (action == GLFW_RELEASE) { m_move_state.back = false; + m_moving = false; } break; case GLFW_KEY_A: if (action == GLFW_PRESS) { m_move_state.left = true; + m_moving = true; } if (action == GLFW_RELEASE) { m_move_state.left = false; + m_moving = false; } break; case GLFW_KEY_D: if (action == GLFW_PRESS) { m_move_state.right = true; + m_moving = true; } if (action == GLFW_RELEASE) { m_move_state.right = false; + m_moving = false; } break; case GLFW_KEY_SPACE: @@ -205,7 +210,7 @@ void ClientPlayer::update_player_move_state(int key, int action) { break; case GLFW_KEY_LEFT_CONTROL: if (action == GLFW_PRESS) { - m_gait = Gait::RUN; + m_sprinting = true; } break; case GLFW_KEY_F4: @@ -224,13 +229,15 @@ void ClientPlayer::update_front_vec(float offset_x, float offset_y) { m_yaw += offset_x * m_sensitivity; m_pitch += offset_y * m_sensitivity; - m_yaw = std::fmod(m_yaw, 360.0); + // m_yaw = std::fmod(m_yaw.load(), 360.0); - m_pitch = std::clamp(m_pitch, -89.0f, 89.0f); + m_pitch = std::clamp(m_pitch.load(), -89.0f, 89.0f); - m_front.x = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); - m_front.y = sin(glm::radians(m_pitch)); - m_front.z = -cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); + m_front.x = + sin(glm::radians(m_yaw.load())) * cos(glm::radians(m_pitch.load())); + m_front.y = sin(glm::radians(m_pitch.load())); + m_front.z = + -cos(glm::radians(m_yaw.load())) * cos(glm::radians(m_pitch.load())); m_front = glm::normalize(m_front); } @@ -298,6 +305,9 @@ void ClientPlayer::update_move(float delta_time) { if (delta_time > 1.0f) { return; } + if (m_xz_speed < 0.01f) { + m_sprinting = false; + } // ensure the thread safe glm::vec3 player_pos; @@ -307,12 +317,10 @@ void ClientPlayer::update_move(float delta_time) { } if (m_game_mode != SPECTATOR) { - if (m_gait == Gait::RUN) { - m_max_speed = m_max_run_speed; - } - if (m_gait == Gait::WALK) { - m_max_speed = m_max_walk_speed; - } + m_max_speed = + (m_gait == Gait::RUN) ? m_max_run_speed : m_max_walk_speed; + } else { + m_max_speed = m_max_run_speed; } if (space_on) { @@ -405,7 +413,7 @@ void ClientPlayer::update_x_move(glm::vec3& player_pos) { if (!m_world.can_pass_block(block_pos)) { AABB block_box = ClientWorld::get_block_aabb(block_pos); if (player_box.intersects(block_box)) { - m_gait = Gait::WALK; + m_sprinting = false; player_pos.x -= move_distance.x; return; } @@ -469,7 +477,7 @@ void ClientPlayer::update_z_move(glm::vec3& player_pos) { if (!m_world.can_pass_block(block_pos)) { AABB block_box = ClientWorld::get_block_aabb(block_pos); if (player_box.intersects(block_box)) { - m_gait = Gait::WALK; + m_sprinting = false; player_pos.z -= move_distance.z; return; } @@ -495,6 +503,16 @@ void ClientPlayer::update_player_chunk() { } } +Gait ClientPlayer::compute_gait() const { + if (m_xz_speed < 0.01f) + return Gait::STOP; + + if (m_sprinting) + return Gait::RUN; + + return Gait::WALK; +} + void ClientPlayer::update_scroll(double yoffset) { if (m_game_mode == SPECTATOR) { if (yoffset > 0) { @@ -546,12 +564,24 @@ float& ClientPlayer::deceleration() { return m_deceleration; } float& ClientPlayer::g() { return m_g; } float& ClientPlayer::fly_y_speed() { return m_fly_y_speed; } unsigned ClientPlayer::place_block() const { return m_place_block; }; -Gait& ClientPlayer::gait() { return m_gait; } +void ClientPlayer::set_gait(Gait gait) { m_gait = gait; } GameMode& ClientPlayer::game_mode() { return m_game_mode; } const ClientWorld& ClientPlayer::get_world() const { return m_world; } -void ClientPlayer::set_uuid(std::string_view uuid) { m_uuid = uuid; } -const std::string& ClientPlayer::get_uuid() const { return m_uuid; } +void ClientPlayer::set_uuid(std::string_view uuid) { + std::lock_guard lock(m_uuid_mutex); + m_uuid = uuid; +} +std::string ClientPlayer::get_uuid() const { + + std::shared_lock lock(m_uuid_mutex); + return m_uuid; +} const std::string& ClientPlayer::get_name() const { return m_name; } void ClientPlayer::init(std::string_view name) { m_name = name; } + +float ClientPlayer::yaw() const { return m_yaw; } +float ClientPlayer::pitch() const { return m_pitch; } +float& ClientPlayer::angle() { return m_angle; } +float& ClientPlayer::walk_time() { return m_walk_time; } } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index f000cd1..6fde996 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -51,7 +51,7 @@ const std::optional& ClientWorld::get_look_block_pos() const { } ClientPlayer& ClientWorld::get_player() { return m_player; } - +const ClientPlayer& ClientWorld::get_player() const { return m_player; } int ClientWorld::get_block(const glm::ivec3& block_pos) const { auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z); chunk_cacc cacc; @@ -262,11 +262,11 @@ void ClientWorld::push_delete_vao(GLuint vao) { void ClientWorld::report_block_change(const glm::ivec3& pos, unsigned id) const { - { + if (id != 0) { AABB block_box = get_block_aabb(pos); - std::shared_lock lock(m_other_players_mutex); + std::shared_lock lock(m_player_info_mutex); - for (auto& [uuid, player] : m_other_players) { + for (auto& [uuid, player] : m_player_info) { AABB box = ClientPlayer::get_aabb(player.target_pos); if (box.intersects(block_box)) { return; @@ -296,16 +296,23 @@ void ClientWorld::receive_time(const UpdateTime& rsp) { } void ClientWorld::receive_remote_player(const PlayerInfoRsp& rsp) { + auto pitch = rsp.pitch(); + auto yaw = rsp.yaw(); { - std::lock_guard lock(m_other_players_mutex); + std::lock_guard lock(m_player_info_mutex); glm::vec3 pos{rsp.pos().x(), rsp.pos().y(), rsp.pos().z()}; - auto it = m_other_players.find(rsp.uuid()); - if (it == m_other_players.end()) { - m_other_players.emplace( + auto it = m_player_info.find(rsp.uuid()); + if (it == m_player_info.end()) { + m_player_info.emplace( std::piecewise_construct, std::forward_as_tuple(rsp.uuid()), - std::forward_as_tuple(rsp.name(), pos, pos)); + std::forward_as_tuple(rsp.name(), rsp.uuid(), pos, pos, yaw, + yaw, pitch, pitch, + get_gait_from_id(rsp.gait()))); } else { it->second.target_pos = pos; + it->second.yaw = yaw; + it->second.pitch = pitch; + it->second.gait = get_gait_from_id(rsp.gait()); } // Logger::info("Player {} pos Update", rsp.name()); } @@ -321,8 +328,8 @@ void ClientWorld::receive_player_logout(const LogoutRsp& rsp) { return; } { - std::lock_guard lock(m_other_players_mutex); - int sum = m_other_players.erase(rsp.uuid()); + std::lock_guard lock(m_player_info_mutex); + int sum = m_player_info.erase(rsp.uuid()); if (sum == 0) { Logger::warn("Player {} not find", rsp.uuid()); } else { @@ -336,7 +343,7 @@ void ClientWorld::init(std::string_view player_name, m_player.init(player_name); m_client = client; // timer - register_timer("player_pos", 1, [this]() { report_player_pos(); }); + register_timer("player_pos", 1, [this]() { report_player_info(); }); LoginReq req; req.set_name(m_player.get_name()); while (!client->is_connected()) { @@ -427,19 +434,23 @@ void ClientWorld::client_run(std::stop_token stoken) { } } -void ClientWorld::report_player_pos() { +void ClientWorld::report_player_info() { if (!m_client) { return; } Arena arena; - auto* pos = Arena::Create(&arena); - pos->set_uuid(m_player.get_uuid()); + auto* info = Arena::Create(&arena); + info->set_uuid(m_player.get_uuid()); glm::vec3 player_pos = m_player.get_player_pos(); - auto* v3 = pos->mutable_pos(); + auto* v3 = info->mutable_pos(); v3->set_x(player_pos.x); v3->set_y(player_pos.y); v3->set_z(player_pos.z); - m_client->send(make_packet(*pos), 0); + info->set_yaw(m_player.yaw()); + info->set_pitch(m_player.pitch()); + info->set_gait(get_gait_id(m_player.get_gait())); + + m_client->send(make_packet(*info), 0); } void ClientWorld::update_chunk(const ChunkPosSet& old, const ChunkPosSet& now) { @@ -690,8 +701,8 @@ void ClientWorld::update(float delta_time) { m_render_player_data.clear(); { - std::lock_guard lock(m_other_players_mutex); - for (auto& [uuid, player] : m_other_players) { + std::lock_guard lock(m_player_info_mutex); + for (auto& [uuid, player] : m_player_info) { player.render_pos = glm::mix(player.render_pos, player.target_pos, 0.15f); if (Math::distance2(player.render_pos, m_player.get_player_pos()) > @@ -699,7 +710,49 @@ void ClientWorld::update(float delta_time) { CHUNK_SIZE) { continue; } - m_render_player_data.emplace_back(player.name, player.render_pos); + player.render_yaw = glm::mix(player.render_yaw, player.yaw, 0.15); + player.render_pitch = + glm::mix(player.render_pitch, player.pitch, 0.15); + + if (player.gait == Gait::WALK || player.gait == Gait::RUN) { + + player.walk_time += delta_time; + + float speed = player.gait == Gait::RUN ? 14.0f : 8.0f; + float amp = player.gait == Gait::RUN ? 50.0f : 35.0f; + // float amp = 90.0f; + player.angle = + glm::sin(player.walk_time * speed) * glm::radians(amp); + } else if (player.gait == Gait::STOP) { + float t = glm::clamp(delta_time * 10.0f, 0.0f, 1.0f); + player.angle = glm::mix(player.angle, 0.0f, t); + } + + m_render_player_data.emplace_back( + player.name, player.uuid, player.render_pos, player.render_yaw, + player.render_pitch, player.gait, player.angle); + } + { + auto gait = m_player.get_gait(); + auto& walk_time = m_player.walk_time(); + auto& angle = m_player.angle(); + if (gait == Gait::WALK || gait == Gait::RUN) { + + walk_time += delta_time; + + float speed = gait == Gait::RUN ? 14.0f : 8.0f; + float amp = gait == Gait::RUN ? 50.0f : 35.0f; + // float amp = 90.0f; + angle = glm::sin(walk_time * speed) * glm::radians(amp); + } else if (gait == Gait::STOP) { + float t = glm::clamp(delta_time * 10.0f, 0.0f, 1.0f); + angle = glm::mix(angle, 0.0f, t); + } + + m_render_player_data.emplace_back( + m_player.get_name(), m_player.get_uuid(), + m_player.get_player_pos(), m_player.yaw(), m_player.pitch(), + m_player.get_gait(), m_player.angle()); } } } @@ -721,6 +774,33 @@ glm::vec3 ClientWorld::sunlight_dir() const { return glm::normalize(-dir); } + +bool ClientWorld::sphere_collide_world(glm::vec3 center, float radius) const { + glm::ivec3 min = glm::floor(center - glm::vec3(radius)); + glm::ivec3 max = glm::floor(center + glm::vec3(radius)); + + for (int x = min.x; x <= max.x; ++x) { + for (int y = min.y; y <= max.y; ++y) { + for (int z = min.z; z <= max.z; ++z) { + if (!is_solid({x, y, z})) + continue; + + glm::vec3 closest; + closest.x = glm::clamp(center.x, float(x), float(x + 1)); + closest.y = glm::clamp(center.y, float(y), float(y + 1)); + closest.z = glm::clamp(center.z, float(z), float(z + 1)); + + glm::vec3 d = center - closest; + + if (glm::dot(d, d) < radius * radius) + return true; + } + } + } + + return false; +} + int ClientWorld::rendering_distance() const { return m_rendering_distance.load(); } @@ -738,8 +818,10 @@ const std::vector& ClientWorld::render_snapshots() const { return m_render_snapshots; }; -const std::vector& -ClientWorld::render_player_data() const { +const std::vector& ClientWorld::render_player_data() const { + return m_render_player_data; +} +std::vector& ClientWorld::render_player_data() { return m_render_player_data; } std::vector& ClientWorld::planes() { return m_planes; } diff --git a/src/gameplay/server_player.cpp b/src/gameplay/server_player.cpp index c8ee2bf..54f83c0 100644 --- a/src/gameplay/server_player.cpp +++ b/src/gameplay/server_player.cpp @@ -53,4 +53,10 @@ ServerPlayer::ChunkPosSet& ServerPlayer::get_chunk_pos_set() { std::lock_guard lock(m_chunk_pos_mutex); return m_player_chunk_pos_set; } +void ServerPlayer::set_yaw(float yaw) { m_yaw = yaw; } +void ServerPlayer::set_pitch(float pitch) { m_pitch = pitch; } +float ServerPlayer::yaw() const { return m_yaw.load(); } +float ServerPlayer::pitch() const { return m_pitch.load(); } +Gait ServerPlayer::gait() const { return m_gait; } +void ServerPlayer::set_gait(Gait gait) { m_gait = gait; } } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index cbee30f..8021bee 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -82,9 +82,11 @@ void ServerWorld::send_time() { rsp->set_day_tick(m_day_tick); rsp->set_game_tick(m_game_ticks); - - for (auto& [uuid, player] : m_players) { - player.get_session()->send(make_packet(*rsp), 3); + { + std::shared_lock lock(m_player_mutex); + for (auto& [uuid, player] : m_players) { + player.get_session()->send(make_packet(*rsp), 3); + } } } @@ -537,9 +539,14 @@ void ServerWorld::update() { } } -void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y, - float z) { +void ServerWorld::sync_player_pos(const C2S_PlayerInfo& prsp) { std::string name; + auto x = prsp.pos().x(); + auto y = prsp.pos().y(); + auto z = prsp.pos().z(); + auto uuid = prsp.uuid(); + auto yaw = prsp.yaw(); + auto pitch = prsp.pitch(); { std::lock_guard lock(m_player_mutex); auto it = m_players.find(uuid); @@ -547,8 +554,13 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y, Logger::warn("Player {} is not in this Server", uuid); return; } + it->second.update_pos(x, y, z); it->second.update_sync_gametick(m_game_ticks); + it->second.set_pitch(pitch); + it->second.set_yaw(yaw); + it->second.set_gait(get_gait_from_id(prsp.gait())); + name = it->second.get_name(); } ChunkPos pos = get_chunk_pos(x, z); @@ -578,6 +590,9 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y, pos->set_x(x); pos->set_y(y); pos->set_z(z); + rsp->set_yaw(yaw); + rsp->set_pitch(pitch); + rsp->set_gait(prsp.gait()); session->send(make_packet(*rsp), 0); } } @@ -605,7 +620,7 @@ void ServerWorld::handle_player_login(const std::string& name, session->send(make_packet(*rsp), 0); return; } - + ++m_player_sum; m_uuid_to_name.emplace(uuid, name); // Pre-insert into new_chunks to ensure correct addition to waiting_player /*ChunkPosSet required_chunks; @@ -647,7 +662,7 @@ void ServerWorld::handle_player_exit(const std::string& uuid) { } m_uuid_to_name.erase(uuid); - + --m_player_sum; update_ref_count(old_set, {}); Arena arena; diff --git a/src/gameplay/session.cpp b/src/gameplay/session.cpp index 496aca1..4a448d1 100644 --- a/src/gameplay/session.cpp +++ b/src/gameplay/session.cpp @@ -64,12 +64,10 @@ asio::awaitable Session::read_loop() { shared_from_this()); } } - if (cmd_id == std::to_underlying(PacketEnum::PLAYER_POS)) { - auto* pos = Arena::Create(&arena); + if (cmd_id == std::to_underlying(PacketEnum::C2S_PLAYER_INFO)) { + auto* pos = Arena::Create(&arena); if (decode_packet(*pos, body_data, header)) { - m_server_world.sync_player_pos(pos->uuid(), pos->pos().x(), - pos->pos().y(), - pos->pos().z()); + m_server_world.sync_player_pos(*pos); } } if (cmd_id == std::to_underlying(PacketEnum::CHUNK_DATA_REQ)) { diff --git a/src/player_renderer.cpp b/src/player_renderer.cpp new file mode 100644 index 0000000..a40380e --- /dev/null +++ b/src/player_renderer.cpp @@ -0,0 +1,375 @@ +#include "Cubed/player_renderer.hpp" + +#include "Cubed/camera.hpp" +#include "Cubed/gameplay/client_world.hpp" +#include "Cubed/primitive_data.hpp" +#include "Cubed/renderer.hpp" +#include "Cubed/texture_manager.hpp" + +#include + +namespace { +struct Cuboid { + glm::vec3 offset; + glm::vec3 size; +}; + +constexpr Cuboid PLAYER_MODEL[] = { + // Head + {{0.25f, 1.50f, 0.25f}, {0.50f, 0.50f, 0.50f}}, + + // Body + {{0.25f, 0.75f, 0.375f}, {0.50f, 0.75f, 0.25f}}, + + // Left Arm + {{0.00f, 0.75f, 0.375f}, {0.25f, 0.75f, 0.25f}}, + + // Right Arm + {{0.75f, 0.75f, 0.375f}, {0.25f, 0.75f, 0.25f}}, + + // Left Leg + {{0.25f, 0.00f, 0.375f}, {0.25f, 0.75f, 0.25f}}, + + // Right Leg + {{0.50f, 0.00f, 0.375f}, {0.25f, 0.75f, 0.25f}}, +}; + +struct UVRect { + int x; + int y; + int w; + int h; +}; +using FaceUV = std::array; +constexpr FaceUV HEAD_UV = {{ + {0, 0, 8, 8}, // front + {8, 0, 8, 8}, // right + {16, 0, 8, 8}, // back + {24, 0, 8, 8}, // left + {32, 0, 8, 8}, // top + {40, 0, 8, 8}, // bottom +}}; + +constexpr FaceUV BODY_UV = {{ + {0, 8, 8, 12}, + {8, 8, 4, 12}, + {12, 8, 8, 12}, + {20, 8, 4, 12}, + {24, 8, 8, 4}, + {32, 8, 8, 4}, +}}; + +constexpr FaceUV LEFT_ARM_UV = {{ + {0, 20, 4, 12}, + {4, 20, 4, 12}, + {8, 20, 4, 12}, + {12, 20, 4, 12}, + {16, 20, 4, 4}, + {20, 20, 4, 4}, +}}; + +constexpr FaceUV RIGHT_ARM_UV = {{ + {24, 20, 4, 12}, + {28, 20, 4, 12}, + {32, 20, 4, 12}, + {36, 20, 4, 12}, + {40, 20, 4, 4}, + {44, 20, 4, 4}, +}}; + +constexpr FaceUV LEFT_LEG_UV = {{ + {0, 32, 4, 12}, // front + {4, 32, 4, 12}, // right + {8, 32, 4, 12}, // back + {12, 32, 4, 12}, // left + {16, 32, 4, 4}, // top + {20, 32, 4, 4}, // bottom +}}; + +constexpr FaceUV RIGHT_LEG_UV = {{ + {24, 32, 4, 12}, // front + {28, 32, 4, 12}, // right + {32, 32, 4, 12}, // back + {36, 32, 4, 12}, // left + {40, 32, 4, 4}, // top + {44, 32, 4, 4}, // bottom +}}; + +constexpr std::array, + Cubed::PlayerRenderer::BODY_PART_NUM> + PLAYER_TEX = {{{HEAD_UV}, + {BODY_UV}, + {LEFT_ARM_UV}, + {RIGHT_ARM_UV}, + {LEFT_LEG_UV}, + {RIGHT_LEG_UV}}}; + +constexpr glm::vec3 HEAD_PIVOT{0.5, 1.5, 0.5}; +constexpr glm::vec3 LEFT_ARM_PIVOT{0.125f, 1.50f, 0.50f}; +constexpr glm::vec3 RIGHT_ARM_PIVOT{0.875, 1.50, 0.50}; +constexpr glm::vec3 LEFT_LEG_PIVOT{0.375, 0.75, 0.50}; +constexpr glm::vec3 RIGHT_LEG_PIVOT{0.625, 0.75, 0.50}; + +} // namespace +namespace Cubed { +PlayerRenderer::PlayerRenderer(Renderer& renderer) : m_renderer(renderer) {} + +PlayerRenderer::~PlayerRenderer() { + if (!m_inited) { + return; + } + glDeleteBuffers(BODY_PART_NUM, m_vbo.data()); + glDeleteVertexArrays(BODY_PART_NUM, m_vao.data()); +} + +void PlayerRenderer::init() { + + for (int i = 0; i < 6; i++) { + const auto& part = PLAYER_MODEL[i]; + + for (int face = 0; face < 6; ++face) { + const auto& rect = PLAYER_TEX[i][face]; + for (int vex = 0; vex < 6; ++vex) { + glm::vec3 p{VERTICES_POS[face][vex][0], + VERTICES_POS[face][vex][1], + VERTICES_POS[face][vex][2]}; + float su = TEX_COORDS[face][vex][0]; + float sv = TEX_COORDS[face][vex][1]; + + if (face == 2) { + float t = su; + su = sv; + sv = 1.0f - t; + } + float u = rect.x + su * rect.w; + float v = rect.y + sv * rect.h; + + u /= 64.0f; + v /= 64.0f; + + p *= part.size; + p += part.offset; + + m_vertices[i].emplace_back( + p.x, p.y, p.z, u, v, NORMALS[face][vex][0], + NORMALS[face][vex][1], NORMALS[face][vex][2], + TANGENTS[face][vex][0], TANGENTS[face][vex][1], + TANGENTS[face][vex][2]); + } + } + } + glGenVertexArrays(BODY_PART_NUM, m_vao.data()); + glGenBuffers(BODY_PART_NUM, m_vbo.data()); + for (int i = 0; i < BODY_PART_NUM; i++) { + glBindVertexArray(m_vao[i]); + + glBindBuffer(GL_ARRAY_BUFFER, m_vbo[i]); + glBufferData(GL_ARRAY_BUFFER, + m_vertices[i].size() * sizeof(PlayerVertex), + m_vertices[i].data(), GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(PlayerVertex), + (void*)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(PlayerVertex), + (void*)offsetof(PlayerVertex, s)); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(PlayerVertex), + (void*)offsetof(PlayerVertex, nx)); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(PlayerVertex), + (void*)offsetof(PlayerVertex, tx)); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + m_inited = true; +} + +void PlayerRenderer::render(const Shader& shader) { + if (!m_inited) { + Logger::error("Player Renderer isn't init"); + return; + } + auto& m_camera = m_renderer.camera(); + auto& m_world = m_renderer.world(); + auto& m_player = m_world.get_player(); + glm::mat4 m_v_mat = m_camera.get_camera_lookat(); + glm::mat4 m_p_mat = m_renderer.proj_mat(); + + auto& players = m_world.render_player_data(); + shader.set_loc("proj_matrix", m_p_mat); + + for (auto& player : players) { + + if (player.uuid == m_player.get_uuid()) { + if (m_camera.is_first_person()) { + continue; + } + } + + glm::mat4 model(1.0f); + + model = glm::translate(model, player.render_pos); + + // model = glm::translate(model, glm::vec3(0.5f, 0.0f, 0.5f)); + // glm::rotate(..., +yaw, Y) follows the OpenGL right‑handed coordinate + // system, where a positive angle means counter‑clockwise rotation. + // Therefore the model must use -yaw to align with the direction of + // m_front. + model = glm::rotate(model, glm::radians(-player.yaw + 180.0f), + glm::vec3(0, 1, 0)); + model = glm::translate(model, glm::vec3(-0.5f, 0.0f, -0.5f)); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_renderer.texture_mamger().get_skin()); + + auto make_rotated = [&](glm::vec3 pivot, float angle) { + glm::mat4 mat = model; + mat = glm::translate(mat, pivot); + mat = glm::rotate(mat, angle, glm::vec3(1, 0, 0)); + mat = glm::translate(mat, -pivot); + return mat; + }; + + for (int i = 0; i < BODY_PART_NUM; i++) { + switch (i) { + case 0: { + glm::mat4 head_model = model; + head_model = glm::translate(head_model, HEAD_PIVOT); + head_model = + glm::rotate(head_model, glm::radians(-player.pitch), + glm::vec3(1, 0, 0)); + head_model = glm::translate(head_model, -HEAD_PIVOT); + glm::mat4 head_mv = m_v_mat * head_model; + shader.set_loc("norm_matrix", + glm::transpose(glm::inverse(head_mv))); + shader.set_loc("modelMatrix", head_model); + shader.set_loc("mv_matrix", head_mv); + } break; + case 1: { + glm::mat4 mv_mat = m_v_mat * model; + shader.set_loc("norm_matrix", + glm::transpose(glm::inverse(mv_mat))); + shader.set_loc("modelMatrix", model); + shader.set_loc("mv_matrix", mv_mat); + } break; + case 2: { // left arm + glm::mat4 model_mat = + make_rotated(LEFT_ARM_PIVOT, player.angle); + glm::mat4 mv_mat = m_v_mat * model_mat; + shader.set_loc("norm_matrix", + glm::transpose(glm::inverse(mv_mat))); + shader.set_loc("modelMatrix", model_mat); + shader.set_loc("mv_matrix", mv_mat); + } break; + case 3: { // right arm + glm::mat4 model_mat = + make_rotated(RIGHT_ARM_PIVOT, -player.angle); + glm::mat4 mv_mat = m_v_mat * model_mat; + shader.set_loc("norm_matrix", + glm::transpose(glm::inverse(mv_mat))); + shader.set_loc("modelMatrix", model_mat); + shader.set_loc("mv_matrix", mv_mat); + } break; + case 4: { // left leg + glm::mat4 model_mat = + make_rotated(LEFT_LEG_PIVOT, -player.angle); + glm::mat4 mv_mat = m_v_mat * model_mat; + shader.set_loc("norm_matrix", + glm::transpose(glm::inverse(mv_mat))); + shader.set_loc("modelMatrix", model_mat); + shader.set_loc("mv_matrix", mv_mat); + } break; + case 5: { // right leg + glm::mat4 model_mat = + make_rotated(RIGHT_LEG_PIVOT, player.angle); + + glm::mat4 mv_mat = m_v_mat * model_mat; + shader.set_loc("norm_matrix", + glm::transpose(glm::inverse(mv_mat))); + shader.set_loc("modelMatrix", model_mat); + shader.set_loc("mv_matrix", mv_mat); + } break; + } + + glBindVertexArray(m_vao[i]); + glEnable(GL_DEPTH_TEST); + glDrawArrays(GL_TRIANGLES, 0, m_vertices[i].size()); + } + } +} + +void PlayerRenderer::shadow_render(const Shader& shader, + glm::mat4& light_matrix) { + if (!m_inited) { + Logger::error("Player Renderer isn't init"); + return; + } + shader.use(); + shader.set_loc("lightSpaceMatrix", light_matrix); + auto& m_world = m_renderer.world(); + auto& players = m_world.render_player_data(); + + for (auto& player : players) { + glm::mat4 model(1.0f); + + model = glm::translate(model, player.render_pos); + + // model = glm::translate(model, glm::vec3(0.5f, 0.0f, 0.5f)); + + model = glm::rotate(model, glm::radians(-player.yaw + 180.0f), + glm::vec3(0, 1, 0)); + model = glm::translate(model, glm::vec3(-0.5f, 0.0f, -0.5f)); + + auto make_rotated = [&](glm::vec3 pivot, float angle) { + glm::mat4 mat = model; + mat = glm::translate(mat, pivot); + mat = glm::rotate(mat, angle, glm::vec3(1, 0, 0)); + mat = glm::translate(mat, -pivot); + return mat; + }; + + for (int i = 0; i < BODY_PART_NUM; i++) { + switch (i) { + case 0: { + glm::mat4 head_model = model; + head_model = glm::translate(head_model, HEAD_PIVOT); + head_model = + glm::rotate(head_model, glm::radians(-player.pitch), + glm::vec3(1, 0, 0)); + head_model = glm::translate(head_model, -HEAD_PIVOT); + shader.set_loc("modelMatrix", head_model); + } break; + case 1: { + shader.set_loc("modelMatrix", model); + } break; + case 2: { // left arm + shader.set_loc("modelMatrix", + make_rotated(LEFT_ARM_PIVOT, player.angle)); + } break; + case 3: { // right arm + shader.set_loc("modelMatrix", + make_rotated(RIGHT_ARM_PIVOT, -player.angle)); + } break; + case 4: { // left leg + shader.set_loc("modelMatrix", + + make_rotated(LEFT_LEG_PIVOT, -player.angle)); + } break; + case 5: { // right leg + shader.set_loc("modelMatrix", + + make_rotated(RIGHT_LEG_PIVOT, player.angle)); + } break; + } + glBindVertexArray(m_vao[i]); + glEnable(GL_DEPTH_TEST); + glDrawArrays(GL_TRIANGLES, 0, m_vertices[i].size()); + } + } +} + +} // namespace Cubed \ No newline at end of file diff --git a/src/proto/player/player.proto b/src/proto/player/player.proto index dfbf483..842823f 100644 --- a/src/proto/player/player.proto +++ b/src/proto/player/player.proto @@ -2,13 +2,19 @@ syntax = "proto3"; import "common/vector3.proto"; -message PlayerPos { +message C2S_PlayerInfo { string uuid = 1; Vec3 pos = 2; + float yaw = 3; + float pitch = 4; + int32 gait = 5; } message PlayerInfoRsp { string uuid = 1; string name = 2; Vec3 pos = 3; + float yaw = 4; + float pitch = 5; + int32 gait = 6; } \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp index 4cfaf09..44b5dd0 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -24,7 +24,8 @@ namespace Cubed { Renderer::Renderer(const Camera& camera, ClientWorld& world, const TextureManager& texture_manager, DevPanel& dev_panel) : m_camera(camera), m_dev_panel(dev_panel), - m_texture_manager(texture_manager), m_world(world) {} + m_texture_manager(texture_manager), m_world(world), + m_player_renderer(*this) {} Renderer::~Renderer() { if (m_init) { @@ -91,6 +92,9 @@ void Renderer::init() { "shaders/water_f_shader.glsl"}; Shader player_shader{"player", "shaders/player_v_shader.glsl", "shaders/player_f_shader.glsl"}; + Shader player_depth_shader{"player_depth", + "shaders/depth_player_shader.glsl", + "shaders/depth_player_fragment_shader.glsl"}; m_shaders.insert({player_shader.hash(), std::move(player_shader)}); m_shaders.insert({world_shader.hash(), std::move(world_shader)}); m_shaders.insert({outline_shader.hash(), std::move(outline_shader)}); @@ -105,7 +109,8 @@ void Renderer::init() { m_shaders.insert({depth_shader.hash(), std::move(depth_shader)}); m_shaders.insert({billboard.hash(), std::move(billboard)}); m_shaders.insert({water_shader.hash(), std::move(water_shader)}); - + m_shaders.insert( + {player_depth_shader.hash(), std::move(player_depth_shader)}); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -171,19 +176,12 @@ void Renderer::init() { glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); - glBindVertexArray(m_vao[5]); - glGenBuffers(1, &m_player_vbo); - glBindBuffer(GL_ARRAY_BUFFER, m_player_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_PLAYER), VERTICES_PLAYER, - GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(0); - init_quad(); init_text(); hot_reload(); + m_player_renderer.init(); + glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); m_init = true; @@ -714,6 +712,9 @@ void Renderer::render_world() { snapshot->normal_discard_vertices_count); } } + // player + auto& player_shadow = get_shader("player_depth"); + m_player_renderer.shadow_render(player_shadow, light_space_matrix); } glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); @@ -964,20 +965,28 @@ void Renderer::render_world() { void Renderer::render_player() { auto& shader = get_shader("player"); shader.use(); - m_v_mat = m_camera.get_camera_lookat(); - auto& players = m_world.render_player_data(); + glm::vec3 light_dir_view = + glm::normalize(glm::mat3(m_v_mat) * m_parallel_light.lightdir); - for (auto& player : players) { - m_m_mat = glm::translate( - glm::mat4(1.0f), player.render_pos + glm::vec3(-0.5f, 0.0f, -0.5f)); - m_mv_mat = m_v_mat * m_m_mat; - shader.set_loc("mv_matrix", m_mv_mat); - shader.set_loc("proj_matrix", m_p_mat); - glBindVertexArray(m_vao[5]); - glEnable(GL_DEPTH_TEST); - glDrawArrays(GL_TRIANGLES, 0, 36); - } + shader.set_loc("lightSpaceMatrix", m_parallel_light.light_space_matrix); + shader.set_loc("ambientStrength", m_ambient_strength); + shader.set_loc("sunlightColor", m_parallel_light.directional_light_color); + shader.set_loc("ambientColor", m_parallel_light.finnal_ambient_color); + shader.set_loc("sunlightDir", light_dir_view); + shader.set_loc("shadowMode", m_shadow_mode); + shader.set_loc("shader_on", m_shader_on); + shader.set_loc("lightSizeUV", static_cast(m_light_size_uv)); + shader.set_loc("minRadius", m_min_radius); + shader.set_loc("maxRadius", m_max_radius); + shader.set_loc("samples", m_samples); + shader.set_loc("renderDistance", m_world.rendering_distance()); + shader.set_loc("skyColor", m_sky_uniform.sky_top); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_depth_map_texture); + + m_player_renderer.render(shader); } #pragma endregion @@ -1054,4 +1063,13 @@ float& Renderer::cloud_threshold_high() { return m_cloud_threshold_high; } float& Renderer::refract_strength() { return m_refract_strength; } float& Renderer::underwater_fog_density() { return m_underwater_fog_density; } float& Renderer::water_density() { return m_water_density; } + +const Camera& Renderer::camera() const { return m_camera; } +const ClientWorld& Renderer::world() const { return m_world; } +ClientWorld& Renderer::world() { return m_world; } +const glm::mat4& Renderer::proj_mat() const { return m_p_mat; } +const TextureManager& Renderer::texture_mamger() const { + return m_texture_manager; +} +float Renderer::delta_time() const { return m_delta_time; } } // namespace Cubed \ No newline at end of file diff --git a/src/texture_manager.cpp b/src/texture_manager.cpp index 1b58e3e..99065fd 100644 --- a/src/texture_manager.cpp +++ b/src/texture_manager.cpp @@ -14,6 +14,7 @@ constexpr int CROSS_PLANE_SIZE = 16; constexpr int BLOCK_ITEM_SIZE = 16; constexpr int UI_SIZE = 16; constexpr int BLOCK_STATUS_SIZE = 16; +constexpr int SKIN_SIZE = 64; unsigned char* generate_flat_normal_map(int width = BLOCK_NORMAL_SIZE, int height = BLOCK_NORMAL_SIZE) { @@ -44,6 +45,7 @@ void TextureManager::delet_texture() { for (auto& id : m_item_textures) { glDeleteTextures(1, &id); } + glDeleteTextures(1, &m_skin); Logger::info("Successfully delete all texture"); } } @@ -67,6 +69,8 @@ const std::vector& TextureManager::item_textures() const { return m_item_textures; } +GLuint TextureManager::get_skin() const { return m_skin; } + void TextureManager::load_block_status(unsigned id) { ASSERT_MSG(id < MAX_BLOCK_STATUS, "Exceed the max status sum limit"); @@ -279,6 +283,31 @@ void TextureManager::init_ui() { glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } + +void TextureManager::init_skin() { + + glGenTextures(1, &m_skin); + glBindTexture(GL_TEXTURE_2D, m_skin); + std::string path = "texture/skin/player001.png"; + unsigned char* image_data = nullptr; + image_data = (Tools::load_image_data(path)); + glBindTexture(GL_TEXTURE_2D, m_skin); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SKIN_SIZE, SKIN_SIZE, 0, GL_RGBA, + GL_UNSIGNED_BYTE, image_data); + Tools::delete_image_data(image_data); + glBindTexture(GL_TEXTURE_2D, m_skin); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(GL_TEXTURE_2D); + + if (m_aniso >= 1) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, + static_cast(m_aniso)); + } +} + void TextureManager::init_block_status() { glGenTextures(1, &m_block_status_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); @@ -315,6 +344,7 @@ void TextureManager::init_texture() { init_block(); init_block_status(); init_ui(); + init_skin(); m_init = true; }