Ship the WeChat hot-update catalogs and the camp, TideWhale T2, UI atlas, and animation-lab work.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-10-24 18:04:41 +08:00
co-authored by Cursor
parent 6ebc9401ce
commit 45e62132c8
1432 changed files with 2971856 additions and 2798 deletions
@@ -0,0 +1,81 @@
"""Weld coincident geometry, retain corner UV seams, then collapse to target.
Studio GLBs split vertices at normals and UV seams. Decimating those as loose
triangles drops patches rather than simplifying a continuous surface.
"""
import hashlib
import json
import sys
from pathlib import Path
import bpy
import bmesh
source, target, requested = sys.argv[sys.argv.index('--') + 1:][:3]
source, target, requested = Path(source), Path(target), int(requested)
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.gltf(filepath=str(source), merge_vertices=False)
objects = [o for o in bpy.context.scene.objects if o.type == 'MESH']
assert len(objects) == 1
obj = objects[0]
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)
assert obj.data.uv_layers.active
def corners():
# Reordering by bmesh is harmless; the (position, per-corner UV) multiset
# must stay the same through welding, including UV seam discontinuities.
uv = obj.data.uv_layers.active.data
return sorted(tuple(round(c, 6) for c in (*obj.data.vertices[l.vertex_index].co, *uv[l.index].uv)) for l in obj.data.loops)
before_corners = corners()
vertices_before = len(obj.data.vertices)
bm = bmesh.new()
bm.from_mesh(obj.data)
bmesh.ops.remove_doubles(bm, verts=list(bm.verts), dist=max(obj.dimensions) * 1e-7)
bm.to_mesh(obj.data)
bm.free()
obj.data.update()
assert before_corners == corners(), 'Welding changed the existing corner UV mapping'
vertices_welded = len(obj.data.vertices)
tri = obj.modifiers.new('Triangulate', 'TRIANGULATE')
bpy.ops.object.modifier_apply(modifier=tri.name)
before = len(obj.data.polygons)
area_before = sum(p.area for p in obj.data.polygons)
modifier = obj.modifiers.new('PreserveCornerUVDecimate', 'DECIMATE')
modifier.decimate_type = 'COLLAPSE'
modifier.use_collapse_triangulate = True
lower, upper = 0.0, 1.0
candidate = min(1.0, requested / before)
best = None
for _ in range(22):
modifier.ratio = candidate
bpy.context.view_layer.update()
count = len(obj.evaluated_get(bpy.context.evaluated_depsgraph_get()).data.polygons)
rank = (abs(count - requested), count > requested)
if best is None or rank < best[0]: best = (rank, candidate)
if count == requested: break
if count > requested: upper = candidate
else: lower = candidate
candidate = (lower + upper) / 2
modifier.ratio = best[1]
bpy.ops.object.modifier_apply(modifier=modifier.name)
after = len(obj.data.polygons)
area_after = sum(p.area for p in obj.data.polygons)
print('Final faces', after, 'target', requested, 'best', best, flush=True)
assert after <= requested * 1.30, (after, requested, best)
assert area_after >= area_before * .80, 'Reduction lost too much surface area'
assert obj.data.uv_layers.active
bpy.ops.export_scene.gltf(filepath=str(target), export_format='GLB', use_selection=True,
export_materials='EXPORT', export_image_format='AUTO')
report = dict(faces_before=before, faces_after=after, target_faces=requested,
vertices_before=vertices_before, vertices_after_weld=vertices_welded,
cornerUVPreservedDuringWeld=True, uv_mode='preserved_no_unwrap',
area_before=area_before, area_after=area_after, area_ratio=area_after / area_before,
ratio=best[1], threshold_percent=30, triggered=True)
target.with_suffix('.json').write_text(json.dumps(report, indent=2), encoding='utf-8')
print('MT_SUMMARY ' + json.dumps(report), flush=True)