diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index b74926aa9..83f516cff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -31,6 +31,7 @@ import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.sparrow.nbt.CompoundTag; import org.bukkit.Location; +import org.bukkit.Particle; import org.bukkit.World; import java.nio.file.Path; @@ -119,6 +120,9 @@ public class FurnitureItemBehavior extends ItemBehavior { // 检查方块、实体阻挡 if (!aabbs.isEmpty()) { if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) { + if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) { +// bukkitPlayer.getWorld().spawnParticle(Particle.FLAME, , List.of(bukkitPlayer), ); + } return InteractionResult.FAIL; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java index df4afd16e..d8a9d0d9a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/SetEntityViewDistanceScaleCommand.java @@ -30,11 +30,11 @@ public class SetEntityViewDistanceScaleCommand extends BukkitCommandFeature { if (!Config.enableEntityCulling()) { - context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); return; } if (Config.entityCullingViewDistance() <= 0) { - context.sender().sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("View distance is not enabled on this server").color(NamedTextColor.RED)); return; } Player player = context.get("player"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java index 29971f70a..1d8786ec9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/ToggleEntityCullingCommand.java @@ -32,7 +32,7 @@ public class ToggleEntityCullingCommand extends BukkitCommandFeature { if (!Config.enableEntityCulling()) { - context.sender().sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); + plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED)); return; } Player player = context.get("player"); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 7728e1641..19c66e96d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.network; +import com.destroystokyo.paper.ParticleBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gson.JsonElement; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index adcf7bcc5..1ddab9cd6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -1480,6 +1480,14 @@ public class BukkitServerPlayer extends Player { return LocationUtils.toWorldPosition(this.getEyeLocation()); } + @Override + public void playParticle(Key particleId, double x, double y, double z) { + Particle particle = Registry.PARTICLE_TYPE.get(KeyUtils.toNamespacedKey(particleId)); + if (particle != null) { + platformPlayer().getWorld().spawnParticle(particle, List.of(platformPlayer()), null, x, y, z, 1, 0, 0,0, 0, null, false); + } + } + public Location getEyeLocation() { org.bukkit.entity.Player player = platformPlayer(); Location eyeLocation = player.getEyeLocation(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java index 98a5fb890..2e3a308a6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -146,28 +146,9 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { hitboxes = List.of(defaultHitBox()); } - List aabbs = new ArrayList<>(); - for (FurnitureHitBoxConfig hitBox : hitboxes) { - hitBox.collectBoundingBox(aabbs::add); - } - double minX = 0; - double minY = 0; - double minZ = 0; - double maxX = 0; - double maxY = 0; - double maxZ = 0; - for (AABB aabb : aabbs) { - minX = Math.min(minX, aabb.minX); - minY = Math.min(minY, aabb.minY); - minZ = Math.min(minZ, aabb.minZ); - maxX = Math.max(maxX, aabb.maxX); - maxY = Math.max(maxY, aabb.maxY); - maxZ = Math.max(maxZ, aabb.maxZ); - } - AABB maxAABB = new AABB(minX, minY, minZ, maxX, maxY, maxZ); variants.put(variantName, new FurnitureVariant( variantName, - parseCullingData(section.get("entity-culling"), maxAABB), + parseCullingData(section.get("entity-culling")), elements.toArray(new FurnitureElementConfig[0]), hitboxes.toArray(new FurnitureHitBoxConfig[0]), externalModel, @@ -185,16 +166,18 @@ public abstract class AbstractFurnitureManager implements FurnitureManager { AbstractFurnitureManager.this.byId.put(id, furniture); } - private CullingData parseCullingData(Object arguments, AABB maxHitbox) { + + + private CullingData parseCullingData(Object arguments) { if (arguments instanceof Boolean b && !b) return null; if (!(arguments instanceof Map)) - return new CullingData(maxHitbox, Config.entityCullingViewDistance(), 0.5, true); + return new CullingData(null, Config.entityCullingViewDistance(), 0.25, true); Map argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling"); return new CullingData( - ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", maxHitbox), "aabb"), + ResourceConfigUtils.getOrDefault(argumentsMap.get("aabb"), it -> ResourceConfigUtils.getAsAABB(it, "aabb"), null), ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"), - ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"), + ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.25), "aabb-expansion"), ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing") ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java index 5099472cf..9210c649e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -23,10 +23,13 @@ import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.World; import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.collision.AABB; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -109,9 +112,62 @@ public abstract class Furniture implements Cullable { if (parent == null) return null; AABB aabb = parent.aabb; WorldPosition position = position(); - Vec3d pos1 = getRelativePosition(position, new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ)); - Vec3d pos2 = getRelativePosition(position, new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ)); - return new CullingData(new AABB(pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + if (aabb == null) { + List aabbs = new ArrayList<>(); + for (FurnitureHitBoxConfig hitBoxConfig : this.currentVariant.hitBoxConfigs()) { + hitBoxConfig.prepareForPlacement(position, aabbs::add); + } + return new CullingData(getMaxAABB(aabbs), parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + } else { + Vector3f[] vertices = new Vector3f[] { + // 底面两个对角点 + new Vector3f((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ), + new Vector3f((float) aabb.maxX, (float) aabb.minY, (float) aabb.maxZ), + // 顶面两个对角点 + new Vector3f((float) aabb.minX, (float) aabb.maxY, (float) aabb.minZ), + new Vector3f((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ) + }; + double minX = Double.MAX_VALUE, minY = aabb.minY; // Y方向不变 + double maxX = -Double.MAX_VALUE, maxY = aabb.maxY; // Y方向不变 + double minZ = Double.MAX_VALUE, maxZ = -Double.MAX_VALUE; + for (Vector3f vertex : vertices) { + Vec3d rotatedPos = getRelativePosition(position, vertex); + minX = Math.min(minX, rotatedPos.x); + minZ = Math.min(minZ, rotatedPos.z); + maxX = Math.max(maxX, rotatedPos.x); + maxZ = Math.max(maxZ, rotatedPos.z); + } + return new CullingData(new AABB(minX, minY, minZ, maxX, maxY, maxZ), + parent.maxDistance, parent.aabbExpansion, parent.rayTracing); + } + } + + private static @NotNull AABB getMaxAABB(List aabbs) { + double minX = 0; + double minY = 0; + double minZ = 0; + double maxX = 0; + double maxY = 0; + double maxZ = 0; + for (int i = 0; i < aabbs.size(); i++) { + AABB aabb = aabbs.get(i); + if (i == 0) { + minX = aabb.minX; + minY = aabb.minY; + minZ = aabb.minZ; + maxX = aabb.maxX; + maxY = aabb.maxY; + maxZ = aabb.maxZ; + } else { + minX = Math.min(minX, aabb.minX); + minY = Math.min(minY, aabb.minY); + minZ = Math.min(minZ, aabb.minZ); + maxX = Math.max(maxX, aabb.maxX); + maxY = Math.max(maxY, aabb.maxY); + maxZ = Math.max(maxZ, aabb.maxZ); + } + } + return new AABB(minX, minY, minZ, maxX, maxY, maxZ); } @Nullable diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index 19e240b33..6fd240dbb 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -230,6 +230,8 @@ public abstract class Player extends AbstractEntity implements NetWorkUser { public void remove() { } + public abstract void playParticle(Key particleId, double x, double y, double z); + public abstract void removeTrackedFurniture(int entityId); public abstract void clearTrackedFurniture(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java index 5ced80c59..df5d26b97 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/entityculling/EntityCulling.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.entityculling; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.world.ChunkPos; import net.momirealms.craftengine.core.world.MutableVec3d; @@ -59,13 +60,12 @@ public final class EntityCulling { AABB aabb = cullable.aabb; double aabbExpansion = cullable.aabbExpansion; - // 根据AABB获取能包裹此AABB的最小长方体 - int minX = MiscUtils.floor(aabb.minX - aabbExpansion); - int minY = MiscUtils.floor(aabb.minY - aabbExpansion); - int minZ = MiscUtils.floor(aabb.minZ - aabbExpansion); - int maxX = MiscUtils.ceil(aabb.maxX + aabbExpansion); - int maxY = MiscUtils.ceil(aabb.maxY + aabbExpansion); - int maxZ = MiscUtils.ceil(aabb.maxZ + aabbExpansion); + double minX = aabb.minX - aabbExpansion; + double minY = aabb.minY - aabbExpansion; + double minZ = aabb.minZ - aabbExpansion; + double maxX = aabb.maxX + aabbExpansion; + double maxY = aabb.maxY + aabbExpansion; + double maxZ = aabb.maxZ + aabbExpansion; double cameraX = cameraPos.x; double cameraY = cameraPos.y; @@ -95,6 +95,10 @@ public final class EntityCulling { if (distanceSq > maxDistanceSq) { return false; } + // 太近了,不剔除 + else if (distanceSq < 1) { + return true; + } } if (!rayTracing || !cullable.rayTracing) { @@ -120,25 +124,31 @@ public final class EntityCulling { } int size = 0; - if (this.dotSelectors[0]) targetPoints[size++].set(minX + 0.05, minY + 0.05, minZ + 0.05); - if (this.dotSelectors[1]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, minZ + 0.05); - if (this.dotSelectors[2]) targetPoints[size++].set(minX + 0.05, minY + 0.05, maxZ - 0.05); - if (this.dotSelectors[3]) targetPoints[size++].set(maxX - 0.05, minY + 0.05, maxZ - 0.05); - if (this.dotSelectors[4]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, minZ + 0.05); - if (this.dotSelectors[5]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, minZ + 0.05); - if (this.dotSelectors[6]) targetPoints[size++].set(minX + 0.05, maxY - 0.05, maxZ - 0.05); - if (this.dotSelectors[7]) targetPoints[size++].set(maxX - 0.05, maxY - 0.05, maxZ - 0.05); + if (this.dotSelectors[0]) targetPoints[size++].set(minX, minY, minZ); + if (this.dotSelectors[1]) targetPoints[size++].set(maxX, minY, minZ); + if (this.dotSelectors[2]) targetPoints[size++].set(minX, minY, maxZ); + if (this.dotSelectors[3]) targetPoints[size++].set(maxX, minY, maxZ); + if (this.dotSelectors[4]) targetPoints[size++].set(minX, maxY, minZ); + if (this.dotSelectors[5]) targetPoints[size++].set(maxX, maxY, minZ); + if (this.dotSelectors[6]) targetPoints[size++].set(minX, maxY, maxZ); + if (this.dotSelectors[7]) targetPoints[size++].set(maxX, maxY, maxZ); // 面中心点 double averageX = (minX + maxX) / 2.0; double averageY = (minY + maxY) / 2.0; double averageZ = (minZ + maxZ) / 2.0; - if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ + 0.05); - if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ - 0.05); - if (this.dotSelectors[10]) targetPoints[size++].set(minX + 0.05, averageY, averageZ); - if (this.dotSelectors[11]) targetPoints[size++].set(maxX - 0.05, averageY, averageZ); - if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY + 0.05, averageZ); - if (this.dotSelectors[13]) targetPoints[size].set(averageX, maxY - 0.05, averageZ); + if (this.dotSelectors[8]) targetPoints[size++].set(averageX, averageY, minZ); + if (this.dotSelectors[9]) targetPoints[size++].set(averageX, averageY, maxZ); + if (this.dotSelectors[10]) targetPoints[size++].set(minX, averageY, averageZ); + if (this.dotSelectors[11]) targetPoints[size++].set(maxX, averageY, averageZ); + if (this.dotSelectors[12]) targetPoints[size++].set(averageX, minY, averageZ); + if (this.dotSelectors[13]) targetPoints[size++].set(averageX, maxY, averageZ); +// if (Config.debugEntityCulling()) { +// for (int i = 0; i < size; i++) { +// MutableVec3d targetPoint = this.targetPoints[i]; +// this.player.playParticle(Key.of("flame"), targetPoint.x, targetPoint.y, targetPoint.z); +// } +// } return isVisible(cameraPos, this.targetPoints, size); } @@ -356,7 +366,7 @@ public final class EntityCulling { return deltaX + 32 * deltaY + 32 * 32 * deltaZ; } - private double distanceSq(int min, int max, double camera, Relative rel) { + private double distanceSq(double min, double max, double camera, Relative rel) { if (rel == Relative.NEGATIVE) { double dx = camera - max; return dx * dx; @@ -394,7 +404,7 @@ public final class EntityCulling { private enum Relative { INSIDE, POSITIVE, NEGATIVE; - public static Relative from(int min, int max, double pos) { + public static Relative from(double min, double max, double pos) { if (min > pos) return POSITIVE; else if (max < pos) return NEGATIVE; return INSIDE; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java index 94c6f52e2..1f759dada 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/collision/AABB.java @@ -7,6 +7,8 @@ import net.momirealms.craftengine.core.world.Vec3d; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; public class AABB { @@ -159,6 +161,78 @@ public class AABB { return (value >= min - EPSILON) && (value <= max + EPSILON); } + public List getEdgePoints(double interval) { + List points = new ArrayList<>(); + + // AABB的8个顶点 + Vec3d[] vertices = { + new Vec3d(minX, minY, minZ), // 0 + new Vec3d(maxX, minY, minZ), // 1 + new Vec3d(minX, maxY, minZ), // 2 + new Vec3d(maxX, maxY, minZ), // 3 + new Vec3d(minX, minY, maxZ), // 4 + new Vec3d(maxX, minY, maxZ), // 5 + new Vec3d(minX, maxY, maxZ), // 6 + new Vec3d(maxX, maxY, maxZ) // 7 + }; + + // 12条边的定义(连接哪两个顶点) + int[][] edges = { + {0, 1}, // 底部X边(前) + {1, 3}, // 底部Y边(右) + {3, 2}, // 底部X边(后) + {2, 0}, // 底部Y边(左) + + {4, 5}, // 顶部X边(前) + {5, 7}, // 顶部Y边(右) + {7, 6}, // 顶部X边(后) + {6, 4}, // 顶部Y边(左) + + {0, 4}, // Z边(左下前) + {1, 5}, // Z边(右下前) + {2, 6}, // Z边(左后上) + {3, 7} // Z边(右后上) + }; + + for (int[] edge : edges) { + Vec3d start = vertices[edge[0]]; + Vec3d end = vertices[edge[1]]; + points.addAll(sampleLine(start, end, interval)); + } + + return points; + } + + private List sampleLine(Vec3d start, Vec3d end, double interval) { + List points = new ArrayList<>(); + + // 计算线段长度 + double dx = end.x - start.x; + double dy = end.y - start.y; + double dz = end.z - start.z; + double length = Math.sqrt(dx * dx + dy * dy + dz * dz); + + // 计算采样点数(去掉终点避免重复) + int numPoints = (int) Math.floor(length / interval); + + // 如果线段太短,至少返回起点 + if (numPoints <= 0) { + points.add(start); + return points; + } + + // 按间隔采样 + for (int i = 0; i <= numPoints; i++) { + double t = (double) i / numPoints; + double x = start.x + dx * t; + double y = start.y + dy * t; + double z = start.z + dz * t; + points.add(new Vec3d(x, y, z)); + } + + return points; + } + @Override public String toString() { return "AABB{" +