From 3a632c1117e3d44239ca5ff69a7df9a6e50e2ea8 Mon Sep 17 00:00:00 2001 From: JR-Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Fri, 21 Oct 2022 21:06:05 +0100 Subject: [PATCH] fixed issue with writing nested prefab assets --- .../ConverterUnity.Geometry.cs | 38 ++++--- .../NativeCache/AbstractNativeCache.cs | 6 +- .../NativeCache/Editor/AssetDBNativeCache.cs | 105 +++++++----------- .../NativeCache/MemoryNativeCache.cs | 20 +++- .../NativeCache/ResourcesNativeCache.cs | 3 +- .../RecursiveConverter.ToNative.cs | 18 ++- ProjectSettings/QualitySettings.asset | 19 +++- 7 files changed, 109 insertions(+), 100 deletions(-) diff --git a/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs b/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs index d834a7b..3139587 100644 --- a/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs +++ b/Packages/systems.speckle.speckle-unity/ConverterUnity.Geometry.cs @@ -222,17 +222,8 @@ namespace Objects.Converter.Unity // Check for existing converted object if(LoadedAssets.TryGetObject(block.blockDefinition, out GameObject? existingGo)) { -#if UNITY_EDITOR - bool isPrefab = PrefabUtility.GetPrefabAssetType(existingGo) != PrefabAssetType.NotAPrefab; - - var go = isPrefab - ? (GameObject) PrefabUtility.InstantiatePrefab(existingGo) - : Object.Instantiate(existingGo); -#else - var go = Object.Instantiate(existingGo); -#endif + var go = InstantiateCopy(existingGo); go.name = block.blockDefinition.name ?? ""; - TransformToNativeTransform(go.transform, block.transform); return go; } @@ -245,13 +236,15 @@ namespace Objects.Converter.Unity foreach (Base geo in block.blockDefinition.geometry) { if (geo is SMesh m) meshes.Add(m); - else if (geo is Brep s) meshes.AddRange(s.displayValue); + else if (geo is IDisplayValue> s) meshes.AddRange(s.displayValue); else others.Add(geo); } + Mesh? nativeMesh = null; if (meshes.Any()) { - if (!TryGetMeshFromCache(block.blockDefinition, meshes, out Mesh? nativeMesh, out _)) + bool foundExisting = TryGetMeshFromCache(block.blockDefinition, meshes, out nativeMesh, out _); + if(!foundExisting) { MeshToNativeMesh(meshes, out nativeMesh); string name = AssetHelpers.GetObjectName(block.blockDefinition); @@ -268,7 +261,7 @@ namespace Objects.Converter.Unity if (c == null) continue; c.transform.SetParent(native.transform, false); } - + LoadedAssets.TrySaveObject(block.blockDefinition, native); TransformToNativeTransform(native.transform, block.transform); @@ -277,6 +270,25 @@ namespace Objects.Converter.Unity } + private static GameObject InstantiateCopy(GameObject existingGo) + { +#if UNITY_EDITOR + GameObject? prefabInstance = null; + bool isPrefab = PrefabUtility.GetPrefabAssetType(existingGo) != PrefabAssetType.NotAPrefab; + if (isPrefab) + { + GameObject? prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(existingGo); + if (prefabAsset == null) prefabAsset = existingGo; + prefabInstance = (GameObject) PrefabUtility.InstantiatePrefab(prefabAsset); + } + + if (prefabInstance != null) + return prefabInstance; +#endif + + return Object.Instantiate(existingGo); + } + /// /// Converts a 4x4 transformation matrix from Speckle's format, diff --git a/Packages/systems.speckle.speckle-unity/NativeCache/AbstractNativeCache.cs b/Packages/systems.speckle.speckle-unity/NativeCache/AbstractNativeCache.cs index 8aca9fe..f86f64a 100644 --- a/Packages/systems.speckle.speckle-unity/NativeCache/AbstractNativeCache.cs +++ b/Packages/systems.speckle.speckle-unity/NativeCache/AbstractNativeCache.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -63,13 +64,14 @@ namespace Speckle.ConnectorUnity.NativeCache return null; } + + private static readonly HashSet InvalidChars = Path.GetInvalidFileNameChars().ToHashSet(); public static string GetAssetName(Base speckleObject, Type nativeType) { string suffix = GetAssetSuffix(nativeType); - var invalidChars = Path.GetInvalidFileNameChars(); string name = GetObjectName(speckleObject); - string sanitisedName = new(name.Where(x => !invalidChars.Contains(x)).ToArray()); + string sanitisedName = new(name.Where(x => !InvalidChars.Contains(x)).ToArray()); return $"{sanitisedName}{suffix}"; } diff --git a/Packages/systems.speckle.speckle-unity/NativeCache/Editor/AssetDBNativeCache.cs b/Packages/systems.speckle.speckle-unity/NativeCache/Editor/AssetDBNativeCache.cs index 9803aa6..a843882 100644 --- a/Packages/systems.speckle.speckle-unity/NativeCache/Editor/AssetDBNativeCache.cs +++ b/Packages/systems.speckle.speckle-unity/NativeCache/Editor/AssetDBNativeCache.cs @@ -15,29 +15,25 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor /// public sealed class AssetDBNativeCache : AbstractNativeCache { - public string path = "Assets/Resources"; + public const string DefaultPath = "Assets/Resources"; + public string path = DefaultPath; private MemoryNativeCache readCache; - - private Dictionary writeBuffer = new(); - + #nullable enable - private void Awake() + + void Awake() { readCache = CreateInstance(); } - + public override bool TryGetObject(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class { if(readCache.TryGetObject(speckleObject, out nativeObject)) return true; Type nativeType = typeof(T); - string? folder = AssetHelpers.GetAssetFolder(nativeType, path); - if (folder == null) return false; - - string assetName = AssetHelpers.GetAssetName(speckleObject, nativeType); - string assetPath = $"{folder}/{assetName}"; + if (!GetAssetPath(nativeType, speckleObject, out string? assetPath)) return false; nativeObject = AssetDatabase.LoadAssetAtPath(assetPath); return nativeObject != null; @@ -45,85 +41,63 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor public override bool TrySaveObject(Base speckleObject, Object nativeObject) { - writeBuffer.TryAdd(speckleObject, nativeObject); - return readCache.TrySaveObject(speckleObject, nativeObject); + return WriteObject(speckleObject, nativeObject); } - - public bool WriteObject(Base speckleObject, Object nativeObject) + + private bool WriteObject(Base speckleObject, Object nativeObject) { Type nativeType = nativeObject.GetType(); - string? folder = AssetHelpers.GetAssetFolder(nativeType, path); - - if (folder == null) return false; - if (!CreateDirectory(folder)) return false; - - string assetName = AssetHelpers.GetAssetName(speckleObject, nativeType); - string assetPath = $"{folder}/{assetName}"; + if (!GetAssetPath(nativeType, speckleObject, out string? assetPath)) return false; // Special case for GameObjects, we want to use PrefabUtility if (nativeObject is GameObject go) { - PrefabUtility.SaveAsPrefabAssetAndConnect(go, assetPath, InteractionMode.AutomatedAction); - return true; + var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(go, assetPath, InteractionMode.AutomatedAction); + return readCache.TrySaveObject(speckleObject, prefab); } // Exit early if there's already an asset Object? existing = AssetDatabase.LoadAssetAtPath(assetPath, nativeObject.GetType()); if (existing != null) { - Debug.LogWarning($"Failed to write asset as one already existed at path: {folder}/{assetName}", this); + Debug.LogWarning($"Failed to write asset as one already existed at path: {assetPath}", this); return false; } - AssetDatabase.CreateAsset(nativeObject, $"{folder}/{assetName}"); - return true; + AssetDatabase.CreateAsset(nativeObject, $"{assetPath}"); + return readCache.TrySaveObject(speckleObject, nativeObject); + } + + + public override void BeginWrite() + { + base.BeginWrite(); + //AssetDatabase.StartAssetEditing(); } - - public void WriteAssets(IEnumerable> assets) - { - //Write Asset Data - try - { - AssetDatabase.StartAssetEditing(); - int i = 0; - int count = writeBuffer.Count; - foreach(var kvp in assets) - { - if (kvp.Value is GameObject p) - { - continue; - } - - EditorUtility.DisplayProgressBar("Writing assets", $"Writing asset for {kvp.Value.name}", (float)i / count); - WriteObject(kvp.Key, kvp.Value); - } - } - finally - { - AssetDatabase.StopAssetEditing(); - EditorUtility.DisplayProgressBar("Writing assets", $"Finishing writing assets", 1f); - AssetDatabase.SaveAssets(); - EditorUtility.ClearProgressBar(); - } - } - public override void FinishWrite() { if (!isWriting) return; - - var prefabs = writeBuffer.Where(x => x.Value is GameObject); - var notPrefabs = writeBuffer.Where(x => x.Value is not GameObject); - - WriteAssets(notPrefabs); - WriteAssets(prefabs); - - writeBuffer.Clear(); + //AssetDatabase.StopAssetEditing(); + AssetDatabase.SaveAssets(); + if (readCache != null) readCache.LoadedAssets.Clear(); base.FinishWrite(); } + + private bool GetAssetPath(Type nativeType, Base speckleObject, [NotNullWhen(true)] out string? outPath) + { + string? folder = AssetHelpers.GetAssetFolder(nativeType, path); + outPath = null; + if (folder == null) return false; + if (!CreateDirectory(folder)) return false; + string assetName = AssetHelpers.GetAssetName(speckleObject, nativeType); + outPath = $"{folder}/{assetName}"; + return true; + } + private static bool CreateDirectory(string directoryPath) { if (Directory.Exists(directoryPath)) @@ -133,10 +107,9 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor AssetDatabase.Refresh(); return info.Exists; } - [ContextMenu("SetPath")] - internal void SetPath_Menu() + public void SetPath_Menu() { var selection = EditorUtility.OpenFolderPanel("Set Assets Path", "Assets/Resources", ""); diff --git a/Packages/systems.speckle.speckle-unity/NativeCache/MemoryNativeCache.cs b/Packages/systems.speckle.speckle-unity/NativeCache/MemoryNativeCache.cs index 84dd4f1..8ab8b4f 100644 --- a/Packages/systems.speckle.speckle-unity/NativeCache/MemoryNativeCache.cs +++ b/Packages/systems.speckle.speckle-unity/NativeCache/MemoryNativeCache.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Speckle.Core.Models; using Object = UnityEngine.Object; @@ -11,28 +12,35 @@ namespace Speckle.ConnectorUnity.NativeCache /// public sealed class MemoryNativeCache : AbstractNativeCache { - public IDictionary LoadedAssets { get; set; } = new Dictionary(); + public IDictionary> LoadedAssets { get; set; } = new Dictionary>(); public override bool TryGetObject(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class { - if (TryGetObject(speckleObject, out Object? e) && e is T t) + if (TryGetObject(speckleObject, out List? e)) { - nativeObject = t; - return true; + nativeObject = (T?)e.FirstOrDefault(x => x is T); + return nativeObject != null; } nativeObject = null; return false; } - public bool TryGetObject(Base speckleObject, [NotNullWhen(true)] out Object? nativeObject) + public bool TryGetObject(Base speckleObject, [NotNullWhen(true)] out List? nativeObject) { return LoadedAssets.TryGetValue(speckleObject.id, out nativeObject); } public override bool TrySaveObject(Base speckleObject, Object nativeObject) { - return LoadedAssets.TryAdd(speckleObject.id, nativeObject); + if (LoadedAssets.ContainsKey(speckleObject.id)) + { + LoadedAssets[speckleObject.id].Add(nativeObject); + return true; + } + + LoadedAssets.Add(speckleObject.id, new List{nativeObject}); + return true; } } } diff --git a/Packages/systems.speckle.speckle-unity/NativeCache/ResourcesNativeCache.cs b/Packages/systems.speckle.speckle-unity/NativeCache/ResourcesNativeCache.cs index cb29f8c..6793901 100644 --- a/Packages/systems.speckle.speckle-unity/NativeCache/ResourcesNativeCache.cs +++ b/Packages/systems.speckle.speckle-unity/NativeCache/ResourcesNativeCache.cs @@ -8,7 +8,8 @@ namespace Speckle.ConnectorUnity.NativeCache #nullable enable /// /// Loads existing assets from - /// optionally accepting byName overrides + /// by friendly id (see ) + /// or by name (when is ) /// public sealed class ResourcesNativeCache : AbstractNativeCache { diff --git a/Packages/systems.speckle.speckle-unity/RecursiveConverter.ToNative.cs b/Packages/systems.speckle.speckle-unity/RecursiveConverter.ToNative.cs index 994ff31..bcccc3d 100644 --- a/Packages/systems.speckle.speckle-unity/RecursiveConverter.ToNative.cs +++ b/Packages/systems.speckle.speckle-unity/RecursiveConverter.ToNative.cs @@ -31,15 +31,21 @@ namespace Speckle.ConnectorUnity AssetCache.nativeCaches = NativeCacheFactory.GetStandaloneCacheSetup(); } - ConverterInstance.SetContextDocument(AssetCache); - AssetCache.BeginWrite(); - var createdGameObjects = new List(); - ConvertChild(o, parent, predicate, createdGameObjects); + ConverterInstance.SetContextDocument(AssetCache); + try + { + AssetCache.BeginWrite(); + ConvertChild(o, parent, predicate, createdGameObjects); + } + finally + { + AssetCache.FinishWrite(); + } + //TODO track event - AssetCache.FinishWrite(); - + return createdGameObjects; } diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset index 7b7658d..402991a 100644 --- a/ProjectSettings/QualitySettings.asset +++ b/ProjectSettings/QualitySettings.asset @@ -18,7 +18,7 @@ QualitySettings: shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 - blendWeights: 1 + skinWeights: 1 textureQuality: 1 anisotropicTextures: 0 antiAliasing: 0 @@ -40,6 +40,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Low @@ -53,7 +54,7 @@ QualitySettings: shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 - blendWeights: 2 + skinWeights: 2 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 @@ -75,6 +76,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Medium @@ -88,7 +90,7 @@ QualitySettings: shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 - blendWeights: 2 + skinWeights: 2 textureQuality: 0 anisotropicTextures: 1 antiAliasing: 0 @@ -110,6 +112,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: High @@ -123,7 +126,7 @@ QualitySettings: shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 - blendWeights: 2 + skinWeights: 2 textureQuality: 0 anisotropicTextures: 1 antiAliasing: 0 @@ -145,6 +148,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Very High @@ -158,7 +162,7 @@ QualitySettings: shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 - blendWeights: 4 + skinWeights: 4 textureQuality: 0 anisotropicTextures: 2 antiAliasing: 2 @@ -180,6 +184,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Ultra @@ -193,7 +198,7 @@ QualitySettings: shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 - blendWeights: 4 + skinWeights: 4 textureQuality: 0 anisotropicTextures: 2 antiAliasing: 2 @@ -215,6 +220,7 @@ QualitySettings: asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] m_PerPlatformDefaultQuality: Android: 2 @@ -223,6 +229,7 @@ QualitySettings: Nintendo Switch: 5 PS4: 5 PSP2: 2 + Server: 0 Stadia: 5 Standalone: 5 WebGL: 3