private IEnumerator GetWWWFiles(string objFilePath, bool useMtl, Material standardMaterial, Material transparentMaterial, ObjData objData) { var www = new WWW(objFilePath); while (!www.isDone) { objData.SetProgress(www.progress * (useMtl? .5f : 1.0f)); yield return(null); } if (www.error != null) { Debug.Log(www.error); objData.SetDone(); yield break; } string objFile = www.text; string filePath = ""; Dictionary <string, Material> materials = null; if (useMtl) { string mtlFileName = GetMTLFileName(ref objFilePath, ref objFile, ref filePath); if (mtlFileName != "") { // Read in mtl file www = new WWW(filePath + mtlFileName); yield return(www); if (www.error != null) { Debug.Log(www.error); objData.SetDone(); yield break; } if (www.text != "") { // Get textures and parse MTL file var linesRef = new LinesRef(); var textures = new Dictionary <string, Texture2D>(); yield return(StartCoroutine(GetTexturesAsync(www.text, linesRef, filePath, textures, objData))); materials = ParseMTL(linesRef.lines, standardMaterial, transparentMaterial, filePath, textures); if (materials == null) { useMtl = false; } } } else { useMtl = false; } } CreateObjects(ref objFile, useMtl, materials, standardMaterial, objData); }
private IEnumerator GetTexturesAsync(string mtlFile, LinesRef linesRef, string filePath, Dictionary <string, Texture2D> textures, ObjData objData) { Texture2D diffuseTexture = null; string[] lines = SplitLines(ref mtlFile); int numberOfTextures = 0; // See how many textures there are (to use for progress) for (int i = 0; i < lines.Length; i++) { var line = lines[i]; CleanLine(ref line); lines[i] = line; if (line.Length < 7 || line[0] == '#') { continue; } if (line.StartsWith("map_Kd") && filePath != "") { numberOfTextures++; } } float progress = .5f; for (int i = 0; i < lines.Length; i++) { if (lines[i].Length < 7 || lines[i][0] == '#') { continue; } // Get diffuse texture if (lines[i].StartsWith("map_Kd") && filePath != "") { var lineInfo = lines[i].Split(' '); if (lineInfo.Length > 1) { var textureFilePath = filePath + lineInfo[1]; var www = new WWW(textureFilePath); while (!www.isDone) { objData.SetProgress(progress + (www.progress / numberOfTextures) * .5f); yield return(null); } if (www.error != null) { Debug.Log(www.error); objData.SetDone(); yield break; } progress += (1.0f / numberOfTextures) * .5f; diffuseTexture = new Texture2D(4, 4); www.LoadImageIntoTexture(diffuseTexture); textures[lineInfo[1]] = diffuseTexture; } continue; } } linesRef.lines = lines; }
private GameObject[] CreateObjects(ref string objFile, bool useMtl, Dictionary <string, Material> materials, Material standardMaterial, ObjData objData) { // Set up obj file string[] fileLines = SplitLines(ref objFile); int totalVertices = 0; int totalUVs = 0; int totalNormals = 0; var foundF = false; var foundGroupStart = false; int firstFCount = 0; int groupCount = 0; var originalGroupIndices = new List <int>(); int faceCount = 0; maxPoints = Mathf.Clamp(maxPoints, 0, 65534); // Find number of groups in file; also find total number of vertices, normals, and uvs for (int i = 0; i < fileLines.Length; i++) { if (fileLines[i].Length < 2) { continue; } var lineStart = fileLines[i].Substring(0, 2).ToLower(); if (lineStart == "f ") { if (!foundF) { firstFCount = faceCount; } faceCount++; if (foundGroupStart && !foundF) { groupCount++; originalGroupIndices.Add(firstFCount); foundGroupStart = false; } foundF = true; } else if (lineStart == "o " || lineStart == "g " || lineStart == "us") { foundGroupStart = true; foundF = false; } else if (lineStart == "v ") { totalVertices++; } else if (lineStart == "vt") { totalUVs++; } else if (useSuppliedNormals && lineStart == "vn") { totalNormals++; } } if (totalVertices == 0) { Debug.LogError("No vertices found in file"); return(null); } if (faceCount == 0) { Debug.LogError("No face data found in file"); return(null); } originalGroupIndices.Add(-1); int verticesCount = 0; int uvsCount = 0; int normalsCount = 0; var objVertices = new Vector3[totalVertices]; var objUVs = new Vector2[totalUVs]; var objNormals = new Vector3[totalNormals]; var triData = new List <string>(); var quadWarning = false; var polyWarning = false; var objectNames = new string[groupCount]; var materialNames = new string[groupCount]; int index = 0; var lineInfo = new string[0]; var groupIndices = new int[groupCount + 1]; int numberOfGroupsUsed = 0; faceCount = 0; groupCount = 0; int mtlCount = 0; int objectNamesCount = 0; try { while (index < fileLines.Length) { var line = fileLines[index++].TrimEnd(); // Skip over comments and short lines if (line.Length < 3 || line[0] == '#') { continue; } // Remove excess whitespace CleanLine(ref line); // Skip over short lines (again, in the off chance the above line made this line too short) if (line.Length < 3) { continue; } // If line ends with "\" then combine with the next line, assuming there is one (should be, but just in case) while (line[line.Length - 1] == '\\' && index < fileLines.Length) { line = line.Substring(0, line.Length - 1) + " " + fileLines[index++].TrimEnd(); } var stringStart = line.Substring(0, 2).ToLower(); // Get material name from usemtl line, plus object name if it doesn't have one from g or o lines if (stringStart == "us") { if (useMtl && line.StartsWith("usemtl") && mtlCount++ == 0) { lineInfo = line.Split(' '); if (lineInfo.Length > 1) { materialNames[groupCount] = lineInfo[1]; if (objectNamesCount++ == 0) { objectNames[groupCount] = lineInfo[1]; } } } } // Get object name else if ((stringStart == "g " || stringStart == "o ") && objectNamesCount++ == 0) { objectNames[groupCount] = line.Substring(2, line.Length - 2); } // Read vertices else if (stringStart == "v ") { lineInfo = line.Split(' '); if (lineInfo.Length != 4) { throw new System.Exception("Incorrect number of points while trying to read vertices:\n" + line + "\n"); } else { // Invert x value so it works properly in Unity (left-handed) objVertices[verticesCount++] = new Vector3(-float.Parse(lineInfo[1]), float.Parse(lineInfo[2]), float.Parse(lineInfo[3])); } } // Read UVs else if (stringStart == "vt") { lineInfo = line.Split(' '); if (lineInfo.Length > 4 || lineInfo.Length < 3) { throw new System.Exception("Incorrect number of points while trying to read UV data:\n" + line + "\n"); } else { objUVs[uvsCount++] = new Vector2(float.Parse(lineInfo[1]), float.Parse(lineInfo[2])); } } // Read normals else if (useSuppliedNormals && stringStart == "vn") { lineInfo = line.Split(' '); if (lineInfo.Length != 4) { throw new System.Exception("Incorrect number of points while trying to read normals:\n" + line + "\n"); } else { // Invert x value so it works properly in Unity objNormals[normalsCount++] = new Vector3(-float.Parse(lineInfo[1]), float.Parse(lineInfo[2]), float.Parse(lineInfo[3])); } } // Read triangle face info else if (stringStart == "f ") { lineInfo = line.Split(' '); if (lineInfo.Length >= 4 && lineInfo.Length <= 5) { // If data is relative offset, dissect it and replace it with calculated absolute data if (lineInfo[1].Substring(0, 1) == "-") { for (int i = 1; i < lineInfo.Length; i++) { var lineInfoParts = lineInfo[i].Split('/'); lineInfoParts[0] = (verticesCount - -int.Parse(lineInfoParts[0]) + 1).ToString(); if (lineInfoParts.Length > 1) { if (lineInfoParts[1] != "") { lineInfoParts[1] = (uvsCount - -int.Parse(lineInfoParts[1]) + 1).ToString(); } if (lineInfoParts.Length == 3) { lineInfoParts[2] = (normalsCount - -int.Parse(lineInfoParts[2]) + 1).ToString(); } } lineInfo[i] = System.String.Join("/", lineInfoParts); } } // Triangle for (int i = 1; i < 4; i++) { triData.Add(lineInfo[i]); } // Quad -- split by adding another triangle if (lineInfo.Length == 5) { quadWarning = true; triData.Add(lineInfo[1]); triData.Add(lineInfo[3]); triData.Add(lineInfo[4]); } } // Line describes polygon containing more than 4 or fewer than 3 points, which are not supported else { polyWarning = true; } // Store index for face group start locations if (++faceCount == originalGroupIndices[groupCount + 1]) { groupIndices[++groupCount] = triData.Count; mtlCount = 0; objectNamesCount = 0; } } } } catch (System.Exception err) { Debug.Log(err.Message); return(null); } if (combineMultipleGroups && !useSubmeshesWhenCombining) { numberOfGroupsUsed = 1; groupIndices[1] = triData.Count; } else { groupIndices[groupCount + 1] = triData.Count; numberOfGroupsUsed = groupIndices.Length - 1; } // Parse vert/uv/normal index data from triangle face lines var triVerts = new int[triData.Count]; var triUVs = new int[triData.Count]; var triNorms = new int[triData.Count]; var lengthCount = 3; for (int i = 0; i < triData.Count; i++) { string triString = triData[i]; lineInfo = triString.Split('/'); triVerts[i] = int.Parse(lineInfo[0]) - 1; if (lineInfo.Length > 1) { if (lineInfo[1] != "") { triUVs[i] = int.Parse(lineInfo[1]) - 1; } if (lineInfo.Length == lengthCount && useSuppliedNormals) { triNorms[i] = int.Parse(lineInfo[2]) - 1; } } } var objVertList = new List <Vector3>(objVertices); if (totalUVs > 0) { SplitOnUVs(triData, triVerts, triUVs, objVertList, objUVs, objVertices, ref verticesCount); } // Warnings if (quadWarning && !suppressWarnings) { Debug.LogWarning("At least one object uses quads...automatic triangle conversion is being used, which may not produce best results"); } if (polyWarning && !suppressWarnings) { Debug.LogWarning("Polygons which are not quads or triangles have been skipped"); } if (totalUVs == 0 && !suppressWarnings) { Debug.LogWarning("At least one object does not seem to be UV mapped...any textures used will appear as a solid color"); } if (totalNormals == 0 && !suppressWarnings) { Debug.LogWarning("No normal data found for at least one object...automatically computing normals instead"); } // Errors if (totalVertices == 0 && triData.Count == 0) { Debug.LogError("No objects seem to be present...possibly the .obj file is damaged or could not be read"); return(null); } else if (totalVertices == 0) { Debug.LogError("The .obj file does not contain any vertices"); return(null); } else if (triData.Count == 0) { Debug.LogError("The .obj file does not contain any polygons"); return(null); } // Set up GameObject array...only 1 object if combining groups var gameObjects = new GameObject[combineMultipleGroups? 1 : numberOfGroupsUsed]; for (int i = 0; i < gameObjects.Length; i++) { gameObjects[i] = new GameObject(objectNames[i], typeof(MeshFilter), typeof(MeshRenderer)); } // -------------------------------- // Create meshes from the .obj data GameObject go = null; Mesh mesh = null; Vector3[] newVertices = null; Vector2[] newUVs = null; Vector3[] newNormals = null; int[] newTriangles = null; var useSubmesh = (combineMultipleGroups && useSubmeshesWhenCombining && numberOfGroupsUsed > 1)? true : false; Material[] newMaterials = null; if (useSubmesh) { newMaterials = new Material[numberOfGroupsUsed]; } Material lastMaterial = null; for (int i = 0; i < numberOfGroupsUsed; i++) { if (!useSubmesh || (useSubmesh && i == 0)) { go = gameObjects[i]; mesh = new Mesh(); // Find the number of unique vertices used by this group, also used to map original vertices into 0..thisVertices.Count range var vertHash = new Dictionary <int, int>(); var thisVertices = new List <Vector3>(); int counter = 0; int vertHashValue = 0; int triStart = groupIndices[i]; int triEnd = groupIndices[i + 1]; if (useSubmesh) { triStart = groupIndices[0]; triEnd = groupIndices[numberOfGroupsUsed]; } for (int j = triStart; j < triEnd; j++) { if (!vertHash.TryGetValue(triVerts[j], out vertHashValue)) { vertHash[triVerts[j]] = counter++; thisVertices.Add(objVertList[triVerts[j]]); } } if (thisVertices.Count > maxPoints) { Debug.LogError("The number of vertices in this object exceeds the maximum allowable limit of " + maxPoints); return(null); } newVertices = new Vector3[thisVertices.Count]; newUVs = new Vector2[thisVertices.Count]; newNormals = new Vector3[thisVertices.Count]; newTriangles = new int[triEnd - triStart]; // Copy .obj mesh data for vertices and triangles to arrays of the correct size if (scaleFactor == Vector3.one && objRotation == Vector3.zero && objPosition == Vector3.zero) { for (int j = 0; j < thisVertices.Count; j++) { newVertices[j] = thisVertices[j]; } } else { transform.eulerAngles = objRotation; transform.position = objPosition; transform.localScale = scaleFactor; var thisMatrix = transform.localToWorldMatrix; for (int j = 0; j < thisVertices.Count; j++) { newVertices[j] = thisMatrix.MultiplyPoint3x4(thisVertices[j]); } transform.position = Vector3.zero; transform.rotation = Quaternion.identity; transform.localScale = Vector3.one; } // Arrange UVs and normals so they match up with vertices if (uvsCount > 0 && normalsCount > 0 && useSuppliedNormals) { for (int j = triStart; j < triEnd; j++) { newUVs[vertHash[triVerts[j]]] = objUVs[triUVs[j]]; // Needs to be normalized or lighting is whacked (especially with specular), and some apps don't output normalized normals newNormals[vertHash[triVerts[j]]] = objNormals[triNorms[j]].normalized; } } else { // Arrange UVs so they match up with vertices if (uvsCount > 0) { for (int j = triStart; j < triEnd; j++) { newUVs[vertHash[triVerts[j]]] = objUVs[triUVs[j]]; } } // Arrange normals so they match up with vertices if (normalsCount > 0 && useSuppliedNormals) { for (int j = triStart; j < triEnd; j++) { newNormals[vertHash[triVerts[j]]] = objNormals[triNorms[j]]; } } } // Since we flipped the normals, swap triangle points 2 & 3 counter = 0; for (int j = triStart; j < triEnd; j += 3) { newTriangles[counter] = vertHash[triVerts[j]]; newTriangles[counter + 1] = vertHash[triVerts[j + 2]]; newTriangles[counter + 2] = vertHash[triVerts[j + 1]]; counter += 3; } mesh.vertices = newVertices; mesh.uv = newUVs; if (useSuppliedNormals) { mesh.normals = newNormals; } if (useSubmesh) { mesh.subMeshCount = numberOfGroupsUsed; } } if (useSubmesh) { int thisLength = groupIndices[i + 1] - groupIndices[i]; var thisTriangles = new int[thisLength]; System.Array.Copy(newTriangles, groupIndices[i], thisTriangles, 0, thisLength); mesh.SetTriangles(thisTriangles, i); if (materialNames[i] != null) { if (useMtl && materials.ContainsKey(materialNames[i])) { newMaterials[i] = materials[materialNames[i]]; lastMaterial = materials[materialNames[i]]; } } else if (lastMaterial != null) { newMaterials[i] = lastMaterial; } } else { mesh.triangles = newTriangles; } // Stuff that's done for each object, or at the end if using submeshes if (!useSubmesh || (useSubmesh && i == numberOfGroupsUsed - 1)) { if (normalsCount == 0 || !useSuppliedNormals) { mesh.RecalculateNormals(); if (computeTangents) { newNormals = mesh.normals; } } if (computeTangents) { var newTangents = new Vector4[newVertices.Length]; CalculateTangents(newVertices, newNormals, newUVs, newTriangles, newTangents); mesh.tangents = newTangents; } mesh.RecalculateBounds(); go.GetComponent <MeshFilter>().mesh = mesh; if (!useSubmesh) { if (useMtl && materialNames[i] != null && materials.ContainsKey(materialNames[i])) { go.GetComponent <Renderer>().material = materials[materialNames[i]]; } else { go.GetComponent <Renderer>().material = standardMaterial; } } else { for (int j = 0; j < newMaterials.Length; j++) { if (newMaterials[j] == null) { newMaterials[j] = standardMaterial; } } go.GetComponent <Renderer>().materials = newMaterials; } } } if (objData != null) { objData.SetDone(); objData.gameObjects = gameObjects; return(null); } return(gameObjects); }
private IEnumerator GetWWWFiles(string objFilePath, bool useMtl, Material standardMaterial, Material transparentMaterial, ObjData objData) { var www = new WWW(objFilePath); while (!www.isDone) { objData.SetProgress (www.progress * (useMtl? .5f : 1.0f)); if (objData.cancel) { yield break; } yield return null; } if (www.error != null) { Debug.LogError ("Error loading " + objFilePath + ": " + www.error); objData.SetDone(); yield break; } string objFile = www.text; string filePath = ""; Dictionary<string, Material> materials = null; if (useMtl) { string mtlFileName = GetMTLFileName (ref objFilePath, ref objFile, ref filePath); if (mtlFileName != "") { // Read in mtl file www = new WWW(filePath + mtlFileName); while (!www.isDone) { if (objData.cancel) { yield break; } yield return null; } if (www.error != null) { if (!useMTLFallback) { Debug.LogError ("Error loading " + (filePath + mtlFileName) + ": " + www.error); objData.SetDone(); yield break; } else { useMtl = false; } } if (useMtl && www.text != "") { // Get textures and parse MTL file var linesRef = new LinesRef(); var textures = new Dictionary<string, Texture2D>(); var loadError = new BoolRef(false); yield return StartCoroutine (GetTexturesAsync (www.text, linesRef, filePath, textures, objData, loadError)); if (loadError.b == true) { yield break; } materials = ParseMTL (linesRef.lines, standardMaterial, transparentMaterial, filePath, textures); if (materials == null) { useMtl = false; } } } else { useMtl = false; } } CreateObjects (ref objFile, useMtl, materials, standardMaterial, objData, Path.GetFileNameWithoutExtension (objFilePath)); }
private IEnumerator GetTexturesAsync(string mtlFile, LinesRef linesRef, string filePath, Dictionary<string, Texture2D> textures, ObjData objData, BoolRef loadError) { Texture2D diffuseTexture = null; string[] lines = mtlFile.Split ('\n'); int numberOfTextures = 0; // See how many textures there are (to use for progress) for (int i = 0; i < lines.Length; i++) { var line = lines[i]; CleanLine (ref line); lines[i] = line; if (line.Length < 7 || line[0] == '#') { continue; } if (IsTextureLine (line) && filePath != "") { numberOfTextures++; } } float progress = .5f; for (int i = 0; i < lines.Length; i++) { if (lines[i].Length < 7 || lines[i][0] == '#') { continue; } // Get diffuse/bump texture if (IsTextureLine (lines[i]) && filePath != "") { var textureFilePath = GetFileName (lines[i], GetToken (lines[i])); if (textureFilePath != "") { var completeFilePath = filePath + textureFilePath; var www = new WWW(completeFilePath); while (!www.isDone) { objData.SetProgress (progress + (www.progress / numberOfTextures) * .5f); if (objData.cancel) { loadError.b = true; yield break; } yield return null; } if (www.error != null) { Debug.LogError ("Error loading " + completeFilePath + ": " + www.error); loadError.b = true; objData.SetDone(); yield break; } progress += (1.0f / numberOfTextures) * .5f; diffuseTexture = new Texture2D (4, 4); www.LoadImageIntoTexture (diffuseTexture); if (lines[i].StartsWith ("map_bump") || lines[i].StartsWith ("bump")) { ConvertToNormalmap (diffuseTexture); } textures[textureFilePath] = diffuseTexture; } } } linesRef.lines = lines; }
private GameObject[] CreateObjects(ref string objFile, bool useMtl, Dictionary<string, Material> materials, Material standardMaterial, ObjData objData, string fileName) { // Set up obj file string[] fileLines = objFile.Split ('\n'); int totalVertices = 0; int totalUVs = 0; int totalNormals = 0; var foundF = false; var foundGroupStart = false; int firstFCount = 0; int groupCount = 0; var originalGroupIndices = new List<int>(); int faceCount = 0; maxPoints = Mathf.Clamp (maxPoints, 0, 65534); // Find number of groups in file; also find total number of vertices, normals, and uvs for (int i = 0; i < fileLines.Length; i++) { if (fileLines[i].Length < 2) continue; char char1 = System.Char.ToLower(fileLines[i][0]); char char2 = System.Char.ToLower(fileLines[i][1]); if (char1 == 'f' && char2 == ' ') { if (!foundF) { firstFCount = faceCount; } faceCount++; if (foundGroupStart && !foundF) { groupCount++; originalGroupIndices.Add (firstFCount); foundGroupStart = false; } foundF = true; } else if ((char1 == 'o' && char2 == ' ') || (char1 == 'g' && char2 == ' ') || (char1 == 'u' && char2 == 's')) { // "us" == "usemtl" foundGroupStart = true; foundF = false; } else if (char1 == 'v' && char2 == ' ') { totalVertices++; } else if (char1 == 'v' && char2 == 't') { totalUVs++; } else if (useSuppliedNormals && char1 == 'v' && char2 == 'n') { totalNormals++; } } if (groupCount == 0) { originalGroupIndices.Add (firstFCount); groupCount = 1; } if (totalVertices == 0) { Debug.LogError ("No vertices found in file"); return null; } if (faceCount == 0) { Debug.LogError ("No face data found in file"); return null; } originalGroupIndices.Add (-1); int verticesCount = 0; int uvsCount = 0; int normalsCount = 0; var objVertices = new Vector3[totalVertices]; var objUVs = new Vector2[totalUVs]; var objNormals = new Vector3[totalNormals]; var triData = new List<string>(); var quadWarning = false; var polyWarning = false; var objectNames = new string[groupCount]; var materialNames = new string[groupCount]; int index = 0; var lineInfo = new string[0]; var groupIndices = new int[groupCount+1]; int numberOfGroupsUsed = 0; faceCount = 0; groupCount = 0; int mtlCount = 0; int objectNamesCount = 0; try { while (index < fileLines.Length) { var line = fileLines[index++]; // Skip over comments and short lines if (line.Length < 3 || line[0] == '#') continue; // Remove excess whitespace CleanLine (ref line); // Skip over short lines (again, in the off chance the above line made this line too short) if (line.Length < 3) continue; // If line ends with "\" then combine with the next line, assuming there is one (should be, but just in case) while (line[line.Length-1] == '\\' && index < fileLines.Length) { line = line.Substring (0, line.Length-1) + " " + fileLines[index++].TrimEnd(); CleanLine (ref line); } char char1 = System.Char.ToLower(line[0]); char char2 = System.Char.ToLower(line[1]); // Get material name from usemtl line, plus object name if it doesn't have one from g or o lines if (char1 == 'u' && char2 == 's') { if (useMtl && line.StartsWith ("usemtl") && mtlCount++ == 0) { lineInfo = line.Split (' '); if (lineInfo.Length > 1) { materialNames[groupCount] = lineInfo[1]; if (objectNamesCount++ == 0) { if (useFileNameAsObjectName && fileName != "") { objectNames[groupCount] = fileName; } else { objectNames[groupCount] = lineInfo[1]; } } } } } // Get object name else if (((char1 == 'o' && char2 == ' ') || (char1 == 'g' && char2 == ' ')) && objectNamesCount++ == 0) { if (useFileNameAsObjectName && fileName != "") { objectNames[groupCount] = fileName; } else { objectNames[groupCount] = line.Substring (2, line.Length-2); } } // Read vertices else if (char1 == 'v' && char2 == ' ') { lineInfo = line.Split (' '); if (lineInfo.Length != 4) { throw new System.Exception ("Incorrect number of points while trying to read vertices:\n" + line + "\n"); } else { // Invert x value so it works properly in Unity (left-handed) objVertices[verticesCount++] = new Vector3(-float.Parse (lineInfo[1], CultureInfo.InvariantCulture), float.Parse (lineInfo[2], CultureInfo.InvariantCulture), float.Parse (lineInfo[3], CultureInfo.InvariantCulture)); } } // Read UVs else if (char1 == 'v' && char2 == 't') { lineInfo = line.Split (' '); if (lineInfo.Length > 4 || lineInfo.Length < 3) { throw new System.Exception ("Incorrect number of points while trying to read UV data:\n" + line + "\n"); } else { objUVs[uvsCount++] = new Vector2(float.Parse (lineInfo[1], CultureInfo.InvariantCulture), float.Parse (lineInfo[2], CultureInfo.InvariantCulture)); } } // Read normals else if (useSuppliedNormals && char1 == 'v' && char2 == 'n') { lineInfo = line.Split (' '); if (lineInfo.Length != 4) { throw new System.Exception ("Incorrect number of points while trying to read normals:\n" + line + "\n"); } else { // Invert x value so it works properly in Unity objNormals[normalsCount++] = new Vector3(-float.Parse (lineInfo[1], CultureInfo.InvariantCulture), float.Parse (lineInfo[2], CultureInfo.InvariantCulture), float.Parse (lineInfo[3], CultureInfo.InvariantCulture)); } } // Read triangle face info else if (char1 == 'f' && char2 == ' ') { lineInfo = line.Split (' '); if (lineInfo.Length >= 4 && lineInfo.Length <= 5) { // If data is relative offset, dissect it and replace it with calculated absolute data if (lineInfo[1].Substring (0, 1) == "-") { for (int i = 1; i < lineInfo.Length; i++) { var lineInfoParts = lineInfo[i].Split ('/'); lineInfoParts[0] = (verticesCount - -int.Parse (lineInfoParts[0])+1).ToString(); if (lineInfoParts.Length > 1) { if (lineInfoParts[1] != "") { lineInfoParts[1] = (uvsCount - -int.Parse (lineInfoParts[1])+1).ToString(); } if (lineInfoParts.Length == 3) { lineInfoParts[2] = (normalsCount - -int.Parse (lineInfoParts[2])+1).ToString(); } } lineInfo[i] = System.String.Join ("/", lineInfoParts); } } // Triangle for (int i = 1; i < 4; i++) { triData.Add (lineInfo[i]); } // Quad -- split by adding another triangle if (lineInfo.Length == 5) { quadWarning = true; triData.Add (lineInfo[1]); triData.Add (lineInfo[3]); triData.Add (lineInfo[4]); } } // Line describes polygon containing more than 4 or fewer than 3 points, which are not supported else { polyWarning = true; } // Store index for face group start locations if (++faceCount == originalGroupIndices[groupCount+1]) { groupIndices[++groupCount] = triData.Count; mtlCount = 0; objectNamesCount = 0; } } } } catch (System.Exception err) { GameObject.Find("Canvas").transform.FindChild("PanelPopupLoad").gameObject.SetActive(false); Debug.LogError (err.Message); return null; } if (combineMultipleGroups && !useSubmeshesWhenCombining) { numberOfGroupsUsed = 1; groupIndices[1] = triData.Count; } else { groupIndices[groupCount+1] = triData.Count; numberOfGroupsUsed = groupIndices.Length-1; } // Parse vert/uv/normal index data from triangle face lines var triVerts = new int[triData.Count]; var triUVs = new int[triData.Count]; var triNorms = new int[triData.Count]; var lengthCount = 3; for (int i = 0; i < triData.Count; i++) { string triString = triData[i]; lineInfo = triString.Split ('/'); triVerts[i] = int.Parse (lineInfo[0])-1; if (lineInfo.Length > 1) { if (lineInfo[1] != "") { triUVs[i] = int.Parse (lineInfo[1])-1; } if (lineInfo.Length == lengthCount && useSuppliedNormals) { triNorms[i] = int.Parse (lineInfo[2])-1; } } } var objVertList = new List<Vector3>(objVertices); if (totalUVs > 0) { SplitOnUVs (triData, triVerts, triUVs, objVertList, objUVs, objVertices, ref verticesCount); } // Warnings if (quadWarning && !suppressWarnings) { Debug.LogWarning ("At least one object uses quads...automatic triangle conversion is being used, which may not produce best results"); } if (polyWarning && !suppressWarnings) { Debug.LogWarning ("Polygons which are not quads or triangles have been skipped"); } if (totalUVs == 0 && !suppressWarnings) { Debug.LogWarning ("At least one object does not seem to be UV mapped...any textures used will appear as a solid color"); } if (totalNormals == 0 && !suppressWarnings) { Debug.LogWarning ("No normal data found for at least one object...automatically computing normals instead"); } // Errors if (totalVertices == 0 && triData.Count == 0) { Debug.LogError ("No objects seem to be present...possibly the .obj file is damaged or could not be read"); return null; } else if (totalVertices == 0) { Debug.LogError ("The .obj file does not contain any vertices"); return null; } else if (triData.Count == 0) { Debug.LogError ("The .obj file does not contain any polygons"); return null; } // Set up GameObject array...only 1 object if combining groups var gameObjects = new GameObject[combineMultipleGroups? 1 : numberOfGroupsUsed]; for (int i = 0; i < gameObjects.Length; i++) { gameObjects[i] = new GameObject(objectNames[i], typeof(MeshFilter), typeof(MeshRenderer)); } // -------------------------------- // Create meshes from the .obj data GameObject go = null; Mesh mesh = null; Vector3[] newVertices = null; Vector2[] newUVs = null; Vector3[] newNormals = null; int[] newTriangles = null; var useSubmesh = (combineMultipleGroups && useSubmeshesWhenCombining && numberOfGroupsUsed > 1)? true : false; Material[] newMaterials = null; if (useSubmesh) { newMaterials = new Material[numberOfGroupsUsed]; } int lastUsedMaterialIndex = 0; bool hasUsedMaterial = false; for (int i = 0; i < numberOfGroupsUsed; i++) { if (!useSubmesh || (useSubmesh && i == 0)) { go = gameObjects[i]; mesh = new Mesh(); // Find the number of unique vertices used by this group, also used to map original vertices into 0..thisVertices.Count range var vertHash = new Dictionary<int, int>(); var thisVertices = new List<Vector3>(); int counter = 0; int vertHashValue = 0; int triStart = groupIndices[i]; int triEnd = groupIndices[i + 1]; if (useSubmesh) { triStart = groupIndices[0]; triEnd = groupIndices[numberOfGroupsUsed]; } for (int j = triStart; j < triEnd; j++) { if (!vertHash.TryGetValue (triVerts[j], out vertHashValue)) { vertHash[triVerts[j]] = counter++; thisVertices.Add (objVertList[triVerts[j]]); } } if (thisVertices.Count > maxPoints) { Debug.LogError ("The number of vertices in the object " + objectNames[i] + " exceeds the maximum allowable limit of " + maxPoints); return null; } newVertices = new Vector3[thisVertices.Count]; newUVs = new Vector2[thisVertices.Count]; newNormals = new Vector3[thisVertices.Count]; newTriangles = new int[triEnd - triStart]; // Copy .obj mesh data for vertices and triangles to arrays of the correct size if (scaleFactor == Vector3.one && objRotation == Vector3.zero && objPosition == Vector3.zero) { for (int j = 0; j < thisVertices.Count; j++) { newVertices[j] = thisVertices[j]; } } else { transform.eulerAngles = objRotation; transform.position = objPosition; transform.localScale = scaleFactor; var thisMatrix = transform.localToWorldMatrix; for (int j = 0; j < thisVertices.Count; j++) { newVertices[j] = thisMatrix.MultiplyPoint3x4 (thisVertices[j]); } transform.position = Vector3.zero; transform.rotation = Quaternion.identity; transform.localScale = Vector3.one; } // Arrange UVs and normals so they match up with vertices if (uvsCount > 0 && normalsCount > 0 && useSuppliedNormals) { for (int j = triStart; j < triEnd; j++) { newUVs[vertHash[triVerts[j]]] = objUVs[triUVs[j]]; // Needs to be normalized or lighting is whacked (especially with specular), and some apps don't output normalized normals newNormals[vertHash[triVerts[j]]] = objNormals[triNorms[j]].normalized; } } else { // Arrange UVs so they match up with vertices if (uvsCount > 0) { for (int j = triStart; j < triEnd; j++) { newUVs[vertHash[triVerts[j]]] = objUVs[triUVs[j]]; } } // Arrange normals so they match up with vertices if (normalsCount > 0 && useSuppliedNormals) { for (int j = triStart; j < triEnd; j++) { newNormals[vertHash[triVerts[j]]] = objNormals[triNorms[j]]; } } } // Since we flipped the normals, swap triangle points 2 & 3 counter = 0; for (int j = triStart; j < triEnd; j += 3) { newTriangles[counter ] = vertHash[triVerts[j ]]; newTriangles[counter+1] = vertHash[triVerts[j+2]]; newTriangles[counter+2] = vertHash[triVerts[j+1]]; counter += 3; } mesh.vertices = newVertices; mesh.uv = newUVs; if (autoCenterOnOrigin) { var offset = mesh.bounds.center; int end = newVertices.Length; for (int j = 0; j < end; j++) { newVertices[j] -= offset; } mesh.vertices = newVertices; } if (useSuppliedNormals) { mesh.normals = newNormals; } if (useSubmesh) { mesh.subMeshCount = numberOfGroupsUsed; } } if (useSubmesh) { int thisLength = groupIndices[i + 1] - groupIndices[i]; var thisTriangles = new int[thisLength]; System.Array.Copy (newTriangles, groupIndices[i], thisTriangles, 0, thisLength); mesh.SetTriangles (thisTriangles, i); if (materialNames[i] != null) { if (useMtl && materials.ContainsKey (materialNames[i])) { newMaterials[i] = materials[materialNames[i]]; if (materials[materialNames[i]]) { } hasUsedMaterial = true; lastUsedMaterialIndex = i; } } else { if (hasUsedMaterial) { newMaterials[i] = materials[materialNames[lastUsedMaterialIndex]]; } else { newMaterials[i] = standardMaterial; } } } else { mesh.triangles = newTriangles; } // Stuff that's done for each object, or at the end if using submeshes if (!useSubmesh || (useSubmesh && i == numberOfGroupsUsed-1) ) { if (normalsCount == 0 || !useSuppliedNormals) { mesh.RecalculateNormals(); if (computeTangents) { newNormals = mesh.normals; } } if (computeTangents) { var newTangents = new Vector4[newVertices.Length]; CalculateTangents (newVertices, newNormals, newUVs, newTriangles, newTangents); mesh.tangents = newTangents; } mesh.RecalculateBounds(); go.GetComponent<MeshFilter>().mesh = mesh; if (!useSubmesh) { if (materialNames[i] != null) { if (useMtl && materials.ContainsKey (materialNames[i])) { go.GetComponent<Renderer>().material = materials[materialNames[i]]; hasUsedMaterial = true; lastUsedMaterialIndex = i; } } else { if (hasUsedMaterial) { go.GetComponent<Renderer>().material = materials[materialNames[lastUsedMaterialIndex]]; } else { go.GetComponent<Renderer>().material = standardMaterial; } } } else { go.GetComponent<Renderer>().materials = newMaterials; } } } if (objData != null) { objData.SetDone(); objData.gameObjects = gameObjects; return null; } return gameObjects; }
private IEnumerator GetWWWFiles(string objFilePath, bool useMtl, Material standardMaterial, Material transparentMaterial, ObjData objData) { var www = new WWW(objFilePath); while (!www.isDone) { objData.SetProgress (www.progress * (useMtl? .5f : 1.0f)); yield return null; } if (www.error != null) { Debug.Log (www.error); objData.SetDone(); yield break; } string objFile = www.text; string filePath = ""; Dictionary<string, Material> materials = null; if (useMtl) { string mtlFileName = GetMTLFileName (ref objFilePath, ref objFile, ref filePath); if (mtlFileName != "") { // Read in mtl file www = new WWW(filePath + mtlFileName); yield return www; if (www.error != null) { Debug.Log (www.error); objData.SetDone(); yield break; } if (www.text != "") { // Get textures and parse MTL file var linesRef = new LinesRef(); var textures = new Dictionary<string, Texture2D>(); yield return StartCoroutine (GetTexturesAsync (www.text, linesRef, filePath, textures, objData)); materials = ParseMTL (linesRef.lines, standardMaterial, transparentMaterial, filePath, textures); if (materials == null) { useMtl = false; } } } else { useMtl = false; } } CreateObjects (ref objFile, useMtl, materials, standardMaterial, objData); }
private IEnumerator GetTexturesAsync(string mtlFile, LinesRef linesRef, string filePath, Dictionary<string, Texture2D> textures, ObjData objData) { Texture2D diffuseTexture = null; string[] lines = SplitLines (ref mtlFile); int numberOfTextures = 0; // See how many textures there are (to use for progress) for (int i = 0; i < lines.Length; i++) { var line = lines[i]; CleanLine (ref line); lines[i] = line; if (line.Length < 7 || line[0] == '#') { continue; } if (line.StartsWith ("map_Kd") && filePath != "") { numberOfTextures++; } } float progress = .5f; for (int i = 0; i < lines.Length; i++) { if (lines[i].Length < 7 || lines[i][0] == '#') { continue; } // Get diffuse texture if (lines[i].StartsWith ("map_Kd") && filePath != "") { var lineInfo = lines[i].Split (' '); if (lineInfo.Length > 1) { var textureFilePath = filePath + lineInfo[1]; var www = new WWW(textureFilePath); while (!www.isDone) { objData.SetProgress (progress + (www.progress / numberOfTextures) * .5f); yield return null; } if (www.error != null) { Debug.Log (www.error); objData.SetDone(); yield break; } progress += (1.0f / numberOfTextures) * .5f; diffuseTexture = new Texture2D (4, 4); www.LoadImageIntoTexture (diffuseTexture); textures[lineInfo[1]] = diffuseTexture; } continue; } } linesRef.lines = lines; }