fixed issue with writing nested prefab assets

This commit is contained in:
JR-Morgan
2022-10-21 21:06:05 +01:00
parent c1a1e576b6
commit 3a632c1117
7 changed files with 109 additions and 100 deletions
@@ -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<List<SMesh>> 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);
}
/// <summary>
/// Converts a 4x4 transformation matrix from Speckle's <see cref="Other.Transform"/> format,
@@ -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<char> 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}";
}
@@ -15,29 +15,25 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
/// </summary>
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<Base, Object> writeBuffer = new();
#nullable enable
private void Awake()
void Awake()
{
readCache = CreateInstance<MemoryNativeCache>();
}
public override bool TryGetObject<T>(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<T>(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<KeyValuePair<Base, Object>> 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", "");
@@ -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
/// </summary>
public sealed class MemoryNativeCache : AbstractNativeCache
{
public IDictionary<string, Object> LoadedAssets { get; set; } = new Dictionary<string, Object>();
public IDictionary<string, List<Object>> LoadedAssets { get; set; } = new Dictionary<string, List<Object>>();
public override bool TryGetObject<T>(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<Object>? 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<Object>? 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<Object>{nativeObject});
return true;
}
}
}
@@ -8,7 +8,8 @@ namespace Speckle.ConnectorUnity.NativeCache
#nullable enable
/// <summary>
/// Loads existing assets from <see cref="Resources"/>
/// optionally accepting byName overrides
/// by friendly id (see <see cref="AssetHelpers.GetAssetName"/>)
/// or by name (when <see cref="ResourcesNativeCache.matchByName"/> is <see langword="true"/>)
/// </summary>
public sealed class ResourcesNativeCache : AbstractNativeCache
{
@@ -31,15 +31,21 @@ namespace Speckle.ConnectorUnity
AssetCache.nativeCaches = NativeCacheFactory.GetStandaloneCacheSetup();
}
ConverterInstance.SetContextDocument(AssetCache);
AssetCache.BeginWrite();
var createdGameObjects = new List<GameObject>();
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;
}
+13 -6
View File
@@ -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