Skip to content

[Audit][High] Frustum.intersectsChunkRelative bounding sphere center is miscalculated #741

Description

@MichaelFisher1997

🔍 Module Scanned

libs/zig-math/ (engine-math re-exports from this library)

📝 Summary

The intersectsChunkRelative function computes a bounding sphere for frustum culling with an incorrect center calculation. The center.y formula uses -cam_y which places the sphere near the camera position instead of at the chunk's center, causing chunks to be incorrectly culled when the camera is above the chunk bottom.

📍 Location

  • File: libs/zig-math/frustum.zig:118-135
  • Function/Scope: intersectsChunkRelative

🔴 Severity: High

  • Critical: Crashes, data corruption, security vulnerabilities, GPU device loss
  • High: Memory leaks, race conditions, incorrect rendering, broken features
  • Medium: Performance degradation, missing error handling, suboptimal patterns
  • Low: Code style, dead code, minor improvements

💥 Impact

When the camera is positioned above chunk bottom (e.g., cam_y=80 in a chunk spanning y=0 to y=256), the bounding sphere center is placed at y=48 instead of y=128. With radius=144, the sphere only covers y=-96 to y=192, missing the top portion of the chunk (y=192 to y=256). This causes chunks to be incorrectly culled, resulting in missing terrain rendering.

🔎 Evidence

pub fn intersectsChunkRelative(self: Frustum, chunk_x: i32, chunk_z: i32, cam_x: f32, cam_y: f32, cam_z: f32) bool {
    const CHUNK_SIZE_X: f32 = 16.0;
    const CHUNK_SIZE_Y: f32 = 256.0;
    const CHUNK_SIZE_Z: f32 = 16.0;

    const world_x: f32 = @as(f32, @floatFromInt(chunk_x * 16)) - cam_x;
    const world_z: f32 = @as(f32, @floatFromInt(chunk_z * 16)) - cam_z;
    const world_y: f32 = -cam_y;  // <-- BUG: This is camera height, not chunk position

    const center = Vec3.init(
        world_x + CHUNK_SIZE_X * 0.5,
        world_y + CHUNK_SIZE_Y * 0.5,  // center.y = -cam_y + 128, should be chunk center y=128
        world_z + CHUNK_SIZE_Z * 0.5,
    );
    const radius: f32 = CHUNK_SIZE_Y * 0.5 + CHUNK_SIZE_X;  // 144, not true circumscribed radius

    return self.intersectsSphere(center, radius);
}

Example with cam_y=80:

  • Current: center.y = -80 + 128 = 48, sphere covers y=[-96, 192]
  • Expected: center.y = 128, sphere covers y=[-16, 272]
  • Chunk spans y=[0, 256]
  • Top ~64 units of chunk are outside the sphere, causing incorrect culling

🛠️ Proposed Fix

The center calculation needs to account for the chunk's vertical position in world space. Since the function lacks a chunk_y parameter, the sphere should be centered at the middle of a standard chunk column (y=128):

const world_y: f32 = CHUNK_SIZE_Y * 0.5 - cam_y;  // chunk center (128) minus camera offset

Alternatively, if the intent is to have the sphere follow the camera vertically while spanning the chunk column, the center should be explicitly:

const center = Vec3.init(
    world_x + CHUNK_SIZE_X * 0.5,
    CHUNK_SIZE_Y * 0.5 - cam_y,  // Place at chunk vertical center, offset by camera
    world_z + CHUNK_SIZE_Z * 0.5,
);

The radius should also be corrected to the true circumscribed radius:

const radius: f32 = std.math.sqrt(
    std.math.pow(f32, CHUNK_SIZE_X * 0.5, 2) +
    std.math.pow(f32, CHUNK_SIZE_Y * 0.5, 2) +
    std.math.pow(f32, CHUNK_SIZE_Z * 0.5, 2)
);  // ~128.25 instead of 144

✅ Acceptance Criteria

  • intersectsChunkRelative correctly culls chunks when camera is at various heights (y=0, y=80, y=200, y=256)
  • The bounding sphere actually contains the full chunk (all 256 Y levels)
  • Existing frustum culling tests pass: nix develop --command zig build test
  • New unit test verifies sphere containment for edge cases (camera at chunk bottom, middle, and top)

📚 References

Metadata

Metadata

Assignees

No one assigned

    Labels

    automated-auditIssues found by automated opencode audit scansbugSomething isn't workingenhancementNew feature or requesthotfix

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions