{"id":23864500,"url":"https://github.com/scanmountgoat/xc3_model_py","last_synced_at":"2026-04-15T12:34:24.205Z","repository":{"id":184516532,"uuid":"671967242","full_name":"ScanMountGoat/xc3_model_py","owner":"ScanMountGoat","description":"Python bindings to xc3_model","archived":false,"fork":false,"pushed_at":"2024-05-27T18:42:36.000Z","size":167,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-05-28T04:18:46.744Z","etag":null,"topics":["pyo3","python","rust","xenoblade"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ScanMountGoat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-28T15:06:49.000Z","updated_at":"2024-08-16T20:20:28.993Z","dependencies_parsed_at":"2023-12-22T03:29:11.972Z","dependency_job_id":"9c38b60b-44e2-46d9-bfde-070aa80fd76a","html_url":"https://github.com/ScanMountGoat/xc3_model_py","commit_stats":null,"previous_names":["scanmountgoat/xc3_model_py"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScanMountGoat%2Fxc3_model_py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScanMountGoat%2Fxc3_model_py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScanMountGoat%2Fxc3_model_py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScanMountGoat%2Fxc3_model_py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ScanMountGoat","download_url":"https://codeload.github.com/ScanMountGoat/xc3_model_py/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240177574,"owners_count":19760392,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pyo3","python","rust","xenoblade"],"created_at":"2025-01-03T08:26:29.742Z","updated_at":"2026-04-15T12:34:24.186Z","avatar_url":"https://github.com/ScanMountGoat.png","language":"Rust","readme":"# xc3_model_py [![PyPI](https://img.shields.io/pypi/v/xc3_model_py)](https://pypi.org/project/xc3_model_py/)\nPython bindings to [xc3_model](https://github.com/ScanMountGoat/xc3_lib) for high level and efficient data access to model files for Xenoblade Chronicles X, Xenoblade Chronicles 1 Definitive Edition, Xenoblade Chronicles 2, Xenoblade Chronicles 3, and Xenoblade Chronicles X Definitive Edition.\n\n## Installation\nThe package can be installed for Python version 3.9, 3.10, 3.11, or 3.12 using pip on newer versions of Windows, Linux, or MacOS. The prebuilt wheels (.whl files) are included only for situations where pip might not be available such as for plugin development for applications. The wheels are zip archives and can be extracted to obtain the compiled .pyd or .so file. xc3_model_py requires the `numpy` package for transforms and vertex data.\n\nInstalling: `pip install xc3_model_py`  \nUpdating: `pip install xc3_model_py --upgrade`\n\n## Introduction\nParsing and conversion to Python happens in optimized Rust code when calling `xc3_model_py.load_map` or `xc3_model_py.load_model`. All files revisions are converted to the same types on import like `xc3_model_py.ModelRoot` or `xc3_model_py.MapRoot`. This avoids needing to add any special handling for different file versions or different games. Pregenerated shader databases compatible with a specific release are linked in [releases](https://github.com/ScanMountGoat/xc3_model_py/releases). For more advanced usage, see [xenoblade_blender](https://github.com/ScanMountGoat/xenoblade_blender).\n\n```python\nimport xc3_model_py\n\n# Get a list of MapRoot.\ndatabase = xc3_model_py.shader_database.ShaderDatabase.from_file(\"xc3.bin\")\nroots = xc3_model_py.load_map(\"xenoblade3_dump/map/ma59a.wismhd\", database)\n\nfor root in roots:\n    for group in root.groups:\n        for models in group.models:\n            for material in models.materials:\n                print(material.name)\n                # The shader contains assignment information when specifying a database.\n\n            for model in models.models:\n                buffers = group.buffers[model.model_buffers_index]\n\n                # prints (num_instances, 4, 4)\n                print(len(model.instances.shape))\n```\n\n```python\n# This returns only a single ModelRoot.\ndatabase = xc3_model_py.shader_database.ShaderDatabase.from_file(\"xc3.bin\")\nroot = xc3_model_py.load_model(\"xenoblade3_dump/chr/chr/01012013.wimdo\", database)\n\nfor material in root.models.materials:\n    print(material.name)\n\nfor model in root.models.models:\n    # prints (1, 4, 4)\n    print(len(model.instances.shape))\n\n    # Access vertex and index data for this model.\n    buffers = root.buffers\n    for buffer in buffers.vertex_buffers:\n        for attribute in buffer.attributes:\n            print(attribute.attribute_type, attribute.data.shape)\n\n    # Access vertex skinning data for each mesh.\n    for mesh in model.meshes:\n        vertex_buffer = buffers.vertex_buffers[mesh.vertex_buffer_index]\n\n        if buffers.weights is not None:\n            # Calculate the index offset based on the weight group for this mesh.\n            technique_type = root.models.materials[mesh.material_index].technique_type\n            lod_item_index = 0 if mesh.lod_item_index is None else mesh.lod_item_index\n            start_index = buffers.weights.weights_start_index(\n                mesh.flags2, lod_item_index, technique_type\n            )\n\n            weight_buffer = buffers.weights.weight_buffer(mesh.flags2)\n            if weight_buffer is not None and root.models.skinning is not None:\n                # Get vertex skinning attributes.\n                for attribute in vertex_buffer.attributes:\n                    if (\n                        attribute.attribute_type\n                        == xc3_model_py.vertex.AttributeType.WeightIndex\n                    ):\n                        # Find the actual per vertex skinning information.\n                        weight_indices = attribute.data[:, 0] + start_index\n                        skin_weights = weight_buffer.weights[weight_indices]\n                        # Note that these indices index into a different bone list than the skeleton.\n                        bone_indices = weight_buffer.bone_indices[weight_indices, 0]\n                        bone_name = root.models.skinning.bones[bone_indices[0]].name\n```\n\nCertain types like matrices and vertex atribute data are stored using `numpy.ndarray`. All transformation matrices are column-major to match the Rust code in xc3_model. This greatly reduces conversion overhead and allows for more optimized Python code. xc3_model_py requires the numpy package to be installed. Blender already provides the numpy package, enabling the use of functions like `foreach_get` and `foreach_set` for efficient property access.\n\n```python\n# blender\nblender_mesh.vertices.add(positions_array.shape[0])\nblender_mesh.vertices.foreach_set('co', positions_array.reshape(-1))\n```\n\nAnimations can be loaded from a file all at once. The track type is currently opaque, meaning that implementation details are not exposed. The values can be sampled at the desired frame using the appropriate methods.\n\n```python\nimport xc3_model_py\n\npath = \"xenoblade3_dump/chr/ch/ch01027000_event.mot\"\nanimations = xc3_model_py.load_animations(path)\n\nfor animation in animations:\n    print(\n        animation.name, animation.space_mode, animation.play_mode, animation.blend_mode\n    )\n    print(f\"frames: {animation.frame_count}, tracks: {len(animation.tracks)}\")\n\n    track = animation.tracks[0]\n\n    # Each track references a bone in one of three ways.\n    bone_index = track.bone_index()\n    bone_hash = track.bone_hash()\n    bone_name = track.bone_name()\n    if bone_index is not None:\n        pass\n    elif bone_hash is not None:\n        # Use xc3_model_py.murmur3(bone_name) for hashing the skeleton bones.\n        pass\n    elif bone_name is not None:\n        pass\n\n    # Sample the transform for a given track at each frame.\n    # This essentially \"bakes\" the keyframes of the animation.\n    for frame in range(animation.frame_count):\n        print(track.sample_scale(frame, animation.frame_count))\n        print(track.sample_rotation(frame, animation.frame_count))\n        print(track.sample_translation(frame, animation.frame_count))\n    print()\n```\n\nxc3_model_py enables Rust log output by default to use with Python's `logging` module.\nLogging can be disabled entirely if not needed using `logging.disable()`.\n\n```python\nimport logging\n\n# Configure log messages to include more information.\nFORMAT = \"%(levelname)s %(name)s %(filename)s:%(lineno)d %(message)s\"\nlogging.basicConfig(format=FORMAT, level=logging.INFO)\n```\n\n## Documentation\nSee the [pyi stub file](https://github.com/ScanMountGoat/xc3_model_py/blob/main/xc3_model_py/__init__.pyi) for complete function and type information. This also enables autocomplete in supported editors like the Python extension for VSCode. The Python API attempts to match the Rust functions and types in xc3_model as closely as possible. \n\n## Installation\nThe compiled extension module can be imported just like any other Python file. On Windows, rename `xc3_model_py.dll` to `xc3_model_py.pyd`. If importing `xc3_model_py` fails, make sure the import path is specified correctly and the current Python version matches the version used when building. For installing in the current Python environment, install [maturin](https://github.com/PyO3/maturin) and use `maturin develop --release`.\n\n## Building\nBuild the project with `cargo build --release`. This will compile a native python module for the current Python interpreter. For use with Blender, make sure to build for the Python version used by Blender. This can be achieved by activating a virtual environment with the appropriate Python version or setting the Python interpeter using the `PYO3_PYTHON` environment variable. See the [PyO3 guide](https://pyo3.rs) for details.\n\n## Limitations\nSome types from `xc3_model_py` are opaque wrappers around the underlying Rust types and cannot be constructed in any way from Python. Some of these limitations should hopefully be resolved in the future.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscanmountgoat%2Fxc3_model_py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscanmountgoat%2Fxc3_model_py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscanmountgoat%2Fxc3_model_py/lists"}