refactor, cleanup, and perf improvements

This commit is contained in:
dfsek
2021-01-05 19:21:42 -07:00
parent 4ad2db3ca8
commit 47cad8a30b
98 changed files with 178 additions and 305 deletions
@@ -10,7 +10,7 @@ import com.dfsek.terra.api.world.generation.GenerationPhase;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.generation.items.flora.FloraLayer;
import com.dfsek.terra.population.items.flora.FloraLayer;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
@@ -13,7 +13,7 @@ import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.generation.items.TerraStructure;
import com.dfsek.terra.population.items.TerraStructure;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
@@ -10,7 +10,7 @@ import com.dfsek.terra.api.world.generation.GenerationPhase;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.generation.items.tree.TreeLayer;
import com.dfsek.terra.population.items.tree.TreeLayer;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
@@ -0,0 +1,41 @@
package com.dfsek.terra.population.items;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.platform.world.Chunk;
import java.util.Random;
public abstract class PlaceableLayer<T> {
protected final double density;
protected final Range level;
protected final ProbabilityCollection<T> layer;
protected final FastNoiseLite noise;
public PlaceableLayer(double density, Range level, ProbabilityCollection<T> layer, FastNoiseLite noise) {
this.density = density;
this.level = level;
this.layer = layer;
this.noise = noise;
}
public FastNoiseLite getNoise() {
return noise;
}
public double getDensity() {
return density;
}
public Range getLevel() {
return level;
}
public ProbabilityCollection<T> getLayer() {
return layer;
}
public abstract void place(Chunk chunk, Vector2 coords, Random random);
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.population.items;
import com.dfsek.terra.api.math.GridSpawn;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.config.templates.StructureTemplate;
public class TerraStructure {
private final ProbabilityCollection<StructureScript> structure;
private final Range spawnStart;
private final GridSpawn spawn;
private final StructureTemplate template;
public TerraStructure(ProbabilityCollection<StructureScript> structures, Range spawnStart, GridSpawn spawn, StructureTemplate template) {
this.structure = structures;
this.spawnStart = spawnStart;
this.spawn = spawn;
this.template = template;
}
public StructureTemplate getTemplate() {
return template;
}
public ProbabilityCollection<StructureScript> getStructure() {
return structure;
}
public Range getSpawnStart() {
return spawnStart;
}
public GridSpawn getSpawn() {
return spawn;
}
}
@@ -0,0 +1,44 @@
package com.dfsek.terra.population.items.flora;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.util.GlueList;
import com.dfsek.terra.api.world.flora.Flora;
import java.util.List;
/**
* Flora that is just 1 layer of a single block.
*/
public class BlockFlora implements Flora {
private final BlockData data;
public BlockFlora(BlockData data) {
this.data = data;
}
@Override
public List<Block> getValidSpawnsAt(Chunk chunk, int x, int z, Range range) {
Block current = chunk.getBlock(x, range.getMin(), z);
List<Block> blocks = new GlueList<>();
for(int y : range) {
if(y > 255 || y < 0) continue;
current = current.getRelative(BlockFace.UP);
if(current.getType().isSolid() && current.getRelative(BlockFace.UP).isEmpty()) {
blocks.add(current); // Add all blocks that are solid with air directly above.
}
}
return blocks;
}
@Override
public boolean plant(Location location) {
location.add(0, 1, 0).getBlock().setBlockData(data, true);
return true;
}
}
@@ -0,0 +1,52 @@
package com.dfsek.terra.population.items.flora;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.util.GlueList;
import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.util.MaterialSet;
import java.util.List;
public class ConstantFlora implements Flora {
private final List<BlockData> data;
private final MaterialSet spawns;
public ConstantFlora(MaterialSet spawns, List<BlockData> data) {
this.data = data;
this.spawns = spawns;
}
@Override
public List<Block> getValidSpawnsAt(Chunk chunk, int x, int z, Range check) {
List<Block> blocks = new GlueList<>();
for(int y : check) {
Block block = chunk.getBlock(x, y, z);
if(spawns.contains(block.getType()) && valid(block)) {
blocks.add(chunk.getBlock(x, y, z));
}
}
return blocks;
}
private boolean valid(Block block) {
for(int i = 1; i < data.size() + 1; i++) {
block = block.getRelative(BlockFace.UP);
if(!block.isEmpty()) return false;
}
return true;
}
@Override
public boolean plant(Location l) {
for(int i = 1; i < data.size() + 1; i++) {
l.clone().add(0, i, 0).getBlock().setBlockData(data.get(i - 1), false);
}
return true;
}
}
@@ -0,0 +1,28 @@
package com.dfsek.terra.population.items.flora;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.population.items.PlaceableLayer;
import java.util.Random;
public class FloraLayer extends PlaceableLayer<Flora> {
public FloraLayer(double density, Range level, ProbabilityCollection<Flora> layer, FastNoiseLite noise) {
super(density, level, layer, noise);
}
public double getDensity() {
return density;
}
@Override
public void place(Chunk chunk, Vector2 coords, Random random) {
Flora item = noise == null ? layer.get(random) : layer.get(noise, (chunk.getX() << 4) + coords.getX(), (chunk.getZ() << 4) + coords.getZ());
item.getValidSpawnsAt(chunk, (int) coords.getX(), (int) coords.getZ(), level).forEach(block -> item.plant(block.getLocation()));
}
}
@@ -0,0 +1,143 @@
package com.dfsek.terra.population.items.flora;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.BlockFace;
import com.dfsek.terra.api.platform.block.data.Directional;
import com.dfsek.terra.api.platform.block.data.MultipleFacing;
import com.dfsek.terra.api.platform.block.data.Rotatable;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.util.GlueList;
import com.dfsek.terra.api.world.flora.Flora;
import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.util.MaterialSet;
import net.jafama.FastMath;
import java.util.ArrayList;
import java.util.List;
public class TerraFlora implements Flora {
private final Palette<BlockData> floraPalette;
private final boolean physics;
private final boolean ceiling;
private final MaterialSet irrigable;
private final MaterialSet spawnable;
private final MaterialSet replaceable;
private final MaterialSet testRotation;
private final int maxPlacements;
private final Search search;
private final boolean spawnBlacklist;
private final int irrigableOffset;
private final TerraPlugin main;
public TerraFlora(Palette<BlockData> floraPalette, boolean physics, boolean ceiling, MaterialSet irrigable, MaterialSet spawnable, MaterialSet replaceable, MaterialSet testRotation, int maxPlacements, Search search, boolean spawnBlacklist, int irrigableOffset, TerraPlugin main) {
this.floraPalette = floraPalette;
this.physics = physics;
this.testRotation = testRotation;
this.spawnBlacklist = spawnBlacklist;
this.ceiling = ceiling;
this.irrigable = irrigable;
this.spawnable = spawnable;
this.replaceable = replaceable;
this.maxPlacements = maxPlacements;
this.search = search;
this.irrigableOffset = irrigableOffset;
this.main = main;
}
@Override
public List<Block> getValidSpawnsAt(Chunk chunk, int x, int z, Range range) {
int size = floraPalette.getSize();
Block current = chunk.getBlock(x, search.equals(Search.UP) ? range.getMin() : range.getMax(), z);
List<Block> blocks = new ArrayList<>();
for(int y : range) {
if(y > 255 || y < 0) continue;
current = current.getRelative(search.equals(Search.UP) ? BlockFace.UP : BlockFace.DOWN);
if((spawnBlacklist != spawnable.contains(current.getType())) && isIrrigated(current.getRelative(BlockFace.UP, irrigableOffset)) && valid(size, current)) {
blocks.add(current);
if(maxPlacements > 0 && blocks.size() >= maxPlacements) break;
}
}
return blocks;
}
private boolean valid(int size, Block block) {
for(int i = 0; i < size; i++) { // Down if ceiling, up if floor
if(block.getY() + 1 > 255 || block.getY() < 0) return false;
block = block.getRelative(ceiling ? BlockFace.DOWN : BlockFace.UP);
if(!replaceable.contains(block.getType())) return false;
}
return true;
}
private boolean isIrrigated(Block b) {
if(irrigable == null) return true;
return irrigable.contains(b.getRelative(BlockFace.NORTH).getType())
|| irrigable.contains(b.getRelative(BlockFace.SOUTH).getType())
|| irrigable.contains(b.getRelative(BlockFace.EAST).getType())
|| irrigable.contains(b.getRelative(BlockFace.WEST).getType());
}
@Override
public boolean plant(Location location) {
WorldHandle handle = main.getWorldHandle();
boolean doRotation = testRotation.size() > 0;
int size = floraPalette.getSize();
int c = ceiling ? -1 : 1;
List<BlockFace> faces = doRotation ? getFaces(location.clone().add(0, c, 0).getBlock()) : new GlueList<>();
if(doRotation && faces.size() == 0) return false; // Don't plant if no faces are valid.
BlockFace oneFace = doRotation ? faces.get(new FastRandom(location.getBlockX() ^ location.getBlockZ()).nextInt(faces.size())) : null; // Get random face.
for(int i = 0; FastMath.abs(i) < size; i += c) { // Down if ceiling, up if floor
int lvl = (FastMath.abs(i));
BlockData data = floraPalette.get((ceiling ? lvl : size - lvl - 1), location.getBlockX(), location.getBlockZ()).clone();
if(doRotation) {
if(data instanceof Directional) {
((Directional) data).setFacing(oneFace);
} else if(data instanceof MultipleFacing) {
MultipleFacing o = (MultipleFacing) data;
for(BlockFace face : o.getFaces()) o.setFace(face, false);
for(BlockFace face : faces) o.setFace(face, true);
} else if(data instanceof Rotatable) {
((Rotatable) data).setRotation(oneFace);
}
}
handle.setBlockData(location.clone().add(0, i + c, 0).getBlock(), data, physics);
}
return true;
}
private List<BlockFace> getFaces(Block b) {
List<BlockFace> faces = new GlueList<>();
test(faces, BlockFace.NORTH, b);
test(faces, BlockFace.SOUTH, b);
test(faces, BlockFace.EAST, b);
test(faces, BlockFace.WEST, b);
return faces;
}
private void test(List<BlockFace> faces, BlockFace f, Block b) {
if(testRotation.contains(b.getRelative(f).getType())) faces.add(f);
}
public enum Search {
UP,
DOWN
}
}
@@ -0,0 +1,50 @@
package com.dfsek.terra.population.items.ores;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.util.MaterialSet;
import java.util.Random;
public class DeformedSphereOre extends Ore {
private final double deform;
private final double deformFrequency;
private final Range size;
public DeformedSphereOre(BlockData material, MaterialSet replaceable, boolean applyGravity, double deform, double deformFrequency, Range size, TerraPlugin main) {
super(material, replaceable, applyGravity, main);
this.deform = deform;
this.deformFrequency = deformFrequency;
this.size = size;
}
@Override
public void generate(Vector3 origin, Chunk c, Random r) {
WorldHandle handle = main.getWorldHandle();
FastNoiseLite ore = new FastNoiseLite(r.nextInt());
ore.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
ore.setFrequency(deformFrequency);
int rad = size.get(r);
for(int x = -rad; x <= rad; x++) {
for(int y = -rad; y <= rad; y++) {
for(int z = -rad; z <= rad; z++) {
Vector3 oreLoc = origin.clone().add(new Vector3(x, y, z));
if(oreLoc.getBlockX() > 15 || oreLoc.getBlockZ() > 15 || oreLoc.getBlockY() > 255 || oreLoc.getBlockX() < 0 || oreLoc.getBlockZ() < 0 || oreLoc.getBlockY() < 0)
continue;
if(oreLoc.distance(origin) < (rad + 0.5) * ((ore.getNoise(x, y, z) + 1) * deform)) {
Block b = c.getBlock(oreLoc.getBlockX(), oreLoc.getBlockY(), oreLoc.getBlockZ());
if(getReplaceable().contains(b.getType()) && b.getLocation().getY() >= 0)
handle.setBlockData(b, getMaterial(), isApplyGravity());
}
}
}
}
}
}
@@ -0,0 +1,43 @@
package com.dfsek.terra.population.items.ores;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.util.MaterialSet;
import java.util.Random;
public abstract class Ore {
private final BlockData material;
private final MaterialSet replaceable;
private final boolean applyGravity;
protected TerraPlugin main;
public Ore(BlockData material, MaterialSet replaceable, boolean applyGravity, TerraPlugin main) {
this.material = material;
this.replaceable = replaceable;
this.applyGravity = applyGravity;
this.main = main;
}
public abstract void generate(Vector3 origin, Chunk c, Random r);
public BlockData getMaterial() {
return material;
}
public MaterialSet getReplaceable() {
return replaceable;
}
public boolean isApplyGravity() {
return applyGravity;
}
public enum Type {
VANILLA, SPHERE
}
}
@@ -0,0 +1,21 @@
package com.dfsek.terra.population.items.ores;
import com.dfsek.terra.api.math.Range;
public class OreConfig {
private final Range amount;
private final Range height;
public OreConfig(Range amount, Range height) {
this.amount = amount;
this.height = height;
}
public Range getAmount() {
return amount;
}
public Range getHeight() {
return height;
}
}
@@ -0,0 +1,40 @@
package com.dfsek.terra.population.items.ores;
import com.dfsek.terra.api.util.GlueList;
import java.util.List;
import java.util.function.BiConsumer;
/**
* Holds ordered list of ores mapped to their configs.
*/
public class OreHolder {
private final List<Entry> entries = new GlueList<>();
public void forEach(BiConsumer<Ore, OreConfig> consumer) {
entries.forEach(entry -> consumer.accept(entry.getOre(), entry.getConfig()));
}
public OreHolder add(Ore ore, OreConfig config) {
entries.add(new Entry(ore, config));
return this;
}
private static final class Entry {
private final Ore ore;
private final OreConfig config;
private Entry(Ore ore, OreConfig config) {
this.ore = ore;
this.config = config;
}
public OreConfig getConfig() {
return config;
}
public Ore getOre() {
return ore;
}
}
}
@@ -0,0 +1,80 @@
package com.dfsek.terra.population.items.ores;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.util.MaterialSet;
import net.jafama.FastMath;
import java.util.Random;
public class VanillaOre extends Ore {
private final Range sizeRange;
public VanillaOre(BlockData material, MaterialSet replaceable, boolean applyGravity, Range size, TerraPlugin main) {
super(material, replaceable, applyGravity, main);
this.sizeRange = size;
}
@Override
public void generate(Vector3 location, Chunk chunk, Random random) {
WorldHandle handle = main.getWorldHandle();
double size = sizeRange.get(random);
int centerX = location.getBlockX();
int centerZ = location.getBlockZ();
int centerY = location.getBlockY();
float f = random.nextFloat() * (float) Math.PI;
double d1 = centerX + 8 + FastMath.sin(f) * size / 8.0F;
double d2 = centerX + 8 - FastMath.sin(f) * size / 8.0F;
double d3 = centerZ + 8 + FastMath.cos(f) * size / 8.0F;
double d4 = centerZ + 8 - FastMath.cos(f) * size / 8.0F;
double d5 = centerY + random.nextInt(3) - 2D;
double d6 = centerY + random.nextInt(3) - 2D;
for(int i = 0; i < size; i++) {
float iFactor = (float) i / (float) size;
double d10 = random.nextDouble() * size / 16.0D;
double d11 = (FastMath.sin(Math.PI * iFactor) + 1.0) * d10 + 1.0;
double d12 = (FastMath.sin(Math.PI * iFactor) + 1.0) * d10 + 1.0;
int xStart = FastMath.roundToInt(FastMath.floor(d1 + (d2 - d1) * iFactor - d11 / 2.0D));
int yStart = FastMath.roundToInt(FastMath.floor(d5 + (d6 - d5) * iFactor - d12 / 2.0D));
int zStart = FastMath.roundToInt(FastMath.floor(d3 + (d4 - d3) * iFactor - d11 / 2.0D));
int xEnd = FastMath.roundToInt(FastMath.floor(d1 + (d2 - d1) * iFactor + d11 / 2.0D));
int yEnd = FastMath.roundToInt(FastMath.floor(d5 + (d6 - d5) * iFactor + d12 / 2.0D));
int zEnd = FastMath.roundToInt(FastMath.floor(d3 + (d4 - d3) * iFactor + d11 / 2.0D));
for(int x = xStart; x <= xEnd; x++) {
double d13 = (x + 0.5D - (d1 + (d2 - d1) * iFactor)) / (d11 / 2.0D);
if(d13 * d13 < 1.0D) {
for(int y = yStart; y <= yEnd; y++) {
double d14 = (y + 0.5D - (d5 + (d6 - d5) * iFactor)) / (d12 / 2.0D);
if(d13 * d13 + d14 * d14 < 1.0D) {
for(int z = zStart; z <= zEnd; z++) {
double d15 = (z + 0.5D - (d3 + (d4 - d3) * iFactor)) / (d11 / 2.0D);
if(x > 15 || z > 15 || y > 255 || x < 0 || z < 0 || y < 0) continue;
Block block = chunk.getBlock(x, y, z);
if((d13 * d13 + d14 * d14 + d15 * d15 < 1.0D) && getReplaceable().contains(block.getType())) {
handle.setBlockData(block, getMaterial(), isApplyGravity());
}
}
}
}
}
}
}
}
}
@@ -0,0 +1,32 @@
package com.dfsek.terra.population.items.tree;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.structures.script.StructureScript;
import com.dfsek.terra.api.structures.structure.Rotation;
import com.dfsek.terra.api.world.tree.Tree;
import com.dfsek.terra.util.MaterialSet;
import java.util.Random;
public class TerraTree implements Tree {
private final MaterialSet spawnable;
private final int yOffset;
private final ProbabilityCollection<StructureScript> structure;
public TerraTree(MaterialSet spawnable, int yOffset, ProbabilityCollection<StructureScript> structure) {
this.spawnable = spawnable;
this.yOffset = yOffset;
this.structure = structure;
}
@Override
public synchronized boolean plant(Location location, Random random) {
return structure.get(random).execute(location.clone().add(0, yOffset, 0), random, Rotation.fromDegrees(90 * random.nextInt(4)));
}
@Override
public MaterialSet getSpawnable() {
return spawnable;
}
}
@@ -0,0 +1,32 @@
package com.dfsek.terra.population.items.tree;
import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.math.noise.FastNoiseLite;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.api.platform.block.Block;
import com.dfsek.terra.api.platform.block.BlockFace;
import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.world.tree.Tree;
import com.dfsek.terra.population.items.PlaceableLayer;
import java.util.Random;
public class TreeLayer extends PlaceableLayer<Tree> {
public TreeLayer(double density, Range level, ProbabilityCollection<Tree> layer, FastNoiseLite noise) {
super(density, level, layer, noise);
}
@Override
public void place(Chunk chunk, Vector2 coords, Random random) {
Tree item = layer.get(random);
Block current = chunk.getBlock((int) coords.getX(), level.getMax(), (int) coords.getZ());
for(int ignored : level) {
current = current.getRelative(BlockFace.DOWN);
if(item.getSpawnable().contains(current.getType())) {
item.plant(current.getLocation().add(0, 1, 0), random);
}
}
}
}