Improve runtime visibility checks using multi-sample target sampling
This commit is contained in:
@@ -5561,17 +5561,20 @@ export class RuntimeHost {
|
||||
switch (npc.collider.mode) {
|
||||
case "capsule":
|
||||
return (
|
||||
Math.max(npc.collider.radius, npc.collider.height * 0.5) +
|
||||
Math.max(npc.collider.radius, TARGETING_VISIBILITY_TARGET_CLEARANCE) +
|
||||
TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING
|
||||
);
|
||||
case "box":
|
||||
return (
|
||||
Math.hypot(
|
||||
npc.collider.size.x,
|
||||
npc.collider.size.y,
|
||||
npc.collider.size.z
|
||||
) *
|
||||
0.5 +
|
||||
clampScalar(
|
||||
Math.max(
|
||||
npc.collider.size.x,
|
||||
npc.collider.size.y,
|
||||
npc.collider.size.z
|
||||
) * 0.25,
|
||||
0.35,
|
||||
0.75
|
||||
) +
|
||||
TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING
|
||||
);
|
||||
case "none":
|
||||
@@ -5597,6 +5600,107 @@ export class RuntimeHost {
|
||||
);
|
||||
}
|
||||
|
||||
private resolveRuntimeTargetVisibilitySamples(target: {
|
||||
kind?: string;
|
||||
entityId?: string;
|
||||
center: { x: number; y: number; z: number };
|
||||
range: number;
|
||||
}): Array<{
|
||||
point: { x: number; y: number; z: number };
|
||||
targetClearance: number;
|
||||
}> {
|
||||
const targetClearance = this.resolveRuntimeTargetVisibilityClearance(target);
|
||||
|
||||
if (this.runtimeScene !== null && target.kind === "npc") {
|
||||
const npc =
|
||||
this.runtimeScene.entities.npcs.find(
|
||||
(candidate) => candidate.entityId === target.entityId
|
||||
) ?? null;
|
||||
|
||||
if (npc !== null) {
|
||||
switch (npc.collider.mode) {
|
||||
case "capsule": {
|
||||
const sampleClearance =
|
||||
Math.max(
|
||||
npc.collider.radius,
|
||||
TARGETING_VISIBILITY_TARGET_CLEARANCE
|
||||
) + TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING;
|
||||
const yAt = (factor: number) =>
|
||||
npc.position.y + npc.collider.height * factor;
|
||||
|
||||
return [
|
||||
{
|
||||
point: { x: npc.position.x, y: yAt(0.82), z: npc.position.z },
|
||||
targetClearance: sampleClearance
|
||||
},
|
||||
{
|
||||
point: { x: npc.position.x, y: yAt(0.62), z: npc.position.z },
|
||||
targetClearance: sampleClearance
|
||||
},
|
||||
{ point: target.center, targetClearance: sampleClearance },
|
||||
{
|
||||
point: { x: npc.position.x, y: yAt(0.38), z: npc.position.z },
|
||||
targetClearance: sampleClearance
|
||||
}
|
||||
];
|
||||
}
|
||||
case "box": {
|
||||
const sampleClearance =
|
||||
clampScalar(
|
||||
Math.max(
|
||||
npc.collider.size.x,
|
||||
npc.collider.size.y,
|
||||
npc.collider.size.z
|
||||
) * 0.25,
|
||||
0.35,
|
||||
0.75
|
||||
) + TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING;
|
||||
const yAt = (factor: number) =>
|
||||
npc.position.y + npc.collider.size.y * factor;
|
||||
|
||||
return [
|
||||
{
|
||||
point: { x: npc.position.x, y: yAt(0.82), z: npc.position.z },
|
||||
targetClearance: sampleClearance
|
||||
},
|
||||
{
|
||||
point: { x: npc.position.x, y: yAt(0.62), z: npc.position.z },
|
||||
targetClearance: sampleClearance
|
||||
},
|
||||
{ point: target.center, targetClearance: sampleClearance },
|
||||
{
|
||||
point: { x: npc.position.x, y: yAt(0.38), z: npc.position.z },
|
||||
targetClearance: sampleClearance
|
||||
}
|
||||
];
|
||||
}
|
||||
case "none":
|
||||
return [
|
||||
{
|
||||
point: {
|
||||
x: npc.position.x,
|
||||
y: npc.position.y + 1.45,
|
||||
z: npc.position.z
|
||||
},
|
||||
targetClearance
|
||||
},
|
||||
{ point: target.center, targetClearance },
|
||||
{
|
||||
point: {
|
||||
x: npc.position.x,
|
||||
y: npc.position.y + 0.65,
|
||||
z: npc.position.z
|
||||
},
|
||||
targetClearance
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [{ point: target.center, targetClearance }];
|
||||
}
|
||||
|
||||
private isRuntimeTargetVisibleFrom(
|
||||
origin: { x: number; y: number; z: number },
|
||||
target: {
|
||||
@@ -5610,9 +5714,13 @@ export class RuntimeHost {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.collisionWorld.isLineSegmentClear(origin, target.center, {
|
||||
targetClearance: this.resolveRuntimeTargetVisibilityClearance(target)
|
||||
});
|
||||
const collisionWorld = this.collisionWorld;
|
||||
|
||||
return this.resolveRuntimeTargetVisibilitySamples(target).some((sample) =>
|
||||
collisionWorld.isLineSegmentClear(origin, sample.point, {
|
||||
targetClearance: sample.targetClearance
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private isRuntimeTargetCameraVisible(target: {
|
||||
|
||||
Reference in New Issue
Block a user