夜風のMixedReality

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

Blenderでサーバーを立てMessagePackでメッシュを送信する その⑤メッシュの構築

本日は昨日に引き続きMixedRealityModelingToolsを紹介します。

先日まででBlenderから選択中のメッシュの情報を取得、MessagePackを使用してシリアライズしたデータをバイナリとしてUnity側に送信、Unity側では受け取ったデータをデシリアライズして使用可能なデータとすることまで行いました。

本日はいよいよUnity側でメッシュを構築していきます。

〇Untiy側でデータからメッシュを構築する

Unityでメッシュを扱う際一般的にはFBXなどの3Dモデルのデータとして扱うことが多いです。

この場合MeshFilterコンポーネント及び、MeshRendererコンポーネントが使用され、メッシュのレンダリングに使用されます。

MeshFilterコンポーネントはメッシュアセットへの参照を行うコンポーネントです。

MeshにはMeshクラスのアセットが入ります。 

MeshRendererコンポーネントはMeshFilterコンポーネントと連携してメッシュを指定したマテリアルやライティングで描画を行うコンポーネントになります。

データからメッシュアセットを構築し、MeshFilterに参照を渡すことでBlenderから受信したメッシュを描画できるようになります。

今回はデータからオブジェクトを構築するクラスとしてObjectBuilderクラスを作成しました。

using System;
using System.Collections;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;

namespace MixedRealityModelingTools.Core
{
    /// <summary>
    /// This class constructs a mesh based on the data received.
    /// </summary>
    public class ObjectBuilder : MonoBehaviour
    {
   ・・・
        private void Update()
        {
            //データを受け取った時に_isGetMeshDataをTrueに変える。  if文内でfalseに変えられデータを受信したフレームのみ実行
            if (_isGetMeshData)
            {
                _meshFilter = _createdObject.GetComponent<MeshFilter>(); //メッシュを参照させるMeshFilter
                _meshRenderer = _createdObject.GetComponent<MeshRenderer>(); //描画するためのMeshRenderer

                mesh = new Mesh(); //Meshの初期化

                List<Vector3> vertices = ConvertToVector3List(meshData.vertices); //頂点情報 
                mesh.SetVertices(vertices);//頂点をメッシュに定義
                mesh.SetTriangles(meshData.triangles, 0);//メッシュのインデックスを定義

                _meshFilter.mesh = mesh;//メッシュフィルターにメッシュを定義

                _isGetMeshData = false; //処理の終了

            }
        }
}

このクラスではmeshFilterにmesh = new Mesh();で新しく作成したメッシュを代入し、MeshDataクラスに格納されている頂点情報、メッシュのインデックスを割り当てています。

ここで頂点を割り当てる際に一つの問題が生じます。

Unity C#では三次元ベクトルをVector3型で定義、使用しますがPython側ではfloat型で定義、Unityでデシリアライズしたデータもfloat型のリストで定義しています。

 public class MeshData
    {
        [DataMember]
        public string objectname;
        
        [DataMember]
        public List<float> vertices;
    
        [DataMember]
        public List<int> triangles;
    
        [DataMember]
        public List<float> normals;
    }

まずこのデータをVector3型に変換する必要が生じています。これを実現するためにConvertToVector3List()という関数を定義しました。

                List<Vector3> vertices = ConvertToVector3List(meshData.vertices); //頂点情報 
                mesh.SetVertices(vertices);//頂点をメッシュに定義

Blender側で頂点のデータを3つ区切りでFloat形の配列に入れているため逆に3つ区切りでデータを取り出し、x,y,zのデータとして3次元化すればよいということになります。

        private List<Vector3> ConvertToVector3List(List<float> floats)
        {
            if (floats.Count % 3 != 0)//データが3の倍数でない場合
            {
                throw new ArgumentException("The float list cannot be divided into groups of three.");
            }

            List<Vector3> result = new List<Vector3>(floats.Count / 3);//新しいVector3型の初期化
            for (int i = 0; i < floats.Count; i += 3)
            {
                result.Add(new Vector3(floats[i], floats[i + 1], floats[i + 2]));//x,y,zで三つ区切りのfloat配列データを三次元化
            }

            return result;
        }

〇TCPClientとの連携

TCPClient側ではデータを受信した際にObjectBuilderクラスの_isGetMeshDataをTrueに変え、ObjectBuilderのMeshDataに受け取ったデータを渡しています。

                while (true)
                {
                    var bytesRead = _stream.Read(responseBytes, 0, responseBytes.Length);
                    // Get Blender rawData
                    Debug.Log(
                        $"Received {bytesRead} bytes: {BitConverter.ToString(responseBytes, 0, bytesRead).Replace("-", " ")}");

                    if (bytesRead == 0) break;
                    
                    
                    try
                    {
                    var meshData = DeserializeMeshData(responseBytes);
                    Debug.Log(
                        $"Received mesh data: vertices={meshData.vertices.Count}, triangles={meshData.triangles.Count}, normals={meshData.normals.Count}");
                    _objectBuilder.meshData = meshData;
                    _objectBuilder._isGetMeshData = true;
                    }
                    catch
                    {
                        Debug.Log("Received: " + Encoding.ASCII.GetString(responseBytes, 0, bytesRead));
                    }
                }
            }).Start();

以上がMessagePackを使用してBlenderからUntiyにメッシュのデータを送信するメソッドになります。

本日は以上です。