Skip to content

Fix black areas on generated maps by initializing Minetest lighting - #195

Open
naulan-chrzaszcz wants to merge 2 commits into
mainfrom
naulan/light-fix
Open

Fix black areas on generated maps by initializing Minetest lighting#195
naulan-chrzaszcz wants to merge 2 commits into
mainfrom
naulan/light-fix

Conversation

@naulan-chrzaszcz

@naulan-chrzaszcz naulan-chrzaszcz commented Jun 22, 2026

Copy link
Copy Markdown
Member

Issue

#196

Changes

Initialized the param1 to 0x0F (full daylight) for all air and water voxels.

Showcase

Before the changes :
Capture d'écran_20260623_145216
Capture d'écran_20260623_145755

After :
Capture d'écran_20260623_144931
Capture d'écran_20260623_150017

Reason

For my blog, I often need to take screenshots of the worlds generated by the tool to illustrate my posts. But the main issue is that every time I generate a world, large black areas appear. Most of the time, these areas just stay there and won't disappear, even if you do something like break blocks inside the dark zone or walk over it.

Consequences of this change

By forcing maximum light on all transparent voxels, natural light attenuation and dynamic shadows are disabled. Areas that would typically be shaded will appear uniformly bright, as natural occlusion is no longer calculated.

Self-checks

  • The code has unit tests associated
  • The code has Javadoc Comments associated
  • Complex / Unexpected code is explained / justified with a small comment
  • Relevant documentation inside the /docs folder has been updated
  • All examples in examples/ work the same (or have been adapted if subject to changes in this PR)
  • Git history is clean (each commit accomplish a single task and describe it accordingly)
  • The texts have been proofread (documentation, error messages, logs, comments...)

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

[Maven Build Status]

📑 Commit: e6c75f0433b8ad6a45fedb5b3ae38c1049015d25
⌚️ Date: 2026-06-27T11:49:04 (CEST)
🛠️ Status: ✅ Success

📦 Download artifact: Generator.jar

@naulan-chrzaszcz

Copy link
Copy Markdown
Member Author
image This PR doesn't fully fix the lighting issue yet, but we can use it as a base to discuss potential solutions.

@naulan-chrzaszcz

naulan-chrzaszcz commented Jun 22, 2026

Copy link
Copy Markdown
Member Author

I've been trying to explain my problem to Gemini like this:
guy-explaining-meme

But AI alone isn't enough to solve it, as I have zero knowledge of how the Luanti rendering engine works.

(This is a good opportunity to find out)

@naulan-chrzaszcz

Copy link
Copy Markdown
Member Author
image

@naulan-chrzaszcz naulan-chrzaszcz linked an issue Jun 22, 2026 that may be closed by this pull request
@naulan-chrzaszcz naulan-chrzaszcz self-assigned this Jun 22, 2026
@naulan-chrzaszcz naulan-chrzaszcz added the type: bug Something isn't working label Jun 22, 2026
@indyteo

indyteo commented Jun 22, 2026

Copy link
Copy Markdown
Member

@FLecordix will be happy about this (even if he can't see this repo) 😉

@pyrollo

pyrollo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor
  • Instruct the Luanti engine that chunk lighting is fully pre-computed to prevent rendering black areas.
  • Set the lighting_complete flag to 0xFFFF during binary serialization.
  • Initialize the param1 channel for air voxels to 0x0F (full daylight) by default, allowing proper light propagation when the map is loaded.

Wouldn't this cause reverse bug: illuminate parts that shouldn't be ?
Anyway, for us, this would be better than dark areas.

@indyteo

indyteo commented Jun 23, 2026

Copy link
Copy Markdown
Member

Before fixing it, have you been able to identify the causes, in a reproducible manner?
I mean, are you able to generate a world with the problem every single time? To be sure you fixed it?

@naulan-chrzaszcz
naulan-chrzaszcz force-pushed the naulan/light-fix branch 3 times, most recently from 4c3f1a3 to e6c66fd Compare June 23, 2026 13:47
@naulan-chrzaszcz naulan-chrzaszcz changed the title DRAFT --- Fix black areas on generated maps by initializing Minetest lighting Fix black areas on generated maps by initializing Minetest lighting Jun 23, 2026
@naulan-chrzaszcz
naulan-chrzaszcz marked this pull request as ready for review June 23, 2026 13:49
@naulan-chrzaszcz

naulan-chrzaszcz commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Before fixing it, have you been able to identify the causes, in a reproducible manner? I mean, are you able to generate a world with the problem every single time? To be sure you fixed it?

For the cause, for now, i'm not sure, but the tool uses mg_name = singlenode, which generates an empty world. Luanti then fails to propagate light correctly, resulting in black artifacts even when data is written.
But, yes, in every generation, I've encountered the black areas...
I have performed several generation tests to compare the rendering between origin/main and origin/naulan/light-fix and for sure, i can confirm that my branch no longer has any black areas.

@naulan-chrzaszcz

naulan-chrzaszcz commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Wouldn't this cause reverse bug: illuminate parts that shouldn't be ?

Yeah, it's a consequence of my change :/


int nodeIndex = (localZ << 8) | (localY << 4) | localX;
int param0 = block.getParam0()[nodeIndex] & 0xFFFF;
if (block.getNameIdMapping().get(param0).equals("default:water_source") || block.getNameIdMapping().get(param0).equals("air"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is this hardcoded stuff ?

Comment on lines +84 to +92
byte[] param1 = block.getParam1();
for (int localZ = 0; localZ < Block.SIZE; localZ++) {
int worldZ = (blockZ << 4) + localZ;
for (int localX = 0; localX < Block.SIZE; localX++) {
int worldX = (blockX << 4) + localX;
for (int localY = 0; localY < Block.SIZE; localY++) {
int worldY = (blockY << 4) + localY;
if (!limits().contains(worldX, worldZ, worldY))
continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why make things so complicated while a single line in MTVoxel constructor would do the job?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

“Compared to human-generated code, LLMs often generated code with a higher cyclomatic complexity. Our outcomes suggest that LLMs tend to over-engineer solutions, which could result in code that is harder to maintain and more prone to errors during the later stages of software development.”

https://medium.com/@abhishek97.edu/why-ai-cant-write-optimized-code-the-verbosity-problem-and-how-to-solve-it-d9339bb9b290

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IA generated ?
image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Powered by Copilot

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

  - &voxel-water 
    node: default:water_source
    param1: 15
  - &voxel-air
    node: air
    param1: 15

I used the MTVoxel constructor with these parameters... It doesn't work either.
image

@indyteo indyteo Jun 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's because you didn't do what @pyrollo suggested (and his suggestion is also incomplete). His code was defining the param1 to 15 on every instance of the default voxel. You defined it in the parameters file, so only for the instances coming from the user.
There is also MTVoxel.DEFAULTVOXEL which should have that param1 value (I guess). BUT, there's something else!

When a Block is initialized, it is full of air, but not using the MTVoxel.DEFAULTVOXEL nor anything coming from the parameters. So, to make it work, you need to do (in addition to defining param1 = 15 on every instance of air voxel, either in the params like you did, or in the constructor like Pierre-Yves suggested) what you already tried in the first place: Filling the param1 array of the Block with 15 initially. It'll work because, by default, every air voxel (which means every voxel when the block is initialized) will be illuminated, and later on, voxels placed in the block by renderers will either erase that light value (replacing it with their own param1 value, most likely 0), or keep it if it was an air voxel because every air voxel already had a param1 value of 15 when you created them

@pyrollo pyrollo Jun 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I forgot about this initialization. I think DEFAULTVOXEL should be used for initialization if there is no problem with that. We may also have DEFAULTVOXEL definition in settings (next to "format").

Anyway, that param1=15 thing will only be a workaround not solving the issue. Should we adopt that ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"Full bright" (and leaky buildings) is better than seemingly random pitch dark areas... I think.

Proper solution would be to either instruct Luanti to recompute light as soon as it loads the world, or to have a task that computes lighting ourselves (which might not be impossible thanks to the "max voxel" heightmap, it would probably look a little bit like the "populate minimap" task (to light up voxels exposed to direct sunlight), with then a propagation step to nearby voxels. Some margin would probably be required to correctly light up tile borders as well).

I suggest that we accept this workaround for now

@pyrollo pyrollo Jun 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I propose a much better solution, which is more or less the same, but fully relying on parameters:

  • Make DEFAULTVOXEL defined (or overridable) in parameters.
  • Make blocks initialized with DEFAULTVOXEL

This will make it possible to use @naulan-chrzaszcz solution (by setting proper parameters) but it won't be hardcoded.
As a bonus, this may ease adaptation to other games (in Luanti and maybe other).

This shouldn't be much more complicated to do (anyway way simpler than the first proposed solution).

@pyrollo pyrollo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems that PR could be done in two lines and no hardcoded names:

@@ -45,6 +45,8 @@ public class MTVoxel implements Placeable {
         this.type = type;
         this.param1 = param1;
         this.param2 = param2;
+        if (type == DEFAULT_VOXEL.type)
+            this.param1 = 0x0F;
     }

This is still not satisfying because:

  • It supposes default voxel (i.e. air) is defined with param1 = light
  • It only lighten all default voxels but does not do any shadow computation

But it's still better than having large dark areas.

@pyrollo pyrollo removed the To review label Jun 26, 2026
@pyrollo

pyrollo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Wouldn't this cause reverse bug: illuminate parts that shouldn't be ?

Yeah, it's a consequence of my change :/

An unsuspected consequence of that: Usually, only "under the sun" nodes gets a light of 15. This is used by many mods to detect "outside" nodes. A good example is weather mods.

So you will have rain and snow inside your building (for example playing with Kidscode).

@indyteo

indyteo commented Jun 27, 2026

Copy link
Copy Markdown
Member

Isn't there a flag or something in Luanti that indicates that the mapblock currently has nodes generated but lighting not yet computed? Something that we could do to instruct the Luanti engine to compute lightings on the first world loading?

@pyrollo

pyrollo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Isn't there a flag or something in Luanti that indicates that the mapblock currently has nodes generated but lighting not yet computed? Something that we could do to instruct the Luanti engine to compute lightings on the first world loading?

This is a long time bug that appears (it seems) randomly. I thought it had been fixed in recent version but this show that it's not the case. But it doesn't seem to have any simple solution. It looks more like a bug triggered in specific case in engine light calculation. I'll try to find out more.

@pyrollo

pyrollo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

@naulan-chrzaszcz can you provide a working parameter file that produces such shadows ?
I'd like to see if we all have the same issue (I feel we don't).

I tried with main branch (commit 3339370) and Luanti 5.10.0 (quite outdated).

I also have the shadows but... they disappear almost instantly when I get to them.

I guess light is updated when a new empty map block is generated above the dark area.

@pyrollo

pyrollo commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

We are correctly setting lighting_complete flags to 0. This is supposed to trigger lighting computation on loading (actually on loading any neighbor, that would be equivalent to "on loading"). See here (we produce map format version 28).

I can't figure out where these flags are actually used in Luanti.

Interesting, tried the same map with Luanti 5.10.0 and 5.15.2.
On both, shadows appears but with 5.10.0 they immediately disappear.

Now, a big bisect have to be performed !

Other noticeable thing: dark areas are random. Running game several times on the same generated map gives different results. There must be some kind of race condition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Black areas on Minetest generated maps

3 participants