Add some of the collision code

This commit is contained in:
2025-07-04 09:16:49 +02:00
parent 0edf4d73bd
commit 1fe8c79f51
5 changed files with 410 additions and 16 deletions
@@ -23,6 +23,7 @@ import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.features.simulator.data.Simulator;
import de.steamwar.bausystem.features.simulator.execute.SimulatorExecutor;
import de.steamwar.bausystem.features.simulator.preview.SimulatorPreviewData;
import de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
@@ -90,6 +91,15 @@ public class SimulatorCommand extends SWCommand {
}
}
@Register(value = "test")
public void test(Player player) {
SimulatorPreviewData data = new SimulatorPreviewData(null);
data.spawnTNT(0, 120, 0);
data.tick();
data.tick();
data.tick();
}
@ClassMapper(value = Simulator.class, local = true)
public TypeMapper<Simulator> allSimulators() {
return new TypeMapper<>() {
@@ -0,0 +1,108 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.simulator.preview;
import lombok.AllArgsConstructor;
import lombok.ToString;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.VoxelShape;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
@ToString
public class SimulatorPreviewAABB {
public double minX;
public double minY;
public double minZ;
public double maxX;
public double maxY;
public double maxZ;
public SimulatorPreviewAABB copy() {
return new SimulatorPreviewAABB(minX, minY, minZ, maxX, maxY, maxZ);
}
public void expandTowards(double vx, double vy, double vz) {
if (vx < 0) {
minX += vx;
} else {
maxX += vx;
}
if (vy < 0) {
minY += vy;
} else {
maxY += vy;
}
if (vz < 0) {
minZ += vz;
} else {
maxZ += vz;
}
}
public void add(double vx, double vy, double vz) {
minX += vx;
minY += vy;
minZ += vz;
}
public double sizeX() {
return maxX - minX;
}
public double sizeY() {
return maxY - minY;
}
public double sizeZ() {
return maxZ - minZ;
}
public List<BoundingBox> intersects(int x, int y, int z, VoxelShape voxelShape) {
return voxelShape.getBoundingBoxes()
.stream()
.filter(boundingBox -> {
return minX - (x + boundingBox.getMaxX()) < -1.0E-7 &&
maxX - (x + boundingBox.getMinX()) > 1.0E-7 &&
minY - (y + boundingBox.getMaxY()) < -1.0E-7 &&
maxY - (y + boundingBox.getMinY()) > 1.0E-7 &&
minZ - (z + boundingBox.getMaxZ()) < -1.0E-7 &&
maxZ - (z + boundingBox.getMinZ()) > 1.0E-7;
})
.collect(Collectors.toList());
}
public boolean intersectsX(double x, double y, double z, BoundingBox boundingBox) {
return minX - (x + boundingBox.getMaxX()) < -1.0E-7 &&
maxX - (x + boundingBox.getMinX()) > 1.0E-7;
}
public boolean intersectsY(int x, int y, int z, BoundingBox boundingBox) {
return minY - (y + boundingBox.getMaxY()) < -1.0E-7 &&
maxY - (y + boundingBox.getMinY()) > 1.0E-7;
}
public boolean intersectsZ(double x, double y, double z, BoundingBox boundingBox) {
return minZ - (z + boundingBox.getMaxZ()) < -1.0E-7 &&
maxZ - (z + boundingBox.getMinZ()) > 1.0E-7;
}
}
@@ -0,0 +1,92 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.simulator.preview;
import de.steamwar.bausystem.features.simulator.data.Simulator;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.VoxelShape;
import java.util.*;
@RequiredArgsConstructor
public class SimulatorPreviewData {
private static final World WORLD = Bukkit.getWorlds().get(0);
public final Simulator simulator;
public final List<SimulatorPreviewTNT> tnts = new ArrayList<>();
private final Set<Pos> AIR = new HashSet<>();
private final Map<Pos, BlockData> datas = new HashMap<>();
private final Map<Pos, VoxelShape> shapes = new HashMap<>();
public BlockData getBlockData(int x, int y, int z) {
Pos pos = new Pos(x, y, z);
if (AIR.contains(pos)) return null;
return datas.computeIfAbsent(pos, __ -> {
BlockData blockData = WORLD.getBlockData(x, y, z);
if (blockData.getMaterial().isAir()) {
AIR.add(pos);
return null;
}
return blockData;
});
}
public VoxelShape getBlockShape(int x, int y, int z) {
Pos pos = new Pos(x, y, z);
if (AIR.contains(pos)) return null;
return shapes.computeIfAbsent(pos, __ -> {
Block block = WORLD.getBlockAt(x, y, z);
if (block.getType().isAir()) {
AIR.add(pos);
return null;
}
return block.getCollisionShape();
});
}
public void setAir(int x, int y, int z) {
Pos pos = new Pos(x, y, z);
AIR.add(pos);
datas.remove(pos);
shapes.remove(pos);
}
public void spawnTNT(double x, double y, double z) {
tnts.add(new SimulatorPreviewTNT(this, x, y, z));
}
public void tick() {
tnts.forEach(SimulatorPreviewTNT::tick);
}
@RequiredArgsConstructor
@EqualsAndHashCode
private final class Pos {
private final int x;
private final int y;
private final int z;
}
}
@@ -19,15 +19,21 @@
package de.steamwar.bausystem.features.simulator.preview;
import lombok.RequiredArgsConstructor;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector;
import org.bukkit.util.VoxelShape;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class SimulatorPreviewTNT {
private static final Random random = new Random();
private boolean gravity = false;
private final SimulatorPreviewData data;
private boolean gravity = true;
private int fuse = 80;
private double x;
private double y;
@@ -36,9 +42,12 @@ public class SimulatorPreviewTNT {
private double vy;
private double vz;
private SimulatorPreviewVec collide = new SimulatorPreviewVec();
private boolean onGround;
public SimulatorPreviewTNT(double x, double y, double z) {
public SimulatorPreviewTNT(SimulatorPreviewData data, double x, double y, double z) {
this.data = data;
this.x = x;
this.y = y;
this.z = z;
@@ -51,7 +60,7 @@ public class SimulatorPreviewTNT {
public void tick() {
// Gravity
if (!gravity) {
if (gravity) {
vy -= 0.04;
}
@@ -83,19 +92,19 @@ public class SimulatorPreviewTNT {
}
public void move() {
Vector vec = collide();
double d = vec.lengthSquared();
if (d > 1.0E-7 || new Vector(vx, vy, vz).lengthSquared() - d < 1.0E-7) {
x += vx;
y += vy;
z += vz;
collide();
double collisionLengthSquared = collide.lengthSquared();
if (collisionLengthSquared > 1.0E-7 || new Vector(vx, vy, vz).lengthSquared() - collisionLengthSquared < 1.0E-7) {
x += collide.x;
y += collide.y;
z += collide.z;
}
boolean xCollision = !equal(vx, vec.getX());
boolean zCollision = !equal(vz, vec.getZ());
boolean xCollision = !equal(vx, collide.x);
boolean zCollision = !equal(vz, collide.z);
boolean horizontalCollision = xCollision || zCollision;
if (Math.abs(vy) > 0.0F) {
boolean verticalCollision = vy != vec.getY();
boolean verticalCollision = vy != collide.y;
onGround = verticalCollision && vy < (double) 0.0F;
}
@@ -103,6 +112,9 @@ public class SimulatorPreviewTNT {
if (xCollision) vx = 0;
if (zCollision) vz = 0;
}
// vx = collide.x;
vy = collide.y;
// vz = collide.z;
// TODO: Get Block -> updateEntityMovementAfterFallOn!
// TODO: Get BlockSpeedFactor multiply
}
@@ -111,13 +123,155 @@ public class SimulatorPreviewTNT {
return Math.abs(y - x) < (double) 1.0E-5F;
}
public Vector collide() {
if (x == 0 && y == 0 && z == 0) return new Vector(0, 0, 0);
// TODO: Implement rest!
return null;
public void collide() {
boolean xZero = vx == 0;
boolean zZero = vz == 0;
if (xZero && vy == 0 && zZero) {
collide.x = 0;
collide.y = 0;
collide.z = 0;
return;
}
SimulatorPreviewAABB box = new SimulatorPreviewAABB(x, y, z, x + 0.98, y + 0.98, z + 0.98);
SimulatorPreviewAABB initialCollisionBox = box.copy();
initialCollisionBox.expandTowards(vx, vy, vz);
System.out.println(vy);
List<CollisionData> collisionDataList = new ArrayList<>();
for (int x = floor(initialCollisionBox.minX - 1.0E-7) - 1; x < floor(initialCollisionBox.maxX + 1.0E-7) + 1; x++) {
for (int y = floor(initialCollisionBox.minY - 1.0E-7) - 1; y < floor(initialCollisionBox.maxY + 1.0E-7) + 1; y++) {
for (int z = floor(initialCollisionBox.minZ - 1.0E-7) - 1; z < floor(initialCollisionBox.maxZ + 1.0E-7) + 1; z++) {
System.out.println(x + " " + y + " " + z);
VoxelShape shape = data.getBlockShape(x, y, z);
if (shape == null) continue;
List<BoundingBox> collisionBoxes = initialCollisionBox.intersects(x, y, z, shape);
if (collisionBoxes.isEmpty()) continue;
collisionDataList.add(new CollisionData(x, y, z, collisionBoxes));
}
}
}
if (collisionDataList.isEmpty()) {
System.out.println("No collision found");
collide.x = vx;
collide.y = vy;
collide.z = vz;
return;
}
double vx = this.vx;
double vy = this.vy;
double vz = this.vz;
System.out.println(vx + " " + vy + " " + vz);
if (vy != 0) {
for (CollisionData collisionData : collisionDataList) {
vy = collideY(box, collisionData, vy);
}
if (vy != 0) box.add(0, vy, 0);
}
boolean xSmaller = Math.abs(vx) < Math.abs(vz);
if (xSmaller && vz != 0) {
for (CollisionData collisionData : collisionDataList) {
vz = collideZ(box, collisionData, vz);
}
if (vz != 0) box.add(0, 0, vz);
}
if (vx != 0) {
for (CollisionData collisionData : collisionDataList) {
vx = collideX(box, collisionData, vx);
}
if (vx != 0) box.add(vx, 0, 0);
}
if (!xSmaller && vz != 0) {
for (CollisionData collisionData : collisionDataList) {
vz = collideZ(box, collisionData, vz);
}
if (vz != 0) box.add(0, 0, vz);
}
System.out.println(vx + " " + vy + " " + vz);
collide.x = vx;
collide.y = vy;
collide.z = vz;
}
public static int floor(double value) {
int i = (int) value;
return value < (double) i ? i - 1 : i;
}
public static double collideX(SimulatorPreviewAABB box, CollisionData collisionData, double vx) {
if (vx < 0) {
for (BoundingBox boundingBox : collisionData.collisions) {
if (box.intersectsX(collisionData.x + vx, collisionData.y, collisionData.z, boundingBox)) {
vx = Math.max(vx, boundingBox.getMaxX());
}
}
} else {
for (BoundingBox boundingBox : collisionData.collisions) {
if (box.intersectsX(collisionData.x + vx, collisionData.y, collisionData.z, boundingBox)) {
vx = Math.min(vx, boundingBox.getMinX());
}
}
vx -= box.sizeY();
}
return vx;
}
public static double collideY(SimulatorPreviewAABB box, CollisionData collisionData, double vy) {
double size = box.sizeY();
box = box.copy();
box.expandTowards(0, vy, 0);
if (vy < 0) {
for (BoundingBox boundingBox : collisionData.collisions) {
if (box.intersectsY(collisionData.x, collisionData.y, collisionData.z, boundingBox)) {
vy = Math.max(vy, boundingBox.getMaxY() + collisionData.y - box.minY - size);
}
}
System.out.println("N: " + vy);
} else {
for (BoundingBox boundingBox : collisionData.collisions) {
if (box.intersectsY(collisionData.x, collisionData.y, collisionData.z, boundingBox)) {
vy = Math.min(vy, boundingBox.getMinY() + collisionData.y - box.minY - size);
}
}
System.out.println("P: " + vy);
}
return vy;
}
public static double collideZ(SimulatorPreviewAABB box, CollisionData collisionData, double vz) {
if (vz < 0) {
for (BoundingBox boundingBox : collisionData.collisions) {
if (box.intersectsZ(collisionData.x, collisionData.y, collisionData.z + vz, boundingBox)) {
vz = Math.max(vz, boundingBox.getMaxZ());
}
}
} else {
for (BoundingBox boundingBox : collisionData.collisions) {
if (box.intersectsZ(collisionData.x, collisionData.y, collisionData.z + vz, boundingBox)) {
vz = Math.min(vz, boundingBox.getMinZ());
}
}
vz -= box.sizeZ();
}
return vz;
}
public void explode() {
// TODO: Implement
}
@RequiredArgsConstructor
private class CollisionData {
private final int x;
private final int y;
private final int z;
private final List<BoundingBox> collisions;
}
}
@@ -0,0 +1,30 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.simulator.preview;
public class SimulatorPreviewVec {
public double x;
public double y;
public double z;
public double lengthSquared() {
return x * x + y * y + z * z;
}
}