auto-git:

[change] src/document/paths.ts
 [change] src/runtime-three/runtime-host.ts
This commit is contained in:
2026-04-22 17:53:18 +02:00
parent 58b029eb81
commit d7910979fe
2 changed files with 152 additions and 1 deletions

View File

@@ -35,6 +35,34 @@ export interface ResolvedScenePath {
totalLength: number;
}
export interface ResolvedScenePathProjectionSource {
points: Array<{
position: Vec3;
}>;
segments: Array<
Pick<
ResolvedScenePathSegment,
| "index"
| "start"
| "end"
| "length"
| "distanceStart"
| "distanceEnd"
| "tangent"
>
>;
totalLength: number;
}
export interface ResolvedScenePathNearestPoint {
progress: number;
distance: number;
distanceAlongPath: number;
segmentIndex: number | null;
position: Vec3;
tangent: Vec3;
}
interface PathPointLike {
position: Vec3;
}
@@ -692,6 +720,99 @@ export function sampleResolvedScenePathPosition(
};
}
export function resolveNearestPointOnResolvedScenePath(
path: ResolvedScenePathProjectionSource,
point: Vec3
): ResolvedScenePathNearestPoint {
assertFiniteVec3(point, "Nearest path query point");
if (path.points.length === 0) {
return {
progress: 0,
distance: Math.hypot(point.x, point.y, point.z),
distanceAlongPath: 0,
segmentIndex: null,
position: {
x: 0,
y: 0,
z: 0
},
tangent: {
x: 0,
y: 0,
z: 0
}
};
}
if (path.segments.length === 0 || path.totalLength <= 0) {
const firstPoint = path.points[0]!.position;
return {
progress: 0,
distance: getVec3Distance(firstPoint, point),
distanceAlongPath: 0,
segmentIndex: null,
position: cloneVec3(firstPoint),
tangent: {
x: 0,
y: 0,
z: 0
}
};
}
let nearestSample: ResolvedScenePathNearestPoint | null = null;
for (const segment of path.segments) {
const delta = subtractVec3(segment.end, segment.start);
const lengthSquared =
delta.x * delta.x + delta.y * delta.y + delta.z * delta.z;
const pointOffset = subtractVec3(point, segment.start);
const unclampedT =
lengthSquared <= 1e-8
? 0
: (pointOffset.x * delta.x +
pointOffset.y * delta.y +
pointOffset.z * delta.z) /
lengthSquared;
const t = Math.min(1, Math.max(0, unclampedT));
const position = {
x: segment.start.x + delta.x * t,
y: segment.start.y + delta.y * t,
z: segment.start.z + delta.z * t
};
const distanceAlongPath = segment.distanceStart + segment.length * t;
const progress = clampProgress(distanceAlongPath / path.totalLength);
const distance = getVec3Distance(position, point);
const tangent =
segment.length > 1e-8
? cloneVec3(segment.tangent)
: findNonZeroSegmentTangent(
path as ResolvedPathLike<PathPointLike, ResolvedPathSegmentLike>,
segment.index
);
const candidate: ResolvedScenePathNearestPoint = {
progress,
distance,
distanceAlongPath,
segmentIndex: segment.index,
position,
tangent,
};
if (
nearestSample === null ||
candidate.distance < nearestSample.distance - 1e-8 ||
(Math.abs(candidate.distance - nearestSample.distance) <= 1e-8 &&
candidate.progress < nearestSample.progress)
) {
nearestSample = candidate;
}
}
return nearestSample!;
}
export function sampleScenePathPosition(
path: Pick<ScenePath, "loop" | "points">,
progress: number,

View File

@@ -81,14 +81,22 @@ import {
fitCelestialDirectionalShadow,
resolveDominantCelestialShadowCaster
} from "../rendering/celestial-shadows";
import { resolveWorldShaderSkyRenderState } from "../rendering/world-shader-sky";
import {
resolveWorldShaderSkyEnvironmentPhaseStates,
resolveWorldShaderSkyRenderState
} from "../rendering/world-shader-sky";
import {
resolveWorldCelestialBodiesState,
resolveWorldEnvironmentState,
WorldBackgroundRenderer
} from "../rendering/world-background-renderer";
import {
createRendererPrecomputedShaderSkyEnvironmentCache,
type PrecomputedShaderSkyEnvironmentCache
} from "../rendering/precomputed-shader-sky-environment-cache";
import {
createRendererQuantizedEnvironmentBlendCache,
createRendererQuantizedPmremBlendCache,
type QuantizedEnvironmentBlendCache
} from "../rendering/quantized-environment-blend-cache";
import {
@@ -399,6 +407,8 @@ export class RuntimeHost {
private readonly controllerContext: RuntimeControllerContext;
private readonly renderer: WebGLRenderer | null;
private readonly environmentBlendCache: QuantizedEnvironmentBlendCache | null;
private readonly shaderSkyEnvironmentBlendCache: QuantizedEnvironmentBlendCache | null;
private readonly shaderSkyEnvironmentCache: PrecomputedShaderSkyEnvironmentCache | null;
private runtimeScene: RuntimeSceneDefinition | null = null;
private collisionWorld: RapierCollisionWorld | null = null;
private collisionWorldRequestId = 0;
@@ -514,6 +524,24 @@ export class RuntimeHost {
this.applyDayNightLighting();
}
});
this.shaderSkyEnvironmentBlendCache =
this.renderer === null
? null
: createRendererQuantizedPmremBlendCache(this.renderer, {
onTextureReady: () => {
this.applyDayNightLighting();
}
});
this.shaderSkyEnvironmentCache =
this.renderer === null
? null
: createRendererPrecomputedShaderSkyEnvironmentCache(
this.renderer,
this.worldBackgroundRenderer,
{
phaseBlendTextureResolver: this.shaderSkyEnvironmentBlendCache
}
);
this.controllerContext = {
camera: this.camera,
@@ -945,6 +973,8 @@ export class RuntimeHost {
this.materialTextureCache.clear();
this.environmentBlendCache?.dispose();
this.shaderSkyEnvironmentBlendCache?.dispose();
this.shaderSkyEnvironmentCache?.dispose();
this.worldBackgroundRenderer.dispose();
this.renderer?.forceContextLoss();
this.renderer?.dispose();