Blender Python导入HGHT格式时顶点坐标错误问题求助
问题分析与解决方案
核心问题诊断
你的脚本存在两个关键问题导致右上角顶点Z值始终为0.0:
- Z-order相邻块计算错误:当前位运算逻辑无法正确获取正上方和右侧的相邻块,导致从错误的块中读取顶点数据,这些块的对应顶点Z值可能未正确导入(默认0.0)。
- 顶点依赖时序问题:右上角顶点对应的相邻块顶点本身属于边缘顶点,若相邻块未被提前处理,其Z值仍为初始的0.0,导致当前块无法获取正确值。
修复步骤
1. 替换错误的Z-order相邻块计算逻辑
当前位运算方式无法正确计算Z-order曲线的相邻块,应先将Z-order值解码为(x,y)坐标,修改坐标后重新编码:
def z_order_to_xy(z): """将32位Z-order值解码为(x,y)坐标""" x = 0 y = 0 for i in range(16): # 提取x的第i位:Z-order的偶数位 x |= ((z >> (2 * i)) & 1) << i # 提取y的第i位:Z-order的奇数位 y |= ((z >> (2 * i + 1)) & 1) << i return x, y def xy_to_z_order(x, y): """将(x,y)坐标编码为32位Z-order值""" z = 0 for i in range(16): # 将x的第i位放入Z-order的偶数位 z |= ((x >> i) & 1) << (2 * i) # 将y的第i位放入Z-order的奇数位 z |= ((y >> i) & 1) << (2 * i + 1) return z
在脚本中使用这两个函数获取相邻块:
z = int(obj.name[2:], base=16) x, y = z_order_to_xy(z) # 上方块:y坐标减1 top = xy_to_z_order(x, y - 1) # 右侧块:x坐标加1 right = xy_to_z_order(x + 1, y) # 简化块名称生成 top_tile_name = f"{obj.name[:2]}{top:08X}" right_tile_name = f"{obj.name[:2]}{right:08X}"
2. 优化相邻块查找效率
提前创建块名称到对象的字典,避免每次遍历所有对象:
# 循环外构建字典 obj_dict = {obj.name: obj for obj in objects} # 循环内查找相邻块 if top_tile_name in obj_dict: top_tile_obj = obj_dict[top_tile_name] # ...后续处理
3. 分阶段处理顶点,解决依赖问题
右上角顶点对应的相邻块顶点均为边缘顶点,需分两轮处理:
- 第一轮:处理所有非角落边缘顶点,这些顶点可从相邻块的内部顶点获取有效值。
- 第二轮:处理所有块的角落顶点,取相邻块已处理的边缘顶点值(若都存在则取平均值)。
4. 释放Bmesh资源
每次使用bmesh.new()创建对象后,调用bm.free()释放内存,避免泄漏。
完整修复后的脚本
def z_order_to_xy(z): x = 0 y = 0 for i in range(16): x |= ((z >> (2 * i)) & 1) << i y |= ((z >> (2 * i + 1)) & 1) << i return x, y def xy_to_z_order(x, y): z = 0 for i in range(16): z |= ((x >> i) & 1) << (2 * i) z |= ((y >> i) & 1) << (2 * i + 1) return z # 提前构建块名称字典 obj_dict = {obj.name: obj for obj in objects} # 第一轮:处理非角落边缘顶点 for obj in objects: z = int(obj.name[2:], base=16) x, y = z_order_to_xy(z) top = xy_to_z_order(x, y - 1) right = xy_to_z_order(x + 1, y) top_tile_name = f"{obj.name[:2]}{top:08X}" right_tile_name = f"{obj.name[:2]}{right:08X}" mesh = bmesh.new() mesh.from_mesh(obj.data) mesh.verts.ensure_lookup_table() # 处理顶部边缘(排除右上角) if top_tile_name in obj_dict: top_tile = bmesh.new() top_tile.from_mesh(obj_dict[top_tile_name].data) top_tile.verts.ensure_lookup_table() for x_coord in range(256): curr_vert_idx = x_coord + 256 * 257 top_vert_idx = x_coord mesh.verts[curr_vert_idx].co.z = top_tile.verts[top_vert_idx].co.z top_tile.free() # 处理右侧边缘(排除右上角) if right_tile_name in obj_dict: right_tile = bmesh.new() right_tile.from_mesh(obj_dict[right_tile_name].data) right_tile.verts.ensure_lookup_table() for y_coord in range(256): curr_vert_idx = 256 + y_coord * 257 right_vert_idx = y_coord * 257 mesh.verts[curr_vert_idx].co.z = right_tile.verts[right_vert_idx].co.z right_tile.free() mesh.to_mesh(obj.data) obj.data.update() mesh.free() # 第二轮:处理所有块的右上角顶点 for obj in objects: z = int(obj.name[2:], base=16) x, y = z_order_to_xy(z) top = xy_to_z_order(x, y - 1) right = xy_to_z_order(x + 1, y) top_tile_name = f"{obj.name[:2]}{top:08X}" right_tile_name = f"{obj.name[:2]}{right:08X}" mesh = bmesh.new() mesh.from_mesh(obj.data) mesh.verts.ensure_lookup_table() corner_idx = 256 + 256 * 257 z_candidates = [] # 从上方块取右边缘顶点值 if top_tile_name in obj_dict: top_tile = bmesh.new() top_tile.from_mesh(obj_dict[top_tile_name].data) top_tile.verts.ensure_lookup_table() top_corner_idx = 256 + 0 * 257 val = top_tile.verts[top_corner_idx].co.z if val != 0.0: z_candidates.append(val) top_tile.free() # 从右侧块取顶部边缘顶点值 if right_tile_name in obj_dict: right_tile = bmesh.new() right_tile.from_mesh(obj_dict[right_tile_name].data) right_tile.verts.ensure_lookup_table() right_corner_idx = 0 + 256 * 257 val = right_tile.verts[right_corner_idx].co.z if val != 0.0: z_candidates.append(val) right_tile.free() # 取有效候选值的平均值 if z_candidates: mesh.verts[corner_idx].co.z = sum(z_candidates) / len(z_candidates) mesh.to_mesh(obj.data) obj.data.update() mesh.free()
内容的提问来源于stack exchange,提问作者guiguig
相关产品推荐
相关产品推荐

