auto-git:

[change] src/geometry/spline-corridor-junction-mesh.ts
This commit is contained in:
2026-05-13 16:34:42 +02:00
parent 1f8f52d785
commit 4d676fcce0

View File

@@ -1,49 +1,18 @@
import { BufferGeometry, Float32BufferAttribute } from "three"; import { BufferGeometry, Float32BufferAttribute } from "three";
import type { Vec3 } from "../core/vector"; import type { Vec3 } from "../core/vector";
import {
resolveScenePath,
sampleResolvedScenePathPosition,
type ScenePathCurveMode,
type ScenePathPoint,
type ScenePathRoadSettings
} from "../document/paths";
import type { SplineCorridorJunction } from "../document/spline-corridor-junctions"; import type { SplineCorridorJunction } from "../document/spline-corridor-junctions";
import { import {
sampleTerrainHeightAtWorldPosition, sampleTerrainHeightAtWorldPosition,
type Terrain type Terrain
} from "../document/terrains"; } from "../document/terrains";
import {
buildSplineCorridorJunctionFootprint,
type SplineCorridorJunctionFootprintPathLike
} from "./spline-corridor-junction-footprint";
export interface SplineCorridorJunctionMeshPathLike { export type SplineCorridorJunctionMeshPathLike =
id: string; SplineCorridorJunctionFootprintPathLike;
loop: boolean;
curveMode?: ScenePathCurveMode;
sampledResolution?: number;
glueToTerrain?: boolean;
terrainOffset?: number;
road: ScenePathRoadSettings;
points: ReadonlyArray<{
id?: string;
pointId?: string;
position: Vec3;
}>;
}
const JUNCTION_SEGMENTS = 32;
function normalizePathPoint(
point: SplineCorridorJunctionMeshPathLike["points"][number],
index: number
): ScenePathPoint {
return {
id: point.id ?? point.pointId ?? `junction-path-point-${index}`,
position: {
x: point.position.x,
y: point.position.y,
z: point.position.z
}
};
}
function sampleHighestTerrainWorldY( function sampleHighestTerrainWorldY(
terrains: readonly Terrain[], terrains: readonly Terrain[],
@@ -73,53 +42,6 @@ function sampleHighestTerrainWorldY(
return highestWorldY; return highestWorldY;
} }
function resolveJunctionPath(
path: SplineCorridorJunctionMeshPathLike,
terrains: readonly Terrain[]
) {
return resolveScenePath(
{
loop: path.loop,
curveMode: path.curveMode,
sampledResolution: path.sampledResolution,
glueToTerrain: path.glueToTerrain,
terrainOffset: path.terrainOffset,
points: path.points.map(normalizePathPoint)
},
{ terrains }
);
}
function resolveFallbackY(options: {
junction: SplineCorridorJunction;
paths: readonly SplineCorridorJunctionMeshPathLike[];
terrains: readonly Terrain[];
}): number {
const sampledPositions = options.junction.connections
.map((connection) => {
const path =
options.paths.find((candidate) => candidate.id === connection.pathId) ??
null;
if (path === null) {
return null;
}
const resolvedPath = resolveJunctionPath(path, options.terrains);
return sampleResolvedScenePathPosition(resolvedPath, connection.progress);
})
.filter((position): position is Vec3 => position !== null);
if (sampledPositions.length === 0) {
return options.junction.center.y;
}
return (
sampledPositions.reduce((sum, position) => sum + position.y, 0) /
sampledPositions.length
);
}
function shouldConformToTerrain(options: { function shouldConformToTerrain(options: {
junction: SplineCorridorJunction; junction: SplineCorridorJunction;
paths: readonly SplineCorridorJunctionMeshPathLike[]; paths: readonly SplineCorridorJunctionMeshPathLike[];
@@ -140,43 +62,73 @@ export function buildSplineCorridorJunctionMeshGeometry(options: {
}): BufferGeometry | null { }): BufferGeometry | null {
const { junction, paths } = options; const { junction, paths } = options;
const terrains = options.terrains ?? []; const terrains = options.terrains ?? [];
const footprint = buildSplineCorridorJunctionFootprint({
junction,
paths,
terrains
});
if (!junction.enabled || !junction.visible || junction.radius <= 0) { if (footprint === null) {
return null; return null;
} }
const conformToTerrain = shouldConformToTerrain({ junction, paths }); const conformToTerrain = shouldConformToTerrain({ junction, paths });
const fallbackY = resolveFallbackY({ junction, paths, terrains });
const positions: number[] = []; const positions: number[] = [];
const uvs: number[] = []; const uvs: number[] = [];
const indices: number[] = []; const indices: number[] = [];
function resolveY(position: Vec3): number { function resolveY(position: Vec3, fallbackY: number, heightOffset: number): number {
const terrainWorldY = conformToTerrain const terrainWorldY = conformToTerrain
? sampleHighestTerrainWorldY(terrains, position) ? sampleHighestTerrainWorldY(terrains, position)
: null; : null;
return terrainWorldY ?? fallbackY; return terrainWorldY === null ? fallbackY : terrainWorldY + heightOffset;
} }
positions.push(junction.center.x, resolveY(junction.center), junction.center.z); function pushUv(x: number, z: number) {
uvs.push(0.5, 0.5); const deltaX = x - footprint.center.x;
const deltaZ = z - footprint.center.z;
uvs.push(
deltaX * footprint.rightAxis.x + deltaZ * footprint.rightAxis.z,
deltaX * footprint.forwardAxis.x + deltaZ * footprint.forwardAxis.z
);
}
for (let index = 0; index < JUNCTION_SEGMENTS; index += 1) { positions.push(
const angle = (index / JUNCTION_SEGMENTS) * Math.PI * 2; footprint.center.x,
const position = { resolveY(
x: junction.center.x + Math.cos(angle) * junction.radius, {
x: footprint.center.x,
y: junction.center.y, y: junction.center.y,
z: junction.center.z + Math.sin(angle) * junction.radius z: footprint.center.z
}; },
footprint.center.fallbackY,
footprint.center.heightOffset
),
footprint.center.z
);
pushUv(footprint.center.x, footprint.center.z);
positions.push(position.x, resolveY(position), position.z); for (const point of footprint.points) {
uvs.push(0.5 + Math.cos(angle) * 0.5, 0.5 + Math.sin(angle) * 0.5); positions.push(
point.x,
resolveY(
{
x: point.x,
y: point.fallbackY,
z: point.z
},
point.fallbackY,
point.heightOffset
),
point.z
);
pushUv(point.x, point.z);
} }
for (let index = 0; index < JUNCTION_SEGMENTS; index += 1) { for (let index = 0; index < footprint.points.length; index += 1) {
const current = index + 1; const current = index + 1;
const next = index === JUNCTION_SEGMENTS - 1 ? 1 : current + 1; const next = index === footprint.points.length - 1 ? 1 : current + 1;
indices.push(0, current, next); indices.push(0, current, next);
} }