update
This commit is contained in:
+364
-129
@@ -146,27 +146,28 @@ class GLBPointCloudProcessor:
|
||||
original_count = len(point_cloud.vertices)
|
||||
total_original_points += original_count
|
||||
|
||||
# 获取顶点和颜色
|
||||
vertices = point_cloud.vertices.copy()
|
||||
# 获取原始点云的所有属性和状态
|
||||
original_vertices = point_cloud.vertices.copy()
|
||||
colors = None
|
||||
|
||||
# 获取颜色信息
|
||||
if hasattr(point_cloud.visual, 'vertex_colors') and point_cloud.visual.vertex_colors is not None:
|
||||
colors = point_cloud.visual.vertex_colors.copy()
|
||||
elif hasattr(point_cloud, 'colors') and point_cloud.colors is not None:
|
||||
colors = point_cloud.colors.copy()
|
||||
|
||||
# 初始化掩码(所有点都保留)
|
||||
keep_mask = np.ones(len(vertices), dtype=bool)
|
||||
keep_mask = np.ones(len(original_vertices), dtype=bool)
|
||||
|
||||
# 删除暗色点(默认启用)
|
||||
# 删除暗色点(仅基于颜色,不改变顶点坐标)
|
||||
if colors is not None:
|
||||
removed_count = self._remove_dark_points(vertices, colors, keep_mask, black_threshold, processing_log)
|
||||
removed_count = self._remove_dark_points(original_vertices, colors, keep_mask, black_threshold, processing_log)
|
||||
processing_log.append(f"删除黑色点: {removed_count} 个")
|
||||
else:
|
||||
processing_log.append("跳过黑色点过滤: 点云无颜色信息")
|
||||
|
||||
# 应用掩码
|
||||
filtered_vertices = vertices[keep_mask]
|
||||
# 应用掩码,保留原始的顶点坐标
|
||||
filtered_vertices = original_vertices[keep_mask]
|
||||
filtered_colors = colors[keep_mask] if colors is not None else None
|
||||
|
||||
remaining_count = len(filtered_vertices)
|
||||
@@ -175,25 +176,53 @@ class GLBPointCloudProcessor:
|
||||
|
||||
processing_log.append(f"点云 {name}: 原始 {original_count} -> 剩余 {remaining_count} (删除 {removed_count})")
|
||||
|
||||
# 创建处理后的点云
|
||||
# 创建处理后的点云,保留所有原始属性
|
||||
if remaining_count > 0:
|
||||
processed_pc = trimesh.PointCloud(vertices=filtered_vertices, colors=filtered_colors)
|
||||
# 使用原始点云作为模板,只更新顶点和颜色
|
||||
processed_pc = point_cloud.copy()
|
||||
processed_pc.vertices = filtered_vertices
|
||||
if filtered_colors is not None:
|
||||
processed_pc.visual.vertex_colors = filtered_colors
|
||||
|
||||
processed_point_clouds.append((name, processed_pc))
|
||||
processing_log.append(f"保留点云 {name} 的所有原始属性和状态")
|
||||
else:
|
||||
processing_log.append(f"警告: 点云 {name} 处理后没有剩余点")
|
||||
|
||||
# 创建新的场景
|
||||
# 直接修改原始场景,保持所有几何信息不变
|
||||
if processed_point_clouds:
|
||||
new_scene = trimesh.Scene()
|
||||
# 使用原始场景作为基础,直接替换点云数据
|
||||
new_scene = scene
|
||||
|
||||
# 添加处理后的点云
|
||||
for name, pc in processed_point_clouds:
|
||||
new_scene.add_geometry(pc, node_name=name)
|
||||
# 直接修改原始场景中的点云几何体,保持变换不变
|
||||
for name, processed_pc in processed_point_clouds:
|
||||
if isinstance(scene, trimesh.Scene):
|
||||
# 获取原始几何体
|
||||
if name in scene.geometry:
|
||||
original_geometry = scene.geometry[name]
|
||||
|
||||
# 如果是点云,直接修改顶点和颜色
|
||||
if isinstance(original_geometry, trimesh.PointCloud):
|
||||
original_geometry.vertices = processed_pc.vertices
|
||||
if hasattr(processed_pc.visual, 'vertex_colors') and processed_pc.visual.vertex_colors is not None:
|
||||
original_geometry.visual.vertex_colors = processed_pc.visual.vertex_colors
|
||||
|
||||
processing_log.append(f"直接修改原始点云 {name},保持所有变换和属性不变")
|
||||
else:
|
||||
# 如果不是点云类型,替换整个几何体但保持变换
|
||||
scene.delete_geometry(name)
|
||||
scene.add_geometry(processed_pc, node_name=name)
|
||||
processing_log.append(f"替换几何体 {name},尝试保持变换")
|
||||
else:
|
||||
# 新增几何体
|
||||
scene.add_geometry(processed_pc, node_name=name)
|
||||
processing_log.append(f"添加新点云 {name}")
|
||||
else:
|
||||
# 单个几何体的情况
|
||||
new_scene = processed_pc
|
||||
processing_log.append("单个几何体,直接使用处理后的点云")
|
||||
|
||||
# 添加其他几何体(如相机模型)
|
||||
for name, geometry in other_geometries:
|
||||
new_scene.add_geometry(geometry, node_name=name)
|
||||
processing_log.append(f"保留几何体: {name}")
|
||||
processing_log.append("直接修改原始场景,所有变换矩阵和几何属性完全保持不变")
|
||||
|
||||
# 生成输出路径
|
||||
output_path = self._generate_output_path(output_filename)
|
||||
@@ -208,9 +237,20 @@ class GLBPointCloudProcessor:
|
||||
file_size = os.path.getsize(output_path)
|
||||
processing_log.append(f"GLB文件保存成功,文件大小: {file_size} bytes")
|
||||
|
||||
|
||||
# 验证输出文件的一致性
|
||||
try:
|
||||
# 加载输出文件进行验证
|
||||
verification_scene = trimesh.load(output_path)
|
||||
processing_log.append("文件验证: 输出文件可正常加载")
|
||||
|
||||
# 检查是否保留了场景结构
|
||||
if isinstance(scene, trimesh.Scene) and isinstance(verification_scene, trimesh.Scene):
|
||||
processing_log.append(f"场景结构验证: 原始 {len(scene.geometry)} 个对象 -> 输出 {len(verification_scene.geometry)} 个对象")
|
||||
|
||||
except Exception as e:
|
||||
processing_log.append(f"文件验证警告: {str(e)}")
|
||||
|
||||
processing_log.append("GLB点云黑色点清理完成!")
|
||||
processing_log.append("GLB点云黑色点清理完成! 已保留原始模型的大小、朝向和几何属性")
|
||||
return (output_path,)
|
||||
else:
|
||||
error_msg = "GLB文件保存失败"
|
||||
@@ -298,7 +338,7 @@ class GLBPointCloudProcessor:
|
||||
|
||||
|
||||
class GLBPointCloudBounds:
|
||||
"""GLB点云包围盒计算器 - 计算包围盒并生成带可视化的点云文件"""
|
||||
"""GLB点云包围盒计算器 - 计算包围盒并生成带可视化的预览文件(原模型数据保持不变)"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -306,7 +346,7 @@ class GLBPointCloudBounds:
|
||||
"required": {
|
||||
"glb_file_path": ("STRING", {
|
||||
"default": "",
|
||||
"tooltip": "GLB点云文件路径:输入需要计算包围盒的GLB格式点云文件。支持绝对路径或相对于ComfyUI output目录的相对路径"
|
||||
"tooltip": "GLB点云文件路径:输入需要计算包围盒的GLB格式点云文件。支持绝对路径或相对于ComfyUI output目录的相对路径。注意:此节点在原模型基础上添加预览信息,原模型数据保持不变"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
@@ -350,7 +390,7 @@ class GLBPointCloudBounds:
|
||||
"scale_json",
|
||||
)
|
||||
OUTPUT_TOOLTIPS = [
|
||||
"输出GLB文件路径 - 包含原始点云+包围盒可视化+坐标轴的完整点云文件",
|
||||
"带预览的GLB文件路径 - 包含原始点云+包围盒可视化+坐标轴的完整点云文件",
|
||||
"纯净的scale数组JSON: {\"scale\": [长, 宽, 高]} - 仅包含scale信息,可直接用于3D引擎缩放",
|
||||
]
|
||||
OUTPUT_NODE = True
|
||||
@@ -403,21 +443,27 @@ class GLBPointCloudBounds:
|
||||
processing_log.append("正在加载GLB文件...")
|
||||
scene = trimesh.load(input_path)
|
||||
|
||||
# 收集所有点云数据
|
||||
# 收集所有点云数据,考虑变换矩阵
|
||||
all_vertices = []
|
||||
point_cloud_count = 0
|
||||
|
||||
if isinstance(scene, trimesh.Scene):
|
||||
for name, geometry in scene.geometry.items():
|
||||
if isinstance(geometry, trimesh.PointCloud):
|
||||
all_vertices.append(geometry.vertices)
|
||||
point_cloud_count += 1
|
||||
processing_log.append(f"发现点云: {name}, 点数: {len(geometry.vertices)}")
|
||||
elif hasattr(geometry, 'vertices') and geometry.vertices is not None:
|
||||
# 处理可能被转换为Mesh的点云
|
||||
all_vertices.append(geometry.vertices)
|
||||
point_cloud_count += 1
|
||||
processing_log.append(f"发现几何体(作为点云处理): {name}, 点数: {len(geometry.vertices)}")
|
||||
for node_name in scene.graph.nodes:
|
||||
if node_name in scene.geometry:
|
||||
geometry = scene.geometry[node_name]
|
||||
transform_matrix = scene.graph[node_name][0] # 获取变换矩阵
|
||||
|
||||
if hasattr(geometry, 'vertices') and geometry.vertices is not None:
|
||||
# 应用变换矩阵获取真实世界坐标
|
||||
if transform_matrix is not None and not np.allclose(transform_matrix, np.eye(4)):
|
||||
world_vertices = trimesh.transformations.transform_points(geometry.vertices, transform_matrix)
|
||||
processing_log.append(f"发现几何体: {node_name}, 点数: {len(geometry.vertices)}, 已应用变换矩阵")
|
||||
else:
|
||||
world_vertices = geometry.vertices.copy()
|
||||
processing_log.append(f"发现几何体: {node_name}, 点数: {len(geometry.vertices)}, 无变换")
|
||||
|
||||
all_vertices.append(world_vertices)
|
||||
point_cloud_count += 1
|
||||
elif isinstance(scene, trimesh.PointCloud):
|
||||
all_vertices.append(scene.vertices)
|
||||
point_cloud_count = 1
|
||||
@@ -436,10 +482,10 @@ class GLBPointCloudBounds:
|
||||
processing_log.append(f"错误: {error_msg}")
|
||||
return ("", "")
|
||||
|
||||
# 合并所有顶点
|
||||
# 合并所有顶点(已经是世界坐标)
|
||||
combined_vertices = np.vstack(all_vertices)
|
||||
total_points = len(combined_vertices)
|
||||
processing_log.append(f"合并了 {point_cloud_count} 个点云,总点数: {total_points}")
|
||||
processing_log.append(f"合并了 {point_cloud_count} 个几何体,总点数: {total_points} (已应用变换矩阵)")
|
||||
|
||||
# 计算包围盒
|
||||
processing_log.append(f"计算包围盒类型: {bounding_box_type}")
|
||||
@@ -451,8 +497,6 @@ class GLBPointCloudBounds:
|
||||
extents = max_point - min_point
|
||||
center = (min_point + max_point) / 2
|
||||
|
||||
|
||||
|
||||
processing_log.append(f"AABB计算完成:")
|
||||
processing_log.append(f" 最小点: [{min_point[0]:.6f}, {min_point[1]:.6f}, {min_point[2]:.6f}]")
|
||||
processing_log.append(f" 最大点: [{max_point[0]:.6f}, {max_point[1]:.6f}, {max_point[2]:.6f}]")
|
||||
@@ -465,15 +509,13 @@ class GLBPointCloudBounds:
|
||||
try:
|
||||
to_origin, obb_extents = trimesh.bounds.oriented_bounds(combined_vertices)
|
||||
|
||||
# 计算OBB的中心点和角点
|
||||
# 计算OBB的中心点
|
||||
obb_center = -to_origin[:3, 3] # 变换矩阵的平移部分的负值
|
||||
|
||||
|
||||
|
||||
center = obb_center # 设置center变量
|
||||
extents = obb_extents
|
||||
|
||||
processing_log.append(f"OBB计算完成:")
|
||||
processing_log.append(f" 中心点: [{obb_center[0]:.6f}, {obb_center[1]:.6f}, {obb_center[2]:.6f}]")
|
||||
processing_log.append(f" 中心点: [{center[0]:.6f}, {center[1]:.6f}, {center[2]:.6f}]")
|
||||
processing_log.append(f" 尺寸: [{extents[0]:.6f}, {extents[1]:.6f}, {extents[2]:.6f}]")
|
||||
processing_log.append(f" 体积: {np.prod(extents):.6f}")
|
||||
|
||||
@@ -508,7 +550,7 @@ class GLBPointCloudBounds:
|
||||
processing_log.append(f" 宽度(Y): {scale_array[1]:.6f} {units}")
|
||||
processing_log.append(f" 高度(Z): {scale_array[2]:.6f} {units}")
|
||||
|
||||
# 生成带可视化的点云文件
|
||||
# 生成带可视化的点云文件(原模型数据不变,仅添加预览)
|
||||
output_glb_path = ""
|
||||
|
||||
try:
|
||||
@@ -590,48 +632,65 @@ class GLBPointCloudBounds:
|
||||
wireframe_density, enhance_visibility, output_filename, processing_log):
|
||||
"""生成包含原始点云、包围盒线框和坐标轴的可视化点云文件"""
|
||||
try:
|
||||
# 重新加载原始GLB文件以保持颜色信息
|
||||
# 直接使用已加载的原始场景数据,避免重新加载可能改变朝向
|
||||
input_path = self._get_current_input_path()
|
||||
original_scene = trimesh.load(input_path)
|
||||
|
||||
# 提取原始点云数据(保持颜色)
|
||||
# 提取原始点云数据(完全保持原始状态)
|
||||
all_vertices = []
|
||||
all_colors = []
|
||||
original_point_count = 0
|
||||
|
||||
# 使用之前已经加载的combined_vertices作为基础,重新从原始场景提取完整数据
|
||||
original_scene = trimesh.load(input_path)
|
||||
|
||||
# 创建一个新的场景,完全保持原始场景的结构和变换
|
||||
visualization_scene = trimesh.Scene()
|
||||
|
||||
# 保持原始场景的完整结构
|
||||
if isinstance(original_scene, trimesh.Scene):
|
||||
for name, geometry in original_scene.geometry.items():
|
||||
if isinstance(geometry, trimesh.PointCloud):
|
||||
all_vertices.append(geometry.vertices)
|
||||
# 保持原始颜色
|
||||
if hasattr(geometry.visual, 'vertex_colors') and geometry.visual.vertex_colors is not None:
|
||||
colors = geometry.visual.vertex_colors.copy()
|
||||
elif hasattr(geometry, 'colors') and geometry.colors is not None:
|
||||
colors = geometry.colors.copy()
|
||||
else:
|
||||
# 如果没有颜色信息,使用白色
|
||||
colors = np.tile([255, 255, 255, 255], (len(geometry.vertices), 1))
|
||||
# 完全复制原始场景的所有几何体和变换
|
||||
for node_name in original_scene.graph.nodes:
|
||||
if node_name in original_scene.geometry:
|
||||
geometry = original_scene.geometry[node_name]
|
||||
transform_matrix = original_scene.graph[node_name][0] # 获取变换矩阵
|
||||
|
||||
# 确保颜色格式一致 (RGBA, 0-255)
|
||||
colors = self._normalize_colors(colors)
|
||||
all_colors.append(colors)
|
||||
original_point_count += len(geometry.vertices)
|
||||
processing_log.append(f"保留原始点云: {name}, {len(geometry.vertices):,} 个点,颜色已保持")
|
||||
elif hasattr(geometry, 'vertices') and geometry.vertices is not None:
|
||||
all_vertices.append(geometry.vertices)
|
||||
# 处理可能被转换为Mesh的点云
|
||||
if hasattr(geometry.visual, 'vertex_colors') and geometry.visual.vertex_colors is not None:
|
||||
colors = geometry.visual.vertex_colors.copy()
|
||||
else:
|
||||
colors = np.tile([255, 255, 255, 255], (len(geometry.vertices), 1))
|
||||
# 完全复制几何体,保持所有属性
|
||||
copied_geometry = geometry.copy()
|
||||
|
||||
# 确保颜色格式一致 (RGBA, 0-255)
|
||||
colors = self._normalize_colors(colors)
|
||||
all_colors.append(colors)
|
||||
original_point_count += len(geometry.vertices)
|
||||
processing_log.append(f"保留原始几何体: {name}, {len(geometry.vertices):,} 个点,颜色已保持")
|
||||
# 添加到新场景,保持原始的变换矩阵
|
||||
visualization_scene.add_geometry(copied_geometry, node_name=node_name, transform=transform_matrix)
|
||||
|
||||
# 提取顶点用于统计(注意:这里用于显示统计,真实包围盒计算已在前面完成)
|
||||
if hasattr(geometry, 'vertices') and geometry.vertices is not None:
|
||||
# 这里不需要再次应用变换,因为包围盒计算已经在combined_vertices中处理过了
|
||||
all_vertices.append(geometry.vertices)
|
||||
|
||||
# 保持原始颜色
|
||||
if isinstance(geometry, trimesh.PointCloud):
|
||||
if hasattr(geometry.visual, 'vertex_colors') and geometry.visual.vertex_colors is not None:
|
||||
colors = geometry.visual.vertex_colors.copy()
|
||||
elif hasattr(geometry, 'colors') and geometry.colors is not None:
|
||||
colors = geometry.colors.copy()
|
||||
else:
|
||||
colors = np.tile([255, 255, 255, 255], (len(geometry.vertices), 1))
|
||||
else:
|
||||
if hasattr(geometry.visual, 'vertex_colors') and geometry.visual.vertex_colors is not None:
|
||||
colors = geometry.visual.vertex_colors.copy()
|
||||
else:
|
||||
colors = np.tile([200, 200, 200, 255], (len(geometry.vertices), 1))
|
||||
|
||||
# 确保颜色格式一致 (RGBA, 0-255)
|
||||
colors = self._normalize_colors(colors)
|
||||
all_colors.append(colors)
|
||||
original_point_count += len(geometry.vertices)
|
||||
|
||||
processing_log.append(f"完全保持原始几何体: {node_name}, 变换矩阵和所有属性已保留")
|
||||
|
||||
elif isinstance(original_scene, trimesh.PointCloud):
|
||||
# 单个点云,直接复制
|
||||
visualization_scene = original_scene.copy()
|
||||
all_vertices.append(original_scene.vertices)
|
||||
|
||||
if hasattr(original_scene.visual, 'vertex_colors') and original_scene.visual.vertex_colors is not None:
|
||||
colors = original_scene.visual.vertex_colors.copy()
|
||||
elif hasattr(original_scene, 'colors') and original_scene.colors is not None:
|
||||
@@ -639,63 +698,71 @@ class GLBPointCloudBounds:
|
||||
else:
|
||||
colors = np.tile([255, 255, 255, 255], (len(original_scene.vertices), 1))
|
||||
|
||||
# 确保颜色格式一致 (RGBA, 0-255)
|
||||
colors = self._normalize_colors(colors)
|
||||
all_colors.append(colors)
|
||||
original_point_count = len(original_scene.vertices)
|
||||
processing_log.append(f"保留原始点云: {original_point_count:,} 个点,颜色已保持")
|
||||
elif hasattr(original_scene, 'vertices') and original_scene.vertices is not None:
|
||||
all_vertices.append(original_scene.vertices)
|
||||
if hasattr(original_scene.visual, 'vertex_colors') and original_scene.visual.vertex_colors is not None:
|
||||
colors = original_scene.visual.vertex_colors.copy()
|
||||
else:
|
||||
colors = np.tile([255, 255, 255, 255], (len(original_scene.vertices), 1))
|
||||
|
||||
# 确保颜色格式一致 (RGBA, 0-255)
|
||||
colors = self._normalize_colors(colors)
|
||||
all_colors.append(colors)
|
||||
original_point_count = len(original_scene.vertices)
|
||||
processing_log.append(f"保留原始几何体: {original_point_count:,} 个点,颜色已保持")
|
||||
processing_log.append(f"完全保持原始点云: {original_point_count:,} 个点")
|
||||
|
||||
processing_log.append(f"原始点云总计: {original_point_count:,} 个点,颜色信息完整保留")
|
||||
else:
|
||||
# 单个几何体,转换为场景保持结构
|
||||
visualization_scene.add_geometry(original_scene.copy(), node_name="main_geometry")
|
||||
if hasattr(original_scene, 'vertices') and original_scene.vertices is not None:
|
||||
all_vertices.append(original_scene.vertices)
|
||||
|
||||
if hasattr(original_scene.visual, 'vertex_colors') and original_scene.visual.vertex_colors is not None:
|
||||
colors = original_scene.visual.vertex_colors.copy()
|
||||
else:
|
||||
colors = np.tile([200, 200, 200, 255], (len(original_scene.vertices), 1))
|
||||
|
||||
colors = self._normalize_colors(colors)
|
||||
all_colors.append(colors)
|
||||
original_point_count = len(original_scene.vertices)
|
||||
processing_log.append(f"完全保持原始几何体: {original_point_count:,} 个点")
|
||||
|
||||
# 添加包围盒线框点云
|
||||
processing_log.append(f"原始模型结构完全保留: {original_point_count:,} 个点,所有变换矩阵和朝向不变")
|
||||
|
||||
# 添加包围盒线框点云到场景
|
||||
if add_bounding_box_visualization:
|
||||
box_vertices, box_colors = self._create_bounding_box_pointcloud(
|
||||
extents, center, bounds_type, wireframe_density, enhance_visibility, processing_log
|
||||
)
|
||||
if len(box_vertices) > 0:
|
||||
all_vertices.append(box_vertices)
|
||||
all_colors.append(box_colors)
|
||||
# 创建包围盒点云并添加到场景
|
||||
bounding_box_pointcloud = trimesh.PointCloud(vertices=box_vertices, colors=box_colors)
|
||||
visualization_scene.add_geometry(bounding_box_pointcloud, node_name="bounding_box_visualization")
|
||||
processing_log.append(f"包围盒线框: {len(box_vertices):,} 个点")
|
||||
|
||||
# 添加坐标轴点云
|
||||
# 添加坐标轴点云到场景
|
||||
if add_coordinate_axes:
|
||||
axes_vertices, axes_colors = self._create_coordinate_axes_pointcloud(
|
||||
extents, center, wireframe_density, processing_log
|
||||
)
|
||||
if len(axes_vertices) > 0:
|
||||
all_vertices.append(axes_vertices)
|
||||
all_colors.append(axes_colors)
|
||||
# 创建坐标轴点云并添加到场景
|
||||
coordinate_axes_pointcloud = trimesh.PointCloud(vertices=axes_vertices, colors=axes_colors)
|
||||
visualization_scene.add_geometry(coordinate_axes_pointcloud, node_name="coordinate_axes")
|
||||
processing_log.append(f"坐标轴: {len(axes_vertices):,} 个点")
|
||||
|
||||
# 合并所有点云
|
||||
final_vertices = np.vstack(all_vertices)
|
||||
final_colors = np.vstack(all_colors)
|
||||
# 统计总点数
|
||||
total_visualization_points = original_point_count
|
||||
box_point_count = 0
|
||||
axes_point_count = 0
|
||||
|
||||
processing_log.append(f"总点数: {len(final_vertices):,} 个点")
|
||||
if add_bounding_box_visualization:
|
||||
box_point_count = len(box_vertices) if 'box_vertices' in locals() and len(box_vertices) > 0 else 0
|
||||
total_visualization_points += box_point_count
|
||||
|
||||
if add_coordinate_axes:
|
||||
axes_point_count = len(axes_vertices) if 'axes_vertices' in locals() and len(axes_vertices) > 0 else 0
|
||||
total_visualization_points += axes_point_count
|
||||
|
||||
processing_log.append(f"总点数: {total_visualization_points:,} 个点")
|
||||
|
||||
# 生成输出路径
|
||||
output_path = self._generate_output_path(output_filename)
|
||||
|
||||
# 创建trimesh点云对象
|
||||
visualization_pointcloud = trimesh.PointCloud(
|
||||
vertices=final_vertices,
|
||||
colors=final_colors
|
||||
)
|
||||
|
||||
# 保存为GLB文件
|
||||
visualization_pointcloud.export(output_path)
|
||||
# 保存场景,完全保持原始模型的结构和朝向
|
||||
visualization_scene.export(output_path)
|
||||
|
||||
processing_log.append(f"可视化点云已保存: {output_path}")
|
||||
return output_path
|
||||
@@ -948,7 +1015,7 @@ class GLBPointCloudBounds:
|
||||
|
||||
|
||||
class GLBPointCloudOriginAdjuster:
|
||||
"""GLB点云原点调整器 - 重新设置点云的原点位置并输出变换信息"""
|
||||
"""GLB点云原点调整计算器 - 计算原点调整信息并生成带坐标轴的预览文件(原模型数据保持不变)"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -956,7 +1023,7 @@ class GLBPointCloudOriginAdjuster:
|
||||
"required": {
|
||||
"glb_file_path": ("STRING", {
|
||||
"default": "",
|
||||
"tooltip": "GLB点云文件路径:输入需要调整原点的GLB格式点云文件"
|
||||
"tooltip": "GLB点云文件路径:输入需要调整原点的GLB格式点云文件。注意:此节点在原模型基础上添加坐标轴预览,原模型数据保持不变"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
@@ -992,7 +1059,7 @@ class GLBPointCloudOriginAdjuster:
|
||||
"transform_info",
|
||||
)
|
||||
OUTPUT_TOOLTIPS = [
|
||||
"调整后的GLB文件完整路径",
|
||||
"带预览的GLB文件路径 - 包含原始点云+坐标轴预览的完整点云文件",
|
||||
"纯净的position信息JSON: {\"position\": [x, y, z]} - 仅包含位置信息,可直接用于UE等引擎",
|
||||
]
|
||||
OUTPUT_NODE = True
|
||||
@@ -1042,18 +1109,29 @@ class GLBPointCloudOriginAdjuster:
|
||||
processing_log.append("正在加载GLB文件...")
|
||||
scene = trimesh.load(input_path)
|
||||
|
||||
# 收集所有几何体和点云
|
||||
# 收集所有几何体和点云,考虑变换矩阵
|
||||
geometries_to_transform = []
|
||||
all_vertices = []
|
||||
total_points = 0
|
||||
|
||||
if isinstance(scene, trimesh.Scene):
|
||||
for name, geometry in scene.geometry.items():
|
||||
geometries_to_transform.append((name, geometry))
|
||||
if hasattr(geometry, 'vertices') and geometry.vertices is not None:
|
||||
all_vertices.append(geometry.vertices)
|
||||
total_points += len(geometry.vertices)
|
||||
processing_log.append(f"发现几何体: {name}, 顶点数: {len(geometry.vertices)}")
|
||||
for node_name in scene.graph.nodes:
|
||||
if node_name in scene.geometry:
|
||||
geometry = scene.geometry[node_name]
|
||||
transform_matrix = scene.graph[node_name][0] # 获取变换矩阵
|
||||
geometries_to_transform.append((node_name, geometry))
|
||||
|
||||
if hasattr(geometry, 'vertices') and geometry.vertices is not None:
|
||||
# 应用变换矩阵获取真实世界坐标
|
||||
if transform_matrix is not None and not np.allclose(transform_matrix, np.eye(4)):
|
||||
world_vertices = trimesh.transformations.transform_points(geometry.vertices, transform_matrix)
|
||||
processing_log.append(f"发现几何体: {node_name}, 顶点数: {len(geometry.vertices)}, 已应用变换矩阵")
|
||||
else:
|
||||
world_vertices = geometry.vertices.copy()
|
||||
processing_log.append(f"发现几何体: {node_name}, 顶点数: {len(geometry.vertices)}, 无变换")
|
||||
|
||||
all_vertices.append(world_vertices)
|
||||
total_points += len(geometry.vertices)
|
||||
else:
|
||||
# 单个几何体
|
||||
geometries_to_transform.append(("main_geometry", scene))
|
||||
@@ -1067,9 +1145,9 @@ class GLBPointCloudOriginAdjuster:
|
||||
processing_log.append(f"错误: {error_msg}")
|
||||
return ("", "")
|
||||
|
||||
# 合并所有顶点计算包围盒
|
||||
# 合并所有顶点计算包围盒(已经是世界坐标)
|
||||
combined_vertices = np.vstack(all_vertices)
|
||||
processing_log.append(f"总顶点数: {total_points:,}")
|
||||
processing_log.append(f"总顶点数: {total_points:,} (已应用变换矩阵)")
|
||||
|
||||
# 计算原始包围盒
|
||||
original_min = np.min(combined_vertices, axis=0)
|
||||
@@ -1158,19 +1236,63 @@ class GLBPointCloudOriginAdjuster:
|
||||
processing_log.append(f" 中心点: [{new_center[0]:.6f}, {new_center[1]:.6f}, {new_center[2]:.6f}]")
|
||||
processing_log.append(f" 尺寸: [{new_size[0]:.6f}, {new_size[1]:.6f}, {new_size[2]:.6f}]")
|
||||
|
||||
# 生成输出文件
|
||||
# 生成带预览的输出文件(原模型数据不变,仅添加坐标轴预览)
|
||||
output_path = self._generate_output_path(output_filename)
|
||||
processing_log.append(f"输出文件路径: {output_path}")
|
||||
|
||||
# 保存变换后的GLB文件
|
||||
processing_log.append("正在保存变换后的GLB文件...")
|
||||
new_scene.export(output_path)
|
||||
# 创建包含原始点云和坐标轴的预览场景,完全保持原始结构
|
||||
original_scene = trimesh.load(input_path) # 重新加载原始场景
|
||||
|
||||
# 创建预览场景,完全复制原始场景的结构
|
||||
if isinstance(original_scene, trimesh.Scene):
|
||||
preview_scene = trimesh.Scene()
|
||||
# 完全复制原始场景的所有几何体和变换矩阵
|
||||
for node_name in original_scene.graph.nodes:
|
||||
if node_name in original_scene.geometry:
|
||||
geometry = original_scene.geometry[node_name]
|
||||
transform_matrix = original_scene.graph[node_name][0] # 获取变换矩阵
|
||||
|
||||
# 完全复制几何体,保持所有属性
|
||||
copied_geometry = geometry.copy()
|
||||
|
||||
# 添加到预览场景,保持原始的变换矩阵
|
||||
preview_scene.add_geometry(copied_geometry, node_name=node_name, transform=transform_matrix)
|
||||
processing_log.append(f"完全保持原始几何体: {node_name}, 变换矩阵和朝向已保留")
|
||||
else:
|
||||
# 单个几何体的情况
|
||||
preview_scene = trimesh.Scene()
|
||||
preview_scene.add_geometry(original_scene.copy(), node_name="main_geometry")
|
||||
processing_log.append("完全保持原始几何体: main_geometry, 所有属性已保留")
|
||||
|
||||
# 添加坐标轴预览(如果需要)
|
||||
if add_coordinate_axes:
|
||||
try:
|
||||
# 使用原始包围盒来确定坐标轴长度
|
||||
axis_length = np.max(original_size) * 0.4
|
||||
|
||||
# 在计算出的新原点位置生成坐标轴
|
||||
axes_vertices, axes_colors = self._create_coordinate_axes_pointcloud_at_position(
|
||||
new_origin, axis_length, wireframe_density, processing_log
|
||||
)
|
||||
|
||||
if len(axes_vertices) > 0:
|
||||
# 创建坐标轴点云并添加到场景
|
||||
axes_pointcloud = trimesh.PointCloud(vertices=axes_vertices, colors=axes_colors)
|
||||
preview_scene.add_geometry(axes_pointcloud, node_name="coordinate_axes_preview")
|
||||
processing_log.append(f"坐标轴预览: {len(axes_vertices):,} 个点 (显示建议的新原点位置)")
|
||||
|
||||
except Exception as e:
|
||||
processing_log.append(f"坐标轴生成失败: {str(e)}")
|
||||
|
||||
# 保存预览文件
|
||||
processing_log.append("正在保存带预览的GLB文件...")
|
||||
preview_scene.export(output_path)
|
||||
|
||||
if os.path.exists(output_path):
|
||||
file_size = os.path.getsize(output_path)
|
||||
processing_log.append(f"GLB文件保存成功,文件大小: {file_size} bytes")
|
||||
processing_log.append(f"预览GLB文件保存成功,文件大小: {file_size} bytes")
|
||||
else:
|
||||
error_msg = "GLB文件保存失败"
|
||||
error_msg = "预览GLB文件保存失败"
|
||||
processing_log.append(f"错误: {error_msg}")
|
||||
return ("", "")
|
||||
|
||||
@@ -1184,12 +1306,10 @@ class GLBPointCloudOriginAdjuster:
|
||||
"position": [float(final_position[0]), float(final_position[1]), float(final_position[2])]
|
||||
}
|
||||
|
||||
|
||||
|
||||
processing_log.append("")
|
||||
processing_log.append(f"变换信息 (单位: {output_units}):")
|
||||
processing_log.append(f" Position: [{transform_info['position'][0]:.2f}, {transform_info['position'][1]:.2f}, {transform_info['position'][2]:.2f}]")
|
||||
processing_log.append("GLB点云原点调整完成!")
|
||||
processing_log.append("原点调整预览生成完成!")
|
||||
|
||||
return (
|
||||
output_path,
|
||||
@@ -1245,6 +1365,121 @@ class GLBPointCloudOriginAdjuster:
|
||||
|
||||
|
||||
|
||||
def _create_coordinate_axes_pointcloud_at_position(self, position, axis_length, wireframe_density, processing_log):
|
||||
"""在指定位置创建坐标轴的点云表示"""
|
||||
try:
|
||||
center = np.array(position) # 指定的位置
|
||||
axis_thickness = axis_length * 0.01 # 坐标轴厚度
|
||||
axes_points = []
|
||||
axes_colors = []
|
||||
|
||||
# 计算坐标轴密度
|
||||
axis_line_density = wireframe_density # 使用参数控制密度
|
||||
axis_thickness_points = max(3, axis_line_density // 15) # 厚度方向的点数
|
||||
|
||||
# X轴 - 红色(从指定位置开始)
|
||||
for i in range(axis_line_density):
|
||||
t = i / (axis_line_density - 1) if axis_line_density > 1 else 0
|
||||
base_point = [center[0] + t * axis_length, center[1], center[2]]
|
||||
|
||||
# 主轴线
|
||||
axes_points.append(base_point)
|
||||
axes_colors.append([255, 0, 0, 255])
|
||||
|
||||
# 增加厚度(在YZ平面上添加点)
|
||||
for j in range(axis_thickness_points):
|
||||
for k in range(axis_thickness_points):
|
||||
offset_y = (j - axis_thickness_points//2) * axis_thickness / axis_thickness_points
|
||||
offset_z = (k - axis_thickness_points//2) * axis_thickness / axis_thickness_points
|
||||
thick_point = [base_point[0], base_point[1] + offset_y, base_point[2] + offset_z]
|
||||
axes_points.append(thick_point)
|
||||
axes_colors.append([255, 0, 0, 255])
|
||||
|
||||
# X轴箭头头部
|
||||
arrow_length = axis_length * 0.1
|
||||
arrow_base = axis_length * 0.9
|
||||
for i in range(axis_line_density // 2):
|
||||
t = i / (axis_line_density // 2 - 1) if axis_line_density > 2 else 0
|
||||
arrow_x = center[0] + arrow_base + t * arrow_length
|
||||
arrow_offset = (1 - t) * axis_thickness * 2
|
||||
|
||||
axes_points.append([arrow_x, center[1] + arrow_offset, center[2]])
|
||||
axes_points.append([arrow_x, center[1] - arrow_offset, center[2]])
|
||||
axes_points.append([arrow_x, center[1], center[2] + arrow_offset])
|
||||
axes_points.append([arrow_x, center[1], center[2] - arrow_offset])
|
||||
axes_colors.extend([[255, 0, 0, 255]] * 4)
|
||||
|
||||
# Y轴 - 绿色(从指定位置开始)
|
||||
for i in range(axis_line_density):
|
||||
t = i / (axis_line_density - 1) if axis_line_density > 1 else 0
|
||||
base_point = [center[0], center[1] + t * axis_length, center[2]]
|
||||
|
||||
# 主轴线
|
||||
axes_points.append(base_point)
|
||||
axes_colors.append([0, 255, 0, 255])
|
||||
|
||||
# 增加厚度(在XZ平面上添加点)
|
||||
for j in range(axis_thickness_points):
|
||||
for k in range(axis_thickness_points):
|
||||
offset_x = (j - axis_thickness_points//2) * axis_thickness / axis_thickness_points
|
||||
offset_z = (k - axis_thickness_points//2) * axis_thickness / axis_thickness_points
|
||||
thick_point = [base_point[0] + offset_x, base_point[1], base_point[2] + offset_z]
|
||||
axes_points.append(thick_point)
|
||||
axes_colors.append([0, 255, 0, 255])
|
||||
|
||||
# Y轴箭头头部
|
||||
for i in range(axis_line_density // 2):
|
||||
t = i / (axis_line_density // 2 - 1) if axis_line_density > 2 else 0
|
||||
arrow_y = center[1] + arrow_base + t * arrow_length
|
||||
arrow_offset = (1 - t) * axis_thickness * 2
|
||||
|
||||
axes_points.append([center[0] + arrow_offset, arrow_y, center[2]])
|
||||
axes_points.append([center[0] - arrow_offset, arrow_y, center[2]])
|
||||
axes_points.append([center[0], arrow_y, center[2] + arrow_offset])
|
||||
axes_points.append([center[0], arrow_y, center[2] - arrow_offset])
|
||||
axes_colors.extend([[0, 255, 0, 255]] * 4)
|
||||
|
||||
# Z轴 - 蓝色(从指定位置开始)
|
||||
for i in range(axis_line_density):
|
||||
t = i / (axis_line_density - 1) if axis_line_density > 1 else 0
|
||||
base_point = [center[0], center[1], center[2] + t * axis_length]
|
||||
|
||||
# 主轴线
|
||||
axes_points.append(base_point)
|
||||
axes_colors.append([0, 0, 255, 255])
|
||||
|
||||
# 增加厚度(在XY平面上添加点)
|
||||
for j in range(axis_thickness_points):
|
||||
for k in range(axis_thickness_points):
|
||||
offset_x = (j - axis_thickness_points//2) * axis_thickness / axis_thickness_points
|
||||
offset_y = (k - axis_thickness_points//2) * axis_thickness / axis_thickness_points
|
||||
thick_point = [base_point[0] + offset_x, base_point[1] + offset_y, base_point[2]]
|
||||
axes_points.append(thick_point)
|
||||
axes_colors.append([0, 0, 255, 255])
|
||||
|
||||
# Z轴箭头头部
|
||||
for i in range(axis_line_density // 2):
|
||||
t = i / (axis_line_density // 2 - 1) if axis_line_density > 2 else 0
|
||||
arrow_z = center[2] + arrow_base + t * arrow_length
|
||||
arrow_offset = (1 - t) * axis_thickness * 2
|
||||
|
||||
axes_points.append([center[0] + arrow_offset, center[1], arrow_z])
|
||||
axes_points.append([center[0] - arrow_offset, center[1], arrow_z])
|
||||
axes_points.append([center[0], center[1] + arrow_offset, arrow_z])
|
||||
axes_points.append([center[0], center[1] - arrow_offset, arrow_z])
|
||||
axes_colors.extend([[0, 0, 255, 255]] * 4)
|
||||
|
||||
axes_vertices = np.array(axes_points)
|
||||
axes_colors_array = np.array(axes_colors)
|
||||
|
||||
processing_log.append(f"坐标轴生成: {len(axes_vertices):,} 个点 (长度={axis_length:.3f}, 密度={wireframe_density}, 位置=[{center[0]:.3f}, {center[1]:.3f}, {center[2]:.3f}])")
|
||||
|
||||
return axes_vertices, axes_colors_array
|
||||
|
||||
except Exception as e:
|
||||
processing_log.append(f"创建坐标轴点云失败: {str(e)}")
|
||||
return np.array([]), np.array([])
|
||||
|
||||
def _create_coordinate_axes_pointcloud_at_origin(self, axis_length, wireframe_density, processing_log):
|
||||
"""在原点(0,0,0)创建坐标轴的点云表示"""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user