2026-01-14 11:37:35 +08:00
|
|
|
|
import json
|
|
|
|
|
|
import struct
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
import DracoPy
|
|
|
|
|
|
|
|
|
|
|
|
class DracoGLBParser:
|
|
|
|
|
|
"""使用 DracoPy 解析包含 Draco 压缩的 GLB 文件"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, glb_file_path):
|
|
|
|
|
|
self.glb_file_path = glb_file_path
|
|
|
|
|
|
self.json_data = None
|
|
|
|
|
|
self.binary_data = None
|
|
|
|
|
|
self.decoded_meshes = [] # 缓存解码后的网格数据
|
|
|
|
|
|
|
|
|
|
|
|
def parse_glb_structure(self):
|
|
|
|
|
|
"""解析 GLB 文件结构"""
|
|
|
|
|
|
with open(self.glb_file_path, 'rb') as f:
|
|
|
|
|
|
# 读取 GLB 头部
|
|
|
|
|
|
magic = f.read(4)
|
|
|
|
|
|
version = struct.unpack('<I', f.read(4))[0]
|
|
|
|
|
|
total_length = struct.unpack('<I', f.read(4))[0]
|
|
|
|
|
|
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
print(f"GLB 文件分析:")
|
|
|
|
|
|
print(f" 文件类型: {magic.decode('utf-8')}")
|
|
|
|
|
|
print(f" 版本: {version}")
|
|
|
|
|
|
print(f" 总大小: {total_length:,} bytes")
|
|
|
|
|
|
|
|
|
|
|
|
# 读取 JSON chunk
|
|
|
|
|
|
json_length = struct.unpack('<I', f.read(4))[0]
|
|
|
|
|
|
json_type = f.read(4)
|
|
|
|
|
|
|
|
|
|
|
|
if json_type != b'JSON':
|
|
|
|
|
|
raise ValueError(f"期望 JSON chunk,但得到: {json_type}")
|
|
|
|
|
|
|
|
|
|
|
|
self.json_data = json.loads(f.read(json_length).decode('utf-8'))
|
|
|
|
|
|
|
|
|
|
|
|
print(f" JSON 大小: {json_length:,} bytes")
|
|
|
|
|
|
|
|
|
|
|
|
# 读取 Binary chunk
|
|
|
|
|
|
if f.tell() < total_length:
|
|
|
|
|
|
bin_length = struct.unpack('<I', f.read(4))[0]
|
|
|
|
|
|
bin_type = f.read(4)
|
|
|
|
|
|
|
|
|
|
|
|
if bin_type != b'BIN\x00':
|
|
|
|
|
|
raise ValueError(f"期望 BIN chunk,但得到: {bin_type}")
|
|
|
|
|
|
|
|
|
|
|
|
self.binary_data = f.read(bin_length)
|
|
|
|
|
|
print(f" 二进制数据大小: {bin_length:,} bytes")
|
|
|
|
|
|
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
|
|
def analyze_structure(self):
|
|
|
|
|
|
"""分析 GLB 结构"""
|
|
|
|
|
|
if not self.json_data:
|
|
|
|
|
|
self.parse_glb_structure()
|
|
|
|
|
|
|
|
|
|
|
|
print("\nGLB 结构分析:")
|
|
|
|
|
|
print("-" * 40)
|
|
|
|
|
|
|
|
|
|
|
|
# 基本信息
|
|
|
|
|
|
asset = self.json_data.get('asset', {})
|
|
|
|
|
|
print(f"生成器: {asset.get('generator', '未知')}")
|
|
|
|
|
|
print(f"glTF 版本: {asset.get('version', '未知')}")
|
|
|
|
|
|
|
|
|
|
|
|
# Draco 扩展
|
|
|
|
|
|
extensions_used = self.json_data.get('extensionsUsed', [])
|
|
|
|
|
|
extensions_required = self.json_data.get('extensionsRequired', [])
|
|
|
|
|
|
|
|
|
|
|
|
if 'KHR_draco_mesh_compression' in extensions_used:
|
2026-01-19 10:42:21 +08:00
|
|
|
|
print("使用 Draco 压缩")
|
2026-01-14 11:37:35 +08:00
|
|
|
|
if 'KHR_draco_mesh_compression' in extensions_required:
|
2026-01-19 10:42:21 +08:00
|
|
|
|
print("Draco 压缩是必需的")
|
2026-01-14 11:37:35 +08:00
|
|
|
|
|
|
|
|
|
|
# 网格信息
|
|
|
|
|
|
meshes = self.json_data.get('meshes', [])
|
|
|
|
|
|
print(f"\n网格数量: {len(meshes)}")
|
|
|
|
|
|
|
|
|
|
|
|
for i, mesh in enumerate(meshes):
|
|
|
|
|
|
print(f" 网格 {i}: {mesh.get('name', '未命名')}")
|
|
|
|
|
|
primitives = mesh.get('primitives', [])
|
|
|
|
|
|
print(f" 图元数量: {len(primitives)}")
|
|
|
|
|
|
|
|
|
|
|
|
for j, primitive in enumerate(primitives):
|
|
|
|
|
|
print(f" 图元 {j}:")
|
|
|
|
|
|
if 'extensions' in primitive:
|
|
|
|
|
|
draco_info = primitive['extensions'].get('KHR_draco_mesh_compression')
|
|
|
|
|
|
if draco_info:
|
2026-01-19 10:42:21 +08:00
|
|
|
|
print(f" 使用 Draco 压缩")
|
2026-01-14 11:37:35 +08:00
|
|
|
|
print(f" 属性: {draco_info.get('attributes', {})}")
|
|
|
|
|
|
|
|
|
|
|
|
# 缓冲区信息
|
|
|
|
|
|
buffers = self.json_data.get('buffers', [])
|
|
|
|
|
|
buffer_views = self.json_data.get('bufferViews', [])
|
|
|
|
|
|
accessors = self.json_data.get('accessors', [])
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n缓冲区: {len(buffers)}")
|
|
|
|
|
|
print(f"BufferViews: {len(buffer_views)}")
|
|
|
|
|
|
print(f"访问器: {len(accessors)}")
|
|
|
|
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
|
|
def decode_draco_meshes(self):
|
|
|
|
|
|
"""解码所有 Draco 压缩的网格"""
|
|
|
|
|
|
if not self.json_data:
|
|
|
|
|
|
self.parse_glb_structure()
|
|
|
|
|
|
|
|
|
|
|
|
meshes = []
|
|
|
|
|
|
buffer_views = self.json_data.get('bufferViews', [])
|
|
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
|
print("开始解码 Draco 压缩数据...")
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
for mesh_idx, mesh in enumerate(self.json_data.get('meshes', [])):
|
|
|
|
|
|
mesh_name = mesh.get('name', f'mesh_{mesh_idx}')
|
|
|
|
|
|
|
|
|
|
|
|
for primitive_idx, primitive in enumerate(mesh.get('primitives', [])):
|
|
|
|
|
|
if 'extensions' in primitive:
|
|
|
|
|
|
draco_info = primitive['extensions'].get('KHR_draco_mesh_compression')
|
|
|
|
|
|
|
|
|
|
|
|
if draco_info:
|
|
|
|
|
|
print(f"\n解码: {mesh_name} - 图元 {primitive_idx}")
|
|
|
|
|
|
|
|
|
|
|
|
# 解码 Draco 数据
|
|
|
|
|
|
mesh_data = self._decode_primitive(draco_info, buffer_views)
|
|
|
|
|
|
|
|
|
|
|
|
if mesh_data:
|
|
|
|
|
|
meshes.append({
|
|
|
|
|
|
'mesh_idx': mesh_idx,
|
|
|
|
|
|
'primitive_idx': primitive_idx,
|
|
|
|
|
|
'name': mesh_name,
|
|
|
|
|
|
**mesh_data
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
self.decoded_meshes = meshes # 缓存解码结果
|
|
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
|
print(f"解码完成!共解码 {len(meshes)} 个网格")
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
return meshes
|
|
|
|
|
|
|
|
|
|
|
|
def get_vertices(self, mesh_idx=0, primitive_idx=0):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取指定网格的顶点集合
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
mesh_idx: 网格索引,默认0
|
|
|
|
|
|
primitive_idx: 图元索引,默认0
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
np.array: 顶点数组,形状为 (n, 3) 或 None
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 如果还没有解码数据,先解码
|
|
|
|
|
|
if not self.decoded_meshes:
|
|
|
|
|
|
self.decode_draco_meshes()
|
|
|
|
|
|
|
|
|
|
|
|
# 查找指定网格
|
|
|
|
|
|
for mesh in self.decoded_meshes:
|
|
|
|
|
|
if mesh['mesh_idx'] == mesh_idx and mesh['primitive_idx'] == primitive_idx:
|
|
|
|
|
|
return mesh.get('vertices')
|
|
|
|
|
|
|
|
|
|
|
|
print(f"未找到网格 {mesh_idx} 的图元 {primitive_idx}")
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def get_all_vertices(self):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取所有网格的所有顶点,合并成一个数组
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
np.array: 所有顶点的合并数组,形状为 (n, 3) 或 None
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 如果还没有解码数据,先解码
|
|
|
|
|
|
if not self.decoded_meshes:
|
|
|
|
|
|
self.decode_draco_meshes()
|
|
|
|
|
|
|
|
|
|
|
|
if not self.decoded_meshes:
|
|
|
|
|
|
print("没有解码的网格数据")
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
# 收集所有顶点
|
|
|
|
|
|
all_vertices = []
|
|
|
|
|
|
for mesh in self.decoded_meshes:
|
|
|
|
|
|
if mesh.get('vertices') is not None:
|
|
|
|
|
|
all_vertices.append(mesh['vertices'])
|
|
|
|
|
|
|
|
|
|
|
|
if not all_vertices:
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
# 合并所有顶点
|
|
|
|
|
|
return np.vstack(all_vertices)
|
|
|
|
|
|
|
|
|
|
|
|
def get_vertices_by_mesh_name(self, mesh_name):
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据网格名称获取顶点集合
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
mesh_name: 网格名称
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
list: 包含所有匹配网格的顶点数组列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 如果还没有解码数据,先解码
|
|
|
|
|
|
if not self.decoded_meshes:
|
|
|
|
|
|
self.decode_draco_meshes()
|
|
|
|
|
|
|
|
|
|
|
|
vertices_list = []
|
|
|
|
|
|
for mesh in self.decoded_meshes:
|
|
|
|
|
|
if mesh['name'] == mesh_name and mesh.get('vertices') is not None:
|
|
|
|
|
|
vertices_list.append(mesh['vertices'])
|
|
|
|
|
|
|
|
|
|
|
|
return vertices_list
|
|
|
|
|
|
|
|
|
|
|
|
def get_vertex_count(self):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取总顶点数
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
int: 所有网格的总顶点数
|
|
|
|
|
|
"""
|
|
|
|
|
|
vertices = self.get_all_vertices()
|
|
|
|
|
|
return len(vertices) if vertices is not None else 0
|
|
|
|
|
|
|
|
|
|
|
|
def _decode_primitive(self, draco_info, buffer_views):
|
|
|
|
|
|
"""解码单个图元的 Draco 数据"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 获取 bufferView 信息
|
|
|
|
|
|
buffer_view_idx = draco_info['bufferView']
|
|
|
|
|
|
attributes = draco_info['attributes']
|
|
|
|
|
|
|
|
|
|
|
|
buffer_view = buffer_views[buffer_view_idx]
|
|
|
|
|
|
byte_offset = buffer_view.get('byteOffset', 0)
|
|
|
|
|
|
byte_length = buffer_view['byteLength']
|
|
|
|
|
|
|
|
|
|
|
|
print(f" BufferView: {buffer_view_idx}")
|
|
|
|
|
|
print(f" 属性映射: {attributes}")
|
|
|
|
|
|
print(f" 数据位置: offset={byte_offset}, length={byte_length}")
|
|
|
|
|
|
|
|
|
|
|
|
# 提取 Draco 压缩数据
|
|
|
|
|
|
draco_data = self.binary_data[byte_offset:byte_offset + byte_length]
|
|
|
|
|
|
print(f" Draco 数据大小: {len(draco_data):,} bytes")
|
|
|
|
|
|
|
|
|
|
|
|
# 使用 DracoPy 解码
|
|
|
|
|
|
print(" 正在使用 DracoPy 解码...")
|
|
|
|
|
|
draco_decoder = DracoPy.decode(draco_data)
|
|
|
|
|
|
|
|
|
|
|
|
# 解析解码结果
|
|
|
|
|
|
mesh_data = self._parse_draco_result(draco_decoder, attributes)
|
|
|
|
|
|
|
|
|
|
|
|
return mesh_data
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f" 解码失败: {e}")
|
|
|
|
|
|
import traceback
|
|
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def _parse_draco_result(self, draco_decoder, attributes):
|
|
|
|
|
|
"""解析 DracoPy 解码结果"""
|
|
|
|
|
|
result = {
|
|
|
|
|
|
'vertices': None,
|
|
|
|
|
|
'faces': None,
|
|
|
|
|
|
'texcoords': None,
|
|
|
|
|
|
'batch_ids': None,
|
|
|
|
|
|
'normals': None,
|
|
|
|
|
|
'colors': None
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 获取顶点
|
|
|
|
|
|
if hasattr(draco_decoder, 'points'):
|
|
|
|
|
|
result['vertices'] = np.array(draco_decoder.points, dtype=np.float32)
|
|
|
|
|
|
print(f" 顶点数量: {len(result['vertices'])}")
|
|
|
|
|
|
|
|
|
|
|
|
# 获取面/三角形
|
|
|
|
|
|
if hasattr(draco_decoder, 'faces'):
|
|
|
|
|
|
faces_data = draco_decoder.faces
|
|
|
|
|
|
|
|
|
|
|
|
# 确保是三角形(每面3个顶点)
|
|
|
|
|
|
if len(faces_data) > 0:
|
|
|
|
|
|
if isinstance(faces_data[0], list) or isinstance(faces_data[0], tuple):
|
|
|
|
|
|
# 如果是列表的列表
|
|
|
|
|
|
result['faces'] = np.array(faces_data, dtype=np.uint32)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 如果是扁平化的数组
|
|
|
|
|
|
result['faces'] = np.array(faces_data, dtype=np.uint32).reshape(-1, 3)
|
|
|
|
|
|
|
|
|
|
|
|
print(f" 面数量: {len(result['faces']) if result['faces'] is not None else 0}")
|
|
|
|
|
|
|
|
|
|
|
|
# 获取属性数据
|
|
|
|
|
|
if hasattr(draco_decoder, 'attributes'):
|
|
|
|
|
|
attrs = draco_decoder.attributes
|
|
|
|
|
|
|
|
|
|
|
|
# 根据属性映射查找数据
|
|
|
|
|
|
for gltf_attr_name, draco_attr_id in attributes.items():
|
|
|
|
|
|
if draco_attr_id in attrs:
|
|
|
|
|
|
attr_data = attrs[draco_attr_id]
|
|
|
|
|
|
|
|
|
|
|
|
if gltf_attr_name == 'POSITION':
|
|
|
|
|
|
result['vertices'] = np.array(attr_data, dtype=np.float32)
|
|
|
|
|
|
elif gltf_attr_name == 'TEXCOORD_0':
|
|
|
|
|
|
result['texcoords'] = np.array(attr_data, dtype=np.float32)
|
|
|
|
|
|
elif gltf_attr_name == '_BATCHID':
|
|
|
|
|
|
result['batch_ids'] = np.array(attr_data, dtype=np.uint32)
|
|
|
|
|
|
elif gltf_attr_name == 'NORMAL':
|
|
|
|
|
|
result['normals'] = np.array(attr_data, dtype=np.float32)
|
|
|
|
|
|
elif gltf_attr_name == 'COLOR_0':
|
|
|
|
|
|
result['colors'] = np.array(attr_data, dtype=np.float32)
|
|
|
|
|
|
|
|
|
|
|
|
print(f" 已提取属性: {gltf_attr_name} (ID: {draco_attr_id})")
|
|
|
|
|
|
|
|
|
|
|
|
# 如果没有通过attributes获取到顶点,尝试其他方式
|
|
|
|
|
|
if result['vertices'] is None and hasattr(draco_decoder, 'get_points'):
|
|
|
|
|
|
try:
|
|
|
|
|
|
result['vertices'] = np.array(draco_decoder.get_points(), dtype=np.float32)
|
|
|
|
|
|
except:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def save_decoded_meshes(self, meshes, output_format='obj'):
|
|
|
|
|
|
"""保存解码后的网格"""
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
base_name = os.path.splitext(os.path.basename(self.glb_file_path))[0]
|
|
|
|
|
|
output_dir = f"{base_name}_decoded"
|
|
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(output_dir):
|
|
|
|
|
|
os.makedirs(output_dir)
|
|
|
|
|
|
|
|
|
|
|
|
for mesh in meshes:
|
|
|
|
|
|
filename = f"{output_dir}/{mesh['name']}_p{mesh['primitive_idx']}.{output_format}"
|
|
|
|
|
|
|
|
|
|
|
|
if output_format == 'obj':
|
|
|
|
|
|
self._save_as_obj(mesh, filename)
|
|
|
|
|
|
elif output_format == 'ply':
|
|
|
|
|
|
self._save_as_ply(mesh, filename)
|
|
|
|
|
|
elif output_format == 'npz':
|
|
|
|
|
|
self._save_as_npz(mesh, filename)
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"不支持的格式: {output_format}")
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
print(f"已保存: {filename}")
|
|
|
|
|
|
|
|
|
|
|
|
def _save_as_obj(self, mesh, filename):
|
|
|
|
|
|
"""保存为 OBJ 格式"""
|
|
|
|
|
|
with open(filename, 'w') as f:
|
|
|
|
|
|
# 写入顶点
|
|
|
|
|
|
if mesh['vertices'] is not None:
|
|
|
|
|
|
for v in mesh['vertices']:
|
|
|
|
|
|
f.write(f"v {v[0]} {v[1]} {v[2]}\n")
|
|
|
|
|
|
|
|
|
|
|
|
# 写入纹理坐标
|
|
|
|
|
|
if mesh['texcoords'] is not None:
|
|
|
|
|
|
for uv in mesh['texcoords']:
|
|
|
|
|
|
f.write(f"vt {uv[0]} {uv[1]}\n")
|
|
|
|
|
|
|
|
|
|
|
|
# 写入法线
|
|
|
|
|
|
if mesh['normals'] is not None:
|
|
|
|
|
|
for n in mesh['normals']:
|
|
|
|
|
|
f.write(f"vn {n[0]} {n[1]} {n[2]}\n")
|
|
|
|
|
|
|
|
|
|
|
|
# 写入面
|
|
|
|
|
|
if mesh['faces'] is not None:
|
|
|
|
|
|
for face in mesh['faces']:
|
|
|
|
|
|
# OBJ 索引从1开始
|
|
|
|
|
|
face_indices = [str(idx + 1) for idx in face]
|
|
|
|
|
|
f.write(f"f {' '.join(face_indices)}\n")
|
|
|
|
|
|
|
|
|
|
|
|
def _save_as_ply(self, mesh, filename):
|
|
|
|
|
|
"""保存为 PLY 格式"""
|
|
|
|
|
|
from plyfile import PlyData, PlyElement
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
vertices = mesh['vertices']
|
|
|
|
|
|
faces = mesh['faces']
|
|
|
|
|
|
|
|
|
|
|
|
if vertices is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 创建顶点数据
|
|
|
|
|
|
vertex_data = np.zeros(len(vertices), dtype=[
|
|
|
|
|
|
('x', 'f4'), ('y', 'f4'), ('z', 'f4')
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
vertex_data['x'] = vertices[:, 0]
|
|
|
|
|
|
vertex_data['y'] = vertices[:, 1]
|
|
|
|
|
|
vertex_data['z'] = vertices[:, 2]
|
|
|
|
|
|
|
|
|
|
|
|
# 创建面数据
|
|
|
|
|
|
if faces is not None:
|
|
|
|
|
|
face_data = np.zeros(len(faces), dtype=[('vertex_indices', 'i4', (3,))])
|
|
|
|
|
|
face_data['vertex_indices'] = faces
|
|
|
|
|
|
|
|
|
|
|
|
# 写入文件
|
|
|
|
|
|
vertex_element = PlyElement.describe(vertex_data, 'vertex')
|
|
|
|
|
|
|
|
|
|
|
|
if faces is not None:
|
|
|
|
|
|
face_element = PlyElement.describe(face_data, 'face')
|
|
|
|
|
|
PlyData([vertex_element, face_element], text=False).write(filename)
|
|
|
|
|
|
else:
|
|
|
|
|
|
PlyData([vertex_element], text=False).write(filename)
|
|
|
|
|
|
|
|
|
|
|
|
def _save_as_npz(self, mesh, filename):
|
|
|
|
|
|
"""保存为 NPZ 格式"""
|
|
|
|
|
|
np.savez(
|
|
|
|
|
|
filename,
|
|
|
|
|
|
vertices=mesh['vertices'],
|
|
|
|
|
|
faces=mesh['faces'],
|
|
|
|
|
|
texcoords=mesh['texcoords'],
|
|
|
|
|
|
batch_ids=mesh['batch_ids'],
|
|
|
|
|
|
normals=mesh['normals'],
|
|
|
|
|
|
colors=mesh['colors']
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 使用示例
|
|
|
|
|
|
def main():
|
|
|
|
|
|
# 初始化解析器
|
|
|
|
|
|
parser = DracoGLBParser(r"D:\devForBdzlWork\ai_project_v1\b3dm\test\temp_glb\temp_6e895637.glb")
|
|
|
|
|
|
|
|
|
|
|
|
# 解析 GLB 结构
|
|
|
|
|
|
parser.parse_glb_structure()
|
|
|
|
|
|
|
|
|
|
|
|
# 分析结构
|
|
|
|
|
|
parser.analyze_structure()
|
|
|
|
|
|
|
|
|
|
|
|
# 解码 Draco 网格
|
|
|
|
|
|
meshes = parser.decode_draco_meshes()
|
|
|
|
|
|
|
|
|
|
|
|
# 使用新增的顶点获取方法
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
|
print("顶点获取方法演示:")
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
# 1. 获取第一个网格的第一个图元的顶点
|
|
|
|
|
|
vertices = parser.get_vertices(mesh_idx=0, primitive_idx=0)
|
|
|
|
|
|
if vertices is not None:
|
|
|
|
|
|
print(f"1. 获取第一个网格顶点:")
|
|
|
|
|
|
print(f" 形状: {vertices.shape}")
|
|
|
|
|
|
print(f" 数据类型: {vertices.dtype}")
|
|
|
|
|
|
print(f" 前5个顶点: \n{vertices[:5]}")
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 获取所有顶点(合并)
|
|
|
|
|
|
all_vertices = parser.get_all_vertices()
|
|
|
|
|
|
if all_vertices is not None:
|
|
|
|
|
|
print(f"\n2. 获取所有网格顶点(合并):")
|
|
|
|
|
|
print(f" 总顶点数: {len(all_vertices)}")
|
|
|
|
|
|
print(f" 形状: {all_vertices.shape}")
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 获取总顶点数
|
|
|
|
|
|
total_vertices = parser.get_vertex_count()
|
|
|
|
|
|
print(f"\n3. 总顶点数: {total_vertices}")
|
|
|
|
|
|
|
|
|
|
|
|
# 4. 根据网格名称获取顶点
|
|
|
|
|
|
if meshes:
|
|
|
|
|
|
mesh_name = meshes[0]['name']
|
|
|
|
|
|
vertices_list = parser.get_vertices_by_mesh_name(mesh_name)
|
|
|
|
|
|
print(f"\n4. 根据名称 '{mesh_name}' 获取的顶点:")
|
|
|
|
|
|
for i, verts in enumerate(vertices_list):
|
|
|
|
|
|
print(f" 图元 {i}: {verts.shape if verts is not None else 'None'}")
|
|
|
|
|
|
|
|
|
|
|
|
# 保存解码后的网格
|
|
|
|
|
|
parser.save_decoded_meshes(meshes, output_format='obj')
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|