diff --git a/io_scene_psk_psa/__init__.py b/io_scene_psk_psa/__init__.py index 86a4768..9eb968c 100644 --- a/io_scene_psk_psa/__init__.py +++ b/io_scene_psk_psa/__init__.py @@ -72,6 +72,7 @@ def register(): bpy.types.TOPBAR_MT_file_import.append(psk_import_menu_func) bpy.types.TOPBAR_MT_file_export.append(psa_export_menu_func) bpy.types.Scene.psa_import = PointerProperty(type=psa_importer.PsaImportPropertyGroup) + bpy.types.Scene.psk_import = PointerProperty(type=psk_importer.PskImportPropertyGroup) bpy.types.Scene.psa_export = PointerProperty(type=psa_exporter.PsaExportPropertyGroup) bpy.types.Scene.psk_export = PointerProperty(type=psk_exporter.PskExportPropertyGroup) diff --git a/io_scene_psk_psa/helpers.py b/io_scene_psk_psa/helpers.py index 34b74cb..51cc9da 100644 --- a/io_scene_psk_psa/helpers.py +++ b/io_scene_psk_psa/helpers.py @@ -1,6 +1,13 @@ from typing import List +def rgb_to_srgb(c): + if c > 0.0031308: + return 1.055 * (pow(c, (1.0 / 2.4))) - 0.055 + else: + return 12.92 * c + + def populate_bone_group_list(armature_object, bone_group_list): bone_group_list.clear() diff --git a/io_scene_psk_psa/psa/importer.py b/io_scene_psk_psa/psa/importer.py index 29febd7..9e904d5 100644 --- a/io_scene_psk_psa/psa/importer.py +++ b/io_scene_psk_psa/psa/importer.py @@ -235,7 +235,7 @@ def on_psa_file_path_updated(property, context): pass -class PsaImportPropertyGroup(bpy.types.PropertyGroup): +class PsaImportPropertyGroup(PropertyGroup): psa_file_path: StringProperty(default='', update=on_psa_file_path_updated, name='PSA File Path') psa_bones: CollectionProperty(type=PsaImportPsaBoneItem) action_list: CollectionProperty(type=PsaImportActionListItem) diff --git a/io_scene_psk_psa/psk/importer.py b/io_scene_psk_psa/psk/importer.py index 35cbb02..9311a59 100644 --- a/io_scene_psk_psa/psk/importer.py +++ b/io_scene_psk_psa/psk/importer.py @@ -1,23 +1,35 @@ import os import bpy import bmesh +import numpy as np +from math import inf from typing import Optional from .data import Psk +from ..helpers import rgb_to_srgb from mathutils import Quaternion, Vector, Matrix from .reader import PskReader -from bpy.props import StringProperty -from bpy.types import Operator +from bpy.props import StringProperty, EnumProperty, BoolProperty +from bpy.types import Operator, PropertyGroup from bpy_extras.io_utils import ImportHelper +class PskImportOptions(object): + def __init__(self): + self.name = '' + self.should_import_vertex_colors = True + self.vertex_color_space = 'sRGB' + self.should_import_vertex_normals = True + self.should_import_extra_uvs = True + + class PskImporter(object): def __init__(self): pass - def import_psk(self, psk: Psk, name: str, context): + def import_psk(self, psk: Psk, context, options: PskImportOptions): # ARMATURE - armature_data = bpy.data.armatures.new(name) - armature_object = bpy.data.objects.new(name, armature_data) + armature_data = bpy.data.armatures.new(options.name) + armature_object = bpy.data.objects.new(options.name, armature_data) armature_object.show_in_front = True context.scene.collection.objects.link(armature_object) @@ -95,8 +107,8 @@ class PskImporter(object): edit_bone['post_quat'] = import_bone.local_rotation.conjugated() # MESH - mesh_data = bpy.data.meshes.new(name) - mesh_object = bpy.data.objects.new(name, mesh_data) + mesh_data = bpy.data.meshes.new(options.name) + mesh_object = bpy.data.objects.new(options.name, mesh_data) # MATERIALS for material in psk.materials: @@ -138,7 +150,7 @@ class PskImporter(object): data_index += 1 # EXTRA UVS - if psk.has_extra_uvs: + if psk.has_extra_uvs and options.should_import_extra_uvs: extra_uv_channel_count = int(len(psk.extra_uvs) / len(psk.wedges)) wedge_index_offset = 0 for extra_uv_index in range(extra_uv_channel_count): @@ -154,21 +166,28 @@ class PskImporter(object): wedge_index_offset += len(psk.wedges) # VERTEX COLORS - if psk.has_vertex_colors: + if psk.has_vertex_colors and options.should_import_vertex_colors: + size = (len(psk.points), 4) + vertex_colors = np.full(size, inf) vertex_color_data = mesh_data.vertex_colors.new(name='VERTEXCOLOR') - vertex_colors = [None] * len(psk.points) ambiguous_vertex_color_point_indices = [] + for wedge_index, wedge in enumerate(psk.wedges): point_index = wedge.point_index - psk_vertex_color = psk.vertex_colors[wedge_index] - if vertex_colors[point_index] is not None and vertex_colors[point_index] != psk_vertex_color: + psk_vertex_color = psk.vertex_colors[wedge_index].normalized() + if vertex_colors[point_index, 0] != inf and tuple(vertex_colors[point_index]) != psk_vertex_color: ambiguous_vertex_color_point_indices.append(point_index) - vertex_colors[point_index] = psk_vertex_color + else: + vertex_colors[point_index] = psk_vertex_color + + if options.vertex_color_space == 'SRGBA': + for i in range(vertex_colors.shape[0]): + vertex_colors[i, :3] = tuple(map(lambda x: rgb_to_srgb(x), vertex_colors[i, :3])) for loop_index, loop in enumerate(mesh_data.loops): vertex_color = vertex_colors[loop.vertex_index] if vertex_color is not None: - vertex_color_data.data[loop_index].color = vertex_color.normalized() + vertex_color_data.data[loop_index].color = vertex_color else: vertex_color_data.data[loop_index].color = 1.0, 1.0, 1.0, 1.0 @@ -176,7 +195,7 @@ class PskImporter(object): print(f'WARNING: {len(ambiguous_vertex_color_point_indices)} vertex(es) with ambiguous vertex colors.') # VERTEX NORMALS - if psk.has_vertex_normals: + if psk.has_vertex_normals and options.should_import_vertex_normals: mesh_data.polygons.foreach_set("use_smooth", [True] * len(mesh_data.polygons)) normals = [] for vertex_normal in psk.vertex_normals: @@ -208,6 +227,21 @@ class PskImporter(object): pass +class PskImportPropertyGroup(PropertyGroup): + should_import_vertex_colors: BoolProperty(default=True, name='Vertex Colors') + vertex_color_space: EnumProperty( + name='Vertex Color Space', + description='', + default='SRGBA', + items=( + ('LINEAR', 'Linear', ''), + ('SRGBA', 'sRGBA', ''), + ) + ) + should_import_vertex_normals: BoolProperty(default=True, name='Vertex Normals') + should_import_extra_uvs: BoolProperty(default=True, name='Extra UVs') + + class PskImportOperator(Operator, ImportHelper): bl_idname = 'import.psk' bl_label = 'Export' @@ -221,13 +255,28 @@ class PskImportOperator(Operator, ImportHelper): default='') def execute(self, context): + pg = context.scene.psk_import reader = PskReader() psk = reader.read(self.filepath) - name = os.path.splitext(os.path.basename(self.filepath))[0] - PskImporter().import_psk(psk, name, context) + options = PskImportOptions() + options.name = os.path.splitext(os.path.basename(self.filepath))[0] + options.vertex_color_space = pg.vertex_color_space + PskImporter().import_psk(psk, context, options) return {'FINISHED'} + def draw(self, context): + pg = context.scene.psk_import + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + layout.prop(pg, 'should_import_vertex_normals') + layout.prop(pg, 'should_import_extra_uvs') + layout.prop(pg, 'should_import_vertex_colors') + if pg.should_import_vertex_colors: + layout.prop(pg, 'vertex_color_space') + __classes__ = [ - PskImportOperator + PskImportOperator, + PskImportPropertyGroup, ]