diff --git a/assets/shaders/depth_model_instance_vert.glsl b/assets/shaders/depth_model_instance_vert.glsl new file mode 100644 index 0000000..dd0ecab --- /dev/null +++ b/assets/shaders/depth_model_instance_vert.glsl @@ -0,0 +1,13 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 3) in mat4 modelMatrix; +uniform mat4 lightSpaceMatrix; + +out vec2 tc; + +void main() { + tc = texCoord; + gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/depth_model_vert copy.glsl b/assets/shaders/depth_model_vert copy.glsl new file mode 100644 index 0000000..93f577b --- /dev/null +++ b/assets/shaders/depth_model_vert copy.glsl @@ -0,0 +1,14 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; + +uniform mat4 lightSpaceMatrix; +uniform mat4 modelMatrix; + +out vec2 tc; + +void main() { + tc = texCoord; + gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/model_instance_vert.glsl b/assets/shaders/model_instance_vert.glsl new file mode 100644 index 0000000..cded335 --- /dev/null +++ b/assets/shaders/model_instance_vert.glsl @@ -0,0 +1,26 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in vec3 aNormal; +layout (location = 3) in mat4 modelMatrix; +uniform mat4 view_matrix; +uniform mat4 proj_matrix; +uniform mat4 lightSpaceMatrix; +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; + mat4 mv_matrix= view_matrix * modelMatrix; + mat4 norm_matrix = transpose(inverse(mv_matrix)); + 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/include/Cubed/render/model_renderer.hpp b/include/Cubed/render/model_renderer.hpp index 629a61d..b590a8e 100644 --- a/include/Cubed/render/model_renderer.hpp +++ b/include/Cubed/render/model_renderer.hpp @@ -3,28 +3,50 @@ #include "Cubed/gameplay/ecs/animation.hpp" #include "Cubed/gameplay/model.hpp" #include "Cubed/render/model_node.hpp" -#include "Cubed/shader.hpp" #include +#include namespace Cubed { class Renderer; class Camera; class ModelRender { public: - ModelRender(Renderer& renderer); - void render_model(ModelID id, const glm::vec3& pos, float yaw, - Camera& camera, const WalkPose& pose); + struct InstanceData { + glm::vec3 pos{0.0f}; + float yaw = 0.0f; + WalkPose pose; + }; - void shadow_pass(ModelID id, const glm::vec3& pos, float yaw, - Camera& camera, const WalkPose& pose); + struct DrawEntry { + const Mesh* mesh = nullptr; + size_t node_slot = 0; + }; + + struct ModelBatch { + size_t node_count = 0; + size_t capacity = 0; + std::vector entries; + std::vector instance_matrices; + std::unique_ptr instance_vbo; + }; + + ModelRender(Renderer& renderer); + + void render_instance(ModelID id, std::span instances, + const Camera& camera, bool shadow); private: Renderer& m_renderer; - void render_node(const ModelNode& node, const glm::mat4& parent, - const glm::mat4& view, const Shader& shader, bool shadow, - const WalkPose& pose, const ModelAnimConfig& cfg); + + std::unordered_map m_batches; + + size_t collect_matrices(const ModelNode& node, const glm::mat4& parent, + const WalkPose& pose, const ModelAnimConfig& cfg, + std::vector& out, size_t slot); glm::mat4 pose_node(const ModelNode& node, const ModelAnimConfig& cfg, const WalkPose& pose); - void render_mesh(const Mesh& mesh, bool shadow); + + ModelBatch& get_batch(ModelID id, const ModelNode& root); + size_t flatten_nodes(const ModelNode& node, size_t slot, ModelBatch& batch); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/vertex_array.hpp b/include/Cubed/render/vertex_array.hpp index 8e55da1..cc5442c 100644 --- a/include/Cubed/render/vertex_array.hpp +++ b/include/Cubed/render/vertex_array.hpp @@ -19,6 +19,7 @@ public: void attribute(GLuint index, GLint size, GLenum type, GLsizei stride, const void* ptr, bool normalized = false) const; + void divisor(GLuint index, GLuint divisor = 1); private: GLuint m_vao = 0; diff --git a/include/Cubed/render/vertex_buffer.hpp b/include/Cubed/render/vertex_buffer.hpp index 83b9380..ca32817 100644 --- a/include/Cubed/render/vertex_buffer.hpp +++ b/include/Cubed/render/vertex_buffer.hpp @@ -26,6 +26,8 @@ public: GLuint id() const; void buffer_data(const void* data, GLsizeiptr size, BufferUsage usage = BufferUsage::STATIC_DRAW) const; + void buffer_sub_data(const void* data, GLsizeiptr size, + GLintptr offset) const; private: GLuint m_vbo = 0; diff --git a/src/render/model_renderer.cpp b/src/render/model_renderer.cpp index 739d8d5..5b042cb 100644 --- a/src/render/model_renderer.cpp +++ b/src/render/model_renderer.cpp @@ -18,77 +18,112 @@ float swing_angle(const WalkPose& pose, float speed, float amp_deg) { ModelRender::ModelRender(Renderer& renderer) : m_renderer(renderer) {} -void ModelRender::render_model(ModelID id, const glm::vec3& pos, float yaw, - Camera& camera, const WalkPose& pose) { +void ModelRender::render_instance(ModelID id, + std::span instances, + const Camera& camera, bool shadow) { + if (instances.empty()) { + return; + } auto& root = ModelManager::model(id).node; - glm::vec3 bob_pos = pos; - if (pose.gait != Gait::STOP && - root.anim.has_role(NodeAnimRule::Role::LEG)) { - bob_pos.y += std::abs(glm::sin(pose.walk_time * root.anim.walk_speed)) * - root.anim.body_bob; + auto& batch = get_batch(id, root); + const size_t PER_INSTANCE_SIZE = batch.node_count * sizeof(glm::mat4); + if (instances.size() > batch.capacity) { + batch.capacity = instances.size() * 2; + batch.instance_matrices.resize(batch.capacity * batch.node_count); + batch.instance_vbo = std::make_unique(); + batch.instance_vbo->buffer_data(batch.instance_matrices.data(), + batch.instance_matrices.size() * + sizeof(glm::mat4), + BufferUsage::DYNAMIC_DRAW); + for (const auto& entry : batch.entries) { + for (int col = 0; col < 4; ++col) { + entry.mesh->vao->attribute( + 3 + col, 4, GL_FLOAT, PER_INSTANCE_SIZE, + (void*)(entry.node_slot * sizeof(glm::mat4) + + col * sizeof(glm::vec4))); + entry.mesh->vao->divisor(3 + col); + } + } } - glm::mat4 transform = glm::translate(glm::mat4(1.0f), bob_pos) * - glm::rotate(glm::mat4(1.0f), yaw, {0, 1, 0}); - auto& shader = m_renderer.get_shader("model_shader"); + for (size_t i = 0; i < instances.size(); ++i) { + glm::vec3 bob_pos = instances[i].pos; + if (instances[i].pose.gait != Gait::STOP && + root.anim.has_role(NodeAnimRule::Role::LEG)) { + float speed = instances[i].pose.gait == Gait::RUN + ? root.anim.run_speed + : root.anim.walk_speed; + bob_pos.y += + std::abs(glm::sin(instances[i].pose.walk_time * speed)) * + root.anim.body_bob; + } + + glm::mat4 transform = + glm::translate(glm::mat4(1.0f), bob_pos) * + glm::rotate(glm::mat4(1.0f), instances[i].yaw, {0, 1, 0}); + collect_matrices(root, transform, instances[i].pose, root.anim, + batch.instance_matrices, i * batch.node_count); + } + + batch.instance_vbo->buffer_sub_data( + batch.instance_matrices.data(), + instances.size() * batch.node_count * sizeof(glm::mat4), 0); + + auto& shader = shadow ? m_renderer.get_shader("depth_model_instance") + : m_renderer.get_shader("model_instance"); glm::mat4 view = camera.get_camera_lookat(); shader.set_loc("proj_matrix", m_renderer.p_mat()); - render_node(root, transform, view, shader, false, pose, root.anim); -} - -void ModelRender::shadow_pass(ModelID id, const glm::vec3& pos, float yaw, - Camera& camera, const WalkPose& pose) { - auto& root = ModelManager::model(id).node; - glm::vec3 bob_pos = pos; - if (pose.gait != Gait::STOP && - root.anim.has_role(NodeAnimRule::Role::LEG)) { - bob_pos.y += std::abs(glm::sin(pose.walk_time * root.anim.walk_speed)) * - root.anim.body_bob; - } - - glm::mat4 transform = glm::translate(glm::mat4(1.0f), bob_pos) * - glm::rotate(glm::mat4(1.0f), yaw, {0, 1, 0}); - - auto& shader = m_renderer.get_shader("depth_model"); - glm::mat4 view = camera.get_camera_lookat(); - - render_node(root, transform, view, shader, true, pose, root.anim); -} - -void ModelRender::render_node(const ModelNode& node, const glm::mat4& parent, - const glm::mat4& view, const Shader& shader, - bool shadow, const WalkPose& pose, - const ModelAnimConfig& cfg) { - - glm::mat4 transform = parent * pose_node(node, cfg, pose); if (shadow) { - shader.set_loc("modelMatrix", transform); } else { - glm::mat4 mv_matrix = view * transform; - shader.set_loc("modelMatrix", transform); - shader.set_loc("mv_matrix", mv_matrix); - shader.set_loc("norm_matrix", glm::transpose(glm::inverse(mv_matrix))); + shader.set_loc("view_matrix", view); } - - for (auto& mesh : node.meshes) { - render_mesh(mesh, shadow); - } - - for (auto& child : node.children) { - render_node(child, transform, view, shader, shadow, pose, cfg); + for (const auto& entry : batch.entries) { + if (entry.mesh->texture) { + entry.mesh->texture->bind(1); + } + entry.mesh->vao->bind(); + glDrawElementsInstanced(GL_TRIANGLES, entry.mesh->indices.size(), + GL_UNSIGNED_INT, 0, instances.size()); } } -void ModelRender::render_mesh(const Mesh& mesh, bool) { - mesh.vao->bind(); - if (mesh.texture) { - mesh.texture->bind(1); - } else { - Logger::error("Model Texture Not Find"); +size_t ModelRender::collect_matrices(const ModelNode& node, + const glm::mat4& parent, + const WalkPose& pose, + const ModelAnimConfig& cfg, + std::vector& out, size_t slot) { + glm::mat4 transform = parent * pose_node(node, cfg, pose); + out[slot] = transform; + size_t next = slot + 1; + for (const auto& clild : node.children) { + next = collect_matrices(clild, transform, pose, cfg, out, next); + } + return next; +} + +ModelRender::ModelBatch& ModelRender::get_batch(ModelID id, + const ModelNode& root) { + auto it = m_batches.find(id); + if (it != m_batches.end()) { + return it->second; + } + ModelBatch batch; + batch.node_count = flatten_nodes(root, 0, batch); + return m_batches.try_emplace(id, std::move(batch)).first->second; +} + +size_t ModelRender::flatten_nodes(const ModelNode& node, size_t slot, + ModelBatch& batch) { + size_t node_slot = slot++; + for (const auto& mesh : node.meshes) { + batch.entries.emplace_back(&mesh, node_slot); } - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), - GL_UNSIGNED_INT, 0); + for (const auto& child : node.children) { + slot = flatten_nodes(child, slot, batch); + } + + return slot; } glm::mat4 ModelRender::pose_node(const ModelNode& node, diff --git a/src/render/shader_manager.cpp b/src/render/shader_manager.cpp index 867f430..4e5f491 100644 --- a/src/render/shader_manager.cpp +++ b/src/render/shader_manager.cpp @@ -35,6 +35,11 @@ void ShaderManager::init() { "shaders/rect_f_shader.glsl"); register_shader("model_shader", "shaders/model_vert.glsl", "shaders/model_frag.glsl"); + register_shader("model_instance", "shaders/model_instance_vert.glsl", + "shaders/model_frag.glsl"); + register_shader("depth_model_instance", + "shaders/depth_model_instance_vert.glsl", + "shaders/depth_model_frag.glsl"); } void ShaderManager::register_shader(const std::string& name, diff --git a/src/render/vertex_array.cpp b/src/render/vertex_array.cpp index a8122ed..9b12b76 100644 --- a/src/render/vertex_array.cpp +++ b/src/render/vertex_array.cpp @@ -35,5 +35,8 @@ void VertexArray::attribute(GLuint index, GLint size, GLenum type, stride, ptr); glEnableVertexAttribArray(index); } - +void VertexArray::divisor(GLuint index, GLuint divisor) { + bind(); + glVertexAttribDivisor(index, divisor); +} } // namespace Cubed \ No newline at end of file diff --git a/src/render/vertex_buffer.cpp b/src/render/vertex_buffer.cpp index 83bc540..7bb5bd2 100644 --- a/src/render/vertex_buffer.cpp +++ b/src/render/vertex_buffer.cpp @@ -48,5 +48,10 @@ void VertexBuffer::buffer_data(const void* data, GLsizeiptr size, glBufferData(target, size, data, std::to_underlying(usage)); } +void VertexBuffer::buffer_sub_data(const void* data, GLsizeiptr size, + GLintptr offset) const { + bind(); + glBufferSubData(get_buffer_target(), offset, size, data); +} } // namespace Cubed \ No newline at end of file diff --git a/src/render/world_renderer.cpp b/src/render/world_renderer.cpp index 4dfe654..63f8d43 100644 --- a/src/render/world_renderer.cpp +++ b/src/render/world_renderer.cpp @@ -11,7 +11,37 @@ #include "Cubed/tools/math_tools.hpp" #include +#include namespace Cubed { + +namespace { +std::unordered_map> +get_instances_data_map(ClientWorld& world) { + std::unordered_map> + instances_data_map; + auto& registry = world.entity_manager().get_registry(); + auto view = registry.view(); + for (auto entity : view) { + auto& creature = view.get(entity); + auto pos = creature.transform.position.value; + if (!world.is_render(pos)) { + continue; + } + auto& t = view.get(entity); + float yaw = std::atan2(t.direction.value.x, t.direction.value.z); + ModelRender::InstanceData data; + data.pos = t.position.value; + data.yaw = yaw; + data.pose = creature.pose; + instances_data_map[creature.model].emplace_back(std::move(data)); + // m_renderer.model_renderer().shadow_pass( + // creature.model, t.position.value, yaw, + // world.world_scene().camera(), creature.pose); + } + return instances_data_map; +}; +} // namespace + WorldRenderer::WorldRenderer(Renderer& renderer) : m_renderer(renderer), m_player_renderer(renderer), m_texture_manager(renderer.texture_mamger()) {} @@ -292,26 +322,24 @@ void WorldRenderer::render_outline(ClientWorld& world) { void WorldRenderer::shadow_entity(ClientWorld& world, const glm::mat4& light_matrix) { - auto& shader = m_renderer.get_shader("depth_model"); - shader.use(); - shader.set_loc("lightSpaceMatrix", light_matrix); glEnable(GL_DEPTH_TEST); - auto& registry = world.entity_manager().get_registry(); - auto view = registry.view(); - for (auto entity : view) { - auto& creature = view.get(entity); - auto pos = creature.transform.position.value; - if (!world.is_render(pos)) { - continue; + { + auto& shader = m_renderer.get_shader("depth_model_instance"); + shader.use(); + shader.set_loc("lightSpaceMatrix", light_matrix); + auto instances_data_map = get_instances_data_map(world); + for (auto& [id, data] : instances_data_map) { + m_renderer.model_renderer().render_instance( + id, data, world.world_scene().camera(), true); } - auto& t = view.get(entity); - float yaw = std::atan2(t.direction.value.x, t.direction.value.z); - - m_renderer.model_renderer().shadow_pass( - creature.model, t.position.value, yaw, world.world_scene().camera(), - creature.pose); } - m_player_renderer.render(shader, world, true); + + { + auto& shader = m_renderer.get_shader("depth_model"); + shader.use(); + shader.set_loc("lightSpaceMatrix", light_matrix); + m_player_renderer.render(shader, world, true); + } } void WorldRenderer::shadow_map_generate(ClientWorld& world) { @@ -700,46 +728,57 @@ void WorldRenderer::render_transparent_block(const glm::mat4& mv_mat, } void WorldRenderer::render_entity(ClientWorld& world) { - auto& shader = m_renderer.get_shader("model_shader"); - shader.use(); - glm::vec3 light_dir_view = - glm::normalize(glm::mat3(view_matrix) * m_parallel_light.lightdir); - - 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); - - m_depth_map_texture->bind(0); - glEnable(GL_DEPTH_TEST); + { + auto& model = m_renderer.get_shader("model_instance"); + model.use(); + glm::vec3 light_dir_view = + glm::normalize(glm::mat3(view_matrix) * m_parallel_light.lightdir); - auto& registry = world.entity_manager().get_registry(); - auto view = registry.view(); - for (auto entity : view) { - auto& creature = view.get(entity); - auto pos = creature.transform.position.value; - if (!world.is_render(pos)) { - continue; + model.set_loc("lightSpaceMatrix", m_parallel_light.light_space_matrix); + model.set_loc("ambientStrength", m_ambient_strength); + model.set_loc("sunlightColor", + m_parallel_light.directional_light_color); + model.set_loc("ambientColor", m_parallel_light.finnal_ambient_color); + model.set_loc("sunlightDir", light_dir_view); + model.set_loc("shadowMode", m_shadow_mode); + model.set_loc("shader_on", m_shader_on); + model.set_loc("lightSizeUV", static_cast(m_light_size_uv)); + model.set_loc("minRadius", m_min_radius); + model.set_loc("maxRadius", m_max_radius); + model.set_loc("samples", m_samples); + m_depth_map_texture->bind(0); + + auto instances_data_map = get_instances_data_map(world); + for (auto& [id, data] : instances_data_map) { + m_renderer.model_renderer().render_instance( + id, data, world.world_scene().camera(), false); } - auto& t = view.get(entity); - float yaw = std::atan2(t.direction.value.x, t.direction.value.z); - m_renderer.model_renderer().render_model( - creature.model, t.position.value, yaw, world.world_scene().camera(), - creature.pose); } - m_player_renderer.render(shader, world, false); + { + auto& model = m_renderer.get_shader("model_shader"); + model.use(); + glm::vec3 light_dir_view = + glm::normalize(glm::mat3(view_matrix) * m_parallel_light.lightdir); + + model.set_loc("lightSpaceMatrix", m_parallel_light.light_space_matrix); + model.set_loc("ambientStrength", m_ambient_strength); + model.set_loc("sunlightColor", + m_parallel_light.directional_light_color); + model.set_loc("ambientColor", m_parallel_light.finnal_ambient_color); + model.set_loc("sunlightDir", light_dir_view); + model.set_loc("shadowMode", m_shadow_mode); + model.set_loc("shader_on", m_shader_on); + model.set_loc("lightSizeUV", static_cast(m_light_size_uv)); + model.set_loc("minRadius", m_min_radius); + model.set_loc("maxRadius", m_max_radius); + model.set_loc("samples", m_samples); + m_player_renderer.render(model, world, false); + } } glm::vec3 WorldRenderer::quantize_sun_direction(const glm::vec3& lightdir,