Skip to content

lighting: fix long-standing issue with invisible objects#7901

Merged
AJenbo merged 3 commits intodiasurgical:masterfrom
rouming:lighting-fix
Apr 13, 2025
Merged

lighting: fix long-standing issue with invisible objects#7901
AJenbo merged 3 commits intodiasurgical:masterfrom
rouming:lighting-fix

Conversation

@rouming
Copy link
Copy Markdown
Contributor

@rouming rouming commented Apr 5, 2025

This PR fixes an issue with visibility, where objects in a straight line of sight parallel to the X or Y coordinate lines become invisible. The #6641 is a perfect illustration of 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 diagonally 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. Tiles, marked as '.' are visible tiles, depict, how rays spread around the observer. 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.

             invisible wall is a bug
                  V
    # # # # #   #   # # # # # # #     
  # . . . . .     . . . . . . . . #   
  # . . . . .     . . . . . . . . #   
  # . . . . . .   . . . . . . . .     
  # . . . . . . # . . . . . . .       
  # . . . . . . . . . . . . # . . #   
  # . . . . . . . . . . . . . . . #   
  # . . . . . . . . . . . . . . .     
    . . . . . . . x . .             < invisible obstacle is a bug        
  # . . . . . . . . . . . . . . .     
  # . . . . . . # . . . . . . . . #   
  # . . . . .   . . . . . . . . . #   
  # . . . .     . . . . . . . . . #   
  # . . .       . . . . . . . . . #   
  # . . .       . . . . . . . . . #   
  # . .         . . . . . . # . . #   
    #         # #   # # # #     #
                  ^
            invisible wall is a bug

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.

    # # # # #   # # # # # # # # #     
  # . . . . .     . . . . . . . . #   
  # . . . . .     . . . . . . . . #   
  # . . . . . .   . . . . . . . .     
  # . . . . . . # . . . . . . .       
  # . . . . . . . . . . . . # . . #   
  # . . . . . . . . . . . . . . . #   
  # . . . . . . . . . . . . . . .     
  # . . . . . . . x . . #             
  # . . . . . . . . . . . . . . .     
  # . . . . . . # . . . . . . . . #   
  # . . . . .   . . . . . . . . . #   
  # . . . .     . . . . . . . . . #   
  # . . .       . . . . . . . . . #   
  # . . .       . . . . . . . . . #   
  # . .         . . . . . . # . . #   
    #         # # # # # # #     #     

@AJenbo
Copy link
Copy Markdown
Member

AJenbo commented Apr 5, 2025

Nice, this always bugged me.

AJenbo
AJenbo previously approved these changes Apr 5, 2025
Comment thread Source/lighting.cpp Outdated
@AJenbo AJenbo added this to the 1.6.0 milestone Apr 5, 2025
Copy link
Copy Markdown
Member

@AJenbo AJenbo left a comment

Choose a reason for hiding this comment

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

This may need a new time demo to be recorded to it to pass the tests.

@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 5, 2025

@AJenbo pushed the change related to the clang-format output, please take a look.

This may need a new time demo to be recorded to it to pass the tests.

I'm not familiar with the development process here, not sure is clear to me what has to be done exactly.

@AJenbo
Copy link
Copy Markdown
Member

AJenbo commented Apr 5, 2025

One of the test is an input recording that plays the two first levels in the shareware. The changes you have made here affects game play so the demo recording no longer syncs, since the change is intentional a new recording has to be made so that it matches the new behavior.

This is done switching to the shareware mode, and copying the save from the test folder to your save folder, starting the game with the following arguments:
./devilutionx --record 0 --create-reference
Then play the two first levels and exit and add the .dmo and reference save to the test folder.

@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 5, 2025

Sweet, thanks for the explanation. I see that timedemo_test complains that file "game" has different content. I assume that because of the differences in the dFlags array, which now should have more "visible" objects around the player.

Are there any requirements for the demo? Like certain actions? I played back the original WarriorLevel1to2 and the player dies on the 1st dungeon and never reaches the 2nd.

@StephenCWills
Copy link
Copy Markdown
Member

I played back the original WarriorLevel1to2 and the player dies on the 1st dungeon and never reaches the 2nd.

Seems to be because this PR completely breaks the timedemo recording, right from the start. I'm not entirely sure why it would do that, unless maybe the change to visibility causes a monster to wake up early and completely skew the sequence of random numbers.

@AJenbo
Copy link
Copy Markdown
Member

AJenbo commented Apr 5, 2025

Here is a recording of the time demo from a couple of versions ago: https://www.youtube.com/watch?v=prPVymbw3t0

What you want is to trigger as many things as possible:

  • Attack animals and undead with sharp and blunt weapons.
  • take damage with and without and armor and with and without a shield.
  • use a bow
  • use a spell
  • repair an item
  • break and item
  • go level up and apply stats to each type
  • enter the secound level
  • identify an item (use it both before and after)
  • drink both mana and healing potions

Note that you do not want to trigger time sensetive things like:

  • using shrines (there message is there for x sec)
    Preferably don't die as that makes it harder to see if there was an issue or not.
    Preferably make the recording short so that it doesn't take up a lot of KB and can be processed quickly when running the tests.

Once submitted you will be able to see if there is a significant difference in test coverage which will let you know if you did more or less things then the previous demo.

@StephenCWills
Copy link
Copy Markdown
Member

  • using shrines (there message is there for x sec)

Is that still an issue? I thought I fixed it. #6516

@AJenbo
Copy link
Copy Markdown
Member

AJenbo commented Apr 6, 2025

Oh i guess not, i suppose that's something we could improve in a new recording then.

@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 6, 2025

I played back the original WarriorLevel1to2 and the player dies on the 1st dungeon and never reaches the 2nd.

Seems to be because this PR completely breaks the timedemo recording, right from the start. I'm not entirely sure why it would do that, unless maybe the change to visibility causes a monster to wake up early and completely skew the sequence of random numbers.

That's actually awesome. I made a side-by-side comparison: https://youtu.be/nhpuuHSKGgk . It all started with the first monster not being killed, then the player had to go to the left arch instead of the right one. Basically, this is a perfect illustration of the butterfly effect. And we can take the reasoning even further: how foresight (seeing of previously invisible objects) leads to death, how the amount of knowledge does not bring happiness, how curiosity becomes a curse :)

I'll prepare the new timedemo.

@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 6, 2025

@AJenbo did the demo, now the test seems passes successfully, please take a look.

@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 7, 2025

Folks, while recording the demo on the latest master I noticed the bug with the health globe. The bug is well depicted on the video I've made: https://youtu.be/UIWqjRxAe3g?t=183 - starting from the 3:05, when the player got a new level, the health was restored, but the health globe reflects that in a weird way. Something known?

@julealgon
Copy link
Copy Markdown
Contributor

Such an awesome change this is. Great stuff @rouming

@Yggdrasill
Copy link
Copy Markdown
Contributor

The bug is well depicted on the video I've made: https://youtu.be/UIWqjRxAe3g?t=183

This seems like the same bug as #7835, except for the health globe rather than mana globe.

@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 8, 2025

The bug is well depicted on the video I've made: https://youtu.be/UIWqjRxAe3g?t=183

This seems like the same bug as #7835, except for the health globe rather than mana globe.

Thanks, will post there then.

@rouming rouming marked this pull request as draft April 8, 2025 21:15
@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 8, 2025

Converting this to draft, will return to this PR in a couple of weeks.

rouming added 3 commits April 13, 2025 01:06
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
 diasurgical#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: diasurgical#6641
Signed-off-by: Roman Penyaev <r.peniaev@gmail.com>
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>
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>
@rouming
Copy link
Copy Markdown
Contributor Author

rouming commented Apr 13, 2025

Initially, I hoped to avoid adjusting the radius, but it turns out this isn't straightforward for any radius value passed to the DoVision function. To accurately create a circle of vision rays around the player, the simplest approach as it is now: is to use an array of length adjustments and then decrement as needed. Remade a timedemo. Now should be all good. Hopefully, I have not missed anything. @AJenbo please take a look.

@rouming rouming marked this pull request as ready for review April 13, 2025 00:28
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 22, 2025
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:
   diasurgical#7901

2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is
   visible through the diagonally adjacent tiles:
   diasurgical#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>
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 22, 2025
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:
   diasurgical#7901

2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is
   visible through the diagonally adjacent tiles:
   diasurgical#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>
AJenbo pushed a commit that referenced this pull request Apr 22, 2025
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:
   #7901

2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is
   visible through the diagonally adjacent tiles:
   #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>
artifacts-storage pushed a commit to artifacts-storage/devilutionx-artifacts that referenced this pull request Apr 22, 2025
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:
   diasurgical/DevilutionX#7901

2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is
   visible through the diagonally adjacent tiles:
   diasurgical/DevilutionX#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>
Hancock33 added a commit to Hancock33/batocera.piboy that referenced this pull request Apr 23, 2025
-----------------------------------------------------------------------------------------------------
chromebook-linux-audio.mk 7acadc46bad5363053874fa625a79314931ba1be # Version: Commits on Apr 21, 2025
-----------------------------------------------------------------------------------------------------
functions: extend chrome platform detection to check product_family,

--------------------------------------------------------------------------------------------------------
batocera-emulationstation.mk cff195416e194420cae771523a980c83afd600d4 # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------------------------
Merge pull request #1904 from Tovarichtch/patch-1

[lightgun] add pause menu to ghost squad (wii),

------------------------------------------------------------------------------------------------
batocera-es-piboy.mk cff195416e194420cae771523a980c83afd600d4 # Version: Commits on Apr 22, 2025
------------------------------------------------------------------------------------------------
Merge pull request #1904 from Tovarichtch/patch-1

[lightgun] add pause menu to ghost squad (wii),

------------------------------------------------------------------------------------------
dolphin-emu.mk fd8ce3ce8062a911e9cb73a725b31df3ad99efa0 # Version: Commits on Apr 22, 2025
------------------------------------------------------------------------------------------
Merge pull request #13555 from jordan-woyak/cubeb-default

AudioCommon: Make cubeb sound backend default on Linux.,

-------------------------------------------------------------------------------------
ikemen.mk 9bcc118d5d2468f933087db9db16d6655b5610e6 # Version: Commits on Apr 22, 2025
-------------------------------------------------------------------------------------
Merge pull request #2419 from Eiton/3d-stage

fix: model attribute,

--------------------------------------------------------------------------------------
openmsx.mk 1bc4ab8d0c625b8657840fd204db5686727d22fc # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------
Prepare init.tcl for Tcl9

This is a (small) part of:

    openMSX/openMSX#1900

Tcl8 and Tcl9 are not 100% compatible, see:

    https://core.tcl-lang.org/tcl/wiki?name=Migrating+scripts+to+Tcl+9

This only fixes `share/init.tcl` not any of the other scripts in

`share/scripts/*.tcl`. I don't expect many fixes wil be needed. I just

haven't checked yet (testing all scripts takes more time than I have

today).,

----------------------------------------------------
pcsx2.mk v2.3.291 # Version: Commits on Apr 22, 2025
----------------------------------------------------
- [CDVD: Adjust precache osd to display decimal value for required ram, adjust linux get available memory logic.](PCSX2/pcsx2#12554)

,

-------------------------------------------------------------------------------------
ppsspp.mk eacbbe0efdddf4ce39ebd387d5532ad99fc8dd7c # Version: Commits on Apr 22, 2025
-------------------------------------------------------------------------------------
Merge pull request #20282 from hrydgard/disable-qt-again

Specify the Qt version in CI,

------------------------------------------------------------------------------------
rpcs3.mk 5ca5b54bf6f01584bd5444f35b3e69d62d6c31a6 # Version: Commits on Apr 22, 2025
------------------------------------------------------------------------------------
Fix sceNpTrophyRegisterContext,

---------------------------------------------------------------
ruffle.mk nightly-2025-04-22 # Version: Commits on Apr 22, 2025
---------------------------------------------------------------
## What's Changed

* build: Set a different channel for local builds by @kjarosh in ruffle-rs/ruffle#20173

* build(deps-dev): bump the npm-minor group in /web with 14 updates by @dependabot in ruffle-rs/ruffle#20237

* build(deps): bump the cargo-minor group with 3 updates by @dependabot in ruffle-rs/ruffle#20239

**Full Changelog**: ruffle-rs/ruffle@nightly-2025-04-20...nightly-2025-04-22,

----------------------------------------------------
ryujinx.mk 1.3.32 # Version: Commits on Apr 21, 2025
----------------------------------------------------
# Canary builds:

These builds are experimental and may sometimes not work, use [regular builds](https://github.com/Ryubing/Stable-Releases/releases/latest) instead if that sounds like something you don't want to deal with.

| Platform | Artifact |

|--|--|

| Windows 64-bit | [Canary Windows Artifact](https://github.com/Ryubing/Canary-Releases/releases/download/1.3.32/ryujinx-canary-1.3.32-win_x64.zip) |

| Windows ARM 64-bit | [Canary Windows ARM Artifact](https://github.com/Ryubing/Canary-Releases/releases/download/1.3.32/ryujinx-canary-1.3.32-win_arm64.zip) |

| Linux 64-bit | [Canary Linux Artifact](https://github.com/Ryubing/Canary-Releases/releases/download/1.3.32/ryujinx-canary-1.3.32-linux_x64.tar.gz) |

| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/Ryubing/Canary-Releases/releases/download/1.3.32/ryujinx-canary-1.3.32-linux_arm64.tar.gz) |

| macOS | [Canary macOS Artifact](https://github.com/Ryubing/Canary-Releases/releases/download/1.3.32/ryujinx-canary-1.3.32-macos_universal.app.tar.gz) |

**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-1.3.31...Canary-1.3.32)**,

--------------------------------------------------------------------------------------
shadps4.mk 0c86c54d48533e3505e56e639059d28331fe4bc3 # Version: Commits on Apr 21, 2025
--------------------------------------------------------------------------------------
Implement SET_PC_B64 instruction (#2823)

* basic impl

* minor improvements

* clang

* more clang

* improvements requested by squidbus,

---------------------------------------------------------------------------------------
thextech.mk 44b4a2da29bd7c785775d727216a3890796cd9d1 # Version: Commits on Apr 21, 2025
---------------------------------------------------------------------------------------
Use layer speed of collided blocks and conveyor belts

Thanks to @Superbloxen for the report,

-------------------------------------------------
vice.mk r45634 # Version: Commits on Apr 22, 2025
-------------------------------------------------
Gtk3: Use \Play\ instead of \Start\ for datasette controls submenu

git-svn-id: https://svn.code.sf.net/p/vice-emu/code/trunk@45634 379a1393-f5fb-40a0-bcee-ef074d9b53f7,

-------------------------------------------------------------------------------------
vita3k.mk 0b4a4e58d18436f9f9631e7e50af101b3187e3c6 # Version: Commits on Apr 20, 2025
-------------------------------------------------------------------------------------
external: Fix link to pre-built OpenSSL by updating it to 3.5.,

-----------------------------------------------------------------------------------
xemu.mk 1a6b858fe83e6817ed8fa931ad591bf4922b65f2 # Version: Commits on Apr 21, 2025
-----------------------------------------------------------------------------------
ci: bump softprops/action-gh-release from 2.2.1 to 2.2.2

Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.1 to 2.2.2.

- [Release notes](https://github.com/softprops/action-gh-release/releases)

- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)

- [Commits](softprops/action-gh-release@c95fe14...da05d55)

---

updated-dependencies:

- dependency-name: softprops/action-gh-release

  dependency-version: 2.2.2

  dependency-type: direct:production

  update-type: version-update:semver-patch

...

Signed-off-by: dependabot[bot] <support@github.com>,

-------------------------------------------------------------------------------------------
xenia-native.mk 7566871220009780f8ac4488cef9620f5d97390d # Version: Commits on Apr 22, 2025
-------------------------------------------------------------------------------------------
[D3D12] Changed default resource state based on GPU Validation

- This prevents GBV from spamming warnings,

----------------------------------------------------------------------------------------
pironman5.mk 11c594d5d0ace7f64ac7e054dc6dfd2325b0d14d # Version: Commits on Apr 22, 2025
----------------------------------------------------------------------------------------
chore: update version to 1.2.6.1,

----------------------------------------------------------------------------------------------
ffmpeg-rockchip.mk 57d5befee96f229b05fa09334a4d7a6f95a324bd # Version: Commits on Apr 22, 2025
----------------------------------------------------------------------------------------------
fixup! lavc/rkmppenc: add UDU SEI support for H26x encoders

polish the udu-sei impl.

Signed-off-by: nyanmisaka <nst799610810@gmail.com>,

------------------------------------------------------------------------------------
box64.mk cc6500b7ddd633c3294225f433fe77caaf4473f9 # Version: Commits on Apr 22, 2025
------------------------------------------------------------------------------------
[RBTREE] Fixed an edge case (#2562),

---------------------------------------------------------------------------------------
corsixth.mk 3c5b742857df63a9c7a1c7bd570b1034e065993d # Version: Commits on Apr 22, 2025
---------------------------------------------------------------------------------------
Merge pull request #2398 from tobylane/fontincluded

Look for a bundled Noto CJK font,

------------------------------------------------------------------------------------------
devilutionx.mk 9e05af66bd4866b4d49ad39ac9231929f2afe8bd # Version: Commits on Apr 22, 2025
------------------------------------------------------------------------------------------
test: add `vision_test`

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:

   diasurgical/DevilutionX#7901

2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is

   visible through the diagonally adjacent tiles:

   diasurgical/DevilutionX#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>,

---------------------------------------------------------------------------------------
etlegacy.mk f50cc647facda20edefca0edd2cdce7892684539 # Version: Commits on Apr 22, 2025
---------------------------------------------------------------------------------------
game: fix c90 standard violations,

-------------------------------------------------------------------------------------------
jazz2-native.mk 9a3418bcfe12b559b90c7bb92b539a46445dc42f # Version: Commits on Apr 22, 2025
-------------------------------------------------------------------------------------------
Multiplayer, renamed \IsElimination\ to \Elimination\, fixed MovingPlatform,

--------------------------------------------------------------------------------------
omf2097.mk e1c9fb24cde7c7334ccaaea2fff467b7926f10f5 # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------
Projectile behavior fixes (#1212)

* Projectile hit behavior fixes

* Fix shredder hands not able to combo

* Fix Electra's projectile not being controllable in hyper mode,

----------------------------------------------------------------------------------
stk.mk 79df88bdd3316e89bf004905313c2fc07e97d82f # Version: Commits on Apr 21, 2025
----------------------------------------------------------------------------------
Fix gles ibl shader,

----------------------------------------------------------------------------------------
supertux2.mk 3cddf3d41a3e3639bb6f2b7cf8099f0c5a2e3604 # Version: Commits on Apr 21, 2025
----------------------------------------------------------------------------------------
re-export test tiles [ci skip],

----------------------------------------------------------------------------------------
hid-tmff2.mk d4c9e065ca41b30e1cb7a9852ba182b53a669b9c # Version: Commits on Apr 22, 2025
----------------------------------------------------------------------------------------
Merge pull request #156 from danielpokladek/patch-1

Adds reminder to add MOK certificate if using Secure Boot + DKMS.,

----------------------------------------------------------------------------------------
retroarch.mk 03525ffd5f179b60e996dfc1d91dde82c62b27c2 # Version: Commits on Apr 22, 2025
----------------------------------------------------------------------------------------
Fetch translations from Crowdin,

----------------------------------------------------------------------------------------
doomretro.mk b469ac9bdb5624fad0b55dd6ac00e1696da03b23 # Version: Commits on Apr 21, 2025
----------------------------------------------------------------------------------------
Fix previous commit for CMake,

-----------------------------------------------------------------------------------
tr1x.mk 0adefabf41f36392c9810f60028a84d90a6bb702 # Version: Commits on Apr 22, 2025
-----------------------------------------------------------------------------------
data: use HD assets,

-----------------------------------------------------------------------------------
tr2x.mk 0adefabf41f36392c9810f60028a84d90a6bb702 # Version: Commits on Apr 22, 2025
-----------------------------------------------------------------------------------
data: use HD assets,

---------------------------------------------------------------------------------------------------
libretro-dosbox-pure.mk 2bd477b2eaff7a3d34b31b12487a29ae4b79e6f3 # Version: Commits on Apr 22, 2025
---------------------------------------------------------------------------------------------------
Make IMGMOUNT drives accessible in disc control menu (#594)

This is done by sharing DOSBox's DriveManager state with DOSBox Pure's disk image management. It is not elegant due to the two different approaches being forced to work together. It would be better to remove DriveManager and just have a single disk mounting system.,

--------------------------------------------------------------------------------------------------
libretro-gearcoleco.mk 18ad36be7a9fe6c57205ee0765290e9a91ceadd8 # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------------------
[libretro] Fix Android build,

--------------------------------------------------------------------------------------------------
libretro-gearsystem.mk 479d38c54e59e8de943752f75153808c09f505da # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------------------
[libretro] Redirect emu log to libretro log,

----------------------------------------------------------------------------------------------
libretro-ppsspp.mk eacbbe0efdddf4ce39ebd387d5532ad99fc8dd7c # Version: Commits on Apr 22, 2025
----------------------------------------------------------------------------------------------
Merge pull request #20282 from hrydgard/disable-qt-again

Specify the Qt version in CI,

--------------------------------------------------------------------------------------------
libretro-puae.mk 7dd724eaa48f05f02d9d2f1bfa131c34b6ab5351 # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------------
Fix archived HDF temp cleanup,

------------------------------------------------------------------------------------------------
libretro-puae2021.mk 87cbcb27809c2800418bf81da21a766de28029a0 # Version: Commits on Apr 22, 2025
------------------------------------------------------------------------------------------------
Fix archived HDF temp cleanup,

---------------------------------------------------------------------------------------------
libretro-vba-m.mk 9e724ea4b8e5298d3b096daac1df59e997f18c40 # Version: Commits on Apr 22, 2025
---------------------------------------------------------------------------------------------
build: support building ARM64 binary for Mac

Make some adjustments to the Mac build scripts to support building an

ARM64 binary for Apple Silicon.

Also support cross-compiling for Intel on Apple Silicon.

Also prefer using Ninja for CMake ports.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>,

--------------------------------------------------------------------------------------------
libretro-vice.mk e5b036f0be19f7a70fde75cc0e8b1b43476adc13 # Version: Commits on Apr 22, 2025
--------------------------------------------------------------------------------------------
Unload-deinit cleanup,
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 27, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 27, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 27, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 27, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 28, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Apr 28, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
Trihedraf pushed a commit to Trihedraf/devilutionX that referenced this pull request May 16, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
Trihedraf pushed a commit to Trihedraf/devilutionX that referenced this pull request May 16, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
Trihedraf pushed a commit to Trihedraf/devilutionX that referenced this pull request May 16, 2025
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:
   diasurgical#7901

2. NoVisibilityThroughAdjacentTiles - test case checks that nothing is
   visible through the diagonally adjacent tiles:
   diasurgical#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>
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request May 21, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request May 21, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request May 21, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request May 21, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Jun 30, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Jun 30, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 7, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 7, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 8, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 8, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 9, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 9, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 23, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Sep 23, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Nov 23, 2025
…#7901)

* 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
 diasurgical#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: diasurgical#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>
(cherry picked from commit 88d0cb7)
rouming added a commit to rouming/DevilutionX-AI that referenced this pull request Nov 23, 2025
This commit fixes a very subtle case where players could see through
diagonally adjacent tiles. The bug was introduced in the recent
commit: 88d0cb7 ("lighting: fix long-standing issue with
invisible objects (diasurgical#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>
(cherry picked from commit 5226d55)
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.

5 participants