Unityで.plyのGameObjectとしてのインポート(Runtime 可能)

.plyファイルは、Unityオブジェクトあるあるのトライアングルメッシュ情報がないためインポートできない(基本的にmeshlabなどで事前の変換が求められる)ことが常で、ここが苦しいところ。.plyファイル軽くて便利なのにね。

そんな.plyファイルをランタイムでもインポートできるスクリプトを考案しました。

ポイント

MeshTopologyの設定をTriangles からPointsに変更する
通常メッシュを貼る3点の対応はmesh.trianglesに入れるが、Pointsの場合(というかTriangles以外の場合)はmesh.indicesに代入する。 各点に割り当てられた色情報をmesh.colorsに入れる。
該当GameObjectのMaterialは、頂点色情報を反映するShaderから作成する。 またそのShaderは裏側からも見えるように設定する。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text;
using System.IO;
using System.Linq;
using UnityEngine.UI;

public class MakeObject : MonoBehaviour
{
    // 色情報を反映するシェーダ
    public Shader shader;

    string[] GetTextArray(string _text)
    {
      string str = "";
      string[] del = new string[] {"\n"};
      string[] text = _text.Split(del, StringSplitOptions.RemoveEmptyEntries);
      return text;
    }

    int GetVertexLength(string[] text)
    {
      int len = text.Length;
      string[] del = new string[] {" "};
      for (int n=0; n<text.Length; n++) {
        if (text[n].Contains("vertex")) {
          len = Int32.DataType.Parse(str.Split(del, StringSplitOptions.RemoveEmptyEntries)[2]);
          break;
        }
      }
    }

    int GetStartLine(string[] text)
    {
      int start = 0;
      for (int n=0; n<text.Length; n++)
      {
        if (text[n].Contains("end_header"))
        {
          start = n + 1;
          break;
        }
      }
    }

    float[] GetFloatValue(string str)
    {
      string[] del = new string[] {" "};
      string[] arr = str.Split(del, StringSplitOptions.RemoveEmptyEntries);

      float[] val = new float[6];
      for (int i = 0; i < 6; i++) val[i] = float.Parse(arr[i]);
      return val;
    }

    void MakePly(string[] text, int len, int start)
    {
        Vector3[] vertices = new Vector3[len]; // 全頂点情報
        Color[] colors = new Color[len]; // 各頂点の色情報
        int[] indices = new int[len]; // 面を生成するための情報

        // 今回ポイントメッシュにするので、各ポイント毎に必要な点を列挙していけばよい。
        for (int i = 0; i < len; i++) indices[i] = i;

        string[] arr = new string[6];
        float[] val = new float[6];
        Vector3 ave = Vector3.zero; // 全頂点の平均値

        for (int n = 0; n < len; n++)
        {
            str = text[start + n];
            if (str == "") continue;
            val = GetFloatValue(str);

            vertices[n] = new Vector3(val[0], val[1], val[2]);
            // vertices[n] = new Vector3(val[0], val[1], -val[2]); // 右手系のplyファイルの場合

            colors[n] = new Color3(val[3] / 255f, val[4] / 255f, val[5] / 255f);

            // 頂点の重心情報のため
            ave += vertices[n];
        }
        ave /= (float)len;
        for (n = 0; n < len; n++) vertices[n] -= ave; // 頂点の重心を原点に持ってくる

        /** GameObjectを生成 */
        // メインスレッド内で行わないと反映されない

        GameObject obj = new GameObject("Object");
        var filter = obj.AddComponent<MeshFilter>();
        Mesh mesh = new Mesh();

        // 1. まずvertexを設定
        mesh.vertices = vertices;
        // 2. index情報を付加(トライアングルメッシュでいう.triangles。同時にメッシュの形状をPointsに設定する)
        mesh.SetIndices(indices, MeshTopology.Points, 0);
        // 3. 各頂点につき色情報を設定
        mesh.colors = colors;
        // 4. MeshFilterにメッシュ情報を反映する
        filter.sharedMesh = mesh;

        // 5. ここまででは形状しか反映されない。
        //    予め用意した色情報を反映するシェーダーから成るMaterialを付与
        MeshRenderer renderer = obj.AddComponent<MeshRenderer>();
        renderer.sharedMaterial = new Material(shader);

      }
    }
}
  • 頂点色情報を反映するShader

5行目のCull offで裏側からも色が見えるようにしています。ここはお好みで

Shader "Custom/VertexPoint" {
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        Cull off

        CGPROGRAM
        #pragma surface surf Lambert vertex:vert
        #pragma target 3.0

        struct Input {
            float4 vertColor;
        };

        void vert(inout appdata_full v, out Input o){
            UNITY_INITIALIZE_OUTPUT(Input, o);
            o.vertColor = v.color;
        }

        void surf (Input IN, inout SurfaceOutput o) {
            o.Albedo = IN.vertColor.rgb;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

これで完成です!.plyファイルをUnityが読み込めないなんて嘘です。