Added scale factor to PSK export
Also a load of cleanup I don't have time to catalog
This commit is contained in:
@@ -8,7 +8,8 @@ from bpy.types import Context, Armature, Action, Object, AnimData, TimelineMarke
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
from bpy_types import Operator
|
||||
|
||||
from .properties import PSA_PG_export, PSA_PG_export_action_list_item, filter_sequences
|
||||
from .properties import PSA_PG_export, PSA_PG_export_action_list_item, filter_sequences, \
|
||||
get_sequences_from_name_and_frame_range
|
||||
from ..builder import build_psa, PsaBuildSequence, PsaBuildOptions
|
||||
from ..writer import write_psa
|
||||
from ...shared.helpers import populate_bone_collection_list, get_nla_strips_in_frame_range
|
||||
@@ -144,7 +145,7 @@ def get_timeline_marker_sequence_frame_ranges(animation_data: AnimData, context:
|
||||
if next_marker_index < len(sorted_timeline_markers):
|
||||
# There is a next marker. Use that next marker's frame position as the last frame of this sequence.
|
||||
frame_end = sorted_timeline_markers[next_marker_index].frame
|
||||
nla_strips = get_nla_strips_in_frame_range(animation_data, marker.frame, frame_end)
|
||||
nla_strips = list(get_nla_strips_in_frame_range(animation_data, marker.frame, frame_end))
|
||||
if len(nla_strips) > 0:
|
||||
frame_end = min(frame_end, max(map(lambda nla_strip: nla_strip.frame_end, nla_strips)))
|
||||
frame_start = max(frame_start, min(map(lambda nla_strip: nla_strip.frame_start, nla_strips)))
|
||||
@@ -168,20 +169,6 @@ def get_timeline_marker_sequence_frame_ranges(animation_data: AnimData, context:
|
||||
return sequence_frame_ranges
|
||||
|
||||
|
||||
def get_sequences_from_name_and_frame_range(name: str, frame_start: int, frame_end: int) -> List[Tuple[str, int, int]]:
|
||||
reversed_pattern = r'(.+)/(.+)'
|
||||
reversed_match = re.match(reversed_pattern, name)
|
||||
if reversed_match:
|
||||
forward_name = reversed_match.group(1)
|
||||
backwards_name = reversed_match.group(2)
|
||||
return [
|
||||
(forward_name, frame_start, frame_end),
|
||||
(backwards_name, frame_end, frame_start)
|
||||
]
|
||||
else:
|
||||
return [(name, frame_start, frame_end)]
|
||||
|
||||
|
||||
def get_sequences_from_action(action: Action) -> List[Tuple[str, int, int]]:
|
||||
frame_start = int(action.frame_range[0])
|
||||
frame_end = int(action.frame_range[1])
|
||||
@@ -266,16 +253,22 @@ class PSA_OT_export(Operator, ExportHelper):
|
||||
row.operator(PSA_OT_export_actions_select_all.bl_idname, text='All', icon='CHECKBOX_HLT')
|
||||
row.operator(PSA_OT_export_actions_deselect_all.bl_idname, text='None', icon='CHECKBOX_DEHLT')
|
||||
|
||||
# ACTIONS
|
||||
if pg.sequence_source == 'ACTIONS':
|
||||
rows = max(3, min(len(pg.action_list), 10))
|
||||
sequences_panel.template_list('PSA_UL_export_sequences', '', pg, 'action_list', pg, 'action_list_index', rows=rows)
|
||||
elif pg.sequence_source == 'TIMELINE_MARKERS':
|
||||
rows = max(3, min(len(pg.marker_list), 10))
|
||||
sequences_panel.template_list('PSA_UL_export_sequences', '', pg, 'marker_list', pg, 'marker_list_index', rows=rows)
|
||||
elif pg.sequence_source == 'NLA_TRACK_STRIPS':
|
||||
rows = max(3, min(len(pg.nla_strip_list), 10))
|
||||
sequences_panel.template_list('PSA_UL_export_sequences', '', pg, 'nla_strip_list', pg, 'nla_strip_list_index', rows=rows)
|
||||
from .ui import PSA_UL_export_sequences
|
||||
|
||||
def get_sequences_propnames_from_source(sequence_source: str) -> Tuple[str, str]:
|
||||
match sequence_source:
|
||||
case 'ACTIONS':
|
||||
return 'action_list', 'action_list_index'
|
||||
case 'TIMELINE_MARKERS':
|
||||
return 'marker_list', 'marker_list_index'
|
||||
case 'NLA_TRACK_STRIPS':
|
||||
return 'nla_strip_list', 'nla_strip_list_index'
|
||||
case _:
|
||||
raise ValueError(f'Unhandled sequence source: {sequence_source}')
|
||||
|
||||
propname, active_propname = get_sequences_propnames_from_source(pg.sequence_source)
|
||||
sequences_panel.template_list(PSA_UL_export_sequences.bl_idname, '', pg, propname, pg, active_propname,
|
||||
rows=max(3, min(len(getattr(pg, propname), 10))))
|
||||
|
||||
flow = sequences_panel.grid_flow()
|
||||
flow.use_property_split = True
|
||||
@@ -379,43 +372,44 @@ class PSA_OT_export(Operator, ExportHelper):
|
||||
|
||||
export_sequences: List[PsaBuildSequence] = []
|
||||
|
||||
if pg.sequence_source == 'ACTIONS':
|
||||
for action_item in filter(lambda x: x.is_selected, pg.action_list):
|
||||
if len(action_item.action.fcurves) == 0:
|
||||
continue
|
||||
export_sequence = PsaBuildSequence()
|
||||
export_sequence.nla_state.action = action_item.action
|
||||
export_sequence.name = action_item.name
|
||||
export_sequence.nla_state.frame_start = action_item.frame_start
|
||||
export_sequence.nla_state.frame_end = action_item.frame_end
|
||||
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [action_item.action])
|
||||
export_sequence.compression_ratio = action_item.action.psa_export.compression_ratio
|
||||
export_sequence.key_quota = action_item.action.psa_export.key_quota
|
||||
export_sequences.append(export_sequence)
|
||||
elif pg.sequence_source == 'TIMELINE_MARKERS':
|
||||
for marker_item in filter(lambda x: x.is_selected, pg.marker_list):
|
||||
export_sequence = PsaBuildSequence()
|
||||
export_sequence.name = marker_item.name
|
||||
export_sequence.nla_state.action = None
|
||||
export_sequence.nla_state.frame_start = marker_item.frame_start
|
||||
export_sequence.nla_state.frame_end = marker_item.frame_end
|
||||
nla_strips_actions = set(
|
||||
map(lambda x: x.action, get_nla_strips_in_frame_range(animation_data, marker_item.frame_start, marker_item.frame_end)))
|
||||
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, nla_strips_actions)
|
||||
export_sequences.append(export_sequence)
|
||||
elif pg.sequence_source == 'NLA_TRACK_STRIPS':
|
||||
for nla_strip_item in filter(lambda x: x.is_selected, pg.nla_strip_list):
|
||||
export_sequence = PsaBuildSequence()
|
||||
export_sequence.name = nla_strip_item.name
|
||||
export_sequence.nla_state.action = None
|
||||
export_sequence.nla_state.frame_start = nla_strip_item.frame_start
|
||||
export_sequence.nla_state.frame_end = nla_strip_item.frame_end
|
||||
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [nla_strip_item.action])
|
||||
export_sequence.compression_ratio = nla_strip_item.action.psa_export.compression_ratio
|
||||
export_sequence.key_quota = nla_strip_item.action.psa_export.key_quota
|
||||
export_sequences.append(export_sequence)
|
||||
else:
|
||||
raise ValueError(f'Unhandled sequence source: {pg.sequence_source}')
|
||||
match pg.sequence_source:
|
||||
case 'ACTIONS':
|
||||
for action_item in filter(lambda x: x.is_selected, pg.action_list):
|
||||
if len(action_item.action.fcurves) == 0:
|
||||
continue
|
||||
export_sequence = PsaBuildSequence()
|
||||
export_sequence.nla_state.action = action_item.action
|
||||
export_sequence.name = action_item.name
|
||||
export_sequence.nla_state.frame_start = action_item.frame_start
|
||||
export_sequence.nla_state.frame_end = action_item.frame_end
|
||||
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [action_item.action])
|
||||
export_sequence.compression_ratio = action_item.action.psa_export.compression_ratio
|
||||
export_sequence.key_quota = action_item.action.psa_export.key_quota
|
||||
export_sequences.append(export_sequence)
|
||||
case 'TIMELINE_MARKERS':
|
||||
for marker_item in filter(lambda x: x.is_selected, pg.marker_list):
|
||||
export_sequence = PsaBuildSequence()
|
||||
export_sequence.name = marker_item.name
|
||||
export_sequence.nla_state.action = None
|
||||
export_sequence.nla_state.frame_start = marker_item.frame_start
|
||||
export_sequence.nla_state.frame_end = marker_item.frame_end
|
||||
nla_strips_actions = set(
|
||||
map(lambda x: x.action, get_nla_strips_in_frame_range(animation_data, marker_item.frame_start, marker_item.frame_end)))
|
||||
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, nla_strips_actions)
|
||||
export_sequences.append(export_sequence)
|
||||
case 'NLA_TRACK_STRIPS':
|
||||
for nla_strip_item in filter(lambda x: x.is_selected, pg.nla_strip_list):
|
||||
export_sequence = PsaBuildSequence()
|
||||
export_sequence.name = nla_strip_item.name
|
||||
export_sequence.nla_state.action = None
|
||||
export_sequence.nla_state.frame_start = nla_strip_item.frame_start
|
||||
export_sequence.nla_state.frame_end = nla_strip_item.frame_end
|
||||
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [nla_strip_item.action])
|
||||
export_sequence.compression_ratio = nla_strip_item.action.psa_export.compression_ratio
|
||||
export_sequence.key_quota = nla_strip_item.action.psa_export.key_quota
|
||||
export_sequences.append(export_sequence)
|
||||
case _:
|
||||
raise ValueError(f'Unhandled sequence source: {pg.sequence_source}')
|
||||
|
||||
options = PsaBuildOptions()
|
||||
options.animation_data = animation_data
|
||||
@@ -448,13 +442,15 @@ class PSA_OT_export_actions_select_all(Operator):
|
||||
@classmethod
|
||||
def get_item_list(cls, context):
|
||||
pg = context.scene.psa_export
|
||||
if pg.sequence_source == 'ACTIONS':
|
||||
return pg.action_list
|
||||
elif pg.sequence_source == 'TIMELINE_MARKERS':
|
||||
return pg.marker_list
|
||||
elif pg.sequence_source == 'NLA_TRACK_STRIPS':
|
||||
return pg.nla_strip_list
|
||||
return None
|
||||
match pg.sequence_source:
|
||||
case 'ACTIONS':
|
||||
return pg.action_list
|
||||
case 'TIMELINE_MARKERS':
|
||||
return pg.marker_list
|
||||
case 'NLA_TRACK_STRIPS':
|
||||
return pg.nla_strip_list
|
||||
case _:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -481,13 +477,15 @@ class PSA_OT_export_actions_deselect_all(Operator):
|
||||
@classmethod
|
||||
def get_item_list(cls, context):
|
||||
pg = context.scene.psa_export
|
||||
if pg.sequence_source == 'ACTIONS':
|
||||
return pg.action_list
|
||||
elif pg.sequence_source == 'TIMELINE_MARKERS':
|
||||
return pg.marker_list
|
||||
elif pg.sequence_source == 'NLA_TRACK_STRIPS':
|
||||
return pg.nla_strip_list
|
||||
return None
|
||||
match pg.sequence_source:
|
||||
case 'ACTIONS':
|
||||
return pg.action_list
|
||||
case 'TIMELINE_MARKERS':
|
||||
return pg.marker_list
|
||||
case 'NLA_TRACK_STRIPS':
|
||||
return pg.nla_strip_list
|
||||
case _:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import re
|
||||
import sys
|
||||
from fnmatch import fnmatch
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from bpy.props import BoolProperty, PointerProperty, EnumProperty, FloatProperty, CollectionProperty, IntProperty, \
|
||||
StringProperty
|
||||
@@ -42,6 +42,20 @@ class PSA_PG_export_nla_strip_list_item(PropertyGroup):
|
||||
is_selected: BoolProperty(default=True)
|
||||
|
||||
|
||||
def get_sequences_from_name_and_frame_range(name: str, frame_start: int, frame_end: int) -> List[Tuple[str, int, int]]:
|
||||
reversed_pattern = r'(.+)/(.+)'
|
||||
reversed_match = re.match(reversed_pattern, name)
|
||||
if reversed_match:
|
||||
forward_name = reversed_match.group(1)
|
||||
backwards_name = reversed_match.group(2)
|
||||
return [
|
||||
(forward_name, frame_start, frame_end),
|
||||
(backwards_name, frame_end, frame_start)
|
||||
]
|
||||
else:
|
||||
return [(name, frame_start, frame_end)]
|
||||
|
||||
|
||||
def nla_track_update_cb(self: 'PSA_PG_export', context: Context) -> None:
|
||||
self.nla_strip_list.clear()
|
||||
match = re.match(r'^(\d+).+$', self.nla_track)
|
||||
@@ -52,11 +66,12 @@ def nla_track_update_cb(self: 'PSA_PG_export', context: Context) -> None:
|
||||
return
|
||||
nla_track = animation_data.nla_tracks[self.nla_track_index]
|
||||
for nla_strip in nla_track.strips:
|
||||
strip: PSA_PG_export_nla_strip_list_item = self.nla_strip_list.add()
|
||||
strip.action = nla_strip.action
|
||||
strip.name = nla_strip.name
|
||||
strip.frame_start = nla_strip.frame_start
|
||||
strip.frame_end = nla_strip.frame_end
|
||||
for sequence_name, frame_start, frame_end in get_sequences_from_name_and_frame_range(nla_strip.name, nla_strip.frame_start, nla_strip.frame_end):
|
||||
strip: PSA_PG_export_nla_strip_list_item = self.nla_strip_list.add()
|
||||
strip.action = nla_strip.action
|
||||
strip.name = sequence_name
|
||||
strip.frame_start = frame_start
|
||||
strip.frame_end = frame_end
|
||||
|
||||
|
||||
def get_animation_data(pg: 'PSA_PG_export', context: Context) -> Optional[AnimData]:
|
||||
@@ -69,10 +84,9 @@ def get_animation_data(pg: 'PSA_PG_export', context: Context) -> Optional[AnimDa
|
||||
def nla_track_search_cb(self, context: Context, edit_text: str):
|
||||
pg = getattr(context.scene, 'psa_export')
|
||||
animation_data = get_animation_data(pg, context)
|
||||
if animation_data is None:
|
||||
return
|
||||
for index, nla_track in enumerate(animation_data.nla_tracks):
|
||||
yield f'{index} - {nla_track.name}'
|
||||
if animation_data is not None:
|
||||
for index, nla_track in enumerate(animation_data.nla_tracks):
|
||||
yield f'{index} - {nla_track.name}'
|
||||
|
||||
|
||||
def animation_data_override_update_cb(self: 'PSA_PG_export', context: Context):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import cast
|
||||
import typing
|
||||
|
||||
from bpy.types import UIList
|
||||
|
||||
@@ -6,6 +6,7 @@ from .properties import PSA_PG_export_action_list_item, filter_sequences
|
||||
|
||||
|
||||
class PSA_UL_export_sequences(UIList):
|
||||
bl_idname = 'PSA_UL_export_sequences'
|
||||
|
||||
def __init__(self):
|
||||
super(PSA_UL_export_sequences, self).__init__()
|
||||
@@ -13,7 +14,7 @@ class PSA_UL_export_sequences(UIList):
|
||||
self.use_filter_show = True
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
item = cast(PSA_PG_export_action_list_item, item)
|
||||
item = typing.cast(PSA_PG_export_action_list_item, item)
|
||||
is_pose_marker = hasattr(item, 'is_pose_marker') and item.is_pose_marker
|
||||
layout.prop(item, 'is_selected', icon_only=True, text=item.name)
|
||||
if hasattr(item, 'action') and item.action is not None and item.action.asset_data is not None:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import typing
|
||||
from typing import Optional
|
||||
|
||||
import bmesh
|
||||
import bpy
|
||||
import numpy as np
|
||||
from bpy.types import Armature, Material, Collection, Context
|
||||
from bpy.types import Material, Collection, Context
|
||||
from mathutils import Matrix
|
||||
|
||||
from .data import *
|
||||
from .properties import triangle_type_and_bit_flags_to_poly_flags
|
||||
@@ -23,6 +24,7 @@ class PskBuildOptions(object):
|
||||
self.object_eval_state = 'EVALUATED'
|
||||
self.materials: List[Material] = []
|
||||
self.should_enforce_bone_name_restrictions = False
|
||||
self.scale = 1.0
|
||||
|
||||
|
||||
def get_mesh_objects_for_collection(collection: Collection, should_exclude_hidden_meshes: bool = True):
|
||||
@@ -40,7 +42,7 @@ def get_mesh_objects_for_context(context: Context):
|
||||
yield obj
|
||||
|
||||
|
||||
def get_armature_for_mesh_objects(mesh_objects: List[Object]) -> Optional[Object]:
|
||||
def get_armature_for_mesh_objects(mesh_objects: Iterable[Object]) -> Optional[Object]:
|
||||
# Ensure that there are either no armature modifiers (static mesh) or that there is exactly one armature modifier
|
||||
# object shared between all meshes.
|
||||
armature_modifier_objects = set()
|
||||
@@ -100,6 +102,8 @@ def build_psk(context, input_objects: PskInputObjects, options: PskBuildOptions)
|
||||
psk = Psk()
|
||||
bones = []
|
||||
|
||||
scale_matrix = Matrix.Scale(options.scale, 4)
|
||||
|
||||
if armature_object is None or len(armature_object.data.bones) == 0:
|
||||
# If the mesh has no armature object or no bones, simply assign it a dummy bone at the root to satisfy the
|
||||
# requirement that a PSK file must have at least one bone.
|
||||
@@ -151,6 +155,8 @@ def build_psk(context, input_objects: PskInputObjects, options: PskBuildOptions)
|
||||
rotation = bone_rotation @ local_rotation
|
||||
rotation.conjugate()
|
||||
|
||||
location = scale_matrix @ location
|
||||
|
||||
psk_bone.location.x = location.x
|
||||
psk_bone.location.y = location.y
|
||||
psk_bone.location.z = location.z
|
||||
@@ -230,11 +236,12 @@ def build_psk(context, input_objects: PskInputObjects, options: PskBuildOptions)
|
||||
armature_object.data.pose_position = old_pose_position
|
||||
|
||||
vertex_offset = len(psk.points)
|
||||
matrix_world = scale_matrix @ mesh_object.matrix_world
|
||||
|
||||
# VERTICES
|
||||
for vertex in mesh_data.vertices:
|
||||
point = Vector3()
|
||||
v = mesh_object.matrix_world @ vertex.co
|
||||
v = matrix_world @ vertex.co
|
||||
point.x = v.x
|
||||
point.y = v.y
|
||||
point.z = v.z
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
import bpy
|
||||
from bpy.props import StringProperty, BoolProperty, EnumProperty
|
||||
from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty
|
||||
from bpy.types import Operator, Context, Object
|
||||
from bpy_extras.io_utils import ExportHelper
|
||||
|
||||
@@ -32,6 +32,7 @@ def get_materials_for_mesh_objects(mesh_objects: List[Object]):
|
||||
materials.append(material)
|
||||
return materials
|
||||
|
||||
|
||||
def populate_material_list(mesh_objects, material_list):
|
||||
materials = get_materials_for_mesh_objects(mesh_objects)
|
||||
material_list.clear()
|
||||
@@ -107,6 +108,13 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
||||
name='Visible Only',
|
||||
description='Export only visible meshes'
|
||||
)
|
||||
scale: FloatProperty(
|
||||
name='Scale',
|
||||
default=1.0,
|
||||
description='Scale factor to apply to the exported mesh and armature',
|
||||
min=0.0001,
|
||||
soft_max=100.0
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
collection = bpy.data.collections.get(self.collection)
|
||||
@@ -122,6 +130,7 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
||||
options.object_eval_state = self.object_eval_state
|
||||
options.materials = get_materials_for_mesh_objects(input_objects.mesh_objects)
|
||||
options.should_enforce_bone_name_restrictions = self.should_enforce_bone_name_restrictions
|
||||
options.scale = self.scale
|
||||
|
||||
try:
|
||||
result = build_psk(context, input_objects, options)
|
||||
@@ -141,6 +150,12 @@ class PSK_OT_export_collection(Operator, ExportHelper):
|
||||
def draw(self, context: Context):
|
||||
layout = self.layout
|
||||
|
||||
flow = layout.grid_flow(row_major=True)
|
||||
flow.use_property_split = True
|
||||
flow.use_property_decorate = False
|
||||
|
||||
flow.prop(self, 'scale')
|
||||
|
||||
# MESH
|
||||
mesh_header, mesh_panel = layout.panel('Mesh', default_closed=False)
|
||||
mesh_header.label(text='Mesh', icon='MESH_DATA')
|
||||
@@ -260,6 +275,7 @@ class PSK_OT_export(Operator, ExportHelper):
|
||||
options.object_eval_state = pg.object_eval_state
|
||||
options.materials = [m.material for m in pg.material_list]
|
||||
options.should_enforce_bone_name_restrictions = pg.should_enforce_bone_name_restrictions
|
||||
options.scale = pg.scale
|
||||
|
||||
try:
|
||||
result = build_psk(context, input_objects, options)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from bpy.props import EnumProperty, CollectionProperty, IntProperty, BoolProperty, PointerProperty
|
||||
from bpy.props import EnumProperty, CollectionProperty, IntProperty, BoolProperty, PointerProperty, FloatProperty
|
||||
from bpy.types import PropertyGroup, Material
|
||||
|
||||
from ...shared.types import PSX_PG_bone_collection_list_item
|
||||
@@ -42,6 +42,13 @@ class PSK_PG_export(PropertyGroup):
|
||||
description='Enforce that bone names must only contain letters, numbers, spaces, hyphens and underscores.\n\n'
|
||||
'Depending on the engine, improper bone names might not be referenced correctly by scripts'
|
||||
)
|
||||
scale: FloatProperty(
|
||||
name='Scale',
|
||||
default=1.0,
|
||||
description='Scale factor to apply to the exported mesh',
|
||||
min=0.0001,
|
||||
soft_max=100.0
|
||||
)
|
||||
|
||||
|
||||
classes = (
|
||||
|
||||
@@ -36,41 +36,43 @@ def read_psk(path: str) -> Psk:
|
||||
while fp.read(1):
|
||||
fp.seek(-1, 1)
|
||||
section = Section.from_buffer_copy(fp.read(ctypes.sizeof(Section)))
|
||||
if section.name == b'ACTRHEAD':
|
||||
pass
|
||||
elif section.name == b'PNTS0000':
|
||||
_read_types(fp, Vector3, section, psk.points)
|
||||
elif section.name == b'VTXW0000':
|
||||
if section.data_size == ctypes.sizeof(Psk.Wedge16):
|
||||
_read_types(fp, Psk.Wedge16, section, psk.wedges)
|
||||
elif section.data_size == ctypes.sizeof(Psk.Wedge32):
|
||||
_read_types(fp, Psk.Wedge32, section, psk.wedges)
|
||||
else:
|
||||
raise RuntimeError('Unrecognized wedge format')
|
||||
elif section.name == b'FACE0000':
|
||||
_read_types(fp, Psk.Face, section, psk.faces)
|
||||
elif section.name == b'MATT0000':
|
||||
_read_types(fp, Psk.Material, section, psk.materials)
|
||||
elif section.name == b'REFSKELT':
|
||||
_read_types(fp, Psk.Bone, section, psk.bones)
|
||||
elif section.name == b'RAWWEIGHTS':
|
||||
_read_types(fp, Psk.Weight, section, psk.weights)
|
||||
elif section.name == b'FACE3200':
|
||||
_read_types(fp, Psk.Face32, section, psk.faces)
|
||||
elif section.name == b'VERTEXCOLOR':
|
||||
_read_types(fp, Color, section, psk.vertex_colors)
|
||||
elif section.name.startswith(b'EXTRAUVS'):
|
||||
_read_types(fp, Vector2, section, psk.extra_uvs)
|
||||
elif section.name == b'VTXNORMS':
|
||||
_read_types(fp, Vector3, section, psk.vertex_normals)
|
||||
elif section.name == b'MRPHINFO':
|
||||
_read_types(fp, Psk.MorphInfo, section, psk.morph_infos)
|
||||
elif section.name == b'MRPHDATA':
|
||||
_read_types(fp, Psk.MorphData, section, psk.morph_data)
|
||||
else:
|
||||
# Section is not handled, skip it.
|
||||
fp.seek(section.data_size * section.data_count, os.SEEK_CUR)
|
||||
warnings.warn(f'Unrecognized section "{section.name} at position {fp.tell():15}"')
|
||||
match section.name:
|
||||
case b'ACTRHEAD':
|
||||
pass
|
||||
case b'PNTS0000':
|
||||
_read_types(fp, Vector3, section, psk.points)
|
||||
case b'VTXW0000':
|
||||
if section.data_size == ctypes.sizeof(Psk.Wedge16):
|
||||
_read_types(fp, Psk.Wedge16, section, psk.wedges)
|
||||
elif section.data_size == ctypes.sizeof(Psk.Wedge32):
|
||||
_read_types(fp, Psk.Wedge32, section, psk.wedges)
|
||||
else:
|
||||
raise RuntimeError('Unrecognized wedge format')
|
||||
case b'FACE0000':
|
||||
_read_types(fp, Psk.Face, section, psk.faces)
|
||||
case b'MATT0000':
|
||||
_read_types(fp, Psk.Material, section, psk.materials)
|
||||
case b'REFSKELT':
|
||||
_read_types(fp, Psk.Bone, section, psk.bones)
|
||||
case b'RAWWEIGHTS':
|
||||
_read_types(fp, Psk.Weight, section, psk.weights)
|
||||
case b'FACE3200':
|
||||
_read_types(fp, Psk.Face32, section, psk.faces)
|
||||
case b'VERTEXCOLOR':
|
||||
_read_types(fp, Color, section, psk.vertex_colors)
|
||||
case b'VTXNORMS':
|
||||
_read_types(fp, Vector3, section, psk.vertex_normals)
|
||||
case b'MRPHINFO':
|
||||
_read_types(fp, Psk.MorphInfo, section, psk.morph_infos)
|
||||
case b'MRPHDATA':
|
||||
_read_types(fp, Psk.MorphData, section, psk.morph_data)
|
||||
case _:
|
||||
if section.name.startswith(b'EXTRAUVS'):
|
||||
_read_types(fp, Vector2, section, psk.extra_uvs)
|
||||
else:
|
||||
# Section is not handled, skip it.
|
||||
fp.seek(section.data_size * section.data_count, os.SEEK_CUR)
|
||||
warnings.warn(f'Unrecognized section "{section.name} at position {fp.tell():15}"')
|
||||
|
||||
'''
|
||||
UEViewer exports a sidecar file (*.props.txt) with fully-qualified reference paths for each material
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import re
|
||||
import typing
|
||||
from typing import List, Iterable
|
||||
from typing import List, Iterable, cast
|
||||
|
||||
import bpy.types
|
||||
from bpy.types import NlaStrip, Object, AnimData
|
||||
import bpy
|
||||
from bpy.props import CollectionProperty
|
||||
from bpy.types import AnimData, Object
|
||||
from bpy.types import Armature
|
||||
|
||||
|
||||
def rgb_to_srgb(c: float):
|
||||
@@ -13,10 +14,9 @@ def rgb_to_srgb(c: float):
|
||||
return 12.92 * c
|
||||
|
||||
|
||||
def get_nla_strips_in_frame_range(animation_data: AnimData, frame_min: float, frame_max: float) -> List[NlaStrip]:
|
||||
def get_nla_strips_in_frame_range(animation_data: AnimData, frame_min: float, frame_max: float):
|
||||
if animation_data is None:
|
||||
return []
|
||||
strips = []
|
||||
return
|
||||
for nla_track in animation_data.nla_tracks:
|
||||
if nla_track.mute:
|
||||
continue
|
||||
@@ -24,11 +24,10 @@ def get_nla_strips_in_frame_range(animation_data: AnimData, frame_min: float, fr
|
||||
if (strip.frame_start < frame_min and strip.frame_end > frame_max) or \
|
||||
(frame_min <= strip.frame_start < frame_max) or \
|
||||
(frame_min < strip.frame_end <= frame_max):
|
||||
strips.append(strip)
|
||||
return strips
|
||||
yield strip
|
||||
|
||||
|
||||
def populate_bone_collection_list(armature_object: Object, bone_collection_list: bpy.props.CollectionProperty) -> None:
|
||||
def populate_bone_collection_list(armature_object: Object, bone_collection_list: CollectionProperty) -> None:
|
||||
"""
|
||||
Updates the bone collections collection.
|
||||
|
||||
@@ -53,7 +52,7 @@ def populate_bone_collection_list(armature_object: Object, bone_collection_list:
|
||||
|
||||
bone_collection_list.clear()
|
||||
|
||||
armature = armature_object.data
|
||||
armature = cast(Armature, armature_object.data)
|
||||
|
||||
if armature is None:
|
||||
return
|
||||
@@ -82,7 +81,7 @@ def check_bone_names(bone_names: Iterable[str]):
|
||||
f'You can bypass this by disabling "Enforce Bone Name Restrictions" in the export settings.')
|
||||
|
||||
|
||||
def get_export_bone_names(armature_object: Object, bone_filter_mode: str, bone_collection_indices: List[int]) -> List[str]:
|
||||
def get_export_bone_names(armature_object: Object, bone_filter_mode: str, bone_collection_indices: Iterable[int]) -> List[str]:
|
||||
"""
|
||||
Returns a sorted list of bone indices that should be exported for the given bone filter mode and bone collections.
|
||||
|
||||
@@ -90,13 +89,13 @@ def get_export_bone_names(armature_object: Object, bone_filter_mode: str, bone_c
|
||||
|
||||
:param armature_object: Blender object with type 'ARMATURE'
|
||||
:param bone_filter_mode: One of ['ALL', 'BONE_COLLECTIONS']
|
||||
:param bone_collection_indices: List of bone collection indices to be exported.
|
||||
:param bone_collection_indices: A list of bone collection indices to export.
|
||||
:return: A sorted list of bone indices that should be exported.
|
||||
"""
|
||||
if armature_object is None or armature_object.type != 'ARMATURE':
|
||||
raise ValueError('An armature object must be supplied')
|
||||
|
||||
armature_data = typing.cast(bpy.types.Armature, armature_object.data)
|
||||
armature_data = cast(Armature, armature_object.data)
|
||||
bones = armature_data.bones
|
||||
bone_names = [x.name for x in bones]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user