fixed issue with writing nested prefab assets
This commit is contained in:
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user