9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-27 19:09:08 +00:00

完成第三人称光线追踪,修复眼睛位置

This commit is contained in:
XiaoMoMi
2025-11-29 21:28:02 +08:00
parent 0b3d52b39b
commit 8d9be05f11
12 changed files with 152 additions and 75 deletions

View File

@@ -715,13 +715,14 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
return null;
}
if (!(arguments instanceof Map)) {
return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5);
return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true);
}
Map<String, Object> argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling");
return new CullingData(
ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"),
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.5), "aabb-expansion"),
ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing")
);
}

View File

@@ -218,4 +218,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
@Override
public void remove() {
}
public abstract WorldPosition eyePosition();
}

View File

@@ -208,6 +208,7 @@ public class Config {
protected boolean client_optimization$entity_culling$enable;
protected int client_optimization$entity_culling$view_distance;
protected int client_optimization$entity_culling$threads;
protected boolean client_optimization$entity_culling$ray_tracing;
protected boolean client_optimization$entity_culling$rate_limiting$enable;
protected int client_optimization$entity_culling$rate_limiting$bucket_size;
protected int client_optimization$entity_culling$rate_limiting$restore_per_tick;
@@ -579,6 +580,7 @@ public class Config {
}
client_optimization$entity_culling$view_distance = config.getInt("client-optimization.entity-culling.view-distance", 64);
client_optimization$entity_culling$threads = config.getInt("client-optimization.entity-culling.threads", 1);
client_optimization$entity_culling$ray_tracing = client_optimization$entity_culling$enable && config.getBoolean("client-optimization.entity-culling.ray-tracing", true);
client_optimization$entity_culling$rate_limiting$enable = config.getBoolean("client-optimization.entity-culling.rate-limiting.enable", true);
client_optimization$entity_culling$rate_limiting$bucket_size = config.getInt("client-optimization.entity-culling.rate-limiting.bucket-size", 300);
client_optimization$entity_culling$rate_limiting$restore_per_tick = config.getInt("client-optimization.entity-culling.rate-limiting.restore-per-tick", 5);
@@ -1197,6 +1199,10 @@ public class Config {
return instance.client_optimization$entity_culling$rate_limiting$restore_per_tick;
}
public static boolean entityCullingRayTracing() {
return instance.client_optimization$entity_culling$ray_tracing;
}
public YamlDocument loadOrCreateYamlData(String fileName) {
Path path = this.plugin.dataFolderPath().resolve(fileName);
if (!Files.exists(path)) {

View File

@@ -6,22 +6,28 @@ public final class CullingData {
public final AABB aabb;
public final int maxDistance;
public final double aabbExpansion;
public final boolean rayTracing;
public CullingData(AABB aabb, int maxDistance, double aabbExpansion) {
public CullingData(AABB aabb, int maxDistance, double aabbExpansion, boolean rayTracing) {
this.aabb = aabb;
this.maxDistance = maxDistance;
this.aabbExpansion = aabbExpansion;
this.rayTracing = rayTracing;
}
public AABB aabb() {
return aabb;
return this.aabb;
}
public int maxDistance() {
return maxDistance;
return this.maxDistance;
}
public double aabbExpansion() {
return aabbExpansion;
return this.aabbExpansion;
}
public boolean rayTracing() {
return this.rayTracing;
}
}

View File

@@ -16,8 +16,8 @@ public final class EntityCulling {
private final Player player;
private final boolean[] dotSelectors = new boolean[MAX_SAMPLES];
private final MutableVec3d[] targetPoints = new MutableVec3d[MAX_SAMPLES];
private final int[] lastHitBlock = new int[MAX_SAMPLES * 3];
private final boolean[] canCheckLastHitBlock = new boolean[MAX_SAMPLES];
private final int[] lastHitBlock = new int[3];
private boolean canCheckLastHitBlock = false;
private int hitBlockCount = 0;
private int lastVisitChunkX = Integer.MAX_VALUE;
private int lastVisitChunkZ = Integer.MAX_VALUE;
@@ -52,9 +52,9 @@ public final class EntityCulling {
return false;
}
public boolean isVisible(CullingData cullable, Vec3d cameraPos) {
public boolean isVisible(CullingData cullable, Vec3d cameraPos, boolean rayTracing) {
// 情空标志位
Arrays.fill(this.canCheckLastHitBlock, false);
this.canCheckLastHitBlock = false;
this.hitBlockCount = 0;
AABB aabb = cullable.aabb;
double aabbExpansion = cullable.aabbExpansion;
@@ -97,6 +97,10 @@ public final class EntityCulling {
}
}
if (!rayTracing || !cullable.rayTracing) {
return true;
}
// 清空之前的缓存
Arrays.fill(this.dotSelectors, false);
if (relX == Relative.POSITIVE) {
@@ -190,7 +194,7 @@ public final class EntityCulling {
int startBlockZ = MiscUtils.floor(start.z);
// 遍历所有目标点进行视线检测
outer: for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) {
for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) {
MutableVec3d currentTarget = targets[targetIndex];
// 计算起点到目标的相对向量(世界坐标差)
@@ -199,14 +203,9 @@ public final class EntityCulling {
double deltaZ = start.z - currentTarget.z;
// 检查之前命中的方块,大概率还是命中
for (int i = 0; i < MAX_SAMPLES; i++) {
if (this.canCheckLastHitBlock[i]) {
int offset = i * 3;
if (rayIntersection(this.lastHitBlock[offset], this.lastHitBlock[offset + 1], this.lastHitBlock[offset + 2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) {
continue outer;
}
} else {
break;
if (this.canCheckLastHitBlock) {
if (rayIntersection(this.lastHitBlock[0], this.lastHitBlock[1], this.lastHitBlock[2], start, new MutableVec3d(deltaX, deltaY, deltaZ).normalize())) {
continue;
}
}
@@ -292,7 +291,7 @@ public final class EntityCulling {
if (isLineOfSightClear) {
return true;
} else {
this.canCheckLastHitBlock[this.hitBlockCount++] = true;
this.canCheckLastHitBlock = true;
}
}

View File

@@ -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.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.logger.Debugger;
import java.util.concurrent.Executors;
@@ -14,6 +15,7 @@ public class EntityCullingThread {
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final int id;
private final int threads;
private int timer;
public EntityCullingThread(int id, int threads) {
this.id = id;
@@ -35,7 +37,7 @@ public class EntityCullingThread {
this.scheduler.execute(() -> {
try {
int processed = 0;
long startTime = System.currentTimeMillis();
long startTime = System.nanoTime();
for (Player player : CraftEngine.instance().networkManager().onlineUsers()) {
// 使用绝对值确保非负,使用 threads 而不是 threads-1 确保均匀分布
@@ -45,10 +47,10 @@ public class EntityCullingThread {
}
}
long duration = System.currentTimeMillis() - startTime;
if (duration > 45) {
String value = String.format("EntityCullingThread-%d processed %d players in %dms (over 45ms)",
this.id, processed, duration);
long duration = System.nanoTime() - startTime;
if (Config.debugEntityCulling() && this.timer++ % 20 == 0) {
String value = String.format("EntityCullingThread-%d processed %d players in %sms",
this.id, processed, String.format("%.2f", duration / 1_000_000.0));
Debugger.ENTITY_CULLING.debug(() -> value);
}
} catch (Exception e) {

View File

@@ -143,7 +143,7 @@ public class CEChunk {
ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(
elements,
Optional.ofNullable(state.cullingData())
.map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion))
.map(data -> new CullingData(data.aabb.move(pos), data.maxDistance, data.aabbExpansion, data.rayTracing))
.orElse(null)
);
World wrappedWorld = this.world.world();