/// <summary> /// Asynchronously imports the given .obj file from the specified url /// </summary> /// <param name="url">The url to the .obj file</param> /// <returns>The GameObject that was created for the imported .obj</returns> public async Task <GameObject> ImportAsync(string url) { i5Debug.Log("Starting import", this); Uri uri = new Uri(url); // fetch the model WebResponse <string> resp = await FetchModelAsync(uri); // if there was an error, we cannot create anything if (!resp.Successful) { i5Debug.LogError("Error fetching obj. No object imported.\n" + resp.ErrorMessage, this); return(null); } // create the parent object // it is a standard GameObject; its only purpose is to bundle the child objects GameObject parentObject = ObjectPool <GameObject> .RequestResource(() => { return(new GameObject()); }); parentObject.name = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath); // parse the .obj file List <ObjParseResult> parseResults = await ParseModelAsync(resp.Content); // for each sub-object in the .obj file, an own parse result was created foreach (ObjParseResult parseResult in parseResults) { // check that the referenced mtl library is already loaded; if not: load it if (!MtlLibrary.LibraryLoaded(parseResult.LibraryPath)) { string mtlUri = UriUtils.RewriteFileUriPath(uri, parseResult.LibraryPath); string libraryName = System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath); bool successful = await MtlLibrary.LoadLibraryAsyc(new Uri(mtlUri), libraryName); if (!successful) { i5Debug.LogError("Could not load .mtl file " + parseResult.LibraryPath, this); } } // get the material constructor of the sub-object MaterialConstructor mat = MtlLibrary.GetMaterialConstructor( System.IO.Path.GetFileNameWithoutExtension(uri.LocalPath), parseResult.MaterialName); if (mat != null) { // first get dependencies; this will e.g. fetch referenced textures await mat.FetchDependencies(); parseResult.ObjectConstructor.MaterialConstructor = mat; } // construct the object and make it a child of the parentObject parseResult.ObjectConstructor.ConstructObject(parentObject.transform); } return(parentObject); }
/// <summary> /// Parses the contents of a .mtl file /// </summary> /// <param name="uri">The full URI where the .mtl file is stored</param> /// <param name="libraryContent">The line array of the file's content</param> /// <returns>A list a material constructor for each material in the library</returns> private List <MaterialConstructor> ParseMaterialLibrary(Uri uri, string[] libraryContent) { List <MaterialConstructor> materials = new List <MaterialConstructor>(); MaterialConstructor current = null; int numberOfErrors = 0; for (int i = 0; i < libraryContent.Length; i++) { string trimmedLine = libraryContent[i].Trim(); // newmtl and # (comment) can be executed also if there is no current material set // skip empty lines if (string.IsNullOrEmpty(trimmedLine)) { continue; } else if (trimmedLine.StartsWith("newmtl")) { if (current != null) { materials.Add(current); } current = new MaterialConstructor(); current.Name = trimmedLine.Substring(6).TrimStart(); } else if (trimmedLine.StartsWith("#")) { if (ExtendedLogging) { i5Debug.Log("Comment found: " + trimmedLine.Substring(1).TrimStart(), this); } } // all other commands require an already initialized material // this means that the line newmtl must have been read at least once until now else { if (current != null) { // Kd sets the diffuse color if (trimmedLine.StartsWith("Kd")) { string[] strValues = trimmedLine.Substring(2).TrimStart().Split(' '); if (strValues.Length != 3) { numberOfErrors++; if (ExtendedLogging) { i5Debug.LogError("Expected three color values but found " + strValues.Length, this); } continue; } if (ParserUtils.TryParseStringArrayToVector3(strValues, out Vector3 colorVector)) { // could successfully parse color vector Color albedo = colorVector.ToColor(); current.Color = albedo; } else { numberOfErrors++; if (ExtendedLogging) { i5Debug.LogError("Could not parse color data", this); } } } // Ks sets the specular intensity else if (trimmedLine.StartsWith("Ks")) { string[] strValues = trimmedLine.Substring(2).TrimStart().Split(' '); if (strValues.Length != 3 && strValues.Length != 1) { numberOfErrors++; if (ExtendedLogging) { i5Debug.LogError("Expected one or three smoothness values but found " + strValues.Length, this); } continue; } // we assume that all values are equal if (float.TryParse(strValues[0], NumberStyles.Any, CultureInfo.InvariantCulture, out float smoothness)) { // could successfully parse smoothness current.SetFloat("_Glossiness", smoothness); } else { numberOfErrors++; if (ExtendedLogging) { i5Debug.LogError("Could not parse color data", this); } } } // map_Kd sets the albedo texture else if (trimmedLine.StartsWith("map_Kd")) { string texturePath = trimmedLine.Substring(6).TrimStart(); // rewrite the URI to the texture's path and then add a texture constructor to the material string fullUri = UriUtils.RewriteFileUriPath(uri, texturePath); current.SetTexture("_MainTex", new TextureConstructor(fullUri)); } } else { i5Debug.LogWarning("Material instruction line found but material has not yet been initialized. The .mtl-file is probably malformed.", this); } } } if (current != null) { materials.Add(current); } // check if materials were created if (materials.Count == 0) { i5Debug.LogWarning(".mtl-file was read but no materials were parsed.", this); } return(materials); }