diff --git a/VisualPinball.Unity/Assets/Presets/Asset Thumbcam/Mid Distance (less).preset b/VisualPinball.Unity/Assets/Presets/Asset Thumbcam/Mid Distance (less).preset
new file mode 100644
index 000000000..a8ec4424c
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Presets/Asset Thumbcam/Mid Distance (less).preset
@@ -0,0 +1,76 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!181963792 &2655988077585873504
+Preset:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: Mid Distance (less)
+ m_TargetType:
+ m_NativeTypeID: 4
+ m_ManagedTypePPtr: {fileID: 0}
+ m_ManagedTypeFallback:
+ m_Properties:
+ - target: {fileID: 0}
+ propertyPath: m_LocalRotation.x
+ value: 0.27781588
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalRotation.y
+ value: 0.36497167
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalRotation.z
+ value: -0.1150751
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalRotation.w
+ value: 0.8811196
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalPosition.x
+ value: -0.0819
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalPosition.y
+ value: 0.1051
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalPosition.z
+ value: -0.0819
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalScale.x
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalScale.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalScale.z
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_ConstrainProportionsScale
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 35
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 45
+ objectReference: {fileID: 0}
+ - target: {fileID: 0}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_ExcludedProperties: []
+ m_CoupledType:
+ m_NativeTypeID: 0
+ m_ManagedTypePPtr: {fileID: 0}
+ m_ManagedTypeFallback:
+ m_CoupledProperties: []
diff --git a/VisualPinball.Unity/Assets/Presets/Asset Thumbcam/Mid Distance (less).preset.meta b/VisualPinball.Unity/Assets/Presets/Asset Thumbcam/Mid Distance (less).preset.meta
new file mode 100644
index 000000000..15a921cd0
--- /dev/null
+++ b/VisualPinball.Unity/Assets/Presets/Asset Thumbcam/Mid Distance (less).preset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b0032d5f974510d47a3825e7cf7b4ed7
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2655988077585873504
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat b/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat
index efb2476c7..cb87fca25 100644
--- a/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat
+++ b/VisualPinball.Unity/Assets/Resources/Materials/Dot Matrix Display (SRP).mat
@@ -24,8 +24,7 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Dot Matrix Display (SRP)
- m_Shader: {fileID: -6465566751694194690, guid: 3dfbd2115636a284d89d71fdefd9b4d2,
- type: 3}
+ m_Shader: {fileID: -6465566751694194690, guid: 3dfbd2115636a284d89d71fdefd9b4d2, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
@@ -197,6 +196,7 @@ Material:
- _DoubleSidedGIMode: 0
- _DoubleSidedNormalMode: 1
- _DstBlend: 0
+ - _DstBlend2: 0
- _EmissiveColorMode: 1
- _EmissiveExposureWeight: 1
- _EmissiveIntensity: 1
@@ -205,6 +205,7 @@ Material:
- _EnableFogOnTransparent: 1
- _EnableGeometricSpecularAA: 0
- _EnergyConservingSpecularColor: 1
+ - _ExcludeFromTUAndAA: 0
- _HeightAmplitude: 0.02
- _HeightCenter: 0.5
- _HeightMapParametrization: 0
@@ -220,6 +221,7 @@ Material:
- _IridescenceThickness: 1
- _LinkDetailsWithBase: 1
- _MaterialID: 1
+ - _MaterialTypeMask: 2
- _Metallic: 0
- _MetallicRemapMax: 1
- _MetallicRemapMin: 0
@@ -231,6 +233,9 @@ Material:
- _PPDMinSamples: 5
- _PPDPrimitiveLength: 1
- _PPDPrimitiveWidth: 1
+ - _PerPixelSorting: 0
+ - _QueueControl: -1
+ - _QueueOffset: 0
- _RayTracing: 0
- _ReceivesSSR: 1
- _ReceivesSSRTransparent: 0
@@ -250,10 +255,10 @@ Material:
- _StencilRefGBuffer: 10
- _StencilRefMV: 40
- _StencilWriteMask: 6
- - _StencilWriteMaskDepth: 8
+ - _StencilWriteMaskDepth: 9
- _StencilWriteMaskDistortionVec: 4
- - _StencilWriteMaskGBuffer: 14
- - _StencilWriteMaskMV: 40
+ - _StencilWriteMaskGBuffer: 15
+ - _StencilWriteMaskMV: 41
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0
@@ -287,8 +292,7 @@ Material:
- __SkewAngle: 0
m_Colors:
- Color_754dade2513142578632eb55adb8f59b: {r: 1, g: 0.36865607, b: 0, a: 0}
- - Color_a1cf9b5e0df34a8c907984367d220f54: {r: 0.25471696, g: 0.25471696, b: 0.25471696,
- a: 0}
+ - Color_a1cf9b5e0df34a8c907984367d220f54: {r: 0.25471696, g: 0.25471696, b: 0.25471696, a: 0}
- Vector2_59fda9737b9e42259b122fbd66ccd94d: {r: 0.7, g: 0.28, b: 0, a: 0}
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _BaseColorMap_MipInfo: {r: 0, g: 0, b: 0, a: 0}
@@ -311,6 +315,7 @@ Material:
- __Padding: {r: 0.3, g: 0.5, b: 0, a: 0}
- __UnlitColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
m_BuildTextureStacks: []
+ m_AllowLocking: 1
--- !u!114 &3792289215621747627
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -323,4 +328,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
- version: 5
+ version: 9
diff --git a/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat b/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat
index 923011b22..f53e3bc1c 100644
--- a/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat
+++ b/VisualPinball.Unity/Assets/Resources/Materials/Segment Display (SRP).mat
@@ -24,8 +24,7 @@ Material:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Segment Display (SRP)
- m_Shader: {fileID: -6465566751694194690, guid: d54eb986991300e419411008eb1f597d,
- type: 3}
+ m_Shader: {fileID: -6465566751694194690, guid: d54eb986991300e419411008eb1f597d, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
@@ -115,6 +114,10 @@ Material:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
+ - _SegmentDisplayCustomFunction_a94cb738cee9426e8bf076adaa3b6811_SegmentData_2_Texture2D:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
- _SpecularColorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
@@ -196,6 +199,7 @@ Material:
- _DoubleSidedGIMode: 0
- _DoubleSidedNormalMode: 1
- _DstBlend: 0
+ - _DstBlend2: 0
- _EmissiveColorMode: 1
- _EmissiveExposureWeight: 1
- _EmissiveIntensity: 1
@@ -204,6 +208,7 @@ Material:
- _EnableFogOnTransparent: 1
- _EnableGeometricSpecularAA: 0
- _EnergyConservingSpecularColor: 1
+ - _ExcludeFromTUAndAA: 0
- _HeightAmplitude: 0.02
- _HeightCenter: 0.5
- _HeightMapParametrization: 0
@@ -219,6 +224,7 @@ Material:
- _IridescenceThickness: 1
- _LinkDetailsWithBase: 1
- _MaterialID: 1
+ - _MaterialTypeMask: 2
- _Metallic: 0
- _MetallicRemapMax: 1
- _MetallicRemapMin: 0
@@ -230,6 +236,9 @@ Material:
- _PPDMinSamples: 5
- _PPDPrimitiveLength: 1
- _PPDPrimitiveWidth: 1
+ - _PerPixelSorting: 0
+ - _QueueControl: -1
+ - _QueueOffset: 0
- _RayTracing: 0
- _ReceivesSSR: 1
- _ReceivesSSRTransparent: 0
@@ -249,10 +258,10 @@ Material:
- _StencilRefGBuffer: 10
- _StencilRefMV: 40
- _StencilWriteMask: 6
- - _StencilWriteMaskDepth: 8
+ - _StencilWriteMaskDepth: 9
- _StencilWriteMaskDistortionVec: 4
- - _StencilWriteMaskGBuffer: 14
- - _StencilWriteMaskMV: 40
+ - _StencilWriteMaskGBuffer: 15
+ - _StencilWriteMaskMV: 41
- _SubsurfaceMask: 1
- _SupportDecals: 1
- _SurfaceType: 0
@@ -276,6 +285,7 @@ Material:
- _ZTestGBuffer: 4
- _ZTestTransparent: 4
- _ZWrite: 1
+ - __Emission: 1
- __HorizontalMiddle: 0
- __NumChars: 2
- __NumSegments: 14
@@ -285,8 +295,7 @@ Material:
- __SkewAngle: 0
m_Colors:
- Color_754dade2513142578632eb55adb8f59b: {r: 1, g: 0.36865607, b: 0, a: 0}
- - Color_a1cf9b5e0df34a8c907984367d220f54: {r: 0.25471696, g: 0.25471696, b: 0.25471696,
- a: 0}
+ - Color_a1cf9b5e0df34a8c907984367d220f54: {r: 0.25471696, g: 0.25471696, b: 0.25471696, a: 0}
- Vector2_59fda9737b9e42259b122fbd66ccd94d: {r: 0.7, g: 0.28, b: 0, a: 0}
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _BaseColorMap_MipInfo: {r: 0, g: 0, b: 0, a: 0}
@@ -309,6 +318,7 @@ Material:
- __SeparatorPos: {r: 1.4, g: 0, b: 0, a: 0}
- __UnlitColor: {r: 0.25471696, g: 0.25471696, b: 0.25471696, a: 0}
m_BuildTextureStacks: []
+ m_AllowLocking: 1
--- !u!114 &6529312484818753363
MonoBehaviour:
m_ObjectHideFlags: 11
@@ -321,4 +331,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
- version: 5
+ version: 9
diff --git a/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md b/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md
index 45ca3a137..c4428ccd7 100644
--- a/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md
+++ b/VisualPinball.Unity/Documentation~/creators-guide/editor/asset-library-styleguide.md
@@ -204,7 +204,6 @@ The smoothness map (the inverse of a roughness map) defines how regularly light
All texture maps must use power-of-two dimensions for width and height (e.g., 256, 512, 1024). They don't have to be square.

-Metallicness set to 1 with smoothness going from 0 to 1.
We're aiming for a resolution of about 6 pixels per millimeter (approximately 150 DPI). For a playfield texture, this means roughly 4096×8192 pixels. Use this resolution when possible, but don't upscale images — the highest resolution should be from your source. This applies to both color and normal maps. For metallic/smoothness maps, half the resolution of the color map is a good balance between performance and visual fidelity.
@@ -224,15 +223,14 @@ We're aiming for a resolution of about 6 pixels per millimeter (approximately 15
>
> So, a texture map at 296×296 would correspond to 6px / mm. Since we're at power of twos, we could go for either 512×512 or 256×256.
+### Compression
-### File Format
-
-Export your texture maps in **PNG format**. Use 32-bit if they include an alpha channel; otherwise, 24-bit is sufficient.
+Export your texture maps in **PNG format**. Use 32-bit if they include an alpha channel; otherwise, 24-bit is sufficient. Use halved resolution for the mask map. Don't quantize / TinyPNG your maps, since together with the GPU's block compression they will result in artifacts.
> [!note]
> Many artists prefer to export their textures as TGA due to faster read/write speeds, but TGA files consume significantly more space. PNG uses lossless compression, and once imported, Unity stores textures in a GPU-friendly format anyway. Additionally, when exporting to the `.vpe` format, image textures are converted into a runtime-optimized format.
-In terms of *packaging*, Unity's HDRP stores metallic, ambient occlusion and smoothness in what they call a [mask map](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.2/manual/Mask-Map-and-Detail-Map.html). Substance Painter exports this out of the box, but it should be pretty easy to do with Blender as well.
+In terms of *packaging*, Unity's HDRP stores metallic, ambient occlusion and smoothness in what they call a [mask map](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.2/manual/Mask-Map-and-Detail-Map.html). Substance Painter exports this out of the box, but it should be pretty easy to do with Blender as well.
### Wear
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs
index 74c85f262..b2eb65d7a 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.cs
@@ -50,8 +50,6 @@ public partial class AssetBrowser : EditorWindow, IDragHandler
[NonSerialized]
public AssetQuery Query;
- public const int ThumbSize = 256;
-
private AssetResult LastSelectedResult {
set => _detailsElement.Asset = value?.Asset;
}
@@ -98,7 +96,7 @@ private void RefreshLibraries()
Libraries = AssetDatabase.FindAssets($"t:{typeof(AssetLibrary)}")
.Select(AssetDatabase.GUIDToAssetPath)
.Select(AssetDatabase.LoadAssetAtPath)
- .Where(asset => asset != null).ToList();
+ .Where(lib => lib != null).ToList();
// toggle label
if (Libraries.Count == 0) {
@@ -198,115 +196,123 @@ private void UpdateQueryResults(List results, long duration)
_statusLabel.text = $"Found {results.Count} asset" + (results.Count == 1 ? "" : "s") + $" in {duration}ms.";
}
- private void AddAssetContextMenu(ContextualMenuPopulateEvent evt)
- {
- if (evt.target is not VisualElement ve || !_resultByElement.ContainsKey(ve)) {
- return;
- }
-
- var clickedAsset = _resultByElement[ve];
- var lib = _resultByElement[ve].Asset.Library;
- if (lib.IsLocked) {
- return;
- }
-
- // lib is not locked, and asset is known.
- evt.menu.AppendAction("Remove from Library", _ => {
- if (!_selectedResults.Contains(clickedAsset)) {
- _selectedResults.Add(clickedAsset);
- ToggleSelectionClass(_elementByAsset[clickedAsset.Asset]);
+ private void AddAssetContextMenu(ContextualMenuPopulateEvent evt)
+ {
+ if (evt.target is not VisualElement target) {
+ Debug.Log("Early out in AddAssetContextMenu, target is no VisualElement.");
+ return;
}
- var numRemovedAssets = 0;
- foreach (var assetResult in _selectedResults.Where(a => !a.Asset.Library.IsLocked).ToList()) {
- _selectedResults.Remove(assetResult);
- assetResult.Asset.Library.RemoveAsset(assetResult.Asset);
- if (_thumbCache.ContainsKey(assetResult.Asset.GUID)) {
- DestroyImmediate(_thumbCache[assetResult.Asset.GUID]);
- _thumbCache.Remove(assetResult.Asset.GUID);
- }
- numRemovedAssets++;
+
+ if (!_resultByElement.ContainsKey(target.parent.parent)) {
+ Debug.Log($"Early out in AddAssetContextMenu, {target.parent.parent} not in resultByElement.");
+ return;
}
- RefreshCategories();
- RefreshAssets();
- _statusLabel.text = $"Removed {numRemovedAssets} assets from library.";
- });
+ var clickedAsset = _resultByElement[target.parent.parent];
+ var libs = clickedAsset.Asset.Libraries;
+ if (libs.All(l => l.IsLocked)) {
+ Debug.Log("Early out in AddAssetContextMenu, all libraries are locked.");
+ return;
+ }
- if (_selectedResults.Count > 1) {
- var srcAsset = clickedAsset.Asset;
- evt.menu.AppendSeparator();
- evt.menu.AppendAction("Add Attributes to Selected", _ => {
- var destAssets = OtherSelected(clickedAsset).ToList();
- foreach (var destAsset in destAssets) {
- foreach (var attr in srcAsset.Attributes) {
- destAsset.AddAttribute(attr.Key, attr.Value);
- }
- destAsset.Save();
- }
- EditorUtility.DisplayDialog("Add Attributes to Selected", $"Added {srcAsset.Attributes.Count} attributes to {destAssets.Count} other assets.", "OK");
-
- });
- evt.menu.AppendAction("Replace Attributes in Selected", _ => {
- var destAssets = OtherSelected(clickedAsset).ToList();
- foreach (var destAsset in destAssets) {
- foreach (var attr in srcAsset.Attributes) {
- destAsset.ReplaceAttribute(attr.Key, attr.Value);
+ // lib is not locked, and asset is known.
+ foreach (var lib in libs.Where(l => !l.IsLocked)) {
+ evt.menu.AppendAction($"Remove from {lib.Name}", _ => {
+ if (_selectedResults.Add(clickedAsset)) {
+ ToggleSelectionClass(_elementByAsset[clickedAsset.Asset]);
}
- destAsset.Save();
- }
- EditorUtility.DisplayDialog("Replace Attributes to Selected", $"Replaced {srcAsset.Attributes.Count} attributes in {destAssets.Count} other assets.", "OK");
- });
-
- evt.menu.AppendSeparator();
- evt.menu.AppendAction("Add Tags to Selected", _ => {
- var destAssets = OtherSelected(clickedAsset).ToList();
- foreach (var destAsset in destAssets) {
- foreach (var tag in srcAsset.Tags) {
- destAsset.AddTag(tag.TagName);
+ var numRemovedAssets = 0;
+ foreach (var assetResult in _selectedResults.Where(a => lib.HasAsset(a.Asset.GUID)).ToList()) {
+ _selectedResults.Remove(assetResult);
+ lib.DeleteAsset(assetResult.Asset);
+ if (libs.Length == 1 && _thumbCache.ContainsKey(assetResult.Asset.GUID)) {
+ DestroyImmediate(_thumbCache[assetResult.Asset.GUID]);
+ _thumbCache.Remove(assetResult.Asset.GUID);
+ }
+ numRemovedAssets++;
}
- destAsset.Save();
- }
- EditorUtility.DisplayDialog("Add Tags to Selected", $"Added {srcAsset.Tags.Count} tags to {destAssets.Count} other assets.", "OK");
- });
-
- evt.menu.AppendAction("Replace Tags in Selected", _ => {
- var destAssets = OtherSelected(clickedAsset).ToList();
- foreach (var destAsset in destAssets) {
- destAsset.Tags.Clear();
- foreach (var tag in srcAsset.Tags) {
- destAsset.AddTag(tag.TagName);
+
+ RefreshCategories();
+ RefreshAssets();
+ _statusLabel.text = $"Removed {numRemovedAssets} asset(s) from library {lib.Name}.";
+ });
+ }
+
+ if (_selectedResults.Count > 1) {
+ var srcAsset = clickedAsset.Asset;
+ evt.menu.AppendSeparator();
+ evt.menu.AppendAction("Add Attributes to Selected", _ => {
+ var destAssets = OtherSelected(clickedAsset).ToList();
+ foreach (var destAsset in destAssets) {
+ foreach (var attr in srcAsset.Attributes) {
+ destAsset.AddAttribute(attr.Key, attr.Value);
+ }
+ destAsset.Save();
}
- destAsset.Save();
- }
- EditorUtility.DisplayDialog("Replace Tags in Selected", $"Replaced {srcAsset.Tags.Count} tags in {destAssets.Count} other assets.", "OK");
- });
-
- evt.menu.AppendSeparator();
- evt.menu.AppendAction("Copy All to Selected", _ => {
- var destAssets = OtherSelected(clickedAsset).ToList();
- foreach (var destAsset in destAssets) {
- if (string.IsNullOrEmpty(destAsset.Description)) {
- destAsset.Description = srcAsset.Description;
+ EditorUtility.DisplayDialog("Add Attributes to Selected", $"Added {srcAsset.Attributes.Count} attributes to {destAssets.Count} other assets.", "OK");
+
+ });
+ evt.menu.AppendAction("Replace Attributes in Selected", _ => {
+ var destAssets = OtherSelected(clickedAsset).ToList();
+ foreach (var destAsset in destAssets) {
+ foreach (var attr in srcAsset.Attributes) {
+ destAsset.ReplaceAttribute(attr.Key, attr.Value);
+ }
+ destAsset.Save();
}
- foreach (var tag in srcAsset.Tags) {
- destAsset.AddTag(tag.TagName);
+ EditorUtility.DisplayDialog("Replace Attributes to Selected", $"Replaced {srcAsset.Attributes.Count} attributes in {destAssets.Count} other assets.", "OK");
+ });
+
+ evt.menu.AppendSeparator();
+ evt.menu.AppendAction("Add Tags to Selected", _ => {
+ var destAssets = OtherSelected(clickedAsset).ToList();
+ foreach (var destAsset in destAssets) {
+ foreach (var tag in srcAsset.Tags) {
+ destAsset.AddTag(tag.TagName);
+ }
+ destAsset.Save();
}
- foreach (var attr in srcAsset.Attributes) {
- destAsset.AddAttribute(attr.Key, attr.Value);
+ EditorUtility.DisplayDialog("Add Tags to Selected", $"Added {srcAsset.Tags.Count} tags to {destAssets.Count} other assets.", "OK");
+ });
+
+ evt.menu.AppendAction("Replace Tags in Selected", _ => {
+ var destAssets = OtherSelected(clickedAsset).ToList();
+ foreach (var destAsset in destAssets) {
+ destAsset.Tags.Clear();
+ foreach (var tag in srcAsset.Tags) {
+ destAsset.AddTag(tag.TagName);
+ }
+ destAsset.Save();
}
- foreach (var link in srcAsset.Links.Where(link => destAsset.Links.FirstOrDefault(l => l.Name == link.Name) != null)) {
- destAsset.Links.Add(new AssetLink(link.Name, link.Url));
+ EditorUtility.DisplayDialog("Replace Tags in Selected", $"Replaced {srcAsset.Tags.Count} tags in {destAssets.Count} other assets.", "OK");
+ });
+
+ evt.menu.AppendSeparator();
+ evt.menu.AppendAction("Copy All to Selected", _ => {
+ var destAssets = OtherSelected(clickedAsset).ToList();
+ foreach (var destAsset in destAssets) {
+ if (string.IsNullOrEmpty(destAsset.Description)) {
+ destAsset.Description = srcAsset.Description;
+ }
+ foreach (var tag in srcAsset.Tags) {
+ destAsset.AddTag(tag.TagName);
+ }
+ foreach (var attr in srcAsset.Attributes) {
+ destAsset.AddAttribute(attr.Key, attr.Value);
+ }
+ foreach (var link in srcAsset.Links.Where(link => destAsset.Links.FirstOrDefault(l => l.Name == link.Name) != null)) {
+ destAsset.Links.Add(new AssetLink(link.Name, link.Url));
+ }
+
+ destAsset.Quality = srcAsset.Quality;
+ destAsset.ThumbCameraPreset = srcAsset.ThumbCameraPreset;
+
+ destAsset.Save();
}
-
- destAsset.Quality = srcAsset.Quality;
- destAsset.ThumbCameraPreset = srcAsset.ThumbCameraPreset;
-
- destAsset.Save();
- }
- EditorUtility.DisplayDialog("Copy All to Selected", $"Copied data of to {destAssets.Count} other assets.", "OK");
- });
+ EditorUtility.DisplayDialog("Copy All to Selected", $"Copied data of to {destAssets.Count} other assets.", "OK");
+ });
+ }
}
- }
private IEnumerable OtherSelected(AssetResult src)
{
@@ -574,16 +580,13 @@ private void OnThumbSizeChanged(ChangeEvent evt)
{
_thumbnailSize = (int)evt.newValue;
foreach (var e in _elementByAsset.Values) {
- e.style.width = _thumbnailSize;
- e.style.height = _thumbnailSize;
+ e.Q().SetSize(_thumbnailSize);
}
}
public void AttachData(AssetResult clickedResult)
{
- if (!_selectedResults.Contains(clickedResult)) {
- _selectedResults.Add(clickedResult);
- }
+ _selectedResults.Add(clickedResult);
DragAndDrop.objectReferences = _selectedResults.Select(result => result.Asset.Object).ToArray();
StartDraggingAssets(_selectedResults);
}
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.uss b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.uss
index d33227f88..129c05d00 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.uss
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.uss
@@ -24,6 +24,10 @@
-unity-text-align: middle-center;
}
+.library-element--dragover {
+ background-color: #515151 !important;
+}
+
/*AssetDetails > TemplateContainer {
flex-grow: 1;
}*/
@@ -60,7 +64,6 @@ LibraryCategoryView {
#gridContent .unity-image {
width: 150px;
height: 150px;
- margin: 10px 10px 5px 10px;
}
#dragErrorContainer, #dragErrorContainerLeft {
diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs
index c6acdb4a9..a109b2434 100644
--- a/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs
+++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser_Init.cs
@@ -15,7 +15,6 @@
// along with this program. If not, see .
using System.Collections.Generic;
-using System.IO;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
@@ -42,9 +41,12 @@ public partial class AssetBrowser
private Slider _sizeSlider;
private VisualTreeAsset _assetTree;
+ private StyleSheet _assetStyle;
private readonly Dictionary _thumbCache = new();
+ private const string ClassDrag = "library-element--dragover";
+
public string DragErrorLeft {
get => _dragErrorContainerLeft.ClassListContains("hidden") ? null : _dragErrorLabelLeft.text;
set {
@@ -84,6 +86,7 @@ public void CreateGUI()
var visualTree = AssetDatabase.LoadAssetAtPath("Packages/org.visualpinball.engine.unity/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/AssetBrowser.uxml");
visualTree.CloneTree(rootVisualElement);
_assetTree = AssetDatabase.LoadAssetAtPath("Packages/org.visualpinball.engine.unity/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/LibraryAssetElement.uxml");
+ _assetStyle = AssetDatabase.LoadAssetAtPath("Packages/org.visualpinball.engine.unity/VisualPinball.Unity/VisualPinball.Unity.Editor/AssetBrowser/LibraryAssetElement.uss");
var ui = rootVisualElement;
@@ -148,33 +151,33 @@ private VisualElement NewItem(AssetResult result)
{
var item = new VisualElement();
_assetTree.CloneTree(item);
- item.Q().Result = result;
+ item.styleSheets.Add(_assetStyle);
+ var assetElement = item.Q();
+ assetElement.Result = result;
LoadThumb(item, result.Asset);
- item.style.width = _thumbnailSize;
- item.style.height = _thumbnailSize;
+ var img = item.Q("thumbnail-mask");
+ assetElement.SetSize(_thumbnailSize);
+
var label = item.Q