You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
9.6 KiB
286 lines
9.6 KiB
import open3d as o3d |
|
import numpy as np |
|
import re |
|
from config import * |
|
|
|
# -------------------------- 开始:运行 ---------------------------------- |
|
|
|
def is_run_local_data(): |
|
if curr_run_mode == run_mode.formal: |
|
return False |
|
return True |
|
|
|
def need_upload_result(): |
|
if curr_run_mode == run_mode.test: |
|
return False |
|
return True |
|
|
|
def is_use_debug_oss(): |
|
if curr_run_mode == run_mode.test: |
|
return True |
|
return False |
|
|
|
# -------------------------- 结束:运行 ---------------------------------- |
|
|
|
# -------------------------- 开始:模型 ---------------------------------- |
|
|
|
def read_mesh(obj_path): |
|
mesh_obj = o3d.io.read_triangle_mesh(obj_path) |
|
return mesh_obj |
|
|
|
def down_sample(pcd, voxel_size, farthest_sample = False): |
|
original_num = len(pcd.points) |
|
target_samples = 1500 # 1000 |
|
num_samples = min(target_samples, original_num) |
|
|
|
# 第一步:使用体素下采样快速减少点数量 |
|
# voxel_size = 3 |
|
if farthest_sample: |
|
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples) |
|
else: |
|
pcd_voxel = pcd.voxel_down_sample(voxel_size) |
|
down_num = len(pcd_voxel.points) |
|
# print(f"original_num={original_num}, down_num={down_num}") |
|
|
|
# 第二步:仅在必要时进行最远点下采样 |
|
if len(pcd_voxel.points) > target_samples and False: |
|
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples) |
|
else: |
|
pcd_downsampled = pcd_voxel |
|
|
|
return pcd_downsampled |
|
|
|
def get_volume_centroid(mesh): |
|
"""向量化版本的体积重心计算""" |
|
vertices = np.asarray(mesh.vertices) |
|
triangles = np.asarray(mesh.triangles) |
|
|
|
# 一次性获取所有三角形的顶点 [n_triangles, 3, 3] |
|
tri_vertices = vertices[triangles] |
|
|
|
# 分别提取三个顶点 |
|
v0 = tri_vertices[:, 0, :] # [n, 3] |
|
v1 = tri_vertices[:, 1, :] # [n, 3] |
|
v2 = tri_vertices[:, 2, :] # [n, 3] |
|
|
|
# 向量化计算叉积和点积 |
|
cross_vec = np.cross(v1, v2) # [n, 3] |
|
dot_results = np.einsum('ij,ij->i', v0, cross_vec) # 批量点积 [n] |
|
|
|
# 计算每个四面体体积 [n] |
|
tetra_volumes = np.abs(dot_results) / 6.0 |
|
|
|
# 计算每个四面体重心 [n, 3] |
|
tetra_centers = (v0 + v1 + v2) / 4.0 |
|
|
|
# 总体积和加权重心 |
|
total_volume = np.sum(tetra_volumes) |
|
|
|
if total_volume > 0: |
|
weighted_center = np.sum(tetra_volumes[:, np.newaxis] * tetra_centers, axis=0) / total_volume |
|
return weighted_center |
|
else: |
|
return np.mean(vertices, axis=0) |
|
|
|
def mesh_tranform_to_pcd(mesh, transform_matrix): |
|
vertices = np.asarray(mesh.vertices) |
|
|
|
transformed_vertices = mesh_transform_by_matrix(vertices, transform_matrix) |
|
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices) |
|
|
|
return mesh_to_pcd(mesh) |
|
|
|
def mesh_to_pcd(mesh, is_down_sample=True): |
|
|
|
vertices = np.asarray(mesh.vertices) |
|
pcd = o3d.geometry.PointCloud() |
|
pcd.points = o3d.utility.Vector3dVector(vertices) |
|
|
|
if is_down_sample: |
|
pcd_downsampled = down_sample(pcd, voxel_size, False) |
|
return pcd_downsampled |
|
else: |
|
return pcd |
|
|
|
def mesh_transform_by_matrix(vertices, transform_matrix): |
|
""" |
|
手动实现网格变换:对每个顶点应用齐次变换矩阵 |
|
参数: |
|
vertices: 网格顶点数组 (N, 3) |
|
transform_matrix: 4x4 齐次变换矩阵 |
|
返回: |
|
变换后的顶点数组 (N, 3) |
|
""" |
|
# 1. 顶点转齐次坐标 (N, 3) → (N, 4) |
|
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1)))) |
|
|
|
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN) |
|
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T |
|
|
|
# 3. 转回非齐次坐标 (3xN) → (N, 3) |
|
transformed_vertices = transformed_homogeneous[:3, :].T |
|
return transformed_vertices |
|
|
|
import os |
|
|
|
def transform_save_bpy(layout_data, original_obj_pid_dir): |
|
# 清除场景 |
|
bpy.ops.object.select_all(action='SELECT') |
|
bpy.ops.object.delete(use_global=False) |
|
|
|
## 尺寸调整,环境设置 |
|
bpy.ops.object.delete(use_global=False, confirm=False) |
|
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' |
|
bpy.context.scene.unit_settings.scale_length = 0.001 |
|
bpy.context.scene.unit_settings.mass_unit = 'GRAMS' |
|
|
|
meshes = [] |
|
need_offset = True |
|
for model in layout_data["models"]: |
|
transform = model.get('transform', {}) |
|
homo_matrix = transform["homo_matrix"] |
|
reconstructed_matrix = np.array(homo_matrix, dtype=np.float64) |
|
|
|
obj_name = model.get('file_name', '') |
|
obj_path = os.path.join(original_obj_pid_dir, obj_name) |
|
|
|
mtl_name_temp = obj_name |
|
separator = "_P" |
|
index = mtl_name_temp.find(separator) |
|
if index != -1: |
|
old_mtl_name = mtl_name_temp[:index] |
|
else: |
|
old_mtl_name = mtl_name_temp # 或者你希望的其他处理逻辑,比如直接使用原字符串或报错 |
|
old_mtl_name = f"{old_mtl_name}.mtl" |
|
old_mtl_path = os.path.join(original_obj_pid_dir, old_mtl_name) |
|
|
|
bpy.ops.wm.obj_import(filepath=obj_path) |
|
imported_object = bpy.context.object |
|
|
|
if imported_object is None or imported_object.type != 'MESH': |
|
print(f"警告: 未能成功导入网格对象 {obj_name}。跳过。") |
|
continue |
|
|
|
mesh_data = imported_object.data |
|
mesh_data.calc_loop_triangles() |
|
original_vertices = np.empty(len(mesh_data.vertices) * 3, dtype=np.float64) |
|
mesh_data.vertices.foreach_get('co', original_vertices) |
|
original_vertices = original_vertices.reshape(-1, 3) |
|
|
|
transformed_vertices = mesh_transform_by_matrix(original_vertices, reconstructed_matrix) |
|
if need_offset: |
|
offset = np.array([-small_machine_size[0], -small_machine_size[1], 0]) |
|
transformed_vertices += offset |
|
print(f"已对模型 {obj_name} 应用偏移: {offset}") |
|
|
|
flattened_verts = transformed_vertices.reshape(-1) |
|
mesh_data.vertices.foreach_set('co', flattened_verts) |
|
|
|
mesh_data.update() |
|
|
|
meshes.append(imported_object) |
|
|
|
# 导出前确保选中当前对象 |
|
bpy.ops.object.select_all(action='DESELECT') |
|
imported_object.select_set(True) |
|
bpy.context.view_layer.objects.active = imported_object |
|
bpy.ops.wm.obj_export( |
|
filepath=obj_path, |
|
export_selected_objects=True, |
|
export_materials=True, |
|
path_mode='AUTO' |
|
) |
|
|
|
os.remove(old_mtl_path) |
|
|
|
def transform_save_o3d(layout_data, original_obj_pid_dir): |
|
meshes = [] |
|
# 小打印机需要偏移[-small_machine_size[0], -small_machine_size[1], 0] |
|
need_offset = True |
|
for model in layout_data["models"]: |
|
transform = model.get('transform', {}) |
|
|
|
homo_matrix = transform["homo_matrix"] # 获取存储的列表 |
|
reconstructed_matrix = np.array(homo_matrix, dtype=np.float64) |
|
|
|
obj_name = model.get('file_name', '') |
|
obj_path = os.path.join(original_obj_pid_dir, obj_name) |
|
# 加载网格 |
|
try: |
|
mesh = o3d.io.read_triangle_mesh(obj_path, enable_post_processing=True) |
|
if not mesh.has_vertices(): |
|
print(f"警告: 网格无有效顶点 - {obj_path}") |
|
continue |
|
except Exception as e: |
|
print(f"加载模型失败: {obj_path} - {e}") |
|
continue |
|
|
|
original_vertices = np.asarray(mesh.vertices) |
|
|
|
transformed_vertices = mesh_transform_by_matrix(original_vertices, reconstructed_matrix) |
|
# 如果 need_offset 为 True,应用额外的偏移 |
|
if need_offset: |
|
offset = np.array([-small_machine_size[0], -small_machine_size[1], 0]) |
|
transformed_vertices += offset |
|
print(f"已对模型 {obj_name} 应用偏移: {offset}") |
|
|
|
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices) |
|
|
|
meshes.append(mesh) |
|
|
|
obj_path_arrange = os.path.join(original_obj_pid_dir, "arrange") |
|
if not os.path.exists(obj_path_arrange): |
|
os.mkdir(obj_path_arrange) |
|
obj_path_arrange_obj = os.path.join(obj_path_arrange, obj_name) |
|
print("obj_path_arrange_obj", obj_path_arrange_obj) |
|
mesh.compute_vertex_normals() |
|
o3d.io.write_triangle_mesh(obj_path_arrange_obj, mesh,write_triangle_uvs=True) |
|
|
|
voxel_size = 3 |
|
|
|
def is_multi_obj(obj_name): |
|
pattern = r'_x(\d+)' |
|
match = re.search(pattern, obj_name) |
|
if match: |
|
number = match.group(1) |
|
|
|
if int(number) > 1 : |
|
return True |
|
|
|
return False |
|
|
|
def is_same_obj(obj_name1, obj_name2): |
|
|
|
pre_name1 = obj_name1.split("_x")[0] |
|
pre_name2 = obj_name2.split("_x")[0] |
|
|
|
print(f"pre_name1={pre_name1}, pre_name2={pre_name2}") |
|
|
|
if (pre_name1==pre_name2): |
|
return True |
|
|
|
return False |
|
|
|
# -------------------------- 结束:模型 ---------------------------------- |
|
|
|
# -------------------------- 开始:碰撞检测和越界 -------------------------- |
|
|
|
extend_dist_border_x_min = 6 |
|
extend_dist_border_x_max = 3 |
|
extend_dist_border_z_max = 3 |
|
extend_dist_border_y_min = 6 |
|
extend_dist_border_y_max = 6 |
|
extend_dist_model_x = 4 |
|
extend_dist_model_y = 2 |
|
extend_dist_model_z = 2 |
|
extend_dist_box = 1 |
|
def is_cross_border_c(x, y, z, mx, my, mz, max_x, max_y, max_z): |
|
if (x - mx < extend_dist_border_x_min or |
|
y - my < extend_dist_border_y_min or |
|
z + mz > max_z - extend_dist_border_z_max or |
|
y > max_y - extend_dist_border_y_max): |
|
return True |
|
|
|
return False |
|
|
|
# -------------------------- 结束:碰撞检测和越界 --------------------------
|
|
|