Files
AIGame/Tools/model_tools/bl_repair_homecamp_surface.py

43 lines
2.2 KiB
Python

"""Repair Studio face winding on welded topology without generating UVs."""
import json
import sys
from pathlib import Path
import bpy
import bmesh
source, destination, kind = sys.argv[sys.argv.index('--') + 1:][:3]
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.gltf(filepath=source, merge_vertices=False)
obj = next(o for o in bpy.context.scene.objects if o.type == 'MESH')
bpy.context.view_layer.objects.active = obj
bpy.ops.object.select_all(action='DESELECT'); obj.select_set(True)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
uv = obj.data.uv_layers.active
uv_before = sorted(tuple(round(c, 6) for c in v.uv) for v in uv.data)
bm = bmesh.new(); bm.from_mesh(obj.data)
bmesh.ops.remove_doubles(bm, verts=list(bm.verts), dist=max(obj.dimensions) * 1e-7)
bmesh.ops.recalc_face_normals(bm, faces=list(bm.faces))
flipped = 0
if kind == 'ground':
# Thin terrain is intentionally authored with a top and underside. Some
# Studio patches are disconnected, so orient each flat patch explicitly.
low = min(v.co.z for v in bm.verts); high = max(v.co.z for v in bm.verts)
midpoint = (low + high) / 2
for face in bm.faces:
face.normal_update()
if abs(face.normal.z) > .35:
# Terrain is viewed and collided from above. Its paving can be
# lower than raised grass, so a global height midpoint cannot
# distinguish the paving top from an underside patch.
if face.normal.z < 0:
face.normal_flip(); flipped += 1
bm.to_mesh(obj.data); bm.free(); obj.data.update()
uv_after = sorted(tuple(round(c, 6) for c in v.uv) for v in obj.data.uv_layers.active.data)
assert uv_before == uv_after, 'UV coordinates changed during winding repair'
bpy.ops.export_scene.gltf(filepath=destination, export_format='GLB', use_selection=True,
export_materials='EXPORT', export_image_format='AUTO')
report = dict(kind=kind, uvPreserved=True, flatTerrainFacesFlipped=flipped,
triangles=sum(len(p.vertices)-2 for p in obj.data.polygons))
Path(destination).with_suffix('.json').write_text(json.dumps(report, indent=2), encoding='utf-8')
print('MT_SUMMARY '+json.dumps(report), flush=True)