C# 将obj模型转换为collada(dae)

记录下,使用C#将obj模型转换为collada格式
完整代码如下

1、这个脚本定义了部分使用到的结构

using System;
using System.Collections.Generic;

namespace ModelProcess
{
    class MathUtils
    {
        public class Vector3
        {
            public float x, y, z;
            public Vector3() { }
            public Vector3(float x, float y, float z)
            {
                this.x = x;
                this.y = y;
                this.z = z;
            }

            public static Vector3 Min
            {
                get
                {
                    return new Vector3(float.MinValue, float.MinValue, float.MinValue);
                }
            }

            public static Vector3 Max
            {
                get
                {
                    return new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
                }
            }

            public static Vector3 operator +(Vector3 a, Vector3 b)
            {
                return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
            }

            public static Vector3 operator -(Vector3 a, Vector3 b)
            {
                return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
            }

            public static Vector3 operator *(Vector3 v, float a)
            {
                return new Vector3(v.x * a, v.y * a, v.z * a);
            }

            public static Vector3 operator /(Vector3 v, float a)
            {
                if (a == 0) throw new Exception("除数不能为0!");
                return new Vector3(v.x / a, v.y / a, v.z / a);
            }

            /// <summary>
            /// 获取向量长度
            /// </summary>
            /// <returns></returns>
            public float Length()
            {
                return (float)Math.Sqrt(x * x + y * y + z * z);
            }

            /// <summary>
            /// 向量单位化
            /// </summary>
            /// <returns></returns>
            public Vector3 Normalize()
            {
                float length = Length();
                if (length == 0) return new Vector3(x, y, z);
                return new Vector3(x / length, y / length, z / length);
            }

            /// <summary>
            /// 获取两个向量中的更小元素,用于获取包围盒
            /// </summary>
            /// <param name="a"></param>
            /// <param name="b"></param>
            /// <returns></returns>
            public static Vector3 GetMaxComponent(Vector3 a, Vector3 b)
            {
                return new Vector3(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z));
            }

            /// <summary>
            /// 获取两个向量中的更大元素,用于获取包围盒
            /// </summary>
            /// <param name="a"></param>
            /// <param name="b"></param>
            /// <returns></returns>
            public static Vector3 GetMinComponent(Vector3 a, Vector3 b)
            {
                return new Vector3(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z));
            }

            /// <summary>
            /// 向量叉乘
            /// </summary>
            /// <param name="a"></param>
            /// <param name="b"></param>
            /// <returns></returns>
            public static Vector3 Cross(Vector3 a, Vector3 b)
            {

                if (Equals(Dot(a, b), 1.0)) return new Vector3(0, 0, 0);

                Vector3 res = new Vector3
                {
                    x = a.y * b.z - a.z * b.y,
                    y = a.z * b.x - a.x * b.z,
                    z = a.x * b.y - a.y * b.x
                };
                return res;
            }

            public static float Dot(Vector3 a, Vector3 b)
            {
                return a.x * b.x + a.y * b.y + a.z * b.z;
            }

            public override string ToString()
            {
                return "x : " + x + "  y : " + y + "  z : " + z;
            }
        }

        public class Vector2
        {
            public float x, y;
            public Vector2() { }
            public Vector2(float x,float y)
            {
                this.x = x;
                this.y = y;
            }
        }

        public class Triangle
        {
            // vertex
            public int a;
            public int b;
            public int c;
            // normal
            public int an;
            public int bn;
            public int cn;
            // uv
            public int au;
            public int bu;
            public int cu;

            public bool hasNormal;
            public bool hasUv;
        }

        public class Group
        {
            public string groupName;
            public string mtlName;
            public List<Triangle> triangles;
        }

        public class ColladaMaterial
        {
            public Vector3 diffuse;
            public Vector3 emissive;
            public string diffuseMap;

            public ColladaMaterial()
            {
                diffuse = new Vector3(1, 1, 1);
                emissive = new Vector3(0, 0, 0);
                diffuseMap = "";
            }
        }
    }
}

2、这个脚本定义了将obj转换为collada的函数,调用方式为ExportColladaFromObj. Export(objPath,outputPath),记得引入命名空间

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using static ModelProcess.MathUtils;

namespace ModelProcess
{
    class ExportColladaFromObj
    {
        /// <summary>
        /// 将obj模型转换为collada格式
        /// </summary>
        /// <param name="objPath">完整的obj路径</param>
        /// <param name="outputPath">完整的collada路径,带后缀.dae</param>
        public static void Export(string objPath, string outputPath)
        {
            List<Vector3> vertices = new List<Vector3>();
            List<Vector3> normals = new List<Vector3>();
            List<Vector2> uvs = new List<Vector2>();
            List<Group> groups = new List<Group>();
            Dictionary<string, ColladaMaterial> materials = new Dictionary<string, ColladaMaterial>();
            GetObjInfo(objPath, ref vertices, ref normals, ref uvs, ref groups);
            GetMtlInfo(objPath, ref materials);

            FileStream fs = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>");
            sw.WriteLine("<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">");


            // asset
            WriteAsset(sw);
            List<string> mapName = WriteImage(sw, materials);
            CopyTexture(mapName, Path.GetDirectoryName(objPath), Path.GetDirectoryName(outputPath));
            WriteEffects(sw, materials);
            WriteMaterials(sw, materials);
            WriteGeometry(sw, vertices, normals, uvs, groups);
            WriteVisualScene(sw, groups, Path.GetFileNameWithoutExtension(objPath));
            WriteScene(sw);

            sw.WriteLine("</COLLADA>");

            sw.Close();
            fs.Close();
        }

        static void CopyTexture(List<string> mapName,string inputPath,string outputPath)
        {
            for(int i = 0; i < mapName.Count; i++)
            {
                Console.WriteLine(mapName[i]);
                File.Copy(Path.Combine(inputPath, mapName[i]), Path.Combine(outputPath, mapName[i]), true);
            }
        }

        static void WriteAsset(StreamWriter sw)
        {
            sw.WriteLine("\t<asset>"); // start asset

            sw.WriteLine("\t\t<contributor>"); // start contributor
            sw.WriteLine("\t\t\t<authoring_tool>export collada from obj</authoring_tool>"); // authoring_tool
            sw.WriteLine("\t\t</contributor>"); // end contributor
            DateTime date = DateTime.Now;
            sw.WriteLine("\t\t<created>" + date.ToString() + "</created>"); // created
            sw.WriteLine("\t\t<modified>" + date.ToString() + "</modified>"); // modified

            sw.WriteLine("\t\t<unit name=\"meter\" meter=\"1\"/>"); //start unit
            sw.WriteLine("\t\t<up_axis>Y_UP</up_axis>"); // up_axis

            sw.WriteLine("\t</asset>");// end asset
        }

        static List<string> WriteImage(StreamWriter sw, Dictionary<string, ColladaMaterial> materials)
        {
            sw.WriteLine("\t<library_images>"); // start library_images
            List<string> mapName = new List<string>();
            foreach (ColladaMaterial mat in materials.Values)
            {
                if (!string.IsNullOrEmpty(mat.diffuseMap))
                {
                    if (!mapName.Contains(mat.diffuseMap))
                    {
                        sw.WriteLine("\t\t<image id=\"" + mat.diffuseMap + "\" name=\"" + mat.diffuseMap + "\">"); // start image
                        sw.WriteLine("\t\t\t<init_from>" + mat.diffuseMap + "</init_from>"); // start image
                        sw.WriteLine("\t\t</image>");
                        mapName.Add(mat.diffuseMap);
                    }
                }
            }
            sw.WriteLine("\t</library_images>"); // end library_images
            return mapName;
        }

        static void WriteEffects(StreamWriter sw, Dictionary<string, ColladaMaterial> materials)
        {
            sw.WriteLine("\t<library_effects>"); // start library_effects
            foreach (string matName in materials.Keys)
            {
                sw.WriteLine("\t\t<effect id=\"" + matName + "-effect\">"); // start effect

                sw.WriteLine("\t\t\t<profile_COMMON>"); // start profile_COMMON

                // map
                if (!string.IsNullOrEmpty(materials[matName].diffuseMap))
                {
                    sw.WriteLine("\t\t\t\t<newparam sid=\"diffuse-surface\">"); // start newparam
                    sw.WriteLine("\t\t\t\t\t<surface type=\"2D\">"); // start surface
                    sw.WriteLine("\t\t\t\t\t\t<init_from>" + materials[matName].diffuseMap + "</init_from>");
                    sw.WriteLine("\t\t\t\t\t</surface>"); // end surface
                    sw.WriteLine("\t\t\t\t</newparam>"); // end newparam

                    sw.WriteLine("\t\t\t\t<newparam sid=\"diffuse-sampler\">"); // start newparam
                    sw.WriteLine("\t\t\t\t\t<sampler2D>"); // start sampler2D
                    sw.WriteLine("\t\t\t\t\t\t<source>diffuse-surface</source>");
                    sw.WriteLine("\t\t\t\t\t</sampler2D>"); // end sampler2D
                    sw.WriteLine("\t\t\t\t</newparam>"); // end newparam
                }

                sw.WriteLine("\t\t\t\t<technique sid=\"" + matName + "\">"); // start technique

                sw.WriteLine("\t\t\t\t\t<lambert>"); // start lambert

                //emission
                sw.WriteLine("\t\t\t\t\t\t<emission>"); // start emission
                sw.WriteLine("\t\t\t\t\t\t\t<color sid=\"emission\">" + materials[matName].emissive.x + " " + materials[matName].emissive.y + " " + materials[matName].emissive.z + " " + 1 + "</color>");
                sw.WriteLine("\t\t\t\t\t\t</emission>"); // end emission

                //diffuse
                sw.WriteLine("\t\t\t\t\t\t<diffuse>"); // start diffuse
                if (string.IsNullOrEmpty(materials[matName].diffuseMap))
                {
                    sw.WriteLine("\t\t\t\t\t\t\t<color sid=\"diffuse\">" + materials[matName].diffuse.x + " " + materials[matName].diffuse.y + " " + materials[matName].diffuse.z + " " + 1 + "</color>");
                }
                else
                {
                    sw.WriteLine("\t\t\t\t\t\t\t<texture texture=\"diffuse-sampler\" texcoord=\"TEXCOORD\" />");
                }
                //myXmlTextWriter.WriteAttributeString("sid", "diffuse");
                sw.WriteLine("\t\t\t\t\t\t</diffuse>"); // end diffuse

                //reflective
                sw.WriteLine("\t\t\t\t\t\t<reflective>"); // start reflective
                sw.WriteLine("\t\t\t\t\t\t\t<color>" + materials[matName].diffuse.x + " " + materials[matName].diffuse.y + " " + materials[matName].diffuse.z + " " + 1 + "</color>");
                //myXmlTextWriter.WriteAttributeString("sid", "reflective");
                sw.WriteLine("\t\t\t\t\t\t</reflective>"); // end reflective

                sw.WriteLine("\t\t\t\t\t</lambert>"); // end lambert
                sw.WriteLine("\t\t\t\t</technique>"); // end technique
                sw.WriteLine("\t\t\t</profile_COMMON>"); // end profile_COMMON
                sw.WriteLine("\t\t</effect>"); // end effect
            }
            sw.WriteLine("\t</library_effects>");  // end library_effects
        }

        static void WriteMaterials(StreamWriter sw, Dictionary<string, ColladaMaterial> materials)
        {
            sw.WriteLine("\t<library_materials>"); // start library_materials
            foreach (string mtlName in materials.Keys)
            {
                sw.WriteLine("\t\t<material id=\"" + mtlName + "\">"); // start material
                sw.WriteLine("\t\t\t<instance_effect url=\"#" + mtlName + "-effect\" />");
                sw.WriteLine("\t\t</material>"); // end material
            }
            sw.WriteLine("\t</library_materials>"); // end library_materials
        }

        static void WriteGeometry(StreamWriter sw, List<Vector3> vertices, List<Vector3> normals, List<Vector2> uvs, List<Group> groups)
        {
            sw.WriteLine("\t<library_geometries>"); //start library_geometries
            int count = 0;
            foreach (Group group in groups)
            {
                sw.WriteLine("\t\t<geometry id=\"" + group.groupName + "\">");//start geometry
                sw.WriteLine("\t\t\t<mesh>");//start mesh

                //vertices
                sw.WriteLine("\t\t\t\t<source id=\"" + group.groupName + "-position\">");//start source
                sw.Write("\t\t\t\t\t<float_array id=\"" + group.groupName + "-position-array\" count=\"" + group.triangles.Count * 9 + "\">");
                for (int i = 0; i < group.triangles.Count; i++)
                {
                    sw.Write(vertices[group.triangles[i].a].x + " ");
                    sw.Write(vertices[group.triangles[i].a].y + " ");
                    sw.Write(vertices[group.triangles[i].a].z + " ");
                    sw.Write(vertices[group.triangles[i].b].x + " ");
                    sw.Write(vertices[group.triangles[i].b].y + " ");
                    sw.Write(vertices[group.triangles[i].b].z + " ");
                    sw.Write(vertices[group.triangles[i].c].x + " ");
                    sw.Write(vertices[group.triangles[i].c].y + " ");
                    sw.Write(vertices[group.triangles[i].c].z);
                    if (i != group.triangles.Count - 1) sw.Write(" ");
                }
                sw.Write("</float_array>\n");
                sw.WriteLine("\t\t\t\t\t<technique_common>");//start technique_common
                sw.WriteLine("\t\t\t\t\t\t<accessor source=\"#" + group.groupName + "-position-array\" count=\"" + group.triangles.Count * 3 + "\" stride=\"3\">");//start accessor
                sw.WriteLine("\t\t\t\t\t\t\t<param name=\"X\" type=\"float\" />");
                sw.WriteLine("\t\t\t\t\t\t\t<param name=\"Y\" type=\"float\" />");
                sw.WriteLine("\t\t\t\t\t\t\t<param name=\"Z\" type=\"float\" />");
                sw.WriteLine("\t\t\t\t\t\t</accessor>");//end accessor
                sw.WriteLine("\t\t\t\t\t</technique_common>");// end technique_common
                sw.WriteLine("\t\t\t\t</source>"); // end source
                sw.WriteLine("\t\t\t\t<vertices id=\"" + group.groupName + "-vertices\">"); //start vertices
                sw.WriteLine("\t\t\t\t\t<input semantic=\"POSITION\" source=\"#" + group.groupName + "-position\" />");
                sw.WriteLine("\t\t\t\t</vertices>"); // end vertices

                //normal
                if (normals.Count > 0)
                {
                    sw.WriteLine("\t\t\t\t<source id=\"" + group.groupName + "-normal\">");//start source
                    sw.Write("\t\t\t\t\t<float_array id=\"" + group.groupName + "-normal-array\" count=\"" + group.triangles.Count * 9 + "\">");
                    for (int i = 0; i < group.triangles.Count; i++)
                    {
                        sw.Write(normals[group.triangles[i].an].x + " ");
                        sw.Write(normals[group.triangles[i].an].y + " ");
                        sw.Write(normals[group.triangles[i].an].z + " ");
                        sw.Write(normals[group.triangles[i].bn].x + " ");
                        sw.Write(normals[group.triangles[i].bn].y + " ");
                        sw.Write(normals[group.triangles[i].bn].z + " ");
                        sw.Write(normals[group.triangles[i].cn].x + " ");
                        sw.Write(normals[group.triangles[i].cn].y + " ");
                        sw.Write(normals[group.triangles[i].cn].z);
                        if (i != group.triangles.Count - 1) sw.Write(" ");
                    }
                    sw.Write("</float_array>\n");
                    sw.WriteLine("\t\t\t\t\t<technique_common>");//start technique_common
                    sw.WriteLine("\t\t\t\t\t\t<accessor source=\"#" + group.groupName + "-normal-array\" count=\"" + group.triangles.Count * 3 + "\" stride=\"3\">");//start accessor
                    sw.WriteLine("\t\t\t\t\t\t\t<param name=\"X\" type=\"float\" />");
                    sw.WriteLine("\t\t\t\t\t\t\t<param name=\"Y\" type=\"float\" />");
                    sw.WriteLine("\t\t\t\t\t\t\t<param name=\"Z\" type=\"float\" />");
                    sw.WriteLine("\t\t\t\t\t\t</accessor>");//end accessor
                    sw.WriteLine("\t\t\t\t\t</technique_common>");// end technique_common
                    sw.WriteLine("\t\t\t\t</source>"); // end source
                }

                //uvs
                if (uvs.Count > 0)
                {
                    sw.WriteLine("\t\t\t\t<source id=\"" + group.groupName + "-texcoord\">");//start source
                    sw.Write("\t\t\t\t\t<float_array id=\"" + group.groupName + "-texcoord-array\" count=\"" + group.triangles.Count * 6 + "\">");
                    for (int i = 0; i < group.triangles.Count; i++)
                    {
                        sw.Write(uvs[group.triangles[i].au].x + " ");
                        sw.Write(uvs[group.triangles[i].au].y + " ");
                        sw.Write(uvs[group.triangles[i].bu].x + " ");
                        sw.Write(uvs[group.triangles[i].bu].y + " ");
                        sw.Write(uvs[group.triangles[i].cu].x + " ");
                        sw.Write(uvs[group.triangles[i].cu].y);
                        if (i != group.triangles.Count - 1) sw.Write(" ");
                    }
                    sw.Write("</float_array>\n");
                    sw.WriteLine("\t\t\t\t\t<technique_common>");//start technique_common
                    sw.WriteLine("\t\t\t\t\t\t<accessor source=\"#" + group.groupName + "-texcoord-array\" count=\"" + group.triangles.Count * 3 + "\" stride=\"2\">");//start accessor
                    sw.WriteLine("\t\t\t\t\t\t\t<param name=\"S\" type=\"float\" />");
                    sw.WriteLine("\t\t\t\t\t\t\t<param name=\"T\" type=\"float\" />");
                    sw.WriteLine("\t\t\t\t\t\t</accessor>");//end accessor
                    sw.WriteLine("\t\t\t\t\t</technique_common>");// end technique_common
                    sw.WriteLine("\t\t\t\t</source>"); // end source
                }

                // triangle
                sw.WriteLine("\t\t\t\t<triangles material=\"" + group.mtlName + "\" count=\"" + group.triangles.Count + "\">"); // start triangles
                sw.WriteLine("\t\t\t\t\t<input semantic=\"VERTEX\" source=\"#" + group.groupName + "-vertices\" offset =\"0\" />");
                if (normals.Count > 0)
                {
                    sw.WriteLine("\t\t\t\t\t<input semantic=\"NORMAL\" source=\"#" + group.groupName + "-normal\" offset =\"0\" />");
                }
                if (uvs.Count > 0)
                {
                    sw.WriteLine("\t\t\t\t\t<input semantic=\"TEXCOORD\" source=\"#" + group.groupName + "-texcoord\" offset =\"0\"  set=\"" + count + "\" />");
                }
                sw.Write("\t\t\t\t\t<p>");
                for (int i = 0; i < group.triangles.Count; i++)
                {
                    sw.Write(i * 3 + " " + (i * 3 + 1) + " " + (i * 3 + 2));
                    if (i != group.triangles.Count - 1)
                    {
                        sw.Write(" ");
                    }
                }
                sw.Write("</p>\n");
                sw.WriteLine("\t\t\t\t</triangles>");//end triangles

                sw.WriteLine("\t\t\t</mesh>"); // end mesh
                sw.WriteLine("\t\t</geometry>"); // end geometry
                count++;
            }
            sw.WriteLine("\t</library_geometries>"); // end library_geometries
        }

        static void WriteVisualScene(StreamWriter sw, List<Group> groups, string objName)
        {
            sw.WriteLine("\t<library_visual_scenes>"); // start library_visual_scenes
            sw.WriteLine("\t\t<visual_scene id=\"Scene\" name=\"" + objName + "\">"); //start visual_scene
            int count = 0;
            foreach (Group group in groups)
            {
                sw.WriteLine("\t\t\t<node name=\"" + group.groupName + "\">");// start node
                sw.WriteLine("\t\t\t\t<matrix>1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>");
                sw.WriteLine("\t\t\t\t<instance_geometry url=\"#" + group.groupName + "\">");// start instance_geometry
                sw.WriteLine("\t\t\t\t\t<bind_material>");// start bind_material
                sw.WriteLine("\t\t\t\t\t\t<technique_common>"); // start  technique_common
                sw.WriteLine("\t\t\t\t\t\t\t<instance_material symbol=\"" + group.mtlName + "\" target=\"#" + group.mtlName + "\" >");//start instance_material
                sw.WriteLine("\t\t\t\t\t\t\t\t<bind_vertex_input semantic=\"TEXCOORD\" input_semantic=\"TEXCOORD\" input_set=\"" + count + "\" />");
                sw.WriteLine("\t\t\t\t\t\t\t</instance_material>");//end instance_material
                sw.WriteLine("\t\t\t\t\t\t</technique_common>"); // end  technique_common
                sw.WriteLine("\t\t\t\t\t</bind_material>");// end bind_material
                sw.WriteLine("\t\t\t\t</instance_geometry>");// end instance_geometry
                sw.WriteLine("\t\t\t</node>");// end node
                count++;
            }
            sw.WriteLine("\t\t</visual_scene>"); //end visual_scene
            sw.WriteLine("\t</library_visual_scenes>"); // end library_visual_scenes
        }

        static void WriteScene(StreamWriter sw)
        {
            sw.WriteLine("\t<scene>");
            sw.WriteLine("\t\t<instance_visual_scene url=\"#Scene\"/>");
            sw.WriteLine("\t</scene>");
        }

        static void GetObjInfo(string objPath, ref List<Vector3> vertices, ref List<Vector3> normals, ref List<Vector2> uvs, ref List<Group> groups)
        {
            FileStream fs = new FileStream(objPath, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(fs);

            Group currenGroup = null;

            string line = "";


            while (!sr.EndOfStream)
            {
                line = Utils.GetStrFields(sr.ReadLine()).Trim();
                if (line.StartsWith("v "))
                {
                    string[] strs = line.Split(' ');
                    if (strs.Length >= 4)
                    {
                        vertices.Add(new Vector3(float.Parse(strs[1]), float.Parse(strs[2]), float.Parse(strs[3])));
                    }
                }
                else if (line.StartsWith("vn "))
                {
                    string[] strs = line.Split(' ');
                    if (strs.Length >= 4)
                    {
                        normals.Add(new Vector3(float.Parse(strs[1]), float.Parse(strs[2]), float.Parse(strs[3])));
                    }
                }
                else if (line.StartsWith("vt "))
                {
                    string[] strs = line.Split(' ');
                    if (strs.Length >= 3)
                    {
                        uvs.Add(new Vector2(float.Parse(strs[1]), float.Parse(strs[2])));
                    }
                }
            }
            fs.Seek(0, SeekOrigin.Begin);

            string currenGroupName = "";
            string currenMtlName = "";
            int avi, ani, aui, bvi, bni, bui, cvi, cni, cui;
            while (!sr.EndOfStream)
            {
                line = Utils.GetStrFields(sr.ReadLine()).Trim();
                if (line.StartsWith("g "))
                {
                    if (line.Length <= 2) currenGroupName = Utils.GetTimestamp();
                    else currenGroupName = Utils.ReplaceAll(line.Substring(2), " ", "_");
                    currenGroup = null;
                    //Console.WriteLine(currenGroupName);
                }
                else if (line.StartsWith("usemtl "))
                {
                    currenMtlName = Utils.ReplaceAll(line.Substring(7), " ", "_");
                    //Console.WriteLine(currenMtlName);
                }
                else if (line.StartsWith("f "))
                {
                    if (currenGroup == null)
                    {
                        currenGroup = new Group
                        {
                            groupName = currenGroupName,
                            mtlName = currenMtlName,
                            triangles = new List<Triangle>()
                        };
                        groups.Add(currenGroup);
                    }

                    string[] strs = line.Split(' ');
                    string[] first = strs[1].Split('/');
                    avi = int.Parse(first[0]);
                    if (avi < 0) avi += vertices.Count;
                    else avi -= 1;
                    ani = 0;
                    aui = 0;
                    if (first.Length >= 2 && !string.IsNullOrEmpty(first[1]))
                    {
                        aui = int.Parse(first[1]);
                    }
                    if (first.Length >= 3 && !string.IsNullOrEmpty(first[2]))
                    {
                        ani = int.Parse(first[2]);
                    }
                    for (int k = 2; k < strs.Length - 1; k++)
                    {
                        string[] second = strs[k].Split('/');
                        string[] third = strs[k + 1].Split('/');
                        bvi = int.Parse(second[0]);
                        if (bvi < 0) bvi += vertices.Count;
                        else bvi -= 1;
                        cvi = int.Parse(third[0]);
                        if (cvi < 0) cvi += vertices.Count;
                        else cvi -= 1;

                        Triangle t = new Triangle()
                        {
                            a = avi,
                            b = bvi,
                            c = cvi
                        };
                        currenGroup.triangles.Add(t);

                        if (aui != 0)
                        {
                            if (aui < 0) aui += uvs.Count;
                            else aui -= 1;
                            bui = int.Parse(second[1]);
                            if (bui < 0) bui += uvs.Count;
                            else bui -= 1;
                            cui = int.Parse(third[1]);
                            if (cui < 0) cui += uvs.Count;
                            else cui -= 1;

                            t.au = aui;
                            t.bu = bui;
                            t.cu = cui;
                            t.hasUv = true;
                        }

                        if (ani != 0)
                        {
                            if (ani < 0) ani += normals.Count;
                            else ani -= 1;
                            bni = int.Parse(second[2]);
                            if (bni < 0) bni += normals.Count;
                            else bni -= 1;
                            cni = int.Parse(third[2]);
                            if (cni < 0) cni += normals.Count;
                            else cni -= 1;

                            t.an = ani;
                            t.bn = bni;
                            t.cn = cni;
                            t.hasNormal = true;
                        }
                    }
                }
            }
            sr.Close();
            fs.Close();
        }

        static void GetMtlInfo(string objPath, ref Dictionary<string, ColladaMaterial> materials)
        {
            string mtlPath = Utils.GetFirstModelPath(Path.GetDirectoryName(objPath), new string[] { ".mtl" });
            if (!string.IsNullOrEmpty(mtlPath))
            {
                FileStream fs = new FileStream(mtlPath, FileMode.Open, FileAccess.Read);
                StreamReader sr = new StreamReader(fs);
                string line = "";
                ColladaMaterial mat = null;
                while (!sr.EndOfStream)
                {
                    line = Utils.GetStrFields(sr.ReadLine()).Trim();
                    if (line.StartsWith("newmtl "))
                    {
                        string name = Utils.ReplaceAll(line.Substring(7), " ", "_");
                        mat = new ColladaMaterial();
                        if (!materials.ContainsKey(name))
                            materials.Add(name, mat);
                    }
                    else if (line.StartsWith("Kd "))
                    {
                        string[] strs = line.Split(' ');
                        if (strs.Length >= 4)
                        {
                            mat.diffuse = new Vector3(float.Parse(strs[1]), float.Parse(strs[2]), float.Parse(strs[3]));
                        }
                    }
                    else if (line.StartsWith("Ke "))
                    {
                        string[] strs = line.Split(' ');
                        if (strs.Length >= 4)
                        {
                            mat.emissive = new Vector3(float.Parse(strs[1]), float.Parse(strs[2]), float.Parse(strs[3]));
                        }
                    }
                    else if (line.StartsWith("map_Kd "))
                    {
                        mat.diffuseMap = line.Substring(7);
                    }
                }

                sr.Close();
                fs.Close();
            }
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将两个 OBJ 模型合并,可以使用以下步骤: 1. 读取两个 OBJ 文件,并将它们解析为顶点、法线和面的列表。 2. 将第二个 OBJ 文件中的所有顶点和面的索引值加上第一个 OBJ 文件中的顶点数,以便将它们添加到第一个 OBJ 文件中。 3. 将第二个 OBJ 文件中的所有顶点、法线和面添加到第一个 OBJ 文件中。 4. 将更新后的 OBJ 文件保存到磁盘上。 下面是一个示例代码,用于将两个 OBJ 文件合并成一个: ```csharp using System; using System.Collections.Generic; using System.IO; class Program { static void Main(string[] args) { // 读取第一个 OBJ 文件 List<Vector3> vertices1 = new List<Vector3>(); List<Vector3> normals1 = new List<Vector3>(); List<int[]> faces1 = new List<int[]>(); using (StreamReader reader = new StreamReader("model1.obj")) { string line; while ((line = reader.ReadLine()) != null) { string[] tokens = line.Split(' '); if (tokens[0] == "v") { float x = float.Parse(tokens[1]); float y = float.Parse(tokens[2]); float z = float.Parse(tokens[3]); vertices1.Add(new Vector3(x, y, z)); } else if (tokens[0] == "vn") { float x = float.Parse(tokens[1]); float y = float.Parse(tokens[2]); float z = float.Parse(tokens[3]); normals1.Add(new Vector3(x, y, z)); } else if (tokens[0] == "f") { int[] face = new int[3]; for (int i = 0; i < 3; i++) { string[] indices = tokens[i + 1].Split('/'); face[i] = int.Parse(indices[0]) - 1; } faces1.Add(face); } } } // 读取第二个 OBJ 文件 List<Vector3> vertices2 = new List<Vector3>(); List<Vector3> normals2 = new List<Vector3>(); List<int[]> faces2 = new List<int[]>(); using (StreamReader reader = new StreamReader("model2.obj")) { string line; while ((line = reader.ReadLine()) != null) { string[] tokens = line.Split(' '); if (tokens[0] == "v") { float x = float.Parse(tokens[1]); float y = float.Parse(tokens[2]); float z = float.Parse(tokens[3]); vertices2.Add(new Vector3(x, y, z)); } else if (tokens[0] == "vn") { float x = float.Parse(tokens[1]); float y = float.Parse(tokens[2]); float z = float.Parse(tokens[3]); normals2.Add(new Vector3(x, y, z)); } else if (tokens[0] == "f") { int[] face = new int[3]; for (int i = 0; i < 3; i++) { string[] indices = tokens[i + 1].Split('/'); face[i] = int.Parse(indices[0]) - 1; } faces2.Add(face); } } } // 合并两个 OBJ 文件 int vertexOffset = vertices1.Count; foreach (Vector3 vertex in vertices2) { vertices1.Add(vertex); } foreach (Vector3 normal in normals2) { normals1.Add(normal); } foreach (int[] face in faces2) { int[] newFace = new int[3]; for (int i = 0; i < 3; i++) { newFace[i] = face[i] + vertexOffset; } faces1.Add(newFace); } // 写入合并后的 OBJ 文件 using (StreamWriter writer = new StreamWriter("merged.obj")) { foreach (Vector3 vertex in vertices1) { writer.WriteLine("v " + vertex.x + " " + vertex.y + " " + vertex.z); } foreach (Vector3 normal in normals1) { writer.WriteLine("vn " + normal.x + " " + normal.y + " " + normal.z); } foreach (int[] face in faces1) { writer.WriteLine("f " + (face[0] + 1) + "//" + (face[0] + 1) + " " + (face[1] + 1) + "//" + (face[1] + 1) + " " + (face[2] + 1) + "//" + (face[2] + 1)); } } } } class Vector3 { public float x, y, z; public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } } ``` 请注意,此示例代码仅处理了顶点和法线,如果您的 OBJ 文件包含纹理坐标信息,则需要相应地修改代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值