Converts particle physics detector geometries from ROOT files to the glTF format used by Phoenix.
It reads a ROOT geometry file, filters and splits it into named subparts according to a config file, deduplicates redundant mesh and material data, and writes out a single .gltf file ready to load in Phoenix.
This project is based on root_cern-To_gltf-Exporter, a browser-based ROOT to glTF converter developed by Sebastien Ponce.
npm installnpm run buildnode bin/cli.js -i <input.root> -c <config.json> [-o <output.gltf>]| Flag | Description |
|---|---|
-i, --input-file |
Required path to the input ROOT file |
-c, --config-file |
Required path to the detector config file |
-o, --output-file |
Optional path for the output glTF file (which defaults to <input-name>.gltf) |
Example:
node bin/cli.js -i CLD_o4_v05.root -c CLD_o4_v05.config.json -o CLD.gltfYou can also call the converter in code. But file I/O is your responsibility — pass an already-opened ROOT file and a config object:
import { writeFile } from "node:fs/promises";
import { openFile } from "jsroot";
import root2gltf from "root2gltf";
const input = await openFile("CLD_o4_v05.root");
const config = {
childrenToHide: [],
maxLevel: 3,
subParts: { "Beam Pipe": ["BeBeampipe_assembly_0"] },
};
const gltfContent = await root2gltf({ input, config });
await writeFile("CLD.gltf", JSON.stringify(gltfContent), "utf8");Each detector needs a custom JSON config file. Here is what the fields do:
| Field | Description |
|---|---|
maxLevel |
How many levels deep to traverse the geometry tree. Higher values produce more detail but larger files. |
childrenToHide |
List of node names to remove before processing. |
subParts |
Maps a display name to a list of volume names. Each entry becomes a separate scene in the glTF file. |
Ready-to-use configs for several FCC-ee detector concepts are in configs/.
{
"childrenToHide": [],
"subParts": {
"Beam Pipe": [
"BeBeampipe_assembly_0",
"BeamPipe_assembly_1",
"SynchRadMask_assembly_2",
"BeamPipeShield_assembly_3",
"BeamPipeShield_noRot_assembly_4"
],
"Screen Solenoid": ["CompSol_assembly_5", "ScreenSol_assembly_6"],
"LumiCal": [
"LumiCal_envelope_7",
"LumiCalInstrumentation_envelope_8",
"LumiCalCooling_envelope_9",
"LumiCalBackShield_envelope_10"
],
"Vertex": ["Vertex_11"],
"STT": ["STT_o1_v01_envelope_12"],
"Silicon Wrapper": ["SiWrB_envelope_13", "SiWrD_envelope_14"],
"ECal": ["ECalBarrel_vol_15"],
"HCal": ["HCalEnvelopeVolume_16"],
"ECal Endcap": ["ECalEndcaps_turbine_17"],
"HCal Endcap": ["HCalThreePartsEndcap_volume_18"],
"Endcap": ["Barrel_assembly_19", "Endcaps_assembly_20"]
},
"maxLevel": 3
}root2gltf/
├── bin/
│ └── cli.js # CLI entry point (argument parsing)
├── src/
│ ├── index.ts # Main conversion logic
│ ├── handleInput.ts # ROOT tree traversal and filtering helpers
│ ├── handleOutput.ts # glTF deduplication (materials and meshes)
│ └── lib/
│ ├── constants.ts # Build options and geometry settings
│ ├── polyfill.ts # FileReader polyfill for Node.js
│ └── types/ # TypeScript type definitions
└── dist/ # Compiled JavaScript output (generated by tsc)