UNPKG

@mateusz.milian/xeokit-convert

Version:

JavaScript utilities to create .XKT files

264 lines (218 loc) 9.8 kB
#!/usr/bin/env node import { TextEncoder, TextDecoder } from 'node:util'; global.TextEncoder = TextEncoder; global.TextDecoder = TextDecoder; import '@loaders.gl/polyfills'; import { installFilePolyfills } from '@loaders.gl/polyfills'; installFilePolyfills(); import { Command } from 'commander'; import WebIFC from 'web-ifc/web-ifc-api-node.js'; import fs from 'node:fs'; import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path, { dirname, resolve } from 'node:path'; const __dirname = dirname(fileURLToPath(import.meta.url)); const npmPackage = JSON.parse( readFileSync(resolve(__dirname, '../package.json'), 'utf8') ); import { convert2xkt } from '../src/convert2xkt.js'; import {XKT_INFO} from "../src/XKT_INFO.js"; import defaultConfigs from './convert2xkt.conf.js'; const program = new Command(); program.version(npmPackage.version, '-v, --version'); program .option('-c, --configs [file]', 'optional path to JSON configs file; overrides convert2xkt.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('-u, --uncompressBuffers', 'do not compress buffers, otherwise output xkt V10 with compressed buffers (optional)') .option('-o, --output [file]', 'path to target .xkt file when -s option given, or JSON manifest for multiple .xkt 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\nXKT version: ${XKT_INFO.xktVersion}`); }); program.parse(process.argv); const options = program.opts(); let configs = defaultConfigs; if (options.source === undefined && options.sourcemanifest === undefined) { console.error('[convert2xkt] [ERROR]: Please specify path to source file or manifest.'); program.help(); process.exit(1); } if (options.source !== undefined && options.sourcemanifest !== undefined) { console.error('[convert2xkt] [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('[convert2xkt] [ERROR]: Please specify target xkt 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() { log(`[convert2xkt] Running convert2xkt v${npmPackage.version}...`); if (options.configs !== undefined) { log(`[convert2xkt] Using JSON configs file: ${options.configs}`); try { let configsData = fs.readFileSync(options.configs); configs = JSON.parse(configsData); } catch (e) { console.error(`[convert2xkt] [ERROR]: Failed to load custom configs file (specified with -c or --configs) - ${e}`); process.exit(1); } } else { log(`[convert2xkt] Using configs in ./convert2xkt.conf.js`); } configs.sourceConfigs ||= {}; if (options.sourcemanifest) { log(`[convert2xkt] Converting glTF files in manifest ${options.sourcemanifest}...`); let manifestData = fs.readFileSync(options.sourcemanifest); let manifest = JSON.parse(manifestData); if (!manifest.gltfOutFiles) { console.error(`[convert2xkt] [ERROR]: Input manifest invalid - missing field: gltfOutFiles`); process.exit(1); } const numInputFiles = manifest.gltfOutFiles.length; if (numInputFiles === 0) { console.error(`[convert2xkt] [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) { return date.toISOString(); } const xktManifest = { inputFile: options.sourcemanifest, converterApplication: "convert2xkt", converterApplicationVersion: `v${npmPackage.version}`, conversionDate: formatDate(new Date()), outputDir, xktFiles: [] }; const sourceConfigs = configs.sourceConfigs || {}; const formatConfig = sourceConfigs["gltf"] || configs["glb"] || {}; const externalMetadata = (!!formatConfig.externalMetadata); if (externalMetadata) { xktManifest.metaModelFiles = []; for (let i = 0, len = manifest.metadataOutFiles.length; i < len; i++) { const metadataSource = manifest.metadataOutFiles[i]; xktManifest.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 outputFileNameXKT = `${outputFileName}.xkt`; 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] // ]; // } convert2xkt({ WebIFC, configs, source, format: ext, metaModelSource: (!externalMetadata) ? metaModelSource : null, modelAABB, output: path.join(outputDir, outputFileNameXKT), 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, zip: !options.uncompressBuffers, log }).then(() => { i++; log(`[convert2xkt] Converted model${i}.xkt (${i} of ${numInputFiles})`); xktManifest.xktFiles.push(outputFileNameXKT); if (i === numInputFiles) { fs.writeFileSync(options.output, JSON.stringify(xktManifest)); log(`[convert2xkt] Done.`); process.exit(0); } else { convertNextFile(); } }).catch((err) => { console.error(`[convert2xkt] [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(`[convert2xkt] Converting single input file ${options.source}...`); convert2xkt({ 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, zip: !options.uncompressBuffers, log }).then(() => { log(`[convert2xkt] Done.`); process.exit(0); }).catch((err) => { console.error(`[convert2xkt] [ERROR]: ${err}`); 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); });