🔍 Module Scanned
src/engine/math/ (automated audit scan)
📝 Summary
The DDA (Digital Differential Analyzer) voxel traversal algorithm in castThroughVoxels has an incorrect tie-breaking condition that causes wrong face selection when Y and Z t_max values are equal. This results in blocks being placed on incorrect faces when looking at blocks from certain angles.
📍 Location
- File: src/engine/math/ray.zig:212-224
- Function/Scope: castThroughVoxels function, main DDA loop tie-breaking section
🔴 Severity: High
- High: Incorrect rendering, broken features (block placement targets wrong face)
💥 Impact
When a player aims at a block and the DDA algorithm encounters a tie between t_max_y and t_max_z, the code at lines 212-224 incorrectly defaults to Y-axis stepping instead of properly handling ties. Specifically, the condition t_max_y < t_max_z misses the equal case, causing blocks to be placed on the wrong face when the ray passes through a Y-Z boundary at the same distance.
🔎 Evidence
The problematic code at lines 212-224:
if (t_max_x < t_max_y) {
if (t_max_x < t_max_z) {
x += step_x;
distance = t_max_x;
t_max_x += t_delta_x;
last_face = if (step_x > 0) .west else .east;
} else {
z += step_z;
distance = t_max_z;
t_max_z += t_delta_z;
last_face = if (step_z > 0) .north else .south;
}
} else {
if (t_max_y < t_max_z) { // line 213 - misses t_max_y == t_max_z
y += step_y;
distance = t_max_y;
t_max_y += t_delta_y;
last_face = if (step_y > 0) .bottom else .top;
} else { // line 218 - handles ties but wrong preference
z += step_z;
distance = t_max_z;
t_max_z += t_delta_z;
last_face = if (step_z > 0) .north else .south;
}
}
When t_max_y == t_max_z, the condition t_max_y < t_max_z at line 213 is false, so the else branch executes, stepping in Z. This is inconsistent with the X-axis logic which handles ties in its else branch. The correct behavior should handle the equal case explicitly with <= instead of <.
🛠️ Proposed Fix
Modify the tie-breaking condition at line 213 to use <= for consistent tie-breaking:
if (t_max_y <= t_max_z) { // Changed < to <= for consistent tie-breaking
y += step_y;
distance = t_max_y;
t_max_y += t_delta_y;
last_face = if (step_y > 0) .bottom else .top;
} else {
z += step_z;
distance = t_max_z;
t_max_z += t_delta_z;
last_face = if (step_z > 0) .north else .south;
}
✅ Acceptance Criteria
📚 References
- Standard DDA algorithm: "A Fast Voxel Traversal Algorithm for Ray Tracing" by John Amanatides and Andrew Woo
- Related code: src/game/player.zig:376-393 uses target.face.getOffset() to determine block placement position
- Face enum definition: src/world/block.zig:128-134
🔍 Module Scanned
src/engine/math/ (automated audit scan)
📝 Summary
The DDA (Digital Differential Analyzer) voxel traversal algorithm in castThroughVoxels has an incorrect tie-breaking condition that causes wrong face selection when Y and Z t_max values are equal. This results in blocks being placed on incorrect faces when looking at blocks from certain angles.
📍 Location
🔴 Severity: High
💥 Impact
When a player aims at a block and the DDA algorithm encounters a tie between t_max_y and t_max_z, the code at lines 212-224 incorrectly defaults to Y-axis stepping instead of properly handling ties. Specifically, the condition t_max_y < t_max_z misses the equal case, causing blocks to be placed on the wrong face when the ray passes through a Y-Z boundary at the same distance.
🔎 Evidence
The problematic code at lines 212-224:
if (t_max_x < t_max_y) {
if (t_max_x < t_max_z) {
x += step_x;
distance = t_max_x;
t_max_x += t_delta_x;
last_face = if (step_x > 0) .west else .east;
} else {
z += step_z;
distance = t_max_z;
t_max_z += t_delta_z;
last_face = if (step_z > 0) .north else .south;
}
} else {
if (t_max_y < t_max_z) { // line 213 - misses t_max_y == t_max_z
y += step_y;
distance = t_max_y;
t_max_y += t_delta_y;
last_face = if (step_y > 0) .bottom else .top;
} else { // line 218 - handles ties but wrong preference
z += step_z;
distance = t_max_z;
t_max_z += t_delta_z;
last_face = if (step_z > 0) .north else .south;
}
}
When t_max_y == t_max_z, the condition t_max_y < t_max_z at line 213 is false, so the else branch executes, stepping in Z. This is inconsistent with the X-axis logic which handles ties in its else branch. The correct behavior should handle the equal case explicitly with <= instead of <.
🛠️ Proposed Fix
Modify the tie-breaking condition at line 213 to use <= for consistent tie-breaking:
if (t_max_y <= t_max_z) { // Changed < to <= for consistent tie-breaking
y += step_y;
distance = t_max_y;
t_max_y += t_delta_y;
last_face = if (step_y > 0) .bottom else .top;
} else {
z += step_z;
distance = t_max_z;
t_max_z += t_delta_z;
last_face = if (step_z > 0) .north else .south;
}
✅ Acceptance Criteria
📚 References