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 0000000..e9fb9e4 Binary files /dev/null and b/assets/texture/skin/player001.png differ diff --git a/include/Cubed/camera.hpp b/include/Cubed/camera.hpp index 89a087b..aa1a649 100644 --- a/include/Cubed/camera.hpp +++ b/include/Cubed/camera.hpp @@ -12,11 +12,21 @@ class ClientPlayer; class Camera { private: + enum class Perspective { + FIRST_PERSON, + THIRD_PERSON_BACK, + THIRD_PERSON_FRONT, + }; + bool m_firse_mouse = true; ClientPlayer* m_player; float m_last_mouse_x, m_last_mouse_y; glm::vec3 m_camera_pos; bool m_under_water = false; + Perspective m_perspective = Perspective::FIRST_PERSON; + glm::vec3 m_front; + glm::vec3 camera_collision(glm::vec3 start, glm::vec3 end, + float radius = 0.2f); public: Camera(); @@ -33,6 +43,8 @@ public: bool is_under_water() const; glm::vec3 get_camera_front() const; + void change_perspective(); + bool is_first_person() const; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/client_player.hpp b/include/Cubed/gameplay/client_player.hpp index 371505a..d03b991 100644 --- a/include/Cubed/gameplay/client_player.hpp +++ b/include/Cubed/gameplay/client_player.hpp @@ -4,6 +4,7 @@ #include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/game_mode.hpp" +#include "Cubed/gameplay/player.hpp" #include "Cubed/input.hpp" #include @@ -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; }