예제 #1
0
        /// <summary>
        /// Write the contents of a single obj & mtl from a set of models.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="models"></param>
        /// <param name="objContents"></param>
        /// <param name="mtlContents"></param>
        /// <param name="textures"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public static bool Export(string name, IEnumerable <Model> models, out string objContents, out string mtlContents, out List <string> textures, ObjOptions options = null)
        {
            if (models == null || models.Count() < 1)
            {
                objContents = null;
                mtlContents = null;
                textures    = null;
                return(false);
            }

            Dictionary <Material, string> materialMap = null;

            if (options == null)
            {
                options = new ObjOptions();
            }

            mtlContents = WriteMtlContents(models, options, out materialMap, out textures);
            objContents = WriteObjContents(name, models, materialMap, options);

            return(true);
        }
예제 #2
0
        /// <summary>
        /// Write the material file for an OBJ. This function handles making the list of Materials unique & ensuring unique names for each group. Material to named mtl group are stored in materialMap.
        /// </summary>
        /// <param name="models"></param>
        /// <param name="options"></param>
        /// <param name="materialMap"></param>
        /// <param name="textures"></param>
        /// <returns></returns>
        static string WriteMtlContents(IEnumerable <Model> models, ObjOptions options, out Dictionary <Material, string> materialMap, out List <string> textures)
        {
            materialMap = new Dictionary <Material, string>();

            foreach (Model model in models)
            {
                for (int i = 0, c = model.submeshCount; i < c; i++)
                {
                    Material material = model.materials[i];

                    if (material == null)
                    {
                        continue;
                    }

                    if (!materialMap.ContainsKey(material))
                    {
                        string escapedName   = material.name.Replace(" ", "_");
                        string name          = escapedName;
                        int    nameIncrement = 1;

                        while (materialMap.Any(x => x.Value.Equals(name)))
                        {
                            name = string.Format("{0}_{1}", escapedName, nameIncrement++);
                        }

                        materialMap.Add(material, name);
                    }
                }
            }

            StringBuilder sb = new StringBuilder();

            textures = new List <string>();

            foreach (KeyValuePair <Material, string> group in materialMap)
            {
                Material mat = group.Key;

                sb.AppendLine(string.Format("newmtl {0}", group.Value));

                // Texture maps
                if (mat.shader != null)
                {
                    foreach (var texPropertyName in mat.GetTexturePropertyNames())
                    {
                        Texture texture     = mat.GetTexture(texPropertyName);
                        string  textureName = null;
#if UNITY_EDITOR
                        string path = texture != null?UnityEditor.AssetDatabase.GetAssetPath(texture) : null;

                        if (!string.IsNullOrEmpty(path))
                        {
                            if (options.copyTextures)
                            {
                                textures.Add(path);
                            }

                            // remove "Assets/" from start of path
                            path = path.Substring(7, path.Length - 7);

                            textureName = options.copyTextures ? Path.GetFileName(path) : string.Format("{0}/{1}", Application.dataPath, path);
                        }
                        else
#endif
                        if (texture)
                        {
                            textureName = texture.name;
                        }

                        if (!string.IsNullOrEmpty(textureName))
                        {
                            string mtlKey;

                            if (s_TextureMapKeys.TryGetValue(texPropertyName, out mtlKey))
                            {
                                Vector2 offset = mat.GetTextureOffset(texPropertyName);
                                Vector2 scale  = mat.GetTextureScale(texPropertyName);

                                if (options.textureOffsetScale)
                                {
                                    sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0} -o {1} {2} -s {3} {4} {5}", mtlKey, offset.x, offset.y, scale.x, scale.y, textureName));
                                }
                                else
                                {
                                    sb.AppendLine(string.Format("{0} {1}", mtlKey, textureName));
                                }
                            }
                        }
                    }
                }

                if (mat.HasProperty("_Color"))
                {
                    Color color = mat.color;

                    // Diffuse
                    sb.AppendLine(string.Format("Kd {0}", string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", color.r, color.g, color.b)));
                    // Transparency
                    sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "d {0}", color.a));
                }
                else
                {
                    sb.AppendLine("Kd 1.0 1.0 1.0");
                    sb.AppendLine("d 1.0");
                }

                sb.AppendLine();
            }

            return(sb.ToString());
        }
예제 #3
0
        static string WriteObjContents(string name, IEnumerable <Model> models, Dictionary <Material, string> materialMap, ObjOptions options)
        {
            // Empty names in OBJ groups can crash some 3d programs (meshlab)
            if (string.IsNullOrEmpty(name))
            {
                name = "default";
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("# ARFace.ObjExporter");
            sb.AppendLine(string.Format("# {0}", System.DateTime.Now));
            sb.AppendLine();
            sb.AppendLine(string.Format("mtllib ./{0}.mtl", name.Replace(" ", "_")));
            sb.AppendLine(string.Format("o {0}", name));
            sb.AppendLine();

            // obj orders indices 1 indexed
            int positionOffset = 1;
            int normalOffset   = 1;
            int textureOffset  = 1;

            bool  reverseWinding = options.handedness == ObjOptions.Handedness.Right;
            float handedness     = options.handedness == ObjOptions.Handedness.Left ? 1f : -1f;

            foreach (Model model in models)
            {
                int       subMeshCount = model.submeshCount;
                Matrix4x4 matrix       = options.applyTransforms ? model.matrix : Matrix4x4.identity;

                int vertexCount = model.vertexCount;

                Vector3[] positions = model.vertices;
                Color[]   colors    = model.mesh.colors;
                Vector2[] textures0 = model.mesh.uv;
                Vector3[] normals   = model.mesh.normals;
                if (colors != null && colors.Length == 0)
                {
                    colors = null;
                }
                if (textures0 != null && textures0.Length == 0)
                {
                    textures0 = null;
                }
                if (normals != null && normals.Length == 0)
                {
                    normals = null;
                }

                // Can skip this entirely if handedness matches Unity & not applying transforms.
                // matrix is set to identity if not applying transforms.
                if (options.handedness != ObjOptions.Handedness.Left || options.applyTransforms)
                {
                    for (int i = 0; i < vertexCount; i++)
                    {
                        if (positions != null)
                        {
                            positions[i]    = matrix.MultiplyPoint3x4(positions[i]);
                            positions[i].x *= handedness;
                        }

                        if (normals != null)
                        {
                            normals[i]    = matrix.MultiplyVector(normals[i]);
                            normals[i].x *= handedness;
                        }
                    }
                }

                sb.AppendLine(string.Format("g {0}", model.name));

                Dictionary <int, int> positionIndexMap;
                var positionCount = AppendPositions(sb, positions, colors, true, options.vertexColors, out positionIndexMap);

                sb.AppendLine();

                Dictionary <int, int> textureIndexMap;
                var textureCount = AppendArrayVec2(sb, textures0, "vt", true, out textureIndexMap);

                sb.AppendLine();

                Dictionary <int, int> normalIndexMap;
                var normalCount = AppendArrayVec3(sb, normals, "vn", true, out normalIndexMap);

                sb.AppendLine();

                // Material assignment
                for (int submeshIndex = 0; submeshIndex < subMeshCount; submeshIndex++)
                {
                    Submesh submesh = model.submeshes[submeshIndex];

                    string materialName = "";

                    if (materialMap.TryGetValue(model.materials[submeshIndex], out materialName))
                    {
                        sb.AppendLine(string.Format("usemtl {0}", materialName));
                    }
                    else
                    {
                        sb.AppendLine(string.Format("usemtl {0}", "null"));
                    }

                    int[] indexes = submesh.m_Indexes;
                    int   inc     = submesh.m_Topology == MeshTopology.Quads ? 4 : 3;
                    int   inc1    = inc - 1;

                    int o0 = reverseWinding ? inc1 : 0;
                    int o1 = reverseWinding ? inc1 - 1 : 1;
                    int o2 = reverseWinding ? inc1 - 2 : 2;
                    int o3 = reverseWinding ? inc1 - 3 : 3;

                    for (int ff = 0; ff < indexes.Length; ff += inc)
                    {
                        int p0 = positionIndexMap[indexes[ff + o0]] + positionOffset;
                        int p1 = positionIndexMap[indexes[ff + o1]] + positionOffset;
                        int p2 = positionIndexMap[indexes[ff + o2]] + positionOffset;

                        int t0 = -1;
                        int t1 = -1;
                        int t2 = -1;
                        if (textureIndexMap != null)
                        {
                            t0 = textureIndexMap[indexes[ff + o0]] + textureOffset;
                            t1 = textureIndexMap[indexes[ff + o1]] + textureOffset;
                            t2 = textureIndexMap[indexes[ff + o2]] + textureOffset;
                        }

                        int n0 = -1;
                        int n1 = -1;
                        int n2 = -1;
                        if (normalIndexMap != null)
                        {
                            n0 = normalIndexMap[indexes[ff + o0]] + normalOffset;
                            n1 = normalIndexMap[indexes[ff + o1]] + normalOffset;
                            n2 = normalIndexMap[indexes[ff + o2]] + normalOffset;
                        }

                        if (inc == 4)
                        {
                            int p3 = positionIndexMap[indexes[ff + o3]] + positionOffset;
                            int n3 = normalIndexMap[indexes[ff + o3]] + normalOffset;
                            int t3 = textureIndexMap[indexes[ff + o3]] + textureOffset;

                            // sb.AppendLine(string.Format(CultureInfo.InvariantCulture,
                            //     "f {0}/{4}/{8} {1}/{5}/{9} {2}/{6}/{10} {3}/{7}/{11}",
                            //         p0, p1, p2, p3,
                            //         t0, t1, t2, t3,
                            //         n0, n1, n2, n3
                            //         ));
                            throw new NotSupportedException();
                        }
                        else
                        {
                            string format;
                            if (textureIndexMap != null && normalIndexMap != null)
                            {
                                format = "f {0}/{3}/{6} {1}/{4}/{7} {2}/{5}/{8}";
                            }
                            else if (textureIndexMap == null && normalIndexMap != null)
                            {
                                format = "f {0}//{6} {1}//{7} {2}//{8}";
                            }
                            else if (textureIndexMap != null && normalIndexMap == null)
                            {
                                format = "f {0}/{3} {1}/{4} {2}/{5}";
                            }
                            else
                            {
                                format = "f {0} {1} {2}";
                            }

                            sb.AppendLine(string.Format(CultureInfo.InvariantCulture,
                                                        format,
                                                        p0, p1, p2,
                                                        t0, t1, t2,
                                                        n0, n1, n2
                                                        ));
                        }
                    }

                    sb.AppendLine();
                }

                positionOffset += positionCount;
                normalOffset   += normalCount;
                textureOffset  += textureCount;
            }

            return(sb.ToString());
        }