bate's blog

調べたこと実装したことなどを取りとめもなく書きます。

Colladaからマテリアルとメッシュ取得

モデルデータに必要なものを取得してみた。
無理やり取得してる感がある。
次はDirectXが読みやすいような形式でバイナリ化

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace WindowsFormsApplication1
{
    /// <summary>
    /// ペア
    /// </summary>
    struct Pair
    {
        public uint Count;
        public uint Stride;
    }

    /// <summary>
    /// マテリアル情報
    /// </summary>
    struct Material
    {
        public string MaterialName;
        public List<string> TextureFileName;
        public Vector4 DiffuseColor;
        public Vector4 AmbientColor;
        public Vector4 EmissionColor;
        public Vector4 SpecularColor;
        public float Shininess;
        public float RefractionIndex;
    }

    /// <summary>
    /// メッシュ
    /// </summary>
    struct Mesh
    {
        public string MeshName;
        public List<Pair> VertexNum;
        public List<Pair> NormalNum;
        public List<Pair> TexCoordNum;
        public List<Vector3> VertexList;
        public List<Vector3> NormalList;
        public List<List<Vector2>> TexCoordList;
        public int MaterialIndex;
    }

    public partial class Form1 : Form
    {
        private Dictionary<string, string> m_ImageDictionary;
        private List<Material> m_MaterialList;
        private List<Mesh> m_MeshList;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Form1()
        {
            m_ImageDictionary = new Dictionary<string, string>();
            m_MaterialList = new List<Material>();
            m_MeshList = new List<Mesh>();

            InitializeComponent();
        }

        /// <summary>
        /// ロードボタンが押された
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.InitialDirectory = @"D:\text\dae";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string dae_filename = openFileDialog1.FileName;
                textBox1.Text = dae_filename;
                parseDAE(dae_filename);
            }
        }

        /// <summary>
        /// DAEを解釈
        /// </summary>
        /// <param name="dae_filename"></param>
        private void parseDAE(string dae_filename)
        {
            var load_dae = XDocument.Load(@dae_filename);
            XNamespace ex = @"http://www.collada.org/2005/11/COLLADASchema";

            // テクスチャ
            m_ImageDictionary = parseTexture(load_dae, ex);

            // マテリアル
            m_MaterialList = parseMaterial(load_dae, ex);

            // メッシュ
            m_MeshList = parseMesh(load_dae, ex);
        }

        /// <summary>
        /// テクスチャの連想配列作成
        /// </summary>
        /// <param name="dae"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private Dictionary<string, string> parseTexture(XDocument dae, XNamespace ex)
        {
            Dictionary<string, string> imageDictionary = new Dictionary<string, string>();
            var query = from n in dae.Descendants(ex + "image") select n;
            foreach (var elem in query)
            {
                imageDictionary.Add(elem.Attribute("id").Value, elem.Value);
            }

            return imageDictionary;
        }

        /// <summary>
        /// マテリアルリスト
        /// </summary>
        /// <param name="dae"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private List<Material> parseMaterial(XDocument dae, XNamespace ex)
        {
            List<Material> materialList = new List<Material>();
            var query_mat = from m in dae.Descendants(ex + "material")
                            select m;
            foreach (var element in query_mat)
            {
                string matName = element.Attribute("id").Value;
                XElement instance_effect = (XElement)element.FirstNode;
                string effectName = instance_effect.Attribute("url").Value.Replace("#", "");
                var query = from n in dae.Descendants(ex + "effect")
                            where (n.Attribute("id").Value.CompareTo(effectName) == 0)
                            select n;
                foreach (var elem in query)
                {
                    Material mat = new Material();
                    mat.TextureFileName = new List<string>();

                    // マテリアル名
                    mat.MaterialName = matName;

                    // テクスチャ名(複数の場合あり)
                    XElement el = elem;
                    var q = from m in el.Descendants(ex + "surface") select m;
                    foreach (var e in q)
                    {
                        string texname = m_ImageDictionary[e.Value];
                        mat.TextureFileName.Add(texname);
                    }

                    // diffuse
                    mat.DiffuseColor = parseColor("diffuse", el, ex);
                    // ambient
                    mat.AmbientColor = parseColor("ambient", el, ex);
                    // emission
                    mat.EmissionColor = parseColor("emission", el, ex);
                    // specular
                    mat.SpecularColor = parseColor("specular", el, ex);
                    // shininess
                    mat.Shininess = parseParam("shiniess", el, ex);
                    // refraction index
                    mat.RefractionIndex = parseParam("index_of_refraction", el, ex);
                    

                    materialList.Add(mat);
                }
            }

            return materialList;
        }

        /// <summary>
        /// library_effects内の色を解析
        /// </summary>
        /// <param name="colorname"></param>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private Vector4 parseColor(string colorname, XElement element, XNamespace ex)
        {
            var query = from m in element.Descendants(ex + colorname) select m;
            Vector4 color = new Vector4();
            foreach (var e in query)
            {
                string col = e.Value;
                string[] array = col.Split(' ');
                if (array.Count() > 1)
                {
                    color.X = float.Parse(array[0]);
                    color.Y = float.Parse(array[1]);
                    color.Z = float.Parse(array[2]);
                    color.W = float.Parse(array[3]);
                }
                else
                {
                    color.X =
                    color.Y =
                    color.Z =
                    color.W = 1.0f;
                }
            }

            return color;
        }

        /// <summary>
        /// float解析
        /// </summary>
        /// <param name="colorname"></param>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private float parseParam(string paramname, XElement element, XNamespace ex)
        {
            var query = from m in element.Descendants(ex + paramname) select m;
            float param = 0.0f;
            foreach (var e in query)
            {
                string p = e.Value;
                if (p.Length > 0)
                {
                    param = float.Parse(p);
                }
            }

            return param;
        }

        /// <summary>
        /// メッシュ解析
        /// </summary>
        /// <param name="dae"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private List<Mesh> parseMesh(XDocument dae, XNamespace ex)
        {
            List<Mesh> meshList = new List<Mesh>();
            var query = from n in dae.Descendants(ex + "geometry") select n;
            foreach (var elem in query)
            {
                Mesh mesh = new Mesh();
                // メッシュ名
                mesh.MeshName = elem.Attribute("id").Value;
                // 頂点数
                mesh.VertexNum = parseElementPairArray(true, "-positions-array", elem, ex);
                // 法線数
                mesh.NormalNum = parseElementPairArray(true, "-normals-array", elem, ex);
                // TexCoord数
                mesh.TexCoordNum = parseElementPairArray(false, "#"+mesh.MeshName + "-map-", elem, ex);
                // 頂点
                mesh.VertexList = parseVector3Element("-positions-array", elem, ex);
                // 法線
                mesh.NormalList = parseVector3Element("-normals-array", elem, ex);
                // TexCoord
                mesh.TexCoordList = parseVector2ElementArray(mesh.TexCoordNum.Count(), false, mesh.MeshName + "-map-", elem, ex);
                // マテリアルインデックス
                mesh.MaterialIndex = parseMeshMaterial(ref m_MaterialList, elem, ex);

                meshList.Add(mesh);
            }

            return meshList;
        }

        /// <summary>
        /// 要素数の解析
        /// </summary>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private Pair parseElementPair( bool end, string endWith, XElement element, XNamespace ex)
        {
            var query = from m in element.Descendants(ex + "accessor")
                        where end?m.Attribute("source").Value.EndsWith(endWith):m.Attribute("source").Value.StartsWith(endWith)
                        select m;
            Pair pair = new Pair();
            foreach (var e in query)
            {
                string countString = e.Attribute("count").Value;
                string strideString = e.Attribute("stride").Value;
                pair.Count = uint.Parse(countString);
                pair.Stride = uint.Parse(strideString);
            }
            return pair;
        }

        /// <summary>
        /// 要素数の解析
        /// </summary>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private List<Pair> parseElementPairArray(bool end, string endWith, XElement element, XNamespace ex)
        {
            var query = from m in element.Descendants(ex + "accessor")
                        where end ? m.Attribute("source").Value.EndsWith(endWith) : m.Attribute("source").Value.StartsWith(endWith)
                        select m;
            List<Pair> pairList = new List<Pair>();
            foreach (var e in query)
            {
                Pair pair = new Pair();
                string countString = e.Attribute("count").Value;
                string strideString = e.Attribute("stride").Value;
                pair.Count = uint.Parse(countString);
                pair.Stride = uint.Parse(strideString);

                pairList.Add(pair);
            }
            return pairList;
        }

        private uint parseElementNum(bool end, string endWith, XElement element, XNamespace ex)
        {
            var query = from m in element.Descendants(ex + "accessor")
                        where end?m.Attribute("source").Value.EndsWith(endWith):m.Attribute("source").Value.StartsWith(endWith)
                        select m;
            uint num = 0;
            foreach (var e in query)
            {
                ++num;
            }
            return num;
        }

        /// <summary>
        /// Vector3データ解析
        /// </summary>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private List<Vector3> parseVector3Element(string vec3element, XElement element, XNamespace ex)
        {
            List<Vector3> vertexList = new List<Vector3>();

            var query = from m in element.Descendants(ex + "float_array")
                        where m.Attribute("id").Value.EndsWith(vec3element)
                        select m;
            Vector3 vec = new Vector3();
            foreach (var e in query)
            {
                string v = e.Value;
                string[] array = v.Split(' ');
                int cnt = 0;
                for (int i = 0; i < array.Count(); ++i)
                {
                    if (cnt == 2)
                    {
                        vec.X = float.Parse(array[i - 2]);
                        vec.Y = float.Parse(array[i - 1]);
                        vec.Z = float.Parse(array[i - 0]);
                        vertexList.Add(vec);
                        cnt = 0;
                    }
                    else
                    {
                        ++cnt;
                    }
                }
            }

            return vertexList;
        }

        /// <summary>
        /// Vector2データ解析
        /// </summary>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private List<Vector2> parseVector2Element(bool end, string vec2element, XElement element, XNamespace ex)
        {
            List<Vector2> vecList = new List<Vector2>();

            var query = from m in element.Descendants(ex + "float_array")
                        where end ? m.Attribute("id").Value.EndsWith(vec2element) : m.Attribute("id").Value.StartsWith(vec2element)
                        select m;
            Vector2 vec = new Vector2();
            foreach (var e in query)
            {
                string v = e.Value;
                string[] array = v.Split(' ');
                int cnt = 0;
                for (int i = 0; i < array.Count(); ++i)
                {
                    if (cnt == 1)
                    {
                        vec.X = float.Parse(array[i - 1]);
                        vec.Y = float.Parse(array[i - 0]);
                        vecList.Add(vec);
                        cnt = 0;
                    }
                    else
                    {
                        ++cnt;
                    }
                }
            }

            return vecList;
        }

        /// <summary>
        /// Vector2配列データ解析
        /// </summary>
        /// <param name="element"></param>
        /// <param name="ex"></param>
        /// <returns></returns>
        private List<List<Vector2>> parseVector2ElementArray(int num, bool end, string vec2element, XElement element, XNamespace ex)
        {
            List<List<Vector2>> listList = new List<List<Vector2>>();
            for (int i = 0; i < num; ++i)
            {
                List<Vector2> list = parseVector2Element(end, vec2element+i.ToString(), element, ex);
                listList.Add(list);
            }

            return listList;
        }

        private int parseMeshMaterial(ref List<Material> matList, XElement element, XNamespace ex)
        {
            var query = from m in element.Descendants(ex + "polylist")
                        select m;
            int ret = 0;
            int num = matList.Count();
            foreach (var e in query)
            {
                string mat = e.Attribute("material").Value;
                for (int i = 0; i < num; ++i)
                {
                    string refMat = matList[i].MaterialName;
                    if (refMat.CompareTo(mat) == 0)
                    {
                        return i;
                    }
                }
            }

            return ret;
        }
    }
}