Add optional GLB compression stage
This commit is contained in:
@@ -44,6 +44,9 @@ if (stages.cesium) {
|
||||
if (stages.preview) {
|
||||
writeCesiumPreview(area);
|
||||
}
|
||||
if (stages.compress) {
|
||||
compressCesiumGlb(area);
|
||||
}
|
||||
|
||||
console.log("Done.");
|
||||
|
||||
@@ -82,6 +85,9 @@ function normalizeAreaConfig(raw) {
|
||||
const outputOverrides = raw.outputs || {};
|
||||
const areaDir = path.resolve(outputOverrides.areaDir || path.join(outputRoot, id));
|
||||
const fileStem = outputOverrides.fileStem || id;
|
||||
const compress = normalizeCompressConfig(raw.compress);
|
||||
const compressedFileStem = outputOverrides.compressedFileStem ||
|
||||
`${fileStem}-compressed-webp${compress.textureSize}${compress.meshopt ? "-meshopt" : ""}`;
|
||||
const outputs = {
|
||||
areaDir,
|
||||
geojsonDir: path.resolve(outputOverrides.geojsonDir || path.join(areaDir, "osm2streets_web_out")),
|
||||
@@ -95,6 +101,15 @@ function normalizeAreaConfig(raw) {
|
||||
cesiumPreview: path.resolve(
|
||||
outputOverrides.cesiumPreview || path.join(areaDir, `${fileStem}-cesium-preview.html`),
|
||||
),
|
||||
compressedGlb: path.resolve(
|
||||
outputOverrides.compressedGlb || path.join(areaDir, `${compressedFileStem}.glb`),
|
||||
),
|
||||
compressedMetadata: path.resolve(
|
||||
outputOverrides.compressedMetadata || path.join(areaDir, `${compressedFileStem}.json`),
|
||||
),
|
||||
compressedCesiumPreview: path.resolve(
|
||||
outputOverrides.compressedCesiumPreview || path.join(areaDir, `${compressedFileStem}-cesium-preview.html`),
|
||||
),
|
||||
vehicleRoute: path.resolve(outputOverrides.vehicleRoute || path.join(areaDir, `${fileStem}-vehicle-route.json`)),
|
||||
vehicleModel: path.resolve(outputOverrides.vehicleModel || path.join(areaDir, `${fileStem}-vehicle-car.gltf`)),
|
||||
pipelineDir: path.resolve(outputOverrides.pipelineDir || path.join(areaDir, "_pipeline")),
|
||||
@@ -112,6 +127,7 @@ function normalizeAreaConfig(raw) {
|
||||
cesium: raw.stages?.cesium ?? true,
|
||||
reimport: false,
|
||||
preview: false,
|
||||
compress: false,
|
||||
},
|
||||
qgis: {
|
||||
arrowScale: raw.qgis?.arrowScale ?? raw.arrowScale ?? 0.8,
|
||||
@@ -136,6 +152,7 @@ function normalizeAreaConfig(raw) {
|
||||
treeStyle: raw.blender?.treeStyle || "natural",
|
||||
officeOverrides: raw.blender?.officeOverrides || raw.blender?.office_overrides || "",
|
||||
},
|
||||
compress,
|
||||
outputs,
|
||||
};
|
||||
}
|
||||
@@ -147,6 +164,32 @@ function requireText(value, key) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function normalizeCompressConfig(raw) {
|
||||
const value = raw || {};
|
||||
return {
|
||||
textureSize: numberOption(value.textureSize, 768, "compress.textureSize", 64, 4096),
|
||||
quality: numberOption(value.quality, 82, "compress.quality", 1, 100),
|
||||
effort: numberOption(value.effort, 80, "compress.effort", 0, 100),
|
||||
meshopt: booleanOption(value.meshopt, false, "compress.meshopt"),
|
||||
};
|
||||
}
|
||||
|
||||
function numberOption(value, fallback, label, min, max) {
|
||||
const number = value === undefined ? fallback : Number(value);
|
||||
if (!Number.isFinite(number) || number < min || number > max) {
|
||||
throw new Error(`${label} must be a finite number in [${min}, ${max}]`);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
function booleanOption(value, fallback, label) {
|
||||
if (value === undefined) return fallback;
|
||||
if (typeof value === "boolean") return value;
|
||||
if (value === "true") return true;
|
||||
if (value === "false") return false;
|
||||
throw new Error(`${label} must be boolean`);
|
||||
}
|
||||
|
||||
function splitList(value) {
|
||||
return String(value)
|
||||
.split(",")
|
||||
@@ -158,6 +201,8 @@ function resolveStages(defaults, requested) {
|
||||
if (!requested) return defaults;
|
||||
// 'reimport' is deliberately absent from 'all': it is a recovery step for
|
||||
// hand-edited GeoPackages, never part of a full build.
|
||||
// 'compress' is also absent from 'all': it creates an alternate Cesium GLB,
|
||||
// not the baseline asset.
|
||||
const aliases = {
|
||||
all: ["intermediates", "blender", "cesium"],
|
||||
qgis: ["intermediates"],
|
||||
@@ -174,12 +219,22 @@ function resolveStages(defaults, requested) {
|
||||
preview: ["preview"],
|
||||
html: ["preview"],
|
||||
cesiumPreview: ["preview"],
|
||||
compress: ["compress"],
|
||||
compression: ["compress"],
|
||||
compressedCesium: ["compress"],
|
||||
};
|
||||
const out = {
|
||||
intermediates: false,
|
||||
reimport: false,
|
||||
blender: false,
|
||||
cesium: false,
|
||||
preview: false,
|
||||
compress: false,
|
||||
};
|
||||
const out = { intermediates: false, reimport: false, blender: false, cesium: false, preview: false };
|
||||
for (const stage of requested) {
|
||||
const mapped = aliases[stage];
|
||||
if (!mapped) {
|
||||
throw new Error(`Unknown stage '${stage}'. Use intermediates, reimport, blender, cesium, preview, or all.`);
|
||||
throw new Error(`Unknown stage '${stage}'. Use intermediates, reimport, blender, cesium, preview, compress, or all.`);
|
||||
}
|
||||
for (const key of mapped) out[key] = true;
|
||||
}
|
||||
@@ -288,6 +343,44 @@ function exportCesium(area) {
|
||||
writeCesiumPreview(area);
|
||||
}
|
||||
|
||||
function compressCesiumGlb(area) {
|
||||
ensureFile(area.outputs.glb, "Cesium GLB");
|
||||
ensureFile(area.outputs.metadata, "Cesium metadata");
|
||||
ensureFile(area.outputs.cesiumPreview, "Cesium preview");
|
||||
ensureFile(path.join(repoRoot, "scripts", "compress-glb.js"), "GLB compressor");
|
||||
fs.mkdirSync(path.dirname(area.outputs.compressedGlb), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(area.outputs.compressedMetadata), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(area.outputs.compressedCesiumPreview), { recursive: true });
|
||||
|
||||
const compressArgs = [
|
||||
path.join(repoRoot, "scripts", "compress-glb.js"),
|
||||
"--input",
|
||||
area.outputs.glb,
|
||||
"--output",
|
||||
area.outputs.compressedGlb,
|
||||
"--texture-size",
|
||||
String(area.compress.textureSize),
|
||||
"--quality",
|
||||
String(area.compress.quality),
|
||||
"--effort",
|
||||
String(area.compress.effort),
|
||||
"--metadata",
|
||||
area.outputs.metadata,
|
||||
"--metadata-output",
|
||||
area.outputs.compressedMetadata,
|
||||
"--preview",
|
||||
area.outputs.cesiumPreview,
|
||||
"--preview-output",
|
||||
area.outputs.compressedCesiumPreview,
|
||||
];
|
||||
if (area.compress.meshopt) {
|
||||
compressArgs.push("--meshopt");
|
||||
}
|
||||
|
||||
console.log("Stage: compress (Cesium GLB texture resize + WebP)");
|
||||
runCommand(process.execPath, compressArgs, "compress");
|
||||
}
|
||||
|
||||
function blenderExecutable(area) {
|
||||
return path.join(area.blenderApp, "Contents", "MacOS", "Blender");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user