Add Chromium-only Blender WebEngine parity work
This commit is contained in:
144
tools/web/blender-check-glb-roundtrip.py
Normal file
144
tools/web/blender-check-glb-roundtrip.py
Normal file
@@ -0,0 +1,144 @@
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def rounded(values):
|
||||
return [round(float(value), 6) for value in values]
|
||||
|
||||
|
||||
def socket_value(node, name, fallback=None):
|
||||
if node is None or name not in node.inputs:
|
||||
return fallback
|
||||
value = node.inputs[name].default_value
|
||||
return round(float(value), 6) if not hasattr(value, "__len__") else rounded(value)
|
||||
|
||||
|
||||
def main(glb_path, output_path):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
result = bpy.ops.import_scene.gltf(filepath=os.path.abspath(glb_path))
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"Blender glTF importer failed: {result}")
|
||||
|
||||
scene = bpy.context.scene
|
||||
scene.render.fps = 24
|
||||
mesh_object = bpy.data.objects.get("RoundTripMesh")
|
||||
if mesh_object is None or mesh_object.type != "MESH":
|
||||
raise RuntimeError("RoundTripMesh was not imported")
|
||||
mesh = mesh_object.data
|
||||
mesh.calc_loop_triangles()
|
||||
|
||||
shape_keys = {}
|
||||
if mesh.shape_keys is not None:
|
||||
basis = mesh.shape_keys.key_blocks[0]
|
||||
for key in mesh.shape_keys.key_blocks[1:]:
|
||||
shape_keys[key.name] = [
|
||||
round((key.data[index].co - basis.data[index].co).length, 6)
|
||||
for index in range(len(key.data))
|
||||
]
|
||||
|
||||
material = mesh.materials[0] if mesh.materials else None
|
||||
principled = None
|
||||
texture = None
|
||||
if material and material.node_tree:
|
||||
principled = next((node for node in material.node_tree.nodes if node.type == "BSDF_PRINCIPLED"), None)
|
||||
texture = next(
|
||||
(node for candidate in bpy.data.materials if candidate.node_tree
|
||||
for node in candidate.node_tree.nodes if node.type == "TEX_IMAGE"),
|
||||
None,
|
||||
)
|
||||
|
||||
def animated_location(frame):
|
||||
scene.frame_set(frame)
|
||||
return rounded(mesh_object.matrix_world.translation)
|
||||
|
||||
armature = next((obj for obj in bpy.data.objects if obj.type == "ARMATURE"), None)
|
||||
skinned = bpy.data.objects.get("SkinnedMesh")
|
||||
weight_sums = []
|
||||
if skinned is not None and skinned.type == "MESH":
|
||||
for vertex in skinned.data.vertices:
|
||||
weight_sums.append(round(sum(group.weight for group in vertex.groups), 6))
|
||||
|
||||
image = texture.image if texture is not None else None
|
||||
report = {
|
||||
"mesh": {
|
||||
"vertexCount": len(mesh.vertices),
|
||||
"triangleCount": len(mesh.loop_triangles),
|
||||
"positions": [rounded(vertex.co) for vertex in mesh.vertices],
|
||||
"frame1WorldTranslation": animated_location(1),
|
||||
"frame25WorldTranslation": animated_location(25),
|
||||
"shapeKeys": shape_keys,
|
||||
},
|
||||
"material": {
|
||||
"name": material.name if material else None,
|
||||
"baseColor": rounded(material.diffuse_color) if material else [],
|
||||
"metallic": round(float(material.metallic), 6) if material else None,
|
||||
"roughness": round(float(material.roughness), 6) if material else None,
|
||||
"blendMethod": material.surface_render_method if material else None,
|
||||
"texture": {
|
||||
"present": texture is not None,
|
||||
"interpolation": texture.interpolation if texture else None,
|
||||
"extension": texture.extension if texture else None,
|
||||
"imageWidth": image.size[0] if image else 0,
|
||||
"imageHeight": image.size[1] if image else 0,
|
||||
"packed": bool(image and image.packed_file),
|
||||
"colorSpace": image.colorspace_settings.name if image else None,
|
||||
},
|
||||
"principled": {
|
||||
"baseColor": rounded(principled.inputs["Base Color"].default_value) if principled else [],
|
||||
"alpha": round(float(principled.inputs["Alpha"].default_value), 6) if principled else None,
|
||||
"metallic": round(float(principled.inputs["Metallic"].default_value), 6) if principled else None,
|
||||
"roughness": round(float(principled.inputs["Roughness"].default_value), 6) if principled else None,
|
||||
"ior": socket_value(principled, "IOR"),
|
||||
"specularIORLevel": socket_value(principled, "Specular IOR Level"),
|
||||
"transmissionWeight": socket_value(principled, "Transmission Weight"),
|
||||
"coatWeight": socket_value(principled, "Coat Weight"),
|
||||
"coatRoughness": socket_value(principled, "Coat Roughness"),
|
||||
"emissionStrength": socket_value(principled, "Emission Strength"),
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"type": node.type,
|
||||
"name": node.name,
|
||||
"inputs": {
|
||||
socket.name: rounded(socket.default_value) if hasattr(socket.default_value, "__len__") else round(float(socket.default_value), 6)
|
||||
for socket in node.inputs
|
||||
if hasattr(socket, "default_value")
|
||||
},
|
||||
}
|
||||
for node in material.node_tree.nodes
|
||||
] if material and material.node_tree else [],
|
||||
},
|
||||
"animation": {
|
||||
"actionCount": len(bpy.data.actions),
|
||||
"frameRange": rounded([scene.frame_start, scene.frame_end]),
|
||||
},
|
||||
"skin": {
|
||||
"armatureCount": sum(1 for obj in bpy.data.objects if obj.type == "ARMATURE"),
|
||||
"bones": sorted(bone.name for bone in armature.data.bones) if armature else [],
|
||||
"boneParents": {
|
||||
bone.name: bone.parent.name if bone.parent else None
|
||||
for bone in armature.data.bones
|
||||
} if armature else {},
|
||||
"modifierCount": len(skinned.modifiers) if skinned else 0,
|
||||
"vertexGroupCount": len(skinned.vertex_groups) if skinned else 0,
|
||||
"weightSums": weight_sums,
|
||||
},
|
||||
}
|
||||
with open(output_path, "w", encoding="utf-8") as handle:
|
||||
json.dump(report, handle, indent=2, sort_keys=True)
|
||||
handle.write("\n")
|
||||
print(
|
||||
"blender-glb-import-ok "
|
||||
f"vertices={report['mesh']['vertexCount']} bones={len(report['skin']['bones'])} "
|
||||
f"actions={report['animation']['actionCount']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python blender-check-glb-roundtrip.py -- input.glb output.json")
|
||||
main(arguments[0], arguments[1])
|
||||
Reference in New Issue
Block a user