From d1dec4da5691e330bb9c07fae61ba576af43436e Mon Sep 17 00:00:00 2001 From: paulevsGitch Date: Wed, 9 Dec 2020 23:49:29 +0300 Subject: [PATCH] Helix tree feature (WIP) --- .../ru/betterend/registry/EndFeatures.java | 2 + .../java/ru/betterend/util/SplineHelper.java | 52 +++++++++++++ .../betterend/world/biome/BiomeAmberLand.java | 1 + .../features/trees/HelixTreeFeature.java | 73 +++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/main/java/ru/betterend/world/features/trees/HelixTreeFeature.java diff --git a/src/main/java/ru/betterend/registry/EndFeatures.java b/src/main/java/ru/betterend/registry/EndFeatures.java index 0b3746eb..b67ce48a 100644 --- a/src/main/java/ru/betterend/registry/EndFeatures.java +++ b/src/main/java/ru/betterend/registry/EndFeatures.java @@ -36,6 +36,7 @@ import ru.betterend.world.features.terrain.SpireFeature; import ru.betterend.world.features.terrain.SulphuricCaveFeature; import ru.betterend.world.features.terrain.SulphuricLakeFeature; import ru.betterend.world.features.trees.DragonTreeFeature; +import ru.betterend.world.features.trees.HelixTreeFeature; import ru.betterend.world.features.trees.LacugroveFeature; import ru.betterend.world.features.trees.MossyGlowshroomFeature; import ru.betterend.world.features.trees.PythadendronTreeFeature; @@ -48,6 +49,7 @@ public class EndFeatures { public static final EndFeature LACUGROVE = new EndFeature("lacugrove", new LacugroveFeature(), 4); public static final EndFeature DRAGON_TREE = new EndFeature("dragon_tree", new DragonTreeFeature(), 3); public static final EndFeature TENANEA = new EndFeature("tenanea", new TenaneaFeature(), 3); + public static final EndFeature HELIX_TREE = new EndFeature("helix_tree", new HelixTreeFeature(), 2); // Bushes // public static final EndFeature PYTHADENDRON_BUSH = new EndFeature("pythadendron_bush", new BushFeature(EndBlocks.PYTHADENDRON_LEAVES, EndBlocks.PYTHADENDRON.bark), 4); diff --git a/src/main/java/ru/betterend/util/SplineHelper.java b/src/main/java/ru/betterend/util/SplineHelper.java index 976d9261..6b211387 100644 --- a/src/main/java/ru/betterend/util/SplineHelper.java +++ b/src/main/java/ru/betterend/util/SplineHelper.java @@ -87,6 +87,15 @@ public class SplineHelper { return true; } + public static void fillSplineForce(List spline, StructureWorldAccess world, BlockState state, BlockPos pos, Function replace) { + Vector3f startPos = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f endPos = spline.get(i); + fillLineForce(startPos, endPos, world, state, pos, replace); + startPos = endPos; + } + } + private static boolean fillLine(Vector3f start, Vector3f end, StructureWorldAccess world, BlockState state, BlockPos pos, Function replace) { float dx = end.getX() - start.getX(); float dy = end.getY() - start.getY(); @@ -137,6 +146,49 @@ public class SplineHelper { } } + private static void fillLineForce(Vector3f start, Vector3f end, StructureWorldAccess world, BlockState state, BlockPos pos, Function replace) { + float dx = end.getX() - start.getX(); + float dy = end.getY() - start.getY(); + float dz = end.getZ() - start.getZ(); + float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); + int count = MHelper.floor(max + 1); + dx /= max; + dy /= max; + dz /= max; + float x = start.getX(); + float y = start.getY(); + float z = start.getZ(); + boolean down = Math.abs(dy) > 0.2; + + BlockState bState; + Mutable bPos = new Mutable(); + for (int i = 0; i < count; i++) { + bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); + bState = world.getBlockState(bPos); + if (replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + x += dx; + y += dy; + z += dz; + } + bPos.set(end.getX() + pos.getX(), end.getY() + pos.getY(), end.getZ() + pos.getZ()); + bState = world.getBlockState(bPos); + if (replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + } + public static boolean canGenerate(List spline, float scale, BlockPos start, StructureWorldAccess world, Function canReplace) { int count = spline.size(); Vector3f vec = spline.get(0); diff --git a/src/main/java/ru/betterend/world/biome/BiomeAmberLand.java b/src/main/java/ru/betterend/world/biome/BiomeAmberLand.java index e58497af..adeec9b4 100644 --- a/src/main/java/ru/betterend/world/biome/BiomeAmberLand.java +++ b/src/main/java/ru/betterend/world/biome/BiomeAmberLand.java @@ -14,6 +14,7 @@ public class BiomeAmberLand extends EndBiome { .setSurface(EndBlocks.AMBER_GRASS) .addFeature(EndFeatures.AMBER_ORE) .addFeature(EndFeatures.END_LAKE_RARE) + .addFeature(EndFeatures.HELIX_TREE) .addFeature(EndFeatures.CHARNIA_ORANGE) .addFeature(EndFeatures.CHARNIA_RED) .addStructureFeature(ConfiguredStructureFeatures.END_CITY) diff --git a/src/main/java/ru/betterend/world/features/trees/HelixTreeFeature.java b/src/main/java/ru/betterend/world/features/trees/HelixTreeFeature.java new file mode 100644 index 00000000..1c0afeba --- /dev/null +++ b/src/main/java/ru/betterend/world/features/trees/HelixTreeFeature.java @@ -0,0 +1,73 @@ +package ru.betterend.world.features.trees; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.minecraft.client.util.math.Vector3f; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.world.StructureWorldAccess; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import ru.betterend.registry.EndBlocks; +import ru.betterend.registry.EndTags; +import ru.betterend.util.MHelper; +import ru.betterend.util.SplineHelper; +import ru.betterend.util.sdf.SDF; +import ru.betterend.util.sdf.operator.SDFRotation; +import ru.betterend.util.sdf.operator.SDFScale; +import ru.betterend.util.sdf.operator.SDFSmoothUnion; +import ru.betterend.util.sdf.operator.SDFTranslate; +import ru.betterend.util.sdf.operator.SDFUnion; +import ru.betterend.world.features.DefaultFeature; + +public class HelixTreeFeature extends DefaultFeature { + @Override + public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) { + if (!world.getBlockState(pos.down()).getBlock().isIn(EndTags.END_GROUND)) return false; + + float angle = random.nextFloat() * MHelper.PI2; + float radiusRange = MHelper.randRange(4.5F, 6F, random); + float scale = MHelper.randRange(0.5F, 1F, random); + + List spline = new ArrayList(10); + for (int i = 0; i < 10; i++) { + float radius = (0.9F - i * 0.1F) * radiusRange; + float dx = (float) Math.sin(i + angle) * radius; + float dz = (float) Math.cos(i + angle) * radius; + spline.add(new Vector3f(dx, i * 2, dz)); + } + SDF sdf = SplineHelper.buildSDF(spline, 1.7F, 0.5F, (p) -> { return EndBlocks.HELIX_TREE.bark.getDefaultState(); }); + SDF rotated = new SDFRotation().setRotation(Vector3f.POSITIVE_Y, (float) Math.PI).setSource(sdf); + sdf = new SDFUnion().setSourceA(rotated).setSourceB(sdf); + + Vector3f lastPoint = spline.get(spline.size() - 1); + List spline2 = SplineHelper.makeSpline(0, 0, 0, 0, 20, 0, 5); + SDF stem = SplineHelper.buildSDF(spline2, 1.0F, 0.5F, (p) -> { return EndBlocks.HELIX_TREE.bark.getDefaultState(); }); + stem = new SDFTranslate().setTranslate(lastPoint.getX(), lastPoint.getY(), lastPoint.getZ()).setSource(stem); + sdf = new SDFSmoothUnion().setRadius(3).setSourceA(sdf).setSourceB(stem); + + sdf = new SDFScale().setScale(scale).setSource(sdf); + float dx = 30 * scale; + float dy1 = -20 * scale; + float dy2 = 100 * scale; + sdf.fillArea(world, pos, new Box(pos.add(-dx, dy1, -dx), pos.add(dx, dy2, dx))); + SplineHelper.scale(spline, scale); + SplineHelper.fillSplineForce(spline, world, EndBlocks.HELIX_TREE.bark.getDefaultState(), pos, (state) -> { + return state.getMaterial().isReplaceable(); + }); + SplineHelper.rotateSpline(spline, (float) Math.PI); + SplineHelper.fillSplineForce(spline, world, EndBlocks.HELIX_TREE.bark.getDefaultState(), pos, (state) -> { + return state.getMaterial().isReplaceable(); + }); + SplineHelper.scale(spline2, scale); + SplineHelper.fillSplineForce(spline2, world, EndBlocks.HELIX_TREE.bark.getDefaultState(), + pos.add(lastPoint.getX() + 0.5, lastPoint.getY() + 0.5, lastPoint.getZ() + 0.5), + (state) -> { + return state.getMaterial().isReplaceable(); + }); + + return true; + } +}