From 21358808e4308664dd9c9d7cc63a1347e11b7e68 Mon Sep 17 00:00:00 2001 From: paulevsGitch Date: Sun, 28 Mar 2021 20:04:52 +0300 Subject: [PATCH] Different island terrain (WIP) --- .../util/sdf/operator/SDFHeightmap.java | 59 ++++++++++++++++++ .../world/generator/IslandLayer.java | 35 +++++++++-- .../textures/heightmaps/mountain_1.png | Bin 0 -> 19907 bytes 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/betterend/util/sdf/operator/SDFHeightmap.java create mode 100644 src/main/resources/assets/betterend/textures/heightmaps/mountain_1.png diff --git a/src/main/java/ru/betterend/util/sdf/operator/SDFHeightmap.java b/src/main/java/ru/betterend/util/sdf/operator/SDFHeightmap.java new file mode 100644 index 00000000..82ba749a --- /dev/null +++ b/src/main/java/ru/betterend/util/sdf/operator/SDFHeightmap.java @@ -0,0 +1,59 @@ +package ru.betterend.util.sdf.operator; + +import net.minecraft.client.texture.NativeImage; +import net.minecraft.util.math.MathHelper; + +public class SDFHeightmap extends SDFDisplacement { + private float intensity = 1F; + private NativeImage map; + private float offsetX; + private float offsetZ; + private float angle; + private float scale; + + public SDFHeightmap() { + setFunction((pos) -> { + if (map == null) { + return 0F; + } + float dx = MathHelper.clamp(pos.getX() * scale + offsetX, 0, map.getWidth() - 2); + float dz = MathHelper.clamp(pos.getZ() * scale + offsetZ, 0, map.getHeight() - 2); + int x1 = MathHelper.floor(dx); + int z1 = MathHelper.floor(dz); + int x2 = x1 + 1; + int z2 = z1 + 1; + dx = dx - x1; + dz = dz - z1; + float a = (map.getPixelColor(x1, z1) & 255) / 255F; + float b = (map.getPixelColor(x2, z1) & 255) / 255F; + float c = (map.getPixelColor(x1, z2) & 255) / 255F; + float d = (map.getPixelColor(x2, z2) & 255) / 255F; + a = MathHelper.lerp(dx, a, b); + b = MathHelper.lerp(dx, c, d); + return -MathHelper.lerp(dz, a, b) * intensity; + }); + } + + public SDFHeightmap setMap(NativeImage map) { + this.map = map; + offsetX = map.getWidth() * 0.5F; + offsetZ = map.getHeight() * 0.5F; + scale = map.getWidth(); + return this; + } + + public SDFHeightmap setAngle(float angle) { + this.angle = angle; + return this; + } + + public SDFHeightmap setScale(float scale) { + this.scale = map.getWidth() * scale; + return this; + } + + public SDFHeightmap setIntensity(float intensity) { + this.intensity = intensity; + return this; + } +} diff --git a/src/main/java/ru/betterend/world/generator/IslandLayer.java b/src/main/java/ru/betterend/world/generator/IslandLayer.java index a5fff6e2..bd08ff1c 100644 --- a/src/main/java/ru/betterend/world/generator/IslandLayer.java +++ b/src/main/java/ru/betterend/world/generator/IslandLayer.java @@ -1,5 +1,7 @@ package ru.betterend.world.generator; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -8,10 +10,13 @@ import java.util.Random; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import net.minecraft.client.texture.NativeImage; import net.minecraft.util.math.BlockPos; +import ru.betterend.BetterEnd; import ru.betterend.noise.OpenSimplexNoise; import ru.betterend.util.MHelper; import ru.betterend.util.sdf.SDF; +import ru.betterend.util.sdf.operator.SDFHeightmap; import ru.betterend.util.sdf.operator.SDFScale; import ru.betterend.util.sdf.operator.SDFSmoothUnion; import ru.betterend.util.sdf.operator.SDFTranslate; @@ -19,7 +24,7 @@ import ru.betterend.util.sdf.primitive.SDFCappedCone; public class IslandLayer { private static final Random RANDOM = new Random(); - private static final SDF ISLAND; + private static final SDF[] ISLAND; private final List positions = new ArrayList(9); private final Map islands = Maps.newHashMap(); @@ -86,11 +91,11 @@ public class IslandLayer { SDF island = islands.get(pos); if (island == null) { if (pos.getX() == 0 && pos.getZ() == 0) { - island = new SDFScale().setScale(1.3F).setSource(ISLAND); + island = new SDFScale().setScale(1.3F).setSource(ISLAND[0]); } else { RANDOM.setSeed(getSeed(pos.getX(), pos.getZ())); - island = new SDFScale().setScale(RANDOM.nextFloat() + 0.5F).setSource(ISLAND); + island = new SDFScale().setScale(RANDOM.nextFloat() + 0.5F).setSource(ISLAND[0]); } islands.put(pos, island); } @@ -130,7 +135,24 @@ public class IslandLayer { return new SDFTranslate().setTranslate(0, minY + hh, 0).setSource(sdf); } + private static NativeImage loadMap(String path) { + InputStream stream = IslandLayer.class.getResourceAsStream(path); + if (stream != null) { + try { + NativeImage map = NativeImage.read(stream); + stream.close(); + return map; + } + catch (IOException e) { + BetterEnd.LOGGER.warning(e.getMessage()); + } + } + return null; + } + static { + NativeImage map = loadMap("/assets/" + BetterEnd.MOD_ID + "/textures/heightmaps/mountain_1.png"); + SDF cone1 = makeCone(0, 0.4F, 0.2F, -0.3F); SDF cone2 = makeCone(0.4F, 0.5F, 0.1F, -0.1F); SDF cone3 = makeCone(0.5F, 0.45F, 0.03F, 0.0F); @@ -139,6 +161,11 @@ public class IslandLayer { SDF coneBottom = new SDFSmoothUnion().setRadius(0.02F).setSourceA(cone1).setSourceB(cone2); SDF coneTop = new SDFSmoothUnion().setRadius(0.02F).setSourceA(cone3).setSourceB(cone4); - ISLAND = new SDFSmoothUnion().setRadius(0.01F).setSourceA(coneTop).setSourceB(coneBottom); + SDF map1 = new SDFHeightmap().setMap(map).setIntensity(0.3F).setSource(coneTop); + + ISLAND = new SDF[] { + new SDFSmoothUnion().setRadius(0.01F).setSourceA(coneTop).setSourceB(coneBottom), + new SDFSmoothUnion().setRadius(0.01F).setSourceA(map1).setSourceB(coneBottom) + }; } } diff --git a/src/main/resources/assets/betterend/textures/heightmaps/mountain_1.png b/src/main/resources/assets/betterend/textures/heightmaps/mountain_1.png new file mode 100644 index 0000000000000000000000000000000000000000..020f8daa9b814881ee0a877d7aea778827726044 GIT binary patch literal 19907 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 literal 0 HcmV?d00001