mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-19 15:09:15 +00:00
初步剔除
This commit is contained in:
@@ -42,7 +42,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop {
|
||||
context = ItemBuildContext.of(player);
|
||||
}
|
||||
}
|
||||
int amountInt = MiscUtils.fastFloor(amount + 0.5F);
|
||||
int amountInt = MiscUtils.floor(amount + 0.5F);
|
||||
ItemStack itemStack = this.customItem.buildItemStack(context, amountInt);
|
||||
return adapt(itemStack).amount(amountInt);
|
||||
}
|
||||
|
||||
@@ -2075,6 +2075,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
// 两种情况都有,那么需要一个个遍历处理视线遮挡数据
|
||||
if (hasOcclusions && hasNoOcclusions) {
|
||||
PackedOcclusionStorage storage = new PackedOcclusionStorage(false);
|
||||
clientSections[i] = new ClientSection(storage);
|
||||
for (int j = 0; j < 4096; j++) {
|
||||
int state = container.get(j);
|
||||
storage.set(j, this.occlusionPredicate.test(state));
|
||||
@@ -2092,6 +2093,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
PackedOcclusionStorage storage = null;
|
||||
if (clientSections != null) {
|
||||
storage = new PackedOcclusionStorage(false);
|
||||
clientSections[i] = new ClientSection(storage);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 4096; j++) {
|
||||
@@ -2258,6 +2260,12 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
BlockPos pos = buf.readBlockPos();
|
||||
int before = buf.readVarInt();
|
||||
if (Config.enableEntityCulling()) {
|
||||
ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4));
|
||||
if (trackedChunk != null) {
|
||||
trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(before));
|
||||
}
|
||||
}
|
||||
if (user.clientModEnabled() && !BlockStateUtils.isVanillaBlock(before)) {
|
||||
return;
|
||||
}
|
||||
@@ -2270,12 +2278,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
buf.writeVarInt(event.packetID());
|
||||
buf.writeBlockPos(pos);
|
||||
buf.writeVarInt(state);
|
||||
if (Config.enableEntityCulling()) {
|
||||
ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(pos.x >> 4, pos.z >> 4));
|
||||
if (trackedChunk != null) {
|
||||
trackedChunk.setOccluding(pos.x, pos.y, pos.z, this.occlusionPredicate.test(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2437,6 +2439,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return;
|
||||
BlockPos blockPos = buf.readBlockPos();
|
||||
int state = buf.readInt();
|
||||
// 移除不透明设置
|
||||
if (Config.enableEntityCulling()) {
|
||||
ClientChunk trackedChunk = user.getTrackedChunk(ChunkPos.asLong(blockPos.x >> 4, blockPos.z >> 4));
|
||||
if (trackedChunk != null) {
|
||||
trackedChunk.setOccluding(blockPos.x, blockPos.y, blockPos.z, false);
|
||||
}
|
||||
}
|
||||
boolean global = buf.readBoolean();
|
||||
int newState = user.clientModEnabled() ? modBlockStateMapper[state] : blockStateMapper[state];
|
||||
Object blockState = BlockStateUtils.idToBlockState(state);
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.context.CooldownData;
|
||||
import net.momirealms.craftengine.core.plugin.entityculling.EntityCulling;
|
||||
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
|
||||
@@ -142,6 +143,8 @@ public class BukkitServerPlayer extends Player {
|
||||
// 跟踪到的方块实体渲染器
|
||||
private final Map<BlockPos, VirtualCullableObject> trackedBlockEntityRenderers = new ConcurrentHashMap<>();
|
||||
|
||||
private final EntityCulling culling;
|
||||
|
||||
public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) {
|
||||
this.channel = channel;
|
||||
this.plugin = plugin;
|
||||
@@ -154,6 +157,7 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.culling = new EntityCulling(this, 64, 0.5);
|
||||
}
|
||||
|
||||
public void setPlayer(org.bukkit.entity.Player player) {
|
||||
@@ -559,6 +563,15 @@ public class BukkitServerPlayer extends Player {
|
||||
this.predictNextBlockToMine();
|
||||
}
|
||||
}
|
||||
if (Config.enableEntityCulling()) {
|
||||
long nano1 = System.nanoTime();
|
||||
for (VirtualCullableObject cullableObject : this.trackedBlockEntityRenderers.values()) {
|
||||
boolean visible = this.culling.isVisible(cullableObject.cullable.aabb(), LocationUtils.toVec3d(platformPlayer().getEyeLocation()));
|
||||
cullableObject.setShown(this, visible);
|
||||
}
|
||||
long nano2 = System.nanoTime();
|
||||
//CraftEngine.instance().logger().info("EntityCulling took " + (nano2 - nano1) / 1_000_000d + "ms");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGUI() {
|
||||
@@ -1303,6 +1316,16 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer) {
|
||||
this.trackedBlockEntityRenderers.put(blockPos, new VirtualCullableObject(renderer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualCullableObject getTrackedBlockEntity(BlockPos blockPos) {
|
||||
return this.trackedBlockEntityRenderers.get(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTrackedBlockEntities(Collection<BlockPos> renders) {
|
||||
for (BlockPos render : renders) {
|
||||
|
||||
@@ -60,7 +60,7 @@ public final class EntityUtils {
|
||||
Object serverLevel = BukkitAdaptors.adapt(player.getWorld()).serverWorld();
|
||||
Object serverPlayer = FastNMS.INSTANCE.method$CraftPlayer$getHandle(player);
|
||||
for (Object pose : List.of(CoreReflections.instance$Pose$STANDING, CoreReflections.instance$Pose$CROUCHING, CoreReflections.instance$Pose$SWIMMING)) {
|
||||
BlockPos pos = new BlockPos(MiscUtils.fastFloor(x), MiscUtils.fastFloor(y), MiscUtils.fastFloor(z));
|
||||
BlockPos pos = new BlockPos(MiscUtils.floor(x), MiscUtils.floor(y), MiscUtils.floor(z));
|
||||
try {
|
||||
double floorHeight = (double) CoreReflections.method$BlockGetter$getBlockFloorHeight.invoke(serverLevel, LocationUtils.toBlockPos(pos));
|
||||
if (pos.y() + floorHeight > y + 0.75 || !isBlockFloorValid(floorHeight)) {
|
||||
|
||||
@@ -178,7 +178,7 @@ templates:
|
||||
# template: default:loot_table/ore
|
||||
# arguments:
|
||||
# ore_block: the ore block
|
||||
# ore_drop: the drops of the ore
|
||||
# ore_drop: the drops of the ore material
|
||||
# ore_drop_count: the amount of the ore materials
|
||||
# exp: the exp to drop
|
||||
default:loot_table/ore:
|
||||
|
||||
@@ -16,6 +16,7 @@ public class ConstantBlockEntityRenderer implements Cullable {
|
||||
this.aabb = aabb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(Player player) {
|
||||
for (BlockEntityElement element : this.elements) {
|
||||
if (element != null) {
|
||||
@@ -24,6 +25,7 @@ public class ConstantBlockEntityRenderer implements Cullable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(Player player) {
|
||||
for (BlockEntityElement element : this.elements) {
|
||||
if (element != null) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.sound.SoundSource;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -202,6 +203,10 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
|
||||
|
||||
public abstract void addTrackedBlockEntities(Map<BlockPos, ConstantBlockEntityRenderer> renders);
|
||||
|
||||
public abstract void addTrackedBlockEntity(BlockPos blockPos, ConstantBlockEntityRenderer renderer);
|
||||
|
||||
public abstract VirtualCullableObject getTrackedBlockEntity(BlockPos blockPos);
|
||||
|
||||
public abstract void removeTrackedBlockEntities(Collection<BlockPos> renders);
|
||||
|
||||
public abstract void clearTrackedBlockEntities();
|
||||
|
||||
@@ -44,7 +44,7 @@ public class LootPool<T> {
|
||||
}
|
||||
if (this.compositeCondition.test(context)) {
|
||||
Consumer<Item<T>> consumer = LootFunction.decorate(this.compositeFunction, lootConsumer, context);
|
||||
int i = this.rolls.getInt(context) + MiscUtils.fastFloor(this.bonusRolls.getFloat(context) * context.luck());
|
||||
int i = this.rolls.getInt(context) + MiscUtils.floor(this.bonusRolls.getFloat(context) * context.luck());
|
||||
for (int j = 0; j < i; ++j) {
|
||||
this.addRandomItem(createFunctionApplier(consumer, context), context);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package net.momirealms.craftengine.core.plugin.context;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ContextHolder {
|
||||
|
||||
@@ -40,7 +40,7 @@ public class MatchBlockCondition<CTX extends Context> implements Condition<CTX>
|
||||
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
|
||||
if (optionalWorldPosition.isPresent()) {
|
||||
World world = optionalWorldPosition.get().world();
|
||||
ExistingBlock blockAt = world.getBlock(MiscUtils.fastFloor(this.x.getDouble(ctx)), MiscUtils.fastFloor(this.y.getDouble(ctx)), MiscUtils.fastFloor(this.z.getDouble(ctx)));
|
||||
ExistingBlock blockAt = world.getBlock(MiscUtils.floor(this.x.getDouble(ctx)), MiscUtils.floor(this.y.getDouble(ctx)), MiscUtils.floor(this.z.getDouble(ctx)));
|
||||
return MiscUtils.matchRegex(blockAt.id().asString(), this.ids, this.regexMatch);
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class BreakBlockFunction<CTX extends Context> extends AbstractConditional
|
||||
@Override
|
||||
public void runInternal(CTX ctx) {
|
||||
Optional<Player> optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER);
|
||||
optionalPlayer.ifPresent(player -> player.breakBlock(MiscUtils.fastFloor(x.getDouble(ctx)), MiscUtils.fastFloor(y.getDouble(ctx)), MiscUtils.fastFloor(z.getDouble(ctx))));
|
||||
optionalPlayer.ifPresent(player -> player.breakBlock(MiscUtils.floor(x.getDouble(ctx)), MiscUtils.floor(y.getDouble(ctx)), MiscUtils.floor(z.getDouble(ctx))));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,9 +55,9 @@ public class CycleBlockPropertyFunction<CTX extends Context> extends AbstractCon
|
||||
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
|
||||
if (optionalWorldPosition.isEmpty()) return;
|
||||
World world = optionalWorldPosition.get().world();
|
||||
int x = MiscUtils.fastFloor(this.x.getDouble(ctx));
|
||||
int y = MiscUtils.fastFloor(this.y.getDouble(ctx));
|
||||
int z = MiscUtils.fastFloor(this.z.getDouble(ctx));
|
||||
int x = MiscUtils.floor(this.x.getDouble(ctx));
|
||||
int y = MiscUtils.floor(this.y.getDouble(ctx));
|
||||
int z = MiscUtils.floor(this.z.getDouble(ctx));
|
||||
BlockStateWrapper wrapper = updateBlockState(world.getBlock(x, y, z).blockState(), ctx);
|
||||
world.setBlockState(x, y, z, wrapper, this.updateFlags.getInt(ctx));
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class PlaceBlockFunction<CTX extends Context> extends AbstractConditional
|
||||
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
|
||||
if (optionalWorldPosition.isPresent()) {
|
||||
World world = optionalWorldPosition.get().world();
|
||||
world.setBlockState(MiscUtils.fastFloor(this.x.getDouble(ctx)), MiscUtils.fastFloor(this.y.getDouble(ctx)), MiscUtils.fastFloor(this.z.getDouble(ctx)), this.lazyBlockState.get(), this.updateFlags.getInt(ctx));
|
||||
world.setBlockState(MiscUtils.floor(this.x.getDouble(ctx)), MiscUtils.floor(this.y.getDouble(ctx)), MiscUtils.floor(this.z.getDouble(ctx)), this.lazyBlockState.get(), this.updateFlags.getInt(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class RunFunction<CTX extends Context> extends AbstractConditionalFunctio
|
||||
for (Function<CTX> function : functions) {
|
||||
function.run(ctx);
|
||||
}
|
||||
}, delay, pos.world().platformWorld(), MiscUtils.fastFloor(pos.x()) >> 4, MiscUtils.fastFloor(pos.z()) >> 4);
|
||||
}, delay, pos.world().platformWorld(), MiscUtils.floor(pos.x()) >> 4, MiscUtils.floor(pos.z()) >> 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ public class TransformBlockFunction<CTX extends Context> extends AbstractConditi
|
||||
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
|
||||
if (optionalWorldPosition.isPresent()) {
|
||||
World world = optionalWorldPosition.get().world();
|
||||
int x = MiscUtils.fastFloor(this.x.getDouble(ctx));
|
||||
int y = MiscUtils.fastFloor(this.y.getDouble(ctx));
|
||||
int z = MiscUtils.fastFloor(this.z.getDouble(ctx));
|
||||
int x = MiscUtils.floor(this.x.getDouble(ctx));
|
||||
int y = MiscUtils.floor(this.y.getDouble(ctx));
|
||||
int z = MiscUtils.floor(this.z.getDouble(ctx));
|
||||
BlockStateWrapper existingBlockState = world.getBlock(x, y, z).blockState().withProperties(this.properties);
|
||||
CompoundTag newProperties = new CompoundTag();
|
||||
for (String propertyName : existingBlockState.getPropertyNames()) {
|
||||
|
||||
@@ -40,9 +40,9 @@ public class UpdateBlockPropertyFunction<CTX extends Context> extends AbstractCo
|
||||
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
|
||||
if (optionalWorldPosition.isPresent()) {
|
||||
World world = optionalWorldPosition.get().world();
|
||||
int x = MiscUtils.fastFloor(this.x.getDouble(ctx));
|
||||
int y = MiscUtils.fastFloor(this.y.getDouble(ctx));
|
||||
int z = MiscUtils.fastFloor(this.z.getDouble(ctx));
|
||||
int x = MiscUtils.floor(this.x.getDouble(ctx));
|
||||
int y = MiscUtils.floor(this.y.getDouble(ctx));
|
||||
int z = MiscUtils.floor(this.z.getDouble(ctx));
|
||||
ExistingBlock blockAt = world.getBlock(x, y, z);
|
||||
BlockStateWrapper wrapper = blockAt.blockState().withProperties(this.properties);
|
||||
world.setBlockState(x, y, z, wrapper, this.updateFlags.getInt(ctx));
|
||||
|
||||
@@ -20,9 +20,9 @@ public class EntityParameterProvider implements ChainParameterProvider<Entity> {
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, Entity::xRot);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, Entity::yRot);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.floor(p.x()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.floor(p.y()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.floor(p.z()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Entity::name);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Entity::uuid);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world);
|
||||
|
||||
@@ -21,9 +21,9 @@ public class PlayerParameterProvider implements ChainParameterProvider<Player> {
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, Entity::xRot);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, Entity::yRot);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, Entity::position);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.floor(p.x()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.floor(p.y()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.floor(p.z()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.FOOD, Player::foodLevel);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.SATURATION, Player::saturation);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name);
|
||||
|
||||
@@ -20,9 +20,9 @@ public class PositionParameterProvider implements ChainParameterProvider<WorldPo
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, WorldPosition::z);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.YAW, WorldPosition::xRot);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.PITCH, WorldPosition::yRot);
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.fastFloor(p.x()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.fastFloor(p.y()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.fastFloor(p.z()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, p -> MiscUtils.floor(p.x()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, p -> MiscUtils.floor(p.y()));
|
||||
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, p -> MiscUtils.floor(p.z()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -1,453 +1,276 @@
|
||||
package net.momirealms.craftengine.core.plugin.entityculling;
|
||||
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.world.ChunkPos;
|
||||
import net.momirealms.craftengine.core.world.MutableVec3d;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.chunk.client.ClientChunk;
|
||||
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
|
||||
public class EntityCulling {
|
||||
|
||||
// 面掩码常量
|
||||
private static final int ON_MIN_X = 0x01;
|
||||
private static final int ON_MAX_X = 0x02;
|
||||
private static final int ON_MIN_Y = 0x04;
|
||||
private static final int ON_MAX_Y = 0x08;
|
||||
private static final int ON_MIN_Z = 0x10;
|
||||
private static final int ON_MAX_Z = 0x20;
|
||||
|
||||
private final int reach;
|
||||
public final class EntityCulling {
|
||||
private final Player player;
|
||||
private final int maxDistance;
|
||||
private final double aabbExpansion;
|
||||
private final DataProvider provider;
|
||||
private final OcclusionCache cache;
|
||||
private final boolean[] dotSelectors = new boolean[14];
|
||||
private final MutableVec3d[] targetPoints = new MutableVec3d[14];
|
||||
|
||||
// 重用数据结构,减少GC压力
|
||||
private final BitSet skipList = new BitSet();
|
||||
private final MutableVec3d[] targetPoints = new MutableVec3d[15];
|
||||
private final MutableVec3d targetPos = new MutableVec3d(0, 0, 0);
|
||||
private final int[] cameraPos = new int[3];
|
||||
private final boolean[] dotselectors = new boolean[14];
|
||||
private final int[] lastHitBlock = new int[3];
|
||||
|
||||
// 状态标志
|
||||
private boolean allowRayChecks = false;
|
||||
private boolean allowWallClipping = false;
|
||||
|
||||
public EntityCulling(int maxDistance, DataProvider provider) {
|
||||
this(maxDistance, provider, new ArrayOcclusionCache(maxDistance), 0.5);
|
||||
}
|
||||
|
||||
public EntityCulling(int maxDistance, DataProvider provider, OcclusionCache cache, double aabbExpansion) {
|
||||
this.reach = maxDistance;
|
||||
this.provider = provider;
|
||||
this.cache = cache;
|
||||
public EntityCulling(Player player, int maxDistance, double aabbExpansion) {
|
||||
this.player = player;
|
||||
this.maxDistance = maxDistance;
|
||||
this.aabbExpansion = aabbExpansion;
|
||||
// 预先初始化点对象
|
||||
for(int i = 0; i < targetPoints.length; i++) {
|
||||
targetPoints[i] = new MutableVec3d(0, 0, 0);
|
||||
for (int i = 0; i < this.targetPoints.length; i++) {
|
||||
this.targetPoints[i] = new MutableVec3d(0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAABBVisible(Vec3d aabbMin, MutableVec3d aabbMax, MutableVec3d viewerPosition) {
|
||||
try {
|
||||
// 计算包围盒范围
|
||||
int maxX = MiscUtils.fastFloor(aabbMax.x + aabbExpansion);
|
||||
int maxY = MiscUtils.fastFloor(aabbMax.y + aabbExpansion);
|
||||
int maxZ = MiscUtils.fastFloor(aabbMax.z + aabbExpansion);
|
||||
int minX = MiscUtils.fastFloor(aabbMin.x - aabbExpansion);
|
||||
int minY = MiscUtils.fastFloor(aabbMin.y - aabbExpansion);
|
||||
int minZ = MiscUtils.fastFloor(aabbMin.z - aabbExpansion);
|
||||
public boolean isVisible(AABB aabb, Vec3d cameraPos) {
|
||||
// 根据AABB获取能包裹此AABB的最小长方体
|
||||
int minX = MiscUtils.floor(aabb.minX - this.aabbExpansion);
|
||||
int minY = MiscUtils.floor(aabb.minY - this.aabbExpansion);
|
||||
int minZ = MiscUtils.floor(aabb.minZ - this.aabbExpansion);
|
||||
int maxX = MiscUtils.ceil(aabb.maxX + this.aabbExpansion);
|
||||
int maxY = MiscUtils.ceil(aabb.maxY + this.aabbExpansion);
|
||||
int maxZ = MiscUtils.ceil(aabb.maxZ + this.aabbExpansion);
|
||||
|
||||
cameraPos[0] = MiscUtils.fastFloor(viewerPosition.x);
|
||||
cameraPos[1] = MiscUtils.fastFloor(viewerPosition.y);
|
||||
cameraPos[2] = MiscUtils.fastFloor(viewerPosition.z);
|
||||
|
||||
// 判断是否在包围盒内部
|
||||
Relative relX = Relative.from(minX, maxX, cameraPos[0]);
|
||||
Relative relY = Relative.from(minY, maxY, cameraPos[1]);
|
||||
Relative relZ = Relative.from(minZ, maxZ, cameraPos[2]);
|
||||
|
||||
if(relX == Relative.INSIDE && relY == Relative.INSIDE && relZ == Relative.INSIDE) {
|
||||
return true;
|
||||
double cameraX = cameraPos.x;
|
||||
double cameraY = cameraPos.y;
|
||||
double cameraZ = cameraPos.z;
|
||||
|
||||
Relative relX = Relative.from(minX, maxX, cameraX);
|
||||
Relative relY = Relative.from(minY, maxY, cameraY);
|
||||
Relative relZ = Relative.from(minZ, maxZ, cameraZ);
|
||||
|
||||
// 相机位于实体内部
|
||||
if (relX == Relative.INSIDE && relY == Relative.INSIDE && relZ == Relative.INSIDE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果设置了最大距离
|
||||
if (this.maxDistance > 0) {
|
||||
// 计算AABB到相机的最小距离
|
||||
double distanceSq = 0.0;
|
||||
// 计算XYZ轴方向的距离
|
||||
distanceSq += distanceSq(minX, maxX, cameraX, relX);
|
||||
distanceSq += distanceSq(minY, maxY, cameraY, relY);
|
||||
distanceSq += distanceSq(minZ, maxZ, cameraZ, relZ);
|
||||
// 检查距离是否超过最大值
|
||||
double maxDistanceSq = this.maxDistance * this.maxDistance;
|
||||
// 超过最大距离,剔除
|
||||
if (distanceSq > maxDistanceSq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
skipList.clear();
|
||||
|
||||
// 1. 快速检查缓存
|
||||
int id = 0;
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
int cachedValue = getCacheValue(x, y, z);
|
||||
if (cachedValue == 1) return true; // 缓存显示可见
|
||||
if (cachedValue != 0) skipList.set(id); // 缓存显示不可见或遮挡
|
||||
id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allowRayChecks = false;
|
||||
id = 0;
|
||||
|
||||
// 2. 遍历体素进行光线投射检查
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
// 预计算X轴面的可见性和边缘数据
|
||||
byte visibleOnFaceX = 0;
|
||||
byte faceEdgeDataX = 0;
|
||||
if (x == minX) { faceEdgeDataX |= ON_MIN_X; if (relX == Relative.POSITIVE) visibleOnFaceX |= ON_MIN_X; }
|
||||
if (x == maxX) { faceEdgeDataX |= ON_MAX_X; if (relX == Relative.NEGATIVE) visibleOnFaceX |= ON_MAX_X; }
|
||||
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
byte visibleOnFaceY = visibleOnFaceX;
|
||||
byte faceEdgeDataY = faceEdgeDataX;
|
||||
if (y == minY) { faceEdgeDataY |= ON_MIN_Y; if (relY == Relative.POSITIVE) visibleOnFaceY |= ON_MIN_Y; }
|
||||
if (y == maxY) { faceEdgeDataY |= ON_MAX_Y; if (relY == Relative.NEGATIVE) visibleOnFaceY |= ON_MAX_Y; }
|
||||
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
// 如果缓存已标记为不可见,跳过
|
||||
if(skipList.get(id++)) continue;
|
||||
|
||||
byte visibleOnFace = visibleOnFaceY;
|
||||
byte faceEdgeData = faceEdgeDataY;
|
||||
if (z == minZ) { faceEdgeData |= ON_MIN_Z; if (relZ == Relative.POSITIVE) visibleOnFace |= ON_MIN_Z; }
|
||||
if (z == maxZ) { faceEdgeData |= ON_MAX_Z; if (relZ == Relative.NEGATIVE) visibleOnFace |= ON_MAX_Z; }
|
||||
|
||||
if (visibleOnFace != 0) {
|
||||
targetPos.set(x, y, z);
|
||||
// 检查单个体素是否可见
|
||||
if (isVoxelVisible(viewerPosition, targetPos, faceEdgeData, visibleOnFace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return true; // 发生异常默认可见,防止渲染错误
|
||||
}
|
||||
}
|
||||
|
||||
// 接口定义
|
||||
public interface DataProvider {
|
||||
boolean prepareChunk(int chunkX, int chunkZ);
|
||||
boolean isOpaqueFullCube(int x, int y, int z);
|
||||
default void cleanup() {}
|
||||
default void checkingPosition(MutableVec3d[] targetPoints, int size, MutableVec3d viewerPosition) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单个体素是否对观察者可见
|
||||
*/
|
||||
private boolean isVoxelVisible(MutableVec3d viewerPosition, MutableVec3d position, byte faceData, byte visibleOnFace) {
|
||||
int targetSize = 0;
|
||||
Arrays.fill(dotselectors, false);
|
||||
|
||||
// 根据相对位置选择需要检测的关键点(角点和面中心点)
|
||||
if((visibleOnFace & ON_MIN_X) != 0){
|
||||
dotselectors[0] = true;
|
||||
if((faceData & ~ON_MIN_X) != 0) { dotselectors[1] = dotselectors[4] = dotselectors[5] = true; }
|
||||
dotselectors[8] = true;
|
||||
}
|
||||
if((visibleOnFace & ON_MIN_Y) != 0){
|
||||
dotselectors[0] = true;
|
||||
if((faceData & ~ON_MIN_Y) != 0) { dotselectors[3] = dotselectors[4] = dotselectors[7] = true; }
|
||||
dotselectors[9] = true;
|
||||
}
|
||||
if((visibleOnFace & ON_MIN_Z) != 0){
|
||||
dotselectors[0] = true;
|
||||
if((faceData & ~ON_MIN_Z) != 0) { dotselectors[1] = dotselectors[4] = dotselectors[5] = true; }
|
||||
dotselectors[10] = true;
|
||||
}
|
||||
if((visibleOnFace & ON_MAX_X) != 0){
|
||||
dotselectors[4] = true;
|
||||
if((faceData & ~ON_MAX_X) != 0) { dotselectors[5] = dotselectors[6] = dotselectors[7] = true; }
|
||||
dotselectors[11] = true;
|
||||
}
|
||||
if((visibleOnFace & ON_MAX_Y) != 0){
|
||||
dotselectors[1] = true;
|
||||
if((faceData & ~ON_MAX_Y) != 0) { dotselectors[2] = dotselectors[5] = dotselectors[6] = true; }
|
||||
dotselectors[12] = true;
|
||||
}
|
||||
if((visibleOnFace & ON_MAX_Z) != 0){
|
||||
dotselectors[2] = true;
|
||||
if((faceData & ~ON_MAX_Z) != 0) { dotselectors[3] = dotselectors[6] = dotselectors[7] = true; }
|
||||
dotselectors[13] = true;
|
||||
}
|
||||
|
||||
// 填充目标点,使用偏移量防止Z-Fighting或精度问题
|
||||
if (dotselectors[0]) targetPoints[targetSize++].add(position, 0.05, 0.05, 0.05);
|
||||
if (dotselectors[1]) targetPoints[targetSize++].add(position, 0.05, 0.95, 0.05);
|
||||
if (dotselectors[2]) targetPoints[targetSize++].add(position, 0.05, 0.95, 0.95);
|
||||
if (dotselectors[3]) targetPoints[targetSize++].add(position, 0.05, 0.05, 0.95);
|
||||
if (dotselectors[4]) targetPoints[targetSize++].add(position, 0.95, 0.05, 0.05);
|
||||
if (dotselectors[5]) targetPoints[targetSize++].add(position, 0.95, 0.95, 0.05);
|
||||
if (dotselectors[6]) targetPoints[targetSize++].add(position, 0.95, 0.95, 0.95);
|
||||
if (dotselectors[7]) targetPoints[targetSize++].add(position, 0.95, 0.05, 0.95);
|
||||
// 清空之前的缓存
|
||||
Arrays.fill(this.dotSelectors, false);
|
||||
if (relX == Relative.POSITIVE) {
|
||||
this.dotSelectors[0] = this.dotSelectors[2] = this.dotSelectors[4] = this.dotSelectors[6] = this.dotSelectors[10] = true;
|
||||
} else if (relX == Relative.NEGATIVE) {
|
||||
this.dotSelectors[1] = this.dotSelectors[3] = this.dotSelectors[5] = this.dotSelectors[7] = this.dotSelectors[11] = true;
|
||||
}
|
||||
if (relY == Relative.POSITIVE) {
|
||||
this.dotSelectors[0] = this.dotSelectors[1] = this.dotSelectors[2] = this.dotSelectors[3] = this.dotSelectors[12] = true;
|
||||
} else if (relY == Relative.NEGATIVE) {
|
||||
this.dotSelectors[4] = this.dotSelectors[5] = this.dotSelectors[6] = this.dotSelectors[7] = this.dotSelectors[13] = true;
|
||||
}
|
||||
if (relZ == Relative.POSITIVE) {
|
||||
this.dotSelectors[0] = this.dotSelectors[1] = this.dotSelectors[4] = this.dotSelectors[5] = this.dotSelectors[8] = true;
|
||||
} else if (relZ == Relative.NEGATIVE) {
|
||||
this.dotSelectors[2] = this.dotSelectors[3] = this.dotSelectors[6] = this.dotSelectors[7] = this.dotSelectors[9] = true;
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
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);
|
||||
// 面中心点
|
||||
if (dotselectors[8]) targetPoints[targetSize++].add(position, 0.05, 0.5, 0.5);
|
||||
if (dotselectors[9]) targetPoints[targetSize++].add(position, 0.5, 0.05, 0.5);
|
||||
if (dotselectors[10]) targetPoints[targetSize++].add(position, 0.5, 0.5, 0.05);
|
||||
if (dotselectors[11]) targetPoints[targetSize++].add(position, 0.95, 0.5, 0.5);
|
||||
if (dotselectors[12]) targetPoints[targetSize++].add(position, 0.5, 0.95, 0.5);
|
||||
if (dotselectors[13]) targetPoints[targetSize++].add(position, 0.5, 0.5, 0.95);
|
||||
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);
|
||||
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);
|
||||
|
||||
return isVisible(viewerPosition, targetPoints, targetSize);
|
||||
}
|
||||
|
||||
// 优化:使用基本数据类型代替对象分配
|
||||
private boolean rayIntersection(int[] b, MutableVec3d rayOrigin, double dirX, double dirY, double dirZ) {
|
||||
double invX = 1.0 / dirX;
|
||||
double invY = 1.0 / dirY;
|
||||
double invZ = 1.0 / dirZ;
|
||||
|
||||
double t1 = (b[0] - rayOrigin.x) * invX;
|
||||
double t2 = (b[0] + 1 - rayOrigin.x) * invX;
|
||||
double t3 = (b[1] - rayOrigin.y) * invY;
|
||||
double t4 = (b[1] + 1 - rayOrigin.y) * invY;
|
||||
double t5 = (b[2] - rayOrigin.z) * invZ;
|
||||
double t6 = (b[2] + 1 - rayOrigin.z) * invZ;
|
||||
|
||||
double tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
|
||||
double tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
|
||||
|
||||
// tmax > 0: 射线与AABB相交,但AABB在身后
|
||||
// tmin > tmax: 射线不相交
|
||||
return tmax > 0 && tmin <= tmax;
|
||||
return isVisible(cameraPos, this.targetPoints, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于网格的光线追踪 (DDA算法)
|
||||
* 使用3D DDA算法检测从起点到多个目标点的视线是否通畅
|
||||
* 算法基于数字微分分析,遍历射线路径上的所有方块
|
||||
*/
|
||||
private boolean isVisible(MutableVec3d start, MutableVec3d[] targets, int size) {
|
||||
int startX = cameraPos[0];
|
||||
int startY = cameraPos[1];
|
||||
int startZ = cameraPos[2];
|
||||
private boolean isVisible(Vec3d start, MutableVec3d[] targets, int targetCount) {
|
||||
// 起点所在方块的整数坐标(世界坐标转换为方块坐标)
|
||||
int startBlockX = MiscUtils.floor(start.x);
|
||||
int startBlockY = MiscUtils.floor(start.y);
|
||||
int startBlockZ = MiscUtils.floor(start.z);
|
||||
|
||||
for (int v = 0; v < size; v++) {
|
||||
MutableVec3d target = targets[v];
|
||||
// 遍历所有目标点进行视线检测
|
||||
for (int targetIndex = 0; targetIndex < targetCount; targetIndex++) {
|
||||
MutableVec3d currentTarget = targets[targetIndex];
|
||||
|
||||
double relX = start.x - target.x;
|
||||
double relY = start.y - target.y;
|
||||
double relZ = start.z - target.z;
|
||||
// 计算起点到目标的相对向量(世界坐标差)
|
||||
double deltaX = start.x - currentTarget.x;
|
||||
double deltaY = start.y - currentTarget.y;
|
||||
double deltaZ = start.z - currentTarget.z;
|
||||
|
||||
// 优化:避免在此处创建新的Vec3d对象进行归一化
|
||||
if(allowRayChecks) {
|
||||
double len = Math.sqrt(relX * relX + relY * relY + relZ * relZ);
|
||||
// 传入归一化后的方向分量
|
||||
if (rayIntersection(lastHitBlock, start, relX / len, relY / len, relZ / len)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
double dimAbsX = Math.abs(relX);
|
||||
double dimAbsY = Math.abs(relY);
|
||||
double dimAbsZ = Math.abs(relZ);
|
||||
// 计算相对向量的绝对值,用于确定各方向上的距离
|
||||
double absDeltaX = Math.abs(deltaX);
|
||||
double absDeltaY = Math.abs(deltaY);
|
||||
double absDeltaZ = Math.abs(deltaZ);
|
||||
|
||||
double dimFracX = 1f / dimAbsX;
|
||||
double dimFracY = 1f / dimAbsY;
|
||||
double dimFracZ = 1f / dimAbsZ;
|
||||
// 预计算每单位距离在各方块边界上的步进增量
|
||||
// 这些值表示射线穿过一个方块所需的时间分数
|
||||
double stepIncrementX = 1.0 / (absDeltaX + 1e-10); // 避免除0
|
||||
double stepIncrementY = 1.0 / (absDeltaY + 1e-10);
|
||||
double stepIncrementZ = 1.0 / (absDeltaZ + 1e-10);
|
||||
|
||||
int intersectCount = 1;
|
||||
int x_inc, y_inc, z_inc;
|
||||
double t_next_y, t_next_x, t_next_z;
|
||||
// 射线将穿过的总方块数量(包括起点和终点)
|
||||
int totalBlocksToCheck = 1;
|
||||
|
||||
// 初始化DDA步进参数
|
||||
if (dimAbsX == 0f) {
|
||||
x_inc = 0; t_next_x = dimFracX;
|
||||
} else if (target.x > start.x) {
|
||||
x_inc = 1;
|
||||
intersectCount += MiscUtils.fastFloor(target.x) - startX;
|
||||
t_next_x = (startX + 1 - start.x) * dimFracX;
|
||||
// 各方块坐标的步进方向(1: 正向, -1: 反向, 0: 静止)
|
||||
int stepDirectionX, stepDirectionY, stepDirectionZ;
|
||||
|
||||
// 到下一个方块边界的时间参数(射线参数化表示)
|
||||
double nextStepTimeX, nextStepTimeY, nextStepTimeZ;
|
||||
|
||||
// X方向步进参数计算
|
||||
if (absDeltaX == 0.0) {
|
||||
// X方向无变化,射线平行于YZ平面
|
||||
stepDirectionX = 0;
|
||||
nextStepTimeX = stepIncrementX;
|
||||
} else if (currentTarget.x > start.x) {
|
||||
// 目标在起点右侧,向右步进
|
||||
stepDirectionX = 1;
|
||||
totalBlocksToCheck += MiscUtils.floor(currentTarget.x) - startBlockX;
|
||||
nextStepTimeX = (startBlockX + 1 - start.x) * stepIncrementX;
|
||||
} else {
|
||||
x_inc = -1;
|
||||
intersectCount += startX - MiscUtils.fastFloor(target.x);
|
||||
t_next_x = (start.x - startX) * dimFracX;
|
||||
// 目标在起点左侧,向左步进
|
||||
stepDirectionX = -1;
|
||||
totalBlocksToCheck += startBlockX - MiscUtils.floor(currentTarget.x);
|
||||
nextStepTimeX = (start.x - startBlockX) * stepIncrementX;
|
||||
}
|
||||
|
||||
if (dimAbsY == 0f) {
|
||||
y_inc = 0; t_next_y = dimFracY;
|
||||
} else if (target.y > start.y) {
|
||||
y_inc = 1;
|
||||
intersectCount += MiscUtils.fastFloor(target.y) - startY;
|
||||
t_next_y = (startY + 1 - start.y) * dimFracY;
|
||||
// Y方向步进参数计算
|
||||
if (absDeltaY == 0.0) {
|
||||
// Y方向无变化,射线平行于XZ平面
|
||||
stepDirectionY = 0;
|
||||
nextStepTimeY = stepIncrementY;
|
||||
} else if (currentTarget.y > start.y) {
|
||||
// 目标在起点上方,向上步进
|
||||
stepDirectionY = 1;
|
||||
totalBlocksToCheck += MiscUtils.floor(currentTarget.y) - startBlockY;
|
||||
nextStepTimeY = (startBlockY + 1 - start.y) * stepIncrementY;
|
||||
} else {
|
||||
y_inc = -1;
|
||||
intersectCount += startY - MiscUtils.fastFloor(target.y);
|
||||
t_next_y = (start.y - startY) * dimFracY;
|
||||
// 目标在起点下方,向下步进
|
||||
stepDirectionY = -1;
|
||||
totalBlocksToCheck += startBlockY - MiscUtils.floor(currentTarget.y);
|
||||
nextStepTimeY = (start.y - startBlockY) * stepIncrementY;
|
||||
}
|
||||
|
||||
if (dimAbsZ == 0f) {
|
||||
z_inc = 0; t_next_z = dimFracZ;
|
||||
} else if (target.z > start.z) {
|
||||
z_inc = 1;
|
||||
intersectCount += MiscUtils.fastFloor(target.z) - startZ;
|
||||
t_next_z = (startZ + 1 - start.z) * dimFracZ;
|
||||
// Z方向步进参数计算
|
||||
if (absDeltaZ == 0.0) {
|
||||
// Z方向无变化,射线平行于XY平面
|
||||
stepDirectionZ = 0;
|
||||
nextStepTimeZ = stepIncrementZ;
|
||||
} else if (currentTarget.z > start.z) {
|
||||
// 目标在起点前方,向前步进
|
||||
stepDirectionZ = 1;
|
||||
totalBlocksToCheck += MiscUtils.floor(currentTarget.z) - startBlockZ;
|
||||
nextStepTimeZ = (startBlockZ + 1 - start.z) * stepIncrementZ;
|
||||
} else {
|
||||
z_inc = -1;
|
||||
intersectCount += startZ - MiscUtils.fastFloor(target.z);
|
||||
t_next_z = (start.z - startZ) * dimFracZ;
|
||||
// 目标在起点后方,向后步进
|
||||
stepDirectionZ = -1;
|
||||
totalBlocksToCheck += startBlockZ - MiscUtils.floor(currentTarget.z);
|
||||
nextStepTimeZ = (start.z - startBlockZ) * stepIncrementZ;
|
||||
}
|
||||
|
||||
boolean finished = stepRay(startX, startY, startZ,
|
||||
dimFracX, dimFracY, dimFracZ, intersectCount,
|
||||
x_inc, y_inc, z_inc,
|
||||
t_next_y, t_next_x, t_next_z);
|
||||
|
||||
provider.cleanup();
|
||||
if (finished) {
|
||||
cacheResult(targets[0], true);
|
||||
// 执行DDA步进算法,遍历射线路径上的所有方块
|
||||
boolean isLineOfSightClear = stepRay(
|
||||
startBlockX, startBlockY, startBlockZ,
|
||||
stepIncrementX, stepIncrementY, stepIncrementZ, totalBlocksToCheck,
|
||||
stepDirectionX, stepDirectionY, stepDirectionZ,
|
||||
nextStepTimeY, nextStepTimeX, nextStepTimeZ);
|
||||
|
||||
// 如果当前目标点可见立即返回
|
||||
if (isLineOfSightClear) {
|
||||
return true;
|
||||
} else {
|
||||
allowRayChecks = true;
|
||||
}
|
||||
}
|
||||
cacheResult(targets[0], false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean stepRay(int currentX, int currentY, int currentZ,
|
||||
double distInX, double distInY, double distInZ,
|
||||
int n, int x_inc, int y_inc, int z_inc,
|
||||
double t_next_y, double t_next_x, double t_next_z) {
|
||||
|
||||
allowWallClipping = true; // 初始允许穿墙直到移出起始方块
|
||||
private boolean stepRay(int currentBlockX, int currentBlockY, int currentBlockZ,
|
||||
double stepSizeX, double stepSizeY, double stepSizeZ,
|
||||
int remainingSteps, int stepDirectionX, int stepDirectionY,
|
||||
int stepDirectionZ, double nextStepTimeY, double nextStepTimeX,
|
||||
double nextStepTimeZ) {
|
||||
|
||||
for (; n > 1; n--) {
|
||||
// 检查缓存状态:2=遮挡
|
||||
int cVal = getCacheValue(currentX, currentY, currentZ);
|
||||
if (cVal == 2 && !allowWallClipping) {
|
||||
lastHitBlock[0] = currentX; lastHitBlock[1] = currentY; lastHitBlock[2] = currentZ;
|
||||
return false;
|
||||
// 遍历射线路径上的所有方块(跳过最后一个目标方块)
|
||||
for (; remainingSteps > 1; remainingSteps--) {
|
||||
|
||||
// 检查当前方块是否遮挡视线
|
||||
if (isOccluding(currentBlockX, currentBlockY, currentBlockZ)) {
|
||||
return false; // 视线被遮挡,立即返回
|
||||
}
|
||||
|
||||
if (cVal == 0) {
|
||||
// 未缓存,查询Provider
|
||||
int chunkX = currentX >> 4;
|
||||
int chunkZ = currentZ >> 4;
|
||||
if (!provider.prepareChunk(chunkX, chunkZ)) return false;
|
||||
|
||||
if (provider.isOpaqueFullCube(currentX, currentY, currentZ)) {
|
||||
if (!allowWallClipping) {
|
||||
cache.setLastHidden();
|
||||
lastHitBlock[0] = currentX; lastHitBlock[1] = currentY; lastHitBlock[2] = currentZ;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
allowWallClipping = false;
|
||||
cache.setLastVisible();
|
||||
}
|
||||
} else if(cVal == 1) {
|
||||
allowWallClipping = false;
|
||||
}
|
||||
|
||||
// DDA算法选择下一个体素
|
||||
if (t_next_y < t_next_x && t_next_y < t_next_z) {
|
||||
currentY += y_inc;
|
||||
t_next_y += distInY;
|
||||
} else if (t_next_x < t_next_y && t_next_x < t_next_z) {
|
||||
currentX += x_inc;
|
||||
t_next_x += distInX;
|
||||
// 基于时间参数选择下一个要遍历的方块方向
|
||||
// 选择距离最近的方块边界作为下一步
|
||||
if (nextStepTimeY < nextStepTimeX && nextStepTimeY < nextStepTimeZ) {
|
||||
// Y方向边界最近,垂直移动
|
||||
currentBlockY += stepDirectionY;
|
||||
nextStepTimeY += stepSizeY;
|
||||
} else if (nextStepTimeX < nextStepTimeY && nextStepTimeX < nextStepTimeZ) {
|
||||
// X方向边界最近,水平移动
|
||||
currentBlockX += stepDirectionX;
|
||||
nextStepTimeX += stepSizeX;
|
||||
} else {
|
||||
currentZ += z_inc;
|
||||
t_next_z += distInZ;
|
||||
// Z方向边界最近,深度移动
|
||||
currentBlockZ += stepDirectionZ;
|
||||
nextStepTimeZ += stepSizeZ;
|
||||
}
|
||||
}
|
||||
|
||||
// 成功遍历所有中间方块,视线通畅
|
||||
return true;
|
||||
}
|
||||
|
||||
// 缓存状态:-1=无效, 0=未检查, 1=可见, 2=遮挡
|
||||
private int getCacheValue(int x, int y, int z) {
|
||||
x -= cameraPos[0];
|
||||
y -= cameraPos[1];
|
||||
z -= cameraPos[2];
|
||||
if (Math.abs(x) > reach - 2 || Math.abs(y) > reach - 2 || Math.abs(z) > reach - 2) {
|
||||
return -1;
|
||||
private double distanceSq(int min, int max, double camera, Relative rel) {
|
||||
if (rel == Relative.NEGATIVE) {
|
||||
double dx = camera - max;
|
||||
return dx * dx;
|
||||
} else if (rel == Relative.POSITIVE) {
|
||||
double dx = min - camera;
|
||||
return dx * dx;
|
||||
}
|
||||
return cache.getState(x + reach, y + reach, z + reach);
|
||||
return 0d;
|
||||
}
|
||||
|
||||
private void cacheResult(MutableVec3d vector, boolean result) {
|
||||
int cx = MiscUtils.fastFloor(vector.x) - cameraPos[0] + reach;
|
||||
int cy = MiscUtils.fastFloor(vector.y) - cameraPos[1] + reach;
|
||||
int cz = MiscUtils.fastFloor(vector.z) - cameraPos[2] + reach;
|
||||
if (result) cache.setVisible(cx, cy, cz);
|
||||
else cache.setHidden(cx, cy, cz);
|
||||
}
|
||||
|
||||
public void resetCache() {
|
||||
this.cache.resetCache();
|
||||
private boolean isOccluding(int x, int y, int z) {
|
||||
ClientChunk trackedChunk = this.player.getTrackedChunk(ChunkPos.asLong(x >> 4, z >> 4));
|
||||
if (trackedChunk == null) {
|
||||
return false;
|
||||
}
|
||||
return trackedChunk.isOccluding(x, y, z);
|
||||
}
|
||||
|
||||
private enum Relative {
|
||||
INSIDE, POSITIVE, NEGATIVE;
|
||||
public static Relative from(int min, int max, int pos) {
|
||||
if (max > pos && min > pos) return POSITIVE;
|
||||
else if (min < pos && max < pos) return NEGATIVE;
|
||||
public static Relative from(int min, int max, double pos) {
|
||||
if (min > pos) return POSITIVE;
|
||||
else if (max < pos) return NEGATIVE;
|
||||
return INSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OcclusionCache {
|
||||
void resetCache();
|
||||
void setVisible(int x, int y, int z);
|
||||
void setHidden(int x, int y, int z);
|
||||
int getState(int x, int y, int z);
|
||||
void setLastHidden();
|
||||
void setLastVisible();
|
||||
}
|
||||
|
||||
// 使用位运算压缩存储状态的缓存实现
|
||||
public static class ArrayOcclusionCache implements OcclusionCache {
|
||||
private final int reachX2;
|
||||
private final byte[] cache;
|
||||
private int entry, offset;
|
||||
|
||||
public ArrayOcclusionCache(int reach) {
|
||||
this.reachX2 = reach * 2;
|
||||
// 每一个位置占2位
|
||||
this.cache = new byte[(reachX2 * reachX2 * reachX2) / 4 + 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCache() {
|
||||
Arrays.fill(cache, (byte) 0);
|
||||
}
|
||||
|
||||
private void calcIndex(int x, int y, int z) {
|
||||
int positionKey = x + y * reachX2 + z * reachX2 * reachX2;
|
||||
entry = positionKey / 4;
|
||||
offset = (positionKey % 4) * 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(int x, int y, int z) {
|
||||
calcIndex(x, y, z);
|
||||
cache[entry] |= 1 << offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHidden(int x, int y, int z) {
|
||||
calcIndex(x, y, z);
|
||||
cache[entry] |= 1 << (offset + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getState(int x, int y, int z) {
|
||||
calcIndex(x, y, z);
|
||||
return (cache[entry] >> offset) & 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastVisible() {
|
||||
cache[entry] |= 1 << offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastHidden() {
|
||||
cache[entry] |= 1 << (offset + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
package net.momirealms.craftengine.core.plugin.entityculling;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
// Amanatides, J., & Woo, A. A Fast Voxel Traversal Algorithm for Ray Tracing. http://www.cse.yorku.ca/~amana/research/grid.pdf.
|
||||
public final class VoxelIterator implements Iterator<int[]> {
|
||||
private int x;
|
||||
private int y;
|
||||
private int z;
|
||||
private int stepX;
|
||||
private int stepY;
|
||||
private int stepZ;
|
||||
private double tMax;
|
||||
private double tMaxX;
|
||||
private double tMaxY;
|
||||
private double tMaxZ;
|
||||
private double tDeltaX;
|
||||
private double tDeltaY;
|
||||
private double tDeltaZ;
|
||||
private int[] ref = new int[3]; // This implementation always returns ref or refSwap to avoid garbage. Can easily be changed if needed.
|
||||
private int[] refSwap = new int[3];
|
||||
private int[] next;
|
||||
|
||||
public VoxelIterator(double startX, double startY, double startZ, double endX, double endY, double endZ) {
|
||||
initialize(startX, startY, startZ, endX, endY, endZ);
|
||||
}
|
||||
|
||||
public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double endX, double endY, double endZ) {
|
||||
initialize(x, y, z, startX, startY, startZ, endX, endY, endZ);
|
||||
}
|
||||
|
||||
public VoxelIterator(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) {
|
||||
initialize(startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
}
|
||||
|
||||
public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) {
|
||||
initialize(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
}
|
||||
|
||||
public VoxelIterator(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance, boolean normalized) {
|
||||
if (normalized) {
|
||||
initializeNormalized(startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
} else {
|
||||
initialize(startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelIterator(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance, boolean normalized) {
|
||||
if (normalized) {
|
||||
initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
} else {
|
||||
initialize(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelIterator initialize(double startX, double startY, double startZ, double endX, double endY, double endZ) {
|
||||
return initialize(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, endX, endY, endZ);
|
||||
}
|
||||
|
||||
public VoxelIterator initialize(int x, int y, int z, double startX, double startY, double startZ, double endX, double endY, double endZ) {
|
||||
double directionX = endX - startX;
|
||||
double directionY = endY - startY;
|
||||
double directionZ = endZ - startZ;
|
||||
double distance = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ);
|
||||
double fixedDistance = distance == 0. ? Double.NaN : distance;
|
||||
directionX /= fixedDistance;
|
||||
directionY /= fixedDistance;
|
||||
directionZ /= fixedDistance;
|
||||
return initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
}
|
||||
|
||||
public VoxelIterator initialize(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) {
|
||||
return initialize(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, directionX, directionY, directionZ, distance);
|
||||
}
|
||||
|
||||
public VoxelIterator initialize(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) {
|
||||
double signum = Math.signum(distance);
|
||||
directionX *= signum;
|
||||
directionY *= signum;
|
||||
directionZ *= signum;
|
||||
double length = Math.sqrt(directionX * directionX + directionY * directionY + directionZ * directionZ);
|
||||
|
||||
if (length == 0.) {
|
||||
length = Double.NaN;
|
||||
}
|
||||
|
||||
directionX /= length;
|
||||
directionY /= length;
|
||||
directionZ /= length;
|
||||
return initializeNormalized(x, y, z, startX, startY, startZ, directionX, directionY, directionZ, Math.abs(distance));
|
||||
}
|
||||
|
||||
public VoxelIterator initializeNormalized(double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) {
|
||||
return initializeNormalized(floor(startX), floor(startY), floor(startZ), startX, startY, startZ, directionX, directionY, directionZ, Math.abs(distance));
|
||||
}
|
||||
|
||||
public VoxelIterator initializeNormalized(int x, int y, int z, double startX, double startY, double startZ, double directionX, double directionY, double directionZ, double distance) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
tMax = distance;
|
||||
stepX = directionX < 0. ? -1 : 1;
|
||||
stepY = directionY < 0. ? -1 : 1;
|
||||
stepZ = directionZ < 0. ? -1 : 1;
|
||||
tMaxX = directionX == 0. ? Double.POSITIVE_INFINITY : (x + (stepX + 1) / 2 - startX) / directionX;
|
||||
tMaxY = directionY == 0. ? Double.POSITIVE_INFINITY : (y + (stepY + 1) / 2 - startY) / directionY;
|
||||
tMaxZ = directionZ == 0. ? Double.POSITIVE_INFINITY : (z + (stepZ + 1) / 2 - startZ) / directionZ;
|
||||
tDeltaX = 1. / Math.abs(directionX);
|
||||
tDeltaY = 1. / Math.abs(directionY);
|
||||
tDeltaZ = 1. / Math.abs(directionZ);
|
||||
next = ref;
|
||||
ref[0] = x;
|
||||
ref[1] = y;
|
||||
ref[2] = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int[] calculateNext() {
|
||||
if (tMaxX < tMaxY) {
|
||||
if (tMaxZ < tMaxX) {
|
||||
if (tMaxZ <= tMax) {
|
||||
z += stepZ;
|
||||
// next = new int[] { x, y, z };
|
||||
ref[0] = x;
|
||||
ref[1] = y;
|
||||
ref[2] = z;
|
||||
tMaxZ += tDeltaZ;
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
} else {
|
||||
if (tMaxX <= tMax) {
|
||||
if (tMaxZ == tMaxX) {
|
||||
z += stepZ;
|
||||
tMaxZ += tDeltaZ;
|
||||
}
|
||||
|
||||
x += stepX;
|
||||
// next = new int[] { x, y, z };
|
||||
ref[0] = x;
|
||||
ref[1] = y;
|
||||
ref[2] = z;
|
||||
tMaxX += tDeltaX;
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
} else if (tMaxY < tMaxZ) {
|
||||
if (tMaxY <= tMax) {
|
||||
if (tMaxX == tMaxY) {
|
||||
x += stepX;
|
||||
tMaxX += tDeltaX;
|
||||
}
|
||||
|
||||
y += stepY;
|
||||
// next = new int[] { x, y, z };
|
||||
ref[0] = x;
|
||||
ref[1] = y;
|
||||
ref[2] = z;
|
||||
tMaxY += tDeltaY;
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
} else {
|
||||
if (tMaxZ <= tMax) {
|
||||
if (tMaxX == tMaxZ) {
|
||||
x += stepX;
|
||||
tMaxX += tDeltaX;
|
||||
}
|
||||
|
||||
if (tMaxY == tMaxZ) {
|
||||
y += stepY;
|
||||
tMaxY += tDeltaY;
|
||||
}
|
||||
|
||||
z += stepZ;
|
||||
// next = new int[] { x, y, z };
|
||||
ref[0] = x;
|
||||
ref[1] = y;
|
||||
ref[2] = z;
|
||||
tMaxZ += tDeltaZ;
|
||||
} else {
|
||||
next = null;
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] next() {
|
||||
int[] next = this.next;
|
||||
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
int[] temp = ref;
|
||||
ref = refSwap;
|
||||
refSwap = temp;
|
||||
this.next = ref;
|
||||
calculateNext();
|
||||
return next;
|
||||
}
|
||||
|
||||
private static int floor(double value) {
|
||||
int i = (int) value;
|
||||
return value < (double) i ? i - 1 : i;
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class Color {
|
||||
}
|
||||
|
||||
public static Color fromVector3f(Vector3f vec) {
|
||||
return new Color(0 << 24 /*不可省略*/ | MiscUtils.fastFloor(vec.x) << 16 | MiscUtils.fastFloor(vec.y) << 8 | MiscUtils.fastFloor(vec.z));
|
||||
return new Color(0 << 24 /*不可省略*/ | MiscUtils.floor(vec.x) << 16 | MiscUtils.floor(vec.y) << 8 | MiscUtils.floor(vec.z));
|
||||
}
|
||||
|
||||
public static int opaque(int color) {
|
||||
|
||||
@@ -21,19 +21,19 @@ public class MiscUtils {
|
||||
}
|
||||
});
|
||||
|
||||
public static int fastFloor(double value) {
|
||||
public static int floor(double value) {
|
||||
int truncated = (int) value;
|
||||
return value < (double) truncated ? truncated - 1 : truncated;
|
||||
}
|
||||
|
||||
public static int fastFloor(float value) {
|
||||
public static int floor(float value) {
|
||||
int truncated = (int) value;
|
||||
return value < (double) truncated ? truncated - 1 : truncated;
|
||||
}
|
||||
|
||||
public static int lerpDiscrete(float delta, int start, int end) {
|
||||
int i = end - start;
|
||||
return start + fastFloor(delta * (float) (i - 1)) + (delta > 0.0F ? 1 : 0);
|
||||
return start + floor(delta * (float) (i - 1)) + (delta > 0.0F ? 1 : 0);
|
||||
}
|
||||
|
||||
public static int murmurHash3Mixer(int value) {
|
||||
@@ -270,7 +270,7 @@ public class MiscUtils {
|
||||
}
|
||||
|
||||
public static byte packDegrees(float degrees) {
|
||||
return (byte) fastFloor(degrees * 256.0F / 360.0F);
|
||||
return (byte) floor(degrees * 256.0F / 360.0F);
|
||||
}
|
||||
|
||||
public static float unpackDegrees(byte degrees) {
|
||||
|
||||
@@ -23,7 +23,7 @@ public class BlockPos extends Vec3i {
|
||||
}
|
||||
|
||||
public static BlockPos fromVec3d(Vec3d vec) {
|
||||
return new BlockPos(MiscUtils.fastFloor(vec.x), MiscUtils.fastFloor(vec.y), MiscUtils.fastFloor(vec.z));
|
||||
return new BlockPos(MiscUtils.floor(vec.x), MiscUtils.floor(vec.y), MiscUtils.floor(vec.z));
|
||||
}
|
||||
|
||||
public static BlockPos of(long packedPos) {
|
||||
|
||||
@@ -23,9 +23,9 @@ public class EntityHitResult {
|
||||
}
|
||||
|
||||
private BlockPos getBlockPos() {
|
||||
int x = MiscUtils.fastFloor(this.position.x);
|
||||
int y = MiscUtils.fastFloor(this.position.y);
|
||||
int z = MiscUtils.fastFloor(this.position.z);
|
||||
int x = MiscUtils.floor(this.position.x);
|
||||
int y = MiscUtils.floor(this.position.y);
|
||||
int z = MiscUtils.floor(this.position.z);
|
||||
if (this.direction == Direction.UP) {
|
||||
if (this.position.y % 1 == 0) {
|
||||
y--;
|
||||
|
||||
@@ -14,9 +14,9 @@ public class MutableVec3d implements Position {
|
||||
}
|
||||
|
||||
public MutableVec3d toCenter() {
|
||||
this.x = MiscUtils.fastFloor(x) + 0.5;
|
||||
this.y = MiscUtils.fastFloor(y) + 0.5;
|
||||
this.z = MiscUtils.fastFloor(z) + 0.5;
|
||||
this.x = MiscUtils.floor(x) + 0.5;
|
||||
this.y = MiscUtils.floor(y) + 0.5;
|
||||
this.z = MiscUtils.floor(z) + 0.5;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class Vec3d implements Position {
|
||||
}
|
||||
|
||||
public Vec3d toCenter() {
|
||||
return new Vec3d(MiscUtils.fastFloor(x) + 0.5, MiscUtils.fastFloor(y) + 0.5, MiscUtils.fastFloor(z) + 0.5);
|
||||
return new Vec3d(MiscUtils.floor(x) + 0.5, MiscUtils.floor(y) + 0.5, MiscUtils.floor(z) + 0.5);
|
||||
}
|
||||
|
||||
public Vec3d add(Vec3d vec) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject;
|
||||
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer;
|
||||
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer;
|
||||
import net.momirealms.sparrow.nbt.ListTag;
|
||||
@@ -155,8 +156,20 @@ public class CEChunk {
|
||||
if (element != null) {
|
||||
elements[0] = element;
|
||||
if (hasTrackedBy) {
|
||||
for (Player player : trackedBy) {
|
||||
element.transform(player);
|
||||
// 如果启用实体剔除,那么只对已经渲染的进行变换
|
||||
if (Config.enableEntityCulling()) {
|
||||
for (Player player : trackedBy) {
|
||||
VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos);
|
||||
if (trackedBlockEntity == null || trackedBlockEntity.isShown()) {
|
||||
element.transform(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 否则直接变换
|
||||
else {
|
||||
for (Player player : trackedBy) {
|
||||
element.transform(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
break outer;
|
||||
@@ -165,9 +178,18 @@ public class CEChunk {
|
||||
BlockEntityElement element = config.create(wrappedWorld, pos);
|
||||
elements[0] = element;
|
||||
if (hasTrackedBy) {
|
||||
for (Player player : trackedBy) {
|
||||
previousElement.hide(player);
|
||||
element.show(player);
|
||||
// 如果启用实体剔除,那么只添加记录
|
||||
if (Config.enableEntityCulling()) {
|
||||
for (Player player : trackedBy) {
|
||||
player.addTrackedBlockEntity(pos, renderer);
|
||||
}
|
||||
}
|
||||
// 否则直接显示
|
||||
else {
|
||||
for (Player player : trackedBy) {
|
||||
previousElement.hide(player);
|
||||
element.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,8 +204,20 @@ public class CEChunk {
|
||||
previousElements[j] = null;
|
||||
elements[i] = newElement;
|
||||
if (hasTrackedBy) {
|
||||
for (Player player : trackedBy) {
|
||||
newElement.transform(player);
|
||||
// 如果启用实体剔除,那么只对已经渲染的进行变换
|
||||
if (Config.enableEntityCulling()) {
|
||||
for (Player player : trackedBy) {
|
||||
VirtualCullableObject trackedBlockEntity = player.getTrackedBlockEntity(pos);
|
||||
if (trackedBlockEntity == null || trackedBlockEntity.isShown()) {
|
||||
newElement.transform(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 否则直接变换
|
||||
else {
|
||||
for (Player player : trackedBy) {
|
||||
newElement.transform(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue outer;
|
||||
@@ -193,8 +227,14 @@ public class CEChunk {
|
||||
BlockEntityElement newElement = config.create(wrappedWorld, pos);
|
||||
elements[i] = newElement;
|
||||
if (hasTrackedBy) {
|
||||
for (Player player : trackedBy) {
|
||||
newElement.show(player);
|
||||
if (Config.enableEntityCulling()) {
|
||||
for (Player player : trackedBy) {
|
||||
player.addTrackedBlockEntity(pos, renderer);
|
||||
}
|
||||
} else {
|
||||
for (Player player : trackedBy) {
|
||||
newElement.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,8 +254,14 @@ public class CEChunk {
|
||||
elements[i] = renderers[i].create(wrappedWorld, pos);
|
||||
}
|
||||
if (hasTrackedBy) {
|
||||
for (Player player : trackedBy) {
|
||||
renderer.show(player);
|
||||
if (Config.enableEntityCulling()) {
|
||||
for (Player player : trackedBy) {
|
||||
player.addTrackedBlockEntity(pos, renderer);
|
||||
}
|
||||
} else {
|
||||
for (Player player : trackedBy) {
|
||||
renderer.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,8 +288,14 @@ public class CEChunk {
|
||||
ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos);
|
||||
if (removed != null) {
|
||||
if (hide) {
|
||||
for (Player player : getTrackedBy()) {
|
||||
removed.hide(player);
|
||||
if (Config.enableEntityCulling()) {
|
||||
for (Player player : getTrackedBy()) {
|
||||
player.removeTrackedBlockEntities(List.of(pos));
|
||||
}
|
||||
} else {
|
||||
for (Player player : getTrackedBy()) {
|
||||
removed.hide(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ClientChunk {
|
||||
int index = sectionIndex(SectionPos.blockToSectionCoord(y));
|
||||
ClientSection section = this.sections[index];
|
||||
if (section == null) return;
|
||||
section.setOccluding((y & 15) << 8 | (z & 15) << 4, occluding);
|
||||
section.setOccluding((y & 15) << 8 | (z & 15) << 4 | x & 15, occluding);
|
||||
}
|
||||
|
||||
public int sectionIndex(int sectionId) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.world.Cullable;
|
||||
|
||||
public class VirtualCullableObject {
|
||||
private final Cullable cullable;
|
||||
public final Cullable cullable;
|
||||
private boolean isShown;
|
||||
|
||||
public VirtualCullableObject(Cullable cullable) {
|
||||
@@ -21,6 +21,7 @@ public class VirtualCullableObject {
|
||||
}
|
||||
|
||||
public void setShown(Player player, boolean shown) {
|
||||
if (this.isShown == shown) return;
|
||||
this.isShown = shown;
|
||||
if (shown) {
|
||||
this.cullable.show(player);
|
||||
|
||||
Reference in New Issue
Block a user