用Python的trimesh库计算3DTiles体积的具体代码示例

以下是使用 Python 的trimesh库计算 3DTiles 中.b3dm瓦片体积的具体代码示例。核心流程是:解析.b3dm 文件提取 glTF 模型→用 trimesh 加载模型→校验闭合性→计算体积。

步骤说明

解析.b3dm 文件:.b3dm 是 3DTiles 的静态模型瓦片格式,内部封装了 glTF 二进制数据(.glb),需先提取 glTF 内容。

加载模型:用 trimesh 直接加载提取的 glTF 数据。

校验闭合性:只有闭合模型(watertight)才能计算有效体积,需先检查。

计算体积:调用 trimesh 的体积计算接口,输出结果(单位取决于模型坐标单位,通常为立方米)

复制代码
import os
import struct
import tempfile
import trimesh


def parse_b3dm(b3dm_path):
    """
    解析.b3dm文件,提取内部的glTF二进制数据(.glb格式)
    :param b3dm_path: .b3dm文件路径
    :return: glTF二进制数据(bytes)
    """
    with open(b3dm_path, 'rb') as f:
        # .b3dm头部结构(共28字节)
        # 参考:https://github.com/CesiumGS/3d-tiles/blob/main/specification/TileFormats/B3DM.md
        magic = f.read(4).decode('utf-8')  # 应为"b3dm"
        version = struct.unpack('<I', f.read(4))[0]  # 版本号,通常为1
        byte_length = struct.unpack('<I', f.read(4))[0]  # 文件总字节数
        feature_table_json_byte_length = struct.unpack('<I', f.read(4))[0]  # 要素表JSON长度
        feature_table_binary_byte_length = struct.unpack('<I', f.read(4))[0]  # 要素表二进制长度
        batch_table_json_byte_length = struct.unpack('<I', f.read(4))[0]  # 批处理表JSON长度
        batch_table_binary_byte_length = struct.unpack('<I', f.read(4))[0]  # 批处理表二进制长度

        # 校验文件格式
        if magic != 'b3dm' or version != 1:
            raise ValueError(f"无效的.b3dm文件:{b3dm_path}")

        # 跳过要素表和批处理表,读取glTF数据(从头部结束位置开始)
        header_size = 28
        feature_table_size = feature_table_json_byte_length + feature_table_binary_byte_length
        batch_table_size = batch_table_json_byte_length + batch_table_binary_byte_length
        glb_start = header_size + feature_table_size + batch_table_size

        f.seek(glb_start)
        glb_data = f.read(byte_length - glb_start)  # glTF二进制数据

    return glb_data


def calculate_b3dm_volume(b3dm_path):
    """
    计算单个.b3dm瓦片的体积
    :param b3dm_path: .b3dm文件路径
    :return: 体积(立方米),若模型不闭合则返回None
    """
    try:
        # 1. 解析.b3dm,提取glTF数据
        glb_data = parse_b3dm(b3dm_path)

        # 2. 用临时文件保存glTF数据(trimesh需从文件加载)
        with tempfile.NamedTemporaryFile(suffix='.glb', delete=False) as temp_glb:
            temp_glb.write(glb_data)
            temp_glb_path = temp_glb.name

        # 3. 用trimesh加载模型
        mesh = trimesh.load(temp_glb_path)

        # 4. 校验模型是否闭合(watertight)
        if not mesh.is_watertight:
            print(f"警告:{b3dm_path} 模型不闭合,无法计算体积")
            return None

        # 5. 计算体积(单位:取决于模型坐标单位,3DTiles通常为米)
        volume = mesh.volume

        # 清理临时文件
        os.unlink(temp_glb_path)

        return volume

    except Exception as e:
        print(f"处理{b3dm_path}时出错:{str(e)}")
        return None


def batch_calculate_volume(b3dm_dir):
    """
    批量计算文件夹中所有.b3dm瓦片的体积
    :param b3dm_dir: 存放.b3dm文件的文件夹路径
    :return: 字典{文件名: 体积}
    """
    volume_results = {}
    for filename in os.listdir(b3dm_dir):
        if filename.endswith('.b3dm'):
            b3dm_path = os.path.join(b3dm_dir, filename)
            volume = calculate_b3dm_volume(b3dm_path)
            if volume is not None:
                volume_results[filename] = round(volume, 2)  # 保留2位小数
    return volume_results


# 示例用法
if __name__ == "__main__":
    # 替换为你的.b3dm文件路径或文件夹路径
    b3dm_directory = "./b3dm_tiles"  # 存放.b3dm瓦片的文件夹
    # 或单个文件:b3dm_file = "./tiles/building.b3dm"

    # 批量计算文件夹中所有瓦片的体积
    results = batch_calculate_volume(b3dm_directory)
    
    # 输出结果
    print("体积计算结果(单位:立方米):")
    for filename, vol in results.items():
        print(f"{filename}: {vol} m³")
相关推荐
我爱画页面3 小时前
vue3封装table组件及属性介绍
开发语言·javascript·ecmascript
逻极3 小时前
Next.js vs Vue.js:2025年全栈战场,谁主沉浮?
开发语言·javascript·vue.js·reactjs
Python私教3 小时前
C 语言进制转换全景指南
c语言·开发语言·arm开发
caimo3 小时前
Java无法访问网址出现Timeout但是浏览器和Postman可以
java·开发语言·postman
三体世界4 小时前
Qt从入门到放弃学习之路(1)
开发语言·c++·git·qt·学习·前端框架·编辑器
悟能不能悟4 小时前
jdk25结构化并发和虚拟线程如何配合使用?有什么最佳实践?
java·开发语言
柠檬07114 小时前
MATLAB相机标定入门:Camera Calibration工具包详解
开发语言·数码相机·matlab
卓码软件测评4 小时前
借助大语言模型实现高效测试迁移:Airbnb的大规模实践
开发语言·前端·javascript·人工智能·语言模型·自然语言处理
熙客4 小时前
Java8:Lambda表达式
java·开发语言