89 lines
3.3 KiB
JavaScript
89 lines
3.3 KiB
JavaScript
import { createHash } from "node:crypto";
|
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
import { getPositionsAndIndices } from "@recast-navigation/three";
|
|
|
|
const EXPECTED_GENERATOR_VERSION = "0.43.1";
|
|
const TILED_WATERSHED_CALL =
|
|
"buildRegions(buildContext, compactHeightfield, tileConfig.borderSize, tileConfig.minRegionArea, tileConfig.mergeRegionArea)";
|
|
const TILED_MONOTONE_CALL =
|
|
"buildRegionsMonotone(buildContext, compactHeightfield, tileConfig.borderSize, tileConfig.minRegionArea, tileConfig.mergeRegionArea)";
|
|
|
|
let generatorPromise;
|
|
|
|
function sha256(value) {
|
|
return createHash("sha256").update(value).digest("hex");
|
|
}
|
|
|
|
async function loadPatchedGenerator() {
|
|
const packageFile = fileURLToPath(import.meta.resolve("@recast-navigation/generators/package.json"));
|
|
const packageJson = JSON.parse(await readFile(packageFile, "utf8"));
|
|
if (packageJson.version !== EXPECTED_GENERATOR_VERSION) {
|
|
throw new Error(
|
|
`Monotone Recast fallback expects @recast-navigation/generators ${EXPECTED_GENERATOR_VERSION}, `
|
|
+ `but found ${packageJson.version}.`,
|
|
);
|
|
}
|
|
|
|
const entryFile = fileURLToPath(import.meta.resolve("@recast-navigation/generators"));
|
|
const upstream = await readFile(entryFile, "utf8");
|
|
if (!upstream.includes("buildRegions, allocContourSet")) {
|
|
throw new Error("Monotone Recast fallback could not locate the pinned generator import.");
|
|
}
|
|
const watershedCallCount = upstream.split(TILED_WATERSHED_CALL).length - 1;
|
|
if (watershedCallCount !== 1) {
|
|
throw new Error(
|
|
`Monotone Recast fallback expected one tiled watershed call, found ${watershedCallCount}.`,
|
|
);
|
|
}
|
|
|
|
const patched = upstream
|
|
.replace("buildRegions, allocContourSet", "buildRegions, buildRegionsMonotone, allocContourSet")
|
|
.replace(TILED_WATERSHED_CALL, TILED_MONOTONE_CALL)
|
|
.replace(/\n\/\/# sourceMappingURL=.*$/u, "");
|
|
const sourceChecksum = sha256(upstream);
|
|
const generatedDirectory = path.resolve(
|
|
"dungeon-pipeline/work/.generated-recast",
|
|
);
|
|
const generatedFile = path.join(
|
|
generatedDirectory,
|
|
`generators-${EXPECTED_GENERATOR_VERSION}-monotone-${sourceChecksum.slice(0, 12)}.mjs`,
|
|
);
|
|
await mkdir(generatedDirectory, { recursive: true });
|
|
await writeFile(generatedFile, patched, "utf8");
|
|
const module = await import(`${pathToFileURL(generatedFile).href}?source=${sourceChecksum}`);
|
|
return {
|
|
generateTiledNavMesh: module.generateTiledNavMesh,
|
|
provenance: {
|
|
package: `@recast-navigation/generators@${EXPECTED_GENERATOR_VERSION}`,
|
|
upstreamEntry: path.relative(process.cwd(), entryFile).replace(/\\/g, "/"),
|
|
upstreamChecksum: sourceChecksum,
|
|
operation: "replace tiled buildRegions watershed call with buildRegionsMonotone",
|
|
},
|
|
};
|
|
}
|
|
|
|
async function monotoneGenerator() {
|
|
generatorPromise ??= loadPatchedGenerator();
|
|
return generatorPromise;
|
|
}
|
|
|
|
export async function threeToMonotoneTiledNavMesh(
|
|
meshes,
|
|
navMeshGeneratorConfig = {},
|
|
keepIntermediates = false,
|
|
) {
|
|
const [positions, indices] = getPositionsAndIndices(meshes);
|
|
const generator = await monotoneGenerator();
|
|
return {
|
|
result: generator.generateTiledNavMesh(
|
|
positions,
|
|
indices,
|
|
navMeshGeneratorConfig,
|
|
keepIntermediates,
|
|
),
|
|
provenance: generator.provenance,
|
|
};
|
|
}
|