本日は筆者のツールMixedRealityModelingTools枠です。
現状筆者が開発しているMRMTではBlenderからメッシュを送信することができますが、単一のマテリアルにのみ対応しています。
〇マルチマテリアルのメッシュ
マテリアルはメッシュに紐づいていますが、通常一つのメッシュに対して一つのマテリアルのみが対応します。
しかし1つのオブジェクトで複数のマテリアルは使用できます。
この場合1つのつながったメッシュではなくデータ上ではマテリアルごとのメッシュが重なり合って1つのメッシュを構成することになります。
イメージとしては成形色ごとに分けられたプラモデルを考えるとわかりやすいです。
成形色=マテリアルごとに分割されたパーツを組み合わせて一つのオブジェクトになるという意味で分かりやすいと思います。
つまり、マルチマテリアルのメッシュをUnityに送信するためにはマテリアル数と同じ長さの配列を用意し、メッシュデータを対応するように格納すればよいということになります。
今回はMRMTでマルチマテリアルのオブジェクトに対応できるようにこの部分のBlender側の組み込みを行います。
〇メッシュデータの取得の仕組み
現行版MRMTのBlender側アドオンではget_mesh_dataというメソッドでメッシュデータを取得しています。
def get_mesh_data(): obj = bpy.context.view_layer.objects.active#現在選択しているオブジェクトを取得 print(f"Active object name: {obj.name}") # Add Triangulate modifier triangulate_mod = obj.modifiers.new(name="Triangulate", type='TRIANGULATE') #三面化モディファイアを適応 triangulate_mod.keep_custom_normals = True triangulate_mod.quad_method = 'BEAUTY' triangulate_mod.ngon_method = 'BEAUTY' # Apply modifiers and get the new mesh data bpy.context.view_layer.update() depsgraph = bpy.context.evaluated_depsgraph_get() obj_eval = obj.evaluated_get(depsgraph) temp_mesh = bpy.data.meshes.new_from_object(obj_eval) #bpy.ops.mesh.customdata_custom_splitnormals_clear() # Get vertices and triangles vertices = [[v.co.x, v.co.y, v.co.z] for v in temp_mesh.vertices] triangles = [] for p in temp_mesh.polygons: triangles.extend(p.vertices) uvs = [] for p in temp_mesh.polygons: for loop_index in p.loop_indices: uv = temp_mesh.uv_layers.active.data[loop_index].uv uvs.extend([uv.x, uv.y]) # Remove Triangulate modifier obj.modifiers.remove(triangulate_mod) # Get normals normals = [[v.normal.x, v.normal.y, v.normal.z] for v in temp_mesh.vertices] # Don't forget to remove the temporary mesh data bpy.data.meshes.remove(temp_mesh) print(f"Mesh data generated: vertices={len(vertices)}, triangles={len(triangles)}, normals={len(normals)}, uvs={len(uvs)}") return (vertices, triangles, normals ,uvs)
詳しい処理の解説は過去の記事を参照ください。
簡単に説明すると3角ポリゴンとして扱うために三角化モディファイアを一度設定後頂点、インデックス、法線、UVを取得していました。
この処理をマテリアルごとに行うために現在のマテリアルスロットを取得します。
material_slots = obj.material_slots for material_slot in material_slots: material_index = material_slots.find(material_slot.name) # Set active material index obj.active_material_index = material_index
マテリアルスロットはobj.material_slots
で取得することができます。
例えば4つのマテリアルをオブジェクトが持っていた場合は4が返されます。
for文を使用してマテリアルスロットの文処理を回すことができます。
次にデータが配列になるので各データを定義します。
# Get vertices and triangles all_vertices = [] all_triangles = [] all_normals = [] all_uvs = []
これでマテリアルごとに処理を行い格納する準備が出来たのでそれぞれのデータを取得します。
material_slots = obj.material_slots # Get vertices and triangles all_vertices = [] all_triangles = [] all_normals = [] all_uvs = [] for material_slot in material_slots: material_index = material_slots.find(material_slot.name) # Set active material index obj.active_material_index = material_index # Get vertices, triangles, and uvs for the current material vertices = [[v.co.x, v.co.y, v.co.z] for v in temp_mesh.vertices] triangles = [] uvs = [] for p in temp_mesh.polygons: if p.material_index == material_index: triangles.extend(p.vertices) for p in temp_mesh.polygons: if p.material_index == material_index: for loop_index in p.loop_indices: uv = temp_mesh.uv_layers.active.data[loop_index].uv uvs.extend([uv.x, uv.y]) normals = [[v.normal.x, v.normal.y, v.normal.z] for v in temp_mesh.vertices] # Append data to the lists all_vertices.append(vertices) all_triangles.append(triangles) all_uvs.append(uvs) all_normals.append(normals)
これにより、マテリアルスロットの配列としてデータを取得できました。
本日は以上です。
〇コード
def get_mesh_data(): #Get activeObject obj = bpy.context.view_layer.objects.active print(f"Active object name: {obj.name}") # Add Triangulate modifier triangulate_mod = obj.modifiers.new(name="Triangulate", type='TRIANGULATE') triangulate_mod.keep_custom_normals = True triangulate_mod.quad_method = 'BEAUTY' triangulate_mod.ngon_method = 'BEAUTY' # Apply modifiers and get the new mesh data bpy.context.view_layer.update() depsgraph = bpy.context.evaluated_depsgraph_get() obj_eval = obj.evaluated_get(depsgraph) temp_mesh = bpy.data.meshes.new_from_object(obj_eval) #bpy.ops.mesh.customdata_custom_splitnormals_clear() material_slots = obj.material_slots # Get vertices and triangles all_vertices = [] all_triangles = [] all_normals = [] all_uvs = [] for material_slot in material_slots: material_index = material_slots.find(material_slot.name) # Set active material index obj.active_material_index = material_index # Get vertices, triangles, and uvs for the current material vertices = [[v.co.x, v.co.y, v.co.z] for v in temp_mesh.vertices] triangles = [] uvs = [] for p in temp_mesh.polygons: if p.material_index == material_index: triangles.extend(p.vertices) for p in temp_mesh.polygons: if p.material_index == material_index: for loop_index in p.loop_indices: uv = temp_mesh.uv_layers.active.data[loop_index].uv uvs.extend([uv.x, uv.y]) normals = [[v.normal.x, v.normal.y, v.normal.z] for v in temp_mesh.vertices] # Append data to the lists all_vertices.append(vertices) all_triangles.append(triangles) all_uvs.append(uvs) all_normals.append(normals) # Remove Triangulate modifier obj.modifiers.remove(triangulate_mod) # Don't forget to remove the temporary mesh data bpy.data.meshes.remove(temp_mesh) print(f"Mesh data generated for {len(material_slots)} materials") return all_vertices, all_triangles, all_normals, all_uvs