def _create_piece( context, name, mesh_vertices, mesh_normals, mesh_tangents, mesh_rgb, mesh_rgba, mesh_scalars, object_skinning, mesh_uv, mesh_uv_aliases, mesh_tuv, mesh_faces, mesh_face_materials, mesh_edges, terrain_points_trans, materials_data, ): handle_unused_arg(__file__, _create_piece.__name__, "mesh_normals", mesh_normals) handle_unused_arg(__file__, _create_piece.__name__, "mesh_tangents", mesh_tangents) handle_unused_arg(__file__, _create_piece.__name__, "mesh_tuv", mesh_tuv) context.window_manager.progress_begin(0.0, 1.0) context.window_manager.progress_update(0) import_scale = _get_scs_globals().import_scale mesh = bpy.data.meshes.new(name) # COORDINATES TRANSFORMATION transformed_mesh_vertices = [ _convert_utils.change_to_scs_xyz_coordinates(vec, import_scale) for vec in mesh_vertices ] context.window_manager.progress_update(0.1) # VISUALISE IMPORTED NORMALS (DEBUG) # NOTE: NOT functional for PIM version 7 since mesh normals are not provided in per vertex fashion! # visualise_normals(name, transformed_mesh_vertices, mesh_normals, import_scale) bm = bmesh.new() # VERTICES _mesh_utils.bm_make_vertices(bm, transformed_mesh_vertices) context.window_manager.progress_update(0.2) # FACES # for fac_i, fac in enumerate(mesh_faces): print(' face[%i]: %s' % (fac_i, str(fac))) mesh_faces, back_faces = _mesh_utils.bm_make_faces(bm, mesh_faces, []) context.window_manager.progress_update(0.3) # SHARP EDGES # print('mesh_edges: %s' % str(mesh_edges)) for edge in bm.edges: edge_verts = [edge.verts[0].index, edge.verts[1].index] edge_verts_inv = [edge.verts[1].index, edge.verts[0].index] if edge_verts in mesh_edges or edge_verts_inv in mesh_edges: # print('edge: %s' % str(edge_verts)) edge.smooth = False context.window_manager.progress_update(0.4) # UV LAYERS if mesh_uv: for uv_layer_name in mesh_uv: _mesh_utils.bm_make_uv_layer(7, bm, mesh_faces, uv_layer_name, mesh_uv[uv_layer_name]) context.window_manager.progress_update(0.5) # VERTEX COLOR mesh_rgb_final = {} if mesh_rgba: mesh_rgb_final.update(mesh_rgba) if mesh_rgb: mesh_rgb_final.update(mesh_rgb) for vc_layer_name in mesh_rgb_final: max_value = mesh_rgb_final[vc_layer_name][0][0][0] / 2 for vc_entry in mesh_rgb_final[vc_layer_name]: for v_i in vc_entry: for i, value in enumerate(v_i): if max_value < value / 2: max_value = value / 2 if max_value > mesh.scs_props.vertex_color_multiplier: mesh.scs_props.vertex_color_multiplier = max_value _mesh_utils.bm_make_vc_layer(7, bm, vc_layer_name, mesh_rgb_final[vc_layer_name], mesh.scs_props.vertex_color_multiplier) bm.to_mesh(mesh) mesh.update() bm.free() # NORMALS - has to be applied after bmesh creation as they are set directly to mesh if _get_scs_globals().import_use_normals: mesh.create_normals_split() # first set normals directly to loops for poly_i, poly in enumerate(mesh.polygons): for poly_loop_i, loop_i in enumerate(poly.loop_indices): curr_n = _convert_utils.scs_to_blend_matrix() @ Vector( mesh_normals[poly_i][poly_loop_i]) mesh.loops[loop_i].normal[:] = curr_n # then we have to go trough very important step they say, # as without validation we get wrong result for some normals mesh.validate(clean_customdata=False ) # *Very* important to not remove lnors here! # set polygons to use smooth representation mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) # finally fill clnors from loops normals and apply them (taken from official Blenders scripts) clnors = array.array('f', [0.0] * (len(mesh.loops) * 3)) mesh.loops.foreach_get("normal", clnors) mesh.normals_split_custom_set(tuple(zip(*(iter(clnors), ) * 3))) mesh.use_auto_smooth = True mesh.free_normals_split() else: # set polygons to use smooth representation only mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) context.window_manager.progress_update(0.6) # Create object out of mesh and link it to active layer collection. obj = bpy.data.objects.new(mesh.name, mesh) obj.scs_props.object_identity = obj.name obj.location = (0.0, 0.0, 0.0) context.view_layer.active_layer_collection.collection.objects.link(obj) obj.select_set(True) bpy.context.view_layer.objects.active = obj # SCALAR LAYERS if mesh_scalars: for sca_layer_name in mesh_scalars: vertex_group = obj.vertex_groups.new(name=sca_layer_name) for val_i, val in enumerate(mesh_scalars[sca_layer_name]): val = float(val[0]) if val != 0.0: vertex_group.add([val_i], val, "ADD") context.window_manager.progress_update(0.7) # TERRAIN POINTS (VERTEX GROUPS) for vertex_i, vertex_pos in enumerate(mesh_vertices): tp_entries = terrain_points_trans.get(vertex_pos) # add current vertex to all combinations of variants/nodes # from found terrain points transitional structures for tp_entry in tp_entries: # first 6 chars in vertex group name will represent variant index # this way we will be able to identify variant during vertex groups # cleanup if this vertex will be set to multiple variants vg_name = str(tp_entry.variant_i).zfill( 6) + _OP_consts.TerrainPoints.vg_name_prefix + str( tp_entry.node_i) if vg_name not in obj.vertex_groups: obj.vertex_groups.new(name=vg_name) vertex_group = obj.vertex_groups[vg_name] vertex_group.add([vertex_i], 1.0, "REPLACE") # SKINNING (VERTEX GROUPS) if object_skinning: if name in object_skinning: for vertex_group_name in object_skinning[name]: vertex_group = obj.vertex_groups.new(name=vertex_group_name) for vertex_i, vertex in enumerate( object_skinning[name][vertex_group_name]): weight = object_skinning[name][vertex_group_name][vertex] if weight != 0.0: vertex_group.add([vertex], weight, "ADD") else: lprint('\nE Missing skin group %r! Skipping...', name) # ADD EDGE SPLIT MODIFIER bpy.ops.object.shade_smooth() bpy.ops.object.modifier_add(type='EDGE_SPLIT') bpy.context.object.modifiers["EdgeSplit"].use_edge_angle = False bpy.context.object.modifiers["EdgeSplit"].name = "ES_" + name # MATERIALS used_mat_indices = set() # print('\n mesh_face_materials:\n%s' % str(mesh_face_materials)) for mat_index in mesh_face_materials: used_mat_indices.add(mat_index) # print(' used_mats:\n%s' % str(used_mats)) context.window_manager.progress_update(0.8) # ADD MATERIALS TO SLOTS # print(' materials_data:\n%s' % str(materials_data)) mat_index_to_mat_slot_map = {} if len(materials_data) > 0: for used_mat_idx in used_mat_indices: material_name = materials_data[used_mat_idx][0] bpy.ops.object.material_slot_add() # Add a material slot last_slot = obj.material_slots.__len__() - 1 # now as we created slot and we know index of it, write down indices of material slots to dictionary # for later usage by assigning faces to proper slots mat_index_to_mat_slot_map[used_mat_idx] = last_slot # print(' used_mat: %s (%i) => %s : %s' % (str(used_mat), mat_i, str(last_slot), str(material))) obj.material_slots[last_slot].material = bpy.data.materials[ material_name] # Assign a material to the slot # NOTE: we are setting texture aliases only first time to avoid duplicates etc. # So we assume that pieces which are using same material will also have same uv aliases alignment used_material = bpy.data.materials[material_name] if "scs_tex_aliases" not in used_material: alias_mapping = {} for uv_lay in mesh_uv_aliases[used_mat_idx]: import re for alias in mesh_uv_aliases[used_mat_idx][uv_lay]: numbers = re.findall("\d+", alias) number = numbers[len(numbers) - 1] alias_mapping[number] = uv_lay used_material["scs_tex_aliases"] = alias_mapping mesh = obj.data context.window_manager.progress_update(0.9) # APPLY MATERIAL SLOT INDICES TO FACES for face_i, face in enumerate(mesh.polygons): face.material_index = mat_index_to_mat_slot_map[ mesh_face_materials[face_i]] context.window_manager.progress_update(1.0) return obj
def _create_piece(context, preview_model, name, ob_material, mesh_vertices, mesh_normals, mesh_tangents, mesh_rgb, mesh_rgba, mesh_scalars, object_skinning, mesh_uv, mesh_tuv, mesh_triangles, materials_data, points_to_weld_list, terrain_points_trans, ignore_backfaces=False): handle_unused_arg(__file__, _create_piece.__name__, "mesh_tangents", mesh_tangents) handle_unused_arg(__file__, _create_piece.__name__, "mesh_scalars", mesh_scalars) handle_unused_arg(__file__, _create_piece.__name__, "mesh_tuv", mesh_tuv) context.window_manager.progress_begin(0.0, 1.0) context.window_manager.progress_update(0) import_scale = _get_scs_globals().import_scale mesh = bpy.data.meshes.new(name) # COORDINATES TRANSFORMATION transformed_mesh_vertices = [ _convert_utils.change_to_scs_xyz_coordinates(vec, import_scale) for vec in mesh_vertices ] context.window_manager.progress_update(0.1) # VISUALISE IMPORTED NORMALS (DEBUG) # visualise_normals(name, transformed_mesh_vertices, mesh_normals, import_scale) # MESH CREATION bm = bmesh.new() # VERTICES _mesh_utils.bm_make_vertices(bm, transformed_mesh_vertices) context.window_manager.progress_update(0.2) # FACES mesh_triangles, back_triangles = _mesh_utils.bm_make_faces( bm, mesh_triangles, points_to_weld_list) context.window_manager.progress_update(0.3) # UV LAYERS if mesh_uv: for uv_layer_name in mesh_uv: _mesh_utils.bm_make_uv_layer(5, bm, mesh_triangles, uv_layer_name, mesh_uv[uv_layer_name]["data"]) context.window_manager.progress_update(0.4) # VERTEX COLOR if mesh_rgba: mesh_rgb_final = mesh_rgba elif mesh_rgb: mesh_rgb_final = mesh_rgb else: mesh_rgb_final = [] for vc_layer_name in mesh_rgb_final: max_value = mesh_rgb_final[vc_layer_name][0][0] / 2 for vc_entry in mesh_rgb_final[vc_layer_name]: for i, value in enumerate(vc_entry): if max_value < value / 2: max_value = value / 2 if max_value > mesh.scs_props.vertex_color_multiplier: mesh.scs_props.vertex_color_multiplier = max_value _mesh_utils.bm_make_vc_layer(5, bm, vc_layer_name, mesh_rgb_final[vc_layer_name], mesh.scs_props.vertex_color_multiplier) context.window_manager.progress_update(0.5) bm.to_mesh(mesh) mesh.update() bm.free() # NORMALS - has to be applied after bmesh creation as they are set directly to mesh if _get_scs_globals().import_use_normals: mesh.create_normals_split() # first set normals directly to loops for loop in mesh.loops: curr_n = _convert_utils.scs_to_blend_matrix() @ Vector( mesh_normals[loop.vertex_index]) loop.normal[:] = curr_n # then we have to go trough very important step they say, # as without validation we get wrong result for some normals mesh.validate(clean_customdata=False ) # *Very* important to not remove lnors here! # set polygons to use smooth representation mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) # finally fill clnors from loops normals and apply them (taken from official Blenders scripts) clnors = array.array('f', [0.0] * (len(mesh.loops) * 3)) mesh.loops.foreach_get("normal", clnors) mesh.normals_split_custom_set(tuple(zip(*(iter(clnors), ) * 3))) mesh.use_auto_smooth = True mesh.free_normals_split() else: # set polygons to use smooth representation only mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) context.window_manager.progress_update(0.6) # Create object out of mesh and link it to active layer collection. obj = bpy.data.objects.new(mesh.name, mesh) obj.scs_props.object_identity = obj.name obj.location = (0.0, 0.0, 0.0) context.view_layer.active_layer_collection.collection.objects.link(obj) obj.select_set(True) bpy.context.view_layer.objects.active = obj context.window_manager.progress_update(0.7) context.window_manager.progress_update(0.8) # TERRAIN POINTS (VERTEX GROUPS) for vertex_i, vertex_pos in enumerate(mesh_vertices): tp_entries = terrain_points_trans.get(vertex_pos) # add current vertex to all combinations of variants/nodes # from found terrain points transitional structures for tp_entry in tp_entries: # first 6 chars in vertex group name will represent variant index # this way we will be able to identify variant during vertex groups # cleanup if this vertex will be set to multiple variants vg_name = str(tp_entry.variant_i).zfill( 6) + _OP_consts.TerrainPoints.vg_name_prefix + str( tp_entry.node_i) if vg_name not in obj.vertex_groups: obj.vertex_groups.new(name=vg_name) vertex_group = obj.vertex_groups[vg_name] vertex_group.add([vertex_i], 1.0, "REPLACE") # SKINNING (VERTEX GROUPS) if object_skinning: if name in object_skinning: for vertex_group_name in object_skinning[name]: vertex_group = obj.vertex_groups.new(name=vertex_group_name) for vertex_i, vertex in enumerate( object_skinning[name][vertex_group_name]): weight = object_skinning[name][vertex_group_name][vertex] if weight != 0.0: if vertex in points_to_weld_list: vertex = points_to_weld_list[vertex] vertex_group.add([vertex], weight, "ADD") else: lprint('\nE Missing skin group %r! Skipping...', name) context.window_manager.progress_update(0.9) # DELETE ORPHAN VERTICES (LEFT IN THE GEOMETRY FROM SMOOTHING RECONSTRUCTION) if points_to_weld_list: bm = bmesh.new() bm.from_mesh(mesh) bm.verts.ensure_lookup_table() for vert_i in points_to_weld_list.keys(): bm.verts[vert_i].select_set(True) verts = [v for v in bm.verts if v.select] if verts: bmesh.ops.delete(bm, geom=verts, context='VERTS') # APPLYING BMESH TO MESH bm.to_mesh(mesh) bm.free() context.window_manager.progress_update(1.0) # MATERIAL if len(materials_data) > 0 and not preview_model: # Assign a material to the last slot used_material = bpy.data.materials[materials_data[ob_material][0]] obj.data.materials.append(used_material) # NOTE: we are setting texture aliases only first time to avoid duplicates etc. # So we assume that pieces which are using same material will also have same uv aliases alignement if "scs_tex_aliases" not in used_material: alias_mapping = {} for uv_lay in mesh_uv: if "aliases" in mesh_uv[uv_lay]: import re for alias in mesh_uv[uv_lay]["aliases"]: numbers = re.findall("\d+", alias) number = numbers[len(numbers) - 1] alias_mapping[number] = uv_lay used_material["scs_tex_aliases"] = alias_mapping context.window_manager.progress_end() # if back triangles are present, then create new object with # back triangles and merge it to original if len(back_triangles) > 0 and not ignore_backfaces: back_obj = _create_piece(context, preview_model, "back_" + name, ob_material, mesh_vertices, mesh_normals, mesh_tangents, mesh_rgb, mesh_rgba, mesh_scalars, object_skinning, mesh_uv, mesh_tuv, back_triangles, materials_data, points_to_weld_list, terrain_points_trans, ignore_backfaces=True) lprint( "W Found %s back face(s) without it's own vertices on object %r, additional vertices were added!", (len(back_obj.data.polygons), obj.name)) # creation of back face object used all original vertices # for proper index accessing during binding all of the data blocks to vertices. # Because of that we have to remove vertices which are not really used # in back faces mesh, so called "loose" vertices back_obj.data = _mesh_utils.bm_delete_loose(back_obj.data) # finally join back object with original override = context.copy() override["active_object"] = obj override["selected_editable_objects"] = (obj, back_obj) bpy.ops.object.join(override) return obj