Skip to content

Conversation

@btipling
Copy link
Owner

@btipling btipling commented Oct 5, 2024

image

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for shadow mapping in shaders and textures.
    • Introduced a new Framebuffer module for managing OpenGL framebuffers.
    • Enhanced vertex data structure to include texture coordinates for improved rendering.
  • Improvements

    • Updated camera functionality to allow dynamic ambient color adjustments.
    • Enhanced buffer management to support new types, including shadow buffers.
    • Improved rendering logic with new drawing functions and color adjustments.
  • Bug Fixes

    • Refined initialization processes for various structures to ensure stability.

These updates enhance the overall rendering capabilities and flexibility of the application.

@coderabbitai
Copy link

coderabbitai bot commented Oct 5, 2024

Walkthrough

The pull request introduces several changes across multiple files, enhancing functionality related to configuration, rendering, and shadow mapping. Key updates include the addition of new fields in the Config structure, enhancements to the ObjectParallelepiped for texture coordinates, a new function in the Camera struct for updating ambient light, and the introduction of a Framebuffer module for managing OpenGL framebuffers. Additional modifications involve shader enhancements for shadow mapping, texture handling improvements, and updates to rendering logic in rhi.zig.

Changes

File Path Change Summary
src/foundations/config/config.zig Added fields fb_width and fb_height to Config structure, initialized to 0.
src/foundations/object/ObjectParallelepiped.zig Enhanced vertex data structure in addSurface to include texture_coords for texture mapping.
src/foundations/physics/camera.zig Introduced pub fn updateGlobalAmbient to update global_ambient property and modified updateMVP to reflect this change.
src/foundations/rhi/Buffer.zig Added chapter8_shadows enum value and updated buffer_data union and related functions to support this new buffer type.
src/foundations/rhi/Framebuffer.zig Introduced a new Framebuffer module with various functions for managing OpenGL framebuffers and error handling.
src/foundations/rhi/Shader.zig Added shadowmaps field, updated fragment_shader_type enum, and refactored shader compilation logic for shadow mapping.
src/foundations/rhi/Texture.zig Enhanced Texture structure to support multiple uniforms and added setupShadow function for shadow texture management.
src/foundations/rhi/mesh.zig Added variables shadowmap_program and gen_shadowmap for shadow mapping functionality.
src/foundations/rhi/rhi.zig Updated rendering logic, added drawArraysMesh function, and modified existing draw functions to support shadow mapping.

Possibly related PRs

  • stub out plane math #3: Modifications to the PlaneDistance module include new parameters and functionality related to planes, potentially connecting to the new fields in the main PR.
  • Add a little sphere at the origin #8: Changes in mesh.zig introduce a new boolean field related to blending, which may relate to the configuration changes in the main PR's Config structure.
  • Reflection across a plane #14: Introduction of reflection capabilities in the Plane class could relate to the new configuration fields in the main PR, enhancing rendering and object management.
  • Support lighting models with textures #35: Changes in Shader.zig regarding fragment shaders and texture handling may connect with the new configuration fields in the main PR, both dealing with rendering enhancements.
  • better solar system #36: Modifications in SimpleSolarSystem.zig involve a shift to a more complex rendering system, potentially relating to the configuration changes in the main PR that enhance object management and rendering capabilities.

🐰 "In the meadow, changes bloom,
New fields and shaders chase the gloom.
Textures dance on shapes so bright,
With shadows cast, they take their flight.
Configs and buffers, all in sync,
A vibrant world, we pause to think!" 🌼


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post

🛑 Comments failed to post (86)
src/foundations/shaders/frag_bindless_header.glsl (1)

4-6: 💡 Codebase verification

Unused bindless samplers detected: Remove or utilize f_samp_1, f_samp_2, f_samp_3.

The newly added bindless sampler uniforms in frag_bindless_header.glsl (f_samp_1, f_samp_2, f_samp_3) are declared but not used within the shader. Unused uniforms can lead to compiler warnings and unnecessary resource allocation.

Consider the following actions:

  1. Remove Unused Samplers: If these samplers are not currently needed, remove their declarations to clean up the shader code.
  2. Utilize or Document: If these samplers are intended for future use, add comments explaining their purpose to improve code maintainability and clarity.
🔗 Analysis chain

Additional bindless samplers added: Verify usage and performance impact.

The addition of three new bindless sampler uniforms (f_samp_1, f_samp_2, f_samp_3) expands the shader's capability to work with multiple textures. This change is consistent with the existing pattern and naming convention.

Consider the following points:

  1. Ensure that these new samplers are actually used in the shader code to avoid unused uniform warnings.
  2. Verify that the corresponding textures are correctly bound in the application code.
  3. Be aware of the potential performance impact of using multiple bindless samplers, especially on lower-end hardware.
  4. Consider adding comments to explain the purpose of each sampler for better code maintainability.

To verify the usage of these new samplers, you can run the following script:

This will help ensure that the new samplers are being utilized in the shader code.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for the usage of the new sampler uniforms in shader files

# Test: Search for usage of new samplers in shader files
echo "Searching for usage of new samplers:"
rg --type glsl -e "f_samp_1|f_samp_2|f_samp_3" src/

Length of output: 165


Script:

#!/bin/bash
# Description: Check for the usage of the new sampler uniforms in shader files

# Test: Search for usage of new samplers in shader files with extensions .glsl, .frag, .vert
echo "Searching for usage of new samplers in shader files:"
rg --glob '*.glsl' --glob '*.frag' --glob '*.vert' -e "f_samp_1|f_samp_2|f_samp_3" src/

Length of output: 924

src/foundations/shaders/shadow_vert.glsl (1)

12-13: 🛠️ Refactor suggestion

Add comments explaining transformation steps and consider performance optimization.

The vertex position calculation looks correct, but there are a few points to consider:

  1. Add comments explaining the purpose of each transformation step. This will make the code more understandable and maintainable.

  2. The current order of matrix multiplications (f_shadow_m * f_transform * f_xup_shadow * vertex_position) might not be optimal for performance. Consider if any of these transformations can be combined or pre-computed on the CPU to reduce per-vertex calculations.

  3. Verify if the order of transformations is intentional and produces the desired visual result.

Example of how you might add comments:

// Apply world transformation
vec4 world_pos = f_transform * vec4(f_position.xyz, 1.0);
// Apply additional shadow-specific transformation
vec4 shadow_space_pos = f_xup_shadow * world_pos;
// Project into shadow map space
gl_Position = f_shadow_m * shadow_space_pos;

This approach also separates the transformations, which might be more efficient depending on your specific use case.

src/foundations/scenes/cgpoc/chapter8/shadows/shadow_vert.glsl (1)

5-15: 🛠️ Refactor suggestion

⚠️ Potential issue

Address undeclared variables and consider additional outputs.

The main function's logic for transforming the vertex position is correct. However, there are a few issues and potential improvements:

  1. Undeclared variables: The shader uses f_t_column0, f_t_column1, f_t_column2, f_t_column3, and f_position which are not declared. These should be defined as either uniform inputs or vertex attributes.

  2. Missing outputs: Depending on your shadow mapping implementation, you might need to output additional data such as the position in light space. Consider adding:

out vec4 v_position_light_space;

And in the main function:

v_position_light_space = f_pos;
  1. Precision qualifier: Consider adding a precision qualifier at the beginning of the shader for better performance on some hardware:
precision highp float;

Please address these issues to ensure the shader compiles and functions as intended.

src/foundations/scenes/scenes.zig (1)

83-83: 💡 Codebase verification

Issues Found: Remaining References to color Module

The recent change from importing color to graphics is incomplete. Numerous files still reference the old color module, which may lead to inconsistencies or bugs.

Key points to address:

  • Replace all @import("../color/color.zig") statements with the graphics module.
  • Update all usages of .color to utilize the corresponding graphics functionalities.

Please review and complete the refactor to ensure all references to color are fully migrated to graphics.

🔗 Analysis chain

LGTM! Verify impact on codebase.

The change from importing color to graphics suggests a refactoring or expansion of graphical capabilities. This update seems intentional and likely beneficial for the project.

To ensure this change doesn't introduce any issues, please verify:

  1. The graphics module includes all functionality previously provided by the color module.
  2. All references to the color module throughout the codebase have been updated to use graphics.

Run the following script to check for any remaining references to the old color module:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for remaining references to the old color module

# Test: Search for references to the old color module
rg --type zig 'color\.color'

# Note: If this returns results, those files may need updating

Length of output: 30


Script:

#!/bin/bash
# Description: Thoroughly check for any remaining references to the old color module

# Test: Search for import statements of the color module
rg --type zig '@import\("color"\)'

# Test: Search for any usage of the color namespace
rg --type zig '\bcolor\b'

Length of output: 19203

src/foundations/ui/ui_glfw.zig (1)

50-54: 💡 Codebase verification

🛠️ Refactor suggestion

Add error handling for glfwGetFramebufferSize.

The glfwGetFramebufferSize function call does not currently handle potential errors. To enhance robustness, consider adding error handling as shown below:

     var width: c_int = 0;
     var height: c_int = 0;
-    c.glfwGetFramebufferSize(win, &width, &height);
+    if (c.glfwGetFramebufferSize(win, &width, &height) == c.GLFW_FALSE) {
+        return GLFWError.Fatal;
+    }
     cfg.fb_width = @intCast(width);
     cfg.fb_height = @intCast(height);

This modification ensures that any failure in retrieving the framebuffer size is appropriately handled, preventing potential issues during runtime.

🔗 Analysis chain

Consider adding error handling and verifying GLFW initialization.

While the changes are correct, consider the following improvements:

  1. Add error handling for the glfwGetFramebufferSize call.
  2. Verify that GLFW and OpenGL initialization is complete before calling glfwGetFramebufferSize.

Here's a suggested implementation:

+    if (c.glfwGetCurrentContext() == null) {
+        return GLFWError.Fatal;
+    }
     var width: c_int = 0;
     var height: c_int = 0;
-    c.glfwGetFramebufferSize(win, &width, &height);
+    if (c.glfwGetFramebufferSize(win, &width, &height) == c.GLFW_FALSE) {
+        return GLFWError.Fatal;
+    }
     cfg.fb_width = @intCast(width);
     cfg.fb_height = @intCast(height);

This change ensures that GLFW is properly initialized and handles potential errors when retrieving the framebuffer size.

To verify the GLFW initialization and usage across the codebase, you can run the following script:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify GLFW initialization and error handling

# Test: Check if glfwInit is called before any other GLFW function
rg --type zig 'glfwInit.*\n.*glfw' -A 10

# Test: Check for error handling after GLFW function calls
rg --type zig 'glfw\w+\(.*\)' -A 3

Length of output: 5696


Script:

#!/bin/bash
# Description: Verify GLFW initialization and error handling with corrected regex

# Test: Check if glfwInit is called before any other GLFW function
rg --multiline --type zig 'glfwInit.*\n.*glfw' -A 10

# Test: Check for error handling after GLFW function calls
rg --type zig 'glfw\w+\(.*\)' -A 3

Length of output: 6421

src/foundations/scenes/graphics/polygon_offset/po_vertex.glsl (2)

5-13: ⚠️ Potential issue

Variables used without declarations will cause compilation errors

The variables f_t_column0, f_t_column1, f_t_column2, f_t_column3, f_xup, f_position, f_mvp, f_normals, and fo_normals are used but not declared in this shader code. Since this is a new file and the entire code is provided, these variables need to be declared to avoid compilation errors.

To fix this, you should declare these variables appropriately. For example:

// Uniforms
uniform vec4 f_t_column0;
uniform vec4 f_t_column1;
uniform vec4 f_t_column2;
uniform vec4 f_t_column3;
uniform mat4 f_xup;
uniform mat4 f_mvp;

// Inputs
in vec3 f_position;
in vec3 f_normals;

// Outputs
out vec3 fo_normals;

11-12: ⚠️ Potential issue

Correct the calculation of the normal matrix and vertex position

The calculations in lines 11 and 12 may contain errors due to incorrect matrix multiplications and type casting.

  • Line 11: The normal matrix should typically be calculated from the model matrix m_matrix without multiplying by f_xup. Multiplying two matrices and then casting to mat3 may result in incorrect values.

  • Line 12: Multiplying m_matrix and f_xup together before applying it to the vertex position may not be the intended operation and could lead to incorrect vertex transformations.

Consider revising the calculations as follows:

-mat3 f_norm_matrix = transpose(inverse(mat3(m_matrix * f_xup)));
+mat3 f_norm_matrix = transpose(inverse(mat3(m_matrix)));

-vec4 f_main_pos = m_matrix * f_xup * vec4(f_position.xyz, 1.0);
+vec4 f_main_pos = m_matrix * vec4(f_position.xyz, 1.0);

Committable suggestion was skipped due to low confidence.

src/foundations/shaders/frag_phong_lighting.glsl (1)

10-25: 🛠️ Refactor suggestion

Refactor the do-while loop to a for loop for clarity.

Since the number of iterations is known, using a for loop enhances readability and is more idiomatic.

Apply this diff to refactor the loop:

-    uint i = 0;
-    do {
+    for (uint i = 0; i < num_lights; ++i) {
         Light f_light = f_lights[i];
         vec3 f_L = normalize(f_light.direction.xyz);
         vec3 f_R = reflect(-f_L, f_N);

         float cosTheta = dot(f_L, f_N);
         float cosPhi = dot(f_V, f_R);

         vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
         vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
         vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess);
        
         rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
-        i += 1;
-    } while (i < num_lights);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    for (uint i = 0; i < num_lights; ++i) {
        Light f_light = f_lights[i];
        vec3 f_L = normalize(f_light.direction.xyz);
        vec3 f_R = reflect(-f_L, f_N);

        float cosTheta = dot(f_L, f_N);
        float cosPhi = dot(f_V, f_R);

        vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
        vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
        vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess);
    
        rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
    }
src/foundations/shaders/frag_blinn_phong_lighting.glsl (2)

23-23: ⚠️ Potential issue

Ensure the alpha component remains consistent in rv

When accumulating color in rv, the alpha component may inadvertently change due to adding a vec4 with an alpha of 0.0. To maintain the alpha value of 1.0, consider updating the accumulation.

Apply this diff to preserve the alpha component:

-        rv += vec4(f_ambient + f_diffuse + f_specular, 0.0);
+        rv.rgb += f_ambient + f_diffuse + f_specular;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        rv.rgb += f_ambient + f_diffuse + f_specular;

11-25: 🛠️ Refactor suggestion

Consider replacing the do-while loop with a for loop for clarity

Using a for loop can improve readability and is more conventional in GLSL shaders.

Apply this diff to refactor the loop:

-    uint i = 0;
-    do {
+    for (uint i = 0u; i < num_lights; i++) {
         Light f_light = f_lights[i];
         vec3 f_L = normalize(f_light.direction.xyz);
-        vec3 f_H = normalize(f_L + f_V).xyz;
+        vec3 f_H = normalize(f_L + f_V);

         float cosTheta = dot(f_L, f_N);
         float cosPhi = dot(f_H, f_N);

-        vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
-        vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
-        vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess * 4.0);
+        vec3 f_ambient = (f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient);
+        vec3 f_diffuse = f_light.diffuse * f_mat.diffuse * max(cosTheta, 0.0);
+        vec3 f_specular = f_mat.specular * f_light.specular * pow(max(cosPhi, 0.0), f_mat.shininess * 4.0);

         rv += vec4(f_ambient + f_diffuse + f_specular, 0.0);
-        i += 1;
-    } while (i < num_lights);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    for (uint i = 0u; i < num_lights; i++) {
        Light f_light = f_lights[i];
        vec3 f_L = normalize(f_light.direction.xyz);
        vec3 f_H = normalize(f_L + f_V);

        float cosTheta = dot(f_L, f_N);
        float cosPhi = dot(f_H, f_N);

        vec3 f_ambient = (f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient);
        vec3 f_diffuse = f_light.diffuse * f_mat.diffuse * max(cosTheta, 0.0);
        vec3 f_specular = f_mat.specular * f_light.specular * pow(max(cosPhi, 0.0), f_mat.shininess * 4.0);

        rv += vec4(f_ambient + f_diffuse + f_specular, 0.0);
    }
src/foundations/scenes/cgpoc/chapter7/dolphin/blinn_phong_frag.glsl (1)

11-25: ⚠️ Potential issue

Fix potential off-by-one error when no lights are present

The use of a do...while loop causes the loop body to execute at least once, even when num_lights is zero. This could lead to unintended behavior when there are no lights to process. Consider using a while loop or a for loop to ensure the loop doesn't execute when num_lights is zero.

Apply this diff to fix the issue:

     uint i = 0;
-    do {
+    while (i < num_lights) {
         Light f_light = f_lights[i];
         vec3 f_L = normalize(f_light.direction.xyz);
-        vec3 f_H = normalize(f_L + f_V).xyz;
+        vec3 f_H = normalize(f_L + f_V);
 
         float cosTheta = dot(f_L, f_N);
         float cosPhi = dot(f_H, f_N);
 
         vec3 f_ambient = (f_ambient_light + f_light.ambient).xyz;
         vec3 f_diffuse = f_light.diffuse.xyz * max(cosTheta, 0.0);
         vec3 f_specular = f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat_shininess * 4.0);
 
         rv = rv + f_tx_color * vec4((f_ambient + f_diffuse), 0.0) + vec4(f_specular, 0);
         i += 1;
-    } while (i < num_lights);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    uint i = 0;
    while (i < num_lights) {
        Light f_light = f_lights[i];
        vec3 f_L = normalize(f_light.direction.xyz);
        vec3 f_H = normalize(f_L + f_V);

        float cosTheta = dot(f_L, f_N);
        float cosPhi = dot(f_H, f_N);

        vec3 f_ambient = (f_ambient_light + f_light.ambient).xyz;
        vec3 f_diffuse = f_light.diffuse.xyz * max(cosTheta, 0.0);
        vec3 f_specular = f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat_shininess * 4.0);

        rv = rv + f_tx_color * vec4((f_ambient + f_diffuse), 0.0) + vec4(f_specular, 0);
        i += 1;
    }
src/foundations/scenes/cgpoc/chapter4/simple_solar_system/blinn_phong_texture_frag.glsl (4)

24-24: ⚠️ Potential issue

Verify the handling of the alpha component in the color accumulation.

In the expression:

rv = rv + f_tx_color * vec4((f_ambient + f_diffuse), 0.0) + vec4(f_specular, 0);

The alpha component is set to 0.0 in both vec4 constructors. This may result in the accumulated color rv having an alpha value that decreases with each iteration, potentially leading to unexpected transparency effects.

Consider setting the alpha component to 1.0 or maintaining the original alpha value:

- rv = rv + f_tx_color * vec4((f_ambient + f_diffuse), 0.0) + vec4(f_specular, 0);
+ rv = rv + f_tx_color * vec4((f_ambient + f_diffuse), f_tx_color.a) + vec4(f_specular, f_tx_color.a);

17-18: ⚠️ Potential issue

Ensure correct calculation of the half-vector in Blinn-Phong shading.

The calculation of the half-vector f_H and subsequently cosPhi might not be accurate if f_L and f_V are not normalized properly. Ensure that both vectors are normalized before adding to compute the half-vector.

Apply this diff to normalize f_L and f_V before computing f_H:

          vec3 f_L = normalize(f_light.direction.xyz);
          vec3 f_V_normalized = normalize(f_V);
-         vec3 f_H = normalize(f_L + f_V).xyz;
+         vec3 f_H = normalize(f_L + f_V_normalized);

Committable suggestion was skipped due to low confidence.


20-22: 🛠️ Refactor suggestion

Adjust the specular exponent for consistent shininess control.

The specular component uses f_mat_shininess * 4.0. Typically, the exponent in the Blinn-Phong model is directly controlled by the material shininess parameter. Multiplying by 4.0 might not be necessary and could make the shininess parameter less intuitive.

Consider using f_mat_shininess directly:

- vec3 f_specular = f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat_shininess * 4.0);
+ vec3 f_specular = f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat_shininess);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        vec3 f_ambient = (f_ambient_light + f_light.ambient).xyz;
        vec3 f_diffuse = f_light.diffuse.xyz * max(cosTheta, 0.0);
        vec3 f_specular = f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat_shininess);

3-28: 🛠️ Refactor suggestion

Consider simplifying the loop structure for clarity.

The current implementation uses a do...while loop starting from i = 0, which can be less intuitive. Switching to a for loop can improve readability and is more conventional for iterating over arrays.

Apply this diff to refactor the loop:

-    uint i = 0;
-    do {
-        Light f_light = f_lights[i];
-        // ... rest of the loop body ...
-        i += 1;
-    } while (i < num_lights);
+    for (uint i = 0; i < num_lights; ++i) {
+        Light f_light = f_lights[i];
+        // ... rest of the loop body ...
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

vec4 f_blinn_phong_lighting_texture(vec4 f_tx_color, Light f_lights[10], uint num_lights, vec4 f_ambient_light, float f_mat_shininess) {
    num_lights = min(num_lights, 10u);

    vec3 f_V = normalize(f_camera_pos.xyz - fo_vert);
    vec3 f_N = normalize(fo_normals);

    vec4 rv = vec4(0.0, 0.0, 0.0, 1.0);
    
    for (uint i = 0; i < num_lights; ++i) {
        Light f_light = f_lights[i];
        vec3 f_L = normalize(f_light.direction.xyz);
        vec3 f_H = normalize(f_L + f_V).xyz;

        float cosTheta = dot(f_L, f_N);
        float cosPhi = dot(f_H, f_N);

        vec3 f_ambient = (f_ambient_light + f_light.ambient).xyz;
        vec3 f_diffuse = f_light.diffuse.xyz * max(cosTheta, 0.0);
        vec3 f_specular = f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat_shininess * 4.0);

        rv = rv + f_tx_color * vec4((f_ambient + f_diffuse), 0.0) + vec4(f_specular, 0);
    }
    return rv;
}
src/foundations/scenes/cgpoc/chapter7/lighting/blinn_phong_frag.glsl (1)

13-28: ⚠️ Potential issue

Potential infinite loop when num_lights is zero

The use of a do-while loop may lead to unintended behavior when num_lights is zero. Since a do-while loop executes at least once, the loop body will run even when there are no lights to process, potentially accessing uninitialized elements in f_lights.

Consider changing the loop to a while loop or a for loop to prevent the loop from executing when num_lights is zero. Here's a suggested fix:

     uint i = 0;
-    do {
+    while (i < num_lights) {
         Light f_light = f_lights[i];
         vec3 f_L = normalize(f_light.direction.xyz);
-        vec3 f_H = normalize(f_L + f_V).xyz;
+        vec3 f_H = normalize(f_L + f_V);

         float cosTheta = dot(f_L, f_N);
         float cosPhi = dot(f_H, f_N);

         vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
         vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
         vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess * 4.0);

         rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
         i += 1;
-    } while (i < num_lights);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    uint i = 0;
    while (i < num_lights) {
        Light f_light = f_lights[i];
        vec3 f_L = normalize(f_light.direction.xyz);
        vec3 f_H = normalize(f_L + f_V);

        float cosTheta = dot(f_L, f_N);
        float cosPhi = dot(f_H, f_N);

        vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
        vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
        vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess * 4.0);

        rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
        i += 1;
    }
src/foundations/scenes/cgpoc/chapter7/lighting/phong_frag.glsl (1)

15-29: ⚠️ Potential issue

Prevent potential out-of-bounds access when num_lights is zero

The current do-while loop executes at least once, even if num_lights is zero. This could lead to accessing uninitialized elements in the f_lights array, causing an out-of-bounds error. To prevent this, consider changing the loop to a while loop that checks the condition before entering the loop.

Apply this diff to modify the loop structure:

     uint i = 0;
-    do {
+    while (i < num_lights) {
         Light f_light = f_lights[i];
         vec3 f_L = normalize(f_light.direction.xyz);
         vec3 f_R = reflect(-f_L, f_N);

         float cosTheta = dot(f_L, f_N);
         float cosPhi = dot(f_V, f_R);

         vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
         vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
         vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess);
     
         rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
         i += 1;
-    } while (i < num_lights);
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    uint i = 0;
    while (i < num_lights) {
        Light f_light = f_lights[i];
        vec3 f_L = normalize(f_light.direction.xyz);
        vec3 f_R = reflect(-f_L, f_N);

        float cosTheta = dot(f_L, f_N);
        float cosPhi = dot(f_V, f_R);

        vec3 f_ambient = ((f_ambient_light * f_mat.ambient) + (f_light.ambient * f_mat.ambient)).xyz;
        vec3 f_diffuse = f_light.diffuse.xyz * f_mat.diffuse.xyz * max(cosTheta, 0.0);
        vec3 f_specular = f_mat.specular.xyz * f_light.specular.xyz * pow(max(cosPhi, 0.0), f_mat.shininess);
    
        rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
        i += 1;
    }
src/foundations/scenes/cgpoc/chapter7/dolphin/blinn_phong_frag_matte.glsl (1)

28-35: 🛠️ Refactor suggestion

Consider using built-in PCF for shadow sampling.

Manually sampling the shadow map multiple times and averaging can be replaced with built-in percentage-closer filtering (PCF) techniques if supported on the target platform. Utilizing hardware-accelerated shadow sampling functions can improve performance and shadow quality.

src/foundations/rhi/Framebuffer.zig (5)

63-66: 🛠️ Refactor suggestion

Consider separating binding and clearing operations in bind

In the bind function, the framebuffer is both bound and cleared. It might be preferable to separate these actions into distinct functions for greater flexibility and control over when clearing occurs.

Consider refactoring as follows:

pub fn bind(self: FrameBuffer) void {
    c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.name);
}

pub fn clear(self: FrameBuffer) void {
    c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT);
}

12-12: 🛠️ Refactor suggestion

Remove unnecessary @ptrCast in glCreateFramebuffers call

The @ptrCast function may be unnecessary if &name is already the correct pointer type expected by glCreateFramebuffers. Removing it can simplify the code.

Apply this diff:

-    c.glCreateFramebuffers(1, @ptrCast(&name));
+    c.glCreateFramebuffers(1, &name);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    c.glCreateFramebuffers(1, &name);

68-70: ⚠️ Potential issue

Remove unused parameter in unbind function

The unbind function takes a FrameBuffer parameter but does not use it. Consider removing the parameter for clarity.

Apply this diff:

-pub fn unbind(_: FrameBuffer) void {
+pub fn unbind() void {
    c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

pub fn unbind() void {
    c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
}

11-11: ⚠️ Potential issue

Use c.GLuint for the name variable in init

In the init function, name is declared as u32, but elsewhere name is of type c.GLuint. For consistency and to avoid potential type issues, declare name as c.GLuint.

Apply this diff to correct the type:

-    var name: u32 = undefined;
+    var name: c.GLuint = undefined;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    var name: c.GLuint = undefined;

1-4: ⚠️ Potential issue

Define FrameBuffer as a struct to encapsulate the name field

The name field is declared at the top level without being enclosed in a struct. Additionally, const FrameBuffer = @This(); is misplaced without a struct definition. To properly encapsulate the name field and associated methods, FrameBuffer should be defined as a struct.

Apply this diff to define the FrameBuffer struct:

+pub const FrameBuffer = struct {
+    name: c.GLuint = 0,
+
+    // Existing methods will be inside this struct
+};
-
-name: c.GLuint = 0,
-
-const FrameBuffer = @This();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

pub const FrameBuffer = struct {
    name: c.GLuint = 0,

    // Existing methods will be inside this struct
};
src/foundations/scenes/cgpoc/chapter8/shadows/blinn_phong_vert.glsl (1)

54-66: 🛠️ Refactor suggestion

Refactor repeated code using loops and arrays

The code from lines 54 to 66 repeats similar operations for both lights and their six view matrices, resulting in redundant code. Refactoring this section using loops and arrays will enhance readability and maintainability.

Here's how you can refactor the code:

  1. Modify the output variable declarations to use arrays:

    out vec4 fo_light_1_views[6];
    out vec4 fo_light_2_views[6];
  2. Replace the repeated code with loops inside the main function:

    // For light 1
    for (int i = 0; i < 6; i++) {
        fo_light_1_views[i] = f_scene_data.light_1_views[i] * f_main_pos;
    }
    
    // For light 2
    for (int i = 0; i < 6; i++) {
        fo_light_2_views[i] = f_scene_data.light_2_views[i] * f_main_pos;
    }

This refactoring reduces code duplication and makes it easier to manage and scale if more lights or views are added in the future.

src/foundations/scenes/cgpoc/chapter8/shadows/gouraud_frag.glsl (4)

30-45: ⚠️ Potential issue

Avoid exact floating-point comparisons when checking not_in_shadow

Comparing floating-point values using == can lead to unreliable results due to precision errors inherent in floating-point arithmetic. Instead of checking for exact equality with 1.0, use a threshold to determine if the value is close enough to 1.0.

Modify your comparisons as follows:

-if (not_in_shadow == 1.0) {
+if (abs(not_in_shadow - 1.0) < 0.0001) {

Also applies to: 47-62


27-27: 🛠️ Refactor suggestion

Adjust the shadow bias to prevent shadow artifacts

Using a bias of 0.0 may cause shadow artifacts such as shadow acne due to precision limitations in shadow mapping. It's a common practice to use a small positive bias to offset the depth values and prevent self-shadowing artifacts.

Consider setting the bias to a small positive value:

-float bias = 0.0;
+float bias = 0.005;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        float bias = 0.005;

74-79: ⚠️ Potential issue

Potential logic error: fo_frag_color may not be correctly updated with light contributions

Currently, after setting fo_frag_color = fo_all_ambient;, the code checks if both lights are not in shadow but does not modify fo_frag_color accordingly. The line fo_frag_color = fo_frag_color; inside the nested if statements has no effect, which means the fragment color remains as ambient light even when lights are contributing.

You might have intended to add the light contributions to fo_frag_color when the lights are not in shadow. Here's how you can adjust the code:

 fo_frag_color = fo_all_ambient;
 if (f_l1_not_in_shadow == 1) {
   if (f_l2_not_in_shadow == 1) {
-    fo_frag_color = fo_frag_color;
+    fo_frag_color += fo_l1_all_l2_all; // Assuming fo_l1_all_l2_all contains combined light contributions
   } else {
+    fo_frag_color += fo_l1_ambient_l2_all; // Light 1 contributes
   }
 } else if (f_l2_not_in_shadow == 1) {
+  fo_frag_color += fo_l1_all_l2_ambient; // Light 2 contributes
 }

Ensure that you have the appropriate light contribution variables (fo_l1_all_l2_all, fo_l1_ambient_l2_all, etc.) calculated earlier in your shader.

Committable suggestion was skipped due to low confidence.


26-72: 🛠️ Refactor suggestion

Consider refactoring to reduce code duplication using arrays and loops

Your current implementation contains repetitive code blocks for handling shadow checks for each light source and their associated shadow textures. By utilizing arrays for shadow textures and light data, along with loops, you can make the code more concise, maintainable, and scalable. This approach aligns with previous practices in the codebase, where arrays are used to manage multiple lights efficiently.

As per the learnings from previous implementations, consider declaring arrays for your shadow textures and light positions:

// Declare arrays for shadow textures
uniform sampler2DShadow f_shadow_textures[12];

// Declare arrays for light directions
in vec4 fo_light_1[6];
in vec4 fo_light_2[6];

Then, modify your loop to iterate over the lights and their corresponding shadow textures:

void main()
{
    uint f_not_in_shadow[2] = uint[](0, 0);
    uint num_lights = 2;
    uint num_textures_per_light = 6;
    
    for (uint i = 0; i < num_lights; ++i) {
        float bias = 0.005; // Adjusted bias value
        float not_in_shadow = 1.0;

        for (uint j = 0; j < num_textures_per_light && not_in_shadow == 1.0; ++j) {
            // Compute the index for shadow textures
            uint texture_index = i * num_textures_per_light + j;
            // Access the correct light direction
            vec4 light_dir = (i == 0) ? fo_light_1[j] : fo_light_2[j];
            // Sample the shadow texture
            not_in_shadow = textureProj(f_shadow_textures[texture_index], light_dir, bias);
        }

        if (abs(not_in_shadow - 1.0) < 0.0001) {
            f_not_in_shadow[i] = 1;
        }
    }

    // Rest of your code...
}
src/foundations/rhi/Buffer.zig (1)

36-36: 🛠️ Refactor suggestion

Refactor data size calculations to avoid duplication

The calculation of data_size for chapter8_shadows is repeated in both the init and update functions. Consider creating a helper function or constant to eliminate code duplication and ensure consistency.

Also applies to: 82-82

src/foundations/scenes/cgpoc/chapter8/shadows/gouraud_vert.glsl (3)

85-97: 🛠️ Refactor suggestion

Consider refactoring repeated assignments into loops

The assignments from lines 85 to 97 for fo_light_1_* and fo_light_2_* output variables involve repetitive code. Refactoring these into loops can reduce code duplication and enhance maintainability.

Here's how you might refactor the code:

// For Light 1
for (int idx = 0; idx < 6; idx++) {
    fo_light_1_views[idx] = f_scene_data.light_1_views[idx] * f_P;
}

// For Light 2
for (int idx = 0; idx < 6; idx++) {
    fo_light_2_views[idx] = f_scene_data.light_2_views[idx] * f_P;
}

You'll need to declare fo_light_1_views and fo_light_2_views as arrays:

out vec4 fo_light_1_views[6];
out vec4 fo_light_2_views[6];

Note: Ensure that your GLSL version supports array outputs and dynamic indexing. Some older GLSL versions or certain hardware might have limitations.


40-83: ⚠️ Potential issue

Ensure all variables and uniforms are properly declared

The shader code references several variables and uniforms that are not declared within this file:

  • f_xup (lines 40, 46, 50)
  • f_t_column0, f_t_column1, f_t_column2, f_t_column3 (lines 41-44)
  • f_position (line 50)
  • f_normals (line 51)
  • f_global_ambient (lines 60, 72)
  • f_camera_pos (line 69)
  • f_mvp (line 76)
  • f_texture_coords (line 77)
  • f_tc (line 77)
  • fo_normals (line 79)
  • f_materials (line 48)
  • f_lights (line 62)
  • f_frag_color (lines 56, 80)

Please ensure that all these variables and uniforms are properly declared in the shader or included from other shader files. Missing declarations can lead to compilation errors.

If they are meant to be:

  • Uniforms: Declare them with the uniform keyword.
  • Inputs: Declare them with the in keyword (for vertex attributes or varying variables from previous shader stages).
  • Outputs: Declare them with the out keyword (for passing to the next shader stage).

For example:

uniform mat4 f_mvp;
in vec3 f_position;
in vec3 f_normals;
// ... and so on

34-35: ⚠️ Potential issue

Correct f_light_1_pos assignment to use light_1_position

Currently, both f_light_1_pos and f_light_2_pos are assigned the position of light_2. This appears to be an error. f_light_1_pos should use f_scene_data.light_1_position.xyz to correctly reference the first light's position.

Apply this diff to fix the assignment:

-    vec3 f_light_1_pos = f_scene_data.light_2_position.xyz;
+    vec3 f_light_1_pos = f_scene_data.light_1_position.xyz;
     vec3 f_light_2_pos = f_scene_data.light_2_position.xyz;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    vec3 f_light_1_pos = f_scene_data.light_1_position.xyz;
    vec3 f_light_2_pos = f_scene_data.light_2_position.xyz;
src/foundations/scenes/cgpoc/chapter6/shuttle/Shuttle.zig (1)

68-73: ⚠️ Potential issue

Reset self.shuttle after deletion to prevent dangling references

After deleting the shuttle object using rhi.deleteObjects, it's advisable to reset self.shuttle to its default state. This prevents any accidental use of a deleted object, which could lead to undefined behavior.

Apply the following diff to reset self.shuttle:

pub fn deleteShuttle(self: *Shuttle) void {
    const objects: [1]object.object = .{
        self.shuttle,
    };
    rhi.deleteObjects(objects[0..]);
+   self.shuttle = .{ .norender = .{} };
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

pub fn deleteShuttle(self: *Shuttle) void {
    const objects: [1]object.object = .{
        self.shuttle,
    };
    rhi.deleteObjects(objects[0..]);
    self.shuttle = .{ .norender = .{} };
}
src/foundations/rhi/Texture.zig (8)

4-5: 🛠️ Refactor suggestion

Consider using a dynamic array for uniforms to improve flexibility

Currently, uniforms is defined as a fixed-size array of 100 elements:

uniforms: [100]Uniform = undefined,
num_uniforms: usize = 0,

Using a fixed-size array may limit the number of uniforms and could lead to issues if more uniforms are needed in the future. Consider using a dynamic array such as std.ArrayList(Uniform) to allow for a variable number of uniforms, enhancing scalability and flexibility.


47-47: ⚠️ Potential issue

Correct the borderColor values to match the intended red color

At line 47, the borderColor is set to white, but the comment indicates it should be red:

const borderColor: [4]c.GLfloat = .{ 1.0, 1.0, 1.0, 1.0 }; // Red border color

To set the border color to red, update the values accordingly:

Apply this diff to fix the color values:

-const borderColor: [4]c.GLfloat = .{ 1.0, 1.0, 1.0, 1.0 }; // Red border color
+const borderColor: [4]c.GLfloat = .{ 1.0, 0.0, 0.0, 1.0 }; // Red border color

55-56: ⚠️ Potential issue

Use num_uniforms as the index when adding a new uniform

In the setupShadow function, you're adding a uniform at index 0 and then incrementing num_uniforms:

self.uniforms[0] = Uniform.init(program, uniform_name);
self.num_uniforms += 1;

If num_uniforms is not zero, this will overwrite existing uniforms. To avoid this, use num_uniforms as the index:

Apply this diff to prevent overwriting existing uniforms:

-self.uniforms[0] = Uniform.init(program, uniform_name);
+self.uniforms[self.num_uniforms] = Uniform.init(program, uniform_name);
 self.num_uniforms += 1;

121-122: ⚠️ Potential issue

Use num_uniforms as the index when adding a new uniform

Similar to the previous issue, in the setup function, ensure you're adding the new uniform at the correct index:

self.uniforms[0] = Uniform.init(program, uniform_name);
self.num_uniforms += 1;

Apply this diff to correct the index:

-self.uniforms[0] = Uniform.init(program, uniform_name);
+self.uniforms[self.num_uniforms] = Uniform.init(program, uniform_name);
 self.num_uniforms += 1;

145-155: ⚠️ Potential issue

Add bounds checking in removeUniform to prevent potential issues

In the removeUniform function, you're creating a new uniforms array and copying over existing uniforms:

var uniforms: [100]Uniform = undefined;

Ensure that when copying uniforms, you handle the array bounds properly to prevent uninitialized elements or out-of-bounds access. Also, consider zeroing out the unused elements if necessary.


158-159: ⚠️ Potential issue

Check for array bounds when adding a uniform in addUniform

In the addUniform function, there's no check to ensure that num_uniforms does not exceed the array size:

self.uniforms[self.num_uniforms] = Uniform.init(program, uniform_name);
self.num_uniforms += 1;

This could lead to a buffer overflow if more than 100 uniforms are added.

Add a bounds check before adding a new uniform:

+if (self.num_uniforms >= self.uniforms.len) {
+    // Handle the error appropriately, e.g., return an error or resize the array if using a dynamic array
+    return;
+}
 self.uniforms[self.num_uniforms] = Uniform.init(program, uniform_name);
 self.num_uniforms += 1;

Alternatively, switch to a dynamic array as suggested earlier.


39-71: ⚠️ Potential issue

Add error handling for OpenGL function calls in setupShadow

The setupShadow function currently lacks error handling after OpenGL calls. If any of these calls fail, it could lead to unintended behavior.

Consider checking for OpenGL errors after critical function calls. For example:

c.glTextureParameteri(name, c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR);
// Check for errors
var error = c.glGetError();
if (error != c.GL_NO_ERROR) {
    // Handle the error appropriately
}

Implementing error checks will improve the robustness of your code.


167-169: 💡 Codebase verification

Potential Out-of-Bounds Access with num_uniforms

The increments of num_uniforms in src/foundations/rhi/Texture.zig are not consistently followed by bounds checks against uniforms.len, which may lead to out-of-bounds access in the bind method.

  • src/foundations/rhi/Texture.zig: self.num_uniforms += 1; (No subsequent bounds check)
🔗 Analysis chain

Ensure num_uniforms does not exceed array bounds in the bind method

In the bind method, you iterate over uniforms using num_uniforms:

for (0..self.num_uniforms) |i| {
    self.uniforms[i].setUniformHandleui64ARB(self.handle);
}

Verify that num_uniforms is always less than or equal to uniforms.len to prevent out-of-bounds access.

Run the following script to check for any increments to num_uniforms without proper bounds checks:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for increments of `num_uniforms` without bounds checks

# Search for increments of `num_uniforms` in the codebase
rg --type zig -A 2 'self\.num_uniforms\s*\+\='

Length of output: 163


Script:

#!/bin/bash
# Description: Check for increments of `num_uniforms` without bounds checks

# Search for increments of `num_uniforms` in the codebase
rg --type zig -A 2 'self\.num_uniforms\s*\+='

Length of output: 496


Script:

#!/bin/bash
# Description: Verify that increments of `num_uniforms` are followed by bounds checks

# Search for increments of `num_uniforms` and check for subsequent bounds checks
rg --type zig 'self\.num_uniforms\s*\+=' src/foundations/rhi/Texture.zig -A 5

Length of output: 474

src/foundations/scenes/graphics/polygon_offset/PolygonOffsetUI.zig (3)

78-78: ⚠️ Potential issue

Correct the typo "poylgon" to "polygon" in labels

The word "poylgon" is misspelled in the labels for the polygon offset parameters. It should be "polygon".

Apply this diff to fix the typos:

For object_1:

-    c.igText("poylgon offset factor");
+    c.igText("polygon offset factor");

-    c.igText("poylgon offset unit");
+    c.igText("polygon offset unit");

For object_2:

-    c.igText("poylgon offset factor");
+    c.igText("polygon offset factor");

-    c.igText("poylgon offset unit");
+    c.igText("polygon offset unit");

Also applies to: 80-80, 116-116, 118-118


79-79: ⚠️ Potential issue

Update transform_updated flag when polygon offset values change

Currently, changes to the polygon offset factor and unit do not set the transform_updated flag. To ensure these changes are properly recognized and applied, consider setting transform_updated to true when these sliders are adjusted.

Apply this diff to modify the if statements:

For object_1:

-    if (c.igSliderFloat("##o1pof", &self.object_1.polygon_factor, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None)) {}
+    if (c.igSliderFloat("##o1pof", &self.object_1.polygon_factor, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None))
+        self.object_1.transform_updated = true;

-    if (c.igSliderFloat("##o1pou", &self.object_1.polygon_unit, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None)) {}
+    if (c.igSliderFloat("##o1pou", &self.object_1.polygon_unit, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None))
+        self.object_1.transform_updated = true;

For object_2:

-    if (c.igSliderFloat("##o2pof", &self.object_2.polygon_factor, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None)) {}
+    if (c.igSliderFloat("##o2pof", &self.object_2.polygon_factor, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None))
+        self.object_2.transform_updated = true;

-    if (c.igSliderFloat("##o2pou", &self.object_2.polygon_unit, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None)) {}
+    if (c.igSliderFloat("##o2pou", &self.object_2.polygon_unit, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None))
+        self.object_2.transform_updated = true;

Also applies to: 81-81, 117-117, 119-119


46-82: 🛠️ Refactor suggestion

Refactor duplicate UI code into a helper function

The code blocks for rendering the UI elements of object_1 and object_2 are nearly identical. To improve maintainability and reduce code duplication, consider refactoring these blocks into a reusable helper function.

For example, you could create a function like:

fn drawObjectUI(self: *ShadowsUI, object: *objectSetting, label: []const u8, id_suffix: []const u8) void {
    c.igNewLine();
    c.igText(label);

    const preview = model_list[object.model];
    const flags = c.ImGuiComboFlags_PopupAlignLeft | c.ImGuiComboFlags_HeightLargest;
    const selectable_size = ui.get_helpers().selectableSize();
    const combo_label = std.fmt.allocPrint(allocator, "##Objects{}", .{id_suffix}) catch return;

    if (c.igBeginCombo(combo_label.*, preview, flags)) {
        for (0..model_list.len) |i| {
            const is_selected: bool = i == object.model;
            if (c.igSelectable_Bool(model_list[i], is_selected, 0, selectable_size)) {
                object.model = i;
                object.updated = true;
            }
            if (is_selected) {
                c.igSetItemDefaultFocus();
            }
        }
        c.igEndCombo();
    }
    allocator.free(combo_label);

    c.igPushItemWidth(-1);
    c.igText("position");
    if (c.igSliderFloat("##posX{}}", &object.position[0], -25, 25, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;
    if (c.igSliderFloat("##posY{}}", &object.position[1], -25, 25, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;
    if (c.igSliderFloat("##posZ{}}", &object.position[2], -25, 25, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;

    c.igText("rotation");
    if (c.igSliderFloat("##rotX{}}", &object.rotation[0], 0, std.math.pi * 2, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;
    if (c.igSliderFloat("##rotY{}}", &object.rotation[1], 0, std.math.pi * 2, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;
    if (c.igSliderFloat("##rotZ{}}", &object.rotation[2], 0, std.math.pi * 2, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;

    c.igText("polygon offset factor");
    if (c.igSliderFloat("##pof{}}", &object.polygon_factor, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;

    c.igText("polygon offset unit");
    if (c.igSliderFloat("##pou{}}", &object.polygon_unit, -10000.0, 10000.0, "%.3f", c.ImGuiSliderFlags_None)) object.transform_updated = true;
}

Then call this function for each object:

self.drawObjectUI(&self.object_1, "Object 1", "1");
self.drawObjectUI(&self.object_2, "Object 2", "2");

This approach reduces code duplication and makes it easier to maintain and extend the UI components.

Also applies to: 84-120

src/foundations/scenes/cgpoc/chapter8/shadows/blinn_phong_frag.glsl (5)

55-113: 🛠️ Refactor suggestion

Refactor shadow mapping logic to reduce code duplication

The shadow mapping code for i == 0 and the else block is highly similar, leading to redundant code. Refactoring this logic into a function or utilizing loops can improve maintainability and readability.


56-56: ⚠️ Potential issue

Logical error in shadow mapping conditional checks

The conditions like if (f_can_get_light_from_z_pos_0 < normal_offset) may not correctly determine whether the fragment can receive light from the specified direction. Since f_can_get_light_from_* is the dot product between the normal and a direction vector (ranging from -1 to 1), a higher value indicates the normal is facing towards the light.

Consider changing the condition to if (f_can_get_light_from_z_pos_0 > normal_offset) to correctly identify when the surface can receive light from that direction.

Also applies to: 59-59, 64-64, 69-69, 74-74, 79-79, 86-86, 91-91, 96-96, 101-101, 106-106, 111-111


115-115: ⚠️ Potential issue

Ensure consistent alpha values when accumulating color

When adding color contributions to rv, the alpha component is set to 0.0, which may inadvertently reduce the overall alpha value and cause transparency issues.

Use an alpha value of 1.0 to maintain consistent opacity:

-rv = rv + vec4((f_ambient + f_diffuse + f_specular), 0.0);
+rv = rv + vec4((f_ambient + f_diffuse + f_specular), 1.0);

-rv = rv + vec4(f_ambient, 0.0);
+rv = rv + vec4(f_ambient, 1.0);

Also applies to: 117-117


127-139: 🛠️ Refactor suggestion

Refactor light initialization in main() to reduce duplication

The code initializing f_ls[0] and f_ls[1] is repetitive. Consider using a loop to streamline the light initialization process.

Example:

for (uint j = 0u; j < 2u; ++j) {
    f_ls[j] = f_lights[j];
    f_ls[j].direction = vec4(fo_light_dirs[j], 0.0);
    f_ls[j].attenuation_constant = fo_light_attenuations[j][0];
    f_ls[j].attenuation_linear = fo_light_attenuations[j][1];
    f_ls[j].attenuation_quadratic = fo_light_attenuations[j][2];
}

This would require combining the light direction and attenuation inputs into arrays:

in vec3 fo_light_dirs[2];
in vec3 fo_light_attenuations[2];

36-120: ⚠️ Potential issue

Potential issue when num_lights > 2 due to hardcoded indices

The loop iterates over num_lights, but the shadow mapping logic is only designed for two lights (i == 0 and i != 0). For indices i >= 2, the code may not function correctly or may result in unexpected behavior.

Consider generalizing the shadow mapping code to handle an arbitrary number of lights. This can be achieved by organizing shadow textures and related data into arrays indexed by i.

src/foundations/scenes/cgpoc/chapter8/shadows/phong_frag.glsl (2)

53-114: 🛠️ Refactor suggestion

Eliminate code duplication in shadow calculations

The shadow calculation code within the if (i == 0) and else blocks is nearly identical, differing primarily in variable names and texture indices. This duplication can lead to maintenance challenges and potential errors.

Consider abstracting the shadow calculation into a separate function or utilizing arrays for the shadow textures and coordinates. For example:

  1. Use arrays for shadow textures and coordinates:

    // Define constants
    const uint NUM_LIGHTS = 2;
    const uint NUM_DIRECTIONS = 6;
    
    // Uniform array for shadow textures
    uniform sampler2DShadow f_shadow_textures[NUM_LIGHTS * NUM_DIRECTIONS];
    
    // Input array for shadow coordinates
    in vec4 fo_light_shadow_coords[NUM_LIGHTS][NUM_DIRECTIONS];
  2. Refactor shadow logic into a loop:

    for (uint dir = 0; dir < NUM_DIRECTIONS; ++dir) {
        if (f_can_get_light_from[dir] < normal_offset && not_in_shadow == 1.0) {
            uint textureIndex = i * NUM_DIRECTIONS + dir;
            not_in_shadow = textureProj(f_shadow_textures[textureIndex], fo_light_shadow_coords[i][dir], bias);
        }
    }

This refactoring reduces code duplication, enhances readability, and makes it easier to extend the code for additional lights or directions in the future.


7-19: 🛠️ Refactor suggestion

Consider using arrays or structures for shadow texture coordinates

The code currently declares individual variables for each shadow texture coordinate, such as fo_light_1_z_pos_0, fo_light_1_y_neg_1, etc. This approach can make the code harder to maintain and scale when adding more lights or directions.

Based on previous practices in the codebase where arrays are used to handle multiple lights (as noted in your prior PR #35), consider grouping these variables into arrays. For example:

// Define constants for the number of lights and directions
const uint NUM_LIGHTS = 2;
const uint NUM_DIRECTIONS = 6;

// Input arrays for shadow texture coordinates
in vec4 fo_light_shadow_coords[NUM_LIGHTS][NUM_DIRECTIONS];

This change would simplify the shadow mapping logic and make it more flexible for future expansions.

src/foundations/rhi/Shader.zig (3)

161-163: ⚠️ Potential issue

Type Mismatch in Method Receivers attachToProgram and link

The methods attachToProgram and link are defined with self: Shader, but they are called on a pointer self: *Shader. This results in a type mismatch.

To fix the type mismatch, update the method signatures to accept self: *Shader:

-fn attachToProgram(self: Shader, shader: c.GLenum, source: []const u8) void {
+fn attachToProgram(self: *Shader, shader: c.GLenum, source: []const u8) void {

...

-fn link(self: Shader) void {
+fn link(self: *Shader) void {

Committable suggestion was skipped due to low confidence.


190-192: ⚠️ Potential issue

Incorrect Pointer Usage in glGetProgramInfoLog

In the link function, similar to the previous issue, replace @ptrCast(&infoLog) with infoLog.ptr to correctly pass the pointer to the buffer.

Apply this diff:

- c.glGetProgramInfoLog(@intCast(self.program), @intCast(log_len), &logSize, @ptrCast(&infoLog));
+ c.glGetProgramInfoLog(@intCast(self.program), @intCast(log_len), &logSize, infoLog.ptr);

Ensure the error message uses the correct slice:

- const len: usize = @intCast(logSize);
- std.debug.panic("ERROR::PROGRAM::LINKING_FAILED\n{s}\n", .{infoLog[0..len]});
+ const len: usize = @intCast(logSize);
+ std.debug.panic("ERROR::PROGRAM::LINKING_FAILED\n{s}\n", .{ infoLog[0..len] });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        c.glGetProgramInfoLog(@intCast(self.program), @intCast(log_len), &logSize, infoLog.ptr);
        const len: usize = @intCast(logSize);
        std.debug.panic("ERROR::PROGRAM::LINKING_FAILED\n{s}\n", .{ infoLog[0..len] });

176-178: ⚠️ Potential issue

Incorrect Pointer Casting in glGetShaderInfoLog

In the attachToProgram function, the use of @ptrCast(&infoLog) may not provide the correct pointer to the buffer. Using infoLog.ptr is more appropriate.

Apply this diff to correct the pointer usage:

- c.glGetShaderInfoLog(shader, @intCast(log_len), &logSize, @ptrCast(&infoLog));
+ c.glGetShaderInfoLog(shader, @intCast(log_len), &logSize, infoLog.ptr);

Also, ensure that you're using the correct slice when logging the error:

- const len: usize = @intCast(logSize);
- std.debug.panic("ERROR::SHADER::COMPILATION_FAILED\n{s}\n{s}\n", .{ infoLog[0..len], source });
+ const len: usize = @intCast(logSize);
+ std.debug.panic("ERROR::SHADER::COMPILATION_FAILED\n{s}\n{s}\n", .{ infoLog[0..len], source });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        c.glGetShaderInfoLog(shader, @intCast(log_len), &logSize, infoLog.ptr);
        const len: usize = @intCast(logSize);
        std.debug.panic("ERROR::SHADER::COMPILATION_FAILED\n{s}\n{s}\n", .{ infoLog[0..len], source });
src/foundations/scenes/graphics/polygon_offset/PolygonOffset.zig (4)

266-285: 🛠️ Refactor suggestion

Refactor renderObject_1 and renderObject_2 to eliminate code duplication

The functions renderObject_1 and renderObject_2 share similar logic. Refactoring them into a single function can reduce code duplication and improve maintainability.

You could modify the code as follows:

pub fn renderObject_N(self: *PolygonOffset, obj_setting: ShadowsUI.objectSetting, object_index: usize) void {
    const prog = rhi.createProgram();
    const obj = self.renderObject(obj_setting, prog);

    var om: rhi.Uniform = .init(prog, "f_object_m");
    const m = getObjectMatrix(obj_setting);
    om.setUniformMatrix(m);

    switch (object_index) {
        1 => {
            self.object_1 = obj;
            self.object_1_m = om;
        },
        2 => {
            self.object_2 = obj;
            self.object_2_m = om;
        },
        else => @panic("Invalid object index"),
    }
}

Then, replace the calls to renderObject_1() and renderObject_2() with:

pd.renderObject_N(pd.ui_state.object_1, 1);
pd.renderObject_N(pd.ui_state.object_2, 2);

103-124: 🛠️ Refactor suggestion

Refactor repeated drawing code to reduce duplication

In the draw function, the code blocks for drawing object_1 and object_2 are nearly identical. Consider creating a helper function to avoid code duplication and enhance readability.

You could create a helper function:

fn drawObjectWithOffset(self: *PolygonOffset, obj: object.object, obj_ui_state: ShadowsUI.objectSetting) void {
    c.glPolygonOffset(
        @floatCast(obj_ui_state.polygon_factor),
        @floatCast(obj_ui_state.polygon_unit),
    );
    const objects: [1]object.object = .{ obj };
    rhi.drawObjects(objects[0..]);
}

Then, in the draw function, use the helper:

c.glEnable(c.GL_POLYGON_OFFSET_FILL);
self.drawObjectWithOffset(self.object_1, self.ui_state.object_1);
self.drawObjectWithOffset(self.object_2, self.ui_state.object_2);
c.glDisable(c.GL_POLYGON_OFFSET_FILL);

130-136: 🛠️ Refactor suggestion

Refactor deleteObject_1 and deleteObject_2 to eliminate duplication

The deleteObject_1 and deleteObject_2 functions perform the same operation for different objects. Refactoring them into a single function can reduce code duplication.

You could implement a single function:

pub fn deleteObject_N(self: *PolygonOffset, object_index: usize) void {
    const obj = if (object_index == 1) self.object_1 else self.object_2;
    self.deleteObject(obj);
}

Replace calls to the delete functions with:

self.deleteObject_N(1);
self.deleteObject_N(2);

26-27: 🛠️ Refactor suggestion

Consider propagating allocation errors instead of panicking

Currently, on memory allocation failure, the code panics. Propagating the error allows higher-level code to handle it appropriately, improving robustness.

Modify the allocation to propagate errors:

const pd = try allocator.create(PolygonOffset);
defer allocator.destroy(pd);

Update the function signature to reflect the possibility of failure:

pub fn init(allocator: std.mem.Allocator, ctx: scenes.SceneContext) !*PolygonOffset {
    // ...
}
src/foundations/scenes/cgpoc/chapter8/shadows/ShadowsUI.zig (5)

74-140: 🛠️ Refactor suggestion

Consider refactoring duplicate code in drawLights function

The code blocks for Light 1 and Light 2 in the drawLights function are nearly identical. Refactoring this duplicated code into a helper function or loop can enhance maintainability and reduce potential errors by centralizing updates.


108-140: 🛠️ Refactor suggestion

Consider refactoring duplicate code in drawLights function

Similar to the previous comment, the code for Light 2 duplicates the logic used for Light 1. Refactor this section to use the same helper function or loop to handle both lights efficiently.


177-288: 🛠️ Refactor suggestion

Consider refactoring duplicate code in drawMaterials function

The sections for Object 1 and Object 2 in the drawMaterials function are almost identical. Refactoring this duplicated code into a helper function or loop will improve code readability and maintainability.


112-116: ⚠️ Potential issue

Correct the format string in bufPrintZ for Light 2

The format string for Light 2 has the same issues as Light 1. Update the format placeholders and add the missing closing parenthesis.

Apply this diff to fix the format string:

-const txt = std.fmt.bufPrintZ(&buf, "Position: ({d:.3}, {d:.3}, {d:.3}", .{
+const txt = std.fmt.bufPrintZ(&buf, "Position: ({.3f}, {.3f}, {.3f})", .{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            const txt = std.fmt.bufPrintZ(&buf, "Position: ({.3f}, {.3f}, {.3f})", .{
                self.light_2.position[0],
                self.light_2.position[1],
                self.light_2.position[2],
            }) catch @panic("bufsize too small");

78-82: ⚠️ Potential issue

Correct the format string in bufPrintZ for Light 1

The format string in bufPrintZ appears to be incorrect. In Zig, the correct placeholder for floating-point numbers is {}, and you can specify precision with {.3}. Additionally, the closing parenthesis is missing in the string.

Apply this diff to fix the format string:

-const txt = std.fmt.bufPrintZ(&buf, "Position: ({d:.3}, {d:.3}, {d:.3}", .{
+const txt = std.fmt.bufPrintZ(&buf, "Position: ({.3f}, {.3f}, {.3f})", .{
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            const txt = std.fmt.bufPrintZ(&buf, "Position: ({.3f}, {.3f}, {.3f})", .{
                self.light_1.position[0],
                self.light_1.position[1],
                self.light_1.position[2],
            }) catch @panic("bufsize too small");
src/foundations/scenes/cgpoc/chapter7/dolphin/Dolphin.zig (3)

312-317: ⚠️ Potential issue

Fix incorrect object deletion in deleteParallelepiped

In the deleteParallelepiped function, self.dolphin is incorrectly deleted instead of self.parallelepiped.

Apply this diff to correct the deletion:

 pub fn deleteParallelepiped(self: *Dolphin) void {
     const objects: [1]object.object = .{
-        self.dolphin,
+        self.parallelepiped,
     };
     rhi.deleteObjects(objects[0..]);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

pub fn deleteParallelepiped(self: *Dolphin) void {
    const objects: [1]object.object = .{
        self.parallelepiped,
    };
    rhi.deleteObjects(objects[0..]);
}

159-185: 🛠️ Refactor suggestion

Review the generateShadowMatrix function for correctness

The function computes the shadow matrix using perspective projection. Consider the following:

  • For directional lights (as used here), an orthographic projection is often more appropriate.
  • The commented-out orthographic projection at line 177 suggests a potential alternative.

Consider replacing the perspective projection with an orthographic projection to improve shadow mapping for directional lights:

-const s = @as(f32, @floatFromInt(ctx.cfg.width)) / @as(f32, @floatFromInt(ctx.cfg.height));
-const g: f32 = 1.0 / @tan(ctx.cfg.fovy * 0.5);
-var P = math.matrix.perspectiveProjectionCamera(g, s, 0.01, 750);
+var P = math.matrix.orthographicProjection(-10, 10, -10, 10, ctx.cfg.near, ctx.cfg.far);

Committable suggestion was skipped due to low confidence.


213-248: ⚠️ Potential issue

Eliminate redundant OpenGL calls in genShadowMap

The function genShadowMap contains multiple calls to c.glEnable(c.GL_DEPTH_TEST); and c.glClear(c.GL_DEPTH_BUFFER_BIT);. This redundancy can be cleaned up.

Apply this diff to remove redundant calls:

 c.glEnable(c.GL_DEPTH_TEST);
 c.glClear(c.GL_DEPTH_BUFFER_BIT);
-c.glEnable(c.GL_DEPTH_TEST);
 c.glDepthFunc(c.GL_LEQUAL);
 
 ...
 
 c.glDisable(c.GL_POLYGON_OFFSET_FILL);
 c.glEnable(c.GL_DEPTH_TEST);
-c.glClear(c.GL_DEPTH_BUFFER_BIT);
-c.glEnable(c.GL_DEPTH_TEST);
 c.glDepthFunc(c.GL_LEQUAL);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

pub fn genShadowMap(self: *Dolphin) void {
    c.glEnable(c.GL_DEPTH_TEST);
    c.glClear(c.GL_DEPTH_BUFFER_BIT);
    c.glDepthFunc(c.GL_LEQUAL);

    c.glEnable(c.GL_POLYGON_OFFSET_FILL);
    c.glPolygonOffset(2.0, 4.0);
    self.shadow_framebuffer.bind();
    self.shadow_framebuffer.attachDepthTexture(self.shadow_texture.?);
    self.shadow_x_up.setUniformMatrix(math.matrix.identity());
    {
        var o = self.parallelepiped;
        o.parallelepiped.mesh.gen_shadowmap = true;
        const objects: [1]object.object = .{o};
        rhi.drawObjects(objects[0..]);
    }
    self.shadow_x_up.setUniformMatrix(math.matrix.transpose(math.matrix.mc(.{
        0, 0, -1, 0,
        1, 0, 0,  0,
        0, 1, 0,  0,
        0, 0, 0,  1,
    })));
    {
        var o = self.dolphin;
        o.obj.mesh.gen_shadowmap = true;
        const objects: [1]object.object = .{o};
        rhi.drawObjects(objects[0..]);
    }
    self.shadow_framebuffer.unbind();
    c.glDisable(c.GL_POLYGON_OFFSET_FILL);
    c.glEnable(c.GL_DEPTH_TEST);
    c.glDepthFunc(c.GL_LEQUAL);
}
src/foundations/scenes/cgpoc/chapter4/simple_solar_system/SimpleSolarSystem.zig (1)

261-266: 🛠️ Refactor suggestion

Refactor Delete Methods to Reduce Code Duplication

The deleteSun, deleteEarth, deleteMoon, deleteBG, and deleteShuttle methods share identical code structures. Consider refactoring them into a single generic method to improve maintainability and reduce repetition.

Here's how you could implement a generic delete method:

fn deleteObject(self: *SimpleSolarSystem, obj: *object.object) void {
    const objects: [1]object.object = .{
        obj.*,
    };
    rhi.deleteObjects(objects[0..]);
}

pub fn deleteSun(self: *SimpleSolarSystem) void {
    self.deleteObject(&self.sun);
}

pub fn deleteEarth(self: *SimpleSolarSystem) void {
    self.deleteObject(&self.earth);
}

pub fn deleteMoon(self: *SimpleSolarSystem) void {
    self.deleteObject(&self.moon);
}

pub fn deleteBG(self: *SimpleSolarSystem) void {
    self.deleteObject(&self.bg);
}

pub fn deleteShuttle(self: *SimpleSolarSystem) void {
    self.deleteObject(&self.shuttle);
}

Also applies to: 310-315, 359-364, 408-413, 450-455

src/foundations/scenes/cgpoc/chapter7/lighting/Lighting.zig (4)

112-113: ⚠️ Potential issue

Ensure Proper Resource Cleanup Order in init Method

Adding errdefer pd.deleteBG(); after pd.renderBG(); is good practice for resource cleanup. However, consider the order of initialization and cleanup to ensure that if an error occurs during renderModel() or subsequent calls, all initialized resources are properly released.


118-119: 🛠️ Refactor suggestion

Refactor Repetitive Sphere Initialization and Cleanup

The patterns for rendersphere_1() and rendersphere_2() along with their corresponding delete functions are repetitive. Consider refactoring these into a single pair of functions that accept parameters to handle both spheres, reducing code duplication and enhancing maintainability.

For example:

pub fn renderSphere(self: *Lighting, sphere: *object.object, ui_light: *LightingUI.LightState) void {
    // Rendering logic using ui_light and storing the result in sphere
}

pub fn deleteSphere(self: *Lighting, sphere: *object.object) void {
    const objects: [1]object.object = .{ *sphere };
    rhi.deleteObjects(objects[0..]);
}

Then, in your initialization:

pd.renderSphere(&pd.sphere_1, &pd.ui_state.light_1);
errdefer pd.deleteSphere(&pd.sphere_1);

pd.renderSphere(&pd.sphere_2, &pd.ui_state.light_2);
errdefer pd.deleteSphere(&pd.sphere_2);

Also applies to: 121-122


127-130: ⚠️ Potential issue

Ensure Idempotent Deletion in deinit Method

When calling delete functions in deinit, ensure that they are safe to call multiple times without causing errors. This prevents issues if the resources were already cleaned up due to an earlier error during initialization.

Consider setting the object references to null after deletion and checking for null at the start of each delete function.

For example:

pub fn deleteBG(self: *Lighting) void {
    if (self.bg == null) return;
    const objects: [1]object.object = .{ self.bg };
    rhi.deleteObjects(objects[0..]);
    self.bg = null;
}

247-253: 🛠️ Refactor suggestion

Avoid Code Duplication in Delete Functions

The deleteBG, deleteModel, deletesphere_1, and deletesphere_2 functions share similar code. Refactor them into a single generic delete function to reduce duplication.

Here's how you might implement a generic delete function:

fn deleteObject(self: *Lighting, obj: *object.object) void {
    if (obj.* == null) return;
    const objects: [1]object.object = .{ obj.* };
    rhi.deleteObjects(objects[0..]);
    obj.* = null;
}

pub fn deleteBG(self: *Lighting) void {
    self.deleteObject(&self.bg);
}

pub fn deleteModel(self: *Lighting) void {
    self.deleteObject(&self.model);
}

// Similarly for deletesphere_1 and deletesphere_2
src/foundations/scenes/cgpoc/chapter8/shadows/Shadows.zig (15)

223-250: 🛠️ Refactor suggestion

Consider reusing code for initializing light properties to reduce duplication.

The initialization of lighting.Light structures for lights[0] and lights[1] is almost identical, differing only in the source of the color values (self.ui_state.light_1 vs self.ui_state.light_2). This code duplication can be reduced by creating a helper function to initialize the lights, improving maintainability.

Refactor the code by introducing a helper function:

fn initLight(self: *Shadows, ui_light: ShadowsUI.lightSetting) lighting.Light {
    const ambient_factor: f32 = 0.1;
    return .{
        .ambient = [4]f32{
            ui_light.color[0] * ambient_factor,
            ui_light.color[1] * ambient_factor,
            ui_light.color[2] * ambient_factor,
            1.0,
        },
        .diffuse = [4]f32{
            ui_light.color[0],
            ui_light.color[1],
            ui_light.color[2],
            1.0,
        },
        .specular = [4]f32{ 1.0, 1.0, 1.0, 1.0 },
        .location = [4]f32{ 0.0, 0.0, 0.0, 1.0 },
        .direction = [4]f32{ -0.5, -1.0, -0.3, 0.0 },
        .cutoff = 0.0,
        .exponent = 0.0,
        .attenuation_constant = 1.0,
        .attenuation_linear = 0.0,
        .attenuation_quadratic = 0.0,
        .light_kind = .positional,
    };
}

Then update the lights array initialization:

 const lights = [_]lighting.Light{
-    .{
-        /* initialization for light 1 */
-    },
-    .{
-        /* initialization for light 2 */
-    },
+    self.initLight(self.ui_state.light_1),
+    self.initLight(self.ui_state.light_2),
 };

353-356: 🛠️ Refactor suggestion

Avoid redundant shadow map generation within a single frame.

The code may generate the shadow map twice within the same frame if certain conditions are met, which is inefficient.

Consider combining the two shadow map generation checks or ensuring that self.generated_shadow_map is appropriately set to prevent redundant generation.

Also applies to: 378-383


808-821: ⚠️ Potential issue

Handle potential failure in texture initialization gracefully.

The function genShadowmapTexture uses @panic to handle failures, which may not be the best practice for error handling in this context.

Consider returning an error from the function and handling it appropriately, instead of panicking. This improves the robustness of the code.


296-384: 🛠️ Refactor suggestion

Improve the structure of the draw function for better readability.

The draw function is lengthy and contains multiple conditional checks that can be organized into helper functions or separate methods to enhance readability and maintainability.

Refactor the draw function by extracting logic into smaller functions, such as updateLight1, updateLight2, updateObject1, and so on.


36-37: ⚠️ Potential issue

Initialize light_1_view_ms and light_2_view_ms arrays before use.

The arrays light_1_view_ms and light_2_view_ms are used in genShadowMap but may not be properly initialized, leading to undefined behavior.

Ensure that these arrays are initialized with valid math.matrix values before they are accessed.


292-294: ⚠️ Potential issue

Handle potential errors when updating scene_data_buffer.

The call to self.scene_data_buffer.update does not handle potential errors that might occur during the update.

Consider checking for errors and handling them appropriately.


150-151: ⚠️ Potential issue

Avoid deinitializing scene_data_buffer before assigning it to self.scene_data_buffer.

The errdefer scene_data_buffer.deinit(); will deinitialize the buffer before it's used, causing pd.scene_data_buffer to point to invalid memory.

Remove the errdefer scene_data_buffer.deinit(); statement.

Apply this diff to fix the issue:

 var scene_data_buffer = rhi.Buffer.init(sd);
-errdefer scene_data_buffer.deinit();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    var scene_data_buffer = rhi.Buffer.init(sd);

809-810: ⚠️ Potential issue

Correct the format string in bufPrint to avoid unwanted characters.

The format string "f_shadow_texture{d};\n" includes a semicolon and newline, which may not be appropriate for a uniform name.

Modify the format string to exclude the semicolon and newline:

-const b = std.fmt.bufPrint(&buf, "f_shadow_texture{d};\n", .{i}) catch @panic("failed create uniform");
+const b = std.fmt.bufPrint(&buf, "f_shadow_texture{d}", .{i}) catch @panic("failed create uniform");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    var buf: [50]u8 = undefined;
    const b = std.fmt.bufPrint(&buf, "f_shadow_texture{d}", .{i}) catch @panic("failed create uniform");

114-115: ⚠️ Potential issue

Avoid deinitializing mats_buf before assigning it to self.materials.

The use of errdefer mats_buf.deinit(); will cause mats_buf to be deinitialized when the function init exits. Since mats_buf is assigned to pd.materials, this means pd.materials will point to a deinitialized buffer, leading to undefined behavior when accessed later.

To fix this, remove the errdefer mats_buf.deinit(); statement, as the buffer will be properly deinitialized in the deinit method.

Apply this diff to fix the issue:

 var mats_buf = rhi.Buffer.init(bd);
-errdefer mats_buf.deinit();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    var mats_buf = rhi.Buffer.init(bd);

264-265: ⚠️ Potential issue

Avoid deinitializing lights_buf before assigning it to self.lights.

In the updateLights function, errdefer lights_buf.deinit(); will deinitialize lights_buf when the function exits, but self.lights is assigned to lights_buf, leading to a dangling reference.

Remove the errdefer lights_buf.deinit(); to prevent this issue.

Apply this diff to fix the issue:

 var lights_buf = rhi.Buffer.init(ld);
-errdefer lights_buf.deinit();
 self.lights = lights_buf;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    var lights_buf = rhi.Buffer.init(ld);
    self.lights = lights_buf;

380-383: ⚠️ Potential issue

Set generated_shadow_map to true after regenerating the shadow map.

Similarly, after the second check for self.should_gen_shadow_map, ensure that self.generated_shadow_map is set to true.

Add the following line:

 self.genShadowMap();
+self.generated_shadow_map = true;
 self.should_gen_shadow_map = false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    if (self.should_gen_shadow_map) {
        self.genShadowMap();
        self.generated_shadow_map = true;
        self.should_gen_shadow_map = false;
    }

146-147: ⚠️ Potential issue

Avoid deinitializing lights_buf before assigning it to self.lights.

Similar to the previous issue, errdefer lights_buf.deinit(); will deinitialize lights_buf at the end of the init function, leaving pd.lights pointing to deallocated memory.

Remove the errdefer lights_buf.deinit(); statement to prevent premature deinitialization.

Apply this diff to fix the issue:

 var lights_buf = rhi.Buffer.init(ld);
-errdefer lights_buf.deinit();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    var lights_buf = rhi.Buffer.init(ld);

354-356: ⚠️ Potential issue

Set generated_shadow_map to true after generating the shadow map.

In the draw function, after calling self.genShadowMap(), the self.generated_shadow_map flag should be set to true to reflect that the shadow map has been generated.

Add the following line after generating the shadow map:

 self.genShadowMap();
+self.generated_shadow_map = true;
 self.should_gen_shadow_map = false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        self.genShadowMap();
        self.generated_shadow_map = true;
        self.should_gen_shadow_map = false;
    }

481-490: ⚠️ Potential issue

Ensure consistent spelling of 'Gouraud' shading.

In the code, 'Gouraud' is misspelled as 'gauraud' in some places, which might lead to incorrect shader selection.

Correct the spelling to 'Gouraud' to match the intended shading model.

Apply this diff to fix the issue:

 else => {
-    s.lighting = .gauraud;
+    s.lighting = .gouraud;
     s.frag_body = gouraud_frag_shader;
     partials = .{gouraud_vertex_shader};
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            1 => {
                s.lighting = .phong;
                s.frag_body = phong_frag_shader;
                partials = .{blinn_phong_vertex_shader};
            },
            else => {
                s.lighting = .gouraud;
                s.frag_body = gouraud_frag_shader;
                partials = .{gouraud_vertex_shader};
            },

618-627: 🛠️ Refactor suggestion

Simplify shadow texture uniform addition with formatted strings.

The switch statement adding uniforms for each shadow texture is repetitive and can be simplified using formatted strings in a loop, improving readability and reducing potential errors.

Refactor the code to use a loop with formatted uniform names:

 for (0..self.shadowmaps.len) |i| {
     var t = self.shadowmaps[i];
-    switch (i) {
-        0 => t.addUniform(prog, "f_shadow_texture0"),
-        1 => t.addUniform(prog, "f_shadow_texture1"),
-        /* ... */
-        11 => t.addUniform(prog, "f_shadow_texture11"),
-        else => {},
-    }
+    var uniform_name_buf: [20]u8 = undefined;
+    const uniform_name = try std.fmt.bufPrint(&uniform_name_buf, "f_shadow_texture{d}", .{i});
+    t.addUniform(prog, uniform_name);
     self.shadowmaps[i] = t;
 }

Committable suggestion was skipped due to low confidence.

@btipling btipling merged commit 78bb1f9 into main Oct 5, 2024
@btipling btipling deleted the bt/chapter_8_shadows branch October 5, 2024 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants