rabidus-test/Assets/PolyFew/ThirdParty/AsImpL/Scripts/Loaders/LoaderObj.cs

982 lines
36 KiB
C#
Raw Normal View History

2023-10-20 13:29:44 +03:00
using UnityEngine;
using UnityEngine.Networking;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Globalization;
using System.Threading.Tasks;
using static BrainFailProductions.PolyFewRuntime.PolyfewRuntime;
using System.Text;
namespace BrainFailProductions.PolyFew.AsImpL
{
/// <summary>
/// Class for loading OBJ files into Unity scene at run-time and in editor mode.
/// </summary>
/// <remarks>
/// Partially derived from "Runtime OBJ Loader"
/// (http://forum.unity3d.com/threads/free-runtime-obj-loader.365884/)
/// and from "runtime .OBJ file loader for Unity3D"
/// (https://github.com/hammmm/unity-obj-loader) and
/// (https://github.com/cmdr2/unity-remote-obj-loader)
///
/// New features:
/// <list type="bullet">
/// <item><description>meshes with more than 65K vertices/indices are splitted and loaded</description></item>
/// <item><description>groups are loaded into game (sub) objects</description></item>
/// <item><description>extended material support</description></item>
/// <item><description>computation of normal maps and tangents</description></item>
/// <item><description>computation of albedo texture from diffuse and opacity textures</description></item>
/// <item><description>progressive loading</description></item>
/// <item><description>reusing data for multiple objects</description></item>
/// <item><description>create a loader for each model for parallel loading</description></item>
/// <item><description>support for asset import</description></item>
/// </list>
/// <seealso cref="DataSet"/>
/// <seealso cref="MaterialData"/>
/// <seealso cref="ObjectBuilder"/>
/// </remarks>
public class LoaderObj : Loader
{
private string mtlLib;
private string loadedText;
/// <summary>
/// Parse dependencies of the given OBJ file.
/// </summary>
/// <param name="absolutePath">absolute file path</param>
/// <returns>The list of dependencies (textures files, if any).</returns>
public override string[] ParseTexturePaths(string absolutePath)
{
List<string> mtlTexPathList = new List<string>();
string basePath = GetDirName(absolutePath);
string mtlLibName = ParseMaterialLibName(absolutePath);
if (!string.IsNullOrEmpty(mtlLibName))
{
//mtlDepPathList.Add(mtlLibName);
string mtlPath = basePath + mtlLibName;
string[] lines = File.ReadAllLines(mtlPath);
List<MaterialData> mtlData = new List<MaterialData>();
ParseMaterialData(lines, mtlData);
foreach (MaterialData mtl in mtlData)
{
if (!string.IsNullOrEmpty(mtl.diffuseTexPath))
{
mtlTexPathList.Add(mtl.diffuseTexPath);
}
if (!string.IsNullOrEmpty(mtl.specularTexPath))
{
mtlTexPathList.Add(mtl.specularTexPath);
}
if (!string.IsNullOrEmpty(mtl.bumpTexPath))
{
mtlTexPathList.Add(mtl.bumpTexPath);
}
if (!string.IsNullOrEmpty(mtl.opacityTexPath))
{
mtlTexPathList.Add(mtl.opacityTexPath);
}
}
}
return mtlTexPathList.ToArray();
}
protected override async Task LoadModelFile(string absolutePath, string texturesFolderPath = "", string materialsFolderPath = "")
{
#pragma warning disable
string url = absolutePath.Contains("//") ? absolutePath : "file:///" + absolutePath;
using (StreamReader sr = new StreamReader(absolutePath))
{
loadedText = await sr.ReadToEndAsync();
}
//yield return LoadOrDownloadText(url);
if (string.IsNullOrEmpty(loadedText))
{
totalProgress.singleProgress.Remove(objLoadingProgress);
throw new InvalidOperationException("Failed to load data from file. The file might be empty or non readable.");
// remove this progress to let complete the total loading process
//return;
}
//Debug.LogFormat("Parsing geometry data in {0}...", www.url);
ParseGeometryData(loadedText);
}
protected override async Task LoadModelFileNetworked(string objURL)
{
bool isWorking = true;
byte[] downloadedBytes = null;
Exception ex = null;
float oldProgress = individualProgress.Value;
try
{
StartCoroutine(DownloadFile(objURL, individualProgress, (bytes) =>
{
isWorking = false;
downloadedBytes = bytes;
//loadedText = Encoding.UTF8.GetString(bytes);
},
(error) =>
{
ObjectImporter.activeDownloads -= 1;
ex = new System.InvalidOperationException("Failed to download base model." + error);
isWorking = false;
}));
}
catch(Exception exc)
{
ObjectImporter.activeDownloads -= 1;
individualProgress.Value = oldProgress;
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f; isWorking = false;
throw exc;
}
while (isWorking)
{
//Debug.Log("Stuck in ISWORKING WHILE LOOP");
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
await Task.Delay(1);
}
if (ex != null) { throw ex; }
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (downloadedBytes != null && downloadedBytes.Length > 0)
{
using (StreamReader sr = new StreamReader(new MemoryStream(downloadedBytes)))
{
loadedText = await sr.ReadToEndAsync();
}
}
//yield return LoadOrDownloadText(url);
if (string.IsNullOrEmpty(loadedText))
{
totalProgress.singleProgress.Remove(objLoadingProgress);
throw new InvalidOperationException("Failed to load data from the downloaded obj file. The file might be empty or non readable.");
// remove this progress to let complete the total loading process
//return;
}
//Debug.LogFormat("Parsing geometry data in {0}...", www.url);
try
{
ParseGeometryData(loadedText);
}
catch (Exception exc)
{
throw exc;
}
}
protected override IEnumerator LoadModelFileNetworkedWebGL(string objURL, Action<Exception> OnError)
{
bool isWorking = true;
Exception ex = null;
float oldProgress = individualProgress.Value;
try
{
StartCoroutine(DownloadFileWebGL(objURL, individualProgress, (text) =>
{
isWorking = false;
loadedText = text;
//loadedText = Encoding.UTF8.GetString(bytes);
},
(error) =>
{
ObjectImporter.activeDownloads -= 1;
ex = new System.InvalidOperationException("Base model download unsuccessful." + error);
ObjectImporter.isException = true;
OnError(ex);
isWorking = false;
}));
}
catch (Exception exc)
{
ObjectImporter.activeDownloads -= 1;
individualProgress.Value = oldProgress;
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f; isWorking = false;
isWorking = false;
OnError(exc);
ObjectImporter.isException = true;
}
while (isWorking)
{
yield return new WaitForSeconds(0.1f);
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
}
if (ObjectImporter.isException)
{
yield return null;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
//yield return LoadOrDownloadText(url);
if (string.IsNullOrEmpty(loadedText))
{
totalProgress.singleProgress.Remove(objLoadingProgress);
throw new InvalidOperationException("Failed to load data from the downloaded obj file. The file might be empty or non readable.");
}
//Debug.LogFormat("Parsing geometry data in {0}...", www.url);
try
{
ParseGeometryData(loadedText);
}
catch (Exception exc)
{
OnError(exc);
ObjectImporter.isException = true;
}
}
protected override async Task LoadMaterialLibrary(string absolutePath, string materialsFolderPath = "")
{
string mtlPath;
string basePath = GetDirName(absolutePath);
if (absolutePath.Contains("//"))
{
int pos;
// handle the special case of a PHP URL containing "...?...=model.obj"
if (absolutePath.Contains("?"))
{
// in this case try to get the library path reading until last "=".
pos = absolutePath.LastIndexOf('=');
}
else
{
pos = absolutePath.LastIndexOf('/');
}
mtlPath = absolutePath.Remove(pos + 1) + mtlLib;
}
else
{
if(Path.IsPathRooted(mtlLib))
{
mtlPath = "file:///" + mtlLib;
}
else
{
mtlPath = "file:///" + basePath + mtlLib;
}
}
string matPath = string.IsNullOrWhiteSpace(materialsFolderPath) ? basePath + mtlLib : materialsFolderPath + mtlLib;
if (File.Exists(matPath))
{
using (StreamReader sr = new StreamReader(matPath))
{
loadedText = await sr.ReadToEndAsync();
}
}
else
{
Debug.LogWarning("Cannot find the associated material file at the path " + basePath + mtlLib);
}
//yield return LoadOrDownloadText(mtlPath,false);
/*
if (loadedText == null)
{
mtlLib = Path.GetFileName(mtlLib);
mtlPath = "file:///" + basePath + mtlLib;
Debug.LogWarningFormat("Material library {0} loaded from the same directory as the OBJ file.\n", mtlLib);
yield return LoadOrDownloadText(mtlPath);
}
*/
if (!string.IsNullOrWhiteSpace(loadedText))
{
//Debug.LogFormat("Parsing material libray {0}...", loader.url);
objLoadingProgress.message = "Parsing material library...";
ParseMaterialData(loadedText);
}
}
protected override async Task LoadMaterialLibrary(string materialURL)
{
bool isWorking = true;
byte[] downloadedBytes = null;
float oldProgress = individualProgress.Value;
try
{
StartCoroutine(DownloadFile(materialURL, individualProgress, (bytes) =>
{
isWorking = false;
downloadedBytes = bytes;
//loadedText = Encoding.UTF8.GetString(bytes);
},
(error) =>
{
ObjectImporter.activeDownloads -= 1;
isWorking = false;
Debug.LogWarning("Failed to load the associated material file." + error);
}));
}
catch (Exception exc)
{
ObjectImporter.activeDownloads -= 1;
individualProgress.Value = oldProgress;
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f; isWorking = false;
throw exc;
}
while (isWorking)
{
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
await Task.Delay(3);
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (downloadedBytes != null && downloadedBytes.Length > 0)
{
using (StreamReader sr = new StreamReader(new MemoryStream(downloadedBytes)))
{
loadedText = await sr.ReadToEndAsync();
}
}
if (!string.IsNullOrWhiteSpace(loadedText))
{
//Debug.LogFormat("Parsing material libray {0}...", loader.url);
objLoadingProgress.message = "Parsing material library...";
ParseMaterialData(loadedText);
}
}
protected override IEnumerator LoadMaterialLibraryWebGL(string materialURL)
{
bool isWorking = true;
float oldProgress = individualProgress.Value;
StartCoroutine(DownloadFileWebGL(materialURL, individualProgress, (text) =>
{
isWorking = false;
loadedText = text;
//loadedText = Encoding.UTF8.GetString(bytes);
},
(error) =>
{
ObjectImporter.activeDownloads -= 1;
isWorking = false;
Debug.LogWarning("Failed to load the associated material file." + error);
}));
while (isWorking)
{
yield return new WaitForSeconds(0.1f);
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (!string.IsNullOrWhiteSpace(loadedText))
{
//Debug.LogFormat("Parsing material libray {0}...", loader.url);
objLoadingProgress.message = "Parsing material library...";
ParseMaterialData(loadedText);
}
}
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;
}
}
}
/// <summary>
/// Convert coordinates according to import options.
/// </summary>
private Vector3 ConvertVec3(float x, float y, float z)
{
if (Scaling != 1f)
{
x *= Scaling;
y *= Scaling;
z *= Scaling;
}
if (ConvertVertAxis) return new Vector3(x, z, y);
return new Vector3(x, y, -z);
}
/// <summary>
/// Parse a string to get a floating point number using the invariant culture.
/// </summary>
/// <param name="floatString">String with the number to be parsed</param>
/// <returns>The parsed floating point number.</returns>
private float ParseFloat(string floatString)
{
return float.Parse(floatString, CultureInfo.InvariantCulture.NumberFormat);
}
/// <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();
}
/// <summary>
/// Extract the material library (file) name from the OBJ file.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private string ParseMaterialLibName(string path)
{
string[] lines = File.ReadAllLines(path);
objLoadingProgress.message = "Parsing geometry data...";
for (int i = 0; i < lines.Length; i++)
{
string l = lines[i].Trim();
if (l.StartsWith("mtllib"))
{
return l.Substring("mtllib".Length).Trim();
}
}
return null;
}
/// <summary>
/// Check if a material library file is defined.
/// </summary>
protected override bool HasMaterialLibrary
{
get
{
return mtlLib != null;
}
}
/// <summary>
/// Parse the material library text to get material data.
/// </summary>
/// <param name="data">material library text (read from file)</param>
private void ParseMaterialData(string data)
{
objLoadingProgress.message = "Parsing material data...";
string[] lines = data.Split("\n".ToCharArray());
materialData = new List<MaterialData>();
ParseMaterialData(lines, materialData);
}
/// <summary>
/// Parse the material library lines to get material data.
/// </summary>
/// <param name="lines">lines read from the material library file</param>
/// <param name="mtlData">list of material data</param>
private void ParseMaterialData(string[] lines, List<MaterialData> mtlData)
{
MaterialData current = new MaterialData();
char[] separators = new char[] { ' ', '\t' };
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i].Trim();
// remove comments
if (line.IndexOf("#") != -1) line = line.Substring(0, line.IndexOf("#"));
string[] p = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (p.Length == 0 || string.IsNullOrEmpty(p[0])) continue;
string parameters = null;
if (line.Length > p[0].Length)
{
parameters = line.Substring(p[0].Length + 1).Trim();
}
try
{
switch (p[0])
{
case "newmtl":
current = new MaterialData();
current.materialName = DataSet.FixMaterialName(parameters);
mtlData.Add(current);
break;
case "Ka": // Ambient component (not supported)
current.ambientColor = StringsToColor(p);
break;
case "Kd": // Diffuse component
current.diffuseColor = StringsToColor(p);
break;
case "Ks": // Specular component
current.specularColor = StringsToColor(p);
break;
case "Ke": // Specular component
current.emissiveColor = StringsToColor(p);
break;
case "Ns": // Specular exponent --> shininess
current.shininess = ParseFloat(p[1]);
break;
case "d": // dissolve into the background (1=opaque, 0=transparent)
current.overallAlpha = p.Length > 1 && p[1] != "" ? ParseFloat(p[1]) : 1.0f;
break;
case "Tr": // Transparency
current.overallAlpha = p.Length > 1 && p[1] != "" ? 1.0f - ParseFloat(p[1]) : 1.0f;
break;
case "map_KD":
case "map_Kd": // Color texture, diffuse reflectivity
if (!string.IsNullOrEmpty(parameters))
{
current.diffuseTexPath = parameters;
}
break;
// TODO: different processing needed, options not supported
case "map_Ks": // specular reflectivity of the material
case "map_kS":
case "map_Ns": // Scalar texture for specular exponent
if (!string.IsNullOrEmpty(parameters))
{
current.specularTexPath = parameters;
}
break;
case "map_bump": // Bump map texture
if (!string.IsNullOrEmpty(parameters))
{
current.bumpTexPath = parameters;
}
break;
case "bump":
ParseBumpParameters(p, current);
break;
case "map_opacity":
case "map_d": // Scalar texture modulating the dissolve into the background
if (!string.IsNullOrEmpty(parameters))
{
current.opacityTexPath = parameters;
}
break;
case "illum": // Illumination model. 1 - diffuse, 2 - specular (not used)
current.illumType = int.Parse(p[1]);
break;
case "refl": // reflection map (replaced with Unity environment reflection)
if (!string.IsNullOrEmpty(parameters))
{
current.hasReflectionTex = true;
}
break;
case "map_Ka": // ambient reflectivity color texture
case "map_kA":
if (!string.IsNullOrEmpty(parameters))
{
Debug.Log("Map not supported:" + line);
}
break;
default:
//Debug.Log("this line was not processed :" + line);
break;
}
}
catch (Exception e)
{
Debug.LogErrorFormat("Error at line {0} in mtl file: {1}", i + 1, e);
}
}
}
/// <summary>
/// Parse bump parameters.
/// </summary>
/// <param name="param">list of paramers</param>
/// <param name="mtlData">material data to be updated</param>
/// <remarks>Only the bump map texture path is used here.</remarks>
/// <seealso cref="https://github.com/hammmm/unity-obj-loader"/>
private void ParseBumpParameters(string[] param, MaterialData mtlData)
{
Regex regexNumber = new Regex(@"^[-+]?[0-9]*\.?[0-9]+$");
var bumpParams = new Dictionary<string, BumpParamDef>();
bumpParams.Add("bm", new BumpParamDef("bm", "string", 1, 1));
bumpParams.Add("clamp", new BumpParamDef("clamp", "string", 1, 1));
bumpParams.Add("blendu", new BumpParamDef("blendu", "string", 1, 1));
bumpParams.Add("blendv", new BumpParamDef("blendv", "string", 1, 1));
bumpParams.Add("imfchan", new BumpParamDef("imfchan", "string", 1, 1));
bumpParams.Add("mm", new BumpParamDef("mm", "string", 1, 1));
bumpParams.Add("o", new BumpParamDef("o", "number", 1, 3));
bumpParams.Add("s", new BumpParamDef("s", "number", 1, 3));
bumpParams.Add("t", new BumpParamDef("t", "number", 1, 3));
bumpParams.Add("texres", new BumpParamDef("texres", "string", 1, 1));
int pos = 1;
string filename = null;
while (pos < param.Length)
{
if (!param[pos].StartsWith("-"))
{
filename = param[pos];
pos++;
continue;
}
// option processing
string optionName = param[pos].Substring(1);
pos++;
if (!bumpParams.ContainsKey(optionName))
{
continue;
}
BumpParamDef def = bumpParams[optionName];
ArrayList args = new ArrayList();
int i = 0;
bool isOptionNotEnough = false;
for (; i < def.valueNumMin; i++, pos++)
{
if (pos >= param.Length)
{
isOptionNotEnough = true;
break;
}
if (def.valueType == "number")
{
Match match = regexNumber.Match(param[pos]);
if (!match.Success)
{
isOptionNotEnough = true;
break;
}
}
args.Add(param[pos]);
}
if (isOptionNotEnough)
{
Debug.Log("bump variable value not enough for option:" + optionName + " of material:" + mtlData.materialName);
continue;
}
for (; i < def.valueNumMax && pos < param.Length; i++, pos++)
{
if (def.valueType == "number")
{
Match match = regexNumber.Match(param[pos]);
if (!match.Success)
{
break;
}
}
args.Add(param[pos]);
}
// TODO: some processing of options
Debug.Log("found option: " + optionName + " of material: " + mtlData.materialName + " args: " + string.Concat(args.ToArray()));
}
// set the file name, if found
// TODO: other parsed parameters are not used for now
if (filename != null)
{
mtlData.bumpTexPath = filename;
}
}
private Color StringsToColor(string[] p)
{
return new Color(ParseFloat(p[1]), ParseFloat(p[2]), ParseFloat(p[3]));
}
private IEnumerator LoadOrDownloadText(string url, bool notifyErrors = true)
{
loadedText = null;
#if UNITY_2018_3_OR_NEWER
UnityWebRequest uwr = UnityWebRequest.Get(url);
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
if (notifyErrors)
{
Debug.LogError(uwr.error);
}
}
else
{
// Get downloaded asset bundle
loadedText = uwr.downloadHandler.text;
}
#else
WWW www = new WWW(url);
yield return www;
if (www.error != null)
{
if (notifyErrors)
{
Debug.LogError("Error loading " + url + "\n" + www.error);
}
}
else
{
loadedText = www.text;
}
#endif
}
/// <summary>
/// Bump parameter definition
/// </summary>
/// <remarks>Not really used for material definition, for now.</remarks>
/// <see cref="https://github.com/hammmm/unity-obj-loader"/>
private class BumpParamDef
{
public string optionName;
public string valueType;
public int valueNumMin;
public int valueNumMax;
public BumpParamDef(string name, string type, int numMin, int numMax)
{
optionName = name;
valueType = type;
valueNumMin = numMin;
valueNumMax = numMax;
}
}
}
}