Export: Different transform value of Leaf bone #112312

Closed
opened 2023-09-13 05:26:03 +02:00 by Jpzhao · 13 comments

System Information
Operating system: Windows 11
Graphics card: NVIDIA GeForce MX550

Blender Version
Broken: 3.4, 4.0
Worked: Don't know

Short description of error
Wrong coordinates of "head_tail" after importing the gltf file

Steps to Reproduce

  • Open attached file, export in gltf format
  • Import back the armature, notice the difference
Original report **Short description of error**

I get the wrong result when using bpy library to move bones. More specifically, the coordinates of "head_tail" in my code shown in the first picture is not the same as the result shown in the second picture.

image

image

Exact steps for others to reproduce the error

  1. download the .glb file
  2. run the code below (code and .glb file should be in the same folder, note that the python version is 3.10.8 and bpy==3.4.0, numpy==1.23.2.)
  3. open the generated file "test.glb" in blender.

code:

import bpy

class RiggingHelper(object):
    def __init__(self, armature_name):
        self.armature = bpy.data.objects[armature_name] # obArm
    
    # bone select function
    def select_bones_edit(self, bones_to_select, head=False, tail=False, body=False):
        # edit mode select bones
        # bones_to_select: list []
        bpy.ops.armature.select_all(action='DESELECT')
        for bone_name in bones_to_select:
            if head:
                self.armature.data.edit_bones[bone_name].select_head = True
            if tail:
                self.armature.data.edit_bones[bone_name].select_tail = True
            if body:
                self.armature.data.edit_bones[bone_name].select= True

    def active_bone_edit(self, bone_name):
        # edit mode
        self.armature.data.edit_bones.active = self.armature.data.edit_bones[bone_name]

    def rename_bone(self, ori_name, new_name):
        self.active_bone_edit(ori_name)
        bpy.context.active_bone.name = new_name

    def move_bone(self, bone_name, head_position, tail_position):
        # head_position and tail_position are three dimensitional tuple
        self.active_bone_edit(bone_name)
        self.armature.data.edit_bones[bone_name].head = head_position
        self.armature.data.edit_bones[bone_name].tail = tail_position

    def set_mode(self, mode='OBJECT'):
        # mode should be Capitalized
        bpy.ops.object.mode_set(mode=mode)
             
    def extrude_bone_edit(self, ori_bone_name, position, x=0, y=0, z=0):
        # position: 'head' or 'tail'
        if position == 'head':
            self.select_bones_edit([ori_bone_name], head=True)
        elif position == 'tail':
            self.select_bones_edit([ori_bone_name], tail=True)
        bpy.ops.armature.extrude_move(TRANSFORM_OT_translate={"value":(x, y, z)}) # deafult original position 

    def clear_parent_edit(self, bones_name_list, type='CLEAR'):
        # type, "CLEAR" or "DISCONNECT", default "CLEAR"
        # clear parent relationship
        self.select_bones_edit(bones_name_list, 
                        head=True, tail=True, body=True)
        bpy.ops.armature.parent_clear(type=type)

hip_head = (0.0060, -0.0106, 0.9484)
hip_tail = (0.0059, -0.0160, 1.0179)
spine_head = (0.0059, -0.0160, 1.0179)
spine_tail = (0.0059, -0.0213, 1.1300)
chest_head = (0.0059, -0.0213, 1.1300)
chest_tail = (0.0060, 0.0324, 1.4304)
neck_head = (0.0060, 0.0324, 1.4304)
neck_tail = (0.0061, 0.0581, 1.5430)
head_head = (0.0061, 0.0581, 1.5430)
head_tail = (0.0061, 0.0457, 1.7155)

bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
# import model
bpy.ops.import_scene.gltf(filepath="A0920230720170849.glb")
bpy.ops.object.armature_add()
helper = RiggingHelper('Armature')
obArm = bpy.data.objects['Armature']
bpy.context.view_layer.objects.active = obArm #
helper.set_mode('EDIT')
helper.rename_bone('Bone', 'Root')
helper.move_bone('Root', (0,0,0), (0, 0.3,0))
## 1.2 add spine bones--------
# extrude another bone from the root bone
helper.extrude_bone_edit('Root', 'head')
helper.rename_bone('Root.001', 'Hip')
helper.clear_parent_edit(['Hip'])

helper.extrude_bone_edit('Hip', 'tail')
helper.rename_bone('Hip.001', 'Spine')

helper.extrude_bone_edit('Spine', 'tail')
helper.rename_bone('Spine.001', 'Chest')

helper.extrude_bone_edit('Chest', 'tail')
helper.rename_bone('Chest.001', 'Neck')

helper.extrude_bone_edit('Neck', 'tail')
helper.rename_bone('Neck.001', 'Head')

# move & scale bones from hip to head
helper.move_bone('Hip', hip_head, hip_tail)
helper.move_bone('Spine', spine_head, spine_tail)
helper.move_bone('Chest', chest_head, chest_tail)
helper.move_bone('Neck', neck_head, neck_tail)
helper.move_bone('Head', head_head, head_tail)
bpy.ops.export_scene.gltf(filepath='test.glb')
**System Information** Operating system: Windows 11 Graphics card: NVIDIA GeForce MX550 **Blender Version** Broken: 3.4, 4.0 Worked: Don't know **Short description of error** Wrong coordinates of "head_tail" after importing the gltf file **Steps to Reproduce** - [Open attached file](https://projects.blender.org/attachments/50a7e6a1-10e5-472c-96d0-5b4674df5799), export in gltf format - Import back the armature, notice the difference <details> <summary> Original report </summary> **Short description of error** I get the wrong result when using bpy library to move bones. More specifically, the coordinates of "head_tail" in my code shown in the first picture is not the same as the result shown in the second picture. ![image](/attachments/bcd0c0f0-0781-4f5c-9cc2-0926bfd59de3) ![image](/attachments/5281e798-14d6-4180-a5ea-b61454e44fe9) **Exact steps for others to reproduce the error** 1. download the .glb file 2. run the code below (code and .glb file should be in the same folder, note that the python version is 3.10.8 and bpy==3.4.0, numpy==1.23.2.) 3. open the generated file "test.glb" in blender. code: ``` import bpy class RiggingHelper(object): def __init__(self, armature_name): self.armature = bpy.data.objects[armature_name] # obArm # bone select function def select_bones_edit(self, bones_to_select, head=False, tail=False, body=False): # edit mode select bones # bones_to_select: list [] bpy.ops.armature.select_all(action='DESELECT') for bone_name in bones_to_select: if head: self.armature.data.edit_bones[bone_name].select_head = True if tail: self.armature.data.edit_bones[bone_name].select_tail = True if body: self.armature.data.edit_bones[bone_name].select= True def active_bone_edit(self, bone_name): # edit mode self.armature.data.edit_bones.active = self.armature.data.edit_bones[bone_name] def rename_bone(self, ori_name, new_name): self.active_bone_edit(ori_name) bpy.context.active_bone.name = new_name def move_bone(self, bone_name, head_position, tail_position): # head_position and tail_position are three dimensitional tuple self.active_bone_edit(bone_name) self.armature.data.edit_bones[bone_name].head = head_position self.armature.data.edit_bones[bone_name].tail = tail_position def set_mode(self, mode='OBJECT'): # mode should be Capitalized bpy.ops.object.mode_set(mode=mode) def extrude_bone_edit(self, ori_bone_name, position, x=0, y=0, z=0): # position: 'head' or 'tail' if position == 'head': self.select_bones_edit([ori_bone_name], head=True) elif position == 'tail': self.select_bones_edit([ori_bone_name], tail=True) bpy.ops.armature.extrude_move(TRANSFORM_OT_translate={"value":(x, y, z)}) # deafult original position def clear_parent_edit(self, bones_name_list, type='CLEAR'): # type, "CLEAR" or "DISCONNECT", default "CLEAR" # clear parent relationship self.select_bones_edit(bones_name_list, head=True, tail=True, body=True) bpy.ops.armature.parent_clear(type=type) hip_head = (0.0060, -0.0106, 0.9484) hip_tail = (0.0059, -0.0160, 1.0179) spine_head = (0.0059, -0.0160, 1.0179) spine_tail = (0.0059, -0.0213, 1.1300) chest_head = (0.0059, -0.0213, 1.1300) chest_tail = (0.0060, 0.0324, 1.4304) neck_head = (0.0060, 0.0324, 1.4304) neck_tail = (0.0061, 0.0581, 1.5430) head_head = (0.0061, 0.0581, 1.5430) head_tail = (0.0061, 0.0457, 1.7155) bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete(use_global=False) # import model bpy.ops.import_scene.gltf(filepath="A0920230720170849.glb") bpy.ops.object.armature_add() helper = RiggingHelper('Armature') obArm = bpy.data.objects['Armature'] bpy.context.view_layer.objects.active = obArm # helper.set_mode('EDIT') helper.rename_bone('Bone', 'Root') helper.move_bone('Root', (0,0,0), (0, 0.3,0)) ## 1.2 add spine bones-------- # extrude another bone from the root bone helper.extrude_bone_edit('Root', 'head') helper.rename_bone('Root.001', 'Hip') helper.clear_parent_edit(['Hip']) helper.extrude_bone_edit('Hip', 'tail') helper.rename_bone('Hip.001', 'Spine') helper.extrude_bone_edit('Spine', 'tail') helper.rename_bone('Spine.001', 'Chest') helper.extrude_bone_edit('Chest', 'tail') helper.rename_bone('Chest.001', 'Neck') helper.extrude_bone_edit('Neck', 'tail') helper.rename_bone('Neck.001', 'Head') # move & scale bones from hip to head helper.move_bone('Hip', hip_head, hip_tail) helper.move_bone('Spine', spine_head, spine_tail) helper.move_bone('Chest', chest_head, chest_tail) helper.move_bone('Neck', neck_head, neck_tail) helper.move_bone('Head', head_head, head_tail) bpy.ops.export_scene.gltf(filepath='test.glb') ``` </details>
Jpzhao added the
Severity
Normal
Type
Report
Status
Needs Triage
labels 2023-09-13 05:26:03 +02:00
Member

Hi, thanks for the report. Script actually generate and translate bones to defined location but gltf export has affected the transform. I might have missed something here, will check again.

Hi, thanks for the report. Script actually generate and translate bones to defined location but gltf export has affected the transform. I might have missed something here, will check again.
Member

@JulienDuroure hi, is this a known behavior with bones/armatures?
I can confirm this with FBX export to. But this is avoidable there with add leaf bone property.


  • Open attached file and export in gltf format
  • Import back the armature and notice the difference
@JulienDuroure hi, is this a known behavior with bones/armatures? I can confirm this with FBX export to. But this is avoidable there with `add leaf bone` property. - - - - Open attached file and export in gltf format - Import back the armature and notice the difference
Member

Hello,
Yes, this is a know behavior, with very long history.
This should be changed with 4.0 (not committed yet).

See https://github.com/KhronosGroup/glTF-Blender-IO/issues/1662 and https://github.com/KhronosGroup/glTF-Blender-IO/pull/1986

There is no "tail bone" feature in glb, as they are storing joints (only 1 matrices, like in any know software except Blender, that has bones (root + tail matrices)

Hello, Yes, this is a know behavior, with very long history. This should be changed with 4.0 (not committed yet). See https://github.com/KhronosGroup/glTF-Blender-IO/issues/1662 and https://github.com/KhronosGroup/glTF-Blender-IO/pull/1986 There is no "tail bone" feature in glb, as they are storing joints (only 1 matrices, like in any know software except Blender, that has bones (root + tail matrices)
Member

Thanks, a real quick response 😃
I'll mark this as confirmed.

Thanks, a real quick response 😃 I'll mark this as confirmed.
Pratik Borhade added
Status
Confirmed
and removed
Status
Needs Info from Developers
labels 2023-09-13 12:55:55 +02:00
Pratik Borhade changed title from Python -- bpy move bone get the wrong result. to Export: Different transform value of Leaf bone 2023-09-13 13:00:22 +02:00
Member

BTW, this is not related to exporter

The PR will only change the default option at import, nothing related to exporter.

So if the user want to keep joint orientation, he has to change the import option to "Blender" instead of default value

BTW, this is not related to exporter The PR will only change the default option at import, nothing related to exporter. So if the user want to keep joint orientation, he has to change the import option to "Blender" instead of default value
Author

BTW, this is not related to exporter

The PR will only change the default option at import, nothing related to exporter.

So if the user want to keep joint orientation, he has to change the import option to "Blender" instead of default value

Thank you!

> BTW, this is not related to exporter > > The PR will only change the default option at import, nothing related to exporter. > > > So if the user want to keep joint orientation, he has to change the import option to "Blender" instead of default value Thank you!
Author

BTW, this is not related to exporter

The PR will only change the default option at import, nothing related to exporter.

So if the user want to keep joint orientation, he has to change the import option to "Blender" instead of default value

Sorry, do you mean that setting the import mode as "BLENDER" of other software to import the glb file exported from blender can get the correct position? But using blender to import the file can't get the correct result?

> BTW, this is not related to exporter > > The PR will only change the default option at import, nothing related to exporter. > > > So if the user want to keep joint orientation, he has to change the import option to "Blender" instead of default value Sorry, do you mean that setting the import mode as "BLENDER" of other software to import the glb file exported from blender can get the correct position? But using blender to import the file can't get the correct result?
Member

Hello,
No, this is not what I mean.
I was talking about using "BLENDER" option to import glb file inside Blender. No other software impacted here.

Hello, No, this is not what I mean. I was talking about using "BLENDER" option to import glb file inside Blender. No other software impacted here.
Author

Hello,
No, this is not what I mean.
I was talking about using "BLENDER" option to import glb file inside Blender. No other software impacted here.

Sorry, I'm still a little bit confused. Is this what you mean?
image

> Hello, > No, this is not what I mean. > I was talking about using "BLENDER" option to import glb file inside Blender. No other software impacted here. Sorry, I'm still a little bit confused. Is this what you mean? ![image](/attachments/0ce05bdc-e807-4ddf-b870-6cf7ba7de023)
228 KiB
Author
No description provided.
> I changed it to "BLENDER", but the position is still not correct.
Member

Blender bone tail is not something that is stored in gltf file format, because this is not something defined in glTF specification.
You can't be 100% sure that this value is constant when you export and then import it back, as this is NOT stored in the file, and there is no way to calculate it.
The "add leaf bone" option can help, but is only a workaround (this feature is on TODO list).

Blender bone tail is not something that is stored in gltf file format, because this is not something defined in glTF specification. You can't be 100% sure that this value is constant when you export and then import it back, as this is NOT stored in the file, and there is no way to calculate it. The "add leaf bone" option can help, but is only a workaround (this feature is on TODO list).
Author

Blender bone tail is not something that is stored in gltf file format, because this is not something defined in glTF specification.
You can't be 100% sure that this value is constant when you export and then import it back, as this is NOT stored in the file, and there is no way to calculate it.
The "add leaf bone" option can help, but is only a workaround (this feature is on TODO list).

I see. I'll look for other solutions then. Thanks a lot for your patience in answering my questions!

> Blender bone tail is not something that is stored in gltf file format, because this is not something defined in glTF specification. > You can't be 100% sure that this value is constant when you export and then import it back, as this is NOT stored in the file, and there is no way to calculate it. > The "add leaf bone" option can help, but is only a workaround (this feature is on TODO list). I see. I'll look for other solutions then. Thanks a lot for your patience in answering my questions!
Blender Bot added
Status
Archived
and removed
Status
Confirmed
labels 2023-11-10 11:49:00 +01:00
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset System
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Code Documentation
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Viewport & EEVEE
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Asset Browser Project
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Module
Viewport & EEVEE
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Severity
High
Severity
Low
Severity
Normal
Severity
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#112312
No description provided.