Slightly improve LOD generation speed and add code related to heightmap

The slight speed increase is done by only generating the chunk to the "FEATURES" status instead of "FULL".

The code related to the heightmap is currently unused, since the LOD color generation requires blocks. Although it may have some use in the future so I will keep it in, albeit commented out.
This commit is contained in:
James Seibel
2021-03-24 19:09:30 -05:00
parent 14a06c220b
commit e20833225f
4 changed files with 150 additions and 39 deletions
@@ -10,8 +10,8 @@ import com.backsun.lod.objects.LodWorld;
import com.backsun.lod.util.LodUtils;
import net.minecraft.world.DimensionType;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.server.ServerWorld;
/**
@@ -20,7 +20,7 @@ import net.minecraft.world.server.ServerWorld;
* (specifically: Lod World, Dimension, Region, and Chunk objects)
*
* @author James Seibel
* @version 2-22-2021
* @version 3-24-2021
*/
public class LodBuilder
{
@@ -40,8 +40,9 @@ public class LodBuilder
/**
* Returns LodWorld so that it can be passed
* to the LodRenderer.
* @param dimensionType
*/
public LodWorld generateLodChunkAsync(Chunk chunk)
public LodWorld generateLodChunkAsync(IChunk chunk, DimensionType dim)
{
if (lodWorld != null)
// is this chunk from the same world as the lodWorld?
@@ -55,11 +56,9 @@ public class LodBuilder
// given a valid chunk object
// (Minecraft often gives back empty
// or null chunks in this method)
if (chunk == null || !isValidChunk(chunk))
if (chunk == null || !hasBlockData(chunk))
return lodWorld;
DimensionType dim = chunk.getWorld().getDimensionType();
ServerWorld world = LodUtils.getServerWorldFromDimension(dim);
@@ -71,6 +70,7 @@ public class LodBuilder
try
{
LodChunk lod = new LodChunk(chunk, world);
LodDimension lodDim;
if (lodWorld == null)
@@ -103,11 +103,14 @@ public class LodBuilder
return lodWorld;
}
/**
* Return whether the given chunk
* has any data in it.
*/
public boolean isValidChunk(Chunk chunk)
public boolean hasBlockData(IChunk chunk)
{
ChunkSection[] blockStorage = chunk.getSections();
@@ -2,23 +2,24 @@ package com.backsun.lod.objects;
import java.awt.Color;
import com.backsun.lod.util.enums.ColorDirection;
import com.backsun.lod.util.enums.LodCorner;
import com.backsun.lod.util.enums.LodLocation;
import com.backsun.lod.enums.ColorDirection;
import com.backsun.lod.enums.LodCorner;
import com.backsun.lod.enums.LodLocation;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.gen.Heightmap;
/**
* This object contains position
* and color data for an LOD object.
*
* @author James Seibel
* @version 03-19-2021
* @version 03-24-2021
*/
public class LodChunk
{
@@ -43,6 +44,7 @@ public class LodChunk
// since each layer is 1/4 the chunk
/** The x coordinate of the chunk. */
public int x;
/** The z coordinate of the chunk. */
@@ -56,7 +58,6 @@ public class LodChunk
/** The average color of each 6 cardinal directions */
public Color colors[];
/** If true that means this LodChunk is just a placeholder and
* no LOD has been generated for this chunk location */
private boolean emptyPlaceholder = false;
@@ -210,7 +211,7 @@ public class LodChunk
* @throws IllegalArgumentException
* thrown if either the chunk or world is null.
*/
public LodChunk(Chunk chunk, World world) throws IllegalArgumentException
public LodChunk(IChunk chunk, World world) throws IllegalArgumentException
{
if(chunk == null)
{
@@ -249,11 +250,16 @@ public class LodChunk
//=====================//
// constructor helpers //
//=====================//
/** GENERATE_TOP, GENERATE_BOTTOM */
private enum SectionGenerationMode
{
GENERATE_TOP,
GENERATE_BOTTOM;
}
/**
* Generate the height for the given LodLocation, either the top or bottom.
@@ -261,13 +267,8 @@ public class LodChunk
* If invalid/null/empty chunks are given
* crashes may occur.
*/
public short generateLodCorner(Chunk chunk, SectionGenerationMode generationMode, LodLocation lodLoc)
public short generateLodCorner(IChunk chunk, SectionGenerationMode sectionGenMode, LodLocation lodLoc)
{
// should have a length of 16
// (each storage is 16x16x16 and the
// world height is 256)
ChunkSection[] chunkSections = chunk.getSections();
// if this LodChunk was a empltyPlaceholder before
// it will hold some data after this method's completion,
// make sure it is handled like a normal LodChunk
@@ -322,17 +323,18 @@ public class LodChunk
}
if(generationMode == SectionGenerationMode.GENERATE_TOP)
// should have a length of 16
// (each storage is 16x16x16 and the
// world height is 256)
ChunkSection[] chunkSections = chunk.getSections();
if(sectionGenMode == SectionGenerationMode.GENERATE_TOP)
return determineTopPoint(chunkSections, startX, endX, startZ, endZ);
else
return determineBottomPoint(chunkSections, startX, endX, startZ, endZ);
}
/** GENERATE_TOP, GENERATE_BOTTOM */
private enum SectionGenerationMode
{
GENERATE_TOP,
GENERATE_BOTTOM;
}
/**
* Find the lowest valid point from the bottom.
@@ -340,7 +342,7 @@ public class LodChunk
private short determineBottomPoint(ChunkSection[] chunkSections, int startX, int endX, int startZ, int endZ)
{
// search from the bottom up
for(int i = 0; i < chunkSections.length; i++)
for(int i = 0; i < CHUNK_DATA_WIDTH; i++)
{
for(int y = 0; y < CHUNK_DATA_HEIGHT; y++)
{
@@ -359,6 +361,19 @@ public class LodChunk
// we never found a valid LOD point
return -1;
}
/**
* Find the lowest valid point from the bottom.
*/
@SuppressWarnings("unused")
private short determineBottomPoint(Heightmap heightmap, int startX, int endX, int startZ, int endZ)
{
// the heightmap only shows how high the blocks go, it
// doesn't have any info about how low they go
return 0;
}
/**
* Find the highest valid point from the Top
@@ -385,6 +400,33 @@ public class LodChunk
return -1;
}
/**
* Find the highest valid point from the Top
*/
@SuppressWarnings("unused")
private short determineTopPoint(Heightmap heightmap, int startX, int endX, int startZ, int endZ)
{
short highest = 0;
for(int x = startX; x < endX; x++)
{
for(int z = startZ; z < endZ; z++)
{
short newHeight = (short) heightmap.getHeight(x, z);
if (newHeight > highest)
highest = newHeight;
}
}
return highest;
}
/**
* Is the layer between the given X, Z, and dataIndex
* values a valid LOD point?
@@ -433,7 +475,7 @@ public class LodChunk
* Generate the color of the given ColorDirection at the given chunk
* in the given world.
*/
private Color generateLodColorForDirection(Chunk chunk, World world, ColorDirection colorDir)
private Color generateLodColorForDirection(IChunk chunk, World world, ColorDirection colorDir)
{
Minecraft mc = Minecraft.getInstance();
BlockColors bc = mc.getBlockColors();
@@ -464,13 +506,15 @@ public class LodChunk
*
* @throws IllegalArgumentException if given a ColorDirection other than TOP or BOTTOM
*/
private Color generateLodColorVertical(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
private Color generateLodColorVertical(IChunk chunk, ColorDirection colorDir, World world, BlockColors bc)
{
if(colorDir != ColorDirection.TOP && colorDir != ColorDirection.BOTTOM)
{
throw new IllegalArgumentException("generateLodColorVertical only accepts the ColorDirection TOP or BOTTOM");
}
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
@@ -540,6 +584,38 @@ public class LodChunk
blue /= numbOfBlocks;
return new Color(red, green, blue);
/*
* unused variation that can be used with only the heightmap,
* although it just returns the foliage color, so it shouldn't
* be used normally.
Heightmap heightmap = chunk.getHeightmap(Heightmap.Type.WORLD_SURFACE_WG);
int numbOfBlocks = CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH;
int red = 0;
int green = 0;
int blue = 0;
for(int x = 0; x < CHUNK_DATA_WIDTH; x++)
{
for(int z = 0; z < CHUNK_DATA_WIDTH; z++)
{
Biome biome = chunk.getBiomes().getNoiseBiome(x,z, heightmap.getHeight(x, z));
Color c = intToColor(biome.getFoliageColor());
red += c.getRed();
green += c.getGreen();
blue += c.getBlue();
}
}
red /= numbOfBlocks;
green /= numbOfBlocks;
blue /= numbOfBlocks;
return new Color(red, green, blue);
*/
}
/**
@@ -547,7 +623,7 @@ public class LodChunk
*
* @throws IllegalArgumentException if given a ColorDirection other than N, S, W, E (North, South, East, West)
*/
private Color generateLodColorHorizontal(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
private Color generateLodColorHorizontal(IChunk chunk, ColorDirection colorDir, World world, BlockColors bc)
{
if(colorDir != ColorDirection.N && colorDir != ColorDirection.S && colorDir != ColorDirection.E && colorDir != ColorDirection.W)
{
@@ -674,6 +750,7 @@ public class LodChunk
return new Color(red, green, blue);
}
/**
* Convert a BlockColors int into a Color object.
*/
@@ -26,7 +26,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
* and is the starting point for most of this program.
*
* @author James_Seibel
* @version 03-19-2021
* @version 03-24-2021
*/
public class ClientProxy
{
@@ -124,7 +124,7 @@ public class ClientProxy
public void chunkLoadEvent(ChunkEvent.Load event)
{
if (event.getChunk().getClass() == Chunk.class)
lodWorld = lodBuilder.generateLodChunkAsync((Chunk) event.getChunk());
lodWorld = lodBuilder.generateLodChunkAsync(event.getChunk(), event.getWorld().getDimensionType());
}
@@ -26,7 +26,6 @@ import com.backsun.lod.renderer.LodRenderer;
import com.backsun.lod.util.LodUtils;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.server.ServerWorld;
@@ -36,7 +35,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker;
* This is used to generate a LodChunk at a given ChunkPos.
*
* @author James Seibel
* @version 03-19-2021
* @version 03-24-2021
*/
public class SingleLodChunkGenWorker implements IWorker
{
@@ -73,15 +72,20 @@ public class SingleLodChunkGenWorker implements IWorker
// be added to the current LodDimension
if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE))
{
IChunk chunk = serverWorld.getChunk(x, z, ChunkStatus.EMPTY, true);
IChunk chunk;// = serverWorld.getChunk(x, z, ChunkStatus.EMPTY, true);
chunk = serverWorld.getChunk(x, z, ChunkStatus.FULL);
//long startTime = System.currentTimeMillis();
chunk = serverWorld.getChunk(x, z, ChunkStatus.FEATURES);
//long endTime = System.currentTimeMillis();
//System.out.println(endTime - startTime + "\t" + lodBuilder.hasBlockData(chunk));
lodBuilder.generateLodChunkAsync((Chunk) chunk);
lodBuilder.generateLodChunkAsync(chunk, serverWorld.getDimensionType());
// this is called so that the new LOD chunk is drawn
// after it is generated
lodRenderer.regenerateLODsNextFrame();
// useful for debugging
//if (lodDim.getLodFromCoordinates(x, z) != null)
// System.out.println(x + " " + z + " Success!");
@@ -103,4 +107,31 @@ public class SingleLodChunkGenWorker implements IWorker
}
return true;
}
/*
* performance/generation tests related to
* serverWorld.getChunk(x, z, ChunkStatus. *** )
true/false is whether they generated blocks or not
the time is how long it took to generate
ChunkStatus.FULL 30 - 50 ms true
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
ChunkStatus.BIOMES 1 - 10 ms false (no height)
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
ChunkStatus.FEATURES 7 - 25 ms true
ChunkStatus.LIGHT 20 - 40 ms true
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
ChunkStatus.SPAWN 50 - 80 ms true
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
At this point I would suggest using FEATURES, as it generates snow and trees
(and any other object that is needed to make biomes distinct)
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
*/
}