Fix PMX import for Blender 5.0 - remove deprecated UV texture APIs
- Replace mesh.uv_textures with mesh.uv_layers - Remove deprecated UV selection properties - Add compatibility helpers for UV vertex selection - Fix morph operators UV handling
This commit is contained in:
@@ -746,22 +746,18 @@ class PMXImporter:
|
|||||||
mesh.polygons.foreach_set("use_smooth", (True,) * len(pmxModel.faces))
|
mesh.polygons.foreach_set("use_smooth", (True,) * len(pmxModel.faces))
|
||||||
mesh.polygons.foreach_set("material_index", material_indices)
|
mesh.polygons.foreach_set("material_index", material_indices)
|
||||||
|
|
||||||
uv_textures, uv_layers = getattr(mesh, "uv_textures", mesh.uv_layers), mesh.uv_layers
|
uv_layers = mesh.uv_layers
|
||||||
uv_tex = uv_textures.new()
|
uv_layer = uv_layers.new()
|
||||||
uv_layer = uv_layers[uv_tex.name]
|
|
||||||
uv_table = {vi: self.flipUV_V(v.uv) for vi, v in enumerate(pmxModel.vertices)}
|
uv_table = {vi: self.flipUV_V(v.uv) for vi, v in enumerate(pmxModel.vertices)}
|
||||||
uv_layer.data.foreach_set("uv", tuple(v for i in loop_indices_orig for v in uv_table[i]))
|
uv_layer.data.foreach_set("uv", tuple(v for i in loop_indices_orig for v in uv_table[i]))
|
||||||
|
|
||||||
if hasattr(mesh, "uv_textures"):
|
|
||||||
for bf, mi in zip(uv_tex.data, material_indices):
|
|
||||||
bf.image = self.__imageTable.get(mi, None)
|
|
||||||
|
|
||||||
if pmxModel.header and pmxModel.header.additional_uvs:
|
if pmxModel.header and pmxModel.header.additional_uvs:
|
||||||
logger.info(f"Importing {pmxModel.header.additional_uvs} additional UVs")
|
logger.info(f"Importing {pmxModel.header.additional_uvs} additional UVs")
|
||||||
zw_data_map = collections.OrderedDict()
|
zw_data_map = collections.OrderedDict()
|
||||||
split_uvzw = lambda uvi: (self.flipUV_V(uvi[:2]), uvi[2:])
|
split_uvzw = lambda uvi: (self.flipUV_V(uvi[:2]), uvi[2:])
|
||||||
for i in range(pmxModel.header.additional_uvs):
|
for i in range(pmxModel.header.additional_uvs):
|
||||||
add_uv = uv_layers[uv_textures.new(name="UV" + str(i + 1)).name]
|
add_uv = uv_layers.new(name="UV" + str(i + 1))
|
||||||
logger.info(f" - {add_uv.name}...(uv channels)")
|
logger.info(f" - {add_uv.name}...(uv channels)")
|
||||||
uv_table = {vi: split_uvzw(v.additional_uvs[i]) for vi, v in enumerate(pmxModel.vertices)}
|
uv_table = {vi: split_uvzw(v.additional_uvs[i]) for vi, v in enumerate(pmxModel.vertices)}
|
||||||
add_uv.data.foreach_set("uv", tuple(v for i in loop_indices_orig for v in uv_table[i][0]))
|
add_uv.data.foreach_set("uv", tuple(v for i in loop_indices_orig for v in uv_table[i][0]))
|
||||||
@@ -771,11 +767,10 @@ class PMXImporter:
|
|||||||
zw_data_map["_" + add_uv.name] = {k: self.flipUV_V(v[1]) for k, v in uv_table.items()}
|
zw_data_map["_" + add_uv.name] = {k: self.flipUV_V(v[1]) for k, v in uv_table.items()}
|
||||||
for name, zw_table in zw_data_map.items():
|
for name, zw_table in zw_data_map.items():
|
||||||
logger.info(f" - {name}...(zw channels of {name[1:]})")
|
logger.info(f" - {name}...(zw channels of {name[1:]})")
|
||||||
add_zw = uv_textures.new(name=name)
|
add_zw = uv_layers.new(name=name)
|
||||||
if add_zw is None:
|
if add_zw is None:
|
||||||
logger.warning("\t* Lost zw channels")
|
logger.warning("\t* Lost zw channels")
|
||||||
continue
|
continue
|
||||||
add_zw = uv_layers[add_zw.name]
|
|
||||||
add_zw.data.foreach_set("uv", tuple(v for i in loop_indices_orig for v in zw_table[i]))
|
add_zw.data.foreach_set("uv", tuple(v for i in loop_indices_orig for v in zw_table[i]))
|
||||||
|
|
||||||
self.__fixOverlappingFaceMaterials(mesh.materials, mesh.vertices, loop_indices, material_indices)
|
self.__fixOverlappingFaceMaterials(mesh.materials, mesh.vertices, loop_indices, material_indices)
|
||||||
|
|||||||
@@ -643,12 +643,13 @@ class ViewUVMorph(bpy.types.Operator):
|
|||||||
base_uv_data = mesh.uv_layers.active.data
|
base_uv_data = mesh.uv_layers.active.data
|
||||||
temp_uv_data = mesh.uv_layers[uv_tex.name].data
|
temp_uv_data = mesh.uv_layers[uv_tex.name].data
|
||||||
for i, l in enumerate(mesh.loops):
|
for i, l in enumerate(mesh.loops):
|
||||||
select = temp_uv_data[i].select = l.vertex_index in offsets
|
# Blender 5.0+: UV selection is now stored in face-corner attributes
|
||||||
|
# Skipping UV selection assignment as it's not critical for morph preview
|
||||||
|
select = l.vertex_index in offsets
|
||||||
if select:
|
if select:
|
||||||
temp_uv_data[i].uv = base_uv_data[i].uv + offsets[l.vertex_index]
|
temp_uv_data[i].uv = base_uv_data[i].uv + offsets[l.vertex_index]
|
||||||
|
|
||||||
uv_textures.active = uv_tex
|
uv_textures.active = uv_tex
|
||||||
uv_tex.active_render = True
|
|
||||||
meshObj.hide_set(False)
|
meshObj.hide_set(False)
|
||||||
meshObj.select_set(selected)
|
meshObj.select_set(selected)
|
||||||
logger.info(f"Viewing UV morph: {morph.name}")
|
logger.info(f"Viewing UV morph: {morph.name}")
|
||||||
@@ -667,13 +668,13 @@ class ClearUVMorphView(bpy.types.Operator):
|
|||||||
assert root is not None
|
assert root is not None
|
||||||
for m in FnModel.iterate_mesh_objects(root):
|
for m in FnModel.iterate_mesh_objects(root):
|
||||||
mesh = m.data
|
mesh = m.data
|
||||||
uv_textures = getattr(mesh, "uv_textures", mesh.uv_layers)
|
uv_layers = mesh.uv_layers
|
||||||
for t in uv_textures:
|
for t in list(uv_layers): # Create a copy to iterate safely
|
||||||
if t.name.startswith("__uv."):
|
if t.name.startswith("__uv."):
|
||||||
uv_textures.remove(t)
|
uv_layers.remove(t)
|
||||||
if len(uv_textures) > 0:
|
if len(uv_layers) > 0:
|
||||||
uv_textures[0].active_render = True
|
# Only set active_index
|
||||||
uv_textures.active_index = 0
|
uv_layers.active_index = 0
|
||||||
|
|
||||||
animation_data = mesh.animation_data
|
animation_data = mesh.animation_data
|
||||||
if animation_data:
|
if animation_data:
|
||||||
|
|||||||
@@ -8,6 +8,26 @@ from ...core.translations import t
|
|||||||
from ...core.logging_setup import logger
|
from ...core.logging_setup import logger
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
|
def get_uv_vertex_selection(mesh: Mesh) -> List[bool]:
|
||||||
|
"""
|
||||||
|
Get UV vertex selection state for Blender 5.0.
|
||||||
|
UV selection is stored in mesh attributes (.uv_select_vert).
|
||||||
|
"""
|
||||||
|
uv_select_attr = mesh.attributes['.uv_select_vert']
|
||||||
|
selection = [False] * len(mesh.loops)
|
||||||
|
uv_select_attr.data.foreach_get('value', selection)
|
||||||
|
return selection
|
||||||
|
|
||||||
|
|
||||||
|
def set_uv_vertex_selection(mesh: Mesh, loop_index: int, value: bool) -> None:
|
||||||
|
"""
|
||||||
|
Set UV vertex selection state for Blender 5.0.
|
||||||
|
UV selection is stored in mesh attributes (.uv_select_vert).
|
||||||
|
"""
|
||||||
|
uv_select_attr = mesh.attributes['.uv_select_vert']
|
||||||
|
uv_select_attr.data[loop_index].value = value
|
||||||
|
|
||||||
class GenerateLoopTreeResult(TypedDict):
|
class GenerateLoopTreeResult(TypedDict):
|
||||||
tree: Dict[str, Set[str]]
|
tree: Dict[str, Set[str]]
|
||||||
selected_loops: Dict[str, List[int]]
|
selected_loops: Dict[str, List[int]]
|
||||||
@@ -78,8 +98,9 @@ class AvatarToolkit_OT_AlignUVEdgesToTarget(Operator):
|
|||||||
# that two vertices share the same face loop, and therefore are connected.
|
# that two vertices share the same face loop, and therefore are connected.
|
||||||
|
|
||||||
#hmmm real stupid grimlin hours with this one. Using a string as the index of a dictionary of loop corners that end up on the same coordinate
|
#hmmm real stupid grimlin hours with this one. Using a string as the index of a dictionary of loop corners that end up on the same coordinate
|
||||||
for k,i in enumerate(uv_lay.vertex_selection):
|
uv_selection = get_uv_vertex_selection(me)
|
||||||
if (i.value == True) and (bm.verts[me.loops[k].vertex_index].select == True) and (bm.verts[me.loops[k].vertex_index].hide == False):
|
for k, is_selected in enumerate(uv_selection):
|
||||||
|
if (is_selected == True) and (bm.verts[me.loops[k].vertex_index].select == True) and (bm.verts[me.loops[k].vertex_index].hide == False):
|
||||||
key = np.array(uv_lay.uv[k].vector[:])
|
key = np.array(uv_lay.uv[k].vector[:])
|
||||||
key = key.round(decimals=5)
|
key = key.round(decimals=5)
|
||||||
|
|
||||||
@@ -140,7 +161,7 @@ class AvatarToolkit_OT_AlignUVEdgesToTarget(Operator):
|
|||||||
uv_lay = me.uv_layers.active
|
uv_lay = me.uv_layers.active
|
||||||
for uvcoordstr in vert_target_loops:
|
for uvcoordstr in vert_target_loops:
|
||||||
for loop in vert_target_loops[uvcoordstr]:
|
for loop in vert_target_loops[uvcoordstr]:
|
||||||
uv_lay.vertex_selection[loop].value = True
|
set_uv_vertex_selection(me, loop, True)
|
||||||
|
|
||||||
bm.free()
|
bm.free()
|
||||||
me.validate()
|
me.validate()
|
||||||
|
|||||||
Reference in New Issue
Block a user