Skip to content

Commit

Permalink
Add Texture size limit setting - increases texture size to match over…
Browse files Browse the repository at this point in the history
…lay size
  • Loading branch information
ManlyMarco committed Oct 6, 2022
1 parent 5176e10 commit 7f0d90c
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 81 deletions.
43 changes: 41 additions & 2 deletions Core_OverlayMods/Skin/KoiSkinOverlayMgr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class KoiSkinOverlayMgr : BaseUnityPlugin
public const string Version = Metadata.Version;

private static ConfigEntry<bool> CompressTextures { get; set; }
internal static ConfigEntry<TextureSizeLimit> SizeLimit { get; private set; }
private static ConfigEntry<string> ExportDirectory { get; set; }

private static readonly string _defaultOverlayDirectory = Path.Combine(Paths.GameRootPath, "UserData\\Overlays");
Expand Down Expand Up @@ -75,8 +76,9 @@ private void Awake()
{
Logger = base.Logger;

ExportDirectory = Config.AddSetting("Maker", "Overlay export/open folder", _defaultOverlayDirectory, "The value needs to be a valid full path to an existing folder. Default folder will be used if the value is invalid. Exported overlays will be saved there, and by default open overlay dialog will show this directory.");
CompressTextures = Config.AddSetting("General", "Compress overlay textures in RAM", false, "Reduces RAM usage to about 1/4th at the cost of lower quality. Use when loading lots of characters with overlays if you're running out of memory.");
ExportDirectory = Config.Bind("Maker", "Overlay export/open folder", _defaultOverlayDirectory, "The value needs to be a valid full path to an existing folder. Default folder will be used if the value is invalid. Exported overlays will be saved there, and by default open overlay dialog will show this directory.");
CompressTextures = Config.Bind("General", "Compress overlay textures in RAM", false, "Reduces RAM usage to about 1/4th at the cost of lower quality. Use when loading lots of characters with overlays if you're running out of memory.");
SizeLimit = Config.Bind("General", "Texture size limit", TextureSizeLimit.OriginalX2, "If an overlay has a higher resolution than the base image, allow the finished texture to be this much larger than the base image size. Original will force the same resolution as the original texture, OriginalX2 will allow sizes up to twice as large. Increases quality at the cost of higher memory usage, turn off if you're running out of memory. If using Unlimited, consider enabling texture compression. Changes take effect after a scene reload.");

Hooks.Init();
CharacterApi.RegisterExtraBehaviour<KoiSkinOverlayController>(GUID);
Expand All @@ -90,5 +92,42 @@ public static TextureFormat GetSelectedOverlayTexFormat(bool isMask)
return CompressTextures.Value ? TextureFormat.DXT1 : TextureFormat.RG16;
return CompressTextures.Value ? TextureFormat.DXT5 : TextureFormat.ARGB32;
}

public static Util.TexSize GetOutputSize(TexType type, Texture original, int maxWidth, int maxHeight)
{
var origSize = original != null ? new Util.TexSize(original.width, original.height) : Util.GetRecommendedTexSize(type);
switch (SizeLimit.Value)
{
case TextureSizeLimit.Original:
return origSize;
case TextureSizeLimit.OriginalX2:
case TextureSizeLimit.Unlimited:
// To keep the original aspect ratio, find the edge that got increased the most and then calculate the shorter edge based on the size ratio
var hDiff = maxHeight / (float)origSize.Height;
var wDiff = maxWidth / (float)origSize.Width;
var topDiff = Mathf.Max(hDiff, wDiff);
if (topDiff <= 1 + float.Epsilon)
return origSize;

if (SizeLimit.Value == TextureSizeLimit.OriginalX2 && topDiff > 2)
topDiff = 2;

var maxSize = SystemInfo.maxTextureSize;
var result = new Util.TexSize(Mathf.Min(Mathf.RoundToInt(origSize.Width * topDiff), maxSize),
Mathf.Min(Mathf.RoundToInt(origSize.Height * topDiff), maxSize));
//Logger.LogDebug($"old {origSize} new {result} diffs {hDiff} {wDiff} {topDiff} max {maxSize}");
return result;

default:
throw new ArgumentOutOfRangeException("value", SizeLimit.Value, "Invalid value");
}
}

public enum TextureSizeLimit
{
Original = 0,
OriginalX2 = 1,
Unlimited = 10
}
}
}
25 changes: 13 additions & 12 deletions Core_OverlayMods/Skin/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace KoiSkinOverlayX
{
internal static class Util
public static class Util
{
public static bool InsideStudio()
{
Expand Down Expand Up @@ -58,16 +58,16 @@ public static void OpenFolderAndSelectFile(string filename)
private static extern void ILFree(IntPtr pidl);
}

public struct Size
public struct TexSize
{
public readonly int Width;
public readonly int Height;
public Size(int width, int height)
public TexSize(int width, int height)
{
Width = width;
Height = height;
}
public Size(int size)
public TexSize(int size)
{
Width = size;
Height = size;
Expand All @@ -78,17 +78,17 @@ public override string ToString()
}
}

public static Size GetRecommendedTexSize(TexType texType)
public static TexSize GetRecommendedTexSize(TexType texType)
{
switch (texType)
{
#if KK || KKS || EC
case TexType.BodyOver:
case TexType.BodyUnder:
return new Size(2048);
return new TexSize(2048);
case TexType.FaceOver:
case TexType.FaceUnder:
return new Size(1024);
return new TexSize(1024);
case TexType.EyeUnder:
case TexType.EyeOver:
case TexType.EyeUnderL:
Expand All @@ -97,30 +97,31 @@ public static Size GetRecommendedTexSize(TexType texType)
case TexType.EyeOverR:
case TexType.EyebrowUnder:
case TexType.EyelineUnder:
return new Size(512);
return new TexSize(512);
#elif AI || HS2
case TexType.BodyOver:
case TexType.BodyUnder:
case TexType.FaceOver:
case TexType.FaceUnder:
return new Size(4096);
return new TexSize(4096);
case TexType.EyeUnder:
case TexType.EyeOver:
case TexType.EyeUnderL:
case TexType.EyeOverL:
case TexType.EyeUnderR:
case TexType.EyeOverR:
return new Size(512);
return new TexSize(512);
case TexType.EyebrowUnder:
return new Size(1024);
return new TexSize(1024);
case TexType.EyelineUnder:
return new Size(1024, 512);
return new TexSize(1024, 512);
#endif
default:
throw new ArgumentOutOfRangeException(nameof(texType), texType, null);
}
}

public static RenderTexture CreateRT(TexSize size) => CreateRT(size.Width, size.Height);
public static RenderTexture CreateRT(int origWidth, int origHeight)
{
var rt = new RenderTexture(origWidth, origHeight, 0);
Expand Down
73 changes: 42 additions & 31 deletions Shared_AIHS2/Skin/Hooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using System.Reflection.Emit;
using AIChara;
using HarmonyLib;
using KKAPI.Utilities;
using UnityEngine;
using Object = UnityEngine.Object;

Expand All @@ -38,21 +37,17 @@ private static void OverlayBlit(Texture source, RenderTexture dest, Material mat
if (dest == null) throw new System.ArgumentNullException(nameof(dest));
if (mat == null) throw new System.ArgumentNullException(nameof(mat));

var overlay = instance.trfParent?.GetComponent<KoiSkinOverlayController>();
if (overlay != null)
var controller = instance.trfParent?.GetComponent<KoiSkinOverlayController>();
if (controller != null)
{
if (overlay.ChaControl.customTexCtrlFace?.createCustomTex?.Contains(instance) == true)
if (controller.ChaControl.customTexCtrlFace?.createCustomTex?.Contains(instance) == true)
{
OverlayBlitImpl(source, dest, mat, pass, overlay, TexType.FaceUnder);
var overlays = overlay.GetOverlayTextures(TexType.FaceOver).ToList();
if (overlays.Count > 0) KoiSkinOverlayController.ApplyOverlays(dest, overlays);
OverlayBlitImpl(source, dest, mat, pass, controller, TexType.FaceUnder, TexType.FaceOver);
return;
}
if (overlay.ChaControl.customTexCtrlBody?.createCustomTex?.Contains(instance) == true)
if (controller.ChaControl.customTexCtrlBody?.createCustomTex?.Contains(instance) == true)
{
OverlayBlitImpl(source, dest, mat, pass, overlay, TexType.BodyUnder);
var overlays = overlay.GetOverlayTextures(TexType.BodyOver).ToList();
if (overlays.Count > 0) KoiSkinOverlayController.ApplyOverlays(dest, overlays);
OverlayBlitImpl(source, dest, mat, pass, controller, TexType.BodyUnder, TexType.BodyOver);
return;
}
}
Expand All @@ -61,14 +56,39 @@ private static void OverlayBlit(Texture source, RenderTexture dest, Material mat
Graphics.Blit(source, dest, mat, pass);
}

private static void OverlayBlitImpl(Texture source, RenderTexture dest, Material mat, int pass, KoiSkinOverlayController overlayController, TexType overlayType)
private static void OverlayBlitImpl(Texture source, RenderTexture dest, Material mat, int pass, KoiSkinOverlayController controller, TexType underlayType, TexType overlayType)
{
var overlays = overlayController.GetOverlayTextures(overlayType).ToList();
if (overlays.Count > 0)
var underlays = controller.GetOverlayTextures(underlayType).ToList();
var overlays = controller.GetOverlayTextures(overlayType).ToList();
var anyUnderlays = underlays.Count > 0;
var anyOverlays = overlays.Count > 0;

// Increase/decrease output texture size if needed to accomodate large overlays
if (KoiSkinOverlayMgr.SizeLimit.Value != KoiSkinOverlayMgr.TextureSizeLimit.Original && (anyUnderlays || anyOverlays))
{
var outSize = KoiSkinOverlayMgr.GetOutputSize(type: underlayType,
original: dest,
maxWidth: underlays.Concat(overlays).Max(x => x.width),
maxHeight: underlays.Concat(overlays).Max(x => x.height));
if (dest.width != outSize.Width)
{
KoiSkinOverlayMgr.Logger.LogDebug($"Changing dest texture size from {dest.width}x{dest.height} to {outSize}");
dest.Release();
dest.width = outSize.Width;
dest.height = outSize.Height;
dest.Create();
Graphics.SetRenderTarget(dest);
GL.Clear(false, true, Color.clear);
Graphics.SetRenderTarget(null);
}
}

if (anyUnderlays)
{
//todo get size, set RT size as needed, might need a gl clear if size did change
var trt = RenderTexture.GetTemporary(source.width, source.height, dest.depth, dest.format);
Graphics.Blit(source, trt);
KoiSkinOverlayController.ApplyOverlays(trt, overlays);
KoiSkinOverlayController.ApplyOverlays(trt, underlays);
Graphics.Blit(trt, dest, mat, pass);
RenderTexture.ReleaseTemporary(trt);
}
Expand All @@ -77,6 +97,9 @@ private static void OverlayBlitImpl(Texture source, RenderTexture dest, Material
// Fall back to original code
Graphics.Blit(source, dest, mat, pass);
}

if (anyOverlays)
KoiSkinOverlayController.ApplyOverlays(dest, overlays);
}

[HarmonyTranspiler, HarmonyPatch(typeof(CustomTextureCreate), nameof(CustomTextureCreate.RebuildTextureAndSetMaterial))]
Expand Down Expand Up @@ -156,20 +179,8 @@ private static void ApplyEyeUnderlays(KoiSkinOverlayController controller, TexTy
if (underlays.Count > 0)
{
var orig = material.GetTexture(propertyID);
int width, height;
if (orig != null)
{
width = orig.width;
height = orig.height;
}
else
{
var recommendedTexSize = Util.GetRecommendedTexSize(texType);
width = recommendedTexSize.Width;
height = recommendedTexSize.Height;
}

var rt = Util.CreateRT(width, height);
var size = KoiSkinOverlayMgr.GetOutputSize(texType, orig, underlays.Max(x => x.width), underlays.Max(x => x.height));
var rt = Util.CreateRT(size);
KoiSkinOverlayController.ApplyOverlays(rt, underlays);
// Never destroy the original texture because game caches it, only overwrite
// bug memory leak, rt will be replaced next time iris is updated, will be cleaned up on next unloadunusedassets
Expand Down Expand Up @@ -205,8 +216,8 @@ private static void ApplyEyeOverlays(KoiSkinOverlayController controller, TexTyp
Object.Destroy(ourMat.mainTexture);
}

var size = Util.GetRecommendedTexSize(texType);
var rt = Util.CreateRT(size.Width, size.Height);
var size = KoiSkinOverlayMgr.GetOutputSize(texType, null, overlays.Max(x => x.width), overlays.Max(x => x.height));
var rt = Util.CreateRT(size);
KoiSkinOverlayController.ApplyOverlays(rt, overlays);
ourMat.mainTexture = rt;
}
Expand Down
Loading

0 comments on commit 7f0d90c

Please sign in to comment.