夜風のMixedReality

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

MixedRealityModelingToolsその⑨ Unityのカメラ情報を取得してMessagePack形式で送信する

本日はMixedRealityModelingTools枠です。

〇データフォーマットの定義

MRMTではデータの送信フォーマットにMessagePackを使用しています。

開発時はBlender側とプラットフォーム側で個のフォーマットが一致しているという条件のもとデータの送受信が行えます。

Unity側からカメラ位置情報をBlenderに送信しますが、この場合もまずデータのフォーマットを定義する必要があります。

今回は次のように定義しました。

・ヘッダー:データによって処理を変えるために使用

・cameratranfform:Unityのカメラ位置

・camerarotation:Unityのカメラ角度

cameraに関してはtransformに角度をまとめることも考えたのですが、処理の都合と今後の機能開発のために分離支えています。

[DataContract]
    public class UnityCameraData
    {
        [DataMember] public string header;
        
        [DataMember] public List<float> cameratransform;
        
        [DataMember] public List<float> camerarotation;
    }

次にMessagePackを使用したカスタムシリアライズ、デシリアライズについての処理です。

ここでは定義した独自のフォーマットでMessagePackを使用した処理を行うための処理になります。

IFormatterResolverを継承したリゾルバークラスとIMessagePackFormatter<(データフォーマットクラス)>を継承するフォーマッターは次のようになります。

なおフォーマッターは、先に定義したデータクラスを使用してデータをフォーマットするクラスです。

ゾルバーとはフォーマッターを使用してデータのシリアライズ、デシリアライズするためのメソッドを定義します。

public class CustomUnityCameraResolver : IFormatterResolver
    {
  //MessagePackの機能とフォーマッターを使用してカスタムリゾルバーを作成
        public static readonly IFormatterResolver Instance = CompositeResolver.Create(
            new IMessagePackFormatter[] { new UnityCameraDataFormatter() }, 
            new IFormatterResolver[] {
                StandardResolver.Instance,
                UnityResolver.Instance
            }
        );
    
        public IMessagePackFormatter<T> GetFormatter<T>()
        {
            return Instance.GetFormatter<T>();
        }
    }

    public class UnityCameraDataFormatter:IMessagePackFormatter<UnityCameraData>
    {
         //シリアライズする際のカスタムメソッド
        public void Serialize(ref MessagePackWriter writer, UnityCameraData value, MessagePackSerializerOptions options)
        {
            //フォーマットのデータの数 それぞれの方に合わせてカスタムシリアライズを定義
            writer.WriteArrayHeader(3);
            options.Resolver.GetFormatterWithVerify<string>().Serialize(ref writer, value.header, options);
            options.Resolver.GetFormatterWithVerify<List<float>>().Serialize(ref writer, value.cameratransform, options);
            options.Resolver.GetFormatterWithVerify<List<float>>().Serialize(ref writer, value.camerarotation, options);
        }
      //フォーマッターのデータに合わせてカスタムデシリアライザーを定義
        public UnityCameraData Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
        {
            if (reader.TryReadNil())
            {
                return null;
            }
           
            options.Security.DepthStep(ref reader);
         
            int length = reader.ReadMapHeader();
            //フォーマット内のデータが3の場合のみ処理(フォーマットに合っていないデータは処理しない)
            if (length != 3)
            {
                throw new MessagePackSerializationException("Invalid map length.");
            }
    
            //データの初期化
            string header = null;
            List<float> cameratransform = null;
            List<float> camerarotation = null;
    
           //データの大きさ処理
            for (int i = 0; i < length; i++)
            {
                string key = reader.ReadString();
                switch (key)
                {
                    case "header":
                        header = options.Resolver.GetFormatterWithVerify<string>().Deserialize(ref reader, options);
                        break;
                    case "cameratransform":
                        cameratransform = options.Resolver.GetFormatterWithVerify<List<float>>().Deserialize(ref reader, options);
                        break;
                    case "camerarotation":
                        camerarotation = options.Resolver.GetFormatterWithVerify<List<float>>().Deserialize(ref reader, options);
                        break;
                    default:
                        reader.Skip();
                        break;
                }
            } 
            reader.Depth--;
            
            return new UnityCameraData() { header = header, cameratransform = cameratransform, camerarotation = camerarotation };
        }

〇カメラデータの取得とBlender側に送信

上記で作成したフォーマットに従いUnityのカメラ情報を取得して送信するメソッドが次になります。

   public byte[] SendCameraTransformData()
        {
            Transform cameraTransform = _camera.transform;//カメラの情報を取得

           //UnityCameraDataのフォーマットに従い新しいデータを作成
            UnityCameraData cameraData = new UnityCameraData
            {
                header = "UCAM", //ヘッダー "U"nity "CAM"era
                cameratransform = new List<float>
                {
                    cameraTransform.position.x,
                    cameraTransform.position.z,
                    cameraTransform.position.y
                },
                camerarotation = new List<float>
                {
                    cameraTransform.eulerAngles.x +90,
                    cameraTransform.eulerAngles.z,
                    cameraTransform.eulerAngles.y
                }
            };
            
    //MessagePackを使用してcameraDataをシリアライズ
            var options = MessagePackSerializerOptions.Standard.WithResolver(CompositeResolver.Create(
                new IMessagePackFormatter[] { new UnityCameraDataFormatter() },
                new IFormatterResolver[] {
                    CustomUnityCameraResolver.Instance
                }
            ));
            byte[] serializedData = MessagePackSerializer.Serialize(cameraData, options);
            

            return serializedData;
        }

最後にTCPCliant側でBlenderにデータを送信するメソッドは次になります。

 public void SendCameraDataToUnity()
        {
            var bytes = _unitySendData.SendCameraTransformData();
            Debug.Log(BitConverter.ToString(bytes).Replace("-", ""));
            
            _stream.Write(_unitySendData.SendCameraTransformData(), 0, _unitySendData.SendCameraTransformData().Length);
        }

ここではcpClient(ipAddress, port).GetStream().Write()を使用してデータの書き込みを行います。

本日は以上です。