Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 0 additions & 58 deletions .gitignore

This file was deleted.

26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
XPS Tools
=========
Blender an addon to Export/Import Haydee assets.

With Blender 2.80 released there where many changes.
Fork of XNALara Mesh import/export tool with Blender 5.0 compatibility.

From v2.0.0 of this addon will only work with Blender 2.80+ (and viceversa).
For Blender 2.79 download v1.8.7
Since the original author (johnzero7) has not updated the code for five years as of 2025, the old version of the plugin can no longer run on the new version of Blender. Therefore, I will maintain this version going forward.

- Blender 2.80 ==> v2.0.0
- Blender 2.79 ==> v1.8.7
Original addon by XNALara community.

With Blender 4.40 released there where many changes.

From v2.1.0 of this addon will only work with Blender 4.4.
From v2.2.0 of this addon will only work with Blender 5.0.

- Blender 5.00 ==> v2.2.0+
- Blender 4.40 ==> v2.1.0

Blender Toolshelf, an addon for Blender to:

Expand All @@ -21,6 +26,11 @@ Main Features:
- Creates Materials on import
- Easily set a new rest pose for the model

more info at:
http://johnzero7.github.io/XNALaraMesh/
### Known Issues

- Summary: Due to critical compatibility issues with the XPS binary format, I have disabled the native .xps binary export. The exporter now redirects to ASCII format by default, which ensures perfect compatibility and stability.

- Technical Details: The current mock_xps_data.py lacks the necessary file header logic. After I manually attempted to implement the header construction, it resulted in severe byte misalignment. While the file is generated, the scene appears empty upon re-import.
The XPS format requires strict alignment for the 1080-byte Settings Block. Due to changes in Blender 5.0's I/O handling, the logic that worked in older versions now causes offset shifts. As an individual developer, the binary structure of XPS remains a "black box" to me, and my current technical skills are insufficient to perform the precise byte-level adjustments required to fix this.

- Call for Help: I welcome any experienced developers to help fix this alignment issue. If you can resolve the binary header construction for Blender 5.0, please submit a Pull Request on GitHub. Thank you for your support!
222 changes: 64 additions & 158 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,167 +1,73 @@
# <pep8 compliant>

"""Blender Addon. XNALara/XPS importer/exporter."""

bl_info = {
"name": "XNALara/XPS Import/Export",
"author": "johnzero7",
"version": (2, 0, 2),
"blender": (2, 80, 0),
"location": "File > Import-Export > XNALara/XPS",
"description": "Import-Export XNALara/XPS",
"warning": "",
"wiki_url": "https://github.com/johnzero7/xps_tools",
"tracker_url": "https://github.com/johnzero7/xps_tools/issues",
"name": "XPS Import/Export",
"author": "maylog",
"version": (2, 2, 6),
"blender": (5, 0, 0),
"location": "File > Import-Export",
"description": "Community-maintained fork of the original XNALara/XPS Tools. Fully Blender 5.0+ compatible.",
"category": "Import-Export",
"support": "COMMUNITY",
"credits": "2025 johnzero7 (original author), 2025 Clothoid, 2025 XNALara/XPS community, 2025 maylog (Blender 5.0+ update & Extensions submission)",
}


#############################################
# support reloading sub-modules
_modules = [
'xps_panels',
'xps_tools',
'xps_toolshelf',
'xps_const',
'xps_types',
'xps_material',
'write_ascii_xps',
'write_bin_xps',
'read_ascii_xps',
'read_bin_xps',
'mock_xps_data',
'export_xnalara_model',
'export_xnalara_pose',
'import_xnalara_model',
'import_xnalara_pose',
'import_obj',
'export_obj',
'ascii_ops',
'bin_ops',
'timing',
'material_creator',
'node_shader_utils',
'addon_updater_ops',
]

# Reload previously loaded modules
if "bpy" in locals():
from importlib import reload
_modules_loaded[:] = [reload(module) for module in _modules_loaded]
del reload


# First import the modules
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]
del _namespace
# support reloading sub-modules
#############################################

import bpy


class UpdaterPreferences(bpy.types.AddonPreferences):
"""Updater Class."""

bl_idname = __package__

# addon updater preferences from `__init__`, be sure to copy all of them
auto_check_update: bpy.props.BoolProperty(
name="Auto-check for Update",
description="If enabled, auto-check for updates using an interval",
default=False,
)
updater_interval_months: bpy.props.IntProperty(
name='Months',
description="Number of months between checking for updates",
default=0,
min=0
)
updater_interval_days: bpy.props.IntProperty(
name='Days',
description="Number of days between checking for updates",
default=7,
min=0,
)
updater_interval_hours: bpy.props.IntProperty(
name='Hours',
description="Number of hours between checking for updates",
default=0,
min=0,
max=23
)
updater_interval_minutes: bpy.props.IntProperty(
name='Minutes',
description="Number of minutes between checking for updates",
default=0,
min=0,
max=59
)

def draw(self, context):
"""Draw Method."""
addon_updater_ops.update_settings_ui(self, context)

#
# Registration
#


classesToRegister = [
UpdaterPreferences,
xps_panels.XPSToolsObjectPanel,
xps_panels.XPSToolsBonesPanel,
xps_panels.XPSToolsAnimPanel,

xps_toolshelf.ArmatureBonesHideByName_Op,
xps_toolshelf.ArmatureBonesHideByVertexGroup_Op,
xps_toolshelf.ArmatureBonesShowAll_Op,
xps_toolshelf.ArmatureBonesRenameToBlender_Op,
xps_toolshelf.ArmatureBonesRenameToXps_Op,
xps_toolshelf.ArmatureBonesConnect_Op,
xps_toolshelf.NewRestPose_Op,

xps_tools.Import_Xps_Model_Op,
xps_tools.Export_Xps_Model_Op,
xps_tools.Import_Xps_Pose_Op,
xps_tools.Export_Xps_Pose_Op,
xps_tools.Import_Poses_To_Keyframes_Op,
xps_tools.Export_Frames_To_Poses_Op,
xps_tools.ArmatureBoneDictGenerate_Op,
xps_tools.ArmatureBoneDictRename_Op,
xps_tools.ArmatureBoneDictRestore_Op,
xps_tools.ImportXpsNgff,
xps_tools.ExportXpsNgff,
xps_tools.XpsImportSubMenu,
xps_tools.XpsExportSubMenu,
]


# Use factory to create method to register and unregister the classes
registerClasses, unregisterClasses = bpy.utils.register_classes_factory(classesToRegister)

from . import (
xps_panels,
xps_tools,
xps_toolshelf,
xps_const,
xps_types,
xps_material,
write_ascii_xps,
write_bin_xps,
read_ascii_xps,
read_bin_xps,
mock_xps_data,
export_xnalara_model,
export_xnalara_pose,
import_xnalara_model,
import_xnalara_pose,
import_obj,
export_obj,
ascii_ops,
bin_ops,
timing,
material_creator,
node_shader_utils,
)


modules = (
xps_const,
xps_types,
xps_material,
read_ascii_xps,
read_bin_xps,
write_ascii_xps,
write_bin_xps,
mock_xps_data,
material_creator,
node_shader_utils,
timing,
ascii_ops,
bin_ops,
import_obj,
export_obj,
import_xnalara_model,
export_xnalara_model,
import_xnalara_pose,
export_xnalara_pose,
xps_tools,
xps_toolshelf,
xps_panels,
)

def register():
"""Register addon classes."""
registerClasses()
xps_tools.register()
addon_updater_ops.register(bl_info)

for mod in modules:
if hasattr(mod, "register"):
mod.register()

def unregister():
"""Unregister addon classes."""
addon_updater_ops.unregister()
xps_tools.unregister()
unregisterClasses()


if __name__ == "__main__":
register()

# call exporter
# bpy.ops.xps_tools.export_model('INVOKE_DEFAULT')

# call importer
# bpy.ops.xps_tools.import_model('INVOKE_DEFAULT')
for mod in reversed(modules):
if hasattr(mod, "unregister"):
mod.unregister()
Loading