diff --git a/GameData/KSPCommunityFixes/Settings.cfg b/GameData/KSPCommunityFixes/Settings.cfg
index d5ceb7d..5ba87e3 100644
--- a/GameData/KSPCommunityFixes/Settings.cfg
+++ b/GameData/KSPCommunityFixes/Settings.cfg
@@ -4,6 +4,9 @@
KSP_COMMUNITY_FIXES
{
+ OnDemandPartTextures = true
+
+
// ##########################
// Major bugfixes
// ##########################
diff --git a/KSPCommunityFixes/KSPCommunityFixes.csproj b/KSPCommunityFixes/KSPCommunityFixes.csproj
index 5ccec49..84d10ec 100644
--- a/KSPCommunityFixes/KSPCommunityFixes.csproj
+++ b/KSPCommunityFixes/KSPCommunityFixes.csproj
@@ -170,6 +170,7 @@
+
diff --git a/KSPCommunityFixes/Library/StaticHelpers.cs b/KSPCommunityFixes/Library/StaticHelpers.cs
index ab45b73..31617c7 100644
--- a/KSPCommunityFixes/Library/StaticHelpers.cs
+++ b/KSPCommunityFixes/Library/StaticHelpers.cs
@@ -7,6 +7,36 @@
namespace KSPCommunityFixes
{
+ // see https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
+ internal enum DDSFourCC : uint
+ {
+ DXT1 = 0x31545844, // "DXT1"
+ DXT2 = 0x32545844, // "DXT2"
+ DXT3 = 0x33545844, // "DXT3"
+ DXT4 = 0x34545844, // "DXT4"
+ DXT5 = 0x35545844, // "DXT5"
+ BC4U_ATI = 0x31495441, // "ATI1" (actually BC4U)
+ BC4U = 0x55344342, // "BC4U"
+ BC4S = 0x53344342, // "BC4S"
+ BC5U_ATI = 0x32495441, // "ATI2" (actually BC5U)
+ BC5U = 0x55354342, // "BC5U"
+ BC5S = 0x53354342, // "BC5S"
+ RGBG = 0x47424752, // "RGBG"
+ GRGB = 0x42475247, // "GRGB"
+ UYVY = 0x59565955, // "UYVY"
+ YUY2 = 0x32595559, // "YUY2"
+ DX10 = 0x30315844, // "DX10", actual DXGI format specified in DX10 header
+ R16G16B16A16_UNORM = 36,
+ R16G16B16A16_SNORM = 110,
+ R16_FLOAT = 111,
+ R16G16_FLOAT = 112,
+ R16G16B16A16_FLOAT = 113,
+ R32_FLOAT = 114,
+ R32G32_FLOAT = 115,
+ R32G32B32A32_FLOAT = 116,
+ CxV8U8 = 117,
+ }
+
static class StaticHelpers
{
public static string HumanReadableBytes(long bytes)
diff --git a/KSPCommunityFixes/Performance/FastLoader.cs b/KSPCommunityFixes/Performance/FastLoader.cs
index f26b41a..24e49cf 100644
--- a/KSPCommunityFixes/Performance/FastLoader.cs
+++ b/KSPCommunityFixes/Performance/FastLoader.cs
@@ -303,6 +303,8 @@ static IEnumerator FastAssetLoader(List configFileTypes)
gdb.progressTitle = "Searching assets to load...";
yield return null;
+ OnDemandPartTextures.GetTextures(out HashSet allPartTextures);
+
double nextFrameTime = ElapsedTime + minFrameTimeD;
// Files loaded by our custom loaders
@@ -350,26 +352,29 @@ static IEnumerator FastAssetLoader(List configFileTypes)
}
break;
case FileType.Texture:
+
+ bool isOnDemand = allPartTextures.Contains(file.url);
+
switch (file.fileExtension)
{
case "dds":
- textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureDDS));
+ textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureDDS, isOnDemand));
break;
case "jpg":
case "jpeg":
- textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureJPG));
+ textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureJPG, isOnDemand));
break;
case "mbm":
- textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureMBM));
+ textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureMBM, isOnDemand));
break;
case "png":
- textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TexturePNG));
+ textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TexturePNG, isOnDemand));
break;
case "tga":
- textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureTGA));
+ textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureTGA, isOnDemand));
break;
case "truecolor":
- textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureTRUECOLOR));
+ textureAssets.Add(new RawAsset(file, RawAsset.AssetType.TextureTRUECOLOR, isOnDemand));
break;
default:
unsupportedTextureFiles.Add(file);
@@ -802,6 +807,12 @@ static void ReadAssetsThread(List files, Deque buffer)
{
foreach (RawAsset rawAsset in files)
{
+ if (rawAsset.IsOnDemand)
+ {
+ buffer.AddToFront(rawAsset);
+ continue;
+ }
+
rawAsset.ReadFromDiskWorkerThread();
SpinWait spin = new SpinWait();
@@ -836,7 +847,7 @@ static void ReadAssetsThread(List files, Deque buffer)
///
/// Asset wrapper class, actual implementation of the disk reader, individual texture/model formats loaders
///
- private class RawAsset
+ internal class RawAsset
{
public enum AssetType
{
@@ -881,18 +892,21 @@ public enum Result
private BinaryReader binaryReader;
private Result result;
private string resultMessage;
+ private bool isOnDemand;
public UrlFile File => file;
public Result State => result;
public string Message => resultMessage;
public int DataLength => dataLength;
public string TypeName => assetTypeNames[(int)assetType];
+ public bool IsOnDemand => isOnDemand;
- public RawAsset(UrlFile file, AssetType assetType)
+ public RawAsset(UrlFile file, AssetType assetType, bool isOnDemand = false)
{
this.result = Result.Valid;
this.file = file;
this.assetType = assetType;
+ this.isOnDemand = isOnDemand;
}
private void SetError(string message)
@@ -1002,35 +1016,49 @@ public void LoadAndDisposeMainThread()
if (file.fileType == FileType.Texture)
{
TextureInfo textureInfo;
- switch (assetType)
+
+ if (isOnDemand)
{
- case AssetType.TextureDDS:
- textureInfo = LoadDDS();
- break;
- case AssetType.TextureJPG:
- textureInfo = LoadJPG();
- break;
- case AssetType.TextureMBM:
- textureInfo = LoadMBM();
- break;
- case AssetType.TexturePNG:
- textureInfo = LoadPNG();
- break;
- case AssetType.TexturePNGCached:
- textureInfo = LoadPNGCached();
- break;
- case AssetType.TextureTGA:
- textureInfo = LoadTGA();
- break;
- case AssetType.TextureTRUECOLOR:
- textureInfo = LoadTRUECOLOR();
- break;
- default:
- SetError("Unknown texture format");
- return;
+ textureInfo = new OnDemandTextureInfo(file, assetType);
}
+ else
+ {
+ switch (assetType)
+ {
+ case AssetType.TextureDDS:
+ textureInfo = LoadDDS();
+ break;
+ case AssetType.TextureJPG:
+ textureInfo = LoadJPG();
+ break;
+ case AssetType.TextureMBM:
+ textureInfo = LoadMBM();
+ break;
+ case AssetType.TexturePNG:
+ textureInfo = LoadPNG();
+ break;
+ case AssetType.TexturePNGCached:
+ textureInfo = LoadPNGCached();
+ break;
+ case AssetType.TextureTGA:
+ textureInfo = LoadTGA();
+ break;
+ case AssetType.TextureTRUECOLOR:
+ textureInfo = LoadTRUECOLOR();
+ break;
+ default:
+ SetError("Unknown texture format");
+ return;
+ }
- if (result == Result.Failed || textureInfo == null || textureInfo.texture.IsNullOrDestroyed())
+ if (textureInfo.texture.IsNullOrDestroyed())
+ result = Result.Failed;
+
+ textureInfo.texture.name = file.url;
+ textureInfo.name = file.url;
+ }
+
+ if (result == Result.Failed || textureInfo == null)
{
result = Result.Failed;
if (string.IsNullOrEmpty(resultMessage))
@@ -1038,8 +1066,6 @@ public void LoadAndDisposeMainThread()
}
else
{
- textureInfo.name = file.url;
- textureInfo.texture.name = file.url;
Instance.databaseTexture.Add(textureInfo);
}
}
@@ -1110,36 +1136,6 @@ public void CheckTextureCache()
this.cachedTextureInfo = cachedTextureInfo;
}
- // see https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
- private enum DDSFourCC : uint
- {
- DXT1 = 0x31545844, // "DXT1"
- DXT2 = 0x32545844, // "DXT2"
- DXT3 = 0x33545844, // "DXT3"
- DXT4 = 0x34545844, // "DXT4"
- DXT5 = 0x35545844, // "DXT5"
- BC4U_ATI = 0x31495441, // "ATI1" (actually BC4U)
- BC4U = 0x55344342, // "BC4U"
- BC4S = 0x53344342, // "BC4S"
- BC5U_ATI = 0x32495441, // "ATI2" (actually BC5U)
- BC5U = 0x55354342, // "BC5U"
- BC5S = 0x53354342, // "BC5S"
- RGBG = 0x47424752, // "RGBG"
- GRGB = 0x42475247, // "GRGB"
- UYVY = 0x59565955, // "UYVY"
- YUY2 = 0x32595559, // "YUY2"
- DX10 = 0x30315844, // "DX10", actual DXGI format specified in DX10 header
- R16G16B16A16_UNORM = 36,
- R16G16B16A16_SNORM = 110,
- R16_FLOAT = 111,
- R16G16_FLOAT = 112,
- R16G16B16A16_FLOAT = 113,
- R32_FLOAT = 114,
- R32G32_FLOAT = 115,
- R32G32B32A32_FLOAT = 116,
- CxV8U8 = 117,
- }
-
private TextureInfo LoadDDS()
{
memoryStream = new MemoryStream(buffer, 0, dataLength);
diff --git a/KSPCommunityFixes/Performance/OnDemandPartTextures.cs b/KSPCommunityFixes/Performance/OnDemandPartTextures.cs
new file mode 100644
index 0000000..3886f0e
--- /dev/null
+++ b/KSPCommunityFixes/Performance/OnDemandPartTextures.cs
@@ -0,0 +1,749 @@
+using DDSHeaders;
+using HarmonyLib;
+using KSPCommunityFixes.QoL;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+using static KSPCommunityFixes.Performance.KSPCFFastLoader;
+
+namespace KSPCommunityFixes.Performance
+{
+ public class OnDemandPartTextures : BasePatch
+ {
+ internal static Dictionary> partsTextures;
+ internal static Dictionary prefabsData = new Dictionary();
+
+ protected override void ApplyPatches(List patches)
+ {
+ MethodInfo m_Instantiate = null;
+ foreach (MethodInfo methodInfo in typeof(UnityEngine.Object).GetMethods(BindingFlags.Public | BindingFlags.Static))
+ {
+ if (methodInfo.IsGenericMethod
+ && methodInfo.Name == nameof(UnityEngine.Object.Instantiate)
+ && methodInfo.GetParameters().Length == 1)
+ {
+ m_Instantiate = methodInfo.MakeGenericMethod(typeof(UnityEngine.Object));
+ break;
+ }
+ }
+
+ patches.Add(new PatchInfo(
+ PatchMethodType.Prefix,
+ m_Instantiate,
+ this, nameof(Instantiate_Prefix)));
+
+ patches.Add(new PatchInfo(
+ PatchMethodType.Postfix,
+ AccessTools.Method(typeof(PartLoader), nameof(PartLoader.ParsePart)),
+ this));
+
+ patches.Add(new PatchInfo(
+ PatchMethodType.Postfix,
+ AccessTools.Method(typeof(Part), nameof(Part.Awake)),
+ this));
+ }
+
+ static void Part_Awake_Postfix(Part __instance)
+ {
+ if (__instance.partInfo == null)
+ return;
+
+ if (!prefabsData.TryGetValue(__instance.partInfo, out PrefabData prefabData))
+ return;
+
+ if (prefabData.areTexturesLoaded)
+ return;
+
+ prefabData.LoadTextures();
+ }
+
+ static void Instantiate_Prefix(UnityEngine.Object original)
+ {
+ //if (original is Part part
+ // && part.partInfo != null
+ // && part.partInfo.partPrefab.IsNotNullRef() // skip instantiation of the special prefabs (kerbals, flag) during loading
+ // && prefabsData.TryGetValue(part.partInfo, out PrefabData prefabData)
+ // && !prefabData.areTexturesLoaded)
+ //{
+ // // load textures
+ // // set them on the prefab
+ // prefabData.areTexturesLoaded = true;
+ //}
+ }
+
+ static void PartLoader_ParsePart_Postfix(AvailablePart __result)
+ {
+ if (!partsTextures.TryGetValue(__result.name, out List textures))
+ return;
+
+ Transform modelTransform = __result.partPrefab.transform.Find("model");
+ if (modelTransform == null)
+ return;
+
+
+ HashSet modelTextures = new HashSet();
+ PrefabData prefabData = null;
+
+ foreach (Renderer renderer in modelTransform.GetComponentsInChildren(true))
+ {
+ if (renderer is ParticleSystemRenderer || renderer.sharedMaterial.IsNullOrDestroyed())
+ continue;
+
+ Material material = renderer.sharedMaterial;
+ MaterialTextures materialTextures = null;
+
+ if (material.HasProperty("_MainTex"))
+ {
+ Texture currentTex = material.GetTexture("_MainTex");
+ if (currentTex.IsNotNullRef())
+ {
+ string currentTexName = currentTex.name;
+ if (OnDemandTextureInfo.texturesByUrl.TryGetValue(currentTexName, out OnDemandTextureInfo textureInfo))
+ {
+ if (prefabData == null)
+ prefabData = new PrefabData();
+
+ if (materialTextures == null)
+ materialTextures = new MaterialTextures(material);
+
+ prefabData.materials.Add(materialTextures);
+ materialTextures.mainTex = textureInfo;
+ modelTextures.Add(currentTexName);
+ }
+
+ }
+ }
+
+ if (material.HasProperty("_BumpMap"))
+ {
+ Texture currentTex = material.GetTexture("_BumpMap");
+ if (currentTex.IsNotNullRef())
+ {
+ string currentTexName = currentTex.name;
+ if (OnDemandTextureInfo.texturesByUrl.TryGetValue(currentTexName, out OnDemandTextureInfo textureInfo))
+ {
+ if (prefabData == null)
+ prefabData = new PrefabData();
+
+ if (materialTextures == null)
+ materialTextures = new MaterialTextures(material);
+
+ prefabData.materials.Add(materialTextures);
+ materialTextures.bumpMap = textureInfo;
+ modelTextures.Add(currentTexName);
+ }
+
+ }
+ }
+
+ if (material.HasProperty("_Emissive"))
+ {
+ Texture currentTex = material.GetTexture("_Emissive");
+ if (currentTex.IsNotNullRef())
+ {
+ string currentTexName = currentTex.name;
+ if (OnDemandTextureInfo.texturesByUrl.TryGetValue(currentTexName, out OnDemandTextureInfo textureInfo))
+ {
+ if (prefabData == null)
+ prefabData = new PrefabData();
+
+ if (materialTextures == null)
+ materialTextures = new MaterialTextures(material);
+
+ prefabData.materials.Add(materialTextures);
+ materialTextures.emissive = textureInfo;
+ modelTextures.Add(currentTexName);
+ }
+
+ }
+ }
+
+ if (material.HasProperty("_SpecMap"))
+ {
+ Texture currentTex = material.GetTexture("_SpecMap");
+ if (currentTex.IsNotNullRef())
+ {
+ string currentTexName = currentTex.name;
+ if (OnDemandTextureInfo.texturesByUrl.TryGetValue(currentTexName, out OnDemandTextureInfo textureInfo))
+ {
+ if (prefabData == null)
+ prefabData = new PrefabData();
+
+ if (materialTextures == null)
+ materialTextures = new MaterialTextures(material);
+
+ prefabData.materials.Add(materialTextures);
+ materialTextures.specMap = textureInfo;
+ modelTextures.Add(currentTexName);
+ }
+
+ }
+ }
+ }
+
+ if (prefabData != null)
+ {
+ prefabsData[__result] = prefabData;
+
+ if (partsTextures.TryGetValue(__result.name, out List partTextures))
+ {
+ foreach (string partTexture in partTextures)
+ {
+ if (modelTextures.Contains(partTexture))
+ continue;
+
+ if (OnDemandTextureInfo.texturesByUrl.TryGetValue(partTexture, out OnDemandTextureInfo textureInfo))
+ {
+ prefabData.additonalTextures.Add(textureInfo);
+ }
+ }
+ }
+ }
+ }
+
+ private static readonly char[] textureSplitChars = { ':', ',', ';' };
+
+ public static void GetTextures(out HashSet allPartTextures)
+ {
+ Dictionary> modelParts = new Dictionary>();
+ Dictionary> partTextureReplacements = new Dictionary>();
+
+ foreach (UrlDir.UrlConfig urlConfig in GameDatabase.Instance.root.AllConfigs)
+ {
+ if (urlConfig.type != "PART")
+ continue;
+
+ bool hasModelInModelNode = false;
+
+ string partName = urlConfig.config.GetValue("name");
+ if (partName == null)
+ continue;
+
+ partName = partName.Replace('_', '.');
+
+ foreach (ConfigNode partNode in urlConfig.config.nodes)
+ {
+ if (partNode.name == "MODEL")
+ {
+ List texturePaths = null;
+ foreach (ConfigNode.Value modelValue in partNode.values)
+ {
+ if (modelValue.name == "model")
+ {
+ hasModelInModelNode = true;
+
+ if (!modelParts.TryGetValue(modelValue.value, out List parts))
+ {
+ parts = new List();
+ modelParts[modelValue.value] = parts;
+ }
+
+ parts.Add(partName);
+ }
+ else if (modelValue.name == "texture")
+ {
+ string[] array = modelValue.value.Split(textureSplitChars, StringSplitOptions.RemoveEmptyEntries);
+ if (array.Length != 2)
+ continue;
+
+ if (texturePaths == null)
+ texturePaths = new List();
+
+ texturePaths.Add(new TextureReplacement(array[0].Trim(), array[1].Trim()));
+ }
+ }
+
+ if (texturePaths != null)
+ partTextureReplacements[partName] = texturePaths;
+ }
+ }
+
+ if (!hasModelInModelNode)
+ {
+ foreach (UrlDir.UrlFile urlFile in urlConfig.parent.parent.files)
+ {
+ if (urlFile.fileExtension == "mu")
+ {
+ if (!modelParts.TryGetValue(urlFile.url, out List parts))
+ {
+ parts = new List();
+ modelParts[urlFile.url] = parts;
+ }
+
+ parts.Add(partName);
+ break; // only first model found should be added
+ }
+ }
+ }
+ }
+
+ partsTextures = new Dictionary>(500);
+ allPartTextures = new HashSet(500);
+
+ HashSet allTextures = new HashSet(2000);
+ HashSet allReplacedTextures = new HashSet(500);
+ List texturePathsBuffer = new List();
+ List textureFileNameBuffer = new List();
+
+ foreach (UrlDir.UrlFile urlFile in GameDatabase.Instance.root.AllFiles)
+ {
+ if (urlFile.fileType == UrlDir.FileType.Texture)
+ {
+ allTextures.Add(urlFile.url);
+ continue;
+ }
+
+ if (urlFile.fileType != UrlDir.FileType.Model)
+ continue;
+
+ if (!modelParts.TryGetValue(urlFile.url, out List parts))
+ continue;
+
+ foreach (string textureFile in MuParser.GetModelTextures(urlFile.fullPath))
+ {
+ string textureFileName = Path.GetFileNameWithoutExtension(textureFile);
+ textureFileNameBuffer.Add(textureFileName);
+ texturePathsBuffer.Add(urlFile.parent.url + "/" + textureFileName);
+ }
+
+ if (texturePathsBuffer.Count == 0)
+ {
+ modelParts.Remove(urlFile.url);
+ continue;
+ }
+
+ foreach (string part in parts)
+ {
+ if (partTextureReplacements.TryGetValue(part, out List textureReplacements))
+ {
+ foreach (TextureReplacement textureReplacement in textureReplacements)
+ {
+ for (int i = 0; i < textureFileNameBuffer.Count; i++)
+ {
+ if (textureReplacement.textureName == textureFileNameBuffer[i])
+ {
+ allReplacedTextures.Add(texturePathsBuffer[i]);
+ texturePathsBuffer[i] = textureReplacement.replacementUrl;
+ }
+ }
+ }
+ }
+
+ if (!partsTextures.TryGetValue(part, out List textures))
+ {
+ textures = new List(texturePathsBuffer);
+ partsTextures[part] = textures;
+ }
+ else
+ {
+ textures.AddRange(texturePathsBuffer);
+ }
+ }
+
+ texturePathsBuffer.Clear();
+ textureFileNameBuffer.Clear();
+ }
+
+ List stringBuffer = new List();
+ foreach (KeyValuePair> partTextures in partsTextures)
+ {
+ for (int i = partTextures.Value.Count; i-- > 0;)
+ {
+ string texture = partTextures.Value[i];
+ if (!allTextures.Contains(texture))
+ {
+ partTextures.Value.RemoveAt(i);
+ }
+ else
+ {
+ allPartTextures.Add(texture);
+ }
+ }
+
+ if (partTextures.Value.Count == 0)
+ stringBuffer.Add(partTextures.Key);
+ }
+
+ for (int i = stringBuffer.Count; i-- > 0;)
+ partsTextures.Remove(stringBuffer[i]);
+
+ stringBuffer.Clear();
+ foreach (string replacedTexture in allReplacedTextures)
+ if (allPartTextures.Contains(replacedTexture))
+ stringBuffer.Add(replacedTexture);
+
+ for (int i = stringBuffer.Count; i-- > 0;)
+ allReplacedTextures.Remove(stringBuffer[i]);
+ }
+ }
+
+
+ [KSPAddon(KSPAddon.Startup.Instantly, true)]
+ public class OnDemandPartTexturesLoader : MonoBehaviour
+ {
+ public static OnDemandPartTexturesLoader instance;
+
+ void Start()
+ {
+ DontDestroyOnLoad(this);
+ instance = this;
+ }
+ }
+
+ internal class TextureReplacement
+ {
+ public string textureName;
+ public string replacementUrl;
+
+ public TextureReplacement(string textureName, string replacementUrl)
+ {
+ this.textureName = textureName;
+ this.replacementUrl = replacementUrl;
+ }
+ }
+
+ internal class PrefabData
+ {
+ public bool areTexturesLoaded;
+ public List materials = new List();
+ public HashSet additonalTextures = new HashSet();
+
+ public void LoadTextures()
+ {
+ foreach (MaterialTextures materialTextures in materials)
+ {
+ materialTextures.LoadTextures();
+ }
+
+ foreach (OnDemandTextureInfo textureInfo in additonalTextures)
+ {
+ if (!textureInfo.isLoaded)
+ {
+ textureInfo.Load();
+ }
+ }
+
+ areTexturesLoaded = true;
+ }
+ }
+
+ internal class MaterialTextures
+ {
+ private Material material;
+ public OnDemandTextureInfo mainTex;
+ public OnDemandTextureInfo bumpMap;
+ public OnDemandTextureInfo emissive;
+ public OnDemandTextureInfo specMap;
+
+ public MaterialTextures(Material material)
+ {
+ this.material = material;
+ }
+
+ public void LoadTextures()
+ {
+ if (mainTex != null)
+ {
+ if (mainTex.isLoaded)
+ material.SetTexture("_MainTex", mainTex.texture);
+ else
+ OnDemandPartTexturesLoader.instance.StartCoroutine(LoadMainTex());
+ }
+
+ if (bumpMap != null)
+ {
+ if (bumpMap.isLoaded)
+ material.SetTexture("_BumpMap", bumpMap.texture);
+ else
+ OnDemandPartTexturesLoader.instance.StartCoroutine(LoadBumpMap());
+ }
+
+ if (emissive != null)
+ {
+ if (emissive.isLoaded)
+ material.SetTexture("_Emissive", emissive.texture);
+ else
+ OnDemandPartTexturesLoader.instance.StartCoroutine(LoadEmissive());
+ }
+
+ if (specMap != null)
+ {
+ if (specMap.isLoaded)
+ material.SetTexture("_SpecMap", specMap.texture);
+ else
+ OnDemandPartTexturesLoader.instance.StartCoroutine(LoadSpecMap());
+ }
+ }
+
+ private IEnumerator LoadMainTex()
+ {
+ mainTex.Load();
+
+ while (!mainTex.isLoaded)
+ yield return null;
+
+ material.SetTexture("_MainTex", mainTex.texture);
+ }
+
+ private IEnumerator LoadBumpMap()
+ {
+ bumpMap.Load();
+
+ while (!bumpMap.isLoaded)
+ yield return null;
+
+ material.SetTexture("_BumpMap", bumpMap.texture);
+ }
+
+ private IEnumerator LoadEmissive()
+ {
+ emissive.Load();
+
+ while (!emissive.isLoaded)
+ yield return null;
+
+ material.SetTexture("_Emissive", emissive.texture);
+ }
+
+ private IEnumerator LoadSpecMap()
+ {
+ specMap.Load();
+
+ while (!specMap.isLoaded)
+ yield return null;
+
+ material.SetTexture("_SpecMap", specMap.texture);
+ }
+ }
+
+ internal class OnDemandTextureInfo : GameDatabase.TextureInfo
+ {
+ public static Dictionary texturesByUrl = new Dictionary();
+
+ private Texture2D dummyTexture;
+
+ public bool isLoaded;
+ private RawAsset.AssetType textureType;
+
+ public OnDemandTextureInfo(UrlDir.UrlFile file, RawAsset.AssetType textureType, bool isNormalMap = false, bool isReadable = false, bool isCompressed = false, Texture2D texture = null)
+ : base(file, texture, isNormalMap, isReadable, isCompressed)
+ {
+ name = file.url;
+ this.textureType = textureType;
+ dummyTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
+ dummyTexture.Apply(false, true);
+ dummyTexture.name = name;
+ this.texture = dummyTexture;
+ texturesByUrl.Add(name, this);
+ }
+
+ public void Load()
+ {
+ switch (textureType)
+ {
+ case RawAsset.AssetType.TextureDDS:
+ LoadDDS();
+ break;
+ case RawAsset.AssetType.TextureJPG:
+ break;
+ case RawAsset.AssetType.TextureMBM:
+ break;
+ case RawAsset.AssetType.TexturePNG:
+ break;
+ case RawAsset.AssetType.TexturePNGCached:
+ break;
+ case RawAsset.AssetType.TextureTGA:
+ break;
+ case RawAsset.AssetType.TextureTRUECOLOR:
+ break;
+ }
+ }
+
+ private void LoadDDS()
+ {
+ FileStream fileStream = new FileStream(file.fullPath, FileMode.Open);
+ BinaryReader binaryReader = new BinaryReader(fileStream);
+
+ if (binaryReader.ReadUInt32() != DDSValues.uintMagic)
+ {
+ binaryReader.Dispose();
+ fileStream.Dispose();
+ return;
+ }
+ DDSHeader dDSHeader = new DDSHeader(binaryReader);
+ bool mipChain = (dDSHeader.dwCaps & DDSPixelFormatCaps.MIPMAP) != 0;
+ bool isNormalMap = (dDSHeader.ddspf.dwFlags & 0x80000u) != 0 || (dDSHeader.ddspf.dwFlags & 0x80000000u) != 0;
+
+ DDSFourCC ddsFourCC = (DDSFourCC)dDSHeader.ddspf.dwFourCC;
+ GraphicsFormat graphicsFormat = GraphicsFormat.None;
+
+ switch (ddsFourCC)
+ {
+ case DDSFourCC.DXT1:
+ graphicsFormat = GraphicsFormatUtility.GetGraphicsFormat(TextureFormat.DXT1, true);
+ break;
+ case DDSFourCC.DXT5:
+ graphicsFormat = GraphicsFormatUtility.GetGraphicsFormat(TextureFormat.DXT5, true);
+ break;
+ case DDSFourCC.BC4U_ATI:
+ case DDSFourCC.BC4U:
+ graphicsFormat = GraphicsFormat.R_BC4_UNorm;
+ break;
+ case DDSFourCC.BC4S:
+ graphicsFormat = GraphicsFormat.R_BC4_SNorm;
+ break;
+ case DDSFourCC.BC5U_ATI:
+ case DDSFourCC.BC5U:
+ graphicsFormat = GraphicsFormat.RG_BC5_UNorm;
+ break;
+ case DDSFourCC.BC5S:
+ graphicsFormat = GraphicsFormat.RG_BC5_SNorm;
+ break;
+ case DDSFourCC.R16G16B16A16_UNORM:
+ graphicsFormat = GraphicsFormat.R16G16B16A16_UNorm;
+ break;
+ case DDSFourCC.R16G16B16A16_SNORM:
+ graphicsFormat = GraphicsFormat.R16G16B16A16_SNorm;
+ break;
+ case DDSFourCC.R16_FLOAT:
+ graphicsFormat = GraphicsFormat.R16_SFloat;
+ break;
+ case DDSFourCC.R16G16_FLOAT:
+ graphicsFormat = GraphicsFormat.R16G16_SFloat;
+ break;
+ case DDSFourCC.R16G16B16A16_FLOAT:
+ graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
+ break;
+ case DDSFourCC.R32_FLOAT:
+ graphicsFormat = GraphicsFormat.R32_SFloat;
+ break;
+ case DDSFourCC.R32G32_FLOAT:
+ graphicsFormat = GraphicsFormat.R32G32_SFloat;
+ break;
+ case DDSFourCC.R32G32B32A32_FLOAT:
+ graphicsFormat = GraphicsFormat.R32G32B32A32_SFloat;
+ break;
+ case DDSFourCC.DX10:
+ DDSHeaderDX10 dx10Header = new DDSHeaderDX10(binaryReader);
+ switch (dx10Header.dxgiFormat)
+ {
+ case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
+ graphicsFormat = GraphicsFormat.RGBA_DXT1_UNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
+ graphicsFormat = GraphicsFormat.RGBA_DXT1_SRGB;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM:
+ graphicsFormat = GraphicsFormat.RGBA_DXT5_UNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM_SRGB:
+ graphicsFormat = GraphicsFormat.RGBA_DXT5_SRGB;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC4_SNORM:
+ graphicsFormat = GraphicsFormat.R_BC4_SNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM:
+ graphicsFormat = GraphicsFormat.R_BC4_UNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC5_SNORM:
+ graphicsFormat = GraphicsFormat.RG_BC5_SNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM:
+ graphicsFormat = GraphicsFormat.RG_BC5_UNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM:
+ graphicsFormat = GraphicsFormat.RGBA_BC7_UNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM_SRGB:
+ graphicsFormat = GraphicsFormat.RGBA_BC7_SRGB;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC6H_SF16:
+ graphicsFormat = GraphicsFormat.RGB_BC6H_SFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_BC6H_UF16:
+ graphicsFormat = GraphicsFormat.RGB_BC6H_UFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM:
+ graphicsFormat = GraphicsFormat.R16G16B16A16_UNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_SNORM:
+ graphicsFormat = GraphicsFormat.R16G16B16A16_SNorm;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R16_FLOAT:
+ graphicsFormat = GraphicsFormat.R16_SFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT:
+ graphicsFormat = GraphicsFormat.R16G16_SFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT:
+ graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT:
+ graphicsFormat = GraphicsFormat.R32_SFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT:
+ graphicsFormat = GraphicsFormat.R32G32_SFloat;
+ break;
+ case DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT:
+ graphicsFormat = GraphicsFormat.R32G32B32A32_SFloat;
+ break;
+ default:
+ //SetError($"DDS: The '{dx10Header.dxgiFormat}' DXT10 format isn't supported");
+ break;
+ }
+ break;
+ case DDSFourCC.DXT2:
+ case DDSFourCC.DXT3:
+ case DDSFourCC.DXT4:
+ case DDSFourCC.RGBG:
+ case DDSFourCC.GRGB:
+ case DDSFourCC.UYVY:
+ case DDSFourCC.YUY2:
+ case DDSFourCC.CxV8U8:
+ //SetError($"DDS: The '{ddsFourCC}' format isn't supported, use DXT1 for RGB textures or DXT5 for RGBA textures");
+ break;
+ default:
+ //SetError($"DDS: Unknown dwFourCC format '0x{ddsFourCC:X}'");
+ break;
+ }
+
+ if (graphicsFormat != GraphicsFormat.None)
+ {
+ if (!SystemInfo.IsFormatSupported(graphicsFormat, FormatUsage.Sample))
+ {
+ if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX &&
+ (graphicsFormat == GraphicsFormat.RGBA_BC7_UNorm
+ || graphicsFormat == GraphicsFormat.RGBA_BC7_SRGB
+ || graphicsFormat == GraphicsFormat.RGB_BC6H_SFloat
+ || graphicsFormat == GraphicsFormat.RGB_BC6H_UFloat))
+ {
+ //SetError($"DDS: The '{graphicsFormat}' format is not supported on MacOS");
+ }
+ else
+ {
+ //SetError($"DDS: The '{graphicsFormat}' format is not supported by your GPU or OS");
+ }
+ }
+ else
+ {
+ texture = new Texture2D((int)dDSHeader.dwWidth, (int)dDSHeader.dwHeight, graphicsFormat, mipChain ? TextureCreationFlags.MipChain : TextureCreationFlags.None);
+ if (texture.IsNullOrDestroyed())
+ {
+ //SetError($"DDS: Failed to load texture, unknown error");
+ }
+ else
+ {
+ byte[] ddsData = binaryReader.ReadBytes((int)(binaryReader.BaseStream.Length - binaryReader.BaseStream.Position));
+ texture.LoadRawTextureData(ddsData);
+ texture.Apply(updateMipmaps: false, makeNoLongerReadable: true);
+ isLoaded = true;
+ }
+ }
+ }
+ }
+ }
+}