diff --git a/Tools/ModelTranslator/bl_bake.py b/Tools/ModelTranslator/bl_bake.py index f857c355..a923cd9c 100644 --- a/Tools/ModelTranslator/bl_bake.py +++ b/Tools/ModelTranslator/bl_bake.py @@ -98,6 +98,30 @@ def _import_fbx_joined(path, warnings): return obj +def _measure_separation(low, high, limit=10000): + """低模面心到高模表面最近距离数组(供自适应射线)。超 limit 面则均匀采样。 + 高模 BVH 无命中的面丢弃;返回距离列表(可能为空)。""" + from mathutils.bvhtree import BVHTree + hv = [high.matrix_world @ v.co for v in high.data.vertices] + hp = [tuple(p.vertices) for p in high.data.polygons] + bvh = BVHTree.FromPolygons(hv, hp, all_triangles=False) + polys = low.data.polygons + n = len(polys) + if n == 0: + return [] + if n <= limit: + idxs = range(n) + else: + idxs = [round(i * (n - 1) / float(limit - 1)) for i in range(limit)] + mw = low.matrix_world + dists = [] + for i in idxs: + hit = bvh.find_nearest(mw @ polys[i].center) + if hit[0] is not None and hit[3] is not None: + dists.append(hit[3]) + return dists + + def _setup_bake_target(low, stem): """低模换成单一烘焙材质,返回接收烘焙结果的 image 节点。""" import bpy @@ -210,7 +234,25 @@ def main(): % (BBOX_TOL * 100, [round(d, 3) for d in high_dims], [round(d, 3) for d in low_dims])) - ray = estimate_ray_distance(low_dims) if ray_arg == "auto" else float(ray_arg) + if ray_arg != "auto": + ray = float(ray_arg) + ray_info = {"source": "explicit", "distance_p99": None, + "value": ray, "capped": False} + else: + dists = _measure_separation(low, high) + try: + if not dists: + raise ValueError("低模面心到高模无有效最近距离") + ray_info = adaptive_ray_distance(dists, low_dims) + ray_info["source"] = "adaptive" + ray = ray_info["value"] + if ray_info["capped"]: + warnings.append("自适应射线达包围盒对角线 1% 上限,请检查高低模对应关系") + except ValueError as e: + ray = estimate_ray_distance(low_dims) + ray_info = {"source": "fixed_fallback", "distance_p99": None, + "value": ray, "capped": False} + warnings.append("自适应射线测量失败(%s),回退固定射线" % e) stem = output_stem(os.path.splitext(os.path.basename(low_fbx))[0]) os.makedirs(outdir, exist_ok=True) @@ -238,9 +280,13 @@ def main(): bpy.ops.export_scene.fbx(filepath=out_fbx, use_selection=True, path_mode='STRIP', embed_textures=False) + ray_info = dict(ray_info) + ray_info["distance_p99"] = (None if ray_info["distance_p99"] is None + else round(ray_info["distance_p99"], 6)) + ray_info["value"] = round(ray_info["value"], 6) print("MT_SUMMARY " + json.dumps( {"stem": stem, "fbx": os.path.basename(out_fbx), "outputs": outputs, - "size": size, "ray_distance": round(ray, 6), + "size": size, "ray_distance": round(ray, 6), "ray": ray_info, "high_tris": len(high.data.polygons), "low_tris": len(low.data.polygons), "warnings": warnings}, ensure_ascii=False)) diff --git a/Tools/ModelTranslator/model_bake.py b/Tools/ModelTranslator/model_bake.py index 39afb0f2..03465f4d 100644 --- a/Tools/ModelTranslator/model_bake.py +++ b/Tools/ModelTranslator/model_bake.py @@ -33,6 +33,11 @@ def main(): blender_args=("-t", "1")) print("== %s(高 %d 面 / 低 %d 面,射线距离 %.4f)-> %s" % (s["stem"], s["high_tris"], s["low_tris"], s["ray_distance"], outdir)) + r = s.get("ray", {}) + p99 = "n/a" if r.get("distance_p99") is None else "%.4f" % r["distance_p99"] + print(" 射线: 来源=%s P99=%s 距离=%.4f%s" % + (r.get("source", "?"), p99, r.get("value", s["ray_distance"]), + "(已夹取)" if r.get("capped") else "")) for name, fname in s["outputs"].items(): print(" %-10s %s" % (name, fname)) for w in s["warnings"]: