From 54eac50c4de8f0a4c71325d7f5b99e2d27a2aa6a Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 4 Feb 2026 16:36:03 -0800 Subject: [PATCH] Add player character shadow casting in shadow pass --- include/rendering/character_renderer.hpp | 1 + src/rendering/character_renderer.cpp | 80 ++++++++++++++++++++++++ src/rendering/renderer.cpp | 24 ++++++- 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/include/rendering/character_renderer.hpp b/include/rendering/character_renderer.hpp index 1b2cdbc0..e3b1618c 100644 --- a/include/rendering/character_renderer.hpp +++ b/include/rendering/character_renderer.hpp @@ -57,6 +57,7 @@ public: void update(float deltaTime); void render(const Camera& camera, const glm::mat4& view, const glm::mat4& projection); + void renderShadow(GLuint shadowShaderProgram); void setInstancePosition(uint32_t instanceId, const glm::vec3& position); void setInstanceRotation(uint32_t instanceId, const glm::vec3& rotation); diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index aef01e26..45aebe56 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -1151,6 +1151,86 @@ void CharacterRenderer::render(const Camera& camera, const glm::mat4& view, cons glEnable(GL_CULL_FACE); // Restore culling for other renderers } +void CharacterRenderer::renderShadow(GLuint shadowShaderProgram) { + if (instances.empty() || shadowShaderProgram == 0) { + return; + } + + GLint modelLoc = glGetUniformLocation(shadowShaderProgram, "uModel"); + GLint useTexLoc = glGetUniformLocation(shadowShaderProgram, "uUseTexture"); + GLint texLoc = glGetUniformLocation(shadowShaderProgram, "uTexture"); + GLint alphaTestLoc = glGetUniformLocation(shadowShaderProgram, "uAlphaTest"); + GLint opacityLoc = glGetUniformLocation(shadowShaderProgram, "uShadowOpacity"); + GLint useBonesLoc = glGetUniformLocation(shadowShaderProgram, "uUseBones"); + if (modelLoc < 0) { + return; + } + + if (useTexLoc >= 0) glUniform1i(useTexLoc, 0); + if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, 0); + if (opacityLoc >= 0) glUniform1f(opacityLoc, 1.0f); + if (useBonesLoc >= 0) glUniform1i(useBonesLoc, 0); + if (texLoc >= 0) glUniform1i(texLoc, 0); + glActiveTexture(GL_TEXTURE0); + + for (const auto& [_, instance] : instances) { + auto modelIt = models.find(instance.modelId); + if (modelIt == models.end()) continue; + const auto& gpuModel = modelIt->second; + + glm::mat4 modelMat = instance.hasOverrideModelMatrix + ? instance.overrideModelMatrix + : getModelMatrix(instance); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, &modelMat[0][0]); + + bool useBones = !instance.boneMatrices.empty(); + if (useBonesLoc >= 0) glUniform1i(useBonesLoc, useBones ? 1 : 0); + if (useBones) { + int numBones = std::min(static_cast(instance.boneMatrices.size()), MAX_BONES); + GLint bonesLoc = glGetUniformLocation(shadowShaderProgram, "uBones[0]"); + if (bonesLoc >= 0) { + glUniformMatrix4fv(bonesLoc, numBones, GL_FALSE, &instance.boneMatrices[0][0][0]); + } + } + + glBindVertexArray(gpuModel.vao); + + if (!gpuModel.data.batches.empty()) { + for (const auto& batch : gpuModel.data.batches) { + GLuint texId = whiteTexture; + if (batch.textureIndex < gpuModel.data.textureLookup.size()) { + uint16_t lookupIdx = gpuModel.data.textureLookup[batch.textureIndex]; + if (lookupIdx < gpuModel.textureIds.size()) { + texId = gpuModel.textureIds[lookupIdx]; + } + } + + bool useTexture = (texId != 0 && texId != whiteTexture); + if (useTexLoc >= 0) glUniform1i(useTexLoc, useTexture ? 1 : 0); + if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, useTexture ? 1 : 0); + if (opacityLoc >= 0) glUniform1f(opacityLoc, 1.0f); + if (useTexture) { + glBindTexture(GL_TEXTURE_2D, texId); + } + + glDrawElements(GL_TRIANGLES, + batch.indexCount, + GL_UNSIGNED_SHORT, + (void*)(batch.indexStart * sizeof(uint16_t))); + } + } else { + if (useTexLoc >= 0) glUniform1i(useTexLoc, 0); + if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, 0); + glDrawElements(GL_TRIANGLES, + static_cast(gpuModel.data.indices.size()), + GL_UNSIGNED_SHORT, + 0); + } + } + + glBindVertexArray(0); +} + glm::mat4 CharacterRenderer::getModelMatrix(const CharacterInstance& instance) const { glm::mat4 model = glm::mat4(1.0f); diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 38991dd7..cd7bcc19 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -1524,11 +1524,24 @@ uint32_t Renderer::compileShadowShader() { uniform mat4 uLightSpaceMatrix; uniform mat4 uModel; layout(location = 0) in vec3 aPos; - layout(location = 4) in vec2 aTexCoord; + layout(location = 2) in vec2 aTexCoord; + layout(location = 3) in vec4 aBoneWeights; + layout(location = 4) in vec4 aBoneIndicesF; + uniform bool uUseBones; + uniform mat4 uBones[200]; out vec2 vTexCoord; void main() { + vec3 pos = aPos; + if (uUseBones) { + ivec4 bi = ivec4(aBoneIndicesF); + mat4 boneTransform = uBones[bi.x] * aBoneWeights.x + + uBones[bi.y] * aBoneWeights.y + + uBones[bi.z] * aBoneWeights.z + + uBones[bi.w] * aBoneWeights.w; + pos = vec3(boneTransform * vec4(aPos, 1.0)); + } vTexCoord = aTexCoord; - gl_Position = uLightSpaceMatrix * uModel * vec4(aPos, 1.0); + gl_Position = uLightSpaceMatrix * uModel * vec4(pos, 1.0); } )"; const char* fragSrc = R"( @@ -1690,9 +1703,11 @@ void Renderer::renderShadowPass() { GLint texLoc = glGetUniformLocation(shadowShaderProgram, "uTexture"); GLint alphaTestLoc = glGetUniformLocation(shadowShaderProgram, "uAlphaTest"); GLint opacityLoc = glGetUniformLocation(shadowShaderProgram, "uShadowOpacity"); + GLint useBonesLoc = glGetUniformLocation(shadowShaderProgram, "uUseBones"); if (useTexLoc >= 0) glUniform1i(useTexLoc, 0); if (alphaTestLoc >= 0) glUniform1i(alphaTestLoc, 0); if (opacityLoc >= 0) glUniform1f(opacityLoc, 1.0f); + if (useBonesLoc >= 0) glUniform1i(useBonesLoc, 0); if (texLoc >= 0) glUniform1i(texLoc, 0); // Render terrain into shadow map @@ -1732,6 +1747,11 @@ void Renderer::renderShadowPass() { m2Renderer->renderShadow(shadowShaderProgram); } + // Render characters into shadow map + if (characterRenderer) { + characterRenderer->renderShadow(shadowShaderProgram); + } + // Restore state glDisable(GL_POLYGON_OFFSET_FILL); glCullFace(GL_BACK);