How to decrease mesh number while STEP to GLB file conversion?


I am currently using Opencascade.js for converting STEP files to GLB format. But in a web viewer mesh number and draw calls are too high.

How can I reduce mesh number? I guess I need to change paramters of BRepMesh_IncrementalMesh_2 but I couldnt find the correct way.

I referenced this code:

You cand find the code snippet below.

Thank you very much.

import fs from 'fs';
import path from 'path';
import initOpenCascade from 'opencascade.js/dist/node.js';

const loadSTEPorIGES = async (openCascade, file, fileName) => {

    const fileText = fs.readFileSync(file.path, 'utf8');

    const fileType = (() => {
        switch (fileName.toLowerCase().split(".").pop()) {
            case "step":
            case "stp":
                return "step";
            case "iges":
            case "igs":
                return "iges";
                return undefined;
    // Writes the uploaded file to Emscripten's Virtual Filesystem
    openCascade.FS.createDataFile("/", `file.${fileType}`, fileText, true, true);

    // Choose the correct OpenCascade file parsers to read the CAD file
    var reader = null;
    if (fileType === "step") {
        reader = new openCascade.STEPCAFControl_Reader_1();
    } else if (fileType === "iges") {
        reader = new openCascade.IGESCAFControl_Reader_1();
    } else { console.error("opencascade.js can't parse this extension! (yet)"); }

    const readResult = reader.ReadFile(`file.${fileType}`);            // Read the file

    if (readResult === openCascade.IFSelect_ReturnStatus.IFSelect_RetDone) {
        console.log("file loaded successfully!     Converting to OCC now...");
        //const numRootsTransferred = reader.TransferRoots(new openCascade.Message_ProgressRange_1());    // Translate all transferable roots to OpenCascade

        // Create a document add our shapes
        const doc = new openCascade.TDocStd_Document(new openCascade.TCollection_ExtendedString_1());

        if (fileType === "step") {
            if (!reader.Transfer_1(new openCascade.Handle_TDocStd_Document_2(doc), new openCascade.Message_ProgressRange_1())) throw new Error();
        } else {
            if (!reader.Transfer(new openCascade.Handle_TDocStd_Document_2(doc), new openCascade.Message_ProgressRange_1())) throw new Error();


        // Obtain the results of translation in one OCCT shape
        console.log(fileName + " converted successfully!  Triangulating now...");

        return visualizeShapes(openCascade, doc, fileName, fileType);

    } else {
        console.error("Something in OCCT went wrong trying to read " + fileName);


function visualizeDoc(oc, doc, fileName, fileType) {

    const justName = path.parse(fileName).name;
    // Export a GLB file (this will also perform the meshing)
    const cafWriter = new oc.RWGltf_CafWriter(new oc.TCollection_AsciiString_2(`./${justName}.glb`), true);
    cafWriter.Perform_2(new oc.Handle_TDocStd_Document_2(doc), new oc.TColStd_IndexedDataMapOfStringString_1(), new oc.Message_ProgressRange_1());

    // Out with the old, in with the new!
    console.log(fileName + " triangulated and added to the scene!");

    // Remove the file when we're done (otherwise we run into errors on reupload)
    // Read the GLB file from the virtual file system
    const glbFile = oc.FS.readFile(`./${justName}.glb`, { encoding: "binary" });
    const fileObj = { file: glbFile, filename: fileName };
    return fileObj;

function visualizeShapes(oc, doc, fileName, fileType) {

    const aRootLabels = new oc.TDF_LabelSequence_1();
    const aCompound = new oc.TopoDS_Compound();
    const shapeTool = oc.XCAFDoc_DocumentTool.ShapeTool(doc.Main()).get();


    const BRepBuilder = new oc.BRep_Builder();


    for (let i = 1; i <= aRootLabels.Length(); i++) {

        const aRootShape = new oc.TopoDS_Shape();
        const aRootLabel = aRootLabels.Value(i);

        if (oc.XCAFDoc_ShapeTool.GetShape_1(aRootLabel, aRootShape)) {
            BRepBuilder.Add(aCompound, aRootShape);


    const aDrawer = new oc.Prs3d_Drawer();

    const BoundBox = new oc.Bnd_Box_1();

    oc.BRepBndLib.Add(aCompound, BoundBox, false);

    const anAlgo = new oc.BRepMesh_IncrementalMesh_2(aCompound, oc.Prs3d.GetDeflection_2(BoundBox, aDrawer.MaximalChordialDeviation(), aDrawer.DeviationCoefficient()), false, (20.0 * Math.PI / 180.0), true);

    anAlgo.Perform_1(new oc.Message_ProgressRange_1());

    // Return our visualized document
    return visualizeDoc(oc, doc, fileName, fileType);

const convertSTEPorIGES = async (file) => {

    const openCascade = await initOpenCascade();

    return loadSTEPorIGES(openCascade, file, file.filename);


export default convertSTEPorIGES;
Kirill Gavrilov's picture

Number of meshes, draw calls, and parameters of BRepMesh_IncrementalMesh are three subjects that have no overlapping:

  • BRepMesh_IncrementalMesh parameters affect overall number of triangles
    • Meshing parameters are important to reach desired visual quality of the mesh and balance model size.
    • BRepMesh_IncrementalMesh preserves topological information, so that overall details might be higher than necessary in some cases.
    • Mesh decimation algorithms might further reduce number of triangles, but may cause visual artifacts.
  • Number of mesh objects depends on complexity of assembly (number of top-level shape) and topology (number of TopoDS_Face within top-level shape).
    • RWGltf_CafWriter::ToMergeFaces() might merge faces within root shapes. This might also reduce glTF file size, but topological information will be partially lost.
    • More complex processing like model simplification requires advanced tools (like CAD Processor).
  • Number of draw calls depends on graphics engine and how application uses it.
    • The most simple engines would draw each "primitives" in glTF structure individually;
    • lightly optimized engines like XCAFPrs_AISObject presentation in OCCT would automatically combine "primitives" sharing the same material within one displayed object;
    • advanced engine might perform more work to further improve rendering performance.
omerization's picture

Hello Kirill,

First of all thank you for your detailed answer.

I am using Microsoft's BabylonJs as web engine. This is the aim of this STEP2GLB conversion. Mostly users upload assembly files and I use transform nodes of parts and attach gizmos to parts. So they can move the parts of assembly.

So I need speed more than visual quality.

I am trying to merging all child meshes of parts while importing to BabylonJs. But it would be way better finding a way while writing file with OCC.

Kirill Gavrilov's picture

As I have mentioned, you may try option RWGltf_CafWriter::ToMergeFaces() at first and see if it makes any difference in case of your web engine.

omerization's picture

Yes I tried to try it first after your message. I just couldn't set ToMergeFaces to true in OpenCascadeJS. :(

I will let you know the result if i can.

Lee Joe's picture

Hello,Taking this topic to ask a question, I have a Step file, which contains 4 parts. After importing and exporting into a Gltf file, they are merged into one. I don't know if you encounter this problem again....