using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System.Threading.Tasks;
using static BrainFailProductions.PolyFewRuntime.PolyfewRuntime;
#if UNITY_EDITOR
using UnityEditor;
#endif
///
/// Asynchronous importer and loader
///
namespace BrainFailProductions.PolyFew.AsImpL
{
///
/// Abstract loader to be used as a base class for specific loaders.
///
public abstract class Loader : MonoBehaviour
{
///
/// Total loading progress, for all the models currently loading.
///
public static LoadingProgress totalProgress = new LoadingProgress();
///
/// Options to define how the model will be loaded and imported.
///
public ImportOptions buildOptions;
public ReferencedNumeric individualProgress = new ReferencedNumeric(0);
#if UNITY_EDITOR
///
/// Alternative texture path: if not null textures will be loaded from here.
///
public string altTexPath = null;
#endif
// raw subdivision in percentages of the loading phases (empirically computed loading a large sample OBJ file)
// TODO: refine or change this method
protected static float LOAD_PHASE_PERC = 8f;
protected static float TEXTURE_PHASE_PERC = 1f;
protected static float MATERIAL_PHASE_PERC = 1f;
protected static float BUILD_PHASE_PERC = 90f;
protected static Dictionary loadedModels = new Dictionary();
protected static Dictionary instanceCount = new Dictionary();
protected DataSet dataSet = new DataSet();
protected ObjectBuilder objectBuilder = new ObjectBuilder();
protected List materialData;
protected SingleLoadingProgress objLoadingProgress = new SingleLoadingProgress();
protected Stats loadStats;
private Texture2D loadedTexture = null;
///
/// Load the file assuming its vertical axis is Z instead of Y
///
public bool ConvertVertAxis
{
get
{
return buildOptions != null ? buildOptions.zUp : false;
}
set
{
if (buildOptions == null)
{
buildOptions = new ImportOptions();
}
buildOptions.zUp = value;
}
}
///
/// Rescaling for the model (1 = no rescaling)
///
public float Scaling
{
get
{
return buildOptions != null ? buildOptions.modelScaling : 1f;
}
set
{
if (buildOptions == null)
{
buildOptions = new ImportOptions();
}
buildOptions.modelScaling = value;
}
}
///
/// Check if a material library is defined for this model
///
protected abstract bool HasMaterialLibrary { get; }
#if UNITY_EDITOR
///
/// Import data as assets in the project (Editor only)
///
public bool ImportingAssets { get { return !string.IsNullOrEmpty(altTexPath); } }
#endif
///
/// Event triggered when an object is created.
///
public event Action ModelCreated;
///
/// Event triggered when an object is successfully loaded.
///
public event Action ModelLoaded;
///
/// Event triggered if failed to load an object
///
public event Action ModelError;
///
/// Get a previusly loaded model by its absolute path
///
/// absolute path used to load the model
/// The game object previously loaded
public static GameObject GetModelByPath(string absolutePath)
{
if (loadedModels.ContainsKey(absolutePath))
{
return loadedModels[absolutePath];
}
return null;
}
///
/// Load a model.
///
/// name of the GameObject, if empty use file name
/// absolute file path
/// Transform to which attach the loaded object (null=scene)
/// You can use StartCoroutine( loader.Load(...) )
public async Task Load(string objName, string objAbsolutePath, Transform parentObj, string texturesFolderPath = "", string materialsFolderPath = "")
{
string fileName = Path.GetFileName(objAbsolutePath);
string fileNameNoExt = Path.GetFileNameWithoutExtension(objAbsolutePath);
string name = objName;
if (name == null || name == "") objName = fileNameNoExt;
totalProgress.singleProgress.Add(objLoadingProgress);
objLoadingProgress.fileName = fileName;
objLoadingProgress.error = false;
objLoadingProgress.message = "Loading " + fileName + "...";
//Debug.LogFormat("Loading {0}\n from: {1}...", objName, absolutePath);
await Task.Yield();
//yield return null;
// TODO: implementation of a caching mechanism for models downloaded from an URL
/*
// if the model was already loaded duplicate the existing object
if (buildOptions != null && buildOptions.reuseLoaded && loadedModels.ContainsKey(absolutePath) && loadedModels[absolutePath] != null)
{
Debug.LogFormat("File {0} already loaded, creating instance.", absolutePath);
instanceCount[absolutePath]++;
if (name == null || name == "") objName = objName + "_" + instanceCount[absolutePath];
objLoadingProgress.message = "Instantiating " + objName + "...";
while (loadedModels[absolutePath] == null)
{
await Task.Yield();
//yield return null;
}
GameObject newObj = Instantiate(loadedModels[absolutePath]);
//yield return newObj;
OnCreated(newObj, absolutePath);
newObj.name = objName;
if (parentObj != null) newObj.transform.parent = parentObj.transform;
totalProgress.singleProgress.Remove(objLoadingProgress);
OnLoaded(newObj, absolutePath);
return;
//yield break;
}
*/
loadedModels[objAbsolutePath] = null; // define a key for the dictionary
instanceCount[objAbsolutePath] = 0; // define a key for the dictionary
float lastTime = Time.realtimeSinceStartup;
float startTime = lastTime;
await LoadModelFile(objAbsolutePath, texturesFolderPath, materialsFolderPath);
loadStats.modelParseTime = Time.realtimeSinceStartup - lastTime;
if (objLoadingProgress.error)
{
OnLoadFailed(objAbsolutePath);
return null;
}
lastTime = Time.realtimeSinceStartup;
if (HasMaterialLibrary)
{
await LoadMaterialLibrary(objAbsolutePath, materialsFolderPath);
}
loadStats.materialsParseTime = Time.realtimeSinceStartup - lastTime;
lastTime = Time.realtimeSinceStartup;
await Build(objAbsolutePath, objName, parentObj, texturesFolderPath);
loadStats.buildTime = Time.realtimeSinceStartup - lastTime;
loadStats.totalTime = Time.realtimeSinceStartup - startTime;
/*
Debug.Log("Done: " + objName
+ "\n Loaded in " + loadStats.totalTime + " seconds"
+ "\n Model data parsed in " + loadStats.modelParseTime + " seconds"
+ "\n Material data parsed in " + loadStats.materialsParseTime + " seconds"
+ "\n Game objects built in " + loadStats.buildTime + " seconds"
+ "\n textures: " + loadStats.buildStats.texturesTime + " seconds"
+ "\n materials: " + loadStats.buildStats.materialsTime + " seconds"
+ "\n objects: " + loadStats.buildStats.objectsTime + " seconds"
);
*/
totalProgress.singleProgress.Remove(objLoadingProgress);
OnLoaded(loadedModels[objAbsolutePath], objAbsolutePath);
return loadedModels[objAbsolutePath];
}
public async Task LoadFromNetwork(string objURL, string diffuseTexURL, string bumpTexURL, string specularTexURL, string opacityTexURL, string materialURL, string objName)
{
string fileName = objName + ".obj";
#pragma warning disable
string fileNameNoExt = objName;
totalProgress.singleProgress.Add(objLoadingProgress);
objLoadingProgress.fileName = fileName;
objLoadingProgress.error = false;
objLoadingProgress.message = "Loading " + fileName + "...";
//Debug.LogFormat("Loading {0}\n from: {1}...", objName, absolutePath);
await Task.Yield();
loadedModels[objURL] = null; // define a key for the dictionary
instanceCount[objURL] = 0; // define a key for the dictionary
float lastTime = Time.realtimeSinceStartup;
float startTime = lastTime;
// base model here
try
{
await LoadModelFileNetworked(objURL);
}
catch (Exception ex)
{
throw ex;
}
loadStats.modelParseTime = Time.realtimeSinceStartup - lastTime;
if (objLoadingProgress.error)
{
OnLoadFailed(objURL);
return null;
}
lastTime = Time.realtimeSinceStartup;
if (HasMaterialLibrary)
{
// materials here
try
{
await LoadMaterialLibrary(materialURL);
}
catch(Exception ex)
{
throw ex;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
loadStats.materialsParseTime = Time.realtimeSinceStartup - lastTime;
lastTime = Time.realtimeSinceStartup;
try
{
await NetworkedBuild(null, objName, objURL, diffuseTexURL, bumpTexURL, specularTexURL, opacityTexURL);
}
catch (Exception ex)
{
throw ex;
}
loadStats.buildTime = Time.realtimeSinceStartup - lastTime;
loadStats.totalTime = Time.realtimeSinceStartup - startTime;
/*
Debug.Log("Done: " + objName
+ "\n Loaded in " + loadStats.totalTime + " seconds"
+ "\n Model data parsed in " + loadStats.modelParseTime + " seconds"
+ "\n Material data parsed in " + loadStats.materialsParseTime + " seconds"
+ "\n Game objects built in " + loadStats.buildTime + " seconds"
+ "\n textures: " + loadStats.buildStats.texturesTime + " seconds"
+ "\n materials: " + loadStats.buildStats.materialsTime + " seconds"
+ "\n objects: " + loadStats.buildStats.objectsTime + " seconds"
);
*/
totalProgress.singleProgress.Remove(objLoadingProgress);
OnLoaded(loadedModels[objURL], objURL);
return loadedModels[objURL];
}
public IEnumerator LoadFromNetworkWebGL(string objURL, string diffuseTexURL, string bumpTexURL, string specularTexURL, string opacityTexURL, string materialURL, string objName, Action OnSuccess, Action OnError)
{
string fileName = objName + ".obj";
#pragma warning disable
string fileNameNoExt = objName;
totalProgress.singleProgress.Add(objLoadingProgress);
objLoadingProgress.fileName = fileName;
objLoadingProgress.error = false;
objLoadingProgress.message = "Loading " + fileName + "...";
//Debug.LogFormat("Loading {0}\n from: {1}...", objName, absolutePath);
loadedModels[objURL] = null; // define a key for the dictionary
instanceCount[objURL] = 0; // define a key for the dictionary
float lastTime = Time.realtimeSinceStartup;
float startTime = lastTime;
// base model here
yield return StartCoroutine(LoadModelFileNetworkedWebGL(objURL, OnError));
if(ObjectImporter.isException) { yield return null; }
loadStats.modelParseTime = Time.realtimeSinceStartup - lastTime;
if (objLoadingProgress.error)
{
OnLoadFailed(objURL);
OnError(new Exception("Load failed due to unknown reasons."));
yield return null;
}
lastTime = Time.realtimeSinceStartup;
if (HasMaterialLibrary)
{
// materials here
yield return StartCoroutine(LoadMaterialLibraryWebGL(materialURL));
}
else
{
ObjectImporter.activeDownloads -= 1;
}
if (ObjectImporter.isException) { yield return null; }
loadStats.materialsParseTime = Time.realtimeSinceStartup - lastTime;
lastTime = Time.realtimeSinceStartup;
yield return StartCoroutine(NetworkedBuildWebGL(null, objName, objURL, diffuseTexURL, bumpTexURL, specularTexURL, opacityTexURL));
if (ObjectImporter.isException) { yield return null; }
loadStats.buildTime = Time.realtimeSinceStartup - lastTime;
loadStats.totalTime = Time.realtimeSinceStartup - startTime;
/*
Debug.Log("Done: " + objName
+ "\n Loaded in " + loadStats.totalTime + " seconds"
+ "\n Model data parsed in " + loadStats.modelParseTime + " seconds"
+ "\n Material data parsed in " + loadStats.materialsParseTime + " seconds"
+ "\n Game objects built in " + loadStats.buildTime + " seconds"
+ "\n textures: " + loadStats.buildStats.texturesTime + " seconds"
+ "\n materials: " + loadStats.buildStats.materialsTime + " seconds"
+ "\n objects: " + loadStats.buildStats.objectsTime + " seconds"
);
*/
totalProgress.singleProgress.Remove(objLoadingProgress);
OnLoaded(loadedModels[objURL], objURL);
OnSuccess(loadedModels[objURL]);
}
///
/// Parse the model to get a list of the paths of all used textures
///
/// absolute path of the model
/// List of paths of the textures referenced by the model
public abstract string[] ParseTexturePaths(string absolutePath);
///
/// Load the main model file
///
/// absolute file path
/// This is called by Load() method
protected abstract Task LoadModelFile(string absolutePath, string texturesFolderPath = "", string materialsFolderPath = "");
protected abstract Task LoadModelFileNetworked(string objURL);
protected abstract IEnumerator LoadModelFileNetworkedWebGL(string objURL, Action OnError);
///
/// Load the material library from the given path.
///
/// absolute file path
/// This is called by Load() method
protected abstract Task LoadMaterialLibrary(string absolutePath, string materialsFolderPath = "");
protected abstract Task LoadMaterialLibrary(string materialURL);
protected abstract IEnumerator LoadMaterialLibraryWebGL(string materialURL);
///
/// Build the game objects from data set, materials and textures.
///
/// absolute file path
/// Name of the main game object (model root)
/// transform to which the model root will be attached (if null it will be a root aobject)
/// This is called by Load() method
protected async Task Build(string absolutePath, string objName, Transform parentTransform, string texturesFolderPath = "")
{
float prevTime = Time.realtimeSinceStartup;
if (materialData != null)
{
//string basePath = GetDirName(absolutePath);
string basePath = Path.GetDirectoryName(absolutePath);
objLoadingProgress.message = "Loading textures...";
int count = 0;
foreach (MaterialData mtl in materialData)
{
objLoadingProgress.percentage = LOAD_PHASE_PERC + TEXTURE_PHASE_PERC * count / materialData.Count;
count++;
if (mtl.diffuseTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.diffuseTex = LoadAssetTexture(mtl.diffuseTexPath);
}
else
#endif
{
await LoadMaterialTexture(basePath, mtl.diffuseTexPath, texturesFolderPath);
mtl.diffuseTex = loadedTexture;
}
}
if (mtl.bumpTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.bumpTex = LoadAssetTexture(mtl.bumpTexPath);
}
else
#endif
{
await LoadMaterialTexture(basePath, mtl.bumpTexPath, texturesFolderPath);
mtl.bumpTex = loadedTexture;
}
}
if (mtl.specularTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.specularTex = LoadAssetTexture(mtl.specularTexPath);
}
else
#endif
{
await LoadMaterialTexture(basePath, mtl.specularTexPath, texturesFolderPath);
mtl.specularTex = loadedTexture;
}
}
if (mtl.opacityTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.opacityTex = LoadAssetTexture(mtl.opacityTexPath);
}
else
#endif
{
await LoadMaterialTexture(basePath, mtl.opacityTexPath, texturesFolderPath);
mtl.opacityTex = loadedTexture;
}
}
}
}
loadStats.buildStats.texturesTime = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
ObjectBuilder.ProgressInfo info = new ObjectBuilder.ProgressInfo();
objLoadingProgress.message = "Loading materials...";
//yield return null;
#if UNITY_EDITOR
objectBuilder.alternativeTexPath = altTexPath;
#endif
objectBuilder.buildOptions = buildOptions;
bool hasColors = dataSet.colorList.Count > 0;
bool hasMaterials = materialData != null;
objectBuilder.InitBuildMaterials(materialData, hasColors);
float objInitPerc = objLoadingProgress.percentage;
if (hasMaterials)
{
while (objectBuilder.BuildMaterials(info))
{
objLoadingProgress.percentage = objInitPerc + MATERIAL_PHASE_PERC * objectBuilder.NumImportedMaterials / materialData.Count;
}
loadStats.buildStats.materialsTime = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
}
objLoadingProgress.message = "Building scene objects...";
GameObject newObj = new GameObject(objName);
if (buildOptions.hideWhileLoading)
{
newObj.SetActive(false);
}
if (parentTransform != null) newObj.transform.SetParent(parentTransform.transform, false);
OnCreated(newObj, absolutePath);
////newObj.transform.localScale = Vector3.one * Scaling;
float initProgress = objLoadingProgress.percentage;
objectBuilder.StartBuildObjectAsync(dataSet, newObj);
while (objectBuilder.BuildObjectAsync(ref info))
{
objLoadingProgress.message = "Building scene objects... " + (info.objectsLoaded + info.groupsLoaded) + "/" + (dataSet.objectList.Count + info.numGroups);
objLoadingProgress.percentage = initProgress + BUILD_PHASE_PERC * (info.objectsLoaded / dataSet.objectList.Count + (float)info.groupsLoaded / info.numGroups);
}
objLoadingProgress.percentage = 100.0f;
loadedModels[absolutePath] = newObj;
loadStats.buildStats.objectsTime = Time.realtimeSinceStartup - prevTime;
}
protected async Task NetworkedBuild(Transform parentTransform, string objName, string objURL, string diffuseTexURL, string bumpTexURL, string specularTexURL, string opacityTexURL)
{
float prevTime = Time.realtimeSinceStartup;
if (materialData != null)
{
objLoadingProgress.message = "Loading textures...";
int count = 0;
foreach (MaterialData mtl in materialData)
{
objLoadingProgress.percentage = LOAD_PHASE_PERC + TEXTURE_PHASE_PERC * count / materialData.Count;
count++;
if (mtl.diffuseTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.diffuseTex = LoadAssetTexture(mtl.diffuseTexPath);
}
else
#endif
{
if(!string.IsNullOrWhiteSpace(diffuseTexURL))
{
try
{
await LoadMaterialTexture(diffuseTexURL);
}
catch (Exception exc)
{
throw exc;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.diffuseTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (mtl.bumpTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.bumpTex = LoadAssetTexture(mtl.bumpTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(bumpTexURL))
{
try
{
await LoadMaterialTexture(bumpTexURL);
}
catch (Exception exc)
{
throw exc;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.bumpTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (mtl.specularTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.specularTex = LoadAssetTexture(mtl.specularTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(specularTexURL))
{
try
{
await LoadMaterialTexture(specularTexURL);
}
catch (Exception exc)
{
throw exc;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.specularTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (mtl.opacityTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.opacityTex = LoadAssetTexture(mtl.opacityTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(opacityTexURL))
{
try
{
await LoadMaterialTexture(opacityTexURL);
}
catch (Exception exc)
{
throw exc;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.opacityTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
}
}
loadStats.buildStats.texturesTime = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
ObjectBuilder.ProgressInfo info = new ObjectBuilder.ProgressInfo();
objLoadingProgress.message = "Loading materials...";
//yield return null;
#if UNITY_EDITOR
objectBuilder.alternativeTexPath = altTexPath;
#endif
objectBuilder.buildOptions = buildOptions;
bool hasColors = dataSet.colorList.Count > 0;
bool hasMaterials = materialData != null;
objectBuilder.InitBuildMaterials(materialData, hasColors);
float objInitPerc = objLoadingProgress.percentage;
if (hasMaterials)
{
while (objectBuilder.BuildMaterials(info))
{
objLoadingProgress.percentage = objInitPerc + MATERIAL_PHASE_PERC * objectBuilder.NumImportedMaterials / materialData.Count;
await Task.Delay(0);
}
loadStats.buildStats.materialsTime = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
}
objLoadingProgress.message = "Building scene objects...";
GameObject newObj = new GameObject(objName);
if (buildOptions.hideWhileLoading)
{
newObj.SetActive(false);
}
if (parentTransform != null) newObj.transform.SetParent(parentTransform.transform, false);
OnCreated(newObj, objURL);
////newObj.transform.localScale = Vector3.one * Scaling;
float initProgress = objLoadingProgress.percentage;
objectBuilder.StartBuildObjectAsync(dataSet, newObj);
while (objectBuilder.BuildObjectAsync(ref info))
{
objLoadingProgress.message = "Building scene objects... " + (info.objectsLoaded + info.groupsLoaded) + "/" + (dataSet.objectList.Count + info.numGroups);
objLoadingProgress.percentage = initProgress + BUILD_PHASE_PERC * (info.objectsLoaded / dataSet.objectList.Count + (float)info.groupsLoaded / info.numGroups);
await Task.Delay(0);
}
objLoadingProgress.percentage = 100.0f;
loadedModels[objURL] = newObj;
loadStats.buildStats.objectsTime = Time.realtimeSinceStartup - prevTime;
}
protected IEnumerator NetworkedBuildWebGL(Transform parentTransform, string objName, string objURL, string diffuseTexURL, string bumpTexURL, string specularTexURL, string opacityTexURL)
{
float prevTime = Time.realtimeSinceStartup;
if (materialData != null)
{
objLoadingProgress.message = "Loading textures...";
int count = 0;
foreach (MaterialData mtl in materialData)
{
objLoadingProgress.percentage = LOAD_PHASE_PERC + TEXTURE_PHASE_PERC * count / materialData.Count;
count++;
if (mtl.diffuseTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.diffuseTex = LoadAssetTexture(mtl.diffuseTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(diffuseTexURL))
{
yield return StartCoroutine(LoadMaterialTextureWebGL(diffuseTexURL));
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.diffuseTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (mtl.bumpTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.bumpTex = LoadAssetTexture(mtl.bumpTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(bumpTexURL))
{
yield return StartCoroutine(LoadMaterialTextureWebGL(bumpTexURL));
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.bumpTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (mtl.specularTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.specularTex = LoadAssetTexture(mtl.specularTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(specularTexURL))
{
yield return StartCoroutine(LoadMaterialTextureWebGL(specularTexURL));
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.specularTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (mtl.opacityTexPath != null)
{
#if UNITY_EDITOR
if (ImportingAssets)
{
mtl.opacityTex = LoadAssetTexture(mtl.opacityTexPath);
}
else
#endif
{
if (!string.IsNullOrWhiteSpace(opacityTexURL))
{
yield return StartCoroutine(LoadMaterialTextureWebGL(opacityTexURL));
}
else
{
ObjectImporter.activeDownloads -= 1;
}
mtl.opacityTex = loadedTexture;
}
}
else
{
ObjectImporter.activeDownloads -= 1;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
}
}
loadStats.buildStats.texturesTime = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
ObjectBuilder.ProgressInfo info = new ObjectBuilder.ProgressInfo();
objLoadingProgress.message = "Loading materials...";
//yield return null;
#if UNITY_EDITOR
objectBuilder.alternativeTexPath = altTexPath;
#endif
objectBuilder.buildOptions = buildOptions;
bool hasColors = dataSet.colorList.Count > 0;
bool hasMaterials = materialData != null;
objectBuilder.InitBuildMaterials(materialData, hasColors);
float objInitPerc = objLoadingProgress.percentage;
if (hasMaterials)
{
while (objectBuilder.BuildMaterials(info))
{
objLoadingProgress.percentage = objInitPerc + MATERIAL_PHASE_PERC * objectBuilder.NumImportedMaterials / materialData.Count;
}
loadStats.buildStats.materialsTime = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
}
objLoadingProgress.message = "Building scene objects...";
GameObject newObj = new GameObject(objName);
if (buildOptions.hideWhileLoading)
{
newObj.SetActive(false);
}
if (parentTransform != null) newObj.transform.SetParent(parentTransform.transform, false);
OnCreated(newObj, objURL);
////newObj.transform.localScale = Vector3.one * Scaling;
float initProgress = objLoadingProgress.percentage;
objectBuilder.StartBuildObjectAsync(dataSet, newObj);
while (objectBuilder.BuildObjectAsync(ref info))
{
objLoadingProgress.message = "Building scene objects... " + (info.objectsLoaded + info.groupsLoaded) + "/" + (dataSet.objectList.Count + info.numGroups);
objLoadingProgress.percentage = initProgress + BUILD_PHASE_PERC * (info.objectsLoaded / dataSet.objectList.Count + (float)info.groupsLoaded / info.numGroups);
}
objLoadingProgress.percentage = 100.0f;
loadedModels[objURL] = newObj;
loadStats.buildStats.objectsTime = Time.realtimeSinceStartup - prevTime;
}
///
/// Get the directory name of the given path, appending the final slash if eeded.
///
/// the absolute path
/// the directory name ending with `/`
protected string GetDirName(string absolutePath)
{
string basePath;
if (absolutePath.Contains("//"))
{
basePath = absolutePath.Remove(absolutePath.LastIndexOf('/') + 1);
}
else
{
string dirName = Path.GetDirectoryName(absolutePath);
basePath = string.IsNullOrEmpty(dirName) ? "" : dirName;
if (!basePath.EndsWith("/"))
{
basePath += "/";
}
}
return basePath;
}
protected virtual void OnLoaded(GameObject obj, string absolutePath)
{
if (obj == null)
{
if (ModelError != null)
{
ModelError(absolutePath);
}
}
else
{
if (buildOptions != null)
{
obj.transform.localPosition = buildOptions.localPosition;
obj.transform.localRotation = Quaternion.Euler(buildOptions.localEulerAngles); ;
obj.transform.localScale = buildOptions.localScale;
if (buildOptions.inheritLayer)
{
obj.layer = obj.transform.parent.gameObject.layer;
MeshRenderer[] mrs = obj.transform.GetComponentsInChildren(true);
for (int i = 0; i < mrs.Length; i++)
{
mrs[i].gameObject.layer = obj.transform.parent.gameObject.layer;
}
}
}
if (buildOptions.hideWhileLoading)
{
obj.SetActive(true);
}
if (ModelLoaded != null)
{
ModelLoaded(obj, absolutePath);
}
}
}
protected virtual void OnCreated(GameObject obj, string absolutePath)
{
if (obj == null)
{
if (ModelError != null)
{
ModelError(absolutePath);
}
}
else
{
if (ModelCreated != null)
{
ModelCreated(obj, absolutePath);
}
}
}
protected virtual void OnLoadFailed(string absolutePath)
{
if (ModelError != null)
{
ModelError(absolutePath);
}
}
#if UNITY_EDITOR
///
/// Load a texture from the asset database
///
/// texture path inside the asset database
/// the loaded texture or null on error
private Texture2D LoadAssetTexture(string texturePath)
{
FileInfo textFileInfo = new FileInfo(texturePath);
string texpath = altTexPath + textFileInfo.Name;
texpath = texpath.Replace("//", "/");
Debug.LogFormat("Loading texture asset '{0}'", texpath);
return AssetDatabase.LoadAssetAtPath(texpath);
}
#endif
///
/// Convert a texture path to a texture URL and update the progress message
///
/// base texture path
/// relative texture path
/// URL of the texture
private string GetTextureUrl(string basePath, string texturePath)
{
string texPath = texturePath.Replace("\\", "/").Replace("//", "/");
if (!Path.IsPathRooted(texPath))
{
texPath = basePath + texturePath;
}
if (!texPath.Contains("//"))
{
texPath = "file:///" + texPath;
}
objLoadingProgress.message = "Loading textures...\n" + texPath;
return texPath;
}
private async Task LoadMaterialTexture(string basePath, string path, string texturesFolderPath="")
{
loadedTexture = null;
//string texPath = GetTextureUrl(basePath, path);
string textPath = string.IsNullOrWhiteSpace(texturesFolderPath) ? basePath + path : (texturesFolderPath + "\\" + path);
textPath = Path.GetFullPath(textPath);
//WWW loader = new WWW(texPath);
//yield return loader;
if (File.Exists(textPath))
{
byte[] result;
using (FileStream stream = File.Open(textPath, FileMode.Open))
{
result = new byte[stream.Length];
await stream.ReadAsync(result, 0, (int)stream.Length);
}
if (result.Length > 0)
{
Texture2D tex = new Texture2D(1, 1);
tex.LoadImage(result);
loadedTexture = tex;
}
}
else
{
Debug.LogWarning("Failed to load texture at path " + textPath + " BasePath " + basePath + " path " + path);
}
}
private async Task LoadMaterialTexture(string textureURL)
{
loadedTexture = null;
bool isWorking = true;
byte[] downloadedBytes = null;
float oldProgress = individualProgress.Value;
try
{
StartCoroutine(DownloadFile(textureURL, individualProgress, (bytes) =>
{
isWorking = false;
downloadedBytes = bytes;
//loadedText = Encoding.UTF8.GetString(bytes);
},
(error) =>
{
ObjectImporter.activeDownloads -= 1;
isWorking = false;
Debug.LogWarning("Failed to load the associated texture 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)
{
Texture2D tex = new Texture2D(1, 1);
tex.LoadImage(downloadedBytes);
loadedTexture = tex;
}
else
{
Debug.LogWarning("Failed to load texture.");
}
}
private IEnumerator LoadMaterialTextureWebGL(string textureURL)
{
loadedTexture = null;
bool isWorking = true;
float oldProgress = individualProgress.Value;
try
{
StartCoroutine(DownloadTexFileWebGL(textureURL, individualProgress, (texture) =>
{
isWorking = false;
loadedTexture = texture;
//loadedText = Encoding.UTF8.GetString(bytes);
},
(error) =>
{
ObjectImporter.activeDownloads -= 1;
isWorking = false;
Debug.LogWarning("Failed to load the associated texture 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)
{
yield return new WaitForSeconds(0.1f);
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
}
ObjectImporter.downloadProgress.Value = (individualProgress.Value / ObjectImporter.activeDownloads) * 100f;
if (loadedTexture == null)
{
Debug.LogWarning("Failed to load texture.");
}
}
///
/// Load a texture from the URL got from the parameter.
///
#if UNITY_2018_3_OR_NEWER
private Texture2D LoadTexture(UnityWebRequest loader)
#else
private Texture2D LoadTexture(WWW loader)
#endif
{
string ext = Path.GetExtension(loader.url).ToLower();
Texture2D tex = null;
// TODO: add support for more formats (bmp, gif, dds, ...)
if (ext == ".tga")
{
tex = TextureLoader.LoadTextureFromUrl(loader.url);
//tex = TgaLoader.LoadTGA(new MemoryStream(loader.bytes));
}
else if (ext == ".png" || ext == ".jpg" || ext == ".jpeg")
{
#if UNITY_2018_3_OR_NEWER
tex = DownloadHandlerTexture.GetContent(loader);
#else
tex = loader.texture;
#endif
}
else
{
Debug.LogWarning("Unsupported texture format: " + ext);
}
if (tex == null)
{
Debug.LogErrorFormat("Failed to load texture {0}", loader.url);
}
else
{
//tex.alphaIsTransparency = true;
//tex.filterMode = FilterMode.Trilinear;
}
return tex;
}
protected struct BuildStats
{
public float texturesTime;
public float materialsTime;
public float objectsTime;
}
protected struct Stats
{
public float modelParseTime;
public float materialsParseTime;
public float buildTime;
public BuildStats buildStats;
public float totalTime;
}
public IEnumerator DownloadFile(string url, ReferencedNumeric downloadProgress, Action DownloadComplete, Action OnError)
{
//Debug.Log("Into the DownloadFile Coroutine");
WWW www = null;
float oldProgress = downloadProgress.Value;
try
{
www = new WWW(url);
}
catch (Exception ex)
{
downloadProgress.Value = oldProgress;
OnError(ex.ToString());
}
Coroutine progress = StartCoroutine(GetProgress(www, downloadProgress));
yield return www;
if (!string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
else
{
if (www.bytes == null || www.bytes.Length == 0)
{
if (string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress;
OnError("No bytes downloaded. The file might be empty.");
}
else
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
}
else
{
if (string.IsNullOrWhiteSpace(www.error))
{
//Debug.Log("Calling Download Complete");
downloadProgress.Value = oldProgress + 1;
DownloadComplete(www.bytes);
}
else
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
}
}
StopCoroutine(progress);
www.Dispose();
//Debug.Log("End of File Download Coroutine.");
}
private IEnumerator GetProgress(WWW www, ReferencedNumeric downloadProgress)
{
float oldProgress = downloadProgress.Value;
if (www != null && downloadProgress != null)
{
while (!www.isDone && string.IsNullOrWhiteSpace(www.error))
{
yield return new WaitForSeconds(0.1f);
downloadProgress.Value = oldProgress + www.progress;
}
if(www.isDone && string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress + www.progress;
Debug.Log("Progress " + www.progress);
}
}
}
public IEnumerator DownloadFileWebGL(string url, ReferencedNumeric downloadProgress, Action DownloadComplete, Action OnError)
{
WWW www = null;
float oldProgress = downloadProgress.Value;
try
{
www = new WWW(url);
}
catch (Exception ex)
{
downloadProgress.Value = oldProgress;
OnError(ex.ToString());
}
Coroutine progress = StartCoroutine(GetProgress(www, downloadProgress));
yield return www;
if (!string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
else
{
if (www.bytes == null || www.bytes.Length == 0)
{
if (string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress;
OnError("No bytes downloaded. The file might be empty.");
}
else
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
}
else
{
if (string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress + 1;
DownloadComplete(www.text);
}
else
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
}
}
try
{
StopCoroutine(progress);
}
catch (Exception ex)
{
}
www.Dispose();
}
public IEnumerator DownloadTexFileWebGL(string url, ReferencedNumeric downloadProgress, Action DownloadComplete, Action OnError)
{
WWW www = null;
float oldProgress = downloadProgress.Value;
try
{
www = new WWW(url);
}
catch (Exception ex)
{
downloadProgress.Value = oldProgress;
OnError(ex.ToString());
}
Coroutine progress = StartCoroutine(GetProgress(www, downloadProgress));
yield return www;
if (!string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
else
{
if (www.bytes == null || www.bytes.Length == 0)
{
if (string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress;
OnError("No bytes downloaded. The file might be empty.");
}
else
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
}
else
{
if (string.IsNullOrWhiteSpace(www.error))
{
downloadProgress.Value = oldProgress + 1;
DownloadComplete(www.texture);
}
else
{
downloadProgress.Value = oldProgress;
OnError(www.error);
}
}
}
StopCoroutine(progress);
www.Dispose();
}
}
}