This test covers a few vision bugs:
1. VisibilityInStraightLineOfSight - test case checks the visibility
of objects in a straight line of sight parallel to the X or Y
coordinate lines:
https://github.com/diasurgical/DevilutionX/pull/7901
2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is
visible through the diagonally adjacent tiles:
https://github.com/diasurgical/DevilutionX/pull/7920
3. VisibleObjects - generic test, which makes sure some objects are
visible, but some - are not.
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
This change does not have any functional impact. Instead, it separates
dungeon-dependent data from the vision algorithm, making the
`DoVision()` function suitable to be called from the test case, which
comes next.
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb749f ("lighting: fix long-standing issue with
invisible objects (#7901)").
The following 2D grid illustrates the bug:
```
. . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . .
# # # # # # #
# . . . . . . . #
# . . . . . . . #
# . . . . . . . #
# . . . x . . . #
# . . . . . . . #
# . . . . . . . #
# . . . . . . . #
# # # # # # #
. . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . .
```
Where 'x' represents the player, surrounded by walls with diagonally
adjacent corners, and '.' represents visible tiles reached by vision
rays cast from the player's position. The figure clearly shows that
rays "leak" through corners.
The fix is quite simple: stop traversing the ray if "light" can't pass
through the adjacent tiles or if the ray hits a tile that doesn't
allow "light" to pass through. Previously, the ray continued to be
traversed even when the "light" couldn't pass through the adjacent
tiles, which is incorrect and leads to the described issue.
Here is the Python script which simulates the bug and the fix:
https://gist.github.com/rouming/8a24789fd5d18c36c40e2b4925915d16
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
* lighting: fix long-standing issue with invisible objects
This fixes an issue with lighting, where objects in a straight line of
sight parallel to the X or Y coordinate lines become invisible. Issue
#6641 perfectly illustrates the bug (see video attached to the bug).
What's worse, the objects are invisible to the observer (player)
regardless of the distance to that objects. The main requirement of a
bug reproduction is line of sight parallel to the X or Y coordinate
lines.
The actual bug lies in the visibility checks of adjacent tiles of a
point, hit by the cast ray. We've cast an approximated ray on an
integer 2D grid, so we need to check if a ray can pass through the
diagonally adjacent tiles. For example, consider this case:
#?
↗ #
x
The ray is cast from the observer 'x', and reaches the '?', but
diagonally adjacent tiles '#' do not pass the light, so the '?' should
not be visible for the 2D observer.
The trick is to perform two additional visibility checks for the
diagonally adjacent tiles, but only for the rays that are not parallel
to the X or Y coordinate lines. Parallel rays, which have a 0 in one
of their coordinate components, do not require any additional adjacent
visibility checks, and the tile, hit by the ray, is always considered
visible.
For the rays that parallel to the X or Y coordinate lines, the adjacent
visibility check always degenerated to the actual ray point visibility
check, which is considered invisible if it does not allow light to
pass through, and this is the actual bug.
To fix the issue, ensure the tile is always considered visible if the
ray that hits it is parallel to the X or Y coordinate lines.
To better demonstrate the problem, here's a straightforward simulation
written in Python:
https://gist.github.com/rouming/25c555720f93735442c2053426e73bf5
The code simulates lighting from the DevilitionX implementation, by
placing the observer 'x' in the center of the grid. The observer is
surrounded by walls and 5 random obstacles, '.' are marked as visible,
were hit by the cast rays. The first matrix output shows the bug: no
walls and obstacles are visible in the line of sight parallel to the X
and Y coordinate lines. In contrast, the second matrix output (with
the fix applied) does not exhibit this problem. Also, note the box
corners are not visible due to the adjacent visibility checks, which
are functioning correctly.
Fixes: #6641
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
* lighting: rename variables and add explicit comments for clarity
This patch improves clarity and readability without affecting
functionality:
1. Renames `VisionCrawlTable` to `VisionRays` and `crawl` to
`rayPoint` for better clarity on the purpose of these structures.
2. Renames `factors` to `quadrants` to reflect the actual purpose of
the mirror operation along the X or Y coordinate lines.
3. Adds more explicit comments to simplify the understanding of the
ray casting algorithm.
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
* test/WarriorLevel1to2: update timedemo
Recent visibility fix impacts game state and causes the timedemo to
behave completely differently, resulting in a butterfly effect:
https://youtu.be/nhpuuHSKGgk. This patch updates the timedemo, which
was recorded with the visibility fixes applied, ensuring the tests
pass successfully. Here's the latest timedemo video for the future
generations: https://youtu.be/udGcWmarYNI
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
---------
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
Building d1 graphics tool from source allows running the container on non-x64 architectures
Building smpq from source works around the arm64 version of smpq distributed in debian 12 repos (which is linked against StormLib 9.22 from 2017) failing an assert when packing our assets.