UNPKG

@xtctwins/tctwins-convert

Version:

JavaScript utilities to create .XTC files

292 lines (247 loc) 9.91 kB
#!/usr/bin/env node const commander = require("commander"); const npmPackage = require("./package.json"); const { convert2xtc, XTC_INFO } = require("./dist/tctwins-convert.cjs.js"); const fs = require("fs"); const defaultConfigs = require(`./convert2xtc.conf.js`); const WebIFC = require("web-ifc"); const path = require("path"); const { createValidator } = require("@typeonly/validator"); // const validator = createValidator({ // bundle: require("./types.to.json") // }); const program = new commander.Command(); program.version(npmPackage.version, "-v, --version"); program .option("-c, --configs [file]", "optional path to JSON configs file; overrides convert2xtc.conf.js") .option("-s, --source [file]", "path to source file") .option("-a, --sourcemanifest [file]", "path to source manifest file (for converting split file output from ifcgltf -s)") .option("-f, --format [string]", "source file format (optional); supported formats are gltf, ifc, laz, las, pcd, ply, stl and cityjson") .option("-m, --metamodel [file]", "path to source metamodel JSON file (optional)") .option("-i, --include [types]", "only convert these types (optional)") .option("-x, --exclude [types]", "never convert these types (optional)") .option("-r, --rotatex", "rotate model 90 degrees about X axis (for las and cityjson)") .option("-g, --disablegeoreuse", "disable geometry reuse (optional)") .option("-z, --minTileSize [number]", "minimum diagonal tile size (optional, default 500)") .option("-t, --disabletextures", "ignore textures (optional)") .option("-n, --disablenormals", "ignore normals (optional)") .option( "-o, --output [file]", "path to target .xtc file when -s option given, or JSON manifest for multiple .xtc files when source manifest file given with -a; creates directories on path automatically if not existing" ) .option("-l, --log", "enable logging (optional)"); program.on("--help", () => { console.log(`\n\XTC version: ${XTC_INFO.xtcVersion}`); }); program.parse(process.argv); const options = program.opts(); let configs = defaultConfigs; if (options.source === undefined && options.sourcemanifest === undefined) { console.error("[convert2xtc] [ERROR]: Please specify path to source file or manifest."); program.help(); process.exit(1); } if (options.source !== undefined && options.sourcemanifest !== undefined) { console.error("[convert2xtc] [ERROR]: Can't specify path to source file AND manifest - only one of these params allowed."); program.help(); process.exit(1); } if (options.output === undefined) { console.error("[convert2xtc] [ERROR]: Please specify target xtc file path."); program.help(); process.exit(1); } function log(msg) { if (options.log) { console.log(msg); } } function getFileExtension(fileName) { let ext = path.extname(fileName); if (ext.charAt(0) === ".") { ext = ext.substring(1); } return ext; } async function main() { // const { memoryUsage } = process; // let maxMemoryUsage = 0; // let lastMemoryUsage = getMemoryUsageInMB(); // function getMemoryUsageInMB() { // const memoryUsage = process.memoryUsage(); // return (memoryUsage.heapTotal / (1024 * 1024)).toFixed(2); // } // const intervalId = setInterval(() => { // const currentMemoryUsage = getMemoryUsageInMB(); // if (currentMemoryUsage > lastMemoryUsage) { // maxMemoryUsage = Math.max(maxMemoryUsage, currentMemoryUsage); // } // lastMemoryUsage = currentMemoryUsage; // }, 1000); let startTime = new Date().getTime(); log(`[convert2xtc] Running convert2xtc v${npmPackage.version}...`); if (options.configs !== undefined) { log(`[convert2xtc] Using JSON configs file: ${options.configs}`); try { let configsData = fs.readFileSync(options.configs); configs = JSON.parse(configsData); } catch (e) { console.error(`[convert2xtc] [ERROR]: Failed to load custom configs file (specified with -c or --configs) - ${e}`); process.exit(1); } } else { log(`[convert2xtc] Using configs in ./convert2xtc.conf.js`); } configs.sourceConfigs ||= {}; if (options.sourcemanifest) { log(`[convert2xtc] Converting glTF files in manifest ${options.sourcemanifest}...`); let manifestData = fs.readFileSync(options.sourcemanifest); let manifest = JSON.parse(manifestData); if (!manifest.gltfOutFiles) { console.error(`[convert2xtc] [ERROR]: Input manifest invalid - missing field: gltfOutFiles`); process.exit(1); } const numInputFiles = manifest.gltfOutFiles.length; if (numInputFiles === 0) { console.error(`[convert2xtc] [ERROR]: Input manifest invalid - gltfOutFiles is zero length`); process.exit(1); } const outputDir = path.dirname(options.output); if (outputDir !== "" && !fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } function formatDate(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); const hours = String(date.getHours()).padStart(2, "0"); const minutes = String(date.getMinutes()).padStart(2, "0"); const seconds = String(date.getSeconds()).padStart(2, "0"); return `${day}-${month}-${year}- ${hours}-${minutes}-${seconds}`; } const xtcManifest = { inputFile: options.sourcemanifest, converterApplication: "convert2xtc", converterApplicationVersion: `v${npmPackage.version}`, conversionDate: formatDate(new Date()), outputDir, xtcFiles: [] }; const sourceConfigs = configs.sourceConfigs || {}; const formatConfig = sourceConfigs["gltf"] || configs["glb"] || {}; const externalMetadata = !!formatConfig.externalMetadata; if (externalMetadata) { xtcManifest.metaModelFiles = []; for (let i = 0, len = manifest.metadataOutFiles.length; i < len; i++) { const metadataSource = manifest.metadataOutFiles[i]; xtcManifest.metaModelFiles.push(path.basename(metadataSource)); } } let i = 0; const convertNextFile = () => { const source = manifest.gltfOutFiles[i]; const metaModelSource = i < manifest.metadataOutFiles.length ? manifest.metadataOutFiles[i] : null; const outputFileName = getFileNameWithoutExtension(source); const outputFileNameXTC = `${outputFileName}.xtc`; const ext = getFileExtension(source); let modelAABB; // if (manifest.modelBoundsMin && manifest.modelBoundsMax) { // modelAABB= [ // manifest.modelBoundsMin[0], // manifest.modelBoundsMin[1], // manifest.modelBoundsMin[2], // manifest.modelBoundsMax[0], // manifest.modelBoundsMax[1], // manifest.modelBoundsMax[2] // ]; // } convert2xtc({ WebIFC, configs, source, format: ext, metaModelSource: !externalMetadata ? metaModelSource : null, modelAABB, output: path.join(outputDir, outputFileNameXTC), includeTypes: options.include ? options.include.slice(",") : null, excludeTypes: options.exclude ? options.exclude.slice(",") : null, rotateX: options.rotatex, reuseGeometries: options.disablegeoreuse !== true, minTileSize: options.minTileSize, includeTextures: !options.disabletextures, includeNormals: !options.disablenormals, log }) .then(() => { i++; log(`[convert2xtc] Converted model${i}.xtc (${i} of ${numInputFiles})`); xtcManifest.xtcFiles.push(outputFileNameXTC); if (i === numInputFiles) { fs.writeFileSync(options.output, JSON.stringify(xtcManifest)); log(`[convert2xtc] Done.`); process.exit(0); } else { convertNextFile(); } }) .catch((err) => { console.error(`[convert2xtc] [ERROR]: ${err}`); process.exit(1); }); }; convertNextFile(); } else { if (options.output) { const outputDir = path.dirname(options.output); if (outputDir !== "" && !fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } } log(`[convert2xtc] Converting single input file ${options.source}...`); convert2xtc({ WebIFC, configs, source: options.source, format: options.format, metaModelSource: options.metamodel, output: options.output, includeTypes: options.include ? options.include.slice(",") : null, excludeTypes: options.exclude ? options.exclude.slice(",") : null, rotateX: options.rotatex, reuseGeometries: options.disablegeoreuse !== true, minTileSize: options.minTileSize, includeTextures: !options.disabletextures, includeNormals: !options.disablenormals, log }) .then(() => { log(`[convert2xtc] Done.`); // clearInterval(intervalId); // // // 打印最大内存消耗 // log(`[MemoryUsage] 最大内存消耗: ${maxMemoryUsage} MB`); let endTime = new Date().getTime(); let executionTime = endTime - startTime; if (executionTime > 600 * 1000) console.log("整体加载时长: " + (executionTime / 1000 / 60).toFixed(2) + " m'"); else console.log("整体加载时长: " + (executionTime / 1000).toFixed(2) + " s'"); setTimeout(() => { process.exit(0); }, 500); }) .catch((err) => { console.error(`[convert2xtc] [ERROR]: ${err}`); let endTime = new Date().getTime(); let executionTime = endTime - startTime; console.log("整体加载时长: " + executionTime + " ms"); process.exit(1); }); } } function getFileNameWithoutExtension(filePath) { const baseName = path.basename(filePath); const fileNameWithoutExtension = path.parse(baseName).name; return fileNameWithoutExtension; } main().catch((err) => { console.error("Error:", err); process.exit(1); });