ModelTranslator: use adaptive ray distance in bake (auto), report source

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ud18010
2026-07-23 17:08:58 +08:00
co-authored by Claude Opus 4.8
parent 3ae981e40d
commit e66ba296c1
2 changed files with 53 additions and 2 deletions
+48 -2
View File
@@ -98,6 +98,30 @@ def _import_fbx_joined(path, warnings):
return obj 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): def _setup_bake_target(low, stem):
"""低模换成单一烘焙材质,返回接收烘焙结果的 image 节点。""" """低模换成单一烘焙材质,返回接收烘焙结果的 image 节点。"""
import bpy import bpy
@@ -210,7 +234,25 @@ def main():
% (BBOX_TOL * 100, % (BBOX_TOL * 100,
[round(d, 3) for d in high_dims], [round(d, 3) for d in high_dims],
[round(d, 3) for d in low_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]) stem = output_stem(os.path.splitext(os.path.basename(low_fbx))[0])
os.makedirs(outdir, exist_ok=True) os.makedirs(outdir, exist_ok=True)
@@ -238,9 +280,13 @@ def main():
bpy.ops.export_scene.fbx(filepath=out_fbx, use_selection=True, bpy.ops.export_scene.fbx(filepath=out_fbx, use_selection=True,
path_mode='STRIP', embed_textures=False) 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( print("MT_SUMMARY " + json.dumps(
{"stem": stem, "fbx": os.path.basename(out_fbx), "outputs": outputs, {"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), "high_tris": len(high.data.polygons), "low_tris": len(low.data.polygons),
"warnings": warnings}, ensure_ascii=False)) "warnings": warnings}, ensure_ascii=False))
+5
View File
@@ -33,6 +33,11 @@ def main():
blender_args=("-t", "1")) blender_args=("-t", "1"))
print("== %s(高 %d 面 / 低 %d 面,射线距离 %.4f-> %s" % print("== %s(高 %d 面 / 低 %d 面,射线距离 %.4f-> %s" %
(s["stem"], s["high_tris"], s["low_tris"], s["ray_distance"], outdir)) (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(): for name, fname in s["outputs"].items():
print(" %-10s %s" % (name, fname)) print(" %-10s %s" % (name, fname))
for w in s["warnings"]: for w in s["warnings"]: