This commit is contained in:
2026-05-13 23:02:02 +07:00
parent 5025383676
commit 93da00c206
885 changed files with 980996 additions and 1987 deletions

View File

@@ -0,0 +1,68 @@
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.FCU.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
namespace DA_Assets.FCU
{
[Serializable]
public class SpriteColorizer : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
public async Task ColorizeSprites(List<FObject> fobjects)
{
if (monoBeh.UsingSVG())
return;
foreach (FObject fobject in fobjects)
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
return;
if (fobject.Data.FcuImageType != FcuImageType.Downloadable)
continue;
if (monoBeh.UsingSpriteRenderer())
{
if (fobject.Data.Graphic.SpriteSingleColor.IsDefault())
continue;
}
else
{
if (fobject.Data.Graphic.SpriteSingleColor.IsDefault() &&
fobject.Data.Graphic.SpriteSingleLinearGradient.IsDefault())
continue;
}
if (File.Exists(fobject.Data.SpritePath.GetFullAssetPath()) == false)
continue;
byte[] rawData = File.ReadAllBytes(fobject.Data.SpritePath.GetFullAssetPath());
Texture2D tex = new Texture2D(fobject.Data.SpriteSize.x, fobject.Data.SpriteSize.y);
tex.LoadImage(rawData);
tex.Colorize(Color.white);
byte[] bytes = new byte[0];
switch (monoBeh.Settings.ImageSpritesSettings.ImageFormat)
{
case ImageFormat.PNG:
bytes = tex.EncodeToPNG();
break;
case ImageFormat.JPG:
bytes = tex.EncodeToJPG();
break;
}
File.WriteAllBytes(fobject.Data.SpritePath, bytes);
await Task.Yield();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79a42f153b8ec0444b26f8cc9b2fda41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,484 @@
using DA_Assets.FCU.Model;
using DA_Assets.DAI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using DA_Assets.FCU.Extensions;
using UnityEngine;
using DA_Assets.Logging;
using DA_Assets.Extensions;
using System.Collections.Concurrent;
#if JSONNET_EXISTS
using Newtonsoft.Json;
#endif
namespace DA_Assets.FCU
{
[Serializable]
public class SpriteDownloader : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
private int _maxConcurrentDownloads = 100;
private int _maxDownloadAttempts = 3;
private float _maxChunkSize = 24_000_000;
private int _errorLogSplitLimit = 50;
private int _logDelayMs = 1000;
private int _maxSpritesCount = 100;
private Dictionary<ImageFormatScaleKey, List<List<SpriteData>>> idFormatChunks;
public async Task CreateImageChunks(List<FObject> fobjects)
{
await Task.Run(() =>
{
List<FObject> needDownload = fobjects
.Where(x => x.Data.NeedDownload)
.GroupBy(x => x.Data.Hash)
.Select(g => g.First())
.ToList();
idFormatChunks = GetIdFormatChunks(needDownload);
});
}
public async Task DownloadSprites(List<FObject> fobjects)
{
DALogger.Log($"Download Sprites");
List<FObject> needDownload = fobjects
.Where(x => x.Data.NeedDownload)
.GroupBy(x => x.Data.Hash)
.Select(g => g.First())
.ToList();
if (needDownload.IsEmpty())
{
DALogger.Log($"DownloadSprites no need");
return;
}
int totalCount = needDownload.Count;
int downloadedCount = 0;
int lastLoggedCount = -1;
CancellationTokenSource downloadLogTokenSource = new CancellationTokenSource();
var missingSpriteLinks = await GetSpriteLinks(needDownload);
SemaphoreSlim semaphore = new SemaphoreSlim(_maxConcurrentDownloads);
List<Task> tasks = new List<Task>();
ConcurrentBag<FObject> failedObjects = new ConcurrentBag<FObject>();
_ = Task.Run(async () =>
{
while (!downloadLogTokenSource.Token.IsCancellationRequested)
{
if (lastLoggedCount != downloadedCount)
{
DALogger.Log(FcuLocKey.log_downloading_images.Localize(downloadedCount, totalCount));
lastLoggedCount = downloadedCount;
}
await Task.Delay(_logDelayMs, downloadLogTokenSource.Token);
}
}, downloadLogTokenSource.Token);
DALogger.Log(FcuLocKey.log_start_download_images.Localize());
foreach (var formatChunks in missingSpriteLinks)
{
foreach (var chunk in formatChunks.Value)
{
foreach (var idFormatLink in chunk)
{
await semaphore.WaitAsync();
Task task = Task.Run(async () =>
{
try
{
bool success = await DownloadSprite(idFormatLink, _maxDownloadAttempts);
if (!success)
{
failedObjects.Add(idFormatLink.FObject);
}
}
catch (Exception ex)
{
Debug.LogException(ex);
failedObjects.Add(idFormatLink.FObject);
}
finally
{
Interlocked.Increment(ref downloadedCount);
semaphore.Release();
}
});
tasks.Add(task);
}
}
}
await Task.WhenAll(tasks);
downloadLogTokenSource.Cancel();
DALogger.Log(FcuLocKey.log_downloading_images.Localize(downloadedCount, totalCount));
LogFailedDownloads(failedObjects);
}
public async Task<bool> DownloadSprite(SpriteData idFormatLink, int maxDownloadAttempts)
{
try
{
if (idFormatLink.Link.IsEmpty())
{
return false;
}
DARequest request = new DARequest
{
RequestType = RequestType.GetFile,
Query = idFormatLink.Link
};
DAResult<byte[]> result = default;
int attempts = 0;
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
{
while (attempts < maxDownloadAttempts && result.Object == null)
{
attempts++;
result = await monoBeh.RequestSender.SendRequest<byte[]>(request);
}
}
switch (result.Error.Status)
{
case 909:
DALogger.LogError(FcuLocKey.log_ssl_error.Localize(result.Error.Message, result.Error.Status));
monoBeh.Events.OnImportFail?.Invoke(monoBeh);
monoBeh.AssetTools.StopImport(StopImportReason.Error);
break;
}
if (result.Object == null)
{
throw new NullReferenceException();
}
File.WriteAllBytes(idFormatLink.FObject.Data.SpritePath, result.Object);
return true;
}
catch (Exception)
{
return false;
}
}
public async Task<Dictionary<ImageFormatScaleKey, List<List<SpriteData>>>> GetSpriteLinks(List<FObject> fobjects)
{
var idFormatLinkChunks = new Dictionary<ImageFormatScaleKey, List<List<SpriteData>>>();
int totalLinks = fobjects.Count;
int obtainedLinks = 0;
int lastLoggedLinks = -1;
CancellationTokenSource linkLogTokenSource = new CancellationTokenSource();
_ = Task.Run(async () =>
{
while (!linkLogTokenSource.Token.IsCancellationRequested)
{
if (lastLoggedLinks != obtainedLinks)
{
DALogger.Log(FcuLocKey.log_getting_links.Localize(obtainedLinks, totalLinks));
lastLoggedLinks = obtainedLinks;
}
await Task.Delay(_logDelayMs, linkLogTokenSource.Token);
}
}, linkLogTokenSource.Token);
foreach (var idFormatChunk in idFormatChunks)
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
break;
foreach (List<SpriteData> chunk in idFormatChunk.Value)
{
IEnumerable<string> ids = chunk.Select(x => x.FObject.Id);
DARequest request = RequestCreator.CreateImageLinksRequest(
monoBeh.Settings.MainSettings.ProjectUrl,
idFormatChunk.Key.ImageFormat.ToLower(),
idFormatChunk.Key.Scale,
ids,
monoBeh.RequestSender.GetRequestHeader(monoBeh.Authorizer.Token));
DAResult<FigmaImageRequest> result = await monoBeh.RequestSender.SendRequest<FigmaImageRequest>(request);
if (result.Success && result.Object.images.IsEmpty())
{
Debug.LogError("result.Success && result.Object.images.IsEmpty()");
}
else if (result.Success)
{
if (!idFormatLinkChunks.ContainsKey(idFormatChunk.Key))
{
idFormatLinkChunks[idFormatChunk.Key] = new List<List<SpriteData>>();
}
List<SpriteData> linkChunk = new List<SpriteData>();
foreach (var idFormat in chunk)
{
result.Object.images.TryGetValue(idFormat.FObject.Id, out string link);
if (monoBeh.Settings.MainSettings.Https == false)
{
link = link.Replace("https://", "http://");
}
linkChunk.Add(new SpriteData
{
FObject = idFormat.FObject,
Format = idFormat.Format,
Link = link ?? string.Empty
});
Interlocked.Increment(ref obtainedLinks);
}
idFormatLinkChunks[idFormatChunk.Key].Add(linkChunk);
}
else
{
Debug.LogError(result.Error.Message);
}
}
}
linkLogTokenSource.Cancel();
DALogger.Log(FcuLocKey.log_getting_links.Localize(obtainedLinks, totalLinks));
return idFormatLinkChunks;
}
public async Task SetScalesAndMaxSpriteSizes(List<FObject> fobjects)
{
await Task.Run(() =>
{
Parallel.ForEach(fobjects.Where(x => x.IsSprite()), fobject =>
{
fobject.Data.MaxSpriteSize = GetMaxSpriteSize(fobject);
fobject.Data.Scale = GetMaxAllowedScale(fobject.Data.MaxSpriteSize, monoBeh.Settings.ImageSpritesSettings.MaxSpriteSize, _maxChunkSize);
});
});
}
public Dictionary<ImageFormatScaleKey, List<List<SpriteData>>> GetIdFormatChunks(List<FObject> fobjects)
{
var formatChunks = new Dictionary<ImageFormatScaleKey, List<List<SpriteData>>>();
// Group FObjects by their ImageFormat and Scale
var fobjectsByFormatAndScale = new Dictionary<ImageFormatScaleKey, List<FObject>>();
foreach (FObject fobject in fobjects.Where(x => x.IsSprite()))
{
ImageFormatScaleKey key = new ImageFormatScaleKey
{
ImageFormat = fobject.Data.ImageFormat,
Scale = fobject.Data.Scale
};
if (!fobjectsByFormatAndScale.ContainsKey(key))
{
fobjectsByFormatAndScale[key] = new List<FObject>();
}
fobjectsByFormatAndScale[key].Add(fobject);
}
// For each group (ImageFormat + Scale) create chunks
foreach (var kvp in fobjectsByFormatAndScale)
{
ImageFormatScaleKey key = kvp.Key;
List<FObject> fobjectList = kvp.Value;
List<List<SpriteData>> chunks = new List<List<SpriteData>>();
List<SpriteData> currentChunk = new List<SpriteData>();
float currentChunkSize = 0;
foreach (FObject fobject in fobjectList)
{
float spriteSize = fobject.Data.MaxSpriteSize.x * fobject.Data.MaxSpriteSize.y * fobject.Data.Scale;
//Debug.LogError($"{spriteSize} | {fobject.Data.MaxSpriteSize.x * fobject.Data.Scale} | {fobject.Data.MaxSpriteSize.y * fobject.Data.Scale}");
// If adding this image exceeds the maximum chunk size, create a new chunk
if (currentChunkSize + spriteSize > _maxChunkSize || currentChunk.Count > _maxSpritesCount)
{
if (currentChunk.Count > 0)
{
chunks.Add(currentChunk);
}
currentChunk = new List<SpriteData>();
currentChunkSize = 0;
}
// Add the image to the current chunk
currentChunk.Add(new SpriteData
{
FObject = fobject,
Format = key.ImageFormat.ToString(),
Scale = key.Scale
});
currentChunkSize += spriteSize;
}
// Add the last chunk if it's not empty
if (currentChunk.Count > 0)
{
chunks.Add(currentChunk);
}
formatChunks[key] = chunks;
}
return formatChunks;
}
private void LogFailedDownloads(ConcurrentBag<FObject> failedObjects)
{
if (failedObjects.Count() > 0)
{
List<List<string>> comps = failedObjects.Select(x => x.Data.NameHierarchy).Split(_errorLogSplitLimit);
foreach (List<string> comp in comps)
{
string hierarchies = string.Join("\n", comp);
DALogger.LogError(
FcuLocKey.log_malformed_url.Localize(comp.Count, hierarchies));
}
}
}
private Vector2 GetMaxSpriteSize(FObject fobject)
{
float maxX;
float maxY;
bool hasBoundingSize = fobject.GetBoundingSize(out Vector2 bSize);
bool hasRenderSize = fobject.GetRenderSize(out Vector2 rSize);
if (hasBoundingSize && hasRenderSize)
{
maxX = Mathf.Max(bSize.x, rSize.x);
maxY = Mathf.Max(bSize.y, rSize.y);
}
else if (hasRenderSize)
{
maxX = rSize.x;
maxY = rSize.y;
}
else if (hasBoundingSize)
{
maxX = bSize.x;
maxY = bSize.y;
}
else
{
maxX = fobject.Size.x;
maxY = fobject.Size.y;
}
return new Vector2(maxX, maxY);
}
public float GetMaxAllowedScale(
Vector2 imageSize,
Vector2 maxSpriteSize,
float maxChunkSize,
float minScale = FcuConfig.IMAGE_SCALE_MIN,
float maxScale = FcuConfig.IMAGE_SCALE_MAX)
{
if (monoBeh.UsingSVG())
{
return 1f;
}
float effectiveMaxSpriteWidth = Mathf.Min(maxSpriteSize.x, monoBeh.Settings.ImageSpritesSettings.MaxSpriteSize.x);
float effectiveMaxSpriteHeight = Mathf.Min(maxSpriteSize.y, monoBeh.Settings.ImageSpritesSettings.MaxSpriteSize.y);
float scaleX = effectiveMaxSpriteWidth / imageSize.x;
float scaleY = effectiveMaxSpriteHeight / imageSize.y;
float maxScaleBySpriteSize = Mathf.Min(scaleX, scaleY);
maxScaleBySpriteSize = Mathf.Max(1f, maxScaleBySpriteSize);
float maxScaleByChunkSize = Mathf.Sqrt(maxChunkSize / (imageSize.x * imageSize.y));
float maxScaleAllowed = Mathf.Min(maxScaleBySpriteSize, maxScaleByChunkSize);
maxScaleAllowed = Mathf.Clamp(maxScaleAllowed, minScale, maxScale);
maxScaleAllowed = (float)Math.Round(maxScaleAllowed, 2);
//Debug.Log($"imageSize: {imageSize}, scaleX: {scaleX}, scaleY: {scaleY}, maxScaleBySpriteSize: {maxScaleBySpriteSize}, maxScaleAllowed: {maxScaleAllowed}");
return maxScaleAllowed;
}
public struct FigmaImageRequest
{
#if JSONNET_EXISTS
[JsonProperty("err")]
#endif
public string error;
#if JSONNET_EXISTS
[JsonProperty("images")]
#endif
// key = id, value = link
public Dictionary<string, string> images;
}
public struct ImageFormatScaleKey
{
public ImageFormat ImageFormat { get; set; }
public float Scale { get; set; }
public override bool Equals(object obj)
{
if (!(obj is ImageFormatScaleKey))
return false;
var other = (ImageFormatScaleKey)obj;
return ImageFormat == other.ImageFormat && Scale.Equals(other.Scale);
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + ImageFormat.GetHashCode();
hash = hash * 23 + Scale.GetHashCode();
return hash;
}
}
}
public struct SpriteData
{
public FObject FObject { get; set; }
public string Format { get; set; }
public string Link { get; set; }
public float Scale { get; set; }
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b7918ee99b0328741b3924857b8be388

View File

@@ -0,0 +1,371 @@
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.FCU.Model;
using DA_Assets.Logging;
using DA_Assets.SVGMeshUnity;
using DA_Assets.Tools;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
namespace DA_Assets.FCU
{
[Serializable]
public class SpriteGenerator : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
[SerializeField] RectTransform rectTransform;
[SerializeField] MeshRenderer meshRenderer;
[SerializeField] MeshFilter meshFilter;
[SerializeField] Camera camera;
private float spriteGenerationDelay = 0.25f;
private int meshUpscaleFactor = 16;
private int renderAntialiasing = 8;
private float blurCoof = 10f;
private FilterMode filterMode = FilterMode.Bilinear;
private TextureFormat textureFormat = TextureFormat.ARGB32;
private RenderTextureFormat renderTextureFormat = RenderTextureFormat.ARGB32;
public async Task GenerateSprites(List<FObject> fobjects)
{
List<FObject> generative = fobjects.Where(x => x.Data.NeedGenerate).ToList();
if (generative.IsEmpty())
return;
if (camera == null)
{
GameObject cameraGo = MonoBehExtensions.CreateEmptyGameObject();
cameraGo.name = "SpriteGeneratorCamera";
cameraGo.TryAddComponent(out camera);
camera.orthographic = true;
camera.backgroundColor = new Color(0, 0, 0, 0);
camera.clearFlags = CameraClearFlags.Color;
}
int generatedCount = 0;
_ = DACycles.ForEach(generative, fobject =>
{
FcuLogger.Debug($"GenerateSprites | {fobject.Data.NameHierarchy} | {fobject.Data.NeedGenerate}");
try
{
Texture2D fillTexture = null;
Texture2D strokeTexture = null;
Texture2D finalTexture = null;
FGraphic graphic = fobject.Data.Graphic;
if (!graphic.HasFill && graphic.HasStroke && !fobject.ContainsRoundedCorners())
{
IndividualStrokeWeights ind = fobject.IndividualStrokeWeights;
if (ind.IsDefault())
{
ind = new IndividualStrokeWeights
{
Left = fobject.StrokeWeight,
Right = fobject.StrokeWeight,
Top = fobject.StrokeWeight,
Bottom = fobject.StrokeWeight,
};
}
if (ind.Left != 0 && ind.Left < 1)
{
ind.Left = 1;
}
if (ind.Right != 0 && ind.Right < 1)
{
ind.Right = 1;
}
if (ind.Top != 0 && ind.Top < 1)
{
ind.Top = 1;
}
if (ind.Bottom != 0 && ind.Bottom < 1)
{
ind.Bottom = 1;
}
float coof = monoBeh.Settings.ImageSpritesSettings.ImageScale;
int texWidth = (int)(fobject.Size.x * coof);
int texHeight = (int)(fobject.Size.y * coof);
int leftWidth = (int)(ind.Left * coof);
int rightWidth = (int)(ind.Right * coof);
int topWidth = (int)(ind.Top * coof);
int bottomWidth = (int)(ind.Bottom * coof);
finalTexture = TextureBorderDrawer.CreateTextureWithBorder(
texWidth, texHeight,
leftWidth, rightWidth, topWidth, bottomWidth,
Color.white);
}
else
{
Vector2Int finalSize = default;
if (graphic.HasFill)
{
Vector2 fillSize = fobject.Size;
fillSize -= new Vector2Int(1, 0);
fillSize.IsSupportedRenderSize(monoBeh.Settings.ImageSpritesSettings.ImageScale, out finalSize, out Vector2Int bakeFillSize);
string fillPath = fobject.FillGeometry[0].Path;
Color textureColor;
if (graphic.HasFill && graphic.HasStroke)
{
textureColor = graphic.Fill.SolidPaint.Color;
}
else
{
textureColor = Color.white;
}
fillTexture = GenerateTexture(fillPath, fillSize, bakeFillSize, textureColor);
}
if (graphic.HasStroke)
{
Vector2 strokeSize = fobject.Size;
strokeSize += new Vector2(fobject.StrokeWeight * 2, fobject.StrokeWeight * 2);
strokeSize.IsSupportedRenderSize(monoBeh.Settings.ImageSpritesSettings.ImageScale, out finalSize, out Vector2Int bakeStrokeSize);
string strokePath = fobject.StrokeGeometry[0].Path;
Color textureColor;
if (graphic.HasFill && graphic.HasStroke)
{
textureColor = graphic.Stroke.SolidPaint.Color;
}
else
{
textureColor = Color.white;
}
strokeTexture = GenerateTexture(strokePath, strokeSize, bakeStrokeSize, textureColor);
}
FcuLogger.Debug($"GenerateSprites | {fobject.Data.NameHierarchy} | hasFills: {graphic.HasFill} | hasStrokes: {graphic.HasStroke}", FcuLogType.Default);
if (fillTexture != null && strokeTexture != null)
{
finalTexture = strokeTexture.Merge(fillTexture);
}
else if (strokeTexture != null)
{
finalTexture = strokeTexture;
}
else if (fillTexture != null)
{
finalTexture = fillTexture;
}
if (finalTexture == null)
{
throw new Exception("finalTexture is null");
}
finalTexture.Blur(monoBeh.Settings.ImageSpritesSettings.ImageScale / blurCoof);
finalTexture.Resize(finalSize, 0, filterMode, renderTextureFormat);
}
byte[] textureBytes = finalTexture.EncodeToPNG();
finalTexture.Destroy();
File.WriteAllBytes(fobject.Data.SpritePath, textureBytes);
}
catch (Exception ex)
{
FcuLogger.Debug($"Can't generate '{fobject.Data.NameHierarchy}'\n{ex}", FcuLogType.Error);
fobject.Data.FcuImageType = FcuImageType.Drawable;
}
generatedCount++;
}, spriteGenerationDelay, 1);
while (true)
{
DALogger.Log(FcuLocKey.log_generating_sprites.Localize(generatedCount, generative.Count));
if (generatedCount >= generative.Count)
break;
await Task.Delay(1000);
}
camera.gameObject.Destroy();
}
public Texture2D GenerateTexture(string svgPath, Vector2 sourceSize, Vector2Int bakeResolution, Color color)
{
GameObject meshObject = MonoBehExtensions.CreateEmptyGameObject();
meshObject.name = sourceSize.ToString();
meshObject.transform.position = new Vector3(-20000, -20000, 0);
try
{
GenerateMesh(meshObject, sourceSize, svgPath);
Texture2D bakedTexture = BakeTexture(meshObject, bakeResolution, color);
return bakedTexture;
}
catch (Exception ex)
{
throw ex;
}
finally
{
meshObject.Destroy();
}
}
private void GenerateMesh(GameObject meshObject, Vector2 objectSize, string svgPath)
{
meshFilter = meshObject.AddComponent<MeshFilter>();
meshRenderer = meshObject.AddComponent<MeshRenderer>();
#if UNITY_EDITOR
Material spriteMaterial = UnityEditor.AssetDatabase.GetBuiltinExtraResource<Material>("Sprites-Default.mat");
meshRenderer.material = spriteMaterial;
#endif
SVGMesh svgMesh = new SVGMesh();
svgMesh.Init(meshUpscaleFactor);
SVGData svgData = new SVGData();
svgData.Path(svgPath);
svgMesh.Fill(svgData, meshFilter);
}
private Texture2D BakeTexture(GameObject meshObject, Vector2Int bakeResolution, Color color)
{
RenderTexture renderTexture = RenderTexture.GetTemporary(bakeResolution.x, bakeResolution.y, 8, renderTextureFormat);
renderTexture.antiAliasing = renderAntialiasing;
renderTexture.filterMode = filterMode;
camera.targetTexture = renderTexture;
camera.SetToObject(meshObject);
camera.Render();
Texture2D texture = new Texture2D(bakeResolution.x, bakeResolution.y, textureFormat, false);
texture.filterMode = filterMode;
RenderTexture.active = renderTexture;
texture.ReadPixels(new Rect(0, 0, bakeResolution.x, bakeResolution.y), 0, 0);
texture.Apply();
texture.Colorize(color);
RenderTexture.ReleaseTemporary(renderTexture);
RenderTexture.active = null;
camera.targetTexture = null;
return texture;
}
}
public static class SpriteGeneratorExtensions
{
public static void SetToObject(this Camera camera, GameObject target)
{
target.transform.position = new Vector3((int)target.transform.position.x, (int)target.transform.position.y, (int)target.transform.position.z);
Renderer objectRenderer = target.GetComponent<Renderer>();
Vector3 objectSize = objectRenderer.bounds.size;
Vector3 objectPosition = objectRenderer.bounds.center;
camera.transform.position = new Vector3(objectPosition.x, objectPosition.y, -1);
camera.orthographicSize = Mathf.Max(objectSize.x / (2f * camera.aspect), objectSize.y / 2f);
}
}
public class TextureBorderDrawer
{
public static Texture2D CreateTextureWithBorder(int width, int height, int leftWidth, int rightWidth, int topWidth, int bottomWidth, Color borderColor)
{
Texture2D texture = new Texture2D(width, height);
Color transparent = new Color(0, 0, 0, 0);
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, transparent);
}
}
if (leftWidth > 0)
DrawLeftBorder(texture, leftWidth, borderColor);
if (rightWidth > 0)
DrawRightBorder(texture, rightWidth, borderColor);
if (topWidth > 0)
DrawTopBorder(texture, topWidth, borderColor);
if (bottomWidth > 0)
DrawBottomBorder(texture, bottomWidth, borderColor);
texture.Apply();
return texture;
}
private static void DrawLeftBorder(Texture2D texture, int borderWidth, Color borderColor)
{
for (int x = 0; x < borderWidth; x++)
{
for (int y = 0; y < texture.height; y++)
{
texture.SetPixel(x, y, borderColor);
}
}
}
private static void DrawRightBorder(Texture2D texture, int borderWidth, Color borderColor)
{
for (int x = texture.width - borderWidth; x < texture.width; x++)
{
for (int y = 0; y < texture.height; y++)
{
texture.SetPixel(x, y, borderColor);
}
}
}
private static void DrawTopBorder(Texture2D texture, int borderWidth, Color borderColor)
{
for (int y = texture.height - borderWidth; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, borderColor);
}
}
}
private static void DrawBottomBorder(Texture2D texture, int borderWidth, Color borderColor)
{
for (int y = 0; y < borderWidth; y++)
{
for (int x = 0; x < texture.width; x++)
{
texture.SetPixel(x, y, borderColor);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 724f10c01606ebc4886699e920ab4818
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.FCU.Model;
using DA_Assets.DAI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
namespace DA_Assets.FCU
{
[Serializable]
public class SpritePathSetter : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
public async Task SetSpritePaths(List<FObject> fobjects)
{
#if UNITY_EDITOR
List<FObject> canHasFile = fobjects
.Where(x => x.IsDownloadableType() || x.IsGenerativeType())
.ToList();
List<FObject> noDuplicates = canHasFile
.GroupBy(x => x.Data.Hash)
.Select(x => x.First())
.ToList();
await Task.Yield();
string filter = $"t:{typeof(Sprite).Name}";
string[] searchInFolder = new string[]
{
monoBeh.Settings.ImageSpritesSettings.SpritesPath
};
string[] assetSpritePathes = UnityEditor.AssetDatabase
.FindAssets(filter, searchInFolder)
.Select(x => UnityEditor.AssetDatabase.GUIDToAssetPath(x))
.ToArray();
await DACycles.ForEach(noDuplicates, item =>
{
bool imageFileExists = GetSpritePath(item, assetSpritePathes, out string spritePath);
SetNeedDownloadFileFlag(item, imageFileExists);
SetNeedGenerateFlag(item, imageFileExists);
foreach (FObject fobject in fobjects)
{
if (fobject.Data.Hash == item.Data.Hash)
{
if (imageFileExists)
{
fobject.Data.SpritePath = spritePath;
}
else
{
fobject.Data.SpritePath = GetSpritePath(item);
}
}
}
}, 0.01f, 500);
#endif
await Task.Yield();
}
private string GetSpritePath(FObject fobject)
{
string spriteDir;
if (fobject.Data.IsMutual)
spriteDir = "Mutual";
else
spriteDir = fobject.Data.RootFrame.Names.FileName;
string absoluteFramePath = Path.Combine(monoBeh.Settings.ImageSpritesSettings.SpritesPath.GetFullAssetPath(), spriteDir);
string relativeAssetPath = Path.Combine(monoBeh.Settings.ImageSpritesSettings.SpritesPath, spriteDir, fobject.Data.Names.FileName);
absoluteFramePath.CreateFolderIfNotExists();
return relativeAssetPath;
}
private bool IsTargetExtension(FObject fobject, string spritePath)
{
string spriteExt = Path.GetExtension(spritePath);
if (spriteExt.StartsWith(".") && spriteExt.Length > 1)
spriteExt = spriteExt.Remove(0, 1);
string targetExt = null;
if (monoBeh.UsingSvgImage())
{
if (fobject.CanUseUnityImage(monoBeh))
{
targetExt = ImageFormat.PNG.ToLower();
}
}
if (targetExt == null)
{
targetExt = monoBeh.Settings.ImageSpritesSettings.ImageFormat.ToLower();
}
return spriteExt == targetExt;
}
public bool GetSpritePath(FObject fobject, string[] spritePathes, out string path)
{
foreach (string spritePath in spritePathes)
{
bool get1 = spritePath.TryParseSpriteName(out float scale1, out System.Numerics.BigInteger hash1);
bool get2 = fobject.Data.Names.FileName.TryParseSpriteName(out float scale2, out System.Numerics.BigInteger hash2);
bool hasData = get1 && get2;
bool validExtension = IsTargetExtension(fobject, spritePath);
bool sameHash = hash1 == hash2;
//bool sameScale = scale1 == scale2;
if (hasData && validExtension && sameHash/* && sameScale*/)
{
path = spritePath;
return true;
}
}
path = null;
return false;
}
private void SetNeedDownloadFileFlag(FObject fobject, bool imageFileExists)
{
if (fobject.IsDownloadableType()/* || fobject.IsGenerativeType()*/)
{
if (monoBeh.Settings.ImageSpritesSettings.RedownloadSprites)
{
fobject.Data.NeedDownload = true;
}
else if (imageFileExists)
{
fobject.Data.NeedDownload = false;
}
else
{
fobject.Data.NeedDownload = true;
}
}
else
{
fobject.Data.NeedDownload = false;
}
}
private void SetNeedGenerateFlag(FObject fobject, bool imageFileExists)
{
if (fobject.IsGenerativeType())
{
if (monoBeh.Settings.ImageSpritesSettings.RedownloadSprites)
{
fobject.Data.NeedGenerate = true;
}
else if (imageFileExists)
{
fobject.Data.NeedGenerate = false;
}
else
{
fobject.Data.NeedGenerate = true;
}
}
else
{
fobject.Data.NeedGenerate = false;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 879ebdbc04dd0c949ae23093a9598549
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,285 @@
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.FCU.Model;
using DA_Assets.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using System.Collections.Concurrent;
#if VECTOR_GRAPHICS_EXISTS && UNITY_EDITOR
using Unity.VectorGraphics.Editor;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace DA_Assets.FCU
{
[Serializable]
public class SpriteProcessor : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
private int _errorLogSplitLimit = 50;
ConcurrentBag<FObject> failedObjects = new ConcurrentBag<FObject>();
public Sprite GetSprite(FObject fobject)
{
if (fobject.Data.SpritePath.IsEmpty())
{
return null;
}
Sprite sprite = null;
#if UNITY_EDITOR
sprite = (Sprite)AssetDatabase.LoadAssetAtPath(fobject.Data.SpritePath, typeof(Sprite));
#endif
return sprite;
}
#if UNITY_EDITOR
public async Task MarkAsSprites(List<FObject> fobjects)
{
failedObjects = new ConcurrentBag<FObject>();
AssetDatabase.Refresh();
List<FObject> fobjectWithSprite = fobjects.Where(x => x.Data.SpritePath != null).ToList();
int allCount = fobjectWithSprite.Count();
int count = 0;
foreach (FObject fobject in fobjectWithSprite)
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
return;
if (fobject.Data.SpritePath.IsEmpty())
continue;
_ = SetImgTypeSprite(fobject, () =>
{
count++;
});
}
int tempCount = -1;
while (FcuLogger.WriteLogBeforeEqual(
ref count,
ref allCount,
FcuLocKey.log_mark_as_sprite.Localize(count, allCount),
ref tempCount))
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
return;
await Task.Delay(1000);
}
LogFailedSprites(failedObjects);
}
private void LogFailedSprites(ConcurrentBag<FObject> failedObjects)
{
if (failedObjects.Count() > 0)
{
List<List<string>> comps = failedObjects.Select(x => x.Data.NameHierarchy).Split(_errorLogSplitLimit);
foreach (List<string> comp in comps)
{
string hierarchies = string.Join("\n", comp);
DALogger.LogError(
FcuLocKey.cant_load_sprites.Localize(comp.Count, hierarchies));
}
}
}
private async Task SetImgTypeSprite(FObject fobject, Action callback)
{
while (true)
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
return;
bool success;
if (fobject.IsSvgExtension())
{
success = SetVectorTextureSettings(fobject);
}
else
{
success = SetRasterTextureSettings(fobject);
}
if (success)
{
callback.Invoke();
break;
}
await Task.Delay(100);
}
}
private bool SetVectorTextureSettings(FObject fobject)
{
try
{
#if VECTOR_GRAPHICS_EXISTS
SVGImporter importer = AssetImporter.GetAtPath(fobject.Data.SpritePath) as SVGImporter;
UpdateVectorTextureSettings(importer, fobject.Data.SpritePath);
if (IsVectorTextureSettingsCorrect(importer))
{
return true;
}
else
{
UpdateVectorTextureSettings(importer, fobject.Data.SpritePath);
return false;
}
#else
return true;
#endif
}
catch (Exception ex)
{
FcuLogger.Debug(ex);
failedObjects.Add(fobject);
return true;
}
}
#if VECTOR_GRAPHICS_EXISTS
private bool IsVectorTextureSettingsCorrect(SVGImporter importer)
{
bool settingsCorrect = importer.SvgType == monoBeh.Settings.SVGImporterSettings.SvgType &&
importer.SvgPixelsPerUnit == monoBeh.Settings.ImageSpritesSettings.ImageScale &&
importer.GradientResolution == monoBeh.Settings.SVGImporterSettings.GradientResolution &&
importer.CustomPivot == monoBeh.Settings.SVGImporterSettings.CustomPivot &&
importer.GeneratePhysicsShape == monoBeh.Settings.SVGImporterSettings.GeneratePhysicsShape &&
importer.ViewportOptions == monoBeh.Settings.SVGImporterSettings.ViewportOptions &&
importer.StepDistance == monoBeh.Settings.SVGImporterSettings.StepDistance &&
importer.SamplingStepDistance == monoBeh.Settings.SVGImporterSettings.SamplingSteps &&
importer.AdvancedMode == monoBeh.Settings.SVGImporterSettings.AdvancedMode &&
importer.MaxCordDeviationEnabled == monoBeh.Settings.SVGImporterSettings.MaxCordDeviationEnabled &&
importer.MaxTangentAngleEnabled == monoBeh.Settings.SVGImporterSettings.MaxTangentAngleEnabled;
return settingsCorrect;
}
private void UpdateVectorTextureSettings(SVGImporter importer, string spritePath)
{
var svgImporterSettings = monoBeh.Settings.SvgImageSettings;
importer.SvgType = monoBeh.Settings.SVGImporterSettings.SvgType;
importer.SvgPixelsPerUnit = monoBeh.Settings.ImageSpritesSettings.ImageScale;
importer.GradientResolution = monoBeh.Settings.SVGImporterSettings.GradientResolution <= ushort.MaxValue ? (ushort)monoBeh.Settings.SVGImporterSettings.GradientResolution : ushort.MaxValue;
importer.CustomPivot = monoBeh.Settings.SVGImporterSettings.CustomPivot;
importer.GeneratePhysicsShape = monoBeh.Settings.SVGImporterSettings.GeneratePhysicsShape;
importer.ViewportOptions = monoBeh.Settings.SVGImporterSettings.ViewportOptions;
importer.StepDistance = monoBeh.Settings.SVGImporterSettings.StepDistance;
importer.SamplingStepDistance = monoBeh.Settings.SVGImporterSettings.SamplingSteps;
importer.AdvancedMode = monoBeh.Settings.SVGImporterSettings.AdvancedMode;
importer.MaxCordDeviationEnabled = monoBeh.Settings.SVGImporterSettings.MaxCordDeviationEnabled;
importer.MaxCordDeviation = monoBeh.Settings.SVGImporterSettings.MaxCordDeviation;
importer.MaxTangentAngleEnabled = monoBeh.Settings.SVGImporterSettings.MaxTangentAngleEnabled;
importer.MaxTangentAngle = monoBeh.Settings.SVGImporterSettings.MaxTangentAngle;
SaveAsset(importer);
}
#endif
private void SaveAsset(AssetImporter importer)
{
importer.SetDirtyExt();
importer.SaveAndReimport();
}
private bool SetRasterTextureSettings(FObject fobject)
{
try
{
TextureImporter importer = AssetImporter.GetAtPath(fobject.Data.SpritePath) as TextureImporter;
SetRasterTextureSize(fobject, importer);
if (IsRasterTextureSettingsCorrect(importer))
{
return true;
}
else
{
UpdateRasterTextureSettings(importer);
return false;
}
}
catch (Exception ex)
{
FcuLogger.Debug(ex);
failedObjects.Add(fobject);
return true;
}
}
private void SetRasterTextureSize(FObject fobject, TextureImporter importer)
{
importer.GetTextureSize(out int width, out int height);
importer.SetMaxTextureSize(width, height);
fobject.Data.SpriteSize = new Vector2Int(width, height);
}
private bool IsRasterTextureSettingsCorrect(TextureImporter importer)
{
bool part1 = importer.isReadable == monoBeh.Settings.TextureImporterSettings.IsReadable &&
importer.textureType == monoBeh.Settings.TextureImporterSettings.TextureType &&
importer.crunchedCompression == monoBeh.Settings.TextureImporterSettings.CrunchedCompression &&
importer.textureCompression == monoBeh.Settings.TextureImporterSettings.TextureCompression &&
importer.mipmapEnabled == monoBeh.Settings.TextureImporterSettings.MipmapEnabled &&
importer.spriteImportMode == monoBeh.Settings.TextureImporterSettings.SpriteImportMode;
bool perUnit;
if (monoBeh.UsingSpriteRenderer())
{
perUnit = importer.spritePixelsPerUnit == monoBeh.Settings.ImageSpritesSettings.ImageScale;
}
else
{
perUnit = importer.spritePixelsPerUnit == monoBeh.Settings.ImageSpritesSettings.PixelsPerUnit;
}
return part1 && perUnit;
}
private void UpdateRasterTextureSettings(TextureImporter importer)
{
importer.isReadable = monoBeh.Settings.TextureImporterSettings.IsReadable;
importer.textureType = monoBeh.Settings.TextureImporterSettings.TextureType;
importer.crunchedCompression = monoBeh.Settings.TextureImporterSettings.CrunchedCompression;
importer.textureCompression = monoBeh.Settings.TextureImporterSettings.TextureCompression;
importer.mipmapEnabled = monoBeh.Settings.TextureImporterSettings.MipmapEnabled;
importer.spriteImportMode = monoBeh.Settings.TextureImporterSettings.SpriteImportMode;
if (monoBeh.UsingSpriteRenderer())
{
importer.spritePixelsPerUnit = monoBeh.Settings.ImageSpritesSettings.ImageScale;
}
else
{
importer.spritePixelsPerUnit = monoBeh.Settings.ImageSpritesSettings.PixelsPerUnit;
}
if (importer.crunchedCompression)
{
importer.compressionQuality = monoBeh.Settings.TextureImporterSettings.CompressionQuality;
}
SaveAsset(importer);
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ed8e2a5f3d4c49247902b6a0ed46cf4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using DA_Assets.DAI;
using DA_Assets.Extensions;
using DA_Assets.FCU.Extensions;
using DA_Assets.FCU.Model;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
namespace DA_Assets.FCU
{
[Serializable]
public class SpriteSlicer : MonoBehaviourLinkerRuntime<FigmaConverterUnity>
{
public async Task SliceSprites(List<FObject> fobjects)
{
foreach (FObject fobject in fobjects)
{
if (monoBeh.IsCancellationRequested(TokenType.Import))
return;
if (!fobject.IsSprite())
continue;
if (fobject.Children.IsEmpty())
continue;
if (fobject.Children.Count != 9)
continue;
Sprite sprite = monoBeh.SpriteProcessor.GetSprite(fobject);
if (sprite == null)
continue;
FObject child0 = fobject.Children[0];
FObject child1 = fobject.Children[1];
FObject child2 = fobject.Children[2];
FObject child3 = fobject.Children[3];
FObject child4 = fobject.Children[4];
FObject child5 = fobject.Children[5];
FObject child6 = fobject.Children[6];
FObject child7 = fobject.Children[7];
FObject child8 = fobject.Children[8];
float imageScale = monoBeh.Settings.ImageSpritesSettings.ImageScale;
int left = (int)(child0.Size.x * imageScale);
int top = (int)(child0.Size.y * imageScale);
int right = (int)(child2.Size.x * imageScale);
int bottom = (int)(child6.Size.y * imageScale);
monoBeh.DelegateHolder.SetSpriteRects(sprite, left, top, right, bottom);
await Task.Yield();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a8421a366c33a94fb1e185550eaa21a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: