CATIA文件的网页预览方案

内容分享9小时前发布
0 0 0

CATIA文件的网页预览方案

背景

CATIA是工业设计中广泛使用的专业软件,产出
.CATPart

.CATProduct
等文件类型,当资产数量较大时,我们希望快捷预览这些文件,就有了
CATPart
文件网页预览的需求

但我们知道,网页上无法直接渲染
CATPart
这种复杂的文件,

CATIA生成的文件(如
CATPart

CATProduct
)不仅包含三维几何信息(例如点、线和曲面),还涵盖了丰富的元数据与专业设计特征。由于这些文件采用达索系统的私有格式,其详细结构并未公开,因此无法像常见图像格式(如 JPEG、PNG)或开放三维格式(如 glTF)那样被浏览器直接解析和渲染。

那有没有办法,能实现CATIA文件在网页上的便捷预览?

研究方案

尝试一

借助CATIA软件的能力,开发CATIA客户端插件,是否可以直接处理成网页可直接预览的网格模型?然后自动上传至网站

经过研究,有下面这几个问题:

CATIA二次开发方式

Automation API:支持使用VBA等语言进行基本自动化操作,可实现整体格式转换,但功能有限,难以实现细节控制(如网格减面、优化等定制处理);CAA(Component Application Architecture) :提供更深度的开发能力,支持转换与网格减面等高级操作,但搭建及学习成本很高,需要深厚的C++和COM技术背景。

即便通过上述方式成功转换,CATIA本身支持的导出格式(如STEP、IGES等)仍无法直接在网页中高效渲染,通常需进一步处理才能适配Web环境

所以这个方案行不通

尝试二

既然
CATPart
格式无法在网页上渲染,那能不能用什么方法,将
CATPart
格式转换成浏览器能直接预览的格式,比如转成
gltf

然而,CATPart作为达索系统的私有格式,其规范并未公开,这就意味着很难写程序直接将
CATPart
转换成
gltf

在这种情况下,通常需要借助CATIA软件本身的能力,将
CATPart
转成STL或STP中间格式,再根据中间格式,转成GLTF这种可以直接网页预览的格式

STL格式:转换后通常会将整个模型合并为单一网格,且仅包含顶点和法线信息,无法保留颜色、材质等元数据,导致视觉信息丢失。STEP(STP)格式:是一种国际标准(ISO 10303),用于在不同CAD系统之间无损交换三维模型数据。相较于STL,STEP能够保留模型中的独立几何体、装配结构及颜色信息,更适用于后续的glTF转换

所以转STP这个中间格式会更适合一点

此外,由于CATIA生成的工业模型通常结构复杂、面数极高,直接转换得到的glTF文件可能在网页中渲染性能较差,最好能想办法给GLTF减面

总结预览方案:


CATPart
文件通过CATIA转成stp中间格式stp转gltpgltf减面网页渲染

程序实现

CATIA转STP

实现一(有缺陷)

用Python脚本,调用CATIA开放的COM API,实现自动化转换

catia2stp.py


import sys
import win32com.client


# 获取参数
input_path = sys.argv[1]
output_path = sys.argv[2]

print(f"输入文件: {input_path}")
print(f"输出文件: {output_path}")

catia = win32com.client.Dispatch('catia.application')
# 获取Application 的Documents 对象
docs = catia.documents
partDocument = docs.open(input_path)
print(f"文件已打开")

partDocument.exportData(output_path, 'stp')
print(f"文件已导出")

partDocument.close()


123456789101112131415161718192021

执行以下命令即可


python catia2stp.py "./demo1.CATPart" "./demo1.stp"

1

但这种自动化方式,会与用户界面进行交互

比如
exportData
导出时会显示进度窗口


open
打开一个错误的文件时,会有错误弹窗,需要用户操作,关闭这个弹窗,才会继续执行;这样就大大影响了文件的转换效率

实现二

CATIA提供了批处理功能,可以完全后台化,无GUI弹窗交互

CATIA的批处理管理器

CATIA文件的网页预览方案

CATIA文件的网页预览方案

转换成STP可以用到这个批处理

关于如何使用批处理,可以参考这个文档:使用批处理监视器运行批处理 — Running Batches Using the Batch Monitor

了解完批处理的使用后,我们知道,CATIA用命令行方式执行批处理任务,需要构建xml配置文件

我们先拿一个标准的CATIA批处理xml作为模板,每次转换时,根据这个模板修改部分参数,就得到了作为这次转换的xml配置

然后执行这条命令即可


"CATBatchStarter.exe" -input ".catiaToStp.xml" -output "指定日志输出目录"

1

程序化转STP实现:


private boolean convert(String inputPath) throws Exception {
        boolean success = false;
        Path path = Paths.get(inputPath);
        Path parent = path.getParent();
        String workDirStr = parent.toString();
        // 构建catia转化的xml配置
        String convertXmlPath = buildConvertXml(inputPath, workDirStr);
        // 启动catia批处理
        String command = String.format(""%s" -input "%s" -output "%s"", CATBatchStarterPath, convertXmlPath, workDirStr);
		DefaultExecutor exec = new DefaultExecutor();
        exec.execute(command);
        return success;
    }

    /**
     * 构建catia转换的xml配置
     */
    private String buildConvertXml(String inputFilePath, String workDirStr) throws Exception {
		// 标准xml配置作为模板
        String templatePath = "./template.xml";
        // 创建文档构建器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        factory.setFeature("http://xml.org/sax/features/validation", false);

        DocumentBuilder builder = factory.newDocumentBuilder();

        // 设置EntityResolver忽略外部DTD
        builder.setEntityResolver((publicId, systemId) -> new InputSource(new StringReader("")));

        // 使用类加载器获取资源流
        InputStream inputStream = CatiaConvertExecutor.class.getClassLoader().getResourceAsStream(templatePath);
        if (inputStream == null) {
            throw new FileNotFoundException("The template file cannot be found: " + templatePath);
        }

        // 解析输入XML文件
        Document document = builder.parse(inputStream);

        // 修改文件路径和输出目录
        updateXmlPaths(document, inputFilePath, workDirStr);
        Path outputXmlPath = Paths.get(workDirStr, "catia2Stp.xml");

        // 保存修改后的XML(保持原格式)
        saveXmlDocument(document, outputXmlPath.toString());

        inputStream.close();
        return outputXmlPath.toString();
    }

    /**
     * 更新XML中的路径信息
     */
    private void updateXmlPaths(Document document, String inputFilePath, String workDirStr) {
        // 获取inputParameters中的file元素
        NodeList fileNodes = document.getElementsByTagName("file");
        for (int i = 0; i < fileNodes.getLength(); i++) {
            Element fileElement = (Element) fileNodes.item(i);
            if ("FileToProcess".equals(fileElement.getAttribute("id"))) {
                // 修改filePath属性
                fileElement.setAttribute("filePath", inputFilePath);
                System.out.println("更新文件路径: " + inputFilePath);
                break;
            }
        }

        // 获取outputParameters中的folder元素
        NodeList folderNodes = document.getElementsByTagName("folder");
        for (int i = 0; i < folderNodes.getLength(); i++) {
            Element folderElement = (Element) folderNodes.item(i);
            if ("OutputFolder".equals(folderElement.getAttribute("id"))) {
                // 修改destination和folderPath属性
                folderElement.setAttribute("destination", workDirStr);
                folderElement.setAttribute("folderPath", workDirStr);
                System.out.println("更新输出目录: " + workDirStr);
                break;
            }
        }
    }

    /**
     * 保存XML文档到文件
     */
    private void saveXmlDocument(Document document, String outputXmlPath) throws Exception {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();

        // 设置输出属性,保持与原XML完全一致
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "Parameters.dtd"); // 保持DOCTYPE声明
        transformer.setOutputProperty(OutputKeys.STANDALONE, "no"); // 移除standalone属性
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        // 禁用输出属性重新排序
        try {
            transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2");
        } catch (Exception e) {
            // 忽略此属性设置失败
        }

        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(new File(outputXmlPath));

        transformer.transform(source, result);
    }


123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107

STP转GLTF

有一个开源工具Mayo,可以支持命令行将stp文件转gltf

Releases · fougue/mayo

执行以下命令:


mayo-conv.exe ".demo1.stp" --export ".demo1.glb"

1

GLTF减面

借用Blender工具的Decimate修改器

写一个python脚本就行


def clear_scene():
    """清空场景中的所有对象"""
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete(use_global=False)

def import_gltf(file_path):
    """导入GLTF文件"""
    try:
        # 检查文件是否存在
        if not os.path.exists(file_path):
            print(f"The file does not exist: {file_path}")
            return False

        bpy.ops.import_scene.gltf(filepath=file_path)
        return True
    except Exception as e:
        print(f"Failed to import the GLTF file: {e}")
        return False


def decimate_mesh(ratio=0.5):
    """减面处理"""
    # 选择所有网格对象
    mesh_objects = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH']

    for obj in mesh_objects:
        # 确保对象被选中
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)

        # 添加减面修改器
        decimate = obj.modifiers.new(name="Decimate", type='DECIMATE')
        decimate.ratio = ratio

        # 应用修改器
        bpy.ops.object.modifier_apply(modifier="Decimate")

def export_gltf(output_path):
    """导出GLTF文件"""
    try:
        bpy.ops.export_scene.gltf(
            filepath=output_path,
            export_format='GLB',
        )
        return True
    except Exception as e:
        print(f"Failed to export the GLTF file: {e}")
        return False

# 按装订区域中的绿色按钮以运行脚本。
if __name__ == '__main__':
    # 获取参数(跳过Blender自己的参数)
    args = sys.argv[sys.argv.index("--") + 1:]
    input_gltf = args[0]
    output_gltf = args[1]
    decimate_ratio = float(args[2]) if len(args) > 2 else 0.5
    # 清空场景
    clear_scene()
    # 导入GLB
    if not import_gltf(input_gltf):
        sys.exit(1)

    # 解除共享 mesh
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', object=True, obdata=True)

    # 减面处理
    decimate_mesh(decimate_ratio)

    # 导出GLB
    if not export_gltf(output_gltf):
        sys.exit(1)


123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172

执行以下命令


"blender.exe" -b -P ".gltfLoseFace.py" -- ".demo1.gltf" ".demo1-out.gltf" 0.2

1

然后转出来的GLTF文件,就能在网页上渲染了,主流wegl库都行(threejs、Babylon.js)

实现效果

CATIA打开显示 最终GLTF预览
CATIA文件的网页预览方案 CATIA文件的网页预览方案

考虑到网页预览对视觉效果的要求不高,且需兼顾浏览器渲染性能,我们通过适当降低显示精度,成功实现了CATIA文件在网页中的流畅预览。

© 版权声明

相关文章

暂无评论

none
暂无评论...