Пример #1
0
        private GameObject ImportSubObject(GameObject parentObj, DataSet.ObjectData objData, Dictionary <string, Material> mats)
        {
            bool       conv2sided = buildOptions != null && buildOptions.convertToDoubleSided;
            GameObject go         = new GameObject();

            go.name = objData.name;
            int count = 0;

            if (parentObj.transform)
            {
                while (parentObj.transform.Find(go.name))
                {
                    count++;
                    go.name = objData.name + count;
                }
            }
            go.transform.SetParent(parentObj.transform, false);

            if (objData.allFaces.Count == 0)
            {
                throw new InvalidOperationException("Failed to parse vertex and uv data. It might be that the file is corrupt or is not a valid wavefront OBJ file.");

                //Debug.LogWarning("Sub object: " + objData.name + " has no face defined. Creating empty game object.");

                //return go;
            }

            //Debug.Log( "Importing sub object:" + objData.Name );

            // count vertices needed for all the faces and map face indices to new vertices
            Dictionary <string, int> vIdxCount = new Dictionary <string, int>();
            int vcount = 0;

            foreach (DataSet.FaceIndices fi in objData.allFaces)
            {
                string key = DataSet.GetFaceIndicesKey(fi);
                int    idx;
                // avoid duplicates
                if (!vIdxCount.TryGetValue(key, out idx))
                {
                    vIdxCount.Add(key, vcount);
                    vcount++;
                }
            }

            int arraySize = conv2sided ? vcount * 2 : vcount;

            Vector3[] newVertices = new Vector3[arraySize];
            Vector2[] newUVs      = new Vector2[arraySize];
            Vector3[] newNormals  = new Vector3[arraySize];
            Color32[] newColors   = new Color32[arraySize];

            bool hasColors = currDataSet.colorList.Count > 0;

            foreach (DataSet.FaceIndices fi in objData.allFaces)
            {
                string key = DataSet.GetFaceIndicesKey(fi);
                int    k   = vIdxCount[key];
                newVertices[k] = currDataSet.vertList[fi.vertIdx];
                if (conv2sided)
                {
                    newVertices[vcount + k] = newVertices[k];
                }
                if (hasColors)
                {
                    newColors[k] = currDataSet.colorList[fi.vertIdx];
                    if (conv2sided)
                    {
                        newColors[vcount + k] = newColors[k];
                    }
                }
                if (currDataSet.uvList.Count > 0)
                {
                    newUVs[k] = currDataSet.uvList[fi.uvIdx];
                    if (conv2sided)
                    {
                        newUVs[vcount + k] = newUVs[k];
                    }
                }
                if (currDataSet.normalList.Count > 0 && fi.normIdx >= 0)
                {
                    newNormals[k] = currDataSet.normalList[fi.normIdx];
                    if (conv2sided)
                    {
                        newNormals[vcount + k] = -newNormals[k];
                    }
                }
            }

            bool objectHasNormals = (currDataSet.normalList.Count > 0 && objData.hasNormals);
            bool objectHasColors  = (currDataSet.colorList.Count > 0 && objData.hasColors);
            bool objectHasUVs     = (currDataSet.uvList.Count > 0);

            int n = objData.faceGroups[0].faces.Count;

            int numIndices = conv2sided ? n * 2 : n;

            MeshFilter meshFilter = go.AddComponent <MeshFilter>();

            go.AddComponent <MeshRenderer>();

            Mesh mesh = new Mesh();

#if UNITY_2017_3_OR_NEWER
            if (Using32bitIndices())
            {
                if (arraySize > MAX_VERT_COUNT || numIndices > MAX_INDICES_LIMIT_FOR_A_MESH)
                {
                    mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
                }
            }
#endif
            mesh.name             = go.name;
            meshFilter.sharedMesh = mesh;

            mesh.vertices = newVertices;
            if (objectHasUVs)
            {
                mesh.uv = newUVs;
            }
            if (objectHasNormals)
            {
                mesh.normals = newNormals;
            }
            if (objectHasColors)
            {
                mesh.colors32 = newColors;
            }

            Material material;

            string   matName  = (objData.faceGroups[0].materialName != null) ? objData.faceGroups[0].materialName : "default";
            Renderer renderer = go.GetComponent <Renderer>();

            if (mats.ContainsKey(matName))
            {
                material = mats[matName];
                renderer.sharedMaterial = material;
#if UNITY_5_6_OR_NEWER
                RendererExtensions.UpdateGIMaterials(renderer);
#else
                DynamicGI.UpdateMaterials(renderer);
#endif
            }
            else
            {
                if (mats.ContainsKey("default"))
                {
                    material = mats["default"];
                    renderer.sharedMaterial = material;
                    Debug.LogWarning("Material: " + matName + " not found. Using the default material.");
                }
                else
                {
                    Debug.LogError("Material: " + matName + " not found.");
                }
            }

            int[] indices = new int[numIndices];

            for (int s = 0; s < n; s++)
            {
                DataSet.FaceIndices fi = objData.faceGroups[0].faces[s];
                string key             = DataSet.GetFaceIndicesKey(fi);
                indices[s] = vIdxCount[key];
            }
            if (conv2sided)
            {
                for (int s = 0; s < n; s++)
                {
                    indices[s + n] = vcount + indices[s / 3 * 3 + 2 - s % 3];
                }
            }

            mesh.SetTriangles(indices, 0);


            if (!objectHasNormals)
            {
                mesh.RecalculateNormals();
            }
            if (objectHasUVs)
            {
                Solve(mesh);
            }
            if (buildOptions != null && buildOptions.buildColliders)
            {
#if UNITY_2018_3_OR_NEWER
                BuildMeshCollider(go, buildOptions.colliderConvex, buildOptions.colliderTrigger);
#else
                BuildMeshCollider(go, buildOptions.colliderConvex, buildOptions.colliderTrigger, buildOptions.colliderInflate, buildOptions.colliderSkinWidth);
#endif
            }
            return(go);
        }
Пример #2
0
        /// <summary>
        /// Parse the OBJ file to extract geometry data.
        /// </summary>
        /// <param name="objDataText">OBJ file text</param>
        /// <returns>Execution is splitted into steps to not freeze the caller method.</returns>
#pragma warning disable
        protected void ParseGeometryData(string objDataText)
        {
            string[] lines = objDataText.Split("\n".ToCharArray());

            bool isFirstInGroup  = true;
            bool isFaceIndexPlus = true;

            objLoadingProgress.message = "Parsing geometry data...";
            // store separators, used multiple times
            char[] separators = new char[] { ' ', '\t' };

            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i].Trim();

                if (line.Length > 0 && line[0] == '#')
                { // comment line
                    continue;
                }
                string[] p = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
                if (p.Length == 0)
                { // empty line
                    continue;
                }
                string parameters = null;
                if (line.Length > p[0].Length)
                {
                    parameters = line.Substring(p[0].Length + 1).Trim();
                }

                switch (p[0])
                {
                case "o":
                    dataSet.AddObject(parameters);
                    isFirstInGroup = true;
                    break;

                case "g":
                    isFirstInGroup = true;
                    dataSet.AddGroup(parameters);
                    break;

                case "v":
                    dataSet.AddVertex(ConvertVec3(ParseFloat(p[1]), ParseFloat(p[2]), ParseFloat(p[3])));
                    if (p.Length >= 7)
                    {
                        // 7 for "v x y z r g b"
                        // 8 for "v x y z r g b w"
                        // w is the weight required for rational curves and surfaces. It is
                        // not required for non - rational curves and surfaces.If you do not
                        // specify a value for w, the default is 1.0. [http://paulbourke.net/dataformats/obj/]
                        dataSet.AddColor(new Color(ParseFloat(p[4]), ParseFloat(p[5]), ParseFloat(p[6]), 1f));
                    }
                    break;

                case "vt":
                    dataSet.AddUV(new Vector2(ParseFloat(p[1]), ParseFloat(p[2])));
                    break;

                case "vn":
                    dataSet.AddNormal(ConvertVec3(ParseFloat(p[1]), ParseFloat(p[2]), ParseFloat(p[3])));
                    break;

                case "f":
                {
                    int numVerts = p.Length - 1;
                    DataSet.FaceIndices[] face = new DataSet.FaceIndices[numVerts];
                    if (isFirstInGroup)
                    {
                        isFirstInGroup = false;
                        string[] c = p[1].Trim().Split("/".ToCharArray());
                        isFaceIndexPlus = (int.Parse(c[0]) >= 0);
                    }

                    GetFaceIndicesByOneFaceLine(face, p, isFaceIndexPlus);

                    if (numVerts == 3)
                    {
                        dataSet.AddFaceIndices(face[0]);
                        dataSet.AddFaceIndices(face[2]);
                        dataSet.AddFaceIndices(face[1]);
                    }
                    else
                    {
                        // Triangulate the polygon
                        // TODO: Texturing and lighting work better with a triangulation that maximizes triangles areas.
                        // TODO: the following true must be replaced to a proper option (disabled by default) as soon as a proper triangulation method is implemented.
                        Triangulator.Triangulate(dataSet, face);
                        // TODO: Maybe triangulation could be done in ObjectImporter instead.
                    }
                }
                break;

                case "mtllib":
                    if (!string.IsNullOrEmpty(parameters))
                    {
                        mtlLib = parameters;
                    }
                    break;

                case "usemtl":
                    if (!string.IsNullOrEmpty(parameters))
                    {
                        dataSet.AddMaterialName(DataSet.FixMaterialName(parameters));
                    }
                    break;
                }


                // update progress only sometimes
                if (i % 7000 == 0)
                {
                    objLoadingProgress.percentage = LOAD_PHASE_PERC * i / lines.Length;
                    //return;
                }
            }

            objLoadingProgress.percentage = LOAD_PHASE_PERC;

            //dataSet.PrintSummary();
        }
Пример #3
0
        /// <summary>
        /// Build an object once at a time, to be reiterated until false is returned.
        /// </summary>
        /// <param name="parentObj">Game object to which the new objects will be attached</param>
        /// <param name="mats">Materials from the previously loaded library</param>
        /// <returns>Return true until no more objects can be added, then false.</returns>
        protected bool BuildNextObject(GameObject parentObj, Dictionary <string, Material> mats)
        {
            // if all the objects were built stop here
            if (buildStatus.objCount >= currDataSet.objectList.Count)
            {
                return(false);
            }

            // get the next object in the list
            DataSet.ObjectData objData = currDataSet.objectList[buildStatus.objCount];

            if (buildStatus.newObject)
            {
                if (buildStatus.objCount == 0 && objData.name == "default")
                {
                    buildStatus.currObjGameObject = parentObj;
                }
                else
                {
                    buildStatus.currObjGameObject = new GameObject();
                    buildStatus.currObjGameObject.transform.parent = parentObj.transform;
                    buildStatus.currObjGameObject.name             = objData.name;
                    // restore the scale if the parent was rescaled
                    buildStatus.currObjGameObject.transform.localScale = Vector3.one;
                }
                buildStatus.subObjParent = buildStatus.currObjGameObject;

                //if (od.Name != "default") go.name = od.Name;
                //Debug.Log("Object: " + objData.name);
                buildStatus.newObject       = false;
                buildStatus.subObjCount     = 0;
                buildStatus.idxCount        = 0;
                buildStatus.grpIdx          = 0;
                buildStatus.grpFaceIdx      = 0;
                buildStatus.meshPartIdx     = 0;
                buildStatus.totFaceIdxCount = 0;
                buildStatus.numGroups       = Mathf.Max(1, objData.faceGroups.Count);
            }

            bool splitLargeMeshes = true;

#if UNITY_2017_3_OR_NEWER
            // GPU support for 32 bit indices is not guaranteed on all platforms;
            // for example Android devices with Mali-400 GPU do not support them.
            // This check is performed in Using32bitIndices().
            // If nothing is rendered on your device problably Using32bitIndices() must be updated.
            if (Using32bitIndices())
            {
                splitLargeMeshes = false;
            }
#endif
            bool splitGrp = false;

            DataSet.FaceGroupData grp = new DataSet.FaceGroupData();
            grp.name         = objData.faceGroups[buildStatus.grpIdx].name;
            grp.materialName = objData.faceGroups[buildStatus.grpIdx].materialName;


            // data for sub-object
            DataSet.ObjectData subObjData = new DataSet.ObjectData();
            subObjData.hasNormals = objData.hasNormals;
            subObjData.hasColors  = objData.hasColors;

            HashSet <int> vertIdxSet = new HashSet <int>();

            bool conv2sided = buildOptions != null && buildOptions.convertToDoubleSided;

            int maxIdx4mesh = conv2sided ? MAX_INDICES_LIMIT_FOR_A_MESH / 2 : MAX_INDICES_LIMIT_FOR_A_MESH;

            // copy blocks of face indices to each sub-object data
            for (int f = buildStatus.grpFaceIdx; f < objData.faceGroups[buildStatus.grpIdx].faces.Count; f++)
            {
                // if large meshed must be split and
                // if passed the max num of vertices and not at the last iteration
                if (splitLargeMeshes && (vertIdxSet.Count / 3 > MAX_VERT_COUNT / 3 || subObjData.allFaces.Count / 3 > maxIdx4mesh / 3))
                {
                    // split the group across more objects
                    splitGrp = true;
                    buildStatus.grpFaceIdx = f;
                    Debug.LogWarningFormat("Maximum vertex number for a mesh exceeded.\nSplitting object {0} (group {1}, starting from index {2})...", grp.name, buildStatus.grpIdx, f);
                    break;
                }
                DataSet.FaceIndices fi = objData.faceGroups[buildStatus.grpIdx].faces[f];
                subObjData.allFaces.Add(fi);
                grp.faces.Add(fi);
                vertIdxSet.Add(fi.vertIdx);
            }
            if (splitGrp || buildStatus.meshPartIdx > 0)
            {
                buildStatus.meshPartIdx++;
            }
            // create an empty (group) object in case the group has been splitted
            if (buildStatus.meshPartIdx == 1)
            {
                GameObject grpObj = new GameObject();
                grpObj.transform.SetParent(buildStatus.currObjGameObject.transform, false);
                grpObj.name = grp.name;
                buildStatus.subObjParent = grpObj;
            }

            // add a suffix to the group name in case the group has been splitted
            if (buildStatus.meshPartIdx > 0)
            {
                grp.name = buildStatus.subObjParent.name + "_MeshPart" + buildStatus.meshPartIdx;
            }
            subObjData.name = grp.name;

            // add the group to the sub object data
            subObjData.faceGroups.Add(grp);

            // update the start index
            buildStatus.idxCount += subObjData.allFaces.Count;

            if (!splitGrp)
            {
                buildStatus.grpFaceIdx = 0;
                buildStatus.grpIdx++;
            }
            buildStatus.totFaceIdxCount += subObjData.allFaces.Count;
            GameObject subobj = ImportSubObject(buildStatus.subObjParent, subObjData, mats);
            if (subobj == null)
            {
                Debug.LogWarningFormat("Error loading sub object n.{0}.", buildStatus.subObjCount);
            }
            //else Debug.LogFormat( "Imported face indices: {0} to {1}", buildStatus.totFaceIdxCount - sub_od.AllFaces.Count, buildStatus.totFaceIdxCount );

            buildStatus.subObjCount++;

            if (buildStatus.totFaceIdxCount >= objData.allFaces.Count || buildStatus.grpIdx >= objData.faceGroups.Count)
            {
                if (buildStatus.totFaceIdxCount != objData.allFaces.Count)
                {
                    Debug.LogWarningFormat("Imported face indices: {0} of {1}", buildStatus.totFaceIdxCount, objData.allFaces.Count);
                    return(false);
                }
                buildStatus.objCount++;
                buildStatus.newObject = true;
            }
            return(true);
        }
Пример #4
0
 private void GetFaceIndicesByOneFaceLine(DataSet.FaceIndices[] faces, string[] p, bool isFaceIndexPlus)
 {
     if (isFaceIndexPlus)
     {
         for (int j = 1; j < p.Length; j++)
         {
             string[]            c  = p[j].Trim().Split("/".ToCharArray());
             DataSet.FaceIndices fi = new DataSet.FaceIndices();
             // vertex
             int vi = int.Parse(c[0]);
             fi.vertIdx = vi - 1;
             // uv
             if (c.Length > 1 && c[1] != "")
             {
                 int vu = int.Parse(c[1]);
                 fi.uvIdx = vu - 1;
             }
             // normal
             if (c.Length > 2 && c[2] != "")
             {
                 int vn = int.Parse(c[2]);
                 fi.normIdx = vn - 1;
             }
             else
             {
                 fi.normIdx = -1;
             }
             faces[j - 1] = fi;
         }
     }
     else
     { // for minus index
         int vertexCount = dataSet.vertList.Count;
         int uvCount     = dataSet.uvList.Count;
         for (int j = 1; j < p.Length; j++)
         {
             string[]            c  = p[j].Trim().Split("/".ToCharArray());
             DataSet.FaceIndices fi = new DataSet.FaceIndices();
             // vertex
             int vi = int.Parse(c[0]);
             fi.vertIdx = vertexCount + vi;
             // uv
             if (c.Length > 1 && c[1] != "")
             {
                 int vu = int.Parse(c[1]);
                 fi.uvIdx = uvCount + vu;
             }
             // normal
             if (c.Length > 2 && c[2] != "")
             {
                 int vn = int.Parse(c[2]);
                 fi.normIdx = vertexCount + vn;
             }
             else
             {
                 fi.normIdx = -1;
             }
             faces[j - 1] = fi;
         }
     }
 }