import os def process_obj_file(input_file, output_file): """ 处理OBJ文件,使用顶点x,y坐标映射纹理UV坐标,忽略z值,同时保持原有文件结构和注释顺序 参数: input_file: 输入OBJ文件路径 output_file: 输出OBJ文件路径 """ # 检查输入文件是否存在 if not os.path.exists(input_file): print(f"错误: 输入文件 {input_file} 不存在") return False try: with open(input_file, 'r', encoding='utf-8') as f_in: lines = f_in.readlines() # 提取顶点坐标并计算UV坐标 vertices = [] # 存储顶点坐标 (x, y) vertex_lines = [] # 存储原始的顶点行 for line in lines: stripped_line = line.strip() if stripped_line.startswith('v ') and not stripped_line.startswith('vt ') and not stripped_line.startswith('vn '): parts = stripped_line.split() if len(parts) >= 3: # 至少要有v x y try: x = float(parts[1]) y = float(parts[2]) vertices.append((x, y)) vertex_lines.append(line.rstrip()) except ValueError: # 如果解析失败,使用默认值 vertices.append((0.0, 0.0)) vertex_lines.append(line.rstrip()) # 计算UV坐标的边界范围 if vertices: x_coords = [v[0] for v in vertices] y_coords = [v[1] for v in vertices] min_x, max_x = min(x_coords), max(x_coords) min_y, max_y = min(y_coords), max(y_coords) # 避免除零错误 x_range = max_x - min_x if max_x != min_x else 1.0 y_range = max_y - min_y if max_y != min_y else 1.0 # 为每个顶点生成UV坐标(基于x,y坐标映射到0-1范围) uv_coords = [] for x, y in vertices: # 将x,y坐标归一化到0-1范围 u = (x - min_x) / x_range v = (y - min_y) / y_range uv_coords.append(f"vt {u:.6f} {v:.6f}") else: uv_coords = [] # 处理每一行,保持原有顺序 processed_lines = [] current_vertex_index = 0 face_count = 0 for line in lines: original_line = line.rstrip() # 去除右侧空白,保持左侧缩进 # 如果是顶点数据,保持原样 if original_line.startswith('v ') and not original_line.startswith('vt ') and not original_line.startswith('vn '): processed_lines.append(original_line) current_vertex_index += 1 # 如果是面数据,为每个顶点引用对应的纹理坐标 elif original_line.startswith('f '): face_parts = original_line.split() if len(face_parts) >= 4: # 至少应该有 "f" 和三个顶点 # 重建面数据,为每个顶点添加对应的纹理坐标索引 new_face_parts = ['f'] vertex_indices = [] # 首先提取所有顶点索引 for part in face_parts[1:]: if '//' in part: # 格式: v//vn v_index, vn_index = part.split('//') vertex_indices.append(int(v_index)) elif '/' in part: # 格式: v/vt/vn 或 v/vt parts = part.split('/') vertex_indices.append(int(parts[0])) else: # 只有顶点索引 vertex_indices.append(int(part)) # 为每个顶点构建新的面数据 for j, part in enumerate(face_parts[1:]): vertex_index = vertex_indices[j] if '//' in part: # 格式: v//vn v_index, vn_index = part.split('//') # 使用顶点索引作为纹理坐标索引(因为是一一对应的) new_part = f"{v_index}/{vertex_index}//{vn_index}" elif '/' in part: # 格式: v/vt/vn 或 v/vt parts = part.split('/') if len(parts) == 2: # v/vt new_part = f"{parts[0]}/{vertex_index}" else: # v/vt/vn new_part = f"{parts[0]}/{vertex_index}/{parts[2]}" else: # 只有顶点索引 new_part = f"{part}/{vertex_index}" new_face_parts.append(new_part) processed_lines.append(' '.join(new_face_parts)) face_count += 1 else: processed_lines.append(original_line) else: processed_lines.append(original_line) # 在顶点数据之后插入纹理坐标定义 output_lines = [] vertices_ended = False uv_inserted = False vertex_count = 0 for i, line in enumerate(processed_lines): output_lines.append(line) # 统计顶点数量 if line.startswith('v ') and not line.startswith('vt ') and not line.startswith('vn '): vertex_count += 1 # 检测顶点数据块的结束(当遇到非顶点数据时) if not vertices_ended and line.startswith('v ') and not line.startswith('vt ') and not line.startswith('vn '): # 当前行是顶点数据,继续 pass elif not vertices_ended and i > 0 and vertex_count > 0: # 当前行不是顶点数据,且我们已经处理过顶点数据 # 这意味着顶点数据块已经结束 vertices_ended = True # 在顶点数据块结束后插入纹理坐标 if not uv_inserted and uv_coords: output_lines.append("") # 空行分隔 output_lines.append("# 添加的纹理坐标 (基于顶点x,y坐标映射,忽略z值)") output_lines.append(f"# UV坐标范围: x[{min_x:.6f}-{max_x:.6f}] -> u[0-1], y[{min_y:.6f}-{max_y:.6f}] -> v[0-1]") for uv in uv_coords: output_lines.append(uv) uv_inserted = True # 如果文件中顶点数据在最后,在最后插入UV坐标 if not uv_inserted and uv_coords: output_lines.append("") # 空行分隔 output_lines.append("# 添加的纹理坐标 (基于顶点x,y坐标映射,忽略z值)") output_lines.append(f"# UV坐标范围: x[{min_x:.6f}-{max_x:.6f}] -> u[0-1], y[{min_y:.6f}-{max_y:.6f}] -> v[0-1]") for uv in uv_coords: output_lines.append(uv) # 写入输出文件 with open(output_file, 'w', encoding='utf-8') as f_out: for line in output_lines: f_out.write(line + '\n') print(f"成功处理文件: {input_file} -> {output_file}") print(f"顶点数量: {len(vertices)}") print(f"处理面数量: {face_count}") print(f"生成的纹理坐标数量: {len(uv_coords)}") print(f"顶点X坐标范围: {min_x:.6f} 到 {max_x:.6f}") print(f"顶点Y坐标范围: {min_y:.6f} 到 {max_y:.6f}") return True except Exception as e: print(f"处理文件时出错: {e}") import traceback traceback.print_exc() return False if __name__ == "__main__": input_file = "face.obj" # 默认输入文件 output_file = "face_with_uv.obj" # 默认输出文件 process_obj_file(input_file, output_file)