Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4c363e33f | |||
| cf7e72aa7d | |||
| 695a25af51 | |||
| 63d83d0044 | |||
| db88d0dc41 | |||
| 5359731dce | |||
| 790e5d8294 | |||
| 00c4a43c3a | |||
| 8e32a0214e | |||
| ab5d4c2fba | |||
| 1b2eeed3eb | |||
| edeec70993 | |||
| ce33e7c454 | |||
| fb1e458970 | |||
| 2755a9abd7 | |||
| ee9795e39f | |||
| 999e6ae4ea | |||
| 8df96eeca4 | |||
| 6aa92d4c57 | |||
| 030cb277b8 | |||
| 5ee498afce | |||
| 0bb1591624 | |||
| 5dd889c898 | |||
| 9c7d1deb0a | |||
| 106d4c8e73 | |||
| 7c8f70c0c0 | |||
| f0be78888f | |||
| 892e77e0ff | |||
| 8a9c21f979 | |||
| aa46d49620 | |||
| 5d92e12eff | |||
| e1a3ae6b9c | |||
| d44ead55db | |||
| a3fb10570e | |||
| 57154a6fb8 | |||
| 5db1f450af | |||
| 62875c0a27 | |||
| b3c6b59721 | |||
| d9f7895b3f | |||
| dc58f6b0b0 | |||
| 556a7eaddf | |||
| 39a79be700 | |||
| 73ef71f4fd | |||
| 6a07ecfd5b | |||
| e512df9c82 | |||
| 59221e89ba | |||
| 32533ddb21 | |||
| fc537e137f | |||
| 2aaaf6572c | |||
| a89e4cdfe7 | |||
| 9a89255ae8 | |||
| 9f3145eb19 | |||
| 5dde0dae60 | |||
| 0794c53b24 | |||
| 2973087beb |
+210
@@ -0,0 +1,210 @@
|
||||
root = true
|
||||
# Don't use tabs for indentation.
|
||||
[*]
|
||||
indent_style = space
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
|
||||
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
|
||||
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
|
||||
|
||||
# ReSharper properties
|
||||
resharper_align_linq_query = false
|
||||
resharper_align_multiline_calls_chain = true
|
||||
resharper_align_multiline_extends_list = true
|
||||
resharper_align_multiline_for_stmt = true
|
||||
resharper_align_multiline_parameter = true
|
||||
resharper_align_multiple_declaration = true
|
||||
resharper_align_multline_type_parameter_constrains = true
|
||||
resharper_align_multline_type_parameter_list = true
|
||||
resharper_braces_for_dowhile = not_required
|
||||
resharper_braces_for_fixed = not_required
|
||||
resharper_braces_for_lock = not_required
|
||||
resharper_braces_for_using = not_required
|
||||
resharper_csharp_align_multiline_calls_chain = false
|
||||
resharper_csharp_align_multiline_extends_list = false
|
||||
resharper_csharp_align_multiline_parameter = false
|
||||
resharper_csharp_int_align_comments = false
|
||||
resharper_csharp_outdent_commas = true
|
||||
resharper_csharp_outdent_dots = false
|
||||
resharper_csharp_wrap_after_declaration_lpar = true
|
||||
resharper_csharp_wrap_after_invocation_lpar = true
|
||||
resharper_csharp_wrap_arguments_style = chop_if_long
|
||||
resharper_csharp_wrap_before_declaration_rpar = true
|
||||
resharper_csharp_wrap_before_invocation_rpar = false
|
||||
resharper_csharp_wrap_extends_list_style = chop_if_long
|
||||
resharper_csharp_wrap_parameters_style = chop_if_long
|
||||
resharper_force_attribute_style = join
|
||||
resharper_indent_nested_fixed_stmt = false
|
||||
resharper_indent_nested_foreach_stmt = true
|
||||
resharper_indent_nested_for_stmt = true
|
||||
resharper_indent_nested_lock_stmt = false
|
||||
resharper_indent_nested_usings_stmt = false
|
||||
resharper_indent_nested_while_stmt = true
|
||||
resharper_int_align = false
|
||||
resharper_int_align_nested_ternary = false
|
||||
resharper_int_align_switch_expressions = false
|
||||
resharper_keep_existing_declaration_block_arrangement = true
|
||||
resharper_keep_existing_declaration_parens_arrangement = false
|
||||
resharper_keep_existing_embedded_block_arrangement = true
|
||||
resharper_keep_existing_enum_arrangement = true
|
||||
resharper_keep_existing_expr_member_arrangement = true
|
||||
resharper_keep_existing_initializer_arrangement = false
|
||||
resharper_local_function_body = expression_body
|
||||
resharper_max_attribute_length_for_same_line = 20
|
||||
resharper_max_formal_parameters_on_line = 5
|
||||
resharper_max_initializer_elements_on_line = 0
|
||||
resharper_max_invocation_arguments_on_line = 5
|
||||
resharper_outdent_binary_ops = true
|
||||
resharper_outdent_dots = false
|
||||
resharper_place_constructor_initializer_on_same_line = false
|
||||
resharper_place_simple_initializer_on_single_line = true
|
||||
resharper_prefer_explicit_discard_declaration = false
|
||||
resharper_wrap_after_declaration_lpar = false
|
||||
resharper_wrap_before_invocation_rpar = false
|
||||
resharper_wrap_chained_binary_expressions = chop_if_long
|
||||
resharper_wrap_chained_binary_patterns = chop_if_long
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_xmldoc_indent_text = ZeroIndent
|
||||
|
||||
# Standard properties
|
||||
insert_final_newline = true
|
||||
|
||||
# (Please don't specify an indent_size here; that has too many unintended consequences.)
|
||||
|
||||
# Code files
|
||||
[*.{cs,csx,vb,vbx}]
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
|
||||
# Xml project files
|
||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
indent_size = 2
|
||||
space_after_last_pi_attribute = false
|
||||
# Xml config files
|
||||
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
|
||||
indent_size = 2
|
||||
space_after_last_pi_attribute = false
|
||||
|
||||
# JSON files
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
# Dotnet code style settings:
|
||||
[*.{cs,vb}]
|
||||
# Sort using and Import directives with System.* appearing first
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_separate_import_directive_groups = false
|
||||
|
||||
# Avoid "this." and "Me." if not necessary
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
|
||||
# Use language keywords instead of framework type names for type references
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:warning
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
|
||||
|
||||
# CSharp code style settings:
|
||||
[*.cs]
|
||||
# Prefer "var" everywhere
|
||||
csharp_style_var_elsewhere = false:none
|
||||
csharp_style_var_for_built_in_types = false:none
|
||||
csharp_style_var_when_type_is_apparent = false:none
|
||||
|
||||
# Prefer method-like constructs to have a block body
|
||||
csharp_style_expression_bodied_methods = false:suggestion
|
||||
csharp_style_expression_bodied_constructors = false:suggestion
|
||||
csharp_style_expression_bodied_operators = true:suggestion
|
||||
|
||||
# Prefer property-like constructs to have an expression-body
|
||||
csharp_style_expression_bodied_properties = true:suggestion
|
||||
csharp_style_expression_bodied_indexers = true:suggestion
|
||||
csharp_style_expression_bodied_accessors = true:suggestion
|
||||
|
||||
# Suggest more modern language features when available
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Newline settings
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
|
||||
# Analyzer settings
|
||||
dotnet_diagnostic.ide0055.severity = none
|
||||
|
||||
# Maintainability rules
|
||||
dotnet_diagnostic.ca1501.severity = warning
|
||||
dotnet_diagnostic.ca1502.severity = warning
|
||||
dotnet_diagnostic.ca1505.severity = warning
|
||||
dotnet_diagnostic.ca1506.severity = warning
|
||||
dotnet_diagnostic.ca1507.severity = warning
|
||||
dotnet_diagnostic.ca1508.severity = warning
|
||||
dotnet_diagnostic.ca1509.severity = warning
|
||||
|
||||
# Misc
|
||||
dotnet_diagnostic.ca1051.severity = none # Do not declare visible instance fields
|
||||
dotnet_diagnostic.ca1062.severity = none # Public method must check all parameters for null
|
||||
dotnet_diagnostic.ca1707.severity = none # Remove underscores in names
|
||||
|
||||
|
||||
dotnet_analyzer_diagnostic.category-globalization.severity = none
|
||||
dotnet_analyzer_diagnostic.category-security.severity = none
|
||||
dotnet_analyzer_diagnostic.category-interoperability.severity = none
|
||||
dotnet_analyzer_diagnostic.category-singlefile.severity = none
|
||||
|
||||
[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 2
|
||||
+2
-2
@@ -45,14 +45,14 @@ ExportedObj/
|
||||
*.pidb
|
||||
*.booproj
|
||||
*.svd
|
||||
*.pdb
|
||||
#*.pdb
|
||||
*.mdb
|
||||
*.opendb
|
||||
*.VC.db
|
||||
|
||||
# Unity3D generated meta files
|
||||
*.pidb.meta
|
||||
*.pdb.meta
|
||||
#*.pdb.meta
|
||||
*.mdb.meta
|
||||
|
||||
# Unity3D generated file on crash reports
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Objects.Converter.Unity;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Example script for grabbing speckle properties for a specific object "on-the-fly"
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// see discussion https://speckle.community/t/reloading-assemblies-takes-too-long/6708
|
||||
/// </remarks>
|
||||
[AddComponentMenu("Speckle/Extras/" + nameof(AttachSpecklePropertiesExample))]
|
||||
public class AttachSpecklePropertiesExample : MonoBehaviour
|
||||
{
|
||||
public string streamId;
|
||||
public string objectId;
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
Client speckleClient = new(AccountManager.GetDefaultAccount()!);
|
||||
|
||||
StartCoroutine(AttachSpeckleProperties(speckleClient, streamId, objectId));
|
||||
}
|
||||
|
||||
public IEnumerator AttachSpeckleProperties(
|
||||
Client speckleClient,
|
||||
string streamId,
|
||||
string objectId
|
||||
)
|
||||
{
|
||||
//Fetch the object from Speckle
|
||||
ServerTransport remoteTransport = new(speckleClient.Account, streamId);
|
||||
Utils.WaitForTask<Base> operation =
|
||||
new(async () => await Operations.Receive(objectId, remoteTransport));
|
||||
|
||||
//yield until task completes
|
||||
yield return operation;
|
||||
Base speckleObject = operation.Result;
|
||||
|
||||
//Do something with the properties. e.g. attach SpeckleProperties component
|
||||
DoSomething(speckleObject);
|
||||
}
|
||||
|
||||
protected virtual void DoSomething(Base speckleObject)
|
||||
{
|
||||
//GetProperties will filter "useful" properties
|
||||
Dictionary<string, object> properties = ConverterUnity.GetProperties(
|
||||
speckleObject,
|
||||
typeof(SpeckleObject)
|
||||
);
|
||||
|
||||
var sd = this.gameObject.AddComponent<SpeckleProperties>();
|
||||
sd.Data = properties;
|
||||
sd.SpeckleType = speckleObject.GetType();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dd598fed5008c44a815ba09e81a2d19
|
||||
guid: b5627857f30c8994c87469d287b2d115
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -4,62 +4,64 @@ using Speckle.ConnectorUnity;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
|
||||
[AddComponentMenu("Speckle/Extras/Manual Receiver")]
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
public class ManualReceive : MonoBehaviour
|
||||
{
|
||||
|
||||
public string authToken;
|
||||
public string serverUrl;
|
||||
public string streamId, objectId;
|
||||
|
||||
private RecursiveConverter receiver;
|
||||
public string streamId,
|
||||
objectId;
|
||||
|
||||
private RecursiveConverter receiver;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
receiver = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
|
||||
IEnumerator Start()
|
||||
{
|
||||
Debug.developerConsoleVisible = true;
|
||||
if(Time.timeSinceLevelLoad > 20) yield return null;
|
||||
if (Time.timeSinceLevelLoad > 20)
|
||||
yield return null;
|
||||
Receive();
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Receive))]
|
||||
public void Receive()
|
||||
{
|
||||
var account = new Account()
|
||||
{
|
||||
token = authToken,
|
||||
serverInfo = new ServerInfo() {url = serverUrl},
|
||||
serverInfo = new ServerInfo() { url = serverUrl },
|
||||
};
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var transport = new ServerTransport(account, streamId);
|
||||
var localTransport = new MemoryTransport();
|
||||
|
||||
var @base = await Operations.Receive(
|
||||
using ServerTransport transport = new(account, streamId);
|
||||
MemoryTransport localTransport = new();
|
||||
|
||||
Base speckleObject = await Operations.Receive(
|
||||
objectId,
|
||||
remoteTransport: transport,
|
||||
localTransport: localTransport,
|
||||
onErrorAction: (m, e)=> Debug.LogError(m + e),
|
||||
disposeTransports: true
|
||||
localTransport: localTransport
|
||||
);
|
||||
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
var parentObject = new GameObject(name);
|
||||
receiver.RecursivelyConvertToNative(@base, parentObject.transform);
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
var parentObject = new GameObject(name);
|
||||
|
||||
Debug.Log($"Receive {objectId} completed");
|
||||
});
|
||||
receiver.RecursivelyConvertToNative_Sync(speckleObject, parentObject.transform);
|
||||
|
||||
Debug.Log($"Receive {objectId} completed");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Extra
|
||||
{
|
||||
/// <summary>
|
||||
/// Script used to generate streams for performance benchmarking in other hostApps.
|
||||
/// Will send several several commits with a varying number of copies on a GameObject (with children)
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(SpeckleSender))]
|
||||
public sealed class PerformanceTestSender : MonoBehaviour
|
||||
{
|
||||
[Range(0, 100)]
|
||||
public int numberOfIterations = 10;
|
||||
|
||||
public Vector3 translation = Vector3.forward * 100;
|
||||
|
||||
public GameObject objectToSend;
|
||||
|
||||
private SpeckleSender sender;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
sender = GetComponent<SpeckleSender>();
|
||||
}
|
||||
|
||||
public async Task SendIterations()
|
||||
{
|
||||
GameObject go = new GameObject();
|
||||
for (int i = 0; i < numberOfIterations; i++)
|
||||
{
|
||||
Instantiate(objectToSend, translation * i, Quaternion.identity, go.transform);
|
||||
|
||||
Base b = sender.Converter.RecursivelyConvertToSpeckle(go, _ => true);
|
||||
await Send(b, $"{i}");
|
||||
}
|
||||
Debug.Log("Done!");
|
||||
}
|
||||
|
||||
private async Task<string> Send(Base data, string branchName)
|
||||
{
|
||||
Client client = sender.Account.Client!;
|
||||
Stream stream = sender.Stream.Selected;
|
||||
Account selectedAccount = sender.Account.Selected!;
|
||||
|
||||
using ServerTransport transport = new(selectedAccount, stream!.id);
|
||||
|
||||
string branchId = await client.BranchCreate(
|
||||
new BranchCreateInput() { streamId = stream.id, name = branchName }
|
||||
);
|
||||
|
||||
return await SpeckleSender.SendDataAsync(
|
||||
remoteTransport: transport,
|
||||
data,
|
||||
client,
|
||||
branchId,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.CustomEditor(typeof(PerformanceTestSender))]
|
||||
public sealed class PerformanceTestSenderEditor : UnityEditor.Editor
|
||||
{
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
if (GUILayout.Button("Create and send"))
|
||||
{
|
||||
await ((PerformanceTestSender)target).SendIterations();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c98b93e32c844fb488992e2e376447a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -2,13 +2,11 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Speckle.ConnectorUnity;
|
||||
using UnityEditor.Experimental;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[RequireComponent(typeof(Sender)), ExecuteAlways]
|
||||
[Obsolete]
|
||||
public class SendChildrenToSpeckle : MonoBehaviour
|
||||
{
|
||||
public LayerMask layerMask;
|
||||
@@ -22,7 +20,7 @@ public class SendChildrenToSpeckle : MonoBehaviour
|
||||
{
|
||||
sender = GetComponent<Sender>();
|
||||
}
|
||||
|
||||
|
||||
[ContextMenu(nameof(Send))]
|
||||
public void Send()
|
||||
{
|
||||
@@ -30,30 +28,32 @@ public class SendChildrenToSpeckle : MonoBehaviour
|
||||
.Where(t => t != this.transform)
|
||||
.Select(o => o.gameObject)
|
||||
.ToImmutableHashSet();
|
||||
|
||||
|
||||
Debug.Log("starting send...");
|
||||
sender.Send(streamId, selected, null, branchName, createCommit,
|
||||
sender.Send(
|
||||
streamId,
|
||||
selected,
|
||||
null,
|
||||
branchName,
|
||||
createCommit,
|
||||
onErrorAction: OnError,
|
||||
onProgressAction: OnProgress,
|
||||
onDataSentAction: OnSent);
|
||||
onDataSentAction: OnSent
|
||||
);
|
||||
}
|
||||
|
||||
private void OnSent(string objectId)
|
||||
{
|
||||
Debug.Log($"Data sent {objectId}", this);
|
||||
}
|
||||
|
||||
|
||||
private void OnError(string message, Exception e)
|
||||
{
|
||||
Debug.LogError($"Error while sending {message} \n {e}", this);
|
||||
|
||||
}
|
||||
|
||||
private void OnProgress(ConcurrentDictionary<string, int> dict)
|
||||
{
|
||||
Debug.Log($"progress was made", this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
{
|
||||
"name": "Speckle.Extra",
|
||||
"references":[ "GUID:eed1b8b83e2c0074d9e5de2348e3ff72", "GUID:e6adfdc4e436206479f48eafc82f32b5" ]
|
||||
}
|
||||
"name": "Speckle.Extra",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72",
|
||||
"GUID:e6adfdc4e436206479f48eafc82f32b5",
|
||||
"GUID:d274441ecc3eb3f43b093eec1503d681",
|
||||
"GUID:50d889142fdf9de4b8501c6eaa4b3225",
|
||||
"GUID:7383cd71541a2aa48a7baf23f74b4d5f"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
+218
-193
@@ -1,219 +1,244 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Logging;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Text = UnityEngine.UI.Text;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
public class InteractionLogic : MonoBehaviour
|
||||
{
|
||||
private Receiver receiver;
|
||||
|
||||
public void InitReceiver(Stream stream, bool autoReceive)
|
||||
[Obsolete]
|
||||
public class InteractionLogic : MonoBehaviour
|
||||
{
|
||||
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
private Receiver _receiver;
|
||||
|
||||
receiver = gameObject.AddComponent<Receiver>();
|
||||
receiver.Stream = stream;
|
||||
|
||||
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
|
||||
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
|
||||
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
|
||||
var branchesDropdown = gameObject.transform.Find("Dropdown").GetComponentInChildren<Dropdown>();
|
||||
var receiveProgress = btn.GetComponentInChildren<Slider>();
|
||||
receiveProgress.gameObject.SetActive(false); //hide
|
||||
|
||||
//populate branches
|
||||
branchesDropdown.options.Clear();
|
||||
List<Branch> branches = receiver.Stream.branches.items;
|
||||
branches.Reverse();
|
||||
foreach (Branch branch in branches)
|
||||
{
|
||||
branchesDropdown.options.Add(new Dropdown.OptionData(branch.name.Replace(' ', '\u00A0')));
|
||||
}
|
||||
|
||||
//trigger ui refresh, maybe there's a better method
|
||||
branchesDropdown.value = -1;
|
||||
branchesDropdown.value = 0;
|
||||
branchesDropdown.onValueChanged.AddListener(index =>
|
||||
{
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
receiver.BranchName = branches[index].name;
|
||||
});
|
||||
|
||||
receiver.Init(stream.id, autoReceive, true,
|
||||
onDataReceivedAction: (go) =>
|
||||
public void InitReceiver(Stream stream, bool autoReceive)
|
||||
{
|
||||
statusText.text = $"Received {go.name}";
|
||||
MakeButtonsInteractable(true);
|
||||
receiveProgress.value = 0;
|
||||
receiveProgress.gameObject.SetActive(false);
|
||||
gameObject.name = $"receiver-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
|
||||
AddComponents(go);
|
||||
},
|
||||
onTotalChildrenCountKnown: (count) => { receiver.TotalChildrenCount = count; },
|
||||
onProgressAction: (dict) =>
|
||||
{
|
||||
//Run on a dispatcher as GOs can only be retrieved on the main thread
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
var val = dict.Values.Average() / receiver.TotalChildrenCount;
|
||||
receiveProgress.gameObject.SetActive(true);
|
||||
receiveProgress.value = (float) val;
|
||||
});
|
||||
});
|
||||
_receiver = gameObject.AddComponent<Receiver>();
|
||||
_receiver.Stream = stream;
|
||||
|
||||
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
|
||||
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
|
||||
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
|
||||
var branchesDropdown = gameObject.transform
|
||||
.Find("Dropdown")
|
||||
.GetComponentInChildren<Dropdown>();
|
||||
var receiveProgress = btn.GetComponentInChildren<Slider>();
|
||||
receiveProgress.gameObject.SetActive(false); //hide
|
||||
|
||||
streamText.text = $"Stream: {stream.name}\nId: {stream.id} - Auto: {autoReceive}";
|
||||
btn.onClick.AddListener(() =>
|
||||
{
|
||||
statusText.text = "Receiving...";
|
||||
MakeButtonsInteractable(false);
|
||||
receiver.Receive();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively adds custom components to all children of a GameObject
|
||||
/// </summary>
|
||||
/// <param name="go"></param>
|
||||
private void AddComponents(GameObject go)
|
||||
{
|
||||
for (var i = 0; i < go.transform.childCount; i++)
|
||||
{
|
||||
var child = go.transform.GetChild(i);
|
||||
|
||||
if (child.childCount > 0)
|
||||
{
|
||||
AddComponents(child.gameObject);
|
||||
}
|
||||
|
||||
child.gameObject.AddComponent<Selectable>();
|
||||
|
||||
//Add extra Components
|
||||
//var rigidbody = child.gameObject.AddComponent<Rigidbody>();
|
||||
//rigidbody.mass = 10;
|
||||
}
|
||||
}
|
||||
|
||||
public void InitSender(Stream stream)
|
||||
{
|
||||
gameObject.name = $"sender-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
|
||||
var sender = gameObject.AddComponent<Sender>();
|
||||
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
|
||||
|
||||
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
|
||||
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
|
||||
|
||||
btn.GetComponentInChildren<Text>().text = "Send";
|
||||
statusText.text = "Ready to send";
|
||||
|
||||
var sendProgress = btn.GetComponentInChildren<Slider>();
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
|
||||
streamText.text = $"Stream: {stream.name}\nId: {stream.id}";
|
||||
|
||||
|
||||
btn.onClick.AddListener(() =>
|
||||
{
|
||||
var objs = SelectionManager.selectedObjects.Select(s => s.gameObject).ToImmutableHashSet();
|
||||
|
||||
if (!objs.Any())
|
||||
{
|
||||
statusText.text = $"No objects selected";
|
||||
return;
|
||||
}
|
||||
|
||||
MakeButtonsInteractable(false);
|
||||
|
||||
statusText.text = "Sending...";
|
||||
try
|
||||
{
|
||||
sender.Send(stream.id, objs,
|
||||
onProgressAction: (dict) =>
|
||||
{
|
||||
//Run on a dispatcher as GOs can only be retrieved on the main thread
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
var val = dict.Values.Average() / objs.Count;
|
||||
sendProgress.gameObject.SetActive(true);
|
||||
sendProgress.value = (float) val;
|
||||
});
|
||||
},
|
||||
onDataSentAction: (objectId) =>
|
||||
{
|
||||
Debug.Log($"Send operation completed, object id: {objectId}", this);
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Sent {objectId}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
});
|
||||
},
|
||||
onErrorAction: (message, e) =>
|
||||
{
|
||||
Debug.LogError("Send operation Failed!", this);
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Error {message}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
Debug.LogError(e, this);
|
||||
});
|
||||
});
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
//populate branches
|
||||
branchesDropdown.options.Clear();
|
||||
List<Branch> branches = _receiver.Stream.branches.items;
|
||||
branches.Reverse();
|
||||
foreach (Branch branch in branches)
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Error {e.Message}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
Debug.LogError(e, this);
|
||||
branchesDropdown.options.Add(
|
||||
new Dropdown.OptionData(branch.name.Replace(' ', '\u00A0'))
|
||||
);
|
||||
}
|
||||
|
||||
//trigger ui refresh, maybe there's a better method
|
||||
branchesDropdown.value = -1;
|
||||
branchesDropdown.value = 0;
|
||||
branchesDropdown.onValueChanged.AddListener(index =>
|
||||
{
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
_receiver.BranchName = branches[index].name;
|
||||
});
|
||||
|
||||
_receiver.Init(
|
||||
stream.id,
|
||||
autoReceive,
|
||||
onDataReceivedAction: (go) =>
|
||||
{
|
||||
statusText.text = $"Received {go.name}";
|
||||
MakeButtonsInteractable(true);
|
||||
receiveProgress.value = 0;
|
||||
receiveProgress.gameObject.SetActive(false);
|
||||
|
||||
AddComponents(go);
|
||||
},
|
||||
onTotalChildrenCountKnown: (count) =>
|
||||
{
|
||||
_receiver.TotalChildrenCount = count;
|
||||
},
|
||||
onProgressAction: (dict) =>
|
||||
{
|
||||
//Run on a dispatcher as GOs can only be retrieved on the main thread
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
var val = dict.Values.Average() / _receiver.TotalChildrenCount;
|
||||
receiveProgress.gameObject.SetActive(true);
|
||||
receiveProgress.value = (float)val;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
streamText.text = $"Stream: {stream.name}\nId: {stream.id} - Auto: {autoReceive}";
|
||||
btn.onClick.AddListener(() =>
|
||||
{
|
||||
statusText.text = "Receiving...";
|
||||
MakeButtonsInteractable(false);
|
||||
_receiver.Receive();
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void MakeButtonsInteractable(bool interactable)
|
||||
{
|
||||
var selectables = gameObject.transform.GetComponentsInChildren<UnityEngine.UI.Selectable>();
|
||||
foreach (var selectable in selectables)
|
||||
{
|
||||
selectable.interactable = interactable;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitRemove()
|
||||
{
|
||||
var close = gameObject.transform.Find("Close").GetComponentInChildren<Button>();
|
||||
|
||||
close.onClick.AddListener(() =>
|
||||
{
|
||||
//remove received geometry
|
||||
if (receiver != null)
|
||||
/// <summary>
|
||||
/// Recursively adds custom components to all children of a GameObject
|
||||
/// </summary>
|
||||
/// <param name="go"></param>
|
||||
private void AddComponents(GameObject go)
|
||||
{
|
||||
Destroy(receiver.ReceivedData);
|
||||
for (var i = 0; i < go.transform.childCount; i++)
|
||||
{
|
||||
var child = go.transform.GetChild(i);
|
||||
|
||||
if (child.childCount > 0)
|
||||
{
|
||||
AddComponents(child.gameObject);
|
||||
}
|
||||
|
||||
child.gameObject.AddComponent<Selectable>();
|
||||
|
||||
//Add extra Components
|
||||
//var rigidbody = child.gameObject.AddComponent<Rigidbody>();
|
||||
//rigidbody.mass = 10;
|
||||
}
|
||||
}
|
||||
|
||||
//update ui
|
||||
GameObject.Find("_SpeckleExamples").GetComponent<SpeckleExamples>().RemoveStreamPrefab(gameObject);
|
||||
public void InitSender(Stream stream)
|
||||
{
|
||||
gameObject.name = $"sender-{stream.id}-{Guid.NewGuid().ToString()}";
|
||||
InitRemove();
|
||||
|
||||
//kill it
|
||||
Destroy(gameObject);
|
||||
});
|
||||
var sender = gameObject.AddComponent<Sender>();
|
||||
var btn = gameObject.transform.Find("Btn").GetComponentInChildren<Button>();
|
||||
|
||||
var streamText = gameObject.transform.Find("StreamText").GetComponentInChildren<Text>();
|
||||
var statusText = gameObject.transform.Find("StatusText").GetComponentInChildren<Text>();
|
||||
|
||||
btn.GetComponentInChildren<Text>().text = "Send";
|
||||
statusText.text = "Ready to send";
|
||||
|
||||
var sendProgress = btn.GetComponentInChildren<Slider>();
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
|
||||
streamText.text = $"Stream: {stream.name}\nId: {stream.id}";
|
||||
|
||||
btn.onClick.AddListener(() =>
|
||||
{
|
||||
var objs = SelectionManager.selectedObjects
|
||||
.Select(s => s.gameObject)
|
||||
.ToImmutableHashSet();
|
||||
|
||||
if (!objs.Any())
|
||||
{
|
||||
statusText.text = $"No objects selected";
|
||||
return;
|
||||
}
|
||||
|
||||
MakeButtonsInteractable(false);
|
||||
|
||||
statusText.text = "Sending...";
|
||||
try
|
||||
{
|
||||
sender.Send(
|
||||
stream.id,
|
||||
objs,
|
||||
onProgressAction: (dict) =>
|
||||
{
|
||||
//Run on a dispatcher as GOs can only be retrieved on the main thread
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
var val = dict.Values.Average() / objs.Count;
|
||||
sendProgress.gameObject.SetActive(true);
|
||||
sendProgress.value = (float)val;
|
||||
});
|
||||
},
|
||||
onDataSentAction: (objectId) =>
|
||||
{
|
||||
Debug.Log($"Send operation completed, object id: {objectId}", this);
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Sent {objectId}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
});
|
||||
},
|
||||
onErrorAction: (message, e) =>
|
||||
{
|
||||
Debug.LogError("Send operation Failed!", this);
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Error {message}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
Debug.LogError(e, this);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
MakeButtonsInteractable(true);
|
||||
statusText.text = $"Error {e.Message}";
|
||||
sendProgress.gameObject.SetActive(false); //hide
|
||||
Debug.LogError(e, this);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void MakeButtonsInteractable(bool interactable)
|
||||
{
|
||||
var selectables =
|
||||
gameObject.transform.GetComponentsInChildren<UnityEngine.UI.Selectable>();
|
||||
foreach (var selectable in selectables)
|
||||
{
|
||||
selectable.interactable = interactable;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitRemove()
|
||||
{
|
||||
var close = gameObject.transform.Find("Close").GetComponentInChildren<Button>();
|
||||
|
||||
close.onClick.AddListener(() =>
|
||||
{
|
||||
//remove received geometry
|
||||
if (_receiver != null)
|
||||
{
|
||||
Destroy(_receiver.ReceivedData);
|
||||
}
|
||||
|
||||
//update ui
|
||||
GameObject
|
||||
.Find("_SpeckleExamples")
|
||||
.GetComponent<SpeckleExamples>()
|
||||
.RemoveStreamPrefab(gameObject);
|
||||
|
||||
//kill it
|
||||
Destroy(gameObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+113
-114
@@ -1,132 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Speckle.Core.Credentials;
|
||||
using System.Linq;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using Stream = Speckle.Core.Api.Stream;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
public class SpeckleExamples : MonoBehaviour
|
||||
{
|
||||
public Text SelectStreamText;
|
||||
public Text DetailsStreamText;
|
||||
public Dropdown StreamSelectionDropdown;
|
||||
public Button AddReceiverBtn;
|
||||
public Toggle AutoReceiveToggle;
|
||||
public Button AddSenderBtn;
|
||||
public GameObject StreamPanel;
|
||||
public Canvas StreamsCanvas;
|
||||
|
||||
private List<Stream> StreamList = null;
|
||||
private Stream SelectedStream = null;
|
||||
private List<GameObject> StreamPanels = new List<GameObject>();
|
||||
|
||||
|
||||
async void Start()
|
||||
[Obsolete]
|
||||
public class SpeckleExamples : MonoBehaviour
|
||||
{
|
||||
if (SelectStreamText == null || StreamSelectionDropdown == null)
|
||||
{
|
||||
Debug.Log("Please set all input fields on _SpeckleExamples");
|
||||
return;
|
||||
}
|
||||
public Text SelectStreamText;
|
||||
public Text DetailsStreamText;
|
||||
public Dropdown StreamSelectionDropdown;
|
||||
public Button AddReceiverBtn;
|
||||
public Toggle AutoReceiveToggle;
|
||||
public Button AddSenderBtn;
|
||||
public GameObject StreamPanel;
|
||||
public Canvas StreamsCanvas;
|
||||
|
||||
var defaultAccount = AccountManager.GetDefaultAccount();
|
||||
if (defaultAccount == null)
|
||||
{
|
||||
Debug.Log("Please set a default account in SpeckleManager");
|
||||
return;
|
||||
}
|
||||
private List<Stream> StreamList = null;
|
||||
private Stream SelectedStream = null;
|
||||
private List<GameObject> StreamPanels = new List<GameObject>();
|
||||
|
||||
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
|
||||
async void Start()
|
||||
{
|
||||
if (SelectStreamText == null || StreamSelectionDropdown == null)
|
||||
{
|
||||
Debug.Log("Please set all input fields on _SpeckleExamples");
|
||||
return;
|
||||
}
|
||||
|
||||
StreamList = await Streams.List(30);
|
||||
if (!StreamList.Any())
|
||||
{
|
||||
Debug.Log("There are no streams in your account, please create one online.");
|
||||
return;
|
||||
}
|
||||
var defaultAccount = AccountManager.GetDefaultAccount();
|
||||
if (defaultAccount == null)
|
||||
{
|
||||
Debug.Log("Please set a default account in SpeckleManager");
|
||||
return;
|
||||
}
|
||||
|
||||
StreamSelectionDropdown.options.Clear();
|
||||
foreach (var stream in StreamList)
|
||||
{
|
||||
StreamSelectionDropdown.options.Add(new Dropdown.OptionData(stream.name + " - " + stream.id));
|
||||
}
|
||||
SelectStreamText.text = $"Select a stream on {defaultAccount.serverInfo.name}:";
|
||||
|
||||
StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged);
|
||||
//trigger ui refresh, maybe there's a better method
|
||||
StreamSelectionDropdown.value = -1;
|
||||
StreamSelectionDropdown.value = 0;
|
||||
StreamList = await Streams.List(30);
|
||||
if (!StreamList.Any())
|
||||
{
|
||||
Debug.Log("There are no streams in your account, please create one online.");
|
||||
return;
|
||||
}
|
||||
|
||||
StreamSelectionDropdown.options.Clear();
|
||||
foreach (var stream in StreamList)
|
||||
{
|
||||
StreamSelectionDropdown.options.Add(
|
||||
new Dropdown.OptionData(stream.name + " - " + stream.id)
|
||||
);
|
||||
}
|
||||
|
||||
AddReceiverBtn.onClick.AddListener(AddReceiver);
|
||||
AddSenderBtn.onClick.AddListener(AddSender);
|
||||
StreamSelectionDropdown.onValueChanged.AddListener(StreamSelectionChanged);
|
||||
//trigger ui refresh, maybe there's a better method
|
||||
StreamSelectionDropdown.value = -1;
|
||||
StreamSelectionDropdown.value = 0;
|
||||
|
||||
AddReceiverBtn.onClick.AddListener(AddReceiver);
|
||||
AddSenderBtn.onClick.AddListener(AddSender);
|
||||
}
|
||||
|
||||
public void StreamSelectionChanged(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
SelectedStream = StreamList[index];
|
||||
DetailsStreamText.text =
|
||||
$"Description: {SelectedStream.description}\n"
|
||||
+ $"Link sharing on: {SelectedStream.isPublic}\n"
|
||||
+ $"Role: {SelectedStream.role}\n"
|
||||
+ $"Collaborators: {SelectedStream.collaborators.Count}\n"
|
||||
+ $"Id: {SelectedStream.id}";
|
||||
}
|
||||
|
||||
// Shows how to create a new Receiver from code and then pull data manually
|
||||
// Created receivers are added to a List of Receivers for future use
|
||||
private async void AddReceiver()
|
||||
{
|
||||
var autoReceive = AutoReceiveToggle.isOn;
|
||||
var stream = await Streams.Get(SelectedStream.id, 30);
|
||||
|
||||
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0), Quaternion.identity);
|
||||
|
||||
//set position
|
||||
streamPrefab.transform.SetParent(StreamsCanvas.transform);
|
||||
var rt = streamPrefab.GetComponent<RectTransform>();
|
||||
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
|
||||
|
||||
streamPrefab.AddComponent<InteractionLogic>().InitReceiver(stream, autoReceive);
|
||||
|
||||
StreamPanels.Add(streamPrefab);
|
||||
}
|
||||
|
||||
private async void AddSender()
|
||||
{
|
||||
var stream = await Streams.Get(SelectedStream.id, 10);
|
||||
|
||||
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0), Quaternion.identity);
|
||||
|
||||
streamPrefab.transform.SetParent(StreamsCanvas.transform);
|
||||
var rt = streamPrefab.GetComponent<RectTransform>();
|
||||
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
|
||||
|
||||
streamPrefab.AddComponent<InteractionLogic>().InitSender(stream);
|
||||
|
||||
StreamPanels.Add(streamPrefab);
|
||||
}
|
||||
|
||||
public void RemoveStreamPrefab(GameObject streamPrefab)
|
||||
{
|
||||
StreamPanels.RemoveAt(StreamPanels.FindIndex(x => x.name == streamPrefab.name));
|
||||
ReorderStreamPrefabs();
|
||||
}
|
||||
|
||||
private void ReorderStreamPrefabs()
|
||||
{
|
||||
for (var i = 0; i < StreamPanels.Count; i++)
|
||||
{
|
||||
var rt = StreamPanels[i].GetComponent<RectTransform>();
|
||||
rt.anchoredPosition = new Vector3(-10, -110 - i * 110, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StreamSelectionChanged(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
SelectedStream = StreamList[index];
|
||||
DetailsStreamText.text =
|
||||
$"Description: {SelectedStream.description}\n" +
|
||||
$"Link sharing on: {SelectedStream.isPublic}\n" +
|
||||
$"Role: {SelectedStream.role}\n" +
|
||||
$"Collaborators: {SelectedStream.collaborators.Count}\n" +
|
||||
$"Id: {SelectedStream.id}";
|
||||
}
|
||||
|
||||
// Shows how to create a new Receiver from code and then pull data manually
|
||||
// Created receivers are added to a List of Receivers for future use
|
||||
private async void AddReceiver()
|
||||
{
|
||||
var autoReceive = AutoReceiveToggle.isOn;
|
||||
var stream = await Streams.Get(SelectedStream.id, 30);
|
||||
|
||||
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0),
|
||||
Quaternion.identity);
|
||||
|
||||
//set position
|
||||
streamPrefab.transform.SetParent(StreamsCanvas.transform);
|
||||
var rt = streamPrefab.GetComponent<RectTransform>();
|
||||
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
|
||||
|
||||
streamPrefab.AddComponent<InteractionLogic>().InitReceiver(stream, autoReceive);
|
||||
|
||||
StreamPanels.Add(streamPrefab);
|
||||
}
|
||||
|
||||
private async void AddSender()
|
||||
{
|
||||
var stream = await Streams.Get(SelectedStream.id, 10);
|
||||
|
||||
var streamPrefab = Instantiate(StreamPanel, new Vector3(0, 0, 0),
|
||||
Quaternion.identity);
|
||||
|
||||
streamPrefab.transform.SetParent(StreamsCanvas.transform);
|
||||
var rt = streamPrefab.GetComponent<RectTransform>();
|
||||
rt.anchoredPosition = new Vector3(-10, -110 - StreamPanels.Count * 110, 0);
|
||||
|
||||
streamPrefab.AddComponent<InteractionLogic>().InitSender(stream);
|
||||
|
||||
StreamPanels.Add(streamPrefab);
|
||||
}
|
||||
|
||||
public void RemoveStreamPrefab(GameObject streamPrefab)
|
||||
{
|
||||
StreamPanels.RemoveAt(StreamPanels.FindIndex(x => x.name == streamPrefab.name));
|
||||
ReorderStreamPrefabs();
|
||||
}
|
||||
|
||||
private void ReorderStreamPrefabs()
|
||||
{
|
||||
for (var i = 0; i < StreamPanels.Count; i++)
|
||||
{
|
||||
var rt = StreamPanels[i].GetComponent<RectTransform>();
|
||||
rt.anchoredPosition = new Vector3(-10, -110 - i * 110, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+226
-199
@@ -849,6 +849,192 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 160171836}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &161249242
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 161249247}
|
||||
- component: {fileID: 161249246}
|
||||
- component: {fileID: 161249245}
|
||||
- component: {fileID: 161249244}
|
||||
- component: {fileID: 161249243}
|
||||
m_Layer: 0
|
||||
m_Name: Speckle Connector
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 2800000, guid: ee2ed9d8fff3a1d4db5590491978062e, type: 3}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &161249243
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b95e704835cc48444b81e33c978f6f7f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 1485638386691080198
|
||||
<Stream>k__BackingField:
|
||||
rid: 1485638386691080199
|
||||
<Branch>k__BackingField:
|
||||
rid: 1485638386691080200
|
||||
OnBranchSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnSendProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 1485638386691080198
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
- rid: 1485638386691080199
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 1485638386691080198
|
||||
- rid: 1485638386691080200
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
<BranchesLimit>k__BackingField: 100
|
||||
<CommitsLimit>k__BackingField: 0
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 1485638386691080199
|
||||
--- !u!114 &161249244
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 942bf0cb27c5c5045bc4cbb7fc0fad71, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
url:
|
||||
--- !u!114 &161249245
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0bc895f6cb37b674995dc13b79783c55, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 1485638386691080194
|
||||
<Stream>k__BackingField:
|
||||
rid: 1485638386691080195
|
||||
<Branch>k__BackingField:
|
||||
rid: 1485638386691080196
|
||||
<Commit>k__BackingField:
|
||||
rid: 1485638386691080197
|
||||
OnCommitSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnReceiveProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnTotalChildrenCountKnown:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnComplete:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 1485638386691080194
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
- rid: 1485638386691080195
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 1485638386691080194
|
||||
- rid: 1485638386691080196
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
<BranchesLimit>k__BackingField: 100
|
||||
<CommitsLimit>k__BackingField: 25
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 1485638386691080195
|
||||
- rid: 1485638386691080197
|
||||
type: {class: CommitSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedId:
|
||||
<BranchSelection>k__BackingField:
|
||||
rid: 1485638386691080196
|
||||
--- !u!114 &161249246
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ed6cbf9ce4dca0349997d163ec9bce7e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<ConverterInstance>k__BackingField:
|
||||
rid: 1485638386691080193
|
||||
<AssetCache>k__BackingField: {fileID: 1438814813}
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 1485638386691080193
|
||||
type: {class: ConverterUnity, ns: Objects.Converter.Unity, asm: Objects.Converter}
|
||||
data:
|
||||
shouldConvertViews: 0
|
||||
shouldAttachProperties: 1
|
||||
<OpaqueMaterialShader>k__BackingField: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||
<TranslucentMaterialShader>k__BackingField: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
|
||||
<ModelUnits>k__BackingField: m
|
||||
--- !u!4 &161249247
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 161249242}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 9
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &194696812
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -928,165 +1114,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 194696812}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &218987857
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 218987861}
|
||||
- component: {fileID: 218987860}
|
||||
- component: {fileID: 218987859}
|
||||
- component: {fileID: 218987858}
|
||||
m_Layer: 0
|
||||
m_Name: Speckle Connector
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 2800000, guid: ee2ed9d8fff3a1d4db5590491978062e, type: 3}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &218987858
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b95e704835cc48444b81e33c978f6f7f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 5855987529328361546
|
||||
<Stream>k__BackingField:
|
||||
rid: 5855987529328361547
|
||||
<Branch>k__BackingField:
|
||||
rid: 5855987529328361548
|
||||
OnBranchSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnSendProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 5855987529328361546
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
- rid: 5855987529328361547
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 5855987529328361546
|
||||
- rid: 5855987529328361548
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchesLimit>k__BackingField: 30
|
||||
<CommitsLimit>k__BackingField: 0
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 5855987529328361547
|
||||
--- !u!114 &218987859
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0bc895f6cb37b674995dc13b79783c55, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Account>k__BackingField:
|
||||
rid: 5855987529328361542
|
||||
<Stream>k__BackingField:
|
||||
rid: 5855987529328361543
|
||||
<Branch>k__BackingField:
|
||||
rid: 5855987529328361544
|
||||
<Commit>k__BackingField:
|
||||
rid: 5855987529328361545
|
||||
OnCommitSelectionChange:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnReceiveProgressAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnErrorAction:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnTotalChildrenCountKnown:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
OnComplete:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 5855987529328361542
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
- rid: 5855987529328361543
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<StreamsLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 5855987529328361542
|
||||
- rid: 5855987529328361544
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchesLimit>k__BackingField: 30
|
||||
<CommitsLimit>k__BackingField: 15
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 5855987529328361543
|
||||
- rid: 5855987529328361545
|
||||
type: {class: CommitSelection, ns: Speckle.ConnectorUnity.Wrappers.Selection, asm: Speckle.ConnectorUnity.Wrappers}
|
||||
data:
|
||||
selectedIndex: 0
|
||||
<BranchSelection>k__BackingField:
|
||||
rid: 5855987529328361544
|
||||
--- !u!114 &218987860
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ed6cbf9ce4dca0349997d163ec9bce7e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<AssetCache>k__BackingField: {fileID: 1710028308}
|
||||
--- !u!4 &218987861
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 218987857}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 9
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &234733581
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1135,6 +1162,18 @@ Transform:
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 4
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &300223686
|
||||
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 &310693430
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1616,18 +1655,6 @@ Rigidbody:
|
||||
m_Interpolate: 0
|
||||
m_Constraints: 0
|
||||
m_CollisionDetection: 0
|
||||
--- !u!114 &540478226
|
||||
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 &641375517
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -2616,6 +2643,19 @@ Rigidbody:
|
||||
m_Interpolate: 0
|
||||
m_Constraints: 0
|
||||
m_CollisionDetection: 0
|
||||
--- !u!114 &1242741158
|
||||
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: 2a4a29c776298714c88f406ad39c6095, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
matchByName: 1
|
||||
--- !u!1 &1279250012
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3019,6 +3059,21 @@ MonoBehaviour:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_IsOn: 1
|
||||
--- !u!114 &1438814813
|
||||
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: 1242741158}
|
||||
- {fileID: 300223686}
|
||||
--- !u!1 &1464556211
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3599,21 +3654,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1707872729}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1710028308
|
||||
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: 1771830985}
|
||||
- {fileID: 540478226}
|
||||
--- !u!1 &1729237655
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3726,19 +3766,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1762991479}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1771830985
|
||||
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: 2a4a29c776298714c88f406ad39c6095, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
matchByName: 1
|
||||
--- !u!1 &1885647142
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Editor",
|
||||
"rootNamespace": "",
|
||||
"name": "EditorTests",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Tests",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Objects.Utils;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
|
||||
public class PerformanceTest
|
||||
{
|
||||
private static readonly string[] dataSource = new[]
|
||||
{
|
||||
"https://latest.speckle.dev/streams/24c3741255/commits/0925840e09"
|
||||
};
|
||||
|
||||
|
||||
//This method is much faster
|
||||
[Test, TestCaseSource(nameof(dataSource))]
|
||||
public void Receive_GetAwaiterResult(string stream)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
Helpers.Receive(stream).GetAwaiter().GetResult();
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
}
|
||||
|
||||
|
||||
//This method takes around 46 seconds to complete
|
||||
[Test, TestCaseSource(nameof(dataSource))]
|
||||
public void Receive_TaskRunAsync(string stream)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Helpers.Receive(stream);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
}
|
||||
|
||||
// [UnityTest, TestCaseSource(nameof(dataSource))]
|
||||
// public IEnumerable Receive_Coroutine(string stream)
|
||||
// {
|
||||
// var stopwatch = Stopwatch.StartNew();
|
||||
//
|
||||
// Task t = Helpers.Receive(stream);
|
||||
// t.Start();
|
||||
//
|
||||
// yield return new WaitUntil(() => !t.IsCompleted || stopwatch.ElapsedMilliseconds >= 100000);
|
||||
//
|
||||
// stopwatch.Stop();
|
||||
// Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
// Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
// Assert.True(t.IsCompletedSuccessfully);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
//This method takes around 46 seconds to complete
|
||||
[Test]
|
||||
public void TestTriangulate()
|
||||
{
|
||||
|
||||
|
||||
Base b = Task.Run(async () =>
|
||||
{
|
||||
return await Helpers.Receive("https://speckle.xyz/streams/4a8fd0c6b6/commits/067bf723b1");
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
|
||||
foreach (Base child in b.Traverse(b => b is Objects.Geometry.Mesh))
|
||||
{
|
||||
if(child is not Objects.Geometry.Mesh m) continue;
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
m.TriangulateMesh();
|
||||
|
||||
Console.WriteLine($"took {stopwatch.ElapsedMilliseconds:ms} to triangulate {child.id}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a4f4baa829261d438b740c7d3028756
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
public abstract class ComponentTest<T> where T : Component
|
||||
{
|
||||
protected T sut;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
GameObject go = new();
|
||||
sut = go.AddComponent<T>();
|
||||
Assert.That(sut, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e2fe277dd9c47ad998138dcdbb024ae
|
||||
timeCreated: 1686757093
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.ConnectorUnity.Wrappers;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
[TestFixture, TestOf(typeof(RecursiveConverter))]
|
||||
public class ConvertToNativeTests : ComponentTest<RecursiveConverter>
|
||||
{
|
||||
private static IEnumerable<string> TestCases()
|
||||
{
|
||||
yield return @"https://latest.speckle.dev/streams/c1faab5c62/commits/704984e22d";
|
||||
}
|
||||
|
||||
private static Base Receive(string stream)
|
||||
{
|
||||
return Task.Run(async () => await Helpers.Receive(stream)).Result;
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
public void ToNative_Passes(string stream)
|
||||
{
|
||||
Base testCase = Receive(stream);
|
||||
var results = sut.RecursivelyConvertToNative_Sync(testCase, null);
|
||||
Assert.That(results, Has.Count.GreaterThan(0));
|
||||
Assert.That(results, HasSomeComponent<Transform>());
|
||||
Assert.That(results, HasSomeComponent<MeshRenderer>());
|
||||
Assert.That(results, HasSomeComponent<SpeckleProperties>());
|
||||
}
|
||||
|
||||
private static Constraint HasSomeComponent<T>()
|
||||
where T : Component
|
||||
{
|
||||
return Has.Some.Matches<ConversionResult>(x =>
|
||||
{
|
||||
return x.WasSuccessful(out var success, out _) && success.GetComponent<T>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d9b0fc7baaf51a4a8e2bcefad8bd7b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "PlayModeTests",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Tests",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
||||
"Speckle.ConnectorUnity.Components",
|
||||
"Utils",
|
||||
"Speckle.ConnectorUnity.Wrappers"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll",
|
||||
"SpeckleCore2.dll",
|
||||
"Objects.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79301723eb79d2745ab1e1a9360f6f2d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
[TestFixture, TestOf(typeof(SpeckleReceiver))]
|
||||
public sealed class SpeckleReceiverTests : ComponentTest<SpeckleReceiver>
|
||||
{
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAsync_Succeeds()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
var task = new Utils.Utils.WaitForTask<Base>(async () => await sut.ReceiveAsync(default));
|
||||
yield return task;
|
||||
Base myBase = task.Result;
|
||||
Assert.That(myBase, Is.Not.Null);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Async_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
sut.ReceiveAndConvert_Async(expectedParent);
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Routine_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
yield return sut.ReceiveAndConvert_Routine(expectedParent);
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1756c50dd28a4e341a70866daa68a8d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"com.unity.2d.sprite": "1.0.0",
|
||||
"com.unity.collab-proxy": "1.17.6",
|
||||
"com.unity.ide.rider": "3.0.16",
|
||||
"com.unity.ide.visualstudio": "2.0.16",
|
||||
"com.unity.collab-proxy": "2.0.7",
|
||||
"com.unity.ide.rider": "3.0.24",
|
||||
"com.unity.ide.visualstudio": "2.0.18",
|
||||
"com.unity.ide.vscode": "1.2.5",
|
||||
"com.unity.test-framework": "1.1.31",
|
||||
"com.unity.test-framework": "1.1.33",
|
||||
"com.unity.textmeshpro": "3.0.6",
|
||||
"com.unity.timeline": "1.6.4",
|
||||
"com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.2",
|
||||
"com.unity.timeline": "1.6.5",
|
||||
"com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.4",
|
||||
"com.unity.ugui": "1.0.0",
|
||||
"com.unity.modules.ai": "1.0.0",
|
||||
"com.unity.modules.androidjni": "1.0.0",
|
||||
|
||||
+12
-32
@@ -7,12 +7,10 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"com.unity.collab-proxy": {
|
||||
"version": "1.17.6",
|
||||
"version": "2.0.7",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.services.core": "1.0.1"
|
||||
},
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ext.nunit": {
|
||||
@@ -23,7 +21,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.rider": {
|
||||
"version": "3.0.16",
|
||||
"version": "3.0.24",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -32,7 +30,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.ide.visualstudio": {
|
||||
"version": "2.0.16",
|
||||
"version": "2.0.18",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -47,42 +45,24 @@
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.nuget.newtonsoft-json": {
|
||||
"version": "3.0.2",
|
||||
"depth": 2,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.services.core": {
|
||||
"version": "1.6.0",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.modules.unitywebrequest": "1.0.0",
|
||||
"com.unity.nuget.newtonsoft-json": "3.0.2",
|
||||
"com.unity.modules.androidjni": "1.0.0"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.sysroot": {
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.5",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.sysroot.linux-x86_64": {
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.4",
|
||||
"depth": 1,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.sysroot": "2.0.3"
|
||||
"com.unity.sysroot": "2.0.5"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.test-framework": {
|
||||
"version": "1.1.31",
|
||||
"version": "1.1.33",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -102,7 +82,7 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.timeline": {
|
||||
"version": "1.6.4",
|
||||
"version": "1.6.5",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
@@ -114,12 +94,12 @@
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
"com.unity.toolchain.win-x86_64-linux-x86_64": {
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.4",
|
||||
"depth": 0,
|
||||
"source": "registry",
|
||||
"dependencies": {
|
||||
"com.unity.sysroot": "2.0.3",
|
||||
"com.unity.sysroot.linux-x86_64": "2.0.2"
|
||||
"com.unity.sysroot": "2.0.5",
|
||||
"com.unity.sysroot.linux-x86_64": "2.0.4"
|
||||
},
|
||||
"url": "https://packages.unity.com"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#nullable enable
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components.Editor
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(ReceiveFromURL))]
|
||||
public class ReceiveFromURLEditor: UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
var speckleReceiver = (ReceiveFromURL)target;
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
bool isBusy = speckleReceiver.IsBusy();
|
||||
|
||||
GUI.enabled = !isBusy;
|
||||
if (GUILayout.Button("Receive!"))
|
||||
{
|
||||
speckleReceiver.Receive();
|
||||
}
|
||||
GUI.enabled = isBusy;
|
||||
if (GUILayout.Button("Cancel!"))
|
||||
{
|
||||
speckleReceiver.Cancel();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f1a8151dcdc40ac8662b102c2f25277
|
||||
timeCreated: 1687985346
|
||||
+284
-122
@@ -1,28 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
namespace Speckle.ConnectorUnity.Components.Editor
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(SpeckleReceiver))]
|
||||
public class SpeckleReceiverEditor : UnityEditor.Editor
|
||||
{
|
||||
private static bool generateAssets = false;
|
||||
private bool foldOutStatus = true;
|
||||
private Texture2D? previewImage;
|
||||
private SerializedProperty _accountSelection;
|
||||
private SerializedProperty _streamSelection;
|
||||
private SerializedProperty _branchSelection;
|
||||
private SerializedProperty _commitSelection;
|
||||
|
||||
#nullable enable
|
||||
private static bool _generateAssets;
|
||||
private bool _foldOutStatus = true;
|
||||
private Texture2D? _previewImage;
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
//Selection
|
||||
EditorGUILayout.PropertyField(_accountSelection);
|
||||
EditorGUILayout.PropertyField(_streamSelection, new GUIContent("Project"));
|
||||
EditorGUILayout.PropertyField(_branchSelection, new GUIContent("Model"));
|
||||
EditorGUILayout.PropertyField(_commitSelection, new GUIContent("Version"));
|
||||
|
||||
//Preview image
|
||||
{
|
||||
_foldOutStatus = EditorGUILayout.Foldout(_foldOutStatus, "Preview Image");
|
||||
if (_foldOutStatus)
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f);
|
||||
if (_previewImage != null)
|
||||
GUI.DrawTexture(rect, _previewImage);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Draw events in a collapsed region
|
||||
|
||||
//Receive settings
|
||||
{
|
||||
bool prev = GUI.enabled;
|
||||
GUI.enabled = !speckleReceiver.IsReceiving;
|
||||
//Receive button
|
||||
bool userRequestedReceive = GUILayout.Button("Receive!");
|
||||
|
||||
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", _generateAssets);
|
||||
if (_generateAssets != selection)
|
||||
{
|
||||
_generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
GUI.enabled = prev;
|
||||
|
||||
if (speckleReceiver.IsReceiving)
|
||||
{
|
||||
var value = Progress.globalProgress; //NOTE: this may include non-speckle items...
|
||||
var percent = Math.Max(0, Mathf.Ceil(value * 100));
|
||||
var rect = EditorGUILayout.GetControlRect(
|
||||
false,
|
||||
EditorGUIUtility.singleLineHeight
|
||||
);
|
||||
EditorGUI.ProgressBar(rect, value, $"{percent}%");
|
||||
}
|
||||
else if (userRequestedReceive)
|
||||
{
|
||||
var id = Progress.Start(
|
||||
"Receiving Speckle Model",
|
||||
"Fetching data from Speckle",
|
||||
Progress.Options.Sticky
|
||||
);
|
||||
Progress.ShowDetails();
|
||||
|
||||
try
|
||||
{
|
||||
await ReceiveSelection(id).ConfigureAwait(true);
|
||||
Progress.Finish(id);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Progress.Finish(id, Progress.Status.Canceled);
|
||||
Debug.Log($"Receive operation cancelled\n{ex}", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Progress.Finish(id, Progress.Status.Failed);
|
||||
Debug.LogError($"Receive operation failed {ex}", this);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
Init();
|
||||
|
||||
_accountSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleReceiver.Account)}>k__BackingField"
|
||||
);
|
||||
_streamSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleReceiver.Stream)}>k__BackingField"
|
||||
);
|
||||
_branchSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleReceiver.Branch)}>k__BackingField"
|
||||
);
|
||||
_commitSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleReceiver.Commit)}>k__BackingField"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Init();
|
||||
@@ -30,7 +127,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
private void Init()
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
UpdatePreviewImage();
|
||||
speckleReceiver.OnCommitSelectionChange.AddListener(_ => UpdatePreviewImage());
|
||||
UpdateGenerateAssets();
|
||||
@@ -38,162 +135,224 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
private void UpdatePreviewImage()
|
||||
{
|
||||
previewImage = null;
|
||||
((SpeckleReceiver)target).GetPreviewImage(t => previewImage = t);
|
||||
_previewImage = null;
|
||||
((SpeckleReceiver)target).GetPreviewImage(t => _previewImage = t);
|
||||
}
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
|
||||
private async Task ReceiveSelection(int progressId)
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
//Preview image
|
||||
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image");
|
||||
if (foldOutStatus)
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
bool shouldCancel = false;
|
||||
|
||||
Progress.RegisterCancelCallback(
|
||||
progressId,
|
||||
() =>
|
||||
{
|
||||
speckleReceiver.Cancel();
|
||||
shouldCancel = true;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f/4f);
|
||||
if(previewImage != null) GUI.DrawTexture(rect, previewImage);
|
||||
var token = speckleReceiver.BeginOperation();
|
||||
commitObject = await Task.Run(
|
||||
async () => await ReceiveCommit(progressId).ConfigureAwait(false),
|
||||
token
|
||||
)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleReceiver.FinishOperation();
|
||||
}
|
||||
|
||||
|
||||
//Receive button
|
||||
bool receive = GUILayout.Button("Receive!");
|
||||
int childrenConverted = 0;
|
||||
int childrenFailed = 0;
|
||||
|
||||
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", generateAssets);
|
||||
if (generateAssets != selection)
|
||||
{
|
||||
generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
|
||||
|
||||
//TODO: Draw events in a collapsed region
|
||||
int totalChildren = (int)Math.Min(commitObject.totalChildrenCount, int.MaxValue);
|
||||
float totalChildrenFloat = commitObject.totalChildrenCount;
|
||||
|
||||
|
||||
|
||||
if (receive)
|
||||
var convertProgress = Progress.Start(
|
||||
"Converting To Native",
|
||||
"Preparing...",
|
||||
Progress.Options.Indefinite | Progress.Options.Sticky,
|
||||
progressId
|
||||
);
|
||||
|
||||
bool BeforeConvert(TraversalContext context)
|
||||
{
|
||||
await ReceiveAndConvert(speckleReceiver);
|
||||
Base b = context.Current;
|
||||
|
||||
//NOTE: progress wont reach 100% because not all objects are convertable
|
||||
float progress = (childrenConverted + childrenFailed) / totalChildrenFloat;
|
||||
|
||||
if (shouldCancel)
|
||||
return false;
|
||||
|
||||
shouldCancel = EditorUtility.DisplayCancelableProgressBar(
|
||||
"Converting To Native...",
|
||||
$"{b.speckle_type} - {b.id}",
|
||||
progress
|
||||
);
|
||||
|
||||
return !shouldCancel;
|
||||
}
|
||||
|
||||
foreach (
|
||||
var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable(
|
||||
commitObject,
|
||||
speckleReceiver.transform,
|
||||
BeforeConvert
|
||||
)
|
||||
)
|
||||
{
|
||||
Base speckleObject = conversionResult.SpeckleObject;
|
||||
if (conversionResult.WasSuccessful(out _, out var ex))
|
||||
{
|
||||
childrenConverted++;
|
||||
}
|
||||
else
|
||||
{
|
||||
childrenFailed++;
|
||||
Debug.LogWarning(
|
||||
$"Failed to convert Speckle object of type {speckleObject.speckle_type}\n{ex}",
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
Progress.Report(
|
||||
progressId,
|
||||
childrenConverted + childrenFailed,
|
||||
totalChildren,
|
||||
"Receiving objects"
|
||||
);
|
||||
|
||||
if (shouldCancel)
|
||||
break;
|
||||
}
|
||||
|
||||
var resultString = $"{childrenConverted} {nameof(GameObject)}s created";
|
||||
if (childrenFailed != 0)
|
||||
resultString += $", {childrenFailed} objects failed to convert!";
|
||||
|
||||
Debug.Log(
|
||||
shouldCancel
|
||||
? $"Stopped converting to native: The operation has been cancelled - {resultString}\n "
|
||||
: $"Finished converting to native.\n{resultString}",
|
||||
speckleReceiver
|
||||
);
|
||||
|
||||
Progress.Finish(convertProgress);
|
||||
|
||||
if (shouldCancel)
|
||||
throw new OperationCanceledException(
|
||||
"Conversion operation canceled through editor dialogue"
|
||||
);
|
||||
}
|
||||
|
||||
private void UpdateGenerateAssets()
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver) target;
|
||||
speckleReceiver.Converter.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets);
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
speckleReceiver.Converter.AssetCache.nativeCaches =
|
||||
NativeCacheFactory.GetDefaultNativeCacheSetup(_generateAssets);
|
||||
}
|
||||
|
||||
public async Task<GameObject?> ReceiveAndConvert(SpeckleReceiver speckleReceiver)
|
||||
private async Task<Base> ReceiveCommit(int progressId)
|
||||
{
|
||||
speckleReceiver.CancellationTokenSource?.Cancel();
|
||||
if (!speckleReceiver.GetSelection(out Client? client, out _, out Commit? commit, out string? error))
|
||||
{
|
||||
Debug.LogWarning($"Not ready to receive: {error}", speckleReceiver);
|
||||
return null;
|
||||
}
|
||||
|
||||
Base? commitObject = await ReceiveCommit(speckleReceiver, client.ServerUrl);
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
if (commitObject == null) return null;
|
||||
|
||||
var gameObject = Convert(speckleReceiver, commitObject, commit.id);
|
||||
Debug.Log($"Successfully received and converted commit: {commit.id}", target);
|
||||
return gameObject;
|
||||
}
|
||||
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
|
||||
|
||||
private GameObject Convert(SpeckleReceiver receiver, Base commitObject, string name)
|
||||
{
|
||||
//Convert Speckle Objects
|
||||
int childrenConverted = 0;
|
||||
float totalChildren = commitObject.totalChildrenCount;
|
||||
|
||||
void BeforeConvertCallback(Base b)
|
||||
{
|
||||
//TODO: this is an incorrect way of measuring progress, as totalChildren != total convertable children
|
||||
float progress = childrenConverted++ / totalChildren;
|
||||
|
||||
EditorUtility.DisplayProgressBar("Converting To Native...",
|
||||
$"{b.speckle_type} - {b.id}",
|
||||
progress);
|
||||
}
|
||||
|
||||
var go = receiver.ConvertToNativeWithCategories(commitObject,
|
||||
name, BeforeConvertCallback);
|
||||
go.transform.SetParent(receiver.transform);
|
||||
return go;
|
||||
}
|
||||
|
||||
private async Task<Base?> ReceiveCommit(SpeckleReceiver speckleReceiver, string serverLogName)
|
||||
{
|
||||
string message = $"Receiving data from {serverLogName}...";
|
||||
EditorUtility.DisplayProgressBar(message, "", 0);
|
||||
int transport = Progress.Start(
|
||||
$"Downloading data from {serverLogName}",
|
||||
"Waiting...",
|
||||
Progress.Options.Sticky,
|
||||
progressId
|
||||
);
|
||||
int deserialize = Progress.Start(
|
||||
"Deserializing data",
|
||||
"Waiting...",
|
||||
Progress.Options.Sticky,
|
||||
progressId
|
||||
);
|
||||
Progress.SetPriority(transport, Progress.Priority.High);
|
||||
|
||||
var totalObjectCount = 1;
|
||||
void OnTotalChildrenKnown(int count)
|
||||
{
|
||||
totalObjectCount = count;
|
||||
};
|
||||
|
||||
Progress.Report(progressId, 0, totalObjectCount, "Receiving objects");
|
||||
}
|
||||
|
||||
void OnProgress(ConcurrentDictionary<string, int> dict)
|
||||
{
|
||||
var currentProgress = dict.Values.Average();
|
||||
var progress = (float) currentProgress / totalObjectCount;
|
||||
EditorApplication.delayCall += () =>
|
||||
bool r = dict.TryGetValue("RemoteTransport", out int rtProgress);
|
||||
bool l = dict.TryGetValue("SQLite", out int ltProgress);
|
||||
if (r || l)
|
||||
{
|
||||
bool shouldCancel = EditorUtility.DisplayCancelableProgressBar(message,
|
||||
$"{currentProgress}/{totalObjectCount}",
|
||||
progress);
|
||||
|
||||
if (shouldCancel)
|
||||
{
|
||||
CancelReceive();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void OnError(string message, Exception e)
|
||||
{
|
||||
if (e is not OperationCanceledException)
|
||||
{
|
||||
Debug.LogError($"Receive failed: {message}\n{e}", speckleReceiver);
|
||||
var fetched = (rtProgress + ltProgress);
|
||||
Progress.Report(
|
||||
transport,
|
||||
fetched,
|
||||
totalObjectCount,
|
||||
$"{fetched}/{totalObjectCount}"
|
||||
);
|
||||
}
|
||||
CancelReceive();
|
||||
};
|
||||
|
||||
Base? commitObject = null;
|
||||
if (dict.TryGetValue("DS", out int tsProgress))
|
||||
{
|
||||
tsProgress--; //The root object isn't included, so we add an extra 1
|
||||
Progress.Report(
|
||||
deserialize,
|
||||
tsProgress,
|
||||
totalObjectCount,
|
||||
$"{tsProgress}/{totalObjectCount}"
|
||||
);
|
||||
Progress.Report(progressId, tsProgress, totalObjectCount);
|
||||
}
|
||||
}
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
speckleReceiver.OnTotalChildrenCountKnown.AddListener(OnTotalChildrenKnown);
|
||||
speckleReceiver.OnReceiveProgressAction.AddListener(OnProgress);
|
||||
speckleReceiver.OnErrorAction.AddListener(OnError);
|
||||
commitObject = await speckleReceiver.ReceiveAsync();
|
||||
if (commitObject == null)
|
||||
{
|
||||
Debug.LogWarning($"Receive warning: Receive operation returned null", speckleReceiver);
|
||||
}
|
||||
commitObject = await speckleReceiver
|
||||
.ReceiveAsync(speckleReceiver.CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Progress.Finish(transport);
|
||||
Progress.Finish(deserialize);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Progress.Finish(transport, Progress.Status.Canceled);
|
||||
Progress.Finish(deserialize, Progress.Status.Canceled);
|
||||
throw;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Progress.Finish(transport, Progress.Status.Failed);
|
||||
Progress.Finish(deserialize, Progress.Status.Failed);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleReceiver.OnTotalChildrenCountKnown.RemoveListener(OnTotalChildrenKnown);
|
||||
speckleReceiver.OnReceiveProgressAction.RemoveListener(OnProgress);
|
||||
speckleReceiver.OnErrorAction.RemoveListener(OnError);
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
return commitObject;
|
||||
}
|
||||
|
||||
private void CancelReceive()
|
||||
{
|
||||
((SpeckleReceiver)target).CancellationTokenSource?.Cancel();
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Speckle/Speckle Connector", false, 10)]
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand) {
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand)
|
||||
{
|
||||
// Create a custom game object
|
||||
GameObject go = new GameObject("Speckle Connector");
|
||||
GameObject go = new("Speckle Connector");
|
||||
// Ensure it gets reparented if this was a context click (otherwise does nothing)
|
||||
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
|
||||
// Register the creation in the undo system
|
||||
@@ -202,10 +361,13 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
go.AddComponent<RecursiveConverter>();
|
||||
go.AddComponent<SpeckleReceiver>();
|
||||
go.AddComponent<ReceiveFromURL>();
|
||||
go.AddComponent<SpeckleSender>();
|
||||
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/systems.speckle.speckle-unity/Editor/Gizmos/logo128.png");
|
||||
var icon = AssetDatabase.LoadAssetAtPath<Texture2D>(
|
||||
"Packages/systems.speckle.speckle-unity/Editor/Gizmos/logo128.png"
|
||||
);
|
||||
EditorGUIUtility.SetIconForObject(go, icon);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@@ -9,39 +10,62 @@ using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Component = UnityEngine.Component;
|
||||
|
||||
#nullable enable
|
||||
namespace Speckle.ConnectorUnity.Components.Editor
|
||||
{
|
||||
|
||||
public enum SelectionFilter
|
||||
{
|
||||
[Tooltip("Convert all children of this GameObject")]
|
||||
Children,
|
||||
[Tooltip("Convert GameObjects currently selected in the hierarchy (consider padlocking this inspector)")]
|
||||
|
||||
[Tooltip(
|
||||
"Convert GameObjects currently selected in the hierarchy (consider padlocking this inspector)"
|
||||
)]
|
||||
Selection,
|
||||
|
||||
[InspectorName("All (excl. disabled)")]
|
||||
[Tooltip("Convert all GameObjects (excluding disabled) in the active scene")]
|
||||
Enabled,
|
||||
|
||||
[Tooltip("Convert all GameObjects (including disabled) in the active scene")]
|
||||
[InspectorName("All (incl. disabled)")]
|
||||
All,
|
||||
}
|
||||
|
||||
|
||||
[CustomEditor(typeof(SpeckleSender))]
|
||||
[CanEditMultipleObjects]
|
||||
public class SpeckleSendEditor : UnityEditor.Editor
|
||||
{
|
||||
|
||||
private SelectionFilter selectedFilter = SelectionFilter.Children;
|
||||
|
||||
private SerializedProperty _accountSelection;
|
||||
private SerializedProperty _streamSelection;
|
||||
private SerializedProperty _branchSelection;
|
||||
|
||||
#nullable enable
|
||||
private SelectionFilter _selectedFilter = SelectionFilter.Children;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
_accountSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleSender.Account)}>k__BackingField"
|
||||
);
|
||||
_streamSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleSender.Stream)}>k__BackingField"
|
||||
);
|
||||
_branchSelection = serializedObject.FindProperty(
|
||||
$"<{nameof(SpeckleSender.Branch)}>k__BackingField"
|
||||
);
|
||||
}
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
//Draw events in a collapsed region
|
||||
DrawDefaultInspector();
|
||||
//Selection
|
||||
EditorGUILayout.PropertyField(_accountSelection);
|
||||
EditorGUILayout.PropertyField(_streamSelection, new GUIContent("Project"));
|
||||
EditorGUILayout.PropertyField(_branchSelection, new GUIContent("Model"));
|
||||
|
||||
bool shouldSend = GUILayout.Button("Send!");
|
||||
selectedFilter = (SelectionFilter)EditorGUILayout.EnumPopup("Selection", selectedFilter);
|
||||
|
||||
_selectedFilter = (SelectionFilter)
|
||||
EditorGUILayout.EnumPopup("Selection", _selectedFilter);
|
||||
|
||||
if (shouldSend)
|
||||
{
|
||||
await ConvertAndSend();
|
||||
@@ -50,30 +74,34 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
public async Task<string?> ConvertAndSend()
|
||||
{
|
||||
var speckleSender = (SpeckleSender) target;
|
||||
var speckleSender = (SpeckleSender)target;
|
||||
|
||||
if (!speckleSender.GetSelection(out _, out _, out _, out string? error))
|
||||
{
|
||||
Debug.LogWarning($"Not ready to send: {error}", speckleSender);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
RecursiveConverter converter = speckleSender.Converter;
|
||||
Base data = selectedFilter switch
|
||||
Base data = _selectedFilter switch
|
||||
{
|
||||
SelectionFilter.All => ConvertAll(converter),
|
||||
SelectionFilter.Enabled => ConvertEnabled(converter),
|
||||
SelectionFilter.Children => ConvertChildren(converter),
|
||||
SelectionFilter.Selection => ConvertSelection(converter),
|
||||
_ => throw new InvalidEnumArgumentException(nameof(selectedFilter), (int) selectedFilter, selectedFilter.GetType()),
|
||||
_
|
||||
=> throw new InvalidEnumArgumentException(
|
||||
nameof(_selectedFilter),
|
||||
(int)_selectedFilter,
|
||||
_selectedFilter.GetType()
|
||||
),
|
||||
};
|
||||
|
||||
//TODO onError action?
|
||||
|
||||
if (data["@objects"] is IList l && l.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"Nothing to send", speckleSender);
|
||||
Debug.LogWarning("Nothing to send", speckleSender);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return await speckleSender.SendDataAsync(data, true);
|
||||
}
|
||||
@@ -81,37 +109,36 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
private Base ConvertChildren(RecursiveConverter converter)
|
||||
{
|
||||
return converter.RecursivelyConvertToSpeckle(
|
||||
new []{((Component)target).gameObject},
|
||||
_ => true);
|
||||
new[] { ((Component)target).gameObject },
|
||||
_ => true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private Base ConvertSelection(RecursiveConverter converter)
|
||||
{
|
||||
ISet<GameObject> selection = Selection.GetFiltered<GameObject>(SelectionMode.Deep).ToImmutableHashSet();
|
||||
ISet<GameObject> selection = Selection
|
||||
.GetFiltered<GameObject>(SelectionMode.Deep)
|
||||
.ToImmutableHashSet();
|
||||
return converter.RecursivelyConvertToSpeckle(
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
go => selection.Contains(go));
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
go => selection.Contains(go)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private Base ConvertAll(RecursiveConverter converter)
|
||||
{
|
||||
return converter.RecursivelyConvertToSpeckle(
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
_ => true);
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
_ => true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private Base ConvertEnabled(RecursiveConverter converter)
|
||||
{
|
||||
return converter.RecursivelyConvertToSpeckle(
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
go => go.activeInHierarchy);
|
||||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
go => go.activeInHierarchy
|
||||
);
|
||||
}
|
||||
|
||||
private void CancelSend()
|
||||
{
|
||||
((SpeckleReceiver)target).CancellationTokenSource?.Cancel();
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
public class StreamManagerEditor : UnityEditor.Editor
|
||||
{
|
||||
private bool _foldOutAccount;
|
||||
private int _totalChildrenCount = 0;
|
||||
private int _totalChildrenCount;
|
||||
private StreamManager _streamManager;
|
||||
|
||||
private static bool generateAssets;
|
||||
private static bool _generateAssets;
|
||||
|
||||
public int StreamsLimit { get; set; } = 30;
|
||||
public int BranchesLimit { get; set; } = 30;
|
||||
public int BranchesLimit { get; set; } = 75;
|
||||
public int CommitsLimit { get; set; } = 25;
|
||||
|
||||
private int SelectedAccountIndex
|
||||
@@ -98,7 +98,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
private List<Branch> Branches
|
||||
{
|
||||
get => _streamManager.Branches;
|
||||
|
||||
set => _streamManager.Branches = value;
|
||||
}
|
||||
|
||||
@@ -142,7 +141,11 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
SelectedStream = Streams[i];
|
||||
|
||||
EditorUtility.DisplayProgressBar("Loading stream details...", "", 0);
|
||||
Branches = await Client.StreamGetBranches(SelectedStream.id, BranchesLimit, CommitsLimit);
|
||||
Branches = await Client.StreamGetBranches(
|
||||
SelectedStream.id,
|
||||
BranchesLimit,
|
||||
CommitsLimit
|
||||
);
|
||||
if (Branches.Any())
|
||||
{
|
||||
SelectedBranchIndex = 0;
|
||||
@@ -155,7 +158,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
|
||||
private async Task Receive()
|
||||
{
|
||||
var transport = new ServerTransport(SelectedAccount, SelectedStream.id);
|
||||
@@ -163,45 +165,82 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
try
|
||||
{
|
||||
Commit selectedCommit = Branches[SelectedBranchIndex].commits.items[
|
||||
SelectedCommitIndex
|
||||
];
|
||||
// Receive Speckle Objects
|
||||
var @base = await Operations.Receive(
|
||||
Branches[SelectedBranchIndex].commits.items[SelectedCommitIndex].referencedObject,
|
||||
selectedCommit.referencedObject,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: dict =>
|
||||
{
|
||||
UnityEditor.EditorApplication.delayCall += () =>
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
EditorUtility.DisplayProgressBar($"Receiving data from {transport.BaseUri}...", "",
|
||||
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount));
|
||||
EditorUtility.DisplayProgressBar(
|
||||
$"Receiving data from {transport.BaseUri}...",
|
||||
"",
|
||||
Convert.ToSingle(dict.Values.Average() / _totalChildrenCount)
|
||||
);
|
||||
};
|
||||
},
|
||||
onTotalChildrenCountKnown: count => { _totalChildrenCount = count; }
|
||||
onTotalChildrenCountKnown: count =>
|
||||
{
|
||||
_totalChildrenCount = count;
|
||||
}
|
||||
);
|
||||
if (@base is null)
|
||||
throw new InvalidOperationException("Received object was null");
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
Analytics.TrackEvent(SelectedAccount, Analytics.Events.Receive);
|
||||
Analytics.TrackEvent(
|
||||
SelectedAccount,
|
||||
Analytics.Events.Receive,
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "mode", nameof(StreamManagerEditor) },
|
||||
{
|
||||
"sourceHostApp",
|
||||
HostApplications
|
||||
.GetHostAppFromString(selectedCommit.sourceApplication)
|
||||
.Slug
|
||||
},
|
||||
{ "sourceHostAppVersion", selectedCommit.sourceApplication ?? "" },
|
||||
{ "hostPlatform", Application.platform.ToString() },
|
||||
{ "isMultiplayer", selectedCommit.authorId != SelectedAccount.userInfo.id },
|
||||
}
|
||||
);
|
||||
|
||||
//Convert Speckle Objects
|
||||
int childrenConverted = 0;
|
||||
|
||||
void BeforeConvertCallback(Base b)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Converting To Native...", $"{b.speckle_type} - {b.id}",
|
||||
Convert.ToSingle(childrenConverted++ / _totalChildrenCount));
|
||||
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);
|
||||
_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 {HostApplications.Unity.Name} Editor",
|
||||
sourceApplication = HostApplications.Unity.Name
|
||||
});
|
||||
await Client.CommitReceived(
|
||||
new CommitReceivedInput
|
||||
{
|
||||
streamId = SelectedStream.id,
|
||||
commitId = Branches[SelectedBranchIndex].commits.items[
|
||||
SelectedCommitIndex
|
||||
].id,
|
||||
message = $"received commit from {HostApplications.Unity.Name} Editor",
|
||||
sourceApplication = HostApplications.Unity.Name
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -215,8 +254,7 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
_streamManager = (StreamManager) target;
|
||||
|
||||
_streamManager = (StreamManager)target;
|
||||
|
||||
#region Account GUI
|
||||
|
||||
@@ -226,12 +264,15 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
SelectedAccountIndex = EditorGUILayout.Popup("Accounts", SelectedAccountIndex,
|
||||
SelectedAccountIndex = EditorGUILayout.Popup(
|
||||
"Accounts",
|
||||
SelectedAccountIndex,
|
||||
Accounts.Select(x => x.userInfo.email + " | " + x.serverInfo.name).ToArray(),
|
||||
GUILayout.ExpandWidth(true), GUILayout.Height(20));
|
||||
GUILayout.ExpandWidth(true),
|
||||
GUILayout.Height(20)
|
||||
);
|
||||
|
||||
if (OldSelectedAccountIndex != SelectedAccountIndex)
|
||||
{
|
||||
@@ -247,26 +288,37 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
|
||||
#region Speckle Account Info
|
||||
|
||||
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(_foldOutAccount, "Account Info");
|
||||
_foldOutAccount = EditorGUILayout.BeginFoldoutHeaderGroup(
|
||||
_foldOutAccount,
|
||||
"Account Info"
|
||||
);
|
||||
|
||||
if (_foldOutAccount)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
|
||||
EditorGUILayout.TextField("Name", SelectedAccount.userInfo.name,
|
||||
EditorGUILayout.TextField(
|
||||
"Name",
|
||||
SelectedAccount.userInfo.name,
|
||||
GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true));
|
||||
GUILayout.ExpandWidth(true)
|
||||
);
|
||||
|
||||
EditorGUILayout.TextField("Server", SelectedAccount.serverInfo.name,
|
||||
EditorGUILayout.TextField(
|
||||
"Server",
|
||||
SelectedAccount.serverInfo.name,
|
||||
GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true));
|
||||
GUILayout.ExpandWidth(true)
|
||||
);
|
||||
|
||||
EditorGUILayout.TextField("URL", SelectedAccount.serverInfo.url,
|
||||
EditorGUILayout.TextField(
|
||||
"URL",
|
||||
SelectedAccount.serverInfo.url,
|
||||
GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true));
|
||||
GUILayout.ExpandWidth(true)
|
||||
);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
@@ -284,9 +336,13 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
SelectedStreamIndex = EditorGUILayout.Popup("Streams",
|
||||
SelectedStreamIndex, Streams.Select(x => x.name).ToArray(), GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true));
|
||||
SelectedStreamIndex = EditorGUILayout.Popup(
|
||||
"Streams",
|
||||
SelectedStreamIndex,
|
||||
Streams.Select(x => x.name).ToArray(),
|
||||
GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true)
|
||||
);
|
||||
|
||||
if (OldSelectedStreamIndex != SelectedStreamIndex)
|
||||
{
|
||||
@@ -311,23 +367,29 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
SelectedBranchIndex = EditorGUILayout.Popup("Branches",
|
||||
SelectedBranchIndex, Branches.Select(x => x.name).ToArray(), GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true));
|
||||
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 = EditorGUILayout.Popup(
|
||||
"Commits",
|
||||
SelectedCommitIndex,
|
||||
Branches[SelectedBranchIndex].commits.items.Select(x => $"{x.message} - {x.id}").ToArray(),
|
||||
Branches[SelectedBranchIndex].commits.items
|
||||
.Select(x => $"{x.message} - {x.id}")
|
||||
.ToArray(),
|
||||
GUILayout.Height(20),
|
||||
GUILayout.ExpandWidth(true));
|
||||
GUILayout.ExpandWidth(true)
|
||||
);
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
@@ -339,12 +401,12 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
|
||||
GUILayout.Label("Generate assets");
|
||||
GUILayout.FlexibleSpace();
|
||||
bool selection = GUILayout.Toggle(generateAssets, "");
|
||||
if (generateAssets != selection)
|
||||
bool selection = GUILayout.Toggle(_generateAssets, "");
|
||||
if (_generateAssets != selection)
|
||||
{
|
||||
|
||||
generateAssets = selection;
|
||||
_streamManager.RC.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets);
|
||||
_generateAssets = selection;
|
||||
_streamManager.RC.AssetCache.nativeCaches =
|
||||
NativeCacheFactory.GetDefaultNativeCacheSetup(_generateAssets);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
@@ -364,4 +426,4 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@@ -18,23 +16,28 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
|
||||
public const string DefaultPath = "Assets/Resources";
|
||||
public string path = DefaultPath;
|
||||
|
||||
private MemoryNativeCache readCache;
|
||||
|
||||
private MemoryNativeCache _readCache;
|
||||
|
||||
#nullable enable
|
||||
|
||||
|
||||
void Awake()
|
||||
{
|
||||
readCache = CreateInstance<MemoryNativeCache>();
|
||||
_readCache = CreateInstance<MemoryNativeCache>();
|
||||
}
|
||||
|
||||
public override bool TryGetObject<T>(Base speckleObject, [NotNullWhen(true)] out T? nativeObject) where T : class
|
||||
|
||||
public override bool TryGetObject<T>(
|
||||
Base speckleObject,
|
||||
[NotNullWhen(true)] out T? nativeObject
|
||||
)
|
||||
where T : class
|
||||
{
|
||||
if(readCache.TryGetObject(speckleObject, out nativeObject))
|
||||
if (_readCache.TryGetObject(speckleObject, out nativeObject))
|
||||
return true;
|
||||
|
||||
|
||||
Type nativeType = typeof(T);
|
||||
if (!GetAssetPath(nativeType, speckleObject, out string? assetPath)) return false;
|
||||
|
||||
if (!GetAssetPath(nativeType, speckleObject, out string? assetPath))
|
||||
return false;
|
||||
|
||||
nativeObject = AssetDatabase.LoadAssetAtPath<T>(assetPath);
|
||||
return nativeObject != null;
|
||||
}
|
||||
@@ -43,66 +46,75 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
|
||||
{
|
||||
return WriteObject(speckleObject, nativeObject);
|
||||
}
|
||||
|
||||
|
||||
private bool WriteObject(Base speckleObject, Object nativeObject)
|
||||
{
|
||||
Type nativeType = nativeObject.GetType();
|
||||
if (!GetAssetPath(nativeType, speckleObject, out string? assetPath)) return false;
|
||||
|
||||
if (!GetAssetPath(nativeType, speckleObject, out string? assetPath))
|
||||
return false;
|
||||
|
||||
// Special case for GameObjects, we want to use PrefabUtility
|
||||
if (nativeObject is GameObject go)
|
||||
{
|
||||
var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(go, assetPath, InteractionMode.AutomatedAction);
|
||||
return readCache.TrySaveObject(speckleObject, prefab);
|
||||
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: {assetPath}", this);
|
||||
Debug.LogWarning(
|
||||
$"Failed to write asset as one already existed at path: {assetPath}",
|
||||
this
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetDatabase.CreateAsset(nativeObject, $"{assetPath}");
|
||||
return readCache.TrySaveObject(speckleObject, nativeObject);
|
||||
}
|
||||
|
||||
|
||||
public override void BeginWrite()
|
||||
{
|
||||
base.BeginWrite();
|
||||
//AssetDatabase.StartAssetEditing();
|
||||
return _readCache.TrySaveObject(speckleObject, nativeObject);
|
||||
}
|
||||
|
||||
public override void FinishWrite()
|
||||
{
|
||||
if (!isWriting) return;
|
||||
if (!isWriting)
|
||||
return;
|
||||
//AssetDatabase.StopAssetEditing();
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
if (readCache != null) readCache.LoadedAssets.Clear();
|
||||
|
||||
if (_readCache != null)
|
||||
_readCache.LoadedAssets.Clear();
|
||||
|
||||
base.FinishWrite();
|
||||
}
|
||||
|
||||
private bool GetAssetPath(Type nativeType, Base speckleObject, [NotNullWhen(true)] out string? outPath)
|
||||
|
||||
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;
|
||||
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))
|
||||
return true;
|
||||
|
||||
|
||||
var info = Directory.CreateDirectory(directoryPath);
|
||||
AssetDatabase.Refresh();
|
||||
return info.Exists;
|
||||
@@ -111,16 +123,20 @@ namespace Speckle.ConnectorUnity.NativeCache.Editor
|
||||
[ContextMenu("SetPath")]
|
||||
public void SetPath_Menu()
|
||||
{
|
||||
var selection = EditorUtility.OpenFolderPanel("Set Assets Path", "Assets/Resources", "");
|
||||
|
||||
if (selection.StartsWith(Application.dataPath)) {
|
||||
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}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+95
-59
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using UnityEditor;
|
||||
@@ -13,8 +14,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
public sealed class AccountSelectionDrawer : OptionSelectionDrawer<Account>
|
||||
{
|
||||
protected override bool DisplayRefresh => true;
|
||||
protected override string FormatOption(Account o) => $"{o.userInfo.email} | {o.serverInfo.name}";
|
||||
|
||||
|
||||
protected override string FormatOption(Account o) =>
|
||||
$"{o.userInfo.email} | {o.serverInfo.name}";
|
||||
|
||||
public AccountSelectionDrawer()
|
||||
{
|
||||
details = new (string, Func<Account, string>)[]
|
||||
@@ -29,74 +32,78 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[CustomPropertyDrawer(typeof(StreamSelection))]
|
||||
public sealed class StreamSelectionDrawer : OptionSelectionDrawer<Stream>
|
||||
{
|
||||
protected override bool DisplayRefresh => true;
|
||||
|
||||
protected override string FormatOption(Stream o) => $"{o.name}";
|
||||
|
||||
public StreamSelectionDrawer()
|
||||
{
|
||||
properties = new []{$"<{nameof(StreamSelection.StreamsLimit)}>k__BackingField"};
|
||||
|
||||
properties = new[] { $"<{nameof(StreamSelection.StreamsLimit)}>k__BackingField" };
|
||||
|
||||
details = new (string, Func<Stream, string>)[]
|
||||
{
|
||||
("Stream id", s => s.id),
|
||||
("Project id", s => s.id),
|
||||
("Description", s => s.description),
|
||||
("Is Public", s => s.isPublic.ToString()),
|
||||
("Role", s => s.role),
|
||||
("Created at", s => s.createdAt),
|
||||
("Updated at", s => s.updatedAt),
|
||||
("Created at", s => s.createdAt.ToString(CultureInfo.InvariantCulture)),
|
||||
("Updated at", s => s.updatedAt.ToString(CultureInfo.InvariantCulture)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[CustomPropertyDrawer(typeof(BranchSelection))]
|
||||
public sealed class BranchSelectionDrawer : OptionSelectionDrawer<Branch>
|
||||
{
|
||||
protected override bool DisplayRefresh => true;
|
||||
|
||||
protected override string FormatOption(Branch o) => $"{o.name}";
|
||||
|
||||
public BranchSelectionDrawer()
|
||||
{
|
||||
properties = new []
|
||||
properties = new[]
|
||||
{
|
||||
$"<{nameof(BranchSelection.BranchesLimit)}>k__BackingField",
|
||||
$"<{nameof(BranchSelection.CommitsLimit)}>k__BackingField",
|
||||
};
|
||||
|
||||
|
||||
details = new (string, Func<Branch, string>)[]
|
||||
{
|
||||
("Model Id", s => s.id),
|
||||
("Description", s => s.description),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[CustomPropertyDrawer(typeof(CommitSelection))]
|
||||
public sealed class CommitSelectionDrawer : OptionSelectionDrawer<Commit>
|
||||
{
|
||||
protected override string FormatOption(Commit o) => $"{o.message} - {o.id}";
|
||||
|
||||
|
||||
public CommitSelectionDrawer()
|
||||
{
|
||||
details = new (string, Func<Commit, string>)[]
|
||||
{
|
||||
("Commit Id", s => s.id),
|
||||
("Version Id", s => s.id),
|
||||
("Author Name", s => s.authorName),
|
||||
("Created At", s => s.createdAt),
|
||||
("Created At", s => s.createdAt.ToString(CultureInfo.InvariantCulture)),
|
||||
("Source Application", s => s.sourceApplication),
|
||||
("Reference Object Id", s => s.referencedObject),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer where TOption : class
|
||||
|
||||
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer
|
||||
where TOption : class
|
||||
{
|
||||
private const float RefreshButtonWidthScale = 0.2f;
|
||||
private const float PrefixIndentation = 100f;
|
||||
protected readonly float DetailsTextHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
protected readonly float DetailsTextHeight =
|
||||
EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
protected virtual bool DisplayRefresh => false;
|
||||
protected abstract string FormatOption(TOption o);
|
||||
@@ -105,10 +112,10 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
protected string[] properties = { };
|
||||
|
||||
protected (string, Func<TOption, string>)[] details = { };
|
||||
|
||||
private string[] GetFormattedOptions(TOption[] options)
|
||||
|
||||
private string[] GetFormattedOptions(IReadOnlyList<TOption> options)
|
||||
{
|
||||
int optionsCount = options.Length;
|
||||
int optionsCount = options.Count;
|
||||
string[] choices = new string[optionsCount];
|
||||
for (int i = 0; i < optionsCount; i++)
|
||||
{
|
||||
@@ -118,7 +125,12 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
return choices;
|
||||
}
|
||||
|
||||
protected virtual void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, TOption? selection)
|
||||
protected virtual void OnGUIDetails(
|
||||
Rect position,
|
||||
SerializedProperty property,
|
||||
GUIContent label,
|
||||
TOption? selection
|
||||
)
|
||||
{
|
||||
position.height = DetailsTextHeight;
|
||||
|
||||
@@ -142,80 +154,106 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
EditorGUI.EndDisabledGroup();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
var t = (OptionSelection<TOption>)fieldInfo.GetValue(property.serializedObject.targetObject);
|
||||
var t =
|
||||
(OptionSelection<TOption>)
|
||||
fieldInfo.GetValue(property.serializedObject.targetObject);
|
||||
|
||||
var selectionRect = position;
|
||||
var selectionRect = position;
|
||||
selectionRect.x += PrefixIndentation + 5;
|
||||
selectionRect.width -= PrefixIndentation + 5;
|
||||
|
||||
|
||||
TOption? selectedOption = t.Selected;
|
||||
|
||||
|
||||
// Options selection
|
||||
{
|
||||
|
||||
var popupSize = DisplayRefresh
|
||||
? new Rect(selectionRect.x, selectionRect.y, selectionRect.width * (1-RefreshButtonWidthScale), DetailsTextHeight)
|
||||
? new Rect(
|
||||
selectionRect.x,
|
||||
selectionRect.y,
|
||||
selectionRect.width * (1 - RefreshButtonWidthScale),
|
||||
DetailsTextHeight
|
||||
)
|
||||
: selectionRect;
|
||||
|
||||
string selectedChoice = selectedOption != null ? FormatOption(selectedOption) : "";
|
||||
|
||||
|
||||
if (GUI.Button(popupSize, selectedChoice, EditorStyles.popup))
|
||||
{
|
||||
var windowPos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
|
||||
var provider = ScriptableObject.CreateInstance<StringListSearchProvider>();
|
||||
provider.Title = typeof(TOption).Name;
|
||||
provider.listItems = GetFormattedOptions(t.Options);;
|
||||
provider.onSetIndexCallback = o => { t.SelectedIndex = o;};
|
||||
provider.listItems = GetFormattedOptions(t.Options);
|
||||
|
||||
provider.onSetIndexCallback = o =>
|
||||
{
|
||||
t.Selected = t.Options[o];
|
||||
};
|
||||
SearchWindow.Open(new SearchWindowContext(windowPos), provider);
|
||||
}
|
||||
|
||||
// Optional refresh
|
||||
if (DisplayRefresh)
|
||||
{
|
||||
var buttonSize = new Rect(selectionRect.x + popupSize.width , selectionRect.y, selectionRect.width * RefreshButtonWidthScale, DetailsTextHeight);
|
||||
var buttonSize = new Rect(
|
||||
selectionRect.x + popupSize.width,
|
||||
selectionRect.y,
|
||||
selectionRect.width * RefreshButtonWidthScale,
|
||||
DetailsTextHeight
|
||||
);
|
||||
if (GUI.Button(buttonSize, "Refresh"))
|
||||
{
|
||||
EditorApplication.delayCall += t.RefreshOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Collapsable details
|
||||
{
|
||||
{
|
||||
int visiblePropCount = property.isExpanded ? GUIDetailsPropertyCount : 0;
|
||||
var detailsHeight = new Vector2(PrefixIndentation, DetailsTextHeight + visiblePropCount * DetailsTextHeight);
|
||||
var foldoutRect = new Rect(position.position, detailsHeight);
|
||||
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(foldoutRect, property.isExpanded, label);
|
||||
var detailsHeight = new Vector2(
|
||||
PrefixIndentation,
|
||||
DetailsTextHeight + visiblePropCount * DetailsTextHeight
|
||||
);
|
||||
var foldoutRect = new Rect(position.position, detailsHeight);
|
||||
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(
|
||||
foldoutRect,
|
||||
property.isExpanded,
|
||||
label
|
||||
);
|
||||
if (property.isExpanded)
|
||||
{
|
||||
OnGUIDetails(position, property, label, selectedOption);
|
||||
}
|
||||
EditorGUI.EndFoldoutHeaderGroup();
|
||||
}
|
||||
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
//EditorUtility.SetDirty(property.serializedObject.targetObject);
|
||||
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var standardHeight = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
if (!property.isExpanded) return standardHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
var detailsHeight = GUIDetailsPropertyCount * (standardHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
|
||||
return standardHeight + detailsHeight + EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.standardVerticalSpacing;
|
||||
if (!property.isExpanded)
|
||||
return standardHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
|
||||
var detailsHeight =
|
||||
GUIDetailsPropertyCount
|
||||
* (standardHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
|
||||
return standardHeight
|
||||
+ detailsHeight
|
||||
+ EditorGUIUtility.standardVerticalSpacing
|
||||
+ EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
|
||||
public sealed class StringListSearchProvider : ScriptableObject, ISearchWindowProvider
|
||||
{
|
||||
@@ -223,12 +261,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
public string[] listItems;
|
||||
|
||||
public Action<int> onSetIndexCallback;
|
||||
|
||||
|
||||
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
|
||||
{
|
||||
List<SearchTreeEntry> searchList = new(listItems.Length + 1) {new SearchTreeGroupEntry(new GUIContent(Title), 0)};
|
||||
|
||||
for(int i = 0; i < listItems.Length; i++)
|
||||
List<SearchTreeEntry> searchList =
|
||||
new(listItems.Length + 1) { new SearchTreeGroupEntry(new GUIContent(Title)) };
|
||||
|
||||
for (int i = 0; i < listItems.Length; i++)
|
||||
{
|
||||
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(listItems[i]))
|
||||
{
|
||||
@@ -237,18 +276,15 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection.Editor
|
||||
};
|
||||
searchList.Add(entry);
|
||||
}
|
||||
|
||||
|
||||
return searchList;
|
||||
}
|
||||
|
||||
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
|
||||
public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context)
|
||||
{
|
||||
onSetIndexCallback?.Invoke((int)SearchTreeEntry.userData);
|
||||
|
||||
onSetIndexCallback?.Invoke((int)searchTreeEntry.userData);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -18,87 +18,110 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
{
|
||||
private static readonly string[] SpeckleTypeOptionStrings;
|
||||
private static readonly Type[] SpeckleTypeOptions;
|
||||
|
||||
|
||||
private static HashSet<string> ArrayFoldoutState = new();
|
||||
private static bool instancePropFoldoutState = true;
|
||||
private static bool dynamicPropFoldoutState = true;
|
||||
private static bool isEditMode = false;
|
||||
private static bool isEditMode;
|
||||
|
||||
static SpecklePropertiesEditor()
|
||||
{
|
||||
var options = typeof(Mesh).Assembly
|
||||
.GetTypes()
|
||||
.Where(x => x.IsSubclassOf(typeof(Base)) && !x.IsAbstract).ToList();
|
||||
.Where(x => x.IsSubclassOf(typeof(Base)) && !x.IsAbstract)
|
||||
.ToList();
|
||||
|
||||
var strings = options
|
||||
.Where(x => x.FullName != null)
|
||||
.Select(x => x.FullName!.Replace('.', '/'));
|
||||
|
||||
|
||||
SpeckleTypeOptionStrings = strings.Append(nameof(Base)).ToArray();
|
||||
SpeckleTypeOptions = options.Append(typeof(Base)).ToArray();
|
||||
var manualTypes = new[] { typeof(Base), typeof(Collection) };
|
||||
var manualStrings = new[] { nameof(Base), nameof(Collection) };
|
||||
|
||||
//Manually Add `Base`
|
||||
SpeckleTypeOptions = options.Concat(manualTypes).ToArray();
|
||||
SpeckleTypeOptionStrings = strings.Concat(manualStrings).ToArray();
|
||||
|
||||
Debug.Assert(SpeckleTypeOptions.Length == SpeckleTypeOptionStrings.Length);
|
||||
}
|
||||
|
||||
|
||||
private static GUILayoutOption[] propLayoutOptions = { GUILayout.ExpandWidth(true) };
|
||||
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
SpeckleProperties properties = (SpeckleProperties)target;
|
||||
|
||||
//Edit Mode
|
||||
isEditMode = EditorGUILayout.ToggleLeft("Enable Inspector Edit Mode (experimental)", isEditMode);
|
||||
isEditMode = EditorGUILayout.ToggleLeft(
|
||||
"Enable Inspector Edit Mode (experimental)",
|
||||
isEditMode
|
||||
);
|
||||
|
||||
if (isEditMode)
|
||||
{
|
||||
GUILayout.Label(
|
||||
"Modifying properties through the inspector is experimental and can lead to invalid objects, proceed at your own risk!",
|
||||
EditorStyles.helpBox);
|
||||
EditorStyles.helpBox
|
||||
);
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
GUI.enabled = isEditMode;
|
||||
|
||||
|
||||
// SpeckleType
|
||||
GUILayout.Label("Speckle Type: ", EditorStyles.boldLabel );
|
||||
|
||||
GUILayout.Label("Speckle Type: ", EditorStyles.boldLabel);
|
||||
|
||||
var oldIndex = Array.IndexOf(SpeckleTypeOptions, properties.SpeckleType);
|
||||
var speckleTypeSelectedIndex = EditorGUILayout.Popup(oldIndex, SpeckleTypeOptionStrings);
|
||||
|
||||
if(oldIndex != speckleTypeSelectedIndex && speckleTypeSelectedIndex >= 0)
|
||||
var speckleTypeSelectedIndex = EditorGUILayout.Popup(
|
||||
oldIndex,
|
||||
SpeckleTypeOptionStrings
|
||||
);
|
||||
|
||||
if (oldIndex != speckleTypeSelectedIndex && speckleTypeSelectedIndex >= 0)
|
||||
{
|
||||
properties.SpeckleType = SpeckleTypeOptions[speckleTypeSelectedIndex];
|
||||
}
|
||||
|
||||
|
||||
// Instance Properties
|
||||
var InstancePropertyNames = DynamicBase.GetInstanceMembersNames(properties.SpeckleType);
|
||||
instancePropFoldoutState = EditorGUILayout.Foldout(instancePropFoldoutState, "Instance Properties: ", EditorStyles.foldoutHeader);
|
||||
var instancePropertyNames =
|
||||
(IReadOnlyCollection<string>)
|
||||
DynamicBase.GetInstanceMembersNames(properties.SpeckleType);
|
||||
instancePropFoldoutState = EditorGUILayout.Foldout(
|
||||
instancePropFoldoutState,
|
||||
"Instance Properties: ",
|
||||
EditorStyles.foldoutHeader
|
||||
);
|
||||
if (instancePropFoldoutState)
|
||||
{
|
||||
foreach (var propName in InstancePropertyNames)
|
||||
foreach (var propName in instancePropertyNames)
|
||||
{
|
||||
if (!properties.Data.TryGetValue(propName, out object? existingValue)) continue;
|
||||
|
||||
if (!properties.Data.TryGetValue(propName, out object? existingValue))
|
||||
continue;
|
||||
|
||||
var newValue = CreateField(existingValue, propName, propLayoutOptions);
|
||||
if(newValue != existingValue)
|
||||
if (newValue != existingValue)
|
||||
properties.Data[propName] = newValue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GUILayout.Space(10);
|
||||
dynamicPropFoldoutState = EditorGUILayout.Foldout(dynamicPropFoldoutState, "Dynamic Properties:", EditorStyles.foldoutHeader);
|
||||
dynamicPropFoldoutState = EditorGUILayout.Foldout(
|
||||
dynamicPropFoldoutState,
|
||||
"Dynamic Properties:",
|
||||
EditorStyles.foldoutHeader
|
||||
);
|
||||
if (dynamicPropFoldoutState)
|
||||
{
|
||||
var ignoreSet = InstancePropertyNames.ToImmutableHashSet();
|
||||
var ignoreSet = instancePropertyNames.ToImmutableHashSet();
|
||||
foreach (var kvp in properties.Data)
|
||||
{
|
||||
if (ignoreSet.Contains(kvp.Key)) continue;
|
||||
|
||||
if (ignoreSet.Contains(kvp.Key))
|
||||
continue;
|
||||
|
||||
var existingValue = kvp.Value;
|
||||
var newValue = CreateField(existingValue, kvp.Key, propLayoutOptions);
|
||||
if(newValue != existingValue)
|
||||
if (newValue != existingValue)
|
||||
properties.Data[kvp.Key] = newValue;
|
||||
|
||||
|
||||
GUILayout.Space(10);
|
||||
}
|
||||
}
|
||||
@@ -114,13 +137,18 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
_ => CreateFieldPrimitive(v, propName, options),
|
||||
};
|
||||
|
||||
if (ret != null) return ret;
|
||||
|
||||
EditorGUILayout.TextField(propName, v == null? "NULL" : v.ToString());
|
||||
if (ret != null)
|
||||
return ret;
|
||||
|
||||
EditorGUILayout.TextField(propName, v == null ? "NULL" : v.ToString());
|
||||
return v;
|
||||
}
|
||||
|
||||
private static object? CreateFieldPrimitive(object? v, string propName, params GUILayoutOption[] options)
|
||||
|
||||
private static object? CreateFieldPrimitive(
|
||||
object? v,
|
||||
string propName,
|
||||
params GUILayoutOption[] options
|
||||
)
|
||||
{
|
||||
return v switch
|
||||
{
|
||||
@@ -131,11 +159,19 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
string s => EditorGUILayout.TextField(propName, s, options),
|
||||
bool b => EditorGUILayout.Toggle(propName, b, options),
|
||||
Enum e => EditorGUILayout.EnumPopup(propName, e, options),
|
||||
Point p => PointToVector3(EditorGUILayout.Vector3Field(propName, new Vector3((float)p.x, (float)p.z, (float)p.z), options), p),
|
||||
Point p
|
||||
=> PointToVector3(
|
||||
EditorGUILayout.Vector3Field(
|
||||
propName,
|
||||
new Vector3((float)p.x, (float)p.z, (float)p.z),
|
||||
options
|
||||
),
|
||||
p
|
||||
),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static Point PointToVector3(Vector3 vector, Point p)
|
||||
{
|
||||
p.x = vector.x;
|
||||
@@ -143,10 +179,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
p.z = vector.z;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
private IList ArrayField(string propName, IList list, params GUILayoutOption[] options)
|
||||
{
|
||||
bool isExpanded = EditorGUILayout.Foldout(ArrayFoldoutState.Contains(propName), propName);
|
||||
bool isExpanded = EditorGUILayout.Foldout(
|
||||
ArrayFoldoutState.Contains(propName),
|
||||
propName
|
||||
);
|
||||
if (isExpanded)
|
||||
{
|
||||
ArrayFoldoutState.Add(propName);
|
||||
@@ -154,10 +193,13 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
{
|
||||
object? item = list[i];
|
||||
var r = CreateFieldPrimitive(item, i.ToString(), options);
|
||||
|
||||
|
||||
if (r == null)
|
||||
{
|
||||
EditorGUILayout.TextField(i.ToString(), item == null? "NULL" : item.ToString());
|
||||
EditorGUILayout.TextField(
|
||||
i.ToString(),
|
||||
item == null ? "NULL" : item.ToString()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
//Update list item
|
||||
@@ -171,6 +213,5 @@ namespace Speckle.ConnectorUnity.Wrappers.Editor
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+183
-156
@@ -5,185 +5,212 @@ using Speckle.Core.Logging;
|
||||
using Speckle.Core.Transports;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Sentry;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Kits;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
/// <summary>
|
||||
/// A Speckle Receiver, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions and subscriptions for you
|
||||
/// </summary>
|
||||
[RequireComponent( typeof( RecursiveConverter ) )]
|
||||
public class Receiver : MonoBehaviour
|
||||
{
|
||||
public string StreamId;
|
||||
public string BranchName = "main";
|
||||
public Stream Stream;
|
||||
public int TotalChildrenCount = 0;
|
||||
public GameObject ReceivedData;
|
||||
|
||||
private bool AutoReceive;
|
||||
private bool DeleteOld;
|
||||
private Action<ConcurrentDictionary<string, int>> OnProgressAction;
|
||||
private Action<string, Exception> OnErrorAction;
|
||||
private Action<int> OnTotalChildrenCountKnown;
|
||||
private Action<GameObject> OnDataReceivedAction;
|
||||
|
||||
|
||||
private Client Client { get; set; }
|
||||
|
||||
|
||||
public Receiver()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Receiver manually
|
||||
/// A Speckle Receiver, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions and subscriptions for you
|
||||
/// </summary>
|
||||
/// <param name="streamId">Id of the stream to receive</param>
|
||||
/// <param name="autoReceive">If true, it will automatically receive updates sent to this stream</param>
|
||||
/// <param name="deleteOld">If true, it will delete previously received objects when new one are received</param>
|
||||
/// <param name="account">Account to use, if null the default account will be used</param>
|
||||
/// <param name="onDataReceivedAction">Action to run after new data has been received and converted</param>
|
||||
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
|
||||
/// <param name="onErrorAction">Action to run on error</param>
|
||||
/// <param name="onTotalChildrenCountKnown">Action to run when the TotalChildrenCount is known</param>
|
||||
public void Init(string streamId, bool autoReceive = false, bool deleteOld = true, Account account = null,
|
||||
Action<GameObject> onDataReceivedAction = null, Action<ConcurrentDictionary<string, int>> onProgressAction = null,
|
||||
Action<string, Exception> onErrorAction = null, Action<int> onTotalChildrenCountKnown = null)
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
[Obsolete("See " + nameof(SpeckleReceiver))]
|
||||
public class Receiver : MonoBehaviour
|
||||
{
|
||||
StreamId = streamId;
|
||||
AutoReceive = autoReceive;
|
||||
DeleteOld = deleteOld;
|
||||
OnDataReceivedAction = onDataReceivedAction;
|
||||
OnErrorAction = onErrorAction;
|
||||
OnProgressAction = onProgressAction;
|
||||
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
|
||||
public string StreamId;
|
||||
public string BranchName = "main";
|
||||
public Stream Stream;
|
||||
public int TotalChildrenCount = 0;
|
||||
public GameObject ReceivedData;
|
||||
|
||||
Client = new Client(account ?? AccountManager.GetDefaultAccount());
|
||||
private bool AutoReceive;
|
||||
private bool DeleteOld;
|
||||
private Action<ConcurrentDictionary<string, int>> OnProgressAction;
|
||||
private Action<string, Exception> OnErrorAction;
|
||||
private Action<int> OnTotalChildrenCountKnown;
|
||||
private Action<GameObject> OnDataReceivedAction;
|
||||
|
||||
private Client Client { get; set; }
|
||||
|
||||
if (AutoReceive)
|
||||
{
|
||||
Client.SubscribeCommitCreated(StreamId);
|
||||
Client.OnCommitCreated += Client_OnCommitCreated;
|
||||
}
|
||||
}
|
||||
public Receiver() { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets and converts the data of the last commit on the Stream
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Receive()
|
||||
{
|
||||
if (Client == null || string.IsNullOrEmpty(StreamId))
|
||||
throw new Exception("Receiver has not been initialized. Please call Init().");
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
/// <summary>
|
||||
/// Initializes the Receiver manually
|
||||
/// </summary>
|
||||
/// <param name="streamId">Id of the stream to receive</param>
|
||||
/// <param name="autoReceive">If true, it will automatically receive updates sent to this stream</param>
|
||||
/// <param name="deleteOld">If true, it will delete previously received objects when new one are received</param>
|
||||
/// <param name="account">Account to use, if null the default account will be used</param>
|
||||
/// <param name="onDataReceivedAction">Action to run after new data has been received and converted</param>
|
||||
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
|
||||
/// <param name="onErrorAction">Action to run on error</param>
|
||||
/// <param name="onTotalChildrenCountKnown">Action to run when the TotalChildrenCount is known</param>
|
||||
public void Init(
|
||||
string streamId,
|
||||
bool autoReceive = false,
|
||||
bool deleteOld = true,
|
||||
Account account = null,
|
||||
Action<GameObject> onDataReceivedAction = null,
|
||||
Action<ConcurrentDictionary<string, int>> onProgressAction = null,
|
||||
Action<string, Exception> onErrorAction = null,
|
||||
Action<int> onTotalChildrenCountKnown = null
|
||||
)
|
||||
{
|
||||
var mainBranch = await Client.BranchGet(StreamId, BranchName, 1);
|
||||
if (!mainBranch.commits.items.Any())
|
||||
throw new Exception("This branch has no commits");
|
||||
var commit = mainBranch.commits.items[0];
|
||||
GetAndConvertObject(commit.referencedObject, commit.id);
|
||||
StreamId = streamId;
|
||||
AutoReceive = autoReceive;
|
||||
DeleteOld = deleteOld;
|
||||
OnDataReceivedAction = onDataReceivedAction;
|
||||
OnErrorAction = onErrorAction;
|
||||
OnProgressAction = onProgressAction;
|
||||
OnTotalChildrenCountKnown = onTotalChildrenCountKnown;
|
||||
|
||||
Client = new Client(account ?? AccountManager.GetDefaultAccount());
|
||||
|
||||
if (AutoReceive)
|
||||
{
|
||||
Client.SubscribeCommitCreated(StreamId);
|
||||
Client.OnCommitCreated += Client_OnCommitCreated;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
/// <summary>
|
||||
/// Gets and converts the data of the last commit on the Stream
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Receive()
|
||||
{
|
||||
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
|
||||
if (Client == null || string.IsNullOrEmpty(StreamId))
|
||||
throw new Exception("Receiver has not been initialized. Please call Init().");
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var mainBranch = await Client.BranchGet(StreamId, BranchName, 1);
|
||||
if (!mainBranch.commits.items.Any())
|
||||
throw new Exception("This branch has no commits");
|
||||
var commit = mainBranch.commits.items[0];
|
||||
GetAndConvertObject(
|
||||
commit.referencedObject,
|
||||
commit.id,
|
||||
commit.sourceApplication,
|
||||
commit.authorId
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#region private methods
|
||||
|
||||
#region private methods
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a new commit is created on this stream
|
||||
/// It receives and converts the objects and then executes the user defined _onCommitCreated action.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void Client_OnCommitCreated(object sender, CommitInfo e)
|
||||
{
|
||||
if (e.branchName == BranchName)
|
||||
{
|
||||
Debug.Log("New commit created");
|
||||
GetAndConvertObject(e.objectId, e.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void GetAndConvertObject(string objectId, string commitId)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var transport = new ServerTransport(Client.Account, StreamId);
|
||||
var @base = await Operations.Receive(
|
||||
objectId,
|
||||
remoteTransport: transport,
|
||||
onErrorAction: OnErrorAction,
|
||||
onProgressAction: OnProgressAction,
|
||||
onTotalChildrenCountKnown: OnTotalChildrenCountKnown,
|
||||
disposeTransports: true
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(Client.Account, Analytics.Events.Receive);
|
||||
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
/// <summary>
|
||||
/// Fired when a new commit is created on this stream
|
||||
/// It receives and converts the objects and then executes the user defined _onCommitCreated action.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void Client_OnCommitCreated(object sender, CommitInfo e)
|
||||
{
|
||||
var root = new GameObject()
|
||||
{
|
||||
name = commitId,
|
||||
};
|
||||
if (e.branchName == BranchName)
|
||||
{
|
||||
Debug.Log("New commit created");
|
||||
GetAndConvertObject(e.objectId, e.id, e.sourceApplication, e.authorId);
|
||||
}
|
||||
}
|
||||
|
||||
var rc = GetComponent<RecursiveConverter>();
|
||||
var go = rc.RecursivelyConvertToNative(@base, root.transform);
|
||||
//remove previously received object
|
||||
if (DeleteOld && ReceivedData != null)
|
||||
Destroy(ReceivedData);
|
||||
ReceivedData = root;
|
||||
OnDataReceivedAction?.Invoke(root);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Client.CommitReceived(new CommitReceivedInput
|
||||
private async void GetAndConvertObject(
|
||||
string objectId,
|
||||
string commitId,
|
||||
string sourceApplication,
|
||||
string authorId
|
||||
)
|
||||
{
|
||||
streamId = StreamId,
|
||||
commitId = commitId,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing!
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var transport = new ServerTransport(Client.Account, StreamId);
|
||||
var @base = await Operations.Receive(
|
||||
objectId,
|
||||
remoteTransport: transport,
|
||||
onErrorAction: OnErrorAction,
|
||||
onProgressAction: OnProgressAction,
|
||||
onTotalChildrenCountKnown: OnTotalChildrenCountKnown,
|
||||
disposeTransports: true
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(
|
||||
Client.Account,
|
||||
Analytics.Events.Receive,
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "mode", nameof(Receiver) },
|
||||
{
|
||||
"sourceHostApp",
|
||||
HostApplications.GetHostAppFromString(sourceApplication).Slug
|
||||
},
|
||||
{ "sourceHostAppVersion", sourceApplication ?? "" },
|
||||
{ "hostPlatform", Application.platform.ToString() },
|
||||
{
|
||||
"isMultiplayer",
|
||||
authorId != null && authorId != Client.Account.userInfo.id
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
Dispatcher
|
||||
.Instance()
|
||||
.Enqueue(() =>
|
||||
{
|
||||
var root = new GameObject() { name = commitId, };
|
||||
|
||||
var rc = GetComponent<RecursiveConverter>();
|
||||
var go = rc.RecursivelyConvertToNative(@base, root.transform);
|
||||
//remove previously received object
|
||||
if (DeleteOld && ReceivedData != null)
|
||||
Destroy(ReceivedData);
|
||||
ReceivedData = root;
|
||||
OnDataReceivedAction?.Invoke(root);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SpeckleException(e.Message, e, true, SentryLevel.Error);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Client.CommitReceived(
|
||||
new CommitReceivedInput
|
||||
{
|
||||
streamId = StreamId,
|
||||
commitId = commitId,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(
|
||||
CoreUtils.GetHostAppVersion()
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing!
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Client?.CommitCreatedSubscription?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Client?.CommitCreatedSubscription?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,134 +16,150 @@ using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
/// <summary>
|
||||
/// A Speckle Sender, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions for you
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
|
||||
public class Sender : MonoBehaviour
|
||||
{
|
||||
|
||||
private ServerTransport transport;
|
||||
private RecursiveConverter converter;
|
||||
private CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
#nullable enable
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
converter = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts and sends the data of the last commit on the Stream
|
||||
/// A Speckle Sender, it's a wrapper around a basic Speckle Client
|
||||
/// that handles conversions for you
|
||||
/// </summary>
|
||||
/// <param name="streamId">ID of the stream to send to</param>
|
||||
/// <param name="gameObjects">List of gameObjects to convert and send</param>
|
||||
/// <param name="account">Account to use. If not provided the default account will be used</param>
|
||||
/// <param name="branchName">Name of branch to send to</param>
|
||||
/// <param name="createCommit">When true, will create a commit using the root object</param>
|
||||
/// <param name="onDataSentAction">Action to run after the data has been sent</param>
|
||||
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
|
||||
/// <param name="onErrorAction">Action to run on error</param>
|
||||
/// <exception cref="SpeckleException"></exception>
|
||||
public void Send(string streamId,
|
||||
ISet<GameObject> gameObjects,
|
||||
Account? account = null,
|
||||
string branchName = "main",
|
||||
bool createCommit = true,
|
||||
Action<string>? onDataSentAction = null,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null)
|
||||
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
|
||||
[Obsolete("See " + nameof(SpeckleSender))]
|
||||
public class Sender : MonoBehaviour
|
||||
{
|
||||
try
|
||||
{
|
||||
CancelOperations();
|
||||
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
var client = new Client(account ?? AccountManager.GetDefaultAccount());
|
||||
transport = new ServerTransport(client.Account, streamId);
|
||||
transport.CancellationToken = cancellationTokenSource.Token;
|
||||
|
||||
var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
|
||||
|
||||
var data = converter.RecursivelyConvertToSpeckle(rootObjects,
|
||||
o => gameObjects.Contains(o));
|
||||
|
||||
SendData(transport, data, client, branchName, createCommit, cancellationTokenSource.Token, onDataSentAction, onProgressAction, onErrorAction);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SpeckleException(e.ToString(), e, true, SentryLevel.Error);
|
||||
}
|
||||
}
|
||||
private ServerTransport transport;
|
||||
private RecursiveConverter converter;
|
||||
private CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public static void SendData(ServerTransport remoteTransport,
|
||||
Base data,
|
||||
Client client,
|
||||
string branchName,
|
||||
bool createCommit,
|
||||
CancellationToken cancellationToken,
|
||||
Action<string>? onDataSentAction = null,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null)
|
||||
{
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
|
||||
var res = await Operations.Send(
|
||||
data,
|
||||
cancellationToken: cancellationToken,
|
||||
new List<ITransport>() {remoteTransport},
|
||||
useDefaultCache: true,
|
||||
disposeTransports: true,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Send);
|
||||
|
||||
if (createCommit && !cancellationToken.IsCancellationRequested)
|
||||
private void Awake()
|
||||
{
|
||||
long count = data.GetTotalChildrenCount();
|
||||
|
||||
await client.CommitCreate(cancellationToken,
|
||||
new CommitCreateInput
|
||||
{
|
||||
streamId = remoteTransport.StreamId,
|
||||
branchName = branchName,
|
||||
objectId = res,
|
||||
message = $"Sent {count} objects from Unity",
|
||||
sourceApplication = HostApplications.Unity.Name,
|
||||
totalChildrenCount = (int)count,
|
||||
});
|
||||
converter = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
onDataSentAction?.Invoke(res);
|
||||
}, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Converts and sends the data of the last commit on the Stream
|
||||
/// </summary>
|
||||
/// <param name="streamId">ID of the stream to send to</param>
|
||||
/// <param name="gameObjects">List of gameObjects to convert and send</param>
|
||||
/// <param name="account">Account to use. If not provided the default account will be used</param>
|
||||
/// <param name="branchName">Name of branch to send to</param>
|
||||
/// <param name="createCommit">When true, will create a commit using the root object</param>
|
||||
/// <param name="onDataSentAction">Action to run after the data has been sent</param>
|
||||
/// <param name="onProgressAction">Action to run when there is download/conversion progress</param>
|
||||
/// <param name="onErrorAction">Action to run on error</param>
|
||||
/// <exception cref="SpeckleException"></exception>
|
||||
public void Send(
|
||||
string streamId,
|
||||
ISet<GameObject> gameObjects,
|
||||
Account? account = null,
|
||||
string branchName = "main",
|
||||
bool createCommit = true,
|
||||
Action<string>? onDataSentAction = null,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
CancelOperations();
|
||||
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
var client = new Client(account ?? AccountManager.GetDefaultAccount()!);
|
||||
transport = new ServerTransport(client.Account, streamId);
|
||||
transport.CancellationToken = cancellationTokenSource.Token;
|
||||
|
||||
var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
|
||||
|
||||
var data = converter.RecursivelyConvertToSpeckle(
|
||||
rootObjects,
|
||||
o => gameObjects.Contains(o)
|
||||
);
|
||||
|
||||
SendData(
|
||||
transport,
|
||||
data,
|
||||
client,
|
||||
branchName,
|
||||
createCommit,
|
||||
cancellationTokenSource.Token,
|
||||
onDataSentAction,
|
||||
onProgressAction,
|
||||
onErrorAction
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new SpeckleException(e.ToString(), e, true, SentryLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendData(
|
||||
ServerTransport remoteTransport,
|
||||
Base data,
|
||||
Client client,
|
||||
string branchName,
|
||||
bool createCommit,
|
||||
CancellationToken cancellationToken,
|
||||
Action<string>? onDataSentAction = null,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null
|
||||
)
|
||||
{
|
||||
Task.Run(
|
||||
async () =>
|
||||
{
|
||||
var res = await Operations.Send(
|
||||
data,
|
||||
cancellationToken: cancellationToken,
|
||||
new List<ITransport>() { remoteTransport },
|
||||
useDefaultCache: true,
|
||||
disposeTransports: true,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Send);
|
||||
|
||||
if (createCommit && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
long count = data.GetTotalChildrenCount();
|
||||
|
||||
await client.CommitCreate(
|
||||
cancellationToken,
|
||||
new CommitCreateInput
|
||||
{
|
||||
streamId = remoteTransport.StreamId,
|
||||
branchName = branchName,
|
||||
objectId = res,
|
||||
message = $"Sent {count} objects from Unity",
|
||||
sourceApplication = HostApplications.Unity.Name,
|
||||
totalChildrenCount = (int)count,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onDataSentAction?.Invoke(res);
|
||||
},
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CancelOperations();
|
||||
}
|
||||
|
||||
public void CancelOperations()
|
||||
{
|
||||
cancellationTokenSource?.Cancel();
|
||||
transport?.Dispose();
|
||||
cancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
#region private methods
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CancelOperations();
|
||||
}
|
||||
|
||||
public void CancelOperations()
|
||||
{
|
||||
cancellationTokenSource?.Cancel();
|
||||
transport?.Dispose();
|
||||
cancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
#region private methods
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-10
@@ -29,16 +29,18 @@ namespace Speckle.ConnectorUnity.Components
|
||||
public List<Branch> Branches;
|
||||
|
||||
public RecursiveConverter RC { get; private set; }
|
||||
|
||||
|
||||
#nullable enable
|
||||
private void Awake()
|
||||
{
|
||||
RC = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
|
||||
public GameObject ConvertRecursivelyToNative(Base @base, string rootObjectName,
|
||||
Action<Base>? beforeConvertCallback)
|
||||
|
||||
public GameObject ConvertRecursivelyToNative(
|
||||
Base @base,
|
||||
string rootObjectName,
|
||||
Action<Base>? beforeConvertCallback
|
||||
)
|
||||
{
|
||||
var rootObject = new GameObject(rootObjectName);
|
||||
|
||||
@@ -46,10 +48,9 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
beforeConvertCallback?.Invoke(o);
|
||||
return RC.ConverterInstance.CanConvertToNative(o) //Accept geometry
|
||||
|| o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children
|
||||
|| 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())
|
||||
@@ -57,13 +58,15 @@ namespace Speckle.ConnectorUnity.Components
|
||||
var converted = RC.RecursivelyConvertToNative(prop.Value, null, Predicate);
|
||||
|
||||
//Skip empties
|
||||
if (converted.Count <= 0) continue;
|
||||
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);
|
||||
if (o.transform.parent == null)
|
||||
o.transform.SetParent(propertyObject.transform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,4 +83,4 @@ namespace Speckle.ConnectorUnity.Components
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
[AddComponentMenu("Speckle/Extras/Receive from URL")]
|
||||
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
|
||||
public class ReceiveFromURL : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Url of your speckle object/commit/branch/stream")]
|
||||
public string url;
|
||||
|
||||
private RecursiveConverter _converter;
|
||||
|
||||
#nullable enable
|
||||
private CancellationTokenSource? _tokenSource;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_converter = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Receive))]
|
||||
public void Receive()
|
||||
{
|
||||
StartCoroutine(Receive_Routine());
|
||||
}
|
||||
|
||||
public IEnumerator Receive_Routine()
|
||||
{
|
||||
if (IsBusy())
|
||||
throw new InvalidOperationException("A receive operation has already started");
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
StreamWrapper sw = new(url);
|
||||
|
||||
if (!sw.IsValid)
|
||||
throw new InvalidOperationException(
|
||||
"Speckle url input is not a valid speckle stream/branch/commit"
|
||||
);
|
||||
|
||||
var accountTask = new Utils.Utils.WaitForTask<Account>(
|
||||
async () => await GetAccount(sw),
|
||||
_tokenSource.Token
|
||||
);
|
||||
yield return accountTask;
|
||||
|
||||
_tokenSource.Token.ThrowIfCancellationRequested();
|
||||
using Client c = new(accountTask.Result);
|
||||
|
||||
var objectIdTask = new Utils.Utils.WaitForTask<(string, Commit?)>(
|
||||
async () => await GetObjectID(sw, c),
|
||||
_tokenSource.Token
|
||||
);
|
||||
|
||||
yield return objectIdTask;
|
||||
(string objectId, Commit? commit) = objectIdTask.Result;
|
||||
|
||||
Debug.Log($"Receiving from {sw.ServerUrl}...");
|
||||
|
||||
var receiveTask = new Utils.Utils.WaitForTask<Base>(
|
||||
async () =>
|
||||
await SpeckleReceiver.ReceiveAsync(
|
||||
c,
|
||||
sw.StreamId,
|
||||
objectId,
|
||||
commit,
|
||||
cancellationToken: _tokenSource.Token
|
||||
),
|
||||
_tokenSource.Token
|
||||
);
|
||||
yield return receiveTask;
|
||||
|
||||
Debug.Log("Converting to native...");
|
||||
_converter.RecursivelyConvertToNative_Sync(receiveTask.Result, transform);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_tokenSource.Dispose();
|
||||
_tokenSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(string objectId, Commit? commit)> GetObjectID(
|
||||
StreamWrapper sw,
|
||||
Client client
|
||||
)
|
||||
{
|
||||
string objectId;
|
||||
Commit? commit = null;
|
||||
//OBJECT URL
|
||||
if (!string.IsNullOrEmpty(sw.ObjectId))
|
||||
{
|
||||
objectId = sw.ObjectId;
|
||||
}
|
||||
//COMMIT URL
|
||||
else if (!string.IsNullOrEmpty(sw.CommitId))
|
||||
{
|
||||
commit = await client.CommitGet(sw.StreamId, sw.CommitId).ConfigureAwait(false);
|
||||
objectId = commit.referencedObject;
|
||||
}
|
||||
//BRANCH URL OR STREAM URL
|
||||
else
|
||||
{
|
||||
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
|
||||
|
||||
var branch = await client
|
||||
.BranchGet(sw.StreamId, branchName, 1)
|
||||
.ConfigureAwait(false);
|
||||
if (!branch.commits.items.Any())
|
||||
throw new SpeckleException("The selected branch has no commits.");
|
||||
|
||||
commit = branch.commits.items[0];
|
||||
objectId = branch.commits.items[0].referencedObject;
|
||||
}
|
||||
|
||||
return (objectId, commit);
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Cancel))]
|
||||
public void Cancel()
|
||||
{
|
||||
if (IsNotBusy())
|
||||
throw new InvalidOperationException(
|
||||
"There are no pending receive operations to cancel"
|
||||
);
|
||||
_tokenSource!.Cancel();
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Cancel), true)]
|
||||
public bool IsBusy()
|
||||
{
|
||||
return _tokenSource is not null;
|
||||
}
|
||||
|
||||
[ContextMenu(nameof(Receive), true)]
|
||||
internal bool IsNotBusy() => !IsBusy();
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_tokenSource?.Cancel();
|
||||
}
|
||||
|
||||
private async Task<Account> GetAccount(StreamWrapper sw)
|
||||
{
|
||||
Account account;
|
||||
try
|
||||
{
|
||||
account = await sw.GetAccount().ConfigureAwait(false);
|
||||
}
|
||||
catch (SpeckleException)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sw.StreamId))
|
||||
throw;
|
||||
|
||||
//Fallback to a non authed account
|
||||
account = new Account
|
||||
{
|
||||
token = "",
|
||||
serverInfo = new ServerInfo { url = sw.ServerUrl },
|
||||
userInfo = new UserInfo()
|
||||
};
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 942bf0cb27c5c5045bc4cbb7fc0fad71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+311
-79
@@ -2,18 +2,264 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Struct that encapsulates the result of a <see cref="RecursiveConverter"/> ToNative conversion of a single Speckle Object (<see cref="Base"/>)
|
||||
/// </summary>
|
||||
public readonly struct ConversionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that was converted ToNative
|
||||
/// </summary>
|
||||
public readonly TraversalContext traversalContext;
|
||||
|
||||
/// <summary>
|
||||
/// The result of conversion a successful conversion
|
||||
/// </summary>
|
||||
public readonly GameObject? converted;
|
||||
|
||||
/// <summary>
|
||||
/// The result of conversion a failed conversion
|
||||
/// </summary>
|
||||
public readonly Exception? exception;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor used for Successful conversions
|
||||
/// </summary>
|
||||
/// <param name="traversalContext">The current traversal context</param>
|
||||
/// <param name="converted">The resultant ToNative conversion of <see cref="TraversalContext.current"/> context object</param>
|
||||
/// <exception cref="ArgumentNullException"/>
|
||||
public ConversionResult(TraversalContext traversalContext, [NotNull] GameObject? converted)
|
||||
: this(traversalContext, converted, null)
|
||||
{
|
||||
if (converted is null)
|
||||
throw new ArgumentNullException(nameof(converted));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor used for Failed conversions
|
||||
/// </summary>
|
||||
/// <param name="traversalContext">The current conversion</param>
|
||||
/// <param name="exception">The operation halting exception that occured</param>
|
||||
/// <param name="converted">Optional converted GameObject</param>
|
||||
/// <exception cref="ArgumentNullException"/>
|
||||
public ConversionResult(
|
||||
TraversalContext traversalContext,
|
||||
[NotNull] Exception? exception,
|
||||
GameObject? converted = null
|
||||
)
|
||||
: this(traversalContext, converted, exception)
|
||||
{
|
||||
if (exception is null)
|
||||
throw new ArgumentNullException(nameof(exception));
|
||||
}
|
||||
|
||||
private ConversionResult(
|
||||
TraversalContext traversalContext,
|
||||
GameObject? converted,
|
||||
Exception? exception
|
||||
)
|
||||
{
|
||||
this.traversalContext = traversalContext;
|
||||
this.converted = converted;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="converted">The converted <see cref="GameObject"/></param>
|
||||
/// <param name="exception">The <see cref="exception"/> that occured during conversion</param>
|
||||
/// <returns>True if the conversion was successful</returns>
|
||||
public bool WasSuccessful(
|
||||
[NotNullWhen(true)] out GameObject? converted,
|
||||
[NotNullWhen(false)] out Exception? exception
|
||||
)
|
||||
{
|
||||
converted = this.converted;
|
||||
exception = this.exception;
|
||||
return WasSuccessful();
|
||||
}
|
||||
|
||||
public bool WasSuccessful() => this.exception == null;
|
||||
|
||||
public Base SpeckleObject => traversalContext.Current;
|
||||
}
|
||||
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
public IEnumerator ConvertCoroutine(Base rootObject, Transform? parent, List<GameObject> outCreatedObjects)
|
||||
=> ConvertCoroutine(rootObject, parent, outCreatedObjects,b => ConverterInstance.CanConvertToNative(b));
|
||||
|
||||
public IEnumerator ConvertCoroutine(Base rootObject, Transform? parent, List<GameObject> outCreatedObjects, Func<Base, bool> predicate)
|
||||
/// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/>
|
||||
/// <remarks>Calling this function will perform the conversion process synchronously</remarks>
|
||||
/// <returns>The conversion result</returns>
|
||||
public List<ConversionResult> RecursivelyConvertToNative_Sync(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
return RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="RecursivelyConvertToNative_Enumerable"/>
|
||||
/// <remarks>Calling this function will start a coroutine to complete later on the coroutine loop</remarks>
|
||||
/// <returns>The started Coroutine</returns>
|
||||
public Coroutine RecursivelyConvertToNative_Coroutine(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
return StartCoroutine(
|
||||
RecursivelyConvertToNative_Enumerable(rootObject, parent, predicate).GetEnumerator()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will recursively traverse the given <paramref name="rootObject"/> and convert convertable child objects
|
||||
/// where the given <see cref="predicate"/>
|
||||
/// </summary>
|
||||
/// <param name="rootObject">The Speckle object to traverse and convert all convertable children</param>
|
||||
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
|
||||
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
|
||||
/// <returns>An unevaluated <see cref="IEnumerable"/> of all created <see cref="GameObject"/>s</returns>
|
||||
public IEnumerable<ConversionResult> RecursivelyConvertToNative_Enumerable(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
var userPredicate = predicate ?? (_ => true);
|
||||
|
||||
var traversalFunc = DefaultTraversal.CreateBIMTraverseFunc(ConverterInstance);
|
||||
|
||||
var objectsToConvert = traversalFunc
|
||||
.Traverse(rootObject)
|
||||
.Where(x => ConverterInstance.CanConvertToNative(x.Current))
|
||||
.Where(x => userPredicate(x));
|
||||
|
||||
Dictionary<Base, GameObject?> created = new();
|
||||
foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created))
|
||||
{
|
||||
if (!isActiveAndEnabled)
|
||||
throw new InvalidOperationException(
|
||||
$"Cannot convert objects while {GetType()} is disabled"
|
||||
);
|
||||
|
||||
yield return conversionResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a objectTree (see <see cref="GraphTraversal"/>) to unevaluated enumerable.
|
||||
/// As this enumerable is iterated through, each context <see cref="Base"/> object will be converted to <see cref="GameObject"/> (if successful)
|
||||
/// or <see langword="null"/> if not.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You may enumerate over multiple frames (e.g. coroutine) but you must ensure the output eventually gets fully enumerated (exactly once)
|
||||
/// </remarks>
|
||||
/// <param name="objectTree"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="outCreatedObjects"></param>
|
||||
/// <returns></returns>
|
||||
protected IEnumerable<ConversionResult> ConvertTree(
|
||||
IEnumerable<TraversalContext> objectTree,
|
||||
Transform? parent,
|
||||
IDictionary<Base, GameObject?> outCreatedObjects
|
||||
)
|
||||
{
|
||||
InitializeAssetCache();
|
||||
AssetCache.BeginWrite();
|
||||
|
||||
foreach (TraversalContext tc in objectTree)
|
||||
{
|
||||
ConversionResult result;
|
||||
try
|
||||
{
|
||||
Transform? currentParent = GetParent(tc, outCreatedObjects) ?? parent;
|
||||
|
||||
var converted = ConvertToNative(tc.Current, currentParent);
|
||||
result = new ConversionResult(tc, converted);
|
||||
outCreatedObjects.TryAdd(tc.Current, result.converted);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new ConversionResult(tc, ex);
|
||||
}
|
||||
|
||||
yield return result;
|
||||
}
|
||||
|
||||
AssetCache.FinishWrite();
|
||||
}
|
||||
|
||||
protected static Transform? GetParent(
|
||||
TraversalContext? tc,
|
||||
IDictionary<Base, GameObject?> createdObjects
|
||||
)
|
||||
{
|
||||
if (tc == null)
|
||||
return null; //We've reached the root object, and still not found a converted parent
|
||||
|
||||
if (createdObjects.TryGetValue(tc.Current, out GameObject? p) && p != null)
|
||||
return p.transform;
|
||||
|
||||
//Go one level up, and repeat!
|
||||
return GetParent(tc.Parent, createdObjects);
|
||||
}
|
||||
|
||||
protected GameObject ConvertToNative(Base speckleObject, Transform? parentTransform)
|
||||
{
|
||||
GameObject? go = ConverterInstance.ConvertToNative(speckleObject) as GameObject;
|
||||
if (go == null)
|
||||
throw new SpeckleException("Conversion Returned Null");
|
||||
|
||||
go.transform.SetParent(parentTransform, true);
|
||||
|
||||
//Set some common for all created GameObjects
|
||||
//TODO add support for more unity specific props
|
||||
if (go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name))
|
||||
go.name = CoreUtils.GenerateObjectName(speckleObject);
|
||||
if (speckleObject["physicsLayer"] is string layerName)
|
||||
{
|
||||
int layer = LayerMask.NameToLayer(layerName); //TODO: check how this can be interoperable with Unreal and Blender
|
||||
if (layer > -1)
|
||||
go.layer = layer;
|
||||
}
|
||||
//if (baseObject["tag"] is string t) go.tag = t;
|
||||
//if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
|
||||
return go;
|
||||
}
|
||||
|
||||
#region deprecated conversion functions
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))]
|
||||
public IEnumerator ConvertCoroutine(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
List<GameObject> outCreatedObjects
|
||||
) =>
|
||||
ConvertCoroutine(
|
||||
rootObject,
|
||||
parent,
|
||||
outCreatedObjects,
|
||||
b => ConverterInstance.CanConvertToNative(b)
|
||||
);
|
||||
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Coroutine))]
|
||||
public IEnumerator ConvertCoroutine(
|
||||
Base rootObject,
|
||||
Transform? parent,
|
||||
List<GameObject> outCreatedObjects,
|
||||
Func<Base, bool> predicate
|
||||
)
|
||||
{
|
||||
foreach (string propertyName in GetPotentialChildren(rootObject))
|
||||
{
|
||||
@@ -21,7 +267,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Given <paramref name="o"/>,
|
||||
/// will recursively convert any objects in the tree
|
||||
@@ -29,21 +275,22 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// <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, b => ConverterInstance.CanConvertToNative(b));
|
||||
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(object? o, Transform? parent) =>
|
||||
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)
|
||||
[Obsolete("Use " + nameof(RecursivelyConvertToNative_Sync))]
|
||||
public virtual List<GameObject> RecursivelyConvertToNative(
|
||||
object? o,
|
||||
Transform? parent,
|
||||
Func<Base, bool> predicate
|
||||
)
|
||||
{
|
||||
//Ensure we have A native cache
|
||||
if (AssetCache.nativeCaches.Any(x => x == null))
|
||||
{
|
||||
AssetCache.nativeCaches = NativeCacheFactory.GetStandaloneCacheSetup();
|
||||
}
|
||||
|
||||
InitializeAssetCache();
|
||||
|
||||
var createdGameObjects = new List<GameObject>();
|
||||
ConverterInstance.SetContextDocument(AssetCache);
|
||||
try
|
||||
{
|
||||
AssetCache.BeginWrite();
|
||||
@@ -55,34 +302,43 @@ namespace Speckle.ConnectorUnity.Components
|
||||
}
|
||||
|
||||
//TODO track event?
|
||||
|
||||
|
||||
|
||||
|
||||
return createdGameObjects;
|
||||
|
||||
}
|
||||
|
||||
protected string[] namePropertyAliases = {"name", "Name"};
|
||||
|
||||
protected virtual string GenerateObjectName(Base baseObject)
|
||||
private void InitializeAssetCache()
|
||||
{
|
||||
// 1. Use explicit name
|
||||
foreach (var nameAlias in namePropertyAliases)
|
||||
//Ensure we have A native cache
|
||||
if (AssetCache.nativeCaches.Any(x => x == null))
|
||||
{
|
||||
string? s = baseObject[nameAlias] as string;
|
||||
if (!string.IsNullOrWhiteSpace(s)) return s; //TODO any sanitization needed?
|
||||
AssetCache.nativeCaches = NativeCacheFactory.GetStandaloneCacheSetup();
|
||||
}
|
||||
|
||||
// 2. Use type + id as fallback name
|
||||
// Only take the most derived type from the speckle type
|
||||
string speckleType = baseObject.speckle_type.Split(':').Last();
|
||||
return $"{speckleType} - {baseObject.id}";
|
||||
ConverterInstance.SetContextDocument(AssetCache);
|
||||
}
|
||||
|
||||
public virtual void RecurseTreeToNative(Base baseObject, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
|
||||
[Obsolete]
|
||||
public virtual void RecurseTreeToNative(
|
||||
Base baseObject,
|
||||
Transform? parent,
|
||||
Func<Base, bool> predicate,
|
||||
IList<GameObject> outCreatedObjects
|
||||
)
|
||||
{
|
||||
object? converted = null;
|
||||
if(predicate(baseObject))
|
||||
converted = ConverterInstance.ConvertToNative(baseObject);
|
||||
if (predicate(baseObject))
|
||||
{
|
||||
try
|
||||
{
|
||||
converted = ConverterInstance.ConvertToNative(baseObject);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"Failed to convert {baseObject.speckle_type} - {baseObject.id}\n{ex}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new GameObjects
|
||||
Transform? nextParent = parent;
|
||||
@@ -90,22 +346,23 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
outCreatedObjects.Add(go);
|
||||
nextParent = go.transform;
|
||||
|
||||
|
||||
go.transform.SetParent(parent, true);
|
||||
|
||||
|
||||
//Set some common for all created GameObjects
|
||||
//TODO add support for more unity specific props
|
||||
if(go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name))
|
||||
go.name = GenerateObjectName(baseObject);
|
||||
if (go.name == "New Game Object" || string.IsNullOrWhiteSpace(go.name))
|
||||
go.name = CoreUtils.GenerateObjectName(baseObject);
|
||||
//if (baseObject["tag"] is string t) go.tag = t;
|
||||
if (baseObject["physicsLayer"] is string layerName)
|
||||
{
|
||||
int layer = LayerMask.NameToLayer(layerName); //TODO: check how this can be interoperable with Unreal and Blender
|
||||
if (layer > -1) go.layer = layer;
|
||||
if (layer > -1)
|
||||
go.layer = layer;
|
||||
}
|
||||
//if (baseObject["isStatic"] is bool isStatic) go.isStatic = isStatic;
|
||||
}
|
||||
|
||||
|
||||
// For geometry, only traverse `elements` prop, otherwise, try and convert everything
|
||||
IEnumerable<string> potentialChildren = GetPotentialChildren(baseObject);
|
||||
|
||||
@@ -114,54 +371,29 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
ConvertChild(baseObject[propertyName], nextParent, predicate, outCreatedObjects);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
private IEnumerable<string> GetPotentialChildren(Base baseObject)
|
||||
{
|
||||
return ConverterInstance.CanConvertToNative(baseObject)
|
||||
? new []{"elements"}
|
||||
? new[] { "elements" }
|
||||
: baseObject.GetMembers().Keys;
|
||||
}
|
||||
|
||||
|
||||
protected virtual void ConvertChild(object? value, Transform? parent, Func<Base, bool> predicate, IList<GameObject> outCreatedObjects)
|
||||
[Obsolete]
|
||||
protected virtual void ConvertChild(
|
||||
object? value,
|
||||
Transform? parent,
|
||||
Func<Base, bool> predicate,
|
||||
IList<GameObject> outCreatedObjects
|
||||
)
|
||||
{
|
||||
if(value == null) return;
|
||||
if(value.GetType().IsPrimitive) return;
|
||||
if (value is string) return;
|
||||
|
||||
if(value is Base o)
|
||||
foreach (Base b in GraphTraversal.TraverseMember(value))
|
||||
{
|
||||
RecurseTreeToNative(o, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
else if (value is IDictionary dictionary)
|
||||
{
|
||||
foreach (object v in dictionary.Keys)
|
||||
{
|
||||
ConvertChild(v, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
else if (value is IList collection)
|
||||
{
|
||||
foreach (object v in collection)
|
||||
{
|
||||
ConvertChild(v, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
else if(!value.GetType().IsValueType) //don't want to output errors for structs
|
||||
{
|
||||
Debug.Log($"Unknown type {value.GetType()} found when traversing tree, will be safely ignored");
|
||||
RecurseTreeToNative(b, parent, predicate, outCreatedObjects);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use RecursivelyConvertToNative instead")]
|
||||
public GameObject ConvertRecursivelyToNative(Base @base, string name)
|
||||
{
|
||||
var parentObject = new GameObject(name);
|
||||
RecursivelyConvertToNative(@base, parentObject.transform);
|
||||
return parentObject;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-17
@@ -8,7 +8,6 @@ using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
|
||||
public partial class RecursiveConverter
|
||||
{
|
||||
/// <summary>
|
||||
@@ -31,37 +30,44 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// <param name="rootObjects">Root objects of a tree</param>
|
||||
/// <param name="predicate">A function to determine if an object should be converted</param>
|
||||
/// <returns>A simple <see cref="Base"/> wrapping converted objects</returns>
|
||||
public virtual Base RecursivelyConvertToSpeckle(IEnumerable<GameObject> rootObjects, Func<GameObject, bool> predicate)
|
||||
public virtual Base RecursivelyConvertToSpeckle(
|
||||
IEnumerable<GameObject> rootObjects,
|
||||
Func<GameObject, bool> predicate
|
||||
)
|
||||
{
|
||||
List<Base> convertedRootObjects = new List<Base>();
|
||||
foreach (GameObject rootObject in rootObjects)
|
||||
{
|
||||
RecurseTreeToSpeckle(rootObject, predicate, convertedRootObjects);
|
||||
}
|
||||
|
||||
return new Base()
|
||||
{
|
||||
["@objects"] = convertedRootObjects,
|
||||
};
|
||||
|
||||
return new Base() { ["@objects"] = convertedRootObjects, };
|
||||
}
|
||||
|
||||
public virtual Base RecursivelyConvertToSpeckle(GameObject rootObject, Func<GameObject, bool> predicate)
|
||||
|
||||
public virtual Base RecursivelyConvertToSpeckle(
|
||||
GameObject rootObject,
|
||||
Func<GameObject, bool> predicate
|
||||
)
|
||||
{
|
||||
return RecursivelyConvertToSpeckle(new[] {rootObject}, predicate);
|
||||
return RecursivelyConvertToSpeckle(new[] { rootObject }, predicate);
|
||||
}
|
||||
|
||||
public virtual void RecurseTreeToSpeckle(GameObject currentObject, Func<GameObject, bool> predicate, List<Base> outConverted)
|
||||
|
||||
public virtual void RecurseTreeToSpeckle(
|
||||
GameObject currentObject,
|
||||
Func<GameObject, bool> predicate,
|
||||
List<Base> outConverted
|
||||
)
|
||||
{
|
||||
// Convert children first
|
||||
var convertedChildren = new List<Base>(currentObject.transform.childCount);
|
||||
foreach(Transform child in currentObject.transform)
|
||||
foreach (Transform child in currentObject.transform)
|
||||
{
|
||||
RecurseTreeToSpeckle(child.gameObject, predicate, convertedChildren);
|
||||
}
|
||||
|
||||
|
||||
if (ConverterInstance.CanConvertToSpeckle(currentObject) && predicate(currentObject))
|
||||
{
|
||||
// Convert and output
|
||||
// Convert and output
|
||||
Base converted = ConverterInstance.ConvertToSpeckle(currentObject);
|
||||
converted.SetDetachedPropertyChecked("elements", convertedChildren);
|
||||
outConverted.Add(converted);
|
||||
@@ -71,7 +77,6 @@ namespace Speckle.ConnectorUnity.Components
|
||||
// Skip this object, and output any children
|
||||
outConverted.AddRange(convertedChildren);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Speckle.ConnectorUnity.Factories;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using UnityEngine;
|
||||
@@ -13,14 +14,24 @@ namespace Speckle.ConnectorUnity.Components
|
||||
[ExecuteAlways, DisallowMultipleComponent]
|
||||
public partial class RecursiveConverter : MonoBehaviour
|
||||
{
|
||||
public ISpeckleConverter ConverterInstance { get; set; } = ConverterFactory.GetDefaultConverter();
|
||||
[field: SerializeReference]
|
||||
public ISpeckleConverter ConverterInstance { get; set; } =
|
||||
ConverterFactory.GetDefaultConverter();
|
||||
|
||||
[field: SerializeField]
|
||||
public AggregateNativeCache AssetCache { get; set; }
|
||||
|
||||
private void Awake()
|
||||
protected void Awake()
|
||||
{
|
||||
Setup.Init(HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()), HostApplications.Unity.Slug);
|
||||
Init();
|
||||
}
|
||||
|
||||
protected void Init()
|
||||
{
|
||||
Setup.Init(
|
||||
HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
|
||||
HostApplications.Unity.Slug
|
||||
);
|
||||
|
||||
if (AssetCache == null)
|
||||
{
|
||||
@@ -28,6 +39,8 @@ namespace Speckle.ConnectorUnity.Components
|
||||
assetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup();
|
||||
this.AssetCache = assetCache;
|
||||
}
|
||||
|
||||
InitializeAssetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@
|
||||
"GUID:eed1b8b83e2c0074d9e5de2348e3ff72",
|
||||
"GUID:13aec21e8e96f864bafd00df49f225fc",
|
||||
"GUID:d274441ecc3eb3f43b093eec1503d681",
|
||||
"GUID:50d889142fdf9de4b8501c6eaa4b3225"
|
||||
"GUID:50d889142fdf9de4b8501c6eaa4b3225",
|
||||
"GUID:7383cd71541a2aa48a7baf23f74b4d5f"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers.Selection;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[assembly: InternalsVisibleTo("Speckle.ConnectorUnity.Components.Editor")]
|
||||
|
||||
namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
[ExecuteAlways]
|
||||
@@ -25,146 +29,301 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
[field: SerializeReference]
|
||||
public AccountSelection Account { get; private set; }
|
||||
|
||||
|
||||
[field: SerializeReference]
|
||||
public StreamSelection Stream { get; private set; }
|
||||
|
||||
|
||||
[field: SerializeReference]
|
||||
public BranchSelection Branch { get; private set; }
|
||||
|
||||
|
||||
[field: SerializeReference]
|
||||
public CommitSelection Commit { get; private set; }
|
||||
|
||||
public RecursiveConverter Converter { get; private set; }
|
||||
|
||||
#nullable enable
|
||||
[Header("Events")]
|
||||
[HideInInspector]
|
||||
public CommitSelectionEvent OnCommitSelectionChange;
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnReceiveProgressAction;
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction;
|
||||
[HideInInspector]
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown;
|
||||
[HideInInspector]
|
||||
public ReceiveCompleteHandler OnComplete;
|
||||
public CommitSelectionEvent OnCommitSelectionChange = new();
|
||||
|
||||
#nullable enable
|
||||
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnReceiveProgressAction = new();
|
||||
|
||||
//TODO runtime receiving
|
||||
public IEnumerator ReceiveAndConvertRoutine(SpeckleReceiver speckleReceiver, string rootObjectName, Action<Base>? beforeConvertCallback = null)
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction = new();
|
||||
|
||||
[HideInInspector]
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown = new();
|
||||
|
||||
[HideInInspector]
|
||||
public ReceiveCompleteHandler OnComplete = new();
|
||||
|
||||
protected CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
public CancellationToken CancellationToken => CancellationTokenSource?.Token ?? default;
|
||||
public bool IsReceiving => CancellationTokenSource != null;
|
||||
|
||||
/// <summary>
|
||||
/// Cancels any current receive operations
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note, this does not cancel any currently executing ConvertToNative, just the <see cref="Operations.Receive"/>.
|
||||
/// </remarks>
|
||||
/// <returns><see langword="true"/> if the cancellation request was made. <see langword="false"/> if there was no pending operation to cancel (see <see cref="IsReceiving"/>)</returns>
|
||||
public bool Cancel()
|
||||
{
|
||||
Task<Base?> receiveOperation = Task.Run(ReceiveAsync);
|
||||
|
||||
yield return new WaitUntil(() => receiveOperation.IsCompleted);
|
||||
|
||||
Base? b = receiveOperation.Result;
|
||||
if (b == null) yield break;
|
||||
|
||||
//TODO make routine break for each catergory/object
|
||||
GameObject go = ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
|
||||
OnComplete.Invoke(go);
|
||||
if (CancellationTokenSource == null)
|
||||
return false;
|
||||
CancellationTokenSource.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ReceiveAndConvert_Async"/>
|
||||
/// <example>
|
||||
/// This function is designed to run as a coroutine i.e.
|
||||
/// <c>StartCoroutine(mySpeckleReceiver.ReceiveAndConvert_Routine());</c>
|
||||
/// </example>
|
||||
public IEnumerator ReceiveAndConvert_Routine(
|
||||
Transform? parent = null,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
if (IsReceiving)
|
||||
{
|
||||
OnErrorAction.Invoke(
|
||||
"Failed to receive",
|
||||
new InvalidOperationException("A pending receive operation has already started")
|
||||
);
|
||||
yield break;
|
||||
}
|
||||
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = new();
|
||||
|
||||
// ReSharper disable once MethodSupportsCancellation
|
||||
Task<Base> receiveOperation = Task.Run(async () =>
|
||||
{
|
||||
Base result = await ReceiveAsync(CancellationToken);
|
||||
CancellationToken.ThrowIfCancellationRequested();
|
||||
return result;
|
||||
});
|
||||
|
||||
yield return new WaitUntil(() => receiveOperation.IsCompleted);
|
||||
|
||||
if (receiveOperation.IsFaulted)
|
||||
{
|
||||
OnErrorAction.Invoke("Failed to receive", receiveOperation.Exception);
|
||||
FinishOperation();
|
||||
yield break;
|
||||
}
|
||||
|
||||
Base b = receiveOperation.Result;
|
||||
|
||||
foreach (var _ in Converter.RecursivelyConvertToNative_Enumerable(b, parent, predicate))
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
OnComplete.Invoke(parent);
|
||||
FinishOperation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive the selected <see cref="Commit"/> object, and converts ToNative as children of <paramref name="parent"/>
|
||||
/// </summary>
|
||||
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
|
||||
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
|
||||
/// <remarks>function does not throw, instead calls <see cref="OnErrorAction"/>, and calls <see cref="OnComplete"/> upon completion</remarks>
|
||||
/// <seealso cref="ReceiveAsync(System.Threading.CancellationToken)"/>
|
||||
/// <seealso cref="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/>
|
||||
public async void ReceiveAndConvert_Async(
|
||||
Transform? parent = null,
|
||||
Predicate<TraversalContext>? predicate = null
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
BeginOperation();
|
||||
Base commitObject = await ReceiveAsync(CancellationToken).ConfigureAwait(true);
|
||||
Converter.RecursivelyConvertToNative_Sync(commitObject, parent, predicate);
|
||||
OnComplete.Invoke(parent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnErrorAction.Invoke("Failed to receive", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
FinishOperation();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receives the selected commit object using async Task
|
||||
/// </summary>
|
||||
/// <returns>Awaitable commit object</returns>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <exception cref="SpeckleException">thrown when selection is incomplete</exception>
|
||||
public async Task<Base?> ReceiveAsync()
|
||||
/// <remarks>
|
||||
/// This function is safe to call concurrently from any threads.
|
||||
/// For this reason we use <paramref name="cancellationToken"/> parameter, rather than use the <see cref="CancellationToken"/> property
|
||||
/// <br/>
|
||||
/// Additionally, <see cref="OnComplete"/> and <see cref="OnErrorAction"/> won't be called.
|
||||
/// </remarks>
|
||||
public async Task<Base> ReceiveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
ValidateSelection(out Client? client, out Stream? stream, out Commit? commit);
|
||||
|
||||
Base result = await ReceiveAsync(
|
||||
client: client,
|
||||
streamId: stream.id,
|
||||
objectId: commit.referencedObject,
|
||||
commit: commit,
|
||||
onProgressAction: dict => OnReceiveProgressAction.Invoke(dict),
|
||||
onTotalChildrenCountKnown: c => OnTotalChildrenCountKnown.Invoke(c),
|
||||
cancellationToken: cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current selection
|
||||
/// </summary>
|
||||
/// <param name="client">The selected Account's Client</param>
|
||||
/// <param name="stream">The selected <see cref="Stream"/></param>
|
||||
/// <param name="commit">The selected <see cref="Commit"/></param>
|
||||
/// <exception cref="InvalidOperationException">Selection was not complete or invalid</exception>
|
||||
public void ValidateSelection(out Client client, out Stream stream, out Commit commit)
|
||||
{
|
||||
Client? selectedClient = Account.Client;
|
||||
client =
|
||||
selectedClient ?? throw new InvalidOperationException("Invalid Speckle account selection");
|
||||
|
||||
Stream? selectedStream = Stream.Selected;
|
||||
stream =
|
||||
selectedStream ?? throw new InvalidOperationException("Invalid Speckle project selection");
|
||||
|
||||
Commit? selectedCommit = Commit.Selected;
|
||||
commit =
|
||||
selectedCommit ?? throw new InvalidOperationException("Invalid Speckle version selection");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new receive operation with a <see cref="CancellationToken"/>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">already receiving</exception>
|
||||
protected internal CancellationToken BeginOperation()
|
||||
{
|
||||
if (IsReceiving)
|
||||
throw new InvalidOperationException(
|
||||
"A pending receive operation has already started"
|
||||
);
|
||||
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
if(!GetSelection(out Client? client, out Stream? stream, out Commit? commit, out string? error))
|
||||
throw new SpeckleException(error);
|
||||
|
||||
return await ReceiveAsync(
|
||||
token: CancellationTokenSource.Token,
|
||||
client: client,
|
||||
streamId: stream.id,
|
||||
objectId: commit.referencedObject,
|
||||
commitId: commit.id,
|
||||
onProgressAction: dict => OnReceiveProgressAction.Invoke(dict),
|
||||
onErrorAction: (m, e) => OnErrorAction.Invoke(m, e),
|
||||
onTotalChildrenCountKnown: c => OnTotalChildrenCountKnown.Invoke(c)
|
||||
);
|
||||
CancellationTokenSource = new();
|
||||
|
||||
return CancellationTokenSource.Token;
|
||||
}
|
||||
|
||||
protected internal void FinishOperation()
|
||||
{
|
||||
if (!IsReceiving)
|
||||
throw new InvalidOperationException("No pending operations to finish");
|
||||
|
||||
CancellationTokenSource!.Dispose();
|
||||
CancellationTokenSource = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receives the requested <see cref="objectId"/> using async Task
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="streamId"></param>
|
||||
/// <param name="objectId"></param>
|
||||
/// <param name="commitId"></param>
|
||||
/// <param name="commit"></param>
|
||||
/// <param name="onProgressAction"></param>
|
||||
/// <param name="onErrorAction"></param>
|
||||
/// <param name="onTotalChildrenCountKnown"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<Base?> ReceiveAsync(CancellationToken token,
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <exception cref="Exception">Throws various types of exceptions to indicate faliure</exception>
|
||||
/// <returns>The requested Speckle object</returns>
|
||||
public static async Task<Base> ReceiveAsync(
|
||||
Client client,
|
||||
string streamId,
|
||||
string objectId,
|
||||
string? commitId,
|
||||
Commit? commit,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null,
|
||||
Action<int>? onTotalChildrenCountKnown = null)
|
||||
Action<int>? onTotalChildrenCountKnown = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
ServerTransport transport = new ServerTransport(client.Account, streamId);
|
||||
transport.CancellationToken = token;
|
||||
|
||||
Base? ret = null;
|
||||
using var transport = new ServerTransport(client.Account, streamId);
|
||||
|
||||
transport.CancellationToken = cancellationToken;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Base requestedObject = await Operations
|
||||
.Receive(
|
||||
objectId,
|
||||
transport,
|
||||
null,
|
||||
onProgressAction,
|
||||
onTotalChildrenCountKnown,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Analytics.TrackEvent(
|
||||
client.Account,
|
||||
Analytics.Events.Receive,
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "mode", nameof(SpeckleReceiver) },
|
||||
{
|
||||
"sourceHostApp",
|
||||
HostApplications.GetHostAppFromString(commit?.sourceApplication).Slug
|
||||
},
|
||||
{ "sourceHostAppVersion", commit?.sourceApplication ?? "" },
|
||||
{ "hostPlatform", Application.platform.ToString() },
|
||||
{
|
||||
"isMultiplayer",
|
||||
commit?.authorId != null && commit?.authorId != client.Account?.userInfo?.id
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
//Read receipt
|
||||
try
|
||||
{
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Receive);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
ret = await Operations.Receive(
|
||||
objectId: objectId,
|
||||
cancellationToken: token,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction,
|
||||
onTotalChildrenCountKnown: onTotalChildrenCountKnown,
|
||||
disposeTransports: true
|
||||
);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
//Read receipt
|
||||
try
|
||||
{
|
||||
await client.CommitReceived(token, new CommitReceivedInput
|
||||
{
|
||||
streamId = streamId,
|
||||
commitId = commitId,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Do nothing!
|
||||
Debug.LogWarning($"Failed to send read receipt\n{e}");
|
||||
}
|
||||
await client
|
||||
.CommitReceived(
|
||||
new CommitReceivedInput
|
||||
{
|
||||
streamId = streamId,
|
||||
commitId = commit?.id,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(
|
||||
CoreUtils.GetHostAppVersion()
|
||||
)
|
||||
},
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception ex)
|
||||
{
|
||||
onErrorAction?.Invoke(e.Message, e);
|
||||
// Do nothing!
|
||||
Debug.LogWarning($"Failed to send read receipt\n{ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
transport?.Dispose();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
return requestedObject;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for using <see cref="RecursiveConverter"/>.
|
||||
/// Creates blank GameObjects for each property/category of the root object.
|
||||
@@ -173,8 +332,18 @@ namespace Speckle.ConnectorUnity.Components
|
||||
/// <param name="rootObjectName">The name of the parent <see cref="GameObject"/> to create</param>
|
||||
/// <param name="beforeConvertCallback">Callback for each object converted</param>
|
||||
/// <returns>The created parent <see cref="GameObject"/></returns>
|
||||
public GameObject ConvertToNativeWithCategories(Base @base, string rootObjectName,
|
||||
Action<Base>? beforeConvertCallback)
|
||||
[Obsolete(
|
||||
"Use "
|
||||
+ nameof(RecursiveConverter)
|
||||
+ " Now we have implemented support for "
|
||||
+ nameof(Collection)
|
||||
+ "s, receiving any collection is now the default behaviour"
|
||||
)]
|
||||
public GameObject ConvertToNativeWithCategories(
|
||||
Base @base,
|
||||
string rootObjectName,
|
||||
Action<Base>? beforeConvertCallback
|
||||
)
|
||||
{
|
||||
var rootObject = new GameObject(rootObjectName);
|
||||
|
||||
@@ -182,10 +351,9 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
beforeConvertCallback?.Invoke(o);
|
||||
return Converter.ConverterInstance.CanConvertToNative(o) //Accept geometry
|
||||
|| o.speckle_type == nameof(Base) && o.totalChildrenCount > 0; // Or Base objects that have children
|
||||
|| 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())
|
||||
@@ -193,52 +361,56 @@ namespace Speckle.ConnectorUnity.Components
|
||||
var converted = Converter.RecursivelyConvertToNative(prop.Value, null, Predicate);
|
||||
|
||||
//Skip empties
|
||||
if (converted.Count <= 0) continue;
|
||||
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);
|
||||
if (o.transform.parent == null)
|
||||
o.transform.SetParent(propertyObject.transform);
|
||||
}
|
||||
}
|
||||
|
||||
return rootObject;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="commit"></param>
|
||||
/// <param name="error">error messages for </param>
|
||||
/// <returns>true if selection is complete, as we are ready to receive</returns>
|
||||
[Obsolete("Use " + nameof(ValidateSelection))]
|
||||
public bool GetSelection(
|
||||
[NotNullWhen(true)] out Client? client,
|
||||
[NotNullWhen(true)] out Stream? stream,
|
||||
[NotNullWhen(true)] out Commit? commit,
|
||||
[NotNullWhen(false)] out string? error)
|
||||
[NotNullWhen(false)] out string? error
|
||||
)
|
||||
{
|
||||
Account? account = Account.Selected;
|
||||
stream = Stream.Selected;
|
||||
commit = Commit.Selected;
|
||||
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
error = "Selected Account is null";
|
||||
client = null;
|
||||
return false;
|
||||
}
|
||||
client = Account.Client ?? new Client(account);
|
||||
|
||||
client = Account.Client ?? new Client(account);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
error = "Selected Stream is null";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (commit == null)
|
||||
|
||||
if (commit == null)
|
||||
{
|
||||
error = "Selected Commit is null";
|
||||
return false;
|
||||
@@ -246,60 +418,66 @@ namespace Speckle.ConnectorUnity.Components
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the commit preview for the currently selected commit
|
||||
/// </summary>
|
||||
/// <param name="allAngles">when <see langword="true"/>, will fetch 360 degree preview image</param>
|
||||
/// <param name="callback">Callback function to be called when the web request completes</param>
|
||||
/// <returns><see langword="false"/> if <see cref="Account"/>, <see cref="Stream"/>, or <see cref="Commit"/> was <see langword="null"/></returns>
|
||||
public bool GetPreviewImage(/*bool allAngles,*/ Action<Texture2D?> callback)
|
||||
/// <returns>The executing <see cref="Coroutine"/> or <see langword="null"/> if <see cref="Account"/>, <see cref="Stream"/>, or <see cref="Commit"/> was <see langword="null"/></returns>
|
||||
public Coroutine? GetPreviewImage(Action<Texture2D?> callback)
|
||||
{
|
||||
Account? account = Account.Selected;
|
||||
if (account == null) return false;
|
||||
if (account == null)
|
||||
return null;
|
||||
string? streamId = Stream.Selected?.id;
|
||||
if (streamId == null) return false;
|
||||
if (streamId == null)
|
||||
return null;
|
||||
string? commitId = Commit.Selected?.id;
|
||||
if (commitId == null) return false;
|
||||
if (commitId == null)
|
||||
return null;
|
||||
|
||||
string angles = /*allAngles ? "all" :*/ "";
|
||||
string angles = /*allAngles ? "all" :*/
|
||||
"";
|
||||
string url = $"{account.serverInfo.url}/preview/{streamId}/commits/{commitId}/{angles}";
|
||||
string authToken = account.token;
|
||||
|
||||
StartCoroutine(Utils.Utils.GetImageRoutine(url, authToken, callback));
|
||||
return true;
|
||||
|
||||
return StartCoroutine(Utils.Utils.GetImageRoutine(url, authToken, callback));
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Open Speckle Stream in Browser")]
|
||||
[ContextMenu("Open Speckle Model in Browser")]
|
||||
protected void OpenUrlInBrowser()
|
||||
{
|
||||
string url = GetSelectedUrl();
|
||||
Application.OpenURL(url);
|
||||
Uri url = GetSelectedUrl();
|
||||
Application.OpenURL(url.ToString());
|
||||
}
|
||||
#endif
|
||||
public string GetSelectedUrl()
|
||||
{
|
||||
string serverUrl = Account.Selected!.serverInfo.url;
|
||||
string? streamId = Stream.Selected?.id;
|
||||
string? branchName = Branch.Selected?.name;
|
||||
string? commitId = Commit.Selected?.id;
|
||||
|
||||
if (string.IsNullOrEmpty(streamId)) return serverUrl;
|
||||
if (!string.IsNullOrEmpty(commitId)) return $"{serverUrl}/streams/{streamId}/commits/{commitId}";
|
||||
if (!string.IsNullOrEmpty(branchName)) return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
|
||||
return $"{serverUrl}/streams/{streamId}";
|
||||
public Uri GetSelectedUrl()
|
||||
{
|
||||
Account selectedAccount = Account.Selected!;
|
||||
StreamWrapper sw =
|
||||
new()
|
||||
{
|
||||
ServerUrl = selectedAccount.serverInfo.url,
|
||||
StreamId = Stream.Selected!.id,
|
||||
BranchName = Branch.Selected?.id,
|
||||
CommitId = Commit.Selected?.id
|
||||
};
|
||||
sw.SetAccount(selectedAccount);
|
||||
|
||||
return sw.ToServerUri();
|
||||
}
|
||||
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
CoreUtils.SetupInit();
|
||||
Converter = GetComponent<RecursiveConverter>();
|
||||
Initialise(true);
|
||||
}
|
||||
|
||||
protected void Initialise(bool forceRefresh = false)
|
||||
{
|
||||
CoreUtils.SetupInit();
|
||||
Account ??= new AccountSelection();
|
||||
Stream ??= new StreamSelection(Account);
|
||||
Branch ??= new BranchSelection(Stream);
|
||||
@@ -307,32 +485,73 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Stream.Initialise();
|
||||
Branch.Initialise();
|
||||
Commit.Initialise();
|
||||
Commit.OnSelectionChange =
|
||||
() => OnCommitSelectionChange?.Invoke(Commit.Selected);
|
||||
if(Account.Options is not {Length: > 0} || forceRefresh)
|
||||
Commit.OnSelectionChange = () => OnCommitSelectionChange?.Invoke(Commit.Selected);
|
||||
if (Account.Options is not { Count: > 0 } || forceRefresh)
|
||||
Account.RefreshOptions();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
CancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
//pass
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
|
||||
#region Deprecated members
|
||||
|
||||
[Obsolete("use " + nameof(ReceiveAndConvert_Routine), true)]
|
||||
public IEnumerator ReceiveAndConvertRoutine(
|
||||
SpeckleReceiver speckleReceiver,
|
||||
string rootObjectName,
|
||||
Action<Base>? beforeConvertCallback = null
|
||||
)
|
||||
{
|
||||
// ReSharper disable once MethodSupportsCancellation
|
||||
Task<Base> receiveOperation = Task.Run(
|
||||
async () => await ReceiveAsync(CancellationToken)
|
||||
);
|
||||
|
||||
yield return new WaitUntil(() => receiveOperation.IsCompleted);
|
||||
|
||||
Base? b = receiveOperation.Result;
|
||||
if (b == null)
|
||||
yield break;
|
||||
|
||||
//NOTE: coroutine doesn't break for each catergory/object
|
||||
ConvertToNativeWithCategories(b, rootObjectName, beforeConvertCallback);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Serializable] public sealed class CommitSelectionEvent : UnityEvent<Commit?> { }
|
||||
[Serializable] public sealed class BranchSelectionEvent : UnityEvent<Branch?> { }
|
||||
[Serializable] public sealed class ErrorActionEvent : UnityEvent<string, Exception> { }
|
||||
[Serializable] public sealed class OperationProgressEvent : UnityEvent<ConcurrentDictionary<string, int>> { }
|
||||
[Serializable] public sealed class ReceiveCompleteHandler : UnityEvent<GameObject> { }
|
||||
[Serializable] public sealed class ChildrenCountHandler : UnityEvent<int> { }
|
||||
|
||||
[Serializable]
|
||||
public sealed class CommitSelectionEvent : UnityEvent<Commit?> { }
|
||||
|
||||
[Serializable]
|
||||
public sealed class BranchSelectionEvent : UnityEvent<Branch?> { }
|
||||
|
||||
[Serializable]
|
||||
public sealed class ErrorActionEvent : UnityEvent<string, Exception> { }
|
||||
|
||||
[Serializable]
|
||||
public sealed class OperationProgressEvent : UnityEvent<ConcurrentDictionary<string, int>> { }
|
||||
|
||||
[Serializable]
|
||||
public sealed class ReceiveCompleteHandler : UnityEvent<Transform?> { }
|
||||
|
||||
[Serializable]
|
||||
public sealed class ChildrenCountHandler : UnityEvent<int> { }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers.Selection;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
@@ -22,132 +23,183 @@ namespace Speckle.ConnectorUnity.Components
|
||||
{
|
||||
[field: SerializeReference]
|
||||
public AccountSelection Account { get; private set; }
|
||||
|
||||
|
||||
[field: SerializeReference]
|
||||
public StreamSelection Stream { get; private set; }
|
||||
|
||||
|
||||
[field: SerializeReference]
|
||||
public BranchSelection Branch { get; private set; }
|
||||
|
||||
|
||||
public RecursiveConverter Converter { get; private set; }
|
||||
|
||||
|
||||
[Header("Events")]
|
||||
[HideInInspector]
|
||||
public BranchSelectionEvent OnBranchSelectionChange;
|
||||
|
||||
[Obsolete("No longer used")]
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction;
|
||||
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnSendProgressAction;
|
||||
#nullable enable
|
||||
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
|
||||
|
||||
//TODO runtime sending
|
||||
|
||||
|
||||
public async Task<string> SendDataAsync(Base data, bool createCommit)
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
if(!GetSelection(out Client? client, out Stream? stream, out Branch? branch, out string? error))
|
||||
if (
|
||||
!GetSelection(
|
||||
out Client? client,
|
||||
out Stream? stream,
|
||||
out Branch? branch,
|
||||
out string? error
|
||||
)
|
||||
)
|
||||
throw new SpeckleException(error);
|
||||
|
||||
ServerTransport transport = new ServerTransport(client.Account, stream.id);
|
||||
|
||||
using ServerTransport transport = new(client.Account, stream.id);
|
||||
transport.CancellationToken = CancellationTokenSource.Token;
|
||||
|
||||
return await SendDataAsync(CancellationTokenSource.Token,
|
||||
|
||||
return await SendDataAsync(
|
||||
remoteTransport: transport,
|
||||
data: data,
|
||||
client: client,
|
||||
branchName: branch.name,
|
||||
createCommit: createCommit,
|
||||
onProgressAction: dict => OnSendProgressAction.Invoke(dict),
|
||||
onErrorAction: (m, e) => OnErrorAction.Invoke(m, e)
|
||||
data,
|
||||
client,
|
||||
branch.id,
|
||||
createCommit,
|
||||
dict => OnSendProgressAction.Invoke(dict),
|
||||
CancellationTokenSource.Token
|
||||
);
|
||||
}
|
||||
|
||||
public static async Task<string> SendDataAsync(CancellationToken cancellationToken,
|
||||
/// <param name="remoteTransport">The transport to send to</param>
|
||||
/// <param name="data">The data to send</param>
|
||||
/// <param name="client">An authenticated Speckle Client</param>
|
||||
/// <param name="branchId">The branch name or id</param>
|
||||
/// <param name="createCommit">when <see langword="true"/> will call <see cref="Client.CommitCreate"/>, otherwise only the object data is sent</param>
|
||||
/// <param name="onProgressAction">Called every progress tick of the <see cref="Operations.Send"/></param>
|
||||
/// <param name="cancellationToken">Optional cancellation token</param>
|
||||
/// <returns>The id (hash) of the object sent</returns>
|
||||
public static async Task<string> SendDataAsync(
|
||||
ServerTransport remoteTransport,
|
||||
Base data,
|
||||
Client client,
|
||||
string branchName,
|
||||
string branchId,
|
||||
bool createCommit,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null)
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
string res = await Operations.Send(
|
||||
data,
|
||||
cancellationToken: cancellationToken,
|
||||
new List<ITransport>{remoteTransport},
|
||||
useDefaultCache: true,
|
||||
disposeTransports: true,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction
|
||||
remoteTransport,
|
||||
true,
|
||||
onProgressAction,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Send);
|
||||
Analytics.TrackEvent(
|
||||
client.Account,
|
||||
Analytics.Events.Send,
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "mode", nameof(SpeckleSender) },
|
||||
{ "hostPlatform", Application.platform.ToString() },
|
||||
}
|
||||
);
|
||||
|
||||
if (createCommit && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
string streamId = remoteTransport.StreamId;
|
||||
string unityVer = $"Unity {Application.unityVersion.Substring(0,6)}";
|
||||
string unityVer = $"Unity {Application.unityVersion.Substring(0, 6)}";
|
||||
data.totalChildrenCount = data.GetTotalChildrenCount();
|
||||
string commitMessage = $"Sent {data.totalChildrenCount} objects from {unityVer}";
|
||||
|
||||
string commitId = await CreateCommit(cancellationToken, data, client, streamId, branchName, res, commitMessage);
|
||||
string url = $"{client.ServerUrl}/streams/{streamId}/commits/{commitId}";
|
||||
|
||||
string commitId = await CreateCommit(
|
||||
data,
|
||||
client,
|
||||
streamId,
|
||||
branchId,
|
||||
res,
|
||||
commitMessage,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
StreamWrapper sw =
|
||||
new()
|
||||
{
|
||||
ServerUrl = client.ServerUrl,
|
||||
StreamId = streamId,
|
||||
BranchName = branchId,
|
||||
CommitId = commitId,
|
||||
};
|
||||
sw.SetAccount(client.Account);
|
||||
|
||||
string url = sw.ToServerUri().GetLeftPart(UriPartial.Path);
|
||||
Debug.Log($"Data successfully sent to <a href=\"{url}\">{url}</a>");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static async Task<string> CreateCommit(CancellationToken cancellationToken,
|
||||
|
||||
public static async Task<string> CreateCommit(
|
||||
Base data,
|
||||
Client client,
|
||||
string streamId,
|
||||
string branchName,
|
||||
string objectId,
|
||||
string message)
|
||||
string message,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
string commitId = await client.CommitCreate(cancellationToken,
|
||||
string commitId = await client.CommitCreate(
|
||||
new CommitCreateInput
|
||||
{
|
||||
streamId = streamId,
|
||||
branchName = branchName,
|
||||
objectId = objectId,
|
||||
message = message,
|
||||
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion()),
|
||||
sourceApplication = HostApplications.Unity.GetVersion(
|
||||
CoreUtils.GetHostAppVersion()
|
||||
),
|
||||
totalChildrenCount = (int)data.totalChildrenCount,
|
||||
});
|
||||
|
||||
},
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
return commitId;
|
||||
}
|
||||
|
||||
|
||||
public bool GetSelection(
|
||||
[NotNullWhen(true)] out Client? client,
|
||||
[NotNullWhen(true)] out Stream? stream,
|
||||
[NotNullWhen(true)] out Branch? branch,
|
||||
[NotNullWhen(false)] out string? error)
|
||||
[NotNullWhen(false)] out string? error
|
||||
)
|
||||
{
|
||||
Account? account = Account.Selected;
|
||||
stream = Stream.Selected;
|
||||
branch = Branch.Selected;
|
||||
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
error = "Selected Account is null";
|
||||
client = null;
|
||||
return false;
|
||||
}
|
||||
client = Account.Client ?? new Client(account);
|
||||
|
||||
client = Account.Client ?? new Client(account);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
error = "Selected Stream is null";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (branch == null)
|
||||
|
||||
if (branch == null)
|
||||
{
|
||||
error = "Selected Branch is null";
|
||||
return false;
|
||||
@@ -155,8 +207,7 @@ namespace Speckle.ConnectorUnity.Components
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Open Speckle Stream in Browser")]
|
||||
protected void OpenUrlInBrowser()
|
||||
@@ -165,27 +216,29 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
#endif
|
||||
|
||||
public string GetSelectedUrl()
|
||||
{
|
||||
string serverUrl = Account.Selected!.serverInfo.url;
|
||||
string? streamId = Stream.Selected?.id;
|
||||
string? branchName = Branch.Selected?.name;
|
||||
|
||||
if (string.IsNullOrEmpty(streamId)) return serverUrl;
|
||||
if (!string.IsNullOrEmpty(branchName)) return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
|
||||
if (string.IsNullOrEmpty(streamId))
|
||||
return serverUrl;
|
||||
if (!string.IsNullOrEmpty(branchName))
|
||||
return $"{serverUrl}/streams/{streamId}/branches/{branchName}";
|
||||
return $"{serverUrl}/streams/{streamId}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
CoreUtils.SetupInit();
|
||||
Initialise(true);
|
||||
Converter = GetComponent<RecursiveConverter>();
|
||||
}
|
||||
|
||||
|
||||
protected void Initialise(bool forceRefresh = false)
|
||||
{
|
||||
CoreUtils.SetupInit();
|
||||
Account ??= new AccountSelection();
|
||||
Stream ??= new StreamSelection(Account);
|
||||
Branch ??= new BranchSelection(Stream);
|
||||
@@ -193,24 +246,47 @@ namespace Speckle.ConnectorUnity.Components
|
||||
Stream.Initialise();
|
||||
Branch.Initialise();
|
||||
Branch.OnSelectionChange = () => OnBranchSelectionChange?.Invoke(Branch.Selected);
|
||||
if(Account.Options is not {Length: > 0} || forceRefresh)
|
||||
if (Account.Options is not { Count: > 0 } || forceRefresh)
|
||||
Account.RefreshOptions();
|
||||
}
|
||||
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
CancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
//pass
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
|
||||
[Obsolete("use other overload")]
|
||||
public static async Task<string> SendDataAsync(
|
||||
CancellationToken cancellationToken,
|
||||
ServerTransport remoteTransport,
|
||||
Base data,
|
||||
Client client,
|
||||
string branchName,
|
||||
bool createCommit,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null
|
||||
)
|
||||
{
|
||||
return await SendDataAsync(
|
||||
remoteTransport,
|
||||
data,
|
||||
client,
|
||||
branchName,
|
||||
createCommit,
|
||||
onProgressAction,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-21
@@ -1,29 +1,50 @@
|
||||
using Objects.BuiltElements;
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Speckle View3D to a GameObject
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
/// <returns></returns>
|
||||
public GameObject View3DToNative(View3D speckleView)
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
var go = new GameObject(speckleView.name);
|
||||
var camera = go.AddComponent<Camera>();
|
||||
camera.transform.position = VectorByCoordinates(speckleView.origin.x, speckleView.origin.y, speckleView.origin.z,
|
||||
speckleView.origin.units);
|
||||
camera.transform.forward = VectorByCoordinates(speckleView.forwardDirection.x, speckleView.forwardDirection.y,
|
||||
speckleView.forwardDirection.z, speckleView.forwardDirection.units);
|
||||
camera.transform.up = VectorByCoordinates(speckleView.upDirection.x, speckleView.upDirection.y,
|
||||
speckleView.upDirection.z, speckleView.upDirection.units);
|
||||
[Tooltip("Enable/Disable the converting of Speckle View objects to Unity Cameras")]
|
||||
public bool shouldConvertViews;
|
||||
|
||||
AttachSpeckleProperties(go, speckleView.GetType(),speckleView.GetMembers());
|
||||
return go;
|
||||
public GameObject View3DToNative(View3D speckleView)
|
||||
{
|
||||
var go = new GameObject(speckleView.name);
|
||||
go.AddComponent<Camera>();
|
||||
|
||||
var matrix = View3DToMatrix(speckleView).transpose;
|
||||
ApplyMatrixToTransform(go.transform, matrix);
|
||||
|
||||
AttachSpeckleProperties(go, speckleView.GetType(), () => speckleView.GetMembers());
|
||||
return go;
|
||||
}
|
||||
|
||||
protected Matrix4x4 View3DToMatrix(View3D view)
|
||||
{
|
||||
var sf = GetConversionFactor(view.units);
|
||||
var tx = (float)(view.origin.x * sf);
|
||||
var ty = (float)(view.origin.z * sf); //Y up -> Z up coordinate transformation
|
||||
var tz = (float)(view.origin.y * sf);
|
||||
|
||||
var forward = new Vector3(
|
||||
(float)view.forwardDirection.x,
|
||||
(float)view.forwardDirection.z,
|
||||
(float)view.forwardDirection.y
|
||||
);
|
||||
var up = new Vector3(
|
||||
(float)view.upDirection.x,
|
||||
(float)view.upDirection.z,
|
||||
(float)view.upDirection.y
|
||||
);
|
||||
var right = Vector3.Cross(forward, up).normalized;
|
||||
|
||||
return new Matrix4x4(
|
||||
new Vector4(right.x, up.x, forward.x, tx),
|
||||
new Vector4(right.y, up.y, forward.y, ty),
|
||||
new Vector4(right.z, up.z, forward.z, tz),
|
||||
new Vector4(0, 0, 0, 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+145
-100
@@ -4,11 +4,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Objects.Other;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.ConnectorUnity.Wrappers;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Mesh = UnityEngine.Mesh;
|
||||
@@ -22,7 +22,6 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
|
||||
#region helper methods
|
||||
|
||||
|
||||
@@ -33,32 +32,42 @@ namespace Objects.Converter.Unity
|
||||
public Vector3 VectorByCoordinates(double x, double y, double z, double scaleFactor = 1d)
|
||||
{
|
||||
// switch y and z //TODO is this correct? LH -> RH
|
||||
return new Vector3((float) (x * scaleFactor), (float) (z * scaleFactor), (float) (y * scaleFactor));
|
||||
return new Vector3(
|
||||
(float)(x * scaleFactor),
|
||||
(float)(z * scaleFactor),
|
||||
(float)(y * scaleFactor)
|
||||
);
|
||||
}
|
||||
|
||||
public Vector3 VectorByCoordinates(double x, double y, double z, string units)
|
||||
{
|
||||
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
var f = GetConversionFactor(units);
|
||||
return VectorByCoordinates(x, y, z, f);
|
||||
}
|
||||
|
||||
public Vector3 VectorFromPoint(Point p) => VectorByCoordinates(p.x, p.y, p.z, p.units);
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="arr"></param>
|
||||
/// <param name="arr">flat list of x,y,z values</param>
|
||||
/// <param name="units"></param>
|
||||
/// <returns></returns>
|
||||
public Vector3[] ArrayToPoints(IList<double> arr, string units)
|
||||
/// <exception cref="ArgumentException">Length of <paramref name="arr"/> must be a multiple of 3</exception>
|
||||
public Vector3[] ArrayToPoints(IReadOnlyList<double> arr, string units)
|
||||
{
|
||||
if (arr.Count % 3 != 0) throw new Exception("Array malformed: length%3 != 0.");
|
||||
if (arr.Count % 3 != 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Array malformed: length not a multiple of 3",
|
||||
nameof(arr)
|
||||
);
|
||||
}
|
||||
|
||||
Vector3[] points = new Vector3[arr.Count / 3];
|
||||
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
double f = GetConversionFactor(units);
|
||||
|
||||
for (int i = 2, k = 0; i < arr.Count; i += 3)
|
||||
{
|
||||
points[k++] = VectorByCoordinates(arr[i - 2], arr[i - 1], arr[i], f);
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
@@ -67,19 +76,12 @@ namespace Objects.Converter.Unity
|
||||
|
||||
#region ToSpeckle
|
||||
|
||||
//TODO: more of these
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("", true)]
|
||||
public virtual Point PointToSpeckle(Vector3 p)
|
||||
{
|
||||
//switch y and z
|
||||
return new Point(p.x, p.z, p.y);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -88,7 +90,8 @@ namespace Objects.Converter.Unity
|
||||
|
||||
protected GameObject? NewPointBasedGameObject(Vector3[] points, string name)
|
||||
{
|
||||
if (points.Length == 0) return null;
|
||||
if (points.Length == 0)
|
||||
return null;
|
||||
|
||||
float pointDiameter = 1; //TODO: figure out how best to change this?
|
||||
|
||||
@@ -114,11 +117,10 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
Vector3 newPt = VectorByCoordinates(point.x, point.y, point.z, point.units);
|
||||
|
||||
var go = NewPointBasedGameObject(new Vector3[] {newPt, newPt}, point.speckle_type);
|
||||
var go = NewPointBasedGameObject(new[] { newPt, newPt }, point.speckle_type);
|
||||
return go;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Speckle <paramref name="line"/> to a <see cref="GameObject"/> with a <see cref="LineRenderer"/>
|
||||
/// </summary>
|
||||
@@ -126,7 +128,11 @@ namespace Objects.Converter.Unity
|
||||
/// <returns></returns>
|
||||
public GameObject? LineToNative(Line line)
|
||||
{
|
||||
var points = new List<Vector3> {VectorFromPoint(line.start), VectorFromPoint(line.end)};
|
||||
var points = new List<Vector3>
|
||||
{
|
||||
VectorFromPoint(line.start),
|
||||
VectorFromPoint(line.end)
|
||||
};
|
||||
|
||||
var go = NewPointBasedGameObject(points.ToArray(), line.speckle_type);
|
||||
return go;
|
||||
@@ -156,37 +162,63 @@ namespace Objects.Converter.Unity
|
||||
var go = NewPointBasedGameObject(points, curve.speckle_type);
|
||||
return go;
|
||||
}
|
||||
|
||||
|
||||
public Dictionary<string, object?> GetProperties(Base o) => GetProperties(o, typeof(Base));
|
||||
/// <summary>Gets all properties of <paramref name="o"/> except ignored ones. <br/>
|
||||
/// Ignored properties include properties of <see cref="Base"/>
|
||||
/// And some hardcoded ones that are likely to be converted (such as material, elements, and name)
|
||||
/// </summary>
|
||||
/// <param name="o">The speckle object to grab properties from</param>
|
||||
/// <returns>The properties</returns>
|
||||
/// <remarks>If you don't want to filter any properties, simply use <see cref="Base.GetMembers"/></remarks>
|
||||
public static Dictionary<string, object?> GetProperties(Base o) =>
|
||||
GetProperties(o, typeof(Base));
|
||||
|
||||
public Dictionary<string, object?> GetProperties(Base o, Type excludeType)
|
||||
/// <summary>
|
||||
/// Gets all properties of <paramref name="o"/> except ignored ones.<br/>
|
||||
/// Ignored properties include properties of <paramref name="excludeType"/>
|
||||
/// And some hardcoded ones that are likely to be converted (such as material, elements, and name)
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="GetProperties(Base)"/>
|
||||
/// <param name="excludeType">A <see cref="Type"/> whose properties should be ignored</param>
|
||||
public static Dictionary<string, object?> GetProperties(Base o, Type excludeType)
|
||||
{
|
||||
var excludeProps = new HashSet<string>(excludeType
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Select(x => x.Name));
|
||||
var excludeProps = new HashSet<string>(
|
||||
excludeType
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Select(x => x.Name)
|
||||
);
|
||||
|
||||
foreach (string alias in DisplayValuePropertyAliases)
|
||||
{
|
||||
excludeProps.Add(alias);
|
||||
}
|
||||
|
||||
excludeProps.Add("@Materials");
|
||||
excludeProps.Add("@Views");
|
||||
excludeProps.Add("renderMaterial");
|
||||
excludeProps.Add("typedDefinition");
|
||||
excludeProps.Add("definition");
|
||||
excludeProps.Add("geometry");
|
||||
excludeProps.Add("elements");
|
||||
excludeProps.Add("transform");
|
||||
excludeProps.Add("name");
|
||||
//excludeProps.Add("tag");
|
||||
excludeProps.Add("physicsLayer");
|
||||
|
||||
return o.GetMembers()
|
||||
.Where(x => !(excludeProps.Contains(x.Key) || excludeProps.Contains(x.Key.TrimStart('@'))))
|
||||
.ToDictionary(x => x.Key, x => (object?) x.Value);
|
||||
.Where(
|
||||
x =>
|
||||
!(
|
||||
excludeProps.Contains(x.Key)
|
||||
|| excludeProps.Contains(x.Key.TrimStart('@'))
|
||||
)
|
||||
)
|
||||
.ToDictionary(x => x.Key, x => (object?)x.Value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private Base CreateSpeckleObjectFromProperties(GameObject go)
|
||||
{
|
||||
@@ -194,7 +226,7 @@ namespace Objects.Converter.Unity
|
||||
if (sd == null || sd.Data == null)
|
||||
return new Base();
|
||||
|
||||
Base sobject = (Base) Activator.CreateInstance(sd.SpeckleType);
|
||||
Base sobject = (Base)Activator.CreateInstance(sd.SpeckleType);
|
||||
|
||||
foreach (var key in sd.Data.Keys)
|
||||
{
|
||||
@@ -211,43 +243,55 @@ namespace Objects.Converter.Unity
|
||||
return sobject;
|
||||
}
|
||||
|
||||
public GameObject? BlockToNative(BlockInstance block)
|
||||
public GameObject InstanceToNative(Instance instance)
|
||||
{
|
||||
if (block.blockDefinition == null)
|
||||
{
|
||||
Debug.Log($"Skipping {typeof(BlockInstance)} {block.id}, block definition was null");
|
||||
return null;
|
||||
}
|
||||
if (instance.definition == null)
|
||||
throw new ArgumentException("Definition was null", nameof(instance));
|
||||
|
||||
var defName = CoreUtils.GenerateObjectName(instance.definition);
|
||||
// Check for existing converted object
|
||||
if(LoadedAssets.TryGetObject(block.blockDefinition, out GameObject? existingGo))
|
||||
if (LoadedAssets.TryGetObject(instance.definition, out GameObject? existingGo))
|
||||
{
|
||||
var go = InstantiateCopy(existingGo);
|
||||
go.name = block.blockDefinition.name ?? "";
|
||||
TransformToNativeTransform(go.transform, block.transform);
|
||||
go.name = defName;
|
||||
TransformToNativeTransform(go.transform, instance.transform);
|
||||
return go;
|
||||
}
|
||||
|
||||
// Convert the block definition
|
||||
GameObject native = new GameObject(block.blockDefinition.name ?? "");
|
||||
|
||||
GameObject native = new(defName);
|
||||
|
||||
List<SMesh> meshes = new();
|
||||
List<Base> others = new();
|
||||
foreach (Base geo in block.blockDefinition.geometry)
|
||||
|
||||
var geometry = instance.definition is BlockDefinition b
|
||||
? b.geometry
|
||||
: GraphTraversal.TraverseMember(
|
||||
new[]
|
||||
{
|
||||
instance.definition["elements"] ?? instance.definition["@elements"],
|
||||
instance.definition["displayValue"] ?? instance.definition["@displayValue"],
|
||||
}
|
||||
);
|
||||
|
||||
foreach (Base geo in geometry)
|
||||
{
|
||||
if (geo is SMesh m) meshes.Add(m);
|
||||
else if (geo is IDisplayValue<List<SMesh>> s) meshes.AddRange(s.displayValue);
|
||||
else others.Add(geo);
|
||||
if (geo is SMesh m)
|
||||
meshes.Add(m);
|
||||
else if (geo is IDisplayValue<List<SMesh>> s)
|
||||
meshes.AddRange(s.displayValue);
|
||||
else
|
||||
others.Add(geo);
|
||||
}
|
||||
|
||||
if (meshes.Any())
|
||||
{
|
||||
if(!TryGetMeshFromCache(block.blockDefinition, meshes, out Mesh? nativeMesh, out _))
|
||||
if (!TryGetMeshFromCache(instance.definition, meshes, out Mesh? nativeMesh, out _))
|
||||
{
|
||||
MeshToNativeMesh(meshes, out nativeMesh);
|
||||
string name = AssetHelpers.GetObjectName(block.blockDefinition);
|
||||
string name = CoreUtils.GenerateObjectName(instance.definition);
|
||||
nativeMesh.name = name;
|
||||
LoadedAssets.TrySaveObject(block.blockDefinition, nativeMesh);
|
||||
LoadedAssets.TrySaveObject(instance.definition, nativeMesh);
|
||||
}
|
||||
var nativeMaterials = RenderMaterialsToNative(meshes);
|
||||
native.SafeMeshSet(nativeMesh, nativeMaterials);
|
||||
@@ -256,76 +300,82 @@ namespace Objects.Converter.Unity
|
||||
foreach (Base child in others)
|
||||
{
|
||||
GameObject? c = ConvertToNativeGameObject(child);
|
||||
if (c == null) continue;
|
||||
if (c == null)
|
||||
continue;
|
||||
c.transform.SetParent(native.transform, false);
|
||||
}
|
||||
|
||||
LoadedAssets.TrySaveObject(block.blockDefinition, native);
|
||||
|
||||
TransformToNativeTransform(native.transform, block.transform);
|
||||
if (block["name"] is string instanceName) native.name = instanceName;
|
||||
|
||||
LoadedAssets.TrySaveObject(instance.definition, native);
|
||||
|
||||
TransformToNativeTransform(native.transform, instance.transform);
|
||||
|
||||
var instanceName =
|
||||
CoreUtils.GetFriendlyObjectName(instance) != null
|
||||
? CoreUtils.GenerateObjectName(instance)
|
||||
: defName;
|
||||
|
||||
native.name = instanceName;
|
||||
|
||||
AttachSpeckleProperties(native, instance.GetType(), () => GetProperties(instance));
|
||||
|
||||
return native;
|
||||
}
|
||||
|
||||
|
||||
private static GameObject InstantiateCopy(GameObject existingGo)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
GameObject? prefabInstance = null;
|
||||
bool isPrefab = PrefabUtility.GetPrefabAssetType(existingGo) != PrefabAssetType.NotAPrefab;
|
||||
bool isPrefab =
|
||||
PrefabUtility.GetPrefabAssetType(existingGo) != PrefabAssetType.NotAPrefab;
|
||||
if (isPrefab)
|
||||
{
|
||||
GameObject? prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(existingGo);
|
||||
if (prefabAsset == null) prefabAsset = existingGo;
|
||||
prefabInstance = (GameObject) PrefabUtility.InstantiatePrefab(prefabAsset);
|
||||
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,
|
||||
/// to a Unity <see cref="Matrix4x4"/>. Applying Z -> Y up conversion, and applying units to the translation
|
||||
/// to a Unity <see cref="Matrix4x4"/>. Applying Z -> Y up conversion, and applying units to the translation
|
||||
/// </summary>
|
||||
/// <param name="speckleTransform"></param>
|
||||
/// <returns>Transformation matrix in Unity's coordinate system</returns>
|
||||
public Matrix4x4 TransformToNativeMatrix(STransform speckleTransform)
|
||||
{
|
||||
double VD(int i) => speckleTransform.value[i];
|
||||
float V(int i) => (float) VD(i);
|
||||
|
||||
var sf = Speckle.Core.Kits.Units.GetConversionFactor(speckleTransform.units, ModelUnits);
|
||||
var sf = GetConversionFactor(speckleTransform.units);
|
||||
var sMatrix = speckleTransform.matrix;
|
||||
|
||||
return new Matrix4x4
|
||||
{
|
||||
// Left (X -> X)
|
||||
[0, 0] = V(0),
|
||||
[2, 0] = V(4),
|
||||
[1, 0] = V(8),
|
||||
[3, 0] = V(12),
|
||||
|
||||
[0, 0] = (float)sMatrix.M11,
|
||||
[2, 0] = (float)sMatrix.M21,
|
||||
[1, 0] = (float)sMatrix.M31,
|
||||
[3, 0] = (float)sMatrix.M41,
|
||||
//Up (Z -> Y)
|
||||
[0, 2] = V(1),
|
||||
[2, 2] = V(5),
|
||||
[1, 2] = V(9),
|
||||
[3, 2] = V(13),
|
||||
|
||||
[0, 2] = (float)sMatrix.M12,
|
||||
[2, 2] = (float)sMatrix.M22,
|
||||
[1, 2] = (float)sMatrix.M32,
|
||||
[3, 2] = (float)sMatrix.M42,
|
||||
//Forwards (Y -> Z)
|
||||
[0, 1] = V(2),
|
||||
[2, 1] = V(6),
|
||||
[1, 1] = V(10),
|
||||
[3, 1] = V(14),
|
||||
|
||||
[0, 1] = (float)sMatrix.M13,
|
||||
[2, 1] = (float)sMatrix.M23,
|
||||
[1, 1] = (float)sMatrix.M33,
|
||||
[3, 1] = (float)sMatrix.M43,
|
||||
//Translation
|
||||
[0, 3] = (float) (VD(3) * sf),
|
||||
[2, 3] = (float) (VD(7) * sf),
|
||||
[1, 3] = (float) (VD(11) * sf),
|
||||
[3, 3] = V(15),
|
||||
[0, 3] = (float)(sMatrix.M14 * sf),
|
||||
[2, 3] = (float)(sMatrix.M24 * sf),
|
||||
[1, 3] = (float)(sMatrix.M34 * sf),
|
||||
[3, 3] = (float)sMatrix.M44,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -337,16 +387,11 @@ namespace Objects.Converter.Unity
|
||||
|
||||
protected static void ApplyMatrixToTransform(Transform transform, Matrix4x4 m)
|
||||
{
|
||||
transform.localScale =
|
||||
m.lossyScale; //doesn't work for non TRS, maybe we could fallback to squareSum approach (see TransformVectorized::SetFromMatrix in UE src)
|
||||
transform.localScale = m.lossyScale; //doesn't work for non TRS, maybe we could fallback to squareSum approach (see TransformVectorized::SetFromMatrix in UE src)
|
||||
|
||||
//We can't use m.rotation, as it gives us incorrect results (perhaps because of RH -> LH? or maybe our MatrixToNative is broken?)
|
||||
transform.localRotation = Quaternion.LookRotation(
|
||||
m.GetColumn(2),
|
||||
m.GetColumn(1)
|
||||
);
|
||||
transform.localRotation = Quaternion.LookRotation(m.GetColumn(2), m.GetColumn(1));
|
||||
transform.localPosition = m.GetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+101
-172
@@ -3,31 +3,22 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Objects.Other;
|
||||
using Objects.Utils;
|
||||
using Speckle.ConnectorUnity.NativeCache;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Material = UnityEngine.Material;
|
||||
using Mesh = UnityEngine.Mesh;
|
||||
using SMesh = Objects.Geometry.Mesh;
|
||||
using SColor = System.Drawing.Color;
|
||||
using Transform = UnityEngine.Transform;
|
||||
using STransform = Objects.Other.Transform;
|
||||
|
||||
#nullable enable
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
|
||||
protected static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
|
||||
protected static readonly int Metallic = Shader.PropertyToID("_Metallic");
|
||||
protected static readonly int Glossiness = Shader.PropertyToID("_Glossiness");
|
||||
|
||||
#region ToSpeckle
|
||||
|
||||
|
||||
public virtual List<SMesh>? MeshToSpeckle(MeshFilter meshFilter)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
@@ -37,7 +28,8 @@ namespace Objects.Converter.Unity
|
||||
Material[]? materials = meshFilter.GetComponent<Renderer>()?.materials;
|
||||
var nativeMesh = meshFilter.mesh;
|
||||
#endif
|
||||
if (nativeMesh == null) return null;
|
||||
if (nativeMesh == null)
|
||||
return null;
|
||||
|
||||
List<SMesh> convertedMeshes = new List<SMesh>(nativeMesh.subMeshCount);
|
||||
for (int i = 0; i < nativeMesh.subMeshCount; i++)
|
||||
@@ -50,31 +42,50 @@ namespace Objects.Converter.Unity
|
||||
// //TODO convert as pointcloud
|
||||
// continue;
|
||||
case MeshTopology.Triangles:
|
||||
converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 3);
|
||||
converted = SubMeshToSpeckle(
|
||||
nativeMesh,
|
||||
meshFilter.transform,
|
||||
subMesh,
|
||||
i,
|
||||
3
|
||||
);
|
||||
convertedMeshes.Add(converted);
|
||||
break;
|
||||
case MeshTopology.Quads:
|
||||
converted = SubMeshToSpeckle(nativeMesh, meshFilter.transform, subMesh, i, 4);
|
||||
converted = SubMeshToSpeckle(
|
||||
nativeMesh,
|
||||
meshFilter.transform,
|
||||
subMesh,
|
||||
i,
|
||||
4
|
||||
);
|
||||
convertedMeshes.Add(converted);
|
||||
break;
|
||||
default:
|
||||
Debug.LogError(
|
||||
$"Failed to convert submesh {i} of {typeof(GameObject)} {meshFilter.gameObject.name} to Speckle, Unsupported Mesh Topography {subMesh.topology}. Submesh will be ignored.");
|
||||
$"Failed to convert submesh {i} of {typeof(GameObject)} {meshFilter.gameObject.name} to Speckle, Unsupported Mesh Topography {subMesh.topology}. Submesh will be ignored."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (materials == null || materials.Length <= i) continue;
|
||||
if (materials == null || materials.Length <= i)
|
||||
continue;
|
||||
|
||||
Material mat = materials[i];
|
||||
if (mat != null) converted["renderMaterial"] = MaterialToSpeckle(mat);
|
||||
if (mat != null)
|
||||
converted["renderMaterial"] = MaterialToSpeckle(mat);
|
||||
}
|
||||
|
||||
return convertedMeshes;
|
||||
}
|
||||
|
||||
|
||||
protected virtual SMesh SubMeshToSpeckle(Mesh nativeMesh, Transform instanceTransform,
|
||||
SubMeshDescriptor subMesh, int subMeshIndex, int faceN)
|
||||
protected virtual SMesh SubMeshToSpeckle(
|
||||
Mesh nativeMesh,
|
||||
Transform instanceTransform,
|
||||
SubMeshDescriptor subMesh,
|
||||
int subMeshIndex,
|
||||
int faceN
|
||||
)
|
||||
{
|
||||
var nFaces = nativeMesh.GetIndices(subMeshIndex, true);
|
||||
int numFaces = nFaces.Length / faceN;
|
||||
@@ -124,7 +135,7 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
|
||||
var nColors = nativeMesh.colors.Skip(indexOffset).Take(vertexTake).ToArray();
|
||||
;
|
||||
|
||||
List<int> sColors = new List<int>(nColors.Length);
|
||||
sColors.AddRange(nColors.Select(c => c.ToIntColor()));
|
||||
|
||||
@@ -148,56 +159,6 @@ namespace Objects.Converter.Unity
|
||||
return convertedMesh;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of officially supported shaders. Will attempt to convert shaders not on this list, but will throw warning.
|
||||
/// </summary>
|
||||
protected static HashSet<string> SupportedShadersToSpeckle = new()
|
||||
{
|
||||
"Legacy Shaders/Transparent/Diffuse", "Standard"
|
||||
};
|
||||
|
||||
public virtual RenderMaterial MaterialToSpeckle(Material nativeMaterial)
|
||||
{
|
||||
//Warning message for unknown shaders
|
||||
if (!SupportedShadersToSpeckle.Contains(nativeMaterial.shader.name))
|
||||
Debug.LogWarning(
|
||||
$"Material Shader \"{nativeMaterial.shader.name}\" is not explicitly supported, the resulting material may be incorrect");
|
||||
|
||||
var color = nativeMaterial.color;
|
||||
var opacity = 1f;
|
||||
if (nativeMaterial.shader.name.ToLower().Contains("transparent"))
|
||||
{
|
||||
opacity = color.a;
|
||||
color.a = 255;
|
||||
}
|
||||
|
||||
var emissive = nativeMaterial.IsKeywordEnabled("_EMISSION")
|
||||
? nativeMaterial.GetColor(EmissionColor).ToIntColor()
|
||||
: SColor.Black.ToArgb();
|
||||
|
||||
var materialName = !string.IsNullOrWhiteSpace(nativeMaterial.name)
|
||||
? nativeMaterial.name.Replace("(Instance)", string.Empty).TrimEnd()
|
||||
: $"material-{Guid.NewGuid().ToString().Substring(0, 8)}";
|
||||
|
||||
var metalness = nativeMaterial.HasProperty(Metallic)
|
||||
? nativeMaterial.GetFloat(Metallic)
|
||||
: 0;
|
||||
|
||||
var roughness = nativeMaterial.HasProperty(Glossiness)
|
||||
? 1 - nativeMaterial.GetFloat(Glossiness)
|
||||
: 1;
|
||||
|
||||
return new RenderMaterial
|
||||
{
|
||||
name = materialName,
|
||||
diffuse = color.ToIntColor(),
|
||||
opacity = opacity,
|
||||
metalness = metalness,
|
||||
roughness = roughness,
|
||||
emissive = emissive,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -209,13 +170,10 @@ namespace Objects.Converter.Unity
|
||||
/// <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)
|
||||
public GameObject MeshesToNative(Base element, IReadOnlyCollection<SMesh> meshes)
|
||||
{
|
||||
if (!meshes.Any())
|
||||
{
|
||||
Debug.Log($"Skipping {element.GetType()} {element.id}, zero {typeof(SMesh)} provided");
|
||||
return null;
|
||||
}
|
||||
throw new ArgumentException("Expected at least one Mesh", nameof(meshes));
|
||||
|
||||
Material[] nativeMaterials = RenderMaterialsToNative(meshes);
|
||||
|
||||
@@ -223,11 +181,11 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
//Convert a new one
|
||||
MeshToNativeMesh(meshes, out nativeMesh, out center);
|
||||
string name = AssetHelpers.GetObjectName(element);
|
||||
string name = CoreUtils.GenerateObjectName(element);
|
||||
nativeMesh.name = name;
|
||||
LoadedAssets.TrySaveObject(element, nativeMesh);
|
||||
}
|
||||
|
||||
|
||||
var go = new GameObject();
|
||||
go.transform.position = center;
|
||||
go.SafeMeshSet(nativeMesh, nativeMaterials);
|
||||
@@ -235,21 +193,17 @@ namespace Objects.Converter.Unity
|
||||
return go;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts <paramref name="speckleMesh"/> to a <see cref="GameObject"/> with a <see cref="MeshRenderer"/>
|
||||
/// </summary>
|
||||
/// <param name="speckleMesh">Mesh to convert</param>
|
||||
/// <returns></returns>
|
||||
public GameObject? MeshToNative(SMesh speckleMesh)
|
||||
public GameObject MeshToNative(SMesh speckleMesh)
|
||||
{
|
||||
if (speckleMesh.vertices.Count == 0 || speckleMesh.faces.Count == 0)
|
||||
{
|
||||
Debug.Log($"Skipping mesh {speckleMesh.id}, mesh data was empty");
|
||||
return null;
|
||||
}
|
||||
throw new ArgumentException("mesh data was empty", nameof(speckleMesh));
|
||||
|
||||
GameObject? converted = MeshesToNative(speckleMesh, new[] {speckleMesh});
|
||||
GameObject converted = MeshesToNative(speckleMesh, new[] { speckleMesh });
|
||||
|
||||
// Raw meshes shouldn't have dynamic props to attach
|
||||
//if (converted != null) AttachSpeckleProperties(converted,speckleMesh.GetType(), GetProperties(speckleMesh, typeof(Mesh)));
|
||||
@@ -257,17 +211,18 @@ namespace Objects.Converter.Unity
|
||||
return converted;
|
||||
}
|
||||
|
||||
protected bool TryGetMeshFromCache(Base element, IReadOnlyCollection<SMesh> meshes, [NotNullWhen(true)] out Mesh? nativeMesh, out Vector3 center)
|
||||
protected bool TryGetMeshFromCache(
|
||||
Base element,
|
||||
IReadOnlyCollection<SMesh> meshes,
|
||||
[NotNullWhen(true)] out Mesh? nativeMesh,
|
||||
out Vector3 center
|
||||
)
|
||||
{
|
||||
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 _,
|
||||
out _,
|
||||
out _);
|
||||
//todo This is pretty inefficient, having to convert the mesh data anyway just to get the center... eek
|
||||
MeshDataToNative(meshes, out List<Vector3> verts, out _, out _, out _);
|
||||
center = CalculateBounds(verts).center;
|
||||
return true;
|
||||
}
|
||||
@@ -276,30 +231,33 @@ namespace Objects.Converter.Unity
|
||||
center = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts Speckle <see cref="SMesh"/>s as a native <see cref="Mesh"/> object
|
||||
/// Converts Speckle <see cref="SMesh"/>s as a native <see cref="Mesh"/> object
|
||||
/// </summary>
|
||||
/// <param name="meshes">meshes to be converted as SubMeshes</param>
|
||||
/// <param name="nativeMesh">The converted native mesh</param>
|
||||
public void MeshToNativeMesh(IReadOnlyCollection<SMesh> meshes, out Mesh nativeMesh)
|
||||
=> MeshToNativeMesh(meshes, out nativeMesh, out _, false);
|
||||
public void MeshToNativeMesh(IReadOnlyCollection<SMesh> meshes, out Mesh nativeMesh) =>
|
||||
MeshToNativeMesh(meshes, out nativeMesh, out _, false);
|
||||
|
||||
|
||||
/// <inheritdoc cref="MeshToNativeMesh(IReadOnlyCollection{Objects.Geometry.Mesh},out Mesh)"/>
|
||||
/// <param name="recenterVerts">when true, will recenter vertices</param>
|
||||
/// <param name="center">Center position for the mesh</param>
|
||||
public void MeshToNativeMesh(IReadOnlyCollection<SMesh> meshes,
|
||||
public void MeshToNativeMesh(
|
||||
IReadOnlyCollection<SMesh> meshes,
|
||||
out Mesh nativeMesh,
|
||||
out Vector3 center,
|
||||
bool recenterVerts = true)
|
||||
bool recenterVerts = true
|
||||
)
|
||||
{
|
||||
MeshDataToNative(meshes,
|
||||
MeshDataToNative(
|
||||
meshes,
|
||||
out List<Vector3> verts,
|
||||
out List<Vector2> uvs,
|
||||
out List<Color> vertexColors,
|
||||
out List<List<int>> subMeshes);
|
||||
|
||||
out List<List<int>> subMeshes
|
||||
);
|
||||
|
||||
Debug.Assert(verts.Count >= 0);
|
||||
|
||||
center = recenterVerts ? RecenterVertices(verts) : Vector3.zero;
|
||||
@@ -310,7 +268,6 @@ namespace Objects.Converter.Unity
|
||||
nativeMesh.SetUVs(0, uvs);
|
||||
nativeMesh.SetColors(vertexColors);
|
||||
|
||||
|
||||
int j = 0;
|
||||
foreach (var subMeshTriangles in subMeshes)
|
||||
{
|
||||
@@ -326,12 +283,14 @@ namespace Objects.Converter.Unity
|
||||
nativeMesh.RecalculateNormals();
|
||||
nativeMesh.RecalculateTangents();
|
||||
}
|
||||
|
||||
public void MeshDataToNative(IReadOnlyCollection<SMesh> meshes,
|
||||
|
||||
public void MeshDataToNative(
|
||||
IReadOnlyCollection<SMesh> meshes,
|
||||
out List<Vector3> verts,
|
||||
out List<Vector2> uvs,
|
||||
out List<Color> vertexColors,
|
||||
out List<List<int>> subMeshes)
|
||||
out List<List<int>> subMeshes
|
||||
)
|
||||
{
|
||||
verts = new List<Vector3>();
|
||||
uvs = new List<Vector2>();
|
||||
@@ -340,17 +299,22 @@ namespace Objects.Converter.Unity
|
||||
|
||||
foreach (SMesh m in meshes)
|
||||
{
|
||||
if (m.vertices.Count == 0 || m.faces.Count == 0) continue;
|
||||
|
||||
if (m.vertices.Count == 0 || m.faces.Count == 0)
|
||||
continue;
|
||||
|
||||
List<int> tris = new List<int>();
|
||||
SubmeshToNative(m, verts, tris, uvs, vertexColors);
|
||||
subMeshes.Add(tris);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void SubmeshToNative(SMesh speckleMesh, List<Vector3> verts, List<int> tris, List<Vector2> texCoords,
|
||||
List<Color> vertexColors)
|
||||
protected void SubmeshToNative(
|
||||
SMesh speckleMesh,
|
||||
List<Vector3> verts,
|
||||
List<int> tris,
|
||||
List<Vector2> texCoords,
|
||||
List<Color> vertexColors
|
||||
)
|
||||
{
|
||||
speckleMesh.AlignVerticesWithTexCoordsByIndex();
|
||||
speckleMesh.TriangulateMesh();
|
||||
@@ -364,7 +328,8 @@ namespace Objects.Converter.Unity
|
||||
bool hasValidUVs = speckleMesh.TextureCoordinatesCount == speckleMesh.VerticesCount;
|
||||
if (speckleMesh.textureCoordinates.Count > 0 && !hasValidUVs)
|
||||
Debug.LogWarning(
|
||||
$"Expected number of UV coordinates to equal vertices. Got {speckleMesh.TextureCoordinatesCount} expected {speckleMesh.VerticesCount}. \nID = {speckleMesh.id}");
|
||||
$"Expected number of UV coordinates to equal vertices. Got {speckleMesh.TextureCoordinatesCount} expected {speckleMesh.VerticesCount}. \nID = {speckleMesh.id}"
|
||||
);
|
||||
|
||||
if (hasValidUVs)
|
||||
{
|
||||
@@ -372,14 +337,20 @@ namespace Objects.Converter.Unity
|
||||
for (int j = 0; j < speckleMesh.TextureCoordinatesCount; j++)
|
||||
{
|
||||
var (u, v) = speckleMesh.GetTextureCoordinate(j);
|
||||
texCoords.Add(new Vector2((float) u, (float) v));
|
||||
texCoords.Add(new Vector2((float)u, (float)v));
|
||||
}
|
||||
}
|
||||
else if (speckleMesh.bbox != null)
|
||||
{
|
||||
// Attempt to generate some crude UV coordinates using bbox
|
||||
texCoords.AddRange(GenerateUV(indexOffset, verts, (float) speckleMesh.bbox.xSize.Length,
|
||||
(float) speckleMesh.bbox.ySize.Length));
|
||||
texCoords.AddRange(
|
||||
GenerateUV(
|
||||
indexOffset,
|
||||
verts,
|
||||
(float)speckleMesh.bbox.xSize.Length,
|
||||
(float)speckleMesh.bbox.ySize.Length
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -397,12 +368,13 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
//TODO what if only some submeshes have colors?
|
||||
Debug.LogWarning(
|
||||
$"{typeof(SMesh)} {speckleMesh.id} has invalid number of vertex {nameof(SMesh.colors)}. Expected 0 or {speckleMesh.VerticesCount}, got {speckleMesh.colors.Count}");
|
||||
$"{typeof(SMesh)} {speckleMesh.id} has invalid number of vertex {nameof(SMesh.colors)}. Expected 0 or {speckleMesh.VerticesCount}, got {speckleMesh.colors.Count}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert faces
|
||||
tris.Capacity += (int) (speckleMesh.faces.Count / 4f) * 3;
|
||||
tris.Capacity += (int)(speckleMesh.faces.Count / 4f) * 3;
|
||||
|
||||
for (int i = 0; i < speckleMesh.faces.Count; i += 4)
|
||||
{
|
||||
@@ -413,89 +385,46 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected static IEnumerable<Vector2> GenerateUV(int indexOffset, IReadOnlyList<Vector3> verts, float xSize,
|
||||
float ySize)
|
||||
protected static IEnumerable<Vector2> GenerateUV(
|
||||
int indexOffset,
|
||||
IReadOnlyList<Vector3> verts,
|
||||
float xSize,
|
||||
float ySize
|
||||
)
|
||||
{
|
||||
var uv = new Vector2[verts.Count - indexOffset];
|
||||
for (int i = 0; i < verts.Count - indexOffset; i++)
|
||||
{
|
||||
|
||||
var vert = verts[i];
|
||||
uv[i] = new Vector2(vert.x / xSize, vert.y / ySize);
|
||||
}
|
||||
|
||||
return uv;
|
||||
}
|
||||
|
||||
|
||||
protected static Vector3 RecenterVertices(IList<Vector3> vertices)
|
||||
{
|
||||
if (!vertices.Any()) return Vector3.zero;
|
||||
|
||||
if (!vertices.Any())
|
||||
return Vector3.zero;
|
||||
|
||||
Bounds meshBounds = CalculateBounds(vertices);
|
||||
|
||||
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
vertices[i] -= meshBounds.center;
|
||||
|
||||
return meshBounds.center;
|
||||
}
|
||||
|
||||
|
||||
protected static Bounds CalculateBounds(IList<Vector3> points)
|
||||
{
|
||||
Bounds b = new Bounds {center = points[0]};
|
||||
Bounds b = new Bounds { center = points[0] };
|
||||
|
||||
foreach (var p in points)
|
||||
b.Encapsulate(p);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
public Material[] RenderMaterialsToNative(IEnumerable<SMesh> meshes)
|
||||
{
|
||||
return meshes.Select(m => RenderMaterialToNative(m["renderMaterial"] as RenderMaterial)).ToArray();
|
||||
}
|
||||
|
||||
//Just used as cache key for the default (null) material
|
||||
private static RenderMaterial defaultMaterialPlaceholder = new(){id="defaultMaterial"};
|
||||
public Material RenderMaterialToNative(RenderMaterial? renderMaterial)
|
||||
{
|
||||
//todo support more complex materials
|
||||
|
||||
// 1. If no renderMaterial was passed, use default material
|
||||
if (renderMaterial == null)
|
||||
{
|
||||
if (!LoadedAssets.TryGetObject(defaultMaterialPlaceholder, out Material? defaultMaterial))
|
||||
{
|
||||
defaultMaterial = new Material(Shader.Find("Standard"));
|
||||
LoadedAssets.TrySaveObject(defaultMaterialPlaceholder, defaultMaterial);
|
||||
}
|
||||
return defaultMaterial;
|
||||
}
|
||||
|
||||
// 2. Try get existing/override material from asset cache
|
||||
if (LoadedAssets.TryGetObject(renderMaterial, out Material? loadedMaterial)) return loadedMaterial;
|
||||
|
||||
// 3. Otherwise, convert fresh!
|
||||
string shaderName = renderMaterial.opacity < 1 ? "Transparent/Diffuse" : "Standard";
|
||||
Shader shader = Shader.Find(shaderName);
|
||||
var mat = new Material(shader);
|
||||
|
||||
var c = renderMaterial.diffuse.ToUnityColor();
|
||||
mat.color = new Color(c.r, c.g, c.b, (float) renderMaterial.opacity);
|
||||
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());
|
||||
|
||||
LoadedAssets.TrySaveObject(renderMaterial, mat);
|
||||
return mat;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Helpers;
|
||||
using Objects.Other;
|
||||
using UnityEngine;
|
||||
using Material = UnityEngine.Material;
|
||||
using SMesh = Objects.Geometry.Mesh;
|
||||
using SColor = System.Drawing.Color;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
protected const string DefaultShader = "Standard";
|
||||
|
||||
protected static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");
|
||||
protected static readonly int Metallic = Shader.PropertyToID("_Metallic");
|
||||
protected static readonly int Glossiness = Shader.PropertyToID("_Glossiness");
|
||||
|
||||
[field: SerializeField]
|
||||
[field: Tooltip("The shader to use when converting opaque RenderMaterials to native")]
|
||||
public Shader OpaqueMaterialShader { get; set; }
|
||||
|
||||
[field: SerializeField]
|
||||
[field: Tooltip("The shader to use when converting non-opaque RenderMaterials to native")]
|
||||
public Shader TranslucentMaterialShader { get; set; }
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// List of officially supported shaders. We will attempt to convert shaders not on this list, but will throw warning.
|
||||
/// </summary>
|
||||
protected static HashSet<string> SupportedShadersToSpeckle =
|
||||
new() { "Legacy Shaders/Transparent/Diffuse", "Standard" };
|
||||
|
||||
#region ToNative
|
||||
|
||||
public Material[] RenderMaterialsToNative(IEnumerable<SMesh> meshes)
|
||||
{
|
||||
return meshes
|
||||
.Select(m => RenderMaterialToNative(m["renderMaterial"] as RenderMaterial))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
//Just used as cache key for the default (null) material
|
||||
private static readonly RenderMaterial DefaultMaterialPlaceholder =
|
||||
new() { id = "defaultMaterial" };
|
||||
|
||||
public virtual Material RenderMaterialToNative(RenderMaterial? renderMaterial)
|
||||
{
|
||||
//todo support more complex materials
|
||||
|
||||
// 1. If no renderMaterial was passed, use default material
|
||||
if (renderMaterial == null)
|
||||
{
|
||||
if (
|
||||
!LoadedAssets.TryGetObject(
|
||||
DefaultMaterialPlaceholder,
|
||||
out Material? defaultMaterial
|
||||
)
|
||||
)
|
||||
{
|
||||
defaultMaterial = new Material(OpaqueMaterialShader);
|
||||
LoadedAssets.TrySaveObject(DefaultMaterialPlaceholder, defaultMaterial);
|
||||
}
|
||||
return defaultMaterial;
|
||||
}
|
||||
|
||||
// 2. Try get existing/override material from asset cache
|
||||
if (LoadedAssets.TryGetObject(renderMaterial, out Material? loadedMaterial))
|
||||
return loadedMaterial;
|
||||
|
||||
// 3. Otherwise, convert fresh!
|
||||
string name = CoreUtils.GenerateObjectName(renderMaterial);
|
||||
Color diffuse = renderMaterial.diffuse.ToUnityColor();
|
||||
bool isOpaque = Math.Abs(renderMaterial.opacity - 1d) < Constants.EPS;
|
||||
Color color = new(diffuse.r, diffuse.g, diffuse.b, (float)renderMaterial.opacity);
|
||||
float metalic = (float)renderMaterial.metalness;
|
||||
float gloss = 1f - (float)renderMaterial.roughness;
|
||||
bool hasEmission = renderMaterial.emissive != SColor.Black.ToArgb();
|
||||
Color emission = renderMaterial.emissive.ToUnityColor();
|
||||
|
||||
Shader shader =
|
||||
renderMaterial.opacity < 1 ? TranslucentMaterialShader : OpaqueMaterialShader;
|
||||
var mat = new Material(shader);
|
||||
mat.color = color;
|
||||
mat.name = name;
|
||||
mat.SetFloat(Metallic, metalic);
|
||||
mat.SetFloat(Glossiness, gloss);
|
||||
mat.SetColor(EmissionColor, emission);
|
||||
if (hasEmission)
|
||||
mat.EnableKeyword("_EMISSION");
|
||||
|
||||
if (!isOpaque)
|
||||
{
|
||||
if (shader.name == "Standard")
|
||||
{
|
||||
ShaderHelpers.SetupMaterialWithBlendMode_Standard(
|
||||
mat,
|
||||
ShaderHelpers.BlendMode.Transparent,
|
||||
true
|
||||
);
|
||||
}
|
||||
else if (shader.name == "Lit") //URP lit
|
||||
{ }
|
||||
}
|
||||
|
||||
LoadedAssets.TrySaveObject(renderMaterial, mat);
|
||||
return mat;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToSpeckle
|
||||
|
||||
public virtual RenderMaterial MaterialToSpeckle(Material nativeMaterial)
|
||||
{
|
||||
//Warning message for unknown shaders
|
||||
if (!SupportedShadersToSpeckle.Contains(nativeMaterial.shader.name))
|
||||
Debug.LogWarning(
|
||||
$"Material Shader \"{nativeMaterial.shader.name}\" is not explicitly supported, the resulting material may be incorrect"
|
||||
);
|
||||
|
||||
var color = nativeMaterial.color;
|
||||
var opacity = 1f;
|
||||
if (nativeMaterial.shader.name.ToLower().Contains("transparent"))
|
||||
{
|
||||
opacity = color.a;
|
||||
color.a = 255;
|
||||
}
|
||||
|
||||
var emissive = nativeMaterial.IsKeywordEnabled("_EMISSION")
|
||||
? nativeMaterial.GetColor(EmissionColor).ToIntColor()
|
||||
: SColor.Black.ToArgb();
|
||||
|
||||
var materialName = !string.IsNullOrWhiteSpace(nativeMaterial.name)
|
||||
? nativeMaterial.name.Replace("(Instance)", string.Empty).TrimEnd()
|
||||
: $"material-{Guid.NewGuid().ToString().Substring(0, 8)}";
|
||||
|
||||
var metalness = nativeMaterial.HasProperty(Metallic)
|
||||
? nativeMaterial.GetFloat(Metallic)
|
||||
: 0;
|
||||
|
||||
var roughness = nativeMaterial.HasProperty(Glossiness)
|
||||
? 1 - nativeMaterial.GetFloat(Glossiness)
|
||||
: 1;
|
||||
|
||||
return new RenderMaterial
|
||||
{
|
||||
name = materialName,
|
||||
diffuse = color.ToIntColor(),
|
||||
opacity = opacity,
|
||||
metalness = metalness,
|
||||
roughness = roughness,
|
||||
emissive = emissive,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f91633e63e64685a6b2a4fd609312a0
|
||||
timeCreated: 1689085502
|
||||
+16
-17
@@ -1,23 +1,22 @@
|
||||
using Objects.Geometry;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using Mesh = Objects.Geometry.Mesh;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
public string ModelUnits = Speckle.Core.Kits.Units.Meters; //the default Unity units are meters
|
||||
private double ScaleToNative(double value, string units)
|
||||
public partial class ConverterUnity
|
||||
{
|
||||
var f = Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
return value * f;
|
||||
[field: SerializeField]
|
||||
[field: Tooltip("The units used by conversion functions")]
|
||||
public string ModelUnits { get; set; } = Speckle.Core.Kits.Units.Meters; //the default Unity units are meters
|
||||
|
||||
private double ScaleToNative(double value, string units)
|
||||
{
|
||||
var f = GetConversionFactor(units);
|
||||
return value * f;
|
||||
}
|
||||
|
||||
private double GetConversionFactor(string units)
|
||||
{
|
||||
return Speckle.Core.Kits.Units.GetConversionFactor(units, ModelUnits);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,14 @@ using Object = UnityEngine.Object;
|
||||
|
||||
namespace Objects.Converter.Unity
|
||||
{
|
||||
[Serializable]
|
||||
public partial class ConverterUnity : ISpeckleConverter
|
||||
{
|
||||
[Tooltip(
|
||||
"Enable/Disable attaching non-converted properties to received objects. Disabling this will lead to lighter weight objects that serialize much faster."
|
||||
)]
|
||||
public bool shouldAttachProperties = true;
|
||||
|
||||
#region implemented methods
|
||||
|
||||
public void SetConverterSettings(object settings) => throw new NotImplementedException();
|
||||
@@ -26,10 +32,11 @@ namespace Objects.Converter.Unity
|
||||
public string Author => "Speckle";
|
||||
public string WebsiteOrEmail => "https://speckle.systems";
|
||||
|
||||
public ProgressReport Report { get; }
|
||||
public ProgressReport Report => throw new NotImplementedException();
|
||||
public ReceiveMode ReceiveMode { get; set; }
|
||||
|
||||
public IEnumerable<string> GetServicedApplications() => new string[] {HostApplications.Unity.Name};
|
||||
public IEnumerable<string> GetServicedApplications() =>
|
||||
new[] { HostApplications.Unity.Name };
|
||||
|
||||
public AbstractNativeCache LoadedAssets { get; private set; }
|
||||
|
||||
@@ -37,13 +44,23 @@ namespace Objects.Converter.Unity
|
||||
{
|
||||
if (doc is not AbstractNativeCache context)
|
||||
throw new ArgumentException(
|
||||
$"Expected {nameof(doc)} to be of type {typeof(Dictionary<string, Object>)}", nameof(doc));
|
||||
$"Expected {nameof(doc)} to be of type {typeof(Dictionary<string, Object>)}",
|
||||
nameof(doc)
|
||||
);
|
||||
LoadedAssets = context;
|
||||
|
||||
if (OpaqueMaterialShader == null)
|
||||
OpaqueMaterialShader = Shader.Find(DefaultShader);
|
||||
|
||||
if (TranslucentMaterialShader == null)
|
||||
TranslucentMaterialShader = Shader.Find(DefaultShader);
|
||||
}
|
||||
|
||||
public void SetContextObjects(List<ApplicationObject> objects) => throw new NotImplementedException();
|
||||
public void SetContextObjects(List<ApplicationObject> objects) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public void SetPreviousContextObjects(List<ApplicationObject> objects) => throw new NotImplementedException();
|
||||
public void SetPreviousContextObjects(List<ApplicationObject> objects) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
#nullable enable
|
||||
public object? ConvertToNative(Base @object) => ConvertToNativeGameObject(@object);
|
||||
@@ -51,7 +68,9 @@ namespace Objects.Converter.Unity
|
||||
public Base ConvertToSpeckle(object @object)
|
||||
{
|
||||
if (!(@object is GameObject go))
|
||||
throw new NotSupportedException($"Cannot convert object of type {@object.GetType()} to Speckle");
|
||||
throw new NotSupportedException(
|
||||
$"Cannot convert object of type {@object.GetType()} to Speckle"
|
||||
);
|
||||
return ConvertGameObjectToSpeckle(go);
|
||||
}
|
||||
|
||||
@@ -87,14 +106,16 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to convert {component.GetType()} component\n{e}", component);
|
||||
Debug.LogError(
|
||||
$"Failed to convert {component.GetType()} component\n{e}",
|
||||
component
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return speckleObject;
|
||||
}
|
||||
|
||||
|
||||
public GameObject? ConvertToNativeGameObject(Base speckleObject)
|
||||
{
|
||||
switch (speckleObject)
|
||||
@@ -111,8 +132,10 @@ namespace Objects.Converter.Unity
|
||||
return View3DToNative(v);
|
||||
case Mesh o:
|
||||
return MeshToNative(o);
|
||||
case BlockInstance o:
|
||||
return BlockToNative(o);
|
||||
case Instance o:
|
||||
return InstanceToNative(o);
|
||||
case Collection c:
|
||||
return CollectionToNative(c);
|
||||
default:
|
||||
|
||||
//Object is not a raw geometry, convert it as display value element
|
||||
@@ -120,7 +143,11 @@ namespace Objects.Converter.Unity
|
||||
if (element != null)
|
||||
{
|
||||
if (!speckleObject.speckle_type.Contains("Objects.Geometry"))
|
||||
AttachSpeckleProperties(element, speckleObject.GetType(), GetProperties(speckleObject));
|
||||
AttachSpeckleProperties(
|
||||
element,
|
||||
speckleObject.GetType(),
|
||||
() => GetProperties(speckleObject)
|
||||
);
|
||||
|
||||
return element;
|
||||
}
|
||||
@@ -129,9 +156,8 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IList<string> DisplayValuePropertyAliases { get; set; } =
|
||||
new[] {"displayValue", "@displayValue", "displayMesh", "@displayMesh"};
|
||||
public static IList<string> DisplayValuePropertyAliases { get; set; } =
|
||||
new[] { "displayValue", "@displayValue", "displayMesh", "@displayMesh" };
|
||||
|
||||
public GameObject? DisplayValueToNative(Base @object)
|
||||
{
|
||||
@@ -143,7 +169,7 @@ namespace Objects.Converter.Unity
|
||||
case IList dvCollection:
|
||||
return MeshesToNative(@object, dvCollection.OfType<Mesh>().ToList());
|
||||
case Mesh dvMesh:
|
||||
return MeshesToNative(@object, new[] {dvMesh});
|
||||
return MeshesToNative(@object, new[] { dvMesh });
|
||||
case Base dvBase:
|
||||
return ConvertToNativeGameObject(dvBase);
|
||||
}
|
||||
@@ -151,16 +177,20 @@ namespace Objects.Converter.Unity
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SpeckleProperties AttachSpeckleProperties(GameObject go, Type speckleType,
|
||||
IDictionary<string, object?> properties)
|
||||
|
||||
private SpeckleProperties? AttachSpeckleProperties(
|
||||
GameObject go,
|
||||
Type speckleType,
|
||||
Func<IDictionary<string, object?>> properties
|
||||
)
|
||||
{
|
||||
if (!shouldAttachProperties)
|
||||
return null;
|
||||
var sd = go.AddComponent<SpeckleProperties>();
|
||||
sd.Data = properties;
|
||||
sd.Data = properties.Invoke();
|
||||
sd.SpeckleType = speckleType;
|
||||
return sd;
|
||||
}
|
||||
|
||||
|
||||
public List<Base> ConvertToSpeckle(List<object> objects)
|
||||
{
|
||||
@@ -172,6 +202,18 @@ namespace Objects.Converter.Unity
|
||||
return objects.Select(x => ConvertToNative(x)).ToList();
|
||||
}
|
||||
|
||||
public object ConvertToNativeDisplayable(Base @object)
|
||||
{
|
||||
throw new NotImplementedException(
|
||||
$"{nameof(ConvertToNativeDisplayable)} is not implemented by this converter, use {nameof(ConvertToNative)} instead"
|
||||
);
|
||||
}
|
||||
|
||||
public bool CanConvertToNativeDisplayable(Base @object)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool CanConvertToSpeckle(object @object)
|
||||
{
|
||||
switch (@object)
|
||||
@@ -183,6 +225,21 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
|
||||
public GameObject CollectionToNative(Collection collection)
|
||||
{
|
||||
var name =
|
||||
collection.name
|
||||
?? $"{collection.collectionType} -- {collection.applicationId ?? collection.id}";
|
||||
var go = new GameObject(name);
|
||||
AttachSpeckleProperties(go, collection.GetType(), () => GetProperties(collection));
|
||||
if (name == "Rooms")
|
||||
{
|
||||
go.SetActive(false);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
public bool CanConvertToNative(Base @object)
|
||||
{
|
||||
switch (@object)
|
||||
@@ -195,13 +252,15 @@ namespace Objects.Converter.Unity
|
||||
// return true;
|
||||
// case Curve _:
|
||||
// return true;
|
||||
// case View3D _:
|
||||
// return true;
|
||||
case View2D _:
|
||||
return false;
|
||||
case Mesh _:
|
||||
// case View2D:
|
||||
// return false;
|
||||
case View3D _:
|
||||
return shouldConvertViews;
|
||||
case Collection:
|
||||
return true;
|
||||
case BlockInstance _:
|
||||
case Mesh:
|
||||
return true;
|
||||
case Instance:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -217,4 +276,4 @@ namespace Objects.Converter.Unity
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e49710593731cc49b9d22a0ba82b5d1
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b875f05888babdd4db73c70d39a33feb
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c822f631fa258514f8b76cdcaacc1b36
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e6c835b7544a374095e58eddb5d2f1c
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+25
-1
@@ -23,6 +23,9 @@ PluginImporter:
|
||||
Exclude WebGL: 0
|
||||
Exclude Win: 0
|
||||
Exclude Win64: 0
|
||||
Exclude WindowsStoreApps: 0
|
||||
Exclude iOS: 0
|
||||
Exclude tvOS: 0
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
@@ -74,9 +77,30 @@ PluginImporter:
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
DontProcess: false
|
||||
PlaceholderPath:
|
||||
SDK: AnySDK
|
||||
ScriptingBackend: DotNet
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
- first:
|
||||
tvOS: tvOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
CompileFlags:
|
||||
FrameworkDependencies:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
Binary file not shown.
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4e83b55b28720a47a937b987aba6b02
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49715c68e5dbf254ca4ca7bc11292537
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c84f8458a24050a48b32654c570e7acc
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f741667477576f444a8a566e2a1f4ce9
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f1430ebe15696242a6b437faa8b8666
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f16b872bea64534a87c7229fa49ac30
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 817747ee0314c9441bac48ea74d34d42
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38b2c99acc4856c4186972b73c6fc332
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2d9231f2f78f0d4ea8786452ac71598
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8ebfa0955817084a887b7a704d43545
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c0b2a3a645b5844faa74f29d90cff65
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d1e392b0c6fd8f4a9e494675b787a9a
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
+33
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0e1bc6a04c09e54188d8d8244b876e4
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3a7df0fa5bb87b43ac372cfefb11aca
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user