Added vertex color space conversion option for sRGBA (this is now the default to match UE4.24)

This commit is contained in:
Colin Basnett
2022-01-27 15:11:32 -08:00
parent 2ba29b04d3
commit a62ee207e1
4 changed files with 76 additions and 19 deletions

View File

@@ -72,6 +72,7 @@ def register():
bpy.types.TOPBAR_MT_file_import.append(psk_import_menu_func) bpy.types.TOPBAR_MT_file_import.append(psk_import_menu_func)
bpy.types.TOPBAR_MT_file_export.append(psa_export_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.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.psa_export = PointerProperty(type=psa_exporter.PsaExportPropertyGroup)
bpy.types.Scene.psk_export = PointerProperty(type=psk_exporter.PskExportPropertyGroup) bpy.types.Scene.psk_export = PointerProperty(type=psk_exporter.PskExportPropertyGroup)

View File

@@ -1,6 +1,13 @@
from typing import List 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): def populate_bone_group_list(armature_object, bone_group_list):
bone_group_list.clear() bone_group_list.clear()

View File

@@ -235,7 +235,7 @@ def on_psa_file_path_updated(property, context):
pass 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_file_path: StringProperty(default='', update=on_psa_file_path_updated, name='PSA File Path')
psa_bones: CollectionProperty(type=PsaImportPsaBoneItem) psa_bones: CollectionProperty(type=PsaImportPsaBoneItem)
action_list: CollectionProperty(type=PsaImportActionListItem) action_list: CollectionProperty(type=PsaImportActionListItem)

View File

@@ -1,23 +1,35 @@
import os import os
import bpy import bpy
import bmesh import bmesh
import numpy as np
from math import inf
from typing import Optional from typing import Optional
from .data import Psk from .data import Psk
from ..helpers import rgb_to_srgb
from mathutils import Quaternion, Vector, Matrix from mathutils import Quaternion, Vector, Matrix
from .reader import PskReader from .reader import PskReader
from bpy.props import StringProperty from bpy.props import StringProperty, EnumProperty, BoolProperty
from bpy.types import Operator from bpy.types import Operator, PropertyGroup
from bpy_extras.io_utils import ImportHelper 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): class PskImporter(object):
def __init__(self): def __init__(self):
pass pass
def import_psk(self, psk: Psk, name: str, context): def import_psk(self, psk: Psk, context, options: PskImportOptions):
# ARMATURE # ARMATURE
armature_data = bpy.data.armatures.new(name) armature_data = bpy.data.armatures.new(options.name)
armature_object = bpy.data.objects.new(name, armature_data) armature_object = bpy.data.objects.new(options.name, armature_data)
armature_object.show_in_front = True armature_object.show_in_front = True
context.scene.collection.objects.link(armature_object) context.scene.collection.objects.link(armature_object)
@@ -95,8 +107,8 @@ class PskImporter(object):
edit_bone['post_quat'] = import_bone.local_rotation.conjugated() edit_bone['post_quat'] = import_bone.local_rotation.conjugated()
# MESH # MESH
mesh_data = bpy.data.meshes.new(name) mesh_data = bpy.data.meshes.new(options.name)
mesh_object = bpy.data.objects.new(name, mesh_data) mesh_object = bpy.data.objects.new(options.name, mesh_data)
# MATERIALS # MATERIALS
for material in psk.materials: for material in psk.materials:
@@ -138,7 +150,7 @@ class PskImporter(object):
data_index += 1 data_index += 1
# EXTRA UVS # 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)) extra_uv_channel_count = int(len(psk.extra_uvs) / len(psk.wedges))
wedge_index_offset = 0 wedge_index_offset = 0
for extra_uv_index in range(extra_uv_channel_count): for extra_uv_index in range(extra_uv_channel_count):
@@ -154,21 +166,28 @@ class PskImporter(object):
wedge_index_offset += len(psk.wedges) wedge_index_offset += len(psk.wedges)
# VERTEX COLORS # 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_color_data = mesh_data.vertex_colors.new(name='VERTEXCOLOR')
vertex_colors = [None] * len(psk.points)
ambiguous_vertex_color_point_indices = [] ambiguous_vertex_color_point_indices = []
for wedge_index, wedge in enumerate(psk.wedges): for wedge_index, wedge in enumerate(psk.wedges):
point_index = wedge.point_index point_index = wedge.point_index
psk_vertex_color = psk.vertex_colors[wedge_index] psk_vertex_color = psk.vertex_colors[wedge_index].normalized()
if vertex_colors[point_index] is not None and vertex_colors[point_index] != psk_vertex_color: if vertex_colors[point_index, 0] != inf and tuple(vertex_colors[point_index]) != psk_vertex_color:
ambiguous_vertex_color_point_indices.append(point_index) 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): for loop_index, loop in enumerate(mesh_data.loops):
vertex_color = vertex_colors[loop.vertex_index] vertex_color = vertex_colors[loop.vertex_index]
if vertex_color is not None: 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: else:
vertex_color_data.data[loop_index].color = 1.0, 1.0, 1.0, 1.0 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.') print(f'WARNING: {len(ambiguous_vertex_color_point_indices)} vertex(es) with ambiguous vertex colors.')
# VERTEX NORMALS # 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)) mesh_data.polygons.foreach_set("use_smooth", [True] * len(mesh_data.polygons))
normals = [] normals = []
for vertex_normal in psk.vertex_normals: for vertex_normal in psk.vertex_normals:
@@ -208,6 +227,21 @@ class PskImporter(object):
pass 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): class PskImportOperator(Operator, ImportHelper):
bl_idname = 'import.psk' bl_idname = 'import.psk'
bl_label = 'Export' bl_label = 'Export'
@@ -221,13 +255,28 @@ class PskImportOperator(Operator, ImportHelper):
default='') default='')
def execute(self, context): def execute(self, context):
pg = context.scene.psk_import
reader = PskReader() reader = PskReader()
psk = reader.read(self.filepath) psk = reader.read(self.filepath)
name = os.path.splitext(os.path.basename(self.filepath))[0] options = PskImportOptions()
PskImporter().import_psk(psk, name, context) 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'} 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__ = [ __classes__ = [
PskImportOperator PskImportOperator,
PskImportPropertyGroup,
] ]