@@ -918,6 +918,7 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
918918 loadedChunks .add (cp );
919919
920920 ChunkData chunkData = chunkDataPair .second ();
921+ boolean didLoadVisibleBlocksFromChunk = false ;
921922 if (chunkData == null ) {
922923 chunkData = EmptyChunkData .INSTANCE ;
923924 }
@@ -1163,6 +1164,8 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
11631164 // X and Z are Chunky position but Y is world position
11641165 emitterGrid .addEmitter (new Grid .EmitterPosition (x , y - origin .y , z , block ));
11651166 }
1167+
1168+ didLoadVisibleBlocksFromChunk = didLoadVisibleBlocksFromChunk || !block .invisible ;
11661169 }
11671170 }
11681171 }
@@ -1193,6 +1196,7 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
11931196 block = palette .get (id );
11941197 chunkData .setBlockAt (x , y , z , id );
11951198 worldOctree .set (id , cp .x * 16 + x - origin .x , y - origin .y , cp .z * 16 + z - origin .z );
1199+ didLoadVisibleBlocksFromChunk = didLoadVisibleBlocksFromChunk || !block .invisible ;
11961200 }
11971201 }
11981202 if (block .isBlockEntity ()) {
@@ -1220,7 +1224,7 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
12201224 }
12211225 }
12221226
1223- if (!chunkData .isEmpty ()) {
1227+ if (!chunkData .isEmpty () && didLoadVisibleBlocksFromChunk ) {
12241228 nonEmptyChunks .add (cp );
12251229 if (dimension .getChunk (cp ).getVersion () == ChunkVersion .PRE_FLATTENING ) {
12261230 legacyChunks .add (cp );
@@ -1248,80 +1252,54 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
12481252// Finalize grass and foliage textures.
12491253// 3x3 box blur.
12501254 ChunkBiomeBlendingHelper chunkBiomeHelper = biomeBlendingHelper .get (cp );
1251- if (chunkBiomeHelper .isBiomeUsed ()) {
1252- if (biomeBlendingRadius > 0 ) {
1253- if (use3dBiomes ) {
1254- ChunkBiomeBlendingHelper [] neighboringChunks = new ChunkBiomeBlendingHelper []{
1255- biomeBlendingHelper .get (new ChunkPosition (cp .x - 1 , cp .z - 1 )),
1256- biomeBlendingHelper .get (new ChunkPosition (cp .x - 1 , cp .z )),
1257- biomeBlendingHelper .get (new ChunkPosition (cp .x - 1 , cp .z + 1 )),
1258- biomeBlendingHelper .get (new ChunkPosition (cp .x , cp .z - 1 )),
1259- biomeBlendingHelper .get (new ChunkPosition (cp .x , cp .z + 1 )),
1260- biomeBlendingHelper .get (new ChunkPosition (cp .x + 1 , cp .z - 1 )),
1261- biomeBlendingHelper .get (new ChunkPosition (cp .x + 1 , cp .z )),
1262- biomeBlendingHelper .get (new ChunkPosition (cp .x + 1 , cp .z + 1 ))
1263- };
1264-
1265- int [] combinedBiomeTransitions = chunkBiomeHelper .combineAndTrimTransitions (neighboringChunks , biomeBlendingRadius );
1266-
1267- // When doing 3D blur we use the list of (vertical) biome transition
1268- // in the chunk or in neighboring ones
1269- // If there is no transition, a 2D blur is enough, otherwise we only
1270- // need to compute the colors around the transitions
1271-
1272- // For example, if loading from y=0 to y=200 with a biome transition at y=20
1273- // and another one at y=50 and with a blur radius of 2 (5*5*5 box)
1274- // We can compute a 2D blur at y=0 and use those color for up to y=17
1275- // For y in [18, 21] we need to compute the real 3D blur (because of the biome transition
1276- // at y=20 and the blur radius of 2)
1277- // Then we can compute the 2D blur at y=22 and use those colors for up to y=47
1278- // And so on, 3D blur for y in [48, 51] and 2D blur for y in [52,200]
1279-
1280- // As such, in spirit every transition make us compute an additional 16*16*(2*biomeBlendingRadius) 3D blur
1281- // and a 16*16 2D blur (that can be combined in a 16*16*(2*biomeBlendingRadius+1) 3D blur)
1282- // (ignoring cases where transition are close to one another which are handled by the code)
1283-
1284- // Note that having a single (x, y) column that effectively has a biome transition
1285- // in the chunk are a neighboring chunk causes us to compute the 3D blur for the whole 16*16
1286- // vertical slice of the chunk. Because vertical biome transition are pretty rare,
1287- // that's probably ok.
1288- int nextY = chunkBiomeHelper .getyMinBiomeRelevant ();
1289-
1290- for (int i = 0 ; i < combinedBiomeTransitions .length ; ++i ) {
1291- int transition = combinedBiomeTransitions [i ];
1292- if (nextY < transition - biomeBlendingRadius ) {
1293- // Do a 2d blur to fill up to the height affected by the transition
1294- BiomeBlendingUtility .chunk2DBlur (
1295- cp ,
1296- biomeBlendingRadius ,
1297- nextY ,
1298- transition - biomeBlendingRadius ,
1299- origin ,
1300- biomePaletteIdxStructure ,
1301- biomePalette ,
1302- nonEmptyChunks ,
1303- grassTexture ,
1304- foliageTexture ,
1305- dryFoliageTexture ,
1306- waterTexture );
1307- nextY = transition - biomeBlendingRadius ;
1308- }
1309-
1310- // Do a 3D blur to fill the next 2*biomeBlendingRadius layers
1311- // or more if the next transition is close by, in which case
1312- // both transition (or even more) are handled by a bigger 3D blur
1313- int maxYWorkedOn = transition + biomeBlendingRadius ;
1314- while (i < combinedBiomeTransitions .length - 1 && maxYWorkedOn >= combinedBiomeTransitions [i + 1 ] - biomeBlendingRadius ) {
1315- // Extends the 3D blur to enclose the next transition as well
1316- maxYWorkedOn = combinedBiomeTransitions [i + 1 ] + biomeBlendingRadius ;
1317- ++i ;
1318- }
1319- int maxYWorkedOnClamped = Math .min (maxYWorkedOn , chunkBiomeHelper .getyMaxBiomeRelevant ());
1320- BiomeBlendingUtility .chunk3DBlur (
1255+ boolean biomeUsed = chunkBiomeHelper .isBiomeUsed ();
1256+ if (biomeBlendingRadius > 0 ) {
1257+ if (use3dBiomes ) {
1258+ ChunkBiomeBlendingHelper [] neighboringChunks = new ChunkBiomeBlendingHelper []{
1259+ biomeBlendingHelper .get (new ChunkPosition (cp .x - 1 , cp .z - 1 )),
1260+ biomeBlendingHelper .get (new ChunkPosition (cp .x - 1 , cp .z )),
1261+ biomeBlendingHelper .get (new ChunkPosition (cp .x - 1 , cp .z + 1 )),
1262+ biomeBlendingHelper .get (new ChunkPosition (cp .x , cp .z - 1 )),
1263+ biomeBlendingHelper .get (new ChunkPosition (cp .x , cp .z + 1 )),
1264+ biomeBlendingHelper .get (new ChunkPosition (cp .x + 1 , cp .z - 1 )),
1265+ biomeBlendingHelper .get (new ChunkPosition (cp .x + 1 , cp .z )),
1266+ biomeBlendingHelper .get (new ChunkPosition (cp .x + 1 , cp .z + 1 ))
1267+ };
1268+
1269+ int [] combinedBiomeTransitions = chunkBiomeHelper .combineAndTrimTransitions (neighboringChunks , biomeBlendingRadius );
1270+
1271+ // When doing 3D blur we use the list of (vertical) biome transition
1272+ // in the chunk or in neighboring ones
1273+ // If there is no transition, a 2D blur is enough, otherwise we only
1274+ // need to compute the colors around the transitions
1275+
1276+ // For example, if loading from y=0 to y=200 with a biome transition at y=20
1277+ // and another one at y=50 and with a blur radius of 2 (5*5*5 box)
1278+ // We can compute a 2D blur at y=0 and use those color for up to y=17
1279+ // For y in [18, 21] we need to compute the real 3D blur (because of the biome transition
1280+ // at y=20 and the blur radius of 2)
1281+ // Then we can compute the 2D blur at y=22 and use those colors for up to y=47
1282+ // And so on, 3D blur for y in [48, 51] and 2D blur for y in [52,200]
1283+
1284+ // As such, in spirit every transition make us compute an additional 16*16*(2*biomeBlendingRadius) 3D blur
1285+ // and a 16*16 2D blur (that can be combined in a 16*16*(2*biomeBlendingRadius+1) 3D blur)
1286+ // (ignoring cases where transition are close to one another which are handled by the code)
1287+
1288+ // Note that having a single (x, y) column that effectively has a biome transition
1289+ // in the chunk are a neighboring chunk causes us to compute the 3D blur for the whole 16*16
1290+ // vertical slice of the chunk. Because vertical biome transition are pretty rare,
1291+ // that's probably ok.
1292+ int nextY = chunkBiomeHelper .getyMinBiomeRelevant ();
1293+
1294+ for (int i = 0 ; i < combinedBiomeTransitions .length ; ++i ) {
1295+ int transition = combinedBiomeTransitions [i ];
1296+ if (nextY < transition - biomeBlendingRadius ) {
1297+ // Do a 2d blur to fill up to the height affected by the transition
1298+ BiomeBlendingUtility .chunk2DBlur (
13211299 cp ,
13221300 biomeBlendingRadius ,
13231301 nextY ,
1324- maxYWorkedOnClamped + 1 ,
1302+ transition - biomeBlendingRadius ,
13251303 origin ,
13261304 biomePaletteIdxStructure ,
13271305 biomePalette ,
@@ -1330,30 +1308,42 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
13301308 foliageTexture ,
13311309 dryFoliageTexture ,
13321310 waterTexture );
1333- nextY = maxYWorkedOnClamped + 1 ;
1311+ nextY = transition - biomeBlendingRadius ;
13341312 }
13351313
1336- // Last 2D blur that extent up to the top
1337- if (nextY <= chunkBiomeHelper .getyMaxBiomeRelevant ()) {
1338- BiomeBlendingUtility .chunk2DBlur (
1339- cp ,
1340- biomeBlendingRadius ,
1341- nextY ,
1342- chunkBiomeHelper .getyMaxBiomeRelevant () + 1 ,
1343- origin ,
1344- biomePaletteIdxStructure ,
1345- biomePalette ,
1346- nonEmptyChunks ,
1347- grassTexture ,
1348- foliageTexture ,
1349- dryFoliageTexture ,
1350- waterTexture );
1314+ // Do a 3D blur to fill the next 2*biomeBlendingRadius layers
1315+ // or more if the next transition is close by, in which case
1316+ // both transition (or even more) are handled by a bigger 3D blur
1317+ int maxYWorkedOn = transition + biomeBlendingRadius ;
1318+ while (i < combinedBiomeTransitions .length - 1 && maxYWorkedOn >= combinedBiomeTransitions [i + 1 ] - biomeBlendingRadius ) {
1319+ // Extends the 3D blur to enclose the next transition as well
1320+ maxYWorkedOn = combinedBiomeTransitions [i + 1 ] + biomeBlendingRadius ;
1321+ ++i ;
13511322 }
1352- } else {
1323+ int maxYWorkedOnClamped = Math .min (maxYWorkedOn , chunkBiomeHelper .getyMaxBiomeRelevant ());
1324+ BiomeBlendingUtility .chunk3DBlur (
1325+ cp ,
1326+ biomeBlendingRadius ,
1327+ nextY ,
1328+ maxYWorkedOnClamped + 1 ,
1329+ origin ,
1330+ biomePaletteIdxStructure ,
1331+ biomePalette ,
1332+ nonEmptyChunks ,
1333+ grassTexture ,
1334+ foliageTexture ,
1335+ dryFoliageTexture ,
1336+ waterTexture );
1337+ nextY = maxYWorkedOnClamped + 1 ;
1338+ }
1339+
1340+ // Last 2D blur that extent up to the top
1341+ if (nextY <= chunkBiomeHelper .getyMaxBiomeRelevant ()) {
13531342 BiomeBlendingUtility .chunk2DBlur (
13541343 cp ,
13551344 biomeBlendingRadius ,
1356- 0 , 1 ,
1345+ nextY ,
1346+ chunkBiomeHelper .getyMaxBiomeRelevant () + 1 ,
13571347 origin ,
13581348 biomePaletteIdxStructure ,
13591349 biomePalette ,
@@ -1363,41 +1353,75 @@ public synchronized void loadChunks(TaskTracker taskTracker, World world, Map<Re
13631353 dryFoliageTexture ,
13641354 waterTexture );
13651355 }
1356+
1357+ // TODO we could skip blending the grass, foliage and dry foliage textures in this case
1358+ if (!biomeUsed ) {
1359+ grassTexture = biomeStructureFactory .create ();
1360+ foliageTexture = biomeStructureFactory .create ();
1361+ dryFoliageTexture = biomeStructureFactory .create ();
1362+ // the water texture is still used to check for loaded chunks and tint the water plane
1363+ }
13661364 } else {
1367- if (use3dBiomes ) {
1368- for (int sectionY = yMin >> 4 ; sectionY < (yMax - 1 >> 4 ) + 1 ; sectionY ++) {
1369- for (int y = 0 ; y < 16 ; y ++) {
1370- int wy = sectionY * 16 + y ;
1371- for (int x = 0 ; x < 16 ; ++x ) {
1372- int wx = cp .x * Chunk .X_MAX + x ;
1373- for (int z = 0 ; z < 16 ; ++z ) {
1374- int wz = cp .z * Chunk .Z_MAX + z ;
1375-
1376- int id = biomePaletteIdxStructure .get (wx , wy , wz );
1377-
1378- Biome biome = biomePalette .get (id );
1379- grassTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .grassColorLinear );
1380- foliageTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .foliageColorLinear );
1381- dryFoliageTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .dryFoliageColorLinear );
1382- waterTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .waterColorLinear );
1383- }
1365+ BiomeBlendingUtility .chunk2DBlur (
1366+ cp ,
1367+ biomeBlendingRadius ,
1368+ 0 , 1 ,
1369+ origin ,
1370+ biomePaletteIdxStructure ,
1371+ biomePalette ,
1372+ nonEmptyChunks ,
1373+ grassTexture ,
1374+ foliageTexture ,
1375+ dryFoliageTexture ,
1376+ waterTexture );
1377+
1378+ // TODO we could skip blending the grass, foliage and dry foliage textures in this case
1379+ if (!biomeUsed ) {
1380+ grassTexture = biomeStructureFactory .create ();
1381+ foliageTexture = biomeStructureFactory .create ();
1382+ dryFoliageTexture = biomeStructureFactory .create ();
1383+ // the water texture is still used to check for loaded chunks and tint the water plane
1384+ }
1385+ }
1386+ } else {
1387+ if (use3dBiomes ) {
1388+ for (int sectionY = yMin >> 4 ; sectionY < (yMax - 1 >> 4 ) + 1 ; sectionY ++) {
1389+ for (int y = 0 ; y < 16 ; y ++) {
1390+ int wy = sectionY * 16 + y ;
1391+ for (int x = 0 ; x < 16 ; ++x ) {
1392+ int wx = cp .x * Chunk .X_MAX + x ;
1393+ for (int z = 0 ; z < 16 ; ++z ) {
1394+ int wz = cp .z * Chunk .Z_MAX + z ;
1395+
1396+ int id = biomePaletteIdxStructure .get (wx , wy , wz );
1397+
1398+ Biome biome = biomePalette .get (id );
1399+ if (biomeUsed ) {
1400+ grassTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .grassColorLinear );
1401+ foliageTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .foliageColorLinear );
1402+ dryFoliageTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .dryFoliageColorLinear );
1403+ }
1404+ // the water texture is used to check for loaded chunks and tint the water plane, so we always need that one
1405+ waterTexture .set (cp .x * 16 + x - origin .x , sectionY * 16 + y - origin .y , cp .z * 16 + z - origin .z , biome .waterColorLinear );
13841406 }
13851407 }
13861408 }
1387- } else {
1388- for (int x = 0 ; x < 16 ; ++x ) {
1389- int wx = cp .x * 16 + x ;
1390- for (int z = 0 ; z < 16 ; ++z ) {
1391- int wz = cp .z * 16 + z ;
1392-
1393- int id = biomePaletteIdxStructure .get (wx , 0 , wz );
1394- Biome biome = biomePalette .get (id );
1395-
1409+ }
1410+ } else {
1411+ for (int x = 0 ; x < 16 ; ++x ) {
1412+ int wx = cp .x * 16 + x ;
1413+ for (int z = 0 ; z < 16 ; ++z ) {
1414+ int wz = cp .z * 16 + z ;
1415+
1416+ int id = biomePaletteIdxStructure .get (wx , 0 , wz );
1417+ Biome biome = biomePalette .get (id );
1418+ if (biomeUsed ) {
13961419 grassTexture .set (cp .x * 16 + x - origin .x , 0 , cp .z * 16 + z - origin .z , biome .grassColorLinear );
13971420 foliageTexture .set (cp .x * 16 + x - origin .x , 0 , cp .z * 16 + z - origin .z , biome .foliageColorLinear );
13981421 dryFoliageTexture .set (cp .x * 16 + x - origin .x , 0 , cp .z * 16 + z - origin .z , biome .dryFoliageColorLinear );
1399- waterTexture .set (cp .x * 16 + x - origin .x , 0 , cp .z * 16 + z - origin .z , biome .waterColorLinear );
14001422 }
1423+ // the water texture is used to check for loaded chunks and tint the water plane, so we always need that one
1424+ waterTexture .set (cp .x * 16 + x - origin .x , 0 , cp .z * 16 + z - origin .z , biome .waterColorLinear );
14011425 }
14021426 }
14031427 }
0 commit comments