Implement support for clipping road mesh geometry using visible station runs

This commit is contained in:
2026-05-13 13:18:39 +02:00
parent 1898298472
commit 5a0f9f1245

View File

@@ -415,6 +415,7 @@ function addEdgeProfileCaps(options: {
export function buildSplineRoadMeshData(options: { export function buildSplineRoadMeshData(options: {
path: SplineRoadPathLike; path: SplineRoadPathLike;
terrains?: readonly Terrain[]; terrains?: readonly Terrain[];
clipIntervals?: readonly SplineCorridorPathClipInterval[];
}): SplineRoadMeshData | null { }): SplineRoadMeshData | null {
const { path } = options; const { path } = options;
const terrains = options.terrains ?? []; const terrains = options.terrains ?? [];
@@ -428,44 +429,59 @@ export function buildSplineRoadMeshData(options: {
const uvs: number[] = []; const uvs: number[] = [];
const indices: number[] = []; const indices: number[] = [];
const halfWidth = path.road.width * 0.5; const halfWidth = path.road.width * 0.5;
const stationRuns = buildVisibleRoadStationRuns(
stations,
options.clipIntervals ?? []
);
let stationCount = 0;
for (let index = 0; index < stations.length; index += 1) { for (const run of stationRuns) {
const station = stations[index]!; const runVertexOffset = positions.length / 3;
const tangent = resolveStationTangent(stations, index);
if (tangent === null) { for (let index = 0; index < run.length; index += 1) {
return null; const station = run[index]!;
const tangent = resolveStationTangent(run, index);
if (tangent === null) {
return null;
}
const perpendicular = {
x: -tangent.z * halfWidth,
y: 0,
z: tangent.x * halfWidth
};
const left = resolveRoadVertexPosition({
path,
terrains,
point: addVec3(station.center, perpendicular),
fallbackY: station.center.y
});
const right = resolveRoadVertexPosition({
path,
terrains,
point: subtractVec3(station.center, perpendicular),
fallbackY: station.center.y
});
positions.push(left.x, left.y, left.z, right.x, right.y, right.z);
uvs.push(0, station.distance, 1, station.distance);
} }
const perpendicular = { for (let index = 0; index < run.length - 1; index += 1) {
x: -tangent.z * halfWidth, const left = runVertexOffset + index * 2;
y: 0, const right = left + 1;
z: tangent.x * halfWidth const nextLeft = left + 2;
}; const nextRight = left + 3;
const left = resolveRoadVertexPosition({
path,
terrains,
point: addVec3(station.center, perpendicular),
fallbackY: station.center.y
});
const right = resolveRoadVertexPosition({
path,
terrains,
point: subtractVec3(station.center, perpendicular),
fallbackY: station.center.y
});
positions.push(left.x, left.y, left.z, right.x, right.y, right.z); indices.push(left, right, nextLeft, nextLeft, right, nextRight);
uvs.push(0, station.distance, 1, station.distance); }
stationCount += run.length;
} }
for (let index = 0; index < stations.length - 1; index += 1) { if (positions.length === 0) {
const left = index * 2; return null;
const right = left + 1;
const nextLeft = left + 2;
const nextRight = left + 3;
indices.push(left, right, nextLeft, nextLeft, right, nextRight);
} }
return { return {
@@ -473,7 +489,7 @@ export function buildSplineRoadMeshData(options: {
positions: new Float32Array(positions), positions: new Float32Array(positions),
uvs: new Float32Array(uvs), uvs: new Float32Array(uvs),
indices: new Uint32Array(indices), indices: new Uint32Array(indices),
stationCount: stations.length, stationCount,
totalLength: stations[stations.length - 1]?.distance ?? 0 totalLength: stations[stations.length - 1]?.distance ?? 0
}; };
} }
@@ -481,6 +497,7 @@ export function buildSplineRoadMeshData(options: {
export function buildSplineRoadMeshGeometry(options: { export function buildSplineRoadMeshGeometry(options: {
path: SplineRoadPathLike; path: SplineRoadPathLike;
terrains?: readonly Terrain[]; terrains?: readonly Terrain[];
clipIntervals?: readonly SplineCorridorPathClipInterval[];
}): BufferGeometry | null { }): BufferGeometry | null {
const meshData = buildSplineRoadMeshData(options); const meshData = buildSplineRoadMeshData(options);