diff --git a/core/mmd/core/pmx/importer.py b/core/mmd/core/pmx/importer.py index 5e94b89..dd601ea 100644 --- a/core/mmd/core/pmx/importer.py +++ b/core/mmd/core/pmx/importer.py @@ -746,22 +746,18 @@ class PMXImporter: mesh.polygons.foreach_set("use_smooth", (True,) * len(pmxModel.faces)) mesh.polygons.foreach_set("material_index", material_indices) - uv_textures, uv_layers = getattr(mesh, "uv_textures", mesh.uv_layers), mesh.uv_layers - uv_tex = uv_textures.new() - uv_layer = uv_layers[uv_tex.name] + uv_layers = mesh.uv_layers + uv_layer = uv_layers.new() 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])) - 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: logger.info(f"Importing {pmxModel.header.additional_uvs} additional UVs") zw_data_map = collections.OrderedDict() split_uvzw = lambda uvi: (self.flipUV_V(uvi[:2]), uvi[2:]) 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)") 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])) @@ -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()} for name, zw_table in zw_data_map.items(): 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: logger.warning("\t* Lost zw channels") 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])) self.__fixOverlappingFaceMaterials(mesh.materials, mesh.vertices, loop_indices, material_indices) diff --git a/core/mmd/operators/morph.py b/core/mmd/operators/morph.py index c58b317..13cd4cf 100644 --- a/core/mmd/operators/morph.py +++ b/core/mmd/operators/morph.py @@ -643,12 +643,13 @@ class ViewUVMorph(bpy.types.Operator): base_uv_data = mesh.uv_layers.active.data temp_uv_data = mesh.uv_layers[uv_tex.name].data 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: temp_uv_data[i].uv = base_uv_data[i].uv + offsets[l.vertex_index] uv_textures.active = uv_tex - uv_tex.active_render = True meshObj.hide_set(False) meshObj.select_set(selected) logger.info(f"Viewing UV morph: {morph.name}") @@ -667,13 +668,13 @@ class ClearUVMorphView(bpy.types.Operator): assert root is not None for m in FnModel.iterate_mesh_objects(root): mesh = m.data - uv_textures = getattr(mesh, "uv_textures", mesh.uv_layers) - for t in uv_textures: + uv_layers = mesh.uv_layers + for t in list(uv_layers): # Create a copy to iterate safely if t.name.startswith("__uv."): - uv_textures.remove(t) - if len(uv_textures) > 0: - uv_textures[0].active_render = True - uv_textures.active_index = 0 + uv_layers.remove(t) + if len(uv_layers) > 0: + # Only set active_index + uv_layers.active_index = 0 animation_data = mesh.animation_data if animation_data: diff --git a/functions/tools/uv_tools.py b/functions/tools/uv_tools.py index 2f5f3e5..872c83f 100644 --- a/functions/tools/uv_tools.py +++ b/functions/tools/uv_tools.py @@ -8,6 +8,26 @@ from ...core.translations import t from ...core.logging_setup import logger 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): tree: Dict[str, Set[str]] 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. #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): - if (i.value == True) and (bm.verts[me.loops[k].vertex_index].select == True) and (bm.verts[me.loops[k].vertex_index].hide == False): + uv_selection = get_uv_vertex_selection(me) + 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 = key.round(decimals=5) @@ -140,7 +161,7 @@ class AvatarToolkit_OT_AlignUVEdgesToTarget(Operator): uv_lay = me.uv_layers.active for uvcoordstr in vert_target_loops: for loop in vert_target_loops[uvcoordstr]: - uv_lay.vertex_selection[loop].value = True + set_uv_vertex_selection(me, loop, True) bm.free() me.validate()