From 2a2128e0246f3c2a1bcdb51c2126e00a61b3bf56 Mon Sep 17 00:00:00 2001 From: Lila Date: Sat, 26 Oct 2024 15:14:13 +0100 Subject: [PATCH] move the thing into the thing and back --- .../f3d/bsdf_converter/converter.py | 119 +++++++++--------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/fast64_internal/f3d/bsdf_converter/converter.py b/fast64_internal/f3d/bsdf_converter/converter.py index ff6c45b6..960d89b3 100644 --- a/fast64_internal/f3d/bsdf_converter/converter.py +++ b/fast64_internal/f3d/bsdf_converter/converter.py @@ -132,7 +132,6 @@ def get_color_from_input(inp: str, previous_color: Color, f3d_mat: F3DMaterialPr def fake_color_from_cycle(cycle: list[str], previous_color: Color, f3d_mat: F3DMaterialProperty, is_alpha=False): - default_colors = [Color(), Color(), Color(), Color(1.0, 1.0, 1.0, 1.0)] a, b, c, d = [get_color_from_input(inp, previous_color, f3d_mat, is_alpha) for inp in cycle] sign_extended_c = c.wrap(-1.0, 1.0001) unwrapped_result = (a - b) * sign_extended_c + d @@ -540,8 +539,10 @@ def find_linked_nodes( return list(dict.fromkeys(nodes).keys()) -def bsdf_mat_to_abstracted(material: Material, output_node: ShaderNode | None): +def bsdf_mat_to_abstracted(material: Material): abstracted_mat = AbstractedN64Material(color=None) + + output_node = find_output_node(material) if output_node is None: abstracted_mat.color = material.diffuse_color return abstracted_mat @@ -661,55 +662,13 @@ def get_vtx_layer(nodes): def material_to_f3d( obj: Object, material: Material, - colors: np.ndarray, - uvs: np.ndarray | None = None, lights_for_colors=False, fog=False, set_render_mode=False, ): print(f"Converting BSDF material {material.name}") - output_node = find_output_node(material) - - abstracted_mat = bsdf_mat_to_abstracted(material, output_node) - - materials = obj.data.materials - loop_indexes = [ - loop_idx - for poly in obj.data.polygons - for loop_idx in poly.loop_indices - if poly.material_index < len(materials) and materials[poly.material_index] == material - ] - - if uvs is not None: - uv_map_layer = obj.data.uv_layers.get(abstracted_mat.uv_map or "", obj.data.uv_layers.active) - print(f"Updating main UV map with {uv_map_layer.name} UVs from {material.name}.") - if uv_map_layer is not None: - for loop_index in loop_indexes: - uvs[loop_index] = uv_map_layer.data[loop_index].uv - - def get_layer_and_convert(layer_name: str | None): - layer = obj.data.color_attributes.get(layer_name or "", obj.data.color_attributes.active) - if layer is None: - return None - layer_name = layer.name - convertColorAttribute(obj.data, layer_name) - return obj.data.color_attributes[layer_name] # HACK: layer cannot be trusted - - # vertex colors are much more complex because of vertex alpha, we always handle them - col_layer = get_layer_and_convert(abstracted_mat.vertex_color) - if col_layer is not None: - for loop_idx in loop_indexes: - colors[loop_idx, :3] = col_layer.data[loop_idx].color[:3] - - alpha_layer = get_layer_and_convert(abstracted_mat.vertex_alpha) - if alpha_layer is not None: - if abstracted_mat.alpha_is_median: - for loop_idx in loop_indexes: - colors[loop_idx, 3] = colorToLuminance(alpha_layer.data[loop_idx].color) - else: - for loop_idx in loop_indexes: - colors[loop_idx, 3] = alpha_layer.data[loop_idx].color[3] + abstracted_mat = bsdf_mat_to_abstracted(material) # TODO: we need to implement a system that can handle different material setups preset = getDefaultMaterialPreset("Shaded Solid") @@ -743,22 +702,67 @@ def get_layer_and_convert(layer_name: str | None): with bpy.context.temp_override(material=new_material): update_all_node_values(new_material, bpy.context) # Reload everything - return new_material + return new_material, abstracted_mat -def obj_to_f3d(obj: Object, materials: dict[Material, Material]): +def obj_to_f3d(obj: Object, converted_materials: dict[Material, tuple[Material, AbstractedN64Material]]): assert obj.type == "MESH" + if len(obj.data.materials) == 0: + preset = getDefaultMaterialPreset("Shaded Solid") + createF3DMat(obj, preset=preset) + return print(f"Converting BSDF materials in {obj.name}") uvs = np.empty((len(obj.data.loops), 2), dtype=np.float32) if len(obj.data.uv_layers) != 1 else None - colors = np.ones((len(obj.data.loops), 4), dtype=np.float32) # vertex colors + colors = np.ones((len(obj.data.loops), 4), dtype=np.float32) if len(obj.data.color_attributes) != 0 else None + + loop_indexes: dict[Material, list] = {} + for poly in obj.data.polygons: + material = obj.data.materials[poly.material_index] if poly.material_index < len(obj.data.materials) else None + if material is None: + continue + if material not in loop_indexes: + loop_indexes[material] = [] + for loop_idx in poly.loop_indices: + loop_indexes[material].append(loop_idx) + + def get_layer_and_convert(layer_name: str | None): + layer = obj.data.color_attributes.get(layer_name or "", obj.data.color_attributes.active) + if layer is None: + return None + layer_name = layer.name + convertColorAttribute(obj.data, layer_name) + return obj.data.color_attributes[layer_name] # HACK: layer cannot be trusted + for index, material_slot in enumerate(obj.material_slots): material = material_slot.material if material is None or is_mat_f3d(material): continue - if material in materials: - obj.material_slots[index].material = materials[material] - else: - obj.material_slots[index].material = material_to_f3d(obj, material, colors, uvs, colors) + if material not in converted_materials: + converted_materials[material] = material_to_f3d(obj, material, colors) + new_material, abstracted_mat = converted_materials[material] + obj.material_slots[index].material = new_material + + if uvs is not None: # apply the used uv or fallback on active + uv_map_layer = obj.data.uv_layers.get(abstracted_mat.uv_map or "", obj.data.uv_layers.active) + print(f"Updating main UV map with {uv_map_layer.name} UVs from {material.name}.") + if uv_map_layer is not None: + for loop_index in loop_indexes[material]: + uvs[loop_index] = uv_map_layer.data[loop_index].uv + + # apply the used color/alpha or fallback on active + col_layer = get_layer_and_convert(abstracted_mat.vertex_color) + if col_layer is not None: + for loop_idx in loop_indexes[material]: + colors[loop_idx, :3] = col_layer.data[loop_idx].color[:3] + alpha_layer = get_layer_and_convert(abstracted_mat.vertex_alpha) + if alpha_layer is not None: + if abstracted_mat.alpha_is_median: + for loop_idx in loop_indexes[material]: + colors[loop_idx, 3] = colorToLuminance(alpha_layer.data[loop_idx].color) + else: + for loop_idx in loop_indexes[material]: + colors[loop_idx, 3] = alpha_layer.data[loop_idx].color[3] + if uvs is not None: # If there wasn´t exactly one UV map, we need to create one singular UV map while len(obj.data.uv_layers.values()) > 0: obj.data.uv_layers.remove(obj.data.uv_layers.values()[0]) @@ -768,15 +772,16 @@ def obj_to_f3d(obj: Object, materials: dict[Material, Material]): while len(obj.data.color_attributes) > 0: # remove all existing colors obj.data.color_attributes.remove(obj.data.color_attributes[0]) - alpha_layer = obj.data.color_attributes.new("Alpha", "FLOAT_COLOR", "CORNER") # get the alpha as rgb, then flatten it + alpha_layer = obj.data.color_attributes.new("Alpha", "FLOAT_COLOR", "CORNER") alpha_layer.data.foreach_set("color", np.repeat(colors[:, 3][:, np.newaxis], 4, axis=1).flatten()) + # set the alpha to 1 for the color layer color_layer = obj.data.color_attributes.new("Col", "FLOAT_COLOR", "CORNER") - colors[:, 3] = 1 # set the alpha to 1 for the color layer + colors[:, 3] = 1 color_layer.data.foreach_set("color", colors.flatten()) -def obj_to_bsdf(obj: Object, materials: dict[Material, Material], put_alpha_into_color: bool): +def obj_to_bsdf(obj: Object, converted_materials: dict[Material, Material], put_alpha_into_color: bool): assert obj.type == "MESH" print(f"Converting F3D materials in {obj.name}") if put_alpha_into_color: @@ -785,7 +790,7 @@ def obj_to_bsdf(obj: Object, materials: dict[Material, Material], put_alpha_into material = material_slot.material if material is None or not is_mat_f3d(material): continue - if material in materials: - obj.material_slots[index].material = materials[material] + if material in converted_materials: + obj.material_slots[index].material = converted_materials[material] else: obj.material_slots[index].material = material_to_bsdf(material, put_alpha_into_color)