CATIA文件的网页预览方案
背景
CATIA是工业设计中广泛使用的专业软件,产出
或
.CATPart
等文件类型,当资产数量较大时,我们希望快捷预览这些文件,就有了
.CATProduct
文件网页预览的需求
CATPart
但我们知道,网页上无法直接渲染
这种复杂的文件,
CATPart
CATIA生成的文件(如
和
CATPart
)不仅包含三维几何信息(例如点、线和曲面),还涵盖了丰富的元数据与专业设计特征。由于这些文件采用达索系统的私有格式,其详细结构并未公开,因此无法像常见图像格式(如 JPEG、PNG)或开放三维格式(如 glTF)那样被浏览器直接解析和渲染。
CATProduct
那有没有办法,能实现CATIA文件在网页上的便捷预览?
研究方案
尝试一
借助CATIA软件的能力,开发CATIA客户端插件,是否可以直接处理成网页可直接预览的网格模型?然后自动上传至网站
经过研究,有下面这几个问题:
CATIA二次开发方式
Automation API:支持使用VBA等语言进行基本自动化操作,可实现整体格式转换,但功能有限,难以实现细节控制(如网格减面、优化等定制处理);CAA(Component Application Architecture) :提供更深度的开发能力,支持转换与网格减面等高级操作,但搭建及学习成本很高,需要深厚的C++和COM技术背景。
即便通过上述方式成功转换,CATIA本身支持的导出格式(如STEP、IGES等)仍无法直接在网页中高效渲染,通常需进一步处理才能适配Web环境
所以这个方案行不通
尝试二
既然
格式无法在网页上渲染,那能不能用什么方法,将
CATPart
格式转换成浏览器能直接预览的格式,比如转成
CATPart
gltf
然而,CATPart作为达索系统的私有格式,其规范并未公开,这就意味着很难写程序直接将
转换成
CATPart
gltf
在这种情况下,通常需要借助CATIA软件本身的能力,将
转成STL或STP中间格式,再根据中间格式,转成GLTF这种可以直接网页预览的格式
CATPart
STL格式:转换后通常会将整个模型合并为单一网格,且仅包含顶点和法线信息,无法保留颜色、材质等元数据,导致视觉信息丢失。STEP(STP)格式:是一种国际标准(ISO 10303),用于在不同CAD系统之间无损交换三维模型数据。相较于STL,STEP能够保留模型中的独立几何体、装配结构及颜色信息,更适用于后续的glTF转换。
所以转STP这个中间格式会更适合一点
此外,由于CATIA生成的工业模型通常结构复杂、面数极高,直接转换得到的glTF文件可能在网页中渲染性能较差,最好能想办法给GLTF减面
总结预览方案:
文件通过CATIA转成stp中间格式stp转gltpgltf减面网页渲染
CATPart
程序实现
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的批处理管理器
转换成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文件在网页中的流畅预览。