夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

マテリアルごとにメッシュをUnityに送信する

本日は筆者のツールMixedRealityModelingTools枠です。

現状筆者が開発しているMRMTではBlenderからメッシュを送信することができますが、単一のマテリアルにのみ対応しています。

〇マルチマテリアルのメッシュ

マテリアルはメッシュに紐づいていますが、通常一つのメッシュに対して一つのマテリアルのみが対応します。

しかし1つのオブジェクトで複数のマテリアルは使用できます。

この場合1つのつながったメッシュではなくデータ上ではマテリアルごとのメッシュが重なり合って1つのメッシュを構成することになります。

イメージとしては成形色ごとに分けられたプラモデルを考えるとわかりやすいです。

https://bandai-hobby.net/site/gunpla-kun/ より引用

成形色=マテリアルごとに分割されたパーツを組み合わせて一つのオブジェクトになるという意味で分かりやすいと思います。

つまり、マルチマテリアルのメッシュをUnityに送信するためにはマテリアル数と同じ長さの配列を用意し、メッシュデータを対応するように格納すればよいということになります。

今回はMRMTでマルチマテリアルのオブジェクトに対応できるようにこの部分のBlender側の組み込みを行います。

〇メッシュデータの取得の仕組み

現行版MRMTのBlender側アドオンではget_mesh_dataというメソッドでメッシュデータを取得しています。

github.com

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)

詳しい処理の解説は過去の記事を参照ください。

redhologerbera.hatenablog.com

簡単に説明すると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