223 lines
7.7 KiB
C#
223 lines
7.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using UnityGLTF;
|
|
using UnityGLTF.Loader;
|
|
|
|
namespace Needle.Cloud.Runtime
|
|
{
|
|
[ExecuteInEditMode]
|
|
public class NeedleCloudAsset : MonoBehaviour, INeedleCloudAsset
|
|
{
|
|
public string Url
|
|
{
|
|
get => url;
|
|
set => url = value;
|
|
}
|
|
|
|
internal string LocalPath => lastPath;
|
|
|
|
internal static readonly List<NeedleCloudAsset> instances = new List<NeedleCloudAsset>();
|
|
|
|
public string url;
|
|
public string password;
|
|
public bool autoRefresh = true;
|
|
|
|
[SerializeField, HideInInspector] private string lastUrl;
|
|
[SerializeField, HideInInspector] private string lastPath;
|
|
|
|
[SerializeField, HideInInspector] private GameObject instance;
|
|
private bool isLoading;
|
|
private string lastLoggedError;
|
|
private bool gotErrorDuringLoading;
|
|
internal bool isMissingPassword;
|
|
|
|
private void OnEnable()
|
|
{
|
|
lastUrl = null;
|
|
lastLoggedError = null;
|
|
gotErrorDuringLoading = false;
|
|
|
|
instances.Add(this);
|
|
Load();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
lastLoggedError = null;
|
|
DestroyImmediate(instance);
|
|
instances.Remove(this);
|
|
}
|
|
|
|
[ContextMenu("Validate")]
|
|
private void OnValidate()
|
|
{
|
|
if (url != lastUrl && enabled)
|
|
{
|
|
Load();
|
|
}
|
|
}
|
|
[ContextMenu("ResetInternalState")]
|
|
private void ResetInternalState()
|
|
{
|
|
isLoading = false;
|
|
}
|
|
|
|
[ContextMenu(nameof(Reload))]
|
|
internal void Reload()
|
|
{
|
|
DestroyImmediate(instance);
|
|
Load();
|
|
}
|
|
|
|
internal async void Load()
|
|
{
|
|
if (string.IsNullOrEmpty(url) || !url.StartsWith("http"))
|
|
{
|
|
// No URL or not a valid URL
|
|
return;
|
|
}
|
|
|
|
var newUrl = url;
|
|
lastUrl = url;
|
|
|
|
var options = new DownloadManager.DownloadOptions();
|
|
options.Password = password;
|
|
var result = await DownloadManager.Download(url, options);
|
|
if (newUrl != url)
|
|
{
|
|
// URL changed during download
|
|
return;
|
|
}
|
|
|
|
isMissingPassword = false;
|
|
if (!result.Success)
|
|
{
|
|
// Something failed during downloading...
|
|
lastPath = null;
|
|
isMissingPassword = result.NeedsPassword;
|
|
if (result.Error != lastLoggedError)
|
|
{
|
|
lastLoggedError = result.Error;
|
|
Debug.LogError($"Error during cloud asset download. {result.Error}", this);
|
|
}
|
|
|
|
// If the URL is the same but we could not download it, then delete whatever instance is possibly still displayed
|
|
if (newUrl == url && instance)
|
|
{
|
|
DestroyImmediate(instance);
|
|
}
|
|
|
|
isLoading = false;
|
|
}
|
|
else if (!this)
|
|
{
|
|
// We got destroyed during download
|
|
}
|
|
// If the download path changed (OR we don't have an instance yet), then we want to build it
|
|
else if (result.Path != lastPath || (!instance && !gotErrorDuringLoading))
|
|
{
|
|
if (!isLoading)
|
|
{
|
|
// var logCollector = new LogCollector();
|
|
try
|
|
{
|
|
isLoading = true;
|
|
lastPath = result.Path;
|
|
DestroyImmediate(instance);
|
|
var path = result.Path;
|
|
var directory = Path.GetDirectoryName(path);
|
|
var opts = new ImportOptions
|
|
{
|
|
CameraImport = CameraImportOption.None,
|
|
DataLoader = new NeedleCloudUnityGLTFDataLoader(
|
|
password,
|
|
directory,
|
|
newUrl),
|
|
// logger = new Logger(logCollector)
|
|
};
|
|
var importer = new GLTFSceneImporter(path, opts);
|
|
importer.SceneParent = transform;
|
|
await importer.LoadSceneAsync(0, false);
|
|
instance = importer.LastLoadedScene;
|
|
if (instance)
|
|
{
|
|
instance.SetActive(true);
|
|
// If we show components, we potentially have issues if users try to reference these components which is currently not supported
|
|
instance.gameObject.hideFlags =
|
|
HideFlags.HideAndDontSave; // HideFlags.NotEditable | HideFlags.DontSave;
|
|
// We never want to export this (this is also currently used for NE to prevent it from being exported!)
|
|
instance.tag = "EditorOnly";
|
|
foreach (var comp in instance.GetComponentsInChildren<Transform>())
|
|
{
|
|
comp.gameObject.hideFlags = instance.gameObject.hideFlags;
|
|
}
|
|
}
|
|
gotErrorDuringLoading = false;
|
|
}
|
|
#pragma warning disable CS0168 // Variable is declared but never used
|
|
catch (Exception err)
|
|
#pragma warning restore CS0168 // Variable is declared but never used
|
|
{
|
|
lastLoggedError = err.Message;
|
|
gotErrorDuringLoading = true;
|
|
Debug.LogError($"Error during cloud asset import. {err}", this);
|
|
// logCollector.LogAndClear("Import messages:\n{0}");
|
|
}
|
|
finally
|
|
{
|
|
isLoading = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class NeedleCloudUnityGLTFDataLoader : IDataLoader
|
|
{
|
|
private readonly string password;
|
|
private readonly string directory;
|
|
private readonly string baseUrl;
|
|
|
|
public NeedleCloudUnityGLTFDataLoader(string password, string directory, string url)
|
|
{
|
|
this.password = password;
|
|
this.directory = directory;
|
|
var lastSlashIndex = url.LastIndexOf('/');
|
|
this.baseUrl = url.Substring(0, lastSlashIndex + 1);
|
|
}
|
|
|
|
public async Task<Stream> LoadStreamAsync(string relativeFilePath)
|
|
{
|
|
if (File.Exists(relativeFilePath))
|
|
{
|
|
return File.OpenRead(relativeFilePath);
|
|
}
|
|
|
|
var newFullPath = Path.Combine(this.directory, relativeFilePath);
|
|
if (File.Exists(newFullPath))
|
|
{
|
|
return File.OpenRead(newFullPath);
|
|
}
|
|
|
|
Debug.Log($"Download resource: \"{relativeFilePath}\" to {newFullPath}");
|
|
var newUrl = this.baseUrl + relativeFilePath;
|
|
|
|
var options = new DownloadManager.DownloadOptions();
|
|
options.Filename = relativeFilePath;
|
|
options.TargetDirectory = Path.GetDirectoryName(newFullPath);
|
|
options.Password = password;
|
|
|
|
var res = await DownloadManager.Download(newUrl, options);
|
|
if (res.Success)
|
|
{
|
|
return File.OpenRead(res.Path);
|
|
}
|
|
Debug.LogError(res.Error);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
} |