Refactor road mesh generation to integrate junction edge seams

This commit is contained in:
2026-05-13 18:48:20 +02:00
parent e1db92f3b9
commit 074385ce78

View File

@@ -13,7 +13,10 @@ import {
sampleTerrainHeightAtWorldPosition, sampleTerrainHeightAtWorldPosition,
type Terrain type Terrain
} from "../document/terrains"; } from "../document/terrains";
import type { SplineCorridorPathClipInterval } from "../spline-corridor/spline-corridor-clips"; import type {
SplineCorridorPathClipInterval,
SplineCorridorRoadEdgeSeam
} from "../spline-corridor/spline-corridor-clips";
export interface SplineRoadPathPointLike { export interface SplineRoadPathPointLike {
id?: string; id?: string;
@@ -431,22 +434,18 @@ function isClipBoundaryDistance(
); );
} }
function expandClipIntervalsForRoadEdge(options: { function findRoadEdgeSeam(options: {
clipIntervals: readonly SplineCorridorPathClipInterval[]; distance: number;
edgeWidth: number; side: ScenePathRoadEdgeSide;
totalLength: number; seams: readonly SplineCorridorRoadEdgeSeam[];
}): SplineCorridorPathClipInterval[] { }): SplineCorridorRoadEdgeSeam | null {
if (options.clipIntervals.length === 0 || options.edgeWidth <= 0) { return (
return [...options.clipIntervals]; options.seams.find(
} (seam) =>
seam.side === options.side &&
const insetDistance = options.edgeWidth * 0.45; Math.abs(seam.distance - options.distance) <= 1e-6
) ?? null
return options.clipIntervals.map((clipInterval) => ({ );
junctionId: clipInterval.junctionId,
startDistance: Math.max(0, clipInterval.startDistance - insetDistance),
endDistance: Math.min(options.totalLength, clipInterval.endDistance + insetDistance)
}));
} }
export function buildSplineRoadMeshData(options: { export function buildSplineRoadMeshData(options: {
@@ -550,6 +549,7 @@ export function buildSplineRoadEdgeMeshData(options: {
side: ScenePathRoadEdgeSide; side: ScenePathRoadEdgeSide;
terrains?: readonly Terrain[]; terrains?: readonly Terrain[];
clipIntervals?: readonly SplineCorridorPathClipInterval[]; clipIntervals?: readonly SplineCorridorPathClipInterval[];
junctionEdgeSeams?: readonly SplineCorridorRoadEdgeSeam[];
}): SplineRoadEdgeMeshData | null { }): SplineRoadEdgeMeshData | null {
const { path, side } = options; const { path, side } = options;
const edge = path.road.edges[side]; const edge = path.road.edges[side];
@@ -580,11 +580,7 @@ export function buildSplineRoadEdgeMeshData(options: {
const uvs: number[] = []; const uvs: number[] = [];
const indices: number[] = []; const indices: number[] = [];
const profileLastIndex = Math.max(1, profile.length - 1); const profileLastIndex = Math.max(1, profile.length - 1);
const edgeClipIntervals = expandClipIntervalsForRoadEdge({ const edgeClipIntervals = options.clipIntervals ?? [];
clipIntervals: options.clipIntervals ?? [],
edgeWidth: edge.width,
totalLength: stations[stations.length - 1]?.distance ?? 0
});
const stationRuns = buildVisibleRoadStationRuns(stations, edgeClipIntervals); const stationRuns = buildVisibleRoadStationRuns(stations, edgeClipIntervals);
let stationCount = 0; let stationCount = 0;
@@ -604,14 +600,40 @@ export function buildSplineRoadEdgeMeshData(options: {
y: 0, y: 0,
z: tangent.x z: tangent.x
}; };
const sideSign = side === "left" ? 1 : -1;
const innerOffset = sideSign * path.road.width * 0.5;
const seam = findRoadEdgeSeam({
distance: station.distance,
side,
seams: options.junctionEdgeSeams ?? []
});
const seamInnerPoint =
seam === null
? null
: addVec3(station.center, {
x: leftDirection.x * innerOffset,
y: 0,
z: leftDirection.z * innerOffset
});
for (let profileIndex = 0; profileIndex < profile.length; profileIndex += 1) { for (let profileIndex = 0; profileIndex < profile.length; profileIndex += 1) {
const profilePoint = profile[profileIndex]!; const profilePoint = profile[profileIndex]!;
const edgePoint = addVec3(station.center, { const profileOffsetRatio =
x: leftDirection.x * profilePoint.offset, edge.width <= 1e-8
y: 0, ? 0
z: leftDirection.z * profilePoint.offset : (profilePoint.offset - innerOffset) / (sideSign * edge.width);
}); const edgePoint =
seam === null || seamInnerPoint === null
? addVec3(station.center, {
x: leftDirection.x * profilePoint.offset,
y: 0,
z: leftDirection.z * profilePoint.offset
})
: {
x: seamInnerPoint.x + seam.outerOffset.x * profileOffsetRatio,
y: seamInnerPoint.y,
z: seamInnerPoint.z + seam.outerOffset.z * profileOffsetRatio
};
const vertex = resolveRoadVertexPosition({ const vertex = resolveRoadVertexPosition({
path, path,
terrains, terrains,