From 1ffa749bbd95896c54d23561ad39b328e3c07ba2 Mon Sep 17 00:00:00 2001 From: codec-xyz <112908328+codec-xyz@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:04:47 -0500 Subject: [PATCH] Refactors photo frame baker --- src/Editor/PhotoFrameBaker.cs | 219 ++++++++++++++++++---------------- 1 file changed, 113 insertions(+), 106 deletions(-) diff --git a/src/Editor/PhotoFrameBaker.cs b/src/Editor/PhotoFrameBaker.cs index c498096..ebf59ff 100644 --- a/src/Editor/PhotoFrameBaker.cs +++ b/src/Editor/PhotoFrameBaker.cs @@ -6,6 +6,7 @@ using UnityEditor; using UnityEngine; using UnityEngine.UIElements.Experimental; +using UnityEngine.WSA; using static codec.PhotoFrame.TextureBaker; using static UnityEditor.PlayerSettings; @@ -20,24 +21,7 @@ public static string AssureAutoSaveFolder() { return folderPath; } - public static TextureBaker.Input PhotoFrameToInput(PhotoFrame pf) { - int shaderId = 0; - if(pf.frameType && pf.frameType.material) shaderId = pf.frameType.material.GetInstanceID(); - - pf.getAspectRatios(out float photoAspectRatio, out float frameAspectRatio, out _); - pf.getCropUV(photoAspectRatio, frameAspectRatio, out Vector2 uvMin, out Vector2 uvMax); - - return new TextureBaker.Input { - texture = pf.photo, - point = pf.transform.position, - sortGroup = shaderId, - uvMin = uvMin, - uvMax = uvMax, - size = pf.getFinalResolution(), - }; - } - - public static (TextureBaker.Input[] inputs, int[] indexes) PhotoFramesToInputs(PhotoFrame[] pfs) { + public static (TextureBaker.Input[] inputs, int[] indexes) PhotoFramesToInputs(PhotoFrame[] pfs, bool joinDuplicates) { var info = pfs.Select(pf => { pf.getAspectRatios(out float photoAspectRatio, out float frameAspectRatio, out _); pf.getCropUV(photoAspectRatio, frameAspectRatio, out Vector2 uvMin, out Vector2 uvMax); @@ -54,34 +38,36 @@ public static (TextureBaker.Input[] inputs, int[] indexes) PhotoFramesToInputs(P } bool original = true; - for(int i = 0; i < inputs.Count(); i++) { - if(inputs[i].i.texture != info[a].texture) continue; - - Vector2Int size = info[a].size; - Vector2 uvMax = info[a].uvMax, uvMin = info[a].uvMin; - int addedBySelf = (int)(size.x * (uvMax.x - uvMin.x) * size.y * (uvMax.y - uvMin.y)); - - size = inputs[i].i.size; - uvMax = inputs[i].i.uvMax; - uvMin = inputs[i].i.uvMin; - int alreadyUsed = (int)(size.x * (uvMax.x - uvMin.x) * size.y * (uvMax.y - uvMin.y)); - - size = new Vector2Int(Math.Max(inputs[i].i.size.x, info[a].size.x), Math.Max(inputs[i].i.size.y, info[a].size.y)); - uvMin = new Vector2(Math.Min(inputs[i].i.uvMin.x, info[a].uvMin.x), Math.Min(inputs[i].i.uvMin.y, info[a].uvMin.y)); - uvMax = new Vector2(Math.Max(inputs[i].i.uvMax.x, info[a].uvMax.x), Math.Max(inputs[i].i.uvMax.y, info[a].uvMax.y)); - int addedTogether = (int)(size.x * (uvMax.x - uvMin.x) * size.y * (uvMax.y - uvMin.y)) - alreadyUsed; - - if(addedTogether > addedBySelf) continue; - - indexes[a] = i; - var input = inputs[i].i; - input.point += info[a].point; - input.uvMin = new Vector2(Math.Min(input.uvMin.x, info[a].uvMin.x), Math.Min(input.uvMin.y, info[a].uvMin.y)); - input.uvMax = new Vector2(Math.Max(input.uvMax.x, info[a].uvMax.x), Math.Max(input.uvMax.y, info[a].uvMax.y)); - input.size = new Vector2Int(Math.Max(input.size.x, info[a].size.x), Math.Max(input.size.y, info[a].size.y)); - inputs[i] = (i: input, count: inputs[i].count + 1); - original = false; - break; + if(joinDuplicates) { + for(int i = 0; i < inputs.Count(); i++) { + if(inputs[i].i.texture != info[a].texture) continue; + + Vector2Int size = info[a].size; + Vector2 uvMax = info[a].uvMax, uvMin = info[a].uvMin; + int addedBySelf = (int)(size.x * (uvMax.x - uvMin.x) * size.y * (uvMax.y - uvMin.y)); + + size = inputs[i].i.size; + uvMax = inputs[i].i.uvMax; + uvMin = inputs[i].i.uvMin; + int alreadyUsed = (int)(size.x * (uvMax.x - uvMin.x) * size.y * (uvMax.y - uvMin.y)); + + size = new Vector2Int(Math.Max(inputs[i].i.size.x, info[a].size.x), Math.Max(inputs[i].i.size.y, info[a].size.y)); + uvMin = new Vector2(Math.Min(inputs[i].i.uvMin.x, info[a].uvMin.x), Math.Min(inputs[i].i.uvMin.y, info[a].uvMin.y)); + uvMax = new Vector2(Math.Max(inputs[i].i.uvMax.x, info[a].uvMax.x), Math.Max(inputs[i].i.uvMax.y, info[a].uvMax.y)); + int addedTogether = (int)(size.x * (uvMax.x - uvMin.x) * size.y * (uvMax.y - uvMin.y)) - alreadyUsed; + + if(addedTogether > addedBySelf) continue; + + indexes[a] = i; + var input = inputs[i].i; + input.point += info[a].point; + input.uvMin = new Vector2(Math.Min(input.uvMin.x, info[a].uvMin.x), Math.Min(input.uvMin.y, info[a].uvMin.y)); + input.uvMax = new Vector2(Math.Max(input.uvMax.x, info[a].uvMax.x), Math.Max(input.uvMax.y, info[a].uvMax.y)); + input.size = new Vector2Int(Math.Max(input.size.x, info[a].size.x), Math.Max(input.size.y, info[a].size.y)); + inputs[i] = (i: input, count: inputs[i].count + 1); + original = false; + break; + } } if(original) { @@ -166,20 +152,13 @@ public static (Vector2 uvMin, Vector2 uvMax) CalcFinalUv(Vector2 uvMin, Vector2 public delegate void BakeProgressUpdate(string info, float progress); public static void Bake(PhotoFrame[] photoFrames, SceneSettings settings, bool isDebug) { if(photoFrames.Length == 0) return; - TextureBaker.Input[] photoFrameInput; - int[] indexes; - if(settings.joinDuplicates) (photoFrameInput, indexes) = PhotoFramesToInputs(photoFrames); - else { - photoFrameInput = photoFrames.Select(PhotoFrameToInput).ToArray(); - indexes = photoFrames.Select((_, i) => i).ToArray(); - } + (TextureBaker.Input[] photoFrameInput, int[] indexes) = PhotoFramesToInputs(photoFrames, settings.joinDuplicates); LimitPhotoSize(photoFrameInput, settings.textureSize, settings.margin); TextureBaker.isDebug = isDebug; TextureBaker.Output[] outputs = TextureBaker.Bake(photoFrameInput, settings.textureSize, settings.margin, settings.scaleMargin, Mathf.Pow(settings.textureFit * 50f, 2), settings.skylineMaxSpread, settings.overhangWeight, settings.neighborhoodWasteWeight, settings.topWasteWeight, settings.estimatedPackEfficiency, out Texture2D[] textures, Progress_BakePackSort); string folder = AssureAutoSaveFolder(); string[] texturePaths = Enumerable.Range(0, textures.Length).Select(_ => $"{folder}/Photo-Texture-{System.Guid.NewGuid()}.png").ToArray(); - var textureMaterials = Enumerable.Range(0, textures.Length).Select(_ => new List<(Material source, Material created)>()).ToArray(); string[] meshPaths = Enumerable.Range(0, photoFrames.Length).Select(_ => $"{folder}/Photo-Mesh-{System.Guid.NewGuid()}.asset").ToArray(); byte[] dummyTextureData = Utils.MakeTexture(1, 1, Color.white).EncodeToPNG(); @@ -189,25 +168,10 @@ public static void Bake(PhotoFrame[] photoFrames, SceneSettings settings, bool i AssetDatabase.ImportAsset(texturePaths[i]); } - var frames = new List<(PhotoFrameType type, int index, float ratio, GameObject prefab, bool isGenerated)>(); + FrameBaker frames = new FrameBaker(); for(int i = 0; i < photoFrames.Length; i++) { - PhotoFrame pf = photoFrames[i]; Progress_PreparingFrames(i, photoFrames.Length); - if(!pf.frameType) continue; - pf.getAspectRatios(out float photoAspectRatio, out float frameAspectRatio, out int frameIndex); - pf.getCropUV(photoAspectRatio, frameAspectRatio, out _, out _); - var size = pf.getPhotoWorldSize(frameAspectRatio, frameIndex, out Vector2 frameScale); - var sameInfo = frames.Find(info => info.type == pf.frameType && info.index == frameIndex && info.ratio == frameAspectRatio); - if(sameInfo != default) continue; - GameObject prefab = pf.frameType.getOrGenerateFrame(frameIndex, frameAspectRatio, out bool isGenerated, size); - if(isGenerated) { - AssetDatabase.CreateAsset(prefab.GetComponent().sharedMesh, $"{folder}/Photo-FrameMesh-{System.Guid.NewGuid()}.asset"); - GameObject prefabFile = PrefabUtility.SaveAsPrefabAsset(prefab, $"{folder}/Photo-FramePrefab-{System.Guid.NewGuid()}.prefab"); - GameObject.DestroyImmediate(prefab); - prefab = prefabFile; - } - sameInfo = (pf.frameType, frameIndex, frameAspectRatio, prefab, isGenerated); - frames.Add(sameInfo); + frames.add_possiblyGenerateAndSave(photoFrames[i], folder); } try { @@ -217,9 +181,10 @@ public static void Bake(PhotoFrame[] photoFrames, SceneSettings settings, bool i settings.hasBake = true; settings.textures = texturePaths.Select(path => AssetDatabase.LoadAssetAtPath(path)).ToArray(); + List[] photoFramesSave = new List[textures.Length + 1].Select(_ => new List()).ToArray(); List[] meshesSave = new List[textures.Length + 1].Select(_ => new List()).ToArray(); - List extraMaterials = new List(); + MaterialBaker materials = new MaterialBaker(); for(int i = 0; i < textures.Length; i++) { Progress_SavingTexutes(i, textures.Length); @@ -237,59 +202,35 @@ public static void Bake(PhotoFrame[] photoFrames, SceneSettings settings, bool i continue; } - Material sourceMaterial = pf.photoMaterial; - Material material = null; - if(textureIndex != -2) material = textureMaterials[textureIndex].Find(matInfo => matInfo.source == sourceMaterial).created; - if(material == null) { - material = new Material(pf.photoMaterial); - if(textureIndex != -2) material.SetTexture(pf.photoMaterialTextureSlot, settings.textures[textureIndex]); - else material.SetTexture(pf.photoMaterialTextureSlot, pf.photo); - AssetDatabase.CreateAsset(material, $"{folder}/Photo-Material-{System.Guid.NewGuid()}.mat"); - if(textureIndex != -2) textureMaterials[textureIndex].Add((source: sourceMaterial, created: material)); - else extraMaterials.Add(material); - } - Progress_SettingUpPhotoFrames(i, photoFrames.Length); + Texture2D texture = textureIndex == -2 ? pf.photo : settings.textures[textureIndex]; + Material material = materials.get_orGenerateAndSave(pf.photoMaterial, texture, pf.photoMaterialTextureSlot, folder); + pf.getAspectRatios(out float photoAspectRatio, out float frameAspectRatio, out _); pf.getCropUV(photoAspectRatio, frameAspectRatio, out Vector2 uvMin, out Vector2 uvMax); - Vector2 newUvMin, newUvMax; + Vector2 newUvMin = uvMin, newUvMax = uvMax; bool uvRotate = false; if(indexes[i] != -1) { (newUvMin, newUvMax) = CalcFinalUv(uvMin, uvMax, photoFrameInput[indexes[i]].uvMin, photoFrameInput[indexes[i]].uvMax, outputs[indexes[i]].uvMin, outputs[indexes[i]].uvMax, outputs[indexes[i]].uvRotate); uvRotate = outputs[indexes[i]].uvRotate; } - else { - newUvMin = uvMin; - newUvMax = uvMax; - } - Mesh mesh = pf.setBakedData(folder, material, newUvMin, newUvMax, uvRotate, - (type, index, ratio, size) => { - if(type == null) return null; - var sameInfo = frames.Find(info => info.type == type && info.index == index && info.ratio == ratio); - return sameInfo.prefab; - }); + Mesh mesh = pf.setBakedData(folder, material, newUvMin, newUvMax, uvRotate, (type, index, ratio, size) => frames.get(type, index, ratio, size)); AssetDatabase.CreateAsset(mesh, meshPaths[i]); - if(textureIndex != -2) { - photoFramesSave[textureIndex].Add(pf); - meshesSave[textureIndex].Add(mesh); - } - else { - photoFramesSave[textures.Length].Add(pf); - meshesSave[textures.Length].Add(mesh); - } + photoFramesSave[textureIndex == -2 ? textures.Length : textureIndex].Add(pf); + meshesSave[textureIndex == -2 ? textures.Length : textureIndex].Add(mesh); } settings.pfCounts = photoFramesSave.Select(a => a.Count).ToArray(); settings.photoFrames = photoFramesSave.SelectMany(a => a).ToArray(); settings.meshes = meshesSave.SelectMany(a => a).ToArray(); - settings.materials = textureMaterials.SelectMany(a => a.Select(b => b.created)).Concat(extraMaterials).ToArray(); - settings.frameMeshes = frames.Where(a => a.isGenerated).Select(a => a.prefab.GetComponent().sharedMesh).ToArray(); - settings.framePrefabs = frames.Where(a => a.isGenerated).Select(a => a.prefab).ToArray(); + settings.materials = materials.getGeneratedMaterials(); + settings.frameMeshes = frames.getGeneratedMeshes(); + settings.framePrefabs = frames.getGeneratedPrefabs(); } finally { //IDK unity 2022 needs two or asset import progress bar does not go away @@ -323,4 +264,70 @@ public static void UpdateTextureImportSettings(SceneSettings settings, string pa importer.SaveAndReimport(); } } + + public class MaterialBaker { + public Dictionary<(Material material, Texture2D texture), Material> materials = new Dictionary<(Material material, Texture2D texture), Material>(); + + public Material get_orGenerateAndSave(Material srcMaterial, Texture2D texture, string textureSlot, string folder) { + bool found = materials.TryGetValue((srcMaterial, texture), out Material material); + if(!found) { + material = new Material(srcMaterial); + material.SetTexture(textureSlot, texture); + AssetDatabase.CreateAsset(material, $"{folder}/Photo-Material-{System.Guid.NewGuid()}.mat"); + materials.Add((srcMaterial, texture), material); + } + return material; + } + + public Material[] getGeneratedMaterials() { + return materials.Values.ToArray(); + } + } + + public class FrameBaker { + public struct TypeInfo { + public PhotoFrameType type; + public int index; + public float ratio; + + public GameObject prefab; + public bool isGenerated; + } + public List frames = new List(); + + public void add_possiblyGenerateAndSave(PhotoFrame pf, string folder) { + if(pf.frameType == null) return; + + pf.getAspectRatios(out float photoAspectRatio, out float frameAspectRatio, out int frameIndex); + pf.getCropUV(photoAspectRatio, frameAspectRatio, out _, out _); + var size = pf.getPhotoWorldSize(frameAspectRatio, frameIndex, out Vector2 frameScale); + + TypeInfo sameInfo = frames.Find(info => info.type == pf.frameType && info.index == frameIndex && info.ratio == frameAspectRatio); + if(sameInfo.type != null) return; + + GameObject prefab = pf.frameType.getOrGenerateFrame(frameIndex, frameAspectRatio, out bool isGenerated, size); + if(isGenerated) { + AssetDatabase.CreateAsset(prefab.GetComponent().sharedMesh, $"{folder}/Photo-FrameMesh-{System.Guid.NewGuid()}.asset"); + GameObject prefabFile = PrefabUtility.SaveAsPrefabAsset(prefab, $"{folder}/Photo-FramePrefab-{System.Guid.NewGuid()}.prefab"); + GameObject.DestroyImmediate(prefab); + prefab = prefabFile; + } + sameInfo = new TypeInfo { type = pf.frameType, index = frameIndex, ratio = frameAspectRatio, prefab = prefab, isGenerated = isGenerated }; + frames.Add(sameInfo); + } + + public GameObject get(PhotoFrameType type, int index, float ratio, Vector2 size) { + if(type == null) return null; + var sameInfo = frames.Find(info => info.type == type && info.index == index && info.ratio == ratio); + return sameInfo.prefab; + } + + public Mesh[] getGeneratedMeshes() { + return frames.Where(a => a.isGenerated).Select(a => a.prefab.GetComponent().sharedMesh).ToArray(); + } + + public GameObject[] getGeneratedPrefabs() { + return frames.Where(a => a.isGenerated).Select(a => a.prefab).ToArray(); + } + } } \ No newline at end of file