diff --git a/glb_point_cloud_processor.py b/glb_point_cloud_processor.py index 644b6a6..5a8f0ee 100755 --- a/glb_point_cloud_processor.py +++ b/glb_point_cloud_processor.py @@ -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: