feat(converter): Added native asset cache to converter to allow for loading/saving assets in an abstract way

This commit is contained in:
JR-Morgan 2022-10-11 14:55:26 +01:00
Родитель 837794f6e9
Коммит abe721a323
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 746B82374C87C2EF
32 изменённых файлов: 1090 добавлений и 517 удалений

Просмотреть файл

@ -2064,6 +2064,19 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &916416845
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 141ce93d2d159c0448b5b8b33b1c0679, type: 3}
m_Name:
m_EditorClassIdentifier:
path: Assets/Resources
--- !u!1 &1031574851
GameObject:
m_ObjectHideFlags: 0
@ -2848,6 +2861,21 @@ MonoBehaviour:
m_PersistentCalls:
m_Calls: []
m_IsOn: 1
--- !u!114 &1409739885
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 88d6b4f2f80eaa14f9f07505f7e44ec2, type: 3}
m_Name:
m_EditorClassIdentifier:
nativeCaches:
- {fileID: 916416845}
- {fileID: 1951948666}
--- !u!1 &1464556211
GameObject:
m_ObjectHideFlags: 0
@ -3844,6 +3872,18 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1903798475}
m_CullTransparentMesh: 1
--- !u!114 &1951948666
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b3354e8208862c341940152f5340d41a, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &2014586909
GameObject:
m_ObjectHideFlags: 0
@ -4034,7 +4074,7 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 2060322467}
- component: {fileID: 2060322466}
- component: {fileID: 2060322469}
- component: {fileID: 2060322468}
m_Layer: 0
m_Name: EditorReceiver
@ -4043,24 +4083,6 @@ GameObject:
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &2060322466
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2060322465}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 730f6d2eecf16994c918395debc877e7, type: 3}
m_Name:
m_EditorClassIdentifier:
SelectedAccountIndex: 0
SelectedStreamIndex: 0
SelectedBranchIndex: 0
SelectedCommitIndex: 0
OldSelectedAccountIndex: 0
OldSelectedStreamIndex: 0
--- !u!4 &2060322467
Transform:
m_ObjectHideFlags: 0
@ -4077,6 +4099,24 @@ Transform:
m_RootOrder: 9
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2060322468
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2060322465}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 730f6d2eecf16994c918395debc877e7, type: 3}
m_Name:
m_EditorClassIdentifier:
SelectedAccountIndex: 0
SelectedStreamIndex: 15
SelectedBranchIndex: 0
SelectedCommitIndex: 0
OldSelectedAccountIndex: 0
OldSelectedStreamIndex: 15
--- !u!114 &2060322469
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@ -4088,6 +4128,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: ed6cbf9ce4dca0349997d163ec9bce7e, type: 3}
m_Name:
m_EditorClassIdentifier:
<AssetCache>k__BackingField: {fileID: 1409739885}
--- !u!1 &2066864134
GameObject:
m_ObjectHideFlags: 0

Просмотреть файл

@ -1,7 +1,7 @@
{
"dependencies": {
"com.unity.2d.sprite": "1.0.0",
"com.unity.collab-proxy": "1.17.0",
"com.unity.collab-proxy": "1.17.2",
"com.unity.ide.rider": "3.0.15",
"com.unity.ide.visualstudio": "2.0.16",
"com.unity.ide.vscode": "1.2.5",

Просмотреть файл

@ -7,7 +7,7 @@
"dependencies": {}
},
"com.unity.collab-proxy": {
"version": "1.17.0",
"version": "1.17.2",
"depth": 0,
"source": "registry",
"dependencies": {
@ -55,7 +55,7 @@
"url": "https://packages.unity.com"
},
"com.unity.services.core": {
"version": "1.4.0",
"version": "1.4.2",
"depth": 1,
"source": "registry",
"dependencies": {

Просмотреть файл

@ -10,6 +10,7 @@ using Speckle.Core.Logging;
using Speckle.Core.Models;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
using SMesh = Objects.Geometry.Mesh;
using Transform = UnityEngine.Transform;
using STransform = Objects.Other.Transform;
@ -216,29 +217,21 @@ namespace Objects.Converter.Unity
return null;
}
#if UNITY_EDITOR
// Check `Resources` for existing cached prefab asset
// TODO: this isn't how we check for existing materials, maybe there's a reason not to call LoadAssetPath constantly
string assetName = $"{GetAssetName(block.blockDefinition)}.prefab"
.Trim(Path.GetInvalidFileNameChars());
const string assetPath = "Assets/Resources/Prefabs/";
if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
// Check for existing conversions
if(LoadedAssets.TryGetObject(block.blockDefinition, out GameObject? existingPrefab))
{
GameObject? existing = AssetDatabase.LoadAssetAtPath<GameObject>($"{assetPath}/{assetName}");
if (existing)
{
var go = (GameObject) PrefabUtility.InstantiatePrefab(existing);
go.name = block.blockDefinition.name ?? "";
return go;
}
}
#if UNITY_EDITOR
var go = (GameObject) PrefabUtility.InstantiatePrefab(existingPrefab);
#else
var go = Object.Instantiate(existingPrefab);
#endif
// No existing found, so we Convert the block
GameObject native = new GameObject(block.blockDefinition.name ?? "");
TransformToNativeTransform(native.transform, block.transform);
go.name = block.blockDefinition.name ?? "";
return go;
}
// Convert the block definition
GameObject native = new GameObject(block.blockDefinition.name ?? "");
List<SMesh> meshes = new();
List<Base> others = new();
foreach (Base geo in block.blockDefinition.geometry)
@ -262,14 +255,9 @@ namespace Objects.Converter.Unity
c.transform.SetParent(native.transform, false);
}
#if UNITY_EDITOR
if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
{
CreateDirectoryFromAssetPath(assetPath);
PrefabUtility.SaveAsPrefabAssetAndConnect(native, $"Assets/Resources/Prefabs/{assetName}",
InteractionMode.AutomatedAction);
}
#endif
LoadedAssets.TrySaveObject(block.blockDefinition, native);
TransformToNativeTransform(native.transform, block.transform);
return native;
}

Просмотреть файл

@ -6,6 +6,7 @@ using Objects.Other;
using Objects.Utils;
using PlasticPipe.Certificates;
using Speckle.ConnectorUnity;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Models;
using UnityEditor;
using UnityEngine;
@ -207,7 +208,7 @@ namespace Objects.Converter.Unity
/// <summary>
/// Converts multiple <paramref name="meshes"/> (e.g. with different materials) into one native mesh
/// </summary>
/// <param name="element">Root element who's name/id is used to identify the mesh</param>
/// <param name="element">The <see cref="Base"/> object being converted</param>
/// <param name="meshes">Collection of <see cref="Objects.Geometry.Mesh"/>es that shall be converted</param>
/// <returns>A <see cref="GameObject"/> with the converted <see cref="UnityEngine.Mesh"/>, <see cref="MeshFilter"/>, and <see cref="MeshRenderer"/></returns>
public GameObject? MeshesToNative(Base element, IReadOnlyCollection<SMesh> meshes)
@ -222,10 +223,10 @@ namespace Objects.Converter.Unity
Material[] nativeMaterials = RenderMaterialsToNative(meshes);
Vector3 center;
if (LoadedAssets.TryGetValue(element.id, out var existingObj)
&& existingObj is Mesh existing)
if (LoadedAssets.TryGetObject(element, out Mesh? existing))
{
nativeMesh = existing;
//todo This is pretty inefficient, having to the mesh data anyway just to get the center... eek
MeshDataToNative(meshes,
out List<Vector3> verts,
out _,
@ -236,22 +237,15 @@ namespace Objects.Converter.Unity
else
{
MeshToNativeMesh(meshes, out nativeMesh, out center);
string name = GetAssetName(element);
string name = AssetHelpers.GetObjectName(element);
nativeMesh.name = name;
#if UNITY_EDITOR
if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
{
const string assetPath = "Assets/Resources/Meshes/Speckle Generated/";
CreateDirectoryFromAssetPath(assetPath);
AssetDatabase.CreateAsset(nativeMesh, $"{assetPath}/{name}");
}
#endif
LoadedAssets.TrySaveObject(element, nativeMesh);
}
var go = new GameObject();
go.transform.position = center;
go.SafeMeshSet(nativeMesh, nativeMaterials);
return go;
}
@ -461,19 +455,14 @@ namespace Objects.Converter.Unity
//todo support more complex materials
var shader = Shader.Find("Standard");
Material mat = new Material(shader);
//if a renderMaterial is passed use that, otherwise try get it from the mesh itself
// 1. If no renderMaterial was passed, use default material
if (renderMaterial == null) return mat;
// 1. match material by name, if any
string materialName = string.IsNullOrWhiteSpace(renderMaterial.name)
? $"material-{renderMaterial.id}"
: renderMaterial.name.Replace('/', '-');
if (LoadedAssets.TryGetValue(materialName, out Object asset)
&& asset is Material loadedMaterial) return loadedMaterial;
// 2. re-create material by setting diffuse color and transparency on standard shaders
// 2. Try get existing/override material from asset cache
if (LoadedAssets.TryGetObject(renderMaterial, out Material? loadedMaterial)) return loadedMaterial;
// 3. Otherwise, convert fresh!
if (renderMaterial.opacity < 1)
{
shader = Shader.Find("Transparent/Diffuse");
@ -482,51 +471,19 @@ namespace Objects.Converter.Unity
var c = renderMaterial.diffuse.ToUnityColor();
mat.color = new Color(c.r, c.g, c.b, (float) renderMaterial.opacity);
mat.name = materialName;
mat.name = AssetHelpers.GetObjectName(renderMaterial);;
mat.SetFloat(Metallic, (float) renderMaterial.metalness);
mat.SetFloat(Glossiness, 1 - (float) renderMaterial.roughness);
if (renderMaterial.emissive != SColor.Black.ToArgb()) mat.EnableKeyword("_EMISSION");
mat.SetColor(EmissionColor, renderMaterial.emissive.ToUnityColor());
#if UNITY_EDITOR
if (StreamManager.GenerateAssets) //TODO: I don't like how the converter is aware of StreamManager
{
var invalidChars = Path.GetInvalidFileNameChars();
string name = new(mat.name.Where(x => !invalidChars.Contains(x)).ToArray());
const string assetPath = "Assets/Resources/Materials/Speckle Generated/";
CreateDirectoryFromAssetPath(assetPath);
if (AssetDatabase.LoadAllAssetsAtPath($"{assetPath}/{name}.mat")
.Length == 0)
AssetDatabase.CreateAsset(mat, $"{assetPath}/{name}.mat");
}
#endif
LoadedAssets.TrySaveObject(renderMaterial, mat);
return mat;
// 3. if not renderMaterial was passed, the default shader will be used
}
protected static string GetAssetName(Base b, bool alwaysIncludeId = true)
{
var invalidChars = Path.GetInvalidFileNameChars();
string id = b.id;
foreach (var nameAlias in new[] {"name", "Name"})
{
string? rawName = b[nameAlias] as string;
if (string.IsNullOrWhiteSpace(rawName)) continue;
string name = new(rawName.Where(x => !invalidChars.Contains(x)).ToArray());
return alwaysIncludeId ? $"{name} - {id}" : name;
}
return id;
}
#endregion
}

Просмотреть файл

@ -5,10 +5,10 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Objects.BuiltElements;
using Objects.Other;
using Speckle.ConnectorUnity;
using Speckle.ConnectorUnity.NativeCache;
using UnityEditor;
using UnityEngine;
using Mesh = Objects.Geometry.Mesh;
@ -32,14 +32,14 @@ namespace Objects.Converter.Unity
public IEnumerable<string> GetServicedApplications() => new string[] {VersionedHostApplications.Unity};
public Dictionary<string, Object> LoadedAssets { get; private set; }
public AbstractNativeCache LoadedAssets { get; private set; }
public void SetContextDocument(object doc)
{
if (doc is not Dictionary<string, Object> loadedAssets)
if (doc is not AbstractNativeCache context)
throw new ArgumentException(
$"Expected {nameof(doc)} to be of type {typeof(Dictionary<string, Object>)}", nameof(doc));
LoadedAssets = loadedAssets;
LoadedAssets = context;
}
public void SetContextObjects(List<ApplicationObject> objects) => throw new NotImplementedException();

Просмотреть файл

@ -2,7 +2,8 @@
"name": "Speckle.Connector.Editor",
"rootNamespace": "Speckle.ConnectorUnity",
"references": [
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72"
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72",
"GUID:05078f9b6da40444fbd72ec600449925"
],
"includePlatforms": [
"Editor"

Просмотреть файл

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sentry;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using Speckle.Core.Kits;
@ -14,336 +15,353 @@ using UnityEngine;
namespace Speckle.ConnectorUnity.Editor
{
[CustomEditor(typeof(StreamManager))]
[CanEditMultipleObjects]
public class StreamManagerEditor : UnityEditor.Editor
{
private bool _foldOutAccount;
private int _totalChildrenCount = 0;
private StreamManager _streamManager;
public int StreamsLimit { get; set; } = 30;
public int BranchesLimit { get; set; } = 30;
public int CommitsLimit { get; set; } = 25;
private int SelectedAccountIndex
[CustomEditor(typeof(StreamManager))]
[CanEditMultipleObjects]
public class StreamManagerEditor : UnityEditor.Editor
{
get => _streamManager.SelectedAccountIndex;
set => _streamManager.SelectedAccountIndex = value;
}
private bool _foldOutAccount;
private int _totalChildrenCount = 0;
private StreamManager _streamManager;
private int SelectedStreamIndex
{
get => _streamManager.SelectedStreamIndex;
set => _streamManager.SelectedStreamIndex = value;
}
private static bool generateAssets;
private int SelectedBranchIndex
{
get => _streamManager.SelectedBranchIndex;
set => _streamManager.SelectedBranchIndex = value;
}
public int StreamsLimit { get; set; } = 30;
public int BranchesLimit { get; set; } = 30;
public int CommitsLimit { get; set; } = 25;
private int SelectedCommitIndex
{
get => _streamManager.SelectedCommitIndex;
set => _streamManager.SelectedCommitIndex = value;
}
private int OldSelectedAccountIndex
{
get => _streamManager.OldSelectedAccountIndex;
set => _streamManager.OldSelectedAccountIndex = value;
}
private int OldSelectedStreamIndex
{
get => _streamManager.OldSelectedStreamIndex;
set => _streamManager.OldSelectedStreamIndex = value;
}
private Client Client
{
get => _streamManager.Client;
set => _streamManager.Client = value;
}
private Account SelectedAccount
{
get => _streamManager.SelectedAccount;
set => _streamManager.SelectedAccount = value;
}
private Stream SelectedStream
{
get => _streamManager.SelectedStream;
set => _streamManager.SelectedStream = value;
}
public List<Account> Accounts
{
get => _streamManager.Accounts;
set => _streamManager.Accounts = value;
}
private List<Stream> Streams
{
get => _streamManager.Streams;
set => _streamManager.Streams = value;
}
private List<Branch> Branches
{
get => _streamManager.Branches;
set => _streamManager.Branches = value;
}
private async Task LoadAccounts()
{
//refresh accounts just in case
Accounts = AccountManager.GetAccounts().ToList();
if (!Accounts.Any())
{
Debug.Log("No Accounts found, please login in Manager");
}
else
{
await SelectAccount(0);
}
}
private async Task SelectAccount(int i)
{
SelectedAccountIndex = i;
OldSelectedAccountIndex = i;
SelectedAccount = Accounts[i];
Client = new Client(SelectedAccount);
await LoadStreams();
}
private async Task LoadStreams()
{
EditorUtility.DisplayProgressBar("Loading streams...", "", 0);
Streams = await Client.StreamsGet(StreamsLimit);
EditorUtility.ClearProgressBar();
if (Streams.Any())
await SelectStream(0);
}
private async Task SelectStream(int i)
{
SelectedStreamIndex = i;
OldSelectedStreamIndex = i;
SelectedStream = Streams[i];
EditorUtility.DisplayProgressBar("Loading stream details...", "", 0);
Branches = await Client.StreamGetBranches(SelectedStream.id, BranchesLimit, CommitsLimit);
if (Branches.Any())
{
SelectedBranchIndex = 0;
if (Branches[SelectedBranchIndex].commits.items.Any())
private int SelectedAccountIndex
{
SelectedCommitIndex = 0;
get => _streamManager.SelectedAccountIndex;
set => _streamManager.SelectedAccountIndex = value;
}
}
EditorUtility.ClearProgressBar();
}
private int SelectedStreamIndex
{
get => _streamManager.SelectedStreamIndex;
set => _streamManager.SelectedStreamIndex = value;
}
private int SelectedBranchIndex
{
get => _streamManager.SelectedBranchIndex;
set => _streamManager.SelectedBranchIndex = value;
}
private async Task Receive()
{
var transport = new ServerTransport(SelectedAccount, SelectedStream.id);
EditorUtility.DisplayProgressBar($"Receiving data from {transport.BaseUri}...", "", 0);
private int SelectedCommitIndex
{
get => _streamManager.SelectedCommitIndex;
set => _streamManager.SelectedCommitIndex = value;
}
try
{
// Receive Speckle Objects
var @base = await Operations.Receive(
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].referencedObject,
remoteTransport: transport,
onProgressAction: dict =>
{
UnityEditor.EditorApplication.delayCall += () =>
private int OldSelectedAccountIndex
{
get => _streamManager.OldSelectedAccountIndex;
set => _streamManager.OldSelectedAccountIndex = value;
}
private int OldSelectedStreamIndex
{
get => _streamManager.OldSelectedStreamIndex;
set => _streamManager.OldSelectedStreamIndex = value;
}
private Client Client
{
get => _streamManager.Client;
set => _streamManager.Client = value;
}
private Account SelectedAccount
{
get => _streamManager.SelectedAccount;
set => _streamManager.SelectedAccount = value;
}
private Stream SelectedStream
{
get => _streamManager.SelectedStream;
set => _streamManager.SelectedStream = value;
}
public List<Account> Accounts
{
get => _streamManager.Accounts;
set => _streamManager.Accounts = value;
}
private List<Stream> Streams
{
get => _streamManager.Streams;
set => _streamManager.Streams = value;
}
private List<Branch> Branches
{
get => _streamManager.Branches;
set => _streamManager.Branches = value;
}
private async Task LoadAccounts()
{
//refresh accounts just in case
Accounts = AccountManager.GetAccounts().ToList();
if (!Accounts.Any())
{
EditorUtility.DisplayProgressBar($"Receiving data from {transport.BaseUri}...", "",
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount));
};
},
onTotalChildrenCountKnown: count => { _totalChildrenCount = count; }
);
EditorUtility.ClearProgressBar();
//Convert Speckle Objects
int childrenConverted = 0;
void BeforeConvertCallback(Base b)
{
EditorUtility.DisplayProgressBar("Converting To Native...", $"{b.speckle_type} - {b.id}",
Convert.ToSingle(childrenConverted++ / _totalChildrenCount));
Debug.Log("No Accounts found, please login in Manager");
}
else
{
await SelectAccount(0);
}
}
var go = _streamManager.ConvertRecursivelyToNative(@base,
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id, BeforeConvertCallback);
// Read Receipt
await Client.CommitReceived(new CommitReceivedInput
private async Task SelectAccount(int i)
{
streamId = SelectedStream.id,
commitId = Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id,
message = $"received commit from {VersionedHostApplications.Unity} Editor",
sourceApplication = VersionedHostApplications.Unity
});
SelectedAccountIndex = i;
OldSelectedAccountIndex = i;
SelectedAccount = Accounts[i];
}
catch (Exception e)
{
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
}
finally
{
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
}
Client = new Client(SelectedAccount);
await LoadStreams();
}
private async Task LoadStreams()
{
EditorUtility.DisplayProgressBar("Loading streams...", "", 0);
Streams = await Client.StreamsGet(StreamsLimit);
EditorUtility.ClearProgressBar();
if (Streams.Any())
await SelectStream(0);
}
private async Task SelectStream(int i)
{
SelectedStreamIndex = i;
OldSelectedStreamIndex = i;
SelectedStream = Streams[i];
EditorUtility.DisplayProgressBar("Loading stream details...", "", 0);
Branches = await Client.StreamGetBranches(SelectedStream.id, BranchesLimit, CommitsLimit);
if (Branches.Any())
{
SelectedBranchIndex = 0;
if (Branches[SelectedBranchIndex].commits.items.Any())
{
SelectedCommitIndex = 0;
}
}
EditorUtility.ClearProgressBar();
}
private async Task Receive()
{
var transport = new ServerTransport(SelectedAccount, SelectedStream.id);
EditorUtility.DisplayProgressBar($"Receiving data from {transport.BaseUri}...", "", 0);
try
{
// Receive Speckle Objects
var @base = await Operations.Receive(
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].referencedObject,
remoteTransport: transport,
onProgressAction: dict =>
{
UnityEditor.EditorApplication.delayCall += () =>
{
EditorUtility.DisplayProgressBar($"Receiving data from {transport.BaseUri}...", "",
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount));
};
},
onTotalChildrenCountKnown: count => { _totalChildrenCount = count; }
);
EditorUtility.ClearProgressBar();
Analytics.TrackEvent(SelectedAccount, Analytics.Events.Receive);
//Convert Speckle Objects
int childrenConverted = 0;
void BeforeConvertCallback(Base b)
{
EditorUtility.DisplayProgressBar("Converting To Native...", $"{b.speckle_type} - {b.id}",
Convert.ToSingle(childrenConverted++ / _totalChildrenCount));
}
var go = _streamManager.ConvertRecursivelyToNative(@base,
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id, BeforeConvertCallback);
// Read Receipt
await Client.CommitReceived(new CommitReceivedInput
{
streamId = SelectedStream.id,
commitId = Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id,
message = $"received commit from {VersionedHostApplications.Unity} Editor",
sourceApplication = VersionedHostApplications.Unity
});
}
catch (Exception e)
{
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
}
finally
{
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
}
}
public override async void OnInspectorGUI()
{
_streamManager = (StreamManager) target;
#region Account GUI
if (Accounts == null)
{
await LoadAccounts();
return;
}
EditorGUILayout.BeginHorizontal();
SelectedAccountIndex = EditorGUILayout.Popup("Accounts", SelectedAccountIndex,
Accounts.Select(x => x.userInfo.email + " | " + x.serverInfo.name).ToArray(),
GUILayout.ExpandWidth(true), GUILayout.Height(20));
if (OldSelectedAccountIndex != SelectedAccountIndex)
{
await SelectAccount(SelectedAccountIndex);
return;
}
if (GUILayout.Button("Refresh", GUILayout.Width(60), GUILayout.Height(20)))
{
await LoadAccounts();
return;
}
EditorGUILayout.EndHorizontal();
#region Speckle Account Info
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(_foldOutAccount, "Account Info");
if (_foldOutAccount)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField("Name", SelectedAccount.userInfo.name,
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.TextField("Server", SelectedAccount.serverInfo.name,
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.TextField("URL", SelectedAccount.serverInfo.url,
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndFoldoutHeaderGroup();
#endregion
#endregion
#region Stream List
if (Streams == null)
return;
EditorGUILayout.BeginHorizontal();
SelectedStreamIndex = EditorGUILayout.Popup("Streams",
SelectedStreamIndex, Streams.Select(x => x.name).ToArray(), GUILayout.Height(20),
GUILayout.ExpandWidth(true));
if (OldSelectedStreamIndex != SelectedStreamIndex)
{
await SelectStream(SelectedStreamIndex);
return;
}
if (GUILayout.Button("Refresh", GUILayout.Width(60), GUILayout.Height(20)))
{
await LoadStreams();
return;
}
EditorGUILayout.EndHorizontal();
#endregion
#region Branch List
if (Branches == null)
return;
EditorGUILayout.BeginHorizontal();
SelectedBranchIndex = EditorGUILayout.Popup("Branches",
SelectedBranchIndex, Branches.Select(x => x.name).ToArray(), GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
if (!Branches[SelectedBranchIndex].commits.items.Any())
return;
EditorGUILayout.BeginHorizontal();
SelectedCommitIndex = EditorGUILayout.Popup("Commits",
SelectedCommitIndex,
Branches[SelectedBranchIndex].commits.items.Select(x => $"{x.message} - {x.id}").ToArray(),
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
#endregion
#region Generate Materials
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Generate assets");
GUILayout.FlexibleSpace();
bool selection = GUILayout.Toggle(generateAssets, "");
if (generateAssets != selection)
{
generateAssets = selection;
_streamManager.RC.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets);
}
EditorGUILayout.EndHorizontal();
#endregion
EditorGUILayout.BeginHorizontal();
bool receive = GUILayout.Button("Receive!");
EditorGUILayout.EndHorizontal();
if (receive)
{
await Receive();
}
}
}
public override async void OnInspectorGUI()
{
_streamManager = (StreamManager)target;
#region Account GUI
if (Accounts == null)
{
await LoadAccounts();
return;
}
EditorGUILayout.BeginHorizontal();
SelectedAccountIndex = EditorGUILayout.Popup("Accounts", SelectedAccountIndex,
Accounts.Select(x => x.userInfo.email + " | " + x.serverInfo.name).ToArray(),
GUILayout.ExpandWidth(true), GUILayout.Height(20));
if (OldSelectedAccountIndex != SelectedAccountIndex)
{
await SelectAccount(SelectedAccountIndex);
return;
}
if (GUILayout.Button("Refresh", GUILayout.Width(60), GUILayout.Height(20)))
{
await LoadAccounts();
return;
}
EditorGUILayout.EndHorizontal();
#region Speckle Account Info
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(_foldOutAccount, "Account Info");
if (_foldOutAccount)
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.TextField("Name", SelectedAccount.userInfo.name,
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.TextField("Server", SelectedAccount.serverInfo.name,
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.TextField("URL", SelectedAccount.serverInfo.url,
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndFoldoutHeaderGroup();
#endregion
#endregion
#region Stream List
if (Streams == null)
return;
EditorGUILayout.BeginHorizontal();
SelectedStreamIndex = EditorGUILayout.Popup("Streams",
SelectedStreamIndex, Streams.Select(x => x.name).ToArray(), GUILayout.Height(20),
GUILayout.ExpandWidth(true));
if (OldSelectedStreamIndex != SelectedStreamIndex)
{
await SelectStream(SelectedStreamIndex);
return;
}
if (GUILayout.Button("Refresh", GUILayout.Width(60), GUILayout.Height(20)))
{
await LoadStreams();
return;
}
EditorGUILayout.EndHorizontal();
#endregion
#region Branch List
if (Branches == null)
return;
EditorGUILayout.BeginHorizontal();
SelectedBranchIndex = EditorGUILayout.Popup("Branches",
SelectedBranchIndex, Branches.Select(x => x.name).ToArray(), GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
if (!Branches[SelectedBranchIndex].commits.items.Any())
return;
EditorGUILayout.BeginHorizontal();
SelectedCommitIndex = EditorGUILayout.Popup("Commits",
SelectedCommitIndex,
Branches[SelectedBranchIndex].commits.items.Select(x => $"{x.message} - {x.id}").ToArray(),
GUILayout.Height(20),
GUILayout.ExpandWidth(true));
EditorGUILayout.EndHorizontal();
#endregion
#region Generate Materials
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Generate material assets");
GUILayout.FlexibleSpace();
StreamManager.GenerateAssets = GUILayout.Toggle(StreamManager.GenerateAssets, "");
EditorGUILayout.EndHorizontal();
#endregion
EditorGUILayout.BeginHorizontal();
bool receive = GUILayout.Button("Receive!");
EditorGUILayout.EndHorizontal();
if (receive)
{
await Receive();
}
}
}
}

Просмотреть файл

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0ef3ae3dd987865439e8142619e4dbf6
guid: 60d87a53f669b0a48b83ad5f49380332
folderAsset: yes
DefaultImporter:
externalObjects: {}

Просмотреть файл

@ -0,0 +1,90 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Speckle.Core.Models;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache
{
#nullable enable
[ExecuteAlways]
public abstract class AbstractNativeCache : ScriptableObject
{
protected bool isWriting = false;
public abstract bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : Object;
public abstract bool TrySaveObject(Base speckleObject, Object nativeObject);
/// <summary>
/// Prepares this <see cref="AbstractNativeCache"/> for save operations
/// </summary>
public virtual void BeginWrite()
{
isWriting = true;
}
/// <summary>
/// Call when finished performing save operations.
/// Instructs the <see cref="AbstractNativeCache"/> to finish writing anything to disk
/// </summary>
public virtual void FinishWrite()
{
isWriting = false;
}
protected virtual void OnDisable()
{
FinishWrite();
}
}
public static class AssetHelpers
{
public static string? GetAssetFolder(Type nativeType, string path)
{
const string format = "{0}/{1}";
if (nativeType == typeof(Mesh))
{
return string.Format(format, path, "Geometry");
}
if (nativeType == typeof(Material))
{
return string.Format(format, path, "Materials");
}
if (nativeType == typeof(GameObject))
{
return string.Format(format, path, "Prefabs");
}
return null;
}
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());
return $"{sanitisedName}{suffix}";
}
public static string GetObjectName(Base speckleObject)
{
string objectName = speckleObject["name"] as string ?? speckleObject.GetType().ToString();
return $"{objectName} - {speckleObject.id}";
}
public static string GetAssetSuffix(Type nativeType)
{
if (nativeType == typeof(Material)) return ".mat";
if (nativeType == typeof(GameObject)) return ".prefab";
return ".asset";
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6cc5b1591bf7e64d9ac5f0ac97dfcf6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Models;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache
{
public class AggregateNativeCache : AbstractNativeCache
{
[SerializeField, SerializeReference]
public List<AbstractNativeCache> nativeCaches;
public override bool TryGetObject<T>(Base speckleObject, out T nativeObject) where T : class
{
foreach (var c in nativeCaches)
{
if (c.TryGetObject(speckleObject, out nativeObject)) return true;
}
nativeObject = null;
return false;
}
public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{
bool hasSavedSomewhere = false;
foreach (var c in nativeCaches)
{
hasSavedSomewhere = hasSavedSomewhere || c.TrySaveObject(speckleObject, nativeObject);
}
return hasSavedSomewhere;
}
public override void BeginWrite()
{
base.BeginWrite();
foreach (var c in nativeCaches)
{
c.BeginWrite();
}
}
public override void FinishWrite()
{
foreach (var c in nativeCaches)
{
try
{
c.FinishWrite();
}
catch (Exception e)
{
Debug.LogError(e);
}
}
base.FinishWrite();
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88d6b4f2f80eaa14f9f07505f7e44ec2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Speckle.Core.Models;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache.Editor
{
/// <summary>
/// Uses Unity's AssetDatabase to load existing assets from a given path
/// </summary>
public sealed class AssetDBNativeCache : AbstractNativeCache
{
public string path = "Assets/Resources";
private MemoryNativeCache readCache;
private Dictionary<Base, Object> writeBuffer = new();
#nullable enable
private 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}";
nativeObject = AssetDatabase.LoadAssetAtPath<T>(assetPath);
return nativeObject != null;
}
public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{
writeBuffer.TryAdd(speckleObject, nativeObject);
return readCache.TrySaveObject(speckleObject, nativeObject);
}
public 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}";
// Special case for GameObjects, we want to use PrefabUtility
if (nativeObject is GameObject go)
{
PrefabUtility.SaveAsPrefabAssetAndConnect(go, assetPath, InteractionMode.AutomatedAction);
return true;
}
// 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);
return false;
}
AssetDatabase.CreateAsset(nativeObject, $"{folder}/{assetName}");
return true;
}
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();
if (readCache != null) readCache.LoadedAssets.Clear();
base.FinishWrite();
}
private static bool CreateDirectory(string directoryPath)
{
if (Directory.Exists(directoryPath))
return true;
var info = Directory.CreateDirectory(directoryPath);
AssetDatabase.Refresh();
return info.Exists;
}
[ContextMenu("SetPath")]
internal void SetPath_Menu()
{
var selection = EditorUtility.OpenFolderPanel("Set Assets Path", "Assets/Resources", "");
if (selection.StartsWith(Application.dataPath)) {
path = "Assets" + selection.Substring(Application.dataPath.Length);
}
else
{
Debug.LogError($"Expected selection to be within {Application.dataPath}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 141ce93d2d159c0448b5b8b33b1c0679
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,16 @@
{
"name": "EditorCaches",
"rootNamespace": "Speckle.ConnectorUnity",
"references": ["GUID:05078f9b6da40444fbd72ec600449925"],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a97f36292d600f8459146719f68d6bb2
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Speckle.Core.Models;
using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache
{
#nullable enable
/// <summary>
/// In memory native object cache
/// </summary>
public sealed class MemoryNativeCache : AbstractNativeCache
{
public IDictionary<string, Object> LoadedAssets { get; set; } = new Dictionary<string, 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)
{
nativeObject = t;
return true;
}
nativeObject = null;
return false;
}
public bool TryGetObject(Base speckleObject, [NotNullWhen(true)] out Object? nativeObject)
{
return LoadedAssets.TryGetValue(speckleObject.id, out nativeObject);
}
public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{
return LoadedAssets.TryAdd(speckleObject.id, nativeObject);
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3354e8208862c341940152f5340d41a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,14 @@
{
"name": "NativeCaches",
"rootNamespace": "Speckle.ConnectorUnity",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

Просмотреть файл

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 05078f9b6da40444fbd72ec600449925
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,39 @@
using System.Diagnostics.CodeAnalysis;
using Speckle.Core.Models;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Speckle.ConnectorUnity.NativeCache
{
#nullable enable
/// <summary>
/// Loads existing assets from <see cref="Resources"/>
/// optionally accepting byName overrides
/// </summary>
public sealed class ResourcesNativeCache : AbstractNativeCache
{
public bool matchByName = true;
public override bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class
{
if (matchByName)
{
string? speckleName = speckleObject["name"] as string ?? speckleObject["Name"] as string;
if (!string.IsNullOrWhiteSpace(speckleName))
{
nativeObject = Resources.Load<T>(speckleName);
if (nativeObject != null) return true;
}
}
nativeObject = Resources.Load<T>(AssetHelpers.GetAssetName(speckleObject, typeof(T)));
return nativeObject != null;
}
public override bool TrySaveObject(Base speckleObject, Object nativeObject)
{
// Pass
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a4a29c776298714c88f406ad39c6095
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -0,0 +1,50 @@
using System.Collections.Generic;
using Speckle.ConnectorUnity.NativeCache;
using UnityEditor.Build;
using UnityEngine;
#if UNITY_EDITOR
using Speckle.ConnectorUnity.NativeCache.Editor;
#endif
namespace Speckle.ConnectorUnity
{
#nullable enable
public static class NativeCacheFactory
{
public static List<AbstractNativeCache> GetDefaultNativeCacheSetup(bool generateAssets = false)
{
#if UNITY_EDITOR
if (generateAssets)
{
return GetEditorCacheSetup();
}
#endif
return GetStandaloneCacheSetup();
}
public static List<AbstractNativeCache> GetStandaloneCacheSetup()
{
return new List<AbstractNativeCache>()
{
ScriptableObject.CreateInstance<ResourcesNativeCache>(),
ScriptableObject.CreateInstance<MemoryNativeCache>(),
};
}
#if UNITY_EDITOR
public static List<AbstractNativeCache> GetEditorCacheSetup()
{
return new List<AbstractNativeCache>()
{
ScriptableObject.CreateInstance<ResourcesNativeCache>(),
ScriptableObject.CreateInstance<AssetDBNativeCache>(),
ScriptableObject.CreateInstance<MemoryNativeCache>(),
};
}
#endif
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fc80190dd595f14ca90df87fe890610
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -139,6 +139,9 @@ namespace Speckle.ConnectorUnity
onTotalChildrenCountKnown: OnTotalChildrenCountKnown,
disposeTransports: true
);
Analytics.TrackEvent(Client.Account, Analytics.Events.Receive);
Dispatcher.Instance().Enqueue(() =>
{
var root = new GameObject()

Просмотреть файл

@ -3,6 +3,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Models;
using UnityEngine;
@ -12,25 +13,34 @@ namespace Speckle.ConnectorUnity
{
/// <summary>
/// Given <paramref name="baseObject"/>,
/// Given <paramref name="o"/>,
/// will recursively convert any objects in the tree
/// </summary>
/// <param name="o">The object to convert (<see cref="Base"/> or <see cref="List{T}"/> of)</param>
/// <param name="parent">Optional parent transform for the created root <see cref="GameObject"/>s</param>
/// <returns> A list of all created <see cref="GameObject"/>s</returns>
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent)
=> RecursivelyConvertToNative(o, parent, o => ConverterInstance.CanConvertToNative(o));
=> RecursivelyConvertToNative(o, parent, b => ConverterInstance.CanConvertToNative(b));
/// <inheritdoc cref="RecursivelyConvertToNative(object, Transform)"/>
/// <param name="predicate">A function to determine if an object should be converted</param>
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent, Func<Base, bool> predicate)
{
LoadMaterialOverrides();
//Ensure we have A native cache
if (AssetCache.nativeCaches.Any(x => x == null))
{
AssetCache.nativeCaches = NativeCacheFactory.GetStandaloneCacheSetup();
}
ConverterInstance.SetContextDocument(AssetCache);
AssetCache.BeginWrite();
var createdGameObjects = new List<GameObject>();
ConvertChild(o, parent, predicate, createdGameObjects);
//TODO track event
AssetCache.FinishWrite();
return createdGameObjects;
}
@ -81,8 +91,7 @@ namespace Speckle.ConnectorUnity
}
// For geometry, only traverse `elements` prop, otherwise, try and convert everything
IEnumerable<string> potentialChildren;
potentialChildren = ConverterInstance.CanConvertToNative(baseObject)
IEnumerable<string> potentialChildren = ConverterInstance.CanConvertToNative(baseObject)
? new []{"elements"}
: baseObject.GetMemberNames();
@ -124,19 +133,6 @@ namespace Speckle.ConnectorUnity
Debug.Log($"Unknown type {value.GetType()} found when traversing tree, will be safely ignored");
}
}
protected virtual void LoadMaterialOverrides()
{
//using the ContextDocument to pass materials
//available in Assets/Materials to the converters
Dictionary<string, UnityEngine.Object> loadedAssets = Resources.LoadAll("", typeof(Material))
.GroupBy(o => o.name)
.Select(o => o.First())
.ToDictionary(o => o.name);
ConverterInstance.SetContextDocument(loadedAssets);
}
[Obsolete("Use RecursivelyConvertToNative instead")]
public GameObject ConvertRecursivelyToNative(Base @base, string name)

Просмотреть файл

@ -1,5 +1,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using Objects.Converter.Unity;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Kits;
using UnityEngine;
@ -12,7 +14,19 @@ namespace Speckle.ConnectorUnity
[ExecuteAlways, DisallowMultipleComponent]
public partial class RecursiveConverter : MonoBehaviour
{
public virtual ISpeckleConverter ConverterInstance { get; set; } = new ConverterUnity();
public ISpeckleConverter ConverterInstance { get; set; } = new ConverterUnity();
[field: SerializeField]
public AggregateNativeCache AssetCache { get; set; }
private void Awake()
{
if (AssetCache == null)
{
var assetCache = ScriptableObject.CreateInstance<AggregateNativeCache>();
assetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup();
this.AssetCache = assetCache;
}
}
}
}

Просмотреть файл

@ -2,7 +2,9 @@
"name": "Speckle.Connector",
"rootNamespace": "Speckle.ConnectorUnity",
"references": [
"GUID:24f666972ea7e9149abddaae766b9c1d"
"GUID:24f666972ea7e9149abddaae766b9c1d",
"GUID:05078f9b6da40444fbd72ec600449925",
"GUID:a97f36292d600f8459146719f68d6bb2"
],
"includePlatforms": [],
"excludePlatforms": [],

Просмотреть файл

@ -2,81 +2,83 @@ using System;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
using System.Collections.Generic;
using Speckle.ConnectorUnity.NativeCache;
using Speckle.Core.Models;
using UnityEngine;
using UnityEngine.Serialization;
namespace Speckle.ConnectorUnity
{
[ExecuteAlways]
[AddComponentMenu("Speckle/Stream Manager")]
public class StreamManager : MonoBehaviour
{
public int SelectedAccountIndex = -1;
public int SelectedStreamIndex = -1;
public int SelectedBranchIndex = -1;
public int SelectedCommitIndex = -1;
public int OldSelectedAccountIndex = -1;
public int OldSelectedStreamIndex = -1;
[ExecuteAlways]
[AddComponentMenu("Speckle/Stream Manager")]
[RequireComponent(typeof(RecursiveConverter))]
public class StreamManager : MonoBehaviour
{
public int SelectedAccountIndex = -1;
public int SelectedStreamIndex = -1;
public int SelectedBranchIndex = -1;
public int SelectedCommitIndex = -1;
public int OldSelectedAccountIndex = -1;
public int OldSelectedStreamIndex = -1;
public Client Client;
public Account SelectedAccount;
public Stream SelectedStream;
public Client Client;
public Account SelectedAccount;
public Stream SelectedStream;
public List<Account> Accounts;
public List<Stream> Streams;
public List<Branch> Branches;
#if UNITY_EDITOR
public static bool GenerateAssets = false;
#endif
public List<Account> Accounts;
public List<Stream> Streams;
public List<Branch> Branches;
public RecursiveConverter RC { get; private set; }
#nullable enable
public GameObject ConvertRecursivelyToNative(Base @base, string rootObjectName, Action<Base>? beforeConvertCallback)
{
private void Awake()
{
RC = GetComponent<RecursiveConverter>();
}
public GameObject ConvertRecursivelyToNative(Base @base, string rootObjectName,
Action<Base>? beforeConvertCallback)
{
var rootObject = new GameObject(rootObjectName);
var rc = GetComponent<RecursiveConverter>();
if (rc == null)
rc = gameObject.AddComponent<RecursiveConverter>();
var rootObject = new GameObject(rootObjectName);
bool Predicate(Base o)
{
beforeConvertCallback?.Invoke(o);
return rc.ConverterInstance.CanConvertToNative(o) //Accept geometry
|| o.speckle_type == "Base" && o.totalChildrenCount > 0; // Or Base objects that have children
}
bool Predicate(Base o)
{
beforeConvertCallback?.Invoke(o);
return RC.ConverterInstance.CanConvertToNative(o) //Accept geometry
|| o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children
}
// For the rootObject only, we will create property GameObjects
// i.e. revit categories
foreach (var prop in @base.GetMembers())
{
var converted = rc.RecursivelyConvertToNative(prop.Value, null, Predicate);
//Skip empties
if(converted.Count <= 0) continue;
// For the rootObject only, we will create property GameObjects
// i.e. revit categories
foreach (var prop in @base.GetMembers())
{
var converted = RC.RecursivelyConvertToNative(prop.Value, null, Predicate);
var propertyObject = new GameObject(prop.Key);
propertyObject.transform.SetParent(rootObject.transform);
foreach (var o in converted)
{
if(o.transform.parent == null) o.transform.SetParent(propertyObject.transform);
}
}
//Skip empties
if (converted.Count <= 0) continue;
var propertyObject = new GameObject(prop.Key);
propertyObject.transform.SetParent(rootObject.transform);
foreach (var o in converted)
{
if (o.transform.parent == null) o.transform.SetParent(propertyObject.transform);
}
}
return rootObject;
}
return rootObject;
}
#if UNITY_EDITOR
[ContextMenu("Open Speckle Stream in Browser")]
protected void OpenUrlInBrowser()
{
string url = $"{SelectedAccount.serverInfo.url}/streams/{SelectedStream.id}/commits/{Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id}";
Application.OpenURL(url);
}
[ContextMenu("Open Speckle Stream in Browser")]
protected void OpenUrlInBrowser()
{
string url =
$"{SelectedAccount.serverInfo.url}/streams/{SelectedStream.id}/commits/{Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].id}";
Application.OpenURL(url);
}
#endif
}
}
}

Просмотреть файл

@ -1,2 +1,2 @@
m_EditorVersion: 2021.3.6f1
m_EditorVersionWithRevision: 2021.3.6f1 (7da38d85baf6)
m_EditorVersion: 2021.3.11f1
m_EditorVersionWithRevision: 2021.3.11f1 (0a5ca18544bf)