MRが楽しい

MRやVRについて学習したことを書き残す

Blender2.8でモンスター型のキャラクターモデルを作成する その1(仕上げ処理を自動化する)

本日はモンスター型モデルの作成枠です。
Blender2.8 環境での仕上げ処理をスクリプトで自動化します。
f:id:bluebirdofoz:20200405231517j:plain

以前、Blender2.79 で作成したモンスター型のキャラクターモデルを Blender2.8 環境に移行しました。
bluebirdofoz.hatenablog.com

今回は1万ポリゴン未満のローポリゴンモデルと、2万ポリゴン以上のハイポリゴンモデルを作成することを目的とします。
まずはその下準備として、ローポリゴンモデルとハイポリゴンモデルの出力処理をそれぞれ自動化したスクリプトを作成しました。
・Joint_HoloMon.py

# bpyインポート(Blender操作のため)
import bpy


# メイン関数
def main():
    # ハイポリゴンモデル作成とローポリゴンモデル作成を切り替える
    isHighModel = True

    # サブディビジョンサーフェスを適用するメッシュ
    sub_meshs = get_endswithmesh_namelist(arg_endswithname="_HP")

    # ローポリゴンモデルで削除するメッシュ
    lowdel_meshs = get_endswithmesh_namelist(arg_endswithname="_LD")

    # 結合オブジェクト名
    jointobj_name = 'JointModel'

    # ハイポリゴンモデル作成の場合の処理を実行する
    if isHighModel == True:
        # 指定のオブジェクトにサブディビジョンサーフェスを追加する
        for sub_mesh in sub_meshs:
            add_modifier_subdivision(arg_objectname=sub_mesh)

    # ローポリゴンモデル作成の場合の処理を実行する
    if isHighModel == False:
        # ハイポリゴンモデルでのみ利用するメッシュを削除する
        for lowdel_mesh in lowdel_meshs:
            delete_object_target(arg_objectname=lowdel_mesh)


    # 結合する全メッシュオブジェクトのオブジェクト名リストを取得する
    joint_meshs = get_mesh_namelist()

    # 各オブジェクトにモディファイアを適用する
    for joint_mesh in joint_meshs:
        apply_modifier_target(arg_objectname=joint_mesh)

    # オブジェクトを全て結合する
    join_objects_mesh(arg_targetnamelist=joint_meshs, arg_jointname=jointobj_name)

    return


# 全メッシュオブジェクト名のリストを作成する
def get_mesh_namelist():
    """全メッシュオブジェクト名のリストを作成する
    """
    # 空リストを作成
    mesh_names=[]
    # シーン中の全てのオブジェクトを走査する
    for obj in bpy.context.view_layer.objects:
        # オブジェクトがメッシュであるか確認する
        if obj.type == 'MESH':
            # 名称をリストに追加する
            mesh_names.append(obj.name)

    return mesh_names


# 指定の後方文字列のメッシュオブジェクト名のリストを作成する
def get_endswithmesh_namelist(arg_endswithname=""):
    """全メッシュオブジェクト名のリストを作成する
    
    Keyword Arguments:
        arg_endswithname {str} -- 先頭一致文字列 (default: {""})
    """

    # 空リストを作成
    mesh_names=[]
    # シーン中の全てのオブジェクトを走査する
    for obj in bpy.context.view_layer.objects:
        # オブジェクトがメッシュであるか確認する
        if obj.type == 'MESH':
            # オブジェクト名の先頭文字列が一致するか確認する
            if obj.name.endswith(arg_endswithname):
                # 名称をリストに追加する
                mesh_names.append(obj.name)

    return mesh_names


# 「サブディビジョンサーフェス」モディファイアの追加
# モディファイア追加の種類とマニュアル
# (https://docs.blender.org/api/current/bpy.ops.object.html#bpy.ops.object.gpencil_modifier_add)
def add_modifier_subdivision(arg_objectname="Default"):
    """「サブディビジョンサーフェス」モディファイアの追加
    
    Keyword Arguments:
        arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    targetob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if targetob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return

    # 変更オブジェクトをアクティブに変更する
    bpy.context.view_layer.objects.active = targetob

    # 「サブディビジョンサーフェス」モディファイアを追加する
    bpy.ops.object.modifier_add(type='SUBSURF')
    # 作成モディファイアを取得する
    selectmod = targetob.modifiers['Subdivision']
    # 細分化レベルを「1」に設定する
    selectmod.levels = 1

    return


# 指定オブジェクトの削除
def delete_object_target(arg_objectname=""):
    """指定オブジェクトの削除
    
    Keyword Arguments:
        arg_objectname {str} -- 削除するオブジェクト名 (default: {""})
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    targetob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if targetob != None:
        # オブジェクトが存在する場合は削除を行う
        bpy.data.objects.remove(targetob)
    
    return


# 以下のモディファイアを適用する
# - ミラーモディファイア
# - シュリンクラップモディファイア
# - 細分割曲面モディファイア
# - 辺分離モディファイア
# - 厚み付けモディファイア
# - ベベルモディファイア
#     対象モディファイアを保持しない場合は無視する
def apply_modifier_target(arg_objectname=""):
    """特定のモディファイアを適用する
    
    Keyword Arguments:
        arg_objectname {str} -- 対象オブジェクト名 (default: {"Default"})
    """

    # 指定オブジェクトを取得する
    # (get関数は対象が存在しない場合 None が返る)
    targetob = bpy.data.objects.get(arg_objectname)

    # 指定オブジェクトが存在するか確認する
    if targetob == None:
        # 指定オブジェクトが存在しない場合は処理しない
        return

    # 変更オブジェクトをアクティブに変更する
    bpy.context.view_layer.objects.active = targetob

    # オブジェクトの全てモディファイアを走査する
    # モディファイアのタイプ一覧
    # (https://docs.blender.org/api/blender_python_api_2_71_release/bpy.types.Modifier.html)
    for modifier in targetob.modifiers:
        # ミラーモディファイアを適用する
        if modifier.type == 'MIRROR':
            bpy.ops.object.modifier_apply(apply_as='DATA',modifier=modifier.name)
        # 細分割曲面モディファイアを適用する
        if modifier.type == 'SUBSURF':
            bpy.ops.object.modifier_apply(apply_as='DATA',modifier=modifier.name)
        # シュリンクラップモディファイアを適用する
        if modifier.type == 'SHRINKWRAP':
            bpy.ops.object.modifier_apply(apply_as='DATA',modifier=modifier.name)
        # 辺分離モディファイアを適用する
        if modifier.type == 'EDGE_SPLIT':
            bpy.ops.object.modifier_apply(apply_as='DATA',modifier=modifier.name)
        # 厚み付けモディファイアを適用する
        if modifier.type == 'SOLIDIFY':
            bpy.ops.object.modifier_apply(apply_as='DATA',modifier=modifier.name)
        # ベベルモディファイアを適用する
        if modifier.type == 'BEVEL':
            bpy.ops.object.modifier_apply(apply_as='DATA',modifier=modifier.name)
    return


# メッシュオブジェクトの結合
def join_objects_mesh(arg_targetnamelist=[], arg_jointname=''):
    """メッシュオブジェクトを結合する
    
    Keyword Arguments:
        arg_objectname {list} -- 対象オブジェクト名リスト (default: {[]})
        arg_objectname {str} -- 結合オブジェクト名 (default: {""})
    """

    # 対象の全てのオブジェクトを走査する
    for targetname in arg_targetnamelist:
        # 指定オブジェクトを取得する
        # (get関数は対象が存在しない場合 None が返る)
        targetob = bpy.data.objects.get(targetname)

        # 指定オブジェクトが存在するか確認する
        if targetob == None:
            # 指定オブジェクトが存在しない場合は処理しない
            return

        # オブジェクトがメッシュであるか確認する
        if targetob.type == 'MESH':
            # メッシュであれば選択状態にする
            targetob.select_set(True)
            # 対象アクティブオブジェクトに切り替える
            # メッシュはアクティブオブジェクトに結合される
            bpy.context.view_layer.objects.active = targetob
        else:
            # メッシュでなければ選択状態にしない
            targetob.select_set(False)

    # オブジェクトの結合を実行する
    bpy.ops.object.join()

    # 結合オブジェクト名が設定されているか
    if len(arg_jointname):
        # オブジェクト名が設定されていれば名前を変更する
        bpy.context.view_layer.objects.active.name = arg_jointname

    return


# main関数の呼び出し
main()

こちらのスクリプトでは isHighModel 変数の True/False 値を切り替えることで、最後の仕上げ処理を切り替えています。
isHighModel を True にした場合、オブジェクト名の末尾に "_HP" と付くオブジェクトは細分割を行った上で結合を行います。
f:id:bluebirdofoz:20200405231700j:plain

isHighModel を False にした場合、細分割は行わず、オブクト名の末尾に "_LD" と付くオブジェクトを削除した上で結合を行います。
f:id:bluebirdofoz:20200405231713j:plain

MRTK v2のドキュメントを少しずつ読み解く テレポートシステム

本日は MRTKv2 の調査枠です。
MRTKv2 の Guides ドキュメントを少しずつ読み進めていきます。

MRTKv2のGuidesドキュメント

以下のドキュメントを読み進めていきます。
microsoft.github.io

以下のページでは有志による本ドキュメントの日本語翻訳が行われています。
投稿時点でこちらで未翻訳、または著者が興味のある部分について記事にしていきます。
hololabinc.github.io

本記事では以下のページを読み進めます。
microsoft.github.io
f:id:bluebirdofoz:20200404191912j:plain

テレポートシステム

テレポートシステムはMRTKのサブシステムです。
アプリケーションが不透明なディスプレイを使用しているときにユーザーのテレポートを処理します。
HoloLensなどのAR体験では、テレポーテーションシステムはアクティブではありません。
没入型HMDエクスペリエンス(OpenVR、WMR)の場合、テレポートシステムを有効にできます。

有効化と無効化

テレポートシステムは、プロファイルのチェックボックスを切り替えることで有効または無効にできます。
これはシーンでMixedRealityToolkitオブジェクトを選択し、[Teleport]をクリックして、[Enable Teleport System]チェックボックスを切り替えることで実行できます。
f:id:bluebirdofoz:20200404191925j:plain

これは実行時にも行うことができます。

void DisableTeleportSystem()
{
    CoreServices.TeleportSystem.Disable();
}

void EnableTeleportSystem()
{
    CoreServices.TeleportSystem.Enable();
}

f:id:bluebirdofoz:20200404191936j:plain

イベント

テレポートシステムは、IMixedRealityTeleportHandler インターフェイスを介してイベントを公開します。
テレポートアクションが開始、終了、またはキャンセルされたときの信号を提供します。
イベントのメカニズムとそれに関連するペイロードの詳細については、リンクされたAPIドキュメントを参照してください。
microsoft.github.io

使用法

以下のコードは、テレポーテーションイベントをリッスンするMonoBehaviourを作成する方法を示しています。
このコードは、テレポートシステムが有効になっていることを前提としています。

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.Teleport;
using UnityEngine;

public class TeleportHandlerExample : MonoBehaviour, IMixedRealityTeleportHandler
{
    public void OnTeleportCanceled(TeleportEventData eventData)
    {
        Debug.Log("Teleport Cancelled");
    }

    public void OnTeleportCompleted(TeleportEventData eventData)
    {
        Debug.Log("Teleport Completed");
    }

    public void OnTeleportRequest(TeleportEventData eventData)
    {
        Debug.Log("Teleport Request");
    }

    public void OnTeleportStarted(TeleportEventData eventData)
    {
        Debug.Log("Teleport Started");
    }

    void OnEnable()
    {
        // This is the critical call that registers this class for events. Without this
        // class's IMixedRealityTeleportHandler interface will not be called.
        CoreServices.TeleportSystem.RegisterHandler<IMixedRealityTeleportHandler>(this);
    }

    void OnDisable()
    {
        // Unregistering when disabled is important, otherwise this class will continue
        // to receive teleportation events.
        CoreServices.TeleportSystem.UnregisterHandler<IMixedRealityTeleportHandler>(this);
    }
}

f:id:bluebirdofoz:20200404191947j:plain

MRTK v2のドキュメントを少しずつ読み解く コントローラーマッピングツール

本日は MRTKv2 の調査枠です。
MRTKv2 の Guides ドキュメントを少しずつ読み進めていきます。

MRTKv2のGuidesドキュメント

以下のドキュメントを読み進めていきます。
microsoft.github.io

以下のページでは有志による本ドキュメントの日本語翻訳が行われています。
投稿時点でこちらで未翻訳、または著者が興味のある部分について記事にしていきます。
hololabinc.github.io

本記事では以下のページを読み進めます。
microsoft.github.io
f:id:bluebirdofoz:20200403224800j:plain

コントローラーマッピングツール

コントローラーマッピングツールはランタイム(デバイス上またはエディター内)ツールです。
開発者はハードウェアコントローラー(例:モーションコントローラー)の Unity 入力軸とボタンマッピングを決定できます。

このツールは新しいハードウェアコントローラーのサポートを開発するときに非常に役立ちます。
また、既存のコントローラーのサポートクラスで疑わしいコントロールマッピングの問題を確認するのにも役立ちます。
f:id:bluebirdofoz:20200403224813j:plain

コントローラマッピングツールの使用

コントローラーマッピングツールを使うには、MixedRealityToolkit.Tools/RuntimeTools/Tools/ControllerMappingTool 配下の ControllerMappingTool シーンを開きます。
f:id:bluebirdofoz:20200403224825j:plain

シーンが読み込まれると、プロジェクトはエディターで再生モードを使用して実行するか、デバイスでビルドして実行できます。

コントローラに対するUnityのマッピングを調べる手順は以下の通りです。
・コントローラーを接続する
・各ボタンを押して各軸を移動
・表示のマッピングを確認する
・コントローラーの入力システムデータプロバイダーのコントロールマッピングを更新する

注意

コントローラマッピングツールは、Microsoft Mixed Reality Toolkit コンポーネントを使用しません。
Unity と直接通信して、コントロールマッピングを決定および表示します。

全てのコントロールが表示されます

大型のディスプレイパネルは、定義されたすべての Unity 入力軸とボタンの状態を報告します(例:Axis 10、Button 3)。
このパネルはコントローラーの状態のビューを提供します。
f:id:bluebirdofoz:20200403224840j:plain

アクティブコントロールディスプレイ

小型のディスプレイパネルには、Unity 入力が固定され、アクティブな状態のボタンが表示されます。
アクティブコントロールディスプレイは、コントローラーの状態の読みやすい概要ビューを提供します。
f:id:bluebirdofoz:20200403224850j:plain

MRTK v2のドキュメントを少しずつ読み解く 依存関係ウィンドウ

本日は MRTKv2 の調査枠です。
MRTKv2 の Guides ドキュメントを少しずつ読み進めていきます。

MRTKv2のGuidesドキュメント

以下のドキュメントを読み進めていきます。
microsoft.github.io

以下のページでは有志による本ドキュメントの日本語翻訳が行われています。
投稿時点でこちらで未翻訳、または著者が興味のある部分について記事にしていきます。
hololabinc.github.io

本記事では以下のページを読み進めます。
microsoft.github.io
f:id:bluebirdofoz:20200402090320j:plain

依存関係ウィンドウ

Unityでは、どのアセットが使用されていて何を参照しているのかを確認することは困難なことが多いです。
「Find References in Scene」オプションは、現在のシーンのみについて確認する場合、効果的です。
f:id:bluebirdofoz:20200402090339j:plain

Unityプロジェクト全体について確認する場合、依存関係ウィンドウが役立ちます。

依存関係ウィンドウには、アセットがどのように参照して相互に依存しているかが表示されます。
依存関係は YAML ファイル内の GUID を解析することによって取得されます。
つまり、スクリプト間の依存関係は考慮されません。

使用方法

ウィンドウを開くには、Mixed Reality Toolkit -> Utilities -> Dependency Window を選択します。
f:id:bluebirdofoz:20200402090359j:plain

依存関係ウィンドウが開き、プロジェクトの[Dependency Graph]の構築が自動的に開始されます。
[Dependency Graph]が作成されたら、[Asset Selection]でアセットを選択して、依存関係を検査できます。
f:id:bluebirdofoz:20200402090411j:plain

ウィンドウには、現在選択されているアセットが依存するアセットのリスト[This asset depends on:]と、依存するアセットの階層リスト[Assets that depend on this:]が表示されます。
現在選択されているアセットが何も依存していない場合は、プロジェクトから削除することを検討できます。
(一部のアセットは、Shader.Find()などのAPIを介してプログラムによって読み込まれ、依存関係トラッカーでキャッチされない場合があります)

他のアセットによって参照されておらず、削除の対象となる可能性があるアセットのリストのみを表示することもできます。
[Unreferenced Asset List]タブを開いて確認します。
f:id:bluebirdofoz:20200402090425j:plain

注意

依存関係ウィンドウの使用中にアセットが変更、追加、または削除された場合は、依存関係グラフを更新して最新の結果を表示することをお勧めします。
f:id:bluebirdofoz:20200402090436j:plain

MRTK v2のドキュメントを少しずつ読み解く MixedRealityServiceRegistryとIMixedRealityServiceRegistrar

本日は MRTKv2 の調査枠です。
MRTKv2 の Guides ドキュメントを少しずつ読み進めていきます。

MRTKv2のGuidesドキュメント

以下のドキュメントを読み進めていきます。
microsoft.github.io

以下のページでは有志による本ドキュメントの日本語翻訳が行われています。
投稿時点でこちらで未翻訳、または著者が興味のある部分について記事にしていきます。
hololabinc.github.io

本記事では以下のページを読み進めます。
microsoft.github.io
f:id:bluebirdofoz:20200401213516j:plain

MixedRealityServiceRegistryとIMixedRealityServiceRegistrarとは?

Mixed Reality Toolkitには、関連するタスクを実行する以下の2つのコンポーネントがあります。
・MixedRealityServiceRegistry
・IMixedRealityServiceRegistrar

MixedRealityServiceRegistry

MixedRealityServiceRegistry は各登録されたサービス(コアシステムと拡張サービス)のインスタンスを含む要素です。

MixedRealityServiceRegistry は静的なC#クラスとして実装されています。
アプリケーションコードでサービスインスタンスを取得するために使用する推奨パターンです。

以下のコードは IMixedRealityInputSystem インスタンスの取得を示しています。

IMixedRealityInputSystem inputSystem = null;

if (!MixedRealityServiceRegistry.TryGetService<IMixedRealityInputSystem>(out inputSystem))
{
    // Failed to acquire the input system.
    // It may not have been registered
    // 入力系の取得に失敗しました。
    // 登録されていない可能性があります。
}
注意

MixedRealityServiceRegistry には IMixed Reality Extension Service を含む、IMixed Reality Service インターフェースを実装するオブジェクトのインスタンスが含まれています。

Mixed Reality Data Provider(IMixedRealityServiceのサブクラス)を実装するオブジェクトは、MixedRealityServiceRegistry に明示的に登録されていません。
これらのオブジェクトは、個々のサービス(例:空間認識)によって管理されます。

IMixedRealityServiceRegistrar

IMixedRealityServiceRegistrar は実装される機能を定義するインターフェースです。
IMixedRealityServiceRegistrar を実装するコンポーネントは MixedRealityServiceRegistry 内のデータの追加と削除を担当します。

その他の Registrar は MixedRealityToolkit.SDK/Experimental/Features フォルダーにあります。
f:id:bluebirdofoz:20200401213653j:plain

これらのコンポーネントを使用して、アプリケーションに単一のサービス(例:Spatial Awareness)サポートを追加できます。
これらの単一サービスマネージャーを以下に示します。

BoundarySystemManager
CameraSystemManager
DiagnosticsSystemManager
InputSystemManager
SpatialAwarenessSystemManager
TeleportSystemManager

上記の各コンポーネントは、InputSystemManager を除き、単一のサービスタイプの登録とステータスを管理します。
InputSystem には InputSystemManager によって管理される追加のサポートサービス(例:FocusProvider)が必要です。

一般に、IMixedRealityServiceRegistrar によって定義されたメソッドは、サービス管理コンポーネントによって内部的に呼び出されるか、正しく機能するために追加のサービスコンポーネントを必要とするサービスによって呼び出されます。
アプリケーションコードは通常、これらのメソッドを呼び出さないでください。
呼び出すと、アプリケーションが予期しない動作をする可能性があります。
(たとえば、キャッシュされたサービスインスタンスが無効になる場合があります)

Blender2.8での頂点結合をマウス操作で行う

本日は Blender2.8 の小ネタ枠です。
Blender2.8での頂点結合をマウス操作で行う方法を記事にします。
f:id:bluebirdofoz:20200331134742j:plain

キー操作で頂点結合を行う場合

通常、キー操作を利用して頂点同士の結合を行おうとすると、以下の手順を実施する必要があります。
1.結合する片方の頂点を選択する。
2.結合するもう片方の頂点をShiftを押しながら選択する。
3.二つの頂点が選択されている状態で Alt+Mキー -> 結合 を行う。
f:id:bluebirdofoz:20200331134754j:plain

一つ二つの結合であれば上記の方法でも問題ありませんが、十個、ニ十個となってくると手間がかかります。
そこでマウスのドラッグ操作のみで頂点結合を行う手順を紹介します。

マウス操作で頂点結合を行う設定

スナップ機能で頂点を吸着させ、距離の自動マージを行うようにするとマウス操作のみで頂点結合が可能です。

最初にモードを[編集モード]に切り替えます。
f:id:bluebirdofoz:20200331134814j:plain

頂点を吸着させるため、スナップ機能を有効化します。
スナップボタンのプルダウンを開き、「スナップ先」に[頂点]を選択します。
f:id:bluebirdofoz:20200331134839j:plain

ビュー右端の[<]ボタンをクリックしてサイドバーを表示させ、[ツール]タブを開きます。
[自動マージ]にチェックを入れると、距離による自動マージが有効化されます。
チェックボックスは[編集モード]でのみ表示されます。
f:id:bluebirdofoz:20200331134852j:plain

自動結合を試す

この設定を行った状態で結合したい頂点を選択し、ドラッグ操作でもう一方の頂点に近づけてみます。
f:id:bluebirdofoz:20200331134902j:plain

すると、ある程度頂点に近づくと、キャプチャのように頂点が頂点に吸い付きます。
この状態で、左クリックでドラッグをやめると、頂点と頂点が結合されます。
f:id:bluebirdofoz:20200331134911j:plain

Blender2.79以前の手順について

Blender2.79以前での手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

Blender2.8で辺のクリース・シャープ・ベベル・シームを個別に確認する

本日は Blender2.8 の小ネタ枠です。
Blender2.8で辺のクリース・シャープ・ベベル・シームを個別に確認する方法を記事にします。
f:id:bluebirdofoz:20200330085748j:plain

クリース・シャープ・ベベル・シームをまとめて設定した場合

同じ辺にクリース・シャープ・ベベル・シームを設定した場合、表示色が混ざり合い、見た目ではどの設定を行ったか分からなくなる場合があります。
f:id:bluebirdofoz:20200330085724j:plain

クリース・シャープ・ベベル・シームを確認する

Blneder2.8 では「ビューポートオーバレイ」から辺の設定を個別に表示することができます。
表示の切り替えを行うため、編集モードに入ります。
f:id:bluebirdofoz:20200330085756j:plain

「ビューポートオーバレイ」を開きます。
「メッシュ編集モード」の[クリース]、[シャープ]、[ベベル]、[シーム]でどの表示を有効化するか切り替えることができます。
これを利用すると辺に設定されている設定を個別に確認できます。
f:id:bluebirdofoz:20200330085804j:plain

クリース

「サブディビジョンサーフェス」モディファイアにおけるエッジの鋭さを設定します。
f:id:bluebirdofoz:20200330085813j:plain

シャープ

「辺分離」モディファイアで辺の分離を行う箇所を設定します。
f:id:bluebirdofoz:20200330085822j:plain

ベベル

「ベベル」モディファイアで利用するベベルのウェイトを設定します。
f:id:bluebirdofoz:20200330085831j:plain

シーム

UV展開を行う際のUVマップの切れ込みを設定します。
f:id:bluebirdofoz:20200330085840j:plain