MRが楽しい

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

MRTKの空間認識システムのレイヤー番号をスクリプトから取得する

本日は MRTK の小ネタ枠です。
MRTKの空間認識システムのレイヤー番号をスクリプトから取得する方法を記事にします。
f:id:bluebirdofoz:20210410231706j:plain

MRTKの空間認識システム

docs.microsoft.com

MRTKの空間認識システムのレイヤー番号をスクリプトから取得するには以下のようなコードを利用します。

// 空間認識のオブザーバを取得する
IMixedRealitySpatialAwarenessMeshObserver SpatialAwarenessMeshObserver =
    CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

// オブザーバからレイヤー番号を取得する
int spatialAwarenessLayer = SpatialAwarenessMeshObserver.MeshPhysicsLayer;

サンプルスクリプト

Collider の Trigger イベントから空間認識のメッシュを判定する以下のサンプルスクリプトを作成しました。
・SpatialMapTest

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// CoreSystemへのアクセスのため
using Microsoft.MixedReality.Toolkit;
// 空間認識情報の取得のため
using Microsoft.MixedReality.Toolkit.SpatialAwareness;

namespace HMProject.Test
{
    [RequireComponent(typeof(Collider))]
    public class SpatialMapTest : MonoBehaviour
    {
        [SerializeField, Tooltip("空間認識レイヤー番号")]
        int p_SpatialAwarenessLayer;

        /// <summary>
        /// 起動処理
        /// </summary>
        void Start()
        {
            // 空間認識のオブザーバを取得する
            IMixedRealitySpatialAwarenessMeshObserver SpatialAwarenessMeshObserver =
                CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();

            // オブザーバからレイヤー番号を取得する
            p_SpatialAwarenessLayer = SpatialAwarenessMeshObserver.MeshPhysicsLayer;
        }


        private void OnTriggerStay(Collider collider)
        {
            // オブジェクトのレイヤー番号を取得する
            int layernumber = collider.gameObject.layer;

            // 空間認識レイヤーか否か
            if (layernumber == p_SpatialAwarenessLayer)
            {
                Debug.Log("OnTriggerStay : " + collider.name + " is Spatial Awareness Layer.");
            }
            else
            {
                Debug.Log("OnTriggerStay : " + collider.name + " is not Spatial Awareness Layer.");
            }
        }
    }
}

f:id:bluebirdofoz:20210410231735j:plain

サンプルスクリプトを Collider コンポーネントを設定したオブジェクトに追加します。
f:id:bluebirdofoz:20210410231752j:plain

この状態でシーンを再生してみます。
ログを確認すると空間認識のメッシュとその他のメッシュをレイヤーで判別できていることがわかります。
f:id:bluebirdofoz:20210410231806j:plain

Blenderで3Dモデルのポリゴンが分かれている部分を別オブジェクトに分離する

本日は Blender の小ネタ枠です。
Blenderで3Dモデルのポリゴンが分かれている部分を別オブジェクトに分離する手順を記事にします。
f:id:bluebirdofoz:20210409225537j:plain

サンプルモデル

以下のような複数の分かれたメッシュを1つのオブジェクトに結合したモデルを準備しました。
f:id:bluebirdofoz:20210409225547j:plain

指定の部分のみ分離する

指定の分かれている部分のみを分離したい場合の手順です。
オブジェクトを指定した状態で[編集モード]に入ります。
f:id:bluebirdofoz:20210409225605j:plain

分割したい部分の頂点を選択します。
f:id:bluebirdofoz:20210409225619j:plain

この状態で[選択 -> リンク選択 -> リンク]を実行します。
すると、選択した頂点に繋がるポリゴン部分が全て選択されます。
f:id:bluebirdofoz:20210409225629j:plain

ポリゴンが選択された状態で[メッシュ -> 分離 -> 選択]を実行します。
f:id:bluebirdofoz:20210409225638j:plain

これで選択していた部分のみが別オブジェクトに分離されます。
f:id:bluebirdofoz:20210409225650j:plain

全ての分かれている部分を別々に分離する

全ての分かれている部分を別々に分離したい場合の手順です。
オブジェクトを指定した状態で[編集モード]に入ります。
f:id:bluebirdofoz:20210409225701j:plain

[メッシュ -> 分離 -> 構造的に分離したパーツで]を実行します。
f:id:bluebirdofoz:20210409225710j:plain

これで全てのポリゴンが分かれている部分が別々のオブジェクトに分離されます。
f:id:bluebirdofoz:20210409225720j:plain

Blenderで3Dモデルを1オブジェクト毎に1マテリアルが割り当てられたモデルに修正する

本日は Blender の小ネタ枠です。
Blenderで3Dモデルを1オブジェクト毎に1マテリアルが割り当てられたモデルに修正する手順を記事にします。
f:id:bluebirdofoz:20210408212711j:plain

サンプルモデル

以下のようなオブジェクトを上下で分け、異なるマテリアルを割り当てた3Dモデルを準備しました。
f:id:bluebirdofoz:20210408212721j:plain
f:id:bluebirdofoz:20210408212730j:plain

このモデルを1オブジェクト毎に1マテリアルが割り当てられたモデルに修正してみます。

モデルを結合する

最初にモデルを1つのオブジェクトに結合します。
[オブジェクトモード]で[選択 -> すべて]を実行して全てのオブジェクトを選択した状態にします。
f:id:bluebirdofoz:20210408212745j:plain

この状態で[オブジェクト -> 結合]を実行すると全てのオブジェクトを結合できます。
f:id:bluebirdofoz:20210408212757j:plain

マテリアルの割り当て毎にオブジェクトを分割する

この状態からマテリアルの割り当て毎にオブジェクトを分割します。
結合したオブジェクトを選択し、[編集モード]に切り替えます。
f:id:bluebirdofoz:20210408212809j:plain

[編集モード]で[メッシュ -> 分離 -> マテリアルで]を実行してオブジェクトを分割します。
f:id:bluebirdofoz:20210408212827j:plain

これで1オブジェクト毎に1マテリアルが割り当てられたモデルに修正することができました。
f:id:bluebirdofoz:20210408212857j:plain
f:id:bluebirdofoz:20210408212909j:plain
f:id:bluebirdofoz:20210408212918j:plain

HoloLens2でホロモンアプリを作る その28(ホロモンが食べ物を引き寄せる)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
f:id:bluebirdofoz:20210407232221j:plain

今回はホロモンが食べ物を引き寄せるメモです。

ホロモンが食べ物を引き寄せる

ホロモンが食べ物を口元で食べているように見せるため、食べ物をホロモンの近くに引き寄せる仕組みを実装します。
食べ物などのアイテムオブジェクトに追加するスクリプトに以下の関数を追加しました。
・ItemCommonController.cs

        /// <summary>
        /// 指定の位置にアイテムを保持する
        /// </summary>
        public void FoldItem(Transform a_HoldTransform)
        {
            // フードオブジェクトを保持座標に移動する
            //this.transform.position = a_HoldTransform.position;
            //this.transform.rotation = a_HoldTransform.rotation;

            // 一旦、慣性をリセットする
            this.GetComponent<Rigidbody>().velocity = Vector3.zero;
            this.GetComponent<Rigidbody>().angularVelocity = Vector3.zero;

            // 重力を切る
            this.GetComponent<Rigidbody>().useGravity = false;

            // アタリ判定を無効化する
            this.GetComponent<Collider>().enabled = false;

            // スムーズにトランスフォームを移動させる
            // 分割フレーム数
            int lerpTime = 20;

            // 反映前トランスフォーム
            Vector3 beforePosition = this.transform.position;
            Quaternion beforeRotation = this.transform.rotation;

            // 反映後トランスフォーム
            Vector3 afterPosition = a_HoldTransform.position;
            Quaternion afterRotation = a_HoldTransform.rotation;

            // トリガーを設定済みの場合は一旦破棄する
            p_LerpTrigger?.Dispose();

            // 1フレームごとにトランスフォームを徐々に変化させる
            p_LerpTrigger = Observable
                .IntervalFrame(1)
                .Take(lerpTime)
                .SubscribeOnMainThread()
                .Subscribe(
                x =>
                {
                    // Lerpを使って徐々にトランスフォームを変化させる
                    float current = (float)(1.0 / lerpTime) * x;
                    Vector3 settingPosition = Vector3.Lerp(beforePosition, afterPosition, current);
                    Quaternion settingRotation = Quaternion.Lerp(beforeRotation, afterRotation, current);

                    this.transform.position = settingPosition;
                    this.transform.rotation = settingRotation;
                },
                () =>
                {
                    // 完了時は最終的なトランスフォーム値を設定する
                    this.transform.position = afterPosition;
                    this.transform.rotation = afterRotation;
                })
                .AddTo(this);
        }

f:id:bluebirdofoz:20210407232235j:plain

トランスフォームをスムーズに変化させるため、以下の Vector3 と Quaternion の Lerp 関数を利用しています。
docs.unity3d.com
docs.unity3d.com

食べ物を保持する位置はホロモンの子オブジェクトとして位置を決めておきます。
f:id:bluebirdofoz:20210407232311j:plain

食べ物を食べるアニメーションを実行するとき、この子オブジェクトを指定して関数を実行します。
シーンを再生すると、ホロモンの手元に食べ物が引き寄せられてアニメーションが再生されます。
f:id:bluebirdofoz:20210407232321j:plain

HoloLens2でホロモンアプリを作る その27(ホロモンの身長をスムーズに変化させる)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
f:id:bluebirdofoz:20210406012303j:plain

今回はホロモンの身長をスムーズに変化させるメモです。

ホロモンの身長をスムーズに変化させる

ホロモンの身長をスムーズに変化させるため、Mathf クラスの Lerp 関数と UniRx の IntervalFrame を利用しました。
Mathf クラスの Lerp 関数は2つの値を線形補完する関数です。
docs.unity3d.com
また、UniRx の IntarvalFlame は指定のフレーム数ごとに処理を実行します。

ホロモンのトランスフォームを操作するスクリプトに以下のような身長をスムーズに変化させる関数を追加しました。
・HoloMonTransformController.cs

        /// <summary>
        /// 現在の身長を反映する
        /// </summary>
        /// <param name="a_CurrentHeight"></param>
        private void ApplyHeightCondition(float a_CurrentHeight)
        {
            // 分割フレーム数
            int lerpTime = 50;

            // 反映後身長
            float afterHeight = a_CurrentHeight;

            // 反映前身長
            float beforeHeight = p_Scale;

            // トリガーを設定済みの場合は一旦破棄する
            p_LeapTrigger?.Dispose();

            // 1フレームごとにスケールを徐々に拡大する
            p_LeapTrigger = Observable
                .IntervalFrame(1)
                .Take(lerpTime)
                .SubscribeOnMainThread()
                .Subscribe(
                x =>
                {
                    // Lerpを使って徐々にスケールを変化させる
                    float current = (float)(1.0 / lerpTime) * x;
                    float settingHeight = Mathf.Lerp(beforeHeight, afterHeight, current);
                    UpdateScale(settingHeight);
                },
                () =>
                {
                    // 完了時は最終的なスケール値を設定する
                    UpdateScale(a_CurrentHeight);
                })
                .AddTo(this);
        }

f:id:bluebirdofoz:20210406012335j:plain

シーンを再生して動作を確認します。
食事を食べると、ホロモンの身長がスムーズに変化して成長します。
f:id:bluebirdofoz:20210406012351j:plain

HoloLens2でホロモンアプリを作る その26(ホロモンの身長に合わせて食事の大きさを変える)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
f:id:bluebirdofoz:20210405233528j:plain

今回はホロモンの身長に合わせて食事の大きさを変えるメモです。

ホロモンの身長に合わせて食事の大きさを変える

前回のホロモンの状態を記録するスクリプトと同じ手法で身体状態を保持するスクリプトを作成しました。
・HoloMonConditionBodySingleton.cs

f:id:bluebirdofoz:20210405233555j:plain

一方でお肉モデルを管理するスクリプトにReactivePropertyをSubscribeして身長の変化に合わせてモデルのサイズを変更する機能を追加します。
・ItemFoodController.cs

f:id:bluebirdofoz:20210405233615j:plain

シーンを再生して動作を確認します。
ホロモンの身長に合わせて食事が徐々に大きくなっていきます。
f:id:bluebirdofoz:20210405233633j:plain

HoloLens2でホロモンアプリを作る その25(ホロモンの身長を数値に合わせて四捨五入する)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
f:id:bluebirdofoz:20210404230446j:plain

今回はホロモンの身長を四捨五入するメモです。

ホロモンの身長を数値に合わせて四捨五入する

ホロモンアプリではホロモンが肉を食べると一定の増加率でホロモンが成長します。
そのとき、身長の値に端数が発生するので、数値に合わせて四捨五入する関数を追加しました。
・HoloMonConditionBodySingleton.cs

        /// <summary>
        /// 身長の値をルールに基づいて四捨五入する
        /// </summary>
        /// <returns></returns>
        private float RoundHeight(float a_Height)
        {
            float roundHeight = 0.0f;

            // 1m 未満の場合は小数点第三位を四捨五入する
            if (a_Height < 1.0f)
            {
                roundHeight = (float)System.Math.Round(a_Height, 2, MidpointRounding.AwayFromZero);
            }
            else
            {
                // 1m ~ 10m の場合は小数点第二位を四捨五入する
                if(a_Height < 10.0f)
                {
                    roundHeight = (float)System.Math.Round(a_Height, 1, MidpointRounding.AwayFromZero);
                }
                // 10m 以上は小数点第一位を四捨五入する
                else
                {
                    roundHeight = (float)System.Math.Round(a_Height, MidpointRounding.AwayFromZero);
                }
            }

            return roundHeight;
        }

System.Math の Round 関数で端数の処理を行います。
このとき、MidpointRounding.AwayFromZero のモードを指定することで指定の桁を四捨五入することができます。
docs.microsoft.com

以下の通り、ホロモンがお肉を食べて成長すると、身長の値が四捨五入されています。
f:id:bluebirdofoz:20210404230600j:plain