Files
workinf_Blender_Wasm/tools/web/generate-weight-paint-golden.py
mes123456 0fe8d2bb56
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

111 lines
3.8 KiB
Python

import json
import pathlib
import sys
import bpy
def group_weight(vertex, group_index):
return next((item.weight for item in vertex.groups if item.group == group_index), 0.0)
def set_weight(obj, vertex_index, group_index, value):
group = obj.vertex_groups[group_index]
if value == 0.0:
try:
group.remove([vertex_index])
except RuntimeError:
pass
else:
group.add([vertex_index], value, "REPLACE")
def normalize_vertex(obj, vertex_index):
vertex = obj.data.vertices[vertex_index]
total = sum(item.weight for item in vertex.groups)
if total > 0.0:
for item in list(vertex.groups):
set_weight(obj, vertex_index, item.group, item.weight / total)
def limit_vertex(obj, vertex_index, limit):
vertex = obj.data.vertices[vertex_index]
while len(vertex.groups) > limit:
remove = min(vertex.groups, key=lambda item: (item.weight, -item.group))
obj.vertex_groups[remove.group].remove([vertex_index])
def mirror_map(obj, axis, tolerance):
result = {}
for source in obj.data.vertices:
reflected = source.co.copy()
reflected[axis] = -reflected[axis]
candidate = min(obj.data.vertices, key=lambda item: ((item.co - reflected).length, item.index))
if (candidate.co - reflected).length > tolerance:
raise RuntimeError("mesh is not mirror symmetric")
result[source.index] = candidate.index
if any(result[result[index]] != index for index in result):
raise RuntimeError("mesh mirror map is not reciprocal")
return result
def snapshot(obj):
groups = [group.name for group in obj.vertex_groups]
return {
"groups": groups,
"vertices": [
{"index": vertex.index, "weights": {
groups[item.group]: round(item.weight, 9) for item in vertex.groups
}}
for vertex in obj.data.vertices
],
}
def main(blend_path, output_path):
bpy.ops.wm.open_mainfile(filepath=str(pathlib.Path(blend_path).resolve()), load_ui=False)
obj = bpy.data.objects.get("RiggedShapeObject")
if obj is None or obj.type != "MESH":
raise RuntimeError("weight paint fixture object is missing")
group = obj.vertex_groups.get("WebPaintGroup") or obj.vertex_groups.new(name="WebPaintGroup")
set_weight(obj, 0, group.index, 0.75)
set_weight(obj, 1, group.index, 0.25)
steps = [{"name": "initial", **snapshot(obj)}]
set_weight(obj, 2, group.index, 0.5)
normalize_vertex(obj, 2)
steps.append({"name": "normalize", **snapshot(obj)})
set_weight(obj, 3, group.index, 0.4)
limit_vertex(obj, 3, 2)
normalize_vertex(obj, 3)
steps.append({"name": "limit-normalize", **snapshot(obj)})
mapping = mirror_map(obj, 0, 1e-4)
for source in (0, mapping[0]):
set_weight(obj, source, group.index, 0.9)
steps.append({"name": "mirror", **snapshot(obj)})
result = {
"schemaVersion": 1,
"fixture": pathlib.Path(blend_path).name,
"blenderVersion": bpy.app.version_string,
"object": obj.name,
"mesh": obj.data.name,
"operations": [
{"name": "normalize", "vertex": 2},
{"name": "limit-normalize", "vertex": 3, "limit": 2},
{"name": "mirror", "vertices": [0, 1], "axis": 0, "tolerance": 1e-4},
],
"steps": steps,
"tolerance": 1e-6,
}
pathlib.Path(output_path).write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8")
if __name__ == "__main__":
if "--" not in sys.argv or len(sys.argv) <= sys.argv.index("--") + 2:
raise SystemExit("usage: blender -b --python generate-weight-paint-golden.py -- input.blend output.json")
main(*sys.argv[sys.argv.index("--") + 1:sys.argv.index("--") + 3])