mirror of
https://github.com/Outer-Wilds-New-Horizons/new-horizons.git
synced 2025-12-11 20:15:44 +01:00
Cache atlas, works first loop
This commit is contained in:
parent
540a501b9a
commit
31ada350b4
@ -11,11 +11,18 @@ using System.IO;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using static NewHorizons.Main;
|
using static NewHorizons.Main;
|
||||||
|
using System.Linq;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
using Epic.OnlineServices.Presence;
|
||||||
|
|
||||||
namespace NewHorizons.Builder.Props
|
namespace NewHorizons.Builder.Props
|
||||||
{
|
{
|
||||||
public static class ProjectionBuilder
|
public static class ProjectionBuilder
|
||||||
{
|
{
|
||||||
|
public const string INVERTED_SLIDE_CACHE_FOLDER = "SlideReelCache/Inverted";
|
||||||
|
public const string ATLAS_SLIDE_CACHE_FOLDER = "SlideReelCache/Atlas";
|
||||||
|
|
||||||
public static GameObject SlideReelWholePrefab { get; private set; }
|
public static GameObject SlideReelWholePrefab { get; private set; }
|
||||||
public static GameObject SlideReelWholePristinePrefab { get; private set; }
|
public static GameObject SlideReelWholePristinePrefab { get; private set; }
|
||||||
public static GameObject SlideReelWholeRustedPrefab { get; private set; }
|
public static GameObject SlideReelWholeRustedPrefab { get; private set; }
|
||||||
@ -113,6 +120,8 @@ namespace NewHorizons.Builder.Props
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetUniqueSlideReelID(IModBehaviour mod, SlideInfo[] slides) => $"{mod.ModHelper.Manifest.UniqueName}{slides.Join(x => x.imagePath)}".GetHashCode().ToString();
|
||||||
|
|
||||||
private static GameObject MakeSlideReel(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod)
|
private static GameObject MakeSlideReel(GameObject planetGO, Sector sector, ProjectionInfo info, IModBehaviour mod)
|
||||||
{
|
{
|
||||||
InitPrefabs();
|
InitPrefabs();
|
||||||
@ -144,6 +153,8 @@ namespace NewHorizons.Builder.Props
|
|||||||
|
|
||||||
var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection);
|
var imageLoader = AddAsyncLoader(slideReelObj, mod, info.slides, ref slideCollection);
|
||||||
|
|
||||||
|
var key = GetUniqueSlideReelID(mod, info.slides);
|
||||||
|
|
||||||
// this variable just lets us track how many of the first 15 slides have been loaded.
|
// this variable just lets us track how many of the first 15 slides have been loaded.
|
||||||
// this way as soon as the last one is loaded (due to async loading, this may be
|
// this way as soon as the last one is loaded (due to async loading, this may be
|
||||||
// slide 7, or slide 3, or whatever), we can build the slide reel texture. This allows us
|
// slide 7, or slide 3, or whatever), we can build the slide reel texture. This allows us
|
||||||
@ -154,7 +165,7 @@ namespace NewHorizons.Builder.Props
|
|||||||
{
|
{
|
||||||
var time = DateTime.Now;
|
var time = DateTime.Now;
|
||||||
|
|
||||||
slideCollection.slides[index]._image = ImageUtilities.Invert(mod, tex, originalPath);
|
slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath);
|
||||||
NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms");
|
NHLogger.LogVerbose($"Slide reel make reel invert texture {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||||
// Track the first 15 to put on the slide reel object
|
// Track the first 15 to put on the slide reel object
|
||||||
if (index < textures.Length)
|
if (index < textures.Length)
|
||||||
@ -167,7 +178,7 @@ namespace NewHorizons.Builder.Props
|
|||||||
var slidesFront = slideReelObj.GetComponentInChildren<TransformAnimator>().transform.Find("Slides_Front").GetComponent<MeshRenderer>();
|
var slidesFront = slideReelObj.GetComponentInChildren<TransformAnimator>().transform.Find("Slides_Front").GetComponent<MeshRenderer>();
|
||||||
|
|
||||||
// Now put together the textures into a 4x4 thing for the materials
|
// Now put together the textures into a 4x4 thing for the materials
|
||||||
var reelTexture = ImageUtilities.MakeReelTexture(textures);
|
var reelTexture = ImageUtilities.MakeReelTexture(mod, textures, key);
|
||||||
slidesBack.material.mainTexture = reelTexture;
|
slidesBack.material.mainTexture = reelTexture;
|
||||||
slidesBack.material.SetTexture(EmissionMap, reelTexture);
|
slidesBack.material.SetTexture(EmissionMap, reelTexture);
|
||||||
slidesBack.material.name = reelTexture.name;
|
slidesBack.material.name = reelTexture.name;
|
||||||
@ -308,7 +319,7 @@ namespace NewHorizons.Builder.Props
|
|||||||
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
|
imageLoader.imageLoadedEvent.AddListener((Texture2D tex, int index, string originalPath) =>
|
||||||
{
|
{
|
||||||
var time = DateTime.Now;
|
var time = DateTime.Now;
|
||||||
slideCollection.slides[index]._image = ImageUtilities.Invert(mod, tex, originalPath);
|
slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath);
|
||||||
NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms");
|
NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -443,10 +454,21 @@ namespace NewHorizons.Builder.Props
|
|||||||
return standingTorch;
|
return standingTorch;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImageUtilities.AsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection)
|
private static SlideReelAsyncImageLoader AddAsyncLoader(GameObject gameObject, IModBehaviour mod, SlideInfo[] slides, ref SlideCollection slideCollection)
|
||||||
{
|
{
|
||||||
var invertedImageLoader = gameObject.AddComponent<ImageUtilities.AsyncImageLoader>();
|
var invertedImageLoader = new SlideReelAsyncImageLoader();
|
||||||
var imageLoader = gameObject.AddComponent<ImageUtilities.AsyncImageLoader>();
|
var atlasImageLoader = new SlideReelAsyncImageLoader();
|
||||||
|
var imageLoader = new SlideReelAsyncImageLoader();
|
||||||
|
|
||||||
|
var textureNames = slides.Select(x => Path.GetFileNameWithoutExtension(x.imagePath)).ToArray();
|
||||||
|
var atlasKey = GetUniqueSlideReelID(mod, slides);
|
||||||
|
|
||||||
|
atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png")));
|
||||||
|
atlasImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) =>
|
||||||
|
{
|
||||||
|
NHLogger.Log($"SLIDE REEL ATLAS from {slides.First().imagePath}: {s}");
|
||||||
|
ImageUtilities.TrackCachedTexture(atlasKey, t);
|
||||||
|
});
|
||||||
for (int i = 0; i < slides.Length; i++)
|
for (int i = 0; i < slides.Length; i++)
|
||||||
{
|
{
|
||||||
var slide = new Slide();
|
var slide = new Slide();
|
||||||
@ -459,7 +481,7 @@ namespace NewHorizons.Builder.Props
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "invertedSlideReelCache", slideInfo.imagePath)));
|
invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath)));
|
||||||
invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) =>
|
invertedImageLoader.imageLoadedEvent.AddListener((Texture2D t, int i, string s) =>
|
||||||
{
|
{
|
||||||
var path = "\\" + Path.Combine(Path.GetFileName(Path.GetDirectoryName(mod.ModHelper.Manifest.ModFolderPath)), slides[i].imagePath) + " > invert";
|
var path = "\\" + Path.Combine(Path.GetFileName(Path.GetDirectoryName(mod.ModHelper.Manifest.ModFolderPath)), slides[i].imagePath) + " > invert";
|
||||||
@ -472,8 +494,11 @@ namespace NewHorizons.Builder.Props
|
|||||||
|
|
||||||
slideCollection.slides[i] = slide;
|
slideCollection.slides[i] = slide;
|
||||||
}
|
}
|
||||||
invertedImageLoader.Start();
|
// Loaders go sequentually - Load the inverted textures to the cache so that ImageUtilities will reuse them later
|
||||||
imageLoader.Start();
|
invertedImageLoader.Start(true);
|
||||||
|
// Atlas texture next so that the normal iamgeLoader knows not to regenerate them unless they were missing
|
||||||
|
atlasImageLoader.Start(false);
|
||||||
|
imageLoader.Start(true);
|
||||||
|
|
||||||
return imageLoader;
|
return imageLoader;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,12 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
|
using NewHorizons.Builder.Props;
|
||||||
using NewHorizons.Utility.OWML;
|
using NewHorizons.Utility.OWML;
|
||||||
using OWML.Common;
|
using OWML.Common;
|
||||||
using OWML.ModHelper;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
namespace NewHorizons.Utility.Files
|
namespace NewHorizons.Utility.Files
|
||||||
{
|
{
|
||||||
@ -102,7 +97,7 @@ namespace NewHorizons.Utility.Files
|
|||||||
/// used specifically for projected slides.
|
/// used specifically for projected slides.
|
||||||
/// also adds a border (to prevent weird visual bug) and makes the texture linear (otherwise the projected image is too bright).
|
/// also adds a border (to prevent weird visual bug) and makes the texture linear (otherwise the projected image is too bright).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Texture2D Invert(IModBehaviour mod, Texture2D texture, string originalPath)
|
public static Texture2D InvertSlideReel(IModBehaviour mod, Texture2D texture, string originalPath)
|
||||||
{
|
{
|
||||||
var key = $"{texture.name} > invert";
|
var key = $"{texture.name} > invert";
|
||||||
if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture;
|
if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture;
|
||||||
@ -138,9 +133,11 @@ namespace NewHorizons.Utility.Files
|
|||||||
|
|
||||||
_textureCache.Add(key, newTexture);
|
_textureCache.Add(key, newTexture);
|
||||||
|
|
||||||
|
// Since doing this is expensive we cache the results to the disk
|
||||||
|
// Preloading cached values is done in ProjectionBuilder
|
||||||
if (!string.IsNullOrEmpty(originalPath))
|
if (!string.IsNullOrEmpty(originalPath))
|
||||||
{
|
{
|
||||||
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "invertedSlideReelCache", originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, ""));
|
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, ""));
|
||||||
NHLogger.LogVerbose($"Caching inverted image to {path}");
|
NHLogger.LogVerbose($"Caching inverted image to {path}");
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
File.WriteAllBytes(path, newTexture.EncodeToPNG());
|
File.WriteAllBytes(path, newTexture.EncodeToPNG());
|
||||||
@ -149,15 +146,14 @@ namespace NewHorizons.Utility.Files
|
|||||||
return newTexture;
|
return newTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Texture2D MakeReelTexture(Texture2D[] textures)
|
public static Texture2D MakeReelTexture(IModBehaviour mod, Texture2D[] textures, string uniqueSlideReelID)
|
||||||
{
|
{
|
||||||
var key = $"SlideReelAtlas of {textures.Join(x => x.name)}";
|
if (_textureCache.TryGetValue(uniqueSlideReelID, out var existingTexture)) return (Texture2D)existingTexture;
|
||||||
if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture;
|
|
||||||
|
|
||||||
var size = 256;
|
var size = 256;
|
||||||
|
|
||||||
var texture = new Texture2D(size * 4, size * 4, TextureFormat.ARGB32, false);
|
var texture = new Texture2D(size * 4, size * 4, TextureFormat.ARGB32, false);
|
||||||
texture.name = key;
|
texture.name = uniqueSlideReelID;
|
||||||
|
|
||||||
var fillPixels = new Color[size * size * 4 * 4];
|
var fillPixels = new Color[size * size * 4 * 4];
|
||||||
for (int xIndex = 0; xIndex < 4; xIndex++)
|
for (int xIndex = 0; xIndex < 4; xIndex++)
|
||||||
@ -199,7 +195,14 @@ namespace NewHorizons.Utility.Files
|
|||||||
texture.SetPixels(fillPixels);
|
texture.SetPixels(fillPixels);
|
||||||
texture.Apply();
|
texture.Apply();
|
||||||
|
|
||||||
_textureCache.Add(key, texture);
|
_textureCache.Add(uniqueSlideReelID, texture);
|
||||||
|
|
||||||
|
// Since doing this is expensive we cache the results to the disk
|
||||||
|
// Preloading cached values is done in ProjectionBuilder
|
||||||
|
var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.ATLAS_SLIDE_CACHE_FOLDER, $"{uniqueSlideReelID}.png");
|
||||||
|
NHLogger.LogVerbose($"Caching atlas image to {path}");
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
File.WriteAllBytes(path, texture.EncodeToPNG());
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
@ -440,148 +443,5 @@ namespace NewHorizons.Utility.Files
|
|||||||
sprite.name = texture.name;
|
sprite.name = texture.name;
|
||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SingletonAsyncImageLoader : MonoBehaviour
|
|
||||||
{
|
|
||||||
public static SingletonAsyncImageLoader Instance { get; private set; }
|
|
||||||
|
|
||||||
private Queue<AsyncImageLoader> loaders = new();
|
|
||||||
|
|
||||||
private bool _isLoading;
|
|
||||||
|
|
||||||
public void Awake()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load(AsyncImageLoader loader)
|
|
||||||
{
|
|
||||||
loaders.Enqueue(loader);
|
|
||||||
if (!_isLoading)
|
|
||||||
{
|
|
||||||
StartCoroutine(Run());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator Run()
|
|
||||||
{
|
|
||||||
NHLogger.Log("Loading slide reels");
|
|
||||||
_isLoading = true;
|
|
||||||
while (loaders.Count > 0)
|
|
||||||
{
|
|
||||||
var loader = loaders.Dequeue();
|
|
||||||
yield return loader.DownloadTextures();
|
|
||||||
NHLogger.Log($"Finished a slide reel, {loaders.Count} left");
|
|
||||||
}
|
|
||||||
_isLoading = false;
|
|
||||||
NHLogger.Log("Done loading slide reels");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modified from https://stackoverflow.com/a/69141085/9643841
|
|
||||||
public class AsyncImageLoader : MonoBehaviour
|
|
||||||
{
|
|
||||||
public List<(int index, string path)> PathsToLoad { get; private set; } = new();
|
|
||||||
|
|
||||||
public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { }
|
|
||||||
public ImageLoadedEvent imageLoadedEvent = new();
|
|
||||||
|
|
||||||
private readonly object _lockObj = new();
|
|
||||||
|
|
||||||
public bool FinishedLoading { get; private set; }
|
|
||||||
private int _loadedCount = 0;
|
|
||||||
|
|
||||||
// TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader,
|
|
||||||
// and make use of that for at least for projector stuff (require player to be in the same sector as the slides
|
|
||||||
// for them to start loading, and unload when the player leaves)
|
|
||||||
|
|
||||||
private bool _started;
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
if (_started) return;
|
|
||||||
|
|
||||||
_started = true;
|
|
||||||
|
|
||||||
if (SingletonAsyncImageLoader.Instance == null)
|
|
||||||
{
|
|
||||||
Main.Instance.gameObject.AddComponent<SingletonAsyncImageLoader>();
|
|
||||||
}
|
|
||||||
|
|
||||||
NHLogger.LogVerbose("Loading new slide reel");
|
|
||||||
imageLoadedEvent.AddListener(OnImageLoaded);
|
|
||||||
SingletonAsyncImageLoader.Instance.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnImageLoaded(Texture texture, int index, string originalPath)
|
|
||||||
{
|
|
||||||
lock (_lockObj)
|
|
||||||
{
|
|
||||||
_loadedCount++;
|
|
||||||
|
|
||||||
if (_loadedCount >= PathsToLoad.Count)
|
|
||||||
{
|
|
||||||
NHLogger.LogVerbose($"Finished loading all textures for {gameObject.name} (one was {PathsToLoad.FirstOrDefault()}");
|
|
||||||
FinishedLoading = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IEnumerator DownloadTextures()
|
|
||||||
{
|
|
||||||
foreach (var (index, path) in PathsToLoad)
|
|
||||||
{
|
|
||||||
NHLogger.Log($"Loaded slide reel {index} of {PathsToLoad.Count}");
|
|
||||||
|
|
||||||
yield return DownloadTexture(path, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator DownloadTexture(string url, int index)
|
|
||||||
{
|
|
||||||
var key = GetKey(url);
|
|
||||||
if (_textureCache.TryGetValue(key, out var existingTexture))
|
|
||||||
{
|
|
||||||
NHLogger.LogVerbose($"Already loaded image {index}:{url}");
|
|
||||||
imageLoadedEvent?.Invoke((Texture2D)existingTexture, index, url);
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url);
|
|
||||||
|
|
||||||
yield return uwr.SendWebRequest();
|
|
||||||
|
|
||||||
var hasError = uwr.error != null && uwr.error != "";
|
|
||||||
|
|
||||||
if (hasError)
|
|
||||||
{
|
|
||||||
NHLogger.LogError($"Failed to load {index}:{url} - {uwr.error}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var handler = (DownloadHandlerTexture)uwr.downloadHandler;
|
|
||||||
|
|
||||||
var texture = DownloadHandlerTexture.GetContent(uwr);
|
|
||||||
texture.name = key;
|
|
||||||
texture.wrapMode = TextureWrapMode.Clamp;
|
|
||||||
|
|
||||||
if (_textureCache.TryGetValue(key, out existingTexture))
|
|
||||||
{
|
|
||||||
NHLogger.LogVerbose($"Already loaded image {index}:{url}");
|
|
||||||
Destroy(texture);
|
|
||||||
texture = (Texture2D)existingTexture;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_textureCache.Add(key, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return null;
|
|
||||||
var time = DateTime.Now;
|
|
||||||
imageLoadedEvent?.Invoke(texture, index, url);
|
|
||||||
NHLogger.LogVerbose($"Slide reel event? {(DateTime.Now - time).TotalMilliseconds}ms");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
162
NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs
Normal file
162
NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
using NewHorizons.Utility.OWML;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Events;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
namespace NewHorizons.Utility.Files;
|
||||||
|
|
||||||
|
// Modified from https://stackoverflow.com/a/69141085/9643841
|
||||||
|
// Having more than one SlideReelAsyncImageLoader running at the same time is LAGGY. While loading the image data from the disk is async, nothing else is!
|
||||||
|
// It will load the images async and then do tens to hundreds of callbacks to imageLoadedEvent all running at around the same time and lagging out the game
|
||||||
|
// This is why we do it sequentially using SingletonAsyncImageLoader
|
||||||
|
public class SlideReelAsyncImageLoader
|
||||||
|
{
|
||||||
|
public List<(int index, string path)> PathsToLoad { get; private set; } = new();
|
||||||
|
|
||||||
|
public class ImageLoadedEvent : UnityEvent<Texture2D, int, string> { }
|
||||||
|
public ImageLoadedEvent imageLoadedEvent = new();
|
||||||
|
|
||||||
|
private readonly object _lockObj = new();
|
||||||
|
|
||||||
|
public bool FinishedLoading { get; private set; }
|
||||||
|
private int _loadedCount = 0;
|
||||||
|
|
||||||
|
// TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader,
|
||||||
|
// and make use of that for at least for projector stuff (require player to be in the same sector as the slides
|
||||||
|
// for them to start loading, and unload when the player leaves)
|
||||||
|
|
||||||
|
private bool _started;
|
||||||
|
private bool _clamp;
|
||||||
|
|
||||||
|
public void Start(bool clamp)
|
||||||
|
{
|
||||||
|
if (_started) return;
|
||||||
|
|
||||||
|
_clamp = clamp;
|
||||||
|
|
||||||
|
_started = true;
|
||||||
|
|
||||||
|
if (SingletonSlideReelAsyncImageLoader.Instance == null)
|
||||||
|
{
|
||||||
|
Main.Instance.gameObject.AddComponent<SingletonSlideReelAsyncImageLoader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
NHLogger.LogVerbose("Loading new slide reel");
|
||||||
|
imageLoadedEvent.AddListener(OnImageLoaded);
|
||||||
|
SingletonSlideReelAsyncImageLoader.Instance.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnImageLoaded(Texture texture, int index, string originalPath)
|
||||||
|
{
|
||||||
|
lock (_lockObj)
|
||||||
|
{
|
||||||
|
_loadedCount++;
|
||||||
|
|
||||||
|
if (_loadedCount >= PathsToLoad.Count)
|
||||||
|
{
|
||||||
|
NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}");
|
||||||
|
FinishedLoading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IEnumerator DownloadTextures()
|
||||||
|
{
|
||||||
|
foreach (var (index, path) in PathsToLoad)
|
||||||
|
{
|
||||||
|
NHLogger.LogVerbose($"Loaded slide reel {index} of {PathsToLoad.Count}");
|
||||||
|
|
||||||
|
yield return DownloadTexture(path, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator DownloadTexture(string url, int index)
|
||||||
|
{
|
||||||
|
var key = ImageUtilities.GetKey(url);
|
||||||
|
if (ImageUtilities.CheckCachedTexture(key, out var existingTexture))
|
||||||
|
{
|
||||||
|
NHLogger.LogVerbose($"Already loaded image {index}:{url}");
|
||||||
|
imageLoadedEvent?.Invoke((Texture2D)existingTexture, index, url);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
using UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url);
|
||||||
|
|
||||||
|
yield return uwr.SendWebRequest();
|
||||||
|
|
||||||
|
var hasError = uwr.error != null && uwr.error != "";
|
||||||
|
|
||||||
|
if (hasError)
|
||||||
|
{
|
||||||
|
NHLogger.LogError($"Failed to load {index}:{url} - {uwr.error}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var handler = (DownloadHandlerTexture)uwr.downloadHandler;
|
||||||
|
|
||||||
|
var texture = DownloadHandlerTexture.GetContent(uwr);
|
||||||
|
texture.name = key;
|
||||||
|
if (_clamp)
|
||||||
|
{
|
||||||
|
texture.wrapMode = TextureWrapMode.Clamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImageUtilities.CheckCachedTexture(key, out existingTexture))
|
||||||
|
{
|
||||||
|
NHLogger.LogVerbose($"Already loaded image {index}:{url}");
|
||||||
|
GameObject.Destroy(texture);
|
||||||
|
texture = (Texture2D)existingTexture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImageUtilities.TrackCachedTexture(key, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return null;
|
||||||
|
var time = DateTime.Now;
|
||||||
|
imageLoadedEvent?.Invoke(texture, index, url);
|
||||||
|
NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SingletonSlideReelAsyncImageLoader : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static SingletonSlideReelAsyncImageLoader Instance { get; private set; }
|
||||||
|
|
||||||
|
private Queue<SlideReelAsyncImageLoader> _loaders = new();
|
||||||
|
|
||||||
|
private bool _isLoading;
|
||||||
|
|
||||||
|
public void Awake()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(SlideReelAsyncImageLoader loader)
|
||||||
|
{
|
||||||
|
_loaders.Enqueue(loader);
|
||||||
|
if (!_isLoading)
|
||||||
|
{
|
||||||
|
StartCoroutine(Run());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator Run()
|
||||||
|
{
|
||||||
|
NHLogger.Log("Loading slide reels");
|
||||||
|
_isLoading = true;
|
||||||
|
while (_loaders.Count > 0)
|
||||||
|
{
|
||||||
|
var loader = _loaders.Dequeue();
|
||||||
|
yield return loader.DownloadTextures();
|
||||||
|
NHLogger.Log($"Finished a slide reel, {_loaders.Count} left");
|
||||||
|
}
|
||||||
|
_isLoading = false;
|
||||||
|
NHLogger.Log("Done loading slide reels");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user