diff --git a/CMakeLists.txt b/CMakeLists.txt index e540c27..236df3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,11 +105,15 @@ set(CLIENT_ONLY_SOURCES "src/gfx/frustum.cpp" "src/gfx/renderer.hpp" "src/gfx/renderer.cpp" + "src/gfx/shader_common.hpp" "src/gfx/shader_defs.hpp" "src/gfx/shader_sources.hpp" "src/gfx/shader_sources.cpp" "src/gfx/shader.hpp" "src/gfx/shader.cpp" + "src/gfx/surface_render_flags.hpp" + "src/gfx/surface_shader.hpp" + "src/gfx/surface_shader.cpp" "src/gfx/surface.hpp" "src/gfx/texture.cpp" "src/gfx/texture.hpp" diff --git a/src/gfx/draw_list.hpp b/src/gfx/draw_list.hpp index c4741f3..0fed964 100644 --- a/src/gfx/draw_list.hpp +++ b/src/gfx/draw_list.hpp @@ -5,6 +5,7 @@ #include "assets/skeleton.hpp" #include "surface.hpp" #include "uniform_buffer.hpp" +#include "surface_render_flags.hpp" namespace gfx { @@ -18,6 +19,7 @@ struct DrawSurfaceCmd uint32_t count = 0; // num triangles float dist = 0.0f; // distance to camera - for transparnt sorting const UniformBuffer* skinning = nullptr; // skinning matrices for skeletal meshes + SurfaceRenderFlags rflags = 0; }; struct DrawBeamCmd diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 4f91811..bab0cab 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -14,9 +14,6 @@ gfx::Renderer::Renderer() { - ShaderSources::MakeShader(mesh_shader_.shader, SS_MESH_VERT, SS_MESH_FRAG); - ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG); - ShaderSources::MakeShader(deform_mesh_shader_.shader, SS_DEFORM_MESH_VERT, SS_DEFORM_MESH_FRAG); ShaderSources::MakeShader(solid_shader_, SS_SOLID_VERT, SS_SOLID_FRAG); ShaderSources::MakeShader(hud_shader_, SS_HUD_VERT, SS_HUD_FRAG); ShaderSources::MakeShader(beam_shader_, SS_BEAM_VERT, SS_BEAM_FRAG); @@ -89,20 +86,32 @@ void gfx::Renderer::SetupBeamVA() void gfx::Renderer::InvalidateShaders() { - InvalidateMeshShader(mesh_shader_); - InvalidateMeshShader(skel_mesh_shader_); - InvalidateMeshShader(deform_mesh_shader_); + // invalidate surface shaders + for (auto& [flags, sshader] : surface_shaders_) + { + InvalidateSurfaceShader(sshader); + } } -void gfx::Renderer::InvalidateMeshShader(MeshShader& mshader) +gfx::SurfaceShader& gfx::Renderer::GetSurfaceShader(SurfaceRenderFlags flags) { - mshader.global_setup = false; - mshader.color = glm::vec4(-1.0f); // invalidate color + auto it = surface_shaders_.find(flags); + + // not yet generated + if (it == surface_shaders_.end()) + { + SurfaceShader& sshader = surface_shaders_[flags]; + sshader.shader = CreateSurfaceShader(flags, sshader.iflags); + + return sshader; + } + + return it->second; } -void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& params) +void gfx::Renderer::SetupSurfaceShader(SurfaceShader& sshader, const DrawListParams& params) { - const Shader& shader = *mshader.shader; + const Shader& shader = *sshader.shader; if (current_shader_ != &shader) { @@ -110,7 +119,7 @@ void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& p current_shader_ = &shader; } - if (mshader.global_setup) + if (sshader.global_setup) { return; // Global uniforms are already set up } @@ -118,23 +127,65 @@ void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& p glUniformMatrix4fv(shader.U(gfx::SU_VIEW_PROJ), 1, GL_FALSE, ¶ms.view_proj[0][0]); // setup lighting - glUniform3fv(shader.U(gfx::SU_AMBIENT_LIGHT), 1, ¶ms.env.ambient_light[0]); - glUniform3fv(shader.U(gfx::SU_SUN_COLOR), 1, ¶ms.env.sun_color[0]); - glUniform3fv(shader.U(gfx::SU_SUN_DIRECTION), 1, ¶ms.env.sun_direction[0]); - glUniform4fv(shader.U(gfx::SU_FOG), 1, ¶ms.env.fog[0]); + if (sshader.iflags & SIF_LIGHTING_DATA) + { + glUniform3fv(shader.U(gfx::SU_AMBIENT_LIGHT), 1, ¶ms.env.ambient_light[0]); + glUniform3fv(shader.U(gfx::SU_SUN_COLOR), 1, ¶ms.env.sun_color[0]); + glUniform3fv(shader.U(gfx::SU_SUN_DIRECTION), 1, ¶ms.env.sun_direction[0]); + // glUniform4fv(shader.U(gfx::SU_FOG), 1, ¶ms.env.fog[0]); + } - mshader.global_setup = true; + sshader.global_setup = true; +} + +void gfx::Renderer::InvalidateSurfaceShader(SurfaceShader& sshader) +{ + sshader.global_setup = false; + sshader.color = glm::vec4(-1.0f); // invalidate color } void gfx::Renderer::DrawSurfaceList(std::span list, const DrawListParams& params) { + // determine render flags + for (auto& cmd : list) + { + if (cmd.surface->sflags & SF_BLEND) + cmd.rflags |= SRF_BLEND; + + if (cmd.surface->texture) + cmd.rflags |= SRF_TEXTURE; + + if ((cmd.surface->mflags & MF_SKELETAL) && cmd.skinning) + cmd.rflags |= SRF_SKELETAL; + + if ((cmd.surface->sflags & SF_DEFORM_GRID) && cmd.surface->deform_tex) + cmd.rflags |= SRF_DEFORM; + + if ((cmd.surface->sflags & SF_UNLIT) == 0) + cmd.rflags |= SRF_LIT; + + if (cmd.color) + { + if (cmd.surface->sflags & SF_OBJECT_COLOR_MULT) + cmd.rflags |= SRF_OBJECT_COLOR; + else if (cmd.surface->sflags & SF_OBJECT_COLOR) + cmd.rflags |= SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND; + } + + if (cmd.surface->sflags & SF_2SIDED) + cmd.rflags |= SRF_2SIDED; + + if (cmd.surface->sflags & SF_BLEND_ADDITIVE) + cmd.rflags |= SRF_BLEND_ADDITIVE; + + if ((cmd.surface->sflags & (SF_BLEND | SF_OBJECT_COLOR)) == 0) + cmd.rflags |= SRF_CULL_ALPHA; + } + // sort the list to minimize state changes std::ranges::sort(list, [](const DrawSurfaceCmd& a, const DrawSurfaceCmd& b) { - const Surface* sa = a.surface; - const Surface* sb = b.surface; - - const bool blend_a = sa->sflags & SF_BLEND; - const bool blend_b = sb->sflags & SF_BLEND; + const bool blend_a = a.rflags & SRF_BLEND; + const bool blend_b = b.rflags & SRF_BLEND; if (blend_a != blend_b) return blend_b; // opaque first @@ -144,9 +195,15 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi return a.dist > b.dist; // do not optimize blended, sort by distance instead } - if (sa == sb) + if (a.surface == b.surface) return false; + if (auto cmp = a.rflags <=> b.rflags; cmp != 0) + return cmp < 0; + + const auto sa = a.surface; + const auto sb = a.surface; + if (auto cmp = sa->texture <=> sb->texture; cmp != 0) return cmp < 0; @@ -162,12 +219,14 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi glActiveTexture(GL_TEXTURE0); // for all future bindings // cache to eliminate fake state changes + SurfaceShader* sshader = nullptr; const gfx::Texture* last_texture = nullptr; const gfx::VertexArray* last_vao = nullptr; const gfx::UniformBuffer* last_skin = nullptr; const DeformTexture* last_deform = nullptr; + InvalidateShaders(); - + // enable depth test glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); @@ -175,80 +234,76 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi // reset face culling glEnable(GL_CULL_FACE); glCullFace(GL_BACK); - bool last_twosided = false; // reset blending glDisable(GL_BLEND); glDepthMask(GL_TRUE); - bool last_blend = false; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // set to opacity blending default - bool last_blend_additive = false; + + SurfaceRenderFlags last_rflags = 0; for (const DrawSurfaceCmd& cmd : list) { const Surface* surface = cmd.surface; - // mesh flags - const bool skeletal_flag = surface->mflags & MF_SKELETAL; - // surface flags - const bool twosided_flag = surface->sflags & SF_2SIDED; - const bool blend_flag = surface->sflags & SF_BLEND; - const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR; - const bool object_color_mult_flag = surface->sflags & SF_OBJECT_COLOR_MULT; - const bool deform_flag = surface->sflags & SF_DEFORM_GRID; - const bool unlit_flag = surface->sflags & SF_UNLIT; + // // mesh flags + // const bool skeletal_flag = surface->mflags & MF_SKELETAL; + // // surface flags + //const bool twosided_flag = cmd.rflags & SRF_2SIDED; + // const bool blend_flag = surface->sflags & SF_BLEND; + // const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR; + // const bool object_color_mult_flag = surface->sflags & SF_OBJECT_COLOR_MULT; + // const bool deform_flag = surface->sflags & SF_DEFORM_GRID; + // const bool unlit_flag = surface->sflags & SF_UNLIT; + + SurfaceRenderFlags rflags_diff = last_rflags ^ cmd.rflags; // sync 2sided - if (last_twosided != twosided_flag) + if (rflags_diff & SRF_2SIDED) { - if (twosided_flag) + if (cmd.rflags & SRF_2SIDED) glDisable(GL_CULL_FACE); else glEnable(GL_CULL_FACE); - - last_twosided = twosided_flag; } - // select shader - MeshShader& mshader = skeletal_flag ? skel_mesh_shader_ : (deform_flag ? deform_mesh_shader_ : mesh_shader_); - SetupMeshShader(mshader, params); + // setup shader + SurfaceRenderFlags last_shader_rflags = last_rflags & SRF__SHADER; + SurfaceRenderFlags shader_rflags = cmd.rflags & SRF__SHADER; + + if (last_shader_rflags != shader_rflags || !sshader) + { + sshader = &GetSurfaceShader(shader_rflags); + SetupSurfaceShader(*sshader, params); + } + + auto shader = sshader->shader.get(); // set model matrix if (cmd.matrices) { - glUniformMatrix4fv(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]); + glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]); } else { // use identity if no matrix provided static const glm::mat4 identity(1.0f); - glUniformMatrix4fv(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]); + glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]); } - // set color - int shflags = SHF_CULL_ALPHA; - - glm::vec4 color = glm::vec4(1.0f); - if (object_color_flag && cmd.color) + // sync color + if (sshader->iflags & SIF_OBJECT_COLOR) { - // use object color and disable alpha cull - - if (!object_color_mult_flag) + if (sshader->color != *cmd.color) { - shflags &= ~SHF_CULL_ALPHA; - shflags |= SHF_BACKGROUND; + glUniform4fv(shader->U(SU_COLOR), 1, &(*cmd.color)[0]); + sshader->color = *cmd.color; } - - color = glm::vec4(*cmd.color); } - // check unlit - if (unlit_flag) - shflags |= SHF_UNLIT; - // sync blending - if (blend_flag != last_blend) + if (rflags_diff & SRF_BLEND) { - if (blend_flag) + if (cmd.rflags & SRF_BLEND) { glEnable(GL_BLEND); glDepthMask(GL_FALSE); @@ -258,43 +313,19 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi glDisable(GL_BLEND); glDepthMask(GL_TRUE); } - - last_blend = blend_flag; } // sync blending type - if (blend_flag) + if ((cmd.rflags & SRF_BLEND) && (rflags_diff & SRF_BLEND_ADDITIVE)) { - shflags &= ~SHF_CULL_ALPHA; - - const bool blend_additive = surface->sflags & SF_BLEND_ADDITIVE; - if (blend_additive != last_blend_additive) - { - if (blend_additive) - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - else - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - last_blend_additive = blend_additive; - } - - // sync cull_alpha - if (mshader.flags != shflags) - { - glUniform1i(mshader.shader->U(SU_FLAGS), shflags); - mshader.flags = shflags; - } - - // sync color - if (mshader.color != color) - { - glUniform4fv(mshader.shader->U(SU_COLOR), 1, &color[0]); - mshader.color = color; + if (cmd.rflags & SRF_BLEND_ADDITIVE) + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + else + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } // bind texture - if (last_texture != surface->texture.get()) + if ((sshader->iflags & SIF_COLOR_TEXTURE) && last_texture != surface->texture.get()) { GLuint tex_id = surface->texture ? surface->texture->GetId() : 0; glActiveTexture(GL_TEXTURE0); @@ -303,14 +334,14 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi } // bind skinning UBO - if (cmd.skinning && last_skin != cmd.skinning) + if ((sshader->iflags & SIF_SKELETAL_DATA) && last_skin != cmd.skinning) { glBindBufferBase(GL_UNIFORM_BUFFER, 0, cmd.skinning->GetId()); last_skin = cmd.skinning; } // bind deform texture - if (deform_flag && surface->deform_tex.get() != last_deform) + if ((sshader->iflags & SIF_DEFORM_DATA) && surface->deform_tex.get() != last_deform) { const auto& deform_tex = *surface->deform_tex; GLuint tex_id = deform_tex.GetId(); @@ -324,7 +355,7 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi deform_info_mat[0] = deform_info.min; deform_info_mat[1] = deform_info.max; deform_info_mat[2] = glm::vec3(deform_info.max_offset, 0.0f, 0.0f); - glUniformMatrix3fv(mshader.shader->U(SU_DEFORM_INFO), 1, GL_FALSE, &deform_info_mat[0][0]); + glUniformMatrix3fv(shader->U(SU_DEFORM_INFO), 1, GL_FALSE, &deform_info_mat[0][0]); } // bind VAO @@ -340,6 +371,8 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi // draw glDrawElements(GL_TRIANGLES, static_cast(num_tris * 3U), GL_UNSIGNED_INT, (void*)(first_tri * 3U * sizeof(GLuint))); + + last_rflags = cmd.rflags; } // reset this as it is rare and other stuff might not reset this diff --git a/src/gfx/renderer.hpp b/src/gfx/renderer.hpp index b41ba70..6b92e69 100644 --- a/src/gfx/renderer.hpp +++ b/src/gfx/renderer.hpp @@ -5,6 +5,7 @@ #include "draw_list.hpp" #include "shader.hpp" +#include "surface_shader.hpp" namespace gfx { @@ -27,14 +28,14 @@ struct DrawListParams size_t screen_height = 0; }; -struct MeshShader +struct SurfaceShader { std::unique_ptr shader; + SurfaceShaderInputFlags iflags; // cached state to avoid redundant uniform updates which are expensive especially on WebGL bool global_setup = false; glm::vec4 color = glm::vec4(-1.0f); // invalid to force initial setup - int flags = 0; }; class Renderer @@ -47,17 +48,17 @@ private: void SetupBeamVA(); void InvalidateShaders(); - void InvalidateMeshShader(MeshShader& mshader); - void SetupMeshShader(MeshShader& mshader, const DrawListParams& params); + + SurfaceShader& GetSurfaceShader(SurfaceRenderFlags flags); + void SetupSurfaceShader(SurfaceShader& sshader, const DrawListParams& params); + void InvalidateSurfaceShader(SurfaceShader& sshader); void DrawSurfaceList(std::span queue, const DrawListParams& params); void DrawBeamList(std::span queue, const DrawListParams& params); void DrawHudList(std::span queue, const DrawListParams& params); private: - MeshShader mesh_shader_; - MeshShader skel_mesh_shader_; - MeshShader deform_mesh_shader_; + std::map surface_shaders_; std::unique_ptr solid_shader_; std::unique_ptr beam_segments_vbo_; diff --git a/src/gfx/shader_common.hpp b/src/gfx/shader_common.hpp new file mode 100644 index 0000000..97eac37 --- /dev/null +++ b/src/gfx/shader_common.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "shader_defs.hpp" +#include "client/gl.hpp" + +#ifndef PG_GLES +#define GLSL_VERSION \ + "#version 330 core\n" \ + "\n" +#else +#define GLSL_VERSION \ + "#version 300 es\n" \ + "precision highp float;\n" \ + "\n" +#endif + +#define STRINGIFY_HELPER(x) #x +#define STRINGIFY(x) STRINGIFY_HELPER(x) + +#define SHADER_DEFS \ + "#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \ + "#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \ + "\n" + +#define SHADER_HEADER \ + GLSL_VERSION \ + SHADER_DEFS diff --git a/src/gfx/shader_defs.hpp b/src/gfx/shader_defs.hpp index 4e48c1c..94d25ae 100644 --- a/src/gfx/shader_defs.hpp +++ b/src/gfx/shader_defs.hpp @@ -2,7 +2,3 @@ #define SD_MAX_LIGHTS 4 #define SD_MAX_BONES 128 - -#define SHF_CULL_ALPHA 1 -#define SHF_BACKGROUND 2 -#define SHF_UNLIT 4 \ No newline at end of file diff --git a/src/gfx/shader_sources.cpp b/src/gfx/shader_sources.cpp index 1159437..515ac08 100644 --- a/src/gfx/shader_sources.cpp +++ b/src/gfx/shader_sources.cpp @@ -1,304 +1,9 @@ #include "shader_sources.hpp" -#include "shader_defs.hpp" -#include "client/gl.hpp" - -#ifndef PG_GLES -#define GLSL_VERSION \ - "#version 330 core\n" \ - "\n" -#else -#define GLSL_VERSION \ - "#version 300 es\n" \ - "precision highp float;\n" \ - "\n" -#endif - -#define STRINGIFY_HELPER(x) #x -#define STRINGIFY(x) STRINGIFY_HELPER(x) - -#define SHADER_DEFS \ - "#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \ - "#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \ - "#define SHF_CULL_ALPHA " STRINGIFY(SHF_CULL_ALPHA) "\n" \ - "#define SHF_BACKGROUND " STRINGIFY(SHF_BACKGROUND) "\n" \ - "#define SHF_UNLIT " STRINGIFY(SHF_UNLIT) "\n" \ - "\n" - -#define SHADER_HEADER \ - GLSL_VERSION \ - SHADER_DEFS - -#define MESH_MATRICES_GLSL R"GLSL( - uniform mat4 u_view_proj; - uniform mat4 u_model; // Transform matrix -)GLSL" - -#define LIGHT_MATRICES_GLSL R"GLSL( - uniform vec3 u_ambient_light; - uniform vec3 u_sun_direction; - uniform vec3 u_sun_color; - uniform vec4 u_fog; - - uniform int u_flags; - - uniform int u_num_lights; - uniform vec3 u_light_positions[MAX_LIGHTS]; - uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius -)GLSL" - -#define COMPUTE_LIGHTS_GLSL R"GLSL( -// Example sun values (can later be uniforms) -//vec3 u_sun_direction = normalize(vec3(0.3, 0.5, -0.8)); // direction from which sunlight comes -//vec3 u_sun_color = vec3(1.0, 0.95, 0.7) * 0.9; // warm sunlight color - -//uniform vec3 u_ambient_light; - -vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal) -{ - if ((u_flags & SHF_UNLIT) > 0) - return vec3(1.0); - - // Base ambient - vec3 color = u_ambient_light; //vec3(0.5, 0.5, 0.5) * 0.9; // u_ambient_light - - // Sunlight contribution - float sun_dot = max(dot(sector_normal, -u_sun_direction), 0.0); - color += u_sun_color * sun_dot; - - // Point lights - for (int i = 0; i < u_num_lights; ++i) { - vec3 light_pos = u_light_positions[i]; - vec3 light_color = u_light_colors_rs[i].rgb; - float light_radius = u_light_colors_rs[i].a; - - vec3 to_light = light_pos - sector_pos; - float dist2 = dot(to_light, to_light); - if (dist2 < light_radius * light_radius) { - float dist = sqrt(dist2); - float attenuation = 1.0 - (dist / light_radius); - float dot_term = max(dot(sector_normal, normalize(to_light)), 0.0); - color += light_color * dot_term * attenuation; - } - } - - return color; -} -)GLSL" +#include "shader_common.hpp" // Zdrojove kody shaderu static const char* const s_srcs[] = { -// SS_MESH_VERT -SHADER_HEADER -R"GLSL( -layout (location = 0) in vec3 a_pos; -layout (location = 1) in vec3 a_normal; -layout (location = 2) in vec4 a_color; -layout (location = 3) in vec2 a_uv; - -)GLSL" -MESH_MATRICES_GLSL -LIGHT_MATRICES_GLSL -COMPUTE_LIGHTS_GLSL -R"GLSL( - -out vec2 v_uv; -out vec3 v_color; - -void main() { - vec4 world_pos = u_model * vec4(a_pos, 1.0); - vec3 world_normal = normalize(mat3(u_model) * a_normal); - gl_Position = u_view_proj * world_pos; - - v_uv = a_uv; - v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb; -} -)GLSL", - -// SS_MESH_FRAG -SHADER_HEADER -R"GLSL( -in vec2 v_uv; -in vec3 v_color; - -uniform sampler2D u_tex; -uniform vec4 u_color; -uniform int u_flags; - -layout (location = 0) out vec4 o_color; - -void main() { - o_color = vec4(texture(u_tex, v_uv)); - - if ((u_flags & SHF_BACKGROUND) > 0) - { - o_color = mix(u_color, o_color, o_color.a); - } - else - { - o_color *= u_color; - } - - if ((u_flags & SHF_CULL_ALPHA) > 0) - { - if (o_color.a < 0.5) - discard; - } - - o_color.rgb *= v_color; // Apply vertex color - //o_color = vec4(1.0, 0.0, 0.0, 1.0); -} - -)GLSL", - -// SS_SKEL_MESH_VERT -SHADER_HEADER -R"GLSL( -layout (location = 0) in vec3 a_pos; -layout (location = 1) in vec3 a_normal; -layout (location = 2) in vec4 a_color; -layout (location = 3) in vec2 a_uv; -layout (location = 5) in ivec4 a_bone_ids; -layout (location = 6) in vec4 a_bone_weights; - -layout (std140) uniform Bones { - mat4 u_bone_matrices[MAX_BONES]; -}; - -)GLSL" -MESH_MATRICES_GLSL -LIGHT_MATRICES_GLSL -COMPUTE_LIGHTS_GLSL -R"GLSL( - -out vec2 v_uv; -out vec3 v_color; - -void main() { - mat4 bone_transform = mat4(0.0); - for (int i = 0; i < 4; ++i) { - int bone_id = a_bone_ids[i]; - if (bone_id >= 0) { - bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i]; - } - } - - vec4 world_pos = bone_transform * vec4(a_pos, 1.0); - vec3 world_normal = normalize(mat3(bone_transform) * a_normal); - gl_Position = u_view_proj * world_pos; - - v_uv = a_uv; - v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb; -} -)GLSL", - -// SS_SKEL_MESH_FRAG -SHADER_HEADER -R"GLSL( -in vec2 v_uv; -in vec3 v_color; - -uniform sampler2D u_tex; -uniform vec4 u_color; -uniform int u_flags; - -layout (location = 0) out vec4 o_color; - -void main() { - o_color = vec4(texture(u_tex, v_uv)); - - if ((u_flags & SHF_BACKGROUND) > 0) - { - o_color = mix(u_color, o_color, o_color.a); - } - else - { - o_color *= u_color; - } - - if ((u_flags & SHF_CULL_ALPHA) > 0) - { - if (o_color.a < 0.5) - discard; - } - - o_color.rgb *= v_color; // Apply vertex color - //o_color = vec4(1.0, 0.0, 0.0, 1.0); -} - -)GLSL", - -// SS_DEFORM_MESH_VERT -SHADER_HEADER -R"GLSL( -layout (location = 0) in vec3 a_pos; -layout (location = 1) in vec3 a_normal; -layout (location = 2) in vec4 a_color; -layout (location = 3) in vec2 a_uv; - -)GLSL" -MESH_MATRICES_GLSL -LIGHT_MATRICES_GLSL -COMPUTE_LIGHTS_GLSL -R"GLSL( - -uniform mediump sampler3D u_deform_tex; -uniform mat3 u_deform_info; - -out vec2 v_uv; -out vec3 v_color; - -void main() { - vec3 deform_pos = (a_pos - u_deform_info[0]) / (u_deform_info[1] - u_deform_info[0]); - vec3 pos = a_pos + texture(u_deform_tex, deform_pos).xyz * u_deform_info[2].x; - //vec3 pos = a_pos + u_deform_info[0] * u_deform_info[2].x; - //vec3 pos = a_pos + vec3(0.0, 0.0, 1.0); - - vec4 world_pos = u_model * vec4(pos, 1.0); - vec3 world_normal = normalize(mat3(u_model) * a_normal); - gl_Position = u_view_proj * world_pos; - - v_uv = a_uv; - v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb; -} -)GLSL", - -// SS_DEFORM_MESH_FRAG -SHADER_HEADER -R"GLSL( -in vec2 v_uv; -in vec3 v_color; - -uniform sampler2D u_tex; -uniform vec4 u_color; -uniform int u_flags; - -layout (location = 0) out vec4 o_color; - -void main() { - o_color = vec4(texture(u_tex, v_uv)); - - if ((u_flags & SHF_BACKGROUND) > 0) - { - o_color = mix(u_color, o_color, o_color.a); - } - else - { - o_color *= u_color; - } - - if ((u_flags & SHF_CULL_ALPHA) > 0) - { - if (o_color.a < 0.5) - discard; - } - - o_color.rgb *= v_color; // Apply vertex color - //o_color = vec4(1.0, 0.0, 0.0, 1.0); -} - -)GLSL", - // SS_SOLID_VERT SHADER_HEADER R"GLSL( diff --git a/src/gfx/shader_sources.hpp b/src/gfx/shader_sources.hpp index 4a89f7a..6a217c3 100644 --- a/src/gfx/shader_sources.hpp +++ b/src/gfx/shader_sources.hpp @@ -7,15 +7,6 @@ namespace gfx { enum ShaderSource { - SS_MESH_VERT, - SS_MESH_FRAG, - - SS_SKEL_MESH_VERT, - SS_SKEL_MESH_FRAG, - - SS_DEFORM_MESH_VERT, - SS_DEFORM_MESH_FRAG, - SS_SOLID_VERT, SS_SOLID_FRAG, diff --git a/src/gfx/surface_render_flags.hpp b/src/gfx/surface_render_flags.hpp new file mode 100644 index 0000000..edc8b47 --- /dev/null +++ b/src/gfx/surface_render_flags.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace gfx +{ + +using SurfaceRenderFlags = uint16_t; + +enum SurfaceRenderFlag : SurfaceRenderFlags +{ + // dont require shader switch + SRF_BLEND_ADDITIVE = 1, + SRF_2SIDED = 2, + + // require shader switch + SRF_CULL_ALPHA = 4, + SRF_OBJECT_COLOR = 8, + SRF_OBJECT_COLOR_BACKGROUND = 16, + SRF_LIT = 32, + SRF_DEFORM = 64, + SRF_SKELETAL = 128, + SRF_TEXTURE = 256, + + SRF__SHADER = SRF_CULL_ALPHA | SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND | SRF_LIT | SRF_SKELETAL | SRF_DEFORM | SRF_TEXTURE, + + // order affects visual result + SRF_BLEND = 512, + +}; + +}; \ No newline at end of file diff --git a/src/gfx/surface_shader.cpp b/src/gfx/surface_shader.cpp new file mode 100644 index 0000000..0a76281 --- /dev/null +++ b/src/gfx/surface_shader.cpp @@ -0,0 +1,189 @@ +#include "surface_shader.hpp" + +#include + +#include "shader_common.hpp" + +std::unique_ptr gfx::CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags) +{ + std::string vert_attributes, vert_uniforms, vert_outs, vert_funcs, vert_pos_calc, vert_main; + std::string frag_ins, frag_uniforms, frag_funcs, frag_main; + + // default unlit untextured skeleton + + vert_attributes = R"GLSL( + layout (location = 0) in vec3 a_pos; + layout (location = 1) in vec3 a_normal; + layout (location = 2) in vec4 a_color; + layout (location = 3) in vec2 a_uv; + )GLSL"; + + vert_uniforms = R"GLSL( + uniform mat4 u_view_proj; + uniform mat4 u_model; + )GLSL"; + + vert_outs = R"GLSL( + out vec3 v_color; + )GLSL"; + + vert_pos_calc = R"GLSL( + vec4 world_pos = u_model * vec4(a_pos, 1.0); + vec3 world_normal = normalize(mat3(u_model) * a_normal); + )GLSL"; + + vert_main = R"GLSL( + gl_Position = u_view_proj * world_pos; + v_color = a_color.rgb; + )GLSL"; + + frag_ins = R"GLSL( + in vec3 v_color; + )GLSL"; + + frag_main = R"GLSL( + o_color = vec4(1.0); + )GLSL"; + + input_flags = 0; + + // color texture + if (flags & SRF_TEXTURE) + { + vert_outs += "out vec2 v_uv;\n"; + vert_main += "v_uv = a_uv;\n"; + + frag_ins += "in vec2 v_uv;\n"; + frag_uniforms += "uniform sampler2D u_tex;\n"; + frag_main += "o_color *= texture(u_tex, v_uv);\n"; + + input_flags |= SIF_COLOR_TEXTURE; + } + + // any object color type + if (flags & (SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND)) + { + frag_uniforms += "uniform vec4 u_color;\n"; + + if (flags & SRF_OBJECT_COLOR_BACKGROUND) + { + frag_main += "o_color = mix(u_color, o_color, o_color.a);\n"; + } + else // just multiply + { + frag_main += "o_color *= u_color;\n"; + } + + input_flags |= SIF_OBJECT_COLOR; + } + + // alpha culling + if (flags & SRF_CULL_ALPHA) + { + frag_main += "if (o_color.a < 0.5) discard;\n"; + } + + // lighting + if (flags & SRF_LIT) + { + vert_uniforms += R"GLSL( + // global + uniform vec3 u_ambient_light; + uniform vec3 u_sun_direction; + uniform vec3 u_sun_color; + + // local + uniform int u_num_lights; + uniform vec3 u_light_positions[MAX_LIGHTS]; + uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius + )GLSL"; + + vert_funcs += R"GLSL( + vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal) + { + // Base ambient + vec3 color = u_ambient_light; + + // Sunlight contribution + float sun_dot = max(dot(sector_normal, -u_sun_direction), 0.0); + color += u_sun_color * sun_dot; + + // Point lights + for (int i = 0; i < u_num_lights; ++i) { + vec3 light_pos = u_light_positions[i]; + vec3 light_color = u_light_colors_rs[i].rgb; + float light_radius = u_light_colors_rs[i].a; + + vec3 to_light = light_pos - sector_pos; + float dist2 = dot(to_light, to_light); + if (dist2 < light_radius * light_radius) { + float dist = sqrt(dist2); + float attenuation = 1.0 - (dist / light_radius); + float dot_term = max(dot(sector_normal, normalize(to_light)), 0.0); + color += light_color * dot_term * attenuation; + } + } + + return color; + } + )GLSL"; + + vert_main += "v_color *= ComputeLights(world_pos.xyz, world_normal);\n"; + + input_flags |= SIF_LIGHTING_DATA; + } + + if (flags & SRF_SKELETAL) // skeletal deform + { + vert_attributes += R"GLSL( + layout (location = 5) in ivec4 a_bone_ids; + layout (location = 6) in vec4 a_bone_weights; + )GLSL"; + + vert_uniforms += R"GLSL( + layout (std140) uniform Bones { + mat4 u_bone_matrices[MAX_BONES]; + }; + )GLSL"; + + vert_pos_calc = R"GLSL( + mat4 bone_transform = mat4(0.0); + for (int i = 0; i < 4; ++i) { + int bone_id = a_bone_ids[i]; + if (bone_id >= 0) { + bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i]; + } + } + + vec4 world_pos = bone_transform * vec4(a_pos, 1.0); + vec3 world_normal = normalize(mat3(bone_transform) * a_normal); + )GLSL"; + + input_flags |= SIF_SKELETAL_DATA; + } + else if (flags & SRF_DEFORM) // grid deform + { + vert_uniforms += R"GLSL( + uniform mediump sampler3D u_deform_tex; + uniform mat3 u_deform_info; + )GLSL"; + + vert_pos_calc = R"GLSL( + vec3 deform_pos = (a_pos - u_deform_info[0]) / (u_deform_info[1] - u_deform_info[0]); + vec3 pos = a_pos + texture(u_deform_tex, deform_pos).xyz * u_deform_info[2].x; + + vec4 world_pos = u_model * vec4(pos, 1.0); + vec3 world_normal = normalize(mat3(u_model) * a_normal); + )GLSL"; + + input_flags |= SIF_DEFORM_DATA; + } + + frag_main += "o_color.rgb *= v_color;"; + vert_main = vert_pos_calc + vert_main; + + std::string vertex_src = SHADER_HEADER + vert_attributes + vert_uniforms + vert_outs + vert_funcs + "\nvoid main() {\n" + vert_main + "\n}\n"; + std::string fragment_src = SHADER_HEADER + frag_ins + frag_uniforms + "\nlayout (location = 0) out vec4 o_color;\n" + frag_funcs + "\nvoid main() {\n" + frag_main + "\n}\n"; + + return std::make_unique(vertex_src.c_str(), fragment_src.c_str()); +} \ No newline at end of file diff --git a/src/gfx/surface_shader.hpp b/src/gfx/surface_shader.hpp new file mode 100644 index 0000000..77a5d2a --- /dev/null +++ b/src/gfx/surface_shader.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "shader.hpp" +#include "surface_render_flags.hpp" + +namespace gfx +{ + + +using SurfaceShaderInputFlags = uint8_t; + +enum SurfaceShaderInputFlag : SurfaceShaderInputFlags +{ + SIF_COLOR_TEXTURE = 1, + SIF_OBJECT_COLOR = 2, + SIF_LIGHTING_DATA = 4, + SIF_SKELETAL_DATA = 8, + SIF_DEFORM_DATA = 16, + +}; + +std::unique_ptr CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags); + +} \ No newline at end of file