Solidworks导出GLB格式完善

通过Solidworks——>STL——>GLB,相较于C#库实现更快,其中,用到了pythoncom、trimesh库
话不多说,直接上代码

点击查看代码
import win32com.client
import pythoncom
import trimesh
import os
import time
from pathlib import Path
import gc


class SolidWorksAPI:
    """SolidWorks API封装类,负责与SolidWorks应用程序交互"""

    def __init__(self, visible=True):
        self.visible = visible
        self.app = None

    def __enter__(self):
        self.connect()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.disconnect()

    def connect(self):
        """连接到SolidWorks应用程序"""
        try:
            self.app = win32com.client.Dispatch("SldWorks.Application")
            self.app.Visible = self.visible
            return True
        except Exception as e:
            print(f"连接SolidWorks失败: {e}")
            return False

    def disconnect(self):
        """断开与SolidWorks的连接并释放资源"""
        if self.app:
            try:
                self.app.ExitApp()
            except:
                pass
            self.app = None
            gc.collect()

    def export_to_stl(self, file_path, output_dir=None):
        """导出SolidWorks文件为STL格式"""
        if not self.app:
            print("未连接到SolidWorks应用程序")
            return None

        try:
            doc_type = 1 if file_path.lower().endswith('.sldprt') else 2

            # 打开文档
            errors = win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
            warnings = win32com.client.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
            sw_model = self.app.OpenDoc6(file_path, doc_type, 0, "", errors, warnings)

            if errors.value != 0 or not sw_model:
                print(f"无法打开文件: {file_path}")
                return None

            # 准备输出路径
            file_name = os.path.basename(file_path)
            if output_dir is None:
                output_dir = os.path.dirname(file_path)

            stl_path = os.path.join(output_dir, f"{file_name}.stl")

            # 导出STL
            save_options = 2  # 覆盖现有文件
            sw_model.SaveAs3(stl_path, 0, save_options)
            # 验证STL文件
            if not self._validate_stl(stl_path):
                return None

            print(f"成功导出STL: {stl_path}")
            return stl_path

        except Exception as e:
            print(f"导出STL时出错: {e}")
            return None
        finally:
            # 确保关闭文档
            if self.app and sw_model:
                self.app.CloseDoc(os.path.basename(file_path))
                gc.collect()

    def _validate_stl(self, stl_path):
        """验证STL文件有效性"""
        if not os.path.exists(stl_path):
            print("STL文件未生成")
            return False

        file_size = os.path.getsize(stl_path)
        if file_size < 1024:
            print("生成的STL文件异常小")
            return False

        return True


class FileConverter:
    """文件转换工具类,负责STL到GLB的转换"""

    @staticmethod
    def stl_to_glb(stl_path, glb_path=None, simplify_ratio=0.5):
        """将STL文件转换为GLB文件"""
        if not stl_path or not os.path.exists(stl_path):
            print(f"STL文件不存在: {stl_path}")
            return None

        try:
            print(f"正在将STL转换为GLB: {stl_path}")

            # 加载STL文件
            mesh = trimesh.load_mesh(stl_path)

            # 处理多网格场景
            if isinstance(mesh, trimesh.Scene):
                print("检测到多网格场景,正在合并...")
                mesh = mesh.dump().sum()
            # 获取原始网格信息
            original_faces = len(mesh.faces)
            # 简化网格以提高性能(可选)
            if simplify_ratio < 1.0:
                target_faces = int(original_faces * simplify_ratio)
                if target_faces > 1000:  # 确保简化后的网格不会过小
                    print(f"正在简化网格: {original_faces} 面 -> {target_faces} 面")
                    # mesh = mesh.simplify_vertex_clustering(ratio=simplify_ratio)
                    mesh = mesh.simplify_quadric_decimation(percent=simplify_ratio, aggression=10)
                else:
                    print(f"目标面数太少 ({target_faces}),取消简化")
            # 准备输出路径
            if glb_path is None:
                glb_path = os.path.splitext(stl_path)[0] + ".glb"

            # 导出为GLB
            mesh.export(glb_path, file_type='glb')

            # 打印转换结果
            glb_size = os.path.getsize(glb_path)
            print(f"成功创建GLB: {glb_path} ({glb_size / 1024:.2f} KB)")
            print(f"网格信息: {len(mesh.vertices)} 顶点, {len(mesh.faces)} 面")

            return glb_path

        except Exception as e:
            print(f"STL转GLB时出错: {e}")
            return None


class BatchProcessor:
    """批量处理工具类,负责处理目录中的所有文件"""

    def __init__(self, input_dir, output_dir=None, simplify_ratio=0.5):
        self.input_dir = input_dir
        self.output_dir = output_dir or os.path.join(input_dir, "generated_glb")
        self.simplify_ratio = simplify_ratio

    def process(self):
        """处理目录中的所有SolidWorks文件"""
        # 创建输出目录
        os.makedirs(self.output_dir, exist_ok=True)

        # 获取所有SolidWorks文件
        solidworks_files = self._get_solidworks_files()
        total_files = len(solidworks_files)

        if total_files == 0:
            print("未找到SolidWorks文件")
            return 0, []

        print(f"\n找到 {total_files} 个SolidWorks文件")
        success_count = 0

        # 使用上下文管理器确保资源释放
        with SolidWorksAPI(visible=True) as sw_api:
            for i, file_path in enumerate(solidworks_files, 1):
                print(f"\n[{i}/{total_files}] 处理文件: {file_path}")

                # 导出为STL
                stl_path = sw_api.export_to_stl(file_path, self.output_dir)
                if not stl_path:
                    continue

                # 转换为GLB
                glb_path = FileConverter.stl_to_glb(stl_path, None, self.simplify_ratio)
                if glb_path:
                    print(f"✓ 成功转换: {os.path.basename(file_path)}")
                    success_count += 1

                    # 删除临时STL文件
                    try:
                        os.remove(stl_path)
                        print(f"已删除临时STL文件: {stl_path}")
                    except Exception as e:
                        print(f"删除STL文件时出错: {e}")

        return success_count, solidworks_files

    def _get_solidworks_files(self):
        """获取目录中所有SolidWorks文件"""
        solidworks_files = []
        for ext in ['.sldprt', '.sldasm']:
            solidworks_files.extend(
                [str(f) for f in Path(self.input_dir).rglob(f'*{ext}') if not f.name.startswith('~$')]
            )
        return solidworks_files


def main():
    """主函数"""
    start_time = time.time()

    # 设置输入和输出目录
    input_dir = r"D:\projects\Data\"
    output_dir = r"D:\projects\CSharpAndSolidWorks-master\data\glb"

    # 检查输入目录是否存在
    if not os.path.exists(input_dir):
        print(f"输入目录不存在: {input_dir}")
        return

    # 设置网格简化比例
    simplify_ratio = 0.7

    print(f"\n开始转换SolidWorks文件到GLB")
    print(f"输入目录: {input_dir}")
    print(f"输出目录: {output_dir}")
    print(f"网格简化比例: {simplify_ratio:.0%}")

    # 初始化COM库
    pythoncom.CoInitialize()

    try:
        # 创建并运行批量处理器
        processor = BatchProcessor(input_dir, output_dir, simplify_ratio)
        success_count, solidworks_files = processor.process()

        print(f"\n处理完成! 共尝试 {len(solidworks_files)} 个文件")
        print(f"成功转换 {success_count} 个文件")
        print(f"总耗时: {time.time() - start_time:.2f} 秒")

    finally:
        # 释放COM库
        pythoncom.CoUninitialize()


if __name__ == "__main__":
    main()

这里我尝试导出了一些装配体和零件,效率如下

当然,代码中存在着一些地方需要完善,
1、当频繁打开文件时,solidworks内存占用会增大

2、简化mesh代码需要根据需求自行调试
3、代码效率收到solidworks打开装配体效率影响

posted @ 2025-05-28 15:44  嘻嘻哈哈555  阅读(343)  评论(0)    收藏  举报