Load FBX characters directly: in-app conversion to GLB
The model button now accepts .fbx too: a new model:convert IPC runs Blender with scripts/fbx2modelglb.py (mesh + skin kept, animations dropped, embedded diffuse recovered even when the FBX references missing external textures) into userData/models3d, then the resulting GLB loads as usual. anim/model conversion share one convertFbx helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a5c0a8b297
commit
d329759961
6 files changed
+93
-19
No files matched your search
@@ -0,0 +1,60 @@
|
||||
# Convert a character FBX (e.g. a Mixamo T-pose download) into a GLB model
|
||||
# for DJ Visualizer: mesh + skeleton/skin kept, animations dropped (the dance
|
||||
# library provides the moves), embedded diffuse texture recovered even when
|
||||
# the FBX references missing external files.
|
||||
# blender -b -P fbx2modelglb.py -- input.fbx output.glb
|
||||
import bpy, sys, os
|
||||
argv = sys.argv[sys.argv.index('--')+1:]
|
||||
SRC, OUT = argv[0], argv[1]
|
||||
TMP = OUT + '.diffuse.png'
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
bpy.ops.import_scene.fbx(filepath=SRC)
|
||||
arm = None; meshes = []
|
||||
for o in bpy.data.objects:
|
||||
if o.type == 'ARMATURE' and arm is None:
|
||||
arm = o
|
||||
if o.type == 'MESH':
|
||||
meshes.append(o)
|
||||
if not meshes:
|
||||
raise SystemExit('nessuna mesh nel file')
|
||||
# drop any animation, keep the rest pose
|
||||
for o in bpy.data.objects:
|
||||
o.animation_data_clear()
|
||||
for a in list(bpy.data.actions):
|
||||
bpy.data.actions.remove(a)
|
||||
if arm:
|
||||
for pb in arm.pose.bones:
|
||||
pb.location = (0, 0, 0)
|
||||
pb.rotation_quaternion = (1, 0, 0, 0)
|
||||
pb.scale = (1, 1, 1)
|
||||
# recover the diffuse: prefer an embedded image that actually has data
|
||||
valid = [i for i in bpy.data.images if i.has_data and i.size[0] > 0]
|
||||
diff = next((i for i in valid if 'Diffuse' in i.name), valid[0] if valid else None)
|
||||
if diff:
|
||||
try:
|
||||
diff.filepath_raw = TMP
|
||||
diff.file_format = 'PNG'
|
||||
diff.save()
|
||||
diff = bpy.data.images.load(TMP)
|
||||
diff.pack()
|
||||
mat = bpy.data.materials.new('skin')
|
||||
mat.use_nodes = True
|
||||
bsdf = next(n for n in mat.node_tree.nodes if n.type == 'BSDF_PRINCIPLED')
|
||||
tex = mat.node_tree.nodes.new('ShaderNodeTexImage')
|
||||
tex.image = diff
|
||||
mat.node_tree.links.new(bsdf.inputs['Base Color'], tex.outputs['Color'])
|
||||
for m in meshes:
|
||||
m.data.materials.clear()
|
||||
m.data.materials.append(mat)
|
||||
except Exception as e:
|
||||
print('texture non recuperata:', e)
|
||||
try:
|
||||
bpy.ops.export_scene.gltf(filepath=OUT, export_format='GLB', export_animations=False,
|
||||
export_skins=True, export_rest_position_armature=True)
|
||||
except TypeError:
|
||||
bpy.ops.export_scene.gltf(filepath=OUT, export_format='GLB', export_animations=False, export_skins=True)
|
||||
try:
|
||||
os.remove(TMP)
|
||||
except OSError:
|
||||
pass
|
||||
print('EXPORT_OK')
|
||||
Reference in new issue
Block a user