Package: xc3-model-py
xc3_model_pyPython bindings toxc3_modelfor high level and efficient data access to model files for Xenoblade 1 DE, Xenoblade 2, and Xenoblade 3.InstallationThe 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.Installing:pip install xc3_model_pyUpdating:pip install xc3_model_py --upgradeIntroductionParsing and processing happens in optimized Rust code when callingxc3_model_py.load_maporxc3_model_py.load_model. All characters, models, and maps are converted to the same scene hierarchy representation. This avoids needing to add any special handling for maps vs characters. Pregenerated shader JSON databases are available fromxc3_lib.importxc3_model_py# Get a list of model roots.roots=xc3_model_py.load_map("xenoblade3_dump/map/ma59a.wismhd",database_path="xc3.json")forrootinroots:forgroupinroot.groups:formodelsingroup.models:formaterialinmodels.materials:print(material.name)# The shader contains assignment information when specifying a JSON database.ifmaterial.shaderisnotNone:# Find the image texture and channel used for the ambient occlusion output.ao=material.shader.sampler_channel_index(2,'z')ifaoisnotNone:sampler_index,channel_index=aoimage_texture_index=material.textures[sampler_index].image_texture_indeximage_texture=root.image_textures[image_texture_index]print(image_texture.name,'xyzw'[channel_index])formodelinmodels.models:# prints (num_instances, 4, 4)print(len(model.instances.shape))# This returns only a single root.root=xc3_model_py.load_model("xenoblade3_dump/chr/chr/01012013.wimdo",database_path="xc3.json")forgroupinroot.groups:formodelsingroup.models:formodelinmodels.models:# prints (1, 4, 4)print(len(model.instances.shape))# Access vertex and index data for this model.buffers=group.buffers[model.model_buffers_index]forbufferinbuffers.vertex_buffers:forattributeinbuffer.attributes:print(attribute.attribute_type,attribute.data.shape)Certain types like matrices and vertex atribute data are stored usingnumpy.ndarray. 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 likeforeach_getandforeach_setfor efficient property access.# blenderblender_mesh.vertices.add(positions_array.shape[0])blender_mesh.vertices.foreach_set('co',positions_array.reshape(-1))Animations 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.importxc3_model_pypath="xenoblade3_dump/chr/ch/ch01027000_event.mot"animations=xc3_model_py.load_animations(path)foranimationinanimations:print(animation.name,animation.space_mode,animation.play_mode,animation.blend_mode)print(f'frames:{animation.frame_count}, tracks:{len(animation.tracks)}')track=animation.tracks[0]# Each track references a bone in one of three ways.bone_index=track.bone_index()bone_hash=track.bone_hash()bone_name=track.bone_name()ifbone_indexisnotNone:passelifbone_hashisnotNone:# Use xc3_model_py.murmur3(bone_name) for hashing the skeleton bones.passelifbone_nameisnotNone:pass# Sample the transform for a given track at each frame.# This essentially "bakes" the keyframes of the animation.forframeinrange(animation.frame_count:)print(track.sample_scale(frame),track.sample_rotation(frame),track.sample_translation(frame))print()xc3_model_py enables Rust log output by default to use with Python'sloggingmodule.
Logging can be disabled entirely if not needed usinglogging.disable().importlogging# Configure log messages to include more information.FORMAT='%(levelname)s%(name)s%(filename)s:%(lineno)d%(message)s'logging.basicConfig(format=FORMAT,level=logging.INFO)DocumentationSee thepyi stub filefor 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.InstallationThe compiled extension module can be imported just like any other Python file. On Windows, renamexc3_model_py.dlltoxc3_model_py.pyd. If importingxc3_model_pyfails, 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, installmaturinand usematurin develop --release.BuildingBuild the project withcargo 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. The easiest way to do this is to use the Python interpreter bundled with Blender. See thePyO3 guidefor details. Some example commands are listed below for different operating systems.Blender 4.0 on Windowsset PYO3_PYTHON = "C:\Program Files\Blender Foundation\Blender 4.0\4.0\python\bin\python.exe"
cargo build --releaseBlender 4.0 on MacOSPYO3_PYTHON="/Applications/Blender.app/Contents/Resources/4.0/python/bin/python3.10" cargo build --releaseLimitationsAll data should be treated as immutable. Attempting to set fields will result in an error. Modifying list elements will appear to work, but changes will not be reflected when accessing the elements again. Types fromxc3_model_pyalso cannot be constructed in any way from Python. These limitations may be lifted in the future. Write support may be added in the future as xc3_lib and xc3_model develop.