夜風のMixedReality

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

UnityでMeshクラスを使用して3Dモデルの原点調整を行う その② 回転

本日は機能に引き続きUnity枠です。

 UnityではBlenderやMayaなどのDCCツールと異なり、あくまでゲームエンジンであるため3Dメッシュの編集等のモデリング機能はデフォルトでサポートされていません。

 そのためメッシュ等に不備がある場合はDCCツールに戻り、作業をすることがありますが、特によくあるシチュエーションとして3Dオブジェクトの原点がずれていることでゲームエンジン側で扱いにくいことがあります。

 今回はUnityでMeshクラスを用いて3Dモデルの原点を任意に変更する実装を行います。

先日は位置の適応を行いました。

redhologerbera.hatenablog.com

〇環境

・Unity2022.3.24f1

・Windows11PC

〇回転の適応

 先日は単純に位置を調整するためにMeshクラスを使用してすべての頂点に対して新しく原点として設定したい距離分頂点を動かしていました。

        // 新しいメッシュを作成し、元のメッシュの情報をコピー
        Mesh newMesh = Instantiate(originalMesh);

        // ピボットを基準に頂点位置を調整
        Vector3 pivotOffset = transform.InverseTransformPoint(pivot.position);
        Vector3[] vertices = newMesh.vertices;

        for (int i = 0; i < vertices.Length; i++)
        {
            vertices[i] -= pivotOffset;
        }

 回転の場合は単純な頂点移動操作だけではすみません。

 回転移動を行う場合は回転行列を使用する必要があります。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

 回転行列を行うことである基準点をベースにオブジェクト全体の回転を行えます。

 そのため処理上は位置のピボットの処理の前に行う必要があります。


が、これは一般的な回転を行う場合の話であり、今回は単純にUnityのワールド座標に対してメッシュのトランスフォームを適応したいだけのためもっと簡単に実装することができます。

〇コード

 

using JetBrains.Annotations;
using UnityEngine;

[RequireComponent(typeof(MeshFilter))]
public class AdjustPivotPoint : MonoBehaviour
{
    [SerializeField] [NotNull] private Transform pivot; // ピボットを設定するTransform
    [SerializeField] private bool adjustPosition = true; // 位置を調整するか
    [SerializeField] private bool adjustRotation = true; // 回転を調整するか
    [SerializeField] private bool adjustScale = true; // スケールを調整するか

    private void Start()
    {
        if (pivot == null)
        {
            Debug.LogWarning("Pivot transform is not assigned.");
            return;
        }

        AdjustMeshPivot();
    }

    private void AdjustMeshPivot()
    {
        // 元のメッシュを取得
        MeshFilter meshFilter = GetComponent<MeshFilter>();
        Mesh originalMesh = meshFilter.sharedMesh;

        if (originalMesh == null)
        {
            Debug.LogWarning("No mesh found on this object.");
            return;
        }

        // 新しいメッシュを作成し、元のメッシュの情報をコピー
        Mesh newMesh = Instantiate(originalMesh);

        // ピボットのワールド座標を取得
        Vector3 pivotWorldPosition = pivot.position;

        // 頂点を取得して調整
        Vector3[] vertices = newMesh.vertices;
        for (int i = 0; i < vertices.Length; i++)
        {
            // 元の頂点をワールド座標に変換
            Vector3 vertexWorldPosition = transform.TransformPoint(vertices[i]);

            // ピボットの位置を考慮して頂点の位置を調整
            vertexWorldPosition -= pivotWorldPosition; // ピボットを新しい原点とする

            // ワールド座標での位置を新しいメッシュに適用
            vertices[i] = vertexWorldPosition;
        }

        // 調整した頂点を新しいメッシュに適用し、境界を再計算
        newMesh.vertices = vertices;
        newMesh.RecalculateBounds();
        // 法線を再計算
        newMesh.RecalculateNormals();

        // メッシュをオブジェクトに適用
        meshFilter.mesh = newMesh;

        // オブジェクトの位置と回転をリセット
        if (adjustPosition)
        {
            transform.position = pivotWorldPosition; // ピボットの位置に合わせる
        }
        if (adjustRotation)
        {
            transform.rotation = Quaternion.identity; // 回転をリセット
        }
        if (adjustScale)
        {
            transform.localScale = Vector3.one; // スケールをリセット   
        }
        
    }
}

ここで行っていることは、メッシュを複製し、頂点の座標をワールド座標に戻し、そのまま新しいメッシュとして代入しています。

これによってピボットがその状態で既往されます。

処理前

処理後

本日は以上です。

次回メッシュを出力して実行時以外でも使用できるようにしていきます。