夜風のMixedReality

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

BlenderのPythonでサーバーを立てMessagePackでメッシュを送信する MixedRealityModelingToolsその④Unity側のメッシュデータのデシリアライズ

本日は昨日に引きつづき自身が開発中のパッケージMixedRealityModelingTools(仮)の機能紹介を行います。

昨日はUnity側のTCPクライアントの実装を見ていきました。

redhologerbera.hatenablog.com

本日はBlenderから送信されたメッシュデータの処理を解説します。

〇メッシュデータのフォーマット

Blender側ではメッシュデータのフォーマットを次のように定義していました。

    data_dict = {
        'vertices': vertices_list,
        'triangles': triangles_list,
        'normals': normals_list
    }

これを可視化すると次のようになります。

'vertices': [1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0], 'triangles': [4, 2, 0, 2, 7, 3, 6, 5, 7, 1, 7, 5, 0, 3, 1, 4, 1, 5, 4, 6, 2, 2, 6, 7, 6, 4, 5, 1, 3, 7, 0, 2, 3, 4, 0, 1], 'normals': [0.5773503184318542, 0.5773503184318542, 0.5773503184318542, 0.5773503184318542, 0.5773503184318542, -0.5773503184318542, 0.5773503184318542, -0.5773503184318542, 0.5773503184318542, 0.5773503184318542, -0.5773503184318542, -0.5773503184318542, -0.5773503184318542, 0.5773503184318542, 0.5773503184318542, -0.5773503184318542, 0.5773503184318542, -0.5773503184318542, -0.5773503184318542, -0.5773503184318542, 0.5773503184318542, -0.5773503184318542, -0.5773503184318542, -0.5773503184318542]

redhologerbera.hatenablog.com

このデータがバイナリとして送信されUnity側で受信します。

そのためUnity側でもバイナリをデシリアライズするうえでフォーマットの定義が必要です。

次のコードで行っています。

using MessagePack;
using MessagePack.Resolvers;
using MessagePack.Formatters;
using MessagePack.Unity;
using System.Collections.Generic;
using System.Runtime.Serialization;
using UnityEngine;

namespace MixedRealityModelingTools.Core
{
    [DataContract]
    public class MeshData
    {        
        [DataMember]
        public List<float> vertices;
    
        [DataMember]
        public List<int> triangles;
    
        [DataMember]
        public List<float> normals;
    }
  ・・・

MeshDataクラスがデータを格納するクラスになります。

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

[DataContract]を付けたクラスではデータコントラクトと呼ばれるクライアントとのデータの取り決めを行い通信においてデータを保証することができます。

[DataMember]の属性を付けた変数は、データ コントラクトの一部である型のメンバーを識別するものです。 これにより、データコントラクトをシリアル化できるシリアライザーが、その変数をJSONなどの形式に変換したり、逆に変換したりできます。

次にMeshDataクラスを使用してデータをデシリアライズ処理について紹介しいます。

〇MeshDataFormatter

Blenderから受け取ったバイナリデータは次のコードでデシリアライズしています。

using MessagePack;
using MessagePack.Formatters;
using UnityEngine;
using System.Collections.Generic;
using MessagePack.Resolvers;
using MixedRealityModelingTools.Core;
public class MeshDataFormatter : IMessagePackFormatter<MeshData>
{
  ・・・

    public MeshData Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
    {
        if (reader.TryReadNil())
        {
            return null;
        }

        options.Security.DepthStep(ref reader);

        int length = reader.ReadMapHeader();
        if (length != 3)
        {
            throw new MessagePackSerializationException("Invalid map length.");
        }

        string objectname = null;
        List<float> vertices = null;
        List<int> triangles = null;
        List<float> normals = null;

        for (int i = 0; i < length; i++)
        {
            string key = reader.ReadString();
            switch (key)
            {
                case "objectname":
                    objectname = options.Resolver.GetFormatterWithVerify<string>().Deserialize(ref reader, options);
                    break;
                case "vertices":
                    vertices = options.Resolver.GetFormatterWithVerify<List<float>>().Deserialize(ref reader, options);
                    break;
                case "triangles":
                    triangles = options.Resolver.GetFormatterWithVerify<List<int>>().Deserialize(ref reader, options);
                    break;
                case "normals":
                    normals = options.Resolver.GetFormatterWithVerify<List<float>>().Deserialize(ref reader, options);
                    break;
                default:
                    reader.Skip();
                    break;
            }
        }

        reader.Depth--;

        return new MeshData { objectname = objectname, vertices = vertices, triangles = triangles, normals = normals };
    }
}
・・・

データが頂点、メッシュのインデックス、法線の3つのデータであるかどうかをチェックしています。

  int length = reader.ReadMapHeader();
        if (length != 3)
        {
            throw new MessagePackSerializationException("Invalid map length.");
        }

ここで違う形式の場合はエラーが出るようになっています。

形式があっているかをチェックしたデータはデシリアライズされます。

最初に各データを収めるListを初期化・定義してデータの数switch文でキーワードに応じてデータを格納しています。

 string objectname = null;
        List<float> vertices = null;
        List<int> triangles = null;
        List<float> normals = null;

        for (int i = 0; i < length; i++)
        {
            string key = reader.ReadString();
            switch (key)
            {
                case "objectname":
                    objectname = options.Resolver.GetFormatterWithVerify<string>().Deserialize(ref reader, options);
                    break;
                case "vertices":
                    vertices = options.Resolver.GetFormatterWithVerify<List<float>>().Deserialize(ref reader, options);
                    break;
                case "triangles":
                    triangles = options.Resolver.GetFormatterWithVerify<List<int>>().Deserialize(ref reader, options);
                    break;
                case "normals":
                    normals = options.Resolver.GetFormatterWithVerify<List<float>>().Deserialize(ref reader, options);
                    break;
                default:
                    reader.Skip();
                    break;
            }
        }

以上がメッシュデータの格納クラスとデシリアライズの処理です。

これによってバイナリデータからデータが格納できるようになりました。

次回は格納したデータを使用してメッシュを構成する処理を説明します