SDF, Noise, Blocks Helper, Spline Helper, Translation Helper
This commit is contained in:
parent
6a5584deae
commit
017d663af6
37 changed files with 4315 additions and 0 deletions
2185
src/main/java/ru/bclib/noise/OpenSimplexNoise.java
Normal file
2185
src/main/java/ru/bclib/noise/OpenSimplexNoise.java
Normal file
File diff suppressed because it is too large
Load diff
140
src/main/java/ru/bclib/noise/VoronoiNoise.java
Normal file
140
src/main/java/ru/bclib/noise/VoronoiNoise.java
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package ru.bclib.noise;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class VoronoiNoise {
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
final int seed;
|
||||||
|
|
||||||
|
public VoronoiNoise() {
|
||||||
|
this(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VoronoiNoise(int seed) {
|
||||||
|
this.seed = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSeed(int x, int y, int z) {
|
||||||
|
int h = seed + x * 374761393 + y * 668265263 + z;
|
||||||
|
h = (h ^ (h >> 13)) * 1274126177;
|
||||||
|
return h ^ (h >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double sample(double x, double y, double z) {
|
||||||
|
int ix = MHelper.floor(x);
|
||||||
|
int iy = MHelper.floor(y);
|
||||||
|
int iz = MHelper.floor(z);
|
||||||
|
|
||||||
|
float px = (float) (x - ix);
|
||||||
|
float py = (float) (y - iy);
|
||||||
|
float pz = (float) (z - iz);
|
||||||
|
|
||||||
|
float d = 10;
|
||||||
|
|
||||||
|
for (int pox = -1; pox < 2; pox++) {
|
||||||
|
for (int poy = -1; poy < 2; poy++) {
|
||||||
|
for (int poz = -1; poz < 2; poz++) {
|
||||||
|
RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz));
|
||||||
|
float pointX = pox + RANDOM.nextFloat();
|
||||||
|
float pointY = poy + RANDOM.nextFloat();
|
||||||
|
float pointZ = poz + RANDOM.nextFloat();
|
||||||
|
float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz);
|
||||||
|
if (d2 < d) {
|
||||||
|
d = d2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.sqrt(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Random getRandom(double x, double y, double z) {
|
||||||
|
int ix = MHelper.floor(x);
|
||||||
|
int iy = MHelper.floor(y);
|
||||||
|
int iz = MHelper.floor(z);
|
||||||
|
|
||||||
|
float px = (float) (x - ix);
|
||||||
|
float py = (float) (y - iy);
|
||||||
|
float pz = (float) (z - iz);
|
||||||
|
|
||||||
|
float d = 10;
|
||||||
|
|
||||||
|
int posX = 0;
|
||||||
|
int posY = 0;
|
||||||
|
int posZ = 0;
|
||||||
|
|
||||||
|
for (int pox = -1; pox < 2; pox++) {
|
||||||
|
for (int poy = -1; poy < 2; poy++) {
|
||||||
|
for (int poz = -1; poz < 2; poz++) {
|
||||||
|
RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz));
|
||||||
|
float pointX = pox + RANDOM.nextFloat();
|
||||||
|
float pointY = poy + RANDOM.nextFloat();
|
||||||
|
float pointZ = poz + RANDOM.nextFloat();
|
||||||
|
float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz);
|
||||||
|
if (d2 < d) {
|
||||||
|
d = d2;
|
||||||
|
posX = pox;
|
||||||
|
posY = poy;
|
||||||
|
posZ = poz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
posX += ix;
|
||||||
|
posY += iy;
|
||||||
|
posZ += iz;
|
||||||
|
|
||||||
|
int seed = MHelper.getSeed(posY, posX, posZ);
|
||||||
|
RANDOM.setSeed(seed);
|
||||||
|
|
||||||
|
return RANDOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos[] getPos(double x, double y, double z, double scale) {
|
||||||
|
int ix = MHelper.floor(x);
|
||||||
|
int iy = MHelper.floor(y);
|
||||||
|
int iz = MHelper.floor(z);
|
||||||
|
|
||||||
|
float px = (float) (x - ix);
|
||||||
|
float py = (float) (y - iy);
|
||||||
|
float pz = (float) (z - iz);
|
||||||
|
|
||||||
|
float d = 10;
|
||||||
|
float selX = 0;
|
||||||
|
float selY = 0;
|
||||||
|
float selZ = 0;
|
||||||
|
float selXPre = 0;
|
||||||
|
float selYPre = 0;
|
||||||
|
float selZPre = 0;
|
||||||
|
|
||||||
|
for (int pox = -1; pox < 2; pox++) {
|
||||||
|
for (int poy = -1; poy < 2; poy++) {
|
||||||
|
for (int poz = -1; poz < 2; poz++) {
|
||||||
|
RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz));
|
||||||
|
float pointX = pox + RANDOM.nextFloat();
|
||||||
|
float pointY = poy + RANDOM.nextFloat();
|
||||||
|
float pointZ = poz + RANDOM.nextFloat();
|
||||||
|
float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz);
|
||||||
|
if (d2 < d) {
|
||||||
|
d = d2;
|
||||||
|
selXPre = selX;
|
||||||
|
selYPre = selY;
|
||||||
|
selZPre = selZ;
|
||||||
|
selX = pointX;
|
||||||
|
selY = pointY;
|
||||||
|
selZ = pointZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos p1 = new BlockPos((ix + (double) selX) * scale, (iy + (double) selY) * scale, (iz + (double) selZ) * scale);
|
||||||
|
BlockPos p2 = new BlockPos((ix + (double) selXPre) * scale, (iy + (double) selYPre) * scale, (iz + (double) selZPre) * scale);
|
||||||
|
return new BlockPos[] {p1, p2};
|
||||||
|
}
|
||||||
|
}
|
104
src/main/java/ru/bclib/sdf/PosInfo.java
Normal file
104
src/main/java/ru/bclib/sdf/PosInfo.java
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package ru.bclib.sdf;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class PosInfo implements Comparable<PosInfo> {
|
||||||
|
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
|
||||||
|
private final Map<BlockPos, PosInfo> blocks;
|
||||||
|
private final Map<BlockPos, PosInfo> add;
|
||||||
|
private final BlockPos pos;
|
||||||
|
private BlockState state;
|
||||||
|
|
||||||
|
public static PosInfo create(Map<BlockPos, PosInfo> blocks, Map<BlockPos, PosInfo> add, BlockPos pos) {
|
||||||
|
return new PosInfo(blocks, add, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PosInfo(Map<BlockPos, PosInfo> blocks, Map<BlockPos, PosInfo> add, BlockPos pos) {
|
||||||
|
this.blocks = blocks;
|
||||||
|
this.add = add;
|
||||||
|
this.pos = pos;
|
||||||
|
blocks.put(pos, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getState(BlockPos pos) {
|
||||||
|
PosInfo info = blocks.get(pos);
|
||||||
|
if (info == null) {
|
||||||
|
info = add.get(pos);
|
||||||
|
return info == null ? AIR : info.getState();
|
||||||
|
}
|
||||||
|
return info.getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(BlockState state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(BlockPos pos, BlockState state) {
|
||||||
|
PosInfo info = blocks.get(pos);
|
||||||
|
if (info != null) {
|
||||||
|
info.setState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getState(Direction dir) {
|
||||||
|
PosInfo info = blocks.get(pos.relative(dir));
|
||||||
|
if (info == null) {
|
||||||
|
info = add.get(pos.relative(dir));
|
||||||
|
return info == null ? AIR : info.getState();
|
||||||
|
}
|
||||||
|
return info.getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getState(Direction dir, int distance) {
|
||||||
|
PosInfo info = blocks.get(pos.relative(dir, distance));
|
||||||
|
if (info == null) {
|
||||||
|
return AIR;
|
||||||
|
}
|
||||||
|
return info.getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getStateUp() {
|
||||||
|
return getState(Direction.UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getStateDown() {
|
||||||
|
return getState(Direction.DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return pos.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof PosInfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pos.equals(((PosInfo) obj).pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(PosInfo info) {
|
||||||
|
return this.pos.getY() - info.pos.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getPos() {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlockPos(BlockPos pos, BlockState state) {
|
||||||
|
PosInfo info = new PosInfo(blocks, add, pos);
|
||||||
|
info.state = state;
|
||||||
|
add.put(pos, info);
|
||||||
|
}
|
||||||
|
}
|
310
src/main/java/ru/bclib/sdf/SDF.java
Normal file
310
src/main/java/ru/bclib/sdf/SDF.java
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
package ru.bclib.sdf;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.ServerLevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import ru.bclib.structures.StructureWorld;
|
||||||
|
import ru.bclib.util.BlocksHelper;
|
||||||
|
|
||||||
|
public abstract class SDF {
|
||||||
|
private List<Function<PosInfo, BlockState>> postProcesses = Lists.newArrayList();
|
||||||
|
private Function<BlockState, Boolean> canReplace = (state) -> {
|
||||||
|
return state.getMaterial().isReplaceable();
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract float getDistance(float x, float y, float z);
|
||||||
|
|
||||||
|
public abstract BlockState getBlockState(BlockPos pos);
|
||||||
|
|
||||||
|
public SDF addPostProcess(Function<PosInfo, BlockState> postProcess) {
|
||||||
|
this.postProcesses.add(postProcess);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDF setReplaceFunction(Function<BlockState, Boolean> canReplace) {
|
||||||
|
this.canReplace = canReplace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillRecursive(ServerLevelAccessor world, BlockPos start) {
|
||||||
|
Map<BlockPos, PosInfo> mapWorld = Maps.newHashMap();
|
||||||
|
Map<BlockPos, PosInfo> addInfo = Maps.newHashMap();
|
||||||
|
Set<BlockPos> blocks = Sets.newHashSet();
|
||||||
|
Set<BlockPos> ends = Sets.newHashSet();
|
||||||
|
Set<BlockPos> add = Sets.newHashSet();
|
||||||
|
ends.add(new BlockPos(0, 0, 0));
|
||||||
|
boolean run = true;
|
||||||
|
|
||||||
|
MutableBlockPos bPos = new MutableBlockPos();
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
for (BlockPos center: ends) {
|
||||||
|
for (Direction dir: Direction.values()) {
|
||||||
|
bPos.set(center).move(dir);
|
||||||
|
BlockPos wpos = bPos.offset(start);
|
||||||
|
|
||||||
|
if (!blocks.contains(bPos) && canReplace.apply(world.getBlockState(wpos))) {
|
||||||
|
if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) {
|
||||||
|
BlockState state = getBlockState(wpos);
|
||||||
|
PosInfo.create(mapWorld, addInfo, wpos).setState(state);
|
||||||
|
add.add(bPos.immutable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.addAll(ends);
|
||||||
|
ends.clear();
|
||||||
|
ends.addAll(add);
|
||||||
|
add.clear();
|
||||||
|
|
||||||
|
run &= !ends.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PosInfo> infos = new ArrayList<PosInfo>(mapWorld.values());
|
||||||
|
if (infos.size() > 0) {
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState());
|
||||||
|
});
|
||||||
|
|
||||||
|
infos.clear();
|
||||||
|
infos.addAll(addInfo.values());
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
if (canReplace.apply(world.getBlockState(info.getPos()))) {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillArea(ServerLevelAccessor world, BlockPos center, AABB box) {
|
||||||
|
Map<BlockPos, PosInfo> mapWorld = Maps.newHashMap();
|
||||||
|
Map<BlockPos, PosInfo> addInfo = Maps.newHashMap();
|
||||||
|
|
||||||
|
MutableBlockPos mut = new MutableBlockPos();
|
||||||
|
for (int y = (int) box.minY; y <= box.maxY; y++) {
|
||||||
|
mut.setY(y);
|
||||||
|
for (int x = (int) box.minX; x <= box.maxX; x++) {
|
||||||
|
mut.setX(x);
|
||||||
|
for (int z = (int) box.minZ; z <= box.maxZ; z++) {
|
||||||
|
mut.setZ(z);
|
||||||
|
if (canReplace.apply(world.getBlockState(mut))) {
|
||||||
|
BlockPos fpos = mut.subtract(center);
|
||||||
|
if (this.getDistance(fpos.getX(), fpos.getY(), fpos.getZ()) < 0) {
|
||||||
|
PosInfo.create(mapWorld, addInfo, mut.immutable()).setState(getBlockState(mut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PosInfo> infos = new ArrayList<PosInfo>(mapWorld.values());
|
||||||
|
if (infos.size() > 0) {
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState());
|
||||||
|
});
|
||||||
|
|
||||||
|
infos.clear();
|
||||||
|
infos.addAll(addInfo.values());
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
if (canReplace.apply(world.getBlockState(info.getPos()))) {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillRecursiveIgnore(ServerLevelAccessor world, BlockPos start, Function<BlockState, Boolean> ignore) {
|
||||||
|
Map<BlockPos, PosInfo> mapWorld = Maps.newHashMap();
|
||||||
|
Map<BlockPos, PosInfo> addInfo = Maps.newHashMap();
|
||||||
|
Set<BlockPos> blocks = Sets.newHashSet();
|
||||||
|
Set<BlockPos> ends = Sets.newHashSet();
|
||||||
|
Set<BlockPos> add = Sets.newHashSet();
|
||||||
|
ends.add(new BlockPos(0, 0, 0));
|
||||||
|
boolean run = true;
|
||||||
|
|
||||||
|
MutableBlockPos bPos = new MutableBlockPos();
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
for (BlockPos center: ends) {
|
||||||
|
for (Direction dir: Direction.values()) {
|
||||||
|
bPos.set(center).move(dir);
|
||||||
|
BlockPos wpos = bPos.offset(start);
|
||||||
|
BlockState state = world.getBlockState(wpos);
|
||||||
|
boolean ign = ignore.apply(state);
|
||||||
|
if (!blocks.contains(bPos) && (ign || canReplace.apply(state))) {
|
||||||
|
if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) {
|
||||||
|
PosInfo.create(mapWorld, addInfo, wpos).setState(ign ? state : getBlockState(bPos));
|
||||||
|
add.add(bPos.immutable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.addAll(ends);
|
||||||
|
ends.clear();
|
||||||
|
ends.addAll(add);
|
||||||
|
add.clear();
|
||||||
|
|
||||||
|
run &= !ends.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PosInfo> infos = new ArrayList<PosInfo>(mapWorld.values());
|
||||||
|
if (infos.size() > 0) {
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState());
|
||||||
|
});
|
||||||
|
|
||||||
|
infos.clear();
|
||||||
|
infos.addAll(addInfo.values());
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
if (canReplace.apply(world.getBlockState(info.getPos()))) {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillRecursive(StructureWorld world, BlockPos start) {
|
||||||
|
Map<BlockPos, PosInfo> mapWorld = Maps.newHashMap();
|
||||||
|
Map<BlockPos, PosInfo> addInfo = Maps.newHashMap();
|
||||||
|
Set<BlockPos> blocks = Sets.newHashSet();
|
||||||
|
Set<BlockPos> ends = Sets.newHashSet();
|
||||||
|
Set<BlockPos> add = Sets.newHashSet();
|
||||||
|
ends.add(new BlockPos(0, 0, 0));
|
||||||
|
boolean run = true;
|
||||||
|
|
||||||
|
MutableBlockPos bPos = new MutableBlockPos();
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
for (BlockPos center: ends) {
|
||||||
|
for (Direction dir: Direction.values()) {
|
||||||
|
bPos.set(center).move(dir);
|
||||||
|
BlockPos wpos = bPos.offset(start);
|
||||||
|
|
||||||
|
if (!blocks.contains(bPos)) {
|
||||||
|
if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) {
|
||||||
|
BlockState state = getBlockState(wpos);
|
||||||
|
PosInfo.create(mapWorld, addInfo, wpos).setState(state);
|
||||||
|
add.add(bPos.immutable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.addAll(ends);
|
||||||
|
ends.clear();
|
||||||
|
ends.addAll(add);
|
||||||
|
add.clear();
|
||||||
|
|
||||||
|
run &= !ends.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PosInfo> infos = new ArrayList<PosInfo>(mapWorld.values());
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
world.setBlock(info.getPos(), info.getState());
|
||||||
|
});
|
||||||
|
|
||||||
|
infos.clear();
|
||||||
|
infos.addAll(addInfo.values());
|
||||||
|
Collections.sort(infos);
|
||||||
|
postProcesses.forEach((postProcess) -> {
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
info.setState(postProcess.apply(info));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
infos.forEach((info) -> {
|
||||||
|
world.setBlock(info.getPos(), info.getState());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<BlockPos> getPositions(ServerLevelAccessor world, BlockPos start) {
|
||||||
|
Set<BlockPos> blocks = Sets.newHashSet();
|
||||||
|
Set<BlockPos> ends = Sets.newHashSet();
|
||||||
|
Set<BlockPos> add = Sets.newHashSet();
|
||||||
|
ends.add(new BlockPos(0, 0, 0));
|
||||||
|
boolean run = true;
|
||||||
|
|
||||||
|
MutableBlockPos bPos = new MutableBlockPos();
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
for (BlockPos center: ends) {
|
||||||
|
for (Direction dir: Direction.values()) {
|
||||||
|
bPos.set(center).move(dir);
|
||||||
|
BlockPos wpos = bPos.offset(start);
|
||||||
|
BlockState state = world.getBlockState(wpos);
|
||||||
|
if (!blocks.contains(wpos) && canReplace.apply(state)) {
|
||||||
|
if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) {
|
||||||
|
add.add(bPos.immutable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ends.forEach((end) -> blocks.add(end.offset(start)));
|
||||||
|
ends.clear();
|
||||||
|
ends.addAll(add);
|
||||||
|
add.clear();
|
||||||
|
|
||||||
|
run &= !ends.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
}
|
34
src/main/java/ru/bclib/sdf/operator/SDFBinary.java
Normal file
34
src/main/java/ru/bclib/sdf/operator/SDFBinary.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import ru.bclib.sdf.SDF;
|
||||||
|
|
||||||
|
public abstract class SDFBinary extends SDF {
|
||||||
|
protected SDF sourceA;
|
||||||
|
protected SDF sourceB;
|
||||||
|
protected boolean firstValue;
|
||||||
|
|
||||||
|
public SDFBinary setSourceA(SDF sourceA) {
|
||||||
|
this.sourceA = sourceA;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFBinary setSourceB(SDF sourceB) {
|
||||||
|
this.sourceB = sourceB;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void selectValue(float a, float b) {
|
||||||
|
firstValue = a < b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getBlockState(BlockPos pos) {
|
||||||
|
if (firstValue) {
|
||||||
|
return sourceA.getBlockState(pos);
|
||||||
|
} else {
|
||||||
|
return sourceB.getBlockState(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java
Normal file
22
src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import com.mojang.math.Vector3f;
|
||||||
|
|
||||||
|
public class SDFCoordModify extends SDFUnary {
|
||||||
|
private static final Vector3f POS = new Vector3f();
|
||||||
|
private Consumer<Vector3f> function;
|
||||||
|
|
||||||
|
public SDFCoordModify setFunction(Consumer<Vector3f> function) {
|
||||||
|
this.function = function;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
POS.set(x, y, z);
|
||||||
|
function.accept(POS);
|
||||||
|
return this.source.getDistance(POS.x(), POS.y(), POS.z());
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java
Normal file
19
src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFCopyRotate extends SDFUnary {
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
public SDFCopyRotate setCount(int count) {
|
||||||
|
this.count = count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float px = (float) Math.atan2(x, z);
|
||||||
|
float pz = MHelper.length(x, z);
|
||||||
|
return this.source.getDistance(px, y, pz);
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java
Normal file
21
src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.mojang.math.Vector3f;
|
||||||
|
|
||||||
|
public class SDFDisplacement extends SDFUnary {
|
||||||
|
private static final Vector3f POS = new Vector3f();
|
||||||
|
private Function<Vector3f, Float> displace;
|
||||||
|
|
||||||
|
public SDFDisplacement setFunction(Function<Vector3f, Float> displace) {
|
||||||
|
this.displace = displace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
POS.set(x, y, z);
|
||||||
|
return this.source.getDistance(x, y, z) + displace.apply(POS);
|
||||||
|
}
|
||||||
|
}
|
28
src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java
Normal file
28
src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
public class SDFFlatWave extends SDFDisplacement {
|
||||||
|
private int rayCount = 1;
|
||||||
|
private float intensity;
|
||||||
|
private float angle;
|
||||||
|
|
||||||
|
public SDFFlatWave() {
|
||||||
|
setFunction((pos) -> {
|
||||||
|
return (float) Math.cos(Math.atan2(pos.x(), pos.z()) * rayCount + angle) * intensity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFFlatWave setRaysCount(int count) {
|
||||||
|
this.rayCount = count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFFlatWave setAngle(float angle) {
|
||||||
|
this.angle = angle;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFFlatWave setIntensity(float intensity) {
|
||||||
|
this.intensity = intensity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
64
src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java
Normal file
64
src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.NativeImage;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
|
public class SDFHeightmap extends SDFDisplacement {
|
||||||
|
private float intensity = 1F;
|
||||||
|
private NativeImage map;
|
||||||
|
private float offsetX;
|
||||||
|
private float offsetZ;
|
||||||
|
private float scale;
|
||||||
|
private float cos = 1;
|
||||||
|
private float sin = 0;
|
||||||
|
|
||||||
|
public SDFHeightmap() {
|
||||||
|
setFunction((pos) -> {
|
||||||
|
if (map == null) {
|
||||||
|
return 0F;
|
||||||
|
}
|
||||||
|
float px = Mth.clamp(pos.x() * scale + offsetX, 0, map.getWidth() - 2);
|
||||||
|
float pz = Mth.clamp(pos.z() * scale + offsetZ, 0, map.getHeight() - 2);
|
||||||
|
float dx = (px * cos - pz * sin);
|
||||||
|
float dz = (pz * cos + px * sin);
|
||||||
|
int x1 = Mth.floor(dx);
|
||||||
|
int z1 = Mth.floor(dz);
|
||||||
|
int x2 = x1 + 1;
|
||||||
|
int z2 = z1 + 1;
|
||||||
|
dx = dx - x1;
|
||||||
|
dz = dz - z1;
|
||||||
|
float a = (map.getPixelRGBA(x1, z1) & 255) / 255F;
|
||||||
|
float b = (map.getPixelRGBA(x2, z1) & 255) / 255F;
|
||||||
|
float c = (map.getPixelRGBA(x1, z2) & 255) / 255F;
|
||||||
|
float d = (map.getPixelRGBA(x2, z2) & 255) / 255F;
|
||||||
|
a = Mth.lerp(dx, a, b);
|
||||||
|
b = Mth.lerp(dx, c, d);
|
||||||
|
return -Mth.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) {
|
||||||
|
sin = Mth.sin(angle);
|
||||||
|
cos = Mth.cos(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;
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ru/bclib/sdf/operator/SDFIntersection.java
Normal file
13
src/main/java/ru/bclib/sdf/operator/SDFIntersection.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFIntersection extends SDFBinary {
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float a = this.sourceA.getDistance(x, y, z);
|
||||||
|
float b = this.sourceB.getDistance(x, y, z);
|
||||||
|
this.selectValue(a, b);
|
||||||
|
return MHelper.max(a, b);
|
||||||
|
}
|
||||||
|
}
|
8
src/main/java/ru/bclib/sdf/operator/SDFInvert.java
Normal file
8
src/main/java/ru/bclib/sdf/operator/SDFInvert.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
public class SDFInvert extends SDFUnary {
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return -this.source.getDistance(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
60
src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java
Normal file
60
src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import ru.bclib.noise.OpenSimplexNoise;
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFRadialNoiseMap extends SDFDisplacement {
|
||||||
|
private static final float SIN = Mth.sin(0.5F);
|
||||||
|
private static final float COS = Mth.cos(0.5F);
|
||||||
|
|
||||||
|
private OpenSimplexNoise noise;
|
||||||
|
private float intensity = 1F;
|
||||||
|
private float radius = 1F;
|
||||||
|
private short offsetX;
|
||||||
|
private short offsetZ;
|
||||||
|
|
||||||
|
public SDFRadialNoiseMap() {
|
||||||
|
setFunction((pos) -> {
|
||||||
|
if (intensity == 0) {
|
||||||
|
return 0F;
|
||||||
|
}
|
||||||
|
float px = pos.x() / radius;
|
||||||
|
float pz = pos.z() / radius;
|
||||||
|
float distance = MHelper.lengthSqr(px, pz);
|
||||||
|
if (distance > 1) {
|
||||||
|
return 0F;
|
||||||
|
}
|
||||||
|
distance = 1 - Mth.sqrt(distance);
|
||||||
|
float nx = px * COS - pz * SIN;
|
||||||
|
float nz = pz * COS + px * SIN;
|
||||||
|
distance *= getNoise(nx * 0.75 + offsetX, nz * 0.75 + offsetZ);
|
||||||
|
return distance * intensity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getNoise(double x, double z) {
|
||||||
|
return (float) noise.eval(x, z) + (float) noise.eval(x * 3 + 1000, z * 3) * 0.5F + (float) noise.eval(x * 9 + 1000, z * 9) * 0.2F;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFRadialNoiseMap setSeed(long seed) {
|
||||||
|
noise = new OpenSimplexNoise(seed);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFRadialNoiseMap setIntensity(float intensity) {
|
||||||
|
this.intensity = intensity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFRadialNoiseMap setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFRadialNoiseMap setOffset(int x, int z) {
|
||||||
|
offsetX = (short) (x & 32767);
|
||||||
|
offsetZ = (short) (z & 32767);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/ru/bclib/sdf/operator/SDFRotation.java
Normal file
21
src/main/java/ru/bclib/sdf/operator/SDFRotation.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import com.mojang.math.Quaternion;
|
||||||
|
import com.mojang.math.Vector3f;
|
||||||
|
|
||||||
|
public class SDFRotation extends SDFUnary {
|
||||||
|
private static final Vector3f POS = new Vector3f();
|
||||||
|
private Quaternion rotation;
|
||||||
|
|
||||||
|
public SDFRotation setRotation(Vector3f axis, float rotationAngle) {
|
||||||
|
rotation = new Quaternion(axis, rotationAngle, false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
POS.set(x, y, z);
|
||||||
|
POS.transform(rotation);
|
||||||
|
return source.getDistance(POS.x(), POS.y(), POS.z());
|
||||||
|
}
|
||||||
|
}
|
15
src/main/java/ru/bclib/sdf/operator/SDFRound.java
Normal file
15
src/main/java/ru/bclib/sdf/operator/SDFRound.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
public class SDFRound extends SDFUnary {
|
||||||
|
private float radius;
|
||||||
|
|
||||||
|
public SDFRound setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return this.source.getDistance(x, y, z) - radius;
|
||||||
|
}
|
||||||
|
}
|
15
src/main/java/ru/bclib/sdf/operator/SDFScale.java
Normal file
15
src/main/java/ru/bclib/sdf/operator/SDFScale.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
public class SDFScale extends SDFUnary {
|
||||||
|
private float scale;
|
||||||
|
|
||||||
|
public SDFScale setScale(float scale) {
|
||||||
|
this.scale = scale;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return source.getDistance(x / scale, y / scale, z / scale) * scale;
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/ru/bclib/sdf/operator/SDFScale3D.java
Normal file
19
src/main/java/ru/bclib/sdf/operator/SDFScale3D.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
public class SDFScale3D extends SDFUnary {
|
||||||
|
private float x;
|
||||||
|
private float y;
|
||||||
|
private float z;
|
||||||
|
|
||||||
|
public SDFScale3D setScale(float x, float y, float z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return source.getDistance(x / this.x, y / this.y, z / this.z);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
|
public class SDFSmoothIntersection extends SDFBinary {
|
||||||
|
private float radius;
|
||||||
|
|
||||||
|
public SDFSmoothIntersection setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float a = this.sourceA.getDistance(x, y, z);
|
||||||
|
float b = this.sourceB.getDistance(x, y, z);
|
||||||
|
this.selectValue(a, b);
|
||||||
|
float h = Mth.clamp(0.5F - 0.5F * (b - a) / radius, 0F, 1F);
|
||||||
|
return Mth.lerp(h, b, a) + radius * h * (1F - h);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
|
public class SDFSmoothSubtraction extends SDFBinary {
|
||||||
|
private float radius;
|
||||||
|
|
||||||
|
public SDFSmoothSubtraction setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float a = this.sourceA.getDistance(x, y, z);
|
||||||
|
float b = this.sourceB.getDistance(x, y, z);
|
||||||
|
this.selectValue(a, b);
|
||||||
|
float h = Mth.clamp(0.5F - 0.5F * (b + a) / radius, 0F, 1F);
|
||||||
|
return Mth.lerp(h, b, -a) + radius * h * (1F - h);
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java
Normal file
21
src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
|
public class SDFSmoothUnion extends SDFBinary {
|
||||||
|
private float radius;
|
||||||
|
|
||||||
|
public SDFSmoothUnion setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float a = this.sourceA.getDistance(x, y, z);
|
||||||
|
float b = this.sourceB.getDistance(x, y, z);
|
||||||
|
this.selectValue(a, b);
|
||||||
|
float h = Mth.clamp(0.5F + 0.5F * (b - a) / radius, 0F, 1F);
|
||||||
|
return Mth.lerp(h, b, a) - radius * h * (1F - h);
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java
Normal file
13
src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFSubtraction extends SDFBinary {
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float a = this.sourceA.getDistance(x, y, z);
|
||||||
|
float b = this.sourceB.getDistance(x, y, z);
|
||||||
|
this.selectValue(a, b);
|
||||||
|
return MHelper.max(a, -b);
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/ru/bclib/sdf/operator/SDFTranslate.java
Normal file
19
src/main/java/ru/bclib/sdf/operator/SDFTranslate.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
public class SDFTranslate extends SDFUnary {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
|
||||||
|
public SDFTranslate setTranslate(float x, float y, float z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return source.getDistance(x - this.x, y - this.y, z - this.z);
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/ru/bclib/sdf/operator/SDFUnary.java
Normal file
19
src/main/java/ru/bclib/sdf/operator/SDFUnary.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import ru.bclib.sdf.SDF;
|
||||||
|
|
||||||
|
public abstract class SDFUnary extends SDF {
|
||||||
|
protected SDF source;
|
||||||
|
|
||||||
|
public SDFUnary setSource(SDF source) {
|
||||||
|
this.source = source;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getBlockState(BlockPos pos) {
|
||||||
|
return source.getBlockState(pos);
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ru/bclib/sdf/operator/SDFUnion.java
Normal file
13
src/main/java/ru/bclib/sdf/operator/SDFUnion.java
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package ru.bclib.sdf.operator;
|
||||||
|
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFUnion extends SDFBinary {
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float a = this.sourceA.getDistance(x, y, z);
|
||||||
|
float b = this.sourceB.getDistance(x, y, z);
|
||||||
|
this.selectValue(a, b);
|
||||||
|
return MHelper.min(a, b);
|
||||||
|
}
|
||||||
|
}
|
39
src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java
Normal file
39
src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFCappedCone extends SDFPrimitive {
|
||||||
|
private float radius1;
|
||||||
|
private float radius2;
|
||||||
|
private float height;
|
||||||
|
|
||||||
|
public SDFCappedCone setRadius1(float radius) {
|
||||||
|
this.radius1 = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFCappedCone setRadius2(float radius) {
|
||||||
|
this.radius2 = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFCappedCone setHeight(float height) {
|
||||||
|
this.height = height;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float qx = MHelper.length(x, z);
|
||||||
|
float k2x = radius2 - radius1;
|
||||||
|
float k2y = 2 * height;
|
||||||
|
float cax = qx - MHelper.min(qx, (y < 0F) ? radius1 : radius2);
|
||||||
|
float cay = Math.abs(y) - height;
|
||||||
|
float mlt = Mth.clamp(MHelper.dot(radius2 - qx, height - y, k2x, k2y) / MHelper.dot(k2x, k2y, k2x, k2y), 0F, 1F);
|
||||||
|
float cbx = qx - radius2 + k2x * mlt;
|
||||||
|
float cby = y - height + k2y * mlt;
|
||||||
|
float s = (cbx < 0F && cay < 0F) ? -1F : 1F;
|
||||||
|
return s * (float) Math.sqrt(MHelper.min(MHelper.dot(cax, cay, cax, cay), MHelper.dot(cbx, cby, cbx, cby)));
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java
Normal file
24
src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFCapsule extends SDFPrimitive {
|
||||||
|
private float radius;
|
||||||
|
private float height;
|
||||||
|
|
||||||
|
public SDFCapsule setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFCapsule setHeight(float height) {
|
||||||
|
this.height = height;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return MHelper.length(x, y - Mth.clamp(y, 0, height), z) - radius;
|
||||||
|
}
|
||||||
|
}
|
8
src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java
Normal file
8
src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
public class SDFFlatland extends SDFPrimitive {
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
26
src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java
Normal file
26
src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFHexPrism extends SDFPrimitive {
|
||||||
|
private float radius;
|
||||||
|
private float height;
|
||||||
|
|
||||||
|
public SDFHexPrism setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFHexPrism setHeight(float height) {
|
||||||
|
this.height = height;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float px = Math.abs(x);
|
||||||
|
float py = Math.abs(y);
|
||||||
|
float pz = Math.abs(z);
|
||||||
|
return MHelper.max(py - height, MHelper.max((px * 0.866025F + pz * 0.5F), pz) - radius);
|
||||||
|
}
|
||||||
|
}
|
49
src/main/java/ru/bclib/sdf/primitive/SDFLine.java
Normal file
49
src/main/java/ru/bclib/sdf/primitive/SDFLine.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFLine extends SDFPrimitive {
|
||||||
|
private float radius;
|
||||||
|
private float x1;
|
||||||
|
private float y1;
|
||||||
|
private float z1;
|
||||||
|
private float x2;
|
||||||
|
private float y2;
|
||||||
|
private float z2;
|
||||||
|
|
||||||
|
public SDFLine setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFLine setStart(float x, float y, float z) {
|
||||||
|
this.x1 = x;
|
||||||
|
this.y1 = y;
|
||||||
|
this.z1 = z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFLine setEnd(float x, float y, float z) {
|
||||||
|
this.x2 = x;
|
||||||
|
this.y2 = y;
|
||||||
|
this.z2 = z;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float pax = x - x1;
|
||||||
|
float pay = y - y1;
|
||||||
|
float paz = z - z1;
|
||||||
|
|
||||||
|
float bax = x2 - x1;
|
||||||
|
float bay = y2 - y1;
|
||||||
|
float baz = z2 - z1;
|
||||||
|
|
||||||
|
float dpb = MHelper.dot(pax, pay, paz, bax, bay, baz);
|
||||||
|
float dbb = MHelper.dot(bax, bay, baz, bax, bay, baz);
|
||||||
|
float h = Mth.clamp(dpb / dbb, 0F, 1F);
|
||||||
|
return MHelper.length(pax - bax * h, pay - bay * h, paz - baz * h) - radius;
|
||||||
|
}
|
||||||
|
}
|
31
src/main/java/ru/bclib/sdf/primitive/SDFPie.java
Normal file
31
src/main/java/ru/bclib/sdf/primitive/SDFPie.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFPie extends SDFPrimitive {
|
||||||
|
private float sin;
|
||||||
|
private float cos;
|
||||||
|
private float radius;
|
||||||
|
|
||||||
|
public SDFPie setAngle(float angle) {
|
||||||
|
this.sin = (float) Math.sin(angle);
|
||||||
|
this.cos = (float) Math.cos(angle);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFPie setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
float px = Math.abs(x);
|
||||||
|
float l = MHelper.length(px, y, z) - radius;
|
||||||
|
float m = MHelper.dot(px, z, sin, cos);
|
||||||
|
m = Mth.clamp(m, 0, radius);
|
||||||
|
m = MHelper.length(px - sin * m, z - cos * m);
|
||||||
|
return MHelper.max(l, m * (float) Math.signum(cos * px - sin * z));
|
||||||
|
}
|
||||||
|
}
|
39
src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java
Normal file
39
src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import ru.bclib.sdf.SDF;
|
||||||
|
|
||||||
|
public abstract class SDFPrimitive extends SDF {
|
||||||
|
protected Function<BlockPos, BlockState> placerFunction;
|
||||||
|
|
||||||
|
public SDFPrimitive setBlock(Function<BlockPos, BlockState> placerFunction) {
|
||||||
|
this.placerFunction = placerFunction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFPrimitive setBlock(BlockState state) {
|
||||||
|
this.placerFunction = (pos) -> {
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDFPrimitive setBlock(Block block) {
|
||||||
|
this.placerFunction = (pos) -> {
|
||||||
|
return block.defaultBlockState();
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getBlockState(BlockPos pos) {
|
||||||
|
return placerFunction.apply(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public abstract CompoundTag toNBT(CompoundTag root) {
|
||||||
|
|
||||||
|
}*/
|
||||||
|
}
|
17
src/main/java/ru/bclib/sdf/primitive/SDFSphere.java
Normal file
17
src/main/java/ru/bclib/sdf/primitive/SDFSphere.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package ru.bclib.sdf.primitive;
|
||||||
|
|
||||||
|
import ru.bclib.util.MHelper;
|
||||||
|
|
||||||
|
public class SDFSphere extends SDFPrimitive {
|
||||||
|
private float radius;
|
||||||
|
|
||||||
|
public SDFSphere setRadius(float radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDistance(float x, float y, float z) {
|
||||||
|
return MHelper.length(x, y, z) - radius;
|
||||||
|
}
|
||||||
|
}
|
170
src/main/java/ru/bclib/structures/StructureWorld.java
Normal file
170
src/main/java/ru/bclib/structures/StructureWorld.java
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
package ru.bclib.structures;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.NbtUtils;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
|
|
||||||
|
public class StructureWorld {
|
||||||
|
private Map<ChunkPos, Part> parts = Maps.newHashMap();
|
||||||
|
private ChunkPos lastPos;
|
||||||
|
private Part lastPart;
|
||||||
|
private int minX = Integer.MAX_VALUE;
|
||||||
|
private int minY = Integer.MAX_VALUE;
|
||||||
|
private int minZ = Integer.MAX_VALUE;
|
||||||
|
private int maxX = Integer.MIN_VALUE;
|
||||||
|
private int maxY = Integer.MIN_VALUE;
|
||||||
|
private int maxZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
public StructureWorld() {}
|
||||||
|
|
||||||
|
public StructureWorld(CompoundTag tag) {
|
||||||
|
minX = tag.getInt("minX");
|
||||||
|
maxX = tag.getInt("maxX");
|
||||||
|
minY = tag.getInt("minY");
|
||||||
|
maxY = tag.getInt("maxY");
|
||||||
|
minZ = tag.getInt("minZ");
|
||||||
|
maxZ = tag.getInt("maxZ");
|
||||||
|
|
||||||
|
ListTag map = tag.getList("parts", 10);
|
||||||
|
map.forEach((element) -> {
|
||||||
|
CompoundTag compound = (CompoundTag) element;
|
||||||
|
Part part = new Part(compound);
|
||||||
|
int x = compound.getInt("x");
|
||||||
|
int z = compound.getInt("z");
|
||||||
|
parts.put(new ChunkPos(x, z), part);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlock(BlockPos pos, BlockState state) {
|
||||||
|
ChunkPos cPos = new ChunkPos(pos);
|
||||||
|
|
||||||
|
if (cPos.equals(lastPos)) {
|
||||||
|
lastPart.addBlock(pos, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Part part = parts.get(cPos);
|
||||||
|
if (part == null) {
|
||||||
|
part = new Part();
|
||||||
|
parts.put(cPos, part);
|
||||||
|
|
||||||
|
if (cPos.x < minX) minX = cPos.x;
|
||||||
|
if (cPos.x > maxX) maxX = cPos.x;
|
||||||
|
if (cPos.z < minZ) minZ = cPos.z;
|
||||||
|
if (cPos.z > maxZ) maxZ = cPos.z;
|
||||||
|
}
|
||||||
|
if (pos.getY() < minY) minY = pos.getY();
|
||||||
|
if (pos.getY() > maxY) maxY = pos.getY();
|
||||||
|
part.addBlock(pos, state);
|
||||||
|
|
||||||
|
lastPos = cPos;
|
||||||
|
lastPart = part;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean placeChunk(WorldGenLevel world, ChunkPos chunkPos) {
|
||||||
|
Part part = parts.get(chunkPos);
|
||||||
|
if (part != null) {
|
||||||
|
ChunkAccess chunk = world.getChunk(chunkPos.x, chunkPos.z);
|
||||||
|
part.placeChunk(chunk);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag toBNT() {
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
tag.putInt("minX", minX);
|
||||||
|
tag.putInt("maxX", maxX);
|
||||||
|
tag.putInt("minY", minY);
|
||||||
|
tag.putInt("maxY", maxY);
|
||||||
|
tag.putInt("minZ", minZ);
|
||||||
|
tag.putInt("maxZ", maxZ);
|
||||||
|
ListTag map = new ListTag();
|
||||||
|
tag.put("parts", map);
|
||||||
|
parts.forEach((pos, part) -> {
|
||||||
|
map.add(part.toNBT(pos.x, pos.z));
|
||||||
|
});
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundingBox getBounds() {
|
||||||
|
if (minX == Integer.MAX_VALUE || maxX == Integer.MIN_VALUE || minZ == Integer.MAX_VALUE || maxZ == Integer.MIN_VALUE) {
|
||||||
|
return BoundingBox.getUnknownBox();
|
||||||
|
}
|
||||||
|
return new BoundingBox(minX << 4, minY, minZ << 4, (maxX << 4) | 15, maxY, (maxZ << 4) | 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Part {
|
||||||
|
Map<BlockPos, BlockState> blocks = Maps.newHashMap();
|
||||||
|
|
||||||
|
public Part() {}
|
||||||
|
|
||||||
|
public Part(CompoundTag tag) {
|
||||||
|
ListTag map = tag.getList("blocks", 10);
|
||||||
|
ListTag map2 = tag.getList("states", 10);
|
||||||
|
BlockState[] states = new BlockState[map2.size()];
|
||||||
|
for (int i = 0; i < states.length; i++) {
|
||||||
|
states[i] = NbtUtils.readBlockState((CompoundTag) map2.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
map.forEach((element) -> {
|
||||||
|
CompoundTag block = (CompoundTag) element;
|
||||||
|
BlockPos pos = NbtUtils.readBlockPos(block.getCompound("pos"));
|
||||||
|
int stateID = block.getInt("state");
|
||||||
|
BlockState state = stateID < states.length ? states[stateID] : Block.stateById(stateID);
|
||||||
|
blocks.put(pos, state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBlock(BlockPos pos, BlockState state) {
|
||||||
|
BlockPos inner = new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15);
|
||||||
|
blocks.put(inner, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void placeChunk(ChunkAccess chunk) {
|
||||||
|
blocks.forEach((pos, state) -> {
|
||||||
|
chunk.setBlockState(pos, state, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag toNBT(int x, int z) {
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
tag.putInt("x", x);
|
||||||
|
tag.putInt("z", z);
|
||||||
|
ListTag map = new ListTag();
|
||||||
|
tag.put("blocks", map);
|
||||||
|
ListTag stateMap = new ListTag();
|
||||||
|
tag.put("states", stateMap);
|
||||||
|
|
||||||
|
int[] id = new int[1];
|
||||||
|
Map<BlockState, Integer> states = Maps.newHashMap();
|
||||||
|
|
||||||
|
blocks.forEach((pos, state) -> {
|
||||||
|
int stateID = states.getOrDefault(states, -1);
|
||||||
|
if (stateID < 0) {
|
||||||
|
stateID = id[0] ++;
|
||||||
|
states.put(state, stateID);
|
||||||
|
stateMap.add(NbtUtils.writeBlockState(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag block = new CompoundTag();
|
||||||
|
block.put("pos", NbtUtils.writeBlockPos(pos));
|
||||||
|
block.putInt("state", stateID);
|
||||||
|
map.add(block);
|
||||||
|
});
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
src/main/java/ru/bclib/util/BlocksHelper.java
Normal file
171
src/main/java/ru/bclib/util/BlocksHelper.java
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package ru.bclib.util;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.Vec3i;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
|
|
||||||
|
public class BlocksHelper {
|
||||||
|
public static final BooleanProperty ROOTS = BooleanProperty.create("roots");
|
||||||
|
private static final Map<Block, Integer> COLOR_BY_BLOCK = Maps.newHashMap();
|
||||||
|
|
||||||
|
public static final int FLAG_UPDATE_BLOCK = 1;
|
||||||
|
public static final int FLAG_SEND_CLIENT_CHANGES = 2;
|
||||||
|
public static final int FLAG_NO_RERENDER = 4;
|
||||||
|
public static final int FORSE_RERENDER = 8;
|
||||||
|
public static final int FLAG_IGNORE_OBSERVERS = 16;
|
||||||
|
|
||||||
|
public static final int SET_SILENT = FLAG_UPDATE_BLOCK | FLAG_IGNORE_OBSERVERS | FLAG_SEND_CLIENT_CHANGES;
|
||||||
|
public static final int SET_OBSERV = FLAG_UPDATE_BLOCK | FLAG_SEND_CLIENT_CHANGES;
|
||||||
|
public static final Direction[] HORIZONTAL = makeHorizontal();
|
||||||
|
public static final Direction[] DIRECTIONS = Direction.values();
|
||||||
|
|
||||||
|
private static final MutableBlockPos POS = new MutableBlockPos();
|
||||||
|
protected static final BlockState AIR = Blocks.AIR.defaultBlockState();
|
||||||
|
protected static final BlockState WATER = Blocks.WATER.defaultBlockState();
|
||||||
|
|
||||||
|
private static final Vec3i[] OFFSETS = new Vec3i[] {
|
||||||
|
new Vec3i(-1, -1, -1), new Vec3i(-1, -1, 0), new Vec3i(-1, -1, 1),
|
||||||
|
new Vec3i(-1, 0, -1), new Vec3i(-1, 0, 0), new Vec3i(-1, 0, 1),
|
||||||
|
new Vec3i(-1, 1, -1), new Vec3i(-1, 1, 0), new Vec3i(-1, 1, 1),
|
||||||
|
|
||||||
|
new Vec3i(0, -1, -1), new Vec3i(0, -1, 0), new Vec3i(0, -1, 1),
|
||||||
|
new Vec3i(0, 0, -1), new Vec3i(0, 0, 0), new Vec3i(0, 0, 1),
|
||||||
|
new Vec3i(0, 1, -1), new Vec3i(0, 1, 0), new Vec3i(0, 1, 1),
|
||||||
|
|
||||||
|
new Vec3i(1, -1, -1), new Vec3i(1, -1, 0), new Vec3i(1, -1, 1),
|
||||||
|
new Vec3i(1, 0, -1), new Vec3i(1, 0, 0), new Vec3i(1, 0, 1),
|
||||||
|
new Vec3i(1, 1, -1), new Vec3i(1, 1, 0), new Vec3i(1, 1, 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void addBlockColor(Block block, int color) {
|
||||||
|
COLOR_BY_BLOCK.put(block, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getBlockColor(Block block) {
|
||||||
|
return COLOR_BY_BLOCK.getOrDefault(block, 0xFF000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, BlockState state) {
|
||||||
|
world.setBlock(pos, state, SET_SILENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, Block block) {
|
||||||
|
world.setBlock(pos, block.defaultBlockState(), SET_SILENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWithUpdate(LevelAccessor world, BlockPos pos, BlockState state) {
|
||||||
|
world.setBlock(pos, state, SET_OBSERV);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWithUpdate(LevelAccessor world, BlockPos pos, Block block) {
|
||||||
|
world.setBlock(pos, block.defaultBlockState(), SET_OBSERV);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int upRay(LevelAccessor world, BlockPos pos, int maxDist) {
|
||||||
|
int length = 0;
|
||||||
|
for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.above(j))); j++) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int downRay(LevelAccessor world, BlockPos pos, int maxDist) {
|
||||||
|
int length = 0;
|
||||||
|
for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.below(j))); j++) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int downRayRep(LevelAccessor world, BlockPos pos, int maxDist) {
|
||||||
|
POS.set(pos);
|
||||||
|
for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) {
|
||||||
|
POS.setY(POS.getY() - 1);
|
||||||
|
}
|
||||||
|
return pos.getY() - POS.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int raycastSqr(LevelAccessor world, BlockPos pos, int dx, int dy, int dz, int maxDist) {
|
||||||
|
POS.set(pos);
|
||||||
|
for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) {
|
||||||
|
POS.move(dx, dy, dz);
|
||||||
|
}
|
||||||
|
return (int) pos.distSqr(POS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockState rotateHorizontal(BlockState state, Rotation rotation, Property<Direction> facing) {
|
||||||
|
return state.setValue(facing, rotation.rotate(state.getValue(facing)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockState mirrorHorizontal(BlockState state, Mirror mirror, Property<Direction> facing) {
|
||||||
|
return state.rotate(mirror.getRotation(state.getValue(facing)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getLengthDown(LevelAccessor world, BlockPos pos, Block block) {
|
||||||
|
int count = 1;
|
||||||
|
while (world.getBlockState(pos.below(count)).getBlock() == block) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void cover(LevelAccessor world, BlockPos center, Block ground, BlockState cover, int radius, Random random) {
|
||||||
|
HashSet<BlockPos> points = new HashSet<BlockPos>();
|
||||||
|
HashSet<BlockPos> points2 = new HashSet<BlockPos>();
|
||||||
|
if (world.getBlockState(center).getBlock() == ground) {
|
||||||
|
points.add(center);
|
||||||
|
points2.add(center);
|
||||||
|
for (int i = 0; i < radius; i++) {
|
||||||
|
Iterator<BlockPos> iterator = points.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
BlockPos pos = iterator.next();
|
||||||
|
for (Vec3i offset : OFFSETS) {
|
||||||
|
if (random.nextBoolean()) {
|
||||||
|
BlockPos pos2 = pos.offset(offset);
|
||||||
|
if (random.nextBoolean() && world.getBlockState(pos2).getBlock() == ground
|
||||||
|
&& !points.contains(pos2))
|
||||||
|
points2.add(pos2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
points.addAll(points2);
|
||||||
|
points2.clear();
|
||||||
|
}
|
||||||
|
Iterator<BlockPos> iterator = points.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
BlockPos pos = iterator.next();
|
||||||
|
BlocksHelper.setWithoutUpdate(world, pos, cover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Direction[] makeHorizontal() {
|
||||||
|
return new Direction[] { Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Direction randomHorizontal(Random random) {
|
||||||
|
return HORIZONTAL[random.nextInt(4)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Direction randomDirection(Random random) {
|
||||||
|
return DIRECTIONS[random.nextInt(6)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isFluid(BlockState blockState) {
|
||||||
|
return !blockState.getFluidState().isEmpty();
|
||||||
|
}
|
||||||
|
}
|
344
src/main/java/ru/bclib/util/SplineHelper.java
Normal file
344
src/main/java/ru/bclib/util/SplineHelper.java
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
package ru.bclib.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.mojang.math.Vector3f;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import ru.bclib.sdf.SDF;
|
||||||
|
import ru.bclib.sdf.operator.SDFUnion;
|
||||||
|
import ru.bclib.sdf.primitive.SDFLine;
|
||||||
|
|
||||||
|
public class SplineHelper {
|
||||||
|
public static List<Vector3f> makeSpline(float x1, float y1, float z1, float x2, float y2, float z2, int points) {
|
||||||
|
List<Vector3f> spline = Lists.newArrayList();
|
||||||
|
spline.add(new Vector3f(x1, y1, z1));
|
||||||
|
int count = points - 1;
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
float delta = (float) i / (float) count;
|
||||||
|
float x = Mth.lerp(delta, x1, x2);
|
||||||
|
float y = Mth.lerp(delta, y1, y2);
|
||||||
|
float z = Mth.lerp(delta, z1, z2);
|
||||||
|
spline.add(new Vector3f(x, y, z));
|
||||||
|
}
|
||||||
|
spline.add(new Vector3f(x2, y2, z2));
|
||||||
|
return spline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Vector3f> smoothSpline(List<Vector3f> spline, int segmentPoints) {
|
||||||
|
List<Vector3f> result = Lists.newArrayList();
|
||||||
|
Vector3f start = spline.get(0);
|
||||||
|
for (int i = 1; i < spline.size(); i++) {
|
||||||
|
Vector3f end = spline.get(i);
|
||||||
|
for (int j = 0; j < segmentPoints; j++) {
|
||||||
|
float delta = (float) j / segmentPoints;
|
||||||
|
delta = 0.5F - 0.5F * Mth.cos(delta * 3.14159F);
|
||||||
|
result.add(lerp(start, end, delta));
|
||||||
|
}
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
result.add(start);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector3f lerp(Vector3f start, Vector3f end, float delta) {
|
||||||
|
float x = Mth.lerp(delta, start.x(), end.x());
|
||||||
|
float y = Mth.lerp(delta, start.y(), end.y());
|
||||||
|
float z = Mth.lerp(delta, start.z(), end.z());
|
||||||
|
return new Vector3f(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void offsetParts(List<Vector3f> spline, Random random, float dx, float dy, float dz) {
|
||||||
|
int count = spline.size();
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
Vector3f pos = spline.get(i);
|
||||||
|
float x = pos.x() + (float) random.nextGaussian() * dx;
|
||||||
|
float y = pos.y() + (float) random.nextGaussian() * dy;
|
||||||
|
float z = pos.z() + (float) random.nextGaussian() * dz;
|
||||||
|
pos.set(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void powerOffset(List<Vector3f> spline, float distance, float power) {
|
||||||
|
int count = spline.size();
|
||||||
|
float max = count + 1;
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
Vector3f pos = spline.get(i);
|
||||||
|
float x = (float) i / max;
|
||||||
|
float y = pos.y() + (float) Math.pow(x, power) * distance;
|
||||||
|
pos.set(pos.x(), y, pos.z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SDF buildSDF(List<Vector3f> spline, float radius1, float radius2, Function<BlockPos, BlockState> placerFunction) {
|
||||||
|
int count = spline.size();
|
||||||
|
float max = count - 2;
|
||||||
|
SDF result = null;
|
||||||
|
Vector3f start = spline.get(0);
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
Vector3f pos = spline.get(i);
|
||||||
|
float delta = (float) (i - 1) / max;
|
||||||
|
SDF line = new SDFLine()
|
||||||
|
.setRadius(Mth.lerp(delta, radius1, radius2))
|
||||||
|
.setStart(start.x(), start.y(), start.z())
|
||||||
|
.setEnd(pos.x(), pos.y(), pos.z())
|
||||||
|
.setBlock(placerFunction);
|
||||||
|
result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line);
|
||||||
|
start = pos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SDF buildSDF(List<Vector3f> spline, Function<Float, Float> radiusFunction, Function<BlockPos, BlockState> placerFunction) {
|
||||||
|
int count = spline.size();
|
||||||
|
float max = count - 2;
|
||||||
|
SDF result = null;
|
||||||
|
Vector3f start = spline.get(0);
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
Vector3f pos = spline.get(i);
|
||||||
|
float delta = (float) (i - 1) / max;
|
||||||
|
SDF line = new SDFLine()
|
||||||
|
.setRadius(radiusFunction.apply(delta))
|
||||||
|
.setStart(start.x(), start.y(), start.z())
|
||||||
|
.setEnd(pos.x(), pos.y(), pos.z())
|
||||||
|
.setBlock(placerFunction);
|
||||||
|
result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line);
|
||||||
|
start = pos;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean fillSpline(List<Vector3f> spline, WorldGenLevel world, BlockState state, BlockPos pos, Function<BlockState, Boolean> replace) {
|
||||||
|
Vector3f startPos = spline.get(0);
|
||||||
|
for (int i = 1; i < spline.size(); i++) {
|
||||||
|
Vector3f endPos = spline.get(i);
|
||||||
|
if (!(fillLine(startPos, endPos, world, state, pos, replace))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
startPos = endPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fillSplineForce(List<Vector3f> spline, WorldGenLevel world, BlockState state, BlockPos pos, Function<BlockState, Boolean> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean fillLine(Vector3f start, Vector3f end, WorldGenLevel world, BlockState state, BlockPos pos, Function<BlockState, Boolean> replace) {
|
||||||
|
float dx = end.x() - start.x();
|
||||||
|
float dy = end.y() - start.y();
|
||||||
|
float dz = end.z() - start.z();
|
||||||
|
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.x();
|
||||||
|
float y = start.y();
|
||||||
|
float z = start.z();
|
||||||
|
boolean down = Math.abs(dy) > 0.2;
|
||||||
|
|
||||||
|
BlockState bState;
|
||||||
|
MutableBlockPos bPos = new MutableBlockPos();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ());
|
||||||
|
bState = world.getBlockState(bPos);
|
||||||
|
if (bState.equals(state) || 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
x += dx;
|
||||||
|
y += dy;
|
||||||
|
z += dz;
|
||||||
|
}
|
||||||
|
bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ());
|
||||||
|
bState = world.getBlockState(bPos);
|
||||||
|
if (bState.equals(state) || 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);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fillLineForce(Vector3f start, Vector3f end, WorldGenLevel world, BlockState state, BlockPos pos, Function<BlockState, Boolean> replace) {
|
||||||
|
float dx = end.x() - start.x();
|
||||||
|
float dy = end.y() - start.y();
|
||||||
|
float dz = end.z() - start.z();
|
||||||
|
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.x();
|
||||||
|
float y = start.y();
|
||||||
|
float z = start.z();
|
||||||
|
boolean down = Math.abs(dy) > 0.2;
|
||||||
|
|
||||||
|
BlockState bState;
|
||||||
|
MutableBlockPos bPos = new MutableBlockPos();
|
||||||
|
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 && replace.apply(bState)) {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, bPos, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x += dx;
|
||||||
|
y += dy;
|
||||||
|
z += dz;
|
||||||
|
}
|
||||||
|
bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.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 && replace.apply(bState)) {
|
||||||
|
BlocksHelper.setWithoutUpdate(world, bPos, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canGenerate(List<Vector3f> spline, float scale, BlockPos start, WorldGenLevel world, Function<BlockState, Boolean> canReplace) {
|
||||||
|
int count = spline.size();
|
||||||
|
Vector3f vec = spline.get(0);
|
||||||
|
MutableBlockPos mut = new MutableBlockPos();
|
||||||
|
float x1 = start.getX() + vec.x() * scale;
|
||||||
|
float y1 = start.getY() + vec.y() * scale;
|
||||||
|
float z1 = start.getZ() + vec.z() * scale;
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
vec = spline.get(i);
|
||||||
|
float x2 = start.getX() + vec.x() * scale;
|
||||||
|
float y2 = start.getY() + vec.y() * scale;
|
||||||
|
float z2 = start.getZ() + vec.z() * scale;
|
||||||
|
|
||||||
|
for (float py = y1; py < y2; py += 3) {
|
||||||
|
if (py - start.getY() < 10) continue;
|
||||||
|
float lerp = (py - y1) / (y2 - y1);
|
||||||
|
float x = Mth.lerp(lerp, x1, x2);
|
||||||
|
float z = Mth.lerp(lerp, z1, z2);
|
||||||
|
mut.set(x, py, z);
|
||||||
|
if (!canReplace.apply(world.getBlockState(mut))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 = x2;
|
||||||
|
y1 = y2;
|
||||||
|
z1 = z2;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canGenerate(List<Vector3f> spline, BlockPos start, WorldGenLevel world, Function<BlockState, Boolean> canReplace) {
|
||||||
|
int count = spline.size();
|
||||||
|
Vector3f vec = spline.get(0);
|
||||||
|
MutableBlockPos mut = new MutableBlockPos();
|
||||||
|
float x1 = start.getX() + vec.x();
|
||||||
|
float y1 = start.getY() + vec.y();
|
||||||
|
float z1 = start.getZ() + vec.z();
|
||||||
|
for (int i = 1; i < count; i++) {
|
||||||
|
vec = spline.get(i);
|
||||||
|
float x2 = start.getX() + vec.x();
|
||||||
|
float y2 = start.getY() + vec.y();
|
||||||
|
float z2 = start.getZ() + vec.z();
|
||||||
|
|
||||||
|
for (float py = y1; py < y2; py += 3) {
|
||||||
|
if (py - start.getY() < 10) continue;
|
||||||
|
float lerp = (py - y1) / (y2 - y1);
|
||||||
|
float x = Mth.lerp(lerp, x1, x2);
|
||||||
|
float z = Mth.lerp(lerp, z1, z2);
|
||||||
|
mut.set(x, py, z);
|
||||||
|
if (!canReplace.apply(world.getBlockState(mut))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 = x2;
|
||||||
|
y1 = y2;
|
||||||
|
z1 = z2;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector3f getPos(List<Vector3f> spline, float index) {
|
||||||
|
int i = (int) index;
|
||||||
|
int last = spline.size() - 1;
|
||||||
|
if (i >= last) {
|
||||||
|
return spline.get(last);
|
||||||
|
}
|
||||||
|
float delta = index - i;
|
||||||
|
Vector3f p1 = spline.get(i);
|
||||||
|
Vector3f p2 = spline.get(i + 1);
|
||||||
|
float x = Mth.lerp(delta, p1.x(), p2.x());
|
||||||
|
float y = Mth.lerp(delta, p1.y(), p2.y());
|
||||||
|
float z = Mth.lerp(delta, p1.z(), p2.z());
|
||||||
|
return new Vector3f(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void rotateSpline(List<Vector3f> spline, float angle) {
|
||||||
|
for (Vector3f v: spline) {
|
||||||
|
float sin = (float) Math.sin(angle);
|
||||||
|
float cos = (float) Math.cos(angle);
|
||||||
|
float x = v.x() * cos + v.z() * sin;
|
||||||
|
float z = v.x() * sin + v.z() * cos;
|
||||||
|
v.set(x, v.y(), z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Vector3f> copySpline(List<Vector3f> spline) {
|
||||||
|
List<Vector3f> result = new ArrayList<Vector3f>(spline.size());
|
||||||
|
for (Vector3f v: spline) {
|
||||||
|
result.add(new Vector3f(v.x(), v.y(), v.z()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void scale(List<Vector3f> spline, float scale) {
|
||||||
|
scale(spline, scale, scale, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void scale(List<Vector3f> spline, float x, float y, float z) {
|
||||||
|
for (Vector3f v: spline) {
|
||||||
|
v.set(v.x() * x, v.y() * y, v.z() * z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void offset(List<Vector3f> spline, Vector3f offset) {
|
||||||
|
for (Vector3f v: spline) {
|
||||||
|
v.set(offset.x() + v.x(), offset.y() + v.y(), offset.z() + v.z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
src/main/java/ru/bclib/util/TagHelper.java
Normal file
73
src/main/java/ru/bclib/util/TagHelper.java
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package ru.bclib.util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.Tag;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
|
||||||
|
public class TagHelper {
|
||||||
|
private static final Map<ResourceLocation, Set<ResourceLocation>> TAGS_BLOCK = Maps.newConcurrentMap();
|
||||||
|
private static final Map<ResourceLocation, Set<ResourceLocation>> TAGS_ITEM = Maps.newConcurrentMap();
|
||||||
|
|
||||||
|
public static void addTag(Tag.Named<Block> tag, Block... blocks) {
|
||||||
|
ResourceLocation tagID = tag.getName();
|
||||||
|
Set<ResourceLocation> set = TAGS_BLOCK.computeIfAbsent(tagID, k -> Sets.newHashSet());
|
||||||
|
for (Block block: blocks) {
|
||||||
|
ResourceLocation id = Registry.BLOCK.getKey(block);
|
||||||
|
if (id != Registry.BLOCK.getDefaultKey()) {
|
||||||
|
set.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addTag(Tag.Named<Item> tag, ItemLike... items) {
|
||||||
|
ResourceLocation tagID = tag.getName();
|
||||||
|
Set<ResourceLocation> set = TAGS_ITEM.computeIfAbsent(tagID, k -> Sets.newHashSet());
|
||||||
|
for (ItemLike item: items) {
|
||||||
|
ResourceLocation id = Registry.ITEM.getKey(item.asItem());
|
||||||
|
if (id != Registry.ITEM.getDefaultKey()) {
|
||||||
|
set.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static void addTags(ItemLike item, Tag.Named<Item>... tags) {
|
||||||
|
for (Tag.Named<Item> tag: tags) {
|
||||||
|
addTag(tag, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static void addTags(Block block, Tag.Named<Block>... tags) {
|
||||||
|
for (Tag.Named<Block> tag: tags) {
|
||||||
|
addTag(tag, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Tag.Builder apply(Tag.Builder builder, Set<ResourceLocation> ids) {
|
||||||
|
ids.forEach(value -> builder.addElement(value, "Better End Code"));
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<ResourceLocation, Tag.Builder> apply(String entry, Map<ResourceLocation, Tag.Builder> tagsMap) {
|
||||||
|
Map<ResourceLocation, Set<ResourceLocation>> endTags = null;
|
||||||
|
if (entry.equals("block")) {
|
||||||
|
endTags = TAGS_BLOCK;
|
||||||
|
} else if (entry.equals("item")) {
|
||||||
|
endTags = TAGS_ITEM;
|
||||||
|
}
|
||||||
|
if (endTags != null) {
|
||||||
|
endTags.forEach((id, ids) -> apply(tagsMap.computeIfAbsent(id, key -> Tag.Builder.tag()), ids));
|
||||||
|
}
|
||||||
|
return tagsMap;
|
||||||
|
}
|
||||||
|
}
|
119
src/main/java/ru/bclib/util/TranslationHelper.java
Normal file
119
src/main/java/ru/bclib/util/TranslationHelper.java
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package ru.bclib.util;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.data.BuiltinRegistries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
public class TranslationHelper {
|
||||||
|
public static void printMissingNames(String modID) {
|
||||||
|
List<String> missingNamesEn = Lists.newArrayList();
|
||||||
|
List<String> missingNamesRu = Lists.newArrayList();
|
||||||
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
InputStream streamEn = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/en_us.json");
|
||||||
|
InputStream streamRu = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/ru_ru.json");
|
||||||
|
JsonObject translationEn = gson.fromJson(new InputStreamReader(streamEn), JsonObject.class);
|
||||||
|
JsonObject translationRu = gson.fromJson(new InputStreamReader(streamRu), JsonObject.class);
|
||||||
|
|
||||||
|
Registry.BLOCK.forEach(block -> {
|
||||||
|
if (Registry.BLOCK.getKey(block).getNamespace().equals(modID)) {
|
||||||
|
String name = block.getName().getString();
|
||||||
|
if (!translationEn.has(name)) {
|
||||||
|
missingNamesEn.add(name);
|
||||||
|
}
|
||||||
|
if (!translationRu.has(name)) {
|
||||||
|
missingNamesRu.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Registry.ITEM.forEach(item -> {
|
||||||
|
if (Registry.ITEM.getKey(item).getNamespace().equals(modID)) {
|
||||||
|
String name = item.getDescription().getString();
|
||||||
|
if (!translationEn.has(name)) {
|
||||||
|
missingNamesEn.add(name);
|
||||||
|
}
|
||||||
|
if (!translationRu.has(name)) {
|
||||||
|
missingNamesRu.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
BuiltinRegistries.BIOME.forEach(biome -> {
|
||||||
|
ResourceLocation id = BuiltinRegistries.BIOME.getKey(biome);
|
||||||
|
if (id.getNamespace().equals(modID)) {
|
||||||
|
String name = "biome." + modID + "." + id.getPath();
|
||||||
|
if (!translationEn.has(name)) {
|
||||||
|
missingNamesEn.add(name);
|
||||||
|
}
|
||||||
|
if (!translationRu.has(name)) {
|
||||||
|
missingNamesRu.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Registry.ENTITY_TYPE.forEach((entity) -> {
|
||||||
|
ResourceLocation id = Registry.ENTITY_TYPE.getKey(entity);
|
||||||
|
if (id.getNamespace().equals(modID)) {
|
||||||
|
String name = "entity." + modID + "." + id.getPath();
|
||||||
|
if (!translationEn.has(name)) {
|
||||||
|
missingNamesEn.add(name);
|
||||||
|
}
|
||||||
|
if (!translationRu.has(name)) {
|
||||||
|
missingNamesRu.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!missingNamesEn.isEmpty() || !missingNamesRu.isEmpty()) {
|
||||||
|
|
||||||
|
System.out.println("========================================");
|
||||||
|
System.out.println(" MISSING NAMES LIST");
|
||||||
|
|
||||||
|
if (!missingNamesEn.isEmpty()) {
|
||||||
|
Collections.sort(missingNamesEn);
|
||||||
|
System.out.println("========================================");
|
||||||
|
System.out.println(" ENGLISH");
|
||||||
|
System.out.println("========================================");
|
||||||
|
missingNamesEn.forEach((name) -> {
|
||||||
|
System.out.println(" \"" + name + "\": \"" + fastTranslateEn(name) + "\",");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missingNamesRu.isEmpty()) {
|
||||||
|
Collections.sort(missingNamesRu);
|
||||||
|
System.out.println("========================================");
|
||||||
|
System.out.println(" RUSSIAN");
|
||||||
|
System.out.println("========================================");
|
||||||
|
missingNamesRu.forEach((name) -> {
|
||||||
|
System.out.println(" \"" + name + "\": \"\",");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("========================================");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String fastTranslateEn(String text) {
|
||||||
|
String[] words = text.substring(text.lastIndexOf('.') + 1).split("_");
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < words.length; i++) {
|
||||||
|
String word = words[i];
|
||||||
|
builder.append(Character.toUpperCase(word.charAt(0)));
|
||||||
|
builder.append(word, 1, word.length());
|
||||||
|
if (i < words.length - 1) {
|
||||||
|
builder.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue