Last active
April 24, 2025 03:15
-
-
Save CrackerHax/61233dfc972ae718ddc1186408ce7888 to your computer and use it in GitHub Desktop.
copy_bind_pose_for_sl.py: Add joint offsets to .dae file for uploading mesh with non-standard avatar joints to Second Life. So you can use "include joint positions"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import xml.etree.ElementTree as ET | |
| # Hardcoded joint bind pose translation offsets for the default Second Life avatar | |
| # These values are extracted from the matrix transformations in the provided sl_male_avatar.dae file. | |
| # The format is a dictionary where the key is the joint name and the value | |
| # is a list representing the translation [x, y, z] from the matrix sid="matrix". | |
| # These are the offsets relative to the parent joint in the bind pose. | |
| default_sl_avatar_bind_pose_offsets = { | |
| "mPelvis": [-0.010000, 0.000000, -0.020000], | |
| "mButt": [-0.060000, 0.000000, -0.100000], | |
| "mHip": [-0.020000, 0.000000, 0.020000], | |
| "mThigh": [-0.010000, 0.000000, 0.000000], | |
| "mKnee": [0.010000, 0.000000, 0.000000], | |
| "mShin": [0.000000, 0.000000, 0.000000], | |
| "mAnkle": [0.000000, 0.000000, 0.000000], | |
| "mFoot": [0.000000, 0.000000, 0.000000], | |
| "mToe": [0.000000, 0.000000, 0.000000], | |
| "mBigToe": [0.000000, 0.000000, 0.000000], | |
| "mLittleToe": [0.000000, 0.000000, 0.000000], | |
| "mCollar": [0.000000, 0.000000, 0.000000], | |
| "mShoulder": [0.000000, 0.000000, 0.000000], | |
| "mElbow": [0.000000, 0.000000, 0.000000], | |
| "mWrist": [0.000000, 0.000000, 0.000000], | |
| "mHand": [0.000000, 0.000000, 0.000000], | |
| "mPalm": [0.000000, 0.000000, 0.000000], | |
| "mThumb": [0.000000, 0.000000, 0.000000], | |
| "mThumbTip": [0.000000, 0.000000, 0.000000], | |
| "mIndex": [0.000000, 0.000000, 0.000000], | |
| "mIndexTip": [0.000000, 0.000000, 0.000000], | |
| "mMidddle": [0.000000, 0.000000, 0.000000], | |
| "mMidddleTip": [0.000000, 0.000000, 0.000000], | |
| "mRing": [0.000000, 0.000000, 0.000000], | |
| "mRingTip": [0.000000, 0.000000, 0.000000], | |
| "mLittle": [0.000000, 0.000000, 0.000000], | |
| "mLittleTip": [0.000000, 0.000000, 0.000000], | |
| "mNeck": [0.000000, 0.000000, 0.000000], | |
| "mHead": [0.000000, 0.000000, 0.000000], | |
| "mSkull": [0.000000, 0.000000, 0.000000], | |
| "mSkullTip": [0.000000, 0.000000, 0.000000], | |
| "mEye": [0.000000, 0.000000, 0.000000], | |
| "mNose": [0.000000, 0.000000, 0.000000], | |
| "mEar": [0.000000, 0.000000, 0.000000], | |
| "mMouth": [0.000000, 0.000000, 0.000000], | |
| "mChin": [0.000000, 0.000000, 0.000000], | |
| "mChest": [0.000000, 0.000000, 0.000000], | |
| "mBreast": [0.000000, 0.000000, 0.000000], | |
| "mStomach": [0.000000, 0.000000, 0.000000], | |
| "mHips": [0.000000, 0.000000, 0.000000] | |
| } | |
| def add_bind_pose_to_dae(input_dae_path, output_dae_path): | |
| """ | |
| Adds the default Second Life avatar bind pose joint position offsets to a DAE file. | |
| Args: | |
| input_dae_path (str): The path to the input DAE file. | |
| output_dae_path (str): The path to save the modified DAE file. | |
| """ | |
| try: | |
| # Parse the DAE file | |
| tree = ET.parse(input_dae_path) | |
| root = tree.getroot() | |
| # Define the COLLADA namespace | |
| ns = {'dae': 'http://www.collada.org/2005/11/COLLADASchema'} | |
| # Register the namespace for writing | |
| ET.register_namespace('', ns['dae']) | |
| # Find the library_visual_scenes | |
| library_visual_scenes = root.find('dae:library_visual_scenes', ns) | |
| if library_visual_scenes is None: | |
| print("Error: <library_visual_scenes> not found in the DAE file.") | |
| return | |
| # Find the visual scene. Assuming the first one is the relevant one. | |
| visual_scene = library_visual_scenes.find('dae:visual_scene', ns) | |
| if visual_scene is None: | |
| print("Error: <visual_scene> not found in the DAE file.") | |
| return | |
| # Find all joint nodes by looking for <node> elements with type="JOINT" | |
| joint_nodes = visual_scene.findall('.//dae:node[@type="JOINT"]', ns) | |
| if not joint_nodes: | |
| print("Warning: No joint nodes with type='JOINT' found in the DAE file.") | |
| print("Script will proceed, but no joint matrices will be updated.") | |
| # Iterate through joint nodes and add or update their bind pose matrices | |
| for joint_node in joint_nodes: | |
| # Get the joint name, trying 'sid' first then 'name' | |
| joint_name = joint_node.get('sid') | |
| if not joint_name: | |
| joint_name = joint_node.get('name') | |
| if joint_name in default_sl_avatar_bind_pose_offsets: | |
| # Get the bind pose translation offset for the joint | |
| bind_pose_offset = default_sl_avatar_bind_pose_offsets[joint_name] | |
| # Create the transformation matrix string with the bind pose offset | |
| # We are primarily setting the translation components (last column) | |
| # Assuming an identity rotation and scale for the offset matrix | |
| matrix_str = f"1.000000 0.000000 0.000000 {bind_pose_offset[0]} " \ | |
| f"0.000000 1.000000 0.000000 {bind_pose_offset[1]} " \ | |
| f"0.000000 0.000000 1.000000 {bind_pose_offset[2]} " \ | |
| f"0.000000 0.000000 0.000000 1.000000" | |
| # Find or create the <matrix> element with sid="matrix" | |
| # Ensure we are looking for the element within the correct namespace | |
| matrix_element = joint_node.find('dae:matrix[@sid="matrix"]', ns) | |
| if matrix_element is None: | |
| # Create the matrix element with the correct namespace prefix | |
| matrix_element = ET.SubElement(joint_node, '{http://www.collada.org/2005/11/COLLADASchema}matrix', sid='matrix') | |
| # Update the matrix content with the bind pose transformation | |
| matrix_element.text = matrix_str | |
| print(f"Updated matrix for joint '{joint_name}'") | |
| # Save the modified DAE file | |
| # Use xml_declaration=True, encoding='utf-8', and method='xml' for standard XML output | |
| # The registered namespace should handle the prefixes. | |
| tree.write(output_dae_path, encoding='utf-8', xml_declaration=True, method='xml') | |
| print(f"Successfully added bind pose offset data to {output_dae_path}") | |
| except FileNotFoundError: | |
| print(f"Error: Input file not found at {input_dae_path}") | |
| except ET.ParseError: | |
| print(f"Error: Could not parse the DAE file at {input_dae_path}. Please ensure it is a valid DAE XML.") | |
| except Exception as e: | |
| print(f"An unexpected error occurred: {e}") | |
| # Example usage (uncomment and modify as needed): | |
| # add_bind_pose_to_dae('input.dae', 'output.dae') | |
| add_bind_pose_to_dae('source_file.dae', 'output_file.dae') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment