update libs

This commit is contained in:
Kirill Chikalin
2025-02-13 17:48:12 +03:00
parent e17e7c2786
commit 275dc598c7
816 changed files with 22479 additions and 10792 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f675855c3d971694785806c0c7a463be
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,97 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom
{
internal class AudioChannel
{
private int _yMin;
private int _yMax;
private int _yBaseline;
private int _yAmplitude;
private List<float> _samples;
public AudioChannel(int minY, int maxY, List<float> samples)
{
_yMin = minY;
_yMax = maxY;
_yBaseline = (_yMin + _yMax) / 2;
_yAmplitude = _yMax - _yBaseline;
_samples = samples;
}
public IEnumerable<AudioChannelCoordinate> GetCoordinateData(int desiredWidth)
{
var coordinates = new List<AudioChannelCoordinate>();
var step = Mathf.RoundToInt((float)_samples.Count / desiredWidth);
for (int i = 0; i < desiredWidth; i++)
{
var startIndex = i * step;
var endIndex = (i + 1) * step;
var sampleChunk = CreateChunk(startIndex, endIndex);
if (sampleChunk.Count() == 0)
break;
DownsampleMax(sampleChunk, out var aboveBaseline, out var belowBaseline);
var yAboveBaseline = SampleToCoordinate(aboveBaseline);
var yBelowBaseline = SampleToCoordinate(belowBaseline);
coordinates.Add(new AudioChannelCoordinate(i, _yBaseline, yAboveBaseline, yBelowBaseline));
}
// If there weren't enough samples to complete the desired width - fill out the rest with zeroes
for (int i = coordinates.Count; i < desiredWidth; i++)
coordinates.Add(new AudioChannelCoordinate(i, _yBaseline, 0, 0));
return coordinates;
}
private IEnumerable<float> CreateChunk(int startIndex, int endIndex)
{
var chunk = new List<float>();
for (int i = startIndex; i < endIndex; i++)
{
if (i >= _samples.Count)
break;
chunk.Add(_samples[i]);
}
return chunk;
}
private void DownsampleMax(IEnumerable<float> samples, out float valueAboveBaseline, out float valueBelowBaseline)
{
valueAboveBaseline = 0;
valueBelowBaseline = 0;
foreach (var sample in samples)
{
if (sample > 0 && sample > valueAboveBaseline)
{
valueAboveBaseline = sample;
continue;
}
if (sample < 0 && sample < valueBelowBaseline)
{
valueBelowBaseline = sample;
continue;
}
}
}
private int SampleToCoordinate(float sample)
{
return _yBaseline + (int)(sample * _yAmplitude);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 82fab55b08a1be94cb2e18f3feae91ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannel.cs
uploadId: 724584

View File

@@ -0,0 +1,18 @@
namespace AssetStoreTools.Previews.Generators.Custom
{
internal struct AudioChannelCoordinate
{
public int X { get; private set; }
public int YBaseline { get; private set; }
public int YAboveBaseline { get; private set; }
public int YBelowBaseline { get; private set; }
public AudioChannelCoordinate(int x, int yBaseline, int yAboveBaseline, int yBelowBaseline)
{
X = x;
YBaseline = yBaseline;
YAboveBaseline = yAboveBaseline;
YBelowBaseline = yBelowBaseline;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b54462f6af82a2644944d6e4bde23c9e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/AudioChannelCoordinate.cs
uploadId: 724584

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c07070deed666d54cb72a89a5fcd7ef7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters
{
internal interface ISceneScreenshotter
{
SceneScreenshotterSettings Settings { get; }
string Screenshot(string outputPath);
string Screenshot(GameObject target, string outputPath);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 045ac265a792af243918af0849ee2ac8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/ISceneScreenshotter.cs
uploadId: 724584

View File

@@ -0,0 +1,32 @@
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters
{
internal class MaterialScreenshotter : SceneScreenshotterBase
{
public MaterialScreenshotter(SceneScreenshotterSettings settings) : base(settings) { }
public override void PositionCamera(GameObject target)
{
var renderers = target.GetComponentsInChildren<Renderer>();
if (renderers == null || renderers.Length == 0)
return;
var bounds = GetGlobalBounds(renderers);
var materialSphereRadius = bounds.extents.y * 1.1f;
var angle = Camera.fieldOfView / 2;
var sinAngle = Mathf.Sin(angle * Mathf.Deg2Rad);
var distance = materialSphereRadius / sinAngle;
Camera.transform.position = new Vector3(bounds.center.x, bounds.center.y + distance, bounds.center.z);
Camera.transform.LookAt(bounds.center);
Camera.transform.RotateAround(bounds.center, Vector3.left, 60);
Camera.transform.RotateAround(bounds.center, Vector3.up, -45);
Camera.nearClipPlane = 0.01f;
Camera.farClipPlane = 10000;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c2bd7b01b0cebeb43a6fbc53377f0ea6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MaterialScreenshotter.cs
uploadId: 724584

View File

@@ -0,0 +1,33 @@
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters
{
internal class MeshScreenshotter : SceneScreenshotterBase
{
public MeshScreenshotter(SceneScreenshotterSettings settings) : base(settings) { }
public override void PositionCamera(GameObject target)
{
var renderers = target.GetComponentsInChildren<Renderer>();
if (renderers == null || renderers.Length == 0)
return;
var bounds = GetGlobalBounds(renderers);
var encapsulatingSphereDiameter = (bounds.max - bounds.min).magnitude;
var encapsulatingSphereRadius = encapsulatingSphereDiameter / 2;
var angle = Camera.fieldOfView / 2;
var sinAngle = Mathf.Sin(angle * Mathf.Deg2Rad);
var distance = encapsulatingSphereRadius / sinAngle;
Camera.transform.position = new Vector3(bounds.center.x, bounds.center.y + distance, bounds.center.z);
Camera.transform.LookAt(bounds.center);
Camera.transform.RotateAround(bounds.center, Vector3.left, 65);
Camera.transform.RotateAround(bounds.center, Vector3.up, 235);
Camera.nearClipPlane = 0.01f;
Camera.farClipPlane = 10000;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: f0339d22d91b7c94ebc18b1de6f1e287
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/MeshScreenshotter.cs
uploadId: 724584

View File

@@ -0,0 +1,124 @@
using AssetStoreTools.Previews.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters
{
internal abstract class SceneScreenshotterBase : ISceneScreenshotter
{
public SceneScreenshotterSettings Settings { get; }
protected Camera Camera => GetCamera();
private Camera _camera;
public SceneScreenshotterBase(SceneScreenshotterSettings settings)
{
Settings = settings;
}
private Camera GetCamera()
{
if (_camera == null)
{
#if UNITY_2022_3_OR_NEWER
_camera = GameObject.FindFirstObjectByType<Camera>(FindObjectsInactive.Include);
#else
_camera = GameObject.FindObjectOfType<Camera>();
#endif
}
return _camera;
}
public virtual void ValidateSettings()
{
if (Settings.Width <= 0)
throw new ArgumentException("Width should be larger than 0");
if (Settings.Height <= 0)
throw new ArgumentException("Height should be larger than 0");
if (Settings.Depth <= 0)
throw new ArgumentException("Depth should be larger than 0");
if (Settings.NativeWidth <= 0)
throw new ArgumentException("Native width should be larger than 0");
if (Settings.NativeHeight <= 0)
throw new ArgumentException("Native height should be larger than 0");
}
public abstract void PositionCamera(GameObject target);
public string Screenshot(string outputPath)
{
ValidateSettings();
var texture = GraphicsUtility.GetTextureFromCamera(Camera, Settings.NativeWidth, Settings.NativeHeight, Settings.Depth);
if (Settings.Width < Settings.NativeWidth || Settings.Height < Settings.NativeHeight)
texture = GraphicsUtility.ResizeTexture(texture, Settings.Width, Settings.Height);
var extension = PreviewConvertUtility.ConvertExtension(Settings.Format);
var writtenPath = $"{outputPath}.{extension}";
var bytes = PreviewConvertUtility.ConvertTexture(texture, Settings.Format);
File.WriteAllBytes(writtenPath, bytes);
return writtenPath;
}
public string Screenshot(GameObject target, string outputPath)
{
PositionCamera(target);
PositionLighting(target);
return Screenshot(outputPath);
}
private void PositionLighting(GameObject target)
{
#if UNITY_2022_3_OR_NEWER
var light = GameObject.FindFirstObjectByType<Light>(FindObjectsInactive.Include);
#else
var light = GameObject.FindObjectOfType<Light>();
#endif
light.transform.position = Camera.transform.position;
light.transform.LookAt(target.transform);
light.transform.RotateAround(target.transform.position, Vector3.forward, 60f);
}
protected Bounds GetGlobalBounds(IEnumerable<Renderer> renderers)
{
var center = Vector3.zero;
foreach (var renderer in renderers)
{
center += renderer.bounds.center;
}
center /= renderers.Count();
var globalBounds = new Bounds(center, Vector3.zero);
foreach (var renderer in renderers)
{
globalBounds.Encapsulate(renderer.bounds);
}
return globalBounds;
}
protected Bounds GetNormalizedBounds(Bounds bounds)
{
var largestExtent = Mathf.Max(bounds.extents.x, bounds.extents.y, bounds.extents.z);
var normalizedBounds = new Bounds()
{
center = bounds.center,
extents = new Vector3(largestExtent, largestExtent, largestExtent)
};
return normalizedBounds;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ce77aefdce8a37f498d17d73da53d0a4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterBase.cs
uploadId: 724584

View File

@@ -0,0 +1,16 @@
using AssetStoreTools.Previews.Data;
namespace AssetStoreTools.Previews.Generators.Custom.Screenshotters
{
internal class SceneScreenshotterSettings
{
public int Width;
public int Height;
public int Depth;
public int NativeWidth;
public int NativeHeight;
public PreviewFormat Format;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: aa34806e243bad949892d06dd47295e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/Screenshotters/SceneScreenshotterSettings.cs
uploadId: 724584

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b4bb2dd0960418d4a8d4efd34b92a418
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
using AssetStoreTools.Previews.Data;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class AudioTypeGeneratorSettings : TypeGeneratorSettings
{
public int Width;
public int Height;
public Color SampleColor;
public Color BackgroundColor;
public PreviewFormat Format;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5b7ab1b072d95be4daf221ee23af1c80
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypeGeneratorSettings.cs
uploadId: 724584

View File

@@ -0,0 +1,207 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class AudioTypePreviewGenerator : TypePreviewGeneratorBase
{
private AudioTypeGeneratorSettings _settings;
private Texture2D _texture;
public override event Action<int, int> OnAssetProcessed;
public AudioTypePreviewGenerator(AudioTypeGeneratorSettings settings) : base(settings)
{
_settings = settings;
}
public override void ValidateSettings()
{
base.ValidateSettings();
if (_settings.Width <= 0)
throw new ArgumentException("Width must be larger than 0");
if (_settings.Height <= 0)
throw new ArgumentException("Height must be larger than 0");
}
protected override IEnumerable<UnityEngine.Object> CollectAssets()
{
var assets = new List<UnityEngine.Object>();
var materialGuids = AssetDatabase.FindAssets("t:audioclip", Settings.InputPaths);
foreach (var guid in materialGuids)
{
var audioClip = AssetDatabase.LoadAssetAtPath<AudioClip>(AssetDatabase.GUIDToAssetPath(guid));
// Skip nested audio clips
if (!AssetDatabase.IsMainAsset(audioClip))
continue;
// Skip materials with an error shader
if (!IsLoadTypeSupported(audioClip))
{
Debug.LogWarning($"Audio clip '{audioClip}' is using a load type which cannot retrieve sample data. Preview will not be generated.");
continue;
}
assets.Add(audioClip);
}
return assets;
}
private bool IsLoadTypeSupported(AudioClip audioClip)
{
if (audioClip.loadType == AudioClipLoadType.DecompressOnLoad)
return true;
return false;
}
protected override async Task<List<PreviewMetadata>> GenerateImpl(IEnumerable<UnityEngine.Object> assets)
{
var generatedPreviews = new List<PreviewMetadata>();
var audioClips = assets.ToList();
for (int i = 0; i < audioClips.Count; i++)
{
var audioClip = audioClips[i] as AudioClip;
if (audioClip != null)
{
var texture = GenerateAudioClipTexture(audioClip);
var outputPath = GenerateOutputPathWithExtension(audioClip, _settings.PreviewFileNamingFormat, _settings.Format);
var bytes = PreviewConvertUtility.ConvertTexture(texture, _settings.Format);
File.WriteAllBytes(outputPath, bytes);
generatedPreviews.Add(ObjectToMetadata(audioClip, outputPath));
}
OnAssetProcessed?.Invoke(i, audioClips.Count);
await Task.Yield();
}
return generatedPreviews;
}
private Texture2D GenerateAudioClipTexture(AudioClip audioClip)
{
if (!audioClip.LoadAudioData())
throw new Exception("Could not load audio data");
try
{
if (_texture == null)
_texture = new Texture2D(_settings.Width, _settings.Height);
else
#if UNITY_2021_3_OR_NEWER || UNITY_2022_1_OR_NEWER || UNITY_2021_2_OR_NEWER
_texture.Reinitialize(_settings.Width, _settings.Height);
#else
_texture.Resize(_settings.Width, _settings.Height);
#endif
FillTextureBackground();
FillTextureForeground(audioClip);
_texture.Apply();
return _texture;
}
finally
{
audioClip.UnloadAudioData();
}
}
private void FillTextureBackground()
{
for (int i = 0; i < _texture.width; i++)
{
for (int j = 0; j < _texture.height; j++)
{
_texture.SetPixel(i, j, _settings.BackgroundColor);
}
}
}
private void FillTextureForeground(AudioClip audioClip)
{
var channels = CreateChannels(audioClip);
for (int i = 0; i < channels.Count; i++)
{
DrawChannel(channels[i]);
}
}
private List<AudioChannel> CreateChannels(AudioClip audioClip)
{
var channelSamples = GetChannelSamples(audioClip);
var sectionSize = _texture.height / audioClip.channels;
var channels = new List<AudioChannel>();
for (int i = 0; i < audioClip.channels; i++)
{
var channelMaxY = (_texture.height - 1) - i * sectionSize;
var channelMinY = _texture.height - (i + 1) * sectionSize;
var channel = new AudioChannel(channelMinY, channelMaxY, channelSamples[i]);
channels.Add(channel);
}
return channels;
}
private List<List<float>> GetChannelSamples(AudioClip audioClip)
{
var channelSamples = new List<List<float>>();
var allSamples = new float[audioClip.samples * audioClip.channels];
if (!audioClip.GetData(allSamples, 0))
throw new Exception("Could not retrieve audio samples");
for (int i = 0; i < audioClip.channels; i++)
{
var samples = new List<float>();
var sampleIndex = i;
while (sampleIndex < allSamples.Length)
{
samples.Add(allSamples[sampleIndex]);
sampleIndex += audioClip.channels;
}
channelSamples.Add(samples);
}
return channelSamples;
}
private void DrawChannel(AudioChannel channel)
{
var sectionData = channel.GetCoordinateData(_texture.width);
foreach (var data in sectionData)
{
DrawVerticalColumn(data.X, data.YBaseline, data.YAboveBaseline, data.YBelowBaseline, _settings.SampleColor);
}
}
private void DrawVerticalColumn(int x, int yBaseline, int y1, int y2, Color color)
{
_texture.SetPixel(x, yBaseline, color);
var startIndex = y1 < y2 ? y1 : y2;
var endIndex = y1 < y2 ? y2 : y1;
for (int i = startIndex; i < endIndex; i++)
{
_texture.SetPixel(x, i, color);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9ddf69bb2dca51a42aff247b3a471bb3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/AudioTypePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,16 @@
using AssetStoreTools.Previews.Data;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal interface ITypePreviewGenerator
{
TypeGeneratorSettings Settings { get; }
event Action<int, int> OnAssetProcessed;
Task<List<PreviewMetadata>> Generate();
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7d9cd368dc73a23478390ee1332cb0be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ITypePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,85 @@
using AssetStoreTools.Previews.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class MaterialTypePreviewGenerator : TypePreviewGeneratorFromScene
{
public override event Action<int, int> OnAssetProcessed;
public MaterialTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { }
protected override IEnumerable<UnityEngine.Object> CollectAssets()
{
var assets = new List<UnityEngine.Object>();
var materialGuids = AssetDatabase.FindAssets("t:material", Settings.InputPaths);
foreach (var guid in materialGuids)
{
var mat = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid));
// Skip nested materials
if (!AssetDatabase.IsMainAsset(mat))
continue;
// Skip materials with an error shader
if (IsShaderInvalid(mat.shader))
{
Debug.LogWarning($"Material '{mat}' is using an erroring shader. Preview will not be generated.");
continue;
}
assets.Add(mat);
}
return assets;
}
protected override async Task<List<PreviewMetadata>> GeneratePreviewsInScene(IEnumerable<UnityEngine.Object> assets)
{
var generatedPreviews = new List<PreviewMetadata>();
var materials = assets.ToList();
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
var hasMeshRenderer = sphere.TryGetComponent<Renderer>(out var meshRenderer);
if (!hasMeshRenderer)
throw new Exception($"Could not find a MeshRenderer for {sphere}");
for (int i = 0; i < materials.Count; i++)
{
ThrowIfSceneChanged();
var material = materials[i] as Material;
if (material != null)
{
meshRenderer.sharedMaterial = material;
var previewPath = Settings.Screenshotter.Screenshot(sphere, GenerateOutputPathWithoutExtension(material, Settings.PreviewFileNamingFormat));
if (!string.IsNullOrEmpty(previewPath))
generatedPreviews.Add(ObjectToMetadata(material, previewPath));
}
OnAssetProcessed?.Invoke(i, materials.Count);
await Task.Yield();
}
UnityEngine.Object.DestroyImmediate(sphere);
return generatedPreviews;
}
private bool IsShaderInvalid(Shader shader)
{
if (ShaderUtil.ShaderHasError(shader))
return true;
if (!shader.isSupported)
return true;
return false;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a4e121bae63a199458e53a523dd18c8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/MaterialTypePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,86 @@
using AssetStoreTools.Previews.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class ModelTypePreviewGenerator : TypePreviewGeneratorFromScene
{
public override event Action<int, int> OnAssetProcessed;
public ModelTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { }
protected override IEnumerable<UnityEngine.Object> CollectAssets()
{
var models = new List<UnityEngine.Object>();
var modelGuids = AssetDatabase.FindAssets("t:model", Settings.InputPaths);
foreach (var guid in modelGuids)
{
var model = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(guid));
// Skip nested models
if (!AssetDatabase.IsMainAsset(model))
continue;
// Skip models without renderers
if (model.GetComponentsInChildren<Renderer>().Length == 0)
continue;
models.Add(model);
}
return models;
}
protected override async Task<List<PreviewMetadata>> GeneratePreviewsInScene(IEnumerable<UnityEngine.Object> assets)
{
var generatedPreviews = new List<PreviewMetadata>();
var models = assets.ToList();
var referenceShader = GetDefaultObjectShader();
for (int i = 0; i < models.Count; i++)
{
ThrowIfSceneChanged();
var model = models[i] as GameObject;
if (model != null)
{
var go = UnityEngine.Object.Instantiate(model, Vector3.zero, Quaternion.Euler(0, 0, 0));
ReplaceShaders(go, referenceShader);
var previewPath = Settings.Screenshotter.Screenshot(go, GenerateOutputPathWithoutExtension(model, Settings.PreviewFileNamingFormat));
if (!string.IsNullOrEmpty(previewPath))
generatedPreviews.Add(ObjectToMetadata(model, previewPath));
UnityEngine.Object.DestroyImmediate(go);
}
OnAssetProcessed?.Invoke(i, models.Count);
await Task.Yield();
}
return generatedPreviews;
}
private void ReplaceShaders(GameObject go, Shader shader)
{
var meshRenderers = go.GetComponentsInChildren<Renderer>();
foreach (var mr in meshRenderers)
{
var materialArray = mr.sharedMaterials;
for (int i = 0; i < materialArray.Length; i++)
{
materialArray[i] = new Material(shader) { color = new Color(0.7f, 0.7f, 0.7f) };
}
mr.sharedMaterials = materialArray;
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: fca8a1fa8a211874cb84d3d811a0158c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/ModelTypePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,113 @@
using AssetStoreTools.Previews.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class PrefabTypePreviewGenerator : TypePreviewGeneratorFromScene
{
public override event Action<int, int> OnAssetProcessed;
public PrefabTypePreviewGenerator(TypePreviewGeneratorFromSceneSettings settings) : base(settings) { }
protected override IEnumerable<UnityEngine.Object> CollectAssets()
{
var prefabs = new List<UnityEngine.Object>();
var prefabGuids = AssetDatabase.FindAssets("t:prefab", Settings.InputPaths);
foreach (var guid in prefabGuids)
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(guid));
// Skip nested prefabs
if (!AssetDatabase.IsMainAsset(prefab))
continue;
// Skip prefabs without renderers
if (prefab.GetComponentsInChildren<Renderer>().Length == 0)
continue;
prefabs.Add(prefab);
}
return prefabs;
}
protected override async Task<List<PreviewMetadata>> GeneratePreviewsInScene(IEnumerable<UnityEngine.Object> assets)
{
var generatedPreviews = new List<PreviewMetadata>();
var prefabs = assets.ToList();
var objectReferenceShader = GetDefaultObjectShader();
var particleReferenceShader = GetDefaultParticleShader();
for (int i = 0; i < prefabs.Count; i++)
{
ThrowIfSceneChanged();
var prefab = prefabs[i] as GameObject;
if (prefab != null)
{
var go = UnityEngine.Object.Instantiate(prefab, Vector3.zero, Quaternion.Euler(0, 0, 0));
ReplaceMissingShaders(go, objectReferenceShader, particleReferenceShader);
HandleParticleSystems(go);
var previewPath = Settings.Screenshotter.Screenshot(go, GenerateOutputPathWithoutExtension(prefab, Settings.PreviewFileNamingFormat));
if (!string.IsNullOrEmpty(previewPath))
generatedPreviews.Add(ObjectToMetadata(prefab, previewPath));
UnityEngine.Object.DestroyImmediate(go);
}
OnAssetProcessed?.Invoke(i, prefabs.Count);
await Task.Yield();
}
return generatedPreviews;
}
private void ReplaceMissingShaders(GameObject go, Shader objectShader, Shader particleShader)
{
var meshRenderers = go.GetComponentsInChildren<Renderer>();
foreach (var mr in meshRenderers)
{
var shaderToUse = mr is ParticleSystemRenderer ? particleShader : objectShader;
var materialArray = mr.sharedMaterials;
for (int i = 0; i < materialArray.Length; i++)
{
if (materialArray[i] == null)
{
materialArray[i] = new Material(shaderToUse);
}
else if (!materialArray[i].shader.isSupported)
{
materialArray[i].shader = shaderToUse;
}
}
mr.sharedMaterials = materialArray;
}
}
private void HandleParticleSystems(GameObject go)
{
var particleSystems = go.GetComponentsInChildren<ParticleSystem>();
if (particleSystems.Length == 0)
return;
foreach (var ps in particleSystems)
{
ps.Stop();
ps.Clear();
ps.randomSeed = 1;
ps.Simulate(10, false, true, false);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 24b15b10bc361c84581f46cb6dd644cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/PrefabTypePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,11 @@
using AssetStoreTools.Previews.Data;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class TextureTypeGeneratorSettings : TypeGeneratorSettings
{
public int MaxWidth;
public int MaxHeight;
public PreviewFormat Format;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 058d746982619b04eb5e200363003899
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypeGeneratorSettings.cs
uploadId: 724584

View File

@@ -0,0 +1,116 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class TextureTypePreviewGenerator : TypePreviewGeneratorBase
{
private TextureTypeGeneratorSettings _settings;
public override event Action<int, int> OnAssetProcessed;
public TextureTypePreviewGenerator(TextureTypeGeneratorSettings settings) : base(settings)
{
_settings = settings;
}
public override void ValidateSettings()
{
base.ValidateSettings();
if (_settings.MaxWidth <= 0)
throw new ArgumentException("Max width should be larger than 0");
if (_settings.MaxHeight <= 0)
throw new ArgumentException("Max height should be larger than 0");
}
protected override IEnumerable<UnityEngine.Object> CollectAssets()
{
var textures = new List<UnityEngine.Object>();
var textureGuids = AssetDatabase.FindAssets("t:texture", Settings.InputPaths);
foreach (var guid in textureGuids)
{
var texture = AssetDatabase.LoadAssetAtPath<Texture>(AssetDatabase.GUIDToAssetPath(guid));
// Skip nested textures
if (!AssetDatabase.IsMainAsset(texture))
continue;
textures.Add(texture);
}
return textures;
}
protected override async Task<List<PreviewMetadata>> GenerateImpl(IEnumerable<UnityEngine.Object> assets)
{
var generatedPreviews = new List<PreviewMetadata>();
var textures = assets.ToList();
for (int i = 0; i < textures.Count; i++)
{
var texture = textures[i] as Texture2D;
if (texture != null)
{
Texture2D resizedTexture;
CalculateTextureSize(texture, out var resizeWidth, out var resizeHeight);
var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;
if (importer != null && importer.textureType == TextureImporterType.NormalMap)
resizedTexture = GraphicsUtility.ResizeTextureNormalMap(texture, resizeWidth, resizeHeight);
else
resizedTexture = GraphicsUtility.ResizeTexture(texture, resizeWidth, resizeHeight);
var previewPath = GenerateOutputPathWithExtension(texture, _settings.PreviewFileNamingFormat, _settings.Format);
// Some textures may be transparent and need to be encoded as PNG to look correctly
var targetFormat = texture.alphaIsTransparency ? PreviewFormat.PNG : _settings.Format;
var bytes = PreviewConvertUtility.ConvertTexture(resizedTexture, targetFormat);
File.WriteAllBytes(previewPath, bytes);
generatedPreviews.Add(ObjectToMetadata(texture, previewPath));
}
OnAssetProcessed?.Invoke(i, textures.Count);
await Task.Yield();
}
return generatedPreviews;
}
private void CalculateTextureSize(Texture2D texture, out int width, out int height)
{
if (texture.width <= _settings.MaxWidth && texture.height <= _settings.MaxHeight)
{
width = texture.width;
height = texture.height;
return;
}
var widthLongerThanHeight = texture.width > texture.height;
if (widthLongerThanHeight)
{
var ratio = (float)texture.width / texture.height;
width = _settings.MaxWidth;
height = Mathf.RoundToInt(width / ratio);
}
else
{
var ratio = (float)texture.height / texture.width;
height = _settings.MaxHeight;
width = Mathf.RoundToInt(height / ratio);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b04f55867ee575c489803356220feb31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TextureTypePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,12 @@
using AssetStoreTools.Previews.Data;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal abstract class TypeGeneratorSettings
{
public string[] InputPaths;
public string[] IgnoredGuids;
public string OutputPath;
public FileNameFormat PreviewFileNamingFormat;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a224a4b41a8c7cf4cb53dd77d6f2518b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypeGeneratorSettings.cs
uploadId: 724584

View File

@@ -0,0 +1,126 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal abstract class TypePreviewGeneratorBase : ITypePreviewGenerator
{
public TypeGeneratorSettings Settings { get; }
public abstract event Action<int, int> OnAssetProcessed;
public TypePreviewGeneratorBase(TypeGeneratorSettings settings)
{
Settings = settings;
}
public virtual void ValidateSettings()
{
if (Settings.InputPaths == null || Settings.InputPaths.Length == 0)
throw new ArgumentException("Input path cannot be null");
foreach (var path in Settings.InputPaths)
{
var inputPath = path.EndsWith("/") ? path.Remove(path.Length - 1) : path;
if (!AssetDatabase.IsValidFolder(inputPath))
throw new ArgumentException($"Input path '{inputPath}' is not a valid ADB folder");
}
if (string.IsNullOrEmpty(Settings.OutputPath))
throw new ArgumentException("Output path cannot be null");
}
public async Task<List<PreviewMetadata>> Generate()
{
var generatedPreviews = new List<PreviewMetadata>();
ValidateSettings();
var assets = CollectAssets();
assets = FilterIgnoredAssets(assets);
if (assets.Count() == 0)
return generatedPreviews;
return await GenerateImpl(assets);
}
protected abstract IEnumerable<UnityEngine.Object> CollectAssets();
private IEnumerable<UnityEngine.Object> FilterIgnoredAssets(IEnumerable<UnityEngine.Object> assets)
{
if (Settings.IgnoredGuids == null || Settings.IgnoredGuids.Length == 0)
return assets;
var filteredAssets = new List<UnityEngine.Object>();
foreach (var asset in assets)
{
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long _))
continue;
if (Settings.IgnoredGuids.Any(x => x == guid))
continue;
filteredAssets.Add(asset);
}
return filteredAssets;
}
protected abstract Task<List<PreviewMetadata>> GenerateImpl(IEnumerable<UnityEngine.Object> assets);
protected PreviewMetadata ObjectToMetadata(UnityEngine.Object obj, string previewPath)
{
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _))
throw new Exception($"Could not retrieve guid for object {obj}");
return new PreviewMetadata()
{
Type = GenerationType.Custom,
Guid = guid,
Name = obj.name,
Path = previewPath
};
}
protected string GenerateOutputPathWithoutExtension(UnityEngine.Object asset, FileNameFormat fileNameFormat)
{
PrepareOutputFolder(Settings.OutputPath, false);
var directoryPath = Settings.OutputPath;
var fileName = PreviewConvertUtility.ConvertFilename(asset, fileNameFormat);
var fullPath = $"{directoryPath}/{fileName}";
return fullPath;
}
protected string GenerateOutputPathWithExtension(UnityEngine.Object asset, FileNameFormat fileNameFormat, PreviewFormat previewFormat)
{
var partialOutputPath = GenerateOutputPathWithoutExtension(asset, fileNameFormat);
var extension = PreviewConvertUtility.ConvertExtension(previewFormat);
return $"{partialOutputPath}.{extension}";
}
private void PrepareOutputFolder(string outputPath, bool cleanup)
{
var dir = new DirectoryInfo(outputPath);
if (!dir.Exists)
{
dir.Create();
return;
}
if (!cleanup)
return;
dir.Delete(true);
dir.Create();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c6fb2d639232bce4698338a252f47f3c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorBase.cs
uploadId: 724584

View File

@@ -0,0 +1,111 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Utility;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal abstract class TypePreviewGeneratorFromScene : TypePreviewGeneratorBase
{
protected new TypePreviewGeneratorFromSceneSettings Settings;
private CancellationTokenSource _cancellationTokenSource;
public TypePreviewGeneratorFromScene(TypePreviewGeneratorFromSceneSettings settings) : base(settings)
{
Settings = settings;
}
public override void ValidateSettings()
{
base.ValidateSettings();
if (Settings.Screenshotter == null)
throw new ArgumentException("Screenshotter cannot be null");
}
protected sealed override async Task<List<PreviewMetadata>> GenerateImpl(IEnumerable<UnityEngine.Object> assets)
{
var originalScenePath = EditorSceneManager.GetActiveScene().path;
await PreviewSceneUtility.OpenPreviewSceneForCurrentPipeline();
try
{
_cancellationTokenSource = new CancellationTokenSource();
EditorSceneManager.sceneOpened += SceneOpenedDuringGeneration;
return await GeneratePreviewsInScene(assets);
}
finally
{
EditorSceneManager.sceneOpened -= SceneOpenedDuringGeneration;
_cancellationTokenSource.Dispose();
if (!string.IsNullOrEmpty(originalScenePath))
EditorSceneManager.OpenScene(originalScenePath);
}
}
protected abstract Task<List<PreviewMetadata>> GeneratePreviewsInScene(IEnumerable<UnityEngine.Object> assets);
private void SceneOpenedDuringGeneration(Scene _, OpenSceneMode __)
{
if (!_cancellationTokenSource.IsCancellationRequested)
_cancellationTokenSource.Cancel();
}
protected void ThrowIfSceneChanged()
{
if (_cancellationTokenSource.Token.IsCancellationRequested)
throw new Exception("Preview generation was aborted due to a change of the scene");
}
protected Shader GetDefaultObjectShader()
{
switch (RenderPipelineUtility.GetCurrentPipeline())
{
case RenderPipeline.BiRP:
return Shader.Find("Standard");
case RenderPipeline.URP:
return Shader.Find("Universal Render Pipeline/Lit");
case RenderPipeline.HDRP:
return Shader.Find("HDRP/Lit");
default:
throw new NotImplementedException("Undefined Render Pipeline");
}
}
protected Shader GetDefaultParticleShader()
{
switch (RenderPipelineUtility.GetCurrentPipeline())
{
case RenderPipeline.BiRP:
return Shader.Find("Particles/Standard Unlit");
case RenderPipeline.URP:
return Shader.Find("Universal Render Pipeline/Particles/Unlit");
case RenderPipeline.HDRP:
return Shader.Find("HDRP/Unlit");
default:
throw new NotImplementedException("Undefined Render Pipeline");
}
}
protected Shader GetDefaultTextureShader()
{
switch (RenderPipelineUtility.GetCurrentPipeline())
{
case RenderPipeline.BiRP:
return Shader.Find("Unlit/Texture");
case RenderPipeline.URP:
return Shader.Find("Universal Render Pipeline/Unlit");
case RenderPipeline.HDRP:
return Shader.Find("HDRP/Unlit");
default:
throw new NotImplementedException("Undefined Render Pipeline");
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1bf0eb8d7ef65f340be785dae96e4b73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromScene.cs
uploadId: 724584

View File

@@ -0,0 +1,9 @@
using AssetStoreTools.Previews.Generators.Custom.Screenshotters;
namespace AssetStoreTools.Previews.Generators.Custom.TypeGenerators
{
internal class TypePreviewGeneratorFromSceneSettings : TypeGeneratorSettings
{
public ISceneScreenshotter Screenshotter;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a6b871798f99ad44d9fca46789239ec1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/Custom/TypeGenerators/TypePreviewGeneratorFromSceneSettings.cs
uploadId: 724584

View File

@@ -0,0 +1,213 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Generators;
using AssetStoreTools.Previews.Generators.Custom.Screenshotters;
using AssetStoreTools.Previews.Generators.Custom.TypeGenerators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
namespace AssetStoreTools.Previews
{
internal class CustomPreviewGenerator : PreviewGeneratorBase
{
private CustomPreviewGenerationSettings _customSettings;
public override event Action<float> OnProgressChanged;
public CustomPreviewGenerator(CustomPreviewGenerationSettings settings)
: base(settings)
{
_customSettings = settings;
}
protected override void Validate()
{
base.Validate();
if (_customSettings.Width <= 0)
throw new ArgumentException("Width should be larger than 0");
if (_customSettings.Height <= 0)
throw new ArgumentException("Height should be larger than 0");
if (_customSettings.Depth <= 0)
throw new ArgumentException("Depth should be larger than 0");
if (_customSettings.NativeWidth <= 0)
throw new ArgumentException("Native width should be larger than 0");
if (_customSettings.NativeHeight <= 0)
throw new ArgumentException("Native height should be larger than 0");
}
protected override async Task<PreviewGenerationResult> GenerateImpl()
{
var result = new PreviewGenerationResult()
{
GenerationType = _customSettings.GenerationType
};
OnProgressChanged?.Invoke(0f);
var generatedPreviews = new List<PreviewMetadata>();
var existingPreviews = GetExistingPreviews();
var generators = CreateGenerators(existingPreviews);
var currentGenerator = 0;
Action<int, int> generatorProgressCallback = null;
generatorProgressCallback = (currentAsset, totalAssets) => ReportProgress(currentGenerator, generators.Count(), currentAsset, totalAssets);
try
{
foreach (var generator in generators)
{
generator.OnAssetProcessed += generatorProgressCallback;
var typeGeneratorPreviews = await generator.Generate();
generatedPreviews.AddRange(typeGeneratorPreviews);
currentGenerator++;
}
AssetDatabase.Refresh();
var allPreviews = new List<PreviewMetadata>();
allPreviews.AddRange(generatedPreviews);
allPreviews.AddRange(existingPreviews);
result.Success = true;
result.GeneratedPreviews = generatedPreviews;
result.Previews = allPreviews;
}
catch (Exception e)
{
result.Success = false;
result.Exception = e;
}
finally
{
foreach (var generator in generators)
generator.OnAssetProcessed -= generatorProgressCallback;
}
return result;
}
private IEnumerable<PreviewMetadata> GetExistingPreviews()
{
var existingPreviews = new List<PreviewMetadata>();
if (Settings.OverwriteExisting || !CachingService.GetCachedMetadata(out var database))
return existingPreviews;
var inputGuids = AssetDatabase.FindAssets("", _customSettings.InputPaths);
existingPreviews = database.Previews.Where(x => x.Type == GenerationType.Custom && x.Exists() && inputGuids.Any(y => y.Equals(x.Guid))).ToList();
return existingPreviews;
}
private IEnumerable<ITypePreviewGenerator> CreateGenerators(IEnumerable<PreviewMetadata> existingPreviews)
{
var ignoredGuids = existingPreviews.Select(x => x.Guid).ToArray();
var generators = new ITypePreviewGenerator[]
{
CreateAudioPreviewGenerator(ignoredGuids),
CreateMaterialPreviewGenerator(ignoredGuids),
CreateModelPreviewGenerator(ignoredGuids),
CreatePrefabPreviewGenerator(ignoredGuids),
CreateTexturePreviewGenerator(ignoredGuids)
};
return generators;
}
private ITypePreviewGenerator CreateAudioPreviewGenerator(string[] ignoredGuids)
{
var settings = new AudioTypeGeneratorSettings()
{
Width = _customSettings.Width,
Height = _customSettings.Height,
InputPaths = _customSettings.InputPaths,
OutputPath = _customSettings.OutputPath,
PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat,
Format = _customSettings.Format,
SampleColor = _customSettings.AudioSampleColor,
BackgroundColor = _customSettings.AudioBackgroundColor,
IgnoredGuids = ignoredGuids
};
return new AudioTypePreviewGenerator(settings);
}
private ITypePreviewGenerator CreateMaterialPreviewGenerator(string[] ignoredGuids)
{
var settings = CreateSceneGeneratorSettings(new MaterialScreenshotter(CreateScreenshotterSettings()), ignoredGuids);
return new MaterialTypePreviewGenerator(settings);
}
private ITypePreviewGenerator CreateModelPreviewGenerator(string[] ignoredGuids)
{
var settings = CreateSceneGeneratorSettings(new MeshScreenshotter(CreateScreenshotterSettings()), ignoredGuids);
return new ModelTypePreviewGenerator(settings);
}
private ITypePreviewGenerator CreatePrefabPreviewGenerator(string[] ignoredGuids)
{
var settings = CreateSceneGeneratorSettings(new MeshScreenshotter(CreateScreenshotterSettings()), ignoredGuids);
return new PrefabTypePreviewGenerator(settings);
}
private ITypePreviewGenerator CreateTexturePreviewGenerator(string[] ignoredGuids)
{
var settings = new TextureTypeGeneratorSettings()
{
MaxWidth = _customSettings.Width,
MaxHeight = _customSettings.Height,
InputPaths = _customSettings.InputPaths,
OutputPath = _customSettings.OutputPath,
Format = _customSettings.Format,
PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat,
IgnoredGuids = ignoredGuids
};
return new TextureTypePreviewGenerator(settings);
}
private TypePreviewGeneratorFromSceneSettings CreateSceneGeneratorSettings(ISceneScreenshotter screenshotter, string[] ignoredGuids)
{
var settings = new TypePreviewGeneratorFromSceneSettings()
{
InputPaths = _customSettings.InputPaths,
OutputPath = _customSettings.OutputPath,
PreviewFileNamingFormat = _customSettings.PreviewFileNamingFormat,
Screenshotter = screenshotter,
IgnoredGuids = ignoredGuids
};
return settings;
}
private SceneScreenshotterSettings CreateScreenshotterSettings()
{
var settings = new SceneScreenshotterSettings()
{
Width = _customSettings.Width,
Height = _customSettings.Height,
Depth = _customSettings.Depth,
Format = _customSettings.Format,
NativeWidth = _customSettings.NativeWidth,
NativeHeight = _customSettings.NativeHeight,
};
return settings;
}
private void ReportProgress(int currentGenerator, int totalGenerators, int currentGeneratorAsset, int totalCurrentGeneratorAssets)
{
var completedGeneratorProgress = (float)currentGenerator / totalGenerators;
var currentGeneratorProgress = ((float)currentGeneratorAsset / totalCurrentGeneratorAssets) / totalGenerators;
var progressToReport = completedGeneratorProgress + currentGeneratorProgress;
OnProgressChanged?.Invoke(progressToReport);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e5906f8cb3e4eec489a2f7a82844bb3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/CustomPreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,15 @@
using AssetStoreTools.Previews.Data;
using System;
using System.Threading.Tasks;
namespace AssetStoreTools.Previews.Generators
{
internal interface IPreviewGenerator
{
PreviewGenerationSettings Settings { get; }
event Action<float> OnProgressChanged;
Task<PreviewGenerationResult> Generate();
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ebddaccb94ca6e34aa36b535d0a47249
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/IPreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,362 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace AssetStoreTools.Previews.Generators
{
internal class NativePreviewGenerator : PreviewGeneratorBase
{
private const double InitialPreviewLoadingTimeoutSeconds = 10;
private NativePreviewGenerationSettings _nativeSettings;
private RenderTexture _renderTexture;
private int _generatedPreviewsCount;
private int _totalPreviewsCount;
public override event Action<float> OnProgressChanged;
public NativePreviewGenerator(NativePreviewGenerationSettings settings)
: base(settings)
{
_nativeSettings = settings;
}
protected override void Validate()
{
base.Validate();
if (_nativeSettings.ChunkSize <= 0)
throw new ArgumentException("Chunk size must be larger than 0");
}
protected override async Task<PreviewGenerationResult> GenerateImpl()
{
var result = new PreviewGenerationResult()
{
GenerationType = _nativeSettings.GenerationType
};
OnProgressChanged?.Invoke(0f);
try
{
var objects = GetObjectsRequiringPreviews(_nativeSettings.InputPaths);
var filteredObjects = new List<PreviewMetadata>();
var reusedPreviews = new List<PreviewMetadata>();
FilterObjects(objects, filteredObjects, reusedPreviews);
_generatedPreviewsCount = 0;
_totalPreviewsCount = objects.Count;
Directory.CreateDirectory(_nativeSettings.OutputPath);
var generatedPreviews = new List<PreviewMetadata>();
if (!_nativeSettings.WaitForPreviews)
{
WritePreviewsWithoutWaiting(filteredObjects, out generatedPreviews);
}
else
{
if (_nativeSettings.ChunkedPreviewLoading)
{
await WaitAndWritePreviewsChunked(filteredObjects, generatedPreviews);
}
else
{
await WaitAndWritePreviews(filteredObjects, generatedPreviews);
}
}
var allPreviews = new List<PreviewMetadata>();
allPreviews.AddRange(generatedPreviews);
allPreviews.AddRange(reusedPreviews);
result.Success = true;
result.GeneratedPreviews = generatedPreviews;
result.Previews = allPreviews;
}
catch (Exception e)
{
result.Success = false;
result.Exception = e;
}
return result;
}
private List<PreviewMetadata> GetObjectsRequiringPreviews(string[] inputPaths)
{
var objects = new List<PreviewMetadata>();
var guids = AssetDatabase.FindAssets("", inputPaths);
foreach (var guid in guids)
{
if (objects.Any(x => x.Guid == guid))
continue;
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
if (!ShouldHavePreview(obj))
continue;
objects.Add(new PreviewMetadata() { Type = GenerationType.Native, Guid = guid });
}
return objects;
}
private void FilterObjects(List<PreviewMetadata> objects, List<PreviewMetadata> filteredObjects, List<PreviewMetadata> reusedPreviews)
{
if (Settings.OverwriteExisting || !CachingService.GetCachedMetadata(out var database))
{
filteredObjects.AddRange(objects);
return;
}
foreach (var obj in objects)
{
var matchingEntry = database.Previews.FirstOrDefault(x =>
x.Guid == obj.Guid
&& x.Type == GenerationType.Native
&& x.Exists());
if (matchingEntry == null)
{
filteredObjects.Add(obj);
}
else
{
reusedPreviews.Add(matchingEntry);
}
}
}
private bool ShouldHavePreview(UnityEngine.Object asset)
{
if (asset == null)
return false;
if (!AssetDatabase.IsMainAsset(asset))
return false;
switch (asset)
{
case AudioClip _:
case Material _:
case Mesh _:
case TerrainLayer _:
case Texture _:
case Tile _:
return true;
case GameObject go:
var renderers = go.GetComponentsInChildren<Renderer>();
return renderers != null && renderers.Length > 0;
default:
return false;
}
}
private PreviewMetadata WritePreviewToDisk(PreviewMetadata metadata, Texture2D texture)
{
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(AssetDatabase.GUIDToAssetPath(metadata.Guid));
var width = Mathf.Min(texture.width, 128);
var height = Mathf.Min(texture.height, 128);
var readableTexture = GraphicsUtility.ResizeTexture(texture, width, height);
var fileName = PreviewConvertUtility.ConvertFilenameWithExtension(asset, _nativeSettings.PreviewFileNamingFormat, _nativeSettings.Format);
var filePath = $"{_nativeSettings.OutputPath}/{fileName}";
var bytes = PreviewConvertUtility.ConvertTexture(readableTexture, _nativeSettings.Format);
File.WriteAllBytes(filePath, bytes);
metadata.Type = GenerationType.Native;
metadata.Name = asset.name;
metadata.Path = filePath;
return metadata;
}
private void WritePreviewsWithoutWaiting(List<PreviewMetadata> objects, out List<PreviewMetadata> generatedPreviews)
{
generatedPreviews = new List<PreviewMetadata>();
foreach (var obj in objects)
{
var texture = GetAssetPreviewFromGuid(obj.Guid);
if (texture == null)
continue;
var generatedPreview = WritePreviewToDisk(obj, texture);
generatedPreviews.Add(generatedPreview);
}
}
private Texture2D GetAssetPreviewFromGuid(string guid)
{
var method = typeof(AssetPreview).GetMethod("GetAssetPreviewFromGUID", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(string) }, null);
var args = new object[] { guid };
return method?.Invoke(null, args) as Texture2D;
}
private async Task WaitAndWritePreviewsChunked(List<PreviewMetadata> objects, List<PreviewMetadata> generatedPreviews)
{
var chunks = objects.Count / _nativeSettings.ChunkSize;
var remainder = objects.Count % _nativeSettings.ChunkSize;
if (remainder != 0)
chunks += 1;
for (int i = 0; i < chunks; i++)
{
var chunkObjects = new List<PreviewMetadata>();
for (int j = 0; j < _nativeSettings.ChunkSize; j++)
{
var index = i * _nativeSettings.ChunkSize + j;
if (index == objects.Count)
break;
chunkObjects.Add(objects[index]);
}
var generatedPreviewsChunk = new List<PreviewMetadata>();
await WaitAndWritePreviews(chunkObjects, generatedPreviewsChunk);
generatedPreviews.AddRange(generatedPreviewsChunk);
}
}
private async Task WaitAndWritePreviews(List<PreviewMetadata> objects, List<PreviewMetadata> generatedPreviews)
{
var initialObjectCount = objects.Count();
if (initialObjectCount == 0)
return;
await WaitAndWritePreviewIteration(objects, generatedPreviews);
var remainingObjectCount = objects.Count;
// First iteration may take longer to start loading objects
var firstIterationStartTime = EditorApplication.timeSinceStartup;
while (true)
{
if (remainingObjectCount < initialObjectCount)
break;
if (EditorApplication.timeSinceStartup - firstIterationStartTime > InitialPreviewLoadingTimeoutSeconds)
throw new Exception("Preview loading timed out.");
await WaitAndWritePreviewIteration(objects, generatedPreviews);
remainingObjectCount = objects.Count;
}
if (remainingObjectCount == 0)
return;
while (true)
{
await WaitForEndOfFrame(1);
await WaitAndWritePreviewIteration(objects, generatedPreviews);
// If no more previews are being loaded, try one more time before quitting
if (objects.Count == remainingObjectCount)
{
await WaitForEndOfFrame(1);
await WaitAndWritePreviewIteration(objects, generatedPreviews);
if (objects.Count == remainingObjectCount)
{
var missingObjects = string.Join("\n", objects.Select(x => AssetDatabase.GUIDToAssetPath(x.Guid)));
Debug.LogWarning($"Unity Editor failed to fetch previews for {objects.Count} objects:\n{missingObjects}");
break;
}
}
remainingObjectCount = objects.Count;
// Exit when all previews are loaded
if (remainingObjectCount == 0)
break;
}
}
private async Task WaitAndWritePreviewIteration(List<PreviewMetadata> objects, List<PreviewMetadata> generatedPreviews)
{
var cacheSize = Mathf.Max(_nativeSettings.ChunkSize * 2, objects.Count() + _nativeSettings.ChunkSize);
AssetPreview.SetPreviewTextureCacheSize(cacheSize);
// Initial queueing
foreach (var obj in objects)
{
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(AssetDatabase.GUIDToAssetPath(obj.Guid));
AssetPreview.GetAssetPreview(asset);
}
await WaitForEndOfFrame();
// Waiting (NOTE: works inconsistently across Unity streams)
foreach (var obj in objects)
{
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(AssetDatabase.GUIDToAssetPath(obj.Guid));
if (AssetPreview.IsLoadingAssetPreview(asset.GetInstanceID()))
{
await WaitForEndOfFrame();
}
}
// Writing
for (int i = 0; i < objects.Count; i++)
{
var obj = objects[i];
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(AssetDatabase.GUIDToAssetPath(obj.Guid));
var texture = AssetPreview.GetAssetPreview(asset);
if (texture == null)
continue;
WritePreviewToDisk(obj, texture);
generatedPreviews.Add(obj);
_generatedPreviewsCount++;
OnProgressChanged?.Invoke((float)_generatedPreviewsCount / _totalPreviewsCount);
}
// Removing written objects from the list
for (int i = objects.Count - 1; i >= 0; i--)
{
if (objects[i].Exists())
objects.RemoveAt(i);
}
}
private async Task WaitForEndOfFrame(double atLeastSeconds)
{
var startTime = EditorApplication.timeSinceStartup;
while (EditorApplication.timeSinceStartup - startTime <= atLeastSeconds)
{
await WaitForEndOfFrame();
}
}
private async Task WaitForEndOfFrame()
{
var isNextFrame = false;
EditorApplication.CallbackFunction callback = null;
callback = () =>
{
EditorApplication.update -= callback;
isNextFrame = true;
};
EditorApplication.update += callback;
while (!isNextFrame)
await Task.Yield();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: dd6eeb2f97a2ed34db51ab5ac0b3ffa1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/NativePreviewGenerator.cs
uploadId: 724584

View File

@@ -0,0 +1,45 @@
using AssetStoreTools.Previews.Data;
using AssetStoreTools.Previews.Services;
using System;
using System.Threading.Tasks;
namespace AssetStoreTools.Previews.Generators
{
internal abstract class PreviewGeneratorBase : IPreviewGenerator
{
public PreviewGenerationSettings Settings { get; }
protected ICachingService CachingService;
public abstract event Action<float> OnProgressChanged;
public PreviewGeneratorBase(PreviewGenerationSettings settings)
{
Settings = settings;
CachingService = PreviewServiceProvider.Instance.GetService<ICachingService>();
}
public async Task<PreviewGenerationResult> Generate()
{
Validate();
var result = await GenerateImpl();
if (result.Success)
{
CachingService.CacheMetadata(result.GeneratedPreviews);
}
return result;
}
protected virtual void Validate()
{
if (Settings.InputPaths == null || Settings.InputPaths.Length == 0)
throw new ArgumentException("Input paths cannot be null");
if (string.IsNullOrEmpty(Settings.OutputPath))
throw new ArgumentException("Output path cannot be null");
}
protected abstract Task<PreviewGenerationResult> GenerateImpl();
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: cedf01448e0edcc4fb55f19f2e92b740
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Previews/Scripts/Generators/PreviewGeneratorBase.cs
uploadId: 724584