Compare commits

..

6 Commits

Author SHA1 Message Date
Colin Basnett
d996f59cae Fix for root bone being incorrectly oriented if it wasn't at the identity rotation in the bind pose 2024-03-25 01:32:03 -07:00
Colin Basnett
bd667d4833 Update README.md 2024-03-14 19:13:48 -07:00
Colin Basnett
d81477673b Fixed a script reload issue 2024-03-02 13:15:48 -08:00
Colin Basnett
4d41f1af83 When exporting PSKs, armatures with no bones are now more sensibly handled
umodel, for some reason, exports some models with no bones. For
compatibility and convenience, an armature with no bones may as well not
exist, so we treat it as though it doesn't on export, and a single fake
root bone is added for maximum compatibility.
2024-03-01 15:14:37 -08:00
Colin Basnett
15e2c6ccdd Importing PSKs with poly flags now works 2024-02-29 00:32:42 -08:00
Colin Basnett
14116963bb Incremented version to 6.2.0 2024-02-28 23:21:31 -08:00
6 changed files with 39 additions and 23 deletions

View File

@@ -10,7 +10,7 @@ This Blender addon allows you to import and export meshes and animations to and
| Blender Version | Addon Version | Long Term Support | | Blender Version | Addon Version | Long Term Support |
|--------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------| |--------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------|
| 4.0+ | [latest](https://github.com/DarklightGames/io_scene_psk_psa/releases/latest) | TBD | | 4.0+ | [latest](https://github.com/DarklightGames/io_scene_psk_psa/releases/latest) | TBD |
| [3.4 - 3.6](https://www.blender.org/download/lts/3-6/) | [5.0.5](https://github.com/DarklightGames/io_scene_psk_psa/releases/tag/5.0.5) | ✅️ June 2025 | | [3.4 - 3.6](https://www.blender.org/download/lts/3-6/) | [5.0.6](https://github.com/DarklightGames/io_scene_psk_psa/releases/tag/5.0.6) | ✅️ June 2025 |
| [2.93 - 3.3](https://www.blender.org/download/releases/3-3/) | [4.3.0](https://github.com/DarklightGames/io_scene_psk_psa/releases/tag/4.3.0) | ✅️ September 2024 | | [2.93 - 3.3](https://www.blender.org/download/releases/3-3/) | [4.3.0](https://github.com/DarklightGames/io_scene_psk_psa/releases/tag/4.3.0) | ✅️ September 2024 |
Bug fixes will be issued for legacy addon versions that are under [Blender's LTS maintenance period](https://www.blender.org/download/lts/). Once the LTS period has ended, legacy addon versions will no longer be supported by the maintainers of this repository, although we will accept pull requests for bug fixes. Bug fixes will be issued for legacy addon versions that are under [Blender's LTS maintenance period](https://www.blender.org/download/lts/). Once the LTS period has ended, legacy addon versions will no longer be supported by the maintainers of this repository, although we will accept pull requests for bug fixes.

View File

@@ -3,7 +3,7 @@ from bpy.app.handlers import persistent
bl_info = { bl_info = {
'name': 'PSK/PSA Importer/Exporter', 'name': 'PSK/PSA Importer/Exporter',
'author': 'Colin Basnett, Yurii Ti', 'author': 'Colin Basnett, Yurii Ti',
'version': (6, 1, 2), 'version': (6, 2, 0),
'blender': (4, 0, 0), 'blender': (4, 0, 0),
'description': 'PSK/PSA Import/Export (.psk/.psa)', 'description': 'PSK/PSA Import/Export (.psk/.psa)',
'warning': '', 'warning': '',
@@ -36,6 +36,7 @@ if 'bpy' in locals():
importlib.reload(psa_reader) importlib.reload(psa_reader)
importlib.reload(psa_writer) importlib.reload(psa_writer)
importlib.reload(psa_builder) importlib.reload(psa_builder)
importlib.reload(psa_importer)
importlib.reload(psa_export_properties) importlib.reload(psa_export_properties)
importlib.reload(psa_export_operators) importlib.reload(psa_export_operators)
importlib.reload(psa_export_ui) importlib.reload(psa_export_ui)

View File

@@ -153,7 +153,8 @@ def import_psa(context: Context, psa_reader: PsaReader, armature_object: Object,
import_bone.original_rotation.conjugate() import_bone.original_rotation.conjugate()
else: else:
import_bone.original_location = armature_bone.matrix_local.translation.copy() import_bone.original_location = armature_bone.matrix_local.translation.copy()
import_bone.original_rotation = armature_bone.matrix_local.to_quaternion() import_bone.original_rotation = armature_bone.matrix_local.to_quaternion().conjugated()
import_bone.post_rotation = import_bone.original_rotation.conjugated() import_bone.post_rotation = import_bone.original_rotation.conjugated()
context.window_manager.progress_begin(0, len(sequences)) context.window_manager.progress_begin(0, len(sequences))

View File

@@ -6,7 +6,7 @@ import numpy as np
from bpy.types import Armature, Material from bpy.types import Armature, Material
from .data import * from .data import *
from .properties import get_poly_flags from .properties import triangle_type_and_bit_flags_to_poly_flags
from ..helpers import * from ..helpers import *
@@ -76,9 +76,9 @@ def build_psk(context, options: PskBuildOptions) -> PskBuildResult:
psk = Psk() psk = Psk()
bones = [] bones = []
if armature_object is None: if armature_object is None or len(armature_object.data.bones) == 0:
# If the mesh has no armature object, simply assign it a dummy bone at the root to satisfy the requirement # If the mesh has no armature object or no bones, simply assign it a dummy bone at the root to satisfy the
# that a PSK file must have at least one bone. # requirement that a PSK file must have at least one bone.
psk_bone = Psk.Bone() psk_bone = Psk.Bone()
psk_bone.name = bytes('root', encoding='windows-1252') psk_bone.name = bytes('root', encoding='windows-1252')
psk_bone.flags = 0 psk_bone.flags = 0
@@ -146,8 +146,8 @@ def build_psk(context, options: PskBuildOptions) -> PskBuildResult:
except UnicodeEncodeError: except UnicodeEncodeError:
raise RuntimeError(f'Material name "{material.name}" contains characters that cannot be encoded in the Windows-1252 codepage') raise RuntimeError(f'Material name "{material.name}" contains characters that cannot be encoded in the Windows-1252 codepage')
psk_material.texture_index = len(psk.materials) psk_material.texture_index = len(psk.materials)
psk_material.poly_flags = get_poly_flags(material.psk) psk_material.poly_flags = triangle_type_and_bit_flags_to_poly_flags(material.psk.mesh_triangle_type,
print(psk_material.name, psk_material.poly_flags) material.psk.mesh_triangle_bit_flags)
psk.materials.append(psk_material) psk.materials.append(psk_material)
context.window_manager.progress_begin(0, len(input_objects.mesh_objects)) context.window_manager.progress_begin(0, len(input_objects.mesh_objects))

View File

@@ -7,6 +7,7 @@ from bpy.types import VertexGroup
from mathutils import Quaternion, Vector, Matrix from mathutils import Quaternion, Vector, Matrix
from .data import Psk from .data import Psk
from .properties import poly_flags_to_triangle_type_and_bit_flags
from ..helpers import rgb_to_srgb, is_bdk_addon_loaded from ..helpers import rgb_to_srgb, is_bdk_addon_loaded
@@ -134,6 +135,9 @@ def import_psk(psk: Psk, context, options: PskImportOptions) -> PskImportResult:
else: else:
# Just create a blank material. # Just create a blank material.
material = bpy.data.materials.new(material_name) material = bpy.data.materials.new(material_name)
mesh_triangle_type, mesh_triangle_bit_flags = poly_flags_to_triangle_type_and_bit_flags(psk_material.poly_flags)
material.psk.mesh_triangle_type = mesh_triangle_type
material.psk.mesh_triangle_bit_flags = mesh_triangle_bit_flags
material.use_nodes = True material.use_nodes = True
mesh_data.materials.append(material) mesh_data.materials.append(material)

View File

@@ -2,19 +2,19 @@ from bpy.props import EnumProperty
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
mesh_triangle_types_items = ( mesh_triangle_types_items = (
('MTT_Normal', 'Normal', 'Normal one-sided', 0), ('NORMAL', 'Normal', 'Normal one-sided', 0),
('MTT_NormalTwoSided', 'Normal Two-Sided', 'Normal but two-sided', 1), ('NORMAL_TWO_SIDED', 'Normal Two-Sided', 'Normal but two-sided', 1),
('MTT_Translucent', 'Translucent', 'Translucent two-sided', 2), ('TRANSLUCENT', 'Translucent', 'Translucent two-sided', 2),
('MTT_Masked', 'Masked', 'Masked two-sided', 3), ('MASKED', 'Masked', 'Masked two-sided', 3),
('MTT_Modulate', 'Modulate', 'Modulation blended two-sided', 4), ('MODULATE', 'Modulate', 'Modulation blended two-sided', 4),
('MTT_Placeholder', 'Placeholder', 'Placeholder triangle for positioning weapon. Invisible', 8), ('PLACEHOLDER', 'Placeholder', 'Placeholder triangle for positioning weapon. Invisible', 8),
) )
mesh_triangle_bit_flags_items = ( mesh_triangle_bit_flags_items = (
('MTT_Unlit', 'Unlit', 'Full brightness, no lighting', 16), ('UNLIT', 'Unlit', 'Full brightness, no lighting', 16),
('MTT_Flat', 'Flat', 'Flat surface, don\'t do bMeshCurvy thing', 32), ('FLAT', 'Flat', 'Flat surface, don\'t do bMeshCurvy thing', 32),
('MTT_Environment', 'Environment', 'Environment mapped', 64), ('ENVIRONMENT', 'Environment', 'Environment mapped', 64),
('MTT_NoSmooth', 'No Smooth', 'No bilinear filtering on this poly\'s texture', 128), ('NO_SMOOTH', 'No Smooth', 'No bilinear filtering on this poly\'s texture', 128),
) )
class PSX_PG_material(PropertyGroup): class PSX_PG_material(PropertyGroup):
@@ -26,13 +26,23 @@ mesh_triangle_types_items_dict = {item[0]: item[3] for item in mesh_triangle_typ
mesh_triangle_bit_flags_items_dict = {item[0]: item[3] for item in mesh_triangle_bit_flags_items} mesh_triangle_bit_flags_items_dict = {item[0]: item[3] for item in mesh_triangle_bit_flags_items}
def get_poly_flags(material: PSX_PG_material) -> int: def triangle_type_and_bit_flags_to_poly_flags(mesh_triangle_type: str, mesh_triangle_bit_flags: set[str]) -> int:
poly_flags = 0 poly_flags = 0
poly_flags |= mesh_triangle_types_items_dict[material.mesh_triangle_type] poly_flags |= mesh_triangle_types_items_dict.get(mesh_triangle_type, 0)
for flag in material.mesh_triangle_bit_flags: for flag in mesh_triangle_bit_flags:
poly_flags |= mesh_triangle_bit_flags_items_dict[flag] poly_flags |= mesh_triangle_bit_flags_items_dict.get(flag, 0)
return poly_flags return poly_flags
def poly_flags_to_triangle_type_and_bit_flags(poly_flags: int) -> (str, set[str]):
try:
triangle_type = next(item[0] for item in mesh_triangle_types_items if item[3] == (poly_flags & 15))
except StopIteration:
triangle_type = 'NORMAL'
triangle_bit_flags = {item[0] for item in mesh_triangle_bit_flags_items if item[3] & poly_flags}
return triangle_type, triangle_bit_flags
classes = ( classes = (
PSX_PG_material, PSX_PG_material,
) )