From 11cd2a7b79bbd13ceafd16e56ceddf346712567e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 1 Nov 2023 11:06:57 +0000 Subject: [PATCH 001/105] API wrapper (autogenerated) --- Assets/ThirdParty/Org.OpenAPITools.meta | 8 + Assets/ThirdParty/Org.OpenAPITools/Api.meta | 8 + .../Org.OpenAPITools/Api/AssetsApi.cs | 1460 +++++++++++ .../Org.OpenAPITools/Api/AssetsApi.cs.meta | 11 + .../Org.OpenAPITools/Api/LoginApi.cs | 434 ++++ .../Org.OpenAPITools/Api/LoginApi.cs.meta | 11 + .../Org.OpenAPITools/Api/PolyApi.cs | 728 ++++++ .../Org.OpenAPITools/Api/PolyApi.cs.meta | 11 + .../Org.OpenAPITools/Api/UsersApi.cs | 2231 +++++++++++++++++ .../Org.OpenAPITools/Api/UsersApi.cs.meta | 11 + .../ThirdParty/Org.OpenAPITools/Client.meta | 8 + .../Org.OpenAPITools/Client/ApiClient.cs | 645 +++++ .../Org.OpenAPITools/Client/ApiClient.cs.meta | 11 + .../Org.OpenAPITools/Client/ApiException.cs | 68 + .../Client/ApiException.cs.meta | 11 + .../Org.OpenAPITools/Client/ApiResponse.cs | 166 ++ .../Client/ApiResponse.cs.meta | 11 + .../Org.OpenAPITools/Client/ClientUtils.cs | 247 ++ .../Client/ClientUtils.cs.meta | 11 + .../Org.OpenAPITools/Client/Configuration.cs | 612 +++++ .../Client/Configuration.cs.meta | 11 + .../Client/ConnectionException.cs | 29 + .../Client/ConnectionException.cs.meta | 11 + .../Client/ExceptionFactory.cs | 22 + .../Client/ExceptionFactory.cs.meta | 11 + .../Client/GlobalConfiguration.cs | 67 + .../Client/GlobalConfiguration.cs.meta | 11 + .../Org.OpenAPITools/Client/IApiAccessor.cs | 37 + .../Client/IApiAccessor.cs.meta | 11 + .../Client/IAsynchronousClient.cs | 100 + .../Client/IAsynchronousClient.cs.meta | 11 + .../Client/IReadableConfiguration.cs | 141 ++ .../Client/IReadableConfiguration.cs.meta | 11 + .../Client/ISynchronousClient.cs | 93 + .../Client/ISynchronousClient.cs.meta | 11 + .../Org.OpenAPITools/Client/Multimap.cs | 295 +++ .../Org.OpenAPITools/Client/Multimap.cs.meta | 11 + .../Client/OpenAPIDateConverter.cs | 29 + .../Client/OpenAPIDateConverter.cs.meta | 11 + .../Org.OpenAPITools/Client/RequestOptions.cs | 68 + .../Client/RequestOptions.cs.meta | 11 + .../Client/UnexpectedResponseException.cs | 34 + .../UnexpectedResponseException.cs.meta | 11 + .../Client/WebRequestPathBuilder.cs | 53 + .../Client/WebRequestPathBuilder.cs.meta | 11 + Assets/ThirdParty/Org.OpenAPITools/Model.meta | 8 + .../Model/AbstractOpenAPISchema.cs | 76 + .../Model/AbstractOpenAPISchema.cs.meta | 11 + .../Org.OpenAPITools/Model/Asset.cs | 371 +++ .../Org.OpenAPITools/Model/Asset.cs.meta | 11 + .../Org.OpenAPITools/Model/AssetFormat.cs | 193 ++ .../Model/AssetFormat.cs.meta | 11 + .../Org.OpenAPITools/Model/AssetPatchData.cs | 172 ++ .../Model/AssetPatchData.cs.meta | 11 + .../Model/EmailChangeAuthenticated.cs | 151 ++ .../Model/EmailChangeAuthenticated.cs.meta | 11 + .../Org.OpenAPITools/Model/FullUser.cs | 215 ++ .../Org.OpenAPITools/Model/FullUser.cs.meta | 11 + .../Model/HTTPValidationError.cs | 119 + .../Model/HTTPValidationError.cs.meta | 11 + .../Org.OpenAPITools/Model/LoginToken.cs | 151 ++ .../Org.OpenAPITools/Model/LoginToken.cs.meta | 11 + .../Org.OpenAPITools/Model/NewUser.cs | 192 ++ .../Org.OpenAPITools/Model/NewUser.cs.meta | 11 + .../Model/PasswordChangeAuthenticated.cs | 151 ++ .../Model/PasswordChangeAuthenticated.cs.meta | 11 + .../Model/PasswordChangeToken.cs | 151 ++ .../Model/PasswordChangeToken.cs.meta | 11 + .../Org.OpenAPITools/Model/PasswordReset.cs | 128 + .../Model/PasswordReset.cs.meta | 11 + .../Org.OpenAPITools/Model/PatchUser.cs | 154 ++ .../Org.OpenAPITools/Model/PatchUser.cs.meta | 11 + .../Org.OpenAPITools/Model/PolyAsset.cs | 389 +++ .../Org.OpenAPITools/Model/PolyAsset.cs.meta | 11 + .../Org.OpenAPITools/Model/PolyFormat.cs | 193 ++ .../Org.OpenAPITools/Model/PolyFormat.cs.meta | 11 + .../Model/PolyFormatComplexity.cs | 132 + .../Model/PolyFormatComplexity.cs.meta | 11 + .../Org.OpenAPITools/Model/PolyList.cs | 166 ++ .../Org.OpenAPITools/Model/PolyList.cs.meta | 11 + .../Model/PolyPresentationParams.cs | 154 ++ .../Model/PolyPresentationParams.cs.meta | 11 + .../Org.OpenAPITools/Model/PolyQuaternion.cs | 156 ++ .../Model/PolyQuaternion.cs.meta | 11 + .../Org.OpenAPITools/Model/PolyRemixInfo.cs | 129 + .../Model/PolyRemixInfo.cs.meta | 11 + .../Org.OpenAPITools/Model/PolyResource.cs | 174 ++ .../Model/PolyResource.cs.meta | 11 + .../Org.OpenAPITools/Model/SubAssetFormat.cs | 174 ++ .../Model/SubAssetFormat.cs.meta | 11 + .../ThirdParty/Org.OpenAPITools/Model/User.cs | 169 ++ .../Org.OpenAPITools/Model/User.cs.meta | 11 + .../Org.OpenAPITools/Model/ValidationError.cs | 175 ++ .../Model/ValidationError.cs.meta | 11 + .../Org.OpenAPITools/Org.OpenAPITools.asmdef | 7 + .../Org.OpenAPITools.asmdef.meta | 7 + Assets/ThirdParty/Org.OpenAPITools/Tests.meta | 8 + .../Org.OpenAPITools/Tests/Api.meta | 8 + .../Tests/Api/AssetsApiTests.cs | 144 ++ .../Tests/Api/AssetsApiTests.cs.meta | 11 + .../Tests/Api/LoginApiTests.cs | 73 + .../Tests/Api/LoginApiTests.cs.meta | 11 + .../Tests/Api/PolyApiTests.cs | 94 + .../Tests/Api/PolyApiTests.cs.meta | 11 + .../Tests/Api/UsersApiTests.cs | 198 ++ .../Tests/Api/UsersApiTests.cs.meta | 11 + .../Org.OpenAPITools/Tests/Model.meta | 8 + .../Tests/Model/AssetFormatTests.cs | 90 + .../Tests/Model/AssetFormatTests.cs.meta | 11 + .../Tests/Model/AssetPatchDataTests.cs | 90 + .../Tests/Model/AssetPatchDataTests.cs.meta | 11 + .../Tests/Model/AssetTests.cs | 162 ++ .../Tests/Model/AssetTests.cs.meta | 11 + .../Model/EmailChangeAuthenticatedTests.cs | 74 + .../EmailChangeAuthenticatedTests.cs.meta | 11 + .../Tests/Model/FullUserTests.cs | 98 + .../Tests/Model/FullUserTests.cs.meta | 11 + .../Tests/Model/HTTPValidationErrorTests.cs | 66 + .../Model/HTTPValidationErrorTests.cs.meta | 11 + .../Tests/Model/LoginTokenTests.cs | 74 + .../Tests/Model/LoginTokenTests.cs.meta | 11 + .../Tests/Model/NewUserTests.cs | 90 + .../Tests/Model/NewUserTests.cs.meta | 11 + .../Model/PasswordChangeAuthenticatedTests.cs | 74 + .../PasswordChangeAuthenticatedTests.cs.meta | 11 + .../Tests/Model/PasswordChangeTokenTests.cs | 74 + .../Model/PasswordChangeTokenTests.cs.meta | 11 + .../Tests/Model/PasswordResetTests.cs | 66 + .../Tests/Model/PasswordResetTests.cs.meta | 11 + .../Tests/Model/PatchUserTests.cs | 82 + .../Tests/Model/PatchUserTests.cs.meta | 11 + .../Tests/Model/PolyAssetTests.cs | 170 ++ .../Tests/Model/PolyAssetTests.cs.meta | 11 + .../Tests/Model/PolyFormatComplexityTests.cs | 74 + .../Model/PolyFormatComplexityTests.cs.meta | 11 + .../Tests/Model/PolyFormatTests.cs | 90 + .../Tests/Model/PolyFormatTests.cs.meta | 11 + .../Tests/Model/PolyListTests.cs | 82 + .../Tests/Model/PolyListTests.cs.meta | 11 + .../Model/PolyPresentationParamsTests.cs | 82 + .../Model/PolyPresentationParamsTests.cs.meta | 11 + .../Tests/Model/PolyQuaternionTests.cs | 90 + .../Tests/Model/PolyQuaternionTests.cs.meta | 11 + .../Tests/Model/PolyRemixInfoTests.cs | 66 + .../Tests/Model/PolyRemixInfoTests.cs.meta | 11 + .../Tests/Model/PolyResourceTests.cs | 82 + .../Tests/Model/PolyResourceTests.cs.meta | 11 + .../Tests/Model/SubAssetFormatTests.cs | 82 + .../Tests/Model/SubAssetFormatTests.cs.meta | 11 + .../Org.OpenAPITools/Tests/Model/UserTests.cs | 82 + .../Tests/Model/UserTests.cs.meta | 11 + .../Tests/Model/ValidationErrorTests.cs | 82 + .../Tests/Model/ValidationErrorTests.cs.meta | 11 + .../Tests/Org.OpenAPITools.Test.asmdef | 15 + .../Tests/Org.OpenAPITools.Test.asmdef.meta | 7 + 155 files changed, 15209 insertions(+) create mode 100644 Assets/ThirdParty/Org.OpenAPITools.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/User.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/User.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta diff --git a/Assets/ThirdParty/Org.OpenAPITools.meta b/Assets/ThirdParty/Org.OpenAPITools.meta new file mode 100644 index 0000000000..f5f2d8a446 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c8c43a57fb9346828af74ee364b666b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api.meta b/Assets/ThirdParty/Org.OpenAPITools/Api.meta new file mode 100644 index 0000000000..b2a63d5ebc --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6f344de6a19014b8980cf1826f8ddada +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs new file mode 100644 index 0000000000..1f2e0dec58 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs @@ -0,0 +1,1460 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net; +using System.Net.Mime; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Api +{ + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IAssetsApiSync : IApiAccessor + { + #region Synchronous Operations + /// + /// Delete Asset + /// + /// Thrown when fails to make API call + /// + /// Object + Object DeleteAssetAssetsAssetDelete(int asset); + + /// + /// Delete Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse DeleteAssetAssetsAssetDeleteWithHttpInfo(int asset); + /// + /// Get Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Asset + Asset GetAssetAssetsUserurlAsseturlGet(string userurl, string asseturl); + + /// + /// Get Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// ApiResponse of Asset + ApiResponse GetAssetAssetsUserurlAsseturlGetWithHttpInfo(string userurl, string asseturl); + /// + /// Get Assets + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// List<Asset> + List GetAssetsAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)); + + /// + /// Get Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// ApiResponse of List<Asset> + ApiResponse> GetAssetsAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)); + /// + /// Get Id Asset + /// + /// Thrown when fails to make API call + /// + /// Asset + Asset GetIdAssetAssetsIdAssetGet(int asset); + + /// + /// Get Id Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Asset + ApiResponse GetIdAssetAssetsIdAssetGetWithHttpInfo(int asset); + /// + /// Unpublish Asset + /// + /// Thrown when fails to make API call + /// + /// Object + Object UnpublishAssetAssetsAssetUnpublishPatch(int asset); + + /// + /// Unpublish Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse UnpublishAssetAssetsAssetUnpublishPatchWithHttpInfo(int asset); + /// + /// Update Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Asset + Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData assetPatchData); + + /// + /// Update Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// ApiResponse of Asset + ApiResponse UpdateAssetAssetsAssetPatchWithHttpInfo(int asset, AssetPatchData assetPatchData); + /// + /// Upload New Assets + /// + /// Thrown when fails to make API call + /// + /// Object + Object UploadNewAssetsAssetsPost(List files); + + /// + /// Upload New Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse UploadNewAssetsAssetsPostWithHttpInfo(List files); + #endregion Synchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IAssetsApiAsync : IApiAccessor + { + #region Asynchronous Operations + /// + /// Delete Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task DeleteAssetAssetsAssetDeleteAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Delete Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> DeleteAssetAssetsAssetDeleteWithHttpInfoAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of Asset + System.Threading.Tasks.Task GetAssetAssetsUserurlAsseturlGetAsync(string userurl, string asseturl, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Asset) + System.Threading.Tasks.Task> GetAssetAssetsUserurlAsseturlGetWithHttpInfoAsync(string userurl, string asseturl, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + System.Threading.Tasks.Task> GetAssetsAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + System.Threading.Tasks.Task>> GetAssetsAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Id Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Asset + System.Threading.Tasks.Task GetIdAssetAssetsIdAssetGetAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Id Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Asset) + System.Threading.Tasks.Task> GetIdAssetAssetsIdAssetGetWithHttpInfoAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Unpublish Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task UnpublishAssetAssetsAssetUnpublishPatchAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Unpublish Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> UnpublishAssetAssetsAssetUnpublishPatchWithHttpInfoAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Update Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of Asset + System.Threading.Tasks.Task UpdateAssetAssetsAssetPatchAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Update Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Asset) + System.Threading.Tasks.Task> UpdateAssetAssetsAssetPatchWithHttpInfoAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Upload New Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task UploadNewAssetsAssetsPostAsync(List files, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Upload New Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> UploadNewAssetsAssetsPostWithHttpInfoAsync(List files, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + #endregion Asynchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IAssetsApi : IAssetsApiSync, IAssetsApiAsync + { + + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public partial class AssetsApi : IDisposable, IAssetsApi + { + private Org.OpenAPITools.Client.ExceptionFactory _exceptionFactory = (name, response) => null; + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// + public AssetsApi() : this((string)null) + { + } + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// The target service's base path in URL format. + /// + /// + public AssetsApi(string basePath) + { + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + new Org.OpenAPITools.Client.Configuration { BasePath = basePath } + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class using Configuration object. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// An instance of Configuration. + /// + /// + public AssetsApi(Org.OpenAPITools.Client.Configuration configuration) + { + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + configuration + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class + /// using a Configuration object and client instance. + /// + /// The client interface for synchronous API access. + /// The client interface for asynchronous API access. + /// The configuration object. + /// + public AssetsApi(Org.OpenAPITools.Client.ISynchronousClient client, Org.OpenAPITools.Client.IAsynchronousClient asyncClient, Org.OpenAPITools.Client.IReadableConfiguration configuration) + { + if (client == null) throw new ArgumentNullException("client"); + if (asyncClient == null) throw new ArgumentNullException("asyncClient"); + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Client = client; + this.AsynchronousClient = asyncClient; + this.Configuration = configuration; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Disposes resources if they were created by us + /// + public void Dispose() + { + this.ApiClient?.Dispose(); + } + + /// + /// Holds the ApiClient if created + /// + public Org.OpenAPITools.Client.ApiClient ApiClient { get; set; } = null; + + /// + /// The client for accessing this underlying API asynchronously. + /// + public Org.OpenAPITools.Client.IAsynchronousClient AsynchronousClient { get; set; } + + /// + /// The client for accessing this underlying API synchronously. + /// + public Org.OpenAPITools.Client.ISynchronousClient Client { get; set; } + + /// + /// Gets the base path of the API client. + /// + /// The base path + public string GetBasePath() + { + return this.Configuration.BasePath; + } + + /// + /// Gets or sets the configuration object + /// + /// An instance of the Configuration + public Org.OpenAPITools.Client.IReadableConfiguration Configuration { get; set; } + + /// + /// Provides a factory method hook for the creation of exceptions. + /// + public Org.OpenAPITools.Client.ExceptionFactory ExceptionFactory + { + get + { + if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1) + { + throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported."); + } + return _exceptionFactory; + } + set { _exceptionFactory = value; } + } + + /// + /// Delete Asset + /// + /// Thrown when fails to make API call + /// + /// Object + public Object DeleteAssetAssetsAssetDelete(int asset) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = DeleteAssetAssetsAssetDeleteWithHttpInfo(asset); + return localVarResponse.Data; + } + + /// + /// Delete Asset + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse DeleteAssetAssetsAssetDeleteWithHttpInfo(int asset) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Delete("/assets/{asset}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("DeleteAssetAssetsAssetDelete", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Delete Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task DeleteAssetAssetsAssetDeleteAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = DeleteAssetAssetsAssetDeleteWithHttpInfoAsync(asset, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Delete Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> DeleteAssetAssetsAssetDeleteWithHttpInfoAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.DeleteAsync("/assets/{asset}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("DeleteAssetAssetsAssetDelete", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Asset + public Asset GetAssetAssetsUserurlAsseturlGet(string userurl, string asseturl) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetAssetAssetsUserurlAsseturlGetWithHttpInfo(userurl, asseturl); + return localVarResponse.Data; + } + + /// + /// Get Asset + /// + /// Thrown when fails to make API call + /// + /// + /// ApiResponse of Asset + public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlGetWithHttpInfo(string userurl, string asseturl) + { + // verify the required parameter 'userurl' is set + if (userurl == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'userurl' when calling AssetsApi->GetAssetAssetsUserurlAsseturlGet"); + + // verify the required parameter 'asseturl' is set + if (asseturl == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'asseturl' when calling AssetsApi->GetAssetAssetsUserurlAsseturlGet"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("userurl", Org.OpenAPITools.Client.ClientUtils.ParameterToString(userurl)); // path parameter + localVarRequestOptions.PathParameters.Add("asseturl", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asseturl)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get("/assets/{userurl}/{asseturl}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetAssetAssetsUserurlAsseturlGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of Asset + public async System.Threading.Tasks.Task GetAssetAssetsUserurlAsseturlGetAsync(string userurl, string asseturl, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetAssetAssetsUserurlAsseturlGetWithHttpInfoAsync(userurl, asseturl, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Asset) + public async System.Threading.Tasks.Task> GetAssetAssetsUserurlAsseturlGetWithHttpInfoAsync(string userurl, string asseturl, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'userurl' is set + if (userurl == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'userurl' when calling AssetsApi->GetAssetAssetsUserurlAsseturlGet"); + + // verify the required parameter 'asseturl' is set + if (asseturl == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'asseturl' when calling AssetsApi->GetAssetAssetsUserurlAsseturlGet"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("userurl", Org.OpenAPITools.Client.ClientUtils.ParameterToString(userurl)); // path parameter + localVarRequestOptions.PathParameters.Add("asseturl", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asseturl)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/assets/{userurl}/{asseturl}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetAssetAssetsUserurlAsseturlGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Assets + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// List<Asset> + public List GetAssetsAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)) + { + Org.OpenAPITools.Client.ApiResponse> localVarResponse = GetAssetsAssetsGetWithHttpInfo(results, page, curated); + return localVarResponse.Data; + } + + /// + /// Get Assets + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// ApiResponse of List<Asset> + public Org.OpenAPITools.Client.ApiResponse> GetAssetsAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + if (results != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "results", results)); + } + if (page != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "page", page)); + } + if (curated != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "curated", curated)); + } + + + // make the HTTP request + var localVarResponse = this.Client.Get>("/assets", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetAssetsAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Assets + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + public async System.Threading.Tasks.Task> GetAssetsAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetAssetsAssetsGetWithHttpInfoAsync(results, page, curated, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Assets + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + public async System.Threading.Tasks.Task>> GetAssetsAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + if (results != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "results", results)); + } + if (page != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "page", page)); + } + if (curated != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "curated", curated)); + } + + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync>("/assets", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetAssetsAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Id Asset + /// + /// Thrown when fails to make API call + /// + /// Asset + public Asset GetIdAssetAssetsIdAssetGet(int asset) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetIdAssetAssetsIdAssetGetWithHttpInfo(asset); + return localVarResponse.Data; + } + + /// + /// Get Id Asset + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Asset + public Org.OpenAPITools.Client.ApiResponse GetIdAssetAssetsIdAssetGetWithHttpInfo(int asset) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get("/assets/id/{asset}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetIdAssetAssetsIdAssetGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Id Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Asset + public async System.Threading.Tasks.Task GetIdAssetAssetsIdAssetGetAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetIdAssetAssetsIdAssetGetWithHttpInfoAsync(asset, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Id Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Asset) + public async System.Threading.Tasks.Task> GetIdAssetAssetsIdAssetGetWithHttpInfoAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/assets/id/{asset}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetIdAssetAssetsIdAssetGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Unpublish Asset + /// + /// Thrown when fails to make API call + /// + /// Object + public Object UnpublishAssetAssetsAssetUnpublishPatch(int asset) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = UnpublishAssetAssetsAssetUnpublishPatchWithHttpInfo(asset); + return localVarResponse.Data; + } + + /// + /// Unpublish Asset + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse UnpublishAssetAssetsAssetUnpublishPatchWithHttpInfo(int asset) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Patch("/assets/{asset}/unpublish", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UnpublishAssetAssetsAssetUnpublishPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Unpublish Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task UnpublishAssetAssetsAssetUnpublishPatchAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = UnpublishAssetAssetsAssetUnpublishPatchWithHttpInfoAsync(asset, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Unpublish Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> UnpublishAssetAssetsAssetUnpublishPatchWithHttpInfoAsync(int asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PatchAsync("/assets/{asset}/unpublish", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UnpublishAssetAssetsAssetUnpublishPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Update Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Asset + public Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData assetPatchData) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = UpdateAssetAssetsAssetPatchWithHttpInfo(asset, assetPatchData); + return localVarResponse.Data; + } + + /// + /// Update Asset + /// + /// Thrown when fails to make API call + /// + /// + /// ApiResponse of Asset + public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWithHttpInfo(int asset, AssetPatchData assetPatchData) + { + // verify the required parameter 'assetPatchData' is set + if (assetPatchData == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'assetPatchData' when calling AssetsApi->UpdateAssetAssetsAssetPatch"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + localVarRequestOptions.Data = assetPatchData; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Patch("/assets/{asset}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UpdateAssetAssetsAssetPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Update Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of Asset + public async System.Threading.Tasks.Task UpdateAssetAssetsAssetPatchAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = UpdateAssetAssetsAssetPatchWithHttpInfoAsync(asset, assetPatchData, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Update Asset + /// + /// Thrown when fails to make API call + /// + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Asset) + public async System.Threading.Tasks.Task> UpdateAssetAssetsAssetPatchWithHttpInfoAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'assetPatchData' is set + if (assetPatchData == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'assetPatchData' when calling AssetsApi->UpdateAssetAssetsAssetPatch"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + localVarRequestOptions.Data = assetPatchData; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PatchAsync("/assets/{asset}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UpdateAssetAssetsAssetPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Upload New Assets + /// + /// Thrown when fails to make API call + /// + /// Object + public Object UploadNewAssetsAssetsPost(List files) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = UploadNewAssetsAssetsPostWithHttpInfo(files); + return localVarResponse.Data; + } + + /// + /// Upload New Assets + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse UploadNewAssetsAssetsPostWithHttpInfo(List files) + { + // verify the required parameter 'files' is set + if (files == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'files' when calling AssetsApi->UploadNewAssetsAssetsPost"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "multipart/form-data" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Post("/assets", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UploadNewAssetsAssetsPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Upload New Assets + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task UploadNewAssetsAssetsPostAsync(List files, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = UploadNewAssetsAssetsPostWithHttpInfoAsync(files, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Upload New Assets + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> UploadNewAssetsAssetsPostWithHttpInfoAsync(List files, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'files' is set + if (files == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'files' when calling AssetsApi->UploadNewAssetsAssetsPost"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "multipart/form-data" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PostAsync("/assets", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UploadNewAssetsAssetsPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs.meta new file mode 100644 index 0000000000..b702a67c91 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83932cc0b007b4808be39d3abb684964 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs new file mode 100644 index 0000000000..57cafff61f --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs @@ -0,0 +1,434 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net; +using System.Net.Mime; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Api +{ + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface ILoginApiSync : IApiAccessor + { + #region Synchronous Operations + /// + /// Login + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// LoginToken + LoginToken LoginLoginPost(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?)); + + /// + /// Login + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// ApiResponse of LoginToken + ApiResponse LoginLoginPostWithHttpInfo(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?)); + #endregion Synchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface ILoginApiAsync : IApiAccessor + { + #region Asynchronous Operations + /// + /// Login + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// Cancellation Token to cancel the request. + /// Task of LoginToken + System.Threading.Tasks.Task LoginLoginPostAsync(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Login + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (LoginToken) + System.Threading.Tasks.Task> LoginLoginPostWithHttpInfoAsync(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + #endregion Asynchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface ILoginApi : ILoginApiSync, ILoginApiAsync + { + + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public partial class LoginApi : IDisposable, ILoginApi + { + private Org.OpenAPITools.Client.ExceptionFactory _exceptionFactory = (name, response) => null; + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// + public LoginApi() : this((string)null) + { + } + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// The target service's base path in URL format. + /// + /// + public LoginApi(string basePath) + { + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + new Org.OpenAPITools.Client.Configuration { BasePath = basePath } + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class using Configuration object. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// An instance of Configuration. + /// + /// + public LoginApi(Org.OpenAPITools.Client.Configuration configuration) + { + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + configuration + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class + /// using a Configuration object and client instance. + /// + /// The client interface for synchronous API access. + /// The client interface for asynchronous API access. + /// The configuration object. + /// + public LoginApi(Org.OpenAPITools.Client.ISynchronousClient client, Org.OpenAPITools.Client.IAsynchronousClient asyncClient, Org.OpenAPITools.Client.IReadableConfiguration configuration) + { + if (client == null) throw new ArgumentNullException("client"); + if (asyncClient == null) throw new ArgumentNullException("asyncClient"); + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Client = client; + this.AsynchronousClient = asyncClient; + this.Configuration = configuration; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Disposes resources if they were created by us + /// + public void Dispose() + { + this.ApiClient?.Dispose(); + } + + /// + /// Holds the ApiClient if created + /// + public Org.OpenAPITools.Client.ApiClient ApiClient { get; set; } = null; + + /// + /// The client for accessing this underlying API asynchronously. + /// + public Org.OpenAPITools.Client.IAsynchronousClient AsynchronousClient { get; set; } + + /// + /// The client for accessing this underlying API synchronously. + /// + public Org.OpenAPITools.Client.ISynchronousClient Client { get; set; } + + /// + /// Gets the base path of the API client. + /// + /// The base path + public string GetBasePath() + { + return this.Configuration.BasePath; + } + + /// + /// Gets or sets the configuration object + /// + /// An instance of the Configuration + public Org.OpenAPITools.Client.IReadableConfiguration Configuration { get; set; } + + /// + /// Provides a factory method hook for the creation of exceptions. + /// + public Org.OpenAPITools.Client.ExceptionFactory ExceptionFactory + { + get + { + if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1) + { + throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported."); + } + return _exceptionFactory; + } + set { _exceptionFactory = value; } + } + + /// + /// Login + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// LoginToken + public LoginToken LoginLoginPost(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?)) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = LoginLoginPostWithHttpInfo(username, password, grantType, scope, clientId, clientSecret); + return localVarResponse.Data; + } + + /// + /// Login + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// ApiResponse of LoginToken + public Org.OpenAPITools.Client.ApiResponse LoginLoginPostWithHttpInfo(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?)) + { + // verify the required parameter 'username' is set + if (username == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'username' when calling LoginApi->LoginLoginPost"); + + // verify the required parameter 'password' is set + if (password == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'password' when calling LoginApi->LoginLoginPost"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/x-www-form-urlencoded" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + if (grantType != null) + { + localVarRequestOptions.FormParameters.Add("grant_type", Org.OpenAPITools.Client.ClientUtils.ParameterToString(grantType)); // form parameter + } + localVarRequestOptions.FormParameters.Add("username", Org.OpenAPITools.Client.ClientUtils.ParameterToString(username)); // form parameter + localVarRequestOptions.FormParameters.Add("password", Org.OpenAPITools.Client.ClientUtils.ParameterToString(password)); // form parameter + if (scope != null) + { + localVarRequestOptions.FormParameters.Add("scope", Org.OpenAPITools.Client.ClientUtils.ParameterToString(scope)); // form parameter + } + if (clientId != null) + { + localVarRequestOptions.FormParameters.Add("client_id", Org.OpenAPITools.Client.ClientUtils.ParameterToString(clientId)); // form parameter + } + if (clientSecret != null) + { + localVarRequestOptions.FormParameters.Add("client_secret", Org.OpenAPITools.Client.ClientUtils.ParameterToString(clientSecret)); // form parameter + } + + + // make the HTTP request + var localVarResponse = this.Client.Post("/login", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("LoginLoginPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Login + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// Cancellation Token to cancel the request. + /// Task of LoginToken + public async System.Threading.Tasks.Task LoginLoginPostAsync(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = LoginLoginPostWithHttpInfoAsync(username, password, grantType, scope, clientId, clientSecret, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Login + /// + /// Thrown when fails to make API call + /// + /// + /// (optional) + /// (optional, default to "") + /// (optional) + /// (optional) + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (LoginToken) + public async System.Threading.Tasks.Task> LoginLoginPostWithHttpInfoAsync(string username, string password, string? grantType = default(string?), string? scope = default(string?), string? clientId = default(string?), string? clientSecret = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'username' is set + if (username == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'username' when calling LoginApi->LoginLoginPost"); + + // verify the required parameter 'password' is set + if (password == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'password' when calling LoginApi->LoginLoginPost"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/x-www-form-urlencoded" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + if (grantType != null) + { + localVarRequestOptions.FormParameters.Add("grant_type", Org.OpenAPITools.Client.ClientUtils.ParameterToString(grantType)); // form parameter + } + localVarRequestOptions.FormParameters.Add("username", Org.OpenAPITools.Client.ClientUtils.ParameterToString(username)); // form parameter + localVarRequestOptions.FormParameters.Add("password", Org.OpenAPITools.Client.ClientUtils.ParameterToString(password)); // form parameter + if (scope != null) + { + localVarRequestOptions.FormParameters.Add("scope", Org.OpenAPITools.Client.ClientUtils.ParameterToString(scope)); // form parameter + } + if (clientId != null) + { + localVarRequestOptions.FormParameters.Add("client_id", Org.OpenAPITools.Client.ClientUtils.ParameterToString(clientId)); // form parameter + } + if (clientSecret != null) + { + localVarRequestOptions.FormParameters.Add("client_secret", Org.OpenAPITools.Client.ClientUtils.ParameterToString(clientSecret)); // form parameter + } + + + // make the HTTP request + + var task = this.AsynchronousClient.PostAsync("/login", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("LoginLoginPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs.meta new file mode 100644 index 0000000000..497467a841 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d534d3cdaa5ca4751a0619feb7d7828b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs new file mode 100644 index 0000000000..e51dada320 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs @@ -0,0 +1,728 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net; +using System.Net.Mime; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Api +{ + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IPolyApiSync : IApiAccessor + { + #region Synchronous Operations + /// + /// Get Poly Asset + /// + /// Thrown when fails to make API call + /// + /// PolyAsset + PolyAsset GetPolyAssetPolyAssetsAssetGet(string asset); + + /// + /// Get Poly Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of PolyAsset + ApiResponse GetPolyAssetPolyAssetsAssetGetWithHttpInfo(string asset); + /// + /// Get Poly Assets List + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// PolyList + PolyList GetPolyAssetsListPolyAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)); + + /// + /// Get Poly Assets List + /// + /// + /// + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// ApiResponse of PolyList + ApiResponse GetPolyAssetsListPolyAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)); + /// + /// Import Poly Data + /// + /// Thrown when fails to make API call + /// + /// Object + Object ImportPolyDataPolyImportPost(List requestBody); + + /// + /// Import Poly Data + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse ImportPolyDataPolyImportPostWithHttpInfo(List requestBody); + #endregion Synchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IPolyApiAsync : IApiAccessor + { + #region Asynchronous Operations + /// + /// Get Poly Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of PolyAsset + System.Threading.Tasks.Task GetPolyAssetPolyAssetsAssetGetAsync(string asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Poly Asset + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (PolyAsset) + System.Threading.Tasks.Task> GetPolyAssetPolyAssetsAssetGetWithHttpInfoAsync(string asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Poly Assets List + /// + /// + /// + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of PolyList + System.Threading.Tasks.Task GetPolyAssetsListPolyAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Poly Assets List + /// + /// + /// + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (PolyList) + System.Threading.Tasks.Task> GetPolyAssetsListPolyAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Import Poly Data + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task ImportPolyDataPolyImportPostAsync(List requestBody, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Import Poly Data + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> ImportPolyDataPolyImportPostWithHttpInfoAsync(List requestBody, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + #endregion Asynchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IPolyApi : IPolyApiSync, IPolyApiAsync + { + + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public partial class PolyApi : IDisposable, IPolyApi + { + private Org.OpenAPITools.Client.ExceptionFactory _exceptionFactory = (name, response) => null; + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// + public PolyApi() : this((string)null) + { + } + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// The target service's base path in URL format. + /// + /// + public PolyApi(string basePath) + { + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + new Org.OpenAPITools.Client.Configuration { BasePath = basePath } + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class using Configuration object. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// An instance of Configuration. + /// + /// + public PolyApi(Org.OpenAPITools.Client.Configuration configuration) + { + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + configuration + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class + /// using a Configuration object and client instance. + /// + /// The client interface for synchronous API access. + /// The client interface for asynchronous API access. + /// The configuration object. + /// + public PolyApi(Org.OpenAPITools.Client.ISynchronousClient client, Org.OpenAPITools.Client.IAsynchronousClient asyncClient, Org.OpenAPITools.Client.IReadableConfiguration configuration) + { + if (client == null) throw new ArgumentNullException("client"); + if (asyncClient == null) throw new ArgumentNullException("asyncClient"); + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Client = client; + this.AsynchronousClient = asyncClient; + this.Configuration = configuration; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Disposes resources if they were created by us + /// + public void Dispose() + { + this.ApiClient?.Dispose(); + } + + /// + /// Holds the ApiClient if created + /// + public Org.OpenAPITools.Client.ApiClient ApiClient { get; set; } = null; + + /// + /// The client for accessing this underlying API asynchronously. + /// + public Org.OpenAPITools.Client.IAsynchronousClient AsynchronousClient { get; set; } + + /// + /// The client for accessing this underlying API synchronously. + /// + public Org.OpenAPITools.Client.ISynchronousClient Client { get; set; } + + /// + /// Gets the base path of the API client. + /// + /// The base path + public string GetBasePath() + { + return this.Configuration.BasePath; + } + + /// + /// Gets or sets the configuration object + /// + /// An instance of the Configuration + public Org.OpenAPITools.Client.IReadableConfiguration Configuration { get; set; } + + /// + /// Provides a factory method hook for the creation of exceptions. + /// + public Org.OpenAPITools.Client.ExceptionFactory ExceptionFactory + { + get + { + if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1) + { + throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported."); + } + return _exceptionFactory; + } + set { _exceptionFactory = value; } + } + + /// + /// Get Poly Asset + /// + /// Thrown when fails to make API call + /// + /// PolyAsset + public PolyAsset GetPolyAssetPolyAssetsAssetGet(string asset) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetPolyAssetPolyAssetsAssetGetWithHttpInfo(asset); + return localVarResponse.Data; + } + + /// + /// Get Poly Asset + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of PolyAsset + public Org.OpenAPITools.Client.ApiResponse GetPolyAssetPolyAssetsAssetGetWithHttpInfo(string asset) + { + // verify the required parameter 'asset' is set + if (asset == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'asset' when calling PolyApi->GetPolyAssetPolyAssetsAssetGet"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + + // make the HTTP request + var localVarResponse = this.Client.Get("/poly/assets/{asset}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetPolyAssetPolyAssetsAssetGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Poly Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of PolyAsset + public async System.Threading.Tasks.Task GetPolyAssetPolyAssetsAssetGetAsync(string asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetPolyAssetPolyAssetsAssetGetWithHttpInfoAsync(asset, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Poly Asset + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (PolyAsset) + public async System.Threading.Tasks.Task> GetPolyAssetPolyAssetsAssetGetWithHttpInfoAsync(string asset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'asset' is set + if (asset == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'asset' when calling PolyApi->GetPolyAssetPolyAssetsAssetGet"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter + + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/poly/assets/{asset}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetPolyAssetPolyAssetsAssetGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Poly Assets List + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// PolyList + public PolyList GetPolyAssetsListPolyAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetPolyAssetsListPolyAssetsGetWithHttpInfo(results, page, curated); + return localVarResponse.Data; + } + + /// + /// Get Poly Assets List + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// ApiResponse of PolyList + public Org.OpenAPITools.Client.ApiResponse GetPolyAssetsListPolyAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + if (results != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "results", results)); + } + if (page != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "page", page)); + } + if (curated != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "curated", curated)); + } + + + // make the HTTP request + var localVarResponse = this.Client.Get("/poly/assets", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetPolyAssetsListPolyAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Poly Assets List + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of PolyList + public async System.Threading.Tasks.Task GetPolyAssetsListPolyAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetPolyAssetsListPolyAssetsGetWithHttpInfoAsync(results, page, curated, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Poly Assets List + /// + /// Thrown when fails to make API call + /// (optional, default to 20) + /// (optional, default to 0) + /// (optional, default to false) + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (PolyList) + public async System.Threading.Tasks.Task> GetPolyAssetsListPolyAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + if (results != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "results", results)); + } + if (page != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "page", page)); + } + if (curated != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "curated", curated)); + } + + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/poly/assets", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetPolyAssetsListPolyAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Import Poly Data + /// + /// Thrown when fails to make API call + /// + /// Object + public Object ImportPolyDataPolyImportPost(List requestBody) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = ImportPolyDataPolyImportPostWithHttpInfo(requestBody); + return localVarResponse.Data; + } + + /// + /// Import Poly Data + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse ImportPolyDataPolyImportPostWithHttpInfo(List requestBody) + { + // verify the required parameter 'requestBody' is set + if (requestBody == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'requestBody' when calling PolyApi->ImportPolyDataPolyImportPost"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = requestBody; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Post("/poly/import", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ImportPolyDataPolyImportPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Import Poly Data + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task ImportPolyDataPolyImportPostAsync(List requestBody, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = ImportPolyDataPolyImportPostWithHttpInfoAsync(requestBody, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Import Poly Data + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> ImportPolyDataPolyImportPostWithHttpInfoAsync(List requestBody, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'requestBody' is set + if (requestBody == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'requestBody' when calling PolyApi->ImportPolyDataPolyImportPost"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = requestBody; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PostAsync("/poly/import", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ImportPolyDataPolyImportPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs.meta new file mode 100644 index 0000000000..51a541e655 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/PolyApi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7231a33deb094e41861d191e3b76693 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs new file mode 100644 index 0000000000..48d627d966 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs @@ -0,0 +1,2231 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Net; +using System.Net.Mime; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Api +{ + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IUsersApiSync : IApiAccessor + { + #region Synchronous Operations + /// + /// Change Authenticated User Email + /// + /// Thrown when fails to make API call + /// + /// Object + Object ChangeAuthenticatedUserEmailUsersMeEmailPatch(EmailChangeAuthenticated emailChangeAuthenticated); + + /// + /// Change Authenticated User Email + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse ChangeAuthenticatedUserEmailUsersMeEmailPatchWithHttpInfo(EmailChangeAuthenticated emailChangeAuthenticated); + /// + /// Change Authenticated User Password + /// + /// Thrown when fails to make API call + /// + /// Object + Object ChangeAuthenticatedUserPasswordUsersMePasswordPatch(PasswordChangeAuthenticated passwordChangeAuthenticated); + + /// + /// Change Authenticated User Password + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse ChangeAuthenticatedUserPasswordUsersMePasswordPatchWithHttpInfo(PasswordChangeAuthenticated passwordChangeAuthenticated); + /// + /// Change Password Via Token + /// + /// Thrown when fails to make API call + /// + /// Object + Object ChangePasswordViaTokenUsersPasswordPatch(PasswordChangeToken passwordChangeToken); + + /// + /// Change Password Via Token + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse ChangePasswordViaTokenUsersPasswordPatchWithHttpInfo(PasswordChangeToken passwordChangeToken); + /// + /// Create User + /// + /// Thrown when fails to make API call + /// + /// FullUser + FullUser CreateUserUsersPost(NewUser newUser); + + /// + /// Create User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of FullUser + ApiResponse CreateUserUsersPostWithHttpInfo(NewUser newUser); + /// + /// Get Id User Assets + /// + /// Thrown when fails to make API call + /// + /// List<Asset> + List GetIdUserAssetsUsersIdUserAssetsGet(int user); + + /// + /// Get Id User Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of List<Asset> + ApiResponse> GetIdUserAssetsUsersIdUserAssetsGetWithHttpInfo(int user); + /// + /// Get Me Assets + /// + /// Thrown when fails to make API call + /// List<Asset> + List GetMeAssetsUsersMeAssetsGet(); + + /// + /// Get Me Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// ApiResponse of List<Asset> + ApiResponse> GetMeAssetsUsersMeAssetsGetWithHttpInfo(); + /// + /// Get User Assets + /// + /// Thrown when fails to make API call + /// + /// List<Asset> + List GetUserAssetsUsersUserAssetsGet(string user); + + /// + /// Get User Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of List<Asset> + ApiResponse> GetUserAssetsUsersUserAssetsGetWithHttpInfo(string user); + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// User + User GetUserUsersIdUserGet(int user); + + /// + /// Get User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of User + ApiResponse GetUserUsersIdUserGetWithHttpInfo(int user); + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// User + User GetUserUsersUserGet(string user); + + /// + /// Get User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of User + ApiResponse GetUserUsersUserGetWithHttpInfo(string user); + /// + /// Get Users Me + /// + /// Thrown when fails to make API call + /// FullUser + FullUser GetUsersMeUsersMeGet(); + + /// + /// Get Users Me + /// + /// + /// + /// + /// Thrown when fails to make API call + /// ApiResponse of FullUser + ApiResponse GetUsersMeUsersMeGetWithHttpInfo(); + /// + /// Update User + /// + /// Thrown when fails to make API call + /// + /// FullUser + FullUser UpdateUserUsersMePatch(PatchUser patchUser); + + /// + /// Update User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of FullUser + ApiResponse UpdateUserUsersMePatchWithHttpInfo(PatchUser patchUser); + /// + /// User Password Reset Request + /// + /// Thrown when fails to make API call + /// + /// Object + Object UserPasswordResetRequestUsersPasswordResetPut(PasswordReset passwordReset); + + /// + /// User Password Reset Request + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + ApiResponse UserPasswordResetRequestUsersPasswordResetPutWithHttpInfo(PasswordReset passwordReset); + #endregion Synchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IUsersApiAsync : IApiAccessor + { + #region Asynchronous Operations + /// + /// Change Authenticated User Email + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task ChangeAuthenticatedUserEmailUsersMeEmailPatchAsync(EmailChangeAuthenticated emailChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Change Authenticated User Email + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> ChangeAuthenticatedUserEmailUsersMeEmailPatchWithHttpInfoAsync(EmailChangeAuthenticated emailChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Change Authenticated User Password + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task ChangeAuthenticatedUserPasswordUsersMePasswordPatchAsync(PasswordChangeAuthenticated passwordChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Change Authenticated User Password + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> ChangeAuthenticatedUserPasswordUsersMePasswordPatchWithHttpInfoAsync(PasswordChangeAuthenticated passwordChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Change Password Via Token + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task ChangePasswordViaTokenUsersPasswordPatchAsync(PasswordChangeToken passwordChangeToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Change Password Via Token + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> ChangePasswordViaTokenUsersPasswordPatchWithHttpInfoAsync(PasswordChangeToken passwordChangeToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Create User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of FullUser + System.Threading.Tasks.Task CreateUserUsersPostAsync(NewUser newUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Create User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (FullUser) + System.Threading.Tasks.Task> CreateUserUsersPostWithHttpInfoAsync(NewUser newUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Id User Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + System.Threading.Tasks.Task> GetIdUserAssetsUsersIdUserAssetsGetAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Id User Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + System.Threading.Tasks.Task>> GetIdUserAssetsUsersIdUserAssetsGetWithHttpInfoAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Me Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + System.Threading.Tasks.Task> GetMeAssetsUsersMeAssetsGetAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Me Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + System.Threading.Tasks.Task>> GetMeAssetsUsersMeAssetsGetWithHttpInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get User Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + System.Threading.Tasks.Task> GetUserAssetsUsersUserAssetsGetAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get User Assets + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + System.Threading.Tasks.Task>> GetUserAssetsUsersUserAssetsGetWithHttpInfoAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of User + System.Threading.Tasks.Task GetUserUsersIdUserGetAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (User) + System.Threading.Tasks.Task> GetUserUsersIdUserGetWithHttpInfoAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of User + System.Threading.Tasks.Task GetUserUsersUserGetAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (User) + System.Threading.Tasks.Task> GetUserUsersUserGetWithHttpInfoAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Get Users Me + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of FullUser + System.Threading.Tasks.Task GetUsersMeUsersMeGetAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Users Me + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (FullUser) + System.Threading.Tasks.Task> GetUsersMeUsersMeGetWithHttpInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// Update User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of FullUser + System.Threading.Tasks.Task UpdateUserUsersMePatchAsync(PatchUser patchUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Update User + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (FullUser) + System.Threading.Tasks.Task> UpdateUserUsersMePatchWithHttpInfoAsync(PatchUser patchUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// + /// User Password Reset Request + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + System.Threading.Tasks.Task UserPasswordResetRequestUsersPasswordResetPutAsync(PasswordReset passwordReset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// User Password Reset Request + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + System.Threading.Tasks.Task> UserPasswordResetRequestUsersPasswordResetPutWithHttpInfoAsync(PasswordReset passwordReset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + #endregion Asynchronous Operations + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public interface IUsersApi : IUsersApiSync, IUsersApiAsync + { + + } + + /// + /// Represents a collection of functions to interact with the API endpoints + /// + public partial class UsersApi : IDisposable, IUsersApi + { + private Org.OpenAPITools.Client.ExceptionFactory _exceptionFactory = (name, response) => null; + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// + public UsersApi() : this((string)null) + { + } + + /// + /// Initializes a new instance of the class. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// The target service's base path in URL format. + /// + /// + public UsersApi(string basePath) + { + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + new Org.OpenAPITools.Client.Configuration { BasePath = basePath } + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class using Configuration object. + /// **IMPORTANT** This will also create an instance of HttpClient, which is less than ideal. + /// It's better to reuse the HttpClient and HttpClientHandler. + /// + /// An instance of Configuration. + /// + /// + public UsersApi(Org.OpenAPITools.Client.Configuration configuration) + { + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Configuration = Org.OpenAPITools.Client.Configuration.MergeConfigurations( + Org.OpenAPITools.Client.GlobalConfiguration.Instance, + configuration + ); + this.ApiClient = new Org.OpenAPITools.Client.ApiClient(this.Configuration.BasePath); + this.Client = this.ApiClient; + this.AsynchronousClient = this.ApiClient; + ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Initializes a new instance of the class + /// using a Configuration object and client instance. + /// + /// The client interface for synchronous API access. + /// The client interface for asynchronous API access. + /// The configuration object. + /// + public UsersApi(Org.OpenAPITools.Client.ISynchronousClient client, Org.OpenAPITools.Client.IAsynchronousClient asyncClient, Org.OpenAPITools.Client.IReadableConfiguration configuration) + { + if (client == null) throw new ArgumentNullException("client"); + if (asyncClient == null) throw new ArgumentNullException("asyncClient"); + if (configuration == null) throw new ArgumentNullException("configuration"); + + this.Client = client; + this.AsynchronousClient = asyncClient; + this.Configuration = configuration; + this.ExceptionFactory = Org.OpenAPITools.Client.Configuration.DefaultExceptionFactory; + } + + /// + /// Disposes resources if they were created by us + /// + public void Dispose() + { + this.ApiClient?.Dispose(); + } + + /// + /// Holds the ApiClient if created + /// + public Org.OpenAPITools.Client.ApiClient ApiClient { get; set; } = null; + + /// + /// The client for accessing this underlying API asynchronously. + /// + public Org.OpenAPITools.Client.IAsynchronousClient AsynchronousClient { get; set; } + + /// + /// The client for accessing this underlying API synchronously. + /// + public Org.OpenAPITools.Client.ISynchronousClient Client { get; set; } + + /// + /// Gets the base path of the API client. + /// + /// The base path + public string GetBasePath() + { + return this.Configuration.BasePath; + } + + /// + /// Gets or sets the configuration object + /// + /// An instance of the Configuration + public Org.OpenAPITools.Client.IReadableConfiguration Configuration { get; set; } + + /// + /// Provides a factory method hook for the creation of exceptions. + /// + public Org.OpenAPITools.Client.ExceptionFactory ExceptionFactory + { + get + { + if (_exceptionFactory != null && _exceptionFactory.GetInvocationList().Length > 1) + { + throw new InvalidOperationException("Multicast delegate for ExceptionFactory is unsupported."); + } + return _exceptionFactory; + } + set { _exceptionFactory = value; } + } + + /// + /// Change Authenticated User Email + /// + /// Thrown when fails to make API call + /// + /// Object + public Object ChangeAuthenticatedUserEmailUsersMeEmailPatch(EmailChangeAuthenticated emailChangeAuthenticated) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = ChangeAuthenticatedUserEmailUsersMeEmailPatchWithHttpInfo(emailChangeAuthenticated); + return localVarResponse.Data; + } + + /// + /// Change Authenticated User Email + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse ChangeAuthenticatedUserEmailUsersMeEmailPatchWithHttpInfo(EmailChangeAuthenticated emailChangeAuthenticated) + { + // verify the required parameter 'emailChangeAuthenticated' is set + if (emailChangeAuthenticated == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'emailChangeAuthenticated' when calling UsersApi->ChangeAuthenticatedUserEmailUsersMeEmailPatch"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = emailChangeAuthenticated; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Patch("/users/me/email", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ChangeAuthenticatedUserEmailUsersMeEmailPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Change Authenticated User Email + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task ChangeAuthenticatedUserEmailUsersMeEmailPatchAsync(EmailChangeAuthenticated emailChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = ChangeAuthenticatedUserEmailUsersMeEmailPatchWithHttpInfoAsync(emailChangeAuthenticated, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Change Authenticated User Email + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> ChangeAuthenticatedUserEmailUsersMeEmailPatchWithHttpInfoAsync(EmailChangeAuthenticated emailChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'emailChangeAuthenticated' is set + if (emailChangeAuthenticated == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'emailChangeAuthenticated' when calling UsersApi->ChangeAuthenticatedUserEmailUsersMeEmailPatch"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = emailChangeAuthenticated; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PatchAsync("/users/me/email", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ChangeAuthenticatedUserEmailUsersMeEmailPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Change Authenticated User Password + /// + /// Thrown when fails to make API call + /// + /// Object + public Object ChangeAuthenticatedUserPasswordUsersMePasswordPatch(PasswordChangeAuthenticated passwordChangeAuthenticated) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = ChangeAuthenticatedUserPasswordUsersMePasswordPatchWithHttpInfo(passwordChangeAuthenticated); + return localVarResponse.Data; + } + + /// + /// Change Authenticated User Password + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse ChangeAuthenticatedUserPasswordUsersMePasswordPatchWithHttpInfo(PasswordChangeAuthenticated passwordChangeAuthenticated) + { + // verify the required parameter 'passwordChangeAuthenticated' is set + if (passwordChangeAuthenticated == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'passwordChangeAuthenticated' when calling UsersApi->ChangeAuthenticatedUserPasswordUsersMePasswordPatch"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = passwordChangeAuthenticated; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Patch("/users/me/password", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ChangeAuthenticatedUserPasswordUsersMePasswordPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Change Authenticated User Password + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task ChangeAuthenticatedUserPasswordUsersMePasswordPatchAsync(PasswordChangeAuthenticated passwordChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = ChangeAuthenticatedUserPasswordUsersMePasswordPatchWithHttpInfoAsync(passwordChangeAuthenticated, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Change Authenticated User Password + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> ChangeAuthenticatedUserPasswordUsersMePasswordPatchWithHttpInfoAsync(PasswordChangeAuthenticated passwordChangeAuthenticated, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'passwordChangeAuthenticated' is set + if (passwordChangeAuthenticated == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'passwordChangeAuthenticated' when calling UsersApi->ChangeAuthenticatedUserPasswordUsersMePasswordPatch"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = passwordChangeAuthenticated; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PatchAsync("/users/me/password", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ChangeAuthenticatedUserPasswordUsersMePasswordPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Change Password Via Token + /// + /// Thrown when fails to make API call + /// + /// Object + public Object ChangePasswordViaTokenUsersPasswordPatch(PasswordChangeToken passwordChangeToken) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = ChangePasswordViaTokenUsersPasswordPatchWithHttpInfo(passwordChangeToken); + return localVarResponse.Data; + } + + /// + /// Change Password Via Token + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse ChangePasswordViaTokenUsersPasswordPatchWithHttpInfo(PasswordChangeToken passwordChangeToken) + { + // verify the required parameter 'passwordChangeToken' is set + if (passwordChangeToken == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'passwordChangeToken' when calling UsersApi->ChangePasswordViaTokenUsersPasswordPatch"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = passwordChangeToken; + + + // make the HTTP request + var localVarResponse = this.Client.Patch("/users/password", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ChangePasswordViaTokenUsersPasswordPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Change Password Via Token + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task ChangePasswordViaTokenUsersPasswordPatchAsync(PasswordChangeToken passwordChangeToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = ChangePasswordViaTokenUsersPasswordPatchWithHttpInfoAsync(passwordChangeToken, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Change Password Via Token + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> ChangePasswordViaTokenUsersPasswordPatchWithHttpInfoAsync(PasswordChangeToken passwordChangeToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'passwordChangeToken' is set + if (passwordChangeToken == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'passwordChangeToken' when calling UsersApi->ChangePasswordViaTokenUsersPasswordPatch"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = passwordChangeToken; + + + // make the HTTP request + + var task = this.AsynchronousClient.PatchAsync("/users/password", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("ChangePasswordViaTokenUsersPasswordPatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Create User + /// + /// Thrown when fails to make API call + /// + /// FullUser + public FullUser CreateUserUsersPost(NewUser newUser) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = CreateUserUsersPostWithHttpInfo(newUser); + return localVarResponse.Data; + } + + /// + /// Create User + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of FullUser + public Org.OpenAPITools.Client.ApiResponse CreateUserUsersPostWithHttpInfo(NewUser newUser) + { + // verify the required parameter 'newUser' is set + if (newUser == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'newUser' when calling UsersApi->CreateUserUsersPost"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = newUser; + + + // make the HTTP request + var localVarResponse = this.Client.Post("/users", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("CreateUserUsersPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Create User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of FullUser + public async System.Threading.Tasks.Task CreateUserUsersPostAsync(NewUser newUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = CreateUserUsersPostWithHttpInfoAsync(newUser, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Create User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (FullUser) + public async System.Threading.Tasks.Task> CreateUserUsersPostWithHttpInfoAsync(NewUser newUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'newUser' is set + if (newUser == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'newUser' when calling UsersApi->CreateUserUsersPost"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = newUser; + + + // make the HTTP request + + var task = this.AsynchronousClient.PostAsync("/users", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("CreateUserUsersPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Id User Assets + /// + /// Thrown when fails to make API call + /// + /// List<Asset> + public List GetIdUserAssetsUsersIdUserAssetsGet(int user) + { + Org.OpenAPITools.Client.ApiResponse> localVarResponse = GetIdUserAssetsUsersIdUserAssetsGetWithHttpInfo(user); + return localVarResponse.Data; + } + + /// + /// Get Id User Assets + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of List<Asset> + public Org.OpenAPITools.Client.ApiResponse> GetIdUserAssetsUsersIdUserAssetsGetWithHttpInfo(int user) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get>("/users/id/{user}/assets", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetIdUserAssetsUsersIdUserAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Id User Assets + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + public async System.Threading.Tasks.Task> GetIdUserAssetsUsersIdUserAssetsGetAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetIdUserAssetsUsersIdUserAssetsGetWithHttpInfoAsync(user, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Id User Assets + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + public async System.Threading.Tasks.Task>> GetIdUserAssetsUsersIdUserAssetsGetWithHttpInfoAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync>("/users/id/{user}/assets", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetIdUserAssetsUsersIdUserAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Me Assets + /// + /// Thrown when fails to make API call + /// List<Asset> + public List GetMeAssetsUsersMeAssetsGet() + { + Org.OpenAPITools.Client.ApiResponse> localVarResponse = GetMeAssetsUsersMeAssetsGetWithHttpInfo(); + return localVarResponse.Data; + } + + /// + /// Get Me Assets + /// + /// Thrown when fails to make API call + /// ApiResponse of List<Asset> + public Org.OpenAPITools.Client.ApiResponse> GetMeAssetsUsersMeAssetsGetWithHttpInfo() + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get>("/users/me/assets", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetMeAssetsUsersMeAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Me Assets + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + public async System.Threading.Tasks.Task> GetMeAssetsUsersMeAssetsGetAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetMeAssetsUsersMeAssetsGetWithHttpInfoAsync(cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Me Assets + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + public async System.Threading.Tasks.Task>> GetMeAssetsUsersMeAssetsGetWithHttpInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync>("/users/me/assets", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetMeAssetsUsersMeAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get User Assets + /// + /// Thrown when fails to make API call + /// + /// List<Asset> + public List GetUserAssetsUsersUserAssetsGet(string user) + { + Org.OpenAPITools.Client.ApiResponse> localVarResponse = GetUserAssetsUsersUserAssetsGetWithHttpInfo(user); + return localVarResponse.Data; + } + + /// + /// Get User Assets + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of List<Asset> + public Org.OpenAPITools.Client.ApiResponse> GetUserAssetsUsersUserAssetsGetWithHttpInfo(string user) + { + // verify the required parameter 'user' is set + if (user == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'user' when calling UsersApi->GetUserAssetsUsersUserAssetsGet"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get>("/users/{user}/assets", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUserAssetsUsersUserAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get User Assets + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of List<Asset> + public async System.Threading.Tasks.Task> GetUserAssetsUsersUserAssetsGetAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetUserAssetsUsersUserAssetsGetWithHttpInfoAsync(user, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get User Assets + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (List<Asset>) + public async System.Threading.Tasks.Task>> GetUserAssetsUsersUserAssetsGetWithHttpInfoAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'user' is set + if (user == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'user' when calling UsersApi->GetUserAssetsUsersUserAssetsGet"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync>("/users/{user}/assets", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUserAssetsUsersUserAssetsGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// User + public User GetUserUsersIdUserGet(int user) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetUserUsersIdUserGetWithHttpInfo(user); + return localVarResponse.Data; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of User + public Org.OpenAPITools.Client.ApiResponse GetUserUsersIdUserGetWithHttpInfo(int user) + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + + // make the HTTP request + var localVarResponse = this.Client.Get("/users/id/{user}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUserUsersIdUserGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of User + public async System.Threading.Tasks.Task GetUserUsersIdUserGetAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetUserUsersIdUserGetWithHttpInfoAsync(user, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (User) + public async System.Threading.Tasks.Task> GetUserUsersIdUserGetWithHttpInfoAsync(int user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/users/id/{user}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUserUsersIdUserGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// User + public User GetUserUsersUserGet(string user) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetUserUsersUserGetWithHttpInfo(user); + return localVarResponse.Data; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of User + public Org.OpenAPITools.Client.ApiResponse GetUserUsersUserGetWithHttpInfo(string user) + { + // verify the required parameter 'user' is set + if (user == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'user' when calling UsersApi->GetUserUsersUserGet"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + + // make the HTTP request + var localVarResponse = this.Client.Get("/users/{user}", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUserUsersUserGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of User + public async System.Threading.Tasks.Task GetUserUsersUserGetAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetUserUsersUserGetWithHttpInfoAsync(user, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (User) + public async System.Threading.Tasks.Task> GetUserUsersUserGetWithHttpInfoAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'user' is set + if (user == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'user' when calling UsersApi->GetUserUsersUserGet"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.PathParameters.Add("user", Org.OpenAPITools.Client.ClientUtils.ParameterToString(user)); // path parameter + + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/users/{user}", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUserUsersUserGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Users Me + /// + /// Thrown when fails to make API call + /// FullUser + public FullUser GetUsersMeUsersMeGet() + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetUsersMeUsersMeGetWithHttpInfo(); + return localVarResponse.Data; + } + + /// + /// Get Users Me + /// + /// Thrown when fails to make API call + /// ApiResponse of FullUser + public Org.OpenAPITools.Client.ApiResponse GetUsersMeUsersMeGetWithHttpInfo() + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get("/users/me", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUsersMeUsersMeGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Users Me + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of FullUser + public async System.Threading.Tasks.Task GetUsersMeUsersMeGetAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetUsersMeUsersMeGetWithHttpInfoAsync(cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Users Me + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (FullUser) + public async System.Threading.Tasks.Task> GetUsersMeUsersMeGetWithHttpInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/users/me", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUsersMeUsersMeGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Update User + /// + /// Thrown when fails to make API call + /// + /// FullUser + public FullUser UpdateUserUsersMePatch(PatchUser patchUser) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = UpdateUserUsersMePatchWithHttpInfo(patchUser); + return localVarResponse.Data; + } + + /// + /// Update User + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of FullUser + public Org.OpenAPITools.Client.ApiResponse UpdateUserUsersMePatchWithHttpInfo(PatchUser patchUser) + { + // verify the required parameter 'patchUser' is set + if (patchUser == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'patchUser' when calling UsersApi->UpdateUserUsersMePatch"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = patchUser; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Patch("/users/me", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UpdateUserUsersMePatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Update User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of FullUser + public async System.Threading.Tasks.Task UpdateUserUsersMePatchAsync(PatchUser patchUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = UpdateUserUsersMePatchWithHttpInfoAsync(patchUser, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Update User + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (FullUser) + public async System.Threading.Tasks.Task> UpdateUserUsersMePatchWithHttpInfoAsync(PatchUser patchUser, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'patchUser' is set + if (patchUser == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'patchUser' when calling UsersApi->UpdateUserUsersMePatch"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = patchUser; + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PatchAsync("/users/me", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UpdateUserUsersMePatch", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// User Password Reset Request + /// + /// Thrown when fails to make API call + /// + /// Object + public Object UserPasswordResetRequestUsersPasswordResetPut(PasswordReset passwordReset) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = UserPasswordResetRequestUsersPasswordResetPutWithHttpInfo(passwordReset); + return localVarResponse.Data; + } + + /// + /// User Password Reset Request + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of Object + public Org.OpenAPITools.Client.ApiResponse UserPasswordResetRequestUsersPasswordResetPutWithHttpInfo(PasswordReset passwordReset) + { + // verify the required parameter 'passwordReset' is set + if (passwordReset == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'passwordReset' when calling UsersApi->UserPasswordResetRequestUsersPasswordResetPut"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = passwordReset; + + + // make the HTTP request + var localVarResponse = this.Client.Put("/users/password/reset", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UserPasswordResetRequestUsersPasswordResetPut", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// User Password Reset Request + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of Object + public async System.Threading.Tasks.Task UserPasswordResetRequestUsersPasswordResetPutAsync(PasswordReset passwordReset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = UserPasswordResetRequestUsersPasswordResetPutWithHttpInfoAsync(passwordReset, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// User Password Reset Request + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (Object) + public async System.Threading.Tasks.Task> UserPasswordResetRequestUsersPasswordResetPutWithHttpInfoAsync(PasswordReset passwordReset, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'passwordReset' is set + if (passwordReset == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'passwordReset' when calling UsersApi->UserPasswordResetRequestUsersPasswordResetPut"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + "application/json" + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.Data = passwordReset; + + + // make the HTTP request + + var task = this.AsynchronousClient.PutAsync("/users/password/reset", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("UserPasswordResetRequestUsersPasswordResetPut", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs.meta new file mode 100644 index 0000000000..3a1a2439da --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb0a24d64c62a4b1a8977646b70307a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client.meta b/Assets/ThirdParty/Org.OpenAPITools/Client.meta new file mode 100644 index 0000000000..b3e42bb7ac --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 36bb7bbc9cde448ebbdbfc9144981c11 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs new file mode 100644 index 0000000000..b98bfedcdc --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs @@ -0,0 +1,645 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Text; +using System.Threading; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; +using System.Net.Http; +using System.Net.Http.Headers; +using UnityEngine.Networking; +using UnityEngine; + +namespace Org.OpenAPITools.Client +{ + /// + /// To Serialize/Deserialize JSON using our custom logic, but only when ContentType is JSON. + /// + internal class CustomJsonCodec + { + private readonly IReadableConfiguration _configuration; + private static readonly string _contentType = "application/json"; + private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings + { + // OpenAPI generated types generally hide default constructors. + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + OverrideSpecifiedNames = false + } + } + }; + + public CustomJsonCodec(IReadableConfiguration configuration) + { + _configuration = configuration; + } + + public CustomJsonCodec(JsonSerializerSettings serializerSettings, IReadableConfiguration configuration) + { + _serializerSettings = serializerSettings; + _configuration = configuration; + } + + /// + /// Serialize the object into a JSON string. + /// + /// Object to be serialized. + /// A JSON string. + public string Serialize(object obj) + { + if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) + { + // the object to be serialized is an oneOf/anyOf schema + return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); + } + else + { + return JsonConvert.SerializeObject(obj, _serializerSettings); + } + } + + public T Deserialize(UnityWebRequest request) + { + var result = (T) Deserialize(request, typeof(T)); + return result; + } + + /// + /// Deserialize the JSON string into a proper object. + /// + /// The UnityWebRequest after it has a response. + /// Object type. + /// Object representation of the JSON string. + internal object Deserialize(UnityWebRequest request, Type type) + { + if (type == typeof(byte[])) // return byte array + { + return request.downloadHandler.data; + } + + // TODO: ? if (type.IsAssignableFrom(typeof(Stream))) + if (type == typeof(Stream)) + { + // NOTE: Ignoring Content-Disposition filename support, since not all platforms + // have a location on disk to write arbitrary data (tvOS, consoles). + return new MemoryStream(request.downloadHandler.data); + } + + if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object + { + return DateTime.Parse(request.downloadHandler.text, null, System.Globalization.DateTimeStyles.RoundtripKind); + } + + if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type + { + return Convert.ChangeType(request.downloadHandler.text, type); + } + + var contentType = request.GetResponseHeader("Content-Type"); + + if (!string.IsNullOrEmpty(contentType) && contentType.Contains("application/json")) + { + var text = request.downloadHandler?.text; + + // Generated APIs that don't expect a return value provide System.Object as the type + if (type == typeof(System.Object) && (string.IsNullOrEmpty(text) || text.Trim() == "null")) + { + return null; + } + + if (request.responseCode >= 200 && request.responseCode < 300) + { + try + { + // Deserialize as a model + return JsonConvert.DeserializeObject(text, type, _serializerSettings); + } + catch (Exception e) + { + throw new UnexpectedResponseException(request, type, e.ToString()); + } + } + else + { + throw new ApiException((int)request.responseCode, request.error, text); + } + } + + if (type != typeof(System.Object) && request.responseCode >= 200 && request.responseCode < 300) + { + throw new UnexpectedResponseException(request, type); + } + + return null; + + } + + public string RootElement { get; set; } + public string Namespace { get; set; } + public string DateFormat { get; set; } + + public string ContentType + { + get { return _contentType; } + set { throw new InvalidOperationException("Not allowed to set content type."); } + } + } + /// + /// Provides a default implementation of an Api client (both synchronous and asynchronous implementations), + /// encapsulating general REST accessor use cases. + /// + /// + /// The Dispose method will manage the HttpClient lifecycle when not passed by constructor. + /// + public partial class ApiClient : IDisposable, ISynchronousClient, IAsynchronousClient + { + private readonly string _baseUrl; + + /// + /// Specifies the settings on a object. + /// These settings can be adjusted to accommodate custom serialization rules. + /// + public JsonSerializerSettings SerializerSettings { get; set; } = new JsonSerializerSettings + { + // OpenAPI generated types generally hide default constructors. + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + OverrideSpecifiedNames = false + } + } + }; + + /// + /// Initializes a new instance of the , defaulting to the global configurations' base url. + /// + public ApiClient() : + this(Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath) + { + } + + /// + /// Initializes a new instance of the . + /// + /// The target service's base path in URL format. + /// + public ApiClient(string basePath) + { + if (string.IsNullOrEmpty(basePath)) throw new ArgumentException("basePath cannot be empty"); + + _baseUrl = basePath; + } + + /// + /// Disposes resources if they were created by us + /// + public void Dispose() + { + } + + /// + /// Provides all logic for constructing a new UnityWebRequest. + /// At this point, all information for querying the service is known. Here, it is simply + /// mapped into the UnityWebRequest. + /// + /// The http verb. + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// [private] A new UnityWebRequest instance. + /// + private UnityWebRequest NewRequest( + string method, + string path, + RequestOptions options, + IReadableConfiguration configuration) + { + if (path == null) throw new ArgumentNullException("path"); + if (options == null) throw new ArgumentNullException("options"); + if (configuration == null) throw new ArgumentNullException("configuration"); + + WebRequestPathBuilder builder = new WebRequestPathBuilder(_baseUrl, path); + + builder.AddPathParameters(options.PathParameters); + + builder.AddQueryParameters(options.QueryParameters); + + string contentType = null; + if (options.HeaderParameters != null && options.HeaderParameters.ContainsKey("Content-Type")) + { + var contentTypes = options.HeaderParameters["Content-Type"]; + contentType = contentTypes.FirstOrDefault(); + } + + var uri = builder.GetFullUri(); + UnityWebRequest request = null; + + if (contentType == "multipart/form-data") + { + var formData = new List(); + foreach (var formParameter in options.FormParameters) + { + formData.Add(new MultipartFormDataSection(formParameter.Key, formParameter.Value)); + } + + request = UnityWebRequest.Post(uri, formData); + request.method = method; + } + else if (contentType == "application/x-www-form-urlencoded") + { + var form = new WWWForm(); + foreach (var kvp in options.FormParameters) + { + form.AddField(kvp.Key, kvp.Value); + } + + request = UnityWebRequest.Post(uri, form); + request.method = method; + } + else if (options.Data != null) + { + var serializer = new CustomJsonCodec(SerializerSettings, configuration); + var jsonData = serializer.Serialize(options.Data); + + // Making a post body application/json encoded is whack with UnityWebRequest. + // See: https://stackoverflow.com/questions/68156230/unitywebrequest-post-not-sending-body + request = UnityWebRequest.Put(uri, jsonData); + request.method = method; + request.SetRequestHeader("Content-Type", "application/json"); + } + else + { + request = new UnityWebRequest(builder.GetFullUri(), method); + } + + if (request.downloadHandler == null && typeof(T) != typeof(System.Object)) + { + request.downloadHandler = new DownloadHandlerBuffer(); + } + +#if UNITY_EDITOR || !UNITY_WEBGL + if (configuration.UserAgent != null) + { + request.SetRequestHeader("User-Agent", configuration.UserAgent); + } +#endif + + if (configuration.DefaultHeaders != null) + { + foreach (var headerParam in configuration.DefaultHeaders) + { + request.SetRequestHeader(headerParam.Key, headerParam.Value); + } + } + + if (options.HeaderParameters != null) + { + foreach (var headerParam in options.HeaderParameters) + { + foreach (var value in headerParam.Value) + { + // Todo make content headers actually content headers + request.SetRequestHeader(headerParam.Key, value); + } + } + } + + if (options.Cookies != null && options.Cookies.Count > 0) + { + #if UNITY_WEBGL + throw new System.InvalidOperationException("UnityWebRequest does not support setting cookies in WebGL"); + #else + if (options.Cookies.Count != 1) + { + UnityEngine.Debug.LogError("Only one cookie supported, ignoring others"); + } + + request.SetRequestHeader("Cookie", options.Cookies[0].ToString()); + #endif + } + + return request; + + } + + partial void InterceptRequest(UnityWebRequest req, string path, RequestOptions options, IReadableConfiguration configuration); + partial void InterceptResponse(UnityWebRequest req, string path, RequestOptions options, IReadableConfiguration configuration, ref object responseData); + + private ApiResponse ToApiResponse(UnityWebRequest request, object responseData) + { + T result = (T) responseData; + + var transformed = new ApiResponse((HttpStatusCode)request.responseCode, new Multimap(), result, request.downloadHandler?.text ?? "") + { + ErrorText = request.error, + Cookies = new List() + }; + + // process response headers, e.g. Access-Control-Allow-Methods + var responseHeaders = request.GetResponseHeaders(); + if (responseHeaders != null) + { + foreach (var responseHeader in request.GetResponseHeaders()) + { + transformed.Headers.Add(responseHeader.Key, ClientUtils.ParameterToString(responseHeader.Value)); + } + } + + return transformed; + } + + private async Task> ExecAsync( + UnityWebRequest request, + string path, + RequestOptions options, + IReadableConfiguration configuration, + System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var deserializer = new CustomJsonCodec(SerializerSettings, configuration); + + using (request) + { + if (configuration.Timeout > 0) + { + request.timeout = (int)Math.Ceiling(configuration.Timeout / 1000.0f); + } + + if (configuration.Proxy != null) + { + throw new InvalidOperationException("Configuration `Proxy` not supported by UnityWebRequest"); + } + + if (configuration.ClientCertificates != null) + { + // Only Android/iOS/tvOS/Standalone players can support certificates, and this + // implementation is intended to work on all platforms. + // + // TODO: Could optionally allow support for this on these platforms. + // + // See: https://docs.unity3d.com/ScriptReference/Networking.CertificateHandler.html + throw new InvalidOperationException("Configuration `ClientCertificates` not supported by UnityWebRequest on all platforms"); + } + + InterceptRequest(request, path, options, configuration); + + var asyncOp = request.SendWebRequest(); + + TaskCompletionSource tsc = new TaskCompletionSource(); + asyncOp.completed += (_) => tsc.TrySetResult(request.result); + + using (var tokenRegistration = cancellationToken.Register(request.Abort, true)) + { + await tsc.Task; + } + + if (request.result == UnityWebRequest.Result.ConnectionError || + request.result == UnityWebRequest.Result.DataProcessingError) + { + throw new ConnectionException(request); + } + + object responseData = deserializer.Deserialize(request); + + // if the response type is oneOf/anyOf, call FromJSON to deserialize the data + if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) + { + responseData = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { new ByteArrayContent(request.downloadHandler.data) }); + } + else if (typeof(T).Name == "Stream") // for binary response + { + responseData = (T) (object) new MemoryStream(request.downloadHandler.data); + } + + InterceptResponse(request, path, options, configuration, ref responseData); + + return ToApiResponse(request, responseData); + } + } + + #region IAsynchronousClient + /// + /// Make a HTTP GET request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> GetAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("GET", path, options, config), path, options, config, cancellationToken); + } + + /// + /// Make a HTTP POST request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> PostAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("POST", path, options, config), path, options, config, cancellationToken); + } + + /// + /// Make a HTTP PUT request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> PutAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("PUT", path, options, config), path, options, config, cancellationToken); + } + + /// + /// Make a HTTP DELETE request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> DeleteAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("DELETE", path, options, config), path, options, config, cancellationToken); + } + + /// + /// Make a HTTP HEAD request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> HeadAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("HEAD", path, options, config), path, options, config, cancellationToken); + } + + /// + /// Make a HTTP OPTION request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> OptionsAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("OPTIONS", path, options, config), path, options, config, cancellationToken); + } + + /// + /// Make a HTTP PATCH request (async). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// Token that enables callers to cancel the request. + /// A Task containing ApiResponse + public Task> PatchAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var config = configuration ?? GlobalConfiguration.Instance; + return ExecAsync(NewRequest("PATCH", path, options, config), path, options, config, cancellationToken); + } + #endregion IAsynchronousClient + + #region ISynchronousClient + /// + /// Make a HTTP GET request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Get(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + + /// + /// Make a HTTP POST request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Post(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + + /// + /// Make a HTTP PUT request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Put(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + + /// + /// Make a HTTP DELETE request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Delete(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + + /// + /// Make a HTTP HEAD request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Head(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + + /// + /// Make a HTTP OPTION request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Options(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + + /// + /// Make a HTTP PATCH request (synchronous). + /// + /// The target path (or resource). + /// The additional request options. + /// A per-request configuration object. It is assumed that any merge with + /// GlobalConfiguration has been done before calling this method. + /// A Task containing ApiResponse + public ApiResponse Patch(string path, RequestOptions options, IReadableConfiguration configuration = null) + { + throw new System.NotImplementedException("UnityWebRequest does not support synchronous operation"); + } + #endregion ISynchronousClient + } +} \ No newline at end of file diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs.meta new file mode 100644 index 0000000000..57805be2b4 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c2f337c5e6cb496c9cceab040961c2c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs new file mode 100644 index 0000000000..c8b51141aa --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs @@ -0,0 +1,68 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; + +namespace Org.OpenAPITools.Client +{ + /// + /// API Exception + /// + public class ApiException : Exception + { + /// + /// Gets or sets the error code (HTTP status code) + /// + /// The error code (HTTP status code). + public int ErrorCode { get; set; } + + /// + /// Gets or sets the error content (body json object) + /// + /// The error content (Http response body). + public object ErrorContent { get; private set; } + + /// + /// Gets or sets the HTTP headers + /// + /// HTTP headers + public Multimap Headers { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public ApiException() { } + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// Error message. + public ApiException(int errorCode, string message) : base(message) + { + this.ErrorCode = errorCode; + } + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// Error message. + /// Error content. + /// HTTP Headers. + public ApiException(int errorCode, string message, object errorContent = null, Multimap headers = null) : base(message) + { + this.ErrorCode = errorCode; + this.ErrorContent = errorContent; + this.Headers = headers; + } + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs.meta new file mode 100644 index 0000000000..7de35190ff --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4648f176361564a7db678fd7bd415d19 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs new file mode 100644 index 0000000000..298c9ab2d8 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs @@ -0,0 +1,166 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.Net; + +namespace Org.OpenAPITools.Client +{ + /// + /// Provides a non-generic contract for the ApiResponse wrapper. + /// + public interface IApiResponse + { + /// + /// The data type of + /// + Type ResponseType { get; } + + /// + /// The content of this response + /// + Object Content { get; } + + /// + /// Gets or sets the status code (HTTP status code) + /// + /// The status code. + HttpStatusCode StatusCode { get; } + + /// + /// Gets or sets the HTTP headers + /// + /// HTTP headers + Multimap Headers { get; } + + /// + /// Gets or sets any error text defined by the calling client. + /// + string ErrorText { get; set; } + + /// + /// Gets or sets any cookies passed along on the response. + /// + List Cookies { get; set; } + + /// + /// The raw content of this response + /// + string RawContent { get; } + } + + /// + /// API Response + /// + public class ApiResponse : IApiResponse + { + #region Properties + + /// + /// Gets or sets the status code (HTTP status code) + /// + /// The status code. + public HttpStatusCode StatusCode { get; } + + /// + /// Gets or sets the HTTP headers + /// + /// HTTP headers + public Multimap Headers { get; } + + /// + /// Gets or sets the data (parsed HTTP body) + /// + /// The data. + public T Data { get; } + + /// + /// Gets or sets any error text defined by the calling client. + /// + public string ErrorText { get; set; } + + /// + /// Gets or sets any cookies passed along on the response. + /// + public List Cookies { get; set; } + + /// + /// The content of this response + /// + public Type ResponseType + { + get { return typeof(T); } + } + + /// + /// The data type of + /// + public object Content + { + get { return Data; } + } + + /// + /// The raw content + /// + public string RawContent { get; } + + #endregion Properties + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// HTTP headers. + /// Data (parsed HTTP body) + /// Raw content. + public ApiResponse(HttpStatusCode statusCode, Multimap headers, T data, string rawContent) + { + StatusCode = statusCode; + Headers = headers; + Data = data; + RawContent = rawContent; + } + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// HTTP headers. + /// Data (parsed HTTP body) + public ApiResponse(HttpStatusCode statusCode, Multimap headers, T data) : this(statusCode, headers, data, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// Data (parsed HTTP body) + /// Raw content. + public ApiResponse(HttpStatusCode statusCode, T data, string rawContent) : this(statusCode, null, data, rawContent) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// HTTP status code. + /// Data (parsed HTTP body) + public ApiResponse(HttpStatusCode statusCode, T data) : this(statusCode, data, null) + { + } + + #endregion Constructors + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs.meta new file mode 100644 index 0000000000..4e0aa987fe --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiResponse.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5b445ecbd8584ea1a861556b3bbecda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs new file mode 100644 index 0000000000..53f47f2ece --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs @@ -0,0 +1,247 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Org.OpenAPITools.Client +{ + /// + /// Utility functions providing some benefit to API client consumers. + /// + public static class ClientUtils + { + /// + /// Sanitize filename by removing the path + /// + /// Filename + /// Filename + public static string SanitizeFilename(string filename) + { + Match match = Regex.Match(filename, @".*[/\\](.*)$"); + return match.Success ? match.Groups[1].Value : filename; + } + + /// + /// Convert params to key/value pairs. + /// Use collectionFormat to properly format lists and collections. + /// + /// The swagger-supported collection format, one of: csv, tsv, ssv, pipes, multi + /// Key name. + /// Value object. + /// A multimap of keys with 1..n associated values. + public static Multimap ParameterToMultiMap(string collectionFormat, string name, object value) + { + var parameters = new Multimap(); + + if (value is ICollection collection && collectionFormat == "multi") + { + foreach (var item in collection) + { + parameters.Add(name, ParameterToString(item)); + } + } + else if (value is IDictionary dictionary) + { + if(collectionFormat == "deepObject") { + foreach (DictionaryEntry entry in dictionary) + { + parameters.Add(name + "[" + entry.Key + "]", ParameterToString(entry.Value)); + } + } + else { + foreach (DictionaryEntry entry in dictionary) + { + parameters.Add(entry.Key.ToString(), ParameterToString(entry.Value)); + } + } + } + else + { + parameters.Add(name, ParameterToString(value)); + } + + return parameters; + } + + /// + /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime. + /// If parameter is a list, join the list with ",". + /// Otherwise just return the string. + /// + /// The parameter (header, path, query, form). + /// An optional configuration instance, providing formatting options used in processing. + /// Formatted string. + public static string ParameterToString(object obj, IReadableConfiguration configuration = null) + { + if (obj is DateTime dateTime) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return dateTime.ToString((configuration ?? GlobalConfiguration.Instance).DateTimeFormat); + if (obj is DateTimeOffset dateTimeOffset) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return dateTimeOffset.ToString((configuration ?? GlobalConfiguration.Instance).DateTimeFormat); + if (obj is bool boolean) + return boolean ? "true" : "false"; + if (obj is ICollection collection) { + List entries = new List(); + foreach (var entry in collection) + entries.Add(ParameterToString(entry, configuration)); + return string.Join(",", entries); + } + if (obj is Enum && HasEnumMemberAttrValue(obj)) + return GetEnumMemberAttrValue(obj); + + return Convert.ToString(obj, CultureInfo.InvariantCulture); + } + + /// + /// Serializes the given object when not null. Otherwise return null. + /// + /// The object to serialize. + /// Serialized string. + public static string Serialize(object obj) + { + return obj != null ? Newtonsoft.Json.JsonConvert.SerializeObject(obj) : null; + } + + /// + /// Encode string in base64 format. + /// + /// string to be encoded. + /// Encoded string. + public static string Base64Encode(string text) + { + return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text)); + } + + /// + /// Convert stream to byte array + /// + /// Input stream to be converted + /// Byte array + public static byte[] ReadAsBytes(Stream inputStream) + { + using (var ms = new MemoryStream()) + { + inputStream.CopyTo(ms); + return ms.ToArray(); + } + } + + /// + /// Select the Content-Type header's value from the given content-type array: + /// if JSON type exists in the given array, use it; + /// otherwise use the first one defined in 'consumes' + /// + /// The Content-Type array to select from. + /// The Content-Type header to use. + public static string SelectHeaderContentType(string[] contentTypes) + { + if (contentTypes.Length == 0) + return null; + + foreach (var contentType in contentTypes) + { + if (IsJsonMime(contentType)) + return contentType; + } + + return contentTypes[0]; // use the first content type specified in 'consumes' + } + + /// + /// Select the Accept header's value from the given accepts array: + /// if JSON exists in the given array, use it; + /// otherwise use all of them (joining into a string) + /// + /// The accepts array to select from. + /// The Accept header to use. + public static string SelectHeaderAccept(string[] accepts) + { + if (accepts.Length == 0) + return null; + + if (accepts.Contains("application/json", StringComparer.OrdinalIgnoreCase)) + return "application/json"; + + return string.Join(",", accepts); + } + + /// + /// Provides a case-insensitive check that a provided content type is a known JSON-like content type. + /// + public static readonly Regex JsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"); + + /// + /// Check if the given MIME is a JSON MIME. + /// JSON MIME examples: + /// application/json + /// application/json; charset=UTF8 + /// APPLICATION/JSON + /// application/vnd.company+json + /// + /// MIME + /// Returns True if MIME type is json. + public static bool IsJsonMime(string mime) + { + if (string.IsNullOrWhiteSpace(mime)) return false; + + return JsonRegex.IsMatch(mime) || mime.Equals("application/json-patch+json"); + } + + /// + /// Is the Enum decorated with EnumMember Attribute + /// + /// + /// true if found + private static bool HasEnumMemberAttrValue(object enumVal) + { + if (enumVal == null) + throw new ArgumentNullException(nameof(enumVal)); + var enumType = enumVal.GetType(); + var memInfo = enumType.GetMember(enumVal.ToString() ?? throw new InvalidOperationException()); + var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType().FirstOrDefault(); + if (attr != null) return true; + return false; + } + + /// + /// Get the EnumMember value + /// + /// + /// EnumMember value as string otherwise null + private static string GetEnumMemberAttrValue(object enumVal) + { + if (enumVal == null) + throw new ArgumentNullException(nameof(enumVal)); + var enumType = enumVal.GetType(); + var memInfo = enumType.GetMember(enumVal.ToString() ?? throw new InvalidOperationException()); + var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType().FirstOrDefault(); + if (attr != null) + { + return attr.Value; + } + return null; + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs.meta new file mode 100644 index 0000000000..2132771f5e --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ClientUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b27c4007314a2462c91582f15b47c9fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs new file mode 100644 index 0000000000..fbc0930ec1 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs @@ -0,0 +1,612 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Net.Http; +using System.Net.Security; + +namespace Org.OpenAPITools.Client +{ + /// + /// Represents a set of configuration settings + /// + public class Configuration : IReadableConfiguration + { + #region Constants + + /// + /// Version of the package. + /// + /// Version of the package. + public const string Version = "1.0.0"; + + /// + /// Identifier for ISO 8601 DateTime Format + /// + /// See https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 for more information. + // ReSharper disable once InconsistentNaming + public const string ISO8601_DATETIME_FORMAT = "o"; + + #endregion Constants + + #region Static Members + + /// + /// Default creation of exceptions for a given method name and response object + /// + public static readonly ExceptionFactory DefaultExceptionFactory = (methodName, response) => + { + var status = (int)response.StatusCode; + if (status >= 400) + { + return new ApiException(status, + string.Format("Error calling {0}: {1}", methodName, response.RawContent), + response.RawContent, response.Headers); + } + if (status == 0) + { + return new ApiException(status, + string.Format("Error calling {0}: {1}", methodName, response.ErrorText), response.ErrorText); + } + return null; + }; + + #endregion Static Members + + #region Private Members + + /// + /// Defines the base path of the target API server. + /// Example: http://localhost:3000/v1/ + /// + private string _basePath; + + private bool _useDefaultCredentials = false; + + /// + /// Gets or sets the API key based on the authentication name. + /// This is the key and value comprising the "secret" for accessing an API. + /// + /// The API key. + private IDictionary _apiKey; + + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// The prefix of the API key. + private IDictionary _apiKeyPrefix; + + private string _dateTimeFormat = ISO8601_DATETIME_FORMAT; + private string _tempFolderPath = Path.GetTempPath(); + + /// + /// Gets or sets the servers defined in the OpenAPI spec. + /// + /// The servers + private IList> _servers; + + /// + /// Gets or sets the operation servers defined in the OpenAPI spec. + /// + /// The operation servers + private IReadOnlyDictionary>> _operationServers; + + #endregion Private Members + + #region Constructors + + /// + /// Initializes a new instance of the class + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] + public Configuration() + { + Proxy = null; + UserAgent = WebUtility.UrlEncode("OpenAPI-Generator/1.0.0/csharp"); + BasePath = "http://localhost"; + DefaultHeaders = new ConcurrentDictionary(); + ApiKey = new ConcurrentDictionary(); + ApiKeyPrefix = new ConcurrentDictionary(); + Servers = new List>() + { + { + new Dictionary { + {"url", ""}, + {"description", "No description provided"}, + } + } + }; + OperationServers = new Dictionary>>() + { + }; + + // Setting Timeout has side effects (forces ApiClient creation). + Timeout = 100000; + } + + /// + /// Initializes a new instance of the class + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] + public Configuration( + IDictionary defaultHeaders, + IDictionary apiKey, + IDictionary apiKeyPrefix, + string basePath = "http://localhost") : this() + { + if (string.IsNullOrWhiteSpace(basePath)) + throw new ArgumentException("The provided basePath is invalid.", "basePath"); + if (defaultHeaders == null) + throw new ArgumentNullException("defaultHeaders"); + if (apiKey == null) + throw new ArgumentNullException("apiKey"); + if (apiKeyPrefix == null) + throw new ArgumentNullException("apiKeyPrefix"); + + BasePath = basePath; + + foreach (var keyValuePair in defaultHeaders) + { + DefaultHeaders.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKey) + { + ApiKey.Add(keyValuePair); + } + + foreach (var keyValuePair in apiKeyPrefix) + { + ApiKeyPrefix.Add(keyValuePair); + } + } + + #endregion Constructors + + #region Properties + + /// + /// Gets or sets the base path for API access. + /// + public virtual string BasePath + { + get { return _basePath; } + set { _basePath = value; } + } + + /// + /// Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + /// + public virtual bool UseDefaultCredentials + { + get { return _useDefaultCredentials; } + set { _useDefaultCredentials = value; } + } + + /// + /// Gets or sets the default header. + /// + [Obsolete("Use DefaultHeaders instead.")] + public virtual IDictionary DefaultHeader + { + get + { + return DefaultHeaders; + } + set + { + DefaultHeaders = value; + } + } + + /// + /// Gets or sets the default headers. + /// + public virtual IDictionary DefaultHeaders { get; set; } + + /// + /// Gets or sets the HTTP timeout (milliseconds) of ApiClient. Default to 100000 milliseconds. + /// + public virtual int Timeout { get; set; } + + /// + /// Gets or sets the proxy + /// + /// Proxy. + public virtual WebProxy Proxy { get; set; } + + /// + /// Gets or sets the HTTP user agent. + /// + /// Http user agent. + public virtual string UserAgent { get; set; } + + /// + /// Gets or sets the username (HTTP basic authentication). + /// + /// The username. + public virtual string Username { get; set; } + + /// + /// Gets or sets the password (HTTP basic authentication). + /// + /// The password. + public virtual string Password { get; set; } + + /// + /// Gets the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + public string GetApiKeyWithPrefix(string apiKeyIdentifier) + { + string apiKeyValue; + ApiKey.TryGetValue(apiKeyIdentifier, out apiKeyValue); + string apiKeyPrefix; + if (ApiKeyPrefix.TryGetValue(apiKeyIdentifier, out apiKeyPrefix)) + { + return apiKeyPrefix + " " + apiKeyValue; + } + + return apiKeyValue; + } + + /// + /// Gets or sets certificate collection to be sent with requests. + /// + /// X509 Certificate collection. + public X509CertificateCollection ClientCertificates { get; set; } + + /// + /// Gets or sets the access token for OAuth2 authentication. + /// + /// This helper property simplifies code generation. + /// + /// The access token. + public virtual string AccessToken { get; set; } + + /// + /// Gets or sets the temporary folder path to store the files downloaded from the server. + /// + /// Folder path. + public virtual string TempFolderPath + { + get { return _tempFolderPath; } + + set + { + if (string.IsNullOrEmpty(value)) + { + _tempFolderPath = Path.GetTempPath(); + return; + } + + // create the directory if it does not exist + if (!Directory.Exists(value)) + { + Directory.CreateDirectory(value); + } + + // check if the path contains directory separator at the end + if (value[value.Length - 1] == Path.DirectorySeparatorChar) + { + _tempFolderPath = value; + } + else + { + _tempFolderPath = value + Path.DirectorySeparatorChar; + } + } + } + + /// + /// Gets or sets the date time format used when serializing in the ApiClient + /// By default, it's set to ISO 8601 - "o", for others see: + /// https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx + /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx + /// No validation is done to ensure that the string you're providing is valid + /// + /// The DateTimeFormat string + public virtual string DateTimeFormat + { + get { return _dateTimeFormat; } + set + { + if (string.IsNullOrEmpty(value)) + { + // Never allow a blank or null string, go back to the default + _dateTimeFormat = ISO8601_DATETIME_FORMAT; + return; + } + + // Caution, no validation when you choose date time format other than ISO 8601 + // Take a look at the above links + _dateTimeFormat = value; + } + } + + /// + /// Gets or sets the prefix (e.g. Token) of the API key based on the authentication name. + /// + /// Whatever you set here will be prepended to the value defined in AddApiKey. + /// + /// An example invocation here might be: + /// + /// ApiKeyPrefix["Authorization"] = "Bearer"; + /// + /// … where ApiKey["Authorization"] would then be used to set the value of your bearer token. + /// + /// + /// OAuth2 workflows should set tokens via AccessToken. + /// + /// + /// The prefix of the API key. + public virtual IDictionary ApiKeyPrefix + { + get { return _apiKeyPrefix; } + set + { + if (value == null) + { + throw new InvalidOperationException("ApiKeyPrefix collection may not be null."); + } + _apiKeyPrefix = value; + } + } + + /// + /// Gets or sets the API key based on the authentication name. + /// + /// The API key. + public virtual IDictionary ApiKey + { + get { return _apiKey; } + set + { + if (value == null) + { + throw new InvalidOperationException("ApiKey collection may not be null."); + } + _apiKey = value; + } + } + + /// + /// Gets or sets the servers. + /// + /// The servers. + public virtual IList> Servers + { + get { return _servers; } + set + { + if (value == null) + { + throw new InvalidOperationException("Servers may not be null."); + } + _servers = value; + } + } + + /// + /// Gets or sets the operation servers. + /// + /// The operation servers. + public virtual IReadOnlyDictionary>> OperationServers + { + get { return _operationServers; } + set + { + if (value == null) + { + throw new InvalidOperationException("Operation servers may not be null."); + } + _operationServers = value; + } + } + + /// + /// Returns URL based on server settings without providing values + /// for the variables + /// + /// Array index of the server settings. + /// The server URL. + public string GetServerUrl(int index) + { + return GetServerUrl(Servers, index, null); + } + + /// + /// Returns URL based on server settings. + /// + /// Array index of the server settings. + /// Dictionary of the variables and the corresponding values. + /// The server URL. + public string GetServerUrl(int index, Dictionary inputVariables) + { + return GetServerUrl(Servers, index, inputVariables); + } + + /// + /// Returns URL based on operation server settings. + /// + /// Operation associated with the request path. + /// Array index of the server settings. + /// The operation server URL. + public string GetOperationServerUrl(string operation, int index) + { + return GetOperationServerUrl(operation, index, null); + } + + /// + /// Returns URL based on operation server settings. + /// + /// Operation associated with the request path. + /// Array index of the server settings. + /// Dictionary of the variables and the corresponding values. + /// The operation server URL. + public string GetOperationServerUrl(string operation, int index, Dictionary inputVariables) + { + if (operation != null && OperationServers.TryGetValue(operation, out var operationServer)) + { + return GetServerUrl(operationServer, index, inputVariables); + } + + return null; + } + + /// + /// Returns URL based on server settings. + /// + /// Dictionary of server settings. + /// Array index of the server settings. + /// Dictionary of the variables and the corresponding values. + /// The server URL. + private string GetServerUrl(IList> servers, int index, Dictionary inputVariables) + { + if (index < 0 || index >= servers.Count) + { + throw new InvalidOperationException($"Invalid index {index} when selecting the server. Must be less than {servers.Count}."); + } + + if (inputVariables == null) + { + inputVariables = new Dictionary(); + } + + IReadOnlyDictionary server = servers[index]; + string url = (string)server["url"]; + + if (server.ContainsKey("variables")) + { + // go through each variable and assign a value + foreach (KeyValuePair variable in (IReadOnlyDictionary)server["variables"]) + { + + IReadOnlyDictionary serverVariables = (IReadOnlyDictionary)(variable.Value); + + if (inputVariables.ContainsKey(variable.Key)) + { + if (((List)serverVariables["enum_values"]).Contains(inputVariables[variable.Key])) + { + url = url.Replace("{" + variable.Key + "}", inputVariables[variable.Key]); + } + else + { + throw new InvalidOperationException($"The variable `{variable.Key}` in the server URL has invalid value #{inputVariables[variable.Key]}. Must be {(List)serverVariables["enum_values"]}"); + } + } + else + { + // use default value + url = url.Replace("{" + variable.Key + "}", (string)serverVariables["default_value"]); + } + } + } + + return url; + } + + /// + /// Gets and Sets the RemoteCertificateValidationCallback + /// + public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } + + #endregion Properties + + #region Methods + + /// + /// Returns a string with essential information for debugging. + /// + public static string ToDebugReport() + { + string report = "C# SDK (Org.OpenAPITools) Debug Report:\n"; + report += " OS: " + System.Environment.OSVersion + "\n"; + report += " .NET Framework Version: " + System.Environment.Version + "\n"; + report += " Version of the API: 0.1.0\n"; + report += " SDK Package Version: 1.0.0\n"; + + return report; + } + + /// + /// Add Api Key Header. + /// + /// Api Key name. + /// Api Key value. + /// + public void AddApiKey(string key, string value) + { + ApiKey[key] = value; + } + + /// + /// Sets the API key prefix. + /// + /// Api Key name. + /// Api Key value. + public void AddApiKeyPrefix(string key, string value) + { + ApiKeyPrefix[key] = value; + } + + #endregion Methods + + #region Static Members + /// + /// Merge configurations. + /// + /// First configuration. + /// Second configuration. + /// Merged configuration. + public static IReadableConfiguration MergeConfigurations(IReadableConfiguration first, IReadableConfiguration second) + { + if (second == null) return first ?? GlobalConfiguration.Instance; + + Dictionary apiKey = first.ApiKey.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Dictionary apiKeyPrefix = first.ApiKeyPrefix.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + Dictionary defaultHeaders = first.DefaultHeaders.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + foreach (var kvp in second.ApiKey) apiKey[kvp.Key] = kvp.Value; + foreach (var kvp in second.ApiKeyPrefix) apiKeyPrefix[kvp.Key] = kvp.Value; + foreach (var kvp in second.DefaultHeaders) defaultHeaders[kvp.Key] = kvp.Value; + + var config = new Configuration + { + ApiKey = apiKey, + ApiKeyPrefix = apiKeyPrefix, + DefaultHeaders = defaultHeaders, + BasePath = second.BasePath ?? first.BasePath, + Timeout = second.Timeout, + Proxy = second.Proxy ?? first.Proxy, + UserAgent = second.UserAgent ?? first.UserAgent, + Username = second.Username ?? first.Username, + Password = second.Password ?? first.Password, + AccessToken = second.AccessToken ?? first.AccessToken, + TempFolderPath = second.TempFolderPath ?? first.TempFolderPath, + DateTimeFormat = second.DateTimeFormat ?? first.DateTimeFormat, + ClientCertificates = second.ClientCertificates ?? first.ClientCertificates, + UseDefaultCredentials = second.UseDefaultCredentials, + RemoteCertificateValidationCallback = second.RemoteCertificateValidationCallback ?? first.RemoteCertificateValidationCallback, + }; + return config; + } + #endregion Static Members + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs.meta new file mode 100644 index 0000000000..4913850e65 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/Configuration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 343b1b38dff1b45e991f60a1a2ff571b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs new file mode 100644 index 0000000000..50074d9f9e --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs @@ -0,0 +1,29 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using UnityEngine.Networking; + +namespace Org.OpenAPITools.Client +{ + public class ConnectionException : Exception + { + public UnityWebRequest.Result Result { get; private set; } + public string Error { get; private set; } + + // NOTE: Cannot keep reference to the request since it will be disposed. + public ConnectionException(UnityWebRequest request) + : base($"result={request.result} error={request.error}") + { + Result = request.result; + Error = request.error ?? ""; + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs.meta new file mode 100644 index 0000000000..1479548c89 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ConnectionException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ebebd66bdf14c47b7bd257b33e7e1804 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs new file mode 100644 index 0000000000..7b8fba2e12 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs @@ -0,0 +1,22 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; + +namespace Org.OpenAPITools.Client +{ + /// + /// A delegate to ExceptionFactory method + /// + /// Method name + /// Response + /// Exceptions + public delegate Exception ExceptionFactory(string methodName, IApiResponse response); +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs.meta new file mode 100644 index 0000000000..7af9e73b58 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ExceptionFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b3dca7911a294c51b851d2eb4a8ca8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs new file mode 100644 index 0000000000..17fb28af08 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs @@ -0,0 +1,67 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System.Collections.Generic; + +namespace Org.OpenAPITools.Client +{ + /// + /// provides a compile-time extension point for globally configuring + /// API Clients. + /// + /// + /// A customized implementation via partial class may reside in another file and may + /// be excluded from automatic generation via a .openapi-generator-ignore file. + /// + public partial class GlobalConfiguration : Configuration + { + #region Private Members + + private static readonly object GlobalConfigSync = new { }; + private static IReadableConfiguration _globalConfiguration; + + #endregion Private Members + + #region Constructors + + /// + private GlobalConfiguration() + { + } + + /// + public GlobalConfiguration(IDictionary defaultHeader, IDictionary apiKey, IDictionary apiKeyPrefix, string basePath = "http://localhost:3000/api") : base(defaultHeader, apiKey, apiKeyPrefix, basePath) + { + } + + static GlobalConfiguration() + { + Instance = new GlobalConfiguration(); + } + + #endregion Constructors + + /// + /// Gets or sets the default Configuration. + /// + /// Configuration. + public static IReadableConfiguration Instance + { + get { return _globalConfiguration; } + set + { + lock (GlobalConfigSync) + { + _globalConfiguration = value; + } + } + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs.meta new file mode 100644 index 0000000000..36f801f265 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/GlobalConfiguration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7a214e7285ed44527938b547788aa53e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs new file mode 100644 index 0000000000..a8e6bb4342 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs @@ -0,0 +1,37 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; + +namespace Org.OpenAPITools.Client +{ + /// + /// Represents configuration aspects required to interact with the API endpoints. + /// + public interface IApiAccessor + { + /// + /// Gets or sets the configuration object + /// + /// An instance of the Configuration + IReadableConfiguration Configuration { get; set; } + + /// + /// Gets the base path of the API client. + /// + /// The base path + string GetBasePath(); + + /// + /// Provides a factory method hook for the creation of exceptions. + /// + ExceptionFactory ExceptionFactory { get; set; } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs.meta new file mode 100644 index 0000000000..4e83e55a28 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/IApiAccessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56284ecd7a31c4f20ae39175e72233e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs new file mode 100644 index 0000000000..a41541642e --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs @@ -0,0 +1,100 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Threading.Tasks; + +namespace Org.OpenAPITools.Client +{ + /// + /// Contract for Asynchronous RESTful API interactions. + /// + /// This interface allows consumers to provide a custom API accessor client. + /// + public interface IAsynchronousClient + { + /// + /// Executes a non-blocking call to some using the GET http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> GetAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Executes a non-blocking call to some using the POST http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> PostAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Executes a non-blocking call to some using the PUT http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> PutAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Executes a non-blocking call to some using the DELETE http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> DeleteAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Executes a non-blocking call to some using the HEAD http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> HeadAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Executes a non-blocking call to some using the OPTIONS http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> OptionsAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Executes a non-blocking call to some using the PATCH http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// Cancellation Token to cancel the request. + /// The return type. + /// A task eventually representing the response data, decorated with + Task> PatchAsync(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs.meta new file mode 100644 index 0000000000..d7b1ee80ec --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/IAsynchronousClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7eb059d43bef1497dbefe1e32059de32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs new file mode 100644 index 0000000000..c326b57a08 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs @@ -0,0 +1,141 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; + +namespace Org.OpenAPITools.Client +{ + /// + /// Represents a readable-only configuration contract. + /// + public interface IReadableConfiguration + { + /// + /// Gets the access token. + /// + /// Access token. + string AccessToken { get; } + + /// + /// Gets the API key. + /// + /// API key. + IDictionary ApiKey { get; } + + /// + /// Gets the API key prefix. + /// + /// API key prefix. + IDictionary ApiKeyPrefix { get; } + + /// + /// Gets the base path. + /// + /// Base path. + string BasePath { get; } + + /// + /// Gets the date time format. + /// + /// Date time format. + string DateTimeFormat { get; } + + /// + /// Gets the default header. + /// + /// Default header. + [Obsolete("Use DefaultHeaders instead.")] + IDictionary DefaultHeader { get; } + + /// + /// Gets the default headers. + /// + /// Default headers. + IDictionary DefaultHeaders { get; } + + /// + /// Gets the temp folder path. + /// + /// Temp folder path. + string TempFolderPath { get; } + + /// + /// Gets the HTTP connection timeout (in milliseconds) + /// + /// HTTP connection timeout. + int Timeout { get; } + + /// + /// Gets the proxy. + /// + /// Proxy. + WebProxy Proxy { get; } + + /// + /// Gets the user agent. + /// + /// User agent. + string UserAgent { get; } + + /// + /// Gets the username. + /// + /// Username. + string Username { get; } + + /// + /// Gets the password. + /// + /// Password. + string Password { get; } + + /// + /// Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false. + /// + bool UseDefaultCredentials { get; } + + /// + /// Get the servers associated with the operation. + /// + /// Operation servers. + IReadOnlyDictionary>> OperationServers { get; } + + /// + /// Gets the API key with prefix. + /// + /// API key identifier (authentication scheme). + /// API key with prefix. + string GetApiKeyWithPrefix(string apiKeyIdentifier); + + /// + /// Gets the Operation server url at the provided index. + /// + /// Operation server name. + /// Index of the operation server settings. + /// + string GetOperationServerUrl(string operation, int index); + + /// + /// Gets certificate collection to be sent with requests. + /// + /// X509 Certificate collection. + X509CertificateCollection ClientCertificates { get; } + + /// + /// Callback function for handling the validation of remote certificates. Useful for certificate pinning and + /// overriding certificate errors in the scope of a request. + /// + RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs.meta new file mode 100644 index 0000000000..eb52e0a603 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/IReadableConfiguration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c00c05b0a57104c01887d215eb67871c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs new file mode 100644 index 0000000000..17551a97d9 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs @@ -0,0 +1,93 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.IO; + +namespace Org.OpenAPITools.Client +{ + /// + /// Contract for Synchronous RESTful API interactions. + /// + /// This interface allows consumers to provide a custom API accessor client. + /// + public interface ISynchronousClient + { + /// + /// Executes a blocking call to some using the GET http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Get(string path, RequestOptions options, IReadableConfiguration configuration = null); + + /// + /// Executes a blocking call to some using the POST http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Post(string path, RequestOptions options, IReadableConfiguration configuration = null); + + /// + /// Executes a blocking call to some using the PUT http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Put(string path, RequestOptions options, IReadableConfiguration configuration = null); + + /// + /// Executes a blocking call to some using the DELETE http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Delete(string path, RequestOptions options, IReadableConfiguration configuration = null); + + /// + /// Executes a blocking call to some using the HEAD http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Head(string path, RequestOptions options, IReadableConfiguration configuration = null); + + /// + /// Executes a blocking call to some using the OPTIONS http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Options(string path, RequestOptions options, IReadableConfiguration configuration = null); + + /// + /// Executes a blocking call to some using the PATCH http verb. + /// + /// The relative path to invoke. + /// The request parameters to pass along to the client. + /// Per-request configurable settings. + /// The return type. + /// The response data, decorated with + ApiResponse Patch(string path, RequestOptions options, IReadableConfiguration configuration = null); + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs.meta new file mode 100644 index 0000000000..0653acf151 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ISynchronousClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f07939cfa05c148409eaf097a177fd12 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs new file mode 100644 index 0000000000..3673b10fe2 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs @@ -0,0 +1,295 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Org.OpenAPITools.Client +{ + /// + /// A dictionary in which one key has many associated values. + /// + /// The type of the key + /// The type of the value associated with the key. + public class Multimap : IDictionary> + { + #region Private Fields + + private readonly Dictionary> _dictionary; + + #endregion Private Fields + + #region Constructors + + /// + /// Empty Constructor. + /// + public Multimap() + { + _dictionary = new Dictionary>(); + } + + /// + /// Constructor with comparer. + /// + /// + public Multimap(IEqualityComparer comparer) + { + _dictionary = new Dictionary>(comparer); + } + + #endregion Constructors + + #region Enumerators + + /// + /// To get the enumerator. + /// + /// Enumerator + public IEnumerator>> GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + /// + /// To get the enumerator. + /// + /// Enumerator + IEnumerator IEnumerable.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + #endregion Enumerators + + #region Public Members + /// + /// Add values to Multimap + /// + /// Key value pair + public void Add(KeyValuePair> item) + { + if (!TryAdd(item.Key, item.Value)) + throw new InvalidOperationException("Could not add values to Multimap."); + } + + /// + /// Add Multimap to Multimap + /// + /// Multimap + public void Add(Multimap multimap) + { + foreach (var item in multimap) + { + if (!TryAdd(item.Key, item.Value)) + throw new InvalidOperationException("Could not add values to Multimap."); + } + } + + /// + /// Clear Multimap + /// + public void Clear() + { + _dictionary.Clear(); + } + + /// + /// Determines whether Multimap contains the specified item. + /// + /// Key value pair + /// Method needs to be implemented + /// true if the Multimap contains the item; otherwise, false. + public bool Contains(KeyValuePair> item) + { + throw new NotImplementedException(); + } + + /// + /// Copy items of the Multimap to an array, + /// starting at a particular array index. + /// + /// The array that is the destination of the items copied + /// from Multimap. The array must have zero-based indexing. + /// The zero-based index in array at which copying begins. + /// Method needs to be implemented + public void CopyTo(KeyValuePair>[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + /// + /// Removes the specified item from the Multimap. + /// + /// Key value pair + /// true if the item is successfully removed; otherwise, false. + /// Method needs to be implemented + public bool Remove(KeyValuePair> item) + { + throw new NotImplementedException(); + } + + /// + /// Gets the number of items contained in the Multimap. + /// + public int Count => _dictionary.Count; + + /// + /// Gets a value indicating whether the Multimap is read-only. + /// + public bool IsReadOnly => false; + + /// + /// Adds an item with the provided key and value to the Multimap. + /// + /// The object to use as the key of the item to add. + /// The object to use as the value of the item to add. + /// Thrown when couldn't add the value to Multimap. + public void Add(TKey key, IList value) + { + if (value != null && value.Count > 0) + { + if (_dictionary.TryGetValue(key, out var list)) + { + foreach (var k in value) list.Add(k); + } + else + { + list = new List(value); + if (!TryAdd(key, list)) + throw new InvalidOperationException("Could not add values to Multimap."); + } + } + } + + /// + /// Determines whether the Multimap contains an item with the specified key. + /// + /// The key to locate in the Multimap. + /// true if the Multimap contains an item with + /// the key; otherwise, false. + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + /// + /// Removes item with the specified key from the Multimap. + /// + /// The key to locate in the Multimap. + /// true if the item is successfully removed; otherwise, false. + public bool Remove(TKey key) + { + return TryRemove(key, out var _); + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the + /// key is found; otherwise, the default value for the type of the value parameter. + /// This parameter is passed uninitialized. + /// true if the object that implements Multimap contains + /// an item with the specified key; otherwise, false. + public bool TryGetValue(TKey key, out IList value) + { + return _dictionary.TryGetValue(key, out value); + } + + /// + /// Gets or sets the item with the specified key. + /// + /// The key of the item to get or set. + /// The value of the specified key. + public IList this[TKey key] + { + get => _dictionary[key]; + set => _dictionary[key] = value; + } + + /// + /// Gets a System.Collections.Generic.ICollection containing the keys of the Multimap. + /// + public ICollection Keys => _dictionary.Keys; + + /// + /// Gets a System.Collections.Generic.ICollection containing the values of the Multimap. + /// + public ICollection> Values => _dictionary.Values; + + /// + /// Copy the items of the Multimap to an System.Array, + /// starting at a particular System.Array index. + /// + /// The one-dimensional System.Array that is the destination of the items copied + /// from Multimap. The System.Array must have zero-based indexing. + /// The zero-based index in array at which copying begins. + public void CopyTo(Array array, int index) + { + ((ICollection)_dictionary).CopyTo(array, index); + } + + /// + /// Adds an item with the provided key and value to the Multimap. + /// + /// The object to use as the key of the item to add. + /// The object to use as the value of the item to add. + /// Thrown when couldn't add value to Multimap. + public void Add(TKey key, TValue value) + { + if (value != null) + { + if (_dictionary.TryGetValue(key, out var list)) + { + list.Add(value); + } + else + { + list = new List { value }; + if (!TryAdd(key, list)) + throw new InvalidOperationException("Could not add value to Multimap."); + } + } + } + + #endregion Public Members + + #region Private Members + + /** + * Helper method to encapsulate generator differences between dictionary types. + */ + private bool TryRemove(TKey key, out IList value) + { + _dictionary.TryGetValue(key, out value); + return _dictionary.Remove(key); + } + + /** + * Helper method to encapsulate generator differences between dictionary types. + */ + private bool TryAdd(TKey key, IList value) + { + try + { + _dictionary.Add(key, value); + } + catch (ArgumentException) + { + return false; + } + + return true; + } + #endregion Private Members + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs.meta new file mode 100644 index 0000000000..3d1c999995 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/Multimap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85d9a5b5e437e44879e1301cb19aa4f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs new file mode 100644 index 0000000000..4dd770eca9 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs @@ -0,0 +1,29 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + +using Newtonsoft.Json.Converters; + +namespace Org.OpenAPITools.Client +{ + /// + /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + public class OpenAPIDateConverter : IsoDateTimeConverter + { + /// + /// Initializes a new instance of the class. + /// + public OpenAPIDateConverter() + { + // full-date = date-fullyear "-" date-month "-" date-mday + DateTimeFormat = "yyyy-MM-dd"; + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs.meta new file mode 100644 index 0000000000..250cca9041 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/OpenAPIDateConverter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e052385fe3f8d41ca8607fb55af93963 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs new file mode 100644 index 0000000000..4c9efae525 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs @@ -0,0 +1,68 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace Org.OpenAPITools.Client +{ + /// + /// A container for generalized request inputs. This type allows consumers to extend the request functionality + /// by abstracting away from the default (built-in) request framework (e.g. RestSharp). + /// + public class RequestOptions + { + /// + /// Parameters to be bound to path parts of the Request's URL + /// + public Dictionary PathParameters { get; set; } + + /// + /// Query parameters to be applied to the request. + /// Keys may have 1 or more values associated. + /// + public Multimap QueryParameters { get; set; } + + /// + /// Header parameters to be applied to to the request. + /// Keys may have 1 or more values associated. + /// + public Multimap HeaderParameters { get; set; } + + /// + /// Form parameters to be sent along with the request. + /// + public Dictionary FormParameters { get; set; } + + /// + /// Cookies to be sent along with the request. + /// + public List Cookies { get; set; } + + /// + /// Any data associated with a request body. + /// + public Object Data { get; set; } + + /// + /// Constructs a new instance of + /// + public RequestOptions() + { + PathParameters = new Dictionary(); + QueryParameters = new Multimap(); + HeaderParameters = new Multimap(); + FormParameters = new Dictionary(); + Cookies = new List(); + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs.meta new file mode 100644 index 0000000000..1abe89a112 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/RequestOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b386f103d2e34b39b021c9d06864afa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs new file mode 100644 index 0000000000..4d0ed3e922 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs @@ -0,0 +1,34 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using UnityEngine.Networking; + +namespace Org.OpenAPITools.Client +{ + // Thrown when a backend doesn't return an expected response based on the expected type + // of the response data. + public class UnexpectedResponseException : Exception + { + public int ErrorCode { get; private set; } + + // NOTE: Cannot keep reference to the request since it will be disposed. + public UnexpectedResponseException(UnityWebRequest request, System.Type type, string extra = "") + : base(CreateMessage(request, type, extra)) + { + ErrorCode = (int)request.responseCode; + } + + private static string CreateMessage(UnityWebRequest request, System.Type type, string extra) + { + return $"httpcode={request.responseCode}, expected {type.Name} but got data: {extra}"; + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs.meta new file mode 100644 index 0000000000..6c8a537af1 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/UnexpectedResponseException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90a213292d9b341d08513f39e9877e18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs new file mode 100644 index 0000000000..d8958eb5bd --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs @@ -0,0 +1,53 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + +using System; +using System.Collections.Generic; + +namespace Org.OpenAPITools.Client +{ + /// + /// A URI builder + /// + class WebRequestPathBuilder + { + private string _baseUrl; + private string _path; + private string _query = "?"; + public WebRequestPathBuilder(string baseUrl, string path) + { + _baseUrl = baseUrl; + _path = path; + } + + public void AddPathParameters(Dictionary parameters) + { + foreach (var parameter in parameters) + { + _path = _path.Replace("{" + parameter.Key + "}", Uri.EscapeDataString(parameter.Value)); + } + } + + public void AddQueryParameters(Multimap parameters) + { + foreach (var parameter in parameters) + { + foreach (var value in parameter.Value) + { + _query = _query + parameter.Key + "=" + Uri.EscapeDataString(value) + "&"; + } + } + } + + public string GetFullUri() + { + return _baseUrl + _path + _query.Substring(0, _query.Length - 1); + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs.meta new file mode 100644 index 0000000000..af41377b84 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/WebRequestPathBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d783b44327494ed3b72ee0b248206ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model.meta b/Assets/ThirdParty/Org.OpenAPITools/Model.meta new file mode 100644 index 0000000000..5c25fe715d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 882e36e1001f443fb894152eb6d56694 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs new file mode 100644 index 0000000000..299c408b39 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs @@ -0,0 +1,76 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Org.OpenAPITools.Model +{ + /// + /// Abstract base class for oneOf, anyOf schemas in the OpenAPI specification + /// + public abstract partial class AbstractOpenAPISchema + { + /// + /// Custom JSON serializer + /// + static public readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings + { + // OpenAPI generated types generally hide default constructors. + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, + MissingMemberHandling = MissingMemberHandling.Error, + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + OverrideSpecifiedNames = false + } + } + }; + + /// + /// Custom JSON serializer for objects with additional properties + /// + static public readonly JsonSerializerSettings AdditionalPropertiesSerializerSettings = new JsonSerializerSettings + { + // OpenAPI generated types generally hide default constructors. + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, + MissingMemberHandling = MissingMemberHandling.Ignore, + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new CamelCaseNamingStrategy + { + OverrideSpecifiedNames = false + } + } + }; + + /// + /// Gets or Sets the actual instance + /// + public abstract Object ActualInstance { get; set; } + + /// + /// Gets or Sets IsNullable to indicate whether the instance is nullable + /// + public bool IsNullable { get; protected set; } + + /// + /// Gets or Sets the schema type, which can be either `oneOf` or `anyOf` + /// + public string SchemaType { get; protected set; } + + /// + /// Converts the instance into JSON string. + /// + public abstract string ToJson(); + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs.meta new file mode 100644 index 0000000000..28f5ebde3e --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/AbstractOpenAPISchema.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bffa34a5f84c748b1bb4bff1df3a370d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs new file mode 100644 index 0000000000..3b21ac9fc0 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs @@ -0,0 +1,371 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// Asset + /// + [DataContract(Name = "Asset")] + public partial class Asset : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected Asset() { } + /// + /// Initializes a new instance of the class. + /// + /// id (required). + /// url. + /// formats (required). + /// name (required). + /// description. + /// owner (required). + /// visibility (required). + /// curated. + /// polyid. + /// polydata. + /// thumbnail. + /// ownername (required). + /// ownerurl (required). + public Asset(string id = default(string), string url = default(string), List formats = default(List), string name = default(string), string description = default(string), string owner = default(string), string visibility = default(string), bool curated = default(bool), string polyid = default(string), PolyAsset polydata = default(PolyAsset), string thumbnail = default(string), string ownername = default(string), string ownerurl = default(string)) + { + // to ensure "id" is required (not null) + if (id == null) + { + throw new ArgumentNullException("id is a required property for Asset and cannot be null"); + } + this.Id = id; + // to ensure "formats" is required (not null) + if (formats == null) + { + throw new ArgumentNullException("formats is a required property for Asset and cannot be null"); + } + this.Formats = formats; + // to ensure "name" is required (not null) + if (name == null) + { + throw new ArgumentNullException("name is a required property for Asset and cannot be null"); + } + this.Name = name; + // to ensure "owner" is required (not null) + if (owner == null) + { + throw new ArgumentNullException("owner is a required property for Asset and cannot be null"); + } + this.Owner = owner; + // to ensure "visibility" is required (not null) + if (visibility == null) + { + throw new ArgumentNullException("visibility is a required property for Asset and cannot be null"); + } + this.Visibility = visibility; + // to ensure "ownername" is required (not null) + if (ownername == null) + { + throw new ArgumentNullException("ownername is a required property for Asset and cannot be null"); + } + this.Ownername = ownername; + // to ensure "ownerurl" is required (not null) + if (ownerurl == null) + { + throw new ArgumentNullException("ownerurl is a required property for Asset and cannot be null"); + } + this.Ownerurl = ownerurl; + this.Url = url; + this.Description = description; + this.Curated = curated; + this.Polyid = polyid; + this.Polydata = polydata; + this.Thumbnail = thumbnail; + } + + /// + /// Gets or Sets Id + /// + [DataMember(Name = "id", IsRequired = true, EmitDefaultValue = true)] + public string Id { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", EmitDefaultValue = false)] + public string Url { get; set; } + + /// + /// Gets or Sets Formats + /// + [DataMember(Name = "formats", IsRequired = true, EmitDefaultValue = true)] + public List Formats { get; set; } + + /// + /// Gets or Sets Name + /// + [DataMember(Name = "name", IsRequired = true, EmitDefaultValue = true)] + public string Name { get; set; } + + /// + /// Gets or Sets Description + /// + [DataMember(Name = "description", EmitDefaultValue = false)] + public string Description { get; set; } + + /// + /// Gets or Sets Owner + /// + [DataMember(Name = "owner", IsRequired = true, EmitDefaultValue = true)] + public string Owner { get; set; } + + /// + /// Gets or Sets Visibility + /// + [DataMember(Name = "visibility", IsRequired = true, EmitDefaultValue = true)] + public string Visibility { get; set; } + + /// + /// Gets or Sets Curated + /// + [DataMember(Name = "curated", EmitDefaultValue = true)] + public bool Curated { get; set; } + + /// + /// Gets or Sets Polyid + /// + [DataMember(Name = "polyid", EmitDefaultValue = false)] + public string Polyid { get; set; } + + /// + /// Gets or Sets Polydata + /// + [DataMember(Name = "polydata", EmitDefaultValue = false)] + public PolyAsset Polydata { get; set; } + + /// + /// Gets or Sets Thumbnail + /// + [DataMember(Name = "thumbnail", EmitDefaultValue = false)] + public string Thumbnail { get; set; } + + /// + /// Gets or Sets Ownername + /// + [DataMember(Name = "ownername", IsRequired = true, EmitDefaultValue = true)] + public string Ownername { get; set; } + + /// + /// Gets or Sets Ownerurl + /// + [DataMember(Name = "ownerurl", IsRequired = true, EmitDefaultValue = true)] + public string Ownerurl { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class Asset {\n"); + sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Formats: ").Append(Formats).Append("\n"); + sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append(" Owner: ").Append(Owner).Append("\n"); + sb.Append(" Visibility: ").Append(Visibility).Append("\n"); + sb.Append(" Curated: ").Append(Curated).Append("\n"); + sb.Append(" Polyid: ").Append(Polyid).Append("\n"); + sb.Append(" Polydata: ").Append(Polydata).Append("\n"); + sb.Append(" Thumbnail: ").Append(Thumbnail).Append("\n"); + sb.Append(" Ownername: ").Append(Ownername).Append("\n"); + sb.Append(" Ownerurl: ").Append(Ownerurl).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as Asset); + } + + /// + /// Returns true if Asset instances are equal + /// + /// Instance of Asset to be compared + /// Boolean + public bool Equals(Asset input) + { + if (input == null) + { + return false; + } + return + ( + this.Id == input.Id || + (this.Id != null && + this.Id.Equals(input.Id)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Formats == input.Formats || + this.Formats != null && + input.Formats != null && + this.Formats.SequenceEqual(input.Formats) + ) && + ( + this.Name == input.Name || + (this.Name != null && + this.Name.Equals(input.Name)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ) && + ( + this.Owner == input.Owner || + (this.Owner != null && + this.Owner.Equals(input.Owner)) + ) && + ( + this.Visibility == input.Visibility || + (this.Visibility != null && + this.Visibility.Equals(input.Visibility)) + ) && + ( + this.Curated == input.Curated || + this.Curated.Equals(input.Curated) + ) && + ( + this.Polyid == input.Polyid || + (this.Polyid != null && + this.Polyid.Equals(input.Polyid)) + ) && + ( + this.Polydata == input.Polydata || + (this.Polydata != null && + this.Polydata.Equals(input.Polydata)) + ) && + ( + this.Thumbnail == input.Thumbnail || + (this.Thumbnail != null && + this.Thumbnail.Equals(input.Thumbnail)) + ) && + ( + this.Ownername == input.Ownername || + (this.Ownername != null && + this.Ownername.Equals(input.Ownername)) + ) && + ( + this.Ownerurl == input.Ownerurl || + (this.Ownerurl != null && + this.Ownerurl.Equals(input.Ownerurl)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Id != null) + { + hashCode = (hashCode * 59) + this.Id.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Formats != null) + { + hashCode = (hashCode * 59) + this.Formats.GetHashCode(); + } + if (this.Name != null) + { + hashCode = (hashCode * 59) + this.Name.GetHashCode(); + } + if (this.Description != null) + { + hashCode = (hashCode * 59) + this.Description.GetHashCode(); + } + if (this.Owner != null) + { + hashCode = (hashCode * 59) + this.Owner.GetHashCode(); + } + if (this.Visibility != null) + { + hashCode = (hashCode * 59) + this.Visibility.GetHashCode(); + } + hashCode = (hashCode * 59) + this.Curated.GetHashCode(); + if (this.Polyid != null) + { + hashCode = (hashCode * 59) + this.Polyid.GetHashCode(); + } + if (this.Polydata != null) + { + hashCode = (hashCode * 59) + this.Polydata.GetHashCode(); + } + if (this.Thumbnail != null) + { + hashCode = (hashCode * 59) + this.Thumbnail.GetHashCode(); + } + if (this.Ownername != null) + { + hashCode = (hashCode * 59) + this.Ownername.GetHashCode(); + } + if (this.Ownerurl != null) + { + hashCode = (hashCode * 59) + this.Ownerurl.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs.meta new file mode 100644 index 0000000000..c36f6c6485 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/Asset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 13903908a3a4649ac8c5cb47853e1115 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs new file mode 100644 index 0000000000..9edef41c58 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs @@ -0,0 +1,193 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// AssetFormat + /// + [DataContract(Name = "AssetFormat")] + public partial class AssetFormat : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected AssetFormat() { } + /// + /// Initializes a new instance of the class. + /// + /// id (required). + /// url (required). + /// format (required). + /// subfiles. + public AssetFormat(string id = default(string), string url = default(string), string format = default(string), List subfiles = default(List)) + { + // to ensure "id" is required (not null) + if (id == null) + { + throw new ArgumentNullException("id is a required property for AssetFormat and cannot be null"); + } + this.Id = id; + // to ensure "url" is required (not null) + if (url == null) + { + throw new ArgumentNullException("url is a required property for AssetFormat and cannot be null"); + } + this.Url = url; + // to ensure "format" is required (not null) + if (format == null) + { + throw new ArgumentNullException("format is a required property for AssetFormat and cannot be null"); + } + this.Format = format; + this.Subfiles = subfiles; + } + + /// + /// Gets or Sets Id + /// + [DataMember(Name = "id", IsRequired = true, EmitDefaultValue = true)] + public string Id { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", IsRequired = true, EmitDefaultValue = true)] + public string Url { get; set; } + + /// + /// Gets or Sets Format + /// + [DataMember(Name = "format", IsRequired = true, EmitDefaultValue = true)] + public string Format { get; set; } + + /// + /// Gets or Sets Subfiles + /// + [DataMember(Name = "subfiles", EmitDefaultValue = false)] + public List Subfiles { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class AssetFormat {\n"); + sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Format: ").Append(Format).Append("\n"); + sb.Append(" Subfiles: ").Append(Subfiles).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as AssetFormat); + } + + /// + /// Returns true if AssetFormat instances are equal + /// + /// Instance of AssetFormat to be compared + /// Boolean + public bool Equals(AssetFormat input) + { + if (input == null) + { + return false; + } + return + ( + this.Id == input.Id || + (this.Id != null && + this.Id.Equals(input.Id)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Format == input.Format || + (this.Format != null && + this.Format.Equals(input.Format)) + ) && + ( + this.Subfiles == input.Subfiles || + this.Subfiles != null && + input.Subfiles != null && + this.Subfiles.SequenceEqual(input.Subfiles) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Id != null) + { + hashCode = (hashCode * 59) + this.Id.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Format != null) + { + hashCode = (hashCode * 59) + this.Format.GetHashCode(); + } + if (this.Subfiles != null) + { + hashCode = (hashCode * 59) + this.Subfiles.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs.meta new file mode 100644 index 0000000000..a4adb2a52f --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8754ff7004224003813d212e74a8510 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs new file mode 100644 index 0000000000..6e9e23e76b --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs @@ -0,0 +1,172 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// AssetPatchData + /// + [DataContract(Name = "AssetPatchData")] + public partial class AssetPatchData : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// name. + /// url. + /// description. + /// visibility. + public AssetPatchData(string name = default(string), string url = default(string), string description = default(string), string visibility = default(string)) + { + this.Name = name; + this.Url = url; + this.Description = description; + this.Visibility = visibility; + } + + /// + /// Gets or Sets Name + /// + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", EmitDefaultValue = false)] + public string Url { get; set; } + + /// + /// Gets or Sets Description + /// + [DataMember(Name = "description", EmitDefaultValue = false)] + public string Description { get; set; } + + /// + /// Gets or Sets Visibility + /// + [DataMember(Name = "visibility", EmitDefaultValue = false)] + public string Visibility { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class AssetPatchData {\n"); + sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append(" Visibility: ").Append(Visibility).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as AssetPatchData); + } + + /// + /// Returns true if AssetPatchData instances are equal + /// + /// Instance of AssetPatchData to be compared + /// Boolean + public bool Equals(AssetPatchData input) + { + if (input == null) + { + return false; + } + return + ( + this.Name == input.Name || + (this.Name != null && + this.Name.Equals(input.Name)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ) && + ( + this.Visibility == input.Visibility || + (this.Visibility != null && + this.Visibility.Equals(input.Visibility)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Name != null) + { + hashCode = (hashCode * 59) + this.Name.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Description != null) + { + hashCode = (hashCode * 59) + this.Description.GetHashCode(); + } + if (this.Visibility != null) + { + hashCode = (hashCode * 59) + this.Visibility.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs.meta new file mode 100644 index 0000000000..837ee104f6 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/AssetPatchData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e55da885c6d2b49549afd61e0670026f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs new file mode 100644 index 0000000000..0ebb01113b --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs @@ -0,0 +1,151 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// EmailChangeAuthenticated + /// + [DataContract(Name = "EmailChangeAuthenticated")] + public partial class EmailChangeAuthenticated : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected EmailChangeAuthenticated() { } + /// + /// Initializes a new instance of the class. + /// + /// newEmail (required). + /// currentPassword (required). + public EmailChangeAuthenticated(string newEmail = default(string), string currentPassword = default(string)) + { + // to ensure "newEmail" is required (not null) + if (newEmail == null) + { + throw new ArgumentNullException("newEmail is a required property for EmailChangeAuthenticated and cannot be null"); + } + this.NewEmail = newEmail; + // to ensure "currentPassword" is required (not null) + if (currentPassword == null) + { + throw new ArgumentNullException("currentPassword is a required property for EmailChangeAuthenticated and cannot be null"); + } + this.CurrentPassword = currentPassword; + } + + /// + /// Gets or Sets NewEmail + /// + [DataMember(Name = "newEmail", IsRequired = true, EmitDefaultValue = true)] + public string NewEmail { get; set; } + + /// + /// Gets or Sets CurrentPassword + /// + [DataMember(Name = "currentPassword", IsRequired = true, EmitDefaultValue = true)] + public string CurrentPassword { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class EmailChangeAuthenticated {\n"); + sb.Append(" NewEmail: ").Append(NewEmail).Append("\n"); + sb.Append(" CurrentPassword: ").Append(CurrentPassword).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as EmailChangeAuthenticated); + } + + /// + /// Returns true if EmailChangeAuthenticated instances are equal + /// + /// Instance of EmailChangeAuthenticated to be compared + /// Boolean + public bool Equals(EmailChangeAuthenticated input) + { + if (input == null) + { + return false; + } + return + ( + this.NewEmail == input.NewEmail || + (this.NewEmail != null && + this.NewEmail.Equals(input.NewEmail)) + ) && + ( + this.CurrentPassword == input.CurrentPassword || + (this.CurrentPassword != null && + this.CurrentPassword.Equals(input.CurrentPassword)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.NewEmail != null) + { + hashCode = (hashCode * 59) + this.NewEmail.GetHashCode(); + } + if (this.CurrentPassword != null) + { + hashCode = (hashCode * 59) + this.CurrentPassword.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs.meta new file mode 100644 index 0000000000..fd20d7531d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/EmailChangeAuthenticated.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6cbeb403da2584de4a1971e3daf03016 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs new file mode 100644 index 0000000000..b71a094ba0 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs @@ -0,0 +1,215 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// FullUser + /// + [DataContract(Name = "FullUser")] + public partial class FullUser : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected FullUser() { } + /// + /// Initializes a new instance of the class. + /// + /// id (required). + /// url (required). + /// email (required). + /// displayname (required). + /// description. + public FullUser(string id = default(string), string url = default(string), string email = default(string), string displayname = default(string), string description = default(string)) + { + // to ensure "id" is required (not null) + if (id == null) + { + throw new ArgumentNullException("id is a required property for FullUser and cannot be null"); + } + this.Id = id; + // to ensure "url" is required (not null) + if (url == null) + { + throw new ArgumentNullException("url is a required property for FullUser and cannot be null"); + } + this.Url = url; + // to ensure "email" is required (not null) + if (email == null) + { + throw new ArgumentNullException("email is a required property for FullUser and cannot be null"); + } + this.Email = email; + // to ensure "displayname" is required (not null) + if (displayname == null) + { + throw new ArgumentNullException("displayname is a required property for FullUser and cannot be null"); + } + this.Displayname = displayname; + this.Description = description; + } + + /// + /// Gets or Sets Id + /// + [DataMember(Name = "id", IsRequired = true, EmitDefaultValue = true)] + public string Id { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", IsRequired = true, EmitDefaultValue = true)] + public string Url { get; set; } + + /// + /// Gets or Sets Email + /// + [DataMember(Name = "email", IsRequired = true, EmitDefaultValue = true)] + public string Email { get; set; } + + /// + /// Gets or Sets Displayname + /// + [DataMember(Name = "displayname", IsRequired = true, EmitDefaultValue = true)] + public string Displayname { get; set; } + + /// + /// Gets or Sets Description + /// + [DataMember(Name = "description", EmitDefaultValue = false)] + public string Description { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class FullUser {\n"); + sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Email: ").Append(Email).Append("\n"); + sb.Append(" Displayname: ").Append(Displayname).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as FullUser); + } + + /// + /// Returns true if FullUser instances are equal + /// + /// Instance of FullUser to be compared + /// Boolean + public bool Equals(FullUser input) + { + if (input == null) + { + return false; + } + return + ( + this.Id == input.Id || + (this.Id != null && + this.Id.Equals(input.Id)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Email == input.Email || + (this.Email != null && + this.Email.Equals(input.Email)) + ) && + ( + this.Displayname == input.Displayname || + (this.Displayname != null && + this.Displayname.Equals(input.Displayname)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Id != null) + { + hashCode = (hashCode * 59) + this.Id.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Email != null) + { + hashCode = (hashCode * 59) + this.Email.GetHashCode(); + } + if (this.Displayname != null) + { + hashCode = (hashCode * 59) + this.Displayname.GetHashCode(); + } + if (this.Description != null) + { + hashCode = (hashCode * 59) + this.Description.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs.meta new file mode 100644 index 0000000000..6f2c889e6d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/FullUser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1d4b242588d04df5bf461d74f47d8c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs new file mode 100644 index 0000000000..5648a358d7 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs @@ -0,0 +1,119 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// HTTPValidationError + /// + [DataContract(Name = "HTTPValidationError")] + public partial class HTTPValidationError : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// detail. + public HTTPValidationError(List detail = default(List)) + { + this.Detail = detail; + } + + /// + /// Gets or Sets Detail + /// + [DataMember(Name = "detail", EmitDefaultValue = false)] + public List Detail { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class HTTPValidationError {\n"); + sb.Append(" Detail: ").Append(Detail).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as HTTPValidationError); + } + + /// + /// Returns true if HTTPValidationError instances are equal + /// + /// Instance of HTTPValidationError to be compared + /// Boolean + public bool Equals(HTTPValidationError input) + { + if (input == null) + { + return false; + } + return + ( + this.Detail == input.Detail || + this.Detail != null && + input.Detail != null && + this.Detail.SequenceEqual(input.Detail) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Detail != null) + { + hashCode = (hashCode * 59) + this.Detail.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs.meta new file mode 100644 index 0000000000..528ca2f0a5 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/HTTPValidationError.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c84cf1dcd613a479ab8ad6f98d82c55e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs new file mode 100644 index 0000000000..24f20f75bf --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs @@ -0,0 +1,151 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// LoginToken + /// + [DataContract(Name = "LoginToken")] + public partial class LoginToken : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected LoginToken() { } + /// + /// Initializes a new instance of the class. + /// + /// accessToken (required). + /// tokenType (required). + public LoginToken(string accessToken = default(string), string tokenType = default(string)) + { + // to ensure "accessToken" is required (not null) + if (accessToken == null) + { + throw new ArgumentNullException("accessToken is a required property for LoginToken and cannot be null"); + } + this.AccessToken = accessToken; + // to ensure "tokenType" is required (not null) + if (tokenType == null) + { + throw new ArgumentNullException("tokenType is a required property for LoginToken and cannot be null"); + } + this.TokenType = tokenType; + } + + /// + /// Gets or Sets AccessToken + /// + [DataMember(Name = "access_token", IsRequired = true, EmitDefaultValue = true)] + public string AccessToken { get; set; } + + /// + /// Gets or Sets TokenType + /// + [DataMember(Name = "token_type", IsRequired = true, EmitDefaultValue = true)] + public string TokenType { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class LoginToken {\n"); + sb.Append(" AccessToken: ").Append(AccessToken).Append("\n"); + sb.Append(" TokenType: ").Append(TokenType).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as LoginToken); + } + + /// + /// Returns true if LoginToken instances are equal + /// + /// Instance of LoginToken to be compared + /// Boolean + public bool Equals(LoginToken input) + { + if (input == null) + { + return false; + } + return + ( + this.AccessToken == input.AccessToken || + (this.AccessToken != null && + this.AccessToken.Equals(input.AccessToken)) + ) && + ( + this.TokenType == input.TokenType || + (this.TokenType != null && + this.TokenType.Equals(input.TokenType)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.AccessToken != null) + { + hashCode = (hashCode * 59) + this.AccessToken.GetHashCode(); + } + if (this.TokenType != null) + { + hashCode = (hashCode * 59) + this.TokenType.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs.meta new file mode 100644 index 0000000000..e5779e1e6d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/LoginToken.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7270badae5584c779dc43a1277073d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs new file mode 100644 index 0000000000..75b56abe8f --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs @@ -0,0 +1,192 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// NewUser + /// + [DataContract(Name = "NewUser")] + public partial class NewUser : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected NewUser() { } + /// + /// Initializes a new instance of the class. + /// + /// email (required). + /// url. + /// password (required). + /// displayName (required). + public NewUser(string email = default(string), string url = default(string), string password = default(string), string displayName = default(string)) + { + // to ensure "email" is required (not null) + if (email == null) + { + throw new ArgumentNullException("email is a required property for NewUser and cannot be null"); + } + this.Email = email; + // to ensure "password" is required (not null) + if (password == null) + { + throw new ArgumentNullException("password is a required property for NewUser and cannot be null"); + } + this.Password = password; + // to ensure "displayName" is required (not null) + if (displayName == null) + { + throw new ArgumentNullException("displayName is a required property for NewUser and cannot be null"); + } + this.DisplayName = displayName; + this.Url = url; + } + + /// + /// Gets or Sets Email + /// + [DataMember(Name = "email", IsRequired = true, EmitDefaultValue = true)] + public string Email { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", EmitDefaultValue = false)] + public string Url { get; set; } + + /// + /// Gets or Sets Password + /// + [DataMember(Name = "password", IsRequired = true, EmitDefaultValue = true)] + public string Password { get; set; } + + /// + /// Gets or Sets DisplayName + /// + [DataMember(Name = "displayName", IsRequired = true, EmitDefaultValue = true)] + public string DisplayName { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class NewUser {\n"); + sb.Append(" Email: ").Append(Email).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Password: ").Append(Password).Append("\n"); + sb.Append(" DisplayName: ").Append(DisplayName).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as NewUser); + } + + /// + /// Returns true if NewUser instances are equal + /// + /// Instance of NewUser to be compared + /// Boolean + public bool Equals(NewUser input) + { + if (input == null) + { + return false; + } + return + ( + this.Email == input.Email || + (this.Email != null && + this.Email.Equals(input.Email)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Password == input.Password || + (this.Password != null && + this.Password.Equals(input.Password)) + ) && + ( + this.DisplayName == input.DisplayName || + (this.DisplayName != null && + this.DisplayName.Equals(input.DisplayName)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Email != null) + { + hashCode = (hashCode * 59) + this.Email.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Password != null) + { + hashCode = (hashCode * 59) + this.Password.GetHashCode(); + } + if (this.DisplayName != null) + { + hashCode = (hashCode * 59) + this.DisplayName.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs.meta new file mode 100644 index 0000000000..22eaf463a0 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/NewUser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 75d47b85fa7d74e7abcbb9c0329757c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs new file mode 100644 index 0000000000..1eb4bc0c97 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs @@ -0,0 +1,151 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PasswordChangeAuthenticated + /// + [DataContract(Name = "PasswordChangeAuthenticated")] + public partial class PasswordChangeAuthenticated : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PasswordChangeAuthenticated() { } + /// + /// Initializes a new instance of the class. + /// + /// oldPassword (required). + /// newPassword (required). + public PasswordChangeAuthenticated(string oldPassword = default(string), string newPassword = default(string)) + { + // to ensure "oldPassword" is required (not null) + if (oldPassword == null) + { + throw new ArgumentNullException("oldPassword is a required property for PasswordChangeAuthenticated and cannot be null"); + } + this.OldPassword = oldPassword; + // to ensure "newPassword" is required (not null) + if (newPassword == null) + { + throw new ArgumentNullException("newPassword is a required property for PasswordChangeAuthenticated and cannot be null"); + } + this.NewPassword = newPassword; + } + + /// + /// Gets or Sets OldPassword + /// + [DataMember(Name = "oldPassword", IsRequired = true, EmitDefaultValue = true)] + public string OldPassword { get; set; } + + /// + /// Gets or Sets NewPassword + /// + [DataMember(Name = "newPassword", IsRequired = true, EmitDefaultValue = true)] + public string NewPassword { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PasswordChangeAuthenticated {\n"); + sb.Append(" OldPassword: ").Append(OldPassword).Append("\n"); + sb.Append(" NewPassword: ").Append(NewPassword).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PasswordChangeAuthenticated); + } + + /// + /// Returns true if PasswordChangeAuthenticated instances are equal + /// + /// Instance of PasswordChangeAuthenticated to be compared + /// Boolean + public bool Equals(PasswordChangeAuthenticated input) + { + if (input == null) + { + return false; + } + return + ( + this.OldPassword == input.OldPassword || + (this.OldPassword != null && + this.OldPassword.Equals(input.OldPassword)) + ) && + ( + this.NewPassword == input.NewPassword || + (this.NewPassword != null && + this.NewPassword.Equals(input.NewPassword)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.OldPassword != null) + { + hashCode = (hashCode * 59) + this.OldPassword.GetHashCode(); + } + if (this.NewPassword != null) + { + hashCode = (hashCode * 59) + this.NewPassword.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs.meta new file mode 100644 index 0000000000..e52415b387 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeAuthenticated.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 29519200cd400434b929afcc7e0b9ffb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs new file mode 100644 index 0000000000..1f85af4eb2 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs @@ -0,0 +1,151 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PasswordChangeToken + /// + [DataContract(Name = "PasswordChangeToken")] + public partial class PasswordChangeToken : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PasswordChangeToken() { } + /// + /// Initializes a new instance of the class. + /// + /// token (required). + /// newPassword (required). + public PasswordChangeToken(string token = default(string), string newPassword = default(string)) + { + // to ensure "token" is required (not null) + if (token == null) + { + throw new ArgumentNullException("token is a required property for PasswordChangeToken and cannot be null"); + } + this.Token = token; + // to ensure "newPassword" is required (not null) + if (newPassword == null) + { + throw new ArgumentNullException("newPassword is a required property for PasswordChangeToken and cannot be null"); + } + this.NewPassword = newPassword; + } + + /// + /// Gets or Sets Token + /// + [DataMember(Name = "token", IsRequired = true, EmitDefaultValue = true)] + public string Token { get; set; } + + /// + /// Gets or Sets NewPassword + /// + [DataMember(Name = "newPassword", IsRequired = true, EmitDefaultValue = true)] + public string NewPassword { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PasswordChangeToken {\n"); + sb.Append(" Token: ").Append(Token).Append("\n"); + sb.Append(" NewPassword: ").Append(NewPassword).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PasswordChangeToken); + } + + /// + /// Returns true if PasswordChangeToken instances are equal + /// + /// Instance of PasswordChangeToken to be compared + /// Boolean + public bool Equals(PasswordChangeToken input) + { + if (input == null) + { + return false; + } + return + ( + this.Token == input.Token || + (this.Token != null && + this.Token.Equals(input.Token)) + ) && + ( + this.NewPassword == input.NewPassword || + (this.NewPassword != null && + this.NewPassword.Equals(input.NewPassword)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Token != null) + { + hashCode = (hashCode * 59) + this.Token.GetHashCode(); + } + if (this.NewPassword != null) + { + hashCode = (hashCode * 59) + this.NewPassword.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs.meta new file mode 100644 index 0000000000..2682fa9efc --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordChangeToken.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c0694c4508a3401aa835b6a88a8a150 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs new file mode 100644 index 0000000000..0f1a8f7718 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs @@ -0,0 +1,128 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PasswordReset + /// + [DataContract(Name = "PasswordReset")] + public partial class PasswordReset : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PasswordReset() { } + /// + /// Initializes a new instance of the class. + /// + /// email (required). + public PasswordReset(string email = default(string)) + { + // to ensure "email" is required (not null) + if (email == null) + { + throw new ArgumentNullException("email is a required property for PasswordReset and cannot be null"); + } + this.Email = email; + } + + /// + /// Gets or Sets Email + /// + [DataMember(Name = "email", IsRequired = true, EmitDefaultValue = true)] + public string Email { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PasswordReset {\n"); + sb.Append(" Email: ").Append(Email).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PasswordReset); + } + + /// + /// Returns true if PasswordReset instances are equal + /// + /// Instance of PasswordReset to be compared + /// Boolean + public bool Equals(PasswordReset input) + { + if (input == null) + { + return false; + } + return + ( + this.Email == input.Email || + (this.Email != null && + this.Email.Equals(input.Email)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Email != null) + { + hashCode = (hashCode * 59) + this.Email.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs.meta new file mode 100644 index 0000000000..38715a23e0 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PasswordReset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5136e21ab95742d2b27b64ce2fa380a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs new file mode 100644 index 0000000000..9ceec3fc68 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs @@ -0,0 +1,154 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PatchUser + /// + [DataContract(Name = "PatchUser")] + public partial class PatchUser : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// url. + /// displayname. + /// description. + public PatchUser(string url = default(string), string displayname = default(string), string description = default(string)) + { + this.Url = url; + this.Displayname = displayname; + this.Description = description; + } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", EmitDefaultValue = false)] + public string Url { get; set; } + + /// + /// Gets or Sets Displayname + /// + [DataMember(Name = "displayname", EmitDefaultValue = false)] + public string Displayname { get; set; } + + /// + /// Gets or Sets Description + /// + [DataMember(Name = "description", EmitDefaultValue = false)] + public string Description { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PatchUser {\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Displayname: ").Append(Displayname).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PatchUser); + } + + /// + /// Returns true if PatchUser instances are equal + /// + /// Instance of PatchUser to be compared + /// Boolean + public bool Equals(PatchUser input) + { + if (input == null) + { + return false; + } + return + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Displayname == input.Displayname || + (this.Displayname != null && + this.Displayname.Equals(input.Displayname)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Displayname != null) + { + hashCode = (hashCode * 59) + this.Displayname.GetHashCode(); + } + if (this.Description != null) + { + hashCode = (hashCode * 59) + this.Description.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs.meta new file mode 100644 index 0000000000..6bdab916fc --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PatchUser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb12ca9466d7b40839243f76e5c9d75a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs new file mode 100644 index 0000000000..9c46ad21c2 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs @@ -0,0 +1,389 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyAsset + /// + [DataContract(Name = "PolyAsset")] + public partial class PolyAsset : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PolyAsset() { } + /// + /// Initializes a new instance of the class. + /// + /// name (required). + /// displayName (required). + /// authorName (required). + /// description. + /// createTime (required). + /// updateTime (required). + /// formats (required). + /// thumbnail. + /// licence. + /// visibility (required). + /// isCurated. + /// presentationParams. + /// metadata. + /// remixInfo. + public PolyAsset(string name = default(string), string displayName = default(string), string authorName = default(string), string description = default(string), string createTime = default(string), string updateTime = default(string), List formats = default(List), PolyResource thumbnail = default(PolyResource), string licence = default(string), string visibility = default(string), bool isCurated = default(bool), PolyPresentationParams presentationParams = default(PolyPresentationParams), string metadata = default(string), PolyRemixInfo remixInfo = default(PolyRemixInfo)) + { + // to ensure "name" is required (not null) + if (name == null) + { + throw new ArgumentNullException("name is a required property for PolyAsset and cannot be null"); + } + this.Name = name; + // to ensure "displayName" is required (not null) + if (displayName == null) + { + throw new ArgumentNullException("displayName is a required property for PolyAsset and cannot be null"); + } + this.DisplayName = displayName; + // to ensure "authorName" is required (not null) + if (authorName == null) + { + throw new ArgumentNullException("authorName is a required property for PolyAsset and cannot be null"); + } + this.AuthorName = authorName; + // to ensure "createTime" is required (not null) + if (createTime == null) + { + throw new ArgumentNullException("createTime is a required property for PolyAsset and cannot be null"); + } + this.CreateTime = createTime; + // to ensure "updateTime" is required (not null) + if (updateTime == null) + { + throw new ArgumentNullException("updateTime is a required property for PolyAsset and cannot be null"); + } + this.UpdateTime = updateTime; + // to ensure "formats" is required (not null) + if (formats == null) + { + throw new ArgumentNullException("formats is a required property for PolyAsset and cannot be null"); + } + this.Formats = formats; + // to ensure "visibility" is required (not null) + if (visibility == null) + { + throw new ArgumentNullException("visibility is a required property for PolyAsset and cannot be null"); + } + this.Visibility = visibility; + this.Description = description; + this.Thumbnail = thumbnail; + this.Licence = licence; + this.IsCurated = isCurated; + this.PresentationParams = presentationParams; + this.Metadata = metadata; + this.RemixInfo = remixInfo; + } + + /// + /// Gets or Sets Name + /// + [DataMember(Name = "name", IsRequired = true, EmitDefaultValue = true)] + public string Name { get; set; } + + /// + /// Gets or Sets DisplayName + /// + [DataMember(Name = "displayName", IsRequired = true, EmitDefaultValue = true)] + public string DisplayName { get; set; } + + /// + /// Gets or Sets AuthorName + /// + [DataMember(Name = "authorName", IsRequired = true, EmitDefaultValue = true)] + public string AuthorName { get; set; } + + /// + /// Gets or Sets Description + /// + [DataMember(Name = "description", EmitDefaultValue = false)] + public string Description { get; set; } + + /// + /// Gets or Sets CreateTime + /// + [DataMember(Name = "createTime", IsRequired = true, EmitDefaultValue = true)] + public string CreateTime { get; set; } + + /// + /// Gets or Sets UpdateTime + /// + [DataMember(Name = "updateTime", IsRequired = true, EmitDefaultValue = true)] + public string UpdateTime { get; set; } + + /// + /// Gets or Sets Formats + /// + [DataMember(Name = "formats", IsRequired = true, EmitDefaultValue = true)] + public List Formats { get; set; } + + /// + /// Gets or Sets Thumbnail + /// + [DataMember(Name = "thumbnail", EmitDefaultValue = false)] + public PolyResource Thumbnail { get; set; } + + /// + /// Gets or Sets Licence + /// + [DataMember(Name = "licence", EmitDefaultValue = false)] + public string Licence { get; set; } + + /// + /// Gets or Sets Visibility + /// + [DataMember(Name = "visibility", IsRequired = true, EmitDefaultValue = true)] + public string Visibility { get; set; } + + /// + /// Gets or Sets IsCurated + /// + [DataMember(Name = "isCurated", EmitDefaultValue = true)] + public bool IsCurated { get; set; } + + /// + /// Gets or Sets PresentationParams + /// + [DataMember(Name = "presentationParams", EmitDefaultValue = false)] + public PolyPresentationParams PresentationParams { get; set; } + + /// + /// Gets or Sets Metadata + /// + [DataMember(Name = "metadata", EmitDefaultValue = false)] + public string Metadata { get; set; } + + /// + /// Gets or Sets RemixInfo + /// + [DataMember(Name = "remixInfo", EmitDefaultValue = false)] + public PolyRemixInfo RemixInfo { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyAsset {\n"); + sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append(" DisplayName: ").Append(DisplayName).Append("\n"); + sb.Append(" AuthorName: ").Append(AuthorName).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append(" CreateTime: ").Append(CreateTime).Append("\n"); + sb.Append(" UpdateTime: ").Append(UpdateTime).Append("\n"); + sb.Append(" Formats: ").Append(Formats).Append("\n"); + sb.Append(" Thumbnail: ").Append(Thumbnail).Append("\n"); + sb.Append(" Licence: ").Append(Licence).Append("\n"); + sb.Append(" Visibility: ").Append(Visibility).Append("\n"); + sb.Append(" IsCurated: ").Append(IsCurated).Append("\n"); + sb.Append(" PresentationParams: ").Append(PresentationParams).Append("\n"); + sb.Append(" Metadata: ").Append(Metadata).Append("\n"); + sb.Append(" RemixInfo: ").Append(RemixInfo).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyAsset); + } + + /// + /// Returns true if PolyAsset instances are equal + /// + /// Instance of PolyAsset to be compared + /// Boolean + public bool Equals(PolyAsset input) + { + if (input == null) + { + return false; + } + return + ( + this.Name == input.Name || + (this.Name != null && + this.Name.Equals(input.Name)) + ) && + ( + this.DisplayName == input.DisplayName || + (this.DisplayName != null && + this.DisplayName.Equals(input.DisplayName)) + ) && + ( + this.AuthorName == input.AuthorName || + (this.AuthorName != null && + this.AuthorName.Equals(input.AuthorName)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ) && + ( + this.CreateTime == input.CreateTime || + (this.CreateTime != null && + this.CreateTime.Equals(input.CreateTime)) + ) && + ( + this.UpdateTime == input.UpdateTime || + (this.UpdateTime != null && + this.UpdateTime.Equals(input.UpdateTime)) + ) && + ( + this.Formats == input.Formats || + this.Formats != null && + input.Formats != null && + this.Formats.SequenceEqual(input.Formats) + ) && + ( + this.Thumbnail == input.Thumbnail || + (this.Thumbnail != null && + this.Thumbnail.Equals(input.Thumbnail)) + ) && + ( + this.Licence == input.Licence || + (this.Licence != null && + this.Licence.Equals(input.Licence)) + ) && + ( + this.Visibility == input.Visibility || + (this.Visibility != null && + this.Visibility.Equals(input.Visibility)) + ) && + ( + this.IsCurated == input.IsCurated || + this.IsCurated.Equals(input.IsCurated) + ) && + ( + this.PresentationParams == input.PresentationParams || + (this.PresentationParams != null && + this.PresentationParams.Equals(input.PresentationParams)) + ) && + ( + this.Metadata == input.Metadata || + (this.Metadata != null && + this.Metadata.Equals(input.Metadata)) + ) && + ( + this.RemixInfo == input.RemixInfo || + (this.RemixInfo != null && + this.RemixInfo.Equals(input.RemixInfo)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Name != null) + { + hashCode = (hashCode * 59) + this.Name.GetHashCode(); + } + if (this.DisplayName != null) + { + hashCode = (hashCode * 59) + this.DisplayName.GetHashCode(); + } + if (this.AuthorName != null) + { + hashCode = (hashCode * 59) + this.AuthorName.GetHashCode(); + } + if (this.Description != null) + { + hashCode = (hashCode * 59) + this.Description.GetHashCode(); + } + if (this.CreateTime != null) + { + hashCode = (hashCode * 59) + this.CreateTime.GetHashCode(); + } + if (this.UpdateTime != null) + { + hashCode = (hashCode * 59) + this.UpdateTime.GetHashCode(); + } + if (this.Formats != null) + { + hashCode = (hashCode * 59) + this.Formats.GetHashCode(); + } + if (this.Thumbnail != null) + { + hashCode = (hashCode * 59) + this.Thumbnail.GetHashCode(); + } + if (this.Licence != null) + { + hashCode = (hashCode * 59) + this.Licence.GetHashCode(); + } + if (this.Visibility != null) + { + hashCode = (hashCode * 59) + this.Visibility.GetHashCode(); + } + hashCode = (hashCode * 59) + this.IsCurated.GetHashCode(); + if (this.PresentationParams != null) + { + hashCode = (hashCode * 59) + this.PresentationParams.GetHashCode(); + } + if (this.Metadata != null) + { + hashCode = (hashCode * 59) + this.Metadata.GetHashCode(); + } + if (this.RemixInfo != null) + { + hashCode = (hashCode * 59) + this.RemixInfo.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs.meta new file mode 100644 index 0000000000..8cdb879940 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cba052ff7b6d24b26b230f54695bc70b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs new file mode 100644 index 0000000000..aac6154011 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs @@ -0,0 +1,193 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyFormat + /// + [DataContract(Name = "PolyFormat")] + public partial class PolyFormat : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PolyFormat() { } + /// + /// Initializes a new instance of the class. + /// + /// root (required). + /// resources. + /// formatComplexity (required). + /// formatType (required). + public PolyFormat(PolyResource root = default(PolyResource), List resources = default(List), PolyFormatComplexity formatComplexity = default(PolyFormatComplexity), string formatType = default(string)) + { + // to ensure "root" is required (not null) + if (root == null) + { + throw new ArgumentNullException("root is a required property for PolyFormat and cannot be null"); + } + this.Root = root; + // to ensure "formatComplexity" is required (not null) + if (formatComplexity == null) + { + throw new ArgumentNullException("formatComplexity is a required property for PolyFormat and cannot be null"); + } + this.FormatComplexity = formatComplexity; + // to ensure "formatType" is required (not null) + if (formatType == null) + { + throw new ArgumentNullException("formatType is a required property for PolyFormat and cannot be null"); + } + this.FormatType = formatType; + this.Resources = resources; + } + + /// + /// Gets or Sets Root + /// + [DataMember(Name = "root", IsRequired = true, EmitDefaultValue = true)] + public PolyResource Root { get; set; } + + /// + /// Gets or Sets Resources + /// + [DataMember(Name = "resources", EmitDefaultValue = false)] + public List Resources { get; set; } + + /// + /// Gets or Sets FormatComplexity + /// + [DataMember(Name = "formatComplexity", IsRequired = true, EmitDefaultValue = true)] + public PolyFormatComplexity FormatComplexity { get; set; } + + /// + /// Gets or Sets FormatType + /// + [DataMember(Name = "formatType", IsRequired = true, EmitDefaultValue = true)] + public string FormatType { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyFormat {\n"); + sb.Append(" Root: ").Append(Root).Append("\n"); + sb.Append(" Resources: ").Append(Resources).Append("\n"); + sb.Append(" FormatComplexity: ").Append(FormatComplexity).Append("\n"); + sb.Append(" FormatType: ").Append(FormatType).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyFormat); + } + + /// + /// Returns true if PolyFormat instances are equal + /// + /// Instance of PolyFormat to be compared + /// Boolean + public bool Equals(PolyFormat input) + { + if (input == null) + { + return false; + } + return + ( + this.Root == input.Root || + (this.Root != null && + this.Root.Equals(input.Root)) + ) && + ( + this.Resources == input.Resources || + this.Resources != null && + input.Resources != null && + this.Resources.SequenceEqual(input.Resources) + ) && + ( + this.FormatComplexity == input.FormatComplexity || + (this.FormatComplexity != null && + this.FormatComplexity.Equals(input.FormatComplexity)) + ) && + ( + this.FormatType == input.FormatType || + (this.FormatType != null && + this.FormatType.Equals(input.FormatType)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Root != null) + { + hashCode = (hashCode * 59) + this.Root.GetHashCode(); + } + if (this.Resources != null) + { + hashCode = (hashCode * 59) + this.Resources.GetHashCode(); + } + if (this.FormatComplexity != null) + { + hashCode = (hashCode * 59) + this.FormatComplexity.GetHashCode(); + } + if (this.FormatType != null) + { + hashCode = (hashCode * 59) + this.FormatType.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs.meta new file mode 100644 index 0000000000..1cfd4cf989 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d331fb7a3d374da9a07b7582b759b28 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs new file mode 100644 index 0000000000..9e193ee06f --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs @@ -0,0 +1,132 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyFormatComplexity + /// + [DataContract(Name = "PolyFormatComplexity")] + public partial class PolyFormatComplexity : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// triangleCount. + /// lodHint. + public PolyFormatComplexity(string triangleCount = default(string), int lodHint = default(int)) + { + this.TriangleCount = triangleCount; + this.LodHint = lodHint; + } + + /// + /// Gets or Sets TriangleCount + /// + [DataMember(Name = "triangleCount", EmitDefaultValue = false)] + public string TriangleCount { get; set; } + + /// + /// Gets or Sets LodHint + /// + [DataMember(Name = "lodHint", EmitDefaultValue = false)] + public int LodHint { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyFormatComplexity {\n"); + sb.Append(" TriangleCount: ").Append(TriangleCount).Append("\n"); + sb.Append(" LodHint: ").Append(LodHint).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyFormatComplexity); + } + + /// + /// Returns true if PolyFormatComplexity instances are equal + /// + /// Instance of PolyFormatComplexity to be compared + /// Boolean + public bool Equals(PolyFormatComplexity input) + { + if (input == null) + { + return false; + } + return + ( + this.TriangleCount == input.TriangleCount || + (this.TriangleCount != null && + this.TriangleCount.Equals(input.TriangleCount)) + ) && + ( + this.LodHint == input.LodHint || + this.LodHint.Equals(input.LodHint) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.TriangleCount != null) + { + hashCode = (hashCode * 59) + this.TriangleCount.GetHashCode(); + } + hashCode = (hashCode * 59) + this.LodHint.GetHashCode(); + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs.meta new file mode 100644 index 0000000000..3c2f80d3c7 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyFormatComplexity.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8fb711e277e9f492bb50291860a2304f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs new file mode 100644 index 0000000000..2c795e51aa --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs @@ -0,0 +1,166 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyList + /// + [DataContract(Name = "PolyList")] + public partial class PolyList : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PolyList() { } + /// + /// Initializes a new instance of the class. + /// + /// assets (required). + /// nextPageToken (required). + /// totalSize (required). + public PolyList(List assets = default(List), string nextPageToken = default(string), int totalSize = default(int)) + { + // to ensure "assets" is required (not null) + if (assets == null) + { + throw new ArgumentNullException("assets is a required property for PolyList and cannot be null"); + } + this.Assets = assets; + // to ensure "nextPageToken" is required (not null) + if (nextPageToken == null) + { + throw new ArgumentNullException("nextPageToken is a required property for PolyList and cannot be null"); + } + this.NextPageToken = nextPageToken; + this.TotalSize = totalSize; + } + + /// + /// Gets or Sets Assets + /// + [DataMember(Name = "assets", IsRequired = true, EmitDefaultValue = true)] + public List Assets { get; set; } + + /// + /// Gets or Sets NextPageToken + /// + [DataMember(Name = "nextPageToken", IsRequired = true, EmitDefaultValue = true)] + public string NextPageToken { get; set; } + + /// + /// Gets or Sets TotalSize + /// + [DataMember(Name = "totalSize", IsRequired = true, EmitDefaultValue = true)] + public int TotalSize { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyList {\n"); + sb.Append(" Assets: ").Append(Assets).Append("\n"); + sb.Append(" NextPageToken: ").Append(NextPageToken).Append("\n"); + sb.Append(" TotalSize: ").Append(TotalSize).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyList); + } + + /// + /// Returns true if PolyList instances are equal + /// + /// Instance of PolyList to be compared + /// Boolean + public bool Equals(PolyList input) + { + if (input == null) + { + return false; + } + return + ( + this.Assets == input.Assets || + this.Assets != null && + input.Assets != null && + this.Assets.SequenceEqual(input.Assets) + ) && + ( + this.NextPageToken == input.NextPageToken || + (this.NextPageToken != null && + this.NextPageToken.Equals(input.NextPageToken)) + ) && + ( + this.TotalSize == input.TotalSize || + this.TotalSize.Equals(input.TotalSize) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Assets != null) + { + hashCode = (hashCode * 59) + this.Assets.GetHashCode(); + } + if (this.NextPageToken != null) + { + hashCode = (hashCode * 59) + this.NextPageToken.GetHashCode(); + } + hashCode = (hashCode * 59) + this.TotalSize.GetHashCode(); + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs.meta new file mode 100644 index 0000000000..f16e09e64d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 494fa7b70cee041a7aaf90b1af006624 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs new file mode 100644 index 0000000000..670168e72f --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs @@ -0,0 +1,154 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyPresentationParams + /// + [DataContract(Name = "PolyPresentationParams")] + public partial class PolyPresentationParams : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// orientingRotation. + /// colorSpace. + /// backgroundColor. + public PolyPresentationParams(PolyQuaternion orientingRotation = default(PolyQuaternion), string colorSpace = default(string), string backgroundColor = default(string)) + { + this.OrientingRotation = orientingRotation; + this.ColorSpace = colorSpace; + this.BackgroundColor = backgroundColor; + } + + /// + /// Gets or Sets OrientingRotation + /// + [DataMember(Name = "orientingRotation", EmitDefaultValue = false)] + public PolyQuaternion OrientingRotation { get; set; } + + /// + /// Gets or Sets ColorSpace + /// + [DataMember(Name = "colorSpace", EmitDefaultValue = false)] + public string ColorSpace { get; set; } + + /// + /// Gets or Sets BackgroundColor + /// + [DataMember(Name = "backgroundColor", EmitDefaultValue = false)] + public string BackgroundColor { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyPresentationParams {\n"); + sb.Append(" OrientingRotation: ").Append(OrientingRotation).Append("\n"); + sb.Append(" ColorSpace: ").Append(ColorSpace).Append("\n"); + sb.Append(" BackgroundColor: ").Append(BackgroundColor).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyPresentationParams); + } + + /// + /// Returns true if PolyPresentationParams instances are equal + /// + /// Instance of PolyPresentationParams to be compared + /// Boolean + public bool Equals(PolyPresentationParams input) + { + if (input == null) + { + return false; + } + return + ( + this.OrientingRotation == input.OrientingRotation || + (this.OrientingRotation != null && + this.OrientingRotation.Equals(input.OrientingRotation)) + ) && + ( + this.ColorSpace == input.ColorSpace || + (this.ColorSpace != null && + this.ColorSpace.Equals(input.ColorSpace)) + ) && + ( + this.BackgroundColor == input.BackgroundColor || + (this.BackgroundColor != null && + this.BackgroundColor.Equals(input.BackgroundColor)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.OrientingRotation != null) + { + hashCode = (hashCode * 59) + this.OrientingRotation.GetHashCode(); + } + if (this.ColorSpace != null) + { + hashCode = (hashCode * 59) + this.ColorSpace.GetHashCode(); + } + if (this.BackgroundColor != null) + { + hashCode = (hashCode * 59) + this.BackgroundColor.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs.meta new file mode 100644 index 0000000000..047ea8a440 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyPresentationParams.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85d7678a5f346475996c5d4ea9b4865a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs new file mode 100644 index 0000000000..9cd2712d36 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs @@ -0,0 +1,156 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyQuaternion + /// + [DataContract(Name = "PolyQuaternion")] + public partial class PolyQuaternion : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// x. + /// y. + /// z. + /// w. + public PolyQuaternion(decimal x = default(decimal), decimal y = default(decimal), decimal z = default(decimal), decimal w = default(decimal)) + { + this.X = x; + this.Y = y; + this.Z = z; + this.W = w; + } + + /// + /// Gets or Sets X + /// + [DataMember(Name = "x", EmitDefaultValue = false)] + public decimal X { get; set; } + + /// + /// Gets or Sets Y + /// + [DataMember(Name = "y", EmitDefaultValue = false)] + public decimal Y { get; set; } + + /// + /// Gets or Sets Z + /// + [DataMember(Name = "z", EmitDefaultValue = false)] + public decimal Z { get; set; } + + /// + /// Gets or Sets W + /// + [DataMember(Name = "w", EmitDefaultValue = false)] + public decimal W { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyQuaternion {\n"); + sb.Append(" X: ").Append(X).Append("\n"); + sb.Append(" Y: ").Append(Y).Append("\n"); + sb.Append(" Z: ").Append(Z).Append("\n"); + sb.Append(" W: ").Append(W).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyQuaternion); + } + + /// + /// Returns true if PolyQuaternion instances are equal + /// + /// Instance of PolyQuaternion to be compared + /// Boolean + public bool Equals(PolyQuaternion input) + { + if (input == null) + { + return false; + } + return + ( + this.X == input.X || + this.X.Equals(input.X) + ) && + ( + this.Y == input.Y || + this.Y.Equals(input.Y) + ) && + ( + this.Z == input.Z || + this.Z.Equals(input.Z) + ) && + ( + this.W == input.W || + this.W.Equals(input.W) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + hashCode = (hashCode * 59) + this.X.GetHashCode(); + hashCode = (hashCode * 59) + this.Y.GetHashCode(); + hashCode = (hashCode * 59) + this.Z.GetHashCode(); + hashCode = (hashCode * 59) + this.W.GetHashCode(); + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs.meta new file mode 100644 index 0000000000..38d102450c --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyQuaternion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 513cabfeb87854824853864d5552e5f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs new file mode 100644 index 0000000000..152eafc33b --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs @@ -0,0 +1,129 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyRemixInfo + /// + [DataContract(Name = "PolyRemixInfo")] + public partial class PolyRemixInfo : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PolyRemixInfo() { } + /// + /// Initializes a new instance of the class. + /// + /// sourceAsset (required). + public PolyRemixInfo(List sourceAsset = default(List)) + { + // to ensure "sourceAsset" is required (not null) + if (sourceAsset == null) + { + throw new ArgumentNullException("sourceAsset is a required property for PolyRemixInfo and cannot be null"); + } + this.SourceAsset = sourceAsset; + } + + /// + /// Gets or Sets SourceAsset + /// + [DataMember(Name = "sourceAsset", IsRequired = true, EmitDefaultValue = true)] + public List SourceAsset { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyRemixInfo {\n"); + sb.Append(" SourceAsset: ").Append(SourceAsset).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyRemixInfo); + } + + /// + /// Returns true if PolyRemixInfo instances are equal + /// + /// Instance of PolyRemixInfo to be compared + /// Boolean + public bool Equals(PolyRemixInfo input) + { + if (input == null) + { + return false; + } + return + ( + this.SourceAsset == input.SourceAsset || + this.SourceAsset != null && + input.SourceAsset != null && + this.SourceAsset.SequenceEqual(input.SourceAsset) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.SourceAsset != null) + { + hashCode = (hashCode * 59) + this.SourceAsset.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs.meta new file mode 100644 index 0000000000..851048f618 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyRemixInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 622877854add744b09304bde8fdccf57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs new file mode 100644 index 0000000000..aef3e28cd6 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs @@ -0,0 +1,174 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// PolyResource + /// + [DataContract(Name = "PolyResource")] + public partial class PolyResource : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected PolyResource() { } + /// + /// Initializes a new instance of the class. + /// + /// relativePath (required). + /// url (required). + /// contentType (required). + public PolyResource(string relativePath = default(string), string url = default(string), string contentType = default(string)) + { + // to ensure "relativePath" is required (not null) + if (relativePath == null) + { + throw new ArgumentNullException("relativePath is a required property for PolyResource and cannot be null"); + } + this.RelativePath = relativePath; + // to ensure "url" is required (not null) + if (url == null) + { + throw new ArgumentNullException("url is a required property for PolyResource and cannot be null"); + } + this.Url = url; + // to ensure "contentType" is required (not null) + if (contentType == null) + { + throw new ArgumentNullException("contentType is a required property for PolyResource and cannot be null"); + } + this.ContentType = contentType; + } + + /// + /// Gets or Sets RelativePath + /// + [DataMember(Name = "relativePath", IsRequired = true, EmitDefaultValue = true)] + public string RelativePath { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", IsRequired = true, EmitDefaultValue = true)] + public string Url { get; set; } + + /// + /// Gets or Sets ContentType + /// + [DataMember(Name = "contentType", IsRequired = true, EmitDefaultValue = true)] + public string ContentType { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class PolyResource {\n"); + sb.Append(" RelativePath: ").Append(RelativePath).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" ContentType: ").Append(ContentType).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as PolyResource); + } + + /// + /// Returns true if PolyResource instances are equal + /// + /// Instance of PolyResource to be compared + /// Boolean + public bool Equals(PolyResource input) + { + if (input == null) + { + return false; + } + return + ( + this.RelativePath == input.RelativePath || + (this.RelativePath != null && + this.RelativePath.Equals(input.RelativePath)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.ContentType == input.ContentType || + (this.ContentType != null && + this.ContentType.Equals(input.ContentType)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.RelativePath != null) + { + hashCode = (hashCode * 59) + this.RelativePath.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.ContentType != null) + { + hashCode = (hashCode * 59) + this.ContentType.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs.meta new file mode 100644 index 0000000000..93ad79dc56 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/PolyResource.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99c97d18903a7424280769ad0ce3adac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs new file mode 100644 index 0000000000..0bb069a7c6 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs @@ -0,0 +1,174 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// SubAssetFormat + /// + [DataContract(Name = "SubAssetFormat")] + public partial class SubAssetFormat : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected SubAssetFormat() { } + /// + /// Initializes a new instance of the class. + /// + /// id (required). + /// url (required). + /// format (required). + public SubAssetFormat(string id = default(string), string url = default(string), string format = default(string)) + { + // to ensure "id" is required (not null) + if (id == null) + { + throw new ArgumentNullException("id is a required property for SubAssetFormat and cannot be null"); + } + this.Id = id; + // to ensure "url" is required (not null) + if (url == null) + { + throw new ArgumentNullException("url is a required property for SubAssetFormat and cannot be null"); + } + this.Url = url; + // to ensure "format" is required (not null) + if (format == null) + { + throw new ArgumentNullException("format is a required property for SubAssetFormat and cannot be null"); + } + this.Format = format; + } + + /// + /// Gets or Sets Id + /// + [DataMember(Name = "id", IsRequired = true, EmitDefaultValue = true)] + public string Id { get; set; } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", IsRequired = true, EmitDefaultValue = true)] + public string Url { get; set; } + + /// + /// Gets or Sets Format + /// + [DataMember(Name = "format", IsRequired = true, EmitDefaultValue = true)] + public string Format { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class SubAssetFormat {\n"); + sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Format: ").Append(Format).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as SubAssetFormat); + } + + /// + /// Returns true if SubAssetFormat instances are equal + /// + /// Instance of SubAssetFormat to be compared + /// Boolean + public bool Equals(SubAssetFormat input) + { + if (input == null) + { + return false; + } + return + ( + this.Id == input.Id || + (this.Id != null && + this.Id.Equals(input.Id)) + ) && + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Format == input.Format || + (this.Format != null && + this.Format.Equals(input.Format)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Id != null) + { + hashCode = (hashCode * 59) + this.Id.GetHashCode(); + } + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Format != null) + { + hashCode = (hashCode * 59) + this.Format.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs.meta new file mode 100644 index 0000000000..8c92bb92d4 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/SubAssetFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0c1a12ac7b184ba7938b44b8bdb1379 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/User.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/User.cs new file mode 100644 index 0000000000..965abe372c --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/User.cs @@ -0,0 +1,169 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// User + /// + [DataContract(Name = "User")] + public partial class User : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected User() { } + /// + /// Initializes a new instance of the class. + /// + /// url (required). + /// displayname (required). + /// description. + public User(string url = default(string), string displayname = default(string), string description = default(string)) + { + // to ensure "url" is required (not null) + if (url == null) + { + throw new ArgumentNullException("url is a required property for User and cannot be null"); + } + this.Url = url; + // to ensure "displayname" is required (not null) + if (displayname == null) + { + throw new ArgumentNullException("displayname is a required property for User and cannot be null"); + } + this.Displayname = displayname; + this.Description = description; + } + + /// + /// Gets or Sets Url + /// + [DataMember(Name = "url", IsRequired = true, EmitDefaultValue = true)] + public string Url { get; set; } + + /// + /// Gets or Sets Displayname + /// + [DataMember(Name = "displayname", IsRequired = true, EmitDefaultValue = true)] + public string Displayname { get; set; } + + /// + /// Gets or Sets Description + /// + [DataMember(Name = "description", EmitDefaultValue = false)] + public string Description { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class User {\n"); + sb.Append(" Url: ").Append(Url).Append("\n"); + sb.Append(" Displayname: ").Append(Displayname).Append("\n"); + sb.Append(" Description: ").Append(Description).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as User); + } + + /// + /// Returns true if User instances are equal + /// + /// Instance of User to be compared + /// Boolean + public bool Equals(User input) + { + if (input == null) + { + return false; + } + return + ( + this.Url == input.Url || + (this.Url != null && + this.Url.Equals(input.Url)) + ) && + ( + this.Displayname == input.Displayname || + (this.Displayname != null && + this.Displayname.Equals(input.Displayname)) + ) && + ( + this.Description == input.Description || + (this.Description != null && + this.Description.Equals(input.Description)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Url != null) + { + hashCode = (hashCode * 59) + this.Url.GetHashCode(); + } + if (this.Displayname != null) + { + hashCode = (hashCode * 59) + this.Displayname.GetHashCode(); + } + if (this.Description != null) + { + hashCode = (hashCode * 59) + this.Description.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/User.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/User.cs.meta new file mode 100644 index 0000000000..cd36ca59f3 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/User.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c57414deb298b432285541b5a494844e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs new file mode 100644 index 0000000000..32c59ac4be --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs @@ -0,0 +1,175 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// ValidationError + /// + [DataContract(Name = "ValidationError")] + public partial class ValidationError : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected ValidationError() { } + /// + /// Initializes a new instance of the class. + /// + /// loc (required). + /// msg (required). + /// type (required). + public ValidationError(List loc = default(List), string msg = default(string), string type = default(string)) + { + // to ensure "loc" is required (not null) + if (loc == null) + { + throw new ArgumentNullException("loc is a required property for ValidationError and cannot be null"); + } + this.Loc = loc; + // to ensure "msg" is required (not null) + if (msg == null) + { + throw new ArgumentNullException("msg is a required property for ValidationError and cannot be null"); + } + this.Msg = msg; + // to ensure "type" is required (not null) + if (type == null) + { + throw new ArgumentNullException("type is a required property for ValidationError and cannot be null"); + } + this.Type = type; + } + + /// + /// Gets or Sets Loc + /// + [DataMember(Name = "loc", IsRequired = true, EmitDefaultValue = true)] + public List Loc { get; set; } + + /// + /// Gets or Sets Msg + /// + [DataMember(Name = "msg", IsRequired = true, EmitDefaultValue = true)] + public string Msg { get; set; } + + /// + /// Gets or Sets Type + /// + [DataMember(Name = "type", IsRequired = true, EmitDefaultValue = true)] + public string Type { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class ValidationError {\n"); + sb.Append(" Loc: ").Append(Loc).Append("\n"); + sb.Append(" Msg: ").Append(Msg).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as ValidationError); + } + + /// + /// Returns true if ValidationError instances are equal + /// + /// Instance of ValidationError to be compared + /// Boolean + public bool Equals(ValidationError input) + { + if (input == null) + { + return false; + } + return + ( + this.Loc == input.Loc || + this.Loc != null && + input.Loc != null && + this.Loc.SequenceEqual(input.Loc) + ) && + ( + this.Msg == input.Msg || + (this.Msg != null && + this.Msg.Equals(input.Msg)) + ) && + ( + this.Type == input.Type || + (this.Type != null && + this.Type.Equals(input.Type)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Loc != null) + { + hashCode = (hashCode * 59) + this.Loc.GetHashCode(); + } + if (this.Msg != null) + { + hashCode = (hashCode * 59) + this.Msg.GetHashCode(); + } + if (this.Type != null) + { + hashCode = (hashCode * 59) + this.Type.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs.meta new file mode 100644 index 0000000000..eb01019141 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/ValidationError.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fe4140e4e47e24540b2aae9c9917dfea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef b/Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef new file mode 100644 index 0000000000..030e83ecf5 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef @@ -0,0 +1,7 @@ +{ + "name": "Org.OpenAPITools", + "overrideReferences": true, + "precompiledReferences": [ + "Newtonsoft.Json.dll" + ] +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef.meta b/Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef.meta new file mode 100644 index 0000000000..98790e8ff5 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Org.OpenAPITools.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c5c131588bc954ab8bd8d38e37ba847f +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests.meta new file mode 100644 index 0000000000..35a518dfbd --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 76fea13e2a69148239baa3b4a512702d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta new file mode 100644 index 0000000000..0249cb9775 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fe2cb35fcf1454aacb9e29068b989379 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs new file mode 100644 index 0000000000..c4961edaea --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs @@ -0,0 +1,144 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Api; +// uncomment below to import models +//using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Test.Api +{ + /// + /// Class for testing AssetsApi + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the API endpoint. + /// + public class AssetsApiTests : IDisposable + { + private AssetsApi instance; + + public AssetsApiTests() + { + instance = new AssetsApi(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of AssetsApi + /// + [Test] + public void InstanceTest() + { + // TODO uncomment below to test 'IsType' AssetsApi + //Assert.IsType(instance); + } + + /// + /// Test DeleteAssetAssetsAssetDelete + /// + [Test] + public void DeleteAssetAssetsAssetDeleteTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int asset = null; + //var response = instance.DeleteAssetAssetsAssetDelete(asset); + //Assert.IsType(response); + } + + /// + /// Test GetAssetAssetsUserurlAsseturlGet + /// + [Test] + public void GetAssetAssetsUserurlAsseturlGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //string userurl = null; + //string asseturl = null; + //var response = instance.GetAssetAssetsUserurlAsseturlGet(userurl, asseturl); + //Assert.IsType(response); + } + + /// + /// Test GetAssetsAssetsGet + /// + [Test] + public void GetAssetsAssetsGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int? results = null; + //int? page = null; + //bool? curated = null; + //var response = instance.GetAssetsAssetsGet(results, page, curated); + //Assert.IsType>(response); + } + + /// + /// Test GetIdAssetAssetsIdAssetGet + /// + [Test] + public void GetIdAssetAssetsIdAssetGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int asset = null; + //var response = instance.GetIdAssetAssetsIdAssetGet(asset); + //Assert.IsType(response); + } + + /// + /// Test UnpublishAssetAssetsAssetUnpublishPatch + /// + [Test] + public void UnpublishAssetAssetsAssetUnpublishPatchTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int asset = null; + //var response = instance.UnpublishAssetAssetsAssetUnpublishPatch(asset); + //Assert.IsType(response); + } + + /// + /// Test UpdateAssetAssetsAssetPatch + /// + [Test] + public void UpdateAssetAssetsAssetPatchTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int asset = null; + //AssetPatchData assetPatchData = null; + //var response = instance.UpdateAssetAssetsAssetPatch(asset, assetPatchData); + //Assert.IsType(response); + } + + /// + /// Test UploadNewAssetsAssetsPost + /// + [Test] + public void UploadNewAssetsAssetsPostTest() + { + // TODO uncomment below to test the method and replace null with proper value + //List files = null; + //var response = instance.UploadNewAssetsAssetsPost(files); + //Assert.IsType(response); + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta new file mode 100644 index 0000000000..9521ea547c --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb3d21165a6034f5688e8016013decff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs new file mode 100644 index 0000000000..d4cf2dc49a --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs @@ -0,0 +1,73 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Api; +// uncomment below to import models +//using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Test.Api +{ + /// + /// Class for testing LoginApi + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the API endpoint. + /// + public class LoginApiTests : IDisposable + { + private LoginApi instance; + + public LoginApiTests() + { + instance = new LoginApi(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of LoginApi + /// + [Test] + public void InstanceTest() + { + // TODO uncomment below to test 'IsType' LoginApi + //Assert.IsType(instance); + } + + /// + /// Test LoginLoginPost + /// + [Test] + public void LoginLoginPostTest() + { + // TODO uncomment below to test the method and replace null with proper value + string username = null; + string password = null; + string? grantType = null; + string? scope = null; + string? clientId = null; + string? clientSecret = null; + var response = instance.LoginLoginPost(username, password, grantType, scope, clientId, clientSecret); + Assert.IsType(response); + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta new file mode 100644 index 0000000000..8a1d54fe74 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ec43ebaef8d44df8b21ae9a1e06f1a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs new file mode 100644 index 0000000000..7bd2dc24f3 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs @@ -0,0 +1,94 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Api; +// uncomment below to import models +//using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Test.Api +{ + /// + /// Class for testing PolyApi + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the API endpoint. + /// + public class PolyApiTests : IDisposable + { + private PolyApi instance; + + public PolyApiTests() + { + instance = new PolyApi(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyApi + /// + [Test] + public void InstanceTest() + { + // TODO uncomment below to test 'IsType' PolyApi + //Assert.IsType(instance); + } + + /// + /// Test GetPolyAssetPolyAssetsAssetGet + /// + [Test] + public void GetPolyAssetPolyAssetsAssetGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //string asset = null; + //var response = instance.GetPolyAssetPolyAssetsAssetGet(asset); + //Assert.IsType(response); + } + + /// + /// Test GetPolyAssetsListPolyAssetsGet + /// + [Test] + public void GetPolyAssetsListPolyAssetsGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int? results = null; + //int? page = null; + //bool? curated = null; + //var response = instance.GetPolyAssetsListPolyAssetsGet(results, page, curated); + //Assert.IsType(response); + } + + /// + /// Test ImportPolyDataPolyImportPost + /// + [Test] + public void ImportPolyDataPolyImportPostTest() + { + // TODO uncomment below to test the method and replace null with proper value + //List requestBody = null; + //var response = instance.ImportPolyDataPolyImportPost(requestBody); + //Assert.IsType(response); + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta new file mode 100644 index 0000000000..e1c80cf337 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: edbc76f90365d4c1ba41519350fab72f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs new file mode 100644 index 0000000000..e134f6575d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs @@ -0,0 +1,198 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Api; +// uncomment below to import models +//using Org.OpenAPITools.Model; + +namespace Org.OpenAPITools.Test.Api +{ + /// + /// Class for testing UsersApi + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the API endpoint. + /// + public class UsersApiTests : IDisposable + { + private UsersApi instance; + + public UsersApiTests() + { + instance = new UsersApi(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of UsersApi + /// + [Test] + public void InstanceTest() + { + // TODO uncomment below to test 'IsType' UsersApi + //Assert.IsType(instance); + } + + /// + /// Test ChangeAuthenticatedUserEmailUsersMeEmailPatch + /// + [Test] + public void ChangeAuthenticatedUserEmailUsersMeEmailPatchTest() + { + // TODO uncomment below to test the method and replace null with proper value + //EmailChangeAuthenticated emailChangeAuthenticated = null; + //var response = instance.ChangeAuthenticatedUserEmailUsersMeEmailPatch(emailChangeAuthenticated); + //Assert.IsType(response); + } + + /// + /// Test ChangeAuthenticatedUserPasswordUsersMePasswordPatch + /// + [Test] + public void ChangeAuthenticatedUserPasswordUsersMePasswordPatchTest() + { + // TODO uncomment below to test the method and replace null with proper value + //PasswordChangeAuthenticated passwordChangeAuthenticated = null; + //var response = instance.ChangeAuthenticatedUserPasswordUsersMePasswordPatch(passwordChangeAuthenticated); + //Assert.IsType(response); + } + + /// + /// Test ChangePasswordViaTokenUsersPasswordPatch + /// + [Test] + public void ChangePasswordViaTokenUsersPasswordPatchTest() + { + // TODO uncomment below to test the method and replace null with proper value + //PasswordChangeToken passwordChangeToken = null; + //var response = instance.ChangePasswordViaTokenUsersPasswordPatch(passwordChangeToken); + //Assert.IsType(response); + } + + /// + /// Test CreateUserUsersPost + /// + [Test] + public void CreateUserUsersPostTest() + { + // TODO uncomment below to test the method and replace null with proper value + //NewUser newUser = null; + //var response = instance.CreateUserUsersPost(newUser); + //Assert.IsType(response); + } + + /// + /// Test GetIdUserAssetsUsersIdUserAssetsGet + /// + [Test] + public void GetIdUserAssetsUsersIdUserAssetsGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int user = null; + //var response = instance.GetIdUserAssetsUsersIdUserAssetsGet(user); + //Assert.IsType>(response); + } + + /// + /// Test GetMeAssetsUsersMeAssetsGet + /// + [Test] + public void GetMeAssetsUsersMeAssetsGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //var response = instance.GetMeAssetsUsersMeAssetsGet(); + //Assert.IsType>(response); + } + + /// + /// Test GetUserAssetsUsersUserAssetsGet + /// + [Test] + public void GetUserAssetsUsersUserAssetsGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //string user = null; + //var response = instance.GetUserAssetsUsersUserAssetsGet(user); + //Assert.IsType>(response); + } + + /// + /// Test GetUserUsersIdUserGet + /// + [Test] + public void GetUserUsersIdUserGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //int user = null; + //var response = instance.GetUserUsersIdUserGet(user); + //Assert.IsType(response); + } + + /// + /// Test GetUserUsersUserGet + /// + [Test] + public void GetUserUsersUserGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //string user = null; + //var response = instance.GetUserUsersUserGet(user); + //Assert.IsType(response); + } + + /// + /// Test GetUsersMeUsersMeGet + /// + [Test] + public void GetUsersMeUsersMeGetTest() + { + // TODO uncomment below to test the method and replace null with proper value + //var response = instance.GetUsersMeUsersMeGet(); + //Assert.IsType(response); + } + + /// + /// Test UpdateUserUsersMePatch + /// + [Test] + public void UpdateUserUsersMePatchTest() + { + // TODO uncomment below to test the method and replace null with proper value + //PatchUser patchUser = null; + //var response = instance.UpdateUserUsersMePatch(patchUser); + //Assert.IsType(response); + } + + /// + /// Test UserPasswordResetRequestUsersPasswordResetPut + /// + [Test] + public void UserPasswordResetRequestUsersPasswordResetPutTest() + { + // TODO uncomment below to test the method and replace null with proper value + //PasswordReset passwordReset = null; + //var response = instance.UserPasswordResetRequestUsersPasswordResetPut(passwordReset); + //Assert.IsType(response); + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta new file mode 100644 index 0000000000..15887be082 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c16251192a0b54a7ea0cc6c83dc98491 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta new file mode 100644 index 0000000000..8ec0c0f7d0 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7b9b82320374c4da584071d18ce37e1c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs new file mode 100644 index 0000000000..570cf24378 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs @@ -0,0 +1,90 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing AssetFormat + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class AssetFormatTests : IDisposable + { + // TODO uncomment below to declare an instance variable for AssetFormat + //private AssetFormat instance; + + public AssetFormatTests() + { + // TODO uncomment below to create an instance of AssetFormat + //instance = new AssetFormat(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of AssetFormat + /// + [Test] + public void AssetFormatInstanceTest() + { + // TODO uncomment below to test "IsType" AssetFormat + //Assert.IsType(instance); + } + + /// + /// Test the property 'Id' + /// + [Test] + public void IdTest() + { + // TODO unit test for the property 'Id' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Format' + /// + [Test] + public void FormatTest() + { + // TODO unit test for the property 'Format' + } + /// + /// Test the property 'Subfiles' + /// + [Test] + public void SubfilesTest() + { + // TODO unit test for the property 'Subfiles' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta new file mode 100644 index 0000000000..ea8afae624 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90bec396919c74a0aadf13caad47ab8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs new file mode 100644 index 0000000000..ec1f94a780 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs @@ -0,0 +1,90 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing AssetPatchData + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class AssetPatchDataTests : IDisposable + { + // TODO uncomment below to declare an instance variable for AssetPatchData + //private AssetPatchData instance; + + public AssetPatchDataTests() + { + // TODO uncomment below to create an instance of AssetPatchData + //instance = new AssetPatchData(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of AssetPatchData + /// + [Test] + public void AssetPatchDataInstanceTest() + { + // TODO uncomment below to test "IsType" AssetPatchData + //Assert.IsType(instance); + } + + /// + /// Test the property 'Name' + /// + [Test] + public void NameTest() + { + // TODO unit test for the property 'Name' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Description' + /// + [Test] + public void DescriptionTest() + { + // TODO unit test for the property 'Description' + } + /// + /// Test the property 'Visibility' + /// + [Test] + public void VisibilityTest() + { + // TODO unit test for the property 'Visibility' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta new file mode 100644 index 0000000000..016b7507b1 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21413f6baa7b84a929111af5b3e894eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs new file mode 100644 index 0000000000..3dc7672d2d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs @@ -0,0 +1,162 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing Asset + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class AssetTests : IDisposable + { + // TODO uncomment below to declare an instance variable for Asset + //private Asset instance; + + public AssetTests() + { + // TODO uncomment below to create an instance of Asset + //instance = new Asset(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of Asset + /// + [Test] + public void AssetInstanceTest() + { + // TODO uncomment below to test "IsType" Asset + //Assert.IsType(instance); + } + + /// + /// Test the property 'Id' + /// + [Test] + public void IdTest() + { + // TODO unit test for the property 'Id' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Formats' + /// + [Test] + public void FormatsTest() + { + // TODO unit test for the property 'Formats' + } + /// + /// Test the property 'Name' + /// + [Test] + public void NameTest() + { + // TODO unit test for the property 'Name' + } + /// + /// Test the property 'Description' + /// + [Test] + public void DescriptionTest() + { + // TODO unit test for the property 'Description' + } + /// + /// Test the property 'Owner' + /// + [Test] + public void OwnerTest() + { + // TODO unit test for the property 'Owner' + } + /// + /// Test the property 'Visibility' + /// + [Test] + public void VisibilityTest() + { + // TODO unit test for the property 'Visibility' + } + /// + /// Test the property 'Curated' + /// + [Test] + public void CuratedTest() + { + // TODO unit test for the property 'Curated' + } + /// + /// Test the property 'Polyid' + /// + [Test] + public void PolyidTest() + { + // TODO unit test for the property 'Polyid' + } + /// + /// Test the property 'Polydata' + /// + [Test] + public void PolydataTest() + { + // TODO unit test for the property 'Polydata' + } + /// + /// Test the property 'Thumbnail' + /// + [Test] + public void ThumbnailTest() + { + // TODO unit test for the property 'Thumbnail' + } + /// + /// Test the property 'Ownername' + /// + [Test] + public void OwnernameTest() + { + // TODO unit test for the property 'Ownername' + } + /// + /// Test the property 'Ownerurl' + /// + [Test] + public void OwnerurlTest() + { + // TODO unit test for the property 'Ownerurl' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta new file mode 100644 index 0000000000..c55ed14e39 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dcde69cf840424b6fa07b231b32c5367 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs new file mode 100644 index 0000000000..9b6ff0c645 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs @@ -0,0 +1,74 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing EmailChangeAuthenticated + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class EmailChangeAuthenticatedTests : IDisposable + { + // TODO uncomment below to declare an instance variable for EmailChangeAuthenticated + //private EmailChangeAuthenticated instance; + + public EmailChangeAuthenticatedTests() + { + // TODO uncomment below to create an instance of EmailChangeAuthenticated + //instance = new EmailChangeAuthenticated(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of EmailChangeAuthenticated + /// + [Test] + public void EmailChangeAuthenticatedInstanceTest() + { + // TODO uncomment below to test "IsType" EmailChangeAuthenticated + //Assert.IsType(instance); + } + + /// + /// Test the property 'NewEmail' + /// + [Test] + public void NewEmailTest() + { + // TODO unit test for the property 'NewEmail' + } + /// + /// Test the property 'CurrentPassword' + /// + [Test] + public void CurrentPasswordTest() + { + // TODO unit test for the property 'CurrentPassword' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta new file mode 100644 index 0000000000..a33814c3f3 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e474606cd14a448ab50f295b9b3ab23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs new file mode 100644 index 0000000000..649472daf7 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs @@ -0,0 +1,98 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing FullUser + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class FullUserTests : IDisposable + { + // TODO uncomment below to declare an instance variable for FullUser + //private FullUser instance; + + public FullUserTests() + { + // TODO uncomment below to create an instance of FullUser + //instance = new FullUser(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of FullUser + /// + [Test] + public void FullUserInstanceTest() + { + // TODO uncomment below to test "IsType" FullUser + //Assert.IsType(instance); + } + + /// + /// Test the property 'Id' + /// + [Test] + public void IdTest() + { + // TODO unit test for the property 'Id' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Email' + /// + [Test] + public void EmailTest() + { + // TODO unit test for the property 'Email' + } + /// + /// Test the property 'Displayname' + /// + [Test] + public void DisplaynameTest() + { + // TODO unit test for the property 'Displayname' + } + /// + /// Test the property 'Description' + /// + [Test] + public void DescriptionTest() + { + // TODO unit test for the property 'Description' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta new file mode 100644 index 0000000000..e61b6638ed --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 753dd804aa9e842b4bd43a6e05a244bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs new file mode 100644 index 0000000000..a8f0d67c68 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs @@ -0,0 +1,66 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing HTTPValidationError + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class HTTPValidationErrorTests : IDisposable + { + // TODO uncomment below to declare an instance variable for HTTPValidationError + //private HTTPValidationError instance; + + public HTTPValidationErrorTests() + { + // TODO uncomment below to create an instance of HTTPValidationError + //instance = new HTTPValidationError(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of HTTPValidationError + /// + [Test] + public void HTTPValidationErrorInstanceTest() + { + // TODO uncomment below to test "IsType" HTTPValidationError + //Assert.IsType(instance); + } + + /// + /// Test the property 'Detail' + /// + [Test] + public void DetailTest() + { + // TODO unit test for the property 'Detail' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta new file mode 100644 index 0000000000..e95a9f9b01 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f89cb15531894ebd98e8d5b1457caee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs new file mode 100644 index 0000000000..dbefcf5969 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs @@ -0,0 +1,74 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing LoginToken + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class LoginTokenTests : IDisposable + { + // TODO uncomment below to declare an instance variable for LoginToken + //private LoginToken instance; + + public LoginTokenTests() + { + // TODO uncomment below to create an instance of LoginToken + //instance = new LoginToken(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of LoginToken + /// + [Test] + public void LoginTokenInstanceTest() + { + // TODO uncomment below to test "IsType" LoginToken + //Assert.IsType(instance); + } + + /// + /// Test the property 'AccessToken' + /// + [Test] + public void AccessTokenTest() + { + // TODO unit test for the property 'AccessToken' + } + /// + /// Test the property 'TokenType' + /// + [Test] + public void TokenTypeTest() + { + // TODO unit test for the property 'TokenType' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta new file mode 100644 index 0000000000..2434cd6ee2 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b831bce3e59d44f2877205d2ae8ac58 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs new file mode 100644 index 0000000000..bd0e7a0db1 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs @@ -0,0 +1,90 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing NewUser + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class NewUserTests : IDisposable + { + // TODO uncomment below to declare an instance variable for NewUser + //private NewUser instance; + + public NewUserTests() + { + // TODO uncomment below to create an instance of NewUser + //instance = new NewUser(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of NewUser + /// + [Test] + public void NewUserInstanceTest() + { + // TODO uncomment below to test "IsType" NewUser + //Assert.IsType(instance); + } + + /// + /// Test the property 'Email' + /// + [Test] + public void EmailTest() + { + // TODO unit test for the property 'Email' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Password' + /// + [Test] + public void PasswordTest() + { + // TODO unit test for the property 'Password' + } + /// + /// Test the property 'DisplayName' + /// + [Test] + public void DisplayNameTest() + { + // TODO unit test for the property 'DisplayName' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta new file mode 100644 index 0000000000..44750444bb --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c65fd1bd7ac64bcebc8e7c094834050 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs new file mode 100644 index 0000000000..7e2c34bcd8 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs @@ -0,0 +1,74 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PasswordChangeAuthenticated + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PasswordChangeAuthenticatedTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PasswordChangeAuthenticated + //private PasswordChangeAuthenticated instance; + + public PasswordChangeAuthenticatedTests() + { + // TODO uncomment below to create an instance of PasswordChangeAuthenticated + //instance = new PasswordChangeAuthenticated(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PasswordChangeAuthenticated + /// + [Test] + public void PasswordChangeAuthenticatedInstanceTest() + { + // TODO uncomment below to test "IsType" PasswordChangeAuthenticated + //Assert.IsType(instance); + } + + /// + /// Test the property 'OldPassword' + /// + [Test] + public void OldPasswordTest() + { + // TODO unit test for the property 'OldPassword' + } + /// + /// Test the property 'NewPassword' + /// + [Test] + public void NewPasswordTest() + { + // TODO unit test for the property 'NewPassword' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta new file mode 100644 index 0000000000..df9b85dbae --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 530bd307703124cbd85667b99e5a0bf9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs new file mode 100644 index 0000000000..05ac223cb4 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs @@ -0,0 +1,74 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PasswordChangeToken + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PasswordChangeTokenTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PasswordChangeToken + //private PasswordChangeToken instance; + + public PasswordChangeTokenTests() + { + // TODO uncomment below to create an instance of PasswordChangeToken + //instance = new PasswordChangeToken(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PasswordChangeToken + /// + [Test] + public void PasswordChangeTokenInstanceTest() + { + // TODO uncomment below to test "IsType" PasswordChangeToken + //Assert.IsType(instance); + } + + /// + /// Test the property 'Token' + /// + [Test] + public void TokenTest() + { + // TODO unit test for the property 'Token' + } + /// + /// Test the property 'NewPassword' + /// + [Test] + public void NewPasswordTest() + { + // TODO unit test for the property 'NewPassword' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta new file mode 100644 index 0000000000..1d1ba6b5fa --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f341d16587e149bfbb399526f04f943 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs new file mode 100644 index 0000000000..78ca2f25a8 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs @@ -0,0 +1,66 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PasswordReset + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PasswordResetTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PasswordReset + //private PasswordReset instance; + + public PasswordResetTests() + { + // TODO uncomment below to create an instance of PasswordReset + //instance = new PasswordReset(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PasswordReset + /// + [Test] + public void PasswordResetInstanceTest() + { + // TODO uncomment below to test "IsType" PasswordReset + //Assert.IsType(instance); + } + + /// + /// Test the property 'Email' + /// + [Test] + public void EmailTest() + { + // TODO unit test for the property 'Email' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta new file mode 100644 index 0000000000..af3223a88d --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ed351f8defe2420ebb0f49b8e9e0df8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs new file mode 100644 index 0000000000..e3faf625a0 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PatchUser + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PatchUserTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PatchUser + //private PatchUser instance; + + public PatchUserTests() + { + // TODO uncomment below to create an instance of PatchUser + //instance = new PatchUser(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PatchUser + /// + [Test] + public void PatchUserInstanceTest() + { + // TODO uncomment below to test "IsType" PatchUser + //Assert.IsType(instance); + } + + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Displayname' + /// + [Test] + public void DisplaynameTest() + { + // TODO unit test for the property 'Displayname' + } + /// + /// Test the property 'Description' + /// + [Test] + public void DescriptionTest() + { + // TODO unit test for the property 'Description' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta new file mode 100644 index 0000000000..3513fc9b0f --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43c1979575112454d88d4b354df81b13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs new file mode 100644 index 0000000000..f678cf7cf6 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs @@ -0,0 +1,170 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyAsset + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyAssetTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyAsset + //private PolyAsset instance; + + public PolyAssetTests() + { + // TODO uncomment below to create an instance of PolyAsset + //instance = new PolyAsset(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyAsset + /// + [Test] + public void PolyAssetInstanceTest() + { + // TODO uncomment below to test "IsType" PolyAsset + //Assert.IsType(instance); + } + + /// + /// Test the property 'Name' + /// + [Test] + public void NameTest() + { + // TODO unit test for the property 'Name' + } + /// + /// Test the property 'DisplayName' + /// + [Test] + public void DisplayNameTest() + { + // TODO unit test for the property 'DisplayName' + } + /// + /// Test the property 'AuthorName' + /// + [Test] + public void AuthorNameTest() + { + // TODO unit test for the property 'AuthorName' + } + /// + /// Test the property 'Description' + /// + [Test] + public void DescriptionTest() + { + // TODO unit test for the property 'Description' + } + /// + /// Test the property 'CreateTime' + /// + [Test] + public void CreateTimeTest() + { + // TODO unit test for the property 'CreateTime' + } + /// + /// Test the property 'UpdateTime' + /// + [Test] + public void UpdateTimeTest() + { + // TODO unit test for the property 'UpdateTime' + } + /// + /// Test the property 'Formats' + /// + [Test] + public void FormatsTest() + { + // TODO unit test for the property 'Formats' + } + /// + /// Test the property 'Thumbnail' + /// + [Test] + public void ThumbnailTest() + { + // TODO unit test for the property 'Thumbnail' + } + /// + /// Test the property 'Licence' + /// + [Test] + public void LicenceTest() + { + // TODO unit test for the property 'Licence' + } + /// + /// Test the property 'Visibility' + /// + [Test] + public void VisibilityTest() + { + // TODO unit test for the property 'Visibility' + } + /// + /// Test the property 'IsCurated' + /// + [Test] + public void IsCuratedTest() + { + // TODO unit test for the property 'IsCurated' + } + /// + /// Test the property 'PresentationParams' + /// + [Test] + public void PresentationParamsTest() + { + // TODO unit test for the property 'PresentationParams' + } + /// + /// Test the property 'Metadata' + /// + [Test] + public void MetadataTest() + { + // TODO unit test for the property 'Metadata' + } + /// + /// Test the property 'RemixInfo' + /// + [Test] + public void RemixInfoTest() + { + // TODO unit test for the property 'RemixInfo' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta new file mode 100644 index 0000000000..b8288a3e14 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aab7589e6ea154542bd635a048243e78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs new file mode 100644 index 0000000000..dbd0285e72 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs @@ -0,0 +1,74 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyFormatComplexity + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyFormatComplexityTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyFormatComplexity + //private PolyFormatComplexity instance; + + public PolyFormatComplexityTests() + { + // TODO uncomment below to create an instance of PolyFormatComplexity + //instance = new PolyFormatComplexity(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyFormatComplexity + /// + [Test] + public void PolyFormatComplexityInstanceTest() + { + // TODO uncomment below to test "IsType" PolyFormatComplexity + //Assert.IsType(instance); + } + + /// + /// Test the property 'TriangleCount' + /// + [Test] + public void TriangleCountTest() + { + // TODO unit test for the property 'TriangleCount' + } + /// + /// Test the property 'LodHint' + /// + [Test] + public void LodHintTest() + { + // TODO unit test for the property 'LodHint' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta new file mode 100644 index 0000000000..9ecf1de31c --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a264eddcbce80495c8898c41d01101cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs new file mode 100644 index 0000000000..e2224d11f4 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs @@ -0,0 +1,90 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyFormat + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyFormatTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyFormat + //private PolyFormat instance; + + public PolyFormatTests() + { + // TODO uncomment below to create an instance of PolyFormat + //instance = new PolyFormat(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyFormat + /// + [Test] + public void PolyFormatInstanceTest() + { + // TODO uncomment below to test "IsType" PolyFormat + //Assert.IsType(instance); + } + + /// + /// Test the property 'Root' + /// + [Test] + public void RootTest() + { + // TODO unit test for the property 'Root' + } + /// + /// Test the property 'Resources' + /// + [Test] + public void ResourcesTest() + { + // TODO unit test for the property 'Resources' + } + /// + /// Test the property 'FormatComplexity' + /// + [Test] + public void FormatComplexityTest() + { + // TODO unit test for the property 'FormatComplexity' + } + /// + /// Test the property 'FormatType' + /// + [Test] + public void FormatTypeTest() + { + // TODO unit test for the property 'FormatType' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta new file mode 100644 index 0000000000..e809d1a8ab --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f125f66bf3ba455b9253dfd072fd1b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs new file mode 100644 index 0000000000..0546a12b4b --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyList + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyListTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyList + //private PolyList instance; + + public PolyListTests() + { + // TODO uncomment below to create an instance of PolyList + //instance = new PolyList(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyList + /// + [Test] + public void PolyListInstanceTest() + { + // TODO uncomment below to test "IsType" PolyList + //Assert.IsType(instance); + } + + /// + /// Test the property 'Assets' + /// + [Test] + public void AssetsTest() + { + // TODO unit test for the property 'Assets' + } + /// + /// Test the property 'NextPageToken' + /// + [Test] + public void NextPageTokenTest() + { + // TODO unit test for the property 'NextPageToken' + } + /// + /// Test the property 'TotalSize' + /// + [Test] + public void TotalSizeTest() + { + // TODO unit test for the property 'TotalSize' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta new file mode 100644 index 0000000000..605a0c1292 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd52b174fc1b245d8ac42b959b8330b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs new file mode 100644 index 0000000000..b69d087b0a --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyPresentationParams + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyPresentationParamsTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyPresentationParams + //private PolyPresentationParams instance; + + public PolyPresentationParamsTests() + { + // TODO uncomment below to create an instance of PolyPresentationParams + //instance = new PolyPresentationParams(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyPresentationParams + /// + [Test] + public void PolyPresentationParamsInstanceTest() + { + // TODO uncomment below to test "IsType" PolyPresentationParams + //Assert.IsType(instance); + } + + /// + /// Test the property 'OrientingRotation' + /// + [Test] + public void OrientingRotationTest() + { + // TODO unit test for the property 'OrientingRotation' + } + /// + /// Test the property 'ColorSpace' + /// + [Test] + public void ColorSpaceTest() + { + // TODO unit test for the property 'ColorSpace' + } + /// + /// Test the property 'BackgroundColor' + /// + [Test] + public void BackgroundColorTest() + { + // TODO unit test for the property 'BackgroundColor' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta new file mode 100644 index 0000000000..98f078b180 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c542cd7788c71429fbd1d8c91f2a8de3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs new file mode 100644 index 0000000000..fa7f8b4cbc --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs @@ -0,0 +1,90 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyQuaternion + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyQuaternionTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyQuaternion + //private PolyQuaternion instance; + + public PolyQuaternionTests() + { + // TODO uncomment below to create an instance of PolyQuaternion + //instance = new PolyQuaternion(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyQuaternion + /// + [Test] + public void PolyQuaternionInstanceTest() + { + // TODO uncomment below to test "IsType" PolyQuaternion + //Assert.IsType(instance); + } + + /// + /// Test the property 'X' + /// + [Test] + public void XTest() + { + // TODO unit test for the property 'X' + } + /// + /// Test the property 'Y' + /// + [Test] + public void YTest() + { + // TODO unit test for the property 'Y' + } + /// + /// Test the property 'Z' + /// + [Test] + public void ZTest() + { + // TODO unit test for the property 'Z' + } + /// + /// Test the property 'W' + /// + [Test] + public void WTest() + { + // TODO unit test for the property 'W' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta new file mode 100644 index 0000000000..01a68d61d2 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1fbb41e4a1f6b449792b56325b0afe44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs new file mode 100644 index 0000000000..e76bdeee9a --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs @@ -0,0 +1,66 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyRemixInfo + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyRemixInfoTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyRemixInfo + //private PolyRemixInfo instance; + + public PolyRemixInfoTests() + { + // TODO uncomment below to create an instance of PolyRemixInfo + //instance = new PolyRemixInfo(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyRemixInfo + /// + [Test] + public void PolyRemixInfoInstanceTest() + { + // TODO uncomment below to test "IsType" PolyRemixInfo + //Assert.IsType(instance); + } + + /// + /// Test the property 'SourceAsset' + /// + [Test] + public void SourceAssetTest() + { + // TODO unit test for the property 'SourceAsset' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta new file mode 100644 index 0000000000..c4db9722f2 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e84534ba8cef140b4ab05549bb3261dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs new file mode 100644 index 0000000000..dd0d83e866 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing PolyResource + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class PolyResourceTests : IDisposable + { + // TODO uncomment below to declare an instance variable for PolyResource + //private PolyResource instance; + + public PolyResourceTests() + { + // TODO uncomment below to create an instance of PolyResource + //instance = new PolyResource(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of PolyResource + /// + [Test] + public void PolyResourceInstanceTest() + { + // TODO uncomment below to test "IsType" PolyResource + //Assert.IsType(instance); + } + + /// + /// Test the property 'RelativePath' + /// + [Test] + public void RelativePathTest() + { + // TODO unit test for the property 'RelativePath' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'ContentType' + /// + [Test] + public void ContentTypeTest() + { + // TODO unit test for the property 'ContentType' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta new file mode 100644 index 0000000000..da390a34e5 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 92dcbe08faad54332b0bdb202a8fe7b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs new file mode 100644 index 0000000000..9abba954ee --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing SubAssetFormat + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class SubAssetFormatTests : IDisposable + { + // TODO uncomment below to declare an instance variable for SubAssetFormat + //private SubAssetFormat instance; + + public SubAssetFormatTests() + { + // TODO uncomment below to create an instance of SubAssetFormat + //instance = new SubAssetFormat(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of SubAssetFormat + /// + [Test] + public void SubAssetFormatInstanceTest() + { + // TODO uncomment below to test "IsType" SubAssetFormat + //Assert.IsType(instance); + } + + /// + /// Test the property 'Id' + /// + [Test] + public void IdTest() + { + // TODO unit test for the property 'Id' + } + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Format' + /// + [Test] + public void FormatTest() + { + // TODO unit test for the property 'Format' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta new file mode 100644 index 0000000000..ef815c092a --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bcaf28a0fc1e24b908fba012419d4b65 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs new file mode 100644 index 0000000000..2e51f25969 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing User + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class UserTests : IDisposable + { + // TODO uncomment below to declare an instance variable for User + //private User instance; + + public UserTests() + { + // TODO uncomment below to create an instance of User + //instance = new User(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of User + /// + [Test] + public void UserInstanceTest() + { + // TODO uncomment below to test "IsType" User + //Assert.IsType(instance); + } + + /// + /// Test the property 'Url' + /// + [Test] + public void UrlTest() + { + // TODO unit test for the property 'Url' + } + /// + /// Test the property 'Displayname' + /// + [Test] + public void DisplaynameTest() + { + // TODO unit test for the property 'Displayname' + } + /// + /// Test the property 'Description' + /// + [Test] + public void DescriptionTest() + { + // TODO unit test for the property 'Description' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta new file mode 100644 index 0000000000..9c9d4ccf69 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 067b53b1347ac430e9ffe0cd92dd6765 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs new file mode 100644 index 0000000000..e71fbefb39 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs @@ -0,0 +1,82 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Linq; +using System.IO; +using System.Collections.Generic; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Model; +using Org.OpenAPITools.Client; +using System.Reflection; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Org.OpenAPITools.Test.Model +{ + /// + /// Class for testing ValidationError + /// + /// + /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). + /// Please update the test case below to test the model. + /// + public class ValidationErrorTests : IDisposable + { + // TODO uncomment below to declare an instance variable for ValidationError + //private ValidationError instance; + + public ValidationErrorTests() + { + // TODO uncomment below to create an instance of ValidationError + //instance = new ValidationError(); + } + + public void Dispose() + { + // Cleanup when everything is done. + } + + /// + /// Test an instance of ValidationError + /// + [Test] + public void ValidationErrorInstanceTest() + { + // TODO uncomment below to test "IsType" ValidationError + //Assert.IsType(instance); + } + + /// + /// Test the property 'Loc' + /// + [Test] + public void LocTest() + { + // TODO unit test for the property 'Loc' + } + /// + /// Test the property 'Msg' + /// + [Test] + public void MsgTest() + { + // TODO unit test for the property 'Msg' + } + /// + /// Test the property 'Type' + /// + [Test] + public void TypeTest() + { + // TODO unit test for the property 'Type' + } + } +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta new file mode 100644 index 0000000000..295c54bc8b --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c17cfc5ff3bd4bb4b7f8c90682e5fbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef b/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef new file mode 100644 index 0000000000..464bf977d5 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef @@ -0,0 +1,15 @@ +{ + "name": "Org.OpenAPITools.Test", + "references": [ + "Org.OpenAPITools", + "UnityEngine.TestRunner" + ], + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "Newtonsoft.Json.dll" + ], + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ] +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta new file mode 100644 index 0000000000..4277b6c803 --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 62a13c20821f747f3be26668d785508e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From a4b5a87f255d8afd4603b2f1bc9aafc703eb6240 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 1 Nov 2023 11:07:40 +0000 Subject: [PATCH 002/105] Initial WIP Icosa auth --- Assets/Scripts/App.cs | 3 + Assets/Scripts/GUI/ProfilePopUpWindow.cs | 23 ++ Assets/Scripts/Sharing/IcosaService.cs | 405 ++++++++++++++++++++ Assets/Scripts/Sharing/IcosaService.cs.meta | 3 + Assets/Scripts/Sharing/OAuth2Identity.cs | 2 + Assets/Scripts/Sharing/VrAssetService.cs | 3 +- 6 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 Assets/Scripts/Sharing/IcosaService.cs create mode 100644 Assets/Scripts/Sharing/IcosaService.cs.meta diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index 32d5895cc5..04f34689c6 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -129,6 +129,7 @@ public enum AppState public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; + public static OAuth2Identity IcosaIdentity => m_Instance.m_IcosaIdentity; public static GoogleUserSettings GoogleUserSettings => m_Instance.m_GoogleUserSettings; @@ -154,6 +155,7 @@ public static OAuth2Identity GetIdentity(Cloud cloud) { case Cloud.Poly: return GoogleIdentity; case Cloud.Sketchfab: return SketchfabIdentity; + case Cloud.Icosa: return IcosaIdentity; default: throw new InvalidOperationException($"No identity for {cloud}"); } } @@ -223,6 +225,7 @@ public static void Log(string msg) [Header("Identities")] [SerializeField] private OAuth2Identity m_GoogleIdentity; [SerializeField] private OAuth2Identity m_SketchfabIdentity; + [SerializeField] private OAuth2Identity m_IcosaIdentity; // ------------------------------------------------------------ // Private data diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 7ee55dec97..f755dc4755 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -37,10 +37,15 @@ public enum Mode [SerializeField] private GameObject m_SketchfabSignedInElements; [SerializeField] private GameObject m_SketchfabSignedOutElements; [SerializeField] private GameObject m_SketchfabConfirmSignOutElements; + [SerializeField] private GameObject m_IcosaSignedInElements; + [SerializeField] private GameObject m_IcosaSignedOutElements; + [SerializeField] private GameObject m_IcosaConfirmSignOutElements; [SerializeField] private Renderer m_GooglePhoto; [SerializeField] private Renderer m_SketchfabPhoto; + [SerializeField] private Renderer m_IcosaPhoto; [SerializeField] private TextMeshPro m_GoogleNameText; [SerializeField] private TextMeshPro m_SketchfabNameText; + [SerializeField] private TextMeshPro m_IcosaNameText; [SerializeField] private Texture2D m_GenericPhoto; [SerializeField] private GameObject m_Accounts; @@ -48,6 +53,7 @@ public enum Mode [SerializeField] private GameObject m_GoogleInfoElements; [SerializeField] private GameObject m_DriveInfoElements; [SerializeField] private GameObject m_SketchfabInfoElements; + [SerializeField] private GameObject m_IcosaInfoElements; [SerializeField] private GameObject m_UnavailableElements; [SerializeField] private GameObject m_DriveSyncEnabledElements; @@ -138,6 +144,18 @@ void RefreshObjects() m_SketchfabPhoto.material.mainTexture = sketchfabInfo.icon; } + // Icosa. + OAuth2Identity.UserInfo icosaInfo = App.IcosaIdentity.Profile; + bool icosaInfoValid = icosaInfo != null; + m_IcosaSignedInElements.SetActive(icosaInfoValid); + m_IcosaSignedOutElements.SetActive(!icosaInfoValid); + m_IcosaConfirmSignOutElements.SetActive(false); + if (icosaInfoValid) + { + m_IcosaNameText.text = icosaInfo.name; + m_IcosaPhoto.material.mainTexture = icosaInfo.icon; + } + m_DriveFullElements.SetActive(driveFull && driveSyncEnabled); m_DriveSyncEnabledElements.SetActive(!driveFull && driveSyncEnabled); m_DriveSyncDisabledElements.SetActive(!driveSyncEnabled); @@ -245,6 +263,11 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) m_SketchfabSignedOutElements.SetActive(false); m_SketchfabConfirmSignOutElements.SetActive(true); break; + case Cloud.Icosa: + m_IcosaSignedInElements.SetActive(false); + m_IcosaSignedOutElements.SetActive(false); + m_IcosaConfirmSignOutElements.SetActive(true); + break; case Cloud.None: break; } break; diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs new file mode 100644 index 0000000000..4b8aa67357 --- /dev/null +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -0,0 +1,405 @@ +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; +using UnityEngine; +using UnityEngine.Assertions; + +namespace TiltBrush +{ + class IcosaService + { + public const string kModelLandingPage = "https://sketchfab.com/3d-models/"; + const string kApiHost = "https://api.sketchfab.com"; + + /// A paginated response, for use with GetNextPageAsync() + public interface Paginated + { + /// URI to the next page of results, or null + string NextUri { get; } + /// URI to the previous page of results, or null + string PreviousUri { get; } + } + + // Classes named "Related" seem to be shared between multiple different response types. + // My guess is "related" is meant in the database-join sense of the word, and each of + // these is its own table in the backend. + + // XxxRelated classes are listed here in alphabetical order. + // Classes specific to an API call are found right after the API wrapper. + // This violates conventions but is more convenient. + + [Serializable, UsedImplicitly] + public class AvatarRelated + { + [Serializable, UsedImplicitly] + public class inline_model + { + public string url; + public int width; + public int height; + public int size; + } + public inline_model[] images; + public string uri; + } + + [Serializable, UsedImplicitly] + public class TagRelated + { + public string slug; + public string uri; + } + + [Serializable, UsedImplicitly] + public class ThumbnailsRelated + { + [Serializable, UsedImplicitly] + public class inline_model_2 + { + public string url; + public int width; + public int size; + public string uid; + public int height; + } + public inline_model_2[] images; + } + + [Serializable, UsedImplicitly] + public class UserRelated + { + public string username; + public string profileUrl; + public string account; + public string displayName; + public string uid; + public AvatarRelated[] avatars; + public string uri; + } + + /// Options are constructed according to: + /// https://docs.sketchfab.com/data-api/v3/index.html#!/models/patch_v3_models_uid_options + [Serializable, UsedImplicitly] + public class Options + { + // shading (string, optional), + public Dictionary background; + // orientation (string, optional), + + public void SetBackgroundColor(Color color) + { + background = new Dictionary(); + background["color"] = "#" + ColorUtility.ToHtmlStringRGB(color); + } + } + + private readonly OAuth2Identity m_identity; + + public IcosaService(OAuth2Identity identity) + { + m_identity = identity; + } + + /// Returns null if there is no next page. + /// It's assumed that you've used the results from the current page, meaning + /// that the task for fetching the current page must be completed. + public Task GetNextPageAsync(Task task) where T : Paginated + { + // Ensure task.Result does not block. + // If this function were async we could await, but then couldn't return null. + // In C# 8 this could be switched to use async enumerators. + if (!task.IsCompleted) + { + throw new ArgumentException("page"); + } + var uri = task.Result.NextUri; + if (string.IsNullOrEmpty(uri)) { return null; } + return new WebRequest(uri, m_identity, "GET") + .SendAsync() + .ContinueWith(antecedent => antecedent.Result.Deserialize()); + } + + public async Task GetUserInfo() + { + var result = await new WebRequest($"{kApiHost}/v3/me", m_identity, "GET").SendAsync(); + return result.Deserialize(); + } + [Serializable, UsedImplicitly] + public class MeDetail + { + // subscriptionCount (integer, optional), + // followerCount (integer, optional), + public string uid; + public string modelsUrl; + // likeCount (integer, optional), + // facebookUsername (string, optional), + // biography (string, optional), + // city (string, optional), + // tagline (string, optional), + public int modelCount; + // twitterUsername (string, optional), + public string email; + public string website; + // billingCycle (string, optional), + // followersUrl (string, optional), + // collectionCount (integer, optional), + // dateJoined (string, optional), + public string account; + public string displayName; + public string profileUrl; + // followingsUrl (string, optional), + // skills (Array[SkillDetail], optional), + // country (string, optional), + public string uri; + // apiToken (string, optional), + public string username; + // linkedinUsername (string, optional), + // likesUrl (string, optional), + public AvatarRelated avatar; + public bool isLimited; + // followingCount (integer, optional), + // collectionsUrl (string, optional) + } + + + /// Returns the metadata for a model. + /// See also GetModelDownload(). + public async Task GetModelDetail(string uid) + { + var result = await new WebRequest( + $"{kApiHost}/v3/models/{uid}", m_identity, "GET").SendAsync(); + return result.Deserialize(); + } + [Serializable, UsedImplicitly] + public class ModelDetail + { + [Serializable, UsedImplicitly] + public class File + { + public int wireframeSize; + public int flag; + public string version; + public int modelSize; + public string uri; + public int osgjsSize; + public JObject metadata; + } + public JObject status; + public File[] files; + public string uid; + public TagRelated[] tags; + public string viewerUrl; + // categories (Array[CategoriesRelated], optional), + public string publishedAt; + public int likeCount; + public int commentCount; + public int vertexCount; + public UserRelated user; + // animationCount (integer, optional), + public bool isDownloadable; + public string description; + // viewCount (integer, optional), + public string name; + public JObject license; // license (object, optional), + public string editorUrl; + // soundCount (integer, optional), + // isAgeRestricted (boolean, optional), + public string uri; + public int faceCount; + // ext (string,null, optional), + // staffpickedAt (string,null, optional), + // createdAt (string, optional), + public ThumbnailsRelated thumbnails; + // downloadCount (integer, optional), + // embedUrl (string, optional), + public JObject options; // options (object, optional) + } + + + /// Returns a temporary URL where a model can be downloaded from + public async Task GetModelDownload(string uid) + { + var result = await new WebRequest( + $"{kApiHost}/v3/models/{uid}/download", m_identity, "GET").SendAsync(); + return result.Deserialize(); + } + [Serializable, UsedImplicitly] + public class ModelDownload + { + [Serializable, UsedImplicitly] + public class inline_model_1 + { + public string url; // temporary URL where the archive can be downloaded + public int expires; // when the temporary URL will expire (in seconds) + } + // This is called "gltf" but it's actually just the .zip archive that was uploaded, I believe + public inline_model_1 gltf; + } + + + public Task GetMeLikes() + { + return new WebRequest($"{kApiHost}/v3/me/likes", m_identity, "GET") + .SendAsync() + .ContinueWith(antecedent => antecedent.Result.Deserialize()); + } + // Documentation for this API is incorrect, so this was created by inspection of the result + // (and therefore may itself have errors) + [Serializable, UsedImplicitly] + public class ModelLikesResponse : Paginated + { + [Serializable, UsedImplicitly] + public class ModelLikesList + { + public string uid; + // public TagRelated[] tags; + public string viewerUrl; + public bool isProtected; + // categories (Array[CategoriesRelated], optional), + // public string publishedAt; + // public int likeCount; + // public int commentCount; + // public int viewCount; + public int vertexCount; + public UserRelated user; + public bool isDownloadable; + public string description; + // public int animationCount; + public string name; + // public int soundCount; + // public bool isAgeRestricted; + public string uri; + public int faceCount; + // staffpickedAt (string,null, optional), + public ThumbnailsRelated thumbnails; + // public string embedUrl; + + // public CategoriesRelated categories; + // public string createdAt; + // public string license; // not documented and sometimes null? + // public ?string? price; + } + public string previous; // uri + public string next; // uri + public ModelLikesList[] results; + + string Paginated.NextUri => next; + string Paginated.PreviousUri => previous; + } + + // TODO: /v3/search and /v3/me/search? + // The parameters to the search functions are the same, except /v3/me/search omits + // the "username" parameter, so maybe we can have a single function which wraps both. + + // + /// Pass: + /// temporaryDirectory - if passed, caller is responsible for cleaning it up + public async Task CreateModel( + string name, + string zipPath, IProgress progress, CancellationToken token, + Options options = null, string temporaryDirectory = null) + { + + // No compression because it's a compressed .zip already + WebRequest uploader = new WebRequest( + $"{kApiHost}/v3/models", m_identity, "POST", compress: false); + + var moreParams = new List<(string, string)> + { + ("name", name), + ("source", "open-brush"), + ("private", "true"), // TODO: remove when this feature is not secret + // https://docs.sketchfab.com/data-api/v3/index.html#!/models/post_v3_models + // "Enables 2D view in model inspector. All downloadable models must have isInspectable + // enabled." + ("isInspectable", "true"), + // ??? https://docs.sketchfab.com/data-api/v3/index.html#!/licenses/get_v3_licenses + ("license", "by-sa"), + // This is how you specify multiple tags: + // ("tags", "[\"tiltbrush\", \"some-other-tag\"]"), + ("tags", "[\"openbrush\", \"tiltbrush\"]"), + ("isPublished", "false"), + // ("description", "Dummy description"), + }; + if (options != null) + { + moreParams.Add(("options", JsonConvert.SerializeObject(options))); + } + uploader.ProgressObject = progress; + var reply = await uploader.SendNamedDataAsync( + "modelFile", File.OpenRead(zipPath), Path.GetFileName(zipPath), "application/zip", + moreParams: moreParams, token, temporaryDirectory); + return reply.Deserialize(); + } + [Serializable, UsedImplicitly] + public struct CreateResponse + { + public string uid; + public string uri; + } + + public void TestUpload(LoginToken token, List files) + { + Configuration config = new Configuration(); + config.BasePath = "https://api.icosa.gallery"; + config.AccessToken = token.AccessToken; + var apiInstance = new AssetsApi(config); + + try + { + // Upload New Assets + var result = apiInstance.UploadNewAssetsAssetsPost(files); + Debug.Log(result); + } + catch (ApiException e) + { + Debug.Log("Exception when calling AssetsApi.UploadNewAssetsAssetsPost: " + e.Message); + Debug.Log("Status Code: " + e.ErrorCode); + Debug.Log(e.StackTrace); + } + } + + public LoginToken TestLogin() + { + Configuration config = new Configuration(); + config.BasePath = "https://api.icosa.gallery"; + var apiInstance = new LoginApi(config); + var username = "andy@andybak.net"; // string | + var password = "clavoi23"; // string | + + try + { + LoginToken result = apiInstance.LoginLoginPost(username, password, grantType, scope, clientId, clientSecret); + Debug.Log(result.AccessToken); + } + catch (ApiException e) + { + Debug.Log("Exception when calling LoginApi.LoginLoginPost: " + e.Message); + Debug.Log("Status Code: " + e.ErrorCode); + Debug.Log(e.StackTrace); + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/IcosaService.cs.meta b/Assets/Scripts/Sharing/IcosaService.cs.meta new file mode 100644 index 0000000000..d785c9b77b --- /dev/null +++ b/Assets/Scripts/Sharing/IcosaService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3115bd38f5444bc395b95fb15d02dfe0 +timeCreated: 1698675823 \ No newline at end of file diff --git a/Assets/Scripts/Sharing/OAuth2Identity.cs b/Assets/Scripts/Sharing/OAuth2Identity.cs index a9e66905c9..3f9940cd33 100644 --- a/Assets/Scripts/Sharing/OAuth2Identity.cs +++ b/Assets/Scripts/Sharing/OAuth2Identity.cs @@ -29,6 +29,8 @@ namespace TiltBrush { /// Handle accessing OAuth2 based web services. + /// Inaccurately named now as Icosa auth is going to + /// either be username/password or timed tokens public class OAuth2Identity : MonoBehaviour { diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 63e1e518c1..db808f93cb 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -35,7 +35,8 @@ public enum Cloud { None, Poly, - Sketchfab + Sketchfab, + Icosa } [Serializable] From 37bf5a55184d6d7da413bba29a7a608400e13007 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 6 Nov 2023 11:08:46 +0000 Subject: [PATCH 003/105] Updated API wrapper --- .../Org.OpenAPITools/Api/AssetsApi.cs | 204 ++++++++++++++---- .../Org.OpenAPITools/Api/LoginApi.cs | 179 +++++++++++++++ .../Org.OpenAPITools/Api/UsersApi.cs | 161 ++++++++++++++ .../Org.OpenAPITools/Model/DeviceCode.cs | 128 +++++++++++ .../DeviceCode.cs.meta} | 2 +- Assets/ThirdParty/Org.OpenAPITools/Tests.meta | 8 - .../Org.OpenAPITools/Tests/Api.meta | 8 - .../Tests/Api/AssetsApiTests.cs | 144 ------------- .../Tests/Api/AssetsApiTests.cs.meta | 11 - .../Tests/Api/LoginApiTests.cs | 73 ------- .../Tests/Api/LoginApiTests.cs.meta | 11 - .../Tests/Api/PolyApiTests.cs | 94 -------- .../Tests/Api/UsersApiTests.cs | 198 ----------------- .../Tests/Api/UsersApiTests.cs.meta | 11 - .../Org.OpenAPITools/Tests/Model.meta | 8 - .../Tests/Model/AssetFormatTests.cs | 90 -------- .../Tests/Model/AssetFormatTests.cs.meta | 11 - .../Tests/Model/AssetPatchDataTests.cs | 90 -------- .../Tests/Model/AssetPatchDataTests.cs.meta | 11 - .../Tests/Model/AssetTests.cs | 162 -------------- .../Tests/Model/AssetTests.cs.meta | 11 - .../Model/EmailChangeAuthenticatedTests.cs | 74 ------- .../EmailChangeAuthenticatedTests.cs.meta | 11 - .../Tests/Model/FullUserTests.cs | 98 --------- .../Tests/Model/FullUserTests.cs.meta | 11 - .../Tests/Model/HTTPValidationErrorTests.cs | 66 ------ .../Model/HTTPValidationErrorTests.cs.meta | 11 - .../Tests/Model/LoginTokenTests.cs | 74 ------- .../Tests/Model/LoginTokenTests.cs.meta | 11 - .../Tests/Model/NewUserTests.cs | 90 -------- .../Tests/Model/NewUserTests.cs.meta | 11 - .../Model/PasswordChangeAuthenticatedTests.cs | 74 ------- .../PasswordChangeAuthenticatedTests.cs.meta | 11 - .../Tests/Model/PasswordChangeTokenTests.cs | 74 ------- .../Model/PasswordChangeTokenTests.cs.meta | 11 - .../Tests/Model/PasswordResetTests.cs | 66 ------ .../Tests/Model/PasswordResetTests.cs.meta | 11 - .../Tests/Model/PatchUserTests.cs | 82 ------- .../Tests/Model/PatchUserTests.cs.meta | 11 - .../Tests/Model/PolyAssetTests.cs | 170 --------------- .../Tests/Model/PolyAssetTests.cs.meta | 11 - .../Tests/Model/PolyFormatComplexityTests.cs | 74 ------- .../Model/PolyFormatComplexityTests.cs.meta | 11 - .../Tests/Model/PolyFormatTests.cs | 90 -------- .../Tests/Model/PolyFormatTests.cs.meta | 11 - .../Tests/Model/PolyListTests.cs | 82 ------- .../Tests/Model/PolyListTests.cs.meta | 11 - .../Model/PolyPresentationParamsTests.cs | 82 ------- .../Model/PolyPresentationParamsTests.cs.meta | 11 - .../Tests/Model/PolyQuaternionTests.cs | 90 -------- .../Tests/Model/PolyQuaternionTests.cs.meta | 11 - .../Tests/Model/PolyRemixInfoTests.cs | 66 ------ .../Tests/Model/PolyRemixInfoTests.cs.meta | 11 - .../Tests/Model/PolyResourceTests.cs | 82 ------- .../Tests/Model/PolyResourceTests.cs.meta | 11 - .../Tests/Model/SubAssetFormatTests.cs | 82 ------- .../Tests/Model/SubAssetFormatTests.cs.meta | 11 - .../Org.OpenAPITools/Tests/Model/UserTests.cs | 82 ------- .../Tests/Model/UserTests.cs.meta | 11 - .../Tests/Model/ValidationErrorTests.cs | 82 ------- .../Tests/Model/ValidationErrorTests.cs.meta | 11 - .../Tests/Org.OpenAPITools.Test.asmdef | 15 -- .../Tests/Org.OpenAPITools.Test.asmdef.meta | 7 - 63 files changed, 633 insertions(+), 2904 deletions(-) create mode 100644 Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs rename Assets/ThirdParty/Org.OpenAPITools/{Tests/Api/PolyApiTests.cs.meta => Model/DeviceCode.cs.meta} (83%) delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef delete mode 100644 Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs index 1f2e0dec58..c394faa073 100644 --- a/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/AssetsApi.cs @@ -71,8 +71,11 @@ public interface IAssetsApiSync : IApiAccessor /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// List<Asset> - List GetAssetsAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)); + List GetAssetsAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?)); /// /// Get Assets @@ -84,8 +87,11 @@ public interface IAssetsApiSync : IApiAccessor /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// ApiResponse of List<Asset> - ApiResponse> GetAssetsAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)); + ApiResponse> GetAssetsAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?)); /// /// Get Id Asset /// @@ -127,9 +133,14 @@ public interface IAssetsApiSync : IApiAccessor /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// Asset - Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData assetPatchData); + Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?)); /// /// Update Asset @@ -139,9 +150,14 @@ public interface IAssetsApiSync : IApiAccessor /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// ApiResponse of Asset - ApiResponse UpdateAssetAssetsAssetPatchWithHttpInfo(int asset, AssetPatchData assetPatchData); + ApiResponse UpdateAssetAssetsAssetPatchWithHttpInfo(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?)); /// /// Upload New Assets /// @@ -227,9 +243,12 @@ public interface IAssetsApiAsync : IApiAccessor /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of List<Asset> - System.Threading.Tasks.Task> GetAssetsAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task> GetAssetsAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// /// Get Assets @@ -241,9 +260,12 @@ public interface IAssetsApiAsync : IApiAccessor /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of ApiResponse (List<Asset>) - System.Threading.Tasks.Task>> GetAssetsAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task>> GetAssetsAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// /// Get Id Asset /// @@ -298,10 +320,15 @@ public interface IAssetsApiAsync : IApiAccessor /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of Asset - System.Threading.Tasks.Task UpdateAssetAssetsAssetPatchAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task UpdateAssetAssetsAssetPatchAsync(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// /// Update Asset @@ -311,10 +338,15 @@ public interface IAssetsApiAsync : IApiAccessor /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of ApiResponse (Asset) - System.Threading.Tasks.Task> UpdateAssetAssetsAssetPatchWithHttpInfoAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task> UpdateAssetAssetsAssetPatchWithHttpInfoAsync(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// /// Upload New Assets /// @@ -771,10 +803,13 @@ public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlG /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// List<Asset> - public List GetAssetsAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)) + public List GetAssetsAssetsGet(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?)) { - Org.OpenAPITools.Client.ApiResponse> localVarResponse = GetAssetsAssetsGetWithHttpInfo(results, page, curated); + Org.OpenAPITools.Client.ApiResponse> localVarResponse = GetAssetsAssetsGetWithHttpInfo(results, page, curated, name, description, ownername); return localVarResponse.Data; } @@ -785,8 +820,11 @@ public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlG /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// ApiResponse of List<Asset> - public Org.OpenAPITools.Client.ApiResponse> GetAssetsAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?)) + public Org.OpenAPITools.Client.ApiResponse> GetAssetsAssetsGetWithHttpInfo(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?)) { Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); @@ -816,6 +854,18 @@ public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlG { localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "curated", curated)); } + if (name != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "name", name)); + } + if (description != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "description", description)); + } + if (ownername != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "ownername", ownername)); + } // make the HTTP request @@ -837,11 +887,14 @@ public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlG /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of List<Asset> - public async System.Threading.Tasks.Task> GetAssetsAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public async System.Threading.Tasks.Task> GetAssetsAssetsGetAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { - var task = GetAssetsAssetsGetWithHttpInfoAsync(results, page, curated, cancellationToken); + var task = GetAssetsAssetsGetWithHttpInfoAsync(results, page, curated, name, description, ownername, cancellationToken); #if UNITY_EDITOR || !UNITY_WEBGL Org.OpenAPITools.Client.ApiResponse> localVarResponse = await task.ConfigureAwait(false); #else @@ -857,9 +910,12 @@ public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlG /// (optional, default to 20) /// (optional, default to 0) /// (optional, default to false) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of ApiResponse (List<Asset>) - public async System.Threading.Tasks.Task>> GetAssetsAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public async System.Threading.Tasks.Task>> GetAssetsAssetsGetWithHttpInfoAsync(int? results = default(int?), int? page = default(int?), bool? curated = default(bool?), string? name = default(string?), string? description = default(string?), string? ownername = default(string?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); @@ -891,6 +947,18 @@ public Org.OpenAPITools.Client.ApiResponse GetAssetAssetsUserurlAsseturlG { localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "curated", curated)); } + if (name != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "name", name)); + } + if (description != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "description", description)); + } + if (ownername != null) + { + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "ownername", ownername)); + } // make the HTTP request @@ -1177,11 +1245,16 @@ public Org.OpenAPITools.Client.ApiResponse UnpublishAssetAssetsAssetUnpu /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// Asset - public Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData assetPatchData) + public Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?)) { - Org.OpenAPITools.Client.ApiResponse localVarResponse = UpdateAssetAssetsAssetPatchWithHttpInfo(asset, assetPatchData); + Org.OpenAPITools.Client.ApiResponse localVarResponse = UpdateAssetAssetsAssetPatchWithHttpInfo(asset, data, name, url, description, visibility, thumbnail); return localVarResponse.Data; } @@ -1190,18 +1263,19 @@ public Asset UpdateAssetAssetsAssetPatch(int asset, AssetPatchData assetPatchDat /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// ApiResponse of Asset - public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWithHttpInfo(int asset, AssetPatchData assetPatchData) + public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWithHttpInfo(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?)) { - // verify the required parameter 'assetPatchData' is set - if (assetPatchData == null) - throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'assetPatchData' when calling AssetsApi->UpdateAssetAssetsAssetPatch"); - Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); string[] _contentTypes = new string[] { - "application/json" + "multipart/form-data" }; // to determine the Accept header @@ -1216,7 +1290,29 @@ public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWit if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter - localVarRequestOptions.Data = assetPatchData; + if (data != null) + { + localVarRequestOptions.FormParameters.Add("data", Org.OpenAPITools.Client.ClientUtils.ParameterToString(data)); // form parameter + } + if (name != null) + { + localVarRequestOptions.FormParameters.Add("name", Org.OpenAPITools.Client.ClientUtils.ParameterToString(name)); // form parameter + } + if (url != null) + { + localVarRequestOptions.FormParameters.Add("url", Org.OpenAPITools.Client.ClientUtils.ParameterToString(url)); // form parameter + } + if (description != null) + { + localVarRequestOptions.FormParameters.Add("description", Org.OpenAPITools.Client.ClientUtils.ParameterToString(description)); // form parameter + } + if (visibility != null) + { + localVarRequestOptions.FormParameters.Add("visibility", Org.OpenAPITools.Client.ClientUtils.ParameterToString(visibility)); // form parameter + } + if (thumbnail != null) + { + } // authentication (OAuth2PasswordBearer) required // oauth required @@ -1242,12 +1338,17 @@ public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWit /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of Asset - public async System.Threading.Tasks.Task UpdateAssetAssetsAssetPatchAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public async System.Threading.Tasks.Task UpdateAssetAssetsAssetPatchAsync(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { - var task = UpdateAssetAssetsAssetPatchWithHttpInfoAsync(asset, assetPatchData, cancellationToken); + var task = UpdateAssetAssetsAssetPatchWithHttpInfoAsync(asset, data, name, url, description, visibility, thumbnail, cancellationToken); #if UNITY_EDITOR || !UNITY_WEBGL Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); #else @@ -1261,20 +1362,21 @@ public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWit /// /// Thrown when fails to make API call /// - /// + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) + /// (optional) /// Cancellation Token to cancel the request. /// Task of ApiResponse (Asset) - public async System.Threading.Tasks.Task> UpdateAssetAssetsAssetPatchWithHttpInfoAsync(int asset, AssetPatchData assetPatchData, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public async System.Threading.Tasks.Task> UpdateAssetAssetsAssetPatchWithHttpInfoAsync(int asset, AssetPatchData? data = default(AssetPatchData?), string? name = default(string?), string? url = default(string?), string? description = default(string?), string? visibility = default(string?), System.IO.Stream? thumbnail = default(System.IO.Stream?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { - // verify the required parameter 'assetPatchData' is set - if (assetPatchData == null) - throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'assetPatchData' when calling AssetsApi->UpdateAssetAssetsAssetPatch"); - Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); string[] _contentTypes = new string[] { - "application/json" + "multipart/form-data" }; // to determine the Accept header @@ -1290,7 +1392,29 @@ public Org.OpenAPITools.Client.ApiResponse UpdateAssetAssetsAssetPatchWit if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); localVarRequestOptions.PathParameters.Add("asset", Org.OpenAPITools.Client.ClientUtils.ParameterToString(asset)); // path parameter - localVarRequestOptions.Data = assetPatchData; + if (data != null) + { + localVarRequestOptions.FormParameters.Add("data", Org.OpenAPITools.Client.ClientUtils.ParameterToString(data)); // form parameter + } + if (name != null) + { + localVarRequestOptions.FormParameters.Add("name", Org.OpenAPITools.Client.ClientUtils.ParameterToString(name)); // form parameter + } + if (url != null) + { + localVarRequestOptions.FormParameters.Add("url", Org.OpenAPITools.Client.ClientUtils.ParameterToString(url)); // form parameter + } + if (description != null) + { + localVarRequestOptions.FormParameters.Add("description", Org.OpenAPITools.Client.ClientUtils.ParameterToString(description)); // form parameter + } + if (visibility != null) + { + localVarRequestOptions.FormParameters.Add("visibility", Org.OpenAPITools.Client.ClientUtils.ParameterToString(visibility)); // form parameter + } + if (thumbnail != null) + { + } // authentication (OAuth2PasswordBearer) required // oauth required diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs index 57cafff61f..4e56b143ea 100644 --- a/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/LoginApi.cs @@ -27,6 +27,24 @@ public interface ILoginApiSync : IApiAccessor { #region Synchronous Operations /// + /// Device Login + /// + /// Thrown when fails to make API call + /// + /// LoginToken + LoginToken DeviceLoginLoginDeviceLoginPost(string deviceCode); + + /// + /// Device Login + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of LoginToken + ApiResponse DeviceLoginLoginDeviceLoginPostWithHttpInfo(string deviceCode); + /// /// Login /// /// Thrown when fails to make API call @@ -64,6 +82,29 @@ public interface ILoginApiAsync : IApiAccessor { #region Asynchronous Operations /// + /// Device Login + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of LoginToken + System.Threading.Tasks.Task DeviceLoginLoginDeviceLoginPostAsync(string deviceCode, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Device Login + /// + /// + /// + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (LoginToken) + System.Threading.Tasks.Task> DeviceLoginLoginDeviceLoginPostWithHttpInfoAsync(string deviceCode, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// /// Login /// /// @@ -240,6 +281,144 @@ public Org.OpenAPITools.Client.ExceptionFactory ExceptionFactory set { _exceptionFactory = value; } } + /// + /// Device Login + /// + /// Thrown when fails to make API call + /// + /// LoginToken + public LoginToken DeviceLoginLoginDeviceLoginPost(string deviceCode) + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = DeviceLoginLoginDeviceLoginPostWithHttpInfo(deviceCode); + return localVarResponse.Data; + } + + /// + /// Device Login + /// + /// Thrown when fails to make API call + /// + /// ApiResponse of LoginToken + public Org.OpenAPITools.Client.ApiResponse DeviceLoginLoginDeviceLoginPostWithHttpInfo(string deviceCode) + { + // verify the required parameter 'deviceCode' is set + if (deviceCode == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'deviceCode' when calling LoginApi->DeviceLoginLoginDeviceLoginPost"); + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "device_code", deviceCode)); + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Post("/login/device_login", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("DeviceLoginLoginDeviceLoginPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Device Login + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of LoginToken + public async System.Threading.Tasks.Task DeviceLoginLoginDeviceLoginPostAsync(string deviceCode, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = DeviceLoginLoginDeviceLoginPostWithHttpInfoAsync(deviceCode, cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Device Login + /// + /// Thrown when fails to make API call + /// + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (LoginToken) + public async System.Threading.Tasks.Task> DeviceLoginLoginDeviceLoginPostWithHttpInfoAsync(string deviceCode, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + // verify the required parameter 'deviceCode' is set + if (deviceCode == null) + throw new Org.OpenAPITools.Client.ApiException(400, "Missing required parameter 'deviceCode' when calling LoginApi->DeviceLoginLoginDeviceLoginPost"); + + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + localVarRequestOptions.QueryParameters.Add(Org.OpenAPITools.Client.ClientUtils.ParameterToMultiMap("", "device_code", deviceCode)); + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.PostAsync("/login/device_login", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("DeviceLoginLoginDeviceLoginPost", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + /// /// Login /// diff --git a/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs b/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs index 48d627d966..1ce7cacc50 100644 --- a/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs +++ b/Assets/ThirdParty/Org.OpenAPITools/Api/UsersApi.cs @@ -187,6 +187,22 @@ public interface IUsersApiSync : IApiAccessor /// ApiResponse of User ApiResponse GetUserUsersUserGetWithHttpInfo(string user); /// + /// Get Users Device Code + /// + /// Thrown when fails to make API call + /// DeviceCode + DeviceCode GetUsersDeviceCodeUsersMeDevicecodeGet(); + + /// + /// Get Users Device Code + /// + /// + /// + /// + /// Thrown when fails to make API call + /// ApiResponse of DeviceCode + ApiResponse GetUsersDeviceCodeUsersMeDevicecodeGetWithHttpInfo(); + /// /// Get Users Me /// /// Thrown when fails to make API call @@ -453,6 +469,27 @@ public interface IUsersApiAsync : IApiAccessor /// Task of ApiResponse (User) System.Threading.Tasks.Task> GetUserUsersUserGetWithHttpInfoAsync(string user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// + /// Get Users Device Code + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of DeviceCode + System.Threading.Tasks.Task GetUsersDeviceCodeUsersMeDevicecodeGetAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + + /// + /// Get Users Device Code + /// + /// + /// + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (DeviceCode) + System.Threading.Tasks.Task> GetUsersDeviceCodeUsersMeDevicecodeGetWithHttpInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + /// /// Get Users Me /// /// @@ -1835,6 +1872,130 @@ public Org.OpenAPITools.Client.ApiResponse GetUserUsersUserGetWithHttpInfo return localVarResponse; } + /// + /// Get Users Device Code + /// + /// Thrown when fails to make API call + /// DeviceCode + public DeviceCode GetUsersDeviceCodeUsersMeDevicecodeGet() + { + Org.OpenAPITools.Client.ApiResponse localVarResponse = GetUsersDeviceCodeUsersMeDevicecodeGetWithHttpInfo(); + return localVarResponse.Data; + } + + /// + /// Get Users Device Code + /// + /// Thrown when fails to make API call + /// ApiResponse of DeviceCode + public Org.OpenAPITools.Client.ApiResponse GetUsersDeviceCodeUsersMeDevicecodeGetWithHttpInfo() + { + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + var localVarResponse = this.Client.Get("/users/me/devicecode", localVarRequestOptions, this.Configuration); + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUsersDeviceCodeUsersMeDevicecodeGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + + /// + /// Get Users Device Code + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of DeviceCode + public async System.Threading.Tasks.Task GetUsersDeviceCodeUsersMeDevicecodeGetAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + var task = GetUsersDeviceCodeUsersMeDevicecodeGetWithHttpInfoAsync(cancellationToken); +#if UNITY_EDITOR || !UNITY_WEBGL + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task.ConfigureAwait(false); +#else + Org.OpenAPITools.Client.ApiResponse localVarResponse = await task; +#endif + return localVarResponse.Data; + } + + /// + /// Get Users Device Code + /// + /// Thrown when fails to make API call + /// Cancellation Token to cancel the request. + /// Task of ApiResponse (DeviceCode) + public async System.Threading.Tasks.Task> GetUsersDeviceCodeUsersMeDevicecodeGetWithHttpInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + { + + Org.OpenAPITools.Client.RequestOptions localVarRequestOptions = new Org.OpenAPITools.Client.RequestOptions(); + + string[] _contentTypes = new string[] { + }; + + // to determine the Accept header + string[] _accepts = new string[] { + "application/json" + }; + + + var localVarContentType = Org.OpenAPITools.Client.ClientUtils.SelectHeaderContentType(_contentTypes); + if (localVarContentType != null) localVarRequestOptions.HeaderParameters.Add("Content-Type", localVarContentType); + + var localVarAccept = Org.OpenAPITools.Client.ClientUtils.SelectHeaderAccept(_accepts); + if (localVarAccept != null) localVarRequestOptions.HeaderParameters.Add("Accept", localVarAccept); + + + // authentication (OAuth2PasswordBearer) required + // oauth required + if (!string.IsNullOrEmpty(this.Configuration.AccessToken) && !localVarRequestOptions.HeaderParameters.ContainsKey("Authorization")) + { + localVarRequestOptions.HeaderParameters.Add("Authorization", "Bearer " + this.Configuration.AccessToken); + } + + // make the HTTP request + + var task = this.AsynchronousClient.GetAsync("/users/me/devicecode", localVarRequestOptions, this.Configuration, cancellationToken); + +#if UNITY_EDITOR || !UNITY_WEBGL + var localVarResponse = await task.ConfigureAwait(false); +#else + var localVarResponse = await task; +#endif + + if (this.ExceptionFactory != null) + { + Exception _exception = this.ExceptionFactory("GetUsersDeviceCodeUsersMeDevicecodeGet", localVarResponse); + if (_exception != null) throw _exception; + } + + return localVarResponse; + } + /// /// Get Users Me /// diff --git a/Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs b/Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs new file mode 100644 index 0000000000..01eb19b9fd --- /dev/null +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs @@ -0,0 +1,128 @@ +/* + * Icosa API + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * Generated by: https://github.com/openapitools/openapi-generator.git + */ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using OpenAPIDateConverter = Org.OpenAPITools.Client.OpenAPIDateConverter; + +namespace Org.OpenAPITools.Model +{ + /// + /// DeviceCode + /// + [DataContract(Name = "DeviceCode")] + public partial class DeviceCode : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected DeviceCode() { } + /// + /// Initializes a new instance of the class. + /// + /// varDeviceCode (required). + public DeviceCode(string varDeviceCode = default(string)) + { + // to ensure "varDeviceCode" is required (not null) + if (varDeviceCode == null) + { + throw new ArgumentNullException("varDeviceCode is a required property for DeviceCode and cannot be null"); + } + this.VarDeviceCode = varDeviceCode; + } + + /// + /// Gets or Sets VarDeviceCode + /// + [DataMember(Name = "deviceCode", IsRequired = true, EmitDefaultValue = true)] + public string VarDeviceCode { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class DeviceCode {\n"); + sb.Append(" VarDeviceCode: ").Append(VarDeviceCode).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as DeviceCode); + } + + /// + /// Returns true if DeviceCode instances are equal + /// + /// Instance of DeviceCode to be compared + /// Boolean + public bool Equals(DeviceCode input) + { + if (input == null) + { + return false; + } + return + ( + this.VarDeviceCode == input.VarDeviceCode || + (this.VarDeviceCode != null && + this.VarDeviceCode.Equals(input.VarDeviceCode)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.VarDeviceCode != null) + { + hashCode = (hashCode * 59) + this.VarDeviceCode.GetHashCode(); + } + return hashCode; + } + } + + } + +} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs.meta similarity index 83% rename from Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta rename to Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs.meta index e1c80cf337..31f77abbe7 100644 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs.meta +++ b/Assets/ThirdParty/Org.OpenAPITools/Model/DeviceCode.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: edbc76f90365d4c1ba41519350fab72f +guid: a2f16d6e33569435d9b4e19f51b62e70 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests.meta deleted file mode 100644 index 35a518dfbd..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 76fea13e2a69148239baa3b4a512702d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta deleted file mode 100644 index 0249cb9775..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: fe2cb35fcf1454aacb9e29068b989379 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs deleted file mode 100644 index c4961edaea..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using NUnit.Framework; - -using Org.OpenAPITools.Client; -using Org.OpenAPITools.Api; -// uncomment below to import models -//using Org.OpenAPITools.Model; - -namespace Org.OpenAPITools.Test.Api -{ - /// - /// Class for testing AssetsApi - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the API endpoint. - /// - public class AssetsApiTests : IDisposable - { - private AssetsApi instance; - - public AssetsApiTests() - { - instance = new AssetsApi(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of AssetsApi - /// - [Test] - public void InstanceTest() - { - // TODO uncomment below to test 'IsType' AssetsApi - //Assert.IsType(instance); - } - - /// - /// Test DeleteAssetAssetsAssetDelete - /// - [Test] - public void DeleteAssetAssetsAssetDeleteTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int asset = null; - //var response = instance.DeleteAssetAssetsAssetDelete(asset); - //Assert.IsType(response); - } - - /// - /// Test GetAssetAssetsUserurlAsseturlGet - /// - [Test] - public void GetAssetAssetsUserurlAsseturlGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //string userurl = null; - //string asseturl = null; - //var response = instance.GetAssetAssetsUserurlAsseturlGet(userurl, asseturl); - //Assert.IsType(response); - } - - /// - /// Test GetAssetsAssetsGet - /// - [Test] - public void GetAssetsAssetsGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int? results = null; - //int? page = null; - //bool? curated = null; - //var response = instance.GetAssetsAssetsGet(results, page, curated); - //Assert.IsType>(response); - } - - /// - /// Test GetIdAssetAssetsIdAssetGet - /// - [Test] - public void GetIdAssetAssetsIdAssetGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int asset = null; - //var response = instance.GetIdAssetAssetsIdAssetGet(asset); - //Assert.IsType(response); - } - - /// - /// Test UnpublishAssetAssetsAssetUnpublishPatch - /// - [Test] - public void UnpublishAssetAssetsAssetUnpublishPatchTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int asset = null; - //var response = instance.UnpublishAssetAssetsAssetUnpublishPatch(asset); - //Assert.IsType(response); - } - - /// - /// Test UpdateAssetAssetsAssetPatch - /// - [Test] - public void UpdateAssetAssetsAssetPatchTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int asset = null; - //AssetPatchData assetPatchData = null; - //var response = instance.UpdateAssetAssetsAssetPatch(asset, assetPatchData); - //Assert.IsType(response); - } - - /// - /// Test UploadNewAssetsAssetsPost - /// - [Test] - public void UploadNewAssetsAssetsPostTest() - { - // TODO uncomment below to test the method and replace null with proper value - //List files = null; - //var response = instance.UploadNewAssetsAssetsPost(files); - //Assert.IsType(response); - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta deleted file mode 100644 index 9521ea547c..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/AssetsApiTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: bb3d21165a6034f5688e8016013decff -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs deleted file mode 100644 index d4cf2dc49a..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using NUnit.Framework; - -using Org.OpenAPITools.Client; -using Org.OpenAPITools.Api; -// uncomment below to import models -//using Org.OpenAPITools.Model; - -namespace Org.OpenAPITools.Test.Api -{ - /// - /// Class for testing LoginApi - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the API endpoint. - /// - public class LoginApiTests : IDisposable - { - private LoginApi instance; - - public LoginApiTests() - { - instance = new LoginApi(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of LoginApi - /// - [Test] - public void InstanceTest() - { - // TODO uncomment below to test 'IsType' LoginApi - //Assert.IsType(instance); - } - - /// - /// Test LoginLoginPost - /// - [Test] - public void LoginLoginPostTest() - { - // TODO uncomment below to test the method and replace null with proper value - string username = null; - string password = null; - string? grantType = null; - string? scope = null; - string? clientId = null; - string? clientSecret = null; - var response = instance.LoginLoginPost(username, password, grantType, scope, clientId, clientSecret); - Assert.IsType(response); - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta deleted file mode 100644 index 8a1d54fe74..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/LoginApiTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5ec43ebaef8d44df8b21ae9a1e06f1a0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs deleted file mode 100644 index 7bd2dc24f3..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/PolyApiTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using NUnit.Framework; - -using Org.OpenAPITools.Client; -using Org.OpenAPITools.Api; -// uncomment below to import models -//using Org.OpenAPITools.Model; - -namespace Org.OpenAPITools.Test.Api -{ - /// - /// Class for testing PolyApi - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the API endpoint. - /// - public class PolyApiTests : IDisposable - { - private PolyApi instance; - - public PolyApiTests() - { - instance = new PolyApi(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyApi - /// - [Test] - public void InstanceTest() - { - // TODO uncomment below to test 'IsType' PolyApi - //Assert.IsType(instance); - } - - /// - /// Test GetPolyAssetPolyAssetsAssetGet - /// - [Test] - public void GetPolyAssetPolyAssetsAssetGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //string asset = null; - //var response = instance.GetPolyAssetPolyAssetsAssetGet(asset); - //Assert.IsType(response); - } - - /// - /// Test GetPolyAssetsListPolyAssetsGet - /// - [Test] - public void GetPolyAssetsListPolyAssetsGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int? results = null; - //int? page = null; - //bool? curated = null; - //var response = instance.GetPolyAssetsListPolyAssetsGet(results, page, curated); - //Assert.IsType(response); - } - - /// - /// Test ImportPolyDataPolyImportPost - /// - [Test] - public void ImportPolyDataPolyImportPostTest() - { - // TODO uncomment below to test the method and replace null with proper value - //List requestBody = null; - //var response = instance.ImportPolyDataPolyImportPost(requestBody); - //Assert.IsType(response); - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs deleted file mode 100644 index e134f6575d..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using NUnit.Framework; - -using Org.OpenAPITools.Client; -using Org.OpenAPITools.Api; -// uncomment below to import models -//using Org.OpenAPITools.Model; - -namespace Org.OpenAPITools.Test.Api -{ - /// - /// Class for testing UsersApi - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the API endpoint. - /// - public class UsersApiTests : IDisposable - { - private UsersApi instance; - - public UsersApiTests() - { - instance = new UsersApi(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of UsersApi - /// - [Test] - public void InstanceTest() - { - // TODO uncomment below to test 'IsType' UsersApi - //Assert.IsType(instance); - } - - /// - /// Test ChangeAuthenticatedUserEmailUsersMeEmailPatch - /// - [Test] - public void ChangeAuthenticatedUserEmailUsersMeEmailPatchTest() - { - // TODO uncomment below to test the method and replace null with proper value - //EmailChangeAuthenticated emailChangeAuthenticated = null; - //var response = instance.ChangeAuthenticatedUserEmailUsersMeEmailPatch(emailChangeAuthenticated); - //Assert.IsType(response); - } - - /// - /// Test ChangeAuthenticatedUserPasswordUsersMePasswordPatch - /// - [Test] - public void ChangeAuthenticatedUserPasswordUsersMePasswordPatchTest() - { - // TODO uncomment below to test the method and replace null with proper value - //PasswordChangeAuthenticated passwordChangeAuthenticated = null; - //var response = instance.ChangeAuthenticatedUserPasswordUsersMePasswordPatch(passwordChangeAuthenticated); - //Assert.IsType(response); - } - - /// - /// Test ChangePasswordViaTokenUsersPasswordPatch - /// - [Test] - public void ChangePasswordViaTokenUsersPasswordPatchTest() - { - // TODO uncomment below to test the method and replace null with proper value - //PasswordChangeToken passwordChangeToken = null; - //var response = instance.ChangePasswordViaTokenUsersPasswordPatch(passwordChangeToken); - //Assert.IsType(response); - } - - /// - /// Test CreateUserUsersPost - /// - [Test] - public void CreateUserUsersPostTest() - { - // TODO uncomment below to test the method and replace null with proper value - //NewUser newUser = null; - //var response = instance.CreateUserUsersPost(newUser); - //Assert.IsType(response); - } - - /// - /// Test GetIdUserAssetsUsersIdUserAssetsGet - /// - [Test] - public void GetIdUserAssetsUsersIdUserAssetsGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int user = null; - //var response = instance.GetIdUserAssetsUsersIdUserAssetsGet(user); - //Assert.IsType>(response); - } - - /// - /// Test GetMeAssetsUsersMeAssetsGet - /// - [Test] - public void GetMeAssetsUsersMeAssetsGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //var response = instance.GetMeAssetsUsersMeAssetsGet(); - //Assert.IsType>(response); - } - - /// - /// Test GetUserAssetsUsersUserAssetsGet - /// - [Test] - public void GetUserAssetsUsersUserAssetsGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //string user = null; - //var response = instance.GetUserAssetsUsersUserAssetsGet(user); - //Assert.IsType>(response); - } - - /// - /// Test GetUserUsersIdUserGet - /// - [Test] - public void GetUserUsersIdUserGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //int user = null; - //var response = instance.GetUserUsersIdUserGet(user); - //Assert.IsType(response); - } - - /// - /// Test GetUserUsersUserGet - /// - [Test] - public void GetUserUsersUserGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //string user = null; - //var response = instance.GetUserUsersUserGet(user); - //Assert.IsType(response); - } - - /// - /// Test GetUsersMeUsersMeGet - /// - [Test] - public void GetUsersMeUsersMeGetTest() - { - // TODO uncomment below to test the method and replace null with proper value - //var response = instance.GetUsersMeUsersMeGet(); - //Assert.IsType(response); - } - - /// - /// Test UpdateUserUsersMePatch - /// - [Test] - public void UpdateUserUsersMePatchTest() - { - // TODO uncomment below to test the method and replace null with proper value - //PatchUser patchUser = null; - //var response = instance.UpdateUserUsersMePatch(patchUser); - //Assert.IsType(response); - } - - /// - /// Test UserPasswordResetRequestUsersPasswordResetPut - /// - [Test] - public void UserPasswordResetRequestUsersPasswordResetPutTest() - { - // TODO uncomment below to test the method and replace null with proper value - //PasswordReset passwordReset = null; - //var response = instance.UserPasswordResetRequestUsersPasswordResetPut(passwordReset); - //Assert.IsType(response); - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta deleted file mode 100644 index 15887be082..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Api/UsersApiTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c16251192a0b54a7ea0cc6c83dc98491 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta deleted file mode 100644 index 8ec0c0f7d0..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7b9b82320374c4da584071d18ce37e1c -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs deleted file mode 100644 index 570cf24378..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing AssetFormat - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class AssetFormatTests : IDisposable - { - // TODO uncomment below to declare an instance variable for AssetFormat - //private AssetFormat instance; - - public AssetFormatTests() - { - // TODO uncomment below to create an instance of AssetFormat - //instance = new AssetFormat(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of AssetFormat - /// - [Test] - public void AssetFormatInstanceTest() - { - // TODO uncomment below to test "IsType" AssetFormat - //Assert.IsType(instance); - } - - /// - /// Test the property 'Id' - /// - [Test] - public void IdTest() - { - // TODO unit test for the property 'Id' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Format' - /// - [Test] - public void FormatTest() - { - // TODO unit test for the property 'Format' - } - /// - /// Test the property 'Subfiles' - /// - [Test] - public void SubfilesTest() - { - // TODO unit test for the property 'Subfiles' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta deleted file mode 100644 index ea8afae624..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetFormatTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 90bec396919c74a0aadf13caad47ab8b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs deleted file mode 100644 index ec1f94a780..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing AssetPatchData - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class AssetPatchDataTests : IDisposable - { - // TODO uncomment below to declare an instance variable for AssetPatchData - //private AssetPatchData instance; - - public AssetPatchDataTests() - { - // TODO uncomment below to create an instance of AssetPatchData - //instance = new AssetPatchData(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of AssetPatchData - /// - [Test] - public void AssetPatchDataInstanceTest() - { - // TODO uncomment below to test "IsType" AssetPatchData - //Assert.IsType(instance); - } - - /// - /// Test the property 'Name' - /// - [Test] - public void NameTest() - { - // TODO unit test for the property 'Name' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Description' - /// - [Test] - public void DescriptionTest() - { - // TODO unit test for the property 'Description' - } - /// - /// Test the property 'Visibility' - /// - [Test] - public void VisibilityTest() - { - // TODO unit test for the property 'Visibility' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta deleted file mode 100644 index 016b7507b1..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetPatchDataTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 21413f6baa7b84a929111af5b3e894eb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs deleted file mode 100644 index 3dc7672d2d..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing Asset - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class AssetTests : IDisposable - { - // TODO uncomment below to declare an instance variable for Asset - //private Asset instance; - - public AssetTests() - { - // TODO uncomment below to create an instance of Asset - //instance = new Asset(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of Asset - /// - [Test] - public void AssetInstanceTest() - { - // TODO uncomment below to test "IsType" Asset - //Assert.IsType(instance); - } - - /// - /// Test the property 'Id' - /// - [Test] - public void IdTest() - { - // TODO unit test for the property 'Id' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Formats' - /// - [Test] - public void FormatsTest() - { - // TODO unit test for the property 'Formats' - } - /// - /// Test the property 'Name' - /// - [Test] - public void NameTest() - { - // TODO unit test for the property 'Name' - } - /// - /// Test the property 'Description' - /// - [Test] - public void DescriptionTest() - { - // TODO unit test for the property 'Description' - } - /// - /// Test the property 'Owner' - /// - [Test] - public void OwnerTest() - { - // TODO unit test for the property 'Owner' - } - /// - /// Test the property 'Visibility' - /// - [Test] - public void VisibilityTest() - { - // TODO unit test for the property 'Visibility' - } - /// - /// Test the property 'Curated' - /// - [Test] - public void CuratedTest() - { - // TODO unit test for the property 'Curated' - } - /// - /// Test the property 'Polyid' - /// - [Test] - public void PolyidTest() - { - // TODO unit test for the property 'Polyid' - } - /// - /// Test the property 'Polydata' - /// - [Test] - public void PolydataTest() - { - // TODO unit test for the property 'Polydata' - } - /// - /// Test the property 'Thumbnail' - /// - [Test] - public void ThumbnailTest() - { - // TODO unit test for the property 'Thumbnail' - } - /// - /// Test the property 'Ownername' - /// - [Test] - public void OwnernameTest() - { - // TODO unit test for the property 'Ownername' - } - /// - /// Test the property 'Ownerurl' - /// - [Test] - public void OwnerurlTest() - { - // TODO unit test for the property 'Ownerurl' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta deleted file mode 100644 index c55ed14e39..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/AssetTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dcde69cf840424b6fa07b231b32c5367 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs deleted file mode 100644 index 9b6ff0c645..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing EmailChangeAuthenticated - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class EmailChangeAuthenticatedTests : IDisposable - { - // TODO uncomment below to declare an instance variable for EmailChangeAuthenticated - //private EmailChangeAuthenticated instance; - - public EmailChangeAuthenticatedTests() - { - // TODO uncomment below to create an instance of EmailChangeAuthenticated - //instance = new EmailChangeAuthenticated(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of EmailChangeAuthenticated - /// - [Test] - public void EmailChangeAuthenticatedInstanceTest() - { - // TODO uncomment below to test "IsType" EmailChangeAuthenticated - //Assert.IsType(instance); - } - - /// - /// Test the property 'NewEmail' - /// - [Test] - public void NewEmailTest() - { - // TODO unit test for the property 'NewEmail' - } - /// - /// Test the property 'CurrentPassword' - /// - [Test] - public void CurrentPasswordTest() - { - // TODO unit test for the property 'CurrentPassword' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta deleted file mode 100644 index a33814c3f3..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/EmailChangeAuthenticatedTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6e474606cd14a448ab50f295b9b3ab23 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs deleted file mode 100644 index 649472daf7..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing FullUser - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class FullUserTests : IDisposable - { - // TODO uncomment below to declare an instance variable for FullUser - //private FullUser instance; - - public FullUserTests() - { - // TODO uncomment below to create an instance of FullUser - //instance = new FullUser(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of FullUser - /// - [Test] - public void FullUserInstanceTest() - { - // TODO uncomment below to test "IsType" FullUser - //Assert.IsType(instance); - } - - /// - /// Test the property 'Id' - /// - [Test] - public void IdTest() - { - // TODO unit test for the property 'Id' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Email' - /// - [Test] - public void EmailTest() - { - // TODO unit test for the property 'Email' - } - /// - /// Test the property 'Displayname' - /// - [Test] - public void DisplaynameTest() - { - // TODO unit test for the property 'Displayname' - } - /// - /// Test the property 'Description' - /// - [Test] - public void DescriptionTest() - { - // TODO unit test for the property 'Description' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta deleted file mode 100644 index e61b6638ed..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/FullUserTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 753dd804aa9e842b4bd43a6e05a244bf -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs deleted file mode 100644 index a8f0d67c68..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing HTTPValidationError - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class HTTPValidationErrorTests : IDisposable - { - // TODO uncomment below to declare an instance variable for HTTPValidationError - //private HTTPValidationError instance; - - public HTTPValidationErrorTests() - { - // TODO uncomment below to create an instance of HTTPValidationError - //instance = new HTTPValidationError(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of HTTPValidationError - /// - [Test] - public void HTTPValidationErrorInstanceTest() - { - // TODO uncomment below to test "IsType" HTTPValidationError - //Assert.IsType(instance); - } - - /// - /// Test the property 'Detail' - /// - [Test] - public void DetailTest() - { - // TODO unit test for the property 'Detail' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta deleted file mode 100644 index e95a9f9b01..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/HTTPValidationErrorTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9f89cb15531894ebd98e8d5b1457caee -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs deleted file mode 100644 index dbefcf5969..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing LoginToken - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class LoginTokenTests : IDisposable - { - // TODO uncomment below to declare an instance variable for LoginToken - //private LoginToken instance; - - public LoginTokenTests() - { - // TODO uncomment below to create an instance of LoginToken - //instance = new LoginToken(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of LoginToken - /// - [Test] - public void LoginTokenInstanceTest() - { - // TODO uncomment below to test "IsType" LoginToken - //Assert.IsType(instance); - } - - /// - /// Test the property 'AccessToken' - /// - [Test] - public void AccessTokenTest() - { - // TODO unit test for the property 'AccessToken' - } - /// - /// Test the property 'TokenType' - /// - [Test] - public void TokenTypeTest() - { - // TODO unit test for the property 'TokenType' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta deleted file mode 100644 index 2434cd6ee2..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/LoginTokenTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2b831bce3e59d44f2877205d2ae8ac58 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs deleted file mode 100644 index bd0e7a0db1..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing NewUser - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class NewUserTests : IDisposable - { - // TODO uncomment below to declare an instance variable for NewUser - //private NewUser instance; - - public NewUserTests() - { - // TODO uncomment below to create an instance of NewUser - //instance = new NewUser(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of NewUser - /// - [Test] - public void NewUserInstanceTest() - { - // TODO uncomment below to test "IsType" NewUser - //Assert.IsType(instance); - } - - /// - /// Test the property 'Email' - /// - [Test] - public void EmailTest() - { - // TODO unit test for the property 'Email' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Password' - /// - [Test] - public void PasswordTest() - { - // TODO unit test for the property 'Password' - } - /// - /// Test the property 'DisplayName' - /// - [Test] - public void DisplayNameTest() - { - // TODO unit test for the property 'DisplayName' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta deleted file mode 100644 index 44750444bb..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/NewUserTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8c65fd1bd7ac64bcebc8e7c094834050 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs deleted file mode 100644 index 7e2c34bcd8..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PasswordChangeAuthenticated - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PasswordChangeAuthenticatedTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PasswordChangeAuthenticated - //private PasswordChangeAuthenticated instance; - - public PasswordChangeAuthenticatedTests() - { - // TODO uncomment below to create an instance of PasswordChangeAuthenticated - //instance = new PasswordChangeAuthenticated(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PasswordChangeAuthenticated - /// - [Test] - public void PasswordChangeAuthenticatedInstanceTest() - { - // TODO uncomment below to test "IsType" PasswordChangeAuthenticated - //Assert.IsType(instance); - } - - /// - /// Test the property 'OldPassword' - /// - [Test] - public void OldPasswordTest() - { - // TODO unit test for the property 'OldPassword' - } - /// - /// Test the property 'NewPassword' - /// - [Test] - public void NewPasswordTest() - { - // TODO unit test for the property 'NewPassword' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta deleted file mode 100644 index df9b85dbae..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeAuthenticatedTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 530bd307703124cbd85667b99e5a0bf9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs deleted file mode 100644 index 05ac223cb4..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PasswordChangeToken - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PasswordChangeTokenTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PasswordChangeToken - //private PasswordChangeToken instance; - - public PasswordChangeTokenTests() - { - // TODO uncomment below to create an instance of PasswordChangeToken - //instance = new PasswordChangeToken(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PasswordChangeToken - /// - [Test] - public void PasswordChangeTokenInstanceTest() - { - // TODO uncomment below to test "IsType" PasswordChangeToken - //Assert.IsType(instance); - } - - /// - /// Test the property 'Token' - /// - [Test] - public void TokenTest() - { - // TODO unit test for the property 'Token' - } - /// - /// Test the property 'NewPassword' - /// - [Test] - public void NewPasswordTest() - { - // TODO unit test for the property 'NewPassword' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta deleted file mode 100644 index 1d1ba6b5fa..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordChangeTokenTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9f341d16587e149bfbb399526f04f943 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs deleted file mode 100644 index 78ca2f25a8..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PasswordReset - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PasswordResetTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PasswordReset - //private PasswordReset instance; - - public PasswordResetTests() - { - // TODO uncomment below to create an instance of PasswordReset - //instance = new PasswordReset(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PasswordReset - /// - [Test] - public void PasswordResetInstanceTest() - { - // TODO uncomment below to test "IsType" PasswordReset - //Assert.IsType(instance); - } - - /// - /// Test the property 'Email' - /// - [Test] - public void EmailTest() - { - // TODO unit test for the property 'Email' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta deleted file mode 100644 index af3223a88d..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PasswordResetTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3ed351f8defe2420ebb0f49b8e9e0df8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs deleted file mode 100644 index e3faf625a0..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PatchUser - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PatchUserTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PatchUser - //private PatchUser instance; - - public PatchUserTests() - { - // TODO uncomment below to create an instance of PatchUser - //instance = new PatchUser(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PatchUser - /// - [Test] - public void PatchUserInstanceTest() - { - // TODO uncomment below to test "IsType" PatchUser - //Assert.IsType(instance); - } - - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Displayname' - /// - [Test] - public void DisplaynameTest() - { - // TODO unit test for the property 'Displayname' - } - /// - /// Test the property 'Description' - /// - [Test] - public void DescriptionTest() - { - // TODO unit test for the property 'Description' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta deleted file mode 100644 index 3513fc9b0f..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PatchUserTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 43c1979575112454d88d4b354df81b13 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs deleted file mode 100644 index f678cf7cf6..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyAsset - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyAssetTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyAsset - //private PolyAsset instance; - - public PolyAssetTests() - { - // TODO uncomment below to create an instance of PolyAsset - //instance = new PolyAsset(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyAsset - /// - [Test] - public void PolyAssetInstanceTest() - { - // TODO uncomment below to test "IsType" PolyAsset - //Assert.IsType(instance); - } - - /// - /// Test the property 'Name' - /// - [Test] - public void NameTest() - { - // TODO unit test for the property 'Name' - } - /// - /// Test the property 'DisplayName' - /// - [Test] - public void DisplayNameTest() - { - // TODO unit test for the property 'DisplayName' - } - /// - /// Test the property 'AuthorName' - /// - [Test] - public void AuthorNameTest() - { - // TODO unit test for the property 'AuthorName' - } - /// - /// Test the property 'Description' - /// - [Test] - public void DescriptionTest() - { - // TODO unit test for the property 'Description' - } - /// - /// Test the property 'CreateTime' - /// - [Test] - public void CreateTimeTest() - { - // TODO unit test for the property 'CreateTime' - } - /// - /// Test the property 'UpdateTime' - /// - [Test] - public void UpdateTimeTest() - { - // TODO unit test for the property 'UpdateTime' - } - /// - /// Test the property 'Formats' - /// - [Test] - public void FormatsTest() - { - // TODO unit test for the property 'Formats' - } - /// - /// Test the property 'Thumbnail' - /// - [Test] - public void ThumbnailTest() - { - // TODO unit test for the property 'Thumbnail' - } - /// - /// Test the property 'Licence' - /// - [Test] - public void LicenceTest() - { - // TODO unit test for the property 'Licence' - } - /// - /// Test the property 'Visibility' - /// - [Test] - public void VisibilityTest() - { - // TODO unit test for the property 'Visibility' - } - /// - /// Test the property 'IsCurated' - /// - [Test] - public void IsCuratedTest() - { - // TODO unit test for the property 'IsCurated' - } - /// - /// Test the property 'PresentationParams' - /// - [Test] - public void PresentationParamsTest() - { - // TODO unit test for the property 'PresentationParams' - } - /// - /// Test the property 'Metadata' - /// - [Test] - public void MetadataTest() - { - // TODO unit test for the property 'Metadata' - } - /// - /// Test the property 'RemixInfo' - /// - [Test] - public void RemixInfoTest() - { - // TODO unit test for the property 'RemixInfo' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta deleted file mode 100644 index b8288a3e14..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyAssetTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: aab7589e6ea154542bd635a048243e78 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs deleted file mode 100644 index dbd0285e72..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyFormatComplexity - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyFormatComplexityTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyFormatComplexity - //private PolyFormatComplexity instance; - - public PolyFormatComplexityTests() - { - // TODO uncomment below to create an instance of PolyFormatComplexity - //instance = new PolyFormatComplexity(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyFormatComplexity - /// - [Test] - public void PolyFormatComplexityInstanceTest() - { - // TODO uncomment below to test "IsType" PolyFormatComplexity - //Assert.IsType(instance); - } - - /// - /// Test the property 'TriangleCount' - /// - [Test] - public void TriangleCountTest() - { - // TODO unit test for the property 'TriangleCount' - } - /// - /// Test the property 'LodHint' - /// - [Test] - public void LodHintTest() - { - // TODO unit test for the property 'LodHint' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta deleted file mode 100644 index 9ecf1de31c..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatComplexityTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a264eddcbce80495c8898c41d01101cc -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs deleted file mode 100644 index e2224d11f4..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyFormat - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyFormatTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyFormat - //private PolyFormat instance; - - public PolyFormatTests() - { - // TODO uncomment below to create an instance of PolyFormat - //instance = new PolyFormat(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyFormat - /// - [Test] - public void PolyFormatInstanceTest() - { - // TODO uncomment below to test "IsType" PolyFormat - //Assert.IsType(instance); - } - - /// - /// Test the property 'Root' - /// - [Test] - public void RootTest() - { - // TODO unit test for the property 'Root' - } - /// - /// Test the property 'Resources' - /// - [Test] - public void ResourcesTest() - { - // TODO unit test for the property 'Resources' - } - /// - /// Test the property 'FormatComplexity' - /// - [Test] - public void FormatComplexityTest() - { - // TODO unit test for the property 'FormatComplexity' - } - /// - /// Test the property 'FormatType' - /// - [Test] - public void FormatTypeTest() - { - // TODO unit test for the property 'FormatType' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta deleted file mode 100644 index e809d1a8ab..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyFormatTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1f125f66bf3ba455b9253dfd072fd1b9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs deleted file mode 100644 index 0546a12b4b..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyList - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyListTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyList - //private PolyList instance; - - public PolyListTests() - { - // TODO uncomment below to create an instance of PolyList - //instance = new PolyList(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyList - /// - [Test] - public void PolyListInstanceTest() - { - // TODO uncomment below to test "IsType" PolyList - //Assert.IsType(instance); - } - - /// - /// Test the property 'Assets' - /// - [Test] - public void AssetsTest() - { - // TODO unit test for the property 'Assets' - } - /// - /// Test the property 'NextPageToken' - /// - [Test] - public void NextPageTokenTest() - { - // TODO unit test for the property 'NextPageToken' - } - /// - /// Test the property 'TotalSize' - /// - [Test] - public void TotalSizeTest() - { - // TODO unit test for the property 'TotalSize' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta deleted file mode 100644 index 605a0c1292..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyListTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dd52b174fc1b245d8ac42b959b8330b6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs deleted file mode 100644 index b69d087b0a..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyPresentationParams - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyPresentationParamsTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyPresentationParams - //private PolyPresentationParams instance; - - public PolyPresentationParamsTests() - { - // TODO uncomment below to create an instance of PolyPresentationParams - //instance = new PolyPresentationParams(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyPresentationParams - /// - [Test] - public void PolyPresentationParamsInstanceTest() - { - // TODO uncomment below to test "IsType" PolyPresentationParams - //Assert.IsType(instance); - } - - /// - /// Test the property 'OrientingRotation' - /// - [Test] - public void OrientingRotationTest() - { - // TODO unit test for the property 'OrientingRotation' - } - /// - /// Test the property 'ColorSpace' - /// - [Test] - public void ColorSpaceTest() - { - // TODO unit test for the property 'ColorSpace' - } - /// - /// Test the property 'BackgroundColor' - /// - [Test] - public void BackgroundColorTest() - { - // TODO unit test for the property 'BackgroundColor' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta deleted file mode 100644 index 98f078b180..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyPresentationParamsTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c542cd7788c71429fbd1d8c91f2a8de3 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs deleted file mode 100644 index fa7f8b4cbc..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyQuaternion - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyQuaternionTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyQuaternion - //private PolyQuaternion instance; - - public PolyQuaternionTests() - { - // TODO uncomment below to create an instance of PolyQuaternion - //instance = new PolyQuaternion(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyQuaternion - /// - [Test] - public void PolyQuaternionInstanceTest() - { - // TODO uncomment below to test "IsType" PolyQuaternion - //Assert.IsType(instance); - } - - /// - /// Test the property 'X' - /// - [Test] - public void XTest() - { - // TODO unit test for the property 'X' - } - /// - /// Test the property 'Y' - /// - [Test] - public void YTest() - { - // TODO unit test for the property 'Y' - } - /// - /// Test the property 'Z' - /// - [Test] - public void ZTest() - { - // TODO unit test for the property 'Z' - } - /// - /// Test the property 'W' - /// - [Test] - public void WTest() - { - // TODO unit test for the property 'W' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta deleted file mode 100644 index 01a68d61d2..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyQuaternionTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1fbb41e4a1f6b449792b56325b0afe44 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs deleted file mode 100644 index e76bdeee9a..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyRemixInfo - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyRemixInfoTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyRemixInfo - //private PolyRemixInfo instance; - - public PolyRemixInfoTests() - { - // TODO uncomment below to create an instance of PolyRemixInfo - //instance = new PolyRemixInfo(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyRemixInfo - /// - [Test] - public void PolyRemixInfoInstanceTest() - { - // TODO uncomment below to test "IsType" PolyRemixInfo - //Assert.IsType(instance); - } - - /// - /// Test the property 'SourceAsset' - /// - [Test] - public void SourceAssetTest() - { - // TODO unit test for the property 'SourceAsset' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta deleted file mode 100644 index c4db9722f2..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyRemixInfoTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e84534ba8cef140b4ab05549bb3261dd -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs deleted file mode 100644 index dd0d83e866..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing PolyResource - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class PolyResourceTests : IDisposable - { - // TODO uncomment below to declare an instance variable for PolyResource - //private PolyResource instance; - - public PolyResourceTests() - { - // TODO uncomment below to create an instance of PolyResource - //instance = new PolyResource(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of PolyResource - /// - [Test] - public void PolyResourceInstanceTest() - { - // TODO uncomment below to test "IsType" PolyResource - //Assert.IsType(instance); - } - - /// - /// Test the property 'RelativePath' - /// - [Test] - public void RelativePathTest() - { - // TODO unit test for the property 'RelativePath' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'ContentType' - /// - [Test] - public void ContentTypeTest() - { - // TODO unit test for the property 'ContentType' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta deleted file mode 100644 index da390a34e5..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/PolyResourceTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 92dcbe08faad54332b0bdb202a8fe7b7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs deleted file mode 100644 index 9abba954ee..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing SubAssetFormat - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class SubAssetFormatTests : IDisposable - { - // TODO uncomment below to declare an instance variable for SubAssetFormat - //private SubAssetFormat instance; - - public SubAssetFormatTests() - { - // TODO uncomment below to create an instance of SubAssetFormat - //instance = new SubAssetFormat(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of SubAssetFormat - /// - [Test] - public void SubAssetFormatInstanceTest() - { - // TODO uncomment below to test "IsType" SubAssetFormat - //Assert.IsType(instance); - } - - /// - /// Test the property 'Id' - /// - [Test] - public void IdTest() - { - // TODO unit test for the property 'Id' - } - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Format' - /// - [Test] - public void FormatTest() - { - // TODO unit test for the property 'Format' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta deleted file mode 100644 index ef815c092a..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/SubAssetFormatTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: bcaf28a0fc1e24b908fba012419d4b65 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs deleted file mode 100644 index 2e51f25969..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing User - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class UserTests : IDisposable - { - // TODO uncomment below to declare an instance variable for User - //private User instance; - - public UserTests() - { - // TODO uncomment below to create an instance of User - //instance = new User(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of User - /// - [Test] - public void UserInstanceTest() - { - // TODO uncomment below to test "IsType" User - //Assert.IsType(instance); - } - - /// - /// Test the property 'Url' - /// - [Test] - public void UrlTest() - { - // TODO unit test for the property 'Url' - } - /// - /// Test the property 'Displayname' - /// - [Test] - public void DisplaynameTest() - { - // TODO unit test for the property 'Displayname' - } - /// - /// Test the property 'Description' - /// - [Test] - public void DescriptionTest() - { - // TODO unit test for the property 'Description' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta deleted file mode 100644 index 9c9d4ccf69..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/UserTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 067b53b1347ac430e9ffe0cd92dd6765 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs deleted file mode 100644 index e71fbefb39..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Icosa API - * - * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) - * - * The version of the OpenAPI document: 0.1.0 - * Generated by: https://github.com/openapitools/openapi-generator.git - */ - - -using System; -using System.Linq; -using System.IO; -using System.Collections.Generic; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Model; -using Org.OpenAPITools.Client; -using System.Reflection; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace Org.OpenAPITools.Test.Model -{ - /// - /// Class for testing ValidationError - /// - /// - /// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech). - /// Please update the test case below to test the model. - /// - public class ValidationErrorTests : IDisposable - { - // TODO uncomment below to declare an instance variable for ValidationError - //private ValidationError instance; - - public ValidationErrorTests() - { - // TODO uncomment below to create an instance of ValidationError - //instance = new ValidationError(); - } - - public void Dispose() - { - // Cleanup when everything is done. - } - - /// - /// Test an instance of ValidationError - /// - [Test] - public void ValidationErrorInstanceTest() - { - // TODO uncomment below to test "IsType" ValidationError - //Assert.IsType(instance); - } - - /// - /// Test the property 'Loc' - /// - [Test] - public void LocTest() - { - // TODO unit test for the property 'Loc' - } - /// - /// Test the property 'Msg' - /// - [Test] - public void MsgTest() - { - // TODO unit test for the property 'Msg' - } - /// - /// Test the property 'Type' - /// - [Test] - public void TypeTest() - { - // TODO unit test for the property 'Type' - } - } -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta deleted file mode 100644 index 295c54bc8b..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Model/ValidationErrorTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1c17cfc5ff3bd4bb4b7f8c90682e5fbd -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef b/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef deleted file mode 100644 index 464bf977d5..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Org.OpenAPITools.Test", - "references": [ - "Org.OpenAPITools", - "UnityEngine.TestRunner" - ], - "overrideReferences": true, - "precompiledReferences": [ - "nunit.framework.dll", - "Newtonsoft.Json.dll" - ], - "defineConstraints": [ - "UNITY_INCLUDE_TESTS" - ] -} diff --git a/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta b/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta deleted file mode 100644 index 4277b6c803..0000000000 --- a/Assets/ThirdParty/Org.OpenAPITools/Tests/Org.OpenAPITools.Test.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 62a13c20821f747f3be26668d785508e -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From a7cc5c84f46f22af54b9e0e98d228edc561c1940 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 6 Nov 2023 16:31:01 +0000 Subject: [PATCH 004/105] Add Icosa to accounts prefab --- .../PopUps/PopUpWindow_Accounts.prefab | 6597 +++++++++++++---- 1 file changed, 5002 insertions(+), 1595 deletions(-) diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab index dc05d48064..1cb3ea0e64 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab @@ -1,5 +1,40 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &162347472523419310 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8994260351568061772} + m_Layer: 16 + m_Name: Icosa_SignedInElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8994260351568061772 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 162347472523419310} + 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: + - {fileID: 3716870166160541890} + - {fileID: 9214833072643761372} + - {fileID: 2434770951962534735} + - {fileID: 5914045499585322770} + m_Father: {fileID: 7706333168622570552} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &217738510970157188 GameObject: m_ObjectHideFlags: 0 @@ -83,6 +118,178 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &237032865188240432 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 96286366463442578} + - component: {fileID: 9096299298168311087} + - component: {fileID: 4903674468560392015} + m_Layer: 16 + m_Name: Icosa_Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &96286366463442578 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 237032865188240432} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1000721931266647293} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.428, y: 0.3524501} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0, y: 1} +--- !u!23 &9096299298168311087 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 237032865188240432} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &4903674468560392015 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 237032865188240432} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Sketchfab + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.35 + m_fontSizeBase: 1.35 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 256 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 9096299298168311087} + m_maskType: 0 --- !u!1 &271835898861049686 GameObject: m_ObjectHideFlags: 0 @@ -168,7 +375,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &440723707841846920 +--- !u!1 &346980884099360615 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -176,62 +383,58 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4590447671262214820} - - component: {fileID: 2121377069448007116} - - component: {fileID: 1795340145885663930} - - component: {fileID: 2756793802573776306} - - component: {fileID: 3740906181516385311} - m_Layer: 16 - m_Name: Google_Cancel + - component: {fileID: 7219417712597752172} + - component: {fileID: 6131316858001926440} + - component: {fileID: 2193359757602459023} + - component: {fileID: 4899035600685365074} + m_Layer: 0 + m_Name: Description m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4590447671262214820 -Transform: +--- !u!224 &7219417712597752172 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 440723707841846920} + m_GameObject: {fileID: 346980884099360615} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.845, y: -0.242, z: -0.06} - m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_LocalPosition: {x: 0, y: 0, z: -0.0025} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 7741920746089690377} - m_RootOrder: 3 + m_Father: {fileID: 6373137851377895237} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &2121377069448007116 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 440723707841846920} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &1795340145885663930 + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.058} + m_SizeDelta: {x: 2.5, y: 0.7} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6131316858001926440 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 440723707841846920} + m_GameObject: {fileID: 346980884099360615} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 1 + m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -240,7 +443,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 1 + m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -253,44 +456,263 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &2756793802573776306 +--- !u!114 &2193359757602459023 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 440723707841846920} + m_GameObject: {fileID: 346980884099360615} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: 0 - m_DescriptionYOffset: 0 - m_DescriptionText: POPUP_ACCOUNTS_CANCEL_BUTTON_DESCRIPTION - m_LocalizedDescription: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 15526523405381632 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionTextExtra: - m_LocalizedDescriptionExtra: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionActivateSpeed: 12 - m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: 957f69e59565cd1469fa095b415fa513, type: 3} - m_AtlasTexture: 0 + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 'Sign in to a Sketchfab account to allow + + uploading to Sketchfab.' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4286085240 + m_fontColor: {r: 0.47058824, g: 0.47058824, b: 0.47058824, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.275 + m_fontSizeBase: 1.275 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 6131316858001926440} + m_maskType: 0 +--- !u!114 &4899035600685365074 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 346980884099360615} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034856 + references: + version: 2 + RefIds: + - rid: 3215294302546034856 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 2193359757602459023} + m_TrackedProperties: + items: + - rid: 3215294302546034857 + m_UpdateType: 0 + - rid: 3215294302546034857 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76138521224077312 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &440723707841846920 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4590447671262214820} + - component: {fileID: 2121377069448007116} + - component: {fileID: 1795340145885663930} + - component: {fileID: 2756793802573776306} + - component: {fileID: 3740906181516385311} + m_Layer: 16 + m_Name: Google_Cancel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4590447671262214820 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 440723707841846920} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.845, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7741920746089690377} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2121377069448007116 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 440723707841846920} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1795340145885663930 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 440723707841846920} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &2756793802573776306 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 440723707841846920} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: POPUP_ACCOUNTS_CANCEL_BUTTON_DESCRIPTION + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 15526523405381632 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 957f69e59565cd1469fa095b415fa513, type: 3} + m_AtlasTexture: 0 m_ToggleButton: 0 m_LongPressReleaseButton: 0 m_ButtonHasPressedAudio: 1 @@ -703,7 +1125,7 @@ MonoBehaviour: references: version: 2 RefIds: [] ---- !u!1 &1420984185611099820 +--- !u!1 &1206425102053680919 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -711,31 +1133,114 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6124434747938532598} - - component: {fileID: 3587958868994276479} - - component: {fileID: 8764641029886384522} - - component: {fileID: 320158017192318128} - - component: {fileID: 6728525253942131070} + - component: {fileID: 2257200555475620437} + - component: {fileID: 5694728525219628291} + - component: {fileID: 8082422626311802404} m_Layer: 16 - m_Name: Sketchfab_SignOut + m_Name: Background m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6124434747938532598 +--- !u!4 &2257200555475620437 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1420984185611099820} + m_GameObject: {fileID: 1206425102053680919} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.845, y: -0.242, z: -0.06} - m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_LocalPosition: {x: 0.0021666286, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 6423827612944397798} + m_Father: {fileID: 7500850895307564819} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5694728525219628291 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1206425102053680919} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8082422626311802404 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1206425102053680919} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 39d7053101c6e8e4b8d692eddc65d2b2, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &1420984185611099820 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6124434747938532598} + - component: {fileID: 3587958868994276479} + - component: {fileID: 8764641029886384522} + - component: {fileID: 320158017192318128} + - component: {fileID: 6728525253942131070} + m_Layer: 16 + m_Name: Sketchfab_SignOut + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6124434747938532598 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1420984185611099820} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.845, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 6423827612944397798} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3587958868994276479 @@ -879,7 +1384,7 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &2114749234407238233 +--- !u!1 &1595746536817818034 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -887,55 +1392,55 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7314565180282189882} - - component: {fileID: 3237075444271635601} - - component: {fileID: 5760690751225334939} - - component: {fileID: 1021251213275692761} - - component: {fileID: 5408017822027362464} + - component: {fileID: 9030048754727846959} + - component: {fileID: 8449302079573380106} + - component: {fileID: 7547523069343855482} + - component: {fileID: 4678841376964907514} + - component: {fileID: 4846547509098454009} m_Layer: 16 - m_Name: Sketchfab_SignOut + m_Name: Icosa_SignOut m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &7314565180282189882 +--- !u!4 &9030048754727846959 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2114749234407238233} + m_GameObject: {fileID: 1595746536817818034} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 1.041, y: 0.247, z: -0.02499998} - m_LocalScale: {x: 0.25, y: 0.24999991, z: 0.2499999} + m_LocalPosition: {x: 0.845, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 529827201699571093} - m_RootOrder: 0 + m_Father: {fileID: 1675920430848132177} + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3237075444271635601 +--- !u!33 &8449302079573380106 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2114749234407238233} + m_GameObject: {fileID: 1595746536817818034} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &5760690751225334939 +--- !u!23 &7547523069343855482 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2114749234407238233} + m_GameObject: {fileID: 1595746536817818034} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 @@ -951,7 +1456,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 0 + m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -964,21 +1469,21 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &1021251213275692761 +--- !u!114 &4678841376964907514 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2114749234407238233} + m_GameObject: {fileID: 1595746536817818034} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} m_Name: m_EditorClassIdentifier: m_DescriptionType: 0 m_DescriptionYOffset: 0 - m_DescriptionText: POPUP_ACCOUNTS_SIGNOUT_BUTTON_DESCRIPTION + m_DescriptionText: Sign Out m_LocalizedDescription: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 @@ -1000,7 +1505,7 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: 42f600d7b08f5514ebdcf4604e0cfa6c, type: 3} + m_ButtonTexture: {fileID: 2800000, guid: 6bd8742b210a6444e85b5c86f80ffebc, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 @@ -1010,7 +1515,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 98 + m_Command: 27 m_CommandParam: 2 m_CommandParam2: -1 m_RequiresPopup: 0 @@ -1039,24 +1544,23 @@ MonoBehaviour: m_ToggleOnTexture: {fileID: 0} m_AllowUnavailable: 0 m_LinkedUIObject: {fileID: 0} - m_CommandIgnored: 1 references: version: 2 RefIds: [] ---- !u!65 &5408017822027362464 +--- !u!65 &4846547509098454009 BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2114749234407238233} + m_GameObject: {fileID: 1595746536817818034} m_Material: {fileID: 0} m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &2130932365216558288 +--- !u!1 &1666263515128110337 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1064,58 +1568,60 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7650010300803901743} - - component: {fileID: 4683226676648572474} - - component: {fileID: 6232891306609240420} - - component: {fileID: 4801091935649164795} - m_Layer: 0 - m_Name: Title + - component: {fileID: 3326273886487770573} + - component: {fileID: 2034091918340690625} + - component: {fileID: 9015730477659628009} + m_Layer: 16 + m_Name: Icosa_BackPlate m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &7650010300803901743 -RectTransform: +--- !u!4 &3326273886487770573 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2130932365216558288} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.0025} - m_LocalScale: {x: 1, y: 1, z: 1} + m_GameObject: {fileID: 1666263515128110337} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.53499997, y: 0.2550001, z: -0.005} + m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 3457603883277620212} + m_Father: {fileID: 1000721931266647293} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.5} - m_SizeDelta: {x: 2.27, y: 0.3} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &4683226676648572474 +--- !u!33 &2034091918340690625 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1666263515128110337} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &9015730477659628009 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2130932365216558288} + m_GameObject: {fileID: 1666263515128110337} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -1137,139 +1643,42 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &6232891306609240420 -MonoBehaviour: +--- !u!1 &2027372909421917180 +GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2130932365216558288} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_text: Share to Sketchfab - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 1.915 - m_fontSizeBase: 1.915 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 0 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - _SortingLayer: 0 - _SortingLayerID: 0 - _SortingOrder: 0 - m_hasFontAssetChanged: 0 - m_renderer: {fileID: 4683226676648572474} - m_maskType: 0 ---- !u!114 &4801091935649164795 -MonoBehaviour: + serializedVersion: 6 + m_Component: + - component: {fileID: 1675920430848132177} + m_Layer: 16 + m_Name: Icosa_ConfirmSignOutElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1675920430848132177 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2130932365216558288} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: - - rid: 3215294302546034854 - references: - version: 2 - RefIds: - - rid: 3215294302546034854 - type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, - asm: Unity.Localization} - data: - m_Target: {fileID: 6232891306609240420} - m_TrackedProperties: - items: - - rid: 3215294302546034855 - m_UpdateType: 0 - - rid: 3215294302546034855 - type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, - asm: Unity.Localization} - data: - m_Localized: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 76138395621449728 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_PropertyPath: m_text ---- !u!1 &2181120061148835783 + m_GameObject: {fileID: 2027372909421917180} + 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: + - {fileID: 8939772953772003517} + - {fileID: 7505721524787016835} + - {fileID: 9030048754727846959} + - {fileID: 1153672243062602464} + m_Father: {fileID: 7706333168622570552} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2114749234407238233 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1277,33 +1686,176 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 3457603883277620212} + - component: {fileID: 7314565180282189882} + - component: {fileID: 3237075444271635601} + - component: {fileID: 5760690751225334939} + - component: {fileID: 1021251213275692761} + - component: {fileID: 5408017822027362464} m_Layer: 16 - m_Name: SketchfabInfoElements + m_Name: Sketchfab_SignOut m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 ---- !u!4 &3457603883277620212 + m_IsActive: 1 +--- !u!4 &7314565180282189882 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2181120061148835783} + m_GameObject: {fileID: 2114749234407238233} 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_LocalPosition: {x: 1.041, y: 0.247, z: -0.02499998} + m_LocalScale: {x: 0.25, y: 0.24999991, z: 0.2499999} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 7650010300803901743} - - {fileID: 4704522903857296483} - - {fileID: 7338107887349877872} - m_Father: {fileID: 8645625734765961102} - m_RootOrder: 4 + m_Children: [] + m_Father: {fileID: 529827201699571093} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &2524197670113838527 +--- !u!33 &3237075444271635601 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114749234407238233} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5760690751225334939 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114749234407238233} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &1021251213275692761 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114749234407238233} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: POPUP_ACCOUNTS_SIGNOUT_BUTTON_DESCRIPTION + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 15522362785837056 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 42f600d7b08f5514ebdcf4604e0cfa6c, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 98 + m_CommandParam: 2 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 1 + references: + version: 2 + RefIds: [] +--- !u!65 &5408017822027362464 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114749234407238233} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &2130932365216558288 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1311,44 +1863,44 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1829480651769887961} - - component: {fileID: 3001700515688151744} - - component: {fileID: 1883450177596816156} - - component: {fileID: 5438294757481915153} - m_Layer: 16 - m_Name: Got it + - component: {fileID: 7650010300803901743} + - component: {fileID: 4683226676648572474} + - component: {fileID: 6232891306609240420} + - component: {fileID: 4801091935649164795} + m_Layer: 0 + m_Name: Title m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &1829480651769887961 +--- !u!224 &7650010300803901743 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2524197670113838527} + m_GameObject: {fileID: 2130932365216558288} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.029333334} - m_LocalScale: {x: 2.9528668, y: 5.9057336, z: 2.9528668} + m_LocalPosition: {x: 0, y: 0, z: -0.0025} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 8645625735159627316} + m_Father: {fileID: 3457603883277620212} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.141, y: 0} - m_SizeDelta: {x: 0.25, y: 0.15} + m_AnchoredPosition: {x: 0, y: 0.5} + m_SizeDelta: {x: 2.27, y: 0.3} m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &3001700515688151744 +--- !u!23 &4683226676648572474 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2524197670113838527} + m_GameObject: {fileID: 2130932365216558288} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -1384,13 +1936,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &1883450177596816156 +--- !u!114 &6232891306609240420 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2524197670113838527} + m_GameObject: {fileID: 2130932365216558288} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -1404,7 +1956,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: GOT IT + m_text: Share to Sketchfab m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -1431,8 +1983,8 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 0.64 - m_fontSizeBase: 0.64 + m_fontSize: 1.915 + m_fontSizeBase: 1.915 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 @@ -1474,9 +2026,256 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 3001700515688151744} + m_renderer: {fileID: 4683226676648572474} m_maskType: 0 ---- !u!114 &5438294757481915153 +--- !u!114 &4801091935649164795 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2130932365216558288} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034854 + references: + version: 2 + RefIds: + - rid: 3215294302546034854 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 6232891306609240420} + m_TrackedProperties: + items: + - rid: 3215294302546034855 + m_UpdateType: 0 + - rid: 3215294302546034855 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76138395621449728 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &2181120061148835783 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3457603883277620212} + m_Layer: 16 + m_Name: SketchfabInfoElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &3457603883277620212 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2181120061148835783} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.13499999, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 + m_Children: + - {fileID: 7650010300803901743} + - {fileID: 4704522903857296483} + - {fileID: 7338107887349877872} + m_Father: {fileID: 8645625734765961102} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2524197670113838527 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1829480651769887961} + - component: {fileID: 3001700515688151744} + - component: {fileID: 1883450177596816156} + - component: {fileID: 5438294757481915153} + m_Layer: 16 + m_Name: Got it + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1829480651769887961 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2524197670113838527} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.029333334} + m_LocalScale: {x: 2.9528668, y: 5.9057336, z: 2.9528668} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8645625735159627316} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.141, y: 0} + m_SizeDelta: {x: 0.25, y: 0.15} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &3001700515688151744 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2524197670113838527} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &1883450177596816156 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2524197670113838527} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: GOT IT + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.64 + m_fontSizeBase: 0.64 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 3001700515688151744} + m_maskType: 0 +--- !u!114 &5438294757481915153 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2199,7 +2998,7 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &3204955315311320290 +--- !u!1 &3011044884306177928 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2207,26 +3006,109 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4111961635044152573} - - component: {fileID: 6060768737393671651} - - component: {fileID: 1725896781546607429} - - component: {fileID: 3626078249338065464} + - component: {fileID: 8939772953772003517} + - component: {fileID: 1159919028371824209} + - component: {fileID: 657338479228588091} m_Layer: 16 - m_Name: Sketchfab_Text + m_Name: Icosa_BackPlate m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &4111961635044152573 -RectTransform: +--- !u!4 &8939772953772003517 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3204955315311320290} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_GameObject: {fileID: 3011044884306177928} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.622, y: 0.2550001, z: -0.005} + m_LocalScale: {x: 32, y: 32, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1675920430848132177} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1159919028371824209 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3011044884306177928} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &657338479228588091 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3011044884306177928} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3204955315311320290 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4111961635044152573} + - component: {fileID: 6060768737393671651} + - component: {fileID: 1725896781546607429} + - component: {fileID: 3626078249338065464} + m_Layer: 16 + m_Name: Sketchfab_Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4111961635044152573 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3204955315311320290} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] @@ -2838,7 +3720,7 @@ MonoBehaviour: m_WaitForCompletion: 0 m_LocalVariables: [] m_PropertyPath: m_text ---- !u!1 &3350558357556252257 +--- !u!1 &3270184853410304968 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2846,47 +3728,51 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7580573523043848490} - - component: {fileID: 8140081930800988506} - - component: {fileID: 2514654466827372125} + - component: {fileID: 5520953697447238279} + - component: {fileID: 5249507884336548187} + - component: {fileID: 3255885207901872536} + - component: {fileID: 1824715322301957706} + - component: {fileID: 3327138301261596308} m_Layer: 16 - m_Name: Background + m_Name: Icosa_SignIn m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &7580573523043848490 +--- !u!4 &5520953697447238279 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3350558357556252257} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0.045} - m_LocalScale: {x: 2, y: 1, z: 1} + m_GameObject: {fileID: 3270184853410304968} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.627, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 181638217984195686} - m_RootOrder: 1 + m_Children: + - {fileID: 2729332839612941327} + - {fileID: 906844544403122228} + m_Father: {fileID: 1000721931266647293} + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &8140081930800988506 +--- !u!33 &5249507884336548187 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3350558357556252257} + m_GameObject: {fileID: 3270184853410304968} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &2514654466827372125 +--- !u!23 &3255885207901872536 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3350558357556252257} - m_Enabled: 1 + m_GameObject: {fileID: 3270184853410304968} + m_Enabled: 0 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 @@ -2899,7 +3785,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 0ead8b8e224bcf040862605f5eed7211, type: 2} + - {fileID: 2100000, guid: 005edae3a0d4d1243ba88ed7543cd61d, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -2921,7 +3807,99 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3387414818891217864 +--- !u!114 &1824715322301957706 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3270184853410304968} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: -1 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 0 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 95 + m_CommandParam: 2 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 1 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!65 &3327138301261596308 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3270184853410304968} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &3292774118003760337 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2929,47 +3907,51 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4946117081540083272} - - component: {fileID: 1555734638323418344} - - component: {fileID: 6328234730399420729} - m_Layer: 16 - m_Name: Sketchfab_Logo + - component: {fileID: 7500850895307564819} + - component: {fileID: 6391502674584969794} + - component: {fileID: 588537084669385653} + - component: {fileID: 9061423159326835474} + - component: {fileID: 4927931040837201254} + m_Layer: 14 + m_Name: ConfirmationButton m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4946117081540083272 +--- !u!4 &7500850895307564819 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3387414818891217864} + m_GameObject: {fileID: 3292774118003760337} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} - m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} + m_LocalPosition: {x: 0, y: -0.434, z: -0.08} + m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 534589999851858800} - m_RootOrder: 1 + m_Children: + - {fileID: 4750169147585981226} + - {fileID: 2257200555475620437} + m_Father: {fileID: 6373137851377895237} + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1555734638323418344 +--- !u!33 &6391502674584969794 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3387414818891217864} + m_GameObject: {fileID: 3292774118003760337} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &6328234730399420729 +--- !u!23 &588537084669385653 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3387414818891217864} - m_Enabled: 1 + m_GameObject: {fileID: 3292774118003760337} + m_Enabled: 0 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 @@ -2982,7 +3964,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -3004,121 +3986,34 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3674036727267325941 -GameObject: +--- !u!65 &9061423159326835474 +BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5521146392998997215} - - component: {fileID: 3994049754972082256} - - component: {fileID: 619260971007631306} - - component: {fileID: 2495991278148453507} - - component: {fileID: 4355862344388155723} - m_Layer: 16 - m_Name: Button_ClosePopup - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &5521146392998997215 -Transform: + m_GameObject: {fileID: 3292774118003760337} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 0.01} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &4927931040837201254 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3674036727267325941} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 1.251, y: -0.75199986, z: -0.018} - m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} - m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 6855474239171570967} - - {fileID: 4100876239645834569} - m_Father: {fileID: 3576301794080265196} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3994049754972082256 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3674036727267325941} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &619260971007631306 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3674036727267325941} - m_Enabled: 0 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 0 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 0 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &2495991278148453507 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3674036727267325941} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1.0000001, y: 1, z: 0.01} - m_Center: {x: 0, y: 0, z: -0.01} ---- !u!114 &4355862344388155723 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3674036727267325941} + m_GameObject: {fileID: 3292774118003760337} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: 0 + m_DescriptionType: -1 m_DescriptionYOffset: 0 - m_DescriptionText: Close + m_DescriptionText: m_LocalizedDescription: m_TableReference: m_TableCollectionName: @@ -3146,7 +4041,7 @@ MonoBehaviour: m_LongPressReleaseButton: 0 m_ButtonHasPressedAudio: 1 m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.03 + m_ZAdjustClick: 0.05 m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 @@ -3183,7 +4078,7 @@ MonoBehaviour: references: version: 2 RefIds: [] ---- !u!1 &3793285321450572269 +--- !u!1 &3350558357556252257 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3191,46 +4086,46 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6855474239171570967} - - component: {fileID: 4593108259088090395} - - component: {fileID: 1617028310973550690} + - component: {fileID: 7580573523043848490} + - component: {fileID: 8140081930800988506} + - component: {fileID: 2514654466827372125} m_Layer: 16 - m_Name: Mesh + m_Name: Background m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6855474239171570967 +--- !u!4 &7580573523043848490 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3793285321450572269} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 0.65, y: 0.65, z: 1} + m_GameObject: {fileID: 3350558357556252257} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.045} + m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 5521146392998997215} - m_RootOrder: 0 + m_Father: {fileID: 181638217984195686} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &4593108259088090395 +--- !u!33 &8140081930800988506 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3793285321450572269} + m_GameObject: {fileID: 3350558357556252257} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &1617028310973550690 +--- !u!23 &2514654466827372125 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3793285321450572269} + m_GameObject: {fileID: 3350558357556252257} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -3244,7 +4139,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 0d7eb02b18ffb4c419fb75924cb900dc, type: 2} + - {fileID: 2100000, guid: 0ead8b8e224bcf040862605f5eed7211, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -3266,7 +4161,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3883720175326442003 +--- !u!1 &3364245950019763117 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3274,46 +4169,46 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6692607038900794975} - - component: {fileID: 1080627186782362197} - - component: {fileID: 2964682892386166551} + - component: {fileID: 5914045499585322770} + - component: {fileID: 7765932664411523162} + - component: {fileID: 1640930553324643105} m_Layer: 16 - m_Name: Google_BackPlate + m_Name: Icosa_BackPlate m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6692607038900794975 +--- !u!4 &5914045499585322770 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3883720175326442003} + m_GameObject: {fileID: 3364245950019763117} m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: -0.621, y: -0.151, z: -0.005} + m_LocalPosition: {x: 0.621, y: -0.151, z: -0.005} m_LocalScale: {x: 47, y: 49.999924, z: 74.2} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1411852402157678472} - m_RootOrder: 4 + m_Father: {fileID: 8994260351568061772} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1080627186782362197 +--- !u!33 &7765932664411523162 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3883720175326442003} + m_GameObject: {fileID: 3364245950019763117} m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} ---- !u!23 &2964682892386166551 +--- !u!23 &1640930553324643105 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3883720175326442003} + m_GameObject: {fileID: 3364245950019763117} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -3349,7 +4244,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &4082436277880791083 +--- !u!1 &3387414818891217864 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3357,33 +4252,82 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 5854179938074699431} + - component: {fileID: 4946117081540083272} + - component: {fileID: 1555734638323418344} + - component: {fileID: 6328234730399420729} m_Layer: 16 - m_Name: Google + m_Name: Sketchfab_Logo m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &5854179938074699431 +--- !u!4 &4946117081540083272 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4082436277880791083} - 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_GameObject: {fileID: 3387414818891217864} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} + m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 1411852402157678472} - - {fileID: 1432546188030660464} - - {fileID: 7741920746089690377} - m_Father: {fileID: 8645625735416266758} - m_RootOrder: 2 + m_Children: [] + m_Father: {fileID: 534589999851858800} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &4103205528941609404 +--- !u!33 &1555734638323418344 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3387414818891217864} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6328234730399420729 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3387414818891217864} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3674036727267325941 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3391,49 +4335,51 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4700142148089133932} - - component: {fileID: 3380311651882997193} - - component: {fileID: 4297529096568456498} - - component: {fileID: 5219542430503434550} - - component: {fileID: 586799646275115208} + - component: {fileID: 5521146392998997215} + - component: {fileID: 3994049754972082256} + - component: {fileID: 619260971007631306} + - component: {fileID: 2495991278148453507} + - component: {fileID: 4355862344388155723} m_Layer: 16 - m_Name: Sketchfab_Info + m_Name: Button_ClosePopup m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4700142148089133932 +--- !u!4 &5521146392998997215 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4103205528941609404} + m_GameObject: {fileID: 3674036727267325941} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 1.046, y: 0.2620001, z: -0.025} - m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_LocalPosition: {x: 1.251, y: -0.75199986, z: -0.018} + m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 534589999851858800} + m_Children: + - {fileID: 6855474239171570967} + - {fileID: 4100876239645834569} + m_Father: {fileID: 3576301794080265196} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3380311651882997193 +--- !u!33 &3994049754972082256 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4103205528941609404} + m_GameObject: {fileID: 3674036727267325941} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &4297529096568456498 +--- !u!23 &619260971007631306 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4103205528941609404} - m_Enabled: 1 + m_GameObject: {fileID: 3674036727267325941} + m_Enabled: 0 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 @@ -3468,21 +4414,34 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &5219542430503434550 -MonoBehaviour: +--- !u!65 &2495991278148453507 +BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4103205528941609404} + m_GameObject: {fileID: 3674036727267325941} + m_Material: {fileID: 0} + m_IsTrigger: 0 m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} - m_Name: - m_EditorClassIdentifier: - m_DescriptionType: -1 + serializedVersion: 2 + m_Size: {x: 1.0000001, y: 1, z: 0.01} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &4355862344388155723 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3674036727267325941} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 m_DescriptionYOffset: 0 - m_DescriptionText: + m_DescriptionText: Close m_LocalizedDescription: m_TableReference: m_TableCollectionName: @@ -3504,18 +4463,18 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: bb4439b4279219b4db937c93530893ff, type: 3} - m_AtlasTexture: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 0 m_ToggleButton: 0 m_LongPressReleaseButton: 0 m_ButtonHasPressedAudio: 1 m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.05 - m_HoverScale: 1.2 + m_ZAdjustClick: 0.03 + m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 73 - m_CommandParam: 2 + m_Command: 0 + m_CommandParam: -1 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -3547,20 +4506,7 @@ MonoBehaviour: references: version: 2 RefIds: [] ---- !u!65 &586799646275115208 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4103205528941609404} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1, y: 1.0000001, z: 0.1} - m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &4105427794534146856 +--- !u!1 &3793285321450572269 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3568,48 +4514,46 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4120900520909079457} - - component: {fileID: 4957999687296029088} - - component: {fileID: 6324396148345559707} - - component: {fileID: 5104710591034478679} - - component: {fileID: 7087448018191981113} + - component: {fileID: 6855474239171570967} + - component: {fileID: 4593108259088090395} + - component: {fileID: 1617028310973550690} m_Layer: 16 - m_Name: Sketchfab_Cancel + m_Name: Mesh m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4120900520909079457 +--- !u!4 &6855474239171570967 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4105427794534146856} + m_GameObject: {fileID: 3793285321450572269} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.398, y: -0.242, z: -0.06} - m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 6423827612944397798} - m_RootOrder: 3 + m_Father: {fileID: 5521146392998997215} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &4957999687296029088 +--- !u!33 &4593108259088090395 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4105427794534146856} + m_GameObject: {fileID: 3793285321450572269} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &6324396148345559707 +--- !u!23 &1617028310973550690 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4105427794534146856} + m_GameObject: {fileID: 3793285321450572269} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -3623,7 +4567,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} + - {fileID: 2100000, guid: 0d7eb02b18ffb4c419fb75924cb900dc, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -3645,99 +4589,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &5104710591034478679 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4105427794534146856} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} - m_Name: - m_EditorClassIdentifier: - m_DescriptionType: 0 - m_DescriptionYOffset: 0 - m_DescriptionText: Cancel - m_LocalizedDescription: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 15526523405381632 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionTextExtra: - m_LocalizedDescriptionExtra: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionActivateSpeed: 12 - m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: 957f69e59565cd1469fa095b415fa513, type: 3} - m_AtlasTexture: 0 - m_ToggleButton: 0 - m_LongPressReleaseButton: 0 - m_ButtonHasPressedAudio: 1 - m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.05 - m_HoverScale: 1.1 - m_HoverBoxColliderGrow: 0.2 - m_AddOverlay: 0 - m_Command: 0 - m_CommandParam: 2 - m_CommandParam2: -1 - m_RequiresPopup: 0 - m_CenterPopupOnButton: 0 - m_PopupOffset: {x: 0, y: 0, z: 0} - m_PopupText: - m_LocalizedPopup: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnDescription: - m_LocalizedToggleOnDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 - m_LinkedUIObject: {fileID: 0} - m_CommandIgnored: 1 - references: - version: 2 - RefIds: [] ---- !u!65 &7087448018191981113 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4105427794534146856} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1, y: 1.0000001, z: 0.1} - m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &4316944982740412120 +--- !u!1 &3883720175326442003 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3745,60 +4597,60 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1180802039870758397} - - component: {fileID: 390951240049198343} - - component: {fileID: 6026671568046064672} + - component: {fileID: 6692607038900794975} + - component: {fileID: 1080627186782362197} + - component: {fileID: 2964682892386166551} m_Layer: 16 - m_Name: IconDisabled + m_Name: Google_BackPlate m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &1180802039870758397 +--- !u!4 &6692607038900794975 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4316944982740412120} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} - m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} + m_GameObject: {fileID: 3883720175326442003} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0.621, y: -0.151, z: -0.005} + m_LocalScale: {x: 47, y: 49.999924, z: 74.2} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1741904442304953456} - m_RootOrder: 1 + m_Father: {fileID: 1411852402157678472} + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &390951240049198343 +--- !u!33 &1080627186782362197 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4316944982740412120} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &6026671568046064672 + m_GameObject: {fileID: 3883720175326442003} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &2964682892386166551 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4316944982740412120} + m_GameObject: {fileID: 3883720175326442003} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 6b65e04b1b7fb5644862eb36bfcfb419, type: 2} + - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -3820,7 +4672,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &4539008613640430378 +--- !u!1 &3893413551512185020 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3828,60 +4680,132 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6661346418304443125} - - component: {fileID: 8976936997367820456} - - component: {fileID: 8201886566812613733} + - component: {fileID: 1000721931266647293} m_Layer: 16 - m_Name: Sketchfab_Logo + m_Name: Icosa_SignedOutElements m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6661346418304443125 +--- !u!4 &1000721931266647293 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4539008613640430378} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.37, y: -0.324, z: -0.025} - m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} + m_GameObject: {fileID: 3893413551512185020} + 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: 5621771514230647440} + m_Children: + - {fileID: 3326273886487770573} + - {fileID: 5038995524656264454} + - {fileID: 96286366463442578} + - {fileID: 2403478784508930027} + - {fileID: 5520953697447238279} + m_Father: {fileID: 7706333168622570552} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &8976936997367820456 -MeshFilter: +--- !u!1 &4082436277880791083 +GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4539008613640430378} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &8201886566812613733 -MeshRenderer: + serializedVersion: 6 + m_Component: + - component: {fileID: 5854179938074699431} + m_Layer: 16 + m_Name: Google + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5854179938074699431 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4539008613640430378} - m_Enabled: 1 - m_CastShadows: 0 - m_ReceiveShadows: 0 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 0 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 + m_GameObject: {fileID: 4082436277880791083} + 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: + - {fileID: 1411852402157678472} + - {fileID: 1432546188030660464} + - {fileID: 7741920746089690377} + m_Father: {fileID: 8645625735416266758} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4103205528941609404 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4700142148089133932} + - component: {fileID: 3380311651882997193} + - component: {fileID: 4297529096568456498} + - component: {fileID: 5219542430503434550} + - component: {fileID: 586799646275115208} + m_Layer: 16 + m_Name: Sketchfab_Info + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4700142148089133932 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4103205528941609404} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.046, y: 0.2620001, z: -0.025} + m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 534589999851858800} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3380311651882997193 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4103205528941609404} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &4297529096568456498 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4103205528941609404} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -3903,7 +4827,99 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &4884541372955623633 +--- !u!114 &5219542430503434550 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4103205528941609404} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: -1 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: bb4439b4279219b4db937c93530893ff, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.2 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 73 + m_CommandParam: 2 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!65 &586799646275115208 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4103205528941609404} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &4105427794534146856 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -3911,44 +4927,48 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 7758143906596955047} - - component: {fileID: 3414497333256173795} - - component: {fileID: 7198143098074655042} - - component: {fileID: 1849472563075584265} + - component: {fileID: 4120900520909079457} + - component: {fileID: 4957999687296029088} + - component: {fileID: 6324396148345559707} + - component: {fileID: 5104710591034478679} + - component: {fileID: 7087448018191981113} m_Layer: 16 - m_Name: Text + m_Name: Sketchfab_Cancel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &7758143906596955047 -RectTransform: +--- !u!4 &4120900520909079457 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4884541372955623633} + m_GameObject: {fileID: 4105427794534146856} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.023000002} - m_LocalScale: {x: 1.7142857, y: 6.6666627, z: 0.8571429} + m_LocalPosition: {x: 0.398, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1741904442304953456} + m_Father: {fileID: 6423827612944397798} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.183, y: -0.019} - m_SizeDelta: {x: 0.4, y: 0.1} - m_Pivot: {x: 0, y: 0.5} ---- !u!23 &3414497333256173795 +--- !u!33 &4957999687296029088 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4105427794534146856} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6324396148345559707 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4884541372955623633} + m_GameObject: {fileID: 4105427794534146856} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -3962,7 +4982,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -3971,10 +4991,10 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 0 + m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 1 + m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -3984,187 +5004,99 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &7198143098074655042 +--- !u!114 &5104710591034478679 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4884541372955623633} + m_GameObject: {fileID: 4105427794534146856} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} m_Name: m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_text: Drive Backup - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 0.785 - m_fontSizeBase: 0.785 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 1 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 0 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - _SortingLayer: 0 - _SortingLayerID: 0 - _SortingOrder: 0 - m_hasFontAssetChanged: 0 - m_renderer: {fileID: 3414497333256173795} - m_maskType: 0 ---- !u!114 &1849472563075584265 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4884541372955623633} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: - - rid: 8021623883140169739 + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: Cancel + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 15526523405381632 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 957f69e59565cd1469fa095b415fa513, type: 3} + m_AtlasTexture: 0 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 0 + m_CommandParam: 2 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 1 references: version: 2 - RefIds: - - rid: 8021623883140169739 - type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, - asm: Unity.Localization} - data: - m_Target: {fileID: 7198143098074655042} - m_TrackedProperties: - items: - - rid: 8021623883140169740 - m_UpdateType: 0 - - rid: 8021623883140169740 - type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, - asm: Unity.Localization} - data: - m_Localized: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 15524391402909696 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_PropertyPath: m_text ---- !u!1 &5048206632594256402 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 3576301794080265196} - - component: {fileID: 480163635885148388} - m_Layer: 16 - m_Name: DriveInfoElements - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 0 ---- !u!4 &3576301794080265196 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5048206632594256402} - 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: - - {fileID: 3755039154609302241} - - {fileID: 6679582736177907422} - - {fileID: 7566473728865432861} - - {fileID: 5521146392998997215} - m_Father: {fileID: 8645625734765961102} - m_RootOrder: 3 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!114 &480163635885148388 -MonoBehaviour: + RefIds: [] +--- !u!65 &7087448018191981113 +BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5048206632594256402} + m_GameObject: {fileID: 4105427794534146856} + m_Material: {fileID: 0} + m_IsTrigger: 0 m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 20ac3005e19346a4296a8f6a73363832, type: 3} - m_Name: - m_EditorClassIdentifier: ---- !u!1 &5111218671440306906 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &4148507089762079577 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4172,51 +5104,51 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 9209058213459650767} - - component: {fileID: 8820378468166149913} - - component: {fileID: 1547790019101272354} - - component: {fileID: 3197312481833515046} - m_Layer: 0 - m_Name: Title + - component: {fileID: 2729332839612941327} + - component: {fileID: 6945091220654625637} + - component: {fileID: 198924042195436663} + - component: {fileID: 2813503354310912859} + m_Layer: 16 + m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &9209058213459650767 +--- !u!224 &2729332839612941327 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5111218671440306906} + m_GameObject: {fileID: 4148507089762079577} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.0025} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.009999992} + m_LocalScale: {x: 4, y: 4, z: 2.857143} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 4825048073929942150} + m_Father: {fileID: 5520953697447238279} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.5} - m_SizeDelta: {x: 2.27, y: 0.3} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &8820378468166149913 + m_AnchoredPosition: {x: -0.202, y: 0} + m_SizeDelta: {x: 0.3, y: 0.15} + m_Pivot: {x: 0, y: 0.5} +--- !u!23 &6945091220654625637 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5111218671440306906} + m_GameObject: {fileID: 4148507089762079577} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 @@ -4235,7 +5167,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 0 + m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -4245,13 +5177,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &1547790019101272354 +--- !u!114 &198924042195436663 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5111218671440306906} + m_GameObject: {fileID: 4148507089762079577} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -4265,7 +5197,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Share with Google + m_text: SIGN IN m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -4292,14 +5224,14 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.915 - m_fontSizeBase: 1.915 + m_fontSize: 0.86 + m_fontSizeBase: 0.86 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 2 + m_HorizontalAlignment: 1 m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 @@ -4335,35 +5267,35 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 8820378468166149913} + m_renderer: {fileID: 6945091220654625637} m_maskType: 0 ---- !u!114 &3197312481833515046 +--- !u!114 &2813503354310912859 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5111218671440306906} + m_GameObject: {fileID: 4148507089762079577} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} m_Name: m_EditorClassIdentifier: m_TrackedObjects: - - rid: 3215294302546034850 + - rid: 3215294302546034848 references: version: 2 RefIds: - - rid: 3215294302546034850 + - rid: 3215294302546034848 type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, asm: Unity.Localization} data: - m_Target: {fileID: 1547790019101272354} + m_Target: {fileID: 198924042195436663} m_TrackedProperties: items: - - rid: 3215294302546034851 + - rid: 3215294302546034849 m_UpdateType: 0 - - rid: 3215294302546034851 + - rid: 3215294302546034849 type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, asm: Unity.Localization} data: @@ -4371,13 +5303,13 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 76137928887689216 + m_KeyId: 7933561489039360 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 m_LocalVariables: [] m_PropertyPath: m_text ---- !u!1 &5123262806756250226 +--- !u!1 &4316944982740412120 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4385,60 +5317,60 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 5208561973859313428} - - component: {fileID: 7929569316632562742} - - component: {fileID: 8052510225724890313} + - component: {fileID: 1180802039870758397} + - component: {fileID: 390951240049198343} + - component: {fileID: 6026671568046064672} m_Layer: 16 - m_Name: Background + m_Name: IconDisabled m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &5208561973859313428 +--- !u!4 &1180802039870758397 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5123262806756250226} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0.045} - m_LocalScale: {x: 2, y: 1, z: 1} + m_GameObject: {fileID: 4316944982740412120} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} + m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 7893921961113671483} + m_Father: {fileID: 1741904442304953456} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &7929569316632562742 +--- !u!33 &390951240049198343 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5123262806756250226} + m_GameObject: {fileID: 4316944982740412120} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &8052510225724890313 +--- !u!23 &6026671568046064672 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5123262806756250226} + m_GameObject: {fileID: 4316944982740412120} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 1 + m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 0ead8b8e224bcf040862605f5eed7211, type: 2} + - {fileID: 2100000, guid: 6b65e04b1b7fb5644862eb36bfcfb419, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -4447,7 +5379,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 1 + m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -4460,7 +5392,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &5301811399408650331 +--- !u!1 &4516902404884197280 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4468,46 +5400,48 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1182483975005714860} - - component: {fileID: 6161329202685458175} - - component: {fileID: 6793863180280690477} + - component: {fileID: 1153672243062602464} + - component: {fileID: 3316034999128707532} + - component: {fileID: 4361165343961033676} + - component: {fileID: 1469645492926563259} + - component: {fileID: 2086513809805596120} m_Layer: 16 - m_Name: Dividor + m_Name: Icosa_Cancel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &1182483975005714860 +--- !u!4 &1153672243062602464 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5301811399408650331} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: -0.151, z: -0.005} - m_LocalScale: {x: 0.01, y: 1, z: 1} + m_GameObject: {fileID: 4516902404884197280} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.398, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 8645625735416266758} - m_RootOrder: 1 + m_Father: {fileID: 1675920430848132177} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &6161329202685458175 +--- !u!33 &3316034999128707532 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5301811399408650331} + m_GameObject: {fileID: 4516902404884197280} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &6793863180280690477 +--- !u!23 &4361165343961033676 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5301811399408650331} + m_GameObject: {fileID: 4516902404884197280} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -4521,7 +5455,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} + - {fileID: 2100000, guid: 40d29de2bdc11f04dbfa25059165916e, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -4530,7 +5464,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 0 + m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -4543,7 +5477,99 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &5315453881601467229 +--- !u!114 &1469645492926563259 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4516902404884197280} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: Cancel + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 15526523405381632 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 957f69e59565cd1469fa095b415fa513, type: 3} + m_AtlasTexture: 0 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 0 + m_CommandParam: 2 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 1 + references: + version: 2 + RefIds: [] +--- !u!65 &2086513809805596120 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4516902404884197280} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &4539008613640430378 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4551,58 +5577,60 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1583684570998085433} - - component: {fileID: 5194402002804098867} - - component: {fileID: 8290597564335685000} - - component: {fileID: 2511332541157942654} + - component: {fileID: 6661346418304443125} + - component: {fileID: 8976936997367820456} + - component: {fileID: 8201886566812613733} m_Layer: 16 - m_Name: Text + m_Name: Sketchfab_Logo m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &1583684570998085433 -RectTransform: +--- !u!4 &6661346418304443125 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5315453881601467229} + m_GameObject: {fileID: 4539008613640430378} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.009999992} - m_LocalScale: {x: 4, y: 4, z: 2.857143} + m_LocalPosition: {x: 0.37, y: -0.324, z: -0.025} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 7893921961113671483} - m_RootOrder: 0 + m_Father: {fileID: 5621771514230647440} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.20199999, y: 0} - m_SizeDelta: {x: 0.3, y: 0.15} - m_Pivot: {x: 0, y: 0.5} ---- !u!23 &5194402002804098867 +--- !u!33 &8976936997367820456 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4539008613640430378} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8201886566812613733 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5315453881601467229} + m_GameObject: {fileID: 4539008613640430378} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 1 + m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -4614,7 +5642,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 1 + m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -4624,139 +5652,41 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &8290597564335685000 -MonoBehaviour: +--- !u!1 &4671315024118560739 +GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5315453881601467229} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_text: SIGN IN - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 0.86 - m_fontSizeBase: 0.86 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 1 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 0 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - _SortingLayer: 0 - _SortingLayerID: 0 - _SortingOrder: 0 - m_hasFontAssetChanged: 0 - m_renderer: {fileID: 5194402002804098867} - m_maskType: 0 ---- !u!114 &2511332541157942654 -MonoBehaviour: + serializedVersion: 6 + m_Component: + - component: {fileID: 7706333168622570552} + m_Layer: 16 + m_Name: Icosa + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7706333168622570552 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5315453881601467229} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: - - rid: 3215294302546034846 - references: - version: 2 - RefIds: - - rid: 3215294302546034846 - type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, - asm: Unity.Localization} - data: - m_Target: {fileID: 8290597564335685000} - m_TrackedProperties: - items: - - rid: 3215294302546034847 - m_UpdateType: 0 - - rid: 3215294302546034847 - type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, - asm: Unity.Localization} - data: - m_Localized: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 7933561489039360 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_PropertyPath: m_text ---- !u!1 &5403683023149528791 + m_GameObject: {fileID: 4671315024118560739} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.5611764, y: -1.1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 8994260351568061772} + - {fileID: 1000721931266647293} + - {fileID: 1675920430848132177} + m_Father: {fileID: 8645625735416266758} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &4884541372955623633 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4764,51 +5694,51 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4704522903857296483} - - component: {fileID: 6212349802531054436} - - component: {fileID: 8705749884328266341} - - component: {fileID: 832193050044176433} - m_Layer: 0 - m_Name: Description + - component: {fileID: 7758143906596955047} + - component: {fileID: 3414497333256173795} + - component: {fileID: 7198143098074655042} + - component: {fileID: 1849472563075584265} + m_Layer: 16 + m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &4704522903857296483 +--- !u!224 &7758143906596955047 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5403683023149528791} + m_GameObject: {fileID: 4884541372955623633} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.0025} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.023000002} + m_LocalScale: {x: 1.7142857, y: 6.6666627, z: 0.8571429} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 3457603883277620212} - m_RootOrder: 1 + m_Father: {fileID: 1741904442304953456} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.058} - m_SizeDelta: {x: 2.5, y: 0.7} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &6212349802531054436 + m_AnchoredPosition: {x: -0.183, y: -0.019} + m_SizeDelta: {x: 0.4, y: 0.1} + m_Pivot: {x: 0, y: 0.5} +--- !u!23 &3414497333256173795 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5403683023149528791} + m_GameObject: {fileID: 4884541372955623633} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 @@ -4827,7 +5757,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 0 + m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -4837,13 +5767,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &8705749884328266341 +--- !u!114 &7198143098074655042 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5403683023149528791} + m_GameObject: {fileID: 4884541372955623633} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -4857,9 +5787,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 'Sign in to a Sketchfab account to allow - - uploading to Sketchfab.' + m_text: Drive Backup m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -4868,8 +5796,8 @@ MonoBehaviour: m_fontMaterials: [] m_fontColor32: serializedVersion: 2 - rgba: 4286085240 - m_fontColor: {r: 0.47058824, g: 0.47058824, b: 0.47058824, a: 1} + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -4886,14 +5814,14 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.275 - m_fontSizeBase: 1.275 + m_fontSize: 0.785 + m_fontSizeBase: 0.785 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 2 + m_HorizontalAlignment: 1 m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 @@ -4929,35 +5857,35 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 6212349802531054436} + m_renderer: {fileID: 3414497333256173795} m_maskType: 0 ---- !u!114 &832193050044176433 +--- !u!114 &1849472563075584265 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5403683023149528791} + m_GameObject: {fileID: 4884541372955623633} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} m_Name: m_EditorClassIdentifier: m_TrackedObjects: - - rid: 3215294302546034856 + - rid: 8021623883140169739 references: version: 2 RefIds: - - rid: 3215294302546034856 + - rid: 8021623883140169739 type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, asm: Unity.Localization} data: - m_Target: {fileID: 8705749884328266341} + m_Target: {fileID: 7198143098074655042} m_TrackedProperties: items: - - rid: 3215294302546034857 + - rid: 8021623883140169740 m_UpdateType: 0 - - rid: 3215294302546034857 + - rid: 8021623883140169740 type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, asm: Unity.Localization} data: @@ -4965,13 +5893,13 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 76138521224077312 + m_KeyId: 15524391402909696 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 m_LocalVariables: [] m_PropertyPath: m_text ---- !u!1 &5419396676487616149 +--- !u!1 &5048206632594256402 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4979,62 +5907,106 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 5446221535905318599} - - component: {fileID: 5255334288434809734} - - component: {fileID: 1369930125958929065} - - component: {fileID: 4730200966117401982} - - component: {fileID: 712852433553107461} + - component: {fileID: 3576301794080265196} + - component: {fileID: 480163635885148388} m_Layer: 16 - m_Name: Google_SignOut + m_Name: DriveInfoElements m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &5446221535905318599 + m_IsActive: 0 +--- !u!4 &3576301794080265196 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5419396676487616149} + m_GameObject: {fileID: 5048206632594256402} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.398, y: -0.242, z: -0.06} - m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 7741920746089690377} - m_RootOrder: 2 + m_LocalPosition: {x: 0, y: 0.13499999, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 + m_Children: + - {fileID: 3755039154609302241} + - {fileID: 6679582736177907422} + - {fileID: 7566473728865432861} + - {fileID: 5521146392998997215} + m_Father: {fileID: 8645625734765961102} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &5255334288434809734 -MeshFilter: +--- !u!114 &480163635885148388 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5419396676487616149} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &1369930125958929065 + m_GameObject: {fileID: 5048206632594256402} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 20ac3005e19346a4296a8f6a73363832, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &5111218671440306906 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9209058213459650767} + - component: {fileID: 8820378468166149913} + - component: {fileID: 1547790019101272354} + - component: {fileID: 3197312481833515046} + m_Layer: 0 + m_Name: Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9209058213459650767 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5111218671440306906} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.0025} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4825048073929942150} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.5} + m_SizeDelta: {x: 2.27, y: 0.3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &8820378468166149913 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5419396676487616149} + m_GameObject: {fileID: 5111218671440306906} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 1 + m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -5043,7 +6015,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 1 + m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -5056,98 +6028,139 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &4730200966117401982 +--- !u!114 &1547790019101272354 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5419396676487616149} + m_GameObject: {fileID: 5111218671440306906} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: 0 - m_DescriptionYOffset: 0 - m_DescriptionText: POPUP_ACCOUNTS_SIGNOUT_BUTTON_DESCRIPTION - m_LocalizedDescription: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 15522362785837056 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionTextExtra: - m_LocalizedDescriptionExtra: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionActivateSpeed: 12 - m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: 6bd8742b210a6444e85b5c86f80ffebc, type: 3} - m_AtlasTexture: 1 - m_ToggleButton: 0 - m_LongPressReleaseButton: 0 - m_ButtonHasPressedAudio: 1 - m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.05 - m_HoverScale: 1.1 - m_HoverBoxColliderGrow: 0.2 - m_AddOverlay: 0 - m_Command: 27 - m_CommandParam: 1 - m_CommandParam2: -1 - m_RequiresPopup: 0 - m_CenterPopupOnButton: 0 - m_PopupOffset: {x: 0, y: 0, z: 0} - m_PopupText: - m_LocalizedPopup: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnDescription: - m_LocalizedToggleOnDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 - m_LinkedUIObject: {fileID: 0} - references: - version: 2 - RefIds: [] ---- !u!65 &712852433553107461 -BoxCollider: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Share with Google + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.915 + m_fontSizeBase: 1.915 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 8820378468166149913} + m_maskType: 0 +--- !u!114 &3197312481833515046 +MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5419396676487616149} - m_Material: {fileID: 0} - m_IsTrigger: 0 + m_GameObject: {fileID: 5111218671440306906} m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1, y: 1.0000001, z: 0.1} - m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &5427861527217224720 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034850 + references: + version: 2 + RefIds: + - rid: 3215294302546034850 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 1547790019101272354} + m_TrackedProperties: + items: + - rid: 3215294302546034851 + m_UpdateType: 0 + - rid: 3215294302546034851 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76137928887689216 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &5123262806756250226 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -5155,64 +6168,60 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6721629502030059466} - - component: {fileID: 744816621245303203} - - component: {fileID: 5315415322598248231} - - component: {fileID: 7149889851386687525} - - component: {fileID: 5696691115232969362} - m_Layer: 14 - m_Name: ConfirmationButton + - component: {fileID: 5208561973859313428} + - component: {fileID: 7929569316632562742} + - component: {fileID: 8052510225724890313} + m_Layer: 16 + m_Name: Background m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6721629502030059466 +--- !u!4 &5208561973859313428 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5427861527217224720} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: -0.434, z: -0.08} - m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} + m_GameObject: {fileID: 5123262806756250226} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.045} + m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 3215585481372348197} - - {fileID: 5384230387207399056} - m_Father: {fileID: 4825048073929942150} - m_RootOrder: 2 + m_Children: [] + m_Father: {fileID: 7893921961113671483} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &744816621245303203 +--- !u!33 &7929569316632562742 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5427861527217224720} + m_GameObject: {fileID: 5123262806756250226} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &5315415322598248231 +--- !u!23 &8052510225724890313 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5427861527217224720} - m_Enabled: 0 + m_GameObject: {fileID: 5123262806756250226} + m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} + - {fileID: 2100000, guid: 0ead8b8e224bcf040862605f5eed7211, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -5221,7 +6230,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 0 + m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -5234,134 +6243,90 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!65 &7149889851386687525 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5427861527217224720} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 0.01} - m_Center: {x: 0, y: 0, z: -0.01} ---- !u!114 &5696691115232969362 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5427861527217224720} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} - m_Name: - m_EditorClassIdentifier: - m_DescriptionType: -1 - m_DescriptionYOffset: 0 - m_DescriptionText: - m_LocalizedDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionTextExtra: - m_LocalizedDescriptionExtra: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_DescriptionActivateSpeed: 12 - m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 0} - m_AtlasTexture: 0 - m_ToggleButton: 0 - m_LongPressReleaseButton: 0 - m_ButtonHasPressedAudio: 1 - m_ZAdjustHover: -0.02 - m_ZAdjustClick: 0.05 - m_HoverScale: 1.1 - m_HoverBoxColliderGrow: 0.2 - m_AddOverlay: 0 - m_Command: 0 - m_CommandParam: -1 - m_CommandParam2: -1 - m_RequiresPopup: 0 - m_CenterPopupOnButton: 0 - m_PopupOffset: {x: 0, y: 0, z: 0} - m_PopupText: - m_LocalizedPopup: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnDescription: - m_LocalizedToggleOnDescription: - m_TableReference: - m_TableCollectionName: - m_TableEntryReference: - m_KeyId: 0 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_ToggleOnTexture: {fileID: 0} - m_AllowUnavailable: 0 - m_LinkedUIObject: {fileID: 0} - m_CommandIgnored: 0 - references: - version: 2 - RefIds: [] ---- !u!1 &5649685825589421528 -GameObject: +--- !u!1 &5301811399408650331 +GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6423827612944397798} + - component: {fileID: 1182483975005714860} + - component: {fileID: 6161329202685458175} + - component: {fileID: 6793863180280690477} m_Layer: 16 - m_Name: Sketchfab_ConfirmSignOutElements + m_Name: Dividor m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6423827612944397798 +--- !u!4 &1182483975005714860 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5649685825589421528} - 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_GameObject: {fileID: 5301811399408650331} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -0.151, z: -0.005} + m_LocalScale: {x: 0.01, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 8245513620673000221} - - {fileID: 4111961635044152573} - - {fileID: 6124434747938532598} - - {fileID: 4120900520909079457} - m_Father: {fileID: 149137880941958609} - m_RootOrder: 2 + m_Children: [] + m_Father: {fileID: 8645625735416266758} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &5866976969575595662 +--- !u!33 &6161329202685458175 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5301811399408650331} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6793863180280690477 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5301811399408650331} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &5315453881601467229 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -5369,34 +6334,1495 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 529827201699571093} + - component: {fileID: 1583684570998085433} + - component: {fileID: 5194402002804098867} + - component: {fileID: 8290597564335685000} + - component: {fileID: 2511332541157942654} m_Layer: 16 - m_Name: Sketchfab_SignedInElements + m_Name: Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &529827201699571093 -Transform: +--- !u!224 &1583684570998085433 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5866976969575595662} - 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_GameObject: {fileID: 5315453881601467229} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.009999992} + m_LocalScale: {x: 4, y: 4, z: 2.857143} m_ConstrainProportionsScale: 0 - m_Children: - - {fileID: 7314565180282189882} - - {fileID: 5621771514230647440} - - {fileID: 90735575297890764} - - {fileID: 5753424830644711893} - m_Father: {fileID: 149137880941958609} + m_Children: [] + m_Father: {fileID: 7893921961113671483} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &5921053250823053932 + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.20199999, y: 0} + m_SizeDelta: {x: 0.3, y: 0.15} + m_Pivot: {x: 0, y: 0.5} +--- !u!23 &5194402002804098867 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5315453881601467229} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &8290597564335685000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5315453881601467229} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: SIGN IN + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.86 + m_fontSizeBase: 0.86 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 5194402002804098867} + m_maskType: 0 +--- !u!114 &2511332541157942654 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5315453881601467229} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034846 + references: + version: 2 + RefIds: + - rid: 3215294302546034846 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 8290597564335685000} + m_TrackedProperties: + items: + - rid: 3215294302546034847 + m_UpdateType: 0 + - rid: 3215294302546034847 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 7933561489039360 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &5403683023149528791 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4704522903857296483} + - component: {fileID: 6212349802531054436} + - component: {fileID: 8705749884328266341} + - component: {fileID: 832193050044176433} + m_Layer: 0 + m_Name: Description + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4704522903857296483 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5403683023149528791} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.0025} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3457603883277620212} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.058} + m_SizeDelta: {x: 2.5, y: 0.7} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6212349802531054436 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5403683023149528791} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &8705749884328266341 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5403683023149528791} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 'Sign in to a Sketchfab account to allow + + uploading to Sketchfab.' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4286085240 + m_fontColor: {r: 0.47058824, g: 0.47058824, b: 0.47058824, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.275 + m_fontSizeBase: 1.275 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 6212349802531054436} + m_maskType: 0 +--- !u!114 &832193050044176433 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5403683023149528791} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034856 + references: + version: 2 + RefIds: + - rid: 3215294302546034856 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 8705749884328266341} + m_TrackedProperties: + items: + - rid: 3215294302546034857 + m_UpdateType: 0 + - rid: 3215294302546034857 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76138521224077312 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &5419396676487616149 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5446221535905318599} + - component: {fileID: 5255334288434809734} + - component: {fileID: 1369930125958929065} + - component: {fileID: 4730200966117401982} + - component: {fileID: 712852433553107461} + m_Layer: 16 + m_Name: Google_SignOut + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5446221535905318599 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5419396676487616149} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.398, y: -0.242, z: -0.06} + m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7741920746089690377} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5255334288434809734 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5419396676487616149} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1369930125958929065 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5419396676487616149} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &4730200966117401982 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5419396676487616149} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c6859eec74651247968d56b594ac313, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: POPUP_ACCOUNTS_SIGNOUT_BUTTON_DESCRIPTION + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 15522362785837056 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: 6bd8742b210a6444e85b5c86f80ffebc, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 27 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + references: + version: 2 + RefIds: [] +--- !u!65 &712852433553107461 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5419396676487616149} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &5427861527217224720 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6721629502030059466} + - component: {fileID: 744816621245303203} + - component: {fileID: 5315415322598248231} + - component: {fileID: 7149889851386687525} + - component: {fileID: 5696691115232969362} + m_Layer: 14 + m_Name: ConfirmationButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6721629502030059466 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5427861527217224720} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -0.434, z: -0.08} + m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 3215585481372348197} + - {fileID: 5384230387207399056} + m_Father: {fileID: 4825048073929942150} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &744816621245303203 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5427861527217224720} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5315415322598248231 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5427861527217224720} + m_Enabled: 0 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!65 &7149889851386687525 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5427861527217224720} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 0.01} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &5696691115232969362 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5427861527217224720} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: -1 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 0 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 0 + m_CommandParam: -1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!1 &5598400511041364639 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9214833072643761372} + - component: {fileID: 3899858871660736573} + - component: {fileID: 5558472698597662980} + m_Layer: 16 + m_Name: Icosa_ProfilePhoto + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9214833072643761372 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5598400511041364639} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.621, y: 0.11, z: -0.010000025} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 8639010961277564811} + - {fileID: 8146115410073256732} + m_Father: {fileID: 8994260351568061772} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3899858871660736573 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5598400511041364639} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5558472698597662980 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5598400511041364639} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: da7f0d43a22e3f24bbc517466728866f, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &5649685825589421528 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6423827612944397798} + m_Layer: 16 + m_Name: Sketchfab_ConfirmSignOutElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6423827612944397798 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5649685825589421528} + 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: + - {fileID: 8245513620673000221} + - {fileID: 4111961635044152573} + - {fileID: 6124434747938532598} + - {fileID: 4120900520909079457} + m_Father: {fileID: 149137880941958609} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &5866976969575595662 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 529827201699571093} + m_Layer: 16 + m_Name: Sketchfab_SignedInElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &529827201699571093 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5866976969575595662} + 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: + - {fileID: 7314565180282189882} + - {fileID: 5621771514230647440} + - {fileID: 90735575297890764} + - {fileID: 5753424830644711893} + m_Father: {fileID: 149137880941958609} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &5921053250823053932 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 798000693479046927} + - component: {fileID: 1551091070195428418} + - component: {fileID: 3372310621534254453} + m_Layer: 16 + m_Name: IconEnabled + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &798000693479046927 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921053250823053932} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} + m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1741904442304953456} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1551091070195428418 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921053250823053932} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3372310621534254453 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5921053250823053932} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3f00bfbfa437f004888833cf848a2416, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &5978289847150237173 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2403478784508930027} + - component: {fileID: 899810967416512585} + - component: {fileID: 4794093715842536848} + - component: {fileID: 8216506891502149021} + - component: {fileID: 2329669131580351156} + m_Layer: 16 + m_Name: Icosa_Info + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2403478784508930027 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5978289847150237173} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.046, y: 0.2620001, z: -0.025} + m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1000721931266647293} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &899810967416512585 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5978289847150237173} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &4794093715842536848 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5978289847150237173} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &8216506891502149021 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5978289847150237173} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: -1 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: bb4439b4279219b4db937c93530893ff, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.2 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 73 + m_CommandParam: 2 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!65 &2329669131580351156 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5978289847150237173} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &6286143077313149806 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5038995524656264454} + - component: {fileID: 7928834966209772352} + - component: {fileID: 3325864671476505547} + m_Layer: 16 + m_Name: Icosa_Logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5038995524656264454 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6286143077313149806} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} + m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1000721931266647293} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7928834966209772352 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6286143077313149806} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3325864671476505547 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6286143077313149806} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &6559160341867036930 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8844894437526037228} + - component: {fileID: 2123032824923807014} + - component: {fileID: 2425192678641992511} + - component: {fileID: 439132055978833134} + m_Layer: 0 + m_Name: Description + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8844894437526037228 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6559160341867036930} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.0025} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4825048073929942150} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0.058} + m_SizeDelta: {x: 2.5, y: 0.7} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &2123032824923807014 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6559160341867036930} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &2425192678641992511 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6559160341867036930} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: 'Sign in to a Google account to access + + Drive and YouTube support.' + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4286085240 + m_fontColor: {r: 0.47058824, g: 0.47058824, b: 0.47058824, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.275 + m_fontSizeBase: 1.275 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 2123032824923807014} + m_maskType: 0 +--- !u!114 &439132055978833134 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6559160341867036930} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034852 + references: + version: 2 + RefIds: + - rid: 3215294302546034852 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 2425192678641992511} + m_TrackedProperties: + items: + - rid: 3215294302546034853 + m_UpdateType: 0 + - rid: 3215294302546034853 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76138083292602368 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &6569154546886767970 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -5404,46 +7830,48 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 798000693479046927} - - component: {fileID: 1551091070195428418} - - component: {fileID: 3372310621534254453} + - component: {fileID: 6952797202003472198} + - component: {fileID: 3857003002993330428} + - component: {fileID: 5268851330295692068} + - component: {fileID: 4382059402420719972} + - component: {fileID: 3651181850301530278} m_Layer: 16 - m_Name: IconEnabled + m_Name: Google_Info m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &798000693479046927 +--- !u!4 &6952797202003472198 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921053250823053932} + m_GameObject: {fileID: 6569154546886767970} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} - m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} + m_LocalPosition: {x: -0.213, y: 0.2620001, z: -0.025} + m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1741904442304953456} - m_RootOrder: 0 + m_Father: {fileID: 1432546188030660464} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1551091070195428418 +--- !u!33 &3857003002993330428 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921053250823053932} + m_GameObject: {fileID: 6569154546886767970} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &3372310621534254453 +--- !u!23 &5268851330295692068 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5921053250823053932} + m_GameObject: {fileID: 6569154546886767970} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -5457,7 +7885,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 3f00bfbfa437f004888833cf848a2416, type: 2} + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -5479,7 +7907,99 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &6559160341867036930 +--- !u!114 &4382059402420719972 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6569154546886767970} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_DescriptionType: -1 + m_DescriptionYOffset: 0 + m_DescriptionText: + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 2800000, guid: bb4439b4279219b4db937c93530893ff, type: 3} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.05 + m_HoverScale: 1.2 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Command: 73 + m_CommandParam: 1 + m_CommandParam2: -1 + m_RequiresPopup: 0 + m_CenterPopupOnButton: 0 + m_PopupOffset: {x: 0, y: 0, z: 0} + m_PopupText: + m_LocalizedPopup: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnDescription: + m_LocalizedToggleOnDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_ToggleOnTexture: {fileID: 0} + m_AllowUnavailable: 0 + m_LinkedUIObject: {fileID: 0} + m_CommandIgnored: 0 + references: + version: 2 + RefIds: [] +--- !u!65 &3651181850301530278 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6569154546886767970} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1.0000001, z: 0.1} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &6655431747887542890 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -5487,51 +8007,50 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 8844894437526037228} - - component: {fileID: 2123032824923807014} - - component: {fileID: 2425192678641992511} - - component: {fileID: 439132055978833134} - m_Layer: 0 - m_Name: Description + - component: {fileID: 277837882360728660} + - component: {fileID: 3967617528463584047} + - component: {fileID: 3967593321760830211} + m_Layer: 16 + m_Name: Google_Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &8844894437526037228 +--- !u!224 &277837882360728660 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6559160341867036930} + m_GameObject: {fileID: 6655431747887542890} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.0025} + m_LocalPosition: {x: 0, y: 0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 4825048073929942150} - m_RootOrder: 1 + m_Father: {fileID: 1432546188030660464} + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0.058} - m_SizeDelta: {x: 2.5, y: 0.7} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!23 &2123032824923807014 + m_AnchoredPosition: {x: -0.8228, y: 0.3524501} + m_SizeDelta: {x: 0.34, y: 0.17} + m_Pivot: {x: 0, y: 1} +--- !u!23 &3967617528463584047 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6559160341867036930} + m_GameObject: {fileID: 6655431747887542890} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 @@ -5550,7 +8069,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 0 + m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -5560,13 +8079,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &2425192678641992511 +--- !u!114 &3967593321760830211 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6559160341867036930} + m_GameObject: {fileID: 6655431747887542890} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -5580,9 +8099,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 'Sign in to a Google account to access - - Drive and YouTube support.' + m_text: Google m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -5591,8 +8108,8 @@ MonoBehaviour: m_fontMaterials: [] m_fontColor32: serializedVersion: 2 - rgba: 4286085240 - m_fontColor: {r: 0.47058824, g: 0.47058824, b: 0.47058824, a: 1} + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -5609,15 +8126,15 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.275 - m_fontSizeBase: 1.275 + m_fontSize: 1.35 + m_fontSizeBase: 1.35 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 m_wordSpacing: 0 @@ -5652,49 +8169,9 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 2123032824923807014} + m_renderer: {fileID: 3967617528463584047} m_maskType: 0 ---- !u!114 &439132055978833134 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6559160341867036930} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} - m_Name: - m_EditorClassIdentifier: - m_TrackedObjects: - - rid: 3215294302546034852 - references: - version: 2 - RefIds: - - rid: 3215294302546034852 - type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, - asm: Unity.Localization} - data: - m_Target: {fileID: 2425192678641992511} - m_TrackedProperties: - items: - - rid: 3215294302546034853 - m_UpdateType: 0 - - rid: 3215294302546034853 - type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, - asm: Unity.Localization} - data: - m_Localized: - m_TableReference: - m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 - m_TableEntryReference: - m_KeyId: 76138083292602368 - m_Key: - m_FallbackState: 0 - m_WaitForCompletion: 0 - m_LocalVariables: [] - m_PropertyPath: m_text ---- !u!1 &6569154546886767970 +--- !u!1 &6696247793707346302 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -5702,48 +8179,48 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 6952797202003472198} - - component: {fileID: 3857003002993330428} - - component: {fileID: 5268851330295692068} - - component: {fileID: 4382059402420719972} - - component: {fileID: 3651181850301530278} + - component: {fileID: 3716870166160541890} + - component: {fileID: 1778902779340506197} + - component: {fileID: 596356743151083422} + - component: {fileID: 4454298294767258161} + - component: {fileID: 4808408806908219571} m_Layer: 16 - m_Name: Google_Info + m_Name: Icosa_SignOut m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &6952797202003472198 +--- !u!4 &3716870166160541890 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6569154546886767970} + m_GameObject: {fileID: 6696247793707346302} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.213, y: 0.2620001, z: -0.025} - m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_LocalPosition: {x: 1.041, y: 0.247, z: -0.02499998} + m_LocalScale: {x: 0.25, y: 0.24999991, z: 0.2499999} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1432546188030660464} - m_RootOrder: 3 + m_Father: {fileID: 8994260351568061772} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &3857003002993330428 +--- !u!33 &1778902779340506197 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6569154546886767970} + m_GameObject: {fileID: 6696247793707346302} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &5268851330295692068 +--- !u!23 &596356743151083422 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6569154546886767970} + m_GameObject: {fileID: 6696247793707346302} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -5779,26 +8256,26 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &4382059402420719972 +--- !u!114 &4454298294767258161 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6569154546886767970} + m_GameObject: {fileID: 6696247793707346302} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fc12adcafa62a22458e635d2ee7e78b5, type: 3} m_Name: m_EditorClassIdentifier: - m_DescriptionType: -1 + m_DescriptionType: 0 m_DescriptionYOffset: 0 - m_DescriptionText: + m_DescriptionText: POPUP_ACCOUNTS_SIGNOUT_BUTTON_DESCRIPTION m_LocalizedDescription: m_TableReference: - m_TableCollectionName: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 0 + m_KeyId: 15522362785837056 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -5815,18 +8292,18 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: bb4439b4279219b4db937c93530893ff, type: 3} + m_ButtonTexture: {fileID: 2800000, guid: 42f600d7b08f5514ebdcf4604e0cfa6c, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 m_ButtonHasPressedAudio: 1 m_ZAdjustHover: -0.02 m_ZAdjustClick: 0.05 - m_HoverScale: 1.2 + m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 73 - m_CommandParam: 1 + m_Command: 98 + m_CommandParam: 2 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -5854,24 +8331,24 @@ MonoBehaviour: m_ToggleOnTexture: {fileID: 0} m_AllowUnavailable: 0 m_LinkedUIObject: {fileID: 0} - m_CommandIgnored: 0 + m_CommandIgnored: 1 references: version: 2 RefIds: [] ---- !u!65 &3651181850301530278 +--- !u!65 &4808408806908219571 BoxCollider: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6569154546886767970} + m_GameObject: {fileID: 6696247793707346302} m_Material: {fileID: 0} m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} ---- !u!1 &6655431747887542890 +--- !u!1 &6877995767082855847 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -5879,43 +8356,126 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 277837882360728660} - - component: {fileID: 3967617528463584047} - - component: {fileID: 3967593321760830211} + - component: {fileID: 3554585349599419749} + - component: {fileID: 2124419336652034620} + - component: {fileID: 1855359805432446158} m_Layer: 16 - m_Name: Google_Text + m_Name: Sketchfab_BackPlate m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &277837882360728660 +--- !u!4 &3554585349599419749 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6877995767082855847} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0.53499997, y: 0.2550001, z: -0.005} + m_LocalScale: {x: 32, y: 32, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 534589999851858800} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2124419336652034620 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6877995767082855847} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &1855359805432446158 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6877995767082855847} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &6896021414389893206 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2434770951962534735} + - component: {fileID: 2805088410587308415} + - component: {fileID: 6196538126404319155} + m_Layer: 0 + m_Name: Icosa_Name + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2434770951962534735 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6655431747887542890} + m_GameObject: {fileID: 6896021414389893206} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_LocalPosition: {x: 0, y: 0, z: -0.010000003} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 1432546188030660464} + m_Father: {fileID: 8994260351568061772} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.8228, y: 0.3524501} - m_SizeDelta: {x: 0.34, y: 0.17} - m_Pivot: {x: 0, y: 1} ---- !u!23 &3967617528463584047 + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0.059, y: -0.477} + m_SizeDelta: {x: 1.1, y: 0.35} + m_Pivot: {x: 0, y: 0} +--- !u!23 &2805088410587308415 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6655431747887542890} + m_GameObject: {fileID: 6896021414389893206} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -5941,7 +8501,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 1 + m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -5951,13 +8511,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &3967593321760830211 +--- !u!114 &6196538126404319155 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6655431747887542890} + m_GameObject: {fileID: 6896021414389893206} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -5971,7 +8531,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Google + m_text: Longnamefirstname Longnamesurname m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -5998,25 +8558,25 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.35 - m_fontSizeBase: 1.35 + m_fontSize: 1.3 + m_fontSizeBase: 1.3 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 1 + m_HorizontalAlignment: 2 m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 m_wordSpacing: 0 - m_lineSpacing: 0 + m_lineSpacing: -22 m_lineSpacingMax: 0 m_paragraphSpacing: 0 m_charWidthMaxAdj: 0 m_enableWordWrapping: 1 m_wordWrappingRatios: 0.4 - m_overflowMode: 0 + m_overflowMode: 3 m_linkedTextComponent: {fileID: 0} parentLinkedComponent: {fileID: 0} m_enableKerning: 1 @@ -6031,7 +8591,7 @@ MonoBehaviour: m_uvLineOffset: 0 m_geometrySortingOrder: 0 m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 + m_VertexBufferAutoSizeReduction: 1 m_useMaxVisibleDescender: 1 m_pageToDisplay: 1 m_margin: {x: 0, y: 0, z: 0, w: 0} @@ -6041,9 +8601,9 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 3967617528463584047} + m_renderer: {fileID: 2805088410587308415} m_maskType: 0 ---- !u!1 &6877995767082855847 +--- !u!1 &7039719445365502712 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -6051,46 +8611,44 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 3554585349599419749} - - component: {fileID: 2124419336652034620} - - component: {fileID: 1855359805432446158} + - component: {fileID: 7505721524787016835} + - component: {fileID: 4823882021682760176} + - component: {fileID: 2425200055429802179} + - component: {fileID: 2982020382161640361} m_Layer: 16 - m_Name: Sketchfab_BackPlate + m_Name: Icosa_Text m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &3554585349599419749 -Transform: +--- !u!224 &7505721524787016835 +RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6877995767082855847} - m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} - m_LocalPosition: {x: 0.53499997, y: 0.2550001, z: -0.005} - m_LocalScale: {x: 32, y: 32, z: 20} + m_GameObject: {fileID: 7039719445365502712} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 534589999851858800} - m_RootOrder: 0 + m_Father: {fileID: 1675920430848132177} + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &2124419336652034620 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6877995767082855847} - m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} ---- !u!23 &1855359805432446158 + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.429, y: 0.3524501} + m_SizeDelta: {x: 0.52, y: 0.2} + m_Pivot: {x: 0, y: 1} +--- !u!23 &4823882021682760176 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6877995767082855847} + m_GameObject: {fileID: 7039719445365502712} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 @@ -6104,7 +8662,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 8fe8230ee7ae32a4eb7fe6d5df34ebd4, type: 2} + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -6116,7 +8674,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 0 + m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -6126,6 +8684,138 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &2425200055429802179 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7039719445365502712} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Sign Out? + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.35 + m_fontSizeBase: 1.35 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 256 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 4823882021682760176} + m_maskType: 0 +--- !u!114 &2982020382161640361 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7039719445365502712} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 8021623883140169743 + references: + version: 2 + RefIds: + - rid: 8021623883140169743 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 2425200055429802179} + m_TrackedProperties: + items: + - rid: 8021623883140169744 + m_UpdateType: 0 + - rid: 8021623883140169744 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 15531555001511936 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text --- !u!1 &7241757417913730636 GameObject: m_ObjectHideFlags: 0 @@ -6416,35 +9106,248 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 5851412377445962644} + m_renderer: {fileID: 5851412377445962644} + m_maskType: 0 +--- !u!114 &495762216464883552 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7389499574749059798} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034854 + references: + version: 2 + RefIds: + - rid: 3215294302546034854 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 5204209675494704913} + m_TrackedProperties: + items: + - rid: 3215294302546034855 + m_UpdateType: 0 + - rid: 3215294302546034855 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 147094550989828096 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text +--- !u!1 &7395606162817237834 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4750169147585981226} + - component: {fileID: 7886381469928682462} + - component: {fileID: 2362981489817548973} + - component: {fileID: 5340067591165246768} + m_Layer: 16 + m_Name: Got it + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4750169147585981226 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7395606162817237834} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.029333334} + m_LocalScale: {x: 2.9528668, y: 5.9057336, z: 2.9528668} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7500850895307564819} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.141, y: 0} + m_SizeDelta: {x: 0.25, y: 0.15} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &7886381469928682462 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7395606162817237834} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &2362981489817548973 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7395606162817237834} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: GOT IT + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 0.64 + m_fontSizeBase: 0.64 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 7886381469928682462} m_maskType: 0 ---- !u!114 &495762216464883552 +--- !u!114 &5340067591165246768 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7389499574749059798} + m_GameObject: {fileID: 7395606162817237834} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} m_Name: m_EditorClassIdentifier: m_TrackedObjects: - - rid: 3215294302546034854 + - rid: 3215294302546034844 references: version: 2 RefIds: - - rid: 3215294302546034854 + - rid: 3215294302546034844 type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, asm: Unity.Localization} data: - m_Target: {fileID: 5204209675494704913} + m_Target: {fileID: 2362981489817548973} m_TrackedProperties: items: - - rid: 3215294302546034855 + - rid: 3215294302546034845 m_UpdateType: 0 - - rid: 3215294302546034855 + - rid: 3215294302546034845 type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, asm: Unity.Localization} data: @@ -6452,7 +9355,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 147094550989828096 + m_KeyId: 76036746777255936 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -6678,40 +9581,296 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &5383840309452991305 +--- !u!224 &5383840309452991305 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7725770829448276136} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 534589999851858800} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0.428, y: 0.3524501} + m_SizeDelta: {x: 20, y: 5} + m_Pivot: {x: 0, y: 1} +--- !u!23 &1591778397208625653 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7725770829448276136} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &3852620365131538827 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7725770829448276136} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Sketchfab + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 1.35 + m_fontSizeBase: 1.35 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 256 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 0 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + _SortingLayer: 0 + _SortingLayerID: 0 + _SortingOrder: 0 + m_hasFontAssetChanged: 0 + m_renderer: {fileID: 1591778397208625653} + m_maskType: 0 +--- !u!1 &7739595941385861960 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8639010961277564811} + - component: {fileID: 751210734990785936} + - component: {fileID: 1903627422300433447} + m_Layer: 16 + m_Name: PhotoBGW + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8639010961277564811 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7739595941385861960} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.0022857143} + m_LocalScale: {x: 1.1, y: 1.1, z: 1.1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 9214833072643761372} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &751210734990785936 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7739595941385861960} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1903627422300433447 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7739595941385861960} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0c298b4a41e7c5b4daf56d8987a83788, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &7769069705588964375 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8304354692682796117} + - component: {fileID: 6573425907134553916} + - component: {fileID: 8994872189112949042} + - component: {fileID: 8588625045096316444} + m_Layer: 0 + m_Name: Title + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8304354692682796117 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7725770829448276136} + m_GameObject: {fileID: 7769069705588964375} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.01} + m_LocalPosition: {x: 0, y: 0, z: -0.0025} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 534589999851858800} - m_RootOrder: 2 + m_Father: {fileID: 6373137851377895237} + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0.428, y: 0.3524501} - m_SizeDelta: {x: 20, y: 5} - m_Pivot: {x: 0, y: 1} ---- !u!23 &1591778397208625653 + m_AnchoredPosition: {x: 0, y: 0.5} + m_SizeDelta: {x: 2.27, y: 0.3} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!23 &6573425907134553916 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7725770829448276136} + m_GameObject: {fileID: 7769069705588964375} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 1 + m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 @@ -6730,7 +9889,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 1 + m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -6740,13 +9899,13 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &3852620365131538827 +--- !u!114 &8994872189112949042 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7725770829448276136} + m_GameObject: {fileID: 7769069705588964375} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} @@ -6760,7 +9919,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Sketchfab + m_text: Share to Sketchfab m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -6787,15 +9946,15 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 1.35 - m_fontSizeBase: 1.35 + m_fontSize: 1.915 + m_fontSizeBase: 1.915 m_fontWeight: 400 m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 m_fontStyle: 0 - m_HorizontalAlignment: 1 - m_VerticalAlignment: 256 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 m_wordSpacing: 0 @@ -6830,8 +9989,48 @@ MonoBehaviour: _SortingLayerID: 0 _SortingOrder: 0 m_hasFontAssetChanged: 0 - m_renderer: {fileID: 1591778397208625653} + m_renderer: {fileID: 6573425907134553916} m_maskType: 0 +--- !u!114 &8588625045096316444 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769069705588964375} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 69beb381e244f92449b8c4cf954630e9, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TrackedObjects: + - rid: 3215294302546034854 + references: + version: 2 + RefIds: + - rid: 3215294302546034854 + type: {class: TrackedUGuiGraphic, ns: UnityEngine.Localization.PropertyVariants.TrackedObjects, + asm: Unity.Localization} + data: + m_Target: {fileID: 8994872189112949042} + m_TrackedProperties: + items: + - rid: 3215294302546034855 + m_UpdateType: 0 + - rid: 3215294302546034855 + type: {class: LocalizedStringProperty, ns: UnityEngine.Localization.PropertyVariants.TrackedProperties, + asm: Unity.Localization} + data: + m_Localized: + m_TableReference: + m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 + m_TableEntryReference: + m_KeyId: 76138395621449728 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_PropertyPath: m_text --- !u!1 &7844204848832429112 GameObject: m_ObjectHideFlags: 0 @@ -6915,6 +10114,89 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &7987582482775903940 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8146115410073256732} + - component: {fileID: 1622672940972688331} + - component: {fileID: 6831363116423794729} + m_Layer: 16 + m_Name: Sketchfab_Logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8146115410073256732 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7987582482775903940} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.37, y: -0.324, z: -0.025} + m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 9214833072643761372} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1622672940972688331 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7987582482775903940} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6831363116423794729 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7987582482775903940} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &8025577052483214110 GameObject: m_ObjectHideFlags: 0 @@ -6939,9 +10221,9 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8025577052483214110} 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_LocalPosition: {x: 0, y: 0.13499999, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 9209058213459650767} - {fileID: 8844894437526037228} @@ -7601,14 +10883,14 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8374217926999613278} 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_LocalPosition: {x: -0.477, y: 0.135, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 9062749716723118463} - {fileID: 1698918594952708344} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 5 + m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8485803142575213209 GameObject: @@ -8384,7 +11666,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587409793350} - m_Mesh: {fileID: 4300000, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} + m_Mesh: {fileID: 4300000, guid: 9732ef7c98f40b34f8094be4fc6cd363, type: 3} --- !u!23 &8621276525838995664 MeshRenderer: m_ObjectHideFlags: 0 @@ -8463,6 +11745,7 @@ Transform: - {fileID: 4825048073929942150} - {fileID: 3576301794080265196} - {fileID: 3457603883277620212} + - {fileID: 6373137851377895237} - {fileID: 1766577842652504206} - {fileID: 8645625735778197046} m_Father: {fileID: 0} @@ -8507,16 +11790,22 @@ MonoBehaviour: m_SketchfabSignedInElements: {fileID: 5866976969575595662} m_SketchfabSignedOutElements: {fileID: 7620355729290749329} m_SketchfabConfirmSignOutElements: {fileID: 5649685825589421528} + m_IcosaSignedInElements: {fileID: 162347472523419310} + m_IcosaSignedOutElements: {fileID: 3893413551512185020} + m_IcosaConfirmSignOutElements: {fileID: 2027372909421917180} m_GooglePhoto: {fileID: 8621276525763300312} m_SketchfabPhoto: {fileID: 6282619306390002852} + m_IcosaPhoto: {fileID: 5558472698597662980} m_GoogleNameText: {fileID: 8530386015597152794} m_SketchfabNameText: {fileID: 3509174102411407934} + m_IcosaNameText: {fileID: 6196538126404319155} m_GenericPhoto: {fileID: 2800000, guid: edd2e08d6f610b840828462609546014, type: 3} m_Accounts: {fileID: 8644332589018115638} m_TakeOffHeadset: {fileID: 8644332588631379036} m_GoogleInfoElements: {fileID: 8025577052483214110} m_DriveInfoElements: {fileID: 5048206632594256402} m_SketchfabInfoElements: {fileID: 2181120061148835783} + m_IcosaInfoElements: {fileID: 8854873750265266201} m_UnavailableElements: {fileID: 8374217926999613278} m_DriveSyncEnabledElements: {fileID: 3439436514154816488} m_DriveSyncDisabledElements: {fileID: 8663098325165672148} @@ -8540,8 +11829,8 @@ BoxCollider: m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 - m_Size: {x: 2.6, y: 1.6, z: 0.01} - m_Center: {x: 0.05, y: -0.05, z: -0.01} + m_Size: {x: 2.18, y: 2.3, z: 0.01} + m_Center: {x: -0.01, y: -0.36, z: -0.01} --- !u!114 &8533537593755566908 MonoBehaviour: m_ObjectHideFlags: 0 @@ -8578,14 +11867,14 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587709734578} 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_LocalPosition: {x: 0, y: -0.349, z: 0} + m_LocalScale: {x: 1.21, y: 1.21, z: 1.21} + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 8645625733637814676} - {fileID: 8645625740661137770} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 6 + m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8644332588524666284 GameObject: @@ -8627,7 +11916,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332588524666284} - m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} + m_Mesh: {fileID: 4300002, guid: 9732ef7c98f40b34f8094be4fc6cd363, type: 3} --- !u!23 &8621276526632499898 MeshRenderer: m_ObjectHideFlags: 0 @@ -8694,9 +11983,9 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332588631379036} 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_LocalPosition: {x: 0, y: 0.13499999, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 1799102450530444095} - {fileID: 8645625734146380956} @@ -8941,14 +12230,15 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332589018115638} 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_LocalPosition: {x: 0, y: 0.13499999, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 m_Children: - {fileID: 6204130650882258485} - {fileID: 1182483975005714860} - {fileID: 5854179938074699431} - {fileID: 149137880941958609} + - {fileID: 7706333168622570552} m_Father: {fileID: 8645625734765961102} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -9687,6 +12977,40 @@ BoxCollider: serializedVersion: 2 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} +--- !u!1 &8854873750265266201 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6373137851377895237} + m_Layer: 16 + m_Name: IcosaInfoElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &6373137851377895237 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8854873750265266201} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: -0.965, z: 0} + m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} + m_ConstrainProportionsScale: 1 + m_Children: + - {fileID: 8304354692682796117} + - {fileID: 7219417712597752172} + - {fileID: 7500850895307564819} + m_Father: {fileID: 8645625734765961102} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &9067465071141378777 GameObject: m_ObjectHideFlags: 0 @@ -9756,6 +13080,89 @@ Transform: m_Father: {fileID: 5854179938074699431} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &9188801593698870886 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 906844544403122228} + - component: {fileID: 2379879017139536075} + - component: {fileID: 4874927894839058143} + m_Layer: 16 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &906844544403122228 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9188801593698870886} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.045} + m_LocalScale: {x: 2, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5520953697447238279} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &2379879017139536075 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9188801593698870886} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &4874927894839058143 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9188801593698870886} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0ead8b8e224bcf040862605f5eed7211, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1001 &4530350375719397069 PrefabInstance: m_ObjectHideFlags: 0 From 54a59f583e30af01591f2aa378c4cd8c6b730c6c Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 6 Nov 2023 16:33:43 +0000 Subject: [PATCH 005/105] Update IcosaService.cs --- Assets/Scripts/Sharing/IcosaService.cs | 199 +++++-------------------- 1 file changed, 37 insertions(+), 162 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 4b8aa67357..54cd3a009e 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -15,23 +15,20 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; -using System.Threading.Tasks; using JetBrains.Annotations; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Org.OpenAPITools.Api; using Org.OpenAPITools.Client; using Org.OpenAPITools.Model; using UnityEngine; -using UnityEngine.Assertions; namespace TiltBrush { class IcosaService { - public const string kModelLandingPage = "https://sketchfab.com/3d-models/"; - const string kApiHost = "https://api.sketchfab.com"; + public const string kModelLandingPage = "https://icosa.gallery/"; + const string kApiHost = "https://api.icosa.gallery"; + /// A paginated response, for use with GetNextPageAsync() public interface Paginated @@ -99,14 +96,10 @@ public class UserRelated public string uri; } - /// Options are constructed according to: - /// https://docs.sketchfab.com/data-api/v3/index.html#!/models/patch_v3_models_uid_options [Serializable, UsedImplicitly] public class Options { - // shading (string, optional), public Dictionary background; - // orientation (string, optional), public void SetBackgroundColor(Color color) { @@ -115,83 +108,30 @@ public void SetBackgroundColor(Color color) } } - private readonly OAuth2Identity m_identity; - - public IcosaService(OAuth2Identity identity) - { - m_identity = identity; - } + private readonly LoginToken m_accessToken; - /// Returns null if there is no next page. - /// It's assumed that you've used the results from the current page, meaning - /// that the task for fetching the current page must be completed. - public Task GetNextPageAsync(Task task) where T : Paginated + public IcosaService(LoginToken token) { - // Ensure task.Result does not block. - // If this function were async we could await, but then couldn't return null. - // In C# 8 this could be switched to use async enumerators. - if (!task.IsCompleted) - { - throw new ArgumentException("page"); - } - var uri = task.Result.NextUri; - if (string.IsNullOrEmpty(uri)) { return null; } - return new WebRequest(uri, m_identity, "GET") - .SendAsync() - .ContinueWith(antecedent => antecedent.Result.Deserialize()); + m_accessToken = token; } - public async Task GetUserInfo() - { - var result = await new WebRequest($"{kApiHost}/v3/me", m_identity, "GET").SendAsync(); - return result.Deserialize(); - } [Serializable, UsedImplicitly] public class MeDetail { - // subscriptionCount (integer, optional), - // followerCount (integer, optional), public string uid; public string modelsUrl; - // likeCount (integer, optional), - // facebookUsername (string, optional), - // biography (string, optional), - // city (string, optional), - // tagline (string, optional), public int modelCount; - // twitterUsername (string, optional), public string email; public string website; - // billingCycle (string, optional), - // followersUrl (string, optional), - // collectionCount (integer, optional), - // dateJoined (string, optional), public string account; public string displayName; public string profileUrl; - // followingsUrl (string, optional), - // skills (Array[SkillDetail], optional), - // country (string, optional), public string uri; - // apiToken (string, optional), public string username; - // linkedinUsername (string, optional), - // likesUrl (string, optional), public AvatarRelated avatar; public bool isLimited; - // followingCount (integer, optional), - // collectionsUrl (string, optional) } - - /// Returns the metadata for a model. - /// See also GetModelDownload(). - public async Task GetModelDetail(string uid) - { - var result = await new WebRequest( - $"{kApiHost}/v3/models/{uid}", m_identity, "GET").SendAsync(); - return result.Deserialize(); - } [Serializable, UsedImplicitly] public class ModelDetail { @@ -211,40 +151,22 @@ public class File public string uid; public TagRelated[] tags; public string viewerUrl; - // categories (Array[CategoriesRelated], optional), public string publishedAt; public int likeCount; public int commentCount; public int vertexCount; public UserRelated user; - // animationCount (integer, optional), public bool isDownloadable; public string description; - // viewCount (integer, optional), public string name; public JObject license; // license (object, optional), public string editorUrl; - // soundCount (integer, optional), - // isAgeRestricted (boolean, optional), public string uri; public int faceCount; - // ext (string,null, optional), - // staffpickedAt (string,null, optional), - // createdAt (string, optional), public ThumbnailsRelated thumbnails; - // downloadCount (integer, optional), - // embedUrl (string, optional), public JObject options; // options (object, optional) } - - /// Returns a temporary URL where a model can be downloaded from - public async Task GetModelDownload(string uid) - { - var result = await new WebRequest( - $"{kApiHost}/v3/models/{uid}/download", m_identity, "GET").SendAsync(); - return result.Deserialize(); - } [Serializable, UsedImplicitly] public class ModelDownload { @@ -258,48 +180,25 @@ public class inline_model_1 public inline_model_1 gltf; } - - public Task GetMeLikes() - { - return new WebRequest($"{kApiHost}/v3/me/likes", m_identity, "GET") - .SendAsync() - .ContinueWith(antecedent => antecedent.Result.Deserialize()); - } // Documentation for this API is incorrect, so this was created by inspection of the result // (and therefore may itself have errors) [Serializable, UsedImplicitly] - public class ModelLikesResponse : Paginated + public class xxModelLikesResponse : Paginated { [Serializable, UsedImplicitly] public class ModelLikesList { public string uid; - // public TagRelated[] tags; public string viewerUrl; public bool isProtected; - // categories (Array[CategoriesRelated], optional), - // public string publishedAt; - // public int likeCount; - // public int commentCount; - // public int viewCount; public int vertexCount; public UserRelated user; public bool isDownloadable; public string description; - // public int animationCount; public string name; - // public int soundCount; - // public bool isAgeRestricted; public string uri; public int faceCount; - // staffpickedAt (string,null, optional), public ThumbnailsRelated thumbnails; - // public string embedUrl; - - // public CategoriesRelated categories; - // public string createdAt; - // public string license; // not documented and sometimes null? - // public ?string? price; } public string previous; // uri public string next; // uri @@ -309,50 +208,6 @@ public class ModelLikesList string Paginated.PreviousUri => previous; } - // TODO: /v3/search and /v3/me/search? - // The parameters to the search functions are the same, except /v3/me/search omits - // the "username" parameter, so maybe we can have a single function which wraps both. - - // - /// Pass: - /// temporaryDirectory - if passed, caller is responsible for cleaning it up - public async Task CreateModel( - string name, - string zipPath, IProgress progress, CancellationToken token, - Options options = null, string temporaryDirectory = null) - { - - // No compression because it's a compressed .zip already - WebRequest uploader = new WebRequest( - $"{kApiHost}/v3/models", m_identity, "POST", compress: false); - - var moreParams = new List<(string, string)> - { - ("name", name), - ("source", "open-brush"), - ("private", "true"), // TODO: remove when this feature is not secret - // https://docs.sketchfab.com/data-api/v3/index.html#!/models/post_v3_models - // "Enables 2D view in model inspector. All downloadable models must have isInspectable - // enabled." - ("isInspectable", "true"), - // ??? https://docs.sketchfab.com/data-api/v3/index.html#!/licenses/get_v3_licenses - ("license", "by-sa"), - // This is how you specify multiple tags: - // ("tags", "[\"tiltbrush\", \"some-other-tag\"]"), - ("tags", "[\"openbrush\", \"tiltbrush\"]"), - ("isPublished", "false"), - // ("description", "Dummy description"), - }; - if (options != null) - { - moreParams.Add(("options", JsonConvert.SerializeObject(options))); - } - uploader.ProgressObject = progress; - var reply = await uploader.SendNamedDataAsync( - "modelFile", File.OpenRead(zipPath), Path.GetFileName(zipPath), "application/zip", - moreParams: moreParams, token, temporaryDirectory); - return reply.Deserialize(); - } [Serializable, UsedImplicitly] public struct CreateResponse { @@ -360,20 +215,38 @@ public struct CreateResponse public string uri; } + public LoginToken TestLogin(string deviceCode) + { + Configuration config = new Configuration(); + config.BasePath = kApiHost; + var apiInstance = new LoginApi(config); + try + { + LoginToken result = apiInstance.DeviceLoginLoginDeviceLoginPost(deviceCode); + Debug.Log(result.AccessToken); + + return new LoginToken(result.AccessToken); + } + catch (ApiException e) + { + Debug.Log("Exception when calling LoginApi.LoginTokenPost: " + e.Message); + Debug.Log("Status Code: " + e.ErrorCode); + Debug.Log(e.StackTrace); + throw; + } + } + public void TestUpload(LoginToken token, List files) { Configuration config = new Configuration(); - config.BasePath = "https://api.icosa.gallery"; + config.BasePath = kApiHost; config.AccessToken = token.AccessToken; var apiInstance = new AssetsApi(config); - try { - // Upload New Assets - var result = apiInstance.UploadNewAssetsAssetsPost(files); - Debug.Log(result); + var foo = apiInstance.UploadNewAssetsAssetsPost(files); } - catch (ApiException e) + catch (ApiException e) { Debug.Log("Exception when calling AssetsApi.UploadNewAssetsAssetsPost: " + e.Message); Debug.Log("Status Code: " + e.ErrorCode); @@ -384,21 +257,23 @@ public void TestUpload(LoginToken token, List files) public LoginToken TestLogin() { Configuration config = new Configuration(); - config.BasePath = "https://api.icosa.gallery"; + config.BasePath = kApiHost; var apiInstance = new LoginApi(config); - var username = "andy@andybak.net"; // string | - var password = "clavoi23"; // string | + var username = "andy@andybak.net"; + var password = "foobar"; try { - LoginToken result = apiInstance.LoginLoginPost(username, password, grantType, scope, clientId, clientSecret); + LoginToken result = apiInstance.LoginLoginPost(username, password); Debug.Log(result.AccessToken); + return result; } catch (ApiException e) { Debug.Log("Exception when calling LoginApi.LoginLoginPost: " + e.Message); Debug.Log("Status Code: " + e.ErrorCode); Debug.Log(e.StackTrace); + return null; } } } From 18383740ac9e9ab7cba6ed32838c660dd44160d3 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 6 Nov 2023 17:37:22 +0000 Subject: [PATCH 006/105] Icosa UI for accounts panel --- Assets/Materials/IcosaIcon.mat | 81 +++++++++++++ Assets/Materials/IcosaIcon.mat.meta | 8 ++ .../PopUps/PopUpWindow_Accounts.prefab | 14 +-- .../PopUps/PopUpWindow_Accounts_Mobile.prefab | 10 ++ Assets/Scenes/Main.unity | 53 +++++++++ Assets/Scripts/SecretsConfig.cs | 3 +- .../Strings/Strings Shared Data.asset | 8 ++ .../Localization/Strings/Strings_en.asset | 18 ++- Assets/Textures/Trademarked/icosalogo.png | Bin 0 -> 72443 bytes .../Textures/Trademarked/icosalogo.png.meta | 110 ++++++++++++++++++ 10 files changed, 294 insertions(+), 11 deletions(-) create mode 100644 Assets/Materials/IcosaIcon.mat create mode 100644 Assets/Materials/IcosaIcon.mat.meta create mode 100644 Assets/Textures/Trademarked/icosalogo.png create mode 100644 Assets/Textures/Trademarked/icosalogo.png.meta diff --git a/Assets/Materials/IcosaIcon.mat b/Assets/Materials/IcosaIcon.mat new file mode 100644 index 0000000000..df7907bd0e --- /dev/null +++ b/Assets/Materials/IcosaIcon.mat @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: IcosaIcon + m_Shader: {fileID: 4800000, guid: e9594cae8deb0e44ba4c46191f40b4f9, type: 3} + m_ValidKeywords: [] + m_InvalidKeywords: + - _EMISSION + m_LightmapFlags: 1 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: ca2b2689a21743448be1a5306c61e90b, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Materials/IcosaIcon.mat.meta b/Assets/Materials/IcosaIcon.mat.meta new file mode 100644 index 0000000000..e0d5d451e5 --- /dev/null +++ b/Assets/Materials/IcosaIcon.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e7a4b5a3979042447af7f344f59498c7 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab index 1cb3ea0e64..03bccd72bd 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab @@ -218,7 +218,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Sketchfab + m_text: Icosa m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -476,9 +476,9 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: 'Sign in to a Sketchfab account to allow + m_text: 'Sign in to an Icosa account to allow - uploading to Sketchfab.' + uploading to Icosa.' m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -584,7 +584,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 76138521224077312 + m_KeyId: 164911761917042688 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 @@ -7585,7 +7585,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + - {fileID: 2100000, guid: e7a4b5a3979042447af7f344f59498c7, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -9919,7 +9919,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Share to Sketchfab + m_text: Share to Icosa m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -10025,7 +10025,7 @@ MonoBehaviour: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 m_TableEntryReference: - m_KeyId: 76138395621449728 + m_KeyId: 164911966586494976 m_Key: m_FallbackState: 0 m_WaitForCompletion: 0 diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts_Mobile.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts_Mobile.prefab index b06aac7fc3..8638f89bed 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts_Mobile.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts_Mobile.prefab @@ -780,6 +780,11 @@ PrefabInstance: m_Modification: m_TransformParent: {fileID: 0} m_Modifications: + - target: {fileID: 1766577842652504206, guid: 4abd2477ca9b56d4ebbc36af78d00b5d, + type: 3} + propertyPath: m_RootOrder + value: 7 + objectReference: {fileID: 0} - target: {fileID: 8529915923179660036, guid: 4abd2477ca9b56d4ebbc36af78d00b5d, type: 3} propertyPath: m_ConfirmLoginElements @@ -855,6 +860,11 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 8645625735778197046, guid: 4abd2477ca9b56d4ebbc36af78d00b5d, + type: 3} + propertyPath: m_RootOrder + value: 8 + objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 4abd2477ca9b56d4ebbc36af78d00b5d, type: 3} --- !u!4 &567307426110797473 stripped diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index e62024eb5b..31e04b1b95 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -10080,6 +10080,7 @@ MonoBehaviour: m_ShaderWarmup: {fileID: 2094189264} m_GoogleIdentity: {fileID: 1209128957} m_SketchfabIdentity: {fileID: 2095572865} + m_IcosaIdentity: {fileID: 1611595292} --- !u!4 &652605545 Transform: m_ObjectHideFlags: 0 @@ -21999,6 +22000,58 @@ TextMesh: type: 3} m_PrefabInstance: {fileID: 622614087} m_PrefabAsset: {fileID: 0} +--- !u!1 &1611595290 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1611595291} + - component: {fileID: 1611595292} + m_Layer: 0 + m_Name: IcosaIdentity + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1611595291 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1611595290} + 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: 669339392} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1611595292 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1611595290} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e1689780c428aa14ea42c2125e391b45, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Service: 5 + m_OAuthScopes: [] + m_AdditionalDesktopOAuthScopes: [] + m_CallbackPath: /sketchfab + m_LoggedInTexture: {fileID: 2800000, guid: 0d05cda193064458a9a7bb085905c5a1, type: 3} + m_TokenStorePrefix: SketchfabOAuth2 + m_AuthorizationServerUrl: https://sketchfab.com/oauth2/authorize/ + m_TokenServerUrl: https://sketchfab.com/oauth2/token/ --- !u!1 &1612665327 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/SecretsConfig.cs b/Assets/Scripts/SecretsConfig.cs index b3f9fa2c32..fa961ac3af 100644 --- a/Assets/Scripts/SecretsConfig.cs +++ b/Assets/Scripts/SecretsConfig.cs @@ -25,7 +25,8 @@ public enum Service Sketchfab = 1, Oculus = 2, OculusMobile = 3, - Pimax = 4 + Pimax = 4, + Icosa = 5 } [Serializable] diff --git a/Assets/Settings/Localization/Strings/Strings Shared Data.asset b/Assets/Settings/Localization/Strings/Strings Shared Data.asset index 7668047cbb..e532aebae4 100644 --- a/Assets/Settings/Localization/Strings/Strings Shared Data.asset +++ b/Assets/Settings/Localization/Strings/Strings Shared Data.asset @@ -3271,6 +3271,14 @@ MonoBehaviour: m_Key: POPUP_RECORD_UNSUPPORTED_TEXT m_Metadata: m_Items: [] + - m_Id: 164911761917042688 + m_Key: POPUP_ACCOUNTS_ICOSAINFO_DESCRIPTION + m_Metadata: + m_Items: [] + - m_Id: 164911966586494976 + m_Key: POPUP_ACCOUNTS_ICOSAINFO_TITLE + m_Metadata: + m_Items: [] m_Metadata: m_Items: [] m_KeyGenerator: diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 3095eb46f3..23b36adf12 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -3101,7 +3101,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 106429517453328384 - m_Localized: "Background Images" + m_Localized: Background Images m_Metadata: m_Items: [] - m_Id: 95221400803737600 @@ -3457,11 +3457,23 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 151490030713540608 - m_Localized: 'You can create a video of your sketch by opening it on a Mac or PC. - Search for "Exporting video" on our docs website: https://docs.openbrush.app + m_Localized: 'You can create a video of your sketch by opening it on a Mac or + PC. Search for "Exporting video" on our docs website: https://docs.openbrush.app for a step by step guide.' m_Metadata: m_Items: [] + - m_Id: 164911761917042688 + m_Localized: 'Sign in to an Icosa account to allow + + uploading to Icosa.' + m_Metadata: + m_Items: [] + - m_Id: 164911966586494976 + m_Localized: Share to Icosa + m_Metadata: + m_Items: [] + m_Metadata: + m_Items: [] references: version: 2 RefIds: [] diff --git a/Assets/Textures/Trademarked/icosalogo.png b/Assets/Textures/Trademarked/icosalogo.png new file mode 100644 index 0000000000000000000000000000000000000000..ebfbcd2f5cab60b0c14b8e692344e9f8bea0b02e GIT binary patch literal 72443 zcmce-2T)V*_cwSGLI^z+=_M2arGr#~0D_2AMWi?BO^_m;geFZy1ZmO~6hWy9f*=H> zsUS@$7J5@!=mA2qm+!C4yF35gnSFO=nPGBsbMEt;=bZDL=X1(UHZ|6xr{SUj0DxZq ziq3TafP$A$009R-c0<1%fgjZVSF8d7fR^R-9|GhTZ~y=$(({I8kfo8Ks`iCUQTU}7=bV8J+ zktynuUw|u0MM_rESw=<{r37AGP?S?vQISB&$;in|%P30ADM-r6sLIHyDkz}-{U-=c z7vSQidR^!8zb6B~sSA1p1^KH=ONWGnNQKBt`31O3%c`iTNXy7c%gITC6q12qw}YHQ zC2t1`{bvLn*Ffh0PyZlKzuTzO5uI-N1qZ1Mf~@{)4Zi;W9rkwMzvTooI$W>kN^dU)ESxFhi8?s8Ovhu1I3G$I$L zAgBK)Iu~bEH@^U1CvfqezE1A0(*C#I1yTPMsp=&^AHM)_I*=Xtf95taQq{j5802)@ z*;QXhT@YNql&7bQs*;@itqX3hPLe9JN-CgjUI4F@+}tD;U1jAJZppYP$+%qj&+|Hd z&cUZz{?GF+|1Y063Gf8BnUl}|_Bf}T@6m$!ENmMvL0Ot93Dv?|STcWH5LZWzc|$gmz8;kNLHAh2E9SeX8e50&A^1M5%F z0jWv%`_|p2`j72N*!R*omTmp_ll;;Ux7_GBWE^o=2*p18>uTzMU&AH%;(&kMhOb?s zgP#5lr{kz)ltloi7vN7CT`1t+j~*lce<%K%f69@ImQEs|#&Z&Z!07|M5FSc@X{->y z08z%8J}idTmSTheVYc!D3P6wC708I?KY0Q{06r8Guv&&02JBDZHk1It`awgfaR&XZ zDx30*;KJ%qZc@eyphS8!b;^NOZd@jUlSm1;13Z`dhD7`1*4pAA$_UDP3IO;Z{ZlO7 z6=?t1KfQVX{V78-r}B9V2&z)g=pp5fyb*aseSoSCW9J9KO2&`HjRR_oGa&#m(2b;( z6Hm`k%m0r=`(yfUkoAEBF0%t#oDC$k_12#mKJ_oF%( zusIN-B9_dFL%S}O*gS&MS!(=42_$(yXUdu6h#?T=ghkq}pEZuaG!;GqAn{?zF?ofz z^|@q9$Q&>%61(gI9fXu5M<5-KN<0x@N0`N6%EZiW#WN`t%lj+K6e`Yt4r z8aEBczk`Q#U{6xC0FK&nr0!pGFQRi`Izcd8u@*-&2GhGhH{n2F=;WltWaN#voYIJ#%08S?@W>UwFwgd=e7vD9 z8T90Y-UD*qKdzKaB~NasPUe(xZFw8rdfs0Q`Vsh@tUdhAUCC|6nib zu!BFGhB1064u3h`PvhM^CHr6TC%?yM+IM+k3kz>n{7%F}h};0?QC|Cyf-D7m;Z&SK zaKWQYfTWClV>deD7tiC+OMuAlpacGLR7Y)pP~Lx#SXb9f{pDcmuhKjGdX75>uNn{# z!W4NJh;Kg*k0{&pi)6<+I4!mRQV^hkubfJbjYjZ2cu%*9fm|!+AaI?(o#6%Pc!;VP zCxkxKYI5!z`)%Yu^Rbb-rtNLQxI0vBH-iW`|uh+sV23!WD--Rfts5iXK3m zhNz7wNPdZl>|xApAbr`4q8pFij+^<&;`4hFhy@@Nj_?>Kt9XmHhBUwSwN5^(r^pUK z9&COiV19ri*_Vbx95uyJ&p^{3geZ@L!^c4b(Isiy z=*YifeB=uL&Mi@?TM_Q;8ykjK^Z<%3x}Wg2y%EK>lN6Bu#D28YBM2OlNiBhaM;~${ z+V<#cmm9XrD40RH*U0Ql|IL30Ic5aCa>+I1FqvhgHl9g_8eb?-Z4W zTKCr@x4r@A1Au~+7I2(kn|+?shz}PAgzaH)4xUeRFcI2ZW6h9gAbP_9ahu_IUIKUr zA*#5M#O+w*9LcBq_X1S#hy@_A00%)vUjm-K7{hGat8E4HcMq>F*S&c|;g@Z!UM?mC zK|P44WpGSGNJlwy*R-p~sdJtEffhI;b1MfzfpdY@GKR0~!lnu5C-`4OXmNJ&u{g}| zshl)GIpJ)RUTmMxzV^ZKYU2M2-BIzI7{wHbPFj^)f&K&6u>r#!m%}&VG}W$-{~P_R$)) zN{)>-i`Fvx@ zT|ozoCC?p(c`DW=Y4X`y{w|PEk~sG#39K?4iyi8cwt|evxm-QREINnKieH7_*DIN1 z!_QgFwv%X(u)`V;&CFZ~VW*$Ce5%2KQ-cvkJ!6CrT7a*oyX+a=Jk?t!(q{7mJ@heM zXz=~t1HLlHfL?#mGXR}3K&(fi9`{!6Gn%eJIcx|=Z$h?uzDQlp%vdk)EXk3h5?QR099Tw<3kp^s$5@jEU}ubnD022>^pWZ*OWKqzMXt%UUU zzzNoV6&*z6vn4M?+}V#Buj76CB!^m_r}d_`9UI6^2XnbxM;S6$M7+a&h9cm z>;*3CdQz>(B#x0cRzr8)iy#apwQuUqTqcyC?TqawE?UpuxQesC&R!l11CSrNtO!5e zyxZz|XFD+}JI4X&9LyJ}_L$JsZa=IHMlqo|9KS$~dP*8Louq$hOyb$5PRG9AQ-N>y=5bgfd7Hw?RZVz*$ z-(e*8Y)}Zl0VE>VCohJcxKfqsO8`2}@{9u3Y~K=v&pwt*lWF@pyQIA^h?_oAB`mTh zo{LNEeS)Zh6t9BPJ^5I&w=RzyhT$-bGoyXWb`$~ESZ8b4UrT>DR;?y2P&x6dC++Q} zdDvqrhs@EJX)n+V{g?A@?S*lyh|0>9fzK0la+w8fzJUZMuvlu+NmVA`g%hu@s?F&#mvqj`LFQ$Xe*3?g%_*X|H;}-V<{O`i zIQanX+95_dHf$7VTd)~^3W6zUTT1=b?a5pG%HbR)#5Klw0!z+}taD%>OMjiBRP_pb z;hjpG<%nPA?d=b&txWZU@0R`+JZGl3-s?WB5-1FNnT-{5zhEQKX25F18)OH|rVL*= zHz8`AXhT08{zdZ(mdzi))vye=!||K4J8t7J`W3b~O2_oLq-X|Q4aD+Ke)ghnbV+dq zqh09H`RbFdV_DT;#>UNiOQg3+-v;@qj=S&M-a26_ek?kr-#`n+%G60e5G^4n7r+UR zrKfsUL6=-Els1EsJ{ID=Dtztg6A^6zD>Ibcc@(wM6$y$`*`#Mx_lhIf){cLaa}ROd zg%Gc~kuHMgLCHlBE%ND>?U4h4%~Lim2UlVC@DR1FH~bxcLeL;Y<5j*WU)HbkYOh>= zFU$H$(x*Y?)jc!7tf+tXJ>-*6JQVlFp_R89rp%|m=vo~69PK%~^^|aZ*C}P2k79V5 zaED{|=EqEGkM$Nr+m<gcNYg}uLB^x-8d|I}4! zIY?KmjArMrIFE3_%)YX}N z6mRX6LykXDUJq-(mCe3)mw7|99!5Q=ja9G5ud$EgkHn>VGOw-VxIec|(n?_BH5GyM z#rHg}FcCI=Or!LuR-hU2Y%zXw3f8`>0P|~$fzbA%q4@cJH68a2mQ#Iby|vpUv*U)r z)7iMpu(EOtb9e%RCcWu|tz@Bz9}{#+n@pdtF70ts5X-DDB*wbi2hvxfkZ6i)T!<@J z^+u43+X+7*COxtHen?<}=DBKakcn2OcUBy|vjH~yNK6B`Z;Gfz5IVRKMaRsc&gy1l z?9Dq=xZb=&Fd%W!Jm$C?_LBnj%z+Yb!;y|vQ4hx|>-FshXy_$uX>)!%)s>X_o3Cb921W>b%PFJbU)CK=gS05aq3I;RwEoEu84!$8d75qeI&xAqg<5Ie-$XJPbK&du1(w zY>r_r5*d!$10`agySz&g&Y}X?Ms7Mc?K^MAMXjOkD!xM!qOWJO&h*x|qhDXM$-M3! zZglse7$icN|3y9~;E^TdvNQK!GzkP}l`QL&z*)E^Jv_?};yLqF!(NNo_Zzl@$6Hgd z48cb22J9m^tV(SBVvCQrYYXsmTRd1sx(J#zJJ4;M)n#2d;HB$t7ZGeM_(Ih6z1a0v zooeeVdRJEM@Jlfk;}Kic!i|VUeIrn3jG1s&p{r#1D{umT=j$|l-AJC4`hcdjb5KDa zQYD)q=23w+o#*ps3zhy3xjUlPG`E~Y>eLYR@vK5lOk#MTmvTCUVRtIMvn{flrvN-3 zZK?2u90p2;VRg2g7q=j#AXWd^eIgb=sGVJ9R0$Xot?&-{(L#Phes z-d1eJ-WMHd$YN8mi_Cev3Ow?Rox7vz>JNLrr;fKkr#qfJcrgW$ob!|0;s2AL1R1!6 z1hNr2LDJZ1O8@81*R$Dmth3Kt8t30oiQuuoOphubPpwV_xa04-d)~QX^y_BiSFRpu zd$FWec7;D`>5;AZC7}}GNBT+qqVaEl>k!8>1i<-8XKW?#8VcrN3zbOH+mfZ#Ju4Ms ziAW0Ac7cBRN!d;?ht;&f=*-~k$?TBU)V$3g5KgxalpS|zU zBrI$3^wy#d8+1gz&>wXfh7d^>4)ZZWOvf~h#Yl{q{$;p=NJ~j_aJbGy@rDx`0SjL% zto>>Sv>mCy-chmd#rjj?LXua^`ibp9M*Y~sQ@sHo!hjGtv+n$67(%)4@LhU1-P|iZ zzg0|@5h>s300U9aJNaEEg)1bc7Wd~;ZilJog7x7cW2+~hGG;D4G+O;*K1qXI)*sXp zqIEcIb%Y>k#=pIMYMz+uEPhL~bM#G6iu+$_%|ByZ*J31m^!jJ;JV3f=Z7e6@@p6Z> zI@(8}Rapkx-q+tg0>(H?A5p+AhmH$`K!$|AI7O6hRnrU04k`gadh`*6GC4-CZned~ zF+^CzP5(anb=)(kYnAP2jit+>YG(Vr!!N&VOMkmpF=WXYOrF1H2Rs~nlbz&qm6?>! z>^;tCTAi9omB4PotvFB-{n(^%?x`Dv#Xvi zQZYuqjN<%U5M z#)I3Gr5w_#tyAo>gqZ>}Bb83Iz%HH6t|)|f9*)6KF33mY`iX<;TPVKc3U?Wm5Z0fo zPixS7vx$Mni`|j+`R}f$`g=JNwt|_1z0W2YtIEhnPnH%L<#3I2mS#xW0MFdB#L_{L znPxa`=YLLnNI`=Aj5iE<5$Ve8x?QcK6my+6u@ej}k&!bOYvP~K`uaT<2*O1=0+{?f zasVhEvGfk;kPsN()8Yn0jCun))|S(s+VvdZXl@hx8Rvgd@qMg*lUj8#Spk zrBaaQp=_r2&5cYWqE~AaQPg{B{6@CMldP(~W2td3V-9`wM>k4jHb&+=>pls3(<5tM zq+iy_=Y-8&OqzqLu^Y1CaYD7m#T4y$1-!WQS5FE~QvixDW*N@xCkEQF$d~ZIAth9( zXVh3Qf!7Eg7JOMlNAzP=JDZ`fag9U!*J}Pe+^egfVm0nq>6a9^Fz$x%)_SPGnAbMOx?&KdZ^>+w{h7YvB9IhJe%ncA@GJ1KPyfXVm z_~Y(RmN#*X2%&lsvLT`-tEegpiKgCBN_?fOa4u4AdRm&j>|FEf^mYlBuT2wsH)DSh zJz3{|U2(g7d5YDX<4OY7@M}EB)j8<(pD|Z%Yb!$5BAhg5{-l<$xN8k!s5?NxkWm!d zctv1ZV2qGUCY?yRL*Wo~0L_Hyt~gCW-XdTtNQ zZ(EX;CF6;0zP9KFBUVtriNOUSIN-UP&DyLNt|0LPs`E;>Yc11=;>N6g{-uXabO->- zs3tVC5N4@o0v(Kkki4fW4+{x zUpTW3W(S`$q7$~F5^I~AlW_+^J<`%kEM6ydnyBwyE{|RptXQ>Q&+Uu;a)pyV_t#zm zN5!|Yg3^5E5krw^?JL@jF4>f<+h@<}LSoN-s6--jIFEQjm#|e#_^t@2!WJ0mh`l2u zaWUSelLLo7`wacLA5#j7sSh+9N+Ij9gTcE-z1t{vP6y444V(S*Irs&BBI5D^Z_JyE zANPCQEFWE7HObz5>}AM!WpVefU&n8aEGMnxkzQpHq+eZMYcyCxXdRW3x(4ub=*IRaI01y83EnT9tMy|UC${eO^ zmfin;^+vXk)CCEaq>jj*BjKF%9!Z>M=dOiRJj{t+3s_S{Y^YGKe&A0y8L^mY-@YCi zr8!D))(Lu)hk53$4R^Gp_Gdypn}*>p*^LhSb^T~kNvN)>o(e=-ptq!KLzndXXFg6& zC3q~|^VqPkOnr~P|3Z7V+W+;>Z=$z|J=k{b9*3v=DbPoj>erPLt*Id#X+DvQfz!RK zH5;9L=>nB~J|$;aR&u@V#ez#&X#(BL&Bfej*-a$*tbWFN=q9<+G)fEw@%T#Zs=z`v zZ-_=~auUVeqF;Cm?q3A`pRP{JVHCqhC|X6Vg8=zZLq4MXML0}%+9S&P{BBOg87MU7 z&e~wR_RR$}4QpF4+Q5 z1EZ-E-L_)>A@7sSIPp+oFFI+5Kax%!X4&2>QWv7lp)6KCJNQZck~*`u<41>CiLLR~ zyr}nrg^?|6pYgWe_e>w!7pOSUUv2w!Ybl+(Pf}oP;~a^{%gf0a(8DCN-nnd>q%4)x z@gNSzmGFf2H-?(n`37N+39feMeAr7bglsAo*r8c1lFxAjr-ku|wO z@9R96@I)}!Ya|S`tU3;wG`6N$B)yq)E?d(q@XI@&#(9Q$QZoFpv(~%`ZI&IDiy+K=lz25J_0RqGPJbb3AYSIW{I(hlz4_Miv!ewaR+VH)pZ?Mm5x^A0 zT0~8UD?hjQyHc#W`D)I))d6SqN0Y_6;@A#i@j2A=w?Ws#BIB2031PW`{j%J(&im%#T0>i-CG7305~9B(ssRF= z$&P=Y5sZK#C0aWDWJ-YjqR8`dG0RVkLvrNNbo;Kf;v)f`rXU)dRc^$y<{!dx7}Hyi z2c3@vM=ef{et2Mf1-dmz+GD?xUeq|dyr=05I-E15vy&-%L11ta)cdw@=^gfOHDXXD z|Bryw8R6*lZ`w#2$gIRQhIwG^%uG*^kAEo<@j?)y8{2h~D zYBw&@lX^hAiKwVs{;}Ky%rti}*D8ZXLy-g}wWV~d)o2A*6$5JcYVXhmmd&o1)biTq zwiwip)@b~fBE_+1rqCSQ3C`DEWkS6i}|+1l~Y zmVvf?D}=EM;!^{dv{LOSn<4B8pKF^<CEcy>6#r&X} z5v^hS%f8O@`Wg~JX8zaStI~bo#b{rvNLE0xL6abNY-V3dev*oL8x-5*awj)EiXAVB zpQO?f-Avpg=73I5dw#Hy@ebuvB%XkRy#?zNx%?nB|KKfnL~)hGk@l4^gn#bA&|d#A{hpHenR}S{ zY5bM@UMvsfyo45umv2s8dbyFJT2L|c)!x57dJgLHCAqz&8UB+OU)hQ`xzRaK^|=~vARPo6DFk9lnymva>gLu=2|=y=L8>pmEC(61D) zp_N&<*sgPuH}Il6c?_AiXwbv-^zQJy4h)5dB;pPV zDL!m+tSz_2&+O%Ne7yNFcxzr~cRVj_LchTY+)KL1;o{!f=hA<@PBOor<2#%f4JGJJ z$Jy=IhK2BURSZ5(a;y_WB<=lq7ZekkcBZH1`0?vGsKJH&BT8nrB*jW^J4acSxwPb; zn2K#0#2@Y}oK37#ROgH}Gqr6slu1S#qL0iknBlk}N>lX5otF?rWVu-W*2%Sx>-@xH zouJG-5QJ*)Gv6D)o(BskOgOO2@Zk2f*e#0iD)Q6gtwx6<*v$=e%6gg+>~`z6``Z+U z3#K+1M)o|9QnQM~pc{7S} zIy#uWboixd!3d>3TRzX#@~X$CndW=GSyj3u>u1%{<&OhD3B;yk#*Db}C$N+?-0wj> zi5YjejNg-{dqVrYS6X%l^HYH-f-;V>L^x^XBM@EXt@>WKt(jlO5Fce^X4k+_Ag+kA0K+j>$1)4m z_G`5!sZezh6a>T5^2oa5@oSV|4vZP~>=hzppF70JAVkb^NK`JOQuz>n61u&Nfl=|R zB@A2yPt!|k>=)Sp2~=|J%{05XU*t&Z*^>Zh=$m2LU(L>4Q#85Om~sMUXui#^` z?$Y>WHLe&D#g(7aG*yt6igz?BfPmeN_|2G*$Qe?i zBdvJx%M=hI9xZ!%~VP)oy=y~MO2U>}Vt9LpYXfSWfzkqRjBQn^NA zzFKP?DSEeETcAYC0-M5PB`J<}6(X7SVhiNXs3t7NTQ$PFXc5#s6iE5d5cZB;nH63% z?06v++#cJt-~GdQw41DF5}c;7M_||yNo2#0+OgDyej7d7{6JQ&#{8vFK3q84)HPw- zl37+7e@MG?Xr*|~+iSR}H-Jj~V8K!0+QHRg{RWcvaCNDDMVY9!whyOi0`}pdUbPssFXZE&m;(n$p)zgwtQD+=;R)^IF3))9 z-HTuf+6oL6;mF}>O8h<;d#KU8jRw5p+g)u!iT|89QQWZ^Y3u8}ur_|LgRJm+9FNfKvIaGn1vNfgpmd0uMW(+%!<~3N4-nuJz)Y4C!tfscQOb5ln z!6XboGz9amY=`$xk*#QTL2ue%B=W!(onDn@MELpBGYI{hOC#oyWBW>WGL3moqDRu> zT#SUCM-=Xm3+?@A!`yVKgn{es+`BU@rtfTZkuu3Lx8yRBNPaV67?5R1YuHb&b1Zmk z2ai;;y-rwjwG{n|EhaO^TEM+EsflzvA6YVgYkR|?E_mJQ0YB2W`3yj?U;yoUvx@}i$G84$yp&E<^ohd4G z@5yhNpA;1WhZcO6n0L4bZa+3~`@sb30iqPvbNubw^lG&| z@)yEAR}n7sL!X&U5;WricJ20R-VbL%x`Wc|4VAUCPd$wJnl6<58V&F`G8zzqeVbjl zRwNP+JnfsCyU~jkQ}t&)tJx_LGelW(N6geiWj-$s$M^C57N2GLU?MHzx7CyRr~4OP z0MR)~8}3Ub-nX(Lalpel2dMI>0@J#n!gM}dE^`b+d>RP4Q;pb>sUEp0GOv{uUme%Usb-BE+S-ml2N{eFQ20FgZaGni??n+ldW&4AA~ti=iz}ct4TldKT|wJT$Q3#78}Xy~m_7UZ-zAx6x-au;h+_Bk47Byj)q>$-S$k%>a~1 zPCq%q@V&UCxbXKc)wGR>CZgDD7WWQ^W1iSe%)6zJYo3uwNhCpea$Ys{pNsoZNEDMg z#f(EqNU2k1Nn{m2R<(f<*k}oYZxWuH%-|B{C#HTdG(;nQXgr5RSB&wO4~+>H)L&Rh zZWr7!sWDTweB>Eu!NEVYa;xunPJN9{B;b39^z@a4`r*EwH(p4`HQx-H@q12x&sF^L zzUm?-G<>6~CS$6Kt@FqClFTHGKyiWDLH9R6%UmX@08!Vf#z(=X=@-e;G2RXw%8${_r>dzd8n=W~zxj?eHDPFq^#$ZO5&tVp)R>?*3Bxt6!kl_;_mj<=s7MD*kobi#cUjy zi?Pn_Cww0{FBcUR@WGSqvw2~M8uiRb**Mz>@Wb@&mpk3Di7)#tmlwOtcWD3EWZ86@ zlj}3Dn|W(q!#ciq1D<6ag@QuwWp=*48&=Z1%7-T@X`)sxd?vE?Wi@&STRXRahceuGZncJ-NtM;YfSYT$0<3c@lyq%h_k&A81 z(~U%B$1<$6b`gTwKE_btz@YI$qMS8m{~~`28CFDWPM>KLm3E%rfJgSyQ*j$NkX|>V zO;)MylcKcS{|dwDSg#4TWQFkllX*G5X*Y7f>;`;g*aGDC zOKE4O9cyICwpHja))HSjOVLO6YP-qhOt0_zA1qlvdkkRDoN=S>x3?dGt<~N&S2lnVgSD!G)S-e|&9y<`xnIcE^r%rLjM8b3y&w0 z8BN?U0@@&;_k{b=+pZY{wqLo(+Lxh#bCsN2afZB78U4Lyq`s`|D@hUAs2400ZB$eH zF*_ET;OSj-EEqfP;JFl&%`%?6f_7g*e^(HQ>S((IK@LYih&B=*_^i^mSg^esd(mpd z(#?BH(7Q_SItmyhCm$2z8;F_AtmT20dDam}Hu;CI?xWRbg(pcK<>n>U^xw|q366dW zf$04Ce$YQRehEVLrTxbJM|oU)kx2>Du{r(o-t?I)k#6 z#2o7vN!i-q_m0aA*mPey?c3CFbg%^bvBB0D=ZZWGbq}Klhn)X(_h{2r9^)**jPX`r zAnnwH%q+3`2yvC`UPe%;dFu0yY5wE1?pLa54h?(agKql z==fD`Si3p6GE)L-<*n(%l-bCX4MX1J7HniVKEVzJuw-hmDI)o0Vo>hO$~_8PDL;n2I(q36z6baFVh9$SS)cSc z?BsgdGhBuoo^WIDZ+ZKbxE{-Xo1(2tXD+~5HPPI0WkS~w^Zm46o2-KPOHw%7jQAsj zv^9Tg=VxjL7<{&zba8<67REDYX!d>nBrVucbgrJNpQlzGQr%85Hc2~E6UEnIGwr<) zmW?y(yEhJm?Nw~_j(D4#y+=QuYv6-MYgQ+M~GIR?8@UW-!Dv)L?M_Z z3y``8ZzNzzd*^*ROhvh)N3p_(*9>E^kzamsg|*!YNmtnU3k3urGcBR(i@x_+A)2@F zdDypdP|c2kk?HDcz*N@HzNquokDW8Dw9u>QtFM91U8}wf0sXNBzi$qlvsoRFsXgWu z36s;4eq&f(Kr}DxjkosuPWuoLyh@1gjEnWj-nS+{mj{6lIwgWnHEzAoF4U&J2JJ-f zkvUlPX8tND;zP4x5YR<}J|157w#gG6@d{N?2^;Qv-wTVcM5-(94abK?@>ysW9<4S> z|DX*Jlgdko&f+WA4ml6+yA^g{Sq?SzQwX^0hPb&Iv$v6sJK9g3S#G1dd(eIJ<12u4 z4to${bj4!g4HRHH;lUoK<%C}s=T$zC~rYUjt67?C{!c`Pl2U+ZO1fdz}^X3M-H|{uTWjXh-9qika zsui^ZGX9VyCBDa^!8+A%xi%M@Cj0|Ee4praBEpQ*Kw z1jsOh9%3386XhRosz>nn9e1bCu6*B_C{&r4nn*F+^?K-Vcf4))9mk!Cho2UT*>0w9 zr6!6Lou3jP92wY8lkoE2X}uME)r$RP-zB56o0xc!*T5++3OlOR*hZ91^9_3Y^sEWX zE9HiROf|2X<0%H|ohhX3aYOb0K;7)0}Yz~ z6YR}ZbilS)5vki`X2L9|meY5NW%*$aKFwlF$n(RUWDA4B54nf#6KF`@@4?iv`wXGi zE>^W%e5XD9YJhMzK|D+f>$A7h1OzZ!+@?4qAGE}f`=**^m_VY2rXc@j0uI_ z%{vE6>+zmoo$}|tC0821&r1X6oq^E9;Cu|BZ?BN z=Htgk6H9Bc=q|9l;Bd|9P(ly)}SI5bZTS+>&{TNbbEyySKa6Fb1n=5SOsgX7)?vtC;ABFD@Xt9A^k_)1+$zsK$sMC z8d}&6RlQ5Iuhf2Trok^ZuB88x6s?AljtTfe5ty)GnvjjUh z(L0@>7rm>FdwQ->Ns+Q@x{EMM9~obn!Kwbu$UgqKzi!#|cdMR^8V%zm&Qu`lny1Q7 zi=7*TerJai1v)xeqD4OZ$w9sc+J42;;;tC-75Zy4X8 zdCqtVNQzI%3QwTPVIJU?(NQc6ySBqlyP)*8`IWwnp?d!lv{dQsk@vyeH#RH7K98Aa zeIJUf{GMC*lcIK}%Ano+hga#fdq65cqlVv*BCL209c#dk|I*{8#iCUMC~LPn&dF*g4vx zTkO}(@^4F={SlQ<9NDpqkQ*=~Tz*!r)W${yX*+KXz`U=ueXaTVQy^zSEhW%Ie7Ppe zl~16eQo+&q)uqX`&*h&7v-gC|8{qN1e{Oz6(dORQ3h|3=2kj9}xUJtALC=WebZeVb z5b%AbXZS{uq!GTyljZ%1_M*|$*j2(5iScwL5z=Su%rO_PA4=zNc9#Z@%eG~92@(-8r<3~ZmyVQODA$;NT4EKMu z-978P7~plIz~vL6cj=W(`Ppe4Z~=~hP}@$u=&;P>z(yUNIe=Pazm~UBJ)H* zIg)3rMT746@iJ2@ey!e5>VkHkO}5L`F!kM}J2$DAqL4VM_b-HW_go_(Gk^7_Vph>R zbg>l)do3ZEC0yn|-#vPzo%Unu_6Sj>xzOr|C@0V8Q!Je|+O&&!pm?JrHSp_dPEqtL zVF_^-v22GYB$n@v1=@ls*N894 zeH`QB5@!tqJUMC2BBu)lP_(;vmRjR!ekoC6oDwZ!Q$a&hhaYTn`VC6gsp1q&*#Yh# zuR347qMZJCnI{>Ka<;{gy zm$4z&%lO6h#es9lV<}I>GDI`Wo`!#BHT!Y4WQxhgWUNiBnx*6>3%_XUy)@C<35xb3 zC?UbU6OKBD z5?-EP|E(f9_2a$Hg%?g1RUfm?4gM<6{yFreLaV0vL&admNbcoGgm0pLJses7SyPU+ zTeq13T8~Y@Hs3G?_+l(96!gEiGfBfG|Ek3koz zN&MJ(XIoDc07~l;+%d;Stw-C6J@a>7c8l+=D29wNvzU7czhSO<)xq&8RL(@}OzP`P zA>Y-`jcD#^ zsXP={7{xe;o`3m!$CKl2FJ#g5-l6x%V$}IqLW8(|VEBR+^jkgcPg4sLvEcoeqp8&! z?4|VH$<>=~@oIM8t=FMj>J6zGu^p{io-9c#ux&Oy{mRz#a_;fs=f&l34qx>Z5U|IlT7jcc8o~PG%D+8)X1}!Wh}a*G zF0R6|sSvUL#9L=7`Lfofcm_I_Tky-?sl)NQ79uhTfTseQHuA zeY|8eE!cqiJchNLxNiugn2UMS<8dw!_L`3xcjbQBt_(b%sbIr8P~kVKsXdtuPJ{Mi zNNZq60~aUyVd)-b=8hW4j32t8f^G2*L0$HC?>`w{sgR3~DonYVz54O{84pEVhQ^$o5A7|;t-Y^m;etrK$WHi(+Gfd+Cg#kj@ zc7|DWTFP<&BP-(KkDv53S2QnMi#vqp%l$Ujd@Ik{$jL^!XRZg@LK79}Z|@Hu*vFB7 z<=|FBmZIt#x1YbtS7v^$rjI{`uEDtAEuT_V)$NMRq&L`X!1-2 z*p-U5(*>hvUN`I7-V~EEX?cy5oe2Zq4Jw!FR1ISY1&c4zQ6Dy+(4P1LjCYN6%q+Pe z*dz`5nQf@}>;HqKs|;)7d%8&o?(XizwG?QP;_mM5#k~;R-6>kExJ%LC4#g=@+@0bS zdGq_f-}7u{ckj+Ub7s!D#jrX%4YP+l;rXzGOE7@Q%c(jjB=vUe`pJAw1Pc{d|M7i& zWN9;T*_CB-cqpUSfNj|9X6+VkmTNLs{{Y=Yq8hQWS^0MosX03y_mCf!stu?+@99V!CK z2vyYg;J;!}_8=<1PDHru!zGy7yWy@okt`=Wt>fmGfJD6|A2qwBpS}tRrXyO)epUt} zQ;US!r!JO|Yv5Q`#qcMU>FyfWsLyd1#Lh^5B#hWi^eS%2nHd<$2!Q3hyIFRr9gRD} zjfggCJnl2Y`~3;Dceu`a_*twTA54|HXC<|&^iS=Z3r1JdRd}VDk7d%yP^Z7xiLt&8 zO*(Q1g;6yi_@kfx)08pB>mHu?s1srpz#0alj{}Hvc>+JKdhbf-4J|155Z--6boM&q z$NE;wWbtGQ$sf5{=n-GSesR@f5!IrNSvpQQQ6MTGxDJcJ!BEuGOIqB(fa5!A9LzW< zXC@SMYq{IHc4(RGs%HJuUr3enJR&*U@TEHZWiBe z|MTobPddBVLG#LCq#oeHp8l!R0UhE>cJTqn(mCfBiSyZ6f%-k1@f+yD>Y@}akKwzq z)xI&TM)j%3BLL8}qi?RKv&`^&&;9#(aKrT)h2Y9lj%DUw&3C2jePjm!y-^BOW1U35r^F0@^(Mb z4=>y=)w3pOHPLb-jmH`z+U1$=+FNzfX45NDP1)U*BG1P(E|d>YB|PCuKFWPV{lLSi zyLqDAFX)F1+b)APjwkoU?{Q}GVK3ke_5yzS!2ro0c<17hS)NYz-N7fXznh2@!#g7X z`s)(Npi_(dAQ;I%3cj@9Q|QQzhmR)C0B!wr zy7M>(9=(lv?D0}IIy7n0KvEOzm-d8c0EhSJ}U8Pkt(-UuvJ4D~H5@EN~pg0u7`!^B=mma+;TBRNN6d~r`jc?^pu~EN|@4oU<_RZ#6Y$4gFWtmuo+gw z-pPxMfQEGE(8yO<;lEddTpnYj55o%>5CXcYA6c?+6|WzKZ@P!Cb~&Dr4%hJ={;UHK z1GtxTvsW0a_cO9z8(ugTe}on;+MGwMrz+Q%D|9ogO{AJjOpq1z;JGPG#lqpxDxx>K zMr2GbJYiAOJu>697rTh*_(AZ25t?@zP?chrE*l8@66U*ly|WQr6->B#j_^G~_5&?M zV}jMt?#-Fd%e{>!F+4aEmchC^1Nqij<0h@9hY=a&^xhM_RQ9h&*z(&SJ?6J?eBAq7 z)1Ey};Bk75P+r*vL(!MGQB?qcV*}F0XigHHew$dXslB@XC6w#fv?&DucaTp_5CZ<= zBM;X|oHMd6&XOTMzx4mZ$}UZP&+3__w?OC?uJqLnhd)}``YTq8RfqGJKXJ?qmrcW$))-@eh=rBKn@ggw?e1!HBoQ{~Xan*m9#$9Os0xhG zEBc@8oMjT5Q7y@F<8_g{rgSZ$NcT{HE}egbyX8P8CIhB8npH4)&)}}ESrGNtrHV4u z985|PX{?$4CT9Kv%X)_PAJ?+eCM3ohBU~ zLpaeAE(lSFAL{||yACV#Wt9e8gJ{nU{K*5pBjW#e86}E@#o22X>VfddBmw4_vP!@j zua_aI`bNbG!eZU3*Y(lcda3VpEm*Pczt386ZdxRZc5UxK8UY!8#VMM*r+xT)(bx0) zcRTPNYlhN#zh@R7cwNv6`vmpioSS0WKUcq**Tqvn7tcJLv83~(R|3W&Wuq*T5Vbu3 zaNOMi*r0yp$7F%+(vVn7pb?Y^yVo3p7?$A>)~KLk%R@CgX51}XKC$(dy|+XJ4yRzA zH8&IdZ~8n{O2f-*9?qK(c(398f4SJ=v5hGE-GFR?{yw>%`nr zcSkjC+#EB1i>J#Wl&FITb1{?Cj8s1_<}-`A07idE0^AN4 zzn7JOt26w!RSYdHhX*G>3leQh-OdMr&W7N)Tw;u>`1-~jGvQ_~Gg6^-G(-mTsxQ;d z7fEo-D>X;6LPuDMv(8HJHn>$iJ6e49N_TZ9n>ZNBbyOnU)$o&lnac#7qC`Z* zNy1o$xKEr(eW5tv{dL2k9B&;LJ8@qoO_W&`PsYz_sMqE7^B=q2m29KleMyBcip)xc z-@oJt23V|`Ov9Zbk9hUAp#S|^XW0+hbOVexU_E@QnjyM0tm6yE_r9#ov<(AUZz9$W znb!%yB`{D;OLAtLoc?o0t|LOig0>7J2@~>?z~!M*D4YDl@V7@BciT#kBI4;s1?emz ziERcA(I%2o)j6s_FCB-Ltsg9Ut)R3+k*wfu)P|ugZ*G!)PwPtu(jtKKNHAZ6NyjH) zQlduqgpbQDVN|A<5OfxtMdXQ3BNRdYltCYZ_xvuU2pEhY#JV~j#6q~FEECSZG1Krj zB1Ce{JoZgi8LA+hv_oKU1}OG-uzY9~ssJu3ZhpNzkOe>nCh84FMA=KG0!W2IV^+uq z9VHx5i^)*+dU#7q0(q+0l1t|ZNRS71xLCcRq%dojR~Dp}RzAl@k2R}(b3+Yb4qsa! z%oZgUBlADG;8mW;9dnNNUvAG_!?xDX)f+UZ$k!1+UT9e!hC2mu<}|IQqe4y0UhAda zRh+vZe&#NC3;@uO(oRcrwup9P(x3u)r1(P&AX3l)#$|$_=R{z`bloV(hZfB4yi94j zgzP-fy4e;Q5`;bs#DJ%DnDi$Dn=V-@j0WQZcA!J?d*&$69RMxXW*FLe;``tOkhCyq z&1V6!*!mp=NWVZRwx<;^{(;|Jurid^ikkcCvy^{Uo zXY1I{HSk_&WH-{zR$Hxld)S*R0^4z0ZK0oednaz|wtqW=hEjo;Knw)%1|!)3QH{L7 zp>o}x5Oib+C;~aqgT=WuL4?@JWo1$Y4IXA6A`1DVvZ{M4h2)QlNKV>2(l<}u3uiu1 zcxOdU64d(jp%Q!t$S2<6sypE|g8ypY22(-lz_}bhQS#W_6Lxug7qnI({zB~89JQmcnW4OwdglB=>r-xaMB<=LM;Dnu_i;| zf-e>0VNEsRWwh8AuHYopZ@@bAZwAYG@C<~W08WszXq^M)uD;1?#3UGuQ6)n~%mwX6 zjDcVyS#U8>z|j)Y=3^~?lHvtD@+9t=;^}NAB}9n?TmB=0-y$+c1UC?;f9Y)qIqXJU zWz?ZHIgN*=W5zg4FhN7vygipkSP?P@>OM_wB7sncj~q(d_6CZo1lnBfB4uk_nSNmh zu?vS@9W`DWKMOv8>9&bIutS!PI>h9;-q5osQ7p)SC4XNy=+8k-Bm@5?58+3cna=J+ z*YeK}Y_>z(tb5uNWA7aPHRor`a#bq?5dxwf_ly(^x)+s-!NDb_*<@CTe>flgWjbcI zUL9F@0s#20&IIPFgyB=y!Uo2b+T^T?g7<{`HGpu&Z3Dka98?NEeY$iEr{t7cM53a+ zL=vW1rhwvNo-P;QgDybApb<+Q7ST--HG5lU5)-rrf;W z7=8qO<7C3MzE2(O3GgGNPPzUXYaN;!g60>&)gbNcKslEcwn-J_1qVei_{(+6xJ+?o za;yL)*o+gdYG-jCqBtdnB~maRSW` zAjC*T*l>C1%M`&-kOS0H`f`YU=#!*^Jvaf0R^ajln-jwXkzaTmb8b0y!6cLz6w3tZ zk7br}79;vi0a^;S_R9pT3$(VZfAa-63e~;k|ZQjD_ZxD|CgPjwV5M|xp45t&Y{N|9b84FkCOOnE?v#?EqJUNTN?QEX`TZ z=}NRdhIChhcn?LIVpywqNRy#E2rleJMsBnnIunKZENfs_O!D zD-!vkSu5yV2|QB(H+7**Cf-GLhJxm(>afKxv*IHfnnWn!6Z9L2$On5P;i&`@Y8L(l zTMF3QOoW@y)rgz|+0>O;V^j#m<}vtQ=M=+&|{uX`@ltkd3puE zA&7Ze`M`YAy>B@BsNK#Ua>rp+*24XHChl-oDtoDRtle_hQJ8)bztI& z(-61$+XO%nh{lN>-&4`6XR^>pT9lb_!I5J6MSn*owR57%VoVh+IL~p3@p6frhEYal<0k&c zQ4)y~nHH)u0n4#V#xE2B*OVV)uDs#vedK2+L&Y?GMYdBli){z8h^71>Trx^dECXiU znN&8B*v2-#94~YrT!mmmTKU}be|5hG2CW%)=Td#%QD15n=*N=JcOkzpB~4c?EU7Xm ze@wh#?5bGQRHy~30suHxl%8QaNI0{|B{Dql5DUs6s#Gu*1oo&qH~u+ZdTor9RGM~< zOrY)~43o^M-+(W;P{H*=6zdtB zy?e#}arfp+wje$k6?utQzY7!0)0m`mr=wtl8UnQEaKG2KU}2O}NtLFO{F zFhVX@A;(HJGWi^3f1*P2y0mT)HqxlMlXSEB&dP_MpNA+urZGhh-qHnz(a12XJ>HDE zB|fifx(?v(rD$8^BGj?xv%W5cr>2!m)1Ta-h$S!!8LYnav`MpQ)1mFyS%Q#WKfQsytP4@(vMa7ByBpjgou1vV;JA~t0#aA*XznLtg%A49~V zq%eDBqaiQj4)~3G(^bz`IA&BCSyUF)JM^fHcl1z$?&mz$PsOEZx48|8oc`AID>}#w z)OVt$vw7}0@-}RESXPOtefL-~Um=T4I;D1DS9&w(*Q9YhqQuw|3l1J)4cn8hx zY<|;0`rS$suk>fA#>hf9jG6)L2!ZIZ4(yLM3}0GUJA(^waaQbv6$ul`p%RpsORvQ< zEsFCwrX*s7e@Fh@m=D9il58Q_FiZki1vO<@=^C^6Q)1VHN|i+Ai&Q zf$S5@DBg$1LDaNxgu!IheR;gxZv}q+#bhHJN)}IJ+6rK~q4RUJ?h9%pq=<_Tp)3h` z{f)-n=#_Wx)ap@wd!-~{>5|lH(rGYbR>|jCGT8PC?i#p5q%VK^&d0OO^`1(CiyRpF zvTspu=gomlZoVHTMYSmT<-ZkxKlrbj7bE()`$|G2O)_m(DcP?cgT@BA3d7p$hzs47 zxtOd{dJ>&G=7<{gB*K!V;^}EXtf}I%fTM1f{NoM_){t3%BILw7^MjJWOMKApnh=%* zDz+pG`GKf6KK)jKChH?!`{afN)>nJ#x5linU16qJ z``0-_=GNAp!n#PAQ){Mx?v8d*HBhY0#^+RV!y2m&;b0oaXdB znT#n_xc(jzKP$TttMYi;%+i-BH$9|<$`R-Bh!=y@2br(VB|mDoc+4cz>D^H_?CbfL zF>Q*sTwp;w^21r|K>qgR7unEv{dX8hY0$n}BX>>PCDJ)d)w0K0mG8#ZCIp>L?_Hy` zCS@gaX^hF414Jx^a!2>Sr>j=u+DpEZ4VMWM`59g37)?MSM+@#ZCKxkCsGp*#=X}P) zexcrXied_Z-2>p27BHt|w{IgFL#o2PQn&Ua`KIavKBBPC6hZtH+~;_ z$s1n2w)9euc$7*DHj1IipZhb69$M3`KRv8Ts*U&BNsKwL>l%eZcG|Zz1_eamI)r$H zFE@!hpQ_ z#n&*1R&p5}{tmLI(rnUw`c|UM;Y*-Tg`!4e5#NM9G?s2gxh+GrX@j7JB>@EH+7hb+ z0#TsG$I*}mowm_G5RrAuJvxj$JAK?tU?Xq8h#;Z3lMY&sDfRnM|0|n}6%tXOj?A!` zhrc4ccbI(}dT-qGEpN3b$7$H56;Jt@9O~vI|h1kIIa&@~v7g}64?HtZP>?~7Ginwhjnp>hhz4IUQ1oqWe z91%&)cV80v2AK9jI<=`bey{uvrv#6xEdpfYW?f*E*$oy&djLUWR#DYeO8>+*KP$iP zyH|-UKl*HW+8-+3Jdh*1cJUJ9>S;1GtRF=l<{yj1hEgT&~~Q;#iIvqALl zS0Atz&5sbQqYlpuaa0g;QDKW{T*+Q}sNP*ymdf(gv;(JDbIB<&VCI4DpoR zukEV$W^E)>i*VXm<}l1rPGs8QS1F$sV)3=J8q#WF$rg;#n2$z+9C&BeC|xeEa`ayr zI78e4iU?Kd{?IdX>QIZ0sV4xF~KWqcsS^z;SGCvt z2HZZt$nbiC31E=}wYIK+=K}G4`>S@ud8gMhl3{iH`R_g?=#&Ft{7$GJ6%u-Ens9xt zUCYV#b4Z!X_B^&gJd4A7G8SDD57zwHcdcouI0x8IFm-*+jJC0cs!>S6_RueHbz8K) z5uK8v%(2-&j8{~`NjFRbT5NKw%<2!AGL9^NINZL_?z(hIE`gvNo(6#gtdzw=fiv_} zdWdM%`fUd6$Hr|&1)5eNpt-=~uYY_V17#3RzS6Bg1Z_f1kJTT=AZNK}&U*O_ibF{9 zkPAc;ET_(EF;0K57}ce>QW&6GTq! z29T)@P{zE`x8O16p`-?R9ZjYIMZN++c){zrbvHYjP=5xGo1&^ zokxplqNnOY_{4y$;QM)H;jT2?(A^Zj@N@#`Eqb_bxngg^V9o28iT#s5$dO>iHH{RQz&S@p4qohEtYtO2P9I_(Zb?3$ zU0K1zCfS>ZFy?B9P@VfqPYZ=uOmr!{k>4>*DWcTnS7UZz^=~y~xlcyGYqaH5g2I1G z)-p|=5)Ln3ns4aZ^&pGCTx!&OBq^4b)!HF4%HDAhqW}8cXhN z1S>Kd@IK|WsKxoW4yD@c3-JOO0J+pY2br6&l3f*w;u{%fQ2m%yt$t_$Y80z z7iO7zX?|}`pbrGpBQiA;1o2A4{}_>1W2?iA?aqwbM`0uv7Ig#73e%@eGe#Gd_fR5b zCQVgF4#k|C1I&a!o0Xkn3sOf%zQO@g*pC;3i^P<8GDMW9o+Kn~)iJ|l(^4Je*K5EF zx=CK^elpw;C8l4t-{nE3YZc}n^L}D@dU=`t?#l^`u10WqhnJmc-9bUo=}94-KpDEz z2B5*Uw;f7vRw3fi=w!4T?Gxnc3a*lfeA`QGFF4SNwJg>fwN%^xo!57l_@tDrKi8zH z0DxK8MExpcDKECJq#M0^wculxY?MJ%TfhML{F-VEWrsYTC=5sj1L_jFe)w2e0J#5P zy|MunEbKldPXk+87VzIs+*yy6nXz+)aTGJcqcoNdtGMD=*tFXO2Ek*G=P5B`RyztQh-&J_#j+ zgnMD3&(Qv%Aq3WOoXh~F@)DP4`D_yP6(?V-%Q{sTQJvF*(Vfv3u)X>EHTNFbifXJ#?t`$-1 z)80)6YC4Fbj)=8hvge&b3(Zx#CD3c>q7M_aOe@5fVuxSnAT(X4Cx+{0{ptLJClRJd z07m?d4XX*OMe6vtuvidQBJHQcAE)HU>NaNf*J;_>`n2?8L;2fs=#nFd^w+~yfufnf z#1>-)U8FI61yk>k@E^0zUk3oyA9yKAN_2bK*pXfBGE%+YhE}JP! zO4aZi*0GG{27WM8HIWrYz*Q`OYW<7m$?6imPA`{X{LNs)#^9n(sK1z~wfZ6czTLO0 zJT_ZDWj(V~gu}hhaEHX022Fs=QTQe8*bxJ|FJUNIk8mc~n7^~NHyG%z>;U~k{bMWNh>9*8430`p-hWe`(M zR=86Okivphs+&(Kgj^)J{vt9L-1dS>Pu$Hl5bhrq@QL=vWVy{n-u}nq9Y0=axIai# zvvBu^E6YVc8hgXNc>SbE@HXYBlR<^%8=4rVVGT7E7Zt;tX9Zv`*-hQY{SHdv)L21zdRvvDU&8lNQ7V#m<0T3#)HD;qh+gOGgYNCg?9ZPoeo$r9 zBD{`n!S8`}hp((=1D`#31*b>;?xG04g?YpaBoU6Z4~zT>zgR_mA=P^KBoblx->sL30Y&CAzLX zZ`-4`NeQ&TcB=jZisU4Tqe4aQEBs}F@<%guebm0c`@p=tJ(wjy1hC|WI~l{%CC(YQT8p9wl{yG87ODP4)yB znB4?3vdaMu<3?%WJQBf)T=@(+BBcD9f9O?YRQit)Sn)&470Z1nO;DWO&KRV2T!
uuE)4o4F`*I4F?JxqbIb;Y68&+GOz@GSGr9GOf6Vbn{md zT$}3-P@o1CY-gHtmw%J2<`k8?>L(b^q5S3KOQeBj`}X0Rz@@U|DjoM*XNSE<5oCBtbLEwMEG;i+vpJIYsZ8taW&M_9> zFV;7-ziU#ttv${eTeQXl$r)+s|Kz#8+FZC6@ZF{|Si(Oq7I({fAOi7fp?w`>U=by( z2aM0n-PFs+fYQp=#kwM=!lbiIS_<@^8u?-U_2!+B#r;X2Auups~L;h z%8(8}QY1;B&wUxOkDj3vTb0@-M6#%1ANJi3_6(SAZrGr$wiMA-+!;}GOEQ9gXaNib zC*dBuF;`ohU&`e#;V{Jt=2@$9Y+2!Jbbj~jB`q|(p-y0_0$ z(Un$?(@nqzQQj8THmjh^VLkR9phQZlD<56D!MBRdaytXxaj}?@5<_co6>~(nEJB3L z#4;N~ahN$NJ__91;`=4oSsgK>?vm=REv0HC%DU7AoRpm3d<4F}x#VH8ZC`V; zGtvb!F}}Lq+7=zXBwaG-V^89(41WJ3+f%;v1>Q9{&ofR^A2^{{R$v)DM6+M@DDrob zU@#-Hy6!-LQZelU?5GC_XxRHp3X~MlO)23lct^nKGK0$%;j0k^Y1a`@rU(V#5Ax#} z^71pUh$KUM46O;h&esOV#-gDqici%u%SQ3ex5a<0<3OO=KMAzb+FR`bT2utFz$e=% z+mgQ|K>Eld9VDirnLAw6me^Dp>Tf(IHjA%>F~8tV@S_mBg2NKEJ+*xFAHoR6ozN7% zcjLV&T59Unj_szi0S}i?o1E=mHYqZ#Q&kn;M3*VPR9E-H7~>Xngi)?w@scNxhg+~F zV`C>i^!{rsBd+jM3lo*f%=XRAyRgGfU@^5pGe}fP-GR&r`{RK?%wf4^PTZ3K!Bynu z-bW@j=*KUk z5zdDL%h4|T*w`OodDqWq1ZOBez_QMvX0I7(4RmDTh5l-Jt zgkSG@rM>CTL|Ruat%UE5GdRA4mMkAM32*FrYmb9kErAcrtfUHYET2 z`*sT$G8If>C&t5cRHEe}z*4735XTi`S0I^N=pty*_=o#e0(YMAT}0Jg?LH&Q+t2P8 zA+HM)K|@*4R`NW2TKdfE^^e(h;QTd?769N(PKo*P34lS7)pre2X|QzG>sX8X@sRmF zKe`5D#!ClgEo7Z>PpI1ew`5G%YK{pS%Y3`Wx(}lBXw1u#EWpi!sn0K`(#ITuU=M7Q zuCGzoN<}eleip0n#IIs*Yc?ex{R!!lgr0YW(-$xDccbL+71mo9oq$x!Mc4qtP2(KH zvX|DI0Q3e?3gPMDpwE@wyHSXn?eTTpQoJ;T@dv@0#R{CD=@XR9i$83m25xK?(@&tJ z8{n0}r`!3sKwbh6pRw@lp_MHgP%tXX-T)A}|CVppzSfcm)}xP8BUcrt1CG@h#MplN8yeAAa$&T}+3cf(CNlCyb|o@bZGQn^>(JD78OeW*|k zO4vO0ZM)pm%@J?|$J5qeA<+|1br*CxiajK0Be6e9x}OK7|9V?AAec;0lU(WXHr~Fd z`u4AuXWq)um@X`xbz#IWIvJWjSQv*A2p*s35Lm|`@{Q=u7Ivek52%Od(CnBiz<<*f z$#rE2kvW{+uSzf0Pnl}Ru4srD{Tl3Mg7Bi}MB48m{2vBN*7t3eDofCEN+rSBw&?zZi0`Kwsm zLgzlT_m3%G*FDS;32-T_zRc|<@h|-tB#=qv}P`E~= z*`(zQOHA1Dezh^GyoB*pytoNa_yvqTD<0ht!b_k2)dc`H|HRbPp#wL23C0{SlM`#N zmG<`1B5LWE1KT5*?rRp_S90Ig6fV8u_lQ`W!Ty;fGib&pwu(*nK*478yT)#_8bwVN zKJ?n3to?whM%ecg3KFH&VX2uYjf%h z_ZTIEz|gb^R=1HOIv0#`uObi%W}!V6_XM{Qm%j)Wn%EA)X?C$EHTlAuZM|Ea=XvhF zaY+fbGkYb#_RKMKaBwmc%ti5%F>xl=Pu{Bfkoom*wp%V%L9fG7|6b+&&SBJNXENnO zhN~cMd8+GEm#@bT0?XIRg*)#bmxe2#xr#yQdO{R!=k zMReN~9r#QbUdjeQy*Hh}{9{!iRTeOcQ+Y5`sEytpYF{1wVcmd${#31z!k}jzxnk}! zzy$dI%O6k@{A!CEDmUV5bH8hBiP}EZH)S`LOE_2xsQKsvoCs*>|E8fo1foVE*{(ly zUXrKMJ0`Kja1>B@3O&!G+Z1@(04(b2()Nlr=dsnkc@zpa*#ZmZy5deCmee?Zq2KwX zav6ryKIp}5%wu>&V^~OOhi|0^MKOPyCE&6*{E)3eX8!woUdCG$gPlSN2Zm2GadY5m z-_yRJr>7^Up>9y|ZDp)t$Pm7sVvyxpS;+>kmGs*#bF4SS4?#t(6W;N?5HlUrO zV3Nv6sSrWU89BorjGOR#K}(w&;%NL;2xVgDP3_ho2pShY zt`BgPitoh!j87)xVSB9j<}=w4S$LS~^!udr?>G`D>t8n7wcWv>PmDxjXPE)3fW%@}(F`iDLiEPGT#GpH@`i zkv-c{0|PXeJH3_GoNi>y_3zG$IXlU?bMUZN+gL=4qRx?7pMnJJUlXcp5m4~~ii~Tx zl-sY8T8o)5bQb}r;@33NW{Td`{zh!SVi>TwL2*URPTngxj|(2#Q&D)6$o$#S=6BDo zrPYW3c2oLBbJl~f&nh>@paWE(Ky~CpYvaMh{OAdcxG)Sl@ijG0o)=T}7gfu3kmVx7 z!<`jKSPtwCdxz0h#V0j$PCNIN``#F2J*F_V^XFA#OUPDK{-3HAtx-D`uPyLl24Vb| z-olDwwXT>eeGo0Hzl*vI6!V_ADf};{+HNV=dLu;N-kxfFXoTY6>E-%y(cd74?=4#F z(KfEA2@OVaW+^$JmB(Cuq$DeDk`;F=#1b71m!%o#mY$U?$t)Ez87l3{VR8z)ru|nz zWP)<=TFESnO%VFbYKULYh)}G|Uh$m)9ztnXS2ZX`@((>EdcUFu7)`37#V5mdds%M{ z4kA$@R&q_Zdux$gUS97T+Sej_u(o)us93M6-O$k6^RzqV_`qJrRL*nl{5or!)A_dU zoDYapwm;^R#0U%v#=)GoaihQTi<++U?`l8nnkaiY*+PsAe)}FMH7)n#BEPy%86#DIk$ zHt3B2f}&OS4rE`Sg0?NdZioqz!U808pK-H(!+tLTR z>E|gKXFRSSyrBe6OYkY24dv+N*+5ge0YA|iJ8=h#$r5+Z0OUG~b*r+I@lI&zam-mW!0P}nhJr*l=q8>|BW;BHPGiD;q}stH+wf`>87+v<$HGtTYyNt`qiN8|pKk)68L0mL9P7+hdvhkE3Mig1s!xViGX0Y3(l*2Ll$ zocm>;yxeuXRTg^1jh2$IBwP%yeB537-dIEO1fQZZv<_w3F0U?PMuqm>b2$-wnDr4e zP76Ohl%8yOj|Oowll^C%R={}mPB--IH~->${`!W7?=}_1<7$v6wlZQ0?WOPckyIZjJN)>gU0Rp8=S=N29AV9=HI&q9RVKc?C^auux^P_H^x_)7Uk*?XJ~o6h*KH)NFRnx);c{< zcdm%iE#;4PQRZ9fWfB6p#^w?^HZ8^qIjMd%NO?b#XY^iTgR81ltX>BDQrK|{RarzS zKPf0Dk$RV}U4Es0%ExEm_t)v2+s|#HPgFG|pI}wW2uXMW$ z?92R$4EZ7SSRhyQb@(yo&h|n*m&^k3l{ciY zj6O?)0_Xcav}GEfD>1Av-`EgigKJKwgtYO2nDzsQ;ghyZMj;ge;`|C;l1i+sYS^#6 zfm=z9*;GJX-H*XivEc4RM{Yi$A2eumi=mHlfAn>@?IFZ}Yr+Vy*Pw$i8c9LBbRQ}r z_D|VI|1(=rJpuvu=Gt~F|0WFkJ2xo)<&SvwoC&lU5S}c007}Zg zjqSgEZ`k#o`%$<{9FM=ke2c~Ayk(s0d)6iW~#y+(h$|{F?G%Z@y3nX z9&yoEkLxoCGs7r;8J4T~=S_=uHNkPU~C2Yg%ojdDJRpTLogkQX10 zYH%^#lgqQWlt`U)HX{Q~Oiu_}!~XULW}p0&T__3CL@&*AGZk%QBB_f}cq0IQ`q$=j z%`lXLJkn5*0f5lV)Q`6O(&`wi@6-O7#Fjw}JDN$@1z3bkeF+ywIyrA`fh?Nd;aUY? zi1z!#+(7`^tyLp?Cj1jW@s>w;Fxwi}S*OljVm{)>eHQl-m}k=w%m9Y4pr_7|(=NFs&YIzf6B5-~NuIh7=gZ-`Q?X&Cd+2L;-DEp9#n8Fpl|gJslJbaa>% zdEcbUM#{!+*0SB;<9Burzus^5+8=PPdQ7a^G=}5T#~`V;Gb?2At1YzuL0zAX<13gg zvM2dTsbYH5Gpp%(!~Ro*+Y*OWh{EoFvB<+>A=VKrurXm?O`b>&9(&e&4_&v(Cl@^+ z`N52aXUVf6@0JV?+gUoi7Wr6S%i+_!$QFh`q*{*^6%vzNIav{iwfVzaMU|I*quYHG zfFRnseNvLgfINIEmbm-i;SBLsTGGP(ox)N!O`Tt&BVzCwkY zru4sI0hHdWNJXgV>6UK&xT@Kc!>QFe`yS%_3m@&vr=l0>9z2BHE{|&l*45yTB-D-^ z3`!P@ueZ(GWTSSN{n8u%Yak*+K5VRZo+6%K&i{fkw5>7KfUIR;1@@6fX=KT09~bc zA~^KPzpq}8j0N53f*p3|FTT_tyMVwey|HJun7W^hQD?cZa*qW*?7;qQ|6=1UUA5pj z?PnJAOx90WdL_;g_QK4`Vp*asjU^UAW-$5F`IozinZ2!{`|Kt7F_a{nFccMlPluZ2 zKNKu=bdSA@Xd5P0z2I2>|3L^WixTIW<%Q{_bh?D#U07Ox2UiNJJ&7Dwr0DlK;#eDc zKZru~Tb)dW^PscvuW2){-=vJcDQ5w7WpKgN+C8xH{6B_iE-4ScUN;fQ9hUZufJyFf zQV&EB5ikg6F)S8*0?IM{kor+`?PHFP)u329I7oSQ;SQvCP_2jrkncw46#YJ=qV39R zvOoIE7!R@TJ;^yv-_7;{wC8de#w;yfnC!P_D(pgg9B}m@7lB)RHpk;Du9FA!PgyW1 z_l*M^%fE$;_q1+dSV z%${Y-hE3BrZS2Oj zoiq*F*k)tf$;>-_zQ51=4$2riD!jD)Mp%jB)FRUCpl4ZmEsFBu4 z|IEi<@oFRkVhzjYJQZOGr(2jYAM;9f=N=;mW1y~gGw zHi;t@bJ4a7%zz8}bCx{rbVx0z*Pw4Vb0s$z6LWO|?W+j5J&{hL^Au}$^RS^wRT^-X zx*J{b9b8&yFP8DgKL$2Htv;;{4GowMV24+Bx7g=iJNa`NE)}1!#`{=f-r2`6|qR>;+Qte!mr2IQ7pK2&^7*fuVb?Kzw_D+~XS=a#sZ=@^Y+Y zs)T(^fZJg*LU%u-x7Zv%uBwLpsICxd>zl ze*vo!*JdRgN)9H%nBsVl`vOV2y0{=*Oo11Pti8gkD!pBC$|`-M!Ev%I`z zbSzpR)ZrTQCljEVYBf+zhiFPMwd(m;sp$*!i71)>fmFwi_+jd}12{qrG8TdE=;)YH ztHkwv-`sxX=6>Y3#QJ6QWDaM>rQ7ruQa_4caOkP3NYrr*>J)m?DZ|o?8)ge*Owi=- zO>QicEu{J-vxKkHeI#N4s2`1rw_MGHfi7KGuEXyMMJCmr6ld}1KHU=o@Ib&mfi%kz zB-8c#30_9uC~&{P8kEEHPKET=6?Dk^7Ro^y|Gl)b?w#)?dd-PVz&aftYE&P1GkR-8 z29HkG|1mN5^I?n*U}Bs|Jt##WSc14vp1k}q{1Fbl67U^U_Gd_#i#aV&A6twLlSn^I zyuNDKI8nb5ZEu*6ne4)`>B=8AG%%aI&=IFCIc7tTDxP*i{}c}6g6Zxop0`^s<=LQ= z`vce#=c$crZD`}(TdCw8G>1tycC+>rh_sEwt_O{yqbbs)^cxHoLY;%vTuj6d<4zKTr%wsA&qx+1qJ2La;BdYBDQ(tGDS{x zF-(Gq?f+ZQzt5FIOY>UT`L`34igOR-R`Z{D-F%-7Rw0?o-%q-5;md#DsWSZNZ1F)( zv;Ngu*e`~&v@o<(rF%%s7#wY79W(tPF@cO04tV%Id|$>&VMaz=9;9UUT8J#Z{Zz6h z7Y19${E$3NK5z>Zt9Lk7O(90S6vD;;P|${^ur=9k2GlJ9?NZxW)wVvRENtF0>@|0| zgaT95TF27wU!g;-_4T$4Miw6uir?pQRzuz|jW%;S?X>WqAb7&xx+CE$7kW;ACVT?J zD-4cP6LZCTV~<`d6u~B?Em}arfq{O74qc}e|LQZ_!4B+d-^;@X0q^0-5aSPHH}<1N z3;UsmxByznE8xiDU92?dofNazVTI!p*G@rxQk*-fJsSTi6*@w?xaeXjm}^t|eZQc` zHOls;68)8VFN!vkch_3y>0lVI$wf(jd-|uBj;Q`I+wn=$u6>sFJ(pj~=}p z7@`WdU9Lx-`V%D1-GgqS!3qvw7Wb3dJ`a#(h0W?+=cjT@NW3!=G*lw7P#k<+?q@Df z>Vm9&GF0`HH=Yrwy=*Ea)kb`^@^?p4Nf0O7U_u$w`9q?XEBanBL5FW`?GY;U zgSQ}`Bbp|QsY0fa=EYG=x#DL$+A)CrMOn3250yy;y0NqLL(G8`Mv(k$c714en@boo za%*nw;}2JsRZA4}gb@ZR7y@`FEKP|fZP^USpM-Q-?GN*??RiA7FfzoF=Q@8osvpLb zO1~XDGO$YV{k?rVZ%D`|VgLC!y3eB9JN<}7rM6=+83yA6*p0hWfv3%L>yA6uEZJ8D5TJErI) zT070@;AQ1Vb;(G+IQ&`c&t@}yEZNO2-a3CsM{PbC0;#pFK0;ZyWIpH`x}cAwnx{5< z$CrM(9>rzj5^1q^BlmWb9Yousmgw94JBY%jhzK|lfbUsX%T`jXobOe|>90`l?<$xYDFMm>a%Y1{P+;?F7wcmaj4@om5mET`{9QQf?H7#RIP9@ zY3;7u+>lr5h%b$KCikb0bQ#PgO#N}C0p|TMQzdd{ynV0Fg2)VyI43K9oZ_D$MWzJ5 zfxfPw72qw$eQwWM!l3Q~7L8z~6vuaGH^m9BIQV;dbLFa!*J``Ayp945{_7)=xi0vi ziB`ki(7~cxaz>5IQI}u1-!EffaUisIf7SobP;^Y?mK@sO{^JjnVJk~Sh`z6}?O^#c z$Lo@66MfmctgA6M__Y9;oX_&_X7wR7y(KJ9E(=2d33UCPD?bOiP9ishX8V)u&AB6X z(>C04(?Hc`lx?qW)16T-CTB>44%}`{HXjG1!PfWLd(l<~pD@QqwhstrtZl5M!RE{8 z`zuq`<$HIc!>B*`HBF8J6q;Yq=v9yaC#n}J^O{6l{Rq}VY*sdWM> zt)nv8!rm%`SiSQk&!5k6*Yug;t*U`p16t5O^Xz6j^o#~2QN~)`d~aEaM}AsyG91fj z_35mc4y%FI@j{1+*0V<&pa{my`MDr?i1^p@SCucXFu!MgRaD0s6R(WE!NlzKBq^I+ zZ;dHs{GrilnI|XrBQ3e+G?)v~KIn5|34E@cK_!ng0xHIjg&*uEeaV+cJ!Z(cBgWdH6_g?*)5696X`Bw zPntDK{t*T0OR&81Xx4e~UQj8gY>|#NFZO<_^AKUOb;Ba`&MXPZ9x)-j?flt`55Qaa zfW0HR1M?wB46+C3f30KVAj9mxgA^+8b{1Dz*rAKQ6F#Mj^)suy!pEt$hjP@%GiEhH z$yrOwLk!eAf#`o{7nBjcGUF;hGB?f@#ewkRG z_XQ6~Fw-YOHXx151!wU?2LS$4T3h!0m=E*MCJ~P3juQo74620W$AN#Lp<& zNX9N~8V$mC%q%-*^ypK1>?J@n4EhcE=8k?%;jKt}y8wDz{AY1TUrxs-f;h$Bl?C`R0Q1wyEeT2XhPXyBjb{%R^Z5q$gT zUs9IlYQkt-^e}c2&%$>S2<#vIh)M~t&50G#K|JX;QqO4Bh<@jfhE51Qcz_3#V$ix( zm&G~7II|S-H;an=c2pE6xk#1#F*s0-WuPr2Nd?D!xlQGjFQ~2YsBToH-qF9)QWf`?pEN!!FV|JCJ=b7?!XufEMk$3g-C%z@^Hnw%R>fg4fI!j z<7o>1dAo$}Ws?HiYAia5yp|yQ#6f=J@kaEqnCb-j{0D>#>$R6cy_e*XH+VkrD2LLJ z4{6eRT}^OPx9lHevz_gs!FRpjp}zN4hci{7(K70<$ESqhZWG?Z#3I4ytRfz83A;El z?pEt>u=~1!U*7$<0_qR6ZlNNqlAcX({aPl#$_M%`&7+p^K=SZGE5%G$1spAo>=T*l z6SyNVU=F3S9rc&v@q%9uxa;&3;);}Zc+}-OgpbRtI~QSg7*E}_aVc4#_ppJO?}JI< z6P0#4^71=84_-^(**7i>9AV|;erNTJIV>;yup*UvK=~5+NqNJIZu|F}sz9{hAnA8p zNEzG4qtnO2__pw5AT>A-*HAgTXJo(d-Fm=PJ+SkdD;9)lz*NWlvpE7agh zeLQ#eLModWfI!qY2vj7L0qP+jXe4-*O(aobLvl4KDziY+i!SuhF}ri!6mD4AKyf+u+T5pPqDrBBvek`!_KFamppRXa=lk;>F?+I$?<)piP{7 zVb7{q^^Uzw)nC(MNRUWpzD-v0sC87x)9Jqp+j0Sk%i-xXR`);~5WPEEWBt|mA&kus zc=NE8Po3)k`%P1RPzqeRthb7Xvk%hrbc$_+kvIa^)l~D{)CbmI+wQdJKXpU(t59iq zC9JB8?B{bpQq?(Jb-fb@Si(>9`%dgwE5FyEO`wc5*7v&ObBzr7ajG}}?!Me|T~iF9 zkLIA04iMl+9zx1*7_cRbKYii%xiQHJIkFAsQdFYL7m7NHid zXQiPqdC~2j#dy)zN?&&VuRGFt$435G`-^EUF6+E9K)I=!h`f&)sJKoPBR91K0#b&b zcIb5l5i$MI>FX>96HGXOozYGe-}Ia4NA5i@_i%rXU#$alU5y4te`0=O%f-uxho_fS zn2Ad+UY?b1n=^=^}KI+q9Rh5(_QhxF>ly2R&?X{cH@$Og@Siqp?LPt1>GysH5(+zb$t z&dE=0BmXmq12jAH`j`FJF$_GN^myrS)^hK8g&J}0Xq|XRH%{ga%V$%Lpru6}+`z*9 z^q)T2U#6GW4!$#t%FbuzQI zhV>Y4ne60B&$Zqu7mG;qOCS|?`0%}sKM}?DU}xZ$t=rw8gkNnoJs`6ODy4O38Ue_m zjEV$|^}8N!%h2@xGvyqlkwuW_0MDKQ9?>kaO zZ=kk1DBRSbJ)ekHX@$vW0YM08Q`AtfHT^ko#xB z&T&+VoV+XqZL7?mFaTO9E)mv3WdgYmXk&CJIA^G4SolcOkyo1-3LGKlsdoiY0JbZ) z+eH=#YlERb&Gy(uaCfTxDCJQu7s=7_6X}~WRmuIkFR|yW@W&3jAZb5I~MIOgS~B zrQ}e0maB0MxL1>(Odkioh+?t5ukowLQ*QBgM%bl}OfmsJ6+t$D6w`Tw=2B^{BrqB;J)_)Ws}xH4YH z{|r+26d|01rPcUNiN-=*c^r}W`mgHx{>r0#*F)Vhj+fGtwBmPc$vN=w0bFZa8%M4R zh1jR%@4TW8LDg><-sJ&D^h~e1x8fQ|}4QG;hb5rGyrQ67tg>QaIs$D0f z^bsaxrUaBjRA&92-zS6FX|xmY5=E964E7r!Wb$}-v`90}m9Sm!ioNi3b;3s*zW#N+ z>2j_<9uCxWN+>8ax6=ng+PPHvD>jvC&XAoA#KYMZU~XX}M!vIB1^>@TZen(Ra-$A+ z%tiWUBr78zwQfdCNduF(5ig&yvD#R#H@eQivSw@Dp8sp}64EKH_3@UlRSiwa^nhOz zyc3Z4dU=t(4i;eN-xS_DGhA;vky3G>gg#VjRoL|OLaZj68yTqO?*6>mXMm(1(E@S5 z_=^tjHaPuQ&@sAaVeyc&pBSEFnqRt`dlk$T7SRfNc}e;|pTSK_Q^o`_DiOA_S3u?X zXoEh&@7D8A&0A0lgp!U$=^5a;)89EPh0e*+~MH9rEKy#VsE+3y7S# zLF4}489rW1L|VDtl9$rXO_tc(-o5MyZ9U>0z=mG{I z$t;yI4{WpfBJnCvXiK1}M38Ic4ENI0Lnv&%_vUH@p#E_Ysyy0@TyARYV9xfpid0fK zaLorgiXMV09#MPhqPMAM?#RfJr2S48mA_eATCE9`f}g~}rG!BtES$41(vOl8)o}r7 zfFMSl4_+`+$n}k!LsY6p5r_V>2Yj^O4K0f}xxt0ofQo)2Et{L$V!H5C8AWgwBru5{ z5r4h*|1adK2X6ETSjnB9rCg=O+Y^&gcG-3UmjO?^X~unOE0)$OfaJUR7Y1psUzkTbQUmGBXLj-{YLy%jsFCOgM2uo&f5O`J zn8pEdL~sZLllXPu#F*>dJH81j3t8>h%Ik^HF+~9Vh>rAs>g9GSWFa_$486628(9=e zmbCPTa7XY5i*Eu#TVx3SBA%gbo1M|uekidKEDBsIE7i8TzHPK$PzxD(pk2#}eBhMGgjJ}7ew2^qAjDo>=TQ)f1p#P8l_yVE! zMo!;P8MRaJ@5@&mPe8e>;o%oQIAa^rvg6e1?9ug^#GgjIKjrWTqM!?VVKuEVH%aE_ zSvtOfKWx@HM+9IYn*`So5Z)Wie8bNMe|}qS&jDMze>37!j9)BU;iCY2Y6;^Mg#JbS z)21%%gU6ljm_J>MtpQ)?pEvq4MqN*#at!Q@Kj{U6(V}URHD;JU?@6jRdP!;@(ZhX) z#D3mwjT13EYSr|-!UoNQ?0vqY_l+Y@cdn1!+uQVeLK*qL0jo&~f8nR~CAXp^lw226 z&&g3LKt;A0JjuPl)I7Zxy=dI_sk|=PFa$QJNbFxa#<42K*sj&)BLcTQ|H-Nj&g~8P zz;?OJ#IL`ek!lhs4@?w(u^(u1RhY`5y>)p9q5?hJH~Sc_R72%st3U=wtIDy?#lbTR z9=sp~Z&cPS;+AzDxT9S09OdSVmE=>R<$lbp$lkbs4L_G;3_B1#(5S#_qSZy&1pI)EpV+@)1o8tAZh6~lq zvTWq86|sWEzNK4X0eXZEKOH0@b$n+)mgsRW5v%bIIs;z3~UB}#2#(M$Uxbn4sCCG^XylahK4_0E(1Tj$aK(;SC-PXL{D z4ImVvZ@z%A`2%+di0L+1zNMNr;WA9Yo0Ydy@EIS!%+KRoyx=QG*}GiV#z>Ju? zjgz9>>jqzzFjVS%W-zU@nmqCI#!Tg=0SIrY36SJhi`B+TbVL={vLL{HF0KGj9R#mp zE$nlK9Oq!+LtIm9zKa-c7&VV%@~_l;RfX?uJGXg(dkl_T4)A0AZg>pa)~;60YA8$B z?t6J3+ux4#`7f08Gli(qvQMMpfr3jB2G7JgVg9eRm(Y1ck7d@_GGQVXkS_nHD&O0T6sxvu!XnZG6G{;)ZD28oigx|{j$WW4XABQ2!hFYYiw+?EvM-KCe66Ia zFZq)r)E_>?t2eh-Q?;6t0DulNw_^1Feu$7wH34MV==z)-Vx-a5b~uAlUZ@UOK5^gt zGcx4%4j<%Jk#OggKNB~k#`_moy+1(T*MEd2@m2#kK8f|)2@fpLvB?0#EF@{ZK6I9f z9AcNzmFDm0;9}?tm}3yb1JbQg{d;3OBgV56PUdFF-kKnWsVG4IxLALZ3vv#f+t{&E zZg+sl0w-=4aNd^wRf&=CluID>K%YQsOaw8j$6Eg%2w!~gA$UrM%+8HBzFX!7H18Pb zrP=-gRLBdF<;$?%hA?sg~`MtsF?Fm)I3ky!u57=#~VHnzhEXQcSc`Q;SaNZ6B zXV$?v457E}_-hb>{A)Uz$)Kj8tR^17Y#CQAwDvyL^}F^upiroB8MVLEFc*3JLth^x zL+j~mcd-VNO{FWYBUm3}S?;f9ux>X!OVfHjxpXJ_U_l!BpJN3Ap?K^-{#L|HE4!{XZUqh&8H(4zP zTq%6EW+Ap=F!|wO{`&)h!UP~(*PhHC4V79mGBBlP4y)biw!$R}_#bGL0T zu!hXtAp_E2`WW@BM$UNCW+;Bri=ImaR#PGDS`7No^*4+yo@lX!FCowL3EFE2-N);LGJ=E0TIq3vwLFbS|QKya(Va&OX22TS#VqbnXy<_GjQM4QcU;b-O+IAIw?#G;62r1b;-vbcTqN zK5HUwIQ2IWsH0Qb{;~(_mxqi7b5NIcpe+Uk*sTWqaWd9Hm`*jNOeB0yWK;w{I3t@) zAnRS+_u&8c_#ku0%f~20pPH_YT}^rbeL`i|)tm0PQS1cX`~{2_5YORj>UO24L6~%2 zU*3k^j*O`W<=`B}&;(N9ik6u2hcZ{bGX+$V)Lm4KZGyRj1O+}_H3{xRcf$f;|=5E7pGj1e#9U{oB85mXFrXnDT_TEF)vVj+K= zLpHJzrvv&4)#K<7Q=ycwE9~6Z24+Lg2(Mk!e`%h6p1`W&MlR@)BKackFbL%b@##O34t`F|`&?uIjL5?F zH|kK$eDO*HwD70dpq3*^^ukJIkm9HAcEX6!TfjO84aUUvI8}_TnFf1)Pvv0&y!Awm zodUCLneWHOmiDu~q36x*wQXUdh!P@3(qJJ>`?B6v|LCYg7NvT?Nd$jOBClgk-I+%R z{{65)MVVwlY7Z364Mg5Zuge>mN&dPmXhI0xKkZ3*oy1D7rl7EDWu~G8YWbpyH0Epk zr=wK*{Bp6_DyL}Bs&b*jEe#sHOUdM@1u#3v2DD}~d+yNhuw@t38_0XvH@N^N;)(gh z-QVv!r-aqPfPR>_?V2{T#F2U@reaC#Cpt0h>!iL3RJbwTYUT=xX6fIi_5F{v6I4_P z{Ofev(Z~o;YzGsWSYFl<)qGvhrG8TM9p$L__PrhgF#YI-xnPv39)fxAByJ6>AN3^H z(_aegA<<3U>ynwVA9dzkO0r%|tdf(tPSF#myj(&px@1u2LR9dKqw)n4`&$uDvri$- zpi#>Ev;faT5q_Jeebw;F+FCR~tIJ z_g&-r?>PO=jEOQX_M^BEK}iHRV<|;2p41t>h|;etkmyj=f3rzu6|HcKS&QVY5*%M< zIh84c)U#yFeW00VnM~@y$azXo5YcBM`@b1T9G(d!Kz`*rTR=BEq8Tzxp*nMZ^aEDO zA4ZCP_OF4^!XCSn!997aIz9$fjcD}AV0KOZCG39j~Q8}k6f zhzv7MkT|R$SP@A~IKj>LD6(evA<*K=0fS~s7W#*zp=N@BuquTwgxpCW9Vn@?-Tqg` z$VJMHHD)UH&vAy~i&Kte!SgArc@&GXJCh-KbgtbcHU>FZ>b0bv;Ii>t(fA#j2Di-> z$Y|SCgA-NOWL&Es;{m*F*H@byhG>rsx?fqyOq{Tw^S_fXf)KDRF4d^bT|s?%pf`Hi z%%0*Wgd7l7qm`_AM7$Y>GS8c*Tl;?<^rt(3(b8P|ddj*r%z;uyxCL7jiaoW?Pm3*G zD&$`gwXp*o;CG9{${J1KiQrTkA|e8cOCx$hd_aZ(;Of0}%o*8>{ zD%beqpkTUQZ~!*G&ijqSvHnwV=pU;XSr=iz)?>d!Kw@%2Dc4#F?jM~y1@06)f%v!vfjC8Dv^Hi@;gwaLfJ$}4A#T0laLQ~@nA_F$$_PXT$WSV+r-!i8E)PX% z>Z-4G*WCe*!1pyUWZMC?iqh!qjUV{#!xj+#&Rtc=czfG4tgGSLsAtnhA{ zH2OBClc;8ls?yluLDlbDojq_%_Z)djg+%&ueUmN?s)V{xY1Meh9FfddAGK|-0vhyq zf8wXuU}!+-pqbsr?Z2sWZZ@^#xya#A$mfC=k8@-Cj=l&?&||ZxM?OOX^drc+@9};&vNsmpgSb3FzizD8 zhKZ-9g>FLx?gH{z!-DT{0UECK>4Fh2us9U`7-xU|fja2b+oS)~DkFIc?X_mDW=7Z% zQh>(g*mGk{sFbzG-grzzG*U7+ZfN{@;qbSpFY(adnP@PXoi5->h$U@<0D=0^tmq)~ zi#%0bI5G6zddYs;VAiX^2Lz7H=BYG31HLQ3A@^r9nSu!KN zyIJAn>(A)LD^dun$jCD4??ZPi1+e*CCM%_Yt5X1;XiWj*2{+#48!MXi2B5;#ltjJp zbAtY6-SmeR=pda}Iw&ZcZtqh(+FitItD(N55dymwEs{=n`e4&{vvEee*W9ti(p*M8 zH8hHKx=?^o&>9T}>^8Z#b@airq#&mZLOfHP{#?3d6+GrIci_SFTOG$I-Y5Y>-I}w% z(DzN=ju`nx0P%cjU}&ZYpsr0DWI>)?ll`&lvyl!#6v!QsFLbg*zfgtiHa(FAGSYXy z_U~vaojYn}J^7K^7lZ*6EG+T0)W1Yqf6c5558Z@&Zj%M$=f6_BSU~aBy6amkq{n~sSJM3Z7+Q8i@!Egw^UMO z4SgxM1oZTTS1edhhd7KLR)d;DQk> z6NX0A`b(EhzamvJXpEJ~)e!J>3RwpNfr2N4oshnC%dcURRn%`2DUN1*9xCyuKaZ$! zndx!hKpO^KuyC{Ypq4jNzL9_s3pU|rPL!d0tLkc8@@(4fvku6plwc@4S&!H{9RO*~ zcJfY>F-Rv;hkQMceDg&YJUF(s*_ie#Z@2Hb@kR$0U;`o!-QYdVAk()rLeXEIOw4iC zfzIr^*71ctVu87l;8pHD7W?9?|Bv~dEP#0IVk}kLX0#s}>SQ?#i1I6Ms_KU7mEL&OoQ9uJ^0((+8N6?pS z(xg+>9X3if%jZYV@AqLTM7ml3j2ZUC}$cBvJ`=@7xg_*e`82rkV` z&@8I@Im>IO#{k=yM#WymRMuI07cjURM-&1Xek`G_mb-!D12|$g& zn+SdVHKMcALHc*Q+X%V`>uYst5rxmWn>qOi1xEFeQQU0BLRDo!g-q)sp(C5$-$Izs zzUKUZgGh#DK5Ii|!Zbps1-Ml8Ze}#rzncKEh}FxV5M7M4mKXHsp__R-HqdO2bFfWac`gBjRgNot#}Pt);P_%DSbR zFdJ9xnu%j1fvKJ%QiiVifw9s}0@>nWxPa$7fiBWXWBzE(!%Ikf7VWBlWATH=nHpjJ zfW){rf|+91t*aeyySbRsIiK}JLEV}=WqRldU`KoYccaGq=_tY7>&yA2%E$3w1H!Ee^--0!-z zMHZ0?t06s&hH_4~N>&^DxvRVdQ%cxnIP3<- zBbm2xB%zv-uB2-*r}O%

C^Nt1r%w(Ti)VB>9RZ(1Nd8)9qvp#~wbr>E0)a6_!*X zQyLNT>C|3FNG4!32VP;;_tng2{@@!EqfQ^fCSy8dd~v3z>^#N0Q;1#3KbYVx9Z@xk zj@KPH^yS8nO8vV`N@-O9qVX zpt|-waqok?=z?b6BkM_bS=viEDE@I4$}Kq)a`$5tK`eyl5b=gSQu-9rW*TprenBJY zs}h)edB`{3;Vp%Fs9S|brr#D+UYq4acs&7$=W9f8-S~tog(pKr^kJ^=ZxJ^}wSPI( z_{Nf_45%YRGCQtMYS>_3cYHq0$K*=>fV@y=QKO_veUQcqICbm3y^U16?gyxaSW(?q zMAF%kM$)0-jOcP-=)$=KJY_#uUzv64;>9BxFvd`(1cYruZgm=h@DD5Dr&W=I)G{|; zjvQ{r2r0x3(`e(=%4X6rQIME{D|fW-a#1Wg5n1(PWL0i!l|7iw0-P#I&+r~$*to%Dz!?}yq~Ofl5KTJCk&DeB)HV?3MU~~ z7{&L1(qNR$kPq21sx)zAeB(ZpoEPkvN;EV;u02bea>)4!-tsBpyQup1td#Hre9zx7 zNX9_Mvk+iY&nkC;VtDv~{YCFIk!L%OQ}L!j<8xAb4#u~2)~&`l3vX{ke7)nZ0@`*h zA%Hl5nBOVwFvP(|ai!Y#v#~b(S!Oo?rIYx!F5!gHTHxhSQ~UPRE}qY+#{`H8eWeKJ1t_%-(PznT)H)?aad_!ppDjiN|ZB(4I+LrN`g0 z79q&6biIpYH$Dpf0WP)`!ft`PJ_~y0^QNdpmhR~$f3zeg-w~4!;PXPO=^}3s$H{m4 zs>uy=ZXsr%D4`CUB{5FBuHT2euf4aL;*UL# zA{RLF?xsUQbFAuxa_31chBH_K9%sK3l5;*xDJ77sMRN&c4n~s*<$2OJ4P_1{=lrp1 z5NV*!HefL{Y>q9YkKUtVxcKxM=xh4o1KsxwO`nfMpFcC!)2mK$0UOe zuVC)j#(IqR^NPE3Dj8(9>GeDAl-7x>UkD*m>%x**Ym?<_Mkjlba8v0Tdz(fqX`@6) z{|8%=?anpUk}a}j6M*d8zOD{UGFb0iTQN^g6I-j?hcvFW510iKuDrTiJv`_4skyeR zeD%Ruy@DL}v1zL;ACX*>_FkoqL&cS^{_Gddj&?-18775P%LXXnhj@34qQ(pu=7!xm zhGjAh1-Ry>E-VbbOg-R_;n9*;;!V#eIIGh!Yd}Owv_cL}nKN+fXN0p5dHgCTt~6;7-Goz;zb(pG z;A+)fsNFU8qtQuxpIcUwS;=@Ff(<99Mu2iK?ZkwiRXx4m2zCtWymy6fJrF$G3bZ5A zWZ!gHBd7?jc*@4nR$%fymZy%8_>JmoqQZ>nuH+KWFFk?oUK$&$*qDxw=S@|~6wj3^E=G9?m$r;%62bhP0Z^Y|@{b^j z%H9of`%~rZ%GrI>d+D!P%3E=iyH{_2FY(x(F32iO)^S{emv zRq1d(kjUHLh3{(#t0mSF7r zD_N$pPv89Xeidq8c$I-_GjSlZh|*s(MMPFr;#Z9LdSd&&weCzNVia5?==TVmjRsSM zgeeEXQANlp%@|~OHMd5Ugn;CrCU+j1p2x8P??k7Zq!{e`t(GaD2a23>%bHFZm08W= zd&zE5KLMVdXmnPN;74GB3cY5E3s9kQ^@zA~6j$3rCOA%7_C}rGg)-w@JCP}-f4{QvL4fA}e$q)2%_{-`^lf>(hU#GR2gEVL+6aF~ z3Y<6ern`v|kn=2Sba_!_%~yePGaEvKW*YsEg1zD(@Y*x9N=;8Tunul0q9 zc&LQ@AquJd&9ve-K~Qf{q5h*EQj-C>Z6AN*8ESy8EYU=Y2bZ~#w`vn;_Jal}yLnTl zQ2v>{-LdY12&JTs8*%#58Vkv9rY7Be*xwU{_go#=fZ+ZDkb(Lv+Hrr`tt8uL@oCEV zc9egU+}6I>&_I*@og;OZv`bQ^wTZIf>2?9F#4f)13rWFl8m(*^>o_65sw$X ztLAwpozP|b{hXeG|JXJBGI~do!N^a-c+Hib4+hk0u6w+D(V-ONTu$kTlFAwXYT4B7P*!S#=ZrAS!wqBPI$96es{h& zk`4~vr%!qqeV%ZY4FQ~-D1)fMRRNsU*aWU7EV2SUm8Qgq8!Eq**?Pn6Feo^v^(o*| z3b(%ced~+0md#mS`}kvxqN}R5X{JR7zf$3QvnHnTez9A$kmR#0wV=yL;hfDbWn)3g z3)0mKp0#-0SZ;R@W3KR+Uqoa#bLG|Iel1Tb^+5SuSjt;_@nI_Mi2|3KLeqowKzou| z@tsU@4Ze?@$#=O)ojc#}-2n}R6K7bs{W7kf@8eMiR5DQcJVw@UYDYVzUPouU)Dtbp zcm+r4guJ(KPT5H8jY2#n6a;+~?ggh-!(U{(_$)!<8r}y|Vt$B_|BRb_JPwj-yy3Yo z*?%E|u5CY%%zM9fPY`PxJTu}8iI+E7>z%~Lxh+x)xu#)837ZUrZ`xviiKeVf=rDOu z_+o^;#76R`ZDJmlCcrQ*DZKd-_*5C?)XXA9Vn7362>$S-k`1-TET-<6nU_*%!9!O6 zN%2P}*-ybFZy4=75A9_i-PDkV6ZJ2KcYDo0himSrY3qhBB4aJiE_?*e4$gsBub8rM-3myU88P8^qY zAR#EZkpQ6p7ilrn^XrxF1W!J&cMh5 z?|`p?YjS_!hh^w*Os8q3*;D@$v&qJ`^$sZ5MXfcH&ZlQ@i-_VeA;32sA!TIuG?;X;73p7Mfd$&5-6RUL9*PUyCp0Cx zMG= zRZfPl6U37>j3XAUx!olXM4U&m>uw{~!jFUUaPJxUMNw*Zklf#T2i6TTZFj>KQGBEt zZHdTnPCaPtnpK7~wh+&|GK-)+p%(20R`jPW1LqzJA0*%t$#w{oOE(DQNTOk>w~7#XR$?rq-LZ9drG-0&8MG4`0m^NT>VZ2lWBHg7u|NYRWtx(R+M# zRWPj4RZ>2|DMCU8?ksKM)iNi|TS0@>k*v{x3_jIX09{F)B5{!10g@vq2gE;RyO`+X zUv*1LZPe@z*0f#IicLGEO7|$Q6LXdRrA0b%X*grR()aU)f#*CJfpgVqQHvHz@_91s zc@8guVR@avZ9+NyX54Z_z~2L#gcU>k_eYJ7d1R@?GrS6u@OPrAsT`LwwE2ur{JDr> zutPO>u3|CpKd;6J-E|p2XlSMe&D+h=8iB#`%8ykAQ6C}%m-e>Lhc_m zLFm@Wbp+8v-sP&!pEdBeB8Ad6$6Bgj<1**yn2*GTU3Hdkziy2^k_znLTB2sNWh?~F zH?5;sFNJkg`rk1D<7jlf7(m>axeidUUWQS98Qi0Xt=aLNRX;I+wUQIjgMmuZV4KHV*pOsKBdcr}Y>Dz7zsK;GAvEw)!pWwn zYkX41zxGQ_nzD7@1|{o!Bt!^*!UMzh9rK;UX!#VqdNm%!8;hfS$+=*f;2>j>?l(i9 z6o+UvP2IHhMI5y^tq%Mn*23a-HoTLs4B^oUXx@V<%cC6HmCnCUAR3?+=eE3vAEjkq zzun`KrmoX37ZG4Tf1X=BFedFVc=n>@UnpGf1M&0q)R}_)E74a^Q?*SeV;a8uPMz~gJD#)zv0vyF0lE(?XKlxhXZu9iTQdc((^|Fy4Grfb z=f!XgOZyAp?eoxnGP*I&#Hv^h&5QK&vPXZ zL%6g$zBwJxf^u)wm*`%T73cSS-D-Fh|JH@0gkPw)U^4z zC^SWGXDS5JUnsj*EOHl$()aV;EUzZ1O^nK*gQ9G<#8tMag(c%Fo`fPKmY{*>=cN5& zZ3d;7Q{L)p_ysmjziu%lBQI`469?89nW===y%#Ii-?o0FzXw`8@dITTzNZEBGmTrN2bdi$+T3aA^7GH%|BS z5k4wKVA9U#v?yt_QF83*IQ269iH|OwaZ@j?^~(#?Q(k;^8oGNv!6Ca9&SBk+3+Pwga+(< zR=H_vPbvFD-X+W14h-%C_$!*sZV(5 zes#)STk|Xf%AThrb!+MC)RZP8dJWnGD|FSRv6_EHOS1w>?W$zm0*m%cYbYdE-)6Ud zNXk1=O0^RYzw7@v%%|u0``0P27{{;YgpYe19KGJZYA!$KlUIR3he1jTJ1UEx1)%#6 zuq2y~;NxB{TQ1{5JPPr)x#2r4>c33Z17<6OrNXP|^f6L!x>)c@1c-+XTd|)`YUp$S zv%8O8FLnP$CbzBg6dVZ@&%A5O{)U@iJ6D{c`%AI-q79*^dn7r<8loUUzx6{if4ev` zbv~2d37zoUfSuDz83At|;H^VlYxESJh3Bh=hZ)+YjX7Q;OB43BH?2VR10A^#G%o4W zj&oJsFVy(eU0FH{0+*p!xz>G?JJg!#b*n$G2UVn=AM-hqS~)7JP>#S=_+iHqO>SQ7 z-4t4iSCbX(!r!RuJ}U9``C^ox{J3Z?zK-5ot*-IWtcp@F)kLUlR-7-Dh6kLP9Ltn0 zSdub>dF#r^?0(HePe@a{AN9ZXyKFz@TpNhdTGvJGPIhXspQ-B?dKPa=8gaT6jYW8W zS`xF$F2U``Ge|0{SNw+Qr8ZQgHaOXZJ6S!gI`gmDU6zwY(jO*E<;?~J=%{C5jyV`$ zZtUK)ryR<|5Br8R^iE;G7GvM&tyZfFh7Pz+Zx()i?!8^QAvs zrAFMXpE!m^bCBxSztvj~1BjCS7|Xbnu2AJr^b@}33zbY06Uu!LhxML(@kYui3A(3)D2Tp<1J*b03Qouu0Elm`A{oHdoYH9d&5MOe%E2n4RWB2Aa4rVszrh_sRR8t@AtF!aPn1rWC z{>TXvpQFAb$}49tV0G@B|4?uh=~|@t3A4RI-KjU@nZc=t>CEB>HDQg#?p&|N zzW{fG)01iPGNDw+!AgT7$d5nVfCl(09O1S0fRQ3OBG-|+@Xc!9l5%Y!3fbn?8TIQH zaf1z%f?J%J`+8$I008w;<&7gKOeimDYxibtxc;;WPlBWkHoHO_#|`dAxAFyLjz|aL zh%m}8s(R--=4D(N(lA~d8}N0mmLVz@7$BjXOx$rnyNt>j)3alddZXg_>G9g>xosbm zIjnJhXjA(y>04Yy+uM{m;eV=nPg@YB%Rz21*M`FhjDx>A;ao*(T=$060v(1Oq%O}- zCnVdn_BtI6*UGeSgcpH!bQND`?SD~}^2pN%Ke;pYz%X>!5L!)Qf0eDw9^bL(N)RbD ze8I=;d96jvUom63aBcI>^y!Zzc4yVplJ4It@uz>ojaQ0Qdl-T(U+W7CZPx$^uh5Pj zAeq><>qE>!c+T@+|6iYdvt%Y$PT_zT-xSELcy|L^!SMjdtH23mfchFlA#0TV+0%Yl zvSjp+#kI7d_Q_J4^-xS7@A5V*R;ch~qMn<3p_j|C$eQ(`S9$PY&n@}Dlww$#D~DB( zQ&xnGpX_0O$^l)P#|?#XB6}^1pd$@wX?J_i3>^X#GgdqxSk|XReq(~b#lZR^Z6vQ9 zl*DubQYB`RnCqym2>xM77H?kt&yvDh>c6BpioOTskHF)>ZrsfB@mQ6#)Y_{MpdBus zh}DBtMc^wGAVpID9^iHGmBu)P~HFKhFfb@FVO` z{|d!{DlW#d`=3{8*PeXf)PA=(uV0sJ_()Chvf=tz+U{^+wZiF<^XaPykd{`O;jYKW zNV4Y5-bUu~KhYPTVN3oPu$-*7MjnAl^$5o*um_qBUltAX+ykgo3ilT+)0fEG0q*>8 z*w-m1NlK-iR%mz{c}qCp!E3{^p8qxdSmTD2v+sO))7F}46k1+GjTW&DNyrGV)dQYw z0e0tO*Mbm*dvZ=^Ufnkpg~G$Xk5WbfKir1rqyW0NJr6O{w7I-hh9?4O&zZmKi&h)R z&XszXCo|+mxhQ#Iuy4D5TUz`yw1)9cY2=thsNwLFViugJ8Tcrl6Cr~zPsuUQ4G^k- zn8y0c0oXMpJ6xx*sHhdC>-S;QcsFi5sHfV@bc%9woEsqk+tVy80BcE zN%&7Z5229{mQHxW+};@fnS~xG;GActav(p}ac6A-$2;BF@S>`&{)3iLDh9xwLcMIL zmF74Guxl4DdoVFaORj*d6kzKc zJ?`OP zlyFi&^S-}U&_UewGvw|?*vW0;?!I67mVf9yF%ISYZ`U(5<%S;l%>Auxh zZjarr`bq+yOd~&8iIG5v0N@3M;qc?l8;9Kuy7`W61GD?*uDoiY}5r!J#-#2VK@6~E*bpA~}x)H^n>9P<>`aMZyy zMZI@vJUo0s^D;M3dKi#O)~u-m9z(f`OHkB@EO?|JdZ0UH3( z{in9^&&j#o`swW68~O4-Kz$`ZXof%&rJ9cv!#Yq*e-{-L!r^`q#?bvDW#HGzbR6Di?nBs|nt@HrdAANc&zx?2 zot%YvD-5p*%NKYGGFhVIF&ORFK@@Uy*s+g@SaC%6p2kqeii&ZKiPze#OcS2Tv0ZJ< zvtO)-sp;IoOy;dvZ2D2dV7KJ`fvBwXoxLtS7I)2Mg*SLDKL9J}fu-Royw^k>K3R)2 z&bLwTTvr1um1W6MXTV{9_uyy5gh$w@24PkcEzJyFNnYDv=i~A*-K454OTmsb5YMTV zk}i|{yM+z3$uZVSgpmH7fPJ+z<}YidvN5cA>Es-uEHE2Uv^0j@!r|aj-dxr<)S>=Z z?Cd%(U7~~J%0-U_Bi_fzp7E_Vy_z!UtLgsW1>5(pacX-cWR+Q`ex@|STjPj1JcIQ% zWjhZCIIwp3q9We-v0v-BCoib`Oo9aQ`F|&g1^~DU+i_ZJ!c!b#2s$FD)&kiZo*tIP zeFyz6Q|6-rfO4c^x-Xoc zCLl`uJL~tN`MYjpXWr0@w*!~8u>BG87G=zew`l43AJN_?Vw#>Vv*prYq(%R>e0Pl9 zs}g5r{k}m1F|QA0SGh~6#w{O9pU-oblwA*k23tD91f#Z&xw0R2QAfGf3llNdCnmR8 zS+Sm{gcinqD`cbUKDZWaChZBDOu-(CeBv$e5E}P@VOh!kFoxTRRo@lZ6i52x%n}Wb z`_@NFEkl{6dn#G~EBUejnT34N%l>p<^vcZ?qK*)cs*bdJ82i>JFoesd(JFx}k|zx0 zIb6R6*ycBYnb*6*K;o6JkKVNgvgw^IP51~t`xOggq}WkV4^SsHaM1*^l0lC3k$&uo zKRUSU^}If6BtOON_A8Hi(j+g_^yp9RI;X;NxWKb-rLMC2{gAY>MblZ#*ooB-e$s=( z|8#GSGkR}Kjx-x8UZ~>^NIxEw7529GTzcE)4g7*_nR2NntTOW0enJJx;3{fsu|=f3 zqlmJQ(~%*X5EdVr2*0{8%058^-EkRfL1OdB9rH`O#8EqG7dh)ktes6rXD-k$SdNT_ ztM?L)R7_smw(aKkdD|O#KL3{^z1f@*=|D^H;D;H$S?C;0NjM&Dp*h}M*0OWp8_avq_W&4)F& z1jS31t3RJ-A2la4tm<+*?ABtPv1oo7K0U-_O%&sZhIlC17C6Zl{h(agPh@>$Mt&OB z#eZe-q^|O|Csqc@-i}xX_bk~Iiyva_b(*Ovz2Q@MQ$#g=OwGKS!{9K(tgb*=hV_{-MIfis!IDHgem;#F0|}KF~?R->+;z9VV-MB=bxb z6P58fO3F&%V#SCt9}bsD4TJ_NVD2}krEaeJv0Fq+g#N5tC-`GudaI=AIeq5u($g>j zwK&9e*&|jSyrxw`s>RwW*#fFuYj%Q?I9{R1?ZiHc`Gt1*oZ0KgLyQ|@H~H`g^MAkz zR9(dM?hM0E^g|D2+UWCH@AWzjpm_Tl6*w`^RNMdPn1KCyAk6YVUU#yc!boF0GV>r? zV)H)z_#q1?Pdjn1^e4{P)24BYB{Ra&o}u0hv{nr#yJzGeZQM^?j22Bdc;4pxYi(GF zTV1>51uMFL)pCqsA8p*zCm7?{SE;9hV!yj@L}Vp|Wn@dok+$#AH5L;mJT)klm9f`t zu`3^yqeky=k-buc8&YEB1~5~9$&;_dy-Fx3dAjl`@b%y9pO^jNt6Bh)XOu?a5-dUw zGC4k@y(_ob^RAP;&Q`S@YL?oZ6&~o9AAxpL01-Z&?LTy%r72c}4BovJMekX%9DvW? zL2YTh! zMuotm7gL9^OiwX=Z7gsYG<<>Q=rbB1H|g&ASu+$iApBDdu(2gs2}>bb>!z8v99epJ zep$drBo{xzRYK-D(j&jXK&oT-*MP0F?#n=O1L6}tS>0GEJ8;$lQYtfQfe7=2|OH9PfCg;~JRpe^&&Q#qYa5R=q#S zP{TI%8q&S1q^v0x#E#RFlUiVs5!IIJ;1zi&KcgUNDt2Oqk_LnT%m8#iiS8k1L5>Sd z&%Fq_xeC1QnTz`Nx)(%K=v7THZ4 zAL$z$a=WvoW!Y-4j}OsM+Ob^@b!7LJ{tpR0VxAp34R7|@Ds4PC2*P@0Z$t^(_roi) zEI9V}1_S&#1Y`|9O^?%aW%Qge}aC% zA|*iIl%k{#B6d~fBGIQ9F2-^lx?N&CW`{^k20J>k3U4}g9&{J`UQVxbu0>+o;|^KY z#b@2m6fRg?Z#C5r_8{v2DX7oCRmbJ|=vUDA=#8!($D||yMExv)>f~#h>{lK+g7uV; z7Rw5bFRwvBfVYl<6-6H>nuz^~+Xa^8IE72&4|%%UrW!4#>wJYpdlkTCi@EE)LFDuA z*!as?vahW#O)Dqlgt;dMFaTf3f7!m5Yy+?!<_Vjo%XPLDKhWyeeM#;>6emA*Rbvna z-32I>qbni~-v=hl8YETF{A&G_l}`3`>dMsPmeSISE?V}+Ky@ikm72v(De#hf^1)j1 zrw{$L=T=Tsn-ahR7??O2YQOw_uoXyU0ejRt z;R$-%$2rH+J>xp6$zf*&66<*pFa!HC0lJrHa)uTz`u@P82P19eb8XLm-ds-~IMok^ zDWPKWw8|Lb?eGT9CT?D^e9L31mf*!e&f2(D>1%P3MB{}bxM02+{# z7=>*Up!bUk>hAS?HyJz1xv`8*SR`7@DNkuA&|1}%+E<}W%OTQ=06>!N6UOZz6h<9$ z9@TkdjMapc_HnzSwdl%abrjgsAV@7DH{{?}HT4$#ZN^w&uDEm&N!tC}m<_aB0=RD} zyetf5+E4%7iq@R}Np_a(j&ejD3Wf34JQTXyG#`K08wsz0Yx7nZAe{@1s~&*Xq0XH{ zTez_+H`1coRntHDxt=PV5>w{YEFEX-Eg5;q0DECFiZu|;-W=n{xHsF9)NJ2wMg(W) zsQ-i{t!5Ni6(?Tf{wMEVkgdViMis-?t^@2No%>J^B*oB+*0@otD>Ux(B#ZK zsU%hlX}rKnH&--%|7r?-kX+Vi5NyCE_kGMXr+}7+^haG8w>9Xnr8&A<`?D#+^mVbj zWy3N`-pppWHjq$8B zEV^AMM6ataI8}Wo{_cT)oQM$Y!!Dh>`j{KpLQj1e6_djv)3*0J_y=lu_SA$*dSn4H z|AWV%qx|!XM1SFXG zIS=i4kWpBtIw&b{SI%7TQy8Rxzh&L0bC$JP!=>8!x77Q7BVzCtCrdIet|SoTVMclA7OU@}B3v z{0bNAEv2jde`eI$AnykSgzWpCTL4d&kmeaLJ5mlz$*DA!EC z#ibCFy+TwSOeVcyO=A4fH14=Bo9#b*8;ka&z3l!yW-zjIB*fS`c>S;uo+W7SQQL22 z$2y9|#fEJifIgCv+%##l&LDgW~X z%#~RvU4e~}z3_ur%R_R5dw0O;)=0UamDeX4QPvw(V4Ya*Q7fH{m+RZ5%{4$OC!nro zQW2#2+$s)Imq~#pL;=6j+qdeDr|9AcE`jwE5YpNbW~I(y$-i<;W_ zmvQJKlb^R-zd%8WznE%?Uw)Ah_Xlki6h~>FM)d}$^ol9jQvHiOu&N@1h=go+hCuCT zh)_VTS;XA=zEsQ|VQTrtrQmhll49XEJoagv;!S`eV$TAo7^)Ttv6)sw>pA(zu7{*n zVv^=%1tHvOh!i(9s_WrmRM6MwsoD!TLyXrkuv3ZbBU*C=s~nRmAQlW`){?sATU~sm zpBwiEaadb!jWmc;|F&+fSb6JJvi=eb7WgrSC)Bqc2Fwrh{Sb!zL4y>iK&Oft z$TsP;B0u`{ALj9AJnMHF7N+4~MN4fz!gLOPa(Lx?tXwfBSa9c7FDBPultx{`O;YmX ztwa<=!*6*=R^?~{0#VI$*`rvviY#Xjuq2T*LAX?JVO2d``Dh>x$78n6tE76)Ph~ZH zyfG<2wu{IyhtC6<%9)vqbFk~>kGoq*=3aw~*4e2%U>tH!w5>vlt;CC-@&Nar_eHmA zBnaPSg}Nwd?f)`V4rpx^N|#trrbvUN9UB>SKeY5?O>H|TylZ3ll58QtBd5&*DiZ93 z5dbbnx@;0c+#{LhC!65&ImXmX?7&yRILD4`#@x>q0EgYqXcrESnQfaap}Oc}ouN01 z2(c0?DlV`lAb}oDU1t@-h*}}U1l@3V{iF#M8X$LEgflXS6e%EDZWi7*zj1zDp@kdG zsLFAP+M?QUMrrYjrns$!FtyAI5h?6xr-1=B(2$cmt;YX!$*xoAkp2dd z>cEwV>rj$IIn6S|T|G&57bXvHsCyH5M1J1at^wz@1~%#_UE8g6_p~$p7ya`9OFP@y zdOhLxuj$fH(>cT>7JxClx=^tF zD2!1q8*_|6t5Xn54yFgXO5K~@i{f9%>Qm-4whfWCm?JbE&jX*%8ap%)SR()51|sKQ zVHbLBg%aD_V>ed0gqrJ|6;(=i^)=q)XsYXv=x39O9#`q zEJ)D*KtTukgZr}k8=}OYrBth)6b(-!%V{nDFe=6c^DwKWZFOFCq47LfB=jtC@5VnX zO*VkK|6?NAVxwh3IQs!e{o}ar!(PS(JPqq1C>7C_ON(RMN*-tp|DY#>$%bXqaD1;r zIQ@3ZBe1&#fe9x{{$a3>ME^vb1IThe#_y>9tgo7&F~Pl*Nj$rxYBw}Lq?X}+#g z#7qA|=jd){`A?mUz=k^heox^Lv5k>0E0-Z+ZoSeCLUA$~<%cEIXobb(P-(!TPqK?? zf^B)C!LsP=RQjOCE(P1YwVG~i4f7Z4CyHW{vC1`NG`j4Nihcd+3-S&DQDePj`>QY8 zKiz!CGn$a)$>5qSDUGbAxV_-p7uw+S_o2Cp#9u$<2erX0>-#w{DslA>TB^V6K<809 z9iMr*_Zz7$#m3SAKI}>zdj{BNO#qg3c^j zs{nMz;^J33B~izgSbAO@(bA(2^K7}|+Hxt>Mohr3^)9qX0bC)1wwD~$X(gK^T5F$S zvM>QV$t3WuQ-ZDWI%`Ds-k|IgNk~|UR+`a9^3Zxp?!Ts#d=NXhox8Ti9!~N4t)8>; z^B*o^N-6IWKLrM?{S%u&7odtybi%7i{B!!h!DL%!|8k1W-N#B@^EBxTe2YsmAGc9c z%kLxH{=*Ivv3P8qZ0G_mawot6sD?GcDW5rMKdC*>vWc1wS%}ddFv*^>t`(ri3K&&C zVS{xPIzRF6tAA~JG`G97F!kdpw^BnCkL%bA>@38_!f)*vmDJ=W2sFG199E$Z^hyir z*jzkMnFinD{y4s_u`0UtT6B(75&ox@4xra2)0xno?`U}~tV`}D*!&)UgqR0(w$4F+^H1=)ClJn=+T4v7d zS7X~b2pN2)eQ$X4*T%08mi zr{4y~>rpC_eX<;A*#>OFN32>y! zU-KL_RQGV0Xy*A$++TBbvZ1*#%c3-$P6`>EeVXHXB0(p=*Fo&TGE9)bi`P`*p6ic2 zha{VhV!wY|Ane%`VaKzkNW)m7tCazvMtNU>8IjWNgkZ5jfO;S6)qMcCd|{ zU6}tL@q%OH0-xyhsXM;MpZV>f7MJZlqSfvZgndnQBia{g3=og2Kl%t|L;}RUr}M-d zuO_;_cpqcp9>k^7t${fBJmVytSPFbEz|2!`8HzsJ4F<44iI=1$koOZ%fOzvRX?+y4h&x(2* z;G{aE*n<5(VzEz6brUdUoJEm@K0_Iij^;m`l@_t1E*1SqssS zI0X_eTo9@#8N3IY=aSpd?<&6}v?oI^w3UkhZ1J%X(~@D+kYOHp{J_=t)yeEY_U0Yp zRI7abDv|JufeM|i)qpV#utXR6lS&oa9xY|}-GBPMq!Pupx34XO?^VAs$Nl}Xk;BgE zz@(l^lV|m@bRI8Rdplr@{Jd0|?fUVW_#q|oVHJg%56(bL;ky66b+(~=QaX)~&lR@n zbC!S88Z9CqnKK)1N8J-fPDRC%A=4;KLe28Cyo2ImI+J~Z1&}cEOqGF9C?_C;IO$aW znBEQBj7RpIcdjBd%l68p0nWGxKjkq@o%h>mzdy(_EAX)DyEdIQvyURA&f|XfDMKi( z?_v4_4>|b56&5az;#U33E!N`5`5RQu*F3i)S>y~t*#1vA9zZshfU{}JaW`lU^2K8X zIVt9LPU~!A<3w+KT&zJ*yap%;x21m1%%?LQSXfgc)L0P*dG#Rp`0XWdVhsSlt@6t@ zU&m(4RmmO{ZiOBgJreLx>mj;b(S)+$+A+4yp5{{%n+VGjc3g?*t1E^GnJekJ7y%Tr zAAMc*qEW?QY2rhx-t$BJw>&K}#qjy+U^xkTxyb(-C1F|E-skZxF4v!tJ&GFszGzko z++G-G)B(;3xe{;o2^#mboPBS~f3{qj%~7W0F_Ia6CmpaZS1C6@0K3^d)EyeHJV*g7 zZt7@kkIrvs9n+8>#H})o(TblqX6LyI1x=^NxRlN4eh0yFjz~!KOHkBYDfl9mk>4G8 zR#Q{ggu$D43iWh`pQD3jB_6Ae?!-P|OoW;;1BHvu3F2p8vbbEuhBCOe{co<`f{_g% zN5M|KGh>x`*%s==c*vp%k^8-NTk^Nlrf_86v&UHiw8@kwSC$FiUvi45p@CH#K#hYm zIo{a1H$N72*<*I=l^3_)&MDAvOtF~eu+4(eV9>e zuJ!neQ8liw!e2=N8Z?gia-8P7|E5ha>`dBu~jGt7Y#^NnvZuoyI0qZtzM0Ejgv5sDUGDprOWoh>6IW z0f+qj4Fp9EW0Y2IUGuPLd(9qftQ6ef#OzHg1uQ}B^JLM$O-`?%j3~u6)sAn>5p{%r zvkYJrMi)(;*QvH$g*w#BFFQYD={oCf`gKrCU%W1&E$2uKkZyC8FQ@zs-Wv!h169Tc zQWdm;O=U{QlcF66HFFd2SvNZLJw9@qL ze9xyvyPN=%MZC8Hp$UCmUMH1XTuY@kMU=}itNR`e%WmxuWfw?_ebWVE5M;d*^h+CZ z?&o5<_DG*L{XK(+0HIntscHo}%n6*Rck_|4nU{u7zP@rw*Pj@rU!hbPqWc-sH$CX( z^V~>OZ)_*n62JVkWcqe*Y|_+-ITAilLWsw;dQi07JgYlAr`z3ZMh{edqR`{EiU%AM z2+h;{snXyiH!+XbI58(fV2bnZ zwRV4rsysmLpXb=U%OBKD^RkFb9NfaoU;0DM0||oMKd$=K!%8LxDhw7P+Au#k-SWicDx)F%a7rbfMcC@X2O78Y4~{T5l|ss3Y3JgOi7I$A1aK zz{X_@==v@c;`eiKFu(Zx_$Eqwj4aDy=`L+@O_{M#QHkLbDiIKB1D>&%i)?tvDhb(cI7012U4U>|=*PLiP9Q&A37pt7UCx@sH6lHH% z7vJcRL8iK7y|RJH{^@K6kD|QwIrzVC**Rp|ga#G$xs3MC;AvJslRcAz0nK_wq#kq1H&D-k6wr$m zeT5m){P)YtE(*TUXKwwSb1e@qiz*+o2FZscYT?YiWIlb~G z?7P9F6Mu4azTapw3mhM70YzGNv}(Es}oX*%n@ z`u%rziM}c&y6M{@5bKqeDkmuHw&xQjC*_%1u-;!;lK75wC~*KzHsCw3aHa!j7`XAC>C4%TjW1oGVw*Wjcqe|pun7l5}u?~We zH21KAd>|;7zZ|>*UDiSRjpXpzvu5JGdN}itLhQ-1=+o4|bM`?es7Y88Xrg8(3;ts_Sp5@2gCA3A5 z5cRuL^0KrSq5{zQfAb~H<3Ex+42zP*2LG%Zd`55rZ_NHF1@bA38ukFaa#D)-jIS+njU8UfkMu z#73isI)}p>c52L3aFa*p6DdYl9-VuuCMacl)Y-}I939y-t}8341V?NF1C^Bm8C#T5 z3RU1LWB{Bvn#N>>52|*q{Iqwyf4x4``(N;0QRL4u@KB;r?65(k_;Qy=TG1#*Q=Oca zp@~TV$R-YTPmfy?FMXbC48mpl0Z^>Nx{LQB(50dCm}r|9oNGPtwf2`6=eF4~3wG9G zT;h1ede z7fpycNf&sgxUmbhTlD)cBf8CJncc*sdhEWWnld$KH#sdB@IbHm1Bs`x@415h2TLG;5pH|7i_ZRjsr8 zzZS7ZL^#-4ZsFJ+w%Xu<8nLnj{*|AjN_iW<;|Ela*FLu<3rRrJZF+zJM>GB-z3DSRo|t$|E-a*YY73Fo_*%y+g8gMDmmp(D-|roN zKZwMnPIv2mTY?J?8R3q^RvzN#$ued8`yO!R^$9VIY~?5tK)+q=SacFtu0Z>CVcT3i-)~n#mA6%Z2{fp;NZ5F&y#x|u< z^!6JLSXn)d|E2uqTAE=8OV()B7sm=RdP(KhRzsh+4BOkEHZZFm8i-KB(V&E*ZiJ$Q z6GgR!_T~?0&X4=1jTKQ?;L=&{vN@OI zC*uAgb*=03Us@}F(2yR^eAyf`8&mX_=P{sk@XnhPYl%7JeKuq;sxw;$nP*)vza2j= z)46o<&GUnO4T33wrAY^x+VA{20Q5qQ_;HjgZ#8YzMPGWMCELb7ucW*)Gu?50K zRtmVl@+&Gt7U2BJG$kt*rxloL(aF!)no8CFATc*L7ddwiLX(I3=d$Dw1e3EceVVgE zmRcr@r#NN<`H*G?^{p5HfwdmT>~*uJQo<8XbRy3m_{anR$_BF zbAcQhq^-Nzl8p!UYpN>*`=~w>D^8m!OcNM={l1;@`){7;=O2c#6Zx3wl!cYteuP08 z0m2860}8HS-kFVP(-|u<9&ji12`7m85{o24<|!y8Tk2=aqsXBw(cgvh;KNj}hjbIw z{-Gcd`h^ACvj<_9D;Mc@qShJSr|S)zPEk~rSKe4dWqEq)bz0YJ7?t~UfaNfrr5CHi zp@WixbU^8QWl^8pA*2^c2=hOwSxHj7w{&Dg39fQvO-{od;wFG?BW3=v<$@s?hY|&l z+7cn@z%O{hI(UTzL|wR3w7dm=i_OWElth=$g5@v{CjxjAxa)l)E-iSK=`eGdBo{r+S?g?i_7w&{(}i^jk5eXvo^ zqrz1fOmrKL(vb9j8tTK?b6dt*?^Ld8im$Jy=%$T}q2wY4F8gJc9KTuId5xx@ z0NLrW#cr}X;2XMbPiEBF#&fms}yyZQ#=w14-y)}s0c}xC)*4sCB0t$R=uYh0VceCfp z3?<1|hE@nMUeGY>_|29L(xcvUmK?JDlX_)+JWjl$0+sHW5f@Wk1Dr$vOmod`kS%k1@X31ClM z^}yRSt{wF3s&>!>z824ge)wIHnCAIhNr>f{5_tsXuf)M*Dd6A-VL>Z2(%jgW{xv*& z|45R+L3#yT;1EKo^Q(Sw@26=LZ}Dvi^ENT8>adO!`5o%HB75?cNx^gL#bi|Y)agz& zJMo4~)BsAH$OrP6Jl3Ep5Y8r_ zY*NQ#E-ptFRwmlJ_C^|*4qDiRNC=kvpHPf*E>uaQ z%u8xUzm;(PgUZQ79IQZf?#iJ1a^J~iOBLS}I)`t(C8{+KfOo1)dWk#@JKv6frUaef zsCEIZ=iYOTxvS4trdejSoXzk*c{aqw{p$IYp9~(TT@OgGe>yDiln^PI zFk2fW7kDxlxj(MCW4;po=<}o!yT2oK-sN66!=Tsy%r!MqY5~f`;OvVw@jOXa5X1ly z_-vZ88a~(tMDZCU#qZ~a5BRHyt8ScJSXmti8-+iTB`Y2$k z>`F|c{3VRqh*WG?U?32f-*6=NSi%6Loo$Ek8z4QHBN+X%i)EhBL#@8%W)yen^MFew zN{EyH2poh(&4NG2|E<;w{>Kv84|bF_3V8`B3iQWHzuyPi!*-YeDLY8qU*H+)Tm34W1DSC4dYn zrG8UtlE(2ogn{rAK7ApOg=nJXHZr(Hff_%Mc=mK;y*m-#iGZ9bb#`md5EjCcdGIWO9HYX;cxn?!T10l2X~7~9jGD|hFiLMov%s5BK}1cE4LH)_ zDilC(M>kvfpHLAPNxEiUH}os;X_pTZ6r!nyI4sp&fW-%3f};^mXz$zu@v7W;#}Q07WEo~9x^ao3dwQ>SORY9_=it={&1;3)`G_%dh`YD*Sa0A(5JV>p2>L|)w6^agAIUdw1Ki9K6irIs3 zp0#d4(L`_NSG!r>4$69<#hD_%aZT`O28i{q+4VjG0u<(?9BV;S*c* z=bg(1mOvWcFP5V&H(g{9bJUeH7qpJ(aXe}=GGBCv!`hj<^W6X~^@!4C>bpEf`}e@` z9Wrcgy*&2yk(AoPFdOD)mr(^=c&xUC=4u$a6x-Uy;li30hFSv@MdUFQl~qRNK^6bj zW_h3Z^yEol9j$*x^6nX|{xqtIi`ax~^Mzz-a@Xvx7_NClW+G z%2sLkC|i+o6|SKLqKQxZXDbv_VyyX+r4qbX?zhQX#Y*_z!;c_29OI8Y$qRD76rqn5 zL~(Htadu!evk3$4pVBW~bSg$kEl0Oh#0HF{6FYg>R9J2lJoAQz$R5;%#N==ahQ|@~ zORcN@5z6F_ZutBw@}WjdW$PxT!mv8?F6kQ zc5l6f|HJH=0axrRcYg!KPh-lBOw~AuarJ%_l3^qo!o4snz|&>`AX{`)zWgpuTpi)> zu;WQIwZ4G&aq>LSpV%L(q(1(j6soFWQIoTK(tqCw$C775O?WE!tIh|C+qF+;o@M=9 z^yE?wcae|ZktO$d77A&kWpbcZiqIymLa+yzc5Sn5-)lqpBl|s37)&5c_n#%Y>^!QJ zQ=xQ4?z(+{vRl^Bf&Y^1Pjy{%bfG(ky#5x>pE@52DV?n(P|w@B8WF}$t?P028D zxPbT!_!tJG^g)!Z77h1)>V) zLE%W^R`v0%7z_Nu32#%_9F=)A^8O=>`fKEaoxnHO6q4Vfv3vU~xl zrgH*pf90y8k-EsyasFR**Z$A+`~J5%&DjW}Fw7xmZ^f*!5g8pEIv_$xdRI=NCWlQ3 zNe(4CIMh3-^d^!+r!7Pws|X!1lk+g=vDrTNzTY1{f5Yegb+5HJ+FJw zqODPLIJyb>pshNZmS1JvQ{5Qk$qYRmQ@op$Z*QeFQ+kxI@nQT_sf+y*s*-N{?RHqu z`V56|hn_1ld$4sW%c=c9+(mNyB$VvyWIqHVQ%|}9!MfS%luw|4G7(;IK(WgXs;~CtyTL2JjL{;DIx6a(Uyrj3;W#O%P0!>a zg^PVU$bU*ioICXUj?q{(_NoJwexJUXJ618FG**2B-DOnZTlnrv@AQ0X>)ZVgbPX~K zy(>eO2F~C_f6pA_$EyOxfdcp^!O`OxCQAwtDND74uAvpbHFP-1c4n!QXs)9G%KB1v zH_B}9KVKc1`3m*K;wVSgu0SmW-mG@RAyMuN&lMb_X zjuUD!^*4L2Kl1zCIf@nboJkeJDZgqvsOn#h9b+d%$gMtALSjke~0psB=29ftZ-o|d}2|$X-Rdc`~8gBiRh?oQgH4i zk#AF}1%F+ct8gM1h+Tcyfp^@oow)MYY~p5(;jNZI{4EJoSVtZIdINN~zT%C_xYzqv$PVt`tvw%dYa6DnF-w0_{}4hl!rl z35{Bhhtdo1m^6cTiQu<>gQCrs)X{&f^STA#foB z0l++5?%%{Jad7NufGY=!B8Mo(0Uu}aOLE<>_>PwWDGDG19GmX(6A)7YjiLfzqTd2n;roB6vD=A0b6q2A0 z75S%~_vJy79RY(dAcwhLmVf7Oqc zH+)*pn{X`Q@?YS%<-1dh&7LZq9OW*?Wqp%Oulr7|aaiU_wwe$1==V`dmGkiDz-Eh~ zK+hx6bIE6l2J0GuYQ5uD_XD8Ka*~}Ulcd$i%P6LiJz7If+jF= zE~sqfLo&Pw{q%6UNr)f+4g?*4xhYpy-j3cwUa&BlRCw4iT*uSX;F=$o$SdOX;=X2z zvDNrt4n!42Zbk+tZ42%%pkD64`--_%M7jdg3h2#+SX^Np9u>3k|FnOeY&tP`Nv(3P zF>zX!vctvQ9C9;>?n=PAQx1s7SnDZo1ilboLxj!xz2Ir5N;qD(yKv3GehuaJ_gHDD zg(3RWocwMyJh!|6{i(XacG;eEPVIWW%q;g5uqhS}Oic&3tyR#5p;&Mj81IQtq?{Up zC`XREzq?X7v@Uf*XQOjQLC@_d)Q!$G$iMSP9AVK6y&@Z)h_Sxce5WJOPjE*nHCp3< zee7i|@~Q*+a@ns>6)T;!sv^vAi76LhKeG9TLUa}qLe)l zHd?!)1pncm(o2vZ*CbE9nsUN7tyKTZCnaSL$j_eLDkx~3UW?|$_ckj*a%9||*gyrE zjg7#3%%`tNd;K=lhHq&|;K=%~w*AspxU_dh!9bL-^{1~_S?&SkLsPyZI#B9l8SHxp z{*hJxasDJu$ZS5X3$W^=%lbo-o>EgWc@8&aW|xkT?_$Jx%7}GGp)lo0Foq{cKj%fr zTGU>4(PN|;Jlk|Jx|*bYk}@kq6x{ue7I6Ty*As_^8?S z)Ts;``zHHScFNTR%iZuTS$KH0w258IVp`281W#*77sm|wMRBatWqfm~jK#q|* z8m8=sWO+yHzl+<6kKxig(9q%^W;SmGFa7zietAS|^r?D#@ zUU2R9fEu6XOAohWVmt(U5jcgn9)0O);_J|ffF#649_yFw()eEAyMY92@upfjpNVt$ z(!npp#Sjz~ElxY!nyscvJBC`HjEyQtw_gAROKZ4%*P~hqBGPfd^8BD>_~6t-h>XTx1GjIFY+M}tToCa_!xs7c-Scbe#0OvW zWTht)Aa+Kmp5;d!?#gek>pV^{ysx0to%+l?g3T^X$Xe;COqDowqde&_WYBEkRO&WA zf=a?Osvgey!~*$=P@v$rkd2gdd$J}XkYG21?Ij}jz-q}xQ9}UYx3>?yPo6O=YpCrC zdRb_=fms89Xot_TC|Ya%S(0yasoQV<$UfTm9?(|3u)ptsiIV0%Q^5g)+r6Ptfad)7 zWPBx6Y?}z*53&A_Fskfgi_(HkKsiX9?MG`e&8n3Kok=Lxg{H;1T}`Wiq&@z?$~VwQ z|L4FyBE><}Fc-2}rP|KbwLET9Htc>=FkPe9Y&D)gl@J=58<4u~nA5F3{debc@@n9` zry0Bep+?#PjkHAE^rFIe*vJdOwV3UjlP$92%2dT|W;Lx2RzGkl&0beNv}J1vB?cK} zo#!z(M`!XCl`d@2vBYouvGKjMx~14kj$nh5+x4+o{rowEc#`^XSeqi-PiOJ25Eon= zk)*UA0$FQF{{55*A!chn`2r5}kL)SCpToT?E1G;6 zqWNy#uM5A58?g;_Y!I0dVJdZgJQLA8vxG!sp?7caK7KmP z6q}zLK4p??$R7>^zJ6209fGSD1?+gJ0NGggf9EoHofR0(epMrF^1oZ&8+RWMIiB~p zpMG8{W)+{`g+X@5ka#`ABb*fcqv)?cTC?n!N|i%{)VCNJUXIQpS11Vq14!bQ zcmllWtoNP4%SND;Z*VpS>n|Acj#}^w4@r>~SFBlHmedq@2kHk5Ic)?iM2J`uAF(#) zolf2l@x=*)72~Uvu&C8ZBj?bSPG^87o~C~PuAN$>?JxRE-lqsybf5S$Nv|HnHAWVt zviQ-6g?*jD?eD=(`aqOZW51>i5(a89yS9zqfKYx!k58Iz#0J+ZAlQc)g-(B7he?l) zcuIgTbV25uGF*UQzo4bfy1nS}w2jYo*@bbay)=$z8!yGNRKQYdbo2CO`AUq>goco) z3sARUal3lWM8;lPHRqvy&LCX)njPQ#lLg`&XNGfcMO!@S+)h!_y^~Izg1KO>=4-VQ z2#KW*#G-#YoKr#RJgU+jOdhdd3t)oY(8YL`vTzb(aD)frq|;t6tiv?@QV!A#C}cw& zdcEnj^!7TT0aT@XO&c-#DQi&G2IK5@vvyC0yQ|5j!7r7zmSAJy6D1%pC^Eb$YcTJT zP>tos=@9okpAD+1j>}Kw_Lg9-P7nEtkupv?Z>uYi>)mBmWPU1tY6+eIUqfEL))$VKd80*Ajzp9wuvFlY+c+w00aWmV-Zn8} z2@+=#2?LrGmzB`E(sQsXF}b?!H(aj7G9>fMWASncKixl$j#y%WUa?QZ%X^(^5)>QA zMTj@U_5G1x4U!5;fg{bxar#4gtrJIL$pS%+SVC`lg~H~wT}|G|HmYN^ChH9Q!doGj z{ePk7)6tUg;PQe3>$FAoKqqjHEy2>cm!yZJ-l#WI^3t@(HuZhviLw+OAl z`OkXa!zDJ`P$+{Ojf@?2k#> zxz*lHVBJICsaTmP>4Wl|t4Iz;cWinxEa#uDu?G@`>lKb*trOHX9XnudINa(^p=^X> tApO6;1Cy2)j+{iHcK-i^+W!j=^2vFJ`c`)qja5RxaCJW5ROJ|S`9BI9Cz${M literal 0 HcmV?d00001 diff --git a/Assets/Textures/Trademarked/icosalogo.png.meta b/Assets/Textures/Trademarked/icosalogo.png.meta new file mode 100644 index 0000000000..09f33c87a4 --- /dev/null +++ b/Assets/Textures/Trademarked/icosalogo.png.meta @@ -0,0 +1,110 @@ +fileFormatVersion: 2 +guid: ca2b2689a21743448be1a5306c61e90b +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: From e0bb4811de986466b0790b01a8a4c51d0f9d6656 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 8 Nov 2023 19:16:55 +0000 Subject: [PATCH 007/105] Working device login --- .../PopUps/PopUpWindow_Accounts.prefab | 1791 ++++++++++++++++- Assets/Scenes/Main.unity | 9 +- Assets/Scripts/App.cs | 7 +- .../GUI/IcosaLoginKeyboardController.cs | 55 + .../GUI/IcosaLoginKeyboardController.cs.meta | 3 + Assets/Scripts/GUI/KeyboardPopUpWindow.cs | 2 +- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 115 +- Assets/Scripts/Sharing/OAuth2Identity.cs | 3 +- Assets/Scripts/SketchControlsScript.cs | 6 +- .../UltimateXR Keyboard/KeyboardUI.cs | 2 +- 10 files changed, 1909 insertions(+), 84 deletions(-) create mode 100644 Assets/Scripts/GUI/IcosaLoginKeyboardController.cs create mode 100644 Assets/Scripts/GUI/IcosaLoginKeyboardController.cs.meta diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab index 03bccd72bd..894ec25b24 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab @@ -375,6 +375,292 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &281059589699444468 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 282387889154546380} + - component: {fileID: 256765526658491878} + - component: {fileID: 267047011047381474} + m_Layer: 16 + m_Name: PopupBg + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &282387889154546380 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059589699444468} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0.09399994, z: 0.000999992} + m_LocalScale: {x: 88.50999, y: 88.51, z: 88.51} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 282387885870267606} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &256765526658491878 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059589699444468} + m_Mesh: {fileID: 4300002, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &267047011047381474 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059589699444468} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: db0305ff9081c3b448ac79e85d26e5d4, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &281059592884163862 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 282387885870267606} + - component: {fileID: 217270776815681370} + - component: {fileID: 3134156942944013523} + - component: {fileID: 7526589234644902223} + m_Layer: 16 + m_Name: Icosa_LoginElements + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &282387885870267606 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592884163862} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.67, y: 0.39, z: -0.212} + m_LocalScale: {x: 1.5, y: 1.4999999, z: 1.4999999} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 3194813434804236579} + - {fileID: 282387889154546380} + - {fileID: 282387882118496818} + - {fileID: 8121802616684643442} + m_Father: {fileID: 7706333168622570552} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &217270776815681370 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592884163862} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2.5, y: 1.5, z: 0.02} + m_Center: {x: 0, y: 0, z: 0.06} +--- !u!23 &3134156942944013523 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592884163862} + m_Enabled: 0 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &7526589234644902223 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592884163862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 16e24e82bd0c4dcbb02c41882fb95f45, type: 3} + m_Name: + m_EditorClassIdentifier: + OnSubmit: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 8529915923179660036} + m_TargetAssemblyTypeName: TiltBrush.ProfilePopUpWindow, Assembly-CSharp + m_MethodName: HandleIcosaLoginSubmit + m_Mode: 0 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 +--- !u!1 &281059592966504990 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 282387882118496818} + - component: {fileID: 256765527167650962} + - component: {fileID: 267047009147745160} + m_Layer: 16 + m_Name: PopupBorder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &282387882118496818 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592966504990} + m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} + m_LocalPosition: {x: 0, y: 0.094, z: 0.001} + m_LocalScale: {x: 88.50999, y: 88.51, z: 88.51} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 282387885870267606} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &256765527167650962 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592966504990} + m_Mesh: {fileID: 4300000, guid: 3efe5b86b55995545a231ce66a53b402, type: 3} +--- !u!23 &267047009147745160 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 281059592966504990} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 77dd4ff8b1158a84397aba783cd0af05, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &346980884099360615 GameObject: m_ObjectHideFlags: 0 @@ -1515,8 +1801,8 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 27 - m_CommandParam: 2 + m_Command: 5602 + m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -3853,8 +4139,8 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 95 - m_CommandParam: 2 + m_Command: 5600 + m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -4244,7 +4530,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3387414818891217864 +--- !u!1 &3364253099164516464 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4252,60 +4538,60 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 4946117081540083272} - - component: {fileID: 1555734638323418344} - - component: {fileID: 6328234730399420729} + - component: {fileID: 7380447630107943821} + - component: {fileID: 2437227728573446938} + - component: {fileID: 7038645423114811503} m_Layer: 16 - m_Name: Sketchfab_Logo + m_Name: MeshBackground m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!4 &4946117081540083272 +--- !u!4 &7380447630107943821 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3387414818891217864} - m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} - m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} + m_GameObject: {fileID: 3364253099164516464} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0.025} + m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] - m_Father: {fileID: 534589999851858800} + m_Father: {fileID: 8121802616684643442} m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1555734638323418344 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!33 &2437227728573446938 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3387414818891217864} - m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} ---- !u!23 &6328234730399420729 + m_GameObject: {fileID: 3364253099164516464} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7038645423114811503 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3387414818891217864} + m_GameObject: {fileID: 3364253099164516464} m_Enabled: 1 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 0 + m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + - {fileID: 2100000, guid: 77dd4ff8b1158a84397aba783cd0af05, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -4314,7 +4600,7 @@ MeshRenderer: m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 - m_PreserveUVs: 0 + m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 0 @@ -4327,7 +4613,7 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!1 &3674036727267325941 +--- !u!1 &3387414818891217864 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -4335,8 +4621,91 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 5521146392998997215} - - component: {fileID: 3994049754972082256} + - component: {fileID: 4946117081540083272} + - component: {fileID: 1555734638323418344} + - component: {fileID: 6328234730399420729} + m_Layer: 16 + m_Name: Sketchfab_Logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4946117081540083272 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3387414818891217864} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} + m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 534589999851858800} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1555734638323418344 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3387414818891217864} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6328234730399420729 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3387414818891217864} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3674036727267325941 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5521146392998997215} + - component: {fileID: 3994049754972082256} - component: {fileID: 619260971007631306} - component: {fileID: 2495991278148453507} - component: {fileID: 4355862344388155723} @@ -5683,6 +6052,7 @@ Transform: - {fileID: 8994260351568061772} - {fileID: 1000721931266647293} - {fileID: 1675920430848132177} + - {fileID: 282387885870267606} m_Father: {fileID: 8645625735416266758} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -7229,6 +7599,89 @@ Transform: m_Father: {fileID: 149137880941958609} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &5782945728072830494 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7015353133639046746} + - component: {fileID: 7835309114794309555} + - component: {fileID: 8424331600989237328} + m_Layer: 16 + m_Name: Mesh + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7015353133639046746 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5782945728072830494} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.65, y: 0.65, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 8121802616684643442} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7835309114794309555 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5782945728072830494} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8424331600989237328 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5782945728072830494} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 0d7eb02b18ffb4c419fb75924cb900dc, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 1 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &5866976969575595662 GameObject: m_ObjectHideFlags: 0 @@ -7479,7 +7932,7 @@ MonoBehaviour: m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 m_Command: 73 - m_CommandParam: 2 + m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -8303,7 +8756,7 @@ MonoBehaviour: m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 m_Command: 98 - m_CommandParam: 2 + m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -10175,7 +10628,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 46140578835b4f14582726c5d76a6eda, type: 2} + - {fileID: 2100000, guid: e7a4b5a3979042447af7f344f59498c7, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -10231,7 +10684,7 @@ Transform: m_Father: {fileID: 8645625734765961102} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &8156563521135199732 +--- !u!1 &8071707898958835644 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -10239,58 +10692,64 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 3376029733079660750} - - component: {fileID: 4750026522078451160} - - component: {fileID: 8973140315319083013} - - component: {fileID: 1977399286951791455} + - component: {fileID: 8121802616684643442} + - component: {fileID: 2977604093551169432} + - component: {fileID: 4846230962093696328} + - component: {fileID: 3099629273452162997} + - component: {fileID: 4597543144213941713} m_Layer: 16 - m_Name: Text + m_Name: Button_ClosePopup m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &3376029733079660750 -RectTransform: +--- !u!4 &8121802616684643442 +Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8156563521135199732} + m_GameObject: {fileID: 8071707898958835644} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: -0.009999992} - m_LocalScale: {x: 4, y: 4, z: 2.857143} + m_LocalPosition: {x: 1.102, y: -0.576, z: -0.054} + m_LocalScale: {x: 0.29999995, y: 0.29999998, z: 0.29999998} m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 181638217984195686} - m_RootOrder: 0 + m_Children: + - {fileID: 7015353133639046746} + - {fileID: 7380447630107943821} + m_Father: {fileID: 282387885870267606} + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.202, y: 0} - m_SizeDelta: {x: 0.3, y: 0.15} - m_Pivot: {x: 0, y: 0.5} ---- !u!23 &4750026522078451160 +--- !u!33 &2977604093551169432 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8071707898958835644} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &4846230962093696328 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8156563521135199732} - m_Enabled: 1 + m_GameObject: {fileID: 8071707898958835644} + m_Enabled: 0 m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 - m_LightProbeUsage: 1 + m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + - {fileID: 2100000, guid: 3c8ca511828182747a0b79564892ec57, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -10302,7 +10761,7 @@ MeshRenderer: m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 - m_StitchLightmapSeams: 1 + m_StitchLightmapSeams: 0 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 @@ -10312,27 +10771,185 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} ---- !u!114 &8973140315319083013 +--- !u!65 &3099629273452162997 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8071707898958835644} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1.0000001, y: 1, z: 0.01} + m_Center: {x: 0, y: 0, z: -0.01} +--- !u!114 &4597543144213941713 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8156563521135199732} + m_GameObject: {fileID: 8071707898958835644} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Script: {fileID: 11500000, guid: 07f172f1096366841bb9362060bb0095, type: 3} m_Name: m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} - m_RaycastTarget: 1 - m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} - m_Maskable: 1 - m_OnCullStateChanged: + m_DescriptionType: 0 + m_DescriptionYOffset: 0 + m_DescriptionText: Close + m_LocalizedDescription: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionTextExtra: + m_LocalizedDescriptionExtra: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] + m_DescriptionActivateSpeed: 12 + m_DescriptionZScale: 1 + m_ButtonTexture: {fileID: 0} + m_AtlasTexture: 1 + m_ToggleButton: 0 + m_LongPressReleaseButton: 0 + m_ButtonHasPressedAudio: 1 + m_ZAdjustHover: -0.02 + m_ZAdjustClick: 0.03 + m_HoverScale: 1.1 + m_HoverBoxColliderGrow: 0.2 + m_AddOverlay: 0 + m_Action: m_PersistentCalls: - m_Calls: [] - m_text: SIGN IN + m_Calls: + - m_Target: {fileID: 8529915923179660036} + m_TargetAssemblyTypeName: TiltBrush.ProfilePopUpWindow, Assembly-CSharp + m_MethodName: HideIcosaLogin + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 2 + references: + version: 2 + RefIds: [] +--- !u!1 &8156563521135199732 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3376029733079660750} + - component: {fileID: 4750026522078451160} + - component: {fileID: 8973140315319083013} + - component: {fileID: 1977399286951791455} + m_Layer: 16 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3376029733079660750 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8156563521135199732} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.009999992} + m_LocalScale: {x: 4, y: 4, z: 2.857143} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 181638217984195686} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.202, y: 0} + m_SizeDelta: {x: 0.3, y: 0.15} + m_Pivot: {x: 0, y: 0.5} +--- !u!23 &4750026522078451160 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8156563521135199732} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &8973140315319083013 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8156563521135199732} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: SIGN IN m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} m_sharedMaterial: {fileID: 2133298, guid: fce54057bad3d2d4cb3c36ee394be518, type: 2} @@ -11793,6 +12410,7 @@ MonoBehaviour: m_IcosaSignedInElements: {fileID: 162347472523419310} m_IcosaSignedOutElements: {fileID: 3893413551512185020} m_IcosaConfirmSignOutElements: {fileID: 2027372909421917180} + m_IcosaLoginElements: {fileID: 281059592884163862} m_GooglePhoto: {fileID: 8621276525763300312} m_SketchfabPhoto: {fileID: 6282619306390002852} m_IcosaPhoto: {fileID: 5558472698597662980} @@ -11829,8 +12447,8 @@ BoxCollider: m_IsTrigger: 0 m_Enabled: 1 serializedVersion: 2 - m_Size: {x: 2.18, y: 2.3, z: 0.01} - m_Center: {x: -0.01, y: -0.36, z: -0.01} + m_Size: {x: 3.4, y: 2.8, z: 0.01} + m_Center: {x: 0.2, y: -0.6, z: -0.01} --- !u!114 &8533537593755566908 MonoBehaviour: m_ObjectHideFlags: 0 @@ -13001,7 +13619,7 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8854873750265266201} m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0, y: -0.965, z: 0} + m_LocalPosition: {x: 0, y: 0.135, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} m_ConstrainProportionsScale: 1 m_Children: @@ -13163,6 +13781,1041 @@ MeshRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_AdditionalVertexStreams: {fileID: 0} +--- !u!1001 &3407973161605095053 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 282387885870267606} + m_Modifications: + - target: {fileID: 1934243545717122, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} + propertyPath: m_Name + value: Keyboard + objectReference: {fileID: 0} + - target: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: _maxLineLength + value: 6 + objectReference: {fileID: 0} + - target: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: _capsLockEnabled + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: _lineDisplayUsesCursor + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: _consoleDisplayUsesCursor + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 114773793380896770, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_Enabled + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 199519161696781973, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_SizeDelta.x + value: 2222 + objectReference: {fileID: 0} + - target: {fileID: 199519161696781973, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_SizeDelta.y + value: 444 + objectReference: {fileID: 0} + - target: {fileID: 224533170015225100, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224533170015225100, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224533170015225100, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224533170015225100, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 224533170015225100, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 256.68396 + objectReference: {fileID: 0} + - target: {fileID: 224533170015225100, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -246.59 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_SizeDelta.x + value: 2700 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_SizeDelta.y + value: 1080 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 0.00089999987 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 0.00090000004 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 0.00090000004 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: -0.044 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -0.13 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 224882291414706024, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224882291414706024, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224882291414706024, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224882291414706024, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 224882291414706024, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 297 + objectReference: {fileID: 0} + - target: {fileID: 224882291414706024, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -673.41 + objectReference: {fileID: 0} + - target: {fileID: 224925074309398680, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224925074309398680, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224925074309398680, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 224925074309398680, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 224925074309398680, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 135.19006 + objectReference: {fileID: 0} + - target: {fileID: 224926869949627142, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1856 + objectReference: {fileID: 0} + - target: {fileID: 224926869949627142, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1856 + objectReference: {fileID: 0} + - target: {fileID: 224926869949627142, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1856 + objectReference: {fileID: 0} + - target: {fileID: 224926869949627142, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 224926869949627142, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 78.839966 + objectReference: {fileID: 0} + - target: {fileID: 224926869949627142, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -33.17801 + objectReference: {fileID: 0} + - target: {fileID: 5277493517249866201, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: C + objectReference: {fileID: 0} + - target: {fileID: 5277493517303974699, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: R + objectReference: {fileID: 0} + - target: {fileID: 5277493517315999433, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: V + objectReference: {fileID: 0} + - target: {fileID: 5277493517320569316, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: + objectReference: {fileID: 0} + - target: {fileID: 5277493517320569316, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 36 + objectReference: {fileID: 0} + - target: {fileID: 5277493517320569316, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493517334691012, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Y + objectReference: {fileID: 0} + - target: {fileID: 5277493517390459847, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Tab + objectReference: {fileID: 0} + - target: {fileID: 5277493517390459847, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493517390459847, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493517514550751, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: J + objectReference: {fileID: 0} + - target: {fileID: 5277493517658775782, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: S + objectReference: {fileID: 0} + - target: {fileID: 5277493517766971825, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Caps Lock + objectReference: {fileID: 0} + - target: {fileID: 5277493517766971825, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 70.9 + objectReference: {fileID: 0} + - target: {fileID: 5277493517766971825, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493517836040783, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Control + objectReference: {fileID: 0} + - target: {fileID: 5277493517836040783, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493517836040783, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493517877139980, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: F + objectReference: {fileID: 0} + - target: {fileID: 5277493517906801478, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Z + objectReference: {fileID: 0} + - target: {fileID: 5277493517921236477, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: N + objectReference: {fileID: 0} + - target: {fileID: 5277493517955558978, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: X + objectReference: {fileID: 0} + - target: {fileID: 5277493517967058681, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: G + objectReference: {fileID: 0} + - target: {fileID: 5277493518058433820, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Q + objectReference: {fileID: 0} + - target: {fileID: 5277493518090577653, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: AltGr + objectReference: {fileID: 0} + - target: {fileID: 5277493518090577653, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493518090577653, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493518097974896, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: U + objectReference: {fileID: 0} + - target: {fileID: 5277493518109558583, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: A + objectReference: {fileID: 0} + - target: {fileID: 5277493518150738224, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: O + objectReference: {fileID: 0} + - target: {fileID: 5277493518218343584, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: D + objectReference: {fileID: 0} + - target: {fileID: 5277493518266940734, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: I + objectReference: {fileID: 0} + - target: {fileID: 5277493518340769154, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Shift + objectReference: {fileID: 0} + - target: {fileID: 5277493518340769154, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493518340769154, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493518477428480, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Control + objectReference: {fileID: 0} + - target: {fileID: 5277493518477428480, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493518477428480, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493518477510903, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: E + objectReference: {fileID: 0} + - target: {fileID: 5277493518499813032, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: H + objectReference: {fileID: 0} + - target: {fileID: 5277493518791869705, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: K + objectReference: {fileID: 0} + - target: {fileID: 5277493518803901753, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: L + objectReference: {fileID: 0} + - target: {fileID: 5277493518929165342, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: B + objectReference: {fileID: 0} + - target: {fileID: 5277493518960887007, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Alt + objectReference: {fileID: 0} + - target: {fileID: 5277493518960887007, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493518960887007, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493519032514996, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: T + objectReference: {fileID: 0} + - target: {fileID: 5277493519050611944, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: W + objectReference: {fileID: 0} + - target: {fileID: 5277493519053288274, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: + objectReference: {fileID: 0} + - target: {fileID: 5277493519053288274, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 36 + objectReference: {fileID: 0} + - target: {fileID: 5277493519053288274, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493519190116170, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: Shift + objectReference: {fileID: 0} + - target: {fileID: 5277493519190116170, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontSize + value: 80 + objectReference: {fileID: 0} + - target: {fileID: 5277493519190116170, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_fontColor32.rgba + value: 4294770430 + objectReference: {fileID: 0} + - target: {fileID: 5277493519225887468, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: P + objectReference: {fileID: 0} + - target: {fileID: 5277493519272292233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_text + value: M + objectReference: {fileID: 0} + - target: {fileID: 6442521557777233233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521557777233233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521557777233233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521557777233233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521557777233233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 19.559937 + objectReference: {fileID: 0} + - target: {fileID: 6442521557777233233, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -453.53 + objectReference: {fileID: 0} + - target: {fileID: 6442521558029674987, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558029674987, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558029674987, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558029674987, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558029674987, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 2197.5 + objectReference: {fileID: 0} + - target: {fileID: 6442521558029674987, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -453.53 + objectReference: {fileID: 0} + - target: {fileID: 6442521558201973977, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1856 + objectReference: {fileID: 0} + - target: {fileID: 6442521558201973977, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1856 + objectReference: {fileID: 0} + - target: {fileID: 6442521558201973977, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1856 + objectReference: {fileID: 0} + - target: {fileID: 6442521558201973977, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558201973977, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 19.559937 + objectReference: {fileID: 0} + - target: {fileID: 6442521558201973977, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 186.71 + objectReference: {fileID: 0} + - target: {fileID: 6442521558266236666, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558266236666, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558266236666, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558266236666, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558266236666, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 1882 + objectReference: {fileID: 0} + - target: {fileID: 6442521558266236666, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -28 + objectReference: {fileID: 0} + - target: {fileID: 6442521558379257519, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558379257519, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558379257519, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558379257519, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558379257519, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 19.559937 + objectReference: {fileID: 0} + - target: {fileID: 6442521558379257519, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -26.705 + objectReference: {fileID: 0} + - target: {fileID: 6442521558723886156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558723886156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558723886156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558723886156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558723886156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 1669.8999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558723886156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -241.5 + objectReference: {fileID: 0} + - target: {fileID: 6442521558765767617, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558765767617, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558765767617, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558765767617, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558765767617, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 458.23987 + objectReference: {fileID: 0} + - target: {fileID: 6442521558765767617, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -453.53 + objectReference: {fileID: 0} + - target: {fileID: 6442521558885621401, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558885621401, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558885621401, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521558885621401, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521558885621401, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 778.36 + objectReference: {fileID: 0} + - target: {fileID: 6442521558885621401, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -453.53 + objectReference: {fileID: 0} + - target: {fileID: 6442521559131244628, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559131244628, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559131244628, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559131244628, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521559131244628, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 2323.1997 + objectReference: {fileID: 0} + - target: {fileID: 6442521559131244628, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -240.12 + objectReference: {fileID: 0} + - target: {fileID: 6442521559291037726, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559291037726, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559291037726, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559291037726, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521559291037726, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 2524.7998 + objectReference: {fileID: 0} + - target: {fileID: 6442521559291037726, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -453.53 + objectReference: {fileID: 0} + - target: {fileID: 6442521559422749340, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.x + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559422749340, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.y + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559422749340, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalScale.z + value: 1.1855999 + objectReference: {fileID: 0} + - target: {fileID: 6442521559422749340, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.012025 + objectReference: {fileID: 0} + - target: {fileID: 6442521559422749340, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 14.819946 + objectReference: {fileID: 0} + - target: {fileID: 6442521559422749340, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_AnchoredPosition.y + value: -240.12 + objectReference: {fileID: 0} + - target: {fileID: 6516906865040715207, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906865109213156, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6516906865272358958, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906865285074865, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906865492943198, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906865735590991, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906865818221851, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906865969425441, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866017401077, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866100164864, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866236365698, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866242050540, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866250146616, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866391513881, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866427954056, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866528436670, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866682539346, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6516906866757688031, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866772971399, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906866851944814, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906867023499934, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6516906867081517386, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8210763275035622327, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_Text + value: + objectReference: {fileID: 0} + - target: {fileID: 8210763275035622327, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_FontData.m_Font + value: + objectReference: {fileID: 12800000, guid: bdb48149d5b0cf849a1135d87f1b619f, + type: 3} + - target: {fileID: 8210763275035622327, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_FontData.m_MinSize + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 8210763275035622327, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_FontData.m_FontSize + value: 300 + objectReference: {fileID: 0} + - target: {fileID: 8210763275035622327, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + propertyPath: m_FontData.m_Alignment + value: 4 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} +--- !u!224 &3194813434804236579 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 224560323429904302, guid: ef7eb6dda2db50c4bbf6c682e4a21736, + type: 3} + m_PrefabInstance: {fileID: 3407973161605095053} + m_PrefabAsset: {fileID: 0} --- !u!1001 &4530350375719397069 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 31e04b1b95..336f3c01e2 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -10056,6 +10056,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 5f54224fb4c2be945a489e461185fd80, type: 3} m_Name: m_EditorClassIdentifier: + IcosaApiBasePath: http://192.168.1.228:8000 m_VrSdk: {fileID: 1412532062} m_SceneScript: {fileID: 1370550524} m_FadeFromBlackDuration: 3 @@ -22047,11 +22048,11 @@ MonoBehaviour: m_Service: 5 m_OAuthScopes: [] m_AdditionalDesktopOAuthScopes: [] - m_CallbackPath: /sketchfab + m_CallbackPath: /icosa m_LoggedInTexture: {fileID: 2800000, guid: 0d05cda193064458a9a7bb085905c5a1, type: 3} - m_TokenStorePrefix: SketchfabOAuth2 - m_AuthorizationServerUrl: https://sketchfab.com/oauth2/authorize/ - m_TokenServerUrl: https://sketchfab.com/oauth2/token/ + m_TokenStorePrefix: IcosaOAuth2 + m_AuthorizationServerUrl: + m_TokenServerUrl: --- !u!1 &1612665327 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index 04f34689c6..06f4bd464c 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -129,7 +129,11 @@ public enum AppState public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; - public static OAuth2Identity IcosaIdentity => m_Instance.m_IcosaIdentity; + + public string IcosaApiBasePath; + public static string IcosaIdentity; + public static string IcosaUserName; + public static Texture IcosaUserIcon; public static GoogleUserSettings GoogleUserSettings => m_Instance.m_GoogleUserSettings; @@ -155,7 +159,6 @@ public static OAuth2Identity GetIdentity(Cloud cloud) { case Cloud.Poly: return GoogleIdentity; case Cloud.Sketchfab: return SketchfabIdentity; - case Cloud.Icosa: return IcosaIdentity; default: throw new InvalidOperationException($"No identity for {cloud}"); } } diff --git a/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs b/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs new file mode 100644 index 0000000000..e7000c4bd9 --- /dev/null +++ b/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs @@ -0,0 +1,55 @@ +// Copyright 2023 The Open Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using UnityEngine; +using UnityEngine.Events; + +namespace TiltBrush +{ + public class IcosaLoginKeyboardController : MonoBehaviour + { + private KeyboardUI m_KeyboardUI; + [NonSerialized] public static string m_InitialText; + public UnityEvent OnSubmit; + + void Awake() + { + m_KeyboardUI = GetComponentInChildren(includeInactive: true); + m_KeyboardUI.KeyPressed += KeyPressed; + m_KeyboardUI.AddConsoleContent(m_InitialText); + } + + private void OnDestroy() + { + m_KeyboardUI.KeyPressed -= KeyPressed; + } + + private void KeyPressed(object sender, KeyboardKeyEventArgs e) + { + switch (e.Key.KeyType) + { + case KeyboardKeyType.Enter: + OnSubmit.Invoke(m_KeyboardUI.ConsoleContent); + break; + } + } + + public void Clear() + { + m_KeyboardUI.Clear(); + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs.meta b/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs.meta new file mode 100644 index 0000000000..b59edf3665 --- /dev/null +++ b/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 16e24e82bd0c4dcbb02c41882fb95f45 +timeCreated: 1699388418 \ No newline at end of file diff --git a/Assets/Scripts/GUI/KeyboardPopUpWindow.cs b/Assets/Scripts/GUI/KeyboardPopUpWindow.cs index 5297a6a5d0..db463b7af3 100644 --- a/Assets/Scripts/GUI/KeyboardPopUpWindow.cs +++ b/Assets/Scripts/GUI/KeyboardPopUpWindow.cs @@ -61,7 +61,7 @@ private void KeyPressed(object sender, KeyboardKeyEventArgs e) if (m_SanitizeFilename) { - m_KeyboardUI.SantizeFilename(); + m_KeyboardUI.SanitizeFilename(); } } } diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index f755dc4755..52edd1b5f7 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Collections; +using System.Numerics; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; using UnityEngine; using TMPro; @@ -27,6 +32,7 @@ public enum Mode GoogleHelp, DriveHelp, SketchfabHelp, + IcosaHelp, ConfirmLogin, Unavailable, } @@ -40,6 +46,7 @@ public enum Mode [SerializeField] private GameObject m_IcosaSignedInElements; [SerializeField] private GameObject m_IcosaSignedOutElements; [SerializeField] private GameObject m_IcosaConfirmSignOutElements; + [SerializeField] private GameObject m_IcosaLoginElements; [SerializeField] private Renderer m_GooglePhoto; [SerializeField] private Renderer m_SketchfabPhoto; [SerializeField] private Renderer m_IcosaPhoto; @@ -55,7 +62,6 @@ public enum Mode [SerializeField] private GameObject m_SketchfabInfoElements; [SerializeField] private GameObject m_IcosaInfoElements; [SerializeField] private GameObject m_UnavailableElements; - [SerializeField] private GameObject m_DriveSyncEnabledElements; [SerializeField] private GameObject m_DriveSyncDisabledElements; [SerializeField] private GameObject m_DriveFullElements; @@ -85,6 +91,7 @@ override public void Init(GameObject rParent, string sText) base.Init(rParent, sText); OAuth2Identity.ProfileUpdated += OnProfileUpdated; RefreshObjects(); + m_IcosaLoginElements.SetActive(false); App.DriveAccess.RefreshFreeSpaceAsync().AsAsyncVoid(); // TODO: Make configurable by secrets/login data available at runtime. @@ -145,15 +152,14 @@ void RefreshObjects() } // Icosa. - OAuth2Identity.UserInfo icosaInfo = App.IcosaIdentity.Profile; - bool icosaInfoValid = icosaInfo != null; + bool icosaInfoValid = App.IcosaIdentity != null; m_IcosaSignedInElements.SetActive(icosaInfoValid); m_IcosaSignedOutElements.SetActive(!icosaInfoValid); m_IcosaConfirmSignOutElements.SetActive(false); if (icosaInfoValid) { - m_IcosaNameText.text = icosaInfo.name; - m_IcosaPhoto.material.mainTexture = icosaInfo.icon; + m_IcosaNameText.text = App.IcosaUserName; + m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; } m_DriveFullElements.SetActive(driveFull && driveSyncEnabled); @@ -164,6 +170,96 @@ void RefreshObjects() m_DriveSyncing = driveSyncing; RefreshBackupProgressText(); } + + public void HideIcosaLogin() + { + m_IcosaLoginElements.SetActive(false); + m_IcosaSignedInElements.SetActive(true); + m_IcosaSignedOutElements.SetActive(true); + m_SketchfabSignedOutElements.SetActive(true); + m_SketchfabSignedInElements.SetActive(true); + m_GoogleSignedInElements.SetActive(true); + m_GoogleSignedOutElements.SetActive(true); + RefreshObjects(); + } + + public void ShowIcosaLogin() + { + m_IcosaLoginElements.SetActive(true); + m_IcosaLoginElements.GetComponent().Clear(); + m_IcosaSignedInElements.SetActive(false); + m_IcosaSignedOutElements.SetActive(false); + m_SketchfabSignedOutElements.SetActive(false); + m_SketchfabSignedInElements.SetActive(false); + m_GoogleSignedInElements.SetActive(false); + m_GoogleSignedOutElements.SetActive(false); + } + + public void HandleIcosaLoginSubmit(string code) + { + Debug.Log($"StartCoroutine"); + StartCoroutine(LoginCoroutine(code)); + } + + private IEnumerator LoginCoroutine(string code) + { + var config = new Configuration(); + var loginApi = new LoginApi(App.Instance.IcosaApiBasePath); + loginApi.Configuration = config; + var loginTask = loginApi.DeviceLoginLoginDeviceLoginPostAsync(code); + yield return new WaitUntil(() => loginTask.IsCompleted); + + if (loginTask.Exception != null) + { + Debug.Log($"Exception != null"); + if (loginTask.Exception.Message.Contains("401 Unauthorized")) + { + Debug.Log($"Unauthorized"); + AudioManager.m_Instance.PlayTrashSound(transform.position); + } + else + { + Debug.Log($"Login failed: {loginTask.Exception}"); + } + yield break; + } + + if (loginTask.Result?.AccessToken == null) + { + Debug.Log($"Access token is null"); + yield break; + } + Debug.Log($"Now call the GetUser method"); + var token = loginTask.Result; + + // Now call the GetUser method + config.AccessToken = token.AccessToken; + var usersApi = new UsersApi(App.Instance.IcosaApiBasePath); + usersApi.Configuration = config; + var getUserTask = usersApi.GetUsersMeUsersMeGetAsync(); + yield return new WaitUntil(() => getUserTask.IsCompleted); + + if (getUserTask.Exception != null) + { + Debug.Log($"GetUser failed with exception: {getUserTask.Exception}"); + yield break; + } + + var userData = getUserTask.Result; + if (userData == null) + { + Debug.Log($"Failure - no user data received"); + } + LoginSuccess(token, userData); + } + + private void LoginSuccess(LoginToken token, FullUser userInfo) + { + HideIcosaLogin(); + App.IcosaIdentity = token.AccessToken; + App.IcosaUserName = userInfo.Displayname; + App.IcosaUserIcon = m_IcosaPhoto.material.mainTexture; + } void RefreshBackupProgressText() { @@ -182,6 +278,7 @@ void UpdateMode(Mode newMode) m_GoogleInfoElements.SetActive(m_CurrentMode == Mode.GoogleHelp); m_DriveInfoElements.SetActive(m_CurrentMode == Mode.DriveHelp); m_SketchfabInfoElements.SetActive(m_CurrentMode == Mode.SketchfabHelp); + m_IcosaInfoElements.SetActive(m_CurrentMode == Mode.IcosaHelp); m_UnavailableElements.SetActive(m_CurrentMode == Mode.Unavailable); if (m_ConfirmLoginElements != null) { @@ -197,6 +294,7 @@ void OnProfileUpdated(OAuth2Identity _) // and they've done so correctly, switch back to the accounts view. if (m_CurrentMode == Mode.TakeOffHeadset) { + Debug.Log($"OnProfileUpdated set AccountMode"); UpdateMode(Mode.Accounts); } RefreshObjects(); @@ -235,6 +333,10 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) } } break; + case SketchControlsScript.GlobalCommands.LoginToIcosa: + ShowIcosaLogin(); + m_Persistent = true; + break; case SketchControlsScript.GlobalCommands.AccountInfo: // Identifier for triggering an info message. switch (button.m_CommandParam) @@ -248,6 +350,9 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) case 2: UpdateMode(Mode.SketchfabHelp); break; + case 3: + UpdateMode(Mode.IcosaHelp); + break; } break; case SketchControlsScript.GlobalCommands.SignOutConfirm: diff --git a/Assets/Scripts/Sharing/OAuth2Identity.cs b/Assets/Scripts/Sharing/OAuth2Identity.cs index 3f9940cd33..ef8484f2cb 100644 --- a/Assets/Scripts/Sharing/OAuth2Identity.cs +++ b/Assets/Scripts/Sharing/OAuth2Identity.cs @@ -104,6 +104,7 @@ private static void LogOutAll() private OAuth2CredentialRequest m_CredentialRequest; public bool IsGoogle => m_Service == SecretsConfig.Service.Google; + public bool IsIcosa => m_Service == SecretsConfig.Service.Icosa; public UserCredential UserCredential => m_CredentialRequest?.UserCredential; private SecretsConfig.ServiceAuthData ServiceAuthData => App.Config.Secrets[m_Service]; public string ClientId => ServiceAuthData?.ClientId; @@ -230,7 +231,7 @@ public async Task ReauthorizeAsync() public async void LoginAsync() { - if (string.IsNullOrEmpty(ClientId) || string.IsNullOrEmpty(ClientSecret)) + if (!IsIcosa && (string.IsNullOrEmpty(ClientId) || string.IsNullOrEmpty(ClientSecret))) { Debug.LogWarning( $"Attempted to log in to {m_Service} with missing Client Id or Client Secret."); diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 9fdf21c0fa..3ebd00e914 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -150,6 +150,9 @@ public enum GlobalCommands RenameSketch = 5200, OpenLayerOptionsPopup = 5201, RenameLayer = 5202, + LoginToIcosa = 5600, + UploadToIcosa = 5601, + LogOutOfIcosa = 5602, OpenScriptsCommandsList = 6000, OpenScriptsList = 6001, OpenExampleScriptsList = 6002, @@ -4940,7 +4943,8 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, OpenUrl($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); break; case GlobalCommands.RepaintOptions: break; // Intentionally blank. - case GlobalCommands.Null: break; // Intentionally blank. + case GlobalCommands.LoginToIcosa: break; // Intentionally blank. + case GlobalCommands.Null: break; // Intentionally blank. default: Debug.LogError($"Unrecognized command {rEnum}"); break; diff --git a/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs b/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs index c0fb7bb0fd..cf5b6cd0f0 100644 --- a/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs +++ b/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs @@ -113,7 +113,7 @@ public bool CapsLockEnabled /// public string ConsoleContent { get; private set; } - public void SantizeFilename() + public void SanitizeFilename() { string invalidChars = System.Text.RegularExpressions.Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())); string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars); From d49b33209f66cc118e5d012c71adf8c7c01df56e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 9 Nov 2023 22:59:23 +0000 Subject: [PATCH 008/105] rename a bunch of Poly things to Icosa in preperation for getting upload working --- Assets/Editor/GlTF_EditorExporter.cs | 2 +- Assets/Editor/Tests/TestVrAssetService.cs | 4 +- .../Prefabs/PopUps/PopupWindow_Upload.prefab | 16 +-- Assets/Scripts/GUI/PolyPanel.cs | 30 ++--- Assets/Scripts/GUI/PolySetButton.cs | 2 +- Assets/Scripts/GUI/SketchbookPanel.cs | 2 +- Assets/Scripts/Model.cs | 2 +- Assets/Scripts/Poly/PolyAssetCatalog.cs | 28 ++--- Assets/Scripts/Save/SaveLoadScript.cs | 4 +- Assets/Scripts/Sharing/AssetLister.cs | 4 +- Assets/Scripts/Sharing/PolySketchSet.cs | 36 +++--- Assets/Scripts/Sharing/UploadPopUpWindow.cs | 75 +++++++----- Assets/Scripts/Sharing/VrAssetService.cs | 111 +++++++++--------- Assets/Scripts/UserConfig.cs | 3 +- Assets/Scripts/Util/AxisConvention.cs | 2 +- 15 files changed, 167 insertions(+), 154 deletions(-) diff --git a/Assets/Editor/GlTF_EditorExporter.cs b/Assets/Editor/GlTF_EditorExporter.cs index b6b5828096..f0729bb914 100644 --- a/Assets/Editor/GlTF_EditorExporter.cs +++ b/Assets/Editor/GlTF_EditorExporter.cs @@ -95,7 +95,7 @@ private static void ExportBrushStrokes_gltf1() { new ExportGlTF().ExportBrushStrokes( GetExportBaseName() + ".gltf", - AxisConvention.kGltfAccordingToPoly, + AxisConvention.kGltfAccordingToIcosa, binary: false, doExtras: true, gltfVersion: 1, diff --git a/Assets/Editor/Tests/TestVrAssetService.cs b/Assets/Editor/Tests/TestVrAssetService.cs index e5ceade4da..d432de63ce 100644 --- a/Assets/Editor/Tests/TestVrAssetService.cs +++ b/Assets/Editor/Tests/TestVrAssetService.cs @@ -131,7 +131,7 @@ public void RunAfterAllTests() public void TestConvertFruToPoly() { // Test that the basis-conversion transforms seem correct - var zFromU = VrAssetService.kPolyFromUnity; + var zFromU = VrAssetService.kIcosaFromUnity; var uFromZ = zFromU.inverse; Assert.AreEqual(kZForward, zFromU * Vector3.forward); Assert.AreEqual(kZRight, zFromU * Vector3.right); @@ -145,7 +145,7 @@ public void TestConvertFruToPoly() public void TestTransformByForBasisChange() { // This is more a test of TrTransform.TransformBy than anything else - var zFromU = VrAssetService.kPolyFromUnity; + var zFromU = VrAssetService.kIcosaFromUnity; // This rotates Unity-forward to Unity-right TrTransform xfFwdToRt_U = TrTransform.R(Quaternion.AngleAxis(90, Vector3.up)); diff --git a/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab b/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab index 42b3563b22..fca77611dd 100644 --- a/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab +++ b/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab @@ -3994,7 +3994,7 @@ GameObject: - component: {fileID: 3275547546821622028} - component: {fileID: 6831880153218701541} m_Layer: 16 - m_Name: Poly_Logo + m_Name: Icosa_Logo m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -4043,7 +4043,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 7a3b280f73263e34c97f3dd6db616888, type: 2} + - {fileID: 2100000, guid: e7a4b5a3979042447af7f344f59498c7, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -4543,7 +4543,7 @@ GameObject: m_Component: - component: {fileID: 791103424055215284} m_Layer: 16 - m_Name: Poly_LoggedOutElements + m_Name: Icosa_LoggedOutElements m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -6497,7 +6497,7 @@ GameObject: m_Component: - component: {fileID: 4248595653238781298} m_Layer: 16 - m_Name: Poly_LoggedInElements + m_Name: Icosa_LoggedInElements m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -7092,7 +7092,7 @@ MonoBehaviour: m_HoverBoxColliderGrow: 0.05 m_AddOverlay: 0 m_Command: 96 - m_CommandParam: 1 + m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -10314,7 +10314,7 @@ MonoBehaviour: m_HoverBoxColliderGrow: 0.05 m_AddOverlay: 0 m_Command: 95 - m_CommandParam: 1 + m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 m_CenterPopupOnButton: 0 @@ -11164,7 +11164,7 @@ GameObject: - component: {fileID: 5387404676713886195} - component: {fileID: 5158317984005904495} m_Layer: 16 - m_Name: Google_ProfilePhoto + m_Name: Icosa_ProfilePhoto m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -11720,7 +11720,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: 7a3b280f73263e34c97f3dd6db616888, type: 2} + - {fileID: 2100000, guid: e7a4b5a3979042447af7f344f59498c7, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 diff --git a/Assets/Scripts/GUI/PolyPanel.cs b/Assets/Scripts/GUI/PolyPanel.cs index 222d6a889f..9cbc1fa35c 100644 --- a/Assets/Scripts/GUI/PolyPanel.cs +++ b/Assets/Scripts/GUI/PolyPanel.cs @@ -19,7 +19,7 @@ namespace TiltBrush { - public enum PolySetType + public enum IcosaSetType { User, Liked, @@ -46,16 +46,16 @@ public class PolyPanel : ModalPanel [SerializeField] private GameObject m_OutOfDateMessage; [SerializeField] private GameObject m_NoPolyConnectionMessage; - private PolySetType m_CurrentSet; + private IcosaSetType m_CurrentSet; private bool m_LoggedIn; // State for automatically loading models. int m_LastPageIndexForLoad = -1; - PolySetType m_LastSetTypeForLoad = PolySetType.User; + IcosaSetType m_LastSetTypeForLoad = IcosaSetType.User; - public bool ShowingFeatured { get { return m_CurrentSet == PolySetType.Featured; } } - public bool ShowingLikes { get { return m_CurrentSet == PolySetType.Liked; } } - public bool ShowingUser { get { return m_CurrentSet == PolySetType.User; } } + public bool ShowingFeatured { get { return m_CurrentSet == IcosaSetType.Featured; } } + public bool ShowingLikes { get { return m_CurrentSet == IcosaSetType.Liked; } } + public bool ShowingUser { get { return m_CurrentSet == IcosaSetType.User; } } @@ -97,7 +97,7 @@ protected override void OnStart() Icons.Add(icon); } - m_CurrentSet = PolySetType.Featured; + m_CurrentSet = IcosaSetType.Featured; // Make sure Poly gallery button starts at greyscale when panel is initialized m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 1); @@ -106,7 +106,7 @@ protected override void OnStart() App.PolyAssetCatalog.CatalogChanged += OnPolyAssetCatalogChanged; } - void SetVisiblePolySet(PolySetType type) + void SetVisiblePolySet(IcosaSetType type) { m_CurrentSet = type; App.PolyAssetCatalog.RequestRefresh(m_CurrentSet); @@ -194,13 +194,13 @@ protected override void RefreshPage() } bool internetError = - App.PolyAssetCatalog.NumCloudModels(PolySetType.Featured) == 0; + App.PolyAssetCatalog.NumCloudModels(IcosaSetType.Featured) == 0; m_InternetError.SetActive(internetError); RefreshPanelText(); switch (m_CurrentSet) { - case PolySetType.User: + case IcosaSetType.User: if (!internetError) { if (App.GoogleIdentity.LoggedIn) @@ -216,7 +216,7 @@ protected override void RefreshPage() } } break; - case PolySetType.Liked: + case IcosaSetType.Liked: if (!internetError) { if (App.GoogleIdentity.LoggedIn) @@ -241,17 +241,17 @@ void RefreshPanelText() { switch (m_CurrentSet) { - case PolySetType.User: + case IcosaSetType.User: m_PanelText.text = PanelTextStandard; m_PanelTextSubtitle.gameObject.SetActive(false); m_PanelTextUserSubtitle.gameObject.SetActive(true); break; - case PolySetType.Featured: + case IcosaSetType.Featured: m_PanelText.text = PanelTextFeatured; m_PanelTextSubtitle.gameObject.SetActive(false); m_PanelTextUserSubtitle.gameObject.SetActive(false); break; - case PolySetType.Liked: + case IcosaSetType.Liked: m_PanelText.text = PanelTextLiked; m_PanelTextSubtitle.gameObject.SetActive(true); m_PanelTextUserSubtitle.gameObject.SetActive(false); @@ -300,7 +300,7 @@ override protected void OnUpdateActive() } // Works specifically with PolySetButtons. - public void ButtonPressed(PolySetType rType) + public void ButtonPressed(IcosaSetType rType) { SetVisiblePolySet(rType); } diff --git a/Assets/Scripts/GUI/PolySetButton.cs b/Assets/Scripts/GUI/PolySetButton.cs index 911b75abd1..ea87e6f455 100644 --- a/Assets/Scripts/GUI/PolySetButton.cs +++ b/Assets/Scripts/GUI/PolySetButton.cs @@ -17,7 +17,7 @@ namespace TiltBrush public class PolySetButton : ModeButton { - public PolySetType m_ButtonType; + public IcosaSetType m_ButtonType; override protected void OnButtonPressed() { diff --git a/Assets/Scripts/GUI/SketchbookPanel.cs b/Assets/Scripts/GUI/SketchbookPanel.cs index a3d0a2ed6e..2cf0070d20 100644 --- a/Assets/Scripts/GUI/SketchbookPanel.cs +++ b/Assets/Scripts/GUI/SketchbookPanel.cs @@ -577,7 +577,7 @@ private void UpdateIcons() lines.Add(icon.Description); SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); - if (info is PolySceneFileInfo polyInfo && + if (info is IcosaSceneFileInfo polyInfo && polyInfo.License != VrAssetService.kCreativeCommonsLicense) { lines.Add(String.Format("© {0}", authors[0])); diff --git a/Assets/Scripts/Model.cs b/Assets/Scripts/Model.cs index cfb70ac916..c028155150 100644 --- a/Assets/Scripts/Model.cs +++ b/Assets/Scripts/Model.cs @@ -146,7 +146,7 @@ public override bool Equals(object obj) { rescalingMode = GltfImportOptions.RescalingMode.CONVERT, scaleFactor = App.METERS_TO_UNITS, - axisConventionOverride = AxisConvention.kGltfAccordingToPoly, + axisConventionOverride = AxisConvention.kGltfAccordingToIcosa, recenter = false }; diff --git a/Assets/Scripts/Poly/PolyAssetCatalog.cs b/Assets/Scripts/Poly/PolyAssetCatalog.cs index 0bd1cda443..b118798372 100644 --- a/Assets/Scripts/Poly/PolyAssetCatalog.cs +++ b/Assets/Scripts/Poly/PolyAssetCatalog.cs @@ -264,7 +264,7 @@ public ModelLoadRequest(Model model, string reason) private string m_CacheDir; private string m_ThumbnailCacheDir; private Dictionary m_ModelsByAssetId; - private Dictionary m_AssetSetByType; + private Dictionary m_AssetSetByType; private bool m_NotifyListeners; private AwaitableRateLimiter m_thumbnailFetchLimiter = @@ -286,10 +286,10 @@ public bool IsLoading(string assetId) return m_IsLoadingMemo.Contains(assetId); } - public void RequestRefresh(PolySetType type) + public void RequestRefresh(IcosaSetType type) { // We don't update featured except on startup. - if (type != PolySetType.Featured && App.GoogleIdentity.LoggedIn) + if (type != IcosaSetType.Featured && App.GoogleIdentity.LoggedIn) { m_AssetSetByType[type].m_RefreshRequested = true; } @@ -339,18 +339,18 @@ public void Init() Debug.LogException(e); } - m_AssetSetByType = new Dictionary + m_AssetSetByType = new Dictionary { { - PolySetType.User, + IcosaSetType.User, new AssetSet() }, { - PolySetType.Liked, + IcosaSetType.Liked, new AssetSet() }, { - PolySetType.Featured, + IcosaSetType.Featured, new AssetSet { m_RefreshRequested = true } } }; @@ -855,7 +855,7 @@ private static HashSet SetMinus(HashSet lhs, HashSet rhs) return result; } - private IEnumerator RefreshAssetSet(PolySetType type) + private IEnumerator RefreshAssetSet(IcosaSetType type) { List models = new List(); // When the list is empty, make it the actual list acted upon so that results start @@ -911,19 +911,19 @@ void RefreshFetchCoroutines() { if (App.GoogleIdentity.Profile != null) { - m_AssetSetByType[PolySetType.User].m_RefreshRequested = true; - m_AssetSetByType[PolySetType.Liked].m_RefreshRequested = true; + m_AssetSetByType[IcosaSetType.User].m_RefreshRequested = true; + m_AssetSetByType[IcosaSetType.Liked].m_RefreshRequested = true; } else { - AssetSet set = m_AssetSetByType[PolySetType.User]; + AssetSet set = m_AssetSetByType[IcosaSetType.User]; if (set.m_FetchMetadataCoroutine != null) { StopCoroutine(set.m_FetchMetadataCoroutine); set.m_FetchMetadataCoroutine = null; } set.m_Models.Clear(); - set = m_AssetSetByType[PolySetType.Liked]; + set = m_AssetSetByType[IcosaSetType.Liked]; if (set.m_FetchMetadataCoroutine != null) { StopCoroutine(set.m_FetchMetadataCoroutine); @@ -947,12 +947,12 @@ void OnDestroy() OAuth2Identity.ProfileUpdated -= OnProfileUpdated; } - public int NumCloudModels(PolySetType type) + public int NumCloudModels(IcosaSetType type) { return m_AssetSetByType[type].m_Models.Count(); } - public AssetDetails GetPolyAsset(PolySetType type, int index) + public AssetDetails GetPolyAsset(IcosaSetType type, int index) { return m_AssetSetByType[type].m_Models[index]; } diff --git a/Assets/Scripts/Save/SaveLoadScript.cs b/Assets/Scripts/Save/SaveLoadScript.cs index d7f9c34cf0..49e2581775 100644 --- a/Assets/Scripts/Save/SaveLoadScript.cs +++ b/Assets/Scripts/Save/SaveLoadScript.cs @@ -341,7 +341,7 @@ public IEnumerator SaveMonoscopic(int slot) /// we either preserve SourceId, or if this was a cloud sketch set it from the original asset. public string TransferredSourceIdFrom(SceneFileInfo info) { - if (info is PolySceneFileInfo polyInfo) + if (info is IcosaSceneFileInfo polyInfo) { // If the original is a Poly sketch it becomes the source. return polyInfo.AssetId; @@ -750,7 +750,7 @@ public bool Load(SceneFileInfo fileInfo, bool bAdditive = false) // Pass even if null; null is treated as empty CustomColorPaletteStorage.m_Instance.SetColorsFromPalette(jsonData.Palette); // Images are not stored on Poly either. - if (!(fileInfo is PolySceneFileInfo)) + if (!(fileInfo is IcosaSceneFileInfo)) { if (ReferenceImageCatalog.m_Instance != null && jsonData.ImageIndex != null) { diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index f742fd1278..7e3e48f7c7 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -36,7 +36,7 @@ public AssetLister(string uri, string errorMessage) m_ErrorMessage = errorMessage; } - public IEnumerator NextPage(List files) + public IEnumerator NextPage(List files) { string uri = m_PageToken == null ? m_Uri : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); @@ -68,7 +68,7 @@ public IEnumerator NextPage(List files) { foreach (var asset in assets) { - var info = new PolySceneFileInfo(asset); + var info = new IcosaSceneFileInfo(asset); info.Author = asset["displayName"].ToString(); ; files.Add(info); diff --git a/Assets/Scripts/Sharing/PolySketchSet.cs b/Assets/Scripts/Sharing/PolySketchSet.cs index e13f13a06b..974b5da7cb 100644 --- a/Assets/Scripts/Sharing/PolySketchSet.cs +++ b/Assets/Scripts/Sharing/PolySketchSet.cs @@ -38,7 +38,7 @@ private class PolySketch : Sketch // before this one. It's used during our sort to retain order from Poly, while // allowing a custom sort on top. public int m_DownloadIndex; - private PolySceneFileInfo m_FileInfo; + private IcosaSceneFileInfo m_FileInfo; private Texture2D m_Icon; public SceneFileInfo SceneFileInfo @@ -67,7 +67,7 @@ public bool IconAndMetadataValid get { return m_Icon != null; } } - public PolySketch(PolySceneFileInfo info) + public PolySketch(IcosaSceneFileInfo info) { m_FileInfo = info; } @@ -79,7 +79,7 @@ public void UnloadIcon() } // Not part of the interface - public PolySceneFileInfo PolySceneFileInfo + public IcosaSceneFileInfo IcosaSceneFileInfo { get { return m_FileInfo; } } @@ -370,7 +370,7 @@ private IEnumerator PopulateSketchesCoroutine() int loadSketchCount = 0; AssetLister lister = null; - List infos = new List(); + List infos = new List(); // If we don't have a connection to Poly and we're querying the Showcase, use // the json metadatas stored in resources, instead of trying to get them from Poly. @@ -381,7 +381,7 @@ private IEnumerator PopulateSketchesCoroutine() for (int i = 0; i < textAssets.Length; ++i) { JObject jo = JObject.Parse(textAssets[i].text); - infos.Add(new PolySceneFileInfo(jo)); + infos.Add(new IcosaSceneFileInfo(jo)); } } else @@ -444,7 +444,7 @@ private IEnumerator PopulateSketchesCoroutine() } for (int i = 0; i < infos.Count; i++) { - PolySceneFileInfo info = infos[i]; + IcosaSceneFileInfo info = infos[i]; PolySketch sketch; if (m_AssetIds.TryGetValue(info.AssetId, out sketch)) { @@ -502,7 +502,7 @@ private IEnumerator PopulateSketchesCoroutine() if (!fromEmpty) { // Find anything that was removed - int removed = m_Sketches.Count(s => !assetIds.ContainsKey(s.PolySceneFileInfo.AssetId)); + int removed = m_Sketches.Count(s => !assetIds.ContainsKey(s.IcosaSceneFileInfo.AssetId)); if (removed > 0) { changed = true; @@ -546,15 +546,15 @@ private IEnumerator PopulateSketchesCoroutine() // sketches list so as not to confuse the user. private void RemoveFailedDownloads(List sketches) { - sketches.RemoveAll(x => !x.PolySceneFileInfo.TiltDownloaded || - !x.PolySceneFileInfo.IconDownloaded); + sketches.RemoveAll(x => !x.IcosaSceneFileInfo.TiltDownloaded || + !x.IcosaSceneFileInfo.IconDownloaded); } // Download tilt files and thumbnails (that we don't already have) private IEnumerator DownloadFilesCoroutine(List sketches) { bool notifyOnError = true; - void NotifyCreateError(PolySceneFileInfo sceneFileInfo, string type, Exception ex) + void NotifyCreateError(IcosaSceneFileInfo sceneFileInfo, string type, Exception ex) { string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}."; ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); @@ -563,7 +563,7 @@ void NotifyCreateError(PolySceneFileInfo sceneFileInfo, string type, Exception e Debug.LogError($"{sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); } - void NotifyWriteError(PolySceneFileInfo sceneFileInfo, string type, UnityWebRequest www) + void NotifyWriteError(IcosaSceneFileInfo sceneFileInfo, string type, UnityWebRequest www) { string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}.\n" + "Out of disk space?"; @@ -576,7 +576,7 @@ void NotifyWriteError(PolySceneFileInfo sceneFileInfo, string type, UnityWebRequ // Load the icons first, then the thumbnails foreach (PolySketch sketch in sketches) { - PolySceneFileInfo sceneFileInfo = sketch.PolySceneFileInfo; + IcosaSceneFileInfo sceneFileInfo = sketch.IcosaSceneFileInfo; // TODO(b/36270116): Check filesizes when Poly can give it to us to detect incomplete downloads if (!sceneFileInfo.IconDownloaded) { @@ -616,7 +616,7 @@ void NotifyWriteError(PolySceneFileInfo sceneFileInfo, string type, UnityWebRequ foreach (PolySketch sketch in sketches) { - PolySceneFileInfo sceneFileInfo = sketch.PolySceneFileInfo; + IcosaSceneFileInfo sceneFileInfo = sketch.IcosaSceneFileInfo; if (!sceneFileInfo.TiltDownloaded) { if (File.Exists(sceneFileInfo.TiltPath)) @@ -688,8 +688,8 @@ private IEnumerator TextureLoaderCoroutine() foreach (int i in m_RequestedIcons) { PolySketch sketch = m_Sketches[i]; - string path = sketch.PolySceneFileInfo.IconPath; - if (sketch.PolySceneFileInfo.IconDownloaded) + string path = sketch.IcosaSceneFileInfo.IconPath; + if (sketch.IcosaSceneFileInfo.IconDownloaded) { byte[] data = File.ReadAllBytes(path); Texture2D t = new Texture2D(2, 2); @@ -741,11 +741,11 @@ private static int CompareSketchesByTriangleCountAndDownloadIndex(PolySketch a, // Buckets the sketches into buckets 100000 tris in size. private static int CloudSketchComplexityBucket(PolySketch s) { - return s.PolySceneFileInfo.GltfTriangleCount / 100000; + return s.IcosaSceneFileInfo.GltfTriangleCount / 100000; } } - public class PolySceneFileInfo : SceneFileInfo + public class IcosaSceneFileInfo : SceneFileInfo { // Asset @@ -764,7 +764,7 @@ public class PolySceneFileInfo : SceneFileInfo // Populate metadata from the JSON returned by Poly for a single asset // See go/vr-assets-service-api - public PolySceneFileInfo(JToken json) + public IcosaSceneFileInfo(JToken json) { m_AssetId = json["name"].ToString().Substring(7); // strip 'assets/' from start m_HumanName = json["displayName"].ToString(); diff --git a/Assets/Scripts/Sharing/UploadPopUpWindow.cs b/Assets/Scripts/Sharing/UploadPopUpWindow.cs index a0732b4d29..1dc1a7ed5a 100644 --- a/Assets/Scripts/Sharing/UploadPopUpWindow.cs +++ b/Assets/Scripts/Sharing/UploadPopUpWindow.cs @@ -14,6 +14,7 @@ using System; using UnityEngine; +using UnityEngine.Serialization; using GlobalCommands = TiltBrush.SketchControlsScript.GlobalCommands; namespace TiltBrush @@ -32,7 +33,7 @@ private enum DisplayMode UploadFailed, UploadingDenied, Waiting, - EmbeddedMediaWarningPoly, + EmbeddedMediaWarningIcosa, EmbeddedMediaWarningSketchfab, NothingToUploadWarning, ConnectionError, @@ -46,13 +47,13 @@ private enum DisplayMode // Things that should be visible when confirming upload. [SerializeField] private GameObject m_ConfirmObjects; - [SerializeField] private GameObject m_PolyLoggedInObjects; - [SerializeField] private GameObject m_PolyLoggedOutObjects; + [FormerlySerializedAs("m_PolyLoggedInObjects")] [SerializeField] private GameObject m_IcosaLoggedInObjects; + [FormerlySerializedAs("m_PolyLoggedOutObjects")] [SerializeField] private GameObject m_IcosaLoggedOutObjects; [SerializeField] private GameObject m_SketchfabLoggedInObjects; [SerializeField] private GameObject m_SketchfabLoggedOutObjects; - [SerializeField] private TMPro.TextMeshPro m_PolyUserName; + [FormerlySerializedAs("m_PolyUserName")] [SerializeField] private TMPro.TextMeshPro m_IcosaUserName; [SerializeField] private TMPro.TextMeshPro m_SketchfabUserName; - [SerializeField] private Renderer m_GooglePhoto; + [FormerlySerializedAs("m_GooglePhoto")] [SerializeField] private Renderer m_IcosaPhoto; [SerializeField] private Renderer m_SketchfabPhoto; // Things that should be visible when uploading. @@ -72,7 +73,7 @@ private enum DisplayMode [SerializeField] private GameObject m_WaitObjects; // Things that should be visible when media library content is in the scene. - [SerializeField] private GameObject m_EmbeddedMediaWarningPoly; + [FormerlySerializedAs("m_EmbeddedMediaWarningPoly")] [SerializeField] private GameObject m_EmbeddedMediaWarningIcosa; [SerializeField] private GameObject m_EmbeddedMediaWarningSketchfab; // Things that should be visible when there's nothing to upload. @@ -81,7 +82,7 @@ private enum DisplayMode // Things that should be visible when there has been a connection error. [SerializeField] private GameObject m_ConnectionErrorObjects; - // Things that should be visible when Tilt Brush can't talk to Poly because it is + // Things that should be visible when Open Brush can't talk to Icosa because it is // out of date. [SerializeField] private GameObject m_OutOfDateObjects; @@ -100,7 +101,7 @@ public override void Init(GameObject rParent, string sText) InitUI(); OAuth2Identity.ProfileUpdated += OnProfileUpdated; - RefreshUploadButton(Cloud.Poly); + RefreshUploadButton(Cloud.Icosa); RefreshUploadButton(Cloud.Sketchfab); m_OnClose += OnClose; @@ -123,7 +124,7 @@ private void SetMode(DisplayMode displayMode) m_UploadFailedObjects.SetActive(displayMode == DisplayMode.UploadFailed); m_UploadingDeniedObjects.SetActive(displayMode == DisplayMode.UploadingDenied); m_WaitObjects.SetActive(displayMode == DisplayMode.Waiting); - m_EmbeddedMediaWarningPoly.SetActive(displayMode == DisplayMode.EmbeddedMediaWarningPoly); + m_EmbeddedMediaWarningIcosa.SetActive(displayMode == DisplayMode.EmbeddedMediaWarningIcosa); m_EmbeddedMediaWarningSketchfab.SetActive( displayMode == DisplayMode.EmbeddedMediaWarningSketchfab); m_NothingToUploadWarning.SetActive(displayMode == DisplayMode.NothingToUploadWarning); @@ -147,13 +148,13 @@ private void InitUI() m_LoggingInType = Cloud.None; SetMode(DisplayMode.Blank); - { // Turn off the poly option - (var name, var inEl, var outEl, var photo) = GetUiFor(Cloud.Poly); - name.gameObject.SetActive(false); - inEl.SetActive(false); - outEl.SetActive(false); - photo.gameObject.SetActive(false); - } + // { // Turn off the Icosa option + // (var name, var inEl, var outEl, var photo) = GetUiFor(Cloud.Icosa); + // name.gameObject.SetActive(false); + // inEl.SetActive(false); + // outEl.SetActive(false); + // photo.gameObject.SetActive(false); + // } if (m_AssetService.UploadProgress <= 0.0f) { @@ -201,7 +202,7 @@ override protected void BaseUpdate() if (m_LoginOnDesktopObjects.activeSelf) { // Check to see if we just logged in. - if ((m_LoggingInType == Cloud.Poly && App.GoogleIdentity.LoggedIn) || + if ((m_LoggingInType == Cloud.Icosa && App.Instance.IcosaToken != null) || (m_LoggingInType == Cloud.Sketchfab && App.SketchfabIdentity.LoggedIn)) { SetMode(DisplayMode.Loggedout); @@ -261,31 +262,43 @@ override protected void BaseUpdate() case Cloud.Sketchfab: return (m_SketchfabUserName, m_SketchfabLoggedInObjects, m_SketchfabLoggedOutObjects, m_SketchfabPhoto); - case Cloud.Poly: - return (m_PolyUserName, m_PolyLoggedInObjects, - m_PolyLoggedOutObjects, m_GooglePhoto); + case Cloud.Icosa: + return (m_IcosaUserName, m_IcosaLoggedInObjects, + m_IcosaLoggedOutObjects, m_IcosaPhoto); default: throw new InvalidOperationException($"{cloud}"); } } void RefreshUploadButton(Cloud backend) { - if (backend == Cloud.Poly) { return; } var ui = GetUiFor(backend); - OAuth2Identity.UserInfo profile = App.GetIdentity(backend).Profile; - ui.loggedInElements.SetActive(profile != null); - ui.loggedOutElements.SetActive(profile == null); - - if (profile != null) + if (backend == Cloud.Icosa) + { + bool icosaLoggedIn = App.Instance.IcosaToken != null; + ui.loggedInElements.SetActive(icosaLoggedIn); + ui.loggedOutElements.SetActive(!icosaLoggedIn); + if (icosaLoggedIn) + { + ui.name.text = App.IcosaUserName; + ui.photo.material.mainTexture = App.IcosaUserIcon; + } + } + else { - ui.name.text = profile.name; - ui.photo.material.mainTexture = profile.icon; + OAuth2Identity.UserInfo profile = App.GetIdentity(backend).Profile; + ui.loggedInElements.SetActive(profile != null); + ui.loggedOutElements.SetActive(profile == null); + if (profile != null) + { + ui.name.text = profile.name; + ui.photo.material.mainTexture = profile.icon; + } } } void OnProfileUpdated(OAuth2Identity _) { - RefreshUploadButton(Cloud.Poly); + RefreshUploadButton(Cloud.Icosa); RefreshUploadButton(Cloud.Sketchfab); } @@ -300,8 +313,8 @@ void OnOperationStackChanged() // An embedded media warning only shows up if the user has tried to upload and there was non- // exportable content. So we only need to be concerned with the case that the embedded warning // is showing and is no longer relevant. - if (m_EmbeddedMediaWarningPoly.activeSelf && - !WidgetManager.m_Instance.HasNonExportableContent(Cloud.Poly)) + if (m_EmbeddedMediaWarningIcosa.activeSelf && + !WidgetManager.m_Instance.HasNonExportableContent(Cloud.Icosa)) { SetMode(DisplayMode.Confirming); } diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index db808f93cb..ef4f939197 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -21,6 +21,7 @@ using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using Org.OpenAPITools.Api; using UnityEngine; using UnityEngine.Networking; @@ -54,24 +55,24 @@ public class VrAssetService : MonoBehaviour const string kGltfName = "sketch.gltf"; - public const string kApiHost = "https://poly.googleapis.com"; - private const string kAssetLandingPage = "https://vr.google.com/sketches/uploads/publish/"; + public const string kApiHost = "https://api.icosa.gallery"; + private const string kAssetLandingPage = "https://icosa.gallery/uploads"; private const string kListAssetsUri = "/v1/assets"; private const string kUserAssetsUri = "/v1/users/me/assets"; private const string kUserLikesUri = "/v1/users/me/likedassets"; private const string kGetVersionUri = "/$discovery/rest?version=v1"; - public static string kPolyApiKey => App.Config.GoogleSecrets?.ApiKey; + public static string kIcosaApiKey => App.Config.GoogleSecrets?.ApiKey; public const string kCreativeCommonsLicense = "CREATIVE_COMMONS_BY"; - // Poly API used by Tilt Brush. - // If Poly doesn't support this version, don't try to talk to Poly and prompt the user to upgrade. - private const string kPolyApiVersion = "v1"; + // Icosa API used by Tilt Brush. + // If Icosa doesn't support this version, don't try to talk to Icosa and prompt the user to upgrade. + private const string kIcosaApiVersion = "v1"; /// Change-of-basis transform - public static readonly TrTransform kPolyFromUnity; + public static readonly TrTransform kIcosaFromUnity; private static Dictionary kGltfMimetypes = new Dictionary { @@ -97,7 +98,7 @@ private enum UploadStep } // These are progress values at the start of each step. - // TODO(b/146892613): have a different set for Poly vs Sketchfab? + // TODO(b/146892613): have a different set for Icosa vs Sketchfab? private static double[] kProgressSteps = { 0.01, // UploadStep.CreateGltf -- progress > 0 means we have begun @@ -242,14 +243,14 @@ public static string ApiHost } } - /// Returns true if Poly would accept a PATCH of the specified asset + /// Returns true if Icosa would accept a PATCH of the specified asset /// from the specified user. /// /// Pass: /// type - /// Where you found the assetId. Necessary because of some shortcuts /// taken by the implementation. - /// userId - Poly user id of the currently-logged-in OAuth user. Get it + /// userId - Icosa user id of the currently-logged-in OAuth user. Get it /// with GetAccountIdAsync(). public static async Task IsMutableAssetIdAsync( FileInfoType type, string assetId, string userId, string apiHost) @@ -268,7 +269,7 @@ public static async Task IsMutableAssetIdAsync( // Assumption: this asset is mutable because it's unlikely for a cloud-based .tilt // to be in the user's Sketches/ folder. Local sketches always become un-published // and mutable assets when uploaded (remember, publishing makes a copy). - // If someone grabbed a .tilt from their Poly asset cache and put it in Sketches/ + // If someone grabbed a .tilt from their Icosa asset cache and put it in Sketches/ // that would break this assumption and I'm not sure what would happen. if (userId == null) { return false; } // It's mutable, but check whether it's mutable by _us_. @@ -276,7 +277,7 @@ public static async Task IsMutableAssetIdAsync( { // The null == null case is handled earlier WebRequest request = new WebRequest( - $"{apiHost}{kListAssetsUri}/{assetId}?key={kPolyApiKey}", + $"{apiHost}{kListAssetsUri}/{assetId}?key={kIcosaApiKey}", App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); return (await request.SendAsync()).JObject?["accountId"].ToString() == userId; } @@ -295,10 +296,10 @@ public static async Task IsMutableAssetIdAsync( static VrAssetService() { - Matrix4x4 polyFromUnity = AxisConvention.GetFromUnity(AxisConvention.kGltfAccordingToPoly); + Matrix4x4 polyFromUnity = AxisConvention.GetFromUnity(AxisConvention.kGltfAccordingToIcosa); // Provably non-lossy: the mat4 is purely TRS, and the S is uniform - kPolyFromUnity = TrTransform.FromMatrix4x4(polyFromUnity); + kIcosaFromUnity = TrTransform.FromMatrix4x4(polyFromUnity); } // Instance API @@ -314,20 +315,20 @@ static VrAssetService() private string m_LastUploadCompleteUrl; TaskAndCts<(string url, long bytes)> m_UploadTask = null; - // Poly account id associated with the Google identity - private string m_PolyAccountId; + // Icosa account id associated with the Google identity + private string m_IcosaAccountId; - private enum PolyStatus + private enum IcosaStatus { Ok, Disabled, NoConnection } - private PolyStatus m_PolyStatus; + private IcosaStatus m_IcosaStatus; - public bool Available => m_PolyStatus == PolyStatus.Ok; + public bool Available => m_IcosaStatus == IcosaStatus.Ok; - public bool NoConnection => m_PolyStatus == PolyStatus.NoConnection; + public bool NoConnection => m_IcosaStatus == IcosaStatus.NoConnection; public float UploadProgress => m_UploadProgress; @@ -389,14 +390,14 @@ void Start() ApiHost, AssetLandingPage); } - // If auto profiling is enabled, disable automatic Poly downloading. + // If auto profiling is enabled, disable automatic Icosa downloading. if (!App.UserConfig.Profiling.AutoProfile) { - VerifyPolyConnectionAndCheckApiVersionAsync(); + VerifyIcosaConnectionAndCheckApiVersionAsync(); } else { - m_PolyStatus = PolyStatus.Disabled; + m_IcosaStatus = IcosaStatus.Disabled; } } @@ -521,46 +522,44 @@ public void CancelUpload() m_UploadTask?.Cancel(); } - private async void VerifyPolyConnectionAndCheckApiVersionAsync() + private async void VerifyIcosaConnectionAndCheckApiVersionAsync() { - m_PolyStatus = await GetPolyStatus(); + m_IcosaStatus = await GetIcosaStatus(); } - private static async Task GetPolyStatus() + private static async Task GetIcosaStatus() { - return PolyStatus.Disabled; - // UserConfig override - if (App.UserConfig.Flags.DisablePoly || - string.IsNullOrEmpty(App.Config.GoogleSecrets?.ApiKey)) + if (App.UserConfig.Flags.DisableIcosa || App.Instance.IcosaToken==null) { - return PolyStatus.Disabled; + return IcosaStatus.Disabled; } string uri = String.Format("{0}{1}", ApiHost, kGetVersionUri); try { - var result = (await new WebRequest(uri, App.GoogleIdentity).SendAsync()).JObject; - string version = result["version"].Value(); - if (version == kPolyApiVersion) + var api = new LoginApi($"{App.ICOSA_API_BASEPATH}"); + var result = new Dictionary { { "version", "v1" } }; // TODO: get version from API + string version = result["version"]; + if (version == kIcosaApiVersion) { - return PolyStatus.Ok; + return IcosaStatus.Ok; } else { - Debug.LogWarning($"Poly requires API {version} > {kPolyApiVersion}"); - return PolyStatus.Disabled; + Debug.LogWarning($"Icosa requires API {version} > {kIcosaApiVersion}"); + return IcosaStatus.Disabled; } } catch (VrAssetServiceException e) { - Debug.LogWarning($"Error connecting to Poly: {e}"); - return PolyStatus.NoConnection; + Debug.LogWarning($"Error connecting to Icosa: {e}"); + return IcosaStatus.NoConnection; } catch (Exception e) { - Debug.LogError($"Internal error connecting to Poly: {e}"); - return PolyStatus.NoConnection; + Debug.LogError($"Internal error connecting to Icosa: {e}"); + return IcosaStatus.NoConnection; } } @@ -755,7 +754,7 @@ public AssetGetter GetAsset(string assetId, VrAssetFormat type, string reason) } else { - uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, assetId, kPolyApiKey); + uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, assetId, kIcosaApiKey); } return new AssetGetter(uri, assetId, type, reason); } @@ -771,15 +770,15 @@ public AssetLister ListAssets(SketchSetType type) { return null; } - filter = $"{kUserLikesUri}?format=TILT&orderBy=LIKED_TIME&key={kPolyApiKey}"; + filter = $"{kUserLikesUri}?format=TILT&orderBy=LIKED_TIME&key={kIcosaApiKey}"; errorMessage = "Failed to access your liked sketches."; break; case SketchSetType.Curated: - if (string.IsNullOrEmpty(kPolyApiKey)) + if (string.IsNullOrEmpty(kIcosaApiKey)) { return null; } - filter = $"{kListAssetsUri}?format=TILT&curated=true&orderBy=NEWEST&key={kPolyApiKey}"; + filter = $"{kListAssetsUri}?format=TILT&curated=true&orderBy=NEWEST&key={kIcosaApiKey}"; errorMessage = "Failed to access featured sketches."; break; } @@ -790,9 +789,9 @@ public AssetLister ListAssets(SketchSetType type) // Get a specific sketch and insert it into the listed sketches at the specified index. public IEnumerator InsertSketchInfo( - string assetId, int index, List infos) + string assetId, int index, List infos) { - string uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, assetId, kPolyApiKey); + string uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, assetId, kIcosaApiKey); WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { @@ -815,33 +814,33 @@ public IEnumerator InsertSketchInfo( Future f = new Future(() => JObject.Parse(request.Result)); JObject json; while (!f.TryGetResult(out json)) { yield return null; } - infos.Insert(index, new PolySceneFileInfo(json.Root)); + infos.Insert(index, new IcosaSceneFileInfo(json.Root)); } - public AssetLister ListAssets(PolySetType type) + public AssetLister ListAssets(IcosaSetType type) { string uri = null; switch (type) { - case PolySetType.Liked: + case IcosaSetType.Liked: uri = $"{ApiHost}{kUserLikesUri}?format=GLTF2&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; break; - case PolySetType.User: + case IcosaSetType.User: uri = $"{ApiHost}{kUserAssetsUri}?format=GLTF2&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; break; - case PolySetType.Featured: - uri = $"{ApiHost}{kListAssetsUri}?key={kPolyApiKey}" + + case IcosaSetType.Featured: + uri = $"{ApiHost}{kListAssetsUri}?key={kIcosaApiKey}" + $"&format=GLTF2&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; break; } - return new AssetLister(uri, "Failed to connect to Poly."); + return new AssetLister(uri, "Failed to connect to Icosa."); } // Download a tilt file to a temporary file and load it public IEnumerator LoadTiltFile(string id) { string path = Path.GetTempFileName(); - string uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, id, kPolyApiKey); + string uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, id, kIcosaApiKey); WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { @@ -859,7 +858,7 @@ public IEnumerator LoadTiltFile(string id) } } JObject json = JObject.Parse(request.Result); - var info = new PolySceneFileInfo(json); + var info = new IcosaSceneFileInfo(json); using (UnityWebRequest www = UnityWebRequest.Get(info.TiltFileUrl)) { yield return www.SendWebRequest(); diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index b3eadfa0b4..48d526e8f2 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; +using UnityEngine.Serialization; namespace TiltBrush { @@ -36,7 +37,7 @@ public struct FlagsConfig { public bool DisableAudio; public bool DisableAutosave; - public bool DisablePoly; + [FormerlySerializedAs("DisablePoly")] public bool DisableIcosa; public bool UnlockScale; public bool GuideToggleVisiblityOnly; public bool HighResolutionSnapshots; // Deprecated diff --git a/Assets/Scripts/Util/AxisConvention.cs b/Assets/Scripts/Util/AxisConvention.cs index 391997ec10..465e3329e4 100644 --- a/Assets/Scripts/Util/AxisConvention.cs +++ b/Assets/Scripts/Util/AxisConvention.cs @@ -38,7 +38,7 @@ public struct AxisConvention }; // When we implemented Poly support the gltf spec didn't specify a forward direction. - public static readonly AxisConvention kGltfAccordingToPoly = new AxisConvention + public static readonly AxisConvention kGltfAccordingToIcosa = new AxisConvention { right = new Vector3(1, 0, 0), up = new Vector3(0, 1, 0), From 74f21c5923d9b41ed6350546394bea68123f4f10 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 9 Nov 2023 22:59:55 +0000 Subject: [PATCH 009/105] Disable Poly Asset component for now --- Assets/Scenes/Main.unity | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 336f3c01e2..7f276fef8e 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -10056,7 +10056,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 5f54224fb4c2be945a489e461185fd80, type: 3} m_Name: m_EditorClassIdentifier: - IcosaApiBasePath: http://192.168.1.228:8000 m_VrSdk: {fileID: 1412532062} m_SceneScript: {fileID: 1370550524} m_FadeFromBlackDuration: 3 @@ -10081,7 +10080,6 @@ MonoBehaviour: m_ShaderWarmup: {fileID: 2094189264} m_GoogleIdentity: {fileID: 1209128957} m_SketchfabIdentity: {fileID: 2095572865} - m_IcosaIdentity: {fileID: 1611595292} --- !u!4 &652605545 Transform: m_ObjectHideFlags: 0 @@ -10596,7 +10594,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 652605543} - m_Enabled: 1 + m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 59a80c71ac022374fbe28ebb361ab7ee, type: 3} m_Name: From ab61c6742de9ce517529350b209f4c5d08a74aaf Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 9 Nov 2023 23:01:17 +0000 Subject: [PATCH 010/105] Fix for double triggering of key events. Login keyboard switches off after submit (was still double triggering for some reason) --- Assets/Scripts/GUI/IcosaLoginKeyboardController.cs | 4 ++++ Assets/Scripts/GUI/KeyboardPopUpWindow.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs b/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs index e7000c4bd9..d2a6cf2256 100644 --- a/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs +++ b/Assets/Scripts/GUI/IcosaLoginKeyboardController.cs @@ -24,12 +24,14 @@ public class IcosaLoginKeyboardController : MonoBehaviour private KeyboardUI m_KeyboardUI; [NonSerialized] public static string m_InitialText; public UnityEvent OnSubmit; + public bool InputEnabled = true; void Awake() { m_KeyboardUI = GetComponentInChildren(includeInactive: true); m_KeyboardUI.KeyPressed += KeyPressed; m_KeyboardUI.AddConsoleContent(m_InitialText); + InputEnabled = true; } private void OnDestroy() @@ -39,9 +41,11 @@ private void OnDestroy() private void KeyPressed(object sender, KeyboardKeyEventArgs e) { + if (!e.IsPress) return; // Ignore key up events switch (e.Key.KeyType) { case KeyboardKeyType.Enter: + InputEnabled = false; // Prevents double submit OnSubmit.Invoke(m_KeyboardUI.ConsoleContent); break; } diff --git a/Assets/Scripts/GUI/KeyboardPopUpWindow.cs b/Assets/Scripts/GUI/KeyboardPopUpWindow.cs index db463b7af3..0c32ad1aba 100644 --- a/Assets/Scripts/GUI/KeyboardPopUpWindow.cs +++ b/Assets/Scripts/GUI/KeyboardPopUpWindow.cs @@ -46,6 +46,7 @@ private void OnDestroy() private void KeyPressed(object sender, KeyboardKeyEventArgs e) { + if (!e.IsPress) return; // Ignore key up events switch (e.Key.KeyType) { case KeyboardKeyType.Enter: From d3cf40c2830aa27e3bf65a0e4a86374f005365b7 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 9 Nov 2023 23:01:54 +0000 Subject: [PATCH 011/105] Comment is no longer true - we used a separate codepath for Icosa tokens --- Assets/Scripts/Sharing/OAuth2Identity.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Assets/Scripts/Sharing/OAuth2Identity.cs b/Assets/Scripts/Sharing/OAuth2Identity.cs index ef8484f2cb..5bafccbb0b 100644 --- a/Assets/Scripts/Sharing/OAuth2Identity.cs +++ b/Assets/Scripts/Sharing/OAuth2Identity.cs @@ -29,8 +29,6 @@ namespace TiltBrush { /// Handle accessing OAuth2 based web services. - /// Inaccurately named now as Icosa auth is going to - /// either be username/password or timed tokens public class OAuth2Identity : MonoBehaviour { From 217cd0c4048655461769e92d1c6fc385ab11da10 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 9 Nov 2023 23:02:35 +0000 Subject: [PATCH 012/105] Fixes for Icosa login --- .../PopUps/PopUpWindow_Accounts.prefab | 12 +-- Assets/Scripts/App.cs | 16 +++- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 87 ++++++++++++------- Assets/Scripts/SketchControlsScript.cs | 38 ++++++-- 4 files changed, 104 insertions(+), 49 deletions(-) diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab index 894ec25b24..c7736606c7 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab @@ -1801,7 +1801,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Command: 5602 + m_Command: 27 m_CommandParam: 3 m_CommandParam2: -1 m_RequiresPopup: 0 @@ -13795,7 +13795,7 @@ PrefabInstance: - target: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: _maxLineLength - value: 6 + value: 5 objectReference: {fileID: 0} - target: {fileID: 114387715371604348, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} @@ -14080,12 +14080,12 @@ PrefabInstance: - target: {fileID: 5277493517320569316, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: m_text - value: + value: Backspace objectReference: {fileID: 0} - target: {fileID: 5277493517320569316, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: m_fontSize - value: 36 + value: 80 objectReference: {fileID: 0} - target: {fileID: 5277493517320569316, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} @@ -14305,12 +14305,12 @@ PrefabInstance: - target: {fileID: 5277493519053288274, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: m_text - value: + value: Enter objectReference: {fileID: 0} - target: {fileID: 5277493519053288274, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} propertyPath: m_fontSize - value: 36 + value: 80 objectReference: {fileID: 0} - target: {fileID: 5277493519053288274, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index 06f4bd464c..d6e851a9c1 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -130,8 +130,17 @@ public enum AppState public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; - public string IcosaApiBasePath; - public static string IcosaIdentity; + // public static string ICOSA_DEVICECODE_URL = "http://icosa.gallery/device"; + public static string ICOSA_DEVICECODE_URL = "http://192.168.1.228:3000/device"; + // public static string ICOSA_API_BASEPATH = "http://api.icosa.gallery"; + public static string ICOSA_API_BASEPATH = "http://192.168.1.228:8000"; + public string IcosaToken + { + get => PlayerPrefs.HasKey("IcosaToken") ? PlayerPrefs.GetString("IcosaToken") : null; + set => PlayerPrefs.SetString("IcosaToken", value); + } + public static bool IcosaIsLoggedIn => !string.IsNullOrEmpty(App.Instance.IcosaToken); + public static string IcosaUserName; public static Texture IcosaUserIcon; @@ -228,8 +237,7 @@ public static void Log(string msg) [Header("Identities")] [SerializeField] private OAuth2Identity m_GoogleIdentity; [SerializeField] private OAuth2Identity m_SketchfabIdentity; - [SerializeField] private OAuth2Identity m_IcosaIdentity; - + // ------------------------------------------------------------ // Private data // ------------------------------------------------------------ diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 52edd1b5f7..b041652ea7 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.Collections; using System.Numerics; using Org.OpenAPITools.Api; @@ -92,6 +93,11 @@ override public void Init(GameObject rParent, string sText) OAuth2Identity.ProfileUpdated += OnProfileUpdated; RefreshObjects(); m_IcosaLoginElements.SetActive(false); + if (App.IcosaIsLoggedIn) + { + StartCoroutine(FetchUserDataCoroutine(null)); + } + App.DriveAccess.RefreshFreeSpaceAsync().AsAsyncVoid(); // TODO: Make configurable by secrets/login data available at runtime. @@ -152,15 +158,9 @@ void RefreshObjects() } // Icosa. - bool icosaInfoValid = App.IcosaIdentity != null; - m_IcosaSignedInElements.SetActive(icosaInfoValid); - m_IcosaSignedOutElements.SetActive(!icosaInfoValid); + m_IcosaSignedInElements.SetActive(App.IcosaIsLoggedIn); + m_IcosaSignedOutElements.SetActive(!App.IcosaIsLoggedIn); m_IcosaConfirmSignOutElements.SetActive(false); - if (icosaInfoValid) - { - m_IcosaNameText.text = App.IcosaUserName; - m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; - } m_DriveFullElements.SetActive(driveFull && driveSyncEnabled); m_DriveSyncEnabledElements.SetActive(!driveFull && driveSyncEnabled); @@ -180,6 +180,7 @@ public void HideIcosaLogin() m_SketchfabSignedInElements.SetActive(true); m_GoogleSignedInElements.SetActive(true); m_GoogleSignedOutElements.SetActive(true); + m_Persistent = false; RefreshObjects(); } @@ -197,50 +198,55 @@ public void ShowIcosaLogin() public void HandleIcosaLoginSubmit(string code) { - Debug.Log($"StartCoroutine"); + if (App.IcosaIsLoggedIn) return; StartCoroutine(LoginCoroutine(code)); } private IEnumerator LoginCoroutine(string code) { var config = new Configuration(); - var loginApi = new LoginApi(App.Instance.IcosaApiBasePath); + var loginApi = new LoginApi(App.ICOSA_API_BASEPATH); loginApi.Configuration = config; var loginTask = loginApi.DeviceLoginLoginDeviceLoginPostAsync(code); yield return new WaitUntil(() => loginTask.IsCompleted); if (loginTask.Exception != null) { - Debug.Log($"Exception != null"); if (loginTask.Exception.Message.Contains("401 Unauthorized")) { - Debug.Log($"Unauthorized"); + // TODO: Show error message. + LoginFailure(); AudioManager.m_Instance.PlayTrashSound(transform.position); } - else - { - Debug.Log($"Login failed: {loginTask.Exception}"); - } yield break; } - + if (loginTask.Result?.AccessToken == null) { - Debug.Log($"Access token is null"); + // TODO: Show error message. + LoginFailure(); + AudioManager.m_Instance.PlayPinSound(transform.position, AudioManager.PinSoundType.Wobble); yield break; } - Debug.Log($"Now call the GetUser method"); - var token = loginTask.Result; - - // Now call the GetUser method - config.AccessToken = token.AccessToken; - var usersApi = new UsersApi(App.Instance.IcosaApiBasePath); + App.Instance.IcosaToken = loginTask.Result.AccessToken; + StartCoroutine(FetchUserDataCoroutine(userData => LoginSuccess(userData))); + } + + private IEnumerator FetchUserDataCoroutine(Action onSuccess) + { + var usersApi = new UsersApi(App.ICOSA_API_BASEPATH); + var config = new Configuration { AccessToken = App.Instance.IcosaToken }; usersApi.Configuration = config; var getUserTask = usersApi.GetUsersMeUsersMeGetAsync(); yield return new WaitUntil(() => getUserTask.IsCompleted); if (getUserTask.Exception != null) { + if (getUserTask.Exception.Message.Contains("401 Unauthorized")) + { + // Clear user token + LoginFailure(); + } Debug.Log($"GetUser failed with exception: {getUserTask.Exception}"); yield break; } @@ -249,16 +255,27 @@ private IEnumerator LoginCoroutine(string code) if (userData == null) { Debug.Log($"Failure - no user data received"); + // TODO should we logout? Clear username/icon? + yield break; } - LoginSuccess(token, userData); - } - private void LoginSuccess(LoginToken token, FullUser userInfo) + // Call the callback delegate if it's provided (which means this was called from the first coroutine) + App.IcosaUserName = userData.Displayname; + App.IcosaUserIcon = m_IcosaPhoto.material.mainTexture; + onSuccess?.Invoke(userData); + } + + private void LoginSuccess(FullUser userInfo) { HideIcosaLogin(); - App.IcosaIdentity = token.AccessToken; - App.IcosaUserName = userInfo.Displayname; - App.IcosaUserIcon = m_IcosaPhoto.material.mainTexture; + } + + private void LoginFailure() + { + HideIcosaLogin(); + App.Instance.IcosaToken = null; + App.IcosaUserName = ""; + App.IcosaUserIcon = null; } void RefreshBackupProgressText() @@ -334,6 +351,16 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) } break; case SketchControlsScript.GlobalCommands.LoginToIcosa: + if (!App.Config.IsMobileHardware) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + SketchControlsScript.kRemoveHeadsetFyi, + fPopScalar: 0.5f + ); + } + + App.OpenURL(App.ICOSA_DEVICECODE_URL); ShowIcosaLogin(); m_Persistent = true; break; diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 3ebd00e914..a0b5a029b4 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -28,7 +28,7 @@ namespace TiltBrush public class SketchControlsScript : MonoBehaviour { public const string kRemoveHeadsetFyi = "Remove headset to view."; - const string kTiltBrushGalleryUrl = "https://poly.google.com/tiltbrush"; + const string kTiltBrushGalleryUrl = "https://icosa.gallery"; const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; const string kPolyMainPageUri = "https://poly.google.com"; @@ -151,8 +151,6 @@ public enum GlobalCommands OpenLayerOptionsPopup = 5201, RenameLayer = 5202, LoginToIcosa = 5600, - UploadToIcosa = 5601, - LogOutOfIcosa = 5602, OpenScriptsCommandsList = 6000, OpenScriptsList = 6001, OpenExampleScriptsList = 6002, @@ -4572,18 +4570,40 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, } case GlobalCommands.LogOutOfGenericCloud: { - var ident = App.GetIdentity((Cloud)iParam1); - if (ident.LoggedIn) { ident.Logout(); } + Cloud cloud = (Cloud)iParam1; + if (cloud == Cloud.Icosa) + { + App.Instance.IcosaToken = null; + App.IcosaUserName = ""; + App.IcosaUserIcon = null; + } + else + { + var ident = App.GetIdentity(cloud); + if (ident.LoggedIn) { ident.Logout(); } + } break; } case GlobalCommands.UploadToGenericCloud: { Cloud cloud = (Cloud)iParam1; - var ident = App.GetIdentity(cloud); - if (!ident.LoggedIn) + if (cloud == Cloud.Icosa) { - ident.LoginAsync(); - break; + if (App.Instance.IcosaToken == null) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + "Not logged in", fPopScalar: 0.5f); + } + } + else + { + var ident = App.GetIdentity(cloud); + if (!ident.LoggedIn) + { + ident.LoginAsync(); + break; + } } SelectionManager.m_Instance.ClearActiveSelection(); VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); From 2d1c23871741022e3ba4cd54b4b088b3c8d51a23 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 13 Nov 2023 15:20:42 +0000 Subject: [PATCH 013/105] Improved name/photo handling [CI BUILD] --- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index b041652ea7..3781dfc40c 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -261,7 +261,9 @@ private IEnumerator FetchUserDataCoroutine(Action onSuccess) // Call the callback delegate if it's provided (which means this was called from the first coroutine) App.IcosaUserName = userData.Displayname; - App.IcosaUserIcon = m_IcosaPhoto.material.mainTexture; + App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API + m_IcosaNameText.text = App.IcosaUserName; + m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; onSuccess?.Invoke(userData); } From 5f9c6b49e42d53e9f930856d8dbc8c8260d97758 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 14 Nov 2023 15:02:56 +0000 Subject: [PATCH 014/105] Remove an assert now we have more than one upload service --- Assets/Scripts/Sharing/UploadPopUpWindow.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/Sharing/UploadPopUpWindow.cs b/Assets/Scripts/Sharing/UploadPopUpWindow.cs index 1dc1a7ed5a..d98bad3a04 100644 --- a/Assets/Scripts/Sharing/UploadPopUpWindow.cs +++ b/Assets/Scripts/Sharing/UploadPopUpWindow.cs @@ -335,7 +335,6 @@ public void UserPressedLoginButton(int param) /// show a warning or error. public void UserPressedUploadButton(Cloud cloud, Action onSafeToUpload) { - Debug.Assert(cloud == Cloud.Sketchfab); // User attempted to upload, but make sure there's actually something to upload. if (!WidgetManager.m_Instance.HasExportableContent(cloud)) { From e864cc3aae0d36590ed5b20cd2cf597fc3cb8e15 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 14 Nov 2023 15:03:45 +0000 Subject: [PATCH 015/105] Small refactor --- Assets/Scripts/App.cs | 9 +++++++ Assets/Scripts/GUI/ProfilePopUpWindow.cs | 32 ++++++++++++++++-------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index d6e851a9c1..f1ddb3ec67 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -134,6 +134,7 @@ public enum AppState public static string ICOSA_DEVICECODE_URL = "http://192.168.1.228:3000/device"; // public static string ICOSA_API_BASEPATH = "http://api.icosa.gallery"; public static string ICOSA_API_BASEPATH = "http://192.168.1.228:8000"; + public string IcosaToken { get => PlayerPrefs.HasKey("IcosaToken") ? PlayerPrefs.GetString("IcosaToken") : null; @@ -142,6 +143,7 @@ public string IcosaToken public static bool IcosaIsLoggedIn => !string.IsNullOrEmpty(App.Instance.IcosaToken); public static string IcosaUserName; + public static string IcosaUserId; public static Texture IcosaUserIcon; public static GoogleUserSettings GoogleUserSettings => m_Instance.m_GoogleUserSettings; @@ -2349,5 +2351,12 @@ private static void CopySupportFiles() } } + public void LogoutIcosa() + { + IcosaUserName = null; + IcosaUserId = null; + IcosaUserIcon = null; + IcosaToken = null; + } } // class App } // namespace TiltBrush diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 3781dfc40c..0e87c17923 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -95,7 +95,13 @@ override public void Init(GameObject rParent, string sText) m_IcosaLoginElements.SetActive(false); if (App.IcosaIsLoggedIn) { - StartCoroutine(FetchUserDataCoroutine(null)); + StartCoroutine(FetchUserDataCoroutine(userData => + { + App.IcosaUserName = userData.Displayname; + App.IcosaUserId = userData.Id; + App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API + RefreshIcosaUserInfoUi(); + })); } App.DriveAccess.RefreshFreeSpaceAsync().AsAsyncVoid(); @@ -161,6 +167,7 @@ void RefreshObjects() m_IcosaSignedInElements.SetActive(App.IcosaIsLoggedIn); m_IcosaSignedOutElements.SetActive(!App.IcosaIsLoggedIn); m_IcosaConfirmSignOutElements.SetActive(false); + RefreshIcosaUserInfoUi(); m_DriveFullElements.SetActive(driveFull && driveSyncEnabled); m_DriveSyncEnabledElements.SetActive(!driveFull && driveSyncEnabled); @@ -171,6 +178,12 @@ void RefreshObjects() RefreshBackupProgressText(); } + private void RefreshIcosaUserInfoUi() + { + m_IcosaNameText.text = App.IcosaUserName; + m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; + } + public void HideIcosaLogin() { m_IcosaLoginElements.SetActive(false); @@ -258,26 +271,23 @@ private IEnumerator FetchUserDataCoroutine(Action onSuccess) // TODO should we logout? Clear username/icon? yield break; } - - // Call the callback delegate if it's provided (which means this was called from the first coroutine) - App.IcosaUserName = userData.Displayname; - App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API - m_IcosaNameText.text = App.IcosaUserName; - m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; onSuccess?.Invoke(userData); } - private void LoginSuccess(FullUser userInfo) + private void LoginSuccess(FullUser userData) { + // Call the callback delegate if it's provided (which means this was called from the first coroutine) + App.IcosaUserName = userData.Displayname; + App.IcosaUserId = userData.Id; + App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API + RefreshIcosaUserInfoUi(); HideIcosaLogin(); } private void LoginFailure() { HideIcosaLogin(); - App.Instance.IcosaToken = null; - App.IcosaUserName = ""; - App.IcosaUserIcon = null; + App.LogoutIcosa(); } void RefreshBackupProgressText() From 23675d41fa4b6d7692019ef57c6ceb66ed1530db Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 14 Nov 2023 15:27:32 +0000 Subject: [PATCH 016/105] Method is no longer static --- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 0e87c17923..9c53da1fd0 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -287,7 +287,7 @@ private void LoginSuccess(FullUser userData) private void LoginFailure() { HideIcosaLogin(); - App.LogoutIcosa(); + App.Instance.LogoutIcosa(); } void RefreshBackupProgressText() From 9bee41954e566920acdc53968dc24258a136a069 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 14 Nov 2023 15:32:10 +0000 Subject: [PATCH 017/105] Uploads to Icosa (WIP pending backend changes to accept zip files) --- Assets/Scripts/Sharing/IcosaService.cs | 109 +++++++++-------------- Assets/Scripts/Sharing/VrAssetService.cs | 74 ++++++++++++++- Assets/Scripts/Sharing/WebRequest.cs | 36 +++++++- 3 files changed, 145 insertions(+), 74 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 54cd3a009e..531bf03592 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -15,21 +15,23 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using JetBrains.Annotations; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Client; -using Org.OpenAPITools.Model; using UnityEngine; namespace TiltBrush { class IcosaService { - public const string kModelLandingPage = "https://icosa.gallery/"; - const string kApiHost = "https://api.icosa.gallery"; - - + // public const string kModelLandingPage = "https://api.icosa.gallery/edit/"; + // const string kApiHost = "https://api.icosa.gallery"; + + public const string kModelLandingPage = "http://192.168.1.228:3000/edit/"; + const string kApiHost = "http://192.168.1.228:3000"; + /// A paginated response, for use with GetNextPageAsync() public interface Paginated { @@ -108,9 +110,9 @@ public void SetBackgroundColor(Color color) } } - private readonly LoginToken m_accessToken; + private readonly string m_accessToken; - public IcosaService(LoginToken token) + public IcosaService(string token) { m_accessToken = token; } @@ -208,73 +210,42 @@ public class ModelLikesList string Paginated.PreviousUri => previous; } - [Serializable, UsedImplicitly] - public struct CreateResponse + // TODO: /v3/search and /v3/me/search? + // The parameters to the search functions are the same, except /v3/me/search omits + // the "username" parameter, so maybe we can have a single function which wraps both. + + // + /// Pass: + /// temporaryDirectory - if passed, caller is responsible for cleaning it up + public async Task CreateModel( + string name, + string zipPath, IProgress progress, CancellationToken token, + Options options = null, string temporaryDirectory = null) { - public string uid; - public string uri; - } - public LoginToken TestLogin(string deviceCode) - { - Configuration config = new Configuration(); - config.BasePath = kApiHost; - var apiInstance = new LoginApi(config); - try - { - LoginToken result = apiInstance.DeviceLoginLoginDeviceLoginPost(deviceCode); - Debug.Log(result.AccessToken); + // No compression because it's a compressed .zip already + WebRequest uploader = new WebRequest( + $"{kApiHost}/assets", App.IcosaUserId, "POST", compress: false); - return new LoginToken(result.AccessToken); - } - catch (ApiException e) + var moreParams = new List<(string, string)>(); + + // Not currently used or supported by the backend + if (options != null) { - Debug.Log("Exception when calling LoginApi.LoginTokenPost: " + e.Message); - Debug.Log("Status Code: " + e.ErrorCode); - Debug.Log(e.StackTrace); - throw; + moreParams.Add(("options", JsonConvert.SerializeObject(options))); } + + uploader.ProgressObject = progress; + var reply = await uploader.SendNamedDataAsync( + "files", File.OpenRead(zipPath), Path.GetFileName(zipPath), "application/zip", + moreParams: moreParams, token, temporaryDirectory); + return reply.Deserialize(); } - - public void TestUpload(LoginToken token, List files) - { - Configuration config = new Configuration(); - config.BasePath = kApiHost; - config.AccessToken = token.AccessToken; - var apiInstance = new AssetsApi(config); - try - { - var foo = apiInstance.UploadNewAssetsAssetsPost(files); - } - catch (ApiException e) - { - Debug.Log("Exception when calling AssetsApi.UploadNewAssetsAssetsPost: " + e.Message); - Debug.Log("Status Code: " + e.ErrorCode); - Debug.Log(e.StackTrace); - } - } - - public LoginToken TestLogin() + [Serializable, UsedImplicitly] + public struct CreateResponse { - Configuration config = new Configuration(); - config.BasePath = kApiHost; - var apiInstance = new LoginApi(config); - var username = "andy@andybak.net"; - var password = "foobar"; - - try - { - LoginToken result = apiInstance.LoginLoginPost(username, password); - Debug.Log(result.AccessToken); - return result; - } - catch (ApiException e) - { - Debug.Log("Exception when calling LoginApi.LoginLoginPost: " + e.Message); - Debug.Log("Status Code: " + e.ErrorCode); - Debug.Log(e.StackTrace); - return null; - } + public string uid; + public string uri; } } } // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index ef4f939197..1311dfc6c6 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -22,6 +22,7 @@ using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Org.OpenAPITools.Api; +using Org.OpenAPITools.Client; using UnityEngine; using UnityEngine.Networking; @@ -466,9 +467,18 @@ void ReportFailure(string userFriendly, string fullMessage = "") AudioManager.m_Instance.UploadLoop(true); var timer = System.Diagnostics.Stopwatch.StartNew(); m_UploadTask = new TaskAndCts<(string url, long bytes)>(); - Debug.Assert(backend == Cloud.Sketchfab); - m_UploadTask.Task = UploadCurrentSketchSketchfabAsync(m_UploadTask.Token, tempUploadDir.Value, - isDemoUpload); + + switch (backend) + { + case Cloud.Icosa: + m_UploadTask.Task = UploadCurrentSketchIcosaAsync(m_UploadTask.Token, tempUploadDir.Value, + isDemoUpload); + break; + case Cloud.Sketchfab: + m_UploadTask.Task = UploadCurrentSketchSketchfabAsync(m_UploadTask.Token, tempUploadDir.Value, + isDemoUpload); + break; + } var (url, totalUploadLength) = await m_UploadTask.Task; m_LastUploadCompleteUrl = url; ControllerConsoleScript.m_Instance.AddNewLine("Upload succeeded!"); @@ -654,6 +664,64 @@ private async Task CreateZipFileAsync( } } + // TODO: Refactor. This is largely the same as UploadCurrentSketchSketchFabAsync aside from a few url changes and the response. + private async Task<(string, long)> UploadCurrentSketchIcosaAsync( + CancellationToken token, string tempUploadDir, bool _) + { + DiskSceneFileInfo fileInfo = GetWritableFile(); + + SetUploadProgress(UploadStep.CreateGltf, 0); + // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. + string gltfFile = Path.Combine(tempUploadDir, kGltfName); + var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( + OverlayType.Export, fadeDuration: 0.5f, + action: () => new ExportGlTF().ExportBrushStrokes( + gltfFile, + AxisConvention.kGltf2, binary: false, doExtras: false, + includeLocalMediaContent: true, gltfVersion: 2, + // TODO: selfContained: false was Poly setting but is it what we want to do now? + selfContained: true)); + if (!exportResults.success) + { + throw new VrAssetServiceException("Internal error creating upload data."); + } + + // Construct options to set the background color to the current environment's clear color. + Color bgColor = SceneSettings.m_Instance.CurrentEnvironment.m_RenderSettings.m_ClearColor; + IcosaService.Options options = null; + // options.SetBackgroundColor(bgColor); + + // TODO(b/146892613): we're not uploading this at the moment. Should we be? + // If we don't, we can probably remove this step...? + SetUploadProgress(UploadStep.CreateTilt, 0); + await CreateTiltForUploadAsync(fileInfo); + token.ThrowIfCancellationRequested(); + + // Create a copy of the .tilt file in tempUploadDir. + string tempTiltPath = Path.Combine(tempUploadDir, "sketch.tilt"); + File.Copy(fileInfo.FullPath, tempTiltPath); + + // Collect files into a .zip file, including the .tilt file. + string zipName = Path.Combine(tempUploadDir, "archive.zip"); + var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath); + await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); + var uploadLength = new FileInfo(zipName).Length; + + var service = new IcosaService(App.Instance.IcosaToken); + var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); + var response = await service.CreateModel( + fileInfo.HumanName, zipName, progress, token, options, tempUploadDir); + // TODO(b/146892613): return the UID and stick it into the .tilt file? + // Or do we not care since we aren't recording provenance and remixing + + // TODO(b/146892613): figure out this flow + // response.uri is not very useful; it is an API uri that gives you json of asset details. + // Also, the 3d-models URI might show that the asset is still processing. We can poll their + // API and find out when it's done and pop up the window then? + string uri = $"{IcosaService.kModelLandingPage}{App.IcosaUserId}/{response.ToString()}"; + return (uri, 0); + } + private async Task<(string, long)> UploadCurrentSketchSketchfabAsync( CancellationToken token, string tempUploadDir, bool _) { diff --git a/Assets/Scripts/Sharing/WebRequest.cs b/Assets/Scripts/Sharing/WebRequest.cs index 54e116a297..cca33b1400 100644 --- a/Assets/Scripts/Sharing/WebRequest.cs +++ b/Assets/Scripts/Sharing/WebRequest.cs @@ -230,7 +230,10 @@ public static Task GetAsync(string uri) private string m_Uri; private string m_Method; + private OAuth2Identity m_Identity; + private string m_LoginToken; + private byte[] m_Result = null; private int m_UploadedBytes = 0; private float? m_PreUploadProgress = null; @@ -301,6 +304,24 @@ public WebRequest(string uri, OAuth2Identity identity, m_Uri = uri; m_Identity = identity; } + + // identity may be null, in which case no authentication takes place + public WebRequest(string uri, string loginToken, + string method = UnityWebRequest.kHttpVerbGET, bool compress = false) + { + if (string.IsNullOrEmpty(uri)) + { + throw new ArgumentException("uri"); + } + if (!kEnableHttpCompression) + { + compress = false; + } + m_Method = method; + m_Compressed = compress; + m_Uri = uri; + m_LoginToken = loginToken; + } /// Sends a multipart form that includes a parameter with data that comes from a stream. /// This method takes ownership of the stream and guarantees that it will be closed. @@ -438,7 +459,11 @@ public async Task SendAsync( } www.downloadHandler = new DownloadHandlerBuffer(); - if (m_Identity != null) + if (m_LoginToken != null) + { + www.SetRequestHeader("Authorization", $"Bearer {m_LoginToken}"); + } + else if (m_Identity != null) { await m_Identity.Authenticate(www); } @@ -555,7 +580,14 @@ async void DelayedDispose(IDisposable id) // authorization has been revoked, so just log out here. if (IsAuthError(www.responseCode)) { - m_Identity.Logout(); + if (m_LoginToken != null) + { + App.Instance.LogoutIcosa(); + } + else if (m_Identity != null) + { + m_Identity.Logout(); + } throw new VrAssetServiceException("Not authorized for login. Automatically logged out.", RedactUriForError(m_Uri)); } From 7230154490604cf433c9811e6f04ba99ac13fe4b Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 15 Nov 2023 15:40:53 +0000 Subject: [PATCH 018/105] Fix temp API url --- Assets/Scripts/Sharing/IcosaService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 531bf03592..693a383583 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -30,7 +30,7 @@ class IcosaService // const string kApiHost = "https://api.icosa.gallery"; public const string kModelLandingPage = "http://192.168.1.228:3000/edit/"; - const string kApiHost = "http://192.168.1.228:3000"; + const string kApiHost = "http://192.168.1.228:8000"; /// A paginated response, for use with GetNextPageAsync() public interface Paginated From cfb1b14bc9cdacfc47ed4dd84e3744a4774a3ab6 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 15 Nov 2023 15:41:22 +0000 Subject: [PATCH 019/105] Minor cleanup --- Assets/Prefabs/PopUps/PopupWindow_Upload.prefab | 12 ++++++------ Assets/Scripts/Sharing/UploadPopUpWindow.cs | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab b/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab index fca77611dd..d9aaa92820 100644 --- a/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab +++ b/Assets/Prefabs/PopUps/PopupWindow_Upload.prefab @@ -1402,7 +1402,7 @@ GameObject: m_Component: - component: {fileID: 4000013830214004} m_Layer: 16 - m_Name: EmbeddedMediaWarningPoly + m_Name: EmbeddedMediaWarningIcosa m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -2079,13 +2079,13 @@ MonoBehaviour: m_Progress: {fileID: 23000010967552864} m_LoginOnDesktopObjects: {fileID: 1000013096383082} m_ConfirmObjects: {fileID: 6508589329552276940} - m_PolyLoggedInObjects: {fileID: 2420543688105002897} - m_PolyLoggedOutObjects: {fileID: 587811777206900469} + m_IcosaLoggedInObjects: {fileID: 2420543688105002897} + m_IcosaLoggedOutObjects: {fileID: 587811777206900469} m_SketchfabLoggedInObjects: {fileID: 4035661904903806700} m_SketchfabLoggedOutObjects: {fileID: 5455702764299504055} - m_PolyUserName: {fileID: 5207780760739753065} + m_IcosaUserName: {fileID: 5207780760739753065} m_SketchfabUserName: {fileID: 8355879036552585063} - m_GooglePhoto: {fileID: 5158317984005904495} + m_IcosaPhoto: {fileID: 5158317984005904495} m_SketchfabPhoto: {fileID: 6725221811800815683} m_UploadObjects: {fileID: 1000013114340806} m_UploadCompleteObjects: {fileID: 1000012022869828} @@ -2093,7 +2093,7 @@ MonoBehaviour: m_UploadFailedMessage: {fileID: 114000010005430638} m_UploadingDeniedObjects: {fileID: 1954885538622324} m_WaitObjects: {fileID: 1000010727908878} - m_EmbeddedMediaWarningPoly: {fileID: 1000011947057708} + m_EmbeddedMediaWarningIcosa: {fileID: 1000011947057708} m_EmbeddedMediaWarningSketchfab: {fileID: 8734179732674817566} m_NothingToUploadWarning: {fileID: 1607740331362510216} m_ConnectionErrorObjects: {fileID: 1000012381343644} diff --git a/Assets/Scripts/Sharing/UploadPopUpWindow.cs b/Assets/Scripts/Sharing/UploadPopUpWindow.cs index d98bad3a04..3767bf60f3 100644 --- a/Assets/Scripts/Sharing/UploadPopUpWindow.cs +++ b/Assets/Scripts/Sharing/UploadPopUpWindow.cs @@ -47,13 +47,13 @@ private enum DisplayMode // Things that should be visible when confirming upload. [SerializeField] private GameObject m_ConfirmObjects; - [FormerlySerializedAs("m_PolyLoggedInObjects")] [SerializeField] private GameObject m_IcosaLoggedInObjects; - [FormerlySerializedAs("m_PolyLoggedOutObjects")] [SerializeField] private GameObject m_IcosaLoggedOutObjects; + [SerializeField] private GameObject m_IcosaLoggedInObjects; + [SerializeField] private GameObject m_IcosaLoggedOutObjects; [SerializeField] private GameObject m_SketchfabLoggedInObjects; [SerializeField] private GameObject m_SketchfabLoggedOutObjects; - [FormerlySerializedAs("m_PolyUserName")] [SerializeField] private TMPro.TextMeshPro m_IcosaUserName; + [SerializeField] private TMPro.TextMeshPro m_IcosaUserName; [SerializeField] private TMPro.TextMeshPro m_SketchfabUserName; - [FormerlySerializedAs("m_GooglePhoto")] [SerializeField] private Renderer m_IcosaPhoto; + [SerializeField] private Renderer m_IcosaPhoto; [SerializeField] private Renderer m_SketchfabPhoto; // Things that should be visible when uploading. @@ -73,7 +73,7 @@ private enum DisplayMode [SerializeField] private GameObject m_WaitObjects; // Things that should be visible when media library content is in the scene. - [FormerlySerializedAs("m_EmbeddedMediaWarningPoly")] [SerializeField] private GameObject m_EmbeddedMediaWarningIcosa; + [SerializeField] private GameObject m_EmbeddedMediaWarningIcosa; [SerializeField] private GameObject m_EmbeddedMediaWarningSketchfab; // Things that should be visible when there's nothing to upload. @@ -202,7 +202,7 @@ override protected void BaseUpdate() if (m_LoginOnDesktopObjects.activeSelf) { // Check to see if we just logged in. - if ((m_LoggingInType == Cloud.Icosa && App.Instance.IcosaToken != null) || + if ((m_LoggingInType == Cloud.Icosa && App.IcosaIsLoggedIn) || (m_LoggingInType == Cloud.Sketchfab && App.SketchfabIdentity.LoggedIn)) { SetMode(DisplayMode.Loggedout); @@ -274,7 +274,7 @@ void RefreshUploadButton(Cloud backend) var ui = GetUiFor(backend); if (backend == Cloud.Icosa) { - bool icosaLoggedIn = App.Instance.IcosaToken != null; + bool icosaLoggedIn = App.IcosaIsLoggedIn; ui.loggedInElements.SetActive(icosaLoggedIn); ui.loggedOutElements.SetActive(!icosaLoggedIn); if (icosaLoggedIn) From 22149648b8a0837b7ceb29d1d2ccc437c7371018 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 15 Nov 2023 15:41:45 +0000 Subject: [PATCH 020/105] Fix for monoscopic mode --- Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab index c7736606c7..3501fdb33a 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab @@ -578,6 +578,7 @@ MonoBehaviour: m_StringArgument: m_BoolArgument: 0 m_CallState: 2 + InputEnabled: 1 --- !u!1 &281059592966504990 GameObject: m_ObjectHideFlags: 0 @@ -12388,7 +12389,7 @@ MonoBehaviour: m_SubtitleCharacterWidth: 0.05625 m_ButtonWidth: 0.5 m_BaseButtonOffset: {x: 0, y: 0, z: 0} - m_ReticleBounds: {x: 1.6, y: 1.4, z: -0.35} + m_ReticleBounds: {x: 1.6, y: 3, z: -0.35} m_PopUpForwardOffset: -0.25 m_AutoPlaceButtons: [] m_TransitionDuration: 0.1 From b8e5eee8e45b028356d600cfa9c829f97bab6e95 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 15 Nov 2023 15:56:16 +0000 Subject: [PATCH 021/105] Allow real keyboard input while keyboard popups are open --- Assets/Prefabs/Keyboard/Keyboard.prefab | 290 ++++++++++++++++++ .../Prefabs/Keyboard/NumericKeyboard.prefab | 80 +++++ Assets/Scripts/InputManager.cs | 16 + .../UltimateXR Keyboard/KeyboardKeyUI.cs | 17 +- .../UltimateXR Keyboard/KeyboardUI.cs | 8 + 5 files changed, 410 insertions(+), 1 deletion(-) diff --git a/Assets/Prefabs/Keyboard/Keyboard.prefab b/Assets/Prefabs/Keyboard/Keyboard.prefab index 1211bfe519..3c834731d5 100644 --- a/Assets/Prefabs/Keyboard/Keyboard.prefab +++ b/Assets/Prefabs/Keyboard/Keyboard.prefab @@ -973,6 +973,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 33 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -1177,6 +1182,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 42 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -1386,6 +1396,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 8 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -1600,6 +1615,11 @@ PrefabInstance: propertyPath: _keyType value: 3 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 72 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -1839,6 +1859,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 45 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -2058,6 +2083,11 @@ PrefabInstance: propertyPath: _keyType value: 1 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 3 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -2282,6 +2312,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 24 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -2496,6 +2531,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 17 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -2710,6 +2750,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 32 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -2919,6 +2964,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 39 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -3138,6 +3188,11 @@ PrefabInstance: propertyPath: _keyType value: 8 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 65 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -3377,6 +3432,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 36 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -3591,6 +3651,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 18 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -3800,6 +3865,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 47 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -4014,6 +4084,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 23 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -4223,6 +4298,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 7 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -4432,6 +4512,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 14 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -4646,6 +4731,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 31 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -4860,6 +4950,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 15 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -5074,6 +5169,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 35 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -5293,6 +5393,11 @@ PrefabInstance: propertyPath: _keyType value: 6 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 54 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -5532,6 +5637,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 29 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -5746,6 +5856,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 28 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -5960,6 +6075,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 21 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -6174,6 +6294,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 38 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -6383,6 +6508,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 13 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -6602,6 +6732,11 @@ PrefabInstance: propertyPath: _keyType value: 4 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 55 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -6841,6 +6976,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 20 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -7055,6 +7195,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 40 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -7264,6 +7409,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 10 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -7473,6 +7623,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 12 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -7687,6 +7842,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 26 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -7896,6 +8056,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 41 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -8110,6 +8275,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 25 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -8319,6 +8489,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 11 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -8528,6 +8703,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 50 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -8737,6 +8917,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 43 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -8956,6 +9141,11 @@ PrefabInstance: propertyPath: _keyType value: 4 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 56 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -9195,6 +9385,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 19 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -9409,6 +9604,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 22 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -9618,6 +9818,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 4 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _controlInput @@ -9817,6 +10022,11 @@ PrefabInstance: propertyPath: _keyType value: 2 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 51 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -10051,6 +10261,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 5 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -10265,6 +10480,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 27 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -10474,6 +10694,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 6 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -10683,6 +10908,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 49 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -10902,6 +11132,11 @@ PrefabInstance: propertyPath: _keyType value: 2 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 52 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -11136,6 +11371,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 48 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -11350,6 +11590,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 30 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -11564,6 +11809,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 44 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -11773,6 +12023,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 9 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -11992,6 +12247,11 @@ PrefabInstance: propertyPath: _keyType value: 7 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 2 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -12231,6 +12491,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 37 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -12445,6 +12710,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 34 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -12654,6 +12924,11 @@ PrefabInstance: propertyPath: m_Text value: objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 46 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -12868,6 +13143,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 1 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -13122,6 +13402,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 16 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift @@ -13341,6 +13626,11 @@ PrefabInstance: propertyPath: _keyType value: 5 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 53 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _printShift diff --git a/Assets/Prefabs/Keyboard/NumericKeyboard.prefab b/Assets/Prefabs/Keyboard/NumericKeyboard.prefab index 2e022cf95a..aff3fcba29 100644 --- a/Assets/Prefabs/Keyboard/NumericKeyboard.prefab +++ b/Assets/Prefabs/Keyboard/NumericKeyboard.prefab @@ -365,6 +365,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 13 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -609,6 +614,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 42 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -838,6 +848,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 45 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -1072,6 +1087,11 @@ PrefabInstance: propertyPath: _keyType value: 8 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 65 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -1326,6 +1346,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 47 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -1555,6 +1580,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 7 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -1784,6 +1814,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 8 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -2013,6 +2048,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 41 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -2242,6 +2282,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 50 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -2471,6 +2516,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 43 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -2700,6 +2750,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 49 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -2929,6 +2984,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 48 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -3163,6 +3223,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 44 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -3397,6 +3462,11 @@ PrefabInstance: propertyPath: _keyType value: 7 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 2 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -3651,6 +3721,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 46 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel @@ -3880,6 +3955,11 @@ PrefabInstance: propertyPath: _layout value: 0 objectReference: {fileID: 0} + - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, + type: 3} + propertyPath: _keycode + value: 1 + objectReference: {fileID: 0} - target: {fileID: 114586615171430532, guid: 6037d0909c862e84a89c912556ca9aaf, type: 3} propertyPath: _forceLabel diff --git a/Assets/Scripts/InputManager.cs b/Assets/Scripts/InputManager.cs index d87cfe1022..f89fbf57d7 100644 --- a/Assets/Scripts/InputManager.cs +++ b/Assets/Scripts/InputManager.cs @@ -314,6 +314,8 @@ public struct TouchInput private Dictionary m_SketchToKeyboardCommandMap = new Dictionary(); + private bool m_DisableKeyboardShortcuts = false; + // // Public properties // @@ -385,6 +387,18 @@ public bool WandOnRight } } + public bool DisableKeyboardShortcuts + { + get + { + return m_DisableKeyboardShortcuts; + } + set + { + m_DisableKeyboardShortcuts = value; + } + } + public void EnablePoseTracking(bool enabled) { UnityEngine.XR.XRDevice.DisableAutoXRCameraTracking(App.VrSdk.GetVrCamera(), !enabled); @@ -529,6 +543,7 @@ void OnControllerPosesApplied() public bool GetKeyboardShortcut(KeyboardShortcut shortcut) { + if (m_DisableKeyboardShortcuts) return false; if (!ActiveKeyMap.TryGetValue((int)shortcut, out Key[] codes)) { return false; @@ -545,6 +560,7 @@ public bool GetKeyboardShortcut(KeyboardShortcut shortcut) public bool GetKeyboardShortcutDown(KeyboardShortcut shortcut) { + if (m_DisableKeyboardShortcuts) return false; if (!ActiveKeyMap.TryGetValue((int)shortcut, out Key[] codes)) { return false; diff --git a/Assets/ThirdParty/UltimateXR Keyboard/KeyboardKeyUI.cs b/Assets/ThirdParty/UltimateXR Keyboard/KeyboardKeyUI.cs index 86392e2580..4a04e7330f 100644 --- a/Assets/ThirdParty/UltimateXR Keyboard/KeyboardKeyUI.cs +++ b/Assets/ThirdParty/UltimateXR Keyboard/KeyboardKeyUI.cs @@ -3,6 +3,7 @@ // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- +using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Localization; @@ -106,6 +107,7 @@ public class KeyboardKeyUI : MonoBehaviour [SerializeField] private TMPro.TMP_Text _multipleLayoutValueBottomLeft; [SerializeField] private TMPro.TMP_Text _multipleLayoutValueBottomRight; [SerializeField] private List _toggleSymbols; + [SerializeField] private UnityEngine.InputSystem.Key _keycode; // Hidden in the custom inspector [SerializeField] private bool _nameDirty; @@ -190,7 +192,7 @@ public void KeyDown() { _keyboard.KeyButton_KeyDown(this); } - + public void KeyUp() { _keyboard.KeyButton_KeyUp(this); @@ -379,6 +381,19 @@ private void Update() UpdateName(); } } + + // Support real keyboard input while open + if (_keycode != UnityEngine.InputSystem.Key.None) + { + if (UnityEngine.InputSystem.Keyboard.current[_keycode].wasPressedThisFrame) + { + KeyDown(); + } + if (UnityEngine.InputSystem.Keyboard.current[_keycode].wasReleasedThisFrame) + { + KeyUp(); + } + } } #endif diff --git a/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs b/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs index cf5b6cd0f0..f5003522aa 100644 --- a/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs +++ b/Assets/ThirdParty/UltimateXR Keyboard/KeyboardUI.cs @@ -301,12 +301,20 @@ protected void Awake() } } + private void OnDestroy() + { + InputManager.m_Instance.DisableKeyboardShortcuts = false; + } + /// /// If there is a console display Text component specified, it becomes updated with the content plus the cursor. /// If there is a caps lock GameObject specified it is updated to reflect the caps lock state as well. /// private void Update() { + // Disable global keyboard shortcuts while active so we can use real keyboard for input + InputManager.m_Instance.DisableKeyboardShortcuts = gameObject.activeInHierarchy; + if (_consoleDisplay != null) { _consoleDisplay.text = FormatStringOutput(_consoleDisplayUsesCursor ? ConsoleContentWithCursor : ConsoleContent, _consoleDisplayUsesCursor); From 3ed97a0e7dca46ff98db54673065a760e01a1a3f Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 3 Apr 2024 13:00:41 +0100 Subject: [PATCH 022/105] dotnet-format --- Assets/Scripts/App.cs | 4 ++-- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 12 ++++++------ Assets/Scripts/Sharing/IcosaService.cs | 8 ++++---- Assets/Scripts/Sharing/UploadPopUpWindow.cs | 2 +- Assets/Scripts/Sharing/VrAssetService.cs | 2 +- Assets/Scripts/Sharing/WebRequest.cs | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index d2f7eb6232..640e125f41 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -134,7 +134,7 @@ public enum AppState public static string ICOSA_DEVICECODE_URL = "http://192.168.1.228:3000/device"; // public static string ICOSA_API_BASEPATH = "http://api.icosa.gallery"; public static string ICOSA_API_BASEPATH = "http://192.168.1.228:8000"; - + public string IcosaToken { get => PlayerPrefs.HasKey("IcosaToken") ? PlayerPrefs.GetString("IcosaToken") : null; @@ -240,7 +240,7 @@ public static void Log(string msg) [Header("Identities")] [SerializeField] private OAuth2Identity m_GoogleIdentity; [SerializeField] private OAuth2Identity m_SketchfabIdentity; - + // ------------------------------------------------------------ // Private data // ------------------------------------------------------------ diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 9c53da1fd0..7b4f049fe4 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -177,7 +177,7 @@ void RefreshObjects() m_DriveSyncing = driveSyncing; RefreshBackupProgressText(); } - + private void RefreshIcosaUserInfoUi() { m_IcosaNameText.text = App.IcosaUserName; @@ -233,7 +233,7 @@ private IEnumerator LoginCoroutine(string code) } yield break; } - + if (loginTask.Result?.AccessToken == null) { // TODO: Show error message. @@ -261,7 +261,7 @@ private IEnumerator FetchUserDataCoroutine(Action onSuccess) LoginFailure(); } Debug.Log($"GetUser failed with exception: {getUserTask.Exception}"); - yield break; + yield break; } var userData = getUserTask.Result; @@ -273,7 +273,7 @@ private IEnumerator FetchUserDataCoroutine(Action onSuccess) } onSuccess?.Invoke(userData); } - + private void LoginSuccess(FullUser userData) { // Call the callback delegate if it's provided (which means this was called from the first coroutine) @@ -283,7 +283,7 @@ private void LoginSuccess(FullUser userData) RefreshIcosaUserInfoUi(); HideIcosaLogin(); } - + private void LoginFailure() { HideIcosaLogin(); @@ -373,7 +373,7 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) } App.OpenURL(App.ICOSA_DEVICECODE_URL); - ShowIcosaLogin(); + ShowIcosaLogin(); m_Persistent = true; break; case SketchControlsScript.GlobalCommands.AccountInfo: diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 693a383583..959ecf7128 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -28,10 +28,10 @@ class IcosaService { // public const string kModelLandingPage = "https://api.icosa.gallery/edit/"; // const string kApiHost = "https://api.icosa.gallery"; - + public const string kModelLandingPage = "http://192.168.1.228:3000/edit/"; const string kApiHost = "http://192.168.1.228:8000"; - + /// A paginated response, for use with GetNextPageAsync() public interface Paginated { @@ -228,13 +228,13 @@ public async Task CreateModel( $"{kApiHost}/assets", App.IcosaUserId, "POST", compress: false); var moreParams = new List<(string, string)>(); - + // Not currently used or supported by the backend if (options != null) { moreParams.Add(("options", JsonConvert.SerializeObject(options))); } - + uploader.ProgressObject = progress; var reply = await uploader.SendNamedDataAsync( "files", File.OpenRead(zipPath), Path.GetFileName(zipPath), "application/zip", diff --git a/Assets/Scripts/Sharing/UploadPopUpWindow.cs b/Assets/Scripts/Sharing/UploadPopUpWindow.cs index 3767bf60f3..7bdf75818a 100644 --- a/Assets/Scripts/Sharing/UploadPopUpWindow.cs +++ b/Assets/Scripts/Sharing/UploadPopUpWindow.cs @@ -281,7 +281,7 @@ void RefreshUploadButton(Cloud backend) { ui.name.text = App.IcosaUserName; ui.photo.material.mainTexture = App.IcosaUserIcon; - } + } } else { diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 1311dfc6c6..5e1b6b46c8 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -540,7 +540,7 @@ private async void VerifyIcosaConnectionAndCheckApiVersionAsync() private static async Task GetIcosaStatus() { // UserConfig override - if (App.UserConfig.Flags.DisableIcosa || App.Instance.IcosaToken==null) + if (App.UserConfig.Flags.DisableIcosa || App.Instance.IcosaToken == null) { return IcosaStatus.Disabled; } diff --git a/Assets/Scripts/Sharing/WebRequest.cs b/Assets/Scripts/Sharing/WebRequest.cs index cca33b1400..5ef0e8b73c 100644 --- a/Assets/Scripts/Sharing/WebRequest.cs +++ b/Assets/Scripts/Sharing/WebRequest.cs @@ -230,10 +230,10 @@ public static Task GetAsync(string uri) private string m_Uri; private string m_Method; - + private OAuth2Identity m_Identity; private string m_LoginToken; - + private byte[] m_Result = null; private int m_UploadedBytes = 0; private float? m_PreUploadProgress = null; @@ -304,7 +304,7 @@ public WebRequest(string uri, OAuth2Identity identity, m_Uri = uri; m_Identity = identity; } - + // identity may be null, in which case no authentication takes place public WebRequest(string uri, string loginToken, string method = UnityWebRequest.kHttpVerbGET, bool compress = false) From 47f745d371670638cdfa88c7175ae4ef171c1c26 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 22 May 2024 11:49:15 +0100 Subject: [PATCH 023/105] Doh. Send the access token not the userId --- Assets/Scripts/Sharing/IcosaService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 959ecf7128..bb9b6bb170 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -225,7 +225,7 @@ public async Task CreateModel( // No compression because it's a compressed .zip already WebRequest uploader = new WebRequest( - $"{kApiHost}/assets", App.IcosaUserId, "POST", compress: false); + $"{kApiHost}/assets", m_accessToken, "POST", compress: false); var moreParams = new List<(string, string)>(); From 3eda3c6e0ce5ff607acfc6e2af9b3488fde71bd3 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 22 May 2024 12:02:06 +0100 Subject: [PATCH 024/105] Clean up some Icosa code --- Assets/Scripts/App.cs | 7 +++---- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 6 +++--- Assets/Scripts/Sharing/IcosaService.cs | 8 +------- Assets/Scripts/Sharing/VrAssetService.cs | 4 ++-- Assets/Scripts/Sharing/WebRequest.cs | 2 +- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index 4bdd385a00..cb704cc245 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -131,10 +131,9 @@ public enum AppState public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; - // public static string ICOSA_DEVICECODE_URL = "http://icosa.gallery/device"; - public static string ICOSA_DEVICECODE_URL = "http://192.168.1.228:3000/device"; - // public static string ICOSA_API_BASEPATH = "http://api.icosa.gallery"; - public static string ICOSA_API_BASEPATH = "http://192.168.1.228:8000"; + // TODO Make these overridable + public static string ICOSA_WEBSITE_URL = "https://icosa.ixxy.co.uk"; + public static string ICOSA_API_URL = "https://icosa-api.ixxy.co.uk"; public string IcosaToken { diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 7b4f049fe4..095d0c16d5 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -218,7 +218,7 @@ public void HandleIcosaLoginSubmit(string code) private IEnumerator LoginCoroutine(string code) { var config = new Configuration(); - var loginApi = new LoginApi(App.ICOSA_API_BASEPATH); + var loginApi = new LoginApi(App.ICOSA_API_URL); loginApi.Configuration = config; var loginTask = loginApi.DeviceLoginLoginDeviceLoginPostAsync(code); yield return new WaitUntil(() => loginTask.IsCompleted); @@ -247,7 +247,7 @@ private IEnumerator LoginCoroutine(string code) private IEnumerator FetchUserDataCoroutine(Action onSuccess) { - var usersApi = new UsersApi(App.ICOSA_API_BASEPATH); + var usersApi = new UsersApi(App.ICOSA_API_URL); var config = new Configuration { AccessToken = App.Instance.IcosaToken }; usersApi.Configuration = config; var getUserTask = usersApi.GetUsersMeUsersMeGetAsync(); @@ -372,7 +372,7 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) ); } - App.OpenURL(App.ICOSA_DEVICECODE_URL); + App.OpenURL($"{App.ICOSA_WEBSITE_URL}/device"); ShowIcosaLogin(); m_Persistent = true; break; diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index bb9b6bb170..4c49f0e7f7 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -26,12 +26,6 @@ namespace TiltBrush { class IcosaService { - // public const string kModelLandingPage = "https://api.icosa.gallery/edit/"; - // const string kApiHost = "https://api.icosa.gallery"; - - public const string kModelLandingPage = "http://192.168.1.228:3000/edit/"; - const string kApiHost = "http://192.168.1.228:8000"; - /// A paginated response, for use with GetNextPageAsync() public interface Paginated { @@ -225,7 +219,7 @@ public async Task CreateModel( // No compression because it's a compressed .zip already WebRequest uploader = new WebRequest( - $"{kApiHost}/assets", m_accessToken, "POST", compress: false); + $"{App.ICOSA_API_URL}/assets", m_accessToken, "POST", compress: false); var moreParams = new List<(string, string)>(); diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 5e1b6b46c8..78cab4ee1f 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -548,7 +548,7 @@ private static async Task GetIcosaStatus() string uri = String.Format("{0}{1}", ApiHost, kGetVersionUri); try { - var api = new LoginApi($"{App.ICOSA_API_BASEPATH}"); + var api = new LoginApi($"{App.ICOSA_API_URL}"); var result = new Dictionary { { "version", "v1" } }; // TODO: get version from API string version = result["version"]; if (version == kIcosaApiVersion) @@ -718,7 +718,7 @@ private async Task CreateZipFileAsync( // response.uri is not very useful; it is an API uri that gives you json of asset details. // Also, the 3d-models URI might show that the asset is still processing. We can poll their // API and find out when it's done and pop up the window then? - string uri = $"{IcosaService.kModelLandingPage}{App.IcosaUserId}/{response.ToString()}"; + string uri = $"{App.ICOSA_WEBSITE_URL}/edit/{App.IcosaUserId}/{response.ToString()}"; return (uri, 0); } diff --git a/Assets/Scripts/Sharing/WebRequest.cs b/Assets/Scripts/Sharing/WebRequest.cs index 5ef0e8b73c..3abec3dd88 100644 --- a/Assets/Scripts/Sharing/WebRequest.cs +++ b/Assets/Scripts/Sharing/WebRequest.cs @@ -461,7 +461,7 @@ public async Task SendAsync( www.downloadHandler = new DownloadHandlerBuffer(); if (m_LoginToken != null) { - www.SetRequestHeader("Authorization", $"Bearer {m_LoginToken}"); + www.SetRequestHeader("Authorization", $"bearer {m_LoginToken}"); } else if (m_Identity != null) { From 1dee77581f521e38f8494aa81541387722bc85ab Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 22 May 2024 12:22:25 +0100 Subject: [PATCH 025/105] Store the upload_job id in the response --- Assets/Scripts/Sharing/IcosaService.cs | 3 +-- Assets/Scripts/Sharing/VrAssetService.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 4c49f0e7f7..7f7414793c 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -238,8 +238,7 @@ public async Task CreateModel( [Serializable, UsedImplicitly] public struct CreateResponse { - public string uid; - public string uri; + public string upload_job; } } } // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 78cab4ee1f..b4a3ad913d 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -718,7 +718,7 @@ private async Task CreateZipFileAsync( // response.uri is not very useful; it is an API uri that gives you json of asset details. // Also, the 3d-models URI might show that the asset is still processing. We can poll their // API and find out when it's done and pop up the window then? - string uri = $"{App.ICOSA_WEBSITE_URL}/edit/{App.IcosaUserId}/{response.ToString()}"; + string uri = $"{App.ICOSA_WEBSITE_URL}/edit/{response.upload_job}"; return (uri, 0); } From 264606fb974df9bd1a24d97f71646c640ef2f014 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 22 May 2024 12:35:02 +0100 Subject: [PATCH 026/105] Use the current sketch name as the upload name if it's valid --- Assets/Scripts/Sharing/VrAssetService.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index b4a3ad913d..96c3107a02 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -670,9 +670,12 @@ private async Task CreateZipFileAsync( { DiskSceneFileInfo fileInfo = GetWritableFile(); + var currentScene = SaveLoadScript.m_Instance.SceneFile; + string uploadName = currentScene.Valid ? currentScene.HumanName : kGltfName; + SetUploadProgress(UploadStep.CreateGltf, 0); // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. - string gltfFile = Path.Combine(tempUploadDir, kGltfName); + string gltfFile = Path.Combine(tempUploadDir, uploadName); var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( OverlayType.Export, fadeDuration: 0.5f, action: () => new ExportGlTF().ExportBrushStrokes( From 8bad587094a1654ba213e2f7dbdc55b0db55c204 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 22 May 2024 12:35:18 +0100 Subject: [PATCH 027/105] Unused code --- Assets/Scripts/Sharing/VrAssetService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 96c3107a02..92e9020e18 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -708,7 +708,6 @@ private async Task CreateZipFileAsync( string zipName = Path.Combine(tempUploadDir, "archive.zip"); var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath); await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); - var uploadLength = new FileInfo(zipName).Length; var service = new IcosaService(App.Instance.IcosaToken); var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); From 6b661e3b7c0d141d5002160e18088c87dd8da571 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 22 May 2024 14:08:13 +0100 Subject: [PATCH 028/105] Include thumbnail in the zip --- Assets/Scripts/Sharing/IcosaService.cs | 1 - Assets/Scripts/Sharing/VrAssetService.cs | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 7f7414793c..104236e3cf 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -212,7 +212,6 @@ public class ModelLikesList /// Pass: /// temporaryDirectory - if passed, caller is responsible for cleaning it up public async Task CreateModel( - string name, string zipPath, IProgress progress, CancellationToken token, Options options = null, string temporaryDirectory = null) { diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 92e9020e18..d823465e59 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -697,22 +697,26 @@ private async Task CreateZipFileAsync( // TODO(b/146892613): we're not uploading this at the moment. Should we be? // If we don't, we can probably remove this step...? SetUploadProgress(UploadStep.CreateTilt, 0); - await CreateTiltForUploadAsync(fileInfo); + var thumbnail = await CreateTiltForUploadAsync(fileInfo); token.ThrowIfCancellationRequested(); // Create a copy of the .tilt file in tempUploadDir. string tempTiltPath = Path.Combine(tempUploadDir, "sketch.tilt"); File.Copy(fileInfo.FullPath, tempTiltPath); - // Collect files into a .zip file, including the .tilt file. + // Save thumbnail as a png to temp path + string tempThumbnailPath = Path.Combine(tempUploadDir, "thumbnail.png"); + File.WriteAllBytes(tempThumbnailPath, thumbnail); + + // Collect files into a .zip file, including the .tilt file and thumbnail string zipName = Path.Combine(tempUploadDir, "archive.zip"); - var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath); + var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath).Append(tempThumbnailPath); await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); var service = new IcosaService(App.Instance.IcosaToken); var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); var response = await service.CreateModel( - fileInfo.HumanName, zipName, progress, token, options, tempUploadDir); + zipName, progress, token, options, tempUploadDir); // TODO(b/146892613): return the UID and stick it into the .tilt file? // Or do we not care since we aren't recording provenance and remixing From 28299ac1075b1e382d2ab0b2fa56f439276fe396 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Fri, 24 May 2024 13:24:05 +0100 Subject: [PATCH 029/105] Icosa login, logout and upload API endpoints --- .../Scripts/API/ApiMethods.GlobalCommands.cs | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs index b415e7aec8..e3b1455ffd 100644 --- a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs +++ b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs @@ -13,6 +13,9 @@ // limitations under the License. using System.IO; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Client; +using UnityEngine; namespace TiltBrush { @@ -40,13 +43,45 @@ public static void SaveNew() SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, 1); } - // TODO - // [ApiEndpoint("upload", "Saves the current scene and uploads it to Poly/Icosa")] - // public static void SaveAndUpload() - // { - // var rEnum = SketchControlsScript.GlobalCommands.SaveAndUpload; - // SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum); - // } + [ApiEndpoint("icosa.login", "Login to the Icosa Gallery")] + public static void IcosaLogin(string username, string password) + { + var config = new Configuration(); + var loginApi = new LoginApi(App.ICOSA_API_URL); + loginApi.Configuration = config; + var token = loginApi.LoginLoginPost(username, password); + App.Instance.IcosaToken = token.AccessToken; + + if (token != null) + { + var usersApi = new UsersApi(App.ICOSA_API_URL); + config = new Configuration { AccessToken = App.Instance.IcosaToken }; + usersApi.Configuration = config; + var userData = usersApi.GetUsersMeUsersMeGet(); + + if (userData != null) + { + App.IcosaUserName = userData.Displayname; + App.IcosaUserId = userData.Id; + } + } + } + + [ApiEndpoint("icosa.logout", "Logout of the Icosa Gallery")] + public static void IcosaLogout() + { + App.IcosaUserName = null; + App.IcosaUserId = null; + App.IcosaUserIcon = null; + App.Instance.IcosaToken = null; + } + + [ApiEndpoint("icosa.upload", "Uploads it to the Icosa Gallery")] + public static void IcosaUpload() + { + var rEnum = SketchControlsScript.GlobalCommands.UploadToGenericCloud; + SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, (int)Cloud.Icosa); + } [ApiEndpoint("export.all", "Exports all the scenes in the users's sketch folder")] public static void ExportAll() From efd7f2c9f39b377646fda224de6e2313715a39f0 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 26 May 2024 19:35:17 +0100 Subject: [PATCH 030/105] Use the new GLTF exporter for editor environment exports --- Assets/Editor/GlTF_EditorExporter.cs | 135 ++++++++++-------- .../Scripts/Export/OpenBrushExportPlugin.cs | 6 +- 2 files changed, 77 insertions(+), 64 deletions(-) diff --git a/Assets/Editor/GlTF_EditorExporter.cs b/Assets/Editor/GlTF_EditorExporter.cs index f86dff32f8..96db1914c1 100644 --- a/Assets/Editor/GlTF_EditorExporter.cs +++ b/Assets/Editor/GlTF_EditorExporter.cs @@ -22,6 +22,7 @@ using Newtonsoft.Json; using UnityEditor; using UnityEngine; +using UnityGLTF; using UObject = UnityEngine.Object; namespace TiltBrush @@ -128,75 +129,85 @@ private static bool ExportBrushStrokes_Enabled() [MenuItem("Open Brush/glTF/Export Environments to glTF", false, 4)] private static void ExportEnvironments() { -#if !GAMEOBJ_EXPORT_TO_GLTF - Debug.LogError("Enable the define and fix up the code"); -#else - // Save the original RenderSettings - Environment.RenderSettingsLite originalRenderSettings = Environment.GetRenderSettings(); - - // Clear out the existing environments directory to do a clean export - string projectPath = Path.GetDirectoryName(Application.dataPath); - string environmentExportPath = Path.Combine(projectPath, - ExportUtils.kProjectRelativeEnvironmentExportRoot); - try { - Directory.Delete(environmentExportPath, recursive: true); - } catch (DirectoryNotFoundException) { - // It's okay if this directory doesn't exist yet as it will be created later. - } + // Save the original RenderSettings + Environment.RenderSettingsLite originalRenderSettings = Environment.GetRenderSettings(); - // Clear out the existing textures directory to do a clean export - string textureExportPath = Path.Combine(projectPath, - ExportUtils.kProjectRelativeTextureExportRoot); - try { - Directory.Delete(textureExportPath, recursive: true); - } catch (DirectoryNotFoundException) { - // It's okay if this directory doesn't exist yet as it will be created later. - } - if (!FileUtils.InitializeDirectoryWithUserError( - textureExportPath, "Failed to export, can't create texture export directory")) { - return; - } + // Clear out the existing environments directory to do a clean export + string projectPath = Path.GetDirectoryName(Application.dataPath); + string environmentExportPath = Path.Combine(projectPath, + ExportUtils.kProjectRelativeEnvironmentExportRoot); + try + { + Directory.Delete(environmentExportPath, recursive: true); + } + catch (DirectoryNotFoundException) + { + // It's okay if this directory doesn't exist yet as it will be created later. + } - // Get the environment - TiltBrushManifest manifest = AssetDatabase.LoadAssetAtPath("Assets/Manifest.asset"); - foreach (Environment env in manifest.Environments) { - // Copy over the RenderSettings - Environment.SetRenderSettings(env.m_RenderSettings); - - // Set up the environment - string envGuid = env.m_Guid.ToString("D"); - Debug.LogFormat("Exporting environment: {0}", env.m_RenderSettings.m_EnvironmentPrefab); - GameObject envPrefab = Resources.Load(env.m_RenderSettings.m_EnvironmentPrefab); - GameObject envGameObject = UObject.Instantiate(envPrefab); - envGameObject.name = envGuid; - - // Hide game objects that don't get exported to Poly. - foreach (Transform child in envGameObject.transform) { - if (SceneSettings.ExcludeFromPolyExport(child)) { - child.gameObject.SetActive(false); - } - } + // Clear out the existing textures directory to do a clean export + string textureExportPath = Path.Combine(projectPath, + ExportUtils.kProjectRelativeTextureExportRoot); + try + { + Directory.Delete(textureExportPath, recursive: true); + } + catch (DirectoryNotFoundException) + { + // It's okay if this directory doesn't exist yet as it will be created later. + } + if (!FileUtils.InitializeDirectoryWithUserError( + textureExportPath, "Failed to export, can't create texture export directory")) + { + return; + } - // Set up the environment export directory - string directoryName = Path.Combine(environmentExportPath, envGuid); - if (!FileUtils.InitializeDirectoryWithUserError( - directoryName, "Failed to export, can't create environment export directory")) { - return; - } + // Get the environment + TiltBrushManifest manifest = AssetDatabase.LoadAssetAtPath("Assets/Manifest.asset"); + foreach (Environment env in manifest.Environments) + { + // Copy over the RenderSettings + Environment.SetRenderSettings(env.m_RenderSettings); + + // Set up the environment + string envGuid = env.m_Guid.ToString("D"); + Debug.LogFormat("Exporting environment: {0}", env.m_RenderSettings.m_EnvironmentPrefab); + GameObject envPrefab = Resources.Load(env.m_RenderSettings.m_EnvironmentPrefab); + GameObject envGameObject = UObject.Instantiate(envPrefab); + envGameObject.name = envGuid; + + // Hide game objects that don't get exported to Poly. + foreach (Transform child in envGameObject.transform) + { + if (SceneSettings.ExcludeFromPolyExport(child)) + { + child.gameObject.SetActive(false); + } + } - string basename = FileUtils.SanitizeFilename(envGameObject.name); - string gltfName = Path.Combine(directoryName, basename + ".gltf"); + // Set up the environment export directory + string directoryName = Path.Combine(environmentExportPath, envGuid); + if (!FileUtils.InitializeDirectoryWithUserError( + directoryName, "Failed to export, can't create environment export directory")) + { + return; + } - var exporter = new ExportGlTF(); - exporter.ExportGameObject(envGameObject, gltfName, env); + string basename = FileUtils.SanitizeFilename(envGameObject.name); - // DestroyImmediate is required because editor mode never runs object garbage collection. - UObject.DestroyImmediate(envGameObject); - } + var settings = GLTFSettings.GetOrCreateSettings(); + settings.UseMainCameraVisibility = false; + var context = new ExportContext(); + var unityGltfexporter = new GLTFSceneExporter(envGameObject.transform, context); + unityGltfexporter.SaveGLTFandBin(directoryName, basename + ".gltf"); + + // DestroyImmediate is required because editor mode never runs object garbage collection. + UObject.DestroyImmediate(envGameObject); + } + + // Restore the original RenderSettings + Environment.SetRenderSettings(originalRenderSettings); - // Restore the original RenderSettings - Environment.SetRenderSettings(originalRenderSettings); -#endif } private static Dictionary GetBrushes() diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index cc670471bd..0e120ba4f0 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -34,7 +34,7 @@ public class OpenBrushExportPluginConfig : GLTFExportPluginContext public override void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { - SelectionManager.m_Instance.ClearActiveSelection(); + SelectionManager.m_Instance?.ClearActiveSelection(); _meshesToBatches = new Dictionary(); } @@ -122,7 +122,9 @@ public override void BeforeNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfR BeforeLayerExport(transform); } - if (!App.UserConfig.Export.KeepStrokes && App.UserConfig.Export.ExportStrokeMetadata) + if (!Application.isPlaying && + !App.UserConfig.Export.KeepStrokes && + App.UserConfig.Export.ExportStrokeMetadata) { // We'll need a way to find the batch for each mesh later var batch = transform.GetComponent(); From a061a33dfb63174d63f646d7a380ecc9e4427b92 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 28 May 2024 12:01:19 +0100 Subject: [PATCH 031/105] Helpful comments --- Assets/Scripts/Export/Export.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/Scripts/Export/Export.cs b/Assets/Scripts/Export/Export.cs index 7f08b8acf3..ebb145d9ae 100644 --- a/Assets/Scripts/Export/Export.cs +++ b/Assets/Scripts/Export/Export.cs @@ -266,6 +266,7 @@ public static void ExportScene() if (App.PlatformConfig.EnableExportGlb && IsExportEnabled("glb")) { + // Legacy GLTF export string extension = App.Config.m_EnableGlbVersion2 ? "glb" : "glb1"; int gltfVersion = App.Config.m_EnableGlbVersion2 ? 2 : 1; filename = MakeExportPath(parent, basename, extension); @@ -292,6 +293,7 @@ public static void ExportScene() if (App.PlatformConfig.EnableExportGlb && IsExportEnabled("newglb")) { + // 'New' GLTF export using UnityGLTF string extension = "glb"; using (var unused = new AutoTimer("glb export")) { From 038aa4f0a423fbff9c06a824b674f5aaee95f052 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 28 May 2024 15:01:18 +0100 Subject: [PATCH 032/105] Cube map rendering editor utility --- Assets/Editor/RenderCubeMap.cs | 69 +++++++++++++++++++++++++++++ Assets/Editor/RenderCubeMap.cs.meta | 11 +++++ 2 files changed, 80 insertions(+) create mode 100644 Assets/Editor/RenderCubeMap.cs create mode 100644 Assets/Editor/RenderCubeMap.cs.meta diff --git a/Assets/Editor/RenderCubeMap.cs b/Assets/Editor/RenderCubeMap.cs new file mode 100644 index 0000000000..e2a9243a7b --- /dev/null +++ b/Assets/Editor/RenderCubeMap.cs @@ -0,0 +1,69 @@ +using UnityEngine; +using UnityEditor; +using System.IO; + +public class RenderCubeMap : EditorWindow +{ + [SerializeField] + static int faceSize = 1024; + + [MenuItem("Open Brush/RenderCubeMap", false, 11)] + static void Init() + { + var cam = Camera.main; + var camPos = cam.transform.position; + var camRot = cam.transform.rotation; + + // cam.fieldOfView = 45; + // cam.farClipPlane = 4000; + // cam.allowMSAA = false; + + cam.transform.position = new Vector3(0, 10, 0); + cam.transform.rotation = Quaternion.identity; + + RenderToCubeMap(Camera.main); + + cam.transform.position = camPos; + cam.transform.rotation = camRot; + } + + static void RenderToCubeMap(Camera Cam) + { + Cubemap cubemap = new Cubemap(faceSize, TextureFormat.ARGB32, false); + + var cubeSavePath = Application.dataPath + "/cube" + ".png"; + + Cam.RenderToCubemap(cubemap, 63); + Texture2D flattenedTexture = new Texture2D(faceSize * 4, faceSize * 3, TextureFormat.ARGB32, false); + for (int i = 0; i < 6; i++) + { + int x = 0, y = 0; + switch (i) + { + case 0: x = faceSize; y = faceSize * 2; break; // Top + case 1: x = faceSize; y = 0; break; // Bottom + case 2: x = faceSize * 3; y = faceSize; break; // Right + case 3: x = 0; y = faceSize; break; // Left + case 4: x = faceSize; y = faceSize; break; // Front + case 5: x = faceSize * 2; y = faceSize; break; // Back + } + Graphics.CopyTexture(cubemap, i, 0, 0, 0, faceSize, faceSize, flattenedTexture, 0, 0, x, y); + } + + byte[] bytes = flattenedTexture.EncodeToPNG(); + DestroyImmediate(flattenedTexture, true); + File.WriteAllBytes(cubeSavePath, bytes); + + // var tex2DSavePath = Application.dataPath + "/360tex" + ".jpg"; + // renderTexCube.ConvertToEquirect(renderTex2D); + DestroyImmediate(cubemap, true); + // Texture2D tex2d = new Texture2D(faceSize, 2048, TextureFormat.RGB24,false); + // RenderTexture.active = renderTex2D; + // tex2d.ReadPixels(new Rect(0,0,renderTex2D.width, renderTex2D.height),0,0); + // DestroyImmediate(renderTex2D, true); + // tex2d.Apply(); + // bytes = tex2d.EncodeToJPG(); + // DestroyImmediate(tex2d, true); + // File.WriteAllBytes(tex2DSavePath, bytes); + } +} diff --git a/Assets/Editor/RenderCubeMap.cs.meta b/Assets/Editor/RenderCubeMap.cs.meta new file mode 100644 index 0000000000..7f77bd4a48 --- /dev/null +++ b/Assets/Editor/RenderCubeMap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d39ae5f85ad4334e8545bab384d79c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 2042cb6452bd49a2eb2261fcdd8595ed0f673133 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 28 May 2024 20:53:55 +0100 Subject: [PATCH 033/105] Optional environment export --- .../EnvironmentPrefabs/AmbientDust.prefab | 166 ++- .../EnvironmentPrefabs/AmbientDustDim.prefab | 166 ++- .../EnvironmentPrefabs/AmbientGrid.prefab | 28 +- .../AmbientGrid_Blue.prefab | 28 +- .../EnvironmentPrefabs/DressForm.prefab | 233 +++- .../EnvironmentPrefabs/NightSky.prefab | 214 +++- .../EnvironmentPrefabs/Pedestal.prefab | 222 +++- .../EnvironmentPrefabs/Snowman.prefab | 361 +++++- .../Resources/EnvironmentPrefabs/Space.prefab | 1083 ++++++++++++++--- .../EnvironmentPrefabs/Standard.prefab | 214 +++- Assets/Scenes/Main.unity | 1 + Assets/Scripts/Config.cs | 2 + Assets/Scripts/Export/Export.cs | 21 +- Assets/Scripts/UserConfig.cs | 11 + Assets/ThirdParty/GlTF/GlTF_Globals.cs | 2 + ProjectSettings/TagManager.asset | 2 +- 16 files changed, 2406 insertions(+), 348 deletions(-) diff --git a/Assets/Resources/EnvironmentPrefabs/AmbientDust.prefab b/Assets/Resources/EnvironmentPrefabs/AmbientDust.prefab index c08643156b..40a8cedd0d 100644 --- a/Assets/Resources/EnvironmentPrefabs/AmbientDust.prefab +++ b/Assets/Resources/EnvironmentPrefabs/AmbientDust.prefab @@ -9,7 +9,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 472934} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientDust m_TagString: Untagged m_Icon: {fileID: 0} @@ -26,6 +26,7 @@ Transform: 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: - {fileID: 483582} m_Father: {fileID: 0} @@ -42,7 +43,7 @@ GameObject: - component: {fileID: 483582} - component: {fileID: 19891146} - component: {fileID: 19910528} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientDust m_TagString: Untagged m_Icon: {fileID: 0} @@ -59,6 +60,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 472934} m_RootOrder: 0 @@ -70,19 +72,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 119290} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -632,6 +634,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1409,6 +1412,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1508,7 +1512,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2155,6 +2159,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2319,8 +2379,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3532,19 +3645,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -3718,17 +3832,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -4679,9 +4796,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4694,6 +4814,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4707,6 +4828,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -4723,9 +4845,15 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 diff --git a/Assets/Resources/EnvironmentPrefabs/AmbientDustDim.prefab b/Assets/Resources/EnvironmentPrefabs/AmbientDustDim.prefab index 8945aa7c80..4ad1dbfc8f 100644 --- a/Assets/Resources/EnvironmentPrefabs/AmbientDustDim.prefab +++ b/Assets/Resources/EnvironmentPrefabs/AmbientDustDim.prefab @@ -9,7 +9,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 472934} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientDustDim m_TagString: Untagged m_Icon: {fileID: 0} @@ -26,6 +26,7 @@ Transform: 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: - {fileID: 427470} m_Father: {fileID: 0} @@ -42,7 +43,7 @@ GameObject: - component: {fileID: 427470} - component: {fileID: 19846126} - component: {fileID: 19992888} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientDustDim m_TagString: Untagged m_Icon: {fileID: 0} @@ -59,6 +60,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 472934} m_RootOrder: 0 @@ -70,19 +72,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 124756} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -632,6 +634,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1409,6 +1412,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1508,7 +1512,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2155,6 +2159,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2319,8 +2379,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3532,19 +3645,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -3718,17 +3832,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -4679,9 +4796,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4694,6 +4814,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4707,6 +4828,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -4723,9 +4845,15 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 diff --git a/Assets/Resources/EnvironmentPrefabs/AmbientGrid.prefab b/Assets/Resources/EnvironmentPrefabs/AmbientGrid.prefab index 7f2b081426..070364abce 100644 --- a/Assets/Resources/EnvironmentPrefabs/AmbientGrid.prefab +++ b/Assets/Resources/EnvironmentPrefabs/AmbientGrid.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 441654} - component: {fileID: 3328860} - component: {fileID: 2387148} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGrid m_TagString: Untagged m_Icon: {fileID: 0} @@ -28,6 +28,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 5.3, y: 5, z: -40} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 462152} m_RootOrder: 0 @@ -51,9 +52,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +69,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -77,6 +82,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &117590 GameObject: m_ObjectHideFlags: 0 @@ -86,7 +92,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 455556} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGridAudio m_TagString: Untagged m_Icon: {fileID: 0} @@ -103,6 +109,7 @@ Transform: 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: 426446} m_RootOrder: 0 @@ -117,7 +124,7 @@ GameObject: m_Component: - component: {fileID: 417594} - component: {fileID: 114616253018658896} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGridSmall m_TagString: Untagged m_Icon: {fileID: 0} @@ -134,6 +141,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 435688} m_Father: {fileID: 426446} @@ -164,7 +172,7 @@ GameObject: m_Component: - component: {fileID: 426446} - component: {fileID: 11449142} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGrid m_TagString: Untagged m_Icon: {fileID: 0} @@ -181,6 +189,7 @@ Transform: 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: - {fileID: 455556} - {fileID: 462152} @@ -215,7 +224,7 @@ GameObject: - component: {fileID: 435688} - component: {fileID: 3364224} - component: {fileID: 2389974} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGrid m_TagString: Untagged m_Icon: {fileID: 0} @@ -232,6 +241,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 5.3, y: 5, z: -40} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 417594} m_RootOrder: 0 @@ -255,9 +265,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -269,6 +282,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -281,6 +295,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &185908 GameObject: m_ObjectHideFlags: 0 @@ -291,7 +306,7 @@ GameObject: m_Component: - component: {fileID: 462152} - component: {fileID: 114356150969872122} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGridBig m_TagString: Untagged m_Icon: {fileID: 0} @@ -308,6 +323,7 @@ Transform: 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: - {fileID: 441654} m_Father: {fileID: 426446} diff --git a/Assets/Resources/EnvironmentPrefabs/AmbientGrid_Blue.prefab b/Assets/Resources/EnvironmentPrefabs/AmbientGrid_Blue.prefab index 050938b9d6..7bc8a56147 100644 --- a/Assets/Resources/EnvironmentPrefabs/AmbientGrid_Blue.prefab +++ b/Assets/Resources/EnvironmentPrefabs/AmbientGrid_Blue.prefab @@ -9,7 +9,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 411922} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGridAudio m_TagString: Untagged m_Icon: {fileID: 0} @@ -26,6 +26,7 @@ Transform: 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: 426446} m_RootOrder: 0 @@ -41,7 +42,7 @@ GameObject: - component: {fileID: 416930} - component: {fileID: 3382168} - component: {fileID: 2355276} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGrid m_TagString: Untagged m_Icon: {fileID: 0} @@ -58,6 +59,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 5.3, y: 5, z: -40} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 452114} m_RootOrder: 0 @@ -81,9 +83,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -95,6 +100,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -107,6 +113,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &146818 GameObject: m_ObjectHideFlags: 0 @@ -117,7 +124,7 @@ GameObject: m_Component: - component: {fileID: 426446} - component: {fileID: 11475594} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGrid_Blue m_TagString: Untagged m_Icon: {fileID: 0} @@ -134,6 +141,7 @@ Transform: 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: - {fileID: 411922} - {fileID: 424610} @@ -168,7 +176,7 @@ GameObject: - component: {fileID: 436200} - component: {fileID: 3327902} - component: {fileID: 2365078} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGrid m_TagString: Untagged m_Icon: {fileID: 0} @@ -185,6 +193,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 5.3, y: 5, z: -40} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 424610} m_RootOrder: 0 @@ -208,9 +217,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -222,6 +234,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -234,6 +247,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &182776 GameObject: m_ObjectHideFlags: 0 @@ -244,7 +258,7 @@ GameObject: m_Component: - component: {fileID: 424610} - component: {fileID: 114430266970355340} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGridBig m_TagString: Untagged m_Icon: {fileID: 0} @@ -261,6 +275,7 @@ Transform: 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: - {fileID: 436200} m_Father: {fileID: 426446} @@ -291,7 +306,7 @@ GameObject: m_Component: - component: {fileID: 452114} - component: {fileID: 114719356951609022} - m_Layer: 0 + m_Layer: 25 m_Name: AmbientGridSmall m_TagString: Untagged m_Icon: {fileID: 0} @@ -308,6 +323,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 416930} m_Father: {fileID: 426446} diff --git a/Assets/Resources/EnvironmentPrefabs/DressForm.prefab b/Assets/Resources/EnvironmentPrefabs/DressForm.prefab index 718a0bdd5e..9536e0b258 100644 --- a/Assets/Resources/EnvironmentPrefabs/DressForm.prefab +++ b/Assets/Resources/EnvironmentPrefabs/DressForm.prefab @@ -9,7 +9,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 455888} - m_Layer: 0 + m_Layer: 25 m_Name: DressFormModel m_TagString: Untagged m_Icon: {fileID: 0} @@ -26,6 +26,7 @@ Transform: m_LocalRotation: {x: 0, y: -1, z: 0, w: -0.00000016292068} m_LocalPosition: {x: 0, y: 0, z: 5} m_LocalScale: {x: 0.10808116, y: 0.105000004, z: 0.108081155} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 410190} - {fileID: 471872} @@ -43,7 +44,7 @@ GameObject: - component: {fileID: 447284} - component: {fileID: 3377986} - component: {fileID: 2320842} - m_Layer: 0 + m_Layer: 25 m_Name: GroundUnderside m_TagString: Untagged m_Icon: {fileID: 0} @@ -60,6 +61,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 7 @@ -83,9 +85,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -97,6 +102,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -109,6 +115,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &116648 GameObject: m_ObjectHideFlags: 0 @@ -120,7 +127,7 @@ GameObject: - component: {fileID: 469106} - component: {fileID: 3360860} - component: {fileID: 2365230} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorBackground m_TagString: Untagged m_Icon: {fileID: 0} @@ -137,6 +144,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 0 @@ -160,9 +168,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -174,6 +185,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -186,6 +198,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &128170 GameObject: m_ObjectHideFlags: 0 @@ -197,7 +210,7 @@ GameObject: - component: {fileID: 476664} - component: {fileID: 3344320} - component: {fileID: 2395968} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorForeground m_TagString: Untagged m_Icon: {fileID: 0} @@ -214,6 +227,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.25, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 1 @@ -237,9 +251,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -251,6 +268,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -263,6 +281,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &139798 GameObject: m_ObjectHideFlags: 0 @@ -274,7 +293,7 @@ GameObject: - component: {fileID: 446564} - component: {fileID: 3356250} - component: {fileID: 2381768} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_PropStage m_TagString: Untagged m_Icon: {fileID: 0} @@ -291,6 +310,7 @@ Transform: m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 0, y: -0.25, z: 0} m_LocalScale: {x: 3, y: 0.05, z: 3} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 5 @@ -314,9 +334,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -328,6 +351,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -340,6 +364,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &144802 GameObject: m_ObjectHideFlags: 0 @@ -352,7 +377,7 @@ GameObject: - component: {fileID: 3347644} - component: {fileID: 2317444} - component: {fileID: 114652373354654332} - m_Layer: 0 + m_Layer: 25 m_Name: GroundDisk m_TagString: Untagged m_Icon: {fileID: 0} @@ -369,6 +394,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 6 @@ -392,9 +418,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -406,6 +435,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -418,6 +448,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &114652373354654332 MonoBehaviour: m_ObjectHideFlags: 0 @@ -442,7 +473,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 438498} - m_Layer: 0 + m_Layer: 25 m_Name: DressForm m_TagString: Untagged m_Icon: {fileID: 0} @@ -459,6 +490,7 @@ Transform: 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: - {fileID: 469106} - {fileID: 476664} @@ -483,7 +515,7 @@ GameObject: - component: {fileID: 19891870} - component: {fileID: 19940258} - component: {fileID: 114392821061360260} - m_Layer: 0 + m_Layer: 25 m_Name: DustMotes m_TagString: Untagged m_Icon: {fileID: 0} @@ -500,6 +532,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 3 @@ -511,19 +544,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 159002} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -1073,6 +1106,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1850,6 +1884,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1949,7 +1984,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2596,6 +2631,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2760,8 +2851,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3973,19 +4117,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -4159,17 +4304,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -5120,9 +5268,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5135,6 +5286,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5148,6 +5300,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -5164,11 +5317,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!114 &114392821061360260 MonoBehaviour: @@ -5196,7 +5355,7 @@ GameObject: - component: {fileID: 410190} - component: {fileID: 3318808} - component: {fileID: 2387822} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Form m_TagString: Untagged m_Icon: {fileID: 0} @@ -5213,6 +5372,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071068} m_LocalPosition: {x: -0.051350594, y: 114.1456, z: -0.86714983} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 455888} m_RootOrder: 0 @@ -5236,9 +5396,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5250,6 +5413,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5262,6 +5426,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &184902 GameObject: m_ObjectHideFlags: 0 @@ -5273,7 +5438,7 @@ GameObject: - component: {fileID: 471872} - component: {fileID: 3313096} - component: {fileID: 2354034} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Metal m_TagString: Untagged m_Icon: {fileID: 0} @@ -5290,6 +5455,7 @@ Transform: 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: 455888} m_RootOrder: 1 @@ -5313,9 +5479,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5327,6 +5496,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5339,6 +5509,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &198728 GameObject: m_ObjectHideFlags: 0 @@ -5350,7 +5521,7 @@ GameObject: - component: {fileID: 424542} - component: {fileID: 3365164} - component: {fileID: 2392740} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Spikes m_TagString: Untagged m_Icon: {fileID: 0} @@ -5367,6 +5538,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 0.4, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 438498} m_RootOrder: 4 @@ -5390,9 +5562,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5404,6 +5579,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5416,3 +5592,4 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Resources/EnvironmentPrefabs/NightSky.prefab b/Assets/Resources/EnvironmentPrefabs/NightSky.prefab index 89500ece77..747fa48da0 100644 --- a/Assets/Resources/EnvironmentPrefabs/NightSky.prefab +++ b/Assets/Resources/EnvironmentPrefabs/NightSky.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 485358} - component: {fileID: 3392822} - component: {fileID: 2357274} - m_Layer: 0 + m_Layer: 25 m_Name: GroundUnderside m_TagString: Untagged m_Icon: {fileID: 0} @@ -28,6 +28,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 6 @@ -51,9 +52,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +69,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -77,6 +82,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &118014 GameObject: m_ObjectHideFlags: 0 @@ -88,7 +94,7 @@ GameObject: - component: {fileID: 407766} - component: {fileID: 3311484} - component: {fileID: 2395680} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_StandardStage m_TagString: Untagged m_Icon: {fileID: 0} @@ -105,6 +111,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.25, z: 0} m_LocalScale: {x: 3, y: 0.05, z: 3} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 4 @@ -128,9 +135,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -142,6 +152,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -154,6 +165,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &118944 GameObject: m_ObjectHideFlags: 0 @@ -163,7 +175,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 413308} - m_Layer: 0 + m_Layer: 25 m_Name: NightSky m_TagString: Untagged m_Icon: {fileID: 0} @@ -180,6 +192,7 @@ Transform: 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: - {fileID: 489614} - {fileID: 419628} @@ -202,7 +215,7 @@ GameObject: - component: {fileID: 489614} - component: {fileID: 3393744} - component: {fileID: 2343648} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorBackground m_TagString: Untagged m_Icon: {fileID: 0} @@ -219,6 +232,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 0 @@ -242,9 +256,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -256,6 +273,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -268,6 +286,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &148692 GameObject: m_ObjectHideFlags: 0 @@ -279,7 +298,7 @@ GameObject: - component: {fileID: 419628} - component: {fileID: 3316466} - component: {fileID: 2395074} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorForeground m_TagString: Untagged m_Icon: {fileID: 0} @@ -296,6 +315,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.25, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 1 @@ -319,9 +339,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -333,6 +356,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -345,6 +369,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &150074 GameObject: m_ObjectHideFlags: 0 @@ -357,7 +382,7 @@ GameObject: - component: {fileID: 3332994} - component: {fileID: 2376766} - component: {fileID: 114161264784855924} - m_Layer: 0 + m_Layer: 25 m_Name: GroundDisk m_TagString: Untagged m_Icon: {fileID: 0} @@ -374,6 +399,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 5 @@ -397,9 +423,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -411,6 +440,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -423,6 +453,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &114161264784855924 MonoBehaviour: m_ObjectHideFlags: 0 @@ -449,7 +480,7 @@ GameObject: - component: {fileID: 438128} - component: {fileID: 3318024} - component: {fileID: 2393344} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Spikes m_TagString: Untagged m_Icon: {fileID: 0} @@ -466,6 +497,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 0.5, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 2 @@ -489,9 +521,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -503,6 +538,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -515,6 +551,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &192090 GameObject: m_ObjectHideFlags: 0 @@ -527,7 +564,7 @@ GameObject: - component: {fileID: 19819992} - component: {fileID: 19994808} - component: {fileID: 114322361939089138} - m_Layer: 0 + m_Layer: 25 m_Name: DustMotes m_TagString: Untagged m_Icon: {fileID: 0} @@ -544,6 +581,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 3 @@ -555,19 +593,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 192090} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -1117,6 +1155,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1894,6 +1933,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1993,7 +2033,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2640,6 +2680,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2804,8 +2900,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -4017,19 +4166,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -4203,17 +4353,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -5164,9 +5317,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5179,6 +5335,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5192,6 +5349,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -5208,11 +5366,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!114 &114322361939089138 MonoBehaviour: diff --git a/Assets/Resources/EnvironmentPrefabs/Pedestal.prefab b/Assets/Resources/EnvironmentPrefabs/Pedestal.prefab index 56ae659c6b..c2e4a1e79f 100644 --- a/Assets/Resources/EnvironmentPrefabs/Pedestal.prefab +++ b/Assets/Resources/EnvironmentPrefabs/Pedestal.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 424176} - component: {fileID: 3352532} - component: {fileID: 2382690} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorBackground m_TagString: Untagged m_Icon: {fileID: 0} @@ -28,6 +28,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 0 @@ -51,9 +52,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +69,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -77,6 +82,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &113000 GameObject: m_ObjectHideFlags: 0 @@ -88,7 +94,7 @@ GameObject: - component: {fileID: 457156} - component: {fileID: 3355718} - component: {fileID: 2399512} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Spikes m_TagString: Untagged m_Icon: {fileID: 0} @@ -105,6 +111,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 0.4, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 2 @@ -128,9 +135,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -142,6 +152,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -154,6 +165,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &114334 GameObject: m_ObjectHideFlags: 0 @@ -166,7 +178,7 @@ GameObject: - component: {fileID: 19893150} - component: {fileID: 19979650} - component: {fileID: 114157723204081412} - m_Layer: 0 + m_Layer: 25 m_Name: DustMotes m_TagString: Untagged m_Icon: {fileID: 0} @@ -183,6 +195,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 4 @@ -194,19 +207,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 114334} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -756,6 +769,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1533,6 +1547,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1632,7 +1647,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2279,6 +2294,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2443,8 +2514,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3656,19 +3780,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -3842,17 +3967,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -4803,9 +4931,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4818,6 +4949,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4831,6 +4963,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -4847,11 +4980,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!114 &114157723204081412 MonoBehaviour: @@ -4880,7 +5019,7 @@ GameObject: - component: {fileID: 3392160} - component: {fileID: 2316744} - component: {fileID: 114969557637603980} - m_Layer: 0 + m_Layer: 25 m_Name: GroundDisk m_TagString: Untagged m_Icon: {fileID: 0} @@ -4897,6 +5036,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 6 @@ -4920,9 +5060,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4934,6 +5077,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4946,6 +5090,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &114969557637603980 MonoBehaviour: m_ObjectHideFlags: 0 @@ -4972,7 +5117,7 @@ GameObject: - component: {fileID: 486512} - component: {fileID: 3388836} - component: {fileID: 2301376} - m_Layer: 0 + m_Layer: 25 m_Name: Pedestal m_TagString: Untagged m_Icon: {fileID: 0} @@ -4989,6 +5134,7 @@ Transform: m_LocalRotation: {x: -0.70751375, y: 0, z: 0, w: 0.7066996} m_LocalPosition: {x: 0, y: 0.21699998, z: 5} m_LocalScale: {x: 23.360954, y: 23.360958, z: 18.2} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 3 @@ -5012,9 +5158,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5026,6 +5175,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5038,6 +5188,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &178168 GameObject: m_ObjectHideFlags: 0 @@ -5049,7 +5200,7 @@ GameObject: - component: {fileID: 460940} - component: {fileID: 3396076} - component: {fileID: 2319138} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorForeground m_TagString: Untagged m_Icon: {fileID: 0} @@ -5066,6 +5217,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.25, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 1 @@ -5089,9 +5241,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5103,6 +5258,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5115,6 +5271,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &191288 GameObject: m_ObjectHideFlags: 0 @@ -5126,7 +5283,7 @@ GameObject: - component: {fileID: 456760} - component: {fileID: 3337052} - component: {fileID: 2351882} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_PropStage m_TagString: Untagged m_Icon: {fileID: 0} @@ -5143,6 +5300,7 @@ Transform: m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 0, y: -0.25, z: 0} m_LocalScale: {x: 3, y: 0.05, z: 3} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 5 @@ -5166,9 +5324,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5180,6 +5341,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5192,6 +5354,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &193958 GameObject: m_ObjectHideFlags: 0 @@ -5203,7 +5366,7 @@ GameObject: - component: {fileID: 492302} - component: {fileID: 3314278} - component: {fileID: 2342112} - m_Layer: 0 + m_Layer: 25 m_Name: GroundUnderside m_TagString: Untagged m_Icon: {fileID: 0} @@ -5220,6 +5383,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 463696} m_RootOrder: 7 @@ -5243,9 +5407,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5257,6 +5424,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5269,6 +5437,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &195766 GameObject: m_ObjectHideFlags: 0 @@ -5278,7 +5447,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 463696} - m_Layer: 0 + m_Layer: 25 m_Name: Pedestal m_TagString: Untagged m_Icon: {fileID: 0} @@ -5295,6 +5464,7 @@ Transform: 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: - {fileID: 424176} - {fileID: 460940} diff --git a/Assets/Resources/EnvironmentPrefabs/Snowman.prefab b/Assets/Resources/EnvironmentPrefabs/Snowman.prefab index 5b35d1acaf..b12aba710f 100644 --- a/Assets/Resources/EnvironmentPrefabs/Snowman.prefab +++ b/Assets/Resources/EnvironmentPrefabs/Snowman.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 445372} - component: {fileID: 3345122} - component: {fileID: 2380094} - m_Layer: 0 + m_Layer: 25 m_Name: HemiSphere m_TagString: Untagged m_Icon: {fileID: 0} @@ -28,6 +28,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -129, z: 0} m_LocalScale: {x: 975.3, y: 1073.689, z: 975.29956} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402394} m_RootOrder: 4 @@ -51,9 +52,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +69,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -77,6 +82,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &121900 GameObject: m_ObjectHideFlags: 0 @@ -86,7 +92,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 402394} - m_Layer: 0 + m_Layer: 25 m_Name: Snowman m_TagString: Untagged m_Icon: {fileID: 0} @@ -103,6 +109,7 @@ Transform: 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: - {fileID: 485540} - {fileID: 445238} @@ -124,7 +131,7 @@ GameObject: - component: {fileID: 445238} - component: {fileID: 3317600} - component: {fileID: 2369508} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Snowman m_TagString: Untagged m_Icon: {fileID: 0} @@ -141,6 +148,7 @@ Transform: m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.3826833} m_LocalPosition: {x: 0, y: 0, z: 5} m_LocalScale: {x: 0.069999985, y: 0.07, z: 0.06999999} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402394} m_RootOrder: 1 @@ -164,9 +172,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -178,6 +189,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -190,6 +202,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &157716 GameObject: m_ObjectHideFlags: 0 @@ -201,7 +214,7 @@ GameObject: - component: {fileID: 485540} - component: {fileID: 3337880} - component: {fileID: 2310628} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Ground m_TagString: Untagged m_Icon: {fileID: 0} @@ -218,6 +231,7 @@ Transform: m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.3826833} m_LocalPosition: {x: 0.06391762, y: 94.89275, z: 5.016025} m_LocalScale: {x: 0.069999985, y: 0.07, z: 0.06999999} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402394} m_RootOrder: 0 @@ -241,9 +255,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -255,6 +272,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -267,6 +285,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &172598 GameObject: m_ObjectHideFlags: 0 @@ -278,7 +297,7 @@ GameObject: - component: {fileID: 458956} - component: {fileID: 19847152} - component: {fileID: 19985736} - m_Layer: 0 + m_Layer: 25 m_Name: Snow m_TagString: Untagged m_Icon: {fileID: 0} @@ -295,6 +314,7 @@ Transform: m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.3826833} m_LocalPosition: {x: 0.030935913, y: 49.21, z: 4.969064} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402394} m_RootOrder: 2 @@ -306,19 +326,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 172598} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 3 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -814,6 +834,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 10000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1573,6 +1594,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1672,7 +1694,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 0 + rowMode: 0 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2319,6 +2341,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 1 x: @@ -2528,8 +2606,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3741,19 +3872,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -3927,17 +4059,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -4888,9 +5023,12 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4903,6 +5041,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4916,6 +5055,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.2 @@ -4932,11 +5072,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &186758 GameObject: @@ -4949,7 +5095,7 @@ GameObject: - component: {fileID: 447626} - component: {fileID: 19807346} - component: {fileID: 19973004} - m_Layer: 0 + m_Layer: 25 m_Name: SnowFiller m_TagString: Untagged m_Icon: {fileID: 0} @@ -4966,6 +5112,7 @@ Transform: m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.3826833} m_LocalPosition: {x: 0.030935831, y: 49.21, z: 4.969064} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402394} m_RootOrder: 3 @@ -4977,19 +5124,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 186758} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 3 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -5485,6 +5632,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 10000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -6244,6 +6392,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -6343,7 +6492,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 0 + rowMode: 0 sprites: - sprite: {fileID: 0} flipU: 0 @@ -6990,6 +7139,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 1 x: @@ -7199,8 +7404,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -8412,19 +8670,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -8598,17 +8857,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -9559,9 +9821,12 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -9574,6 +9839,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -9587,6 +9853,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.2 @@ -9603,11 +9870,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &197206 GameObject: @@ -9620,7 +9893,7 @@ GameObject: - component: {fileID: 445918} - component: {fileID: 3309060} - component: {fileID: 2384180} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_GroundBackfaces m_TagString: Untagged m_Icon: {fileID: 0} @@ -9637,6 +9910,7 @@ Transform: m_LocalRotation: {x: 0, y: 0.92387956, z: 0, w: 0.3826833} m_LocalPosition: {x: 0.06391762, y: 94.89275, z: 5.016025} m_LocalScale: {x: 0.06999998, y: 0.07, z: 0.06999999} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402394} m_RootOrder: 5 @@ -9660,9 +9934,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -9674,6 +9951,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -9686,3 +9964,4 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Resources/EnvironmentPrefabs/Space.prefab b/Assets/Resources/EnvironmentPrefabs/Space.prefab index eac8183d69..c24d4a0ccc 100644 --- a/Assets/Resources/EnvironmentPrefabs/Space.prefab +++ b/Assets/Resources/EnvironmentPrefabs/Space.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 434888} - component: {fileID: 3368352} - component: {fileID: 2327474} - m_Layer: 0 + m_Layer: 25 m_Name: Moon m_TagString: Untagged m_Icon: {fileID: 0} @@ -28,6 +28,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 13, z: 5} m_LocalScale: {x: 3.6417453, y: 3.6417463, z: 3.6417463} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 0 @@ -51,9 +52,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -65,6 +69,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -77,6 +82,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &132296 GameObject: m_ObjectHideFlags: 0 @@ -86,7 +92,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 444254} - m_Layer: 0 + m_Layer: 25 m_Name: Space m_TagString: Untagged m_Icon: {fileID: 0} @@ -103,6 +109,7 @@ Transform: 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: - {fileID: 434888} - {fileID: 405328} @@ -126,7 +133,7 @@ GameObject: - component: {fileID: 475926} - component: {fileID: 19837794} - component: {fileID: 19975236} - m_Layer: 0 + m_Layer: 25 m_Name: StarParticles3 m_TagString: Untagged m_Icon: {fileID: 0} @@ -143,6 +150,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 3 @@ -154,19 +162,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 136844} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -716,6 +724,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1493,6 +1502,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 1 mode: 0 timeMode: 0 @@ -1592,7 +1602,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2239,6 +2249,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2403,8 +2469,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3616,19 +3735,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -3802,17 +3922,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -4763,9 +4886,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4778,6 +4904,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4791,6 +4918,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -4807,11 +4935,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &139100 GameObject: @@ -4825,7 +4959,7 @@ GameObject: - component: {fileID: 3362916} - component: {fileID: 6499546} - component: {fileID: 2367046} - m_Layer: 0 + m_Layer: 25 m_Name: Flare (2) m_TagString: Untagged m_Icon: {fileID: 0} @@ -4842,6 +4976,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 2.8080032, y: -0.007659245, z: -21.834728} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 466976} m_RootOrder: 2 @@ -4864,9 +4999,9 @@ MeshCollider: m_Material: {fileID: 0} m_IsTrigger: 0 m_Enabled: 1 - serializedVersion: 3 + serializedVersion: 4 m_Convex: 0 - m_CookingOptions: 14 + m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} --- !u!23 &2367046 MeshRenderer: @@ -4879,9 +5014,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4893,6 +5031,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4905,6 +5044,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &156152 GameObject: m_ObjectHideFlags: 0 @@ -4917,7 +5057,7 @@ GameObject: - component: {fileID: 3394466} - component: {fileID: 6414138} - component: {fileID: 2320484} - m_Layer: 0 + m_Layer: 25 m_Name: Flare m_TagString: Untagged m_Icon: {fileID: 0} @@ -4934,6 +5074,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.63420355, y: -0.058673907, z: -4.690826} m_LocalScale: {x: 8, y: 8, z: 8} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 466976} m_RootOrder: 0 @@ -4956,9 +5097,9 @@ MeshCollider: m_Material: {fileID: 0} m_IsTrigger: 0 m_Enabled: 1 - serializedVersion: 3 + serializedVersion: 4 m_Convex: 0 - m_CookingOptions: 14 + m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} --- !u!23 &2320484 MeshRenderer: @@ -4971,9 +5112,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4985,6 +5129,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4997,6 +5142,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &163134 GameObject: m_ObjectHideFlags: 0 @@ -5008,7 +5154,7 @@ GameObject: - component: {fileID: 445294} - component: {fileID: 19805002} - component: {fileID: 19935524} - m_Layer: 0 + m_Layer: 25 m_Name: StarParticles2 m_TagString: Untagged m_Icon: {fileID: 0} @@ -5025,6 +5171,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 2 @@ -5036,19 +5183,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 163134} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -5580,6 +5727,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -6357,6 +6505,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 1 mode: 0 timeMode: 0 @@ -6456,7 +6605,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -7103,6 +7252,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -7267,8 +7472,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -8480,19 +8738,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -8666,17 +8925,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -9627,9 +9889,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -9642,6 +9907,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -9655,6 +9921,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -9671,11 +9938,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &163938 GameObject: @@ -9688,7 +9961,7 @@ GameObject: - component: {fileID: 430766} - component: {fileID: 19832202} - component: {fileID: 19926748} - m_Layer: 0 + m_Layer: 25 m_Name: StarParticles5 m_TagString: Untagged m_Icon: {fileID: 0} @@ -9705,6 +9978,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 5 @@ -9716,19 +9990,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 163938} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -10278,6 +10552,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -11055,6 +11330,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 1 mode: 0 timeMode: 0 @@ -11154,7 +11430,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -11801,13 +12077,13 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 - ForceModule: + LifetimeByEmitterSpeedModule: enabled: 0 - x: + m_Curve: serializedVersion: 2 - minMaxState: 0 - scalar: 0 - minScalar: 0 + minMaxState: 1 + scalar: 1 + minScalar: 1 maxCurve: serializedVersion: 2 m_Curve: @@ -11815,15 +12091,15 @@ ParticleSystem: time: 0 value: 1 inSlope: 0 - outSlope: 0 + outSlope: -0.8 tangentMode: 0 weightedMode: 0 inWeight: 0.33333334 outWeight: 0.33333334 - serializedVersion: 3 time: 1 - value: 1 - inSlope: 0 + value: 0.2 + inSlope: -0.8 outSlope: 0 tangentMode: 0 weightedMode: 0 @@ -11837,7 +12113,7 @@ ParticleSystem: m_Curve: - serializedVersion: 3 time: 0 - value: 0 + value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -11846,7 +12122,7 @@ ParticleSystem: outWeight: 0.33333334 - serializedVersion: 3 time: 1 - value: 0 + value: 1 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -11856,7 +12132,10 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 - y: + m_Range: {x: 0, y: 1} + ForceModule: + enabled: 0 + x: serializedVersion: 2 minMaxState: 0 scalar: 0 @@ -11909,7 +12188,7 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 - z: + y: serializedVersion: 2 minMaxState: 0 scalar: 0 @@ -11962,22 +12241,128 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 - inWorldSpace: 0 - randomizePerFrame: 0 - ExternalForcesModule: - enabled: 0 - multiplier: 1 - influenceFilter: 0 - influenceMask: - serializedVersion: 2 - m_Bits: 4294967295 - influenceList: [] - ClampVelocityModule: - enabled: 0 - x: + z: serializedVersion: 2 minMaxState: 0 - scalar: 1 + scalar: 0 + minScalar: 0 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + inWorldSpace: 0 + randomizePerFrame: 0 + ExternalForcesModule: + serializedVersion: 2 + enabled: 0 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + influenceFilter: 0 + influenceMask: + serializedVersion: 2 + m_Bits: 4294967295 + influenceList: [] + ClampVelocityModule: + enabled: 0 + x: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 minScalar: 1 maxCurve: serializedVersion: 2 @@ -13178,19 +13563,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -13364,17 +13750,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -14325,9 +14714,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -14340,6 +14732,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -14353,6 +14746,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -14369,11 +14763,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &181288 GameObject: @@ -14387,7 +14787,7 @@ GameObject: - component: {fileID: 3399614} - component: {fileID: 6427118} - component: {fileID: 2322542} - m_Layer: 0 + m_Layer: 25 m_Name: Flare (1) m_TagString: Untagged m_Icon: {fileID: 0} @@ -14404,6 +14804,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0.07428203, w: -0.99723727} m_LocalPosition: {x: 2.4045722, y: 0.0139310835, z: -18.784296} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 466976} m_RootOrder: 1 @@ -14426,9 +14827,9 @@ MeshCollider: m_Material: {fileID: 0} m_IsTrigger: 0 m_Enabled: 1 - serializedVersion: 3 + serializedVersion: 4 m_Convex: 0 - m_CookingOptions: 14 + m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} --- !u!23 &2322542 MeshRenderer: @@ -14441,9 +14842,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -14455,6 +14859,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -14467,6 +14872,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &182958 GameObject: m_ObjectHideFlags: 0 @@ -14478,7 +14884,7 @@ GameObject: - component: {fileID: 405328} - component: {fileID: 19823946} - component: {fileID: 19940492} - m_Layer: 0 + m_Layer: 25 m_Name: StarParticles1 m_TagString: Untagged m_Icon: {fileID: 0} @@ -14495,6 +14901,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 1 @@ -14506,19 +14913,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 182958} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -15050,6 +15457,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -15827,6 +16235,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 1 mode: 0 timeMode: 0 @@ -15926,7 +16335,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -16573,6 +16982,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -16737,8 +17202,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -17950,19 +18468,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -18136,17 +18655,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -19097,9 +19619,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -19112,6 +19637,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -19125,6 +19651,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -19141,11 +19668,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &190718 GameObject: @@ -19158,7 +19691,7 @@ GameObject: - component: {fileID: 482038} - component: {fileID: 19829876} - component: {fileID: 19928512} - m_Layer: 0 + m_Layer: 25 m_Name: StarParticles4 m_TagString: Untagged m_Icon: {fileID: 0} @@ -19175,6 +19708,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 4 @@ -19186,19 +19720,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 190718} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -19748,6 +20282,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -20525,6 +21060,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 1 mode: 0 timeMode: 0 @@ -20624,7 +21160,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -21271,6 +21807,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -21435,8 +22027,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -22648,19 +23293,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -22834,17 +23480,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -23795,9 +24444,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -23810,6 +24462,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -23823,6 +24476,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -23839,11 +24493,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!1 &191648 GameObject: @@ -23858,7 +24518,7 @@ GameObject: - component: {fileID: 2323518} - component: {fileID: 11480676} - component: {fileID: 114121872901766576} - m_Layer: 0 + m_Layer: 25 m_Name: Sun_HighLOD m_TagString: Untagged m_Icon: {fileID: 0} @@ -23875,6 +24535,7 @@ Transform: m_LocalRotation: {x: -0.17884307, y: -0.8438009, z: -0.15270436, w: 0.48238635} m_LocalPosition: {x: -649, y: 412, z: -576} m_LocalScale: {x: 40, y: 40, z: 40} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 459352} - {fileID: 400078} @@ -23901,9 +24562,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -23915,6 +24579,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 1 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -23927,6 +24592,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &11480676 MonoBehaviour: m_ObjectHideFlags: 0 @@ -23975,7 +24641,7 @@ GameObject: - component: {fileID: 438068} - component: {fileID: 19885538} - component: {fileID: 19945510} - m_Layer: 0 + m_Layer: 25 m_Name: StarParticles6 m_TagString: Untagged m_Icon: {fileID: 0} @@ -23992,6 +24658,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 444254} m_RootOrder: 6 @@ -24003,19 +24670,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 195752} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -24547,6 +25214,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -25324,6 +25992,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 1 mode: 0 timeMode: 0 @@ -25423,7 +26092,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -26070,6 +26739,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -26234,8 +26959,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -27447,19 +28225,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -27633,17 +28412,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -28594,9 +29376,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -28609,6 +29394,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -28622,6 +29408,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -28638,9 +29425,15 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 diff --git a/Assets/Resources/EnvironmentPrefabs/Standard.prefab b/Assets/Resources/EnvironmentPrefabs/Standard.prefab index 453c541329..c8a87dc5c4 100644 --- a/Assets/Resources/EnvironmentPrefabs/Standard.prefab +++ b/Assets/Resources/EnvironmentPrefabs/Standard.prefab @@ -12,7 +12,7 @@ GameObject: - component: {fileID: 3356686} - component: {fileID: 2317658} - component: {fileID: 114108179853941988} - m_Layer: 0 + m_Layer: 25 m_Name: GroundDisk m_TagString: Untagged m_Icon: {fileID: 0} @@ -29,6 +29,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 5 @@ -52,9 +53,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -66,6 +70,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -78,6 +83,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!114 &114108179853941988 MonoBehaviour: m_ObjectHideFlags: 0 @@ -104,7 +110,7 @@ GameObject: - component: {fileID: 487626} - component: {fileID: 3311816} - component: {fileID: 2300214} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorBackground m_TagString: Untagged m_Icon: {fileID: 0} @@ -121,6 +127,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 1 @@ -144,9 +151,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -158,6 +168,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -170,6 +181,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &115408 GameObject: m_ObjectHideFlags: 0 @@ -182,7 +194,7 @@ GameObject: - component: {fileID: 19825366} - component: {fileID: 19954150} - component: {fileID: 114527970182492068} - m_Layer: 0 + m_Layer: 25 m_Name: DustMotes m_TagString: Untagged m_Icon: {fileID: 0} @@ -199,6 +211,7 @@ Transform: m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068} m_LocalPosition: {x: 0, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 4 @@ -210,19 +223,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 115408} - serializedVersion: 6 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 1 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 1 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -772,6 +785,7 @@ ParticleSystem: m_RotationOrder: 4 randomizeRotationDirection: 0 maxNumParticles: 1000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1549,6 +1563,7 @@ ParticleSystem: m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: + serializedVersion: 2 enabled: 0 mode: 0 timeMode: 0 @@ -1648,7 +1663,7 @@ ParticleSystem: rowIndex: 0 cycles: 1 uvChannelMask: -1 - randomRow: 1 + rowMode: 1 sprites: - sprite: {fileID: 0} flipU: 0 @@ -2295,6 +2310,62 @@ ParticleSystem: m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 + LifetimeByEmitterSpeedModule: + enabled: 0 + m_Curve: + serializedVersion: 2 + minMaxState: 1 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: -0.8 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0.2 + inSlope: -0.8 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + m_Range: {x: 0, y: 1} ForceModule: enabled: 0 x: @@ -2459,8 +2530,61 @@ ParticleSystem: inWorldSpace: 0 randomizePerFrame: 0 ExternalForcesModule: + serializedVersion: 2 enabled: 0 - multiplier: 1 + multiplierCurve: + serializedVersion: 2 + minMaxState: 0 + scalar: 1 + minScalar: 1 + maxCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + minCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 influenceFilter: 0 influenceMask: serializedVersion: 2 @@ -3672,19 +3796,20 @@ ParticleSystem: range: {x: 0, y: 1} CollisionModule: enabled: 0 - serializedVersion: 3 + serializedVersion: 4 type: 0 collisionMode: 0 colliderForce: 0 multiplyColliderForceByParticleSize: 0 multiplyColliderForceByParticleSpeed: 0 multiplyColliderForceByCollisionAngle: 1 - plane0: {fileID: 0} - plane1: {fileID: 0} - plane2: {fileID: 0} - plane3: {fileID: 0} - plane4: {fileID: 0} - plane5: {fileID: 0} + m_Planes: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} m_Dampen: serializedVersion: 2 minMaxState: 0 @@ -3858,17 +3983,20 @@ ParticleSystem: interiorCollisions: 1 TriggerModule: enabled: 0 - collisionShape0: {fileID: 0} - collisionShape1: {fileID: 0} - collisionShape2: {fileID: 0} - collisionShape3: {fileID: 0} - collisionShape4: {fileID: 0} - collisionShape5: {fileID: 0} + serializedVersion: 2 inside: 1 outside: 0 enter: 0 exit: 0 + colliderQueryMode: 0 radiusScale: 1 + primitives: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} SubModule: serializedVersion: 2 enabled: 0 @@ -4819,9 +4947,12 @@ ParticleSystemRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4834,6 +4965,7 @@ ParticleSystemRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4847,6 +4979,7 @@ ParticleSystemRenderer: m_SortingLayer: 0 m_SortingOrder: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 0 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -4863,11 +4996,17 @@ ParticleSystemRenderer: m_EnableGPUInstancing: 0 m_ApplyActiveColorSpace: 0 m_AllowRoll: 1 + m_FreeformStretching: 0 + m_RotateWithStretchDirection: 1 m_VertexStreams: 0001030405 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 m_MaskInteraction: 0 --- !u!114 &114527970182492068 MonoBehaviour: @@ -4893,7 +5032,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 413308} - m_Layer: 0 + m_Layer: 25 m_Name: Standard m_TagString: Untagged m_Icon: {fileID: 0} @@ -4910,6 +5049,7 @@ Transform: 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: - {fileID: 471000} - {fileID: 487626} @@ -4932,7 +5072,7 @@ GameObject: - component: {fileID: 471000} - component: {fileID: 3369048} - component: {fileID: 2332448} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_StandardStage m_TagString: Untagged m_Icon: {fileID: 0} @@ -4949,6 +5089,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.25, z: 0} m_LocalScale: {x: 3, y: 0.05, z: 3} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 0 @@ -4972,9 +5113,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -4986,6 +5130,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -4998,6 +5143,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &146846 GameObject: m_ObjectHideFlags: 0 @@ -5009,7 +5155,7 @@ GameObject: - component: {fileID: 402588} - component: {fileID: 3342762} - component: {fileID: 2351194} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_FloorForeground m_TagString: Untagged m_Icon: {fileID: 0} @@ -5026,6 +5172,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.25, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 2 @@ -5049,9 +5196,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5063,6 +5213,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5075,6 +5226,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &148614 GameObject: m_ObjectHideFlags: 0 @@ -5086,7 +5238,7 @@ GameObject: - component: {fileID: 411736} - component: {fileID: 3363450} - component: {fileID: 2393132} - m_Layer: 0 + m_Layer: 25 m_Name: Geo_Spikes m_TagString: Untagged m_Icon: {fileID: 0} @@ -5103,6 +5255,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 0.5, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 3 @@ -5126,9 +5279,12 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5140,6 +5296,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5152,6 +5309,7 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} --- !u!1 &195596 GameObject: m_ObjectHideFlags: 0 @@ -5163,7 +5321,7 @@ GameObject: - component: {fileID: 450412} - component: {fileID: 3362728} - component: {fileID: 2379212} - m_Layer: 0 + m_Layer: 25 m_Name: GroundUnderside m_TagString: Untagged m_Icon: {fileID: 0} @@ -5180,6 +5338,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0, y: -0.5, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 413308} m_RootOrder: 6 @@ -5203,9 +5362,12 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5217,6 +5379,7 @@ MeshRenderer: m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 + m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 @@ -5229,3 +5392,4 @@ MeshRenderer: m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index aab449efb0..7e44fd385e 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -3652,6 +3652,7 @@ MonoBehaviour: m_EnableGlbVersion2: 1 m_DebugUpload: 1 m_TbtSettings: {fileID: 11400000, guid: 86eb32a1d469be44a99294025e7795de, type: 2} + m_UnityGLTFSettings: {fileID: 1, guid: 4bcbc565b83276a46b0353b376d2a77d, type: 2} m_ReplaceBrushesOnLoad: 0 m_BrushReplacementMap: [] m_IntroSketchUsdFilename: diff --git a/Assets/Scripts/Config.cs b/Assets/Scripts/Config.cs index 7ff74dcbbb..984338e9b8 100644 --- a/Assets/Scripts/Config.cs +++ b/Assets/Scripts/Config.cs @@ -31,6 +31,7 @@ using System.Linq; using System.Reflection; using UnityEngine; +using UnityGLTF; #if OCULUS_SUPPORTED using Unity.XR.Oculus; @@ -208,6 +209,7 @@ public bool IsMobileHardware [Tooltip("Causes the temporary Upload directory to be kept around (Editor only)")] public bool m_DebugUpload; public TiltBrushToolkit.TbtSettings m_TbtSettings; + public GLTFSettings m_UnityGLTFSettings; [Header("Loading")] public bool m_ReplaceBrushesOnLoad; diff --git a/Assets/Scripts/Export/Export.cs b/Assets/Scripts/Export/Export.cs index ebb145d9ae..3c782d74a8 100644 --- a/Assets/Scripts/Export/Export.cs +++ b/Assets/Scripts/Export/Export.cs @@ -298,14 +298,21 @@ public static void ExportScene() using (var unused = new AutoTimer("glb export")) { OverlayManager.m_Instance.UpdateProgress(0.7f); - var settings = GLTFSettings.GetOrCreateSettings(); - settings.UseMainCameraVisibility = false; - var context = new ExportContext + var settings = App.Config.m_UnityGLTFSettings; + var context = new ExportContext(settings); + + // Beware the two meanings of "layer" in the following code - Unity layers and Open Brush layers + + var layerCanvases = App.Scene.LayerCanvases.Select(x => x.transform).ToList(); + var layerMask = LayerMask.GetMask("MainCanvas"); + if (App.UserConfig.Export.ExportEnvironment) { - ExportLayers = LayerMask.GetMask("MainCanvas") - }; - var layers = App.Scene.LayerCanvases.Select(x => x.transform).ToArray(); - var unityGltfexporter = new GLTFSceneExporter(layers, context); + layerCanvases.Add(App.Instance.m_EnvironmentTransform); + layerMask |= LayerMask.GetMask("Environment"); + } + context.ExportLayers = layerMask; + var unityGltfexporter = new GLTFSceneExporter(layerCanvases.ToArray(), context); + unityGltfexporter.SaveGLB(Path.Combine(parent, $"newglb"), $"{basename}.{extension}"); } progress.CompleteWork("newglb"); diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index 2ab9971445..8b670c540a 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -290,6 +290,7 @@ public bool ExportStrokeTimestamp set { m_ExportStrokeTimestamp = value; } } + // Used by UnityGLTF exporter bool? m_ExportStrokeMetadata; public bool ExportStrokeMetadata { @@ -297,6 +298,7 @@ public bool ExportStrokeMetadata set { m_ExportStrokeMetadata = value; } } + // Used by UnityGLTF exporter bool? m_KeepStrokes; public bool KeepStrokes { @@ -304,6 +306,7 @@ public bool KeepStrokes set { m_KeepStrokes = value; } } + // Used by UnityGLTF exporter bool? m_KeepGroups; public bool KeepGroups { @@ -311,6 +314,14 @@ public bool KeepGroups set { m_KeepGroups = value; } } + // Used by UnityGLTF exporter + private bool? m_ExportEnvironment; + public bool ExportEnvironment + { + get { return m_ExportEnvironment ?? false; } + set { m_ExportEnvironment = value; } + } + private Dictionary m_Formats; [JsonProperty] public Dictionary Formats diff --git a/Assets/ThirdParty/GlTF/GlTF_Globals.cs b/Assets/ThirdParty/GlTF/GlTF_Globals.cs index bf6d84af03..f4542c43d3 100644 --- a/Assets/ThirdParty/GlTF/GlTF_Globals.cs +++ b/Assets/ThirdParty/GlTF/GlTF_Globals.cs @@ -472,6 +472,8 @@ public void WriteNamedObject(string name, object value) { WriteNamedInt(name, longValue); } else if (value is string stringValue) { WriteNamedString(name, stringValue); + } else if (value is float floatValue) { + WriteNamedFloat(name, floatValue); } else if (value is ExportFileReference fileValue) { WriteNamedFile(name, fileValue); } else { diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset index f4a231f125..5068e998fe 100644 --- a/ProjectSettings/TagManager.asset +++ b/ProjectSettings/TagManager.asset @@ -31,7 +31,7 @@ TagManager: - GpuIntersection - PinnedStencilObject - Overlay - - + - Environment - - - From 80fafecefc931702524dc7cf398564a715a178df Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 28 May 2024 20:55:30 +0100 Subject: [PATCH 034/105] Editor utility to export environments as GLB --- Assets/Editor/GlTF_EditorExporter.cs | 55 +++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/Assets/Editor/GlTF_EditorExporter.cs b/Assets/Editor/GlTF_EditorExporter.cs index 96db1914c1..6acc7fcdfb 100644 --- a/Assets/Editor/GlTF_EditorExporter.cs +++ b/Assets/Editor/GlTF_EditorExporter.cs @@ -20,9 +20,13 @@ using System.Text; using JetBrains.Annotations; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SimpleJSON; using UnityEditor; +using UnityEditor.ShaderGraph.Serialization; using UnityEngine; using UnityGLTF; +using UnityGLTF.Extensions; using UObject = UnityEngine.Object; namespace TiltBrush @@ -164,6 +168,9 @@ private static void ExportEnvironments() // Get the environment TiltBrushManifest manifest = AssetDatabase.LoadAssetAtPath("Assets/Manifest.asset"); + JToken colorToJArray(Color c) => JToken.FromObject(new { c.r, c.g, c.b, c.a }); + JToken vector3ToJArray(Vector3 c) => JToken.FromObject(new { c.x, c.y, c.z }); + var envJson = new JObject(); foreach (Environment env in manifest.Environments) { // Copy over the RenderSettings @@ -171,6 +178,48 @@ private static void ExportEnvironments() // Set up the environment string envGuid = env.m_Guid.ToString("D"); + var envJsonItem = new JObject(); + envJsonItem["name"] = env.name; + envJsonItem["description"] = env.m_EnvironmentDescription.GetLocalizedString(); + envJsonItem["guid"] = envGuid; + var envRenderSettingsJson = new JObject(); + envRenderSettingsJson["fogEnabled"] = env.m_RenderSettings.m_FogEnabled; + envRenderSettingsJson["fogColor"] = colorToJArray(env.m_RenderSettings.m_FogColor); + envRenderSettingsJson["fogDensity"] = env.m_RenderSettings.m_FogDensity; + envRenderSettingsJson["fogStartDistance"] = env.m_RenderSettings.m_FogStartDistance; + envRenderSettingsJson["fogEndDistance"] = env.m_RenderSettings.m_FogEndDistance; + envRenderSettingsJson["clearColor"] = colorToJArray(env.m_RenderSettings.m_ClearColor); + envRenderSettingsJson["ambientColor"] = colorToJArray(env.m_RenderSettings.m_AmbientColor); + envRenderSettingsJson["skyboxExposure"] = env.m_RenderSettings.m_SkyboxExposure; + envRenderSettingsJson["skyboxTint"] = colorToJArray(env.m_RenderSettings.m_SkyboxTint); + envRenderSettingsJson["environmentPrefab"] = env.m_RenderSettings.m_EnvironmentPrefab; + envRenderSettingsJson["environmentReverbZone"] = env.m_RenderSettings.m_EnvironmentReverbZonePrefab; + envRenderSettingsJson["skyboxCubemap"] = env.m_RenderSettings.m_SkyboxCubemap.ToString(); + envRenderSettingsJson["reflectionCubemap"] = env.m_RenderSettings.m_ReflectionCubemap.ToString(); + envRenderSettingsJson["reflectionIntensity"] = env.m_RenderSettings.m_ReflectionIntensity; + envJsonItem["renderSettings"] = envRenderSettingsJson; + var envLights = new JArray(); + foreach (var light in env.m_Lights) + { + var envLight = new JObject(); + envLight["color"] = colorToJArray(light.Color); + envLight["position"] = vector3ToJArray(light.m_Position); + envLight["rotation"] = vector3ToJArray(light.m_Rotation.eulerAngles); + envLight["type"] = light.m_Type.ToString(); + envLight["range"] = light.m_Range; + envLight["spotAngle"] = light.m_SpotAngle; + envLight["shadowsEnabled"] = light.m_ShadowsEnabled; + envLights.Add(envLight); + + } + envJsonItem["lights"] = envLights; + envJsonItem["teleportBoundsHalfWidth"] = env.m_TeleportBoundsHalfWidth; + envJsonItem["controllerXRayHeight"] = env.m_ControllerXRayHeight; + envJsonItem["widgetHome"] = vector3ToJArray(env.m_WidgetHome); + envJsonItem["skyboxColorA"] = colorToJArray(env.m_SkyboxColorA); + envJsonItem["skyboxColorB"] = colorToJArray(env.m_SkyboxColorB); + envJson[envGuid] = envJsonItem; + Debug.LogFormat("Exporting environment: {0}", env.m_RenderSettings.m_EnvironmentPrefab); GameObject envPrefab = Resources.Load(env.m_RenderSettings.m_EnvironmentPrefab); GameObject envGameObject = UObject.Instantiate(envPrefab); @@ -199,12 +248,16 @@ private static void ExportEnvironments() settings.UseMainCameraVisibility = false; var context = new ExportContext(); var unityGltfexporter = new GLTFSceneExporter(envGameObject.transform, context); - unityGltfexporter.SaveGLTFandBin(directoryName, basename + ".gltf"); + unityGltfexporter.SaveGLB(directoryName, basename + ".glb"); // DestroyImmediate is required because editor mode never runs object garbage collection. UObject.DestroyImmediate(envGameObject); } + string jsonString = envJson.ToString(); + string path = Path.Combine(environmentExportPath, "environments.json"); + File.WriteAllText(path, jsonString); + // Restore the original RenderSettings Environment.SetRenderSettings(originalRenderSettings); From 437f4c53bfad13a718de4717b1061f4256c1f30d Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 29 May 2024 09:35:49 +0100 Subject: [PATCH 035/105] "Save as" API endpoint --- Assets/Scripts/API/ApiMethods.GlobalCommands.cs | 13 +++++++++++-- Assets/Scripts/SketchControlsScript.cs | 12 +++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs index e3b1455ffd..ef55f955c2 100644 --- a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs +++ b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs @@ -43,6 +43,15 @@ public static void SaveNew() SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, 1); } + [ApiEndpoint("save.as", "Saves the current scene with the given name")] + public static void SaveAs(string filename) + { + var rEnum = SketchControlsScript.GlobalCommands.SaveNew; + SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, 1); + rEnum = SketchControlsScript.GlobalCommands.RenameSketch; + SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, 0, (int)SketchSetType.User, sParam: filename); + } + [ApiEndpoint("icosa.login", "Login to the Icosa Gallery")] public static void IcosaLogin(string username, string password) { @@ -297,8 +306,8 @@ public static void ExportRaw() public static void ShowSketchFolder(int index) { var rEnum = SketchControlsScript.GlobalCommands.ShowSketchFolder; - // TODO 0 is User folder. Do we need to support the other SketchSetTypes? - SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, index, 0); + // TODO Do we need to support the other SketchSetTypes? + SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum, index, (int)SketchSetType.User); } // TODO Why no "enabled" counterpart? diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 6f6de2ef4e..165eab31d3 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -4494,9 +4494,19 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, { var sketchSetType = (SketchSetType)iParam2; SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + string newName = sParam; + if (string.IsNullOrEmpty(newName)) + { + newName = KeyboardPopUpWindow.m_LastInput; + if (string.IsNullOrEmpty(newName)) + { + break; + } + } + if (sketchSetType == SketchSetType.User) { - sketchSet.RenameSketch(iParam1, KeyboardPopUpWindow.m_LastInput); + sketchSet.RenameSketch(iParam1, newName); } DismissPopupOnCurrentGazeObject(false); break; From d456a353003ff106ca93b2e71effe78054984c6b Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 29 May 2024 09:36:45 +0100 Subject: [PATCH 036/105] Environment metadata for new GLTF exports --- .../Scripts/Export/OpenBrushExportPlugin.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index 0e120ba4f0..fa4b37e8a9 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -3,6 +3,7 @@ using System.Linq; using GLTF.Schema; using Newtonsoft.Json.Linq; +using Unity.VectorGraphics; using UnityEngine; using UnityGLTF; using UnityGLTF.Plugins; @@ -286,6 +287,33 @@ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gl public override void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { gltfRoot.Asset.Generator = $"Open Brush UnityGLTF Exporter {App.Config.m_VersionNumber}.{App.Config.m_BuildStamp})"; + + JToken ColorToJArray(Color c) => JToken.FromObject(new { c.r, c.g, c.b, c.a }); + JToken Vector3ToJArray(Vector3 c) => JToken.FromObject(new { c.x, c.y, c.z }); + + var metadata = new SketchSnapshot().GetSketchMetadata(); + + var settings = SceneSettings.m_Instance; + Environment env = settings.GetDesiredPreset(); + var extras = new JObject(); + + var pose = metadata.SceneTransformInRoomSpace; + extras["TB_EnvironmentGuid"] = env.m_Guid.ToString("D"); + extras["TB_Environment"] = env.Description; + extras["TB_UseGradient"] = settings.InGradient ? "true" : "false"; + extras["TB_SkyColorA"] = ColorToJArray(settings.SkyColorA); + extras["TB_SkyColorB"] = ColorToJArray(settings.SkyColorB); + Matrix4x4 exportFromUnity = AxisConvention.GetFromUnity(AxisConvention.kGltf2); + extras["TB_SkyGradientDirection"] = Vector3ToJArray( + exportFromUnity * (settings.GradientOrientation * Vector3.up)); + extras["TB_FogColor"] = ColorToJArray(settings.FogColor); + extras["TB_FogDensity"] = settings.FogDensity; + extras["TB_PoseTranslation"] = Vector3ToJArray(pose.translation); + extras["TB_PoseRotation"] = Vector3ToJArray(pose.rotation.eulerAngles); + extras["TB_PoseScale"] = pose.scale; + // Experimental + // extras["TB_metadata"] = JObject.FromObject(metadata); + gltfRoot.Extras = extras; } private static void SafeDestroy(Object o) From df6a67f8522cb8f221b3295be4f2358647c170e8 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 29 May 2024 09:38:12 +0100 Subject: [PATCH 037/105] Pose metadata for legacy GLTF export --- Assets/Scripts/Export/ExportGlTF.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Assets/Scripts/Export/ExportGlTF.cs b/Assets/Scripts/Export/ExportGlTF.cs index 293ce00c90..a8151d9600 100644 --- a/Assets/Scripts/Export/ExportGlTF.cs +++ b/Assets/Scripts/Export/ExportGlTF.cs @@ -193,6 +193,9 @@ private void SetExtras( Color skyColorB = payload.env.skyColorB; Vector3 skyGradientDir = payload.env.skyGradientDir; + // var camPose = SketchControlsScript.m_Instance.GetSaveIconTool().LastSaveCameraRigState.GetLossyTrTransform(); + var pose = App.Scene.Pose; + // Scene-level extras: exporter.G.extras["TB_EnvironmentGuid"] = payload.env.guid.ToString("D"); exporter.G.extras["TB_Environment"] = payload.env.description; @@ -204,6 +207,9 @@ private void SetExtras( exportFromUnity * skyGradientDir); exporter.G.extras["TB_FogColor"] = CommaFormattedFloatRGB(payload.env.fogColor); exporter.G.extras["TB_FogDensity"] = payload.env.fogDensity.ToString(); + exporter.G.extras["TB_PoseTranslation"] = CommaFormattedVector3(pose.translation); + exporter.G.extras["TB_PoseRotation"] = CommaFormattedVector3(pose.rotation.eulerAngles); + exporter.G.extras["TB_PoseScale"] = pose.scale; // TODO: remove when Poly starts using the new color data exporter.G.extras["TB_SkyColorHorizon"] = CommaFormattedFloatRGB(skyColorA); From 604f995804318010115edfd4f526277c8f48d231 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 29 May 2024 16:04:47 +0100 Subject: [PATCH 038/105] Fix upload naming issues. Include extras in uploaded gltf --- Assets/Scripts/Sharing/VrAssetService.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index d823465e59..45b0009180 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -54,7 +54,7 @@ public class VrAssetService : MonoBehaviour { // Constants - const string kGltfName = "sketch.gltf"; + const string kDefaultName = "sketch"; public const string kApiHost = "https://api.icosa.gallery"; private const string kAssetLandingPage = "https://icosa.gallery/uploads"; @@ -671,18 +671,18 @@ private async Task CreateZipFileAsync( DiskSceneFileInfo fileInfo = GetWritableFile(); var currentScene = SaveLoadScript.m_Instance.SceneFile; - string uploadName = currentScene.Valid ? currentScene.HumanName : kGltfName; + string uploadName = currentScene.Valid ? currentScene.HumanName : kDefaultName; + string gltfUploadName = $"{uploadName}.gltf"; SetUploadProgress(UploadStep.CreateGltf, 0); // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. - string gltfFile = Path.Combine(tempUploadDir, uploadName); + string gltfFile = Path.Combine(tempUploadDir, gltfUploadName); var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( OverlayType.Export, fadeDuration: 0.5f, action: () => new ExportGlTF().ExportBrushStrokes( gltfFile, - AxisConvention.kGltf2, binary: false, doExtras: false, + AxisConvention.kGltf2, binary: false, doExtras: true, includeLocalMediaContent: true, gltfVersion: 2, - // TODO: selfContained: false was Poly setting but is it what we want to do now? selfContained: true)); if (!exportResults.success) { @@ -701,7 +701,7 @@ private async Task CreateZipFileAsync( token.ThrowIfCancellationRequested(); // Create a copy of the .tilt file in tempUploadDir. - string tempTiltPath = Path.Combine(tempUploadDir, "sketch.tilt"); + string tempTiltPath = Path.Combine(tempUploadDir, $"{uploadName}.tilt"); File.Copy(fileInfo.FullPath, tempTiltPath); // Save thumbnail as a png to temp path @@ -735,7 +735,7 @@ private async Task CreateZipFileAsync( SetUploadProgress(UploadStep.CreateGltf, 0); // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. - string gltfFile = Path.Combine(tempUploadDir, kGltfName); + string gltfFile = Path.Combine(tempUploadDir, $"{kDefaultName}.gltf"); var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( OverlayType.Export, fadeDuration: 0.5f, action: () => new ExportGlTF().ExportBrushStrokes( From 21068e25b4fc2bf62682238b1191bf8a34c43d11 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 30 May 2024 11:35:23 +0100 Subject: [PATCH 039/105] Allow non-runtime gltf export without needing to disable our plugin --- Assets/Scripts/Export/OpenBrushExportPlugin.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index fa4b37e8a9..977d02101c 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -122,9 +122,8 @@ public override void BeforeNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfR { BeforeLayerExport(transform); } - - if (!Application.isPlaying && - !App.UserConfig.Export.KeepStrokes && + if (!Application.isPlaying) return; + if (!App.UserConfig.Export.KeepStrokes && App.UserConfig.Export.ExportStrokeMetadata) { // We'll need a way to find the batch for each mesh later @@ -180,6 +179,7 @@ public override void AfterNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRo AfterLayerExport(transform); } + if (!Application.isPlaying) return; if (App.UserConfig.Export.KeepStrokes && App.UserConfig.Export.ExportStrokeMetadata) { var brush = transform.GetComponent(); @@ -229,6 +229,7 @@ public override void AfterNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRo public override void AfterPrimitiveExport(GLTFSceneExporter exporter, Mesh mesh, MeshPrimitive primitive, int index) { + if (!Application.isPlaying) return; if (App.UserConfig.Export.ExportStrokeMetadata) { if (App.UserConfig.Export.KeepStrokes) @@ -286,6 +287,7 @@ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gl public override void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { + if (!Application.isPlaying) return; gltfRoot.Asset.Generator = $"Open Brush UnityGLTF Exporter {App.Config.m_VersionNumber}.{App.Config.m_BuildStamp})"; JToken ColorToJArray(Color c) => JToken.FromObject(new { c.r, c.g, c.b, c.a }); From 16c7e3c37883fdca8d7ccb6669966802772b5697 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 2 Jun 2024 18:50:08 +0100 Subject: [PATCH 040/105] Add the scene metadata needed for gltf files to recreate lights --- Assets/Editor/GlTF_EditorExporter.cs | 7 ++++--- Assets/Scripts/Export/ExportGlTF.cs | 13 +++++++++---- Assets/Scripts/Sharing/AssetGetter.cs | 2 +- Assets/Scripts/Sharing/VrAssetService.cs | 10 +++++----- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Assets/Editor/GlTF_EditorExporter.cs b/Assets/Editor/GlTF_EditorExporter.cs index 6acc7fcdfb..a66868aa38 100644 --- a/Assets/Editor/GlTF_EditorExporter.cs +++ b/Assets/Editor/GlTF_EditorExporter.cs @@ -180,7 +180,6 @@ private static void ExportEnvironments() string envGuid = env.m_Guid.ToString("D"); var envJsonItem = new JObject(); envJsonItem["name"] = env.name; - envJsonItem["description"] = env.m_EnvironmentDescription.GetLocalizedString(); envJsonItem["guid"] = envGuid; var envRenderSettingsJson = new JObject(); envRenderSettingsJson["fogEnabled"] = env.m_RenderSettings.m_FogEnabled; @@ -194,8 +193,10 @@ private static void ExportEnvironments() envRenderSettingsJson["skyboxTint"] = colorToJArray(env.m_RenderSettings.m_SkyboxTint); envRenderSettingsJson["environmentPrefab"] = env.m_RenderSettings.m_EnvironmentPrefab; envRenderSettingsJson["environmentReverbZone"] = env.m_RenderSettings.m_EnvironmentReverbZonePrefab; - envRenderSettingsJson["skyboxCubemap"] = env.m_RenderSettings.m_SkyboxCubemap.ToString(); - envRenderSettingsJson["reflectionCubemap"] = env.m_RenderSettings.m_ReflectionCubemap.ToString(); + envRenderSettingsJson["skyboxCubemap"] = env.m_RenderSettings.m_SkyboxCubemap != null ? + env.m_RenderSettings.m_SkyboxCubemap.name : ""; + envRenderSettingsJson["reflectionCubemap"] = env.m_RenderSettings.m_ReflectionCubemap != null ? + env.m_RenderSettings.m_ReflectionCubemap.name : ""; envRenderSettingsJson["reflectionIntensity"] = env.m_RenderSettings.m_ReflectionIntensity; envJsonItem["renderSettings"] = envRenderSettingsJson; var envLights = new JArray(); diff --git a/Assets/Scripts/Export/ExportGlTF.cs b/Assets/Scripts/Export/ExportGlTF.cs index a8151d9600..32644618fb 100644 --- a/Assets/Scripts/Export/ExportGlTF.cs +++ b/Assets/Scripts/Export/ExportGlTF.cs @@ -207,13 +207,18 @@ private void SetExtras( exportFromUnity * skyGradientDir); exporter.G.extras["TB_FogColor"] = CommaFormattedFloatRGB(payload.env.fogColor); exporter.G.extras["TB_FogDensity"] = payload.env.fogDensity.ToString(); + + exporter.G.extras["TB_AmbientLightColor"] = CommaFormattedFloatRGB(payload.lights.ambientColor); + exporter.G.extras["TB_SceneLight0Color"] = CommaFormattedFloatRGB(payload.lights.lights[0].lightColor); + exporter.G.extras["TB_SceneLight0Rotation"] = CommaFormattedVector3( + payload.lights.lights[0].xform.rotation.eulerAngles); + exporter.G.extras["TB_SceneLight1Color"] = CommaFormattedFloatRGB(payload.lights.lights[1].lightColor); + exporter.G.extras["TB_SceneLight1Rotation"] = CommaFormattedVector3( + payload.lights.lights[1].xform.rotation.eulerAngles); + exporter.G.extras["TB_PoseTranslation"] = CommaFormattedVector3(pose.translation); exporter.G.extras["TB_PoseRotation"] = CommaFormattedVector3(pose.rotation.eulerAngles); exporter.G.extras["TB_PoseScale"] = pose.scale; - - // TODO: remove when Poly starts using the new color data - exporter.G.extras["TB_SkyColorHorizon"] = CommaFormattedFloatRGB(skyColorA); - exporter.G.extras["TB_SkyColorZenith"] = CommaFormattedFloatRGB(skyColorB); } // Returns a GlTF_Node; null means "there is no node for this group". diff --git a/Assets/Scripts/Sharing/AssetGetter.cs b/Assets/Scripts/Sharing/AssetGetter.cs index b880d8e173..2ae2d31284 100644 --- a/Assets/Scripts/Sharing/AssetGetter.cs +++ b/Assets/Scripts/Sharing/AssetGetter.cs @@ -96,7 +96,7 @@ public IEnumerator GetAssetCoroutine() OAuth2Identity identity = null; - if (!m_URI.StartsWith(VrAssetService.ApiHost)) + if (!m_URI.StartsWith(VrAssetService.m_Instance.ApiHost)) { m_Asset.SetRootElement(UnityWebRequest.EscapeURL(m_URI), m_URI); } diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 45b0009180..42a463c579 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -56,8 +56,8 @@ public class VrAssetService : MonoBehaviour const string kDefaultName = "sketch"; - public const string kApiHost = "https://api.icosa.gallery"; - private const string kAssetLandingPage = "https://icosa.gallery/uploads"; + private string kApiHost => App.ICOSA_API_URL; + private string kAssetLandingPage => $"{App.ICOSA_WEBSITE_URL}/uploads"; private const string kListAssetsUri = "/v1/assets"; private const string kUserAssetsUri = "/v1/users/me/assets"; @@ -234,7 +234,7 @@ public void Dispose() public static VrAssetService m_Instance; // Currently this always returns the standard API host when running unit tests - public static string ApiHost + public string ApiHost { get { @@ -537,7 +537,7 @@ private async void VerifyIcosaConnectionAndCheckApiVersionAsync() m_IcosaStatus = await GetIcosaStatus(); } - private static async Task GetIcosaStatus() + private async Task GetIcosaStatus() { // UserConfig override if (App.UserConfig.Flags.DisableIcosa || App.Instance.IcosaToken == null) @@ -545,7 +545,7 @@ private static async Task GetIcosaStatus() return IcosaStatus.Disabled; } - string uri = String.Format("{0}{1}", ApiHost, kGetVersionUri); + string uri = ApiHost; try { var api = new LoginApi($"{App.ICOSA_API_URL}"); From a60ede941642dae7457e49aa5c9f17be83ddac69 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 10 Jun 2024 18:26:27 +0100 Subject: [PATCH 041/105] WIP implementation of sketchbook and poly panel API integration --- .../{PolyPanel.prefab => IcosaPanel.prefab} | 68 +- ...nel.prefab.meta => IcosaPanel.prefab.meta} | 0 ...Mobile.prefab => IcosaPanel_Mobile.prefab} | 0 ...fab.meta => IcosaPanel_Mobile.prefab.meta} | 0 Assets/Prefabs/Panels/SketchbookPanel.prefab | 2 +- Assets/Scenes/Main.unity | 4 +- Assets/Scripts/API/ApiManager.cs | 1756 +-- Assets/Scripts/App.cs | 4726 +++---- Assets/Scripts/Export/Export.cs | 666 +- Assets/Scripts/Export/ExportUtils.cs | 1744 +-- Assets/Scripts/Export/TiltBrushUriLoader.cs | 138 +- Assets/Scripts/GUI/BasePanel.cs | 3374 ++--- ...PolyModelButton.cs => IcosaModelButton.cs} | 390 +- ...utton.cs.meta => IcosaModelButton.cs.meta} | 0 .../GUI/{PolyPanel.cs => IcosaPanel.cs} | 618 +- .../{PolyPanel.cs.meta => IcosaPanel.cs.meta} | 0 .../{PolySetButton.cs => IcosaSetButton.cs} | 62 +- ...tButton.cs.meta => IcosaSetButton.cs.meta} | 0 Assets/Scripts/GUI/ProfilePopUpWindow.cs | 837 +- Assets/Scripts/GUI/SketchbookPanel.cs | 1586 ++- Assets/Scripts/Model.cs | 1922 +-- Assets/Scripts/PacDebugWindow.cs | 204 +- ...lyAssetCatalog.cs => IcosaAssetCatalog.cs} | 1999 ++- ...alog.cs.meta => IcosaAssetCatalog.cs.meta} | 0 .../{PolyRawAsset.cs => IcosaRawAsset.cs} | 372 +- ...RawAsset.cs.meta => IcosaRawAsset.cs.meta} | 0 Assets/Scripts/Save/FileSketchSet.cs | 1074 +- Assets/Scripts/Save/MetadataUtils.cs | 892 +- Assets/Scripts/Save/SketchCatalog.cs | 246 +- Assets/Scripts/Save/SketchMetadata.cs | 1640 +-- Assets/Scripts/Sharing/AssetGetter.cs | 501 +- Assets/Scripts/Sharing/AssetLister.cs | 320 +- .../Scripts/Sharing/GoogleDriveSketchSet.cs | 882 +- .../{PolySketchSet.cs => IcosaSketchSet.cs} | 1850 +-- ...etchSet.cs.meta => IcosaSketchSet.cs.meta} | 0 Assets/Scripts/Sharing/VrAssetService.cs | 1903 ++- Assets/Scripts/SketchControlsScript.cs | 10450 ++++++++-------- Assets/Scripts/UserConfig.cs | 1390 +- Assets/Scripts/Util/ImageUtils.cs | 934 +- Assets/Scripts/WidgetManager.cs | 3118 ++--- Assets/Scripts/Widgets/ModelWidget.cs | 1684 +-- 41 files changed, 23692 insertions(+), 23660 deletions(-) rename Assets/Prefabs/Panels/{PolyPanel.prefab => IcosaPanel.prefab} (99%) rename Assets/Prefabs/Panels/{PolyPanel.prefab.meta => IcosaPanel.prefab.meta} (100%) rename Assets/Prefabs/Panels/{PolyPanel_Mobile.prefab => IcosaPanel_Mobile.prefab} (100%) rename Assets/Prefabs/Panels/{PolyPanel_Mobile.prefab.meta => IcosaPanel_Mobile.prefab.meta} (100%) rename Assets/Scripts/GUI/{PolyModelButton.cs => IcosaModelButton.cs} (74%) rename Assets/Scripts/GUI/{PolyModelButton.cs.meta => IcosaModelButton.cs.meta} (100%) rename Assets/Scripts/GUI/{PolyPanel.cs => IcosaPanel.cs} (84%) rename Assets/Scripts/GUI/{PolyPanel.cs.meta => IcosaPanel.cs.meta} (100%) rename Assets/Scripts/GUI/{PolySetButton.cs => IcosaSetButton.cs} (77%) rename Assets/Scripts/GUI/{PolySetButton.cs.meta => IcosaSetButton.cs.meta} (100%) rename Assets/Scripts/Poly/{PolyAssetCatalog.cs => IcosaAssetCatalog.cs} (95%) rename Assets/Scripts/Poly/{PolyAssetCatalog.cs.meta => IcosaAssetCatalog.cs.meta} (100%) rename Assets/Scripts/Poly/{PolyRawAsset.cs => IcosaRawAsset.cs} (93%) rename Assets/Scripts/Poly/{PolyRawAsset.cs.meta => IcosaRawAsset.cs.meta} (100%) rename Assets/Scripts/Sharing/{PolySketchSet.cs => IcosaSketchSet.cs} (87%) rename Assets/Scripts/Sharing/{PolySketchSet.cs.meta => IcosaSketchSet.cs.meta} (100%) diff --git a/Assets/Prefabs/Panels/PolyPanel.prefab b/Assets/Prefabs/Panels/IcosaPanel.prefab similarity index 99% rename from Assets/Prefabs/Panels/PolyPanel.prefab rename to Assets/Prefabs/Panels/IcosaPanel.prefab index e639dd7116..ac8ccd224a 100644 --- a/Assets/Prefabs/Panels/PolyPanel.prefab +++ b/Assets/Prefabs/Panels/IcosaPanel.prefab @@ -164,7 +164,7 @@ GameObject: - component: {fileID: 11474066} - component: {fileID: 114370098467888466} m_Layer: 16 - m_Name: PolyPanel + m_Name: IcosaPanel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -259,17 +259,17 @@ MonoBehaviour: m_ClampToBounds: 1 m_ReticleBounds: {x: 2.55, y: 2.35, z: 0} m_BorderSphereHighlightRadius: 2.5 - m_PositioningSpheresBounds: {x: 1, y: 0} + m_PositioningSpheresBounds: {x: 1, y: 1} m_PositioningSphereRadius: 0.4 m_UseGazeRotation: 1 m_MaxGazeRotation: 20 m_GazeActivateSpeed: 8 - m_InitialSpawnPos: {x: 0, y: 0, z: 0} + m_InitialSpawnPos: {x: -7, y: 15, z: 6} m_InitialSpawnRotEulers: {x: 0, y: 0, z: 0} m_WandAttachAngle: 0 m_WandAttachYOffset: 0 m_WandAttachHalfHeight: 1.3 - m_BeginFixed: 0 + m_BeginFixed: 1 m_CanBeFixedToWand: 1 m_CanBeDetachedFromWand: 1 m_PopUpGazeDuration: 0.2 @@ -856,7 +856,15 @@ MonoBehaviour: m_UnloadedTexture: {fileID: 2800000, guid: 92139e46e58d3924fb8e16ba42a0145f, type: 3} m_LoadedTexture: {fileID: 2800000, guid: e9a34c5f95833b248b106d4f7d83d9ad, type: 3} m_ErrorTexture: {fileID: 2800000, guid: 0c91e975bed1cd948a150b672f3fec48, type: 3} - m_LoadHelpText: Click to Load + m_LoadHelpText: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_LoadingOverlay: {fileID: 1000011887412796} references: version: 2 @@ -1857,7 +1865,15 @@ MonoBehaviour: m_UnloadedTexture: {fileID: 2800000, guid: 92139e46e58d3924fb8e16ba42a0145f, type: 3} m_LoadedTexture: {fileID: 2800000, guid: e9a34c5f95833b248b106d4f7d83d9ad, type: 3} m_ErrorTexture: {fileID: 2800000, guid: 0c91e975bed1cd948a150b672f3fec48, type: 3} - m_LoadHelpText: Click to Load + m_LoadHelpText: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_LoadingOverlay: {fileID: 1000012134891236} references: version: 2 @@ -2043,7 +2059,15 @@ MonoBehaviour: m_UnloadedTexture: {fileID: 2800000, guid: 92139e46e58d3924fb8e16ba42a0145f, type: 3} m_LoadedTexture: {fileID: 2800000, guid: e9a34c5f95833b248b106d4f7d83d9ad, type: 3} m_ErrorTexture: {fileID: 2800000, guid: 0c91e975bed1cd948a150b672f3fec48, type: 3} - m_LoadHelpText: Click to Load + m_LoadHelpText: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_LoadingOverlay: {fileID: 1000014145540092} references: version: 2 @@ -3122,7 +3146,15 @@ MonoBehaviour: m_UnloadedTexture: {fileID: 2800000, guid: 92139e46e58d3924fb8e16ba42a0145f, type: 3} m_LoadedTexture: {fileID: 2800000, guid: e9a34c5f95833b248b106d4f7d83d9ad, type: 3} m_ErrorTexture: {fileID: 2800000, guid: 0c91e975bed1cd948a150b672f3fec48, type: 3} - m_LoadHelpText: Click to Load + m_LoadHelpText: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_LoadingOverlay: {fileID: 1000010298050724} references: version: 2 @@ -4414,7 +4446,15 @@ MonoBehaviour: m_UnloadedTexture: {fileID: 2800000, guid: 92139e46e58d3924fb8e16ba42a0145f, type: 3} m_LoadedTexture: {fileID: 2800000, guid: e9a34c5f95833b248b106d4f7d83d9ad, type: 3} m_ErrorTexture: {fileID: 2800000, guid: 0c91e975bed1cd948a150b672f3fec48, type: 3} - m_LoadHelpText: Click to Load + m_LoadHelpText: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_LoadingOverlay: {fileID: 1000014019108166} references: version: 2 @@ -5541,7 +5581,15 @@ MonoBehaviour: m_UnloadedTexture: {fileID: 2800000, guid: 92139e46e58d3924fb8e16ba42a0145f, type: 3} m_LoadedTexture: {fileID: 2800000, guid: e9a34c5f95833b248b106d4f7d83d9ad, type: 3} m_ErrorTexture: {fileID: 2800000, guid: 0c91e975bed1cd948a150b672f3fec48, type: 3} - m_LoadHelpText: Click to Load + m_LoadHelpText: + m_TableReference: + m_TableCollectionName: + m_TableEntryReference: + m_KeyId: 0 + m_Key: + m_FallbackState: 0 + m_WaitForCompletion: 0 + m_LocalVariables: [] m_LoadingOverlay: {fileID: 1000013864904278} references: version: 2 diff --git a/Assets/Prefabs/Panels/PolyPanel.prefab.meta b/Assets/Prefabs/Panels/IcosaPanel.prefab.meta similarity index 100% rename from Assets/Prefabs/Panels/PolyPanel.prefab.meta rename to Assets/Prefabs/Panels/IcosaPanel.prefab.meta diff --git a/Assets/Prefabs/Panels/PolyPanel_Mobile.prefab b/Assets/Prefabs/Panels/IcosaPanel_Mobile.prefab similarity index 100% rename from Assets/Prefabs/Panels/PolyPanel_Mobile.prefab rename to Assets/Prefabs/Panels/IcosaPanel_Mobile.prefab diff --git a/Assets/Prefabs/Panels/PolyPanel_Mobile.prefab.meta b/Assets/Prefabs/Panels/IcosaPanel_Mobile.prefab.meta similarity index 100% rename from Assets/Prefabs/Panels/PolyPanel_Mobile.prefab.meta rename to Assets/Prefabs/Panels/IcosaPanel_Mobile.prefab.meta diff --git a/Assets/Prefabs/Panels/SketchbookPanel.prefab b/Assets/Prefabs/Panels/SketchbookPanel.prefab index 5a8693b797..4030ed258e 100644 --- a/Assets/Prefabs/Panels/SketchbookPanel.prefab +++ b/Assets/Prefabs/Panels/SketchbookPanel.prefab @@ -13518,7 +13518,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &4459060226476560 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 7e44fd385e..41e5ef778d 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -10629,12 +10629,12 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 652605543} - m_Enabled: 0 + m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 59a80c71ac022374fbe28ebb361ab7ee, type: 3} m_Name: m_EditorClassIdentifier: - m_ThumbnailSuffix: s128 + m_ThumbnailSuffix: --- !u!114 &652605562 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/API/ApiManager.cs b/Assets/Scripts/API/ApiManager.cs index 0b90f808a9..10f178c8ee 100644 --- a/Assets/Scripts/API/ApiManager.cs +++ b/Assets/Scripts/API/ApiManager.cs @@ -1,878 +1,878 @@ -// Copyright 2021 The Open Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Reflection; -using System.Text; -using System.Threading; -using Newtonsoft.Json; -using UnityEngine; -using UnityEngine.Networking; -using WebSocketServer; - -namespace TiltBrush -{ - public class ApiManager : MonoBehaviour - { - private const string ROOT_API_URL = "/api/v1"; - private const string BASE_USER_SCRIPTS_URL = "/scripts"; - private const string BASE_EXAMPLE_SCRIPTS_URL = "/examplescripts"; - private const string BASE_HTML = @" - -{0}"; - - - private FileSystemWatcher m_FileWatcher; - private string m_UserScriptsPath; - private Queue m_RequestedCommandQueue = Queue.Synchronized(new Queue()); - private Dictionary m_CommandStatuses; - private Queue m_OutgoingCommandQueue = Queue.Synchronized(new Queue()); - private List m_OutgoingApiListeners; - private static ApiManager m_Instance; - private Dictionary endpoints; - private byte[] CameraViewPng; - - private bool cameraViewRequested; - private bool cameraViewGenerated; - - [NonSerialized] public Vector3 BrushOrigin = new(0, 13, 3); // Good origin for monoscopic - [NonSerialized] public Quaternion BrushInitialRotation = Quaternion.LookRotation(Vector3.forward, Vector3.up); - [NonSerialized] public Vector3 BrushPosition; - [NonSerialized] public Quaternion BrushRotation; - public bool ForcePaintingOn = false; - private Dictionary m_UserScripts; - private Dictionary m_ExampleScripts; - - public static ApiManager Instance - { - get { return m_Instance; } - } - [NonSerialized] public Stack<(Vector3, Quaternion)> BrushTransformStack; - [NonSerialized] public Dictionary CommandExamples; - public string m_startupScriptName = "startup.sketchscript"; - - public string UserScriptsPath() { return m_UserScriptsPath; } - - void Awake() - { - m_Instance = this; - m_UserScriptsPath = Path.Combine(App.UserPath(), "Scripts"); - App.HttpServer.AddHttpHandler($"/help", InfoCallback); - App.HttpServer.AddHttpHandler($"/help/commands", InfoCallback); - App.HttpServer.AddHttpHandler($"/help/brushes", InfoCallback); - App.HttpServer.AddRawHttpHandler("/cameraview", CameraViewCallback); - PopulateApi(); - m_UserScripts = new Dictionary(); - m_ExampleScripts = new Dictionary(); - m_CommandStatuses = new Dictionary(); - PopulateExampleScripts(); - PopulateUserScripts(); - BrushTransformStack = new Stack<(Vector3, Quaternion)>(); - ResetBrushTransform(); - if (!Directory.Exists(m_UserScriptsPath)) - { - Directory.CreateDirectory(m_UserScriptsPath); - } - if (Directory.Exists(m_UserScriptsPath)) - { - m_FileWatcher = new FileSystemWatcher(m_UserScriptsPath, "*.html"); - m_FileWatcher.NotifyFilter = NotifyFilters.LastWrite; - m_FileWatcher.Created += OnScriptsDirectoryChanged; - m_FileWatcher.Changed += OnScriptsDirectoryChanged; - // m_FileWatcher.FileDeleted += OnScriptsDirectoryChanged; TODO - m_FileWatcher.EnableRaisingEvents = true; - } - if (CommandExamples == null) - { - CommandExamples = new Dictionary(); - } - CommandExamples["draw.paths"] = "[[0,0,0],[1,0,0],[1,1,0]],[[0,0,-1],[-1,0,-1],[-1,1,-1]]"; - CommandExamples["draw.path"] = "[0,0,0],[1,0,0],[1,1,0],[0,1,0]"; - CommandExamples["draw.stroke"] = "[0,0,0,0,180,90,.75],[1,0,0,0,180,90,.75],[1,1,0,0,180,90,.75],[0,1,0,0,180,90,.75]"; - CommandExamples["listenfor.strokes"] = "http://localhost:8000/"; - CommandExamples["draw.polygon"] = "5,1,0"; - CommandExamples["draw.text"] = "hello"; - CommandExamples["draw.svg"] = "M 184,199 116,170 53,209.6 60,136.2 4.3,88"; - CommandExamples["draw.camerapath"] = "0"; - CommandExamples["brush.type"] = "ink"; - CommandExamples["color.add.hsv"] = "0.1,0.2,0.3"; - CommandExamples["color.add.rgb"] = "0.1,0.2,0.3"; - CommandExamples["color.set.rgb"] = "0.1,0.2,0.3"; - CommandExamples["color.set.hsv"] = "0.1,0.2,0.3"; - CommandExamples["color.set.html"] = "darkblue"; - CommandExamples["brush.size.set"] = ".5"; - CommandExamples["brush.size.add"] = ".1"; - CommandExamples["spectator.move.to"] = "1,1,1"; - CommandExamples["spectator.move.by"] = "1,1,1"; - CommandExamples["spectator.turn.y"] = "45"; - CommandExamples["spectator.turn.x"] = "45"; - CommandExamples["spectator.turn.z"] = "45"; - CommandExamples["spectator.direction"] = "45,45,0"; - CommandExamples["spectator.look.at"] = "1,2,3"; - CommandExamples["spectator.mode"] = "circular"; - CommandExamples["spectator.show"] = "panels"; - CommandExamples["spectator.hide"] = "widgets"; - CommandExamples["user.move.to"] = "1,1,1"; - CommandExamples["user.move.by"] = "1,1,1"; - CommandExamples["brush.move.to"] = "1,1,1"; - CommandExamples["brush.move.by"] = "1,1,1"; - CommandExamples["brush.move"] = "1"; - CommandExamples["brush.draw"] = "1"; - CommandExamples["brush.turn.y"] = "45"; - CommandExamples["brush.turn.x"] = "45"; - CommandExamples["brush.turn.z"] = "45"; - CommandExamples["brush.look.at"] = "1,1,1"; - CommandExamples["stroke.delete"] = "0"; - CommandExamples["stroke.select"] = "0"; - CommandExamples["strokes.select"] = "0,3"; - CommandExamples["selection.trim"] = "2"; - CommandExamples["selection.points.addnoise"] = "x,0.5"; - CommandExamples["selection.points.quantize"] = "0.1"; - CommandExamples["strokes.join"] = "0,2"; - CommandExamples["stroke.add"] = "0"; - CommandExamples["load.user"] = "0"; - CommandExamples["load.curated"] = "0"; - CommandExamples["load.liked"] = "0"; - CommandExamples["load.drive"] = "0"; - CommandExamples["load.named"] = "Untitled_0.tilt"; - CommandExamples["showfolder.sketch"] = "0"; - CommandExamples["import.model"] = "Andy\\Andy.obj"; - CommandExamples["import.image"] = "TiltBrushLogo.png"; - CommandExamples["import.video"] = "animated-logo.mp4"; - App.Instance.StateChanged += RunStartupScript; - } - - public void ResetBrushTransform() - { - // Resets the "turtle" transform back to it's original values - BrushPosition = BrushOrigin; - BrushRotation = BrushInitialRotation; - } - - public void RunStartupScript(App.AppState oldState, App.AppState newState) - { - - if (!(oldState == App.AppState.LoadingBrushesAndLighting && newState == App.AppState.Standard)) return; - - var startupScriptPath = Path.Combine(m_UserScriptsPath, m_startupScriptName); - - if (File.Exists(startupScriptPath)) - { - var lines = File.ReadAllLines(startupScriptPath); - foreach (string pair in lines) - { - EnqueueCommand(pair); - } - } - } - - private class EnqueuedApiCommand - { - private Guid m_Handle; - private string m_Command; - private string m_Parameters; - - public Guid Handle => m_Handle; - public string Command => m_Command; - public string Parameters => m_Parameters; - - public EnqueuedApiCommand(string command, string parameters) - { - m_Handle = Guid.NewGuid(); - m_Command = command; - m_Parameters = parameters; - } - } - - private EnqueuedApiCommand EnqueueCommand(string commandString) - { - if (string.IsNullOrWhiteSpace(commandString)) return null; - if (commandString.StartsWith("//")) return null; - string[] commandPair = commandString.Split(new[] { '=' }, 2); - if (commandPair.Length < 1) return null; - string parameters; - parameters = commandPair.Length == 2 ? UnityWebRequest.UnEscapeURL(commandPair[1]) : ""; - EnqueuedApiCommand cmd = new EnqueuedApiCommand(commandPair[0], parameters); - m_RequestedCommandQueue.Enqueue(cmd); - return cmd; - } - - private void OnScriptsDirectoryChanged(object sender, FileSystemEventArgs e) - { - var fileinfo = new FileInfo(e.FullPath); - RegisterUserScript(fileinfo); - } - - private string InfoCallback(HttpListenerRequest request) - { - string html; - StringBuilder builder; - switch (request.Url.Segments.Last()) - { - case "commands": - - var host = $"{request.LocalEndPoint.Address}:{request.LocalEndPoint.Port}"; - host = host.Replace("127.0.0.1", "localhost"); - - if (request.Url.Query.Contains("raw")) - { - html = String.Join("\n", endpoints.Keys); - } - else if (request.Url.Query.Contains("json")) - { - html = JsonConvert.SerializeObject(ListApiCommands(), Formatting.Indented); - } - else - { - var commandList = ListApiCommandsAsStrings(); - builder = new StringBuilder("

Open Brush API Commands

"); - builder.AppendLine($"

To run commands a request to this url with http://{host}/api/v1?

"); - builder.AppendLine("

Commands are querystring parameters: commandname=parameters

"); - builder.AppendLine("

Separate multiple commands with &

"); - builder.AppendLine($"

Example: http://{host}/api/v1?brush.turn.y=45&brush.draw=1

"); - builder.AppendLine("
"); - foreach (var key in commandList.Keys) - { - string paramList = commandList[key].Item1; - if (paramList != "") - { - paramList = $"({paramList})"; - } - builder.AppendLine($@"
{key} {paramList} - Try it
-
{commandList[key].Item2}

"); - } - builder.AppendLine("
"); - html = String.Format(BASE_HTML, builder); - } - break; - case "brushes": - var brushes = BrushCatalog.m_Instance.AllBrushes.Where(x => x.DurableName != ""); - if (request.Url.Query.Contains("raw")) - { - html = String.Join("\n", brushes.Select(x => x.DurableName)); - } - else - { - builder = new StringBuilder("

Open Brush Brushes

"); - builder.AppendLine("
    "); - foreach (var b in brushes) - { - builder.AppendLine($"
  • {b.DurableName}
  • "); - } - builder.AppendLine("
"); - html = String.Format(BASE_HTML, builder); - } - break; - case "help": - default: - html = $@"

Open Brush API Help

-"; - break; - } - return html; - } - - private string getCommandExample(string key) - { - if (CommandExamples.ContainsKey(key)) - { - return $"{key}={CommandExamples[key]}"; - } - else - { - return key; - } - } - - private void PopulateExampleScripts() - { - App.HttpServer.AddHttpHandler(BASE_EXAMPLE_SCRIPTS_URL, ExampleScriptsCallback); - var exampleScripts = Resources.LoadAll("ScriptExamples", typeof(TextAsset)); - foreach (TextAsset htmlFile in exampleScripts) - { - string filename = $"{BASE_EXAMPLE_SCRIPTS_URL}/{htmlFile.name}.html"; - m_ExampleScripts[filename] = htmlFile.ToString(); - App.HttpServer.AddHttpHandler(filename, ExampleScriptsCallback); - } - } - - private void PopulateUserScripts() - { - App.HttpServer.AddHttpHandler(BASE_USER_SCRIPTS_URL, UserScriptsCallback); - if (!Directory.Exists(m_UserScriptsPath)) - { - Directory.CreateDirectory(m_UserScriptsPath); - } - if (Directory.Exists(m_UserScriptsPath)) - { - var dirInfo = new DirectoryInfo(m_UserScriptsPath); - FileInfo[] AllFileInfo = dirInfo.GetFiles(); - foreach (FileInfo fileinfo in AllFileInfo) - { - RegisterUserScript(fileinfo); - } - } - } - - private void RegisterUserScript(FileInfo file) - { - if (file.Extension == ".html" || file.Extension == ".htm") - { - var f = file.OpenText(); - string filename = $"{BASE_USER_SCRIPTS_URL}/{file.Name}"; - m_UserScripts[filename] = f.ReadToEnd(); - f.Close(); - if (!App.HttpServer.HttpHandlerExists(filename)) - { - App.HttpServer.AddHttpHandler(filename, UserScriptsCallback); - } - } - } - - private void PopulateApi() - { - endpoints = new Dictionary(); - var types = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(t => t.GetTypes()) - .Where(t => t.IsClass && t.Namespace == "TiltBrush"); - - foreach (var type in types) - { - foreach (MethodInfo methodInfo in type.GetMethods()) - { - var attrs = Attribute.GetCustomAttributes(methodInfo, typeof(ApiEndpoint)); - foreach (Attribute attr in attrs) - { - ApiEndpoint apiEndpoint = (ApiEndpoint)attr; - bool valid = false; - if (type.IsAbstract && type.IsSealed) // therefore is static - { - apiEndpoint.instance = null; - valid = true; - } - else if (type.IsSubclassOf(typeof(MonoBehaviour))) - { - apiEndpoint.instance = FindObjectOfType(type); - if (apiEndpoint.instance != null) - { - valid = true; - } - else - { - Debug.LogWarning($"No instance found for ApiEndpoint on: {type}"); - } - } - - if (valid) - { - apiEndpoint.type = type; - apiEndpoint.methodInfo = methodInfo; - apiEndpoint.parameterInfo = methodInfo.GetParameters(); - endpoints[apiEndpoint.Endpoint] = apiEndpoint; - } - else - { - Debug.LogWarning($"ApiEndpoint declared on invalid class: {type}"); - } - } - } - } - App.HttpServer.AddHttpHandler(ROOT_API_URL, ApiCommandCallback); - } - - private string InvokeEndpoint(EnqueuedApiCommand command) - { - if (endpoints.ContainsKey(command.Command)) - { - var endpoint = endpoints[command.Command]; - var parameters = endpoint.DecodeParams(command.Parameters); - return endpoint.Invoke(parameters)?.ToString(); - } - if (!command.Command.StartsWith("//")) - { - Debug.LogError($"Invalid API command: {command.Command}"); - } - return null; - } - - [ContextMenu("Log Api Commands")] - public void LogCommandsList() - { - if (!Application.isPlaying) - { - Debug.LogError("Please run in play mode"); - } - else - { - var builder = new StringBuilder(); - var commands = ListApiCommandsAsStrings(); - foreach (var k in commands.Keys) - { - builder.AppendLine($"{k} ({commands[k].Item2}): {commands[k].Item2}"); - } - } - } - - Dictionary ListApiCommandsAsStrings() - { - var commandList = new Dictionary(); - foreach (var endpoint in endpoints.Keys) - { - var paramInfoText = new List(); - foreach (var param in endpoints[endpoint].parameterInfo) - { - string typeName = param.ParameterType.Name - .Replace("Single", "float") - .Replace("Int32", "int") - .Replace("String", "string"); - paramInfoText.Add($"{typeName} {param.Name}"); - } - string paramInfo = String.Join(", ", paramInfoText); - commandList[endpoint] = (paramInfo, endpoints[endpoint].Description); - } - return commandList; - } - - Dictionary ListApiCommands() - { - var commandList = new Dictionary(); - foreach (var endpoint in endpoints.Keys) - { - commandList[endpoint] = new - { - parameters = endpoints[endpoint].ParamsAsDict(), - description = endpoints[endpoint].Description - }; - } - return commandList; - } - - private string UserScriptsCallback(HttpListenerRequest request) - { - string html; - if (request.Url.Segments.Length == 2) - { - var builder = new StringBuilder("

Open Brush User Scripts

"); - builder.AppendLine("
    "); - foreach (var e in m_UserScripts) - { - builder.AppendLine($"
  • {e.Key}
  • "); - } - - // Only show this button on Windows - // TODO Update this is ApiMethods.OpenUserFolder is ever cross platform - // (Also see similar global commands that will need updating) - if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor) - { - builder.AppendLine($""); - } - builder.AppendLine("
"); - html = String.Format(BASE_HTML, builder); - } - else - { - html = m_UserScripts[Uri.UnescapeDataString(request.Url.AbsolutePath)]; - } - return ScriptTemplateSubstitution(html); - } - - private string ExampleScriptsCallback(HttpListenerRequest request) - { - string html; - if (request.Url.Segments.Length == 2) - { - var builder = new StringBuilder("

Open Brush Example Scripts

"); - builder.AppendLine("
    "); - foreach (var e in m_ExampleScripts) - { - builder.AppendLine($"
  • {e.Key}
  • "); - } - builder.AppendLine("
"); - html = String.Format(BASE_HTML, builder); - } - else - { - html = m_ExampleScripts[Uri.UnescapeDataString(request.Url.AbsolutePath)]; - } - return ScriptTemplateSubstitution(html); - } - - private string ScriptTemplateSubstitution(string html) - { - - // TODO Document these - - string[] brushNameList = BrushCatalog.m_Instance.AllBrushes - .Where(x => x.Description != "") - .Where(x => x.m_SupersededBy == null) - .Select(x => x.Description.Replace(" ", "").Replace(".", "").Replace("(", "").Replace(")", "")) - .ToArray(); - string brushesJson = JsonConvert.SerializeObject(brushNameList); - html = html.Replace("{{brushesJson}}", brushesJson); - - string pointFamilies = JsonConvert.SerializeObject(Enum.GetNames(typeof(SymmetryGroup.R))); - html = html.Replace("{{pointFamiliesJson}}", pointFamilies); - - string wallpaperGroups = JsonConvert.SerializeObject(Enum.GetNames(typeof(PointSymmetry.Family))); - html = html.Replace("{{wallpaperGroupsJson}}", wallpaperGroups); - - string[] environmentNameList = EnvironmentCatalog.m_Instance.AllEnvironments - .Select(x => x.Description.Replace(" ", "")) - .ToArray(); - string environmentsJson = JsonConvert.SerializeObject(environmentNameList); - html = html.Replace("{{environmentsJson}}", environmentsJson); - - string commandsJson = JsonConvert.SerializeObject(ListApiCommands()); - html = html.Replace("{{commandsJson}}", commandsJson); - - return html; - } - - public void ReceiveWebSocketMessage(WebSocketMessage message) - { - foreach (var cmd in message.data.Split("&")) - { - EnqueueCommand(cmd); - } - } - - string ApiCommandCallback(HttpListenerRequest request) - { - // GET commands - List commandStrings = request.Url.Query.TrimStart('?').Split('&').ToList(); - - // POST commands - if (request.HasEntityBody) - { - using (Stream body = request.InputStream) - { - using (var reader = new StreamReader(body, request.ContentEncoding)) - { - // TODO also accept JSON - var formdata = Uri.UnescapeDataString(reader.ReadToEnd()); - var formdataCommands = formdata.Replace("+", " ").Split('&').Where(s => s.Trim().Length > 0); - commandStrings.AddRange(formdataCommands); - } - } - } - - List responses = new List(); - - foreach (string commandString in commandStrings) - { - if (commandString.StartsWith("query.")) - { - responses.Add(HandleApiQuery(commandString)); - } - else - { - EnqueueCommand(commandString); - } - } - - return String.Join("\n", responses); - } - - private string HandleApiQuery(string commandString) - { - - // API queries are distinct from commands in that they return immediate results and never change the scene - - string[] commandPair = commandString.Split(new[] { '=' }, 2); - if (commandPair.Length < 1) return null; - switch (commandPair[0]) - { - case "query.queue": - return m_OutgoingCommandQueue.Count.ToString(); - case "query.command": - if (m_CommandStatuses.ContainsKey(commandPair[1])) - { - return m_CommandStatuses[commandPair[1]]; - } - else - { - return $"pending"; - } - case "query.spectator.position": - return ApiMainThreadObserver.Instance.SpectatorCamPosition.ToString(); - case "query.spectator.rotation": - return ApiMainThreadObserver.Instance.SpectatorCamRotation.eulerAngles.ToString(); - case "query.spectator.target": - return ApiMainThreadObserver.Instance.SpectatorCamTargetPosition.ToString(); - } - return "unknown query"; - } - - public bool HasOutgoingListeners => m_OutgoingApiListeners != null && m_OutgoingApiListeners.Count > 0; - - public void EnqueueOutgoingCommands(List> commands) - { - if (!HasOutgoingListeners) return; - foreach (var command in commands) - { - m_OutgoingCommandQueue.Enqueue(command); - } - } - - public void AddOutgoingCommandListener(Uri uri) - { - if (m_OutgoingApiListeners == null) m_OutgoingApiListeners = new List(); - if (m_OutgoingApiListeners.Contains(uri)) return; - m_OutgoingApiListeners.Add(uri); - - } - - private void OutgoingApiCommand() - { - if (!HasOutgoingListeners) return; - - KeyValuePair command; - try - { - command = (KeyValuePair)m_OutgoingCommandQueue.Dequeue(); - } - catch (InvalidOperationException) - { - return; - } - - foreach (var listenerUrl in m_OutgoingApiListeners) - { - string getUri = $"{listenerUrl}?{command.Key}={command.Value}"; - if (getUri.Length < 512) // Actually limit is 2083 but let's be conservative - { - StartCoroutine(GetRequest(getUri)); - } - else - { - var formData = new Dictionary - { - {command.Key, command.Value} - }; - StartCoroutine(PostRequest(listenerUrl.ToString(), formData)); - } - } - } - - IEnumerator GetRequest(string uri) - { - using (UnityWebRequest webRequest = UnityWebRequest.Get(uri)) - { - yield return webRequest.SendWebRequest(); - } - } - - IEnumerator PostRequest(string uri, Dictionary formData) - { - using (UnityWebRequest webRequest = UnityWebRequest.Post(uri, formData)) - { - yield return webRequest.SendWebRequest(); - } - } - - private bool HandleApiCommand() - { - EnqueuedApiCommand command; - try - { - command = (EnqueuedApiCommand)m_RequestedCommandQueue.Dequeue(); - } - catch (InvalidOperationException) - { - return false; - } - var result = Instance.InvokeEndpoint(command); - m_CommandStatuses[command.Handle.ToString()] = result; - return true; - } - - private void Update() - { - HandleApiCommand(); - OutgoingApiCommand(); - UpdateCameraView(); - } - - - IEnumerator ScreenCap() - { - yield return new WaitForEndOfFrame(); - var rt = new RenderTexture(Screen.width, Screen.height, 0); - ScreenCapture.CaptureScreenshotIntoRenderTexture(rt); - var oldTex = RenderTexture.active; - var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false); - RenderTexture.active = rt; - tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0); - FlipTextureVertically(tex); - tex.Apply(); - RenderTexture.active = oldTex; - CameraViewPng = tex.EncodeToPNG(); - Destroy(tex); - - cameraViewRequested = false; - cameraViewGenerated = true; - } - - public static void FlipTextureVertically(Texture2D original) - { - // ScreenCap is upside down so flip it - // Orientation might be platform specific so we might need some logic around this - - var originalPixels = original.GetPixels(); - - Color[] newPixels = new Color[originalPixels.Length]; - - int width = original.width; - int rows = original.height; - - for (int x = 0; x < width; x++) - { - for (int y = 0; y < rows; y++) - { - newPixels[x + y * width] = originalPixels[x + (rows - y - 1) * width]; - } - } - - original.SetPixels(newPixels); - original.Apply(); - } - - private void UpdateCameraView() - { - if (cameraViewRequested) StartCoroutine(ScreenCap()); - } - - private HttpListenerContext CameraViewCallback(HttpListenerContext ctx) - { - - cameraViewRequested = true; - while (cameraViewGenerated == false) - { - Thread.Sleep(5); - } - cameraViewGenerated = false; - - ctx.Response.AddHeader("Content-Type", "image/png"); - ctx.Response.ContentLength64 = CameraViewPng.Length; - try - { - if (ctx.Response.OutputStream.CanWrite) - { - ctx.Response.OutputStream.Write(CameraViewPng, 0, CameraViewPng.Length); - } - } - catch (SocketException e) - { - Debug.LogWarning(e.Message); - } - finally - { - ctx.Response.Close(); - } - ctx = null; - return ctx; - } - - public void LoadPolyModel(string assetId) - { - StartCoroutine(SpawnModelCoroutine(assetId, "API")); - } - - public void LoadPolyModel(Uri uri) - { - string assetId = UnityWebRequest.EscapeURL(uri.ToString()); - StartCoroutine(SpawnModelCoroutine(assetId, "API")); - } - - private static IEnumerator SpawnModelCoroutine(string assetId, string reason) - { - // Same as calling Model.RequestModelPreload -> RequestModelLoadInternal, except - // this won't ignore the request if the load-into-memory previously failed. - App.PolyAssetCatalog.RequestModelLoad(assetId, reason); - - // It is possible from this section forward that the user may have moved on to a different page - // on the Poly panel, which is why we use a local copy of 'model' rather than m_Model. - Model model; - // A model in the catalog will become non-null once the gltf has been downloaded or is in the - // cache. - while ((model = App.PolyAssetCatalog.GetModel(assetId)) == null) - { - yield return null; - } - - // A model becomes valid once the gltf has been successfully read into a Unity mesh. - if (!model.m_Valid) - { - // The model might be in the "loaded with error" state, but it seems harmless to try again. - // If the user keeps clicking, we'll keep trying. - yield return model.LoadFullyCoroutine(reason); - Debug.Assert(model.m_Valid || model.Error != null); - } - - if (!model.m_Valid) - { - OutputWindowScript.Error($"Couldn't load model: {model.Error?.message}", model.Error?.detail); - } - else - { - TrTransform xfSpawn = new TrTransform(); - CreateWidgetCommand createCommand = new CreateWidgetCommand( - WidgetManager.m_Instance.ModelWidgetPrefab, xfSpawn, Quaternion.identity, true - ); - SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand); - ModelWidget modelWidget = createCommand.Widget as ModelWidget; - modelWidget.Model = model; - modelWidget.Show(true); - createCommand.SetWidgetCost(modelWidget.GetTiltMeterCost()); - - WidgetManager.m_Instance.WidgetsDormant = false; - SketchControlsScript.m_Instance.EatGazeObjectInput(); - SelectionManager.m_Instance.RemoveFromSelection(false); - } - } - - public void HandleStrokeListeners(IEnumerable controlPoints, Guid guid, Color color, float size) - { - if (!HasOutgoingListeners) return; - var pointsAsStrings = new List(); - foreach (var cp in controlPoints) - { - var pos = cp.m_Pos; - var rot = cp.m_Orient.eulerAngles; - pointsAsStrings.Add($"[{pos.x},{pos.y},{pos.z},{rot.x},{rot.y},{rot.z},{cp.m_Pressure}]"); - } - EnqueueOutgoingCommands( - new List> - { - new ("brush.type", guid.ToString()), - new ("brush.size.set", size.ToString()), - new ("color.set.rgb", $"{color.r},{color.g},{color.b}"), - new ("draw.stroke", string.Join(",", pointsAsStrings)) - } - ); - } - } -} +// Copyright 2021 The Open Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Threading; +using Newtonsoft.Json; +using UnityEngine; +using UnityEngine.Networking; +using WebSocketServer; + +namespace TiltBrush +{ + public class ApiManager : MonoBehaviour + { + private const string ROOT_API_URL = "/api/v1"; + private const string BASE_USER_SCRIPTS_URL = "/scripts"; + private const string BASE_EXAMPLE_SCRIPTS_URL = "/examplescripts"; + private const string BASE_HTML = @" + +{0}"; + + + private FileSystemWatcher m_FileWatcher; + private string m_UserScriptsPath; + private Queue m_RequestedCommandQueue = Queue.Synchronized(new Queue()); + private Dictionary m_CommandStatuses; + private Queue m_OutgoingCommandQueue = Queue.Synchronized(new Queue()); + private List m_OutgoingApiListeners; + private static ApiManager m_Instance; + private Dictionary endpoints; + private byte[] CameraViewPng; + + private bool cameraViewRequested; + private bool cameraViewGenerated; + + [NonSerialized] public Vector3 BrushOrigin = new(0, 13, 3); // Good origin for monoscopic + [NonSerialized] public Quaternion BrushInitialRotation = Quaternion.LookRotation(Vector3.forward, Vector3.up); + [NonSerialized] public Vector3 BrushPosition; + [NonSerialized] public Quaternion BrushRotation; + public bool ForcePaintingOn = false; + private Dictionary m_UserScripts; + private Dictionary m_ExampleScripts; + + public static ApiManager Instance + { + get { return m_Instance; } + } + [NonSerialized] public Stack<(Vector3, Quaternion)> BrushTransformStack; + [NonSerialized] public Dictionary CommandExamples; + public string m_startupScriptName = "startup.sketchscript"; + + public string UserScriptsPath() { return m_UserScriptsPath; } + + void Awake() + { + m_Instance = this; + m_UserScriptsPath = Path.Combine(App.UserPath(), "Scripts"); + App.HttpServer.AddHttpHandler($"/help", InfoCallback); + App.HttpServer.AddHttpHandler($"/help/commands", InfoCallback); + App.HttpServer.AddHttpHandler($"/help/brushes", InfoCallback); + App.HttpServer.AddRawHttpHandler("/cameraview", CameraViewCallback); + PopulateApi(); + m_UserScripts = new Dictionary(); + m_ExampleScripts = new Dictionary(); + m_CommandStatuses = new Dictionary(); + PopulateExampleScripts(); + PopulateUserScripts(); + BrushTransformStack = new Stack<(Vector3, Quaternion)>(); + ResetBrushTransform(); + if (!Directory.Exists(m_UserScriptsPath)) + { + Directory.CreateDirectory(m_UserScriptsPath); + } + if (Directory.Exists(m_UserScriptsPath)) + { + m_FileWatcher = new FileSystemWatcher(m_UserScriptsPath, "*.html"); + m_FileWatcher.NotifyFilter = NotifyFilters.LastWrite; + m_FileWatcher.Created += OnScriptsDirectoryChanged; + m_FileWatcher.Changed += OnScriptsDirectoryChanged; + // m_FileWatcher.FileDeleted += OnScriptsDirectoryChanged; TODO + m_FileWatcher.EnableRaisingEvents = true; + } + if (CommandExamples == null) + { + CommandExamples = new Dictionary(); + } + CommandExamples["draw.paths"] = "[[0,0,0],[1,0,0],[1,1,0]],[[0,0,-1],[-1,0,-1],[-1,1,-1]]"; + CommandExamples["draw.path"] = "[0,0,0],[1,0,0],[1,1,0],[0,1,0]"; + CommandExamples["draw.stroke"] = "[0,0,0,0,180,90,.75],[1,0,0,0,180,90,.75],[1,1,0,0,180,90,.75],[0,1,0,0,180,90,.75]"; + CommandExamples["listenfor.strokes"] = "http://localhost:8000/"; + CommandExamples["draw.polygon"] = "5,1,0"; + CommandExamples["draw.text"] = "hello"; + CommandExamples["draw.svg"] = "M 184,199 116,170 53,209.6 60,136.2 4.3,88"; + CommandExamples["draw.camerapath"] = "0"; + CommandExamples["brush.type"] = "ink"; + CommandExamples["color.add.hsv"] = "0.1,0.2,0.3"; + CommandExamples["color.add.rgb"] = "0.1,0.2,0.3"; + CommandExamples["color.set.rgb"] = "0.1,0.2,0.3"; + CommandExamples["color.set.hsv"] = "0.1,0.2,0.3"; + CommandExamples["color.set.html"] = "darkblue"; + CommandExamples["brush.size.set"] = ".5"; + CommandExamples["brush.size.add"] = ".1"; + CommandExamples["spectator.move.to"] = "1,1,1"; + CommandExamples["spectator.move.by"] = "1,1,1"; + CommandExamples["spectator.turn.y"] = "45"; + CommandExamples["spectator.turn.x"] = "45"; + CommandExamples["spectator.turn.z"] = "45"; + CommandExamples["spectator.direction"] = "45,45,0"; + CommandExamples["spectator.look.at"] = "1,2,3"; + CommandExamples["spectator.mode"] = "circular"; + CommandExamples["spectator.show"] = "panels"; + CommandExamples["spectator.hide"] = "widgets"; + CommandExamples["user.move.to"] = "1,1,1"; + CommandExamples["user.move.by"] = "1,1,1"; + CommandExamples["brush.move.to"] = "1,1,1"; + CommandExamples["brush.move.by"] = "1,1,1"; + CommandExamples["brush.move"] = "1"; + CommandExamples["brush.draw"] = "1"; + CommandExamples["brush.turn.y"] = "45"; + CommandExamples["brush.turn.x"] = "45"; + CommandExamples["brush.turn.z"] = "45"; + CommandExamples["brush.look.at"] = "1,1,1"; + CommandExamples["stroke.delete"] = "0"; + CommandExamples["stroke.select"] = "0"; + CommandExamples["strokes.select"] = "0,3"; + CommandExamples["selection.trim"] = "2"; + CommandExamples["selection.points.addnoise"] = "x,0.5"; + CommandExamples["selection.points.quantize"] = "0.1"; + CommandExamples["strokes.join"] = "0,2"; + CommandExamples["stroke.add"] = "0"; + CommandExamples["load.user"] = "0"; + CommandExamples["load.curated"] = "0"; + CommandExamples["load.liked"] = "0"; + CommandExamples["load.drive"] = "0"; + CommandExamples["load.named"] = "Untitled_0.tilt"; + CommandExamples["showfolder.sketch"] = "0"; + CommandExamples["import.model"] = "Andy\\Andy.obj"; + CommandExamples["import.image"] = "TiltBrushLogo.png"; + CommandExamples["import.video"] = "animated-logo.mp4"; + App.Instance.StateChanged += RunStartupScript; + } + + public void ResetBrushTransform() + { + // Resets the "turtle" transform back to it's original values + BrushPosition = BrushOrigin; + BrushRotation = BrushInitialRotation; + } + + public void RunStartupScript(App.AppState oldState, App.AppState newState) + { + + if (!(oldState == App.AppState.LoadingBrushesAndLighting && newState == App.AppState.Standard)) return; + + var startupScriptPath = Path.Combine(m_UserScriptsPath, m_startupScriptName); + + if (File.Exists(startupScriptPath)) + { + var lines = File.ReadAllLines(startupScriptPath); + foreach (string pair in lines) + { + EnqueueCommand(pair); + } + } + } + + private class EnqueuedApiCommand + { + private Guid m_Handle; + private string m_Command; + private string m_Parameters; + + public Guid Handle => m_Handle; + public string Command => m_Command; + public string Parameters => m_Parameters; + + public EnqueuedApiCommand(string command, string parameters) + { + m_Handle = Guid.NewGuid(); + m_Command = command; + m_Parameters = parameters; + } + } + + private EnqueuedApiCommand EnqueueCommand(string commandString) + { + if (string.IsNullOrWhiteSpace(commandString)) return null; + if (commandString.StartsWith("//")) return null; + string[] commandPair = commandString.Split(new[] { '=' }, 2); + if (commandPair.Length < 1) return null; + string parameters; + parameters = commandPair.Length == 2 ? UnityWebRequest.UnEscapeURL(commandPair[1]) : ""; + EnqueuedApiCommand cmd = new EnqueuedApiCommand(commandPair[0], parameters); + m_RequestedCommandQueue.Enqueue(cmd); + return cmd; + } + + private void OnScriptsDirectoryChanged(object sender, FileSystemEventArgs e) + { + var fileinfo = new FileInfo(e.FullPath); + RegisterUserScript(fileinfo); + } + + private string InfoCallback(HttpListenerRequest request) + { + string html; + StringBuilder builder; + switch (request.Url.Segments.Last()) + { + case "commands": + + var host = $"{request.LocalEndPoint.Address}:{request.LocalEndPoint.Port}"; + host = host.Replace("127.0.0.1", "localhost"); + + if (request.Url.Query.Contains("raw")) + { + html = String.Join("\n", endpoints.Keys); + } + else if (request.Url.Query.Contains("json")) + { + html = JsonConvert.SerializeObject(ListApiCommands(), Formatting.Indented); + } + else + { + var commandList = ListApiCommandsAsStrings(); + builder = new StringBuilder("

Open Brush API Commands

"); + builder.AppendLine($"

To run commands a request to this url with http://{host}/api/v1?

"); + builder.AppendLine("

Commands are querystring parameters: commandname=parameters

"); + builder.AppendLine("

Separate multiple commands with &

"); + builder.AppendLine($"

Example: http://{host}/api/v1?brush.turn.y=45&brush.draw=1

"); + builder.AppendLine("
"); + foreach (var key in commandList.Keys) + { + string paramList = commandList[key].Item1; + if (paramList != "") + { + paramList = $"({paramList})"; + } + builder.AppendLine($@"
{key} {paramList} + Try it
+
{commandList[key].Item2}

"); + } + builder.AppendLine("
"); + html = String.Format(BASE_HTML, builder); + } + break; + case "brushes": + var brushes = BrushCatalog.m_Instance.AllBrushes.Where(x => x.DurableName != ""); + if (request.Url.Query.Contains("raw")) + { + html = String.Join("\n", brushes.Select(x => x.DurableName)); + } + else + { + builder = new StringBuilder("

Open Brush Brushes

"); + builder.AppendLine("
    "); + foreach (var b in brushes) + { + builder.AppendLine($"
  • {b.DurableName}
  • "); + } + builder.AppendLine("
"); + html = String.Format(BASE_HTML, builder); + } + break; + case "help": + default: + html = $@"

Open Brush API Help

+"; + break; + } + return html; + } + + private string getCommandExample(string key) + { + if (CommandExamples.ContainsKey(key)) + { + return $"{key}={CommandExamples[key]}"; + } + else + { + return key; + } + } + + private void PopulateExampleScripts() + { + App.HttpServer.AddHttpHandler(BASE_EXAMPLE_SCRIPTS_URL, ExampleScriptsCallback); + var exampleScripts = Resources.LoadAll("ScriptExamples", typeof(TextAsset)); + foreach (TextAsset htmlFile in exampleScripts) + { + string filename = $"{BASE_EXAMPLE_SCRIPTS_URL}/{htmlFile.name}.html"; + m_ExampleScripts[filename] = htmlFile.ToString(); + App.HttpServer.AddHttpHandler(filename, ExampleScriptsCallback); + } + } + + private void PopulateUserScripts() + { + App.HttpServer.AddHttpHandler(BASE_USER_SCRIPTS_URL, UserScriptsCallback); + if (!Directory.Exists(m_UserScriptsPath)) + { + Directory.CreateDirectory(m_UserScriptsPath); + } + if (Directory.Exists(m_UserScriptsPath)) + { + var dirInfo = new DirectoryInfo(m_UserScriptsPath); + FileInfo[] AllFileInfo = dirInfo.GetFiles(); + foreach (FileInfo fileinfo in AllFileInfo) + { + RegisterUserScript(fileinfo); + } + } + } + + private void RegisterUserScript(FileInfo file) + { + if (file.Extension == ".html" || file.Extension == ".htm") + { + var f = file.OpenText(); + string filename = $"{BASE_USER_SCRIPTS_URL}/{file.Name}"; + m_UserScripts[filename] = f.ReadToEnd(); + f.Close(); + if (!App.HttpServer.HttpHandlerExists(filename)) + { + App.HttpServer.AddHttpHandler(filename, UserScriptsCallback); + } + } + } + + private void PopulateApi() + { + endpoints = new Dictionary(); + var types = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(t => t.GetTypes()) + .Where(t => t.IsClass && t.Namespace == "TiltBrush"); + + foreach (var type in types) + { + foreach (MethodInfo methodInfo in type.GetMethods()) + { + var attrs = Attribute.GetCustomAttributes(methodInfo, typeof(ApiEndpoint)); + foreach (Attribute attr in attrs) + { + ApiEndpoint apiEndpoint = (ApiEndpoint)attr; + bool valid = false; + if (type.IsAbstract && type.IsSealed) // therefore is static + { + apiEndpoint.instance = null; + valid = true; + } + else if (type.IsSubclassOf(typeof(MonoBehaviour))) + { + apiEndpoint.instance = FindObjectOfType(type); + if (apiEndpoint.instance != null) + { + valid = true; + } + else + { + Debug.LogWarning($"No instance found for ApiEndpoint on: {type}"); + } + } + + if (valid) + { + apiEndpoint.type = type; + apiEndpoint.methodInfo = methodInfo; + apiEndpoint.parameterInfo = methodInfo.GetParameters(); + endpoints[apiEndpoint.Endpoint] = apiEndpoint; + } + else + { + Debug.LogWarning($"ApiEndpoint declared on invalid class: {type}"); + } + } + } + } + App.HttpServer.AddHttpHandler(ROOT_API_URL, ApiCommandCallback); + } + + private string InvokeEndpoint(EnqueuedApiCommand command) + { + if (endpoints.ContainsKey(command.Command)) + { + var endpoint = endpoints[command.Command]; + var parameters = endpoint.DecodeParams(command.Parameters); + return endpoint.Invoke(parameters)?.ToString(); + } + if (!command.Command.StartsWith("//")) + { + Debug.LogError($"Invalid API command: {command.Command}"); + } + return null; + } + + [ContextMenu("Log Api Commands")] + public void LogCommandsList() + { + if (!Application.isPlaying) + { + Debug.LogError("Please run in play mode"); + } + else + { + var builder = new StringBuilder(); + var commands = ListApiCommandsAsStrings(); + foreach (var k in commands.Keys) + { + builder.AppendLine($"{k} ({commands[k].Item2}): {commands[k].Item2}"); + } + } + } + + Dictionary ListApiCommandsAsStrings() + { + var commandList = new Dictionary(); + foreach (var endpoint in endpoints.Keys) + { + var paramInfoText = new List(); + foreach (var param in endpoints[endpoint].parameterInfo) + { + string typeName = param.ParameterType.Name + .Replace("Single", "float") + .Replace("Int32", "int") + .Replace("String", "string"); + paramInfoText.Add($"{typeName} {param.Name}"); + } + string paramInfo = String.Join(", ", paramInfoText); + commandList[endpoint] = (paramInfo, endpoints[endpoint].Description); + } + return commandList; + } + + Dictionary ListApiCommands() + { + var commandList = new Dictionary(); + foreach (var endpoint in endpoints.Keys) + { + commandList[endpoint] = new + { + parameters = endpoints[endpoint].ParamsAsDict(), + description = endpoints[endpoint].Description + }; + } + return commandList; + } + + private string UserScriptsCallback(HttpListenerRequest request) + { + string html; + if (request.Url.Segments.Length == 2) + { + var builder = new StringBuilder("

Open Brush User Scripts

"); + builder.AppendLine("
    "); + foreach (var e in m_UserScripts) + { + builder.AppendLine($"
  • {e.Key}
  • "); + } + + // Only show this button on Windows + // TODO Update this is ApiMethods.OpenUserFolder is ever cross platform + // (Also see similar global commands that will need updating) + if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor) + { + builder.AppendLine($""); + } + builder.AppendLine("
"); + html = String.Format(BASE_HTML, builder); + } + else + { + html = m_UserScripts[Uri.UnescapeDataString(request.Url.AbsolutePath)]; + } + return ScriptTemplateSubstitution(html); + } + + private string ExampleScriptsCallback(HttpListenerRequest request) + { + string html; + if (request.Url.Segments.Length == 2) + { + var builder = new StringBuilder("

Open Brush Example Scripts

"); + builder.AppendLine("
    "); + foreach (var e in m_ExampleScripts) + { + builder.AppendLine($"
  • {e.Key}
  • "); + } + builder.AppendLine("
"); + html = String.Format(BASE_HTML, builder); + } + else + { + html = m_ExampleScripts[Uri.UnescapeDataString(request.Url.AbsolutePath)]; + } + return ScriptTemplateSubstitution(html); + } + + private string ScriptTemplateSubstitution(string html) + { + + // TODO Document these + + string[] brushNameList = BrushCatalog.m_Instance.AllBrushes + .Where(x => x.Description != "") + .Where(x => x.m_SupersededBy == null) + .Select(x => x.Description.Replace(" ", "").Replace(".", "").Replace("(", "").Replace(")", "")) + .ToArray(); + string brushesJson = JsonConvert.SerializeObject(brushNameList); + html = html.Replace("{{brushesJson}}", brushesJson); + + string pointFamilies = JsonConvert.SerializeObject(Enum.GetNames(typeof(SymmetryGroup.R))); + html = html.Replace("{{pointFamiliesJson}}", pointFamilies); + + string wallpaperGroups = JsonConvert.SerializeObject(Enum.GetNames(typeof(PointSymmetry.Family))); + html = html.Replace("{{wallpaperGroupsJson}}", wallpaperGroups); + + string[] environmentNameList = EnvironmentCatalog.m_Instance.AllEnvironments + .Select(x => x.Description.Replace(" ", "")) + .ToArray(); + string environmentsJson = JsonConvert.SerializeObject(environmentNameList); + html = html.Replace("{{environmentsJson}}", environmentsJson); + + string commandsJson = JsonConvert.SerializeObject(ListApiCommands()); + html = html.Replace("{{commandsJson}}", commandsJson); + + return html; + } + + public void ReceiveWebSocketMessage(WebSocketMessage message) + { + foreach (var cmd in message.data.Split("&")) + { + EnqueueCommand(cmd); + } + } + + string ApiCommandCallback(HttpListenerRequest request) + { + // GET commands + List commandStrings = request.Url.Query.TrimStart('?').Split('&').ToList(); + + // POST commands + if (request.HasEntityBody) + { + using (Stream body = request.InputStream) + { + using (var reader = new StreamReader(body, request.ContentEncoding)) + { + // TODO also accept JSON + var formdata = Uri.UnescapeDataString(reader.ReadToEnd()); + var formdataCommands = formdata.Replace("+", " ").Split('&').Where(s => s.Trim().Length > 0); + commandStrings.AddRange(formdataCommands); + } + } + } + + List responses = new List(); + + foreach (string commandString in commandStrings) + { + if (commandString.StartsWith("query.")) + { + responses.Add(HandleApiQuery(commandString)); + } + else + { + EnqueueCommand(commandString); + } + } + + return String.Join("\n", responses); + } + + private string HandleApiQuery(string commandString) + { + + // API queries are distinct from commands in that they return immediate results and never change the scene + + string[] commandPair = commandString.Split(new[] { '=' }, 2); + if (commandPair.Length < 1) return null; + switch (commandPair[0]) + { + case "query.queue": + return m_OutgoingCommandQueue.Count.ToString(); + case "query.command": + if (m_CommandStatuses.ContainsKey(commandPair[1])) + { + return m_CommandStatuses[commandPair[1]]; + } + else + { + return $"pending"; + } + case "query.spectator.position": + return ApiMainThreadObserver.Instance.SpectatorCamPosition.ToString(); + case "query.spectator.rotation": + return ApiMainThreadObserver.Instance.SpectatorCamRotation.eulerAngles.ToString(); + case "query.spectator.target": + return ApiMainThreadObserver.Instance.SpectatorCamTargetPosition.ToString(); + } + return "unknown query"; + } + + public bool HasOutgoingListeners => m_OutgoingApiListeners != null && m_OutgoingApiListeners.Count > 0; + + public void EnqueueOutgoingCommands(List> commands) + { + if (!HasOutgoingListeners) return; + foreach (var command in commands) + { + m_OutgoingCommandQueue.Enqueue(command); + } + } + + public void AddOutgoingCommandListener(Uri uri) + { + if (m_OutgoingApiListeners == null) m_OutgoingApiListeners = new List(); + if (m_OutgoingApiListeners.Contains(uri)) return; + m_OutgoingApiListeners.Add(uri); + + } + + private void OutgoingApiCommand() + { + if (!HasOutgoingListeners) return; + + KeyValuePair command; + try + { + command = (KeyValuePair)m_OutgoingCommandQueue.Dequeue(); + } + catch (InvalidOperationException) + { + return; + } + + foreach (var listenerUrl in m_OutgoingApiListeners) + { + string getUri = $"{listenerUrl}?{command.Key}={command.Value}"; + if (getUri.Length < 512) // Actually limit is 2083 but let's be conservative + { + StartCoroutine(GetRequest(getUri)); + } + else + { + var formData = new Dictionary + { + {command.Key, command.Value} + }; + StartCoroutine(PostRequest(listenerUrl.ToString(), formData)); + } + } + } + + IEnumerator GetRequest(string uri) + { + using (UnityWebRequest webRequest = UnityWebRequest.Get(uri)) + { + yield return webRequest.SendWebRequest(); + } + } + + IEnumerator PostRequest(string uri, Dictionary formData) + { + using (UnityWebRequest webRequest = UnityWebRequest.Post(uri, formData)) + { + yield return webRequest.SendWebRequest(); + } + } + + private bool HandleApiCommand() + { + EnqueuedApiCommand command; + try + { + command = (EnqueuedApiCommand)m_RequestedCommandQueue.Dequeue(); + } + catch (InvalidOperationException) + { + return false; + } + var result = Instance.InvokeEndpoint(command); + m_CommandStatuses[command.Handle.ToString()] = result; + return true; + } + + private void Update() + { + HandleApiCommand(); + OutgoingApiCommand(); + UpdateCameraView(); + } + + + IEnumerator ScreenCap() + { + yield return new WaitForEndOfFrame(); + var rt = new RenderTexture(Screen.width, Screen.height, 0); + ScreenCapture.CaptureScreenshotIntoRenderTexture(rt); + var oldTex = RenderTexture.active; + var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false); + RenderTexture.active = rt; + tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0); + FlipTextureVertically(tex); + tex.Apply(); + RenderTexture.active = oldTex; + CameraViewPng = tex.EncodeToPNG(); + Destroy(tex); + + cameraViewRequested = false; + cameraViewGenerated = true; + } + + public static void FlipTextureVertically(Texture2D original) + { + // ScreenCap is upside down so flip it + // Orientation might be platform specific so we might need some logic around this + + var originalPixels = original.GetPixels(); + + Color[] newPixels = new Color[originalPixels.Length]; + + int width = original.width; + int rows = original.height; + + for (int x = 0; x < width; x++) + { + for (int y = 0; y < rows; y++) + { + newPixels[x + y * width] = originalPixels[x + (rows - y - 1) * width]; + } + } + + original.SetPixels(newPixels); + original.Apply(); + } + + private void UpdateCameraView() + { + if (cameraViewRequested) StartCoroutine(ScreenCap()); + } + + private HttpListenerContext CameraViewCallback(HttpListenerContext ctx) + { + + cameraViewRequested = true; + while (cameraViewGenerated == false) + { + Thread.Sleep(5); + } + cameraViewGenerated = false; + + ctx.Response.AddHeader("Content-Type", "image/png"); + ctx.Response.ContentLength64 = CameraViewPng.Length; + try + { + if (ctx.Response.OutputStream.CanWrite) + { + ctx.Response.OutputStream.Write(CameraViewPng, 0, CameraViewPng.Length); + } + } + catch (SocketException e) + { + Debug.LogWarning(e.Message); + } + finally + { + ctx.Response.Close(); + } + ctx = null; + return ctx; + } + + public void LoadPolyModel(string assetId) + { + StartCoroutine(SpawnModelCoroutine(assetId, "API")); + } + + public void LoadPolyModel(Uri uri) + { + string assetId = UnityWebRequest.EscapeURL(uri.ToString()); + StartCoroutine(SpawnModelCoroutine(assetId, "API")); + } + + private static IEnumerator SpawnModelCoroutine(string assetId, string reason) + { + // Same as calling Model.RequestModelPreload -> RequestModelLoadInternal, except + // this won't ignore the request if the load-into-memory previously failed. + App.IcosaAssetCatalog.RequestModelLoad(assetId, reason); + + // It is possible from this section forward that the user may have moved on to a different page + // on the Poly panel, which is why we use a local copy of 'model' rather than m_Model. + Model model; + // A model in the catalog will become non-null once the gltf has been downloaded or is in the + // cache. + while ((model = App.IcosaAssetCatalog.GetModel(assetId)) == null) + { + yield return null; + } + + // A model becomes valid once the gltf has been successfully read into a Unity mesh. + if (!model.m_Valid) + { + // The model might be in the "loaded with error" state, but it seems harmless to try again. + // If the user keeps clicking, we'll keep trying. + yield return model.LoadFullyCoroutine(reason); + Debug.Assert(model.m_Valid || model.Error != null); + } + + if (!model.m_Valid) + { + OutputWindowScript.Error($"Couldn't load model: {model.Error?.message}", model.Error?.detail); + } + else + { + TrTransform xfSpawn = new TrTransform(); + CreateWidgetCommand createCommand = new CreateWidgetCommand( + WidgetManager.m_Instance.ModelWidgetPrefab, xfSpawn, Quaternion.identity, true + ); + SketchMemoryScript.m_Instance.PerformAndRecordCommand(createCommand); + ModelWidget modelWidget = createCommand.Widget as ModelWidget; + modelWidget.Model = model; + modelWidget.Show(true); + createCommand.SetWidgetCost(modelWidget.GetTiltMeterCost()); + + WidgetManager.m_Instance.WidgetsDormant = false; + SketchControlsScript.m_Instance.EatGazeObjectInput(); + SelectionManager.m_Instance.RemoveFromSelection(false); + } + } + + public void HandleStrokeListeners(IEnumerable controlPoints, Guid guid, Color color, float size) + { + if (!HasOutgoingListeners) return; + var pointsAsStrings = new List(); + foreach (var cp in controlPoints) + { + var pos = cp.m_Pos; + var rot = cp.m_Orient.eulerAngles; + pointsAsStrings.Add($"[{pos.x},{pos.y},{pos.z},{rot.x},{rot.y},{rot.z},{cp.m_Pressure}]"); + } + EnqueueOutgoingCommands( + new List> + { + new ("brush.type", guid.ToString()), + new ("brush.size.set", size.ToString()), + new ("color.set.rgb", $"{color.r},{color.g},{color.b}"), + new ("draw.stroke", string.Join(",", pointsAsStrings)) + } + ); + } + } +} diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index cb704cc245..3aa2fd3c9d 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -1,2363 +1,2363 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Runtime.CompilerServices; -using UnityEngine; -using Newtonsoft.Json; -using TMPro; -#if USD_SUPPORTED -using Unity.Formats.USD; -#endif -#if USE_DOTNETZIP -using ZipSubfileReader = ZipSubfileReader_DotNetZip; -using ZipLibrary = Ionic.Zip; -#else -using ZipSubfileReader = TiltBrush.ZipSubfileReader_SharpZipLib; -using ZipLibrary = ICSharpCode.SharpZipLib.Zip; -#endif - -#if !UNITY_2020_3_OR_NEWER -xxx "This is the minimal Unity supported by Open Brush" xxx -#endif - -[assembly: InternalsVisibleTo("Assembly-CSharp-Editor")] -namespace TiltBrush -{ - public partial class App : MonoBehaviour - { - // ------------------------------------------------------------ - // Constants and types - // ------------------------------------------------------------ - - public const float METERS_TO_UNITS = 10f; - public const float UNITS_TO_METERS = .1f; - - // This is the name of the app, as displayed to the users running it. - public const string kAppDisplayName = "Open Brush"; - // This is the App name used when speaking to Google services - public const string kGoogleServicesAppName = kAppDisplayName; - // The name of the configuration file. You may want to change this if you think your users may - // want to have a different config file for your edition of the app. - public const string kConfigFileName = "Open Brush.cfg"; - // The name of the App folder (In the user's Documents folder) - original Tilt Brush used "Tilt Brush" - // If you are forking Open Brush, you may want to leave this as "Open Brush" or not. - public const string kAppFolderName = "Open Brush"; - // The data folder used on Google Drive. - public const string kDriveFolderName = kAppDisplayName; - - public const string kPlayerPrefHasPlayedBefore = "Has played before"; - public const string kReferenceImagesSeeded = "Reference Images seeded"; - public const string kBackgroundImagesSeeded = "Background Images seeded"; - - - private const string kDefaultConfigPath = "DefaultConfig"; - - private const int kHttpListenerPort = 40074; - private const string kProtocolHandlerPrefix = "tiltbrush://remix/"; - private const string kFileMoveFilename = "WhereHaveMyFilesGone.txt"; - - private const string kFileMoveContents = - "All your " + kAppDisplayName + " files have been moved to\n" + - "/sdcard/" + kAppFolderName + ".\n"; - - public enum AppState - { - Error, - LoadingBrushesAndLighting, - FadeFromBlack, - FirstRunIntro, - Intro, - Loading, - QuickLoad, - Standard, - MemoryExceeded, - Saving, - Reset, - Uploading, - AutoProfiling, - OfflineRendering, - } - - // ------------------------------------------------------------ - // Static API - // ------------------------------------------------------------ - - private static App m_Instance; - - // Accessible at all times after config is initialized. - public static Config Config => Config.m_SingletonState; - - public static UserConfig UserConfig => m_Instance.m_UserConfig; - - public static PlatformConfig PlatformConfig => Config.PlatformConfig; - - public static VrSdk VrSdk => m_Instance.m_VrSdk; - - public static SceneScript Scene => m_Instance.m_SceneScript; - - public static CanvasScript ActiveCanvas => Scene.ActiveCanvas; - - public static PolyAssetCatalog PolyAssetCatalog => m_Instance.m_PolyAssetCatalog; - - public static Switchboard Switchboard => m_Instance.m_Switchboard; - - public static BrushColorController BrushColor => m_Instance.m_BrushColorController; - - public static GroupManager GroupManager => m_Instance.m_GroupManager; - - public static HttpServer HttpServer => m_Instance.m_HttpServer; - - public static DriveAccess DriveAccess => m_Instance.m_DriveAccess; - public static DriveSync DriveSync => m_Instance.m_DriveSync; - - public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; - public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; - - // TODO Make these overridable - public static string ICOSA_WEBSITE_URL = "https://icosa.ixxy.co.uk"; - public static string ICOSA_API_URL = "https://icosa-api.ixxy.co.uk"; - - public string IcosaToken - { - get => PlayerPrefs.HasKey("IcosaToken") ? PlayerPrefs.GetString("IcosaToken") : null; - set => PlayerPrefs.SetString("IcosaToken", value); - } - public static bool IcosaIsLoggedIn => !string.IsNullOrEmpty(App.Instance.IcosaToken); - - public static string IcosaUserName; - public static string IcosaUserId; - public static Texture IcosaUserIcon; - - public static GoogleUserSettings GoogleUserSettings => m_Instance.m_GoogleUserSettings; - - /// Returns the App instance, or null if the app has not been initialized - /// with Awake(). Note that the App may not have had Start() called yet. - /// - /// Do not modify the script execution order if you only need inspector - /// data from App.Instance. Put the inspector data in App.Config instead. - public static App Instance - { - get { return m_Instance; } -#if UNITY_EDITOR - // Bleh. Needed by BuildTiltBrush.cs - internal set { m_Instance = value; } -#endif - } - - public static AppState CurrentState => m_Instance == null ? AppState.Loading : m_Instance.m_CurrentAppState; - - public static OAuth2Identity GetIdentity(Cloud cloud) - { - switch (cloud) - { - case Cloud.Poly: return GoogleIdentity; - case Cloud.Sketchfab: return SketchfabIdentity; - default: throw new InvalidOperationException($"No identity for {cloud}"); - } - } - - // Log to editor console when developing and console log when running. This avoids all the stack spam in the log. - public static void Log(string msg) - { -#if UNITY_EDITOR - Debug.Log("[OB] " + msg); -#else - Console.WriteLine("[OB] " + msg); -#endif - } - - // ------------------------------------------------------------ - // Events - // ------------------------------------------------------------ - - public event Action StateChanged; - - // ------------------------------------------------------------ - // Inspector data - // ------------------------------------------------------------ - // Unless otherwise stated, intended to be read-only even if public - - [Header("External References")] - [SerializeField] VrSdk m_VrSdk; - [SerializeField] SceneScript m_SceneScript; - - [Header("General inspector")] - [SerializeField] float m_FadeFromBlackDuration; - [SerializeField] float m_QuickLoadHintDelay = 2f; - - [SerializeField] GpuIntersector m_GpuIntersector; - - public TiltBrushManifest m_Manifest; - - // Previously Experimental-Mode only - [SerializeField] private TiltBrushManifest m_ManifestExperimental; - [SerializeField] private TiltBrushManifest m_ZapboxManifest; - - [SerializeField] private SelectionEffect m_SelectionEffect; - - /// The root object for the "Room" coordinate system - public Transform m_RoomTransform => transform; - /// The root object for the "Scene" coordinate system ("/SceneParent") - public Transform m_SceneTransform; - /// The root object for the "Canvas" coordinate system ("/SceneParent/Canvas") - /// TODO: remove, in favor of .ActiveCanvas.transform - public Transform m_CanvasTransform; - /// The object "/SceneParent/EnvironmentParent" - public Transform m_EnvironmentTransform; - [SerializeField] GameObject m_SketchSurface; - [SerializeField] GameObject m_ErrorDialog; - [SerializeField] GameObject m_OdsPrefab; - GameObject m_OdsPivot; - - [Header("Intro")] - [SerializeField] float m_IntroSketchFadeInDuration = 5.0f; - [SerializeField] float m_IntroSketchFadeOutDuration = 1.5f; - [SerializeField] float m_IntroSketchMobileFadeInDuration = 3.0f; - [SerializeField] float m_IntroSketchMobileFadeOutDuration = 1.5f; - - [SerializeField] FrameCountDisplay m_FrameCountDisplay; - - [SerializeField] private GameObject m_ShaderWarmup; - - [Header("Identities")] - [SerializeField] private OAuth2Identity m_GoogleIdentity; - [SerializeField] private OAuth2Identity m_SketchfabIdentity; - - // ------------------------------------------------------------ - // Private data - // ------------------------------------------------------------ - - /// Use C# event in preference to Unity callbacks because - /// Unity doesn't send callbacks to disabled objects - public event Action AppExit; - - private Queue m_RequestedTiltFileQueue = Queue.Synchronized(new Queue()); - private HttpServer m_HttpServer; - - private SketchSurfacePanel m_SketchSurfacePanel; - private UserConfig m_UserConfig; - private string m_UserPath; - private string m_OldUserPath; - - private PolyAssetCatalog m_PolyAssetCatalog; - private Switchboard m_Switchboard; - private BrushColorController m_BrushColorController; - private GroupManager m_GroupManager; - - /// Time origin of sketch in seconds for case when drawing is not sync'd to media. - private double m_sketchTimeBase = 0; - private float m_AppStateCountdown; - private float m_QuickLoadHintCountdown; - private bool m_QuickLoadInputWasValid; - private bool m_QuickLoadEatInput; - private AppState m_CurrentAppState; - private AppState m_DesiredAppState_; // Temporary: to narrow down b/37256058 - private AppState m_DesiredAppState - { - get => m_DesiredAppState_; - set - { - if (m_DesiredAppState_ != value) - { - Console.WriteLine("App State <- {0}", value); - } - m_DesiredAppState_ = value; - } - } - private int m_TargetFrameRate; - private float m_RoomRadius; - private bool m_AutosaveRestoreFileExists; - private bool m_ShowAutosaveHint = false; - private bool? m_ShowControllers; - private int m_QuickloadStallFrames; - - private GameObject m_IntroSketch; - private Renderer[] m_IntroSketchRenderers; - private float m_IntroFadeTimer; - - private bool m_FirstRunExperience; - private bool m_RequestingAudioReactiveMode; - private DriveAccess m_DriveAccess; - private DriveSync m_DriveSync; - private GoogleUserSettings m_GoogleUserSettings; - - // ------------------------------------------------------------ - // Properties - // ------------------------------------------------------------ - - /// Time spent in current sketch, in seconds. - /// On load, this is restored to the timestamp of the last stroke. - /// Updated per-frame. - public double CurrentSketchTime - { - // Unity's Time.time has useful precision probably <= 1ms, and unknown - // drift/accuracy. It is a single (but is a double, internally), so its - // raw precision drops to ~2ms after ~4 hours and so on. - // Time.timeSinceLevelLoad is also an option. - // - // C#'s DateTime API has low-ish precision (10+ ms depending on OS) - // but likely the highest accuracy with respect to wallclock, since - // it's reading from an RTC. - // - // High-precision timers are the opposite: high precision, but are - // subject to drift. - // - // For realtime sync, Time.time is probably the best thing to use. - // For postproduction sync, probably C# DateTime. - get - { - // If you change this, also modify SketchTimeToLevelLoadTime - return Time.timeSinceLevelLoad - m_sketchTimeBase; - } - set - { - if (value < 0) { throw new ArgumentException("negative"); } - m_sketchTimeBase = Time.timeSinceLevelLoad - value; - } - } - - public float RoomRadius => m_RoomRadius; - - public SelectionEffect SelectionEffect => m_SelectionEffect; - public bool IsFirstRunExperience => m_FirstRunExperience; - public bool HasPlayedBefore { get; private set; } - - public bool StartupError { get; set; } - - public bool ShowControllers - { - get => m_ShowControllers.GetValueOrDefault(true); - set - { - InputManager.m_Instance.ShowControllers(value); - m_ShowControllers = value; - } - } - - public bool AutosaveRestoreFileExists - { - get => m_AutosaveRestoreFileExists; - set - { - if (value != m_AutosaveRestoreFileExists) - { - try - { - string filePath = AutosaveRestoreFilePath(); - if (value) - { - var autosaveFile = File.Create(filePath); - autosaveFile.Close(); - } - else - { - if (File.Exists(filePath)) - { - File.Delete(filePath); - } - } - } - catch (IOException) { return; } - catch (UnauthorizedAccessException) { return; } - - m_AutosaveRestoreFileExists = value; - } - } - } - - public GpuIntersector GpuIntersector => m_GpuIntersector; - - public TrTransform OdsHeadPrimary { get; set; } - public TrTransform OdsScenePrimary { get; set; } - - public TrTransform OdsHeadSecondary { get; set; } - public TrTransform OdsSceneSecondary { get; set; } - - public FrameCountDisplay FrameCountDisplay => m_FrameCountDisplay; - - // ------------------------------------------------------------ - // Implementation - // ------------------------------------------------------------ - - public bool RequestingAudioReactiveMode => m_RequestingAudioReactiveMode; - - public void ToggleAudioReactiveModeRequest() - { - m_RequestingAudioReactiveMode ^= true; - } - - public void ToggleAudioReactiveBrushesRequest() - { - ToggleAudioReactiveModeRequest(); - AudioCaptureManager.m_Instance.CaptureAudio(m_RequestingAudioReactiveMode); - VisualizerManager.m_Instance.EnableVisuals(m_RequestingAudioReactiveMode); - Switchboard.TriggerAudioReactiveStateChanged(); - } - - public double SketchTimeToLevelLoadTime(double sketchTime) - { - return sketchTime + m_sketchTimeBase; - } - - public void SetOdsCameraTransforms(TrTransform headXf, TrTransform sceneXf) - { - if (Config.m_SdkMode != SdkMode.Ods) { return; } - OdsScenePrimary = sceneXf; - OdsHeadPrimary = headXf; - - // To simplify down-stream code, copy primary into secondary. - if (OdsHeadSecondary == new TrTransform()) - { - OdsHeadSecondary = headXf; - OdsSceneSecondary = sceneXf; - } - } - - // Tilt Brush code assumes the current directory is next to the Support/ - // folder. Enforce that assumption - static void SetCurrentDirectoryToApplication() - { - // dataPath is: - // Editor - /Assets - // Windows - TiltBrush_Data/ - // Linux - TiltBrush_Data/ - // OSX - TiltBrush.app/Contents/ -#if UNITY_STANDALONE_WIN - string oldDir = Directory.GetCurrentDirectory(); - string dataDir = UnityEngine.Application.dataPath; - string appDir = Path.GetDirectoryName(dataDir); - try - { - Directory.SetCurrentDirectory(appDir); - } - catch (Exception e) - { - Debug.LogErrorFormat("Couldn't set dir to {0}: {1}", appDir, e); - } - string curDir = Directory.GetCurrentDirectory(); - Debug.LogFormat("Dir {0} -> {1}", oldDir, curDir); -#endif - } - - void CreateIntroSketch() - { - // Load intro if not already cached. - if (m_IntroSketch == null && PlatformConfig.IntroSketchPrefab != null) - { - m_IntroSketch = Instantiate(PlatformConfig.IntroSketchPrefab); - m_IntroSketchRenderers = m_IntroSketch.GetComponentsInChildren(); - for (int i = 0; i < m_IntroSketchRenderers.Length; ++i) - { - m_IntroSketchRenderers[i].material.SetFloat("_IntroDissolve", 1); - m_IntroSketchRenderers[i].material.SetFloat("_GreyScale", 0); - } - } - } - - void DestroyIntroSketch() - { - Destroy(m_IntroSketch); - m_IntroSketchRenderers = null; - - // Eject the (rather large) intro sketch from memory. - // TODO: The Unity Way would be to put these prefab references and instantiations - // in an additive scene. - // Don't do this in the editor, because it mutates the asset on disk! -#if !UNITY_EDITOR - PlatformConfig.IntroSketchPrefab = null; -#endif - Resources.UnloadUnusedAssets(); - } - - static string GetStartupString() - { - string str = $"{App.kAppDisplayName} {Config.m_VersionNumber}"; - - if (!string.IsNullOrEmpty(Config.m_BuildStamp)) - str += $" build {Config.m_BuildStamp}"; - -#if UNITY_ANDROID - str += $" code {AndroidUtils.GetVersionCode()}"; -#endif -#if DEBUG - str += $" {PlatformConfig.name}"; -#endif - return str; - } - - void Awake() - { - m_Instance = this; - Log(GetStartupString()); - Log($"SdkMode: {App.Config.m_SdkMode}."); - - // Begone, physics! You were using 0.3 - 1.3ms per frame on Quest! - Physics.autoSimulation = false; - - // See if this is the first time - HasPlayedBefore = PlayerPrefs.GetInt(kPlayerPrefHasPlayedBefore, 0) == 1; - -#if ZAPBOX_SUPPORTED - // TODO:Mikesky - fix zapbox support. - HasPlayedBefore = true; -#endif - - // Copy files into Support directory - CopySupportFiles(); - - InitUserPath(); - SetCurrentDirectoryToApplication(); - Coords.Init(this); - Scene.Init(); - CreateDefaultConfig(); - RefreshUserConfig(); - CameraConfig.Init(); - if (!string.IsNullOrEmpty(m_UserConfig.Profiling.SketchToLoad)) - { - Config.m_SketchFiles = new string[] { m_UserConfig.Profiling.SketchToLoad }; - } - - if (m_UserConfig.Testing.FirstRun) - { - PlayerPrefs.DeleteKey(kPlayerPrefHasPlayedBefore); - PlayerPrefs.DeleteKey(kReferenceImagesSeeded); - PlayerPrefs.DeleteKey(PanelManager.kPlayerPrefAdvancedMode); - AdvancedPanelLayouts.ClearPlayerPrefs(); - PointerManager.ClearPlayerPrefs(); - HasPlayedBefore = false; - } - // Cache this variable for the length of the play session. HasPlayedBefore will be updated, - // but m_FirstRunExperience should not. - m_FirstRunExperience = !HasPlayedBefore; - - m_Switchboard = new Switchboard(); - m_GroupManager = new GroupManager(); - - m_PolyAssetCatalog = GetComponent(); - m_PolyAssetCatalog.Init(); - - m_BrushColorController = GetComponent(); - - // Tested on Windows. I hope they don't change the names of these preferences. - PlayerPrefs.DeleteKey("Screenmanager Is Fullscreen mode"); - PlayerPrefs.DeleteKey("Screenmanager Resolution Height"); - PlayerPrefs.DeleteKey("Screenmanager Resolution Width"); - - if (DevOptions.I.UseAutoProfiler) - { - gameObject.AddComponent(); - } - - m_Manifest = GetMergedManifest(); - - m_HttpServer = GetComponentInChildren(); - if (!Config.IsMobileHardware) - { - HttpServer.AddHttpHandler("/load", HttpLoadSketchCallback); - } - - m_AutosaveRestoreFileExists = File.Exists(AutosaveRestoreFilePath()); - - m_GoogleUserSettings = new GoogleUserSettings(m_GoogleIdentity); - m_DriveAccess = new DriveAccess(m_GoogleIdentity, m_GoogleUserSettings); - m_DriveSync = new DriveSync(m_DriveAccess, m_GoogleIdentity); - } - - // TODO: should add OnDestroy to other scripts that create background tasks - void OnDestroy() - { - if (!Config.IsMobileHardware) - { - HttpServer.RemoveHttpHandler("/load"); - } - - if (m_DriveSync != null) - { - m_DriveSync.UninitializeAsync().AsAsyncVoid(); - } - if (m_DriveAccess != null) - { - m_DriveAccess.UninitializeAsync().AsAsyncVoid(); - } - } - - // Called from HttpListener thread. Supported requests: - // /load? - // Loads sketch given path on local filesystem. Any pending load is canceled. - // Response body: none - string HttpLoadSketchCallback(HttpListenerRequest request) - { - var urlPath = request.Url.LocalPath; - var query = Uri.UnescapeDataString(request.Url.Query); - if (urlPath == "/load" && query.Length > 1) - { - var filePath = query.Substring(1); - m_RequestedTiltFileQueue.Enqueue(filePath); - } - return ""; - } - - void Start() - { - // Use of ControllerConsoleScript must wait until Start() - ControllerConsoleScript.m_Instance.AddNewLine(GetStartupString()); - - if (!VrSdk.IsHmdInitialized() && !UserConfig.Flags.EnableMonoscopicMode) - { - // If XR is disabled or fails to initialize - // and we haven't enabled monoscopic mode - // then fall back to the 2d View-only mode - CreateFailedToDetectVrDialog(); - } - else - { - Debug.LogFormat("Sdk mode: {0} XRDevice.model: {1}", - Config.m_SdkMode, - UnityEngine.XR.InputDevices.GetDeviceAtXRNode(UnityEngine.XR.XRNode.Head).manufacturer); - } - - m_TargetFrameRate = VrSdk.GetHmdTargetFrameRate(); - if (VrSdk.GetHmdDof() == VrSdk.DoF.None) - { - Application.targetFrameRate = m_TargetFrameRate; - } - - if (VrSdk.HasRoomBounds()) - { - Vector3 extents = VrSdk.GetRoomExtents(); - m_RoomRadius = Mathf.Min(Mathf.Abs(extents.x), Mathf.Abs(extents.z)); - } - -#if USD_SUPPORTED - // Load the Usd Plugins - InitUsd.Initialize(); -#endif - - foreach (string s in Config.m_SketchFiles) - { - // Assume all relative paths are relative to the Sketches directory. - string sketch = s; - if (!System.IO.Path.IsPathRooted(sketch)) - { - sketch = System.IO.Path.Combine(App.UserSketchPath(), sketch); - } - m_RequestedTiltFileQueue.Enqueue(sketch); - if (Config.m_SdkMode == SdkMode.Ods || Config.OfflineRender) - { - // We only load one sketch for ODS rendering & offline rendering. - break; - } - } - - if (Config.m_AutosaveRestoreEnabled && AutosaveRestoreFileExists) - { - string lastAutosave = SaveLoadScript.m_Instance.MostRecentAutosaveFile(); - if (lastAutosave != null) - { - string newPath = SaveLoadScript.m_Instance.GenerateNewUntitledFilename( - UserSketchPath(), SaveLoadScript.TILT_SUFFIX); - if (newPath != null) - { - File.Copy(lastAutosave, newPath); - m_ShowAutosaveHint = true; - } - } - AutosaveRestoreFileExists = false; - } - - if (Config.m_SdkMode == SdkMode.Ods) - { - m_OdsPivot = (GameObject)Instantiate(m_OdsPrefab); - - OdsDriver driver = m_OdsPivot.GetComponent(); - driver.FramesToCapture = Config.m_OdsNumFrames; - driver.m_fps = Config.m_OdsFps; - driver.TurnTableRotation = Config.m_OdsTurnTableDegrees; - driver.OutputFolder = Config.m_OdsOutputPath; - driver.OutputBasename = Config.m_OdsOutputPrefix; - if (!string.IsNullOrEmpty(App.Config.m_VideoPathToRender)) - { - driver.CameraPath = App.Config.m_VideoPathToRender; - } - - ODS.HybridCamera cam = driver.OdsCamera; - cam.CollapseIpd = Config.m_OdsCollapseIpd; - cam.imageWidth /= Config.m_OdsPreview ? 4 : 1; - Debug.LogFormat("Configuring ODS:{0}" + - "Frames: {1}{0}" + - "FPS: {8}{0}" + - "TurnTable: {2}{0}" + - "Output: {3}{0}" + - "Basename: {4}{0}" + - "QuickLoad: {5}{0}" + - "CollapseIPD: {6}{0}" + - "ImageWidth: {7}{0}", - System.Environment.NewLine, - driver.FramesToCapture, - driver.TurnTableRotation, - driver.OutputFolder, - driver.OutputBasename, - Config.m_QuickLoad, - cam.CollapseIpd, - cam.imageWidth, - driver.m_fps); - } - - //these guys don't need to be alive just yet - PointerManager.m_Instance.EnablePointerStrokeGeneration(false); - - Console.WriteLine("RenderODS: {0}, numFrames: {1}", - m_OdsPivot != null, - m_OdsPivot ? m_OdsPivot.GetComponent().FramesToCapture - : 0); - - if (!AppAllowsCreation()) - { - TutorialManager.m_Instance.IntroState = IntroTutorialState.InitializeForNoCreation; - } - else - { - TutorialManager.m_Instance.IntroState = IntroTutorialState.Done; - } - if (m_RequestedTiltFileQueue.Count == 0) - { - TutorialManager.m_Instance.ActivateControllerTutorial(InputManager.ControllerName.Brush, false); - TutorialManager.m_Instance.ActivateControllerTutorial(InputManager.ControllerName.Wand, false); - } - - ViewpointScript.m_Instance.Init(); - QualityControls.m_Instance.Init(); - bool bVR = VrSdk.GetHmdDof() != TiltBrush.VrSdk.DoF.None; - InputManager.m_Instance.AllowVrControllers = bVR; - PointerManager.m_Instance.UseSymmetryWidget(bVR); - - switch (VrSdk.GetControllerDof()) - { - case TiltBrush.VrSdk.DoF.Six: - // Vive, Rift + Touch - SketchControlsScript.m_Instance.ActiveControlsType = - SketchControlsScript.ControlsType.SixDofControllers; - break; - case TiltBrush.VrSdk.DoF.None: - SketchControlsScript.m_Instance.ActiveControlsType = - SketchControlsScript.ControlsType.ViewingOnly; - break; - case TiltBrush.VrSdk.DoF.Two: - // Monoscopic - SketchControlsScript.m_Instance.ActiveControlsType = - SketchControlsScript.ControlsType.KeyboardMouse; - break; - } - - m_CurrentAppState = AppState.Standard; - m_DesiredAppState = AppState.LoadingBrushesAndLighting; - if (StartupError) - { - m_DesiredAppState = AppState.Error; - } - - m_SketchSurfacePanel = m_SketchSurface.GetComponent(); - - ViewpointScript.m_Instance.SetHeadMeshVisible(App.UserConfig.Flags.ShowHeadset); - ShowControllers = App.UserConfig.Flags.ShowControllers; - - SwitchState(); - - if (Config.m_AutoProfile || m_UserConfig.Profiling.AutoProfile) - { - StateChanged += AutoProfileOnStartAndQuit; - } - } - - private void AutoProfileOnStartAndQuit(AppState oldState, AppState newState) - { - if (newState == AppState.Standard) - { - Invoke("AutoProfileAndQuit", Config.m_AutoProfileWaitTime); - StateChanged -= AutoProfileOnStartAndQuit; - } - } - - private void AutoProfileAndQuit() - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.DoAutoProfileAndQuit); - } - - public void SetDesiredState(AppState rDesiredState) - { - m_DesiredAppState = rDesiredState; - } - - void Update() - { -#if UNITY_EDITOR - // All changes to Scene transform must go through Coords.cs - if (m_SceneTransform.hasChanged) - { - Debug.LogError("Detected unsanctioned change to Scene transform"); - m_SceneTransform.hasChanged = false; - } -#endif - - //look for state change - if (m_CurrentAppState != m_DesiredAppState) - { - SwitchState(); - } - - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate)) - { - // kinda heavy-handed, but whatevs - InitCursor(); - } - - // Wait for the environment transition to complete before capturing. - if (m_OdsPivot - && !m_OdsPivot.activeInHierarchy - && !SceneSettings.m_Instance.IsTransitioning - && ((m_CurrentAppState == AppState.Loading && !Config.m_QuickLoad) - || m_CurrentAppState == AppState.Standard)) - { - try - { - OdsDriver driver = m_OdsPivot.GetComponent(); - - // Load the secondary transform, if a second sketch was specified. - if (Config.m_SketchFiles.Length > 1) - { - string sketch = Config.m_SketchFiles[1]; - // Assume relative paths are relative to the sketches directory. - if (!System.IO.Path.IsPathRooted(sketch)) - { - sketch = System.IO.Path.Combine(App.UserSketchPath(), sketch); - } - var head = TrTransform.identity; - var scene = TrTransform.identity; - if (SaveLoadScript.m_Instance.LoadTransformsForOds(new DiskSceneFileInfo(sketch), - ref head, - ref scene)) - { - OdsHeadSecondary = head; - OdsSceneSecondary = scene; - } - else - { - Debug.LogErrorFormat("Failed to load secondary sketch for ODS: {0}", sketch); - } - } - - if (driver.OutputBasename == null || driver.OutputBasename == "") - { - driver.OutputBasename = - FileUtils.SanitizeFilename(SaveLoadScript.m_Instance.SceneFile.HumanName); - if (driver.OutputBasename == null || driver.OutputBasename == "") - { - if (Config.m_SketchFiles.Length > 0) - { - driver.OutputBasename = System.IO.Path.GetFileNameWithoutExtension( - Config.m_SketchFiles[0]); - } - else - { - driver.OutputBasename = "Untitled"; - } - } - } - - if (driver.OutputFolder == null || driver.OutputFolder == "") - { - driver.OutputFolder = App.VrVideosPath(); - FileUtils.InitializeDirectoryWithUserError(driver.OutputFolder); - } - - InputManager.m_Instance.EnablePoseTracking(false); - - driver.BeginRender(); - } - catch (System.Exception ex) - { - Debug.LogException(ex); - Application.Quit(); - Debug.Break(); - } - } - - m_PolyAssetCatalog.UpdateCatalog(); - - //update state - switch (m_CurrentAppState) - { - case AppState.LoadingBrushesAndLighting: - { - if (!BrushCatalog.m_Instance.IsLoading - && !EnvironmentCatalog.m_Instance.IsLoading - && !m_ShaderWarmup.activeInHierarchy) - { - if (AppAllowsCreation()) - { - BrushController.m_Instance.SetBrushToDefault(); - BrushColor.SetColorToDefault(); - } - else - { - PointerManager.m_Instance.SetBrushForAllPointers(BrushCatalog.m_Instance.DefaultBrush); - } - - AudioManager.Enabled = true; - SceneSettings.m_Instance.SetDesiredPreset(EnvironmentCatalog.m_Instance.DefaultEnvironment); - - bool skipStandardIntro = true; - if (HandleExternalTiltOpenRequest()) - { - // tilt requested on command line was loaded - } - else if (Config.m_FilePatternsToExport != null) - { - m_DesiredAppState = AppState.Standard; - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.ExportListed); - } - else if (Config.OfflineRender) - { - m_DesiredAppState = AppState.Standard; - } - else if (DemoManager.m_Instance.DemoModeEnabled) - { - OnIntroComplete(); - } - else if (!VrSdk.IsHmdInitialized() || - UserConfig.Flags.SkipIntro || - UserConfig.Flags.DisableXrMode || - UserConfig.Flags.EnableMonoscopicMode) - { - OnIntroComplete(); - PanelManager.m_Instance.ReviveFloatingPanelsForStartup(); - } - else - { - if (Config.m_SdkMode == SdkMode.Ods) - { - // Skip the fade from black when we're rendering ODS. - m_DesiredAppState = AppState.Standard; - } - else - { - m_DesiredAppState = AppState.FadeFromBlack; - skipStandardIntro = false; - } - } - - if (skipStandardIntro) - { - DestroyIntroSketch(); - ViewpointScript.m_Instance.FadeToScene(float.MaxValue); - } - } - break; - } - case AppState.FadeFromBlack: - { - // On the Oculus platform, the Health and Safety warning may be visible, blocking the - // user's view. If this is the case, hold black until the warning is dismissed. - if (!VrSdk.IsAppFocusBlocked() || Config.m_SdkMode == SdkMode.Ods) - { - m_AppStateCountdown -= Time.deltaTime; - } - - if (m_AppStateCountdown <= 0.0f) - { - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - m_AppStateCountdown = 0; - if (!HasPlayedBefore) - { - m_DesiredAppState = AppState.FirstRunIntro; - } - else - { - m_DesiredAppState = AppState.Intro; - } - } - break; - } - case AppState.FirstRunIntro: - { - if (UpdateIntroFadeIsFinished()) - { - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - SaveLoadScript.m_Instance.NewAutosaveFile(); - m_DesiredAppState = AppState.Standard; - } - break; - } - case AppState.Intro: - { - if (UpdateIntroFadeIsFinished()) - { - if (!Config.IsMobileHardware) - { - InputManager.Brush.Behavior.BuzzAndGlow(1.0f, 7, .1f); - InputManager.Wand.Behavior.BuzzAndGlow(1.0f, 7, .1f); - AudioManager.m_Instance.PlayMagicControllerSound(); - } - PanelManager.m_Instance.ShowIntroSketchbookPanels(); - PointerManager.m_Instance.IndicateBrushSize = false; - PromoManager.m_Instance.RequestPromo(PromoType.InteractIntroPanel); - OnIntroComplete(); - } - break; - } - case AppState.Loading: - { - HandleExternalTiltOpenRequest(); - SketchControlsScript.m_Instance.UpdateControlsForLoading(); - - if (WidgetManager.m_Instance.CreatingMediaWidgets) - { - break; - } - - //trigger our tutorial a little bit after we started loading so it doesn't show up immediately - if (!m_QuickLoadEatInput) - { - float fPrevTutorialValue = m_QuickLoadHintCountdown; - m_QuickLoadHintCountdown -= Time.deltaTime; - if (fPrevTutorialValue > 0.0f && m_QuickLoadHintCountdown <= 0.0f) - { - TutorialManager.m_Instance.EnableQuickLoadTutorial(true); - } - } - - if (OverlayManager.m_Instance.CanDisplayQuickloadOverlay) - { - // Watch for speed up button presses and keep on loadin' - // Don't allow for quickloading yet if we are fading out the overlay from loading media. - UpdateQuickLoadLogic(); - } - if ((m_OdsPivot && Config.m_QuickLoad) || - (Config.OfflineRender) || !string.IsNullOrEmpty(m_UserConfig.Profiling.SketchToLoad)) - { - m_DesiredAppState = AppState.QuickLoad; - } - - // Call ContinueDrawingFromMemory() unless we are rendering ODS, in which case we don't want - // to animate the strokes until the renderer has actually started. - bool bContinueDrawing = true; - if (Config.m_SdkMode != SdkMode.Ods || m_OdsPivot.GetComponent().IsRendering) - { - bContinueDrawing = SketchMemoryScript.m_Instance.ContinueDrawingFromMemory(); - } - if (!bContinueDrawing) - { - FinishLoading(); - InputManager.m_Instance.TriggerHapticsPulse( - InputManager.ControllerName.Brush, 4, 0.15f, 0.1f); - InputManager.m_Instance.TriggerHapticsPulse( - InputManager.ControllerName.Wand, 4, 0.15f, 0.1f); - } - break; - } - case AppState.QuickLoad: - { - // Allow extra frames to complete fade to black. - // Required for OVR to position the overlay because it only does so once the transition - // is complete. - if (m_QuickloadStallFrames-- < 0) - { - bool bContinueDrawing = SketchMemoryScript.m_Instance.ContinueDrawingFromMemory(); - if (!bContinueDrawing) - { - FinishLoading(); - } - } - break; - } - case AppState.Uploading: - SketchControlsScript.m_Instance.UpdateControlsForUploading(); - break; - case AppState.MemoryExceeded: - SketchControlsScript.m_Instance.UpdateControlsForMemoryExceeded(); - break; - case AppState.Standard: - // Logic for fading out intro sketches. - if (m_IntroFadeTimer > 0 && - !PanelManager.m_Instance.IntroSketchbookMode && - !TutorialManager.m_Instance.TutorialActive()) - { - if (UpdateIntroFadeIsFinished()) - { - PanelManager.m_Instance.ReviveFloatingPanelsForStartup(); - } - } - - // If the app doesn't have focus, don't update. - if (VrSdk.IsAppFocusBlocked() && Config.m_SdkMode != SdkMode.Ods) - { - break; - } - - // Intro tutorial state machine. - TutorialManager.m_Instance.UpdateIntroTutorial(); - - // Continue edit-time playback, if any. - SketchMemoryScript.m_Instance.ContinueDrawingFromMemory(); - if (PanelManager.m_Instance.SketchbookActiveIncludingTransitions() && - PanelManager.m_Instance.IntroSketchbookMode) - { - // Limit controls if the user hasn't exited from the sketchbook post intro. - SketchControlsScript.m_Instance.UpdateControlsPostIntro(); - } - else - { - SketchControlsScript.m_Instance.UpdateControls(); - } - - // This should happen after SMS.ContinueDrawingFromMemory, so we're not loading and - // continuing in one frame. - HandleExternalTiltOpenRequest(); - break; - case AppState.Reset: - SketchControlsScript.m_Instance.UpdateControls(); - if (!PointerManager.m_Instance.IsMainPointerCreatingStroke() && - !PointerManager.m_Instance.IsMainPointerProcessingLine()) - { - StartReset(); - } - break; - } - } - - public void ExitIntroSketch() - { - PanelManager.m_Instance.SetInIntroSketchbookMode(false); - PointerManager.m_Instance.IndicateBrushSize = true; - PointerManager.m_Instance.PointerColor = PointerManager.m_Instance.PointerColor; - PromoManager.m_Instance.RequestPromo(PromoType.BrushSize); - } - - private void StartReset() - { - // Switch to paint tool if not already there. - SketchSurfacePanel.m_Instance.EnableDefaultTool(); - - // Disable preview line. - PointerManager.m_Instance.AllowPointerPreviewLine(false); - - // Switch to the default brush type and size. - BrushController.m_Instance.SetBrushToDefault(); - - // Disable audio reactive mode. - if (m_RequestingAudioReactiveMode) - { - ToggleAudioReactiveModeRequest(); - } - - // Reset to the default brush color. - BrushColor.SetColorToDefault(); - - // Clear saved colors. - CustomColorPaletteStorage.m_Instance.ClearAllColors(); - - // Turn off straightedge - PointerManager.m_Instance.StraightEdgeModeEnabled = false; - - // Turn off straightedge ruler. - if (PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter()) - { - PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); - } - - // Close any panel menus that might be open (e.g. Sketchbook) - if (PanelManager.m_Instance.SketchbookActive()) - { - PanelManager.m_Instance.ToggleSketchbookPanels(); - } - else if (PanelManager.m_Instance.SettingsActive()) - { - PanelManager.m_Instance.ToggleSettingsPanels(); - } - else if (PanelManager.m_Instance.MemoryWarningActive()) - { - PanelManager.m_Instance.ToggleMemoryWarningMode(); - } - else if (PanelManager.m_Instance.BrushLabActive()) - { - PanelManager.m_Instance.ToggleBrushLabPanels(); - } - - // Hide all panels. - SketchControlsScript.m_Instance.RequestPanelsVisibility(false); - - // Reset all panels. - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.ResetAllPanels); - - // Rotate want panels to default orientation (color picker). - PanelManager.m_Instance.ResetWandPanelRotation(); - - // Close Twitch widget. - if (SketchControlsScript.m_Instance.IsCommandActive(SketchControlsScript.GlobalCommands.IRC)) - { - SketchControlsScript.m_Instance.IssueGlobalCommand(SketchControlsScript.GlobalCommands.IRC); - } - - // Close Youtube Chat widget. - if (SketchControlsScript.m_Instance.IsCommandActive( - SketchControlsScript.GlobalCommands.YouTubeChat)) - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.YouTubeChat); - } - - // Hide the pointer and reticle. - PointerManager.m_Instance.RequestPointerRendering(false); - SketchControlsScript.m_Instance.ForceShowUIReticle(false); - } - - private void FinishReset() - { - // Switch to the default environment. - SceneSettings.m_Instance.SetDesiredPreset(EnvironmentCatalog.m_Instance.DefaultEnvironment); - - // Clear the sketch and reset the scene transform. - SketchControlsScript.m_Instance.NewSketch(fade: false); - - // Disable mirror. - PointerManager.m_Instance.SetSymmetryMode(PointerManager.SymmetryMode.None); - - // Reset mirror position. - PointerManager.m_Instance.ResetSymmetryToHome(); - - // Show the wand panels. - SketchControlsScript.m_Instance.RequestPanelsVisibility(true); - - // Show the pointer. - PointerManager.m_Instance.RequestPointerRendering(true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - - // Forget command history. - SketchMemoryScript.m_Instance.ClearMemory(); - } - - void FinishLoading() - { - // Force progress to be full before exiting (for small scenes) - OverlayManager.m_Instance.UpdateProgress(1); - OverlayManager.m_Instance.HideOverlay(); - //if we just released the button, kick a fade out - if (m_QuickLoadInputWasValid) - { - OverlayManager.m_Instance.PauseRendering(false); - OverlayManager.m_Instance.FadeFromCompositor(0); - } - - m_DesiredAppState = AppState.Standard; - if (VrSdk.GetControllerDof() == TiltBrush.VrSdk.DoF.Six) - { - float holdDelay = (m_CurrentAppState == AppState.QuickLoad) ? 1.0f : 0.0f; - StartCoroutine(DelayedSketchLoadedCard(holdDelay)); - } - else - { - OutputWindowScript.m_Instance.AddNewLine( - OutputWindowScript.LineType.Special, "Sketch Loaded!"); - } - - OnPlaybackComplete(); - m_SketchSurfacePanel.EnableRenderer(true); - - //turn off quick load tutorial - TutorialManager.m_Instance.EnableQuickLoadTutorial(false); - - AudioManager.m_Instance.PlaySketchLoadedSound( - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); - - SketchControlsScript.m_Instance.RequestPanelsVisibility(true); - if (VideoRecorderUtils.ActiveVideoRecording == null) - { - SketchSurfacePanel.m_Instance.EatToolsInput(); - } - SketchSurfacePanel.m_Instance.RequestHideActiveTool(false); - SketchControlsScript.m_Instance.RestoreFloatingPanels(); - PointerManager.m_Instance.RequestPointerRendering( - SketchSurfacePanel.m_Instance.ShouldShowPointer()); - PointerManager.m_Instance.RestoreBrushInfo(); - WidgetManager.m_Instance.LoadingState(false); - WidgetManager.m_Instance.WidgetsDormant = true; - SketchControlsScript.m_Instance.EatGrabInput(); - SaveLoadScript.m_Instance.MarkAsAutosaveDone(); - if (SaveLoadScript.m_Instance.SceneFile.InfoType == FileInfoType.Disk) - { - PromoManager.m_Instance.RequestAdvancedPanelsPromo(); - } - SketchMemoryScript.m_Instance.SanitizeMemoryList(); - - if (Config.OfflineRender) - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.RenderCameraPath); - } - - Scene.BroadcastCanvasUpdate(); - } - - private IEnumerator DelayedSketchLoadedCard(float delay) - { - float stall = delay; - while (stall >= 0.0f) - { - stall -= Time.deltaTime; - yield return null; - } - - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, "Sketch Loaded!"); - } - - void SwitchState() - { - switch (m_CurrentAppState) - { - case AppState.LoadingBrushesAndLighting: - if (VrSdk.GetControllerDof() == VrSdk.DoF.Two) - { - // Sketch surface tool is not properly loaded because - // it is the default tool. - SketchSurfacePanel.m_Instance.ActiveTool.EnableTool(false); - SketchSurfacePanel.m_Instance.ActiveTool.EnableTool(true); - } - break; - case AppState.Reset: - // Demos should reset to the standard state only. - Debug.Assert(m_DesiredAppState == AppState.Standard); - FinishReset(); - break; - case AppState.AutoProfiling: - case AppState.OfflineRendering: - InputManager.m_Instance.EnablePoseTracking(true); - break; - case AppState.MemoryExceeded: - SketchSurfacePanel.m_Instance.EnableDefaultTool(); - PanelManager.m_Instance.ToggleMemoryWarningMode(); - PointerManager.m_Instance.RequestPointerRendering( - SketchSurfacePanel.m_Instance.ShouldShowPointer()); - break; - } - - switch (m_DesiredAppState) - { - case AppState.LoadingBrushesAndLighting: - BrushCatalog.m_Instance.BeginReload(); - EnvironmentCatalog.m_Instance.BeginReload(); - CreateIntroSketch(); - break; - case AppState.FadeFromBlack: - ViewpointScript.m_Instance.FadeToScene(1.0f / m_FadeFromBlackDuration); - m_AppStateCountdown = m_FadeFromBlackDuration; - break; - case AppState.FirstRunIntro: - AudioManager.m_Instance.PlayFirstRunMusic(AudioManager.FirstRunMusic.IntroAmbient); - m_SketchSurfacePanel.EnableRenderer(false); - TutorialManager.m_Instance.IntroState = IntroTutorialState.ActivateBrush; - m_IntroFadeTimer = 0; - break; - case AppState.Intro: - AudioManager.m_Instance.PlayFirstRunMusic(AudioManager.FirstRunMusic.IntroAmbient); - m_SketchSurfacePanel.EnableRenderer(false); - m_IntroFadeTimer = 0; - break; - case AppState.Loading: - if (m_IntroFadeTimer > 0) - { - AudioManager.m_Instance.SetMusicVolume(0.0f); - m_IntroFadeTimer = 0; - DestroyIntroSketch(); - PanelManager.m_Instance.ReviveFloatingPanelsForStartup(); - } - PointerManager.m_Instance.StoreBrushInfo(); - m_QuickLoadHintCountdown = m_QuickLoadHintDelay; - m_QuickLoadEatInput = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Panic); - m_QuickLoadInputWasValid = false; - - // Don't disable tools if we've got a valid load tool active. - bool bToolsAllowed = SketchSurfacePanel.m_Instance.ActiveTool.AvailableDuringLoading(); - if (!bToolsAllowed) - { - m_SketchSurfacePanel.EnableRenderer(false); - } - else - { - m_SketchSurfacePanel.RequestHideActiveTool(false); - } - - if (!bToolsAllowed) - { - SketchSurfacePanel.m_Instance.EnableDefaultTool(); - } - PointerManager.m_Instance.RequestPointerRendering(false); - SketchControlsScript.m_Instance.RequestPanelsVisibility(false); - SketchControlsScript.m_Instance.ResetActivePanel(); - PanelManager.m_Instance.HideAllPanels(); - SketchControlsScript.m_Instance.ForceShowUIReticle(false); - PointerManager.m_Instance.SetSymmetryMode(PointerManager.SymmetryMode.None, false); - WidgetManager.m_Instance.LoadingState(true); - WidgetManager.m_Instance.StencilsDisabled = true; - break; - case AppState.QuickLoad: - SketchMemoryScript.m_Instance.QuickLoadDrawingMemory(); - break; - case AppState.MemoryExceeded: - if (!PanelManager.m_Instance.MemoryWarningActive()) - { - PanelManager.m_Instance.ToggleMemoryWarningMode(); - } - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.EmptyTool); - AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); - break; - case AppState.Standard: - PointerManager.m_Instance.DisablePointerPreviewLine(); - // Refresh the tinting on the controllers - PointerManager.m_Instance.PointerColor = PointerManager.m_Instance.PointerColor; - - if (m_ShowAutosaveHint) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController(InputManager.ControllerName.Wand, - "Abnormal program termination detected!\n" + - "The last autosave has been copied into your sketchbook."); - m_ShowAutosaveHint = false; - } - break; - case AppState.Reset: - PointerManager.m_Instance.EnablePointerStrokeGeneration(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.EatLineEnabledInput(); - PointerManager.m_Instance.EnableLine(false); - break; - case AppState.AutoProfiling: - case AppState.OfflineRendering: - InputManager.m_Instance.EnablePoseTracking(false); - break; - } - - var oldState = m_CurrentAppState; - m_CurrentAppState = m_DesiredAppState; - if (StateChanged != null) - { - StateChanged(oldState, m_CurrentAppState); - } - } - - /// Load one requested sketch, if any, returning true if pending request was processed. - private bool HandleExternalTiltOpenRequest() - { - // Early out if we're in the intro tutorial. - if (TutorialManager.m_Instance.TutorialActive() || m_RequestedTiltFileQueue.Count == 0) - { - return false; - } - - string path; - try - { - path = (string)m_RequestedTiltFileQueue.Dequeue(); - } - catch (InvalidOperationException) - { - return false; - } - Debug.LogFormat("Received external request to load {0}", path); - - if (path.StartsWith(kProtocolHandlerPrefix)) - { - return HandlePolyRequest(path); - } - - // Copy to sketch folder in order to discourage the user from explicitly saving - // to gallery for future access, which would (by design) strip attribution. - // Crypto hash suffix is added to the filename for (deterministic) uniqueness. - try - { - string dstFilename = Path.GetFileName(path); - if (Path.GetFullPath(Path.GetDirectoryName(path)) != Path.GetFullPath(UserSketchPath()) && - SaveLoadScript.Md5Suffix(dstFilename) == null) - { - dstFilename = SaveLoadScript.AddMd5Suffix(dstFilename, - SaveLoadScript.GetMd5(path)); - } - string dstPath = Path.Combine(UserSketchPath(), dstFilename); - if (!File.Exists(dstPath)) - { - File.Copy(path, dstPath); - } - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.LoadNamedFile, sParam: dstPath); - } - catch (FileNotFoundException) - { - OutputWindowScript.Error(String.Format("Couldn't open {0}", path)); - return false; - } - return true; - } - - private bool HandlePolyRequest(string request) - { - string id = request.Substring(kProtocolHandlerPrefix.Length); // Strip prefix to get asset id - StartCoroutine(VrAssetService.m_Instance.LoadTiltFile(id)); - return true; - } - - public bool ShouldTintControllers() - { - return m_DesiredAppState == AppState.Standard && !PanelManager.m_Instance.IntroSketchbookMode; - } - - public bool IsInStateThatAllowsPainting() - { - return !TutorialManager.m_Instance.TutorialActive() && - CurrentState == AppState.Standard && - !PanelManager.m_Instance.IntroSketchbookMode; - } - - public bool IsInStateThatAllowsAnyGrabbing() - { - return !TutorialManager.m_Instance.TutorialActive() && - !PanelManager.m_Instance.IntroSketchbookMode && - (CurrentState == AppState.Standard || CurrentState == AppState.Loading) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - public bool IsLoading() - { - return CurrentState == AppState.Loading || CurrentState == AppState.QuickLoad; - } - - void UpdateQuickLoadLogic() - { - if (CurrentState == AppState.Loading && AppAllowsCreation()) - { - //require the user to stop holding the trigger before pulling it again to speed load - if (m_QuickLoadEatInput) - { - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Panic)) - { - m_QuickLoadEatInput = false; - } - } - else - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Panic) && - !SketchControlsScript.m_Instance.IsUserInteractingWithAnyWidget() && - !SketchControlsScript.m_Instance.IsUserGrabbingWorld() && - (VideoRecorderUtils.ActiveVideoRecording == null) && - (!VrSdk.IsAppFocusBlocked() || Config.m_SdkMode == SdkMode.Ods)) - { - OverlayManager.m_Instance.SetOverlayFromType(OverlayType.LoadSketch); - //if we just pressed the button, kick a fade in - if (!m_QuickLoadInputWasValid) - { - if (ViewpointScript.m_Instance.AllowsFading) - { - OverlayManager.m_Instance.FadeToCompositor(0); - } - else - { - ViewpointScript.m_Instance.SetOverlayToBlack(); - } - OverlayManager.m_Instance.PauseRendering(true); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); - } - - m_QuickLoadInputWasValid = true; - if (m_CurrentAppState != AppState.QuickLoad) - { - OverlayManager.m_Instance.SetOverlayTransitionRatio(1.0f); - m_QuickloadStallFrames = 1; - m_DesiredAppState = AppState.QuickLoad; - m_SketchSurfacePanel.EnableRenderer(false); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.1f); - } - } - else - { - //if we just released the button, kick a fade out - if (m_QuickLoadInputWasValid) - { - OverlayManager.m_Instance.PauseRendering(false); - OverlayManager.m_Instance.FadeFromCompositor(0); - } - m_QuickLoadInputWasValid = false; - } - } - } - } - - void OnIntroComplete() - { - SaveLoadScript.m_Instance.NewAutosaveFile(); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - SketchControlsScript.m_Instance.RequestPanelsVisibility(true); - - // If the user chooses to skip the intro, assume they've done the tutorial before. - PlayerPrefs.SetInt(App.kPlayerPrefHasPlayedBefore, 1); - - m_DesiredAppState = AppState.Standard; - } - - // Updates the intro fade (both in and out) and returns true when finished. - // Has a special case for Mobile, so that the scene fades out as soon as it has faded in. - // - // For desktop: - // FFFFFFFFFFFFT FFFFFFFFFFFFFFFFFT - // ^ Start ^ Faded in ^ Standard Mode ^ Faded out - // - // For mobile: - // FFFFFFFFFFFFFFFFFFFFFFFFFFFT T - // ^ Start ^ Faded in ^ Faded out ^ Standard Mode - // - // (F = returns false, T = returns true) - - bool UpdateIntroFadeIsFinished() - { - if (m_IntroSketchRenderers == null) - { - m_IntroFadeTimer = 0; - // This code path gets triggered when running on mobile. At this point the fade out has - // already happened, so we just return true so that the 'once-fadeout-has-finished' code gets - // triggered. - return true; - } - - bool isMobile = Config.IsMobileHardware; - bool isFadingIn = m_IntroFadeTimer < 1f; - float fadeMax = isFadingIn ? 1f : 2f; - float fadeDuration; - if (isMobile) - { - fadeDuration = isFadingIn - ? m_IntroSketchMobileFadeInDuration - : m_IntroSketchMobileFadeOutDuration; - } - else - { - fadeDuration = isFadingIn ? m_IntroSketchFadeInDuration : m_IntroSketchFadeOutDuration; - } - - m_IntroFadeTimer += Time.deltaTime / fadeDuration; - if (m_IntroFadeTimer > fadeMax) - { - m_IntroFadeTimer = fadeMax; - } - - for (int i = 0; i < m_IntroSketchRenderers.Length; ++i) - { - m_IntroSketchRenderers[i].material.SetFloat("_IntroDissolve", - Mathf.SmoothStep(0, 1, Math.Abs(1 - m_IntroFadeTimer))); - } - - if (m_IntroFadeTimer == fadeMax) - { - if (isFadingIn) - { - // With Mobile, we fade in then out, so the fade isn't complete at fade-in. - return !isMobile; - } - else - { - DestroyIntroSketch(); - m_IntroSketchRenderers = null; - return true; - } - } - - return false; - } - - void InitCursor() - { - if (StartupError) - { - return; - } - if (VrSdk.GetHmdDof() == TiltBrush.VrSdk.DoF.None) - { - Cursor.visible = false; - Cursor.lockState = CursorLockMode.Locked; - } - } - - public static T DeserializeObjectWithWarning(string text, out string warning) - { - // Try twice, once to catch "unknown key" warnings, once to actually get a result. - warning = null; - try - { - return JsonConvert.DeserializeObject(text, new JsonSerializerSettings - { - MissingMemberHandling = MissingMemberHandling.Error - }); - } - catch (JsonSerializationException e) - { - warning = e.Message; - return JsonConvert.DeserializeObject(text); - } - } - - void CreateDefaultConfig() - { - // If we don't have a .cfg in our Tilt Brush directory, drop a default one. - string tiltBrushFolder = UserPath(); - if (!Directory.Exists(tiltBrushFolder)) - { - return; - } - - string configPath = Path.Combine(tiltBrushFolder, kConfigFileName); - if (!File.Exists(configPath)) - { - FileUtils.WriteTextFromResources(kDefaultConfigPath, configPath); - } - } - - public void RefreshUserConfig() - { - m_UserConfig = new UserConfig(); - - try - { - string sConfigPath = App.ConfigPath(); - if (!File.Exists(sConfigPath)) - { - return; - } - - string text; - try - { - text = File.ReadAllText(sConfigPath, System.Text.Encoding.UTF8); - } - catch (Exception e) - { - // UnauthorizedAccessException, IOException - OutputWindowScript.Error($"Error reading {kConfigFileName}", e.Message); - return; - } - - try - { - string warning; - m_UserConfig = DeserializeObjectWithWarning(text, out warning); - if (warning != null) - { - OutputWindowScript.Error($"Warning reading {kConfigFileName}", warning); - } - } - catch (Exception e) - { - OutputWindowScript.Error($"Error reading {kConfigFileName}", e.Message); - return; - } - } - finally - { - // Apply any overrides sent through via the command line, even if reading from Tilt Brush.cfg - // goes horribly wrong. - Config.ApplyUserConfigOverrides(m_UserConfig); - } - } - - public void CreateFailedToDetectVrDialog(string msg = null, bool allowViewing = true) - { - GameObject dialog = Instantiate(m_ErrorDialog); - var initScript = dialog.GetComponent(); - if (!string.IsNullOrEmpty(msg)) - { - var textMesh = initScript.m_Heading; - textMesh.text = @$" Tiltasaurus says... - {msg}"; - } - initScript.ShowSketchSelectorUi(allowViewing && !StartupError); - } - - static public bool AppAllowsCreation() - { - // TODO: this feels like it should be an explicit part of Config, - // not something based on VR hardware... - return App.VrSdk.GetControllerDof() != TiltBrush.VrSdk.DoF.None; - } - - static public string PlatformPath() - { - if (!Application.isEditor && Application.platform == RuntimePlatform.OSXPlayer) - { - return System.IO.Directory.GetParent(Application.dataPath).Parent.ToString(); - } - else if (Application.platform == RuntimePlatform.Android) - { - return Application.persistentDataPath; - } - - return System.IO.Directory.GetParent(Application.dataPath).ToString(); - } - - static public string SupportPath() - { - return Path.Combine(PlatformPath(), "Support"); - } - - /// Returns a parent of UserPath; used to figure out how much path - /// is necessary to display to the user when giving feedback. We - /// assume this is the "boring" portion of the path that they can infer. - public static string DocumentsPath() - { - switch (Application.platform) - { - case RuntimePlatform.WindowsPlayer: - case RuntimePlatform.WindowsEditor: - case RuntimePlatform.OSXPlayer: - case RuntimePlatform.OSXEditor: - case RuntimePlatform.LinuxPlayer: - case RuntimePlatform.LinuxEditor: - return System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); - case RuntimePlatform.Android: - case RuntimePlatform.IPhonePlayer: - default: - return Application.persistentDataPath; - } - } - - void InitUserPath() - { - switch (Application.platform) - { - case RuntimePlatform.WindowsPlayer: - case RuntimePlatform.WindowsEditor: - // user Documents folder - m_UserPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); - - // GetFolderPath() can fail, returning an empty string. - if (m_UserPath == "") - { - // If that happens, try a bunch of other folders. - m_UserPath = System.Environment.GetFolderPath( - System.Environment.SpecialFolder.MyDocuments); - if (m_UserPath == "") - { - m_UserPath = System.Environment.GetFolderPath( - System.Environment.SpecialFolder.DesktopDirectory); - } - } - break; - case RuntimePlatform.OSXPlayer: - case RuntimePlatform.OSXEditor: - case RuntimePlatform.LinuxPlayer: - case RuntimePlatform.LinuxEditor: - // user Documents folder - m_UserPath = Path.Combine(System.Environment.GetFolderPath( - System.Environment.SpecialFolder.Personal), - "Documents"); - break; - case RuntimePlatform.Android: - m_UserPath = "/sdcard/"; - m_OldUserPath = Application.persistentDataPath; - break; - case RuntimePlatform.IPhonePlayer: - default: - m_UserPath = Application.persistentDataPath; - break; - } - - m_UserPath = Path.Combine(m_UserPath, App.kAppFolderName); - - // In the case that we have changed the location of the user data, move the user data from the - // old location to the new one. - if (!string.IsNullOrEmpty(m_OldUserPath)) - { - MoveUserDataFromOldLocation(); - } - - if (!Path.IsPathRooted(m_UserPath)) - { - StartupError = true; - CreateFailedToDetectVrDialog( - "Failed to find Documents folder.\nIn Windows, try modifying your Controlled Folder Access settings.", - allowViewing: false - ); - } - } - - private void MoveUserDataFromOldLocation() - { - m_OldUserPath = Path.Combine(m_OldUserPath, App.kAppFolderName); - - if (!Directory.Exists(m_OldUserPath)) - { - return; - } - - if (Directory.Exists(m_UserPath)) - { - return; - } - - try - { - Directory.Move(m_OldUserPath, m_UserPath); - // Recreate the old directory and put a message in there so a user used to looking in the old - // location can find out where to get their files. - Directory.CreateDirectory(m_OldUserPath); - string moveMessageFilename = Path.Combine(m_OldUserPath, kFileMoveFilename); - File.WriteAllText(moveMessageFilename, kFileMoveContents); - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - // Return path of root directory for storing user sketches, snapshots, etc. - public static string UserPath() - { - return App.m_Instance.m_UserPath; - } - - public static bool InitDirectoryAtPath(string path) - { - if (Directory.Exists(path)) - { - return true; - } - if (!FileUtils.InitializeDirectoryWithUserError(path)) - { - return false; - } - return true; - } - - public static string ShortenForDescriptionText(string desc) - { - desc = desc.Split('\n')[0]; - if (desc.Length > 33) - { - desc = desc.Substring(0, 30) + "..."; - } - return desc; - } - - /// Creates the Media Library directory if it does not already exist. - /// Returns true if the directory already exists or if it is created successfully, false if the - /// directory could not be created. - public static bool InitMediaLibraryPath() - { - string mediaLibraryPath = MediaLibraryPath(); - if (!InitDirectoryAtPath(mediaLibraryPath)) { return false; } - string readmeFile = Path.Combine(mediaLibraryPath, Config.m_MediaLibraryReadme); - FileUtils.WriteTextFromResources(Config.m_MediaLibraryReadme, - Path.ChangeExtension(readmeFile, ".txt")); - return true; - } - - /// Creates the Model Catalog directory and copies in the provided default models. - /// Returns true if the directory already exists or if it is created successfully, false if the - /// directory could not be created. - public static bool InitModelLibraryPath(string[] defaultModels) - { - string modelsDirectory = ModelLibraryPath(); - - // TODO:Mikesky - Re-enable this check in a few versions, - // and remove the one in the obj removal loop. - - // if (Directory.Exists(modelsDirectory)) { return true; } - - if (!InitDirectoryAtPath(modelsDirectory)) { return false; } - - // Tidy up old obj models and replace with gltfs - // We can remove this at some point. - var targetDirs = new string[] { "Andy", "Tiltasaurus" }; - foreach (string target in targetDirs) - { - var path = Path.Combine(modelsDirectory, target); - if (Directory.Exists(path)) - { - Debug.Log($"Found old model file: \"{path}\", removing."); - Directory.Delete(path, true); - } - else - { - // We've already tidied up the old models, or the user has and understands - // the reference library. Let's not interfere. - return true; - } - } - - foreach (string fileName in defaultModels) - { - string[] path = fileName.Split( - new[] { '\\', '/' }, 3, StringSplitOptions.RemoveEmptyEntries); - string newModel = Path.Combine(modelsDirectory, path[1]); - - if (!File.Exists(newModel)) - { - FileUtils.WriteBytesFromResources(fileName, newModel); - } - } - return true; - } - - /// Creates the Background Images directory and copies in the provided default images. - /// Returns true if the directory already exists or if it is created successfully, false if the - /// directory could not be created. - public static bool InitBackgroundImagesPath(string[] defaultBackgroundImages) - { - string path = BackgroundImagesLibraryPath(); - if (!Directory.Exists(path)) - { - if (!FileUtils.InitializeDirectoryWithUserError(path)) - { - return false; - } - } - - // Populate the reference images folder exactly once. - int seeded = PlayerPrefs.GetInt(kBackgroundImagesSeeded); - if (seeded == 0) - { - foreach (string fileName in defaultBackgroundImages) - { - FileUtils.WriteBytesFromResources(fileName, - Path.Combine(path, Path.GetFileName(fileName.Replace(".bytes", "")))); - } - PlayerPrefs.SetInt(kBackgroundImagesSeeded, 1); - } - return true; - } - - /// Creates the Reference Images directory and copies in the provided default images. - /// Returns true if the directory already exists or if it is created successfully, false if the - /// directory could not be created. - public static bool InitReferenceImagePath(string[] defaultImages) - { - string path = ReferenceImagePath(); - if (!Directory.Exists(path)) - { - if (!FileUtils.InitializeDirectoryWithUserError(path)) - { - return false; - } - } - - // Populate the reference images folder exactly once. - int seeded = PlayerPrefs.GetInt(kReferenceImagesSeeded); - if (seeded == 0) - { - foreach (string fileName in defaultImages) - { - FileUtils.WriteTextureFromResources(fileName, - Path.Combine(path, Path.GetFileName(fileName))); - } - PlayerPrefs.SetInt(kReferenceImagesSeeded, 1); - } - return true; - } - - public static bool InitVideoLibraryPath(string[] defaultVideos) - { - string videosDirectory = VideoLibraryPath(); - if (Directory.Exists(videosDirectory)) - { - return true; - } - if (!InitDirectoryAtPath(videosDirectory)) - { - return false; - } - foreach (var video in defaultVideos) - { - string destFilename = Path.GetFileName(video); - FileUtils.WriteBytesFromResources(video, Path.Combine(videosDirectory, destFilename)); - } - - return true; - } - - public static string FeaturedSketchesPath() - { - return Path.Combine(Application.persistentDataPath, "Featured Sketches"); - } - - public static string MediaLibraryPath() - { - return Path.Combine(UserPath(), "Media Library"); - } - - public static string ModelLibraryPath() - { - return Path.Combine(MediaLibraryPath(), "Models"); - } - - public static string ReferenceImagePath() - { - return Path.Combine(MediaLibraryPath(), "Images"); - } - - public static string VideoLibraryPath() - { - return Path.Combine(MediaLibraryPath(), "Videos"); - } - - public static string BackgroundImagesLibraryPath() - { - return Path.Combine(MediaLibraryPath(), "BackgroundImages"); - } - - static public string UserSketchPath() - { - return Path.Combine(UserPath(), "Sketches"); - } - - static public string AutosavePath() - { - return Path.Combine(UserPath(), "Sketches/Autosave"); - } - - static public string ConfigPath() - { - return Path.Combine(UserPath(), kConfigFileName); - } - - static public string UserExportPath() - { - return App.Config.m_ExportPath ?? Path.Combine(UserPath(), "Exports"); - } - - static public string AutosaveRestoreFilePath() - { - return Path.Combine(UserPath(), "Sketches/Autosave/AutosaveRestore"); - } - - static public string SnapshotPath() - { - return Path.Combine(UserPath(), "Snapshots"); - } - - static public string VideosPath() - { - return Path.Combine(UserPath(), "Videos"); - } - - static public string VrVideosPath() - { - return Path.Combine(UserPath(), "VRVideos"); - } - - void OnApplicationQuit() - { - if (AppExit != null) - { - AppExit(); - } - - AutosaveRestoreFileExists = false; - } - - void OnPlaybackComplete() - { - SaveLoadScript.m_Instance.SignalPlaybackCompletion(); - if (SketchControlsScript.m_Instance.SketchPlaybackMode != - SketchMemoryScript.PlaybackMode.Timestamps) - { - - // For non-timestamp playback mode, adjust current time to last stroke in drawing. - try - { - this.CurrentSketchTime = SketchMemoryScript.m_Instance.GetApproximateLatestTimestamp(); - } - catch (InvalidOperationException) - { - // Can happen as an edge case, eg if we try to load a file that doesn't exist. - this.CurrentSketchTime = 0; - } - } - } - - public TiltBrushManifest GetMergedManifest(bool forceExperimental = false) - { - var manifest = m_Manifest; - if (Config.IsExperimental || forceExperimental) - { - if (m_ManifestExperimental != null) - { - manifest = Instantiate(m_Manifest); - manifest.AppendFrom(m_ManifestExperimental); - } - } -#if ZAPBOX_SUPPORTED - manifest = m_ZapboxManifest; -#endif - return manifest; - } - - // Previously Experimental-Mode only - public bool IsBrushExperimental(BrushDescriptor brush) - { - return m_ManifestExperimental.Brushes.Contains(brush); - } - - DateTime GetLinkerTime(Assembly assembly, TimeZoneInfo target = null) - { -#if !(UNITY_ANDROID || UNITY_IOS) - var filePath = assembly.Location; - const int c_PeHeaderOffset = 60; - const int c_LinkerTimestampOffset = 8; - - var buffer = new byte[2048]; - - using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - stream.Read(buffer, 0, 2048); - - var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset); - var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset); - var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - var linkTimeUtc = epoch.AddSeconds(secondsSince1970); - return linkTimeUtc.ToLocalTime(); -#else - return DateTime.Now; -#endif - } - - // By executing the URL directly windows will open it without making the browser a child - // process of Tilt Brush. If this fails or throws an exception we fall back to Unity's - // OpenURL(). - public static void OpenURL(string url) - { - var isPolyUrl = (url.Contains("poly.google.com/") || url.Contains("vr.google.com")); - if (isPolyUrl && GoogleIdentity.LoggedIn) - { - var email = GoogleIdentity.Profile.email; - url = $"https://accounts.google.com/AccountChooser?Email={email}&continue={url}"; - } -#if UNITY_STANDALONE_WINDOWS - var startInfo = new System.Diagnostics.ProcessStartInfo(url); - startInfo.UseShellExecute = true; - try { - if (System.Diagnostics.Process.Start(startInfo) == null) { - Application.OpenURL(url); - } - } catch (Exception) { - Application.OpenURL(url); - } -#else - switch (Application.platform) - { - case RuntimePlatform.OSXEditor: - case RuntimePlatform.OSXPlayer: - System.Diagnostics.Process.Start(url); - break; - default: - Application.OpenURL(url); - break; - } -#endif - } - - /// This copies the support files from inside the Streaming Assets folder to the support folder. - /// This only happens on Android. The files have to be extracted directly from the .apk. - private static void CopySupportFiles() - { - if (Application.platform != RuntimePlatform.Android) - { - return; - } - if (!Directory.Exists(SupportPath())) - { - Directory.CreateDirectory(SupportPath()); - } - - Func GetIndexOfEnd = (s) => Application.streamingAssetsPath.IndexOf(s) + s.Length; - - // Find the apk file - int apkIndex = GetIndexOfEnd("file://"); - int fileIndex = Application.streamingAssetsPath.IndexOf("!/"); - string apkFilename = Application.streamingAssetsPath.Substring(apkIndex, fileIndex - apkIndex); - - const string supportBeginning = "assets/Support/"; - - try - { - using (Stream zipFile = File.Open(apkFilename, FileMode.Open, FileAccess.Read)) - { - ZipLibrary.ZipFile zip = new ZipLibrary.ZipFile(zipFile); - foreach (ZipLibrary.ZipEntry entry in zip) - { - if (entry.IsFile && entry.Name.StartsWith(supportBeginning)) - { - // Create the directory if needed. - string fullPath = Path.Combine(App.SupportPath(), - entry.Name.Substring(supportBeginning.Length)); - string directory = Path.GetDirectoryName(fullPath); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } - - // Copy the data over to a file. - using (Stream entryStream = zip.GetInputStream(entry)) - { - using (FileStream fileStream = File.Create(fullPath)) - { - byte[] buffer = new byte[16 * 1024]; // Do it in 16k chunks - while (true) - { - int size = entryStream.Read(buffer, 0, buffer.Length); - if (size > 0) - { - fileStream.Write(buffer, 0, size); - } - else - { - break; - } - } - } - } - - } - } - zip.Close(); - } - } - catch (Exception ex) - { - Debug.LogException(ex); - } - } - - public void LogoutIcosa() - { - IcosaUserName = null; - IcosaUserId = null; - IcosaUserIcon = null; - IcosaToken = null; - } - } // class App -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Runtime.CompilerServices; +using UnityEngine; +using Newtonsoft.Json; +using TMPro; +#if USD_SUPPORTED +using Unity.Formats.USD; +#endif +#if USE_DOTNETZIP +using ZipSubfileReader = ZipSubfileReader_DotNetZip; +using ZipLibrary = Ionic.Zip; +#else +using ZipSubfileReader = TiltBrush.ZipSubfileReader_SharpZipLib; +using ZipLibrary = ICSharpCode.SharpZipLib.Zip; +#endif + +#if !UNITY_2020_3_OR_NEWER +xxx "This is the minimal Unity supported by Open Brush" xxx +#endif + +[assembly: InternalsVisibleTo("Assembly-CSharp-Editor")] +namespace TiltBrush +{ + public partial class App : MonoBehaviour + { + // ------------------------------------------------------------ + // Constants and types + // ------------------------------------------------------------ + + public const float METERS_TO_UNITS = 10f; + public const float UNITS_TO_METERS = .1f; + + // This is the name of the app, as displayed to the users running it. + public const string kAppDisplayName = "Open Brush"; + // This is the App name used when speaking to Google services + public const string kGoogleServicesAppName = kAppDisplayName; + // The name of the configuration file. You may want to change this if you think your users may + // want to have a different config file for your edition of the app. + public const string kConfigFileName = "Open Brush.cfg"; + // The name of the App folder (In the user's Documents folder) - original Tilt Brush used "Tilt Brush" + // If you are forking Open Brush, you may want to leave this as "Open Brush" or not. + public const string kAppFolderName = "Open Brush"; + // The data folder used on Google Drive. + public const string kDriveFolderName = kAppDisplayName; + + public const string kPlayerPrefHasPlayedBefore = "Has played before"; + public const string kReferenceImagesSeeded = "Reference Images seeded"; + public const string kBackgroundImagesSeeded = "Background Images seeded"; + + + private const string kDefaultConfigPath = "DefaultConfig"; + + private const int kHttpListenerPort = 40074; + private const string kProtocolHandlerPrefix = "tiltbrush://remix/"; + private const string kFileMoveFilename = "WhereHaveMyFilesGone.txt"; + + private const string kFileMoveContents = + "All your " + kAppDisplayName + " files have been moved to\n" + + "/sdcard/" + kAppFolderName + ".\n"; + + public enum AppState + { + Error, + LoadingBrushesAndLighting, + FadeFromBlack, + FirstRunIntro, + Intro, + Loading, + QuickLoad, + Standard, + MemoryExceeded, + Saving, + Reset, + Uploading, + AutoProfiling, + OfflineRendering, + } + + // ------------------------------------------------------------ + // Static API + // ------------------------------------------------------------ + + private static App m_Instance; + + // Accessible at all times after config is initialized. + public static Config Config => Config.m_SingletonState; + + public static UserConfig UserConfig => m_Instance.m_UserConfig; + + public static PlatformConfig PlatformConfig => Config.PlatformConfig; + + public static VrSdk VrSdk => m_Instance.m_VrSdk; + + public static SceneScript Scene => m_Instance.m_SceneScript; + + public static CanvasScript ActiveCanvas => Scene.ActiveCanvas; + + public static IcosaAssetCatalog IcosaAssetCatalog => m_Instance.m_IcosaAssetCatalog; + + public static Switchboard Switchboard => m_Instance.m_Switchboard; + + public static BrushColorController BrushColor => m_Instance.m_BrushColorController; + + public static GroupManager GroupManager => m_Instance.m_GroupManager; + + public static HttpServer HttpServer => m_Instance.m_HttpServer; + + public static DriveAccess DriveAccess => m_Instance.m_DriveAccess; + public static DriveSync DriveSync => m_Instance.m_DriveSync; + + public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; + public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; + + // TODO Make these overridable + public static string ICOSA_WEBSITE_URL = "https://icosa.ixxy.co.uk"; + public static string ICOSA_API_URL = "https://icosa-api.ixxy.co.uk"; + + public string IcosaToken + { + get => PlayerPrefs.HasKey("IcosaToken") ? PlayerPrefs.GetString("IcosaToken") : null; + set => PlayerPrefs.SetString("IcosaToken", value); + } + public static bool IcosaIsLoggedIn => !string.IsNullOrEmpty(App.Instance.IcosaToken); + + public static string IcosaUserName; + public static string IcosaUserId; + public static Texture IcosaUserIcon; + + public static GoogleUserSettings GoogleUserSettings => m_Instance.m_GoogleUserSettings; + + /// Returns the App instance, or null if the app has not been initialized + /// with Awake(). Note that the App may not have had Start() called yet. + /// + /// Do not modify the script execution order if you only need inspector + /// data from App.Instance. Put the inspector data in App.Config instead. + public static App Instance + { + get { return m_Instance; } +#if UNITY_EDITOR + // Bleh. Needed by BuildTiltBrush.cs + internal set { m_Instance = value; } +#endif + } + + public static AppState CurrentState => m_Instance == null ? AppState.Loading : m_Instance.m_CurrentAppState; + + public static OAuth2Identity GetIdentity(Cloud cloud) + { + switch (cloud) + { + case Cloud.Sketchfab: return SketchfabIdentity; + case Cloud.Icosa: throw new InvalidOperationException("Icosa does not use OAuth2"); + default: throw new InvalidOperationException($"No OAuth2 identity for {cloud}"); + } + } + + // Log to editor console when developing and console log when running. This avoids all the stack spam in the log. + public static void Log(string msg) + { +#if UNITY_EDITOR + Debug.Log("[OB] " + msg); +#else + Console.WriteLine("[OB] " + msg); +#endif + } + + // ------------------------------------------------------------ + // Events + // ------------------------------------------------------------ + + public event Action StateChanged; + + // ------------------------------------------------------------ + // Inspector data + // ------------------------------------------------------------ + // Unless otherwise stated, intended to be read-only even if public + + [Header("External References")] + [SerializeField] VrSdk m_VrSdk; + [SerializeField] SceneScript m_SceneScript; + + [Header("General inspector")] + [SerializeField] float m_FadeFromBlackDuration; + [SerializeField] float m_QuickLoadHintDelay = 2f; + + [SerializeField] GpuIntersector m_GpuIntersector; + + public TiltBrushManifest m_Manifest; + + // Previously Experimental-Mode only + [SerializeField] private TiltBrushManifest m_ManifestExperimental; + [SerializeField] private TiltBrushManifest m_ZapboxManifest; + + [SerializeField] private SelectionEffect m_SelectionEffect; + + /// The root object for the "Room" coordinate system + public Transform m_RoomTransform => transform; + /// The root object for the "Scene" coordinate system ("/SceneParent") + public Transform m_SceneTransform; + /// The root object for the "Canvas" coordinate system ("/SceneParent/Canvas") + /// TODO: remove, in favor of .ActiveCanvas.transform + public Transform m_CanvasTransform; + /// The object "/SceneParent/EnvironmentParent" + public Transform m_EnvironmentTransform; + [SerializeField] GameObject m_SketchSurface; + [SerializeField] GameObject m_ErrorDialog; + [SerializeField] GameObject m_OdsPrefab; + GameObject m_OdsPivot; + + [Header("Intro")] + [SerializeField] float m_IntroSketchFadeInDuration = 5.0f; + [SerializeField] float m_IntroSketchFadeOutDuration = 1.5f; + [SerializeField] float m_IntroSketchMobileFadeInDuration = 3.0f; + [SerializeField] float m_IntroSketchMobileFadeOutDuration = 1.5f; + + [SerializeField] FrameCountDisplay m_FrameCountDisplay; + + [SerializeField] private GameObject m_ShaderWarmup; + + [Header("Identities")] + [SerializeField] private OAuth2Identity m_GoogleIdentity; + [SerializeField] private OAuth2Identity m_SketchfabIdentity; + + // ------------------------------------------------------------ + // Private data + // ------------------------------------------------------------ + + /// Use C# event in preference to Unity callbacks because + /// Unity doesn't send callbacks to disabled objects + public event Action AppExit; + + private Queue m_RequestedTiltFileQueue = Queue.Synchronized(new Queue()); + private HttpServer m_HttpServer; + + private SketchSurfacePanel m_SketchSurfacePanel; + private UserConfig m_UserConfig; + private string m_UserPath; + private string m_OldUserPath; + + private IcosaAssetCatalog m_IcosaAssetCatalog; + private Switchboard m_Switchboard; + private BrushColorController m_BrushColorController; + private GroupManager m_GroupManager; + + /// Time origin of sketch in seconds for case when drawing is not sync'd to media. + private double m_sketchTimeBase = 0; + private float m_AppStateCountdown; + private float m_QuickLoadHintCountdown; + private bool m_QuickLoadInputWasValid; + private bool m_QuickLoadEatInput; + private AppState m_CurrentAppState; + private AppState m_DesiredAppState_; // Temporary: to narrow down b/37256058 + private AppState m_DesiredAppState + { + get => m_DesiredAppState_; + set + { + if (m_DesiredAppState_ != value) + { + Console.WriteLine("App State <- {0}", value); + } + m_DesiredAppState_ = value; + } + } + private int m_TargetFrameRate; + private float m_RoomRadius; + private bool m_AutosaveRestoreFileExists; + private bool m_ShowAutosaveHint = false; + private bool? m_ShowControllers; + private int m_QuickloadStallFrames; + + private GameObject m_IntroSketch; + private Renderer[] m_IntroSketchRenderers; + private float m_IntroFadeTimer; + + private bool m_FirstRunExperience; + private bool m_RequestingAudioReactiveMode; + private DriveAccess m_DriveAccess; + private DriveSync m_DriveSync; + private GoogleUserSettings m_GoogleUserSettings; + + // ------------------------------------------------------------ + // Properties + // ------------------------------------------------------------ + + /// Time spent in current sketch, in seconds. + /// On load, this is restored to the timestamp of the last stroke. + /// Updated per-frame. + public double CurrentSketchTime + { + // Unity's Time.time has useful precision probably <= 1ms, and unknown + // drift/accuracy. It is a single (but is a double, internally), so its + // raw precision drops to ~2ms after ~4 hours and so on. + // Time.timeSinceLevelLoad is also an option. + // + // C#'s DateTime API has low-ish precision (10+ ms depending on OS) + // but likely the highest accuracy with respect to wallclock, since + // it's reading from an RTC. + // + // High-precision timers are the opposite: high precision, but are + // subject to drift. + // + // For realtime sync, Time.time is probably the best thing to use. + // For postproduction sync, probably C# DateTime. + get + { + // If you change this, also modify SketchTimeToLevelLoadTime + return Time.timeSinceLevelLoad - m_sketchTimeBase; + } + set + { + if (value < 0) { throw new ArgumentException("negative"); } + m_sketchTimeBase = Time.timeSinceLevelLoad - value; + } + } + + public float RoomRadius => m_RoomRadius; + + public SelectionEffect SelectionEffect => m_SelectionEffect; + public bool IsFirstRunExperience => m_FirstRunExperience; + public bool HasPlayedBefore { get; private set; } + + public bool StartupError { get; set; } + + public bool ShowControllers + { + get => m_ShowControllers.GetValueOrDefault(true); + set + { + InputManager.m_Instance.ShowControllers(value); + m_ShowControllers = value; + } + } + + public bool AutosaveRestoreFileExists + { + get => m_AutosaveRestoreFileExists; + set + { + if (value != m_AutosaveRestoreFileExists) + { + try + { + string filePath = AutosaveRestoreFilePath(); + if (value) + { + var autosaveFile = File.Create(filePath); + autosaveFile.Close(); + } + else + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + } + catch (IOException) { return; } + catch (UnauthorizedAccessException) { return; } + + m_AutosaveRestoreFileExists = value; + } + } + } + + public GpuIntersector GpuIntersector => m_GpuIntersector; + + public TrTransform OdsHeadPrimary { get; set; } + public TrTransform OdsScenePrimary { get; set; } + + public TrTransform OdsHeadSecondary { get; set; } + public TrTransform OdsSceneSecondary { get; set; } + + public FrameCountDisplay FrameCountDisplay => m_FrameCountDisplay; + + // ------------------------------------------------------------ + // Implementation + // ------------------------------------------------------------ + + public bool RequestingAudioReactiveMode => m_RequestingAudioReactiveMode; + + public void ToggleAudioReactiveModeRequest() + { + m_RequestingAudioReactiveMode ^= true; + } + + public void ToggleAudioReactiveBrushesRequest() + { + ToggleAudioReactiveModeRequest(); + AudioCaptureManager.m_Instance.CaptureAudio(m_RequestingAudioReactiveMode); + VisualizerManager.m_Instance.EnableVisuals(m_RequestingAudioReactiveMode); + Switchboard.TriggerAudioReactiveStateChanged(); + } + + public double SketchTimeToLevelLoadTime(double sketchTime) + { + return sketchTime + m_sketchTimeBase; + } + + public void SetOdsCameraTransforms(TrTransform headXf, TrTransform sceneXf) + { + if (Config.m_SdkMode != SdkMode.Ods) { return; } + OdsScenePrimary = sceneXf; + OdsHeadPrimary = headXf; + + // To simplify down-stream code, copy primary into secondary. + if (OdsHeadSecondary == new TrTransform()) + { + OdsHeadSecondary = headXf; + OdsSceneSecondary = sceneXf; + } + } + + // Tilt Brush code assumes the current directory is next to the Support/ + // folder. Enforce that assumption + static void SetCurrentDirectoryToApplication() + { + // dataPath is: + // Editor - /Assets + // Windows - TiltBrush_Data/ + // Linux - TiltBrush_Data/ + // OSX - TiltBrush.app/Contents/ +#if UNITY_STANDALONE_WIN + string oldDir = Directory.GetCurrentDirectory(); + string dataDir = UnityEngine.Application.dataPath; + string appDir = Path.GetDirectoryName(dataDir); + try + { + Directory.SetCurrentDirectory(appDir); + } + catch (Exception e) + { + Debug.LogErrorFormat("Couldn't set dir to {0}: {1}", appDir, e); + } + string curDir = Directory.GetCurrentDirectory(); + Debug.LogFormat("Dir {0} -> {1}", oldDir, curDir); +#endif + } + + void CreateIntroSketch() + { + // Load intro if not already cached. + if (m_IntroSketch == null && PlatformConfig.IntroSketchPrefab != null) + { + m_IntroSketch = Instantiate(PlatformConfig.IntroSketchPrefab); + m_IntroSketchRenderers = m_IntroSketch.GetComponentsInChildren(); + for (int i = 0; i < m_IntroSketchRenderers.Length; ++i) + { + m_IntroSketchRenderers[i].material.SetFloat("_IntroDissolve", 1); + m_IntroSketchRenderers[i].material.SetFloat("_GreyScale", 0); + } + } + } + + void DestroyIntroSketch() + { + Destroy(m_IntroSketch); + m_IntroSketchRenderers = null; + + // Eject the (rather large) intro sketch from memory. + // TODO: The Unity Way would be to put these prefab references and instantiations + // in an additive scene. + // Don't do this in the editor, because it mutates the asset on disk! +#if !UNITY_EDITOR + PlatformConfig.IntroSketchPrefab = null; +#endif + Resources.UnloadUnusedAssets(); + } + + static string GetStartupString() + { + string str = $"{App.kAppDisplayName} {Config.m_VersionNumber}"; + + if (!string.IsNullOrEmpty(Config.m_BuildStamp)) + str += $" build {Config.m_BuildStamp}"; + +#if UNITY_ANDROID + str += $" code {AndroidUtils.GetVersionCode()}"; +#endif +#if DEBUG + str += $" {PlatformConfig.name}"; +#endif + return str; + } + + void Awake() + { + m_Instance = this; + Log(GetStartupString()); + Log($"SdkMode: {App.Config.m_SdkMode}."); + + // Begone, physics! You were using 0.3 - 1.3ms per frame on Quest! + Physics.autoSimulation = false; + + // See if this is the first time + HasPlayedBefore = PlayerPrefs.GetInt(kPlayerPrefHasPlayedBefore, 0) == 1; + +#if ZAPBOX_SUPPORTED + // TODO:Mikesky - fix zapbox support. + HasPlayedBefore = true; +#endif + + // Copy files into Support directory + CopySupportFiles(); + + InitUserPath(); + SetCurrentDirectoryToApplication(); + Coords.Init(this); + Scene.Init(); + CreateDefaultConfig(); + RefreshUserConfig(); + CameraConfig.Init(); + if (!string.IsNullOrEmpty(m_UserConfig.Profiling.SketchToLoad)) + { + Config.m_SketchFiles = new string[] { m_UserConfig.Profiling.SketchToLoad }; + } + + if (m_UserConfig.Testing.FirstRun) + { + PlayerPrefs.DeleteKey(kPlayerPrefHasPlayedBefore); + PlayerPrefs.DeleteKey(kReferenceImagesSeeded); + PlayerPrefs.DeleteKey(PanelManager.kPlayerPrefAdvancedMode); + AdvancedPanelLayouts.ClearPlayerPrefs(); + PointerManager.ClearPlayerPrefs(); + HasPlayedBefore = false; + } + // Cache this variable for the length of the play session. HasPlayedBefore will be updated, + // but m_FirstRunExperience should not. + m_FirstRunExperience = !HasPlayedBefore; + + m_Switchboard = new Switchboard(); + m_GroupManager = new GroupManager(); + + m_IcosaAssetCatalog = GetComponent(); + m_IcosaAssetCatalog.Init(); + + m_BrushColorController = GetComponent(); + + // Tested on Windows. I hope they don't change the names of these preferences. + PlayerPrefs.DeleteKey("Screenmanager Is Fullscreen mode"); + PlayerPrefs.DeleteKey("Screenmanager Resolution Height"); + PlayerPrefs.DeleteKey("Screenmanager Resolution Width"); + + if (DevOptions.I.UseAutoProfiler) + { + gameObject.AddComponent(); + } + + m_Manifest = GetMergedManifest(); + + m_HttpServer = GetComponentInChildren(); + if (!Config.IsMobileHardware) + { + HttpServer.AddHttpHandler("/load", HttpLoadSketchCallback); + } + + m_AutosaveRestoreFileExists = File.Exists(AutosaveRestoreFilePath()); + + m_GoogleUserSettings = new GoogleUserSettings(m_GoogleIdentity); + m_DriveAccess = new DriveAccess(m_GoogleIdentity, m_GoogleUserSettings); + m_DriveSync = new DriveSync(m_DriveAccess, m_GoogleIdentity); + } + + // TODO: should add OnDestroy to other scripts that create background tasks + void OnDestroy() + { + if (!Config.IsMobileHardware) + { + HttpServer.RemoveHttpHandler("/load"); + } + + if (m_DriveSync != null) + { + m_DriveSync.UninitializeAsync().AsAsyncVoid(); + } + if (m_DriveAccess != null) + { + m_DriveAccess.UninitializeAsync().AsAsyncVoid(); + } + } + + // Called from HttpListener thread. Supported requests: + // /load? + // Loads sketch given path on local filesystem. Any pending load is canceled. + // Response body: none + string HttpLoadSketchCallback(HttpListenerRequest request) + { + var urlPath = request.Url.LocalPath; + var query = Uri.UnescapeDataString(request.Url.Query); + if (urlPath == "/load" && query.Length > 1) + { + var filePath = query.Substring(1); + m_RequestedTiltFileQueue.Enqueue(filePath); + } + return ""; + } + + void Start() + { + // Use of ControllerConsoleScript must wait until Start() + ControllerConsoleScript.m_Instance.AddNewLine(GetStartupString()); + + if (!VrSdk.IsHmdInitialized() && !UserConfig.Flags.EnableMonoscopicMode) + { + // If XR is disabled or fails to initialize + // and we haven't enabled monoscopic mode + // then fall back to the 2d View-only mode + CreateFailedToDetectVrDialog(); + } + else + { + Debug.LogFormat("Sdk mode: {0} XRDevice.model: {1}", + Config.m_SdkMode, + UnityEngine.XR.InputDevices.GetDeviceAtXRNode(UnityEngine.XR.XRNode.Head).manufacturer); + } + + m_TargetFrameRate = VrSdk.GetHmdTargetFrameRate(); + if (VrSdk.GetHmdDof() == VrSdk.DoF.None) + { + Application.targetFrameRate = m_TargetFrameRate; + } + + if (VrSdk.HasRoomBounds()) + { + Vector3 extents = VrSdk.GetRoomExtents(); + m_RoomRadius = Mathf.Min(Mathf.Abs(extents.x), Mathf.Abs(extents.z)); + } + +#if USD_SUPPORTED + // Load the Usd Plugins + InitUsd.Initialize(); +#endif + + foreach (string s in Config.m_SketchFiles) + { + // Assume all relative paths are relative to the Sketches directory. + string sketch = s; + if (!System.IO.Path.IsPathRooted(sketch)) + { + sketch = System.IO.Path.Combine(App.UserSketchPath(), sketch); + } + m_RequestedTiltFileQueue.Enqueue(sketch); + if (Config.m_SdkMode == SdkMode.Ods || Config.OfflineRender) + { + // We only load one sketch for ODS rendering & offline rendering. + break; + } + } + + if (Config.m_AutosaveRestoreEnabled && AutosaveRestoreFileExists) + { + string lastAutosave = SaveLoadScript.m_Instance.MostRecentAutosaveFile(); + if (lastAutosave != null) + { + string newPath = SaveLoadScript.m_Instance.GenerateNewUntitledFilename( + UserSketchPath(), SaveLoadScript.TILT_SUFFIX); + if (newPath != null) + { + File.Copy(lastAutosave, newPath); + m_ShowAutosaveHint = true; + } + } + AutosaveRestoreFileExists = false; + } + + if (Config.m_SdkMode == SdkMode.Ods) + { + m_OdsPivot = (GameObject)Instantiate(m_OdsPrefab); + + OdsDriver driver = m_OdsPivot.GetComponent(); + driver.FramesToCapture = Config.m_OdsNumFrames; + driver.m_fps = Config.m_OdsFps; + driver.TurnTableRotation = Config.m_OdsTurnTableDegrees; + driver.OutputFolder = Config.m_OdsOutputPath; + driver.OutputBasename = Config.m_OdsOutputPrefix; + if (!string.IsNullOrEmpty(App.Config.m_VideoPathToRender)) + { + driver.CameraPath = App.Config.m_VideoPathToRender; + } + + ODS.HybridCamera cam = driver.OdsCamera; + cam.CollapseIpd = Config.m_OdsCollapseIpd; + cam.imageWidth /= Config.m_OdsPreview ? 4 : 1; + Debug.LogFormat("Configuring ODS:{0}" + + "Frames: {1}{0}" + + "FPS: {8}{0}" + + "TurnTable: {2}{0}" + + "Output: {3}{0}" + + "Basename: {4}{0}" + + "QuickLoad: {5}{0}" + + "CollapseIPD: {6}{0}" + + "ImageWidth: {7}{0}", + System.Environment.NewLine, + driver.FramesToCapture, + driver.TurnTableRotation, + driver.OutputFolder, + driver.OutputBasename, + Config.m_QuickLoad, + cam.CollapseIpd, + cam.imageWidth, + driver.m_fps); + } + + //these guys don't need to be alive just yet + PointerManager.m_Instance.EnablePointerStrokeGeneration(false); + + Console.WriteLine("RenderODS: {0}, numFrames: {1}", + m_OdsPivot != null, + m_OdsPivot ? m_OdsPivot.GetComponent().FramesToCapture + : 0); + + if (!AppAllowsCreation()) + { + TutorialManager.m_Instance.IntroState = IntroTutorialState.InitializeForNoCreation; + } + else + { + TutorialManager.m_Instance.IntroState = IntroTutorialState.Done; + } + if (m_RequestedTiltFileQueue.Count == 0) + { + TutorialManager.m_Instance.ActivateControllerTutorial(InputManager.ControllerName.Brush, false); + TutorialManager.m_Instance.ActivateControllerTutorial(InputManager.ControllerName.Wand, false); + } + + ViewpointScript.m_Instance.Init(); + QualityControls.m_Instance.Init(); + bool bVR = VrSdk.GetHmdDof() != TiltBrush.VrSdk.DoF.None; + InputManager.m_Instance.AllowVrControllers = bVR; + PointerManager.m_Instance.UseSymmetryWidget(bVR); + + switch (VrSdk.GetControllerDof()) + { + case TiltBrush.VrSdk.DoF.Six: + // Vive, Rift + Touch + SketchControlsScript.m_Instance.ActiveControlsType = + SketchControlsScript.ControlsType.SixDofControllers; + break; + case TiltBrush.VrSdk.DoF.None: + SketchControlsScript.m_Instance.ActiveControlsType = + SketchControlsScript.ControlsType.ViewingOnly; + break; + case TiltBrush.VrSdk.DoF.Two: + // Monoscopic + SketchControlsScript.m_Instance.ActiveControlsType = + SketchControlsScript.ControlsType.KeyboardMouse; + break; + } + + m_CurrentAppState = AppState.Standard; + m_DesiredAppState = AppState.LoadingBrushesAndLighting; + if (StartupError) + { + m_DesiredAppState = AppState.Error; + } + + m_SketchSurfacePanel = m_SketchSurface.GetComponent(); + + ViewpointScript.m_Instance.SetHeadMeshVisible(App.UserConfig.Flags.ShowHeadset); + ShowControllers = App.UserConfig.Flags.ShowControllers; + + SwitchState(); + + if (Config.m_AutoProfile || m_UserConfig.Profiling.AutoProfile) + { + StateChanged += AutoProfileOnStartAndQuit; + } + } + + private void AutoProfileOnStartAndQuit(AppState oldState, AppState newState) + { + if (newState == AppState.Standard) + { + Invoke("AutoProfileAndQuit", Config.m_AutoProfileWaitTime); + StateChanged -= AutoProfileOnStartAndQuit; + } + } + + private void AutoProfileAndQuit() + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.DoAutoProfileAndQuit); + } + + public void SetDesiredState(AppState rDesiredState) + { + m_DesiredAppState = rDesiredState; + } + + void Update() + { +#if UNITY_EDITOR + // All changes to Scene transform must go through Coords.cs + if (m_SceneTransform.hasChanged) + { + Debug.LogError("Detected unsanctioned change to Scene transform"); + m_SceneTransform.hasChanged = false; + } +#endif + + //look for state change + if (m_CurrentAppState != m_DesiredAppState) + { + SwitchState(); + } + + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate)) + { + // kinda heavy-handed, but whatevs + InitCursor(); + } + + // Wait for the environment transition to complete before capturing. + if (m_OdsPivot + && !m_OdsPivot.activeInHierarchy + && !SceneSettings.m_Instance.IsTransitioning + && ((m_CurrentAppState == AppState.Loading && !Config.m_QuickLoad) + || m_CurrentAppState == AppState.Standard)) + { + try + { + OdsDriver driver = m_OdsPivot.GetComponent(); + + // Load the secondary transform, if a second sketch was specified. + if (Config.m_SketchFiles.Length > 1) + { + string sketch = Config.m_SketchFiles[1]; + // Assume relative paths are relative to the sketches directory. + if (!System.IO.Path.IsPathRooted(sketch)) + { + sketch = System.IO.Path.Combine(App.UserSketchPath(), sketch); + } + var head = TrTransform.identity; + var scene = TrTransform.identity; + if (SaveLoadScript.m_Instance.LoadTransformsForOds(new DiskSceneFileInfo(sketch), + ref head, + ref scene)) + { + OdsHeadSecondary = head; + OdsSceneSecondary = scene; + } + else + { + Debug.LogErrorFormat("Failed to load secondary sketch for ODS: {0}", sketch); + } + } + + if (driver.OutputBasename == null || driver.OutputBasename == "") + { + driver.OutputBasename = + FileUtils.SanitizeFilename(SaveLoadScript.m_Instance.SceneFile.HumanName); + if (driver.OutputBasename == null || driver.OutputBasename == "") + { + if (Config.m_SketchFiles.Length > 0) + { + driver.OutputBasename = System.IO.Path.GetFileNameWithoutExtension( + Config.m_SketchFiles[0]); + } + else + { + driver.OutputBasename = "Untitled"; + } + } + } + + if (driver.OutputFolder == null || driver.OutputFolder == "") + { + driver.OutputFolder = App.VrVideosPath(); + FileUtils.InitializeDirectoryWithUserError(driver.OutputFolder); + } + + InputManager.m_Instance.EnablePoseTracking(false); + + driver.BeginRender(); + } + catch (System.Exception ex) + { + Debug.LogException(ex); + Application.Quit(); + Debug.Break(); + } + } + + m_IcosaAssetCatalog.UpdateCatalog(); + + //update state + switch (m_CurrentAppState) + { + case AppState.LoadingBrushesAndLighting: + { + if (!BrushCatalog.m_Instance.IsLoading + && !EnvironmentCatalog.m_Instance.IsLoading + && !m_ShaderWarmup.activeInHierarchy) + { + if (AppAllowsCreation()) + { + BrushController.m_Instance.SetBrushToDefault(); + BrushColor.SetColorToDefault(); + } + else + { + PointerManager.m_Instance.SetBrushForAllPointers(BrushCatalog.m_Instance.DefaultBrush); + } + + AudioManager.Enabled = true; + SceneSettings.m_Instance.SetDesiredPreset(EnvironmentCatalog.m_Instance.DefaultEnvironment); + + bool skipStandardIntro = true; + if (HandleExternalTiltOpenRequest()) + { + // tilt requested on command line was loaded + } + else if (Config.m_FilePatternsToExport != null) + { + m_DesiredAppState = AppState.Standard; + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.ExportListed); + } + else if (Config.OfflineRender) + { + m_DesiredAppState = AppState.Standard; + } + else if (DemoManager.m_Instance.DemoModeEnabled) + { + OnIntroComplete(); + } + else if (!VrSdk.IsHmdInitialized() || + UserConfig.Flags.SkipIntro || + UserConfig.Flags.DisableXrMode || + UserConfig.Flags.EnableMonoscopicMode) + { + OnIntroComplete(); + PanelManager.m_Instance.ReviveFloatingPanelsForStartup(); + } + else + { + if (Config.m_SdkMode == SdkMode.Ods) + { + // Skip the fade from black when we're rendering ODS. + m_DesiredAppState = AppState.Standard; + } + else + { + m_DesiredAppState = AppState.FadeFromBlack; + skipStandardIntro = false; + } + } + + if (skipStandardIntro) + { + DestroyIntroSketch(); + ViewpointScript.m_Instance.FadeToScene(float.MaxValue); + } + } + break; + } + case AppState.FadeFromBlack: + { + // On the Oculus platform, the Health and Safety warning may be visible, blocking the + // user's view. If this is the case, hold black until the warning is dismissed. + if (!VrSdk.IsAppFocusBlocked() || Config.m_SdkMode == SdkMode.Ods) + { + m_AppStateCountdown -= Time.deltaTime; + } + + if (m_AppStateCountdown <= 0.0f) + { + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + m_AppStateCountdown = 0; + if (!HasPlayedBefore) + { + m_DesiredAppState = AppState.FirstRunIntro; + } + else + { + m_DesiredAppState = AppState.Intro; + } + } + break; + } + case AppState.FirstRunIntro: + { + if (UpdateIntroFadeIsFinished()) + { + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + SaveLoadScript.m_Instance.NewAutosaveFile(); + m_DesiredAppState = AppState.Standard; + } + break; + } + case AppState.Intro: + { + if (UpdateIntroFadeIsFinished()) + { + if (!Config.IsMobileHardware) + { + InputManager.Brush.Behavior.BuzzAndGlow(1.0f, 7, .1f); + InputManager.Wand.Behavior.BuzzAndGlow(1.0f, 7, .1f); + AudioManager.m_Instance.PlayMagicControllerSound(); + } + PanelManager.m_Instance.ShowIntroSketchbookPanels(); + PointerManager.m_Instance.IndicateBrushSize = false; + PromoManager.m_Instance.RequestPromo(PromoType.InteractIntroPanel); + OnIntroComplete(); + } + break; + } + case AppState.Loading: + { + HandleExternalTiltOpenRequest(); + SketchControlsScript.m_Instance.UpdateControlsForLoading(); + + if (WidgetManager.m_Instance.CreatingMediaWidgets) + { + break; + } + + //trigger our tutorial a little bit after we started loading so it doesn't show up immediately + if (!m_QuickLoadEatInput) + { + float fPrevTutorialValue = m_QuickLoadHintCountdown; + m_QuickLoadHintCountdown -= Time.deltaTime; + if (fPrevTutorialValue > 0.0f && m_QuickLoadHintCountdown <= 0.0f) + { + TutorialManager.m_Instance.EnableQuickLoadTutorial(true); + } + } + + if (OverlayManager.m_Instance.CanDisplayQuickloadOverlay) + { + // Watch for speed up button presses and keep on loadin' + // Don't allow for quickloading yet if we are fading out the overlay from loading media. + UpdateQuickLoadLogic(); + } + if ((m_OdsPivot && Config.m_QuickLoad) || + (Config.OfflineRender) || !string.IsNullOrEmpty(m_UserConfig.Profiling.SketchToLoad)) + { + m_DesiredAppState = AppState.QuickLoad; + } + + // Call ContinueDrawingFromMemory() unless we are rendering ODS, in which case we don't want + // to animate the strokes until the renderer has actually started. + bool bContinueDrawing = true; + if (Config.m_SdkMode != SdkMode.Ods || m_OdsPivot.GetComponent().IsRendering) + { + bContinueDrawing = SketchMemoryScript.m_Instance.ContinueDrawingFromMemory(); + } + if (!bContinueDrawing) + { + FinishLoading(); + InputManager.m_Instance.TriggerHapticsPulse( + InputManager.ControllerName.Brush, 4, 0.15f, 0.1f); + InputManager.m_Instance.TriggerHapticsPulse( + InputManager.ControllerName.Wand, 4, 0.15f, 0.1f); + } + break; + } + case AppState.QuickLoad: + { + // Allow extra frames to complete fade to black. + // Required for OVR to position the overlay because it only does so once the transition + // is complete. + if (m_QuickloadStallFrames-- < 0) + { + bool bContinueDrawing = SketchMemoryScript.m_Instance.ContinueDrawingFromMemory(); + if (!bContinueDrawing) + { + FinishLoading(); + } + } + break; + } + case AppState.Uploading: + SketchControlsScript.m_Instance.UpdateControlsForUploading(); + break; + case AppState.MemoryExceeded: + SketchControlsScript.m_Instance.UpdateControlsForMemoryExceeded(); + break; + case AppState.Standard: + // Logic for fading out intro sketches. + if (m_IntroFadeTimer > 0 && + !PanelManager.m_Instance.IntroSketchbookMode && + !TutorialManager.m_Instance.TutorialActive()) + { + if (UpdateIntroFadeIsFinished()) + { + PanelManager.m_Instance.ReviveFloatingPanelsForStartup(); + } + } + + // If the app doesn't have focus, don't update. + if (VrSdk.IsAppFocusBlocked() && Config.m_SdkMode != SdkMode.Ods) + { + break; + } + + // Intro tutorial state machine. + TutorialManager.m_Instance.UpdateIntroTutorial(); + + // Continue edit-time playback, if any. + SketchMemoryScript.m_Instance.ContinueDrawingFromMemory(); + if (PanelManager.m_Instance.SketchbookActiveIncludingTransitions() && + PanelManager.m_Instance.IntroSketchbookMode) + { + // Limit controls if the user hasn't exited from the sketchbook post intro. + SketchControlsScript.m_Instance.UpdateControlsPostIntro(); + } + else + { + SketchControlsScript.m_Instance.UpdateControls(); + } + + // This should happen after SMS.ContinueDrawingFromMemory, so we're not loading and + // continuing in one frame. + HandleExternalTiltOpenRequest(); + break; + case AppState.Reset: + SketchControlsScript.m_Instance.UpdateControls(); + if (!PointerManager.m_Instance.IsMainPointerCreatingStroke() && + !PointerManager.m_Instance.IsMainPointerProcessingLine()) + { + StartReset(); + } + break; + } + } + + public void ExitIntroSketch() + { + PanelManager.m_Instance.SetInIntroSketchbookMode(false); + PointerManager.m_Instance.IndicateBrushSize = true; + PointerManager.m_Instance.PointerColor = PointerManager.m_Instance.PointerColor; + PromoManager.m_Instance.RequestPromo(PromoType.BrushSize); + } + + private void StartReset() + { + // Switch to paint tool if not already there. + SketchSurfacePanel.m_Instance.EnableDefaultTool(); + + // Disable preview line. + PointerManager.m_Instance.AllowPointerPreviewLine(false); + + // Switch to the default brush type and size. + BrushController.m_Instance.SetBrushToDefault(); + + // Disable audio reactive mode. + if (m_RequestingAudioReactiveMode) + { + ToggleAudioReactiveModeRequest(); + } + + // Reset to the default brush color. + BrushColor.SetColorToDefault(); + + // Clear saved colors. + CustomColorPaletteStorage.m_Instance.ClearAllColors(); + + // Turn off straightedge + PointerManager.m_Instance.StraightEdgeModeEnabled = false; + + // Turn off straightedge ruler. + if (PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter()) + { + PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); + } + + // Close any panel menus that might be open (e.g. Sketchbook) + if (PanelManager.m_Instance.SketchbookActive()) + { + PanelManager.m_Instance.ToggleSketchbookPanels(); + } + else if (PanelManager.m_Instance.SettingsActive()) + { + PanelManager.m_Instance.ToggleSettingsPanels(); + } + else if (PanelManager.m_Instance.MemoryWarningActive()) + { + PanelManager.m_Instance.ToggleMemoryWarningMode(); + } + else if (PanelManager.m_Instance.BrushLabActive()) + { + PanelManager.m_Instance.ToggleBrushLabPanels(); + } + + // Hide all panels. + SketchControlsScript.m_Instance.RequestPanelsVisibility(false); + + // Reset all panels. + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.ResetAllPanels); + + // Rotate want panels to default orientation (color picker). + PanelManager.m_Instance.ResetWandPanelRotation(); + + // Close Twitch widget. + if (SketchControlsScript.m_Instance.IsCommandActive(SketchControlsScript.GlobalCommands.IRC)) + { + SketchControlsScript.m_Instance.IssueGlobalCommand(SketchControlsScript.GlobalCommands.IRC); + } + + // Close Youtube Chat widget. + if (SketchControlsScript.m_Instance.IsCommandActive( + SketchControlsScript.GlobalCommands.YouTubeChat)) + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.YouTubeChat); + } + + // Hide the pointer and reticle. + PointerManager.m_Instance.RequestPointerRendering(false); + SketchControlsScript.m_Instance.ForceShowUIReticle(false); + } + + private void FinishReset() + { + // Switch to the default environment. + SceneSettings.m_Instance.SetDesiredPreset(EnvironmentCatalog.m_Instance.DefaultEnvironment); + + // Clear the sketch and reset the scene transform. + SketchControlsScript.m_Instance.NewSketch(fade: false); + + // Disable mirror. + PointerManager.m_Instance.SetSymmetryMode(PointerManager.SymmetryMode.None); + + // Reset mirror position. + PointerManager.m_Instance.ResetSymmetryToHome(); + + // Show the wand panels. + SketchControlsScript.m_Instance.RequestPanelsVisibility(true); + + // Show the pointer. + PointerManager.m_Instance.RequestPointerRendering(true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + + // Forget command history. + SketchMemoryScript.m_Instance.ClearMemory(); + } + + void FinishLoading() + { + // Force progress to be full before exiting (for small scenes) + OverlayManager.m_Instance.UpdateProgress(1); + OverlayManager.m_Instance.HideOverlay(); + //if we just released the button, kick a fade out + if (m_QuickLoadInputWasValid) + { + OverlayManager.m_Instance.PauseRendering(false); + OverlayManager.m_Instance.FadeFromCompositor(0); + } + + m_DesiredAppState = AppState.Standard; + if (VrSdk.GetControllerDof() == TiltBrush.VrSdk.DoF.Six) + { + float holdDelay = (m_CurrentAppState == AppState.QuickLoad) ? 1.0f : 0.0f; + StartCoroutine(DelayedSketchLoadedCard(holdDelay)); + } + else + { + OutputWindowScript.m_Instance.AddNewLine( + OutputWindowScript.LineType.Special, "Sketch Loaded!"); + } + + OnPlaybackComplete(); + m_SketchSurfacePanel.EnableRenderer(true); + + //turn off quick load tutorial + TutorialManager.m_Instance.EnableQuickLoadTutorial(false); + + AudioManager.m_Instance.PlaySketchLoadedSound( + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); + + SketchControlsScript.m_Instance.RequestPanelsVisibility(true); + if (VideoRecorderUtils.ActiveVideoRecording == null) + { + SketchSurfacePanel.m_Instance.EatToolsInput(); + } + SketchSurfacePanel.m_Instance.RequestHideActiveTool(false); + SketchControlsScript.m_Instance.RestoreFloatingPanels(); + PointerManager.m_Instance.RequestPointerRendering( + SketchSurfacePanel.m_Instance.ShouldShowPointer()); + PointerManager.m_Instance.RestoreBrushInfo(); + WidgetManager.m_Instance.LoadingState(false); + WidgetManager.m_Instance.WidgetsDormant = true; + SketchControlsScript.m_Instance.EatGrabInput(); + SaveLoadScript.m_Instance.MarkAsAutosaveDone(); + if (SaveLoadScript.m_Instance.SceneFile.InfoType == FileInfoType.Disk) + { + PromoManager.m_Instance.RequestAdvancedPanelsPromo(); + } + SketchMemoryScript.m_Instance.SanitizeMemoryList(); + + if (Config.OfflineRender) + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.RenderCameraPath); + } + + Scene.BroadcastCanvasUpdate(); + } + + private IEnumerator DelayedSketchLoadedCard(float delay) + { + float stall = delay; + while (stall >= 0.0f) + { + stall -= Time.deltaTime; + yield return null; + } + + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, "Sketch Loaded!"); + } + + void SwitchState() + { + switch (m_CurrentAppState) + { + case AppState.LoadingBrushesAndLighting: + if (VrSdk.GetControllerDof() == VrSdk.DoF.Two) + { + // Sketch surface tool is not properly loaded because + // it is the default tool. + SketchSurfacePanel.m_Instance.ActiveTool.EnableTool(false); + SketchSurfacePanel.m_Instance.ActiveTool.EnableTool(true); + } + break; + case AppState.Reset: + // Demos should reset to the standard state only. + Debug.Assert(m_DesiredAppState == AppState.Standard); + FinishReset(); + break; + case AppState.AutoProfiling: + case AppState.OfflineRendering: + InputManager.m_Instance.EnablePoseTracking(true); + break; + case AppState.MemoryExceeded: + SketchSurfacePanel.m_Instance.EnableDefaultTool(); + PanelManager.m_Instance.ToggleMemoryWarningMode(); + PointerManager.m_Instance.RequestPointerRendering( + SketchSurfacePanel.m_Instance.ShouldShowPointer()); + break; + } + + switch (m_DesiredAppState) + { + case AppState.LoadingBrushesAndLighting: + BrushCatalog.m_Instance.BeginReload(); + EnvironmentCatalog.m_Instance.BeginReload(); + CreateIntroSketch(); + break; + case AppState.FadeFromBlack: + ViewpointScript.m_Instance.FadeToScene(1.0f / m_FadeFromBlackDuration); + m_AppStateCountdown = m_FadeFromBlackDuration; + break; + case AppState.FirstRunIntro: + AudioManager.m_Instance.PlayFirstRunMusic(AudioManager.FirstRunMusic.IntroAmbient); + m_SketchSurfacePanel.EnableRenderer(false); + TutorialManager.m_Instance.IntroState = IntroTutorialState.ActivateBrush; + m_IntroFadeTimer = 0; + break; + case AppState.Intro: + AudioManager.m_Instance.PlayFirstRunMusic(AudioManager.FirstRunMusic.IntroAmbient); + m_SketchSurfacePanel.EnableRenderer(false); + m_IntroFadeTimer = 0; + break; + case AppState.Loading: + if (m_IntroFadeTimer > 0) + { + AudioManager.m_Instance.SetMusicVolume(0.0f); + m_IntroFadeTimer = 0; + DestroyIntroSketch(); + PanelManager.m_Instance.ReviveFloatingPanelsForStartup(); + } + PointerManager.m_Instance.StoreBrushInfo(); + m_QuickLoadHintCountdown = m_QuickLoadHintDelay; + m_QuickLoadEatInput = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Panic); + m_QuickLoadInputWasValid = false; + + // Don't disable tools if we've got a valid load tool active. + bool bToolsAllowed = SketchSurfacePanel.m_Instance.ActiveTool.AvailableDuringLoading(); + if (!bToolsAllowed) + { + m_SketchSurfacePanel.EnableRenderer(false); + } + else + { + m_SketchSurfacePanel.RequestHideActiveTool(false); + } + + if (!bToolsAllowed) + { + SketchSurfacePanel.m_Instance.EnableDefaultTool(); + } + PointerManager.m_Instance.RequestPointerRendering(false); + SketchControlsScript.m_Instance.RequestPanelsVisibility(false); + SketchControlsScript.m_Instance.ResetActivePanel(); + PanelManager.m_Instance.HideAllPanels(); + SketchControlsScript.m_Instance.ForceShowUIReticle(false); + PointerManager.m_Instance.SetSymmetryMode(PointerManager.SymmetryMode.None, false); + WidgetManager.m_Instance.LoadingState(true); + WidgetManager.m_Instance.StencilsDisabled = true; + break; + case AppState.QuickLoad: + SketchMemoryScript.m_Instance.QuickLoadDrawingMemory(); + break; + case AppState.MemoryExceeded: + if (!PanelManager.m_Instance.MemoryWarningActive()) + { + PanelManager.m_Instance.ToggleMemoryWarningMode(); + } + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.EmptyTool); + AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); + break; + case AppState.Standard: + PointerManager.m_Instance.DisablePointerPreviewLine(); + // Refresh the tinting on the controllers + PointerManager.m_Instance.PointerColor = PointerManager.m_Instance.PointerColor; + + if (m_ShowAutosaveHint) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController(InputManager.ControllerName.Wand, + "Abnormal program termination detected!\n" + + "The last autosave has been copied into your sketchbook."); + m_ShowAutosaveHint = false; + } + break; + case AppState.Reset: + PointerManager.m_Instance.EnablePointerStrokeGeneration(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.EatLineEnabledInput(); + PointerManager.m_Instance.EnableLine(false); + break; + case AppState.AutoProfiling: + case AppState.OfflineRendering: + InputManager.m_Instance.EnablePoseTracking(false); + break; + } + + var oldState = m_CurrentAppState; + m_CurrentAppState = m_DesiredAppState; + if (StateChanged != null) + { + StateChanged(oldState, m_CurrentAppState); + } + } + + /// Load one requested sketch, if any, returning true if pending request was processed. + private bool HandleExternalTiltOpenRequest() + { + // Early out if we're in the intro tutorial. + if (TutorialManager.m_Instance.TutorialActive() || m_RequestedTiltFileQueue.Count == 0) + { + return false; + } + + string path; + try + { + path = (string)m_RequestedTiltFileQueue.Dequeue(); + } + catch (InvalidOperationException) + { + return false; + } + Debug.LogFormat("Received external request to load {0}", path); + + if (path.StartsWith(kProtocolHandlerPrefix)) + { + return HandlePolyRequest(path); + } + + // Copy to sketch folder in order to discourage the user from explicitly saving + // to gallery for future access, which would (by design) strip attribution. + // Crypto hash suffix is added to the filename for (deterministic) uniqueness. + try + { + string dstFilename = Path.GetFileName(path); + if (Path.GetFullPath(Path.GetDirectoryName(path)) != Path.GetFullPath(UserSketchPath()) && + SaveLoadScript.Md5Suffix(dstFilename) == null) + { + dstFilename = SaveLoadScript.AddMd5Suffix(dstFilename, + SaveLoadScript.GetMd5(path)); + } + string dstPath = Path.Combine(UserSketchPath(), dstFilename); + if (!File.Exists(dstPath)) + { + File.Copy(path, dstPath); + } + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.LoadNamedFile, sParam: dstPath); + } + catch (FileNotFoundException) + { + OutputWindowScript.Error(String.Format("Couldn't open {0}", path)); + return false; + } + return true; + } + + private bool HandlePolyRequest(string request) + { + string id = request.Substring(kProtocolHandlerPrefix.Length); // Strip prefix to get asset id + StartCoroutine(VrAssetService.m_Instance.LoadTiltFile(id)); + return true; + } + + public bool ShouldTintControllers() + { + return m_DesiredAppState == AppState.Standard && !PanelManager.m_Instance.IntroSketchbookMode; + } + + public bool IsInStateThatAllowsPainting() + { + return !TutorialManager.m_Instance.TutorialActive() && + CurrentState == AppState.Standard && + !PanelManager.m_Instance.IntroSketchbookMode; + } + + public bool IsInStateThatAllowsAnyGrabbing() + { + return !TutorialManager.m_Instance.TutorialActive() && + !PanelManager.m_Instance.IntroSketchbookMode && + (CurrentState == AppState.Standard || CurrentState == AppState.Loading) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + public bool IsLoading() + { + return CurrentState == AppState.Loading || CurrentState == AppState.QuickLoad; + } + + void UpdateQuickLoadLogic() + { + if (CurrentState == AppState.Loading && AppAllowsCreation()) + { + //require the user to stop holding the trigger before pulling it again to speed load + if (m_QuickLoadEatInput) + { + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Panic)) + { + m_QuickLoadEatInput = false; + } + } + else + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Panic) && + !SketchControlsScript.m_Instance.IsUserInteractingWithAnyWidget() && + !SketchControlsScript.m_Instance.IsUserGrabbingWorld() && + (VideoRecorderUtils.ActiveVideoRecording == null) && + (!VrSdk.IsAppFocusBlocked() || Config.m_SdkMode == SdkMode.Ods)) + { + OverlayManager.m_Instance.SetOverlayFromType(OverlayType.LoadSketch); + //if we just pressed the button, kick a fade in + if (!m_QuickLoadInputWasValid) + { + if (ViewpointScript.m_Instance.AllowsFading) + { + OverlayManager.m_Instance.FadeToCompositor(0); + } + else + { + ViewpointScript.m_Instance.SetOverlayToBlack(); + } + OverlayManager.m_Instance.PauseRendering(true); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); + } + + m_QuickLoadInputWasValid = true; + if (m_CurrentAppState != AppState.QuickLoad) + { + OverlayManager.m_Instance.SetOverlayTransitionRatio(1.0f); + m_QuickloadStallFrames = 1; + m_DesiredAppState = AppState.QuickLoad; + m_SketchSurfacePanel.EnableRenderer(false); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.1f); + } + } + else + { + //if we just released the button, kick a fade out + if (m_QuickLoadInputWasValid) + { + OverlayManager.m_Instance.PauseRendering(false); + OverlayManager.m_Instance.FadeFromCompositor(0); + } + m_QuickLoadInputWasValid = false; + } + } + } + } + + void OnIntroComplete() + { + SaveLoadScript.m_Instance.NewAutosaveFile(); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + SketchControlsScript.m_Instance.RequestPanelsVisibility(true); + + // If the user chooses to skip the intro, assume they've done the tutorial before. + PlayerPrefs.SetInt(App.kPlayerPrefHasPlayedBefore, 1); + + m_DesiredAppState = AppState.Standard; + } + + // Updates the intro fade (both in and out) and returns true when finished. + // Has a special case for Mobile, so that the scene fades out as soon as it has faded in. + // + // For desktop: + // FFFFFFFFFFFFT FFFFFFFFFFFFFFFFFT + // ^ Start ^ Faded in ^ Standard Mode ^ Faded out + // + // For mobile: + // FFFFFFFFFFFFFFFFFFFFFFFFFFFT T + // ^ Start ^ Faded in ^ Faded out ^ Standard Mode + // + // (F = returns false, T = returns true) + + bool UpdateIntroFadeIsFinished() + { + if (m_IntroSketchRenderers == null) + { + m_IntroFadeTimer = 0; + // This code path gets triggered when running on mobile. At this point the fade out has + // already happened, so we just return true so that the 'once-fadeout-has-finished' code gets + // triggered. + return true; + } + + bool isMobile = Config.IsMobileHardware; + bool isFadingIn = m_IntroFadeTimer < 1f; + float fadeMax = isFadingIn ? 1f : 2f; + float fadeDuration; + if (isMobile) + { + fadeDuration = isFadingIn + ? m_IntroSketchMobileFadeInDuration + : m_IntroSketchMobileFadeOutDuration; + } + else + { + fadeDuration = isFadingIn ? m_IntroSketchFadeInDuration : m_IntroSketchFadeOutDuration; + } + + m_IntroFadeTimer += Time.deltaTime / fadeDuration; + if (m_IntroFadeTimer > fadeMax) + { + m_IntroFadeTimer = fadeMax; + } + + for (int i = 0; i < m_IntroSketchRenderers.Length; ++i) + { + m_IntroSketchRenderers[i].material.SetFloat("_IntroDissolve", + Mathf.SmoothStep(0, 1, Math.Abs(1 - m_IntroFadeTimer))); + } + + if (m_IntroFadeTimer == fadeMax) + { + if (isFadingIn) + { + // With Mobile, we fade in then out, so the fade isn't complete at fade-in. + return !isMobile; + } + else + { + DestroyIntroSketch(); + m_IntroSketchRenderers = null; + return true; + } + } + + return false; + } + + void InitCursor() + { + if (StartupError) + { + return; + } + if (VrSdk.GetHmdDof() == TiltBrush.VrSdk.DoF.None) + { + Cursor.visible = false; + Cursor.lockState = CursorLockMode.Locked; + } + } + + public static T DeserializeObjectWithWarning(string text, out string warning) + { + // Try twice, once to catch "unknown key" warnings, once to actually get a result. + warning = null; + try + { + return JsonConvert.DeserializeObject(text, new JsonSerializerSettings + { + MissingMemberHandling = MissingMemberHandling.Error + }); + } + catch (JsonSerializationException e) + { + warning = e.Message; + return JsonConvert.DeserializeObject(text); + } + } + + void CreateDefaultConfig() + { + // If we don't have a .cfg in our Tilt Brush directory, drop a default one. + string tiltBrushFolder = UserPath(); + if (!Directory.Exists(tiltBrushFolder)) + { + return; + } + + string configPath = Path.Combine(tiltBrushFolder, kConfigFileName); + if (!File.Exists(configPath)) + { + FileUtils.WriteTextFromResources(kDefaultConfigPath, configPath); + } + } + + public void RefreshUserConfig() + { + m_UserConfig = new UserConfig(); + + try + { + string sConfigPath = App.ConfigPath(); + if (!File.Exists(sConfigPath)) + { + return; + } + + string text; + try + { + text = File.ReadAllText(sConfigPath, System.Text.Encoding.UTF8); + } + catch (Exception e) + { + // UnauthorizedAccessException, IOException + OutputWindowScript.Error($"Error reading {kConfigFileName}", e.Message); + return; + } + + try + { + string warning; + m_UserConfig = DeserializeObjectWithWarning(text, out warning); + if (warning != null) + { + OutputWindowScript.Error($"Warning reading {kConfigFileName}", warning); + } + } + catch (Exception e) + { + OutputWindowScript.Error($"Error reading {kConfigFileName}", e.Message); + return; + } + } + finally + { + // Apply any overrides sent through via the command line, even if reading from Tilt Brush.cfg + // goes horribly wrong. + Config.ApplyUserConfigOverrides(m_UserConfig); + } + } + + public void CreateFailedToDetectVrDialog(string msg = null, bool allowViewing = true) + { + GameObject dialog = Instantiate(m_ErrorDialog); + var initScript = dialog.GetComponent(); + if (!string.IsNullOrEmpty(msg)) + { + var textMesh = initScript.m_Heading; + textMesh.text = @$" Tiltasaurus says... + {msg}"; + } + initScript.ShowSketchSelectorUi(allowViewing && !StartupError); + } + + static public bool AppAllowsCreation() + { + // TODO: this feels like it should be an explicit part of Config, + // not something based on VR hardware... + return App.VrSdk.GetControllerDof() != TiltBrush.VrSdk.DoF.None; + } + + static public string PlatformPath() + { + if (!Application.isEditor && Application.platform == RuntimePlatform.OSXPlayer) + { + return System.IO.Directory.GetParent(Application.dataPath).Parent.ToString(); + } + else if (Application.platform == RuntimePlatform.Android) + { + return Application.persistentDataPath; + } + + return System.IO.Directory.GetParent(Application.dataPath).ToString(); + } + + static public string SupportPath() + { + return Path.Combine(PlatformPath(), "Support"); + } + + /// Returns a parent of UserPath; used to figure out how much path + /// is necessary to display to the user when giving feedback. We + /// assume this is the "boring" portion of the path that they can infer. + public static string DocumentsPath() + { + switch (Application.platform) + { + case RuntimePlatform.WindowsPlayer: + case RuntimePlatform.WindowsEditor: + case RuntimePlatform.OSXPlayer: + case RuntimePlatform.OSXEditor: + case RuntimePlatform.LinuxPlayer: + case RuntimePlatform.LinuxEditor: + return System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); + case RuntimePlatform.Android: + case RuntimePlatform.IPhonePlayer: + default: + return Application.persistentDataPath; + } + } + + void InitUserPath() + { + switch (Application.platform) + { + case RuntimePlatform.WindowsPlayer: + case RuntimePlatform.WindowsEditor: + // user Documents folder + m_UserPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); + + // GetFolderPath() can fail, returning an empty string. + if (m_UserPath == "") + { + // If that happens, try a bunch of other folders. + m_UserPath = System.Environment.GetFolderPath( + System.Environment.SpecialFolder.MyDocuments); + if (m_UserPath == "") + { + m_UserPath = System.Environment.GetFolderPath( + System.Environment.SpecialFolder.DesktopDirectory); + } + } + break; + case RuntimePlatform.OSXPlayer: + case RuntimePlatform.OSXEditor: + case RuntimePlatform.LinuxPlayer: + case RuntimePlatform.LinuxEditor: + // user Documents folder + m_UserPath = Path.Combine(System.Environment.GetFolderPath( + System.Environment.SpecialFolder.Personal), + "Documents"); + break; + case RuntimePlatform.Android: + m_UserPath = "/sdcard/"; + m_OldUserPath = Application.persistentDataPath; + break; + case RuntimePlatform.IPhonePlayer: + default: + m_UserPath = Application.persistentDataPath; + break; + } + + m_UserPath = Path.Combine(m_UserPath, App.kAppFolderName); + + // In the case that we have changed the location of the user data, move the user data from the + // old location to the new one. + if (!string.IsNullOrEmpty(m_OldUserPath)) + { + MoveUserDataFromOldLocation(); + } + + if (!Path.IsPathRooted(m_UserPath)) + { + StartupError = true; + CreateFailedToDetectVrDialog( + "Failed to find Documents folder.\nIn Windows, try modifying your Controlled Folder Access settings.", + allowViewing: false + ); + } + } + + private void MoveUserDataFromOldLocation() + { + m_OldUserPath = Path.Combine(m_OldUserPath, App.kAppFolderName); + + if (!Directory.Exists(m_OldUserPath)) + { + return; + } + + if (Directory.Exists(m_UserPath)) + { + return; + } + + try + { + Directory.Move(m_OldUserPath, m_UserPath); + // Recreate the old directory and put a message in there so a user used to looking in the old + // location can find out where to get their files. + Directory.CreateDirectory(m_OldUserPath); + string moveMessageFilename = Path.Combine(m_OldUserPath, kFileMoveFilename); + File.WriteAllText(moveMessageFilename, kFileMoveContents); + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + + // Return path of root directory for storing user sketches, snapshots, etc. + public static string UserPath() + { + return App.m_Instance.m_UserPath; + } + + public static bool InitDirectoryAtPath(string path) + { + if (Directory.Exists(path)) + { + return true; + } + if (!FileUtils.InitializeDirectoryWithUserError(path)) + { + return false; + } + return true; + } + + public static string ShortenForDescriptionText(string desc) + { + desc = desc.Split('\n')[0]; + if (desc.Length > 33) + { + desc = desc.Substring(0, 30) + "..."; + } + return desc; + } + + /// Creates the Media Library directory if it does not already exist. + /// Returns true if the directory already exists or if it is created successfully, false if the + /// directory could not be created. + public static bool InitMediaLibraryPath() + { + string mediaLibraryPath = MediaLibraryPath(); + if (!InitDirectoryAtPath(mediaLibraryPath)) { return false; } + string readmeFile = Path.Combine(mediaLibraryPath, Config.m_MediaLibraryReadme); + FileUtils.WriteTextFromResources(Config.m_MediaLibraryReadme, + Path.ChangeExtension(readmeFile, ".txt")); + return true; + } + + /// Creates the Model Catalog directory and copies in the provided default models. + /// Returns true if the directory already exists or if it is created successfully, false if the + /// directory could not be created. + public static bool InitModelLibraryPath(string[] defaultModels) + { + string modelsDirectory = ModelLibraryPath(); + + // TODO:Mikesky - Re-enable this check in a few versions, + // and remove the one in the obj removal loop. + + // if (Directory.Exists(modelsDirectory)) { return true; } + + if (!InitDirectoryAtPath(modelsDirectory)) { return false; } + + // Tidy up old obj models and replace with gltfs + // We can remove this at some point. + var targetDirs = new string[] { "Andy", "Tiltasaurus" }; + foreach (string target in targetDirs) + { + var path = Path.Combine(modelsDirectory, target); + if (Directory.Exists(path)) + { + Debug.Log($"Found old model file: \"{path}\", removing."); + Directory.Delete(path, true); + } + else + { + // We've already tidied up the old models, or the user has and understands + // the reference library. Let's not interfere. + return true; + } + } + + foreach (string fileName in defaultModels) + { + string[] path = fileName.Split( + new[] { '\\', '/' }, 3, StringSplitOptions.RemoveEmptyEntries); + string newModel = Path.Combine(modelsDirectory, path[1]); + + if (!File.Exists(newModel)) + { + FileUtils.WriteBytesFromResources(fileName, newModel); + } + } + return true; + } + + /// Creates the Background Images directory and copies in the provided default images. + /// Returns true if the directory already exists or if it is created successfully, false if the + /// directory could not be created. + public static bool InitBackgroundImagesPath(string[] defaultBackgroundImages) + { + string path = BackgroundImagesLibraryPath(); + if (!Directory.Exists(path)) + { + if (!FileUtils.InitializeDirectoryWithUserError(path)) + { + return false; + } + } + + // Populate the reference images folder exactly once. + int seeded = PlayerPrefs.GetInt(kBackgroundImagesSeeded); + if (seeded == 0) + { + foreach (string fileName in defaultBackgroundImages) + { + FileUtils.WriteBytesFromResources(fileName, + Path.Combine(path, Path.GetFileName(fileName.Replace(".bytes", "")))); + } + PlayerPrefs.SetInt(kBackgroundImagesSeeded, 1); + } + return true; + } + + /// Creates the Reference Images directory and copies in the provided default images. + /// Returns true if the directory already exists or if it is created successfully, false if the + /// directory could not be created. + public static bool InitReferenceImagePath(string[] defaultImages) + { + string path = ReferenceImagePath(); + if (!Directory.Exists(path)) + { + if (!FileUtils.InitializeDirectoryWithUserError(path)) + { + return false; + } + } + + // Populate the reference images folder exactly once. + int seeded = PlayerPrefs.GetInt(kReferenceImagesSeeded); + if (seeded == 0) + { + foreach (string fileName in defaultImages) + { + FileUtils.WriteTextureFromResources(fileName, + Path.Combine(path, Path.GetFileName(fileName))); + } + PlayerPrefs.SetInt(kReferenceImagesSeeded, 1); + } + return true; + } + + public static bool InitVideoLibraryPath(string[] defaultVideos) + { + string videosDirectory = VideoLibraryPath(); + if (Directory.Exists(videosDirectory)) + { + return true; + } + if (!InitDirectoryAtPath(videosDirectory)) + { + return false; + } + foreach (var video in defaultVideos) + { + string destFilename = Path.GetFileName(video); + FileUtils.WriteBytesFromResources(video, Path.Combine(videosDirectory, destFilename)); + } + + return true; + } + + public static string FeaturedSketchesPath() + { + return Path.Combine(Application.persistentDataPath, "Featured Sketches"); + } + + public static string MediaLibraryPath() + { + return Path.Combine(UserPath(), "Media Library"); + } + + public static string ModelLibraryPath() + { + return Path.Combine(MediaLibraryPath(), "Models"); + } + + public static string ReferenceImagePath() + { + return Path.Combine(MediaLibraryPath(), "Images"); + } + + public static string VideoLibraryPath() + { + return Path.Combine(MediaLibraryPath(), "Videos"); + } + + public static string BackgroundImagesLibraryPath() + { + return Path.Combine(MediaLibraryPath(), "BackgroundImages"); + } + + static public string UserSketchPath() + { + return Path.Combine(UserPath(), "Sketches"); + } + + static public string AutosavePath() + { + return Path.Combine(UserPath(), "Sketches/Autosave"); + } + + static public string ConfigPath() + { + return Path.Combine(UserPath(), kConfigFileName); + } + + static public string UserExportPath() + { + return App.Config.m_ExportPath ?? Path.Combine(UserPath(), "Exports"); + } + + static public string AutosaveRestoreFilePath() + { + return Path.Combine(UserPath(), "Sketches/Autosave/AutosaveRestore"); + } + + static public string SnapshotPath() + { + return Path.Combine(UserPath(), "Snapshots"); + } + + static public string VideosPath() + { + return Path.Combine(UserPath(), "Videos"); + } + + static public string VrVideosPath() + { + return Path.Combine(UserPath(), "VRVideos"); + } + + void OnApplicationQuit() + { + if (AppExit != null) + { + AppExit(); + } + + AutosaveRestoreFileExists = false; + } + + void OnPlaybackComplete() + { + SaveLoadScript.m_Instance.SignalPlaybackCompletion(); + if (SketchControlsScript.m_Instance.SketchPlaybackMode != + SketchMemoryScript.PlaybackMode.Timestamps) + { + + // For non-timestamp playback mode, adjust current time to last stroke in drawing. + try + { + this.CurrentSketchTime = SketchMemoryScript.m_Instance.GetApproximateLatestTimestamp(); + } + catch (InvalidOperationException) + { + // Can happen as an edge case, eg if we try to load a file that doesn't exist. + this.CurrentSketchTime = 0; + } + } + } + + public TiltBrushManifest GetMergedManifest(bool forceExperimental = false) + { + var manifest = m_Manifest; + if (Config.IsExperimental || forceExperimental) + { + if (m_ManifestExperimental != null) + { + manifest = Instantiate(m_Manifest); + manifest.AppendFrom(m_ManifestExperimental); + } + } +#if ZAPBOX_SUPPORTED + manifest = m_ZapboxManifest; +#endif + return manifest; + } + + // Previously Experimental-Mode only + public bool IsBrushExperimental(BrushDescriptor brush) + { + return m_ManifestExperimental.Brushes.Contains(brush); + } + + DateTime GetLinkerTime(Assembly assembly, TimeZoneInfo target = null) + { +#if !(UNITY_ANDROID || UNITY_IOS) + var filePath = assembly.Location; + const int c_PeHeaderOffset = 60; + const int c_LinkerTimestampOffset = 8; + + var buffer = new byte[2048]; + + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + stream.Read(buffer, 0, 2048); + + var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset); + var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset); + var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + var linkTimeUtc = epoch.AddSeconds(secondsSince1970); + return linkTimeUtc.ToLocalTime(); +#else + return DateTime.Now; +#endif + } + + // By executing the URL directly windows will open it without making the browser a child + // process of Tilt Brush. If this fails or throws an exception we fall back to Unity's + // OpenURL(). + public static void OpenURL(string url) + { + var isPolyUrl = (url.Contains("poly.google.com/") || url.Contains("vr.google.com")); + if (isPolyUrl && GoogleIdentity.LoggedIn) + { + var email = GoogleIdentity.Profile.email; + url = $"https://accounts.google.com/AccountChooser?Email={email}&continue={url}"; + } +#if UNITY_STANDALONE_WINDOWS + var startInfo = new System.Diagnostics.ProcessStartInfo(url); + startInfo.UseShellExecute = true; + try { + if (System.Diagnostics.Process.Start(startInfo) == null) { + Application.OpenURL(url); + } + } catch (Exception) { + Application.OpenURL(url); + } +#else + switch (Application.platform) + { + case RuntimePlatform.OSXEditor: + case RuntimePlatform.OSXPlayer: + System.Diagnostics.Process.Start(url); + break; + default: + Application.OpenURL(url); + break; + } +#endif + } + + /// This copies the support files from inside the Streaming Assets folder to the support folder. + /// This only happens on Android. The files have to be extracted directly from the .apk. + private static void CopySupportFiles() + { + if (Application.platform != RuntimePlatform.Android) + { + return; + } + if (!Directory.Exists(SupportPath())) + { + Directory.CreateDirectory(SupportPath()); + } + + Func GetIndexOfEnd = (s) => Application.streamingAssetsPath.IndexOf(s) + s.Length; + + // Find the apk file + int apkIndex = GetIndexOfEnd("file://"); + int fileIndex = Application.streamingAssetsPath.IndexOf("!/"); + string apkFilename = Application.streamingAssetsPath.Substring(apkIndex, fileIndex - apkIndex); + + const string supportBeginning = "assets/Support/"; + + try + { + using (Stream zipFile = File.Open(apkFilename, FileMode.Open, FileAccess.Read)) + { + ZipLibrary.ZipFile zip = new ZipLibrary.ZipFile(zipFile); + foreach (ZipLibrary.ZipEntry entry in zip) + { + if (entry.IsFile && entry.Name.StartsWith(supportBeginning)) + { + // Create the directory if needed. + string fullPath = Path.Combine(App.SupportPath(), + entry.Name.Substring(supportBeginning.Length)); + string directory = Path.GetDirectoryName(fullPath); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + // Copy the data over to a file. + using (Stream entryStream = zip.GetInputStream(entry)) + { + using (FileStream fileStream = File.Create(fullPath)) + { + byte[] buffer = new byte[16 * 1024]; // Do it in 16k chunks + while (true) + { + int size = entryStream.Read(buffer, 0, buffer.Length); + if (size > 0) + { + fileStream.Write(buffer, 0, size); + } + else + { + break; + } + } + } + } + + } + } + zip.Close(); + } + } + catch (Exception ex) + { + Debug.LogException(ex); + } + } + + public void LogoutIcosa() + { + IcosaUserName = null; + IcosaUserId = null; + IcosaUserIcon = null; + IcosaToken = null; + } + } // class App +} // namespace TiltBrush diff --git a/Assets/Scripts/Export/Export.cs b/Assets/Scripts/Export/Export.cs index 3c782d74a8..a1f6af2434 100644 --- a/Assets/Scripts/Export/Export.cs +++ b/Assets/Scripts/Export/Export.cs @@ -1,333 +1,333 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using UnityEngine; -using UnityEngine.Localization.Settings; -using UnityGLTF; - -namespace TiltBrush -{ - - public static class Export - { - const string kExportDocumentationUrl = "https://docs.google.com/document/d/11ZsHozYn9FnWG7y3s3WAyKIACfbfwb4PbaS8cZ_xjvo#heading=h.im5f33smiavy"; -#if UNITY_ANDROID || UNITY_IOS - const string kExportReadmeName = "README.txt"; - const string kExportReadmeBody = "Please see " + kExportDocumentationUrl; -#else - const string kExportReadmeName = "README.url"; - const string kExportReadmeBody = @"[InternetShortcut] -URL=" + kExportDocumentationUrl; -#endif - const string kExportSuccess = "EXPORT_SUCCESS"; - - // Returns a writable name for the export file, creating any directories as necessary; - // or null on failure. - private static string MakeExportPath(string parent, string basename, string ext) - { - string child = FileUtils.GenerateNonexistentFilename(parent, basename: ext, extension: ""); - if (!FileUtils.InitializeDirectoryWithUserError( - child, "Failed to create export directory for " + ext)) - { - return null; - } - - return Path.Combine(child, string.Format("{0}.{1}", basename, ext)); - } - - // A helper for managing our progress bar - class Progress - { - private Dictionary m_workAmount = new Dictionary(); - private float m_totalWork = 0.001f; // avoids divide-by-zero - private float m_completedWork = 0; - - public Progress() - { - OverlayManager.m_Instance.UpdateProgress(0); - } - - // If you call SetWork with a non-zero value, you shoud also eventually call CompleteWork - public void SetWork(string name, float effort = 1) - { - if (!m_workAmount.ContainsKey(name)) - { - m_workAmount[name] = effort; - m_totalWork += effort; - } - else - { - Debug.LogErrorFormat("Added {0} twice", name); - } - } - - // It's ok to call CompleteWork with a name you haven't yet set; it's a no-op. - public void CompleteWork(string name) - { - float work; - m_workAmount.TryGetValue(name, out work); - m_completedWork += work; - OverlayManager.m_Instance.UpdateProgress(m_completedWork / m_totalWork); - } - } - - public class AutoTimer : IDisposable - { - private System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); - private string m_tag; - public AutoTimer(string tag) - { - m_tag = tag; - timer.Start(); - } - public void Dispose() - { - timer.Stop(); - Debug.LogFormat("{0} time: {1}s", m_tag, timer.ElapsedMilliseconds / 1000.0f); - } - } - - static bool IsExportEnabled(string format) - { - // I couldn't figure out how to get the default value from the config file for a dictionary with a getter/setter - // so I'm handling defaults here. - var formats = App.UserConfig.Export.Formats; - if (formats == null) - { - formats = new Dictionary - { - { "fbx", true }, - { "glb", true }, - { "newglb", false }, - { "json", false }, - { "latk", false }, - { "obj", true }, - { "stl", false }, - { "usd", false }, - { "wrl", false }, - }; - } - return formats.GetValueOrDefault(format); - } - - public static void ExportScene() - { - var current = SaveLoadScript.m_Instance.SceneFile; - string safeHumanName = FileUtils.SanitizeFilename(current.HumanName); - string basename = FileUtils.SanitizeFilename( - (current.Valid && (safeHumanName != "")) ? safeHumanName : "Untitled"); - - string parent = FileUtils.GenerateNonexistentFilename(App.UserExportPath(), basename, ""); - if (!FileUtils.InitializeDirectoryWithUserError( - parent, "Failed to create export directory")) return; - - // Set up progress bar. - var progress = new Progress(); - if (App.PlatformConfig.EnableExportJson && IsExportEnabled("json")) - { - progress.SetWork("json"); - } -#if FBX_SUPPORTED - if (App.PlatformConfig.EnableExportFbx && IsExportEnabled("fbx")) - { - progress.SetWork("fbx"); - } - if (IsExportEnabled("obj")) - { - progress.SetWork("obj"); - } -#endif - -#if USD_SUPPORTED - if (App.PlatformConfig.EnableExportUsd && IsExportEnabled("usd")) - { - progress.SetWork("usd"); - } -#endif - - if (IsExportEnabled("latk")) - { - progress.SetWork("latk"); - } - - if (IsExportEnabled("wrl")) - { - progress.SetWork("wrl"); - } - - if (IsExportEnabled("stl")) - { - progress.SetWork("stl"); - } - - if (App.PlatformConfig.EnableExportGlb) - { - if (IsExportEnabled("glb")) { progress.SetWork("glb"); } - if (IsExportEnabled("newglb")) { progress.SetWork("newglb"); } - } - - string filename; - - if (App.PlatformConfig.EnableExportJson && IsExportEnabled("json") && - (filename = MakeExportPath(parent, basename, "json")) != null) - { - using (var unused = new AutoTimer("raw export")) - { - OverlayManager.m_Instance.UpdateProgress(0.1f); - ExportRaw.Export(filename); - - // Also write the metadata that would normally go in the .tilt file - SketchSnapshot.ExportMetadata(filename.Replace(".json", ".metadata.json")); - } - progress.CompleteWork("json"); - } - -#if FBX_SUPPORTED - if (App.PlatformConfig.EnableExportFbx && IsExportEnabled("fbx") && - (filename = MakeExportPath(parent, basename, "fbx")) != null) - { - using (var unused = new AutoTimer("fbx export")) - { - OverlayManager.m_Instance.UpdateProgress(0.3f); - ExportFbx.Export(filename, - App.UserConfig.Export.ExportBinaryFbx ? ExportFbx.kFbxBinary : ExportFbx.kFbxAscii, - App.UserConfig.Export.ExportFbxVersion); - OverlayManager.m_Instance.UpdateProgress(0.5f); - } - progress.CompleteWork("fbx"); - } - - if (IsExportEnabled("obj") && App.PlatformConfig.EnableExportFbx && - (filename = MakeExportPath(parent, basename, "obj")) != null) - { - // This has never been tested with the new fbx export style and may not work - ExportFbx.Export(filename, ExportFbx.kObj); - progress.CompleteWork("obj"); - } -#endif - -#if USD_SUPPORTED - if (App.PlatformConfig.EnableExportUsd && IsExportEnabled("usd") && - (filename = MakeExportPath(parent, basename, "usd")) != null) - { - using (var unused = new AutoTimer("usd export")) - { - ExportUsd.ExportPayload(filename); - } - progress.CompleteWork("usd"); - } -#endif - - if (IsExportEnabled("latk") && - (filename = MakeExportPath(parent, basename, "latk")) != null) - { - using (var unused = new AutoTimer("latk export")) - { - ExportLatk.Export(filename); - } - progress.CompleteWork("latk"); - } - - if (IsExportEnabled("wrl") && - (filename = MakeExportPath(parent, basename, "wrl")) != null) - { - ExportVrml.Export(filename); - progress.CompleteWork("wrl"); - } - - if (IsExportEnabled("stl") && - (filename = MakeExportPath(parent, basename, "stl")) != null) - { - try - { - ExportStl.Export(filename); - } - catch (ArgumentOutOfRangeException e) - { - OutputWindowScript.Error("STL export failed", e.Message); - } - progress.CompleteWork("stl"); - } - - if (App.PlatformConfig.EnableExportGlb && IsExportEnabled("glb")) - { - // Legacy GLTF export - string extension = App.Config.m_EnableGlbVersion2 ? "glb" : "glb1"; - int gltfVersion = App.Config.m_EnableGlbVersion2 ? 2 : 1; - filename = MakeExportPath(parent, basename, extension); - if (filename != null) - { - using (var unused = new AutoTimer("glb export")) - { - OverlayManager.m_Instance.UpdateProgress(0.6f); - - // TBT doesn't need (or want) brush textures in the output because it replaces all - // the materials, so it's fine to keep those http:. However, Sketchfab doesn't support - // http textures so if uploaded, this glb will have missing textures. - var exporter = new ExportGlTF(); - exporter.ExportBrushStrokes( - filename, AxisConvention.kGltf2, binary: true, doExtras: false, - includeLocalMediaContent: true, - gltfVersion: gltfVersion, - selfContained: true - ); - } - } - progress.CompleteWork("glb"); - } - - if (App.PlatformConfig.EnableExportGlb && IsExportEnabled("newglb")) - { - // 'New' GLTF export using UnityGLTF - string extension = "glb"; - using (var unused = new AutoTimer("glb export")) - { - OverlayManager.m_Instance.UpdateProgress(0.7f); - var settings = App.Config.m_UnityGLTFSettings; - var context = new ExportContext(settings); - - // Beware the two meanings of "layer" in the following code - Unity layers and Open Brush layers - - var layerCanvases = App.Scene.LayerCanvases.Select(x => x.transform).ToList(); - var layerMask = LayerMask.GetMask("MainCanvas"); - if (App.UserConfig.Export.ExportEnvironment) - { - layerCanvases.Add(App.Instance.m_EnvironmentTransform); - layerMask |= LayerMask.GetMask("Environment"); - } - context.ExportLayers = layerMask; - var unityGltfexporter = new GLTFSceneExporter(layerCanvases.ToArray(), context); - - unityGltfexporter.SaveGLB(Path.Combine(parent, $"newglb"), $"{basename}.{extension}"); - } - progress.CompleteWork("newglb"); - } - - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, basename + - $" {LocalizationSettings.StringDatabase.GetLocalizedString(kExportSuccess)}"); - ControllerConsoleScript.m_Instance.AddNewLine("Located in " + App.UserExportPath()); - - string readmeFilename = Path.Combine(App.UserExportPath(), kExportReadmeName); - if (!File.Exists(readmeFilename) && !Directory.Exists(readmeFilename)) - { - File.WriteAllText(readmeFilename, kExportReadmeBody); - } - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; +using UnityEngine.Localization.Settings; +using UnityGLTF; + +namespace TiltBrush +{ + + public static class Export + { + const string kExportDocumentationUrl = "https://docs.google.com/document/d/11ZsHozYn9FnWG7y3s3WAyKIACfbfwb4PbaS8cZ_xjvo#heading=h.im5f33smiavy"; +#if UNITY_ANDROID || UNITY_IOS + const string kExportReadmeName = "README.txt"; + const string kExportReadmeBody = "Please see " + kExportDocumentationUrl; +#else + const string kExportReadmeName = "README.url"; + const string kExportReadmeBody = @"[InternetShortcut] +URL=" + kExportDocumentationUrl; +#endif + const string kExportSuccess = "EXPORT_SUCCESS"; + + // Returns a writable name for the export file, creating any directories as necessary; + // or null on failure. + private static string MakeExportPath(string parent, string basename, string ext) + { + string child = FileUtils.GenerateNonexistentFilename(parent, basename: ext, extension: ""); + if (!FileUtils.InitializeDirectoryWithUserError( + child, "Failed to create export directory for " + ext)) + { + return null; + } + + return Path.Combine(child, string.Format("{0}.{1}", basename, ext)); + } + + // A helper for managing our progress bar + class Progress + { + private Dictionary m_workAmount = new Dictionary(); + private float m_totalWork = 0.001f; // avoids divide-by-zero + private float m_completedWork = 0; + + public Progress() + { + OverlayManager.m_Instance.UpdateProgress(0); + } + + // If you call SetWork with a non-zero value, you shoud also eventually call CompleteWork + public void SetWork(string name, float effort = 1) + { + if (!m_workAmount.ContainsKey(name)) + { + m_workAmount[name] = effort; + m_totalWork += effort; + } + else + { + Debug.LogErrorFormat("Added {0} twice", name); + } + } + + // It's ok to call CompleteWork with a name you haven't yet set; it's a no-op. + public void CompleteWork(string name) + { + float work; + m_workAmount.TryGetValue(name, out work); + m_completedWork += work; + OverlayManager.m_Instance.UpdateProgress(m_completedWork / m_totalWork); + } + } + + public class AutoTimer : IDisposable + { + private System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); + private string m_tag; + public AutoTimer(string tag) + { + m_tag = tag; + timer.Start(); + } + public void Dispose() + { + timer.Stop(); + Debug.LogFormat("{0} time: {1}s", m_tag, timer.ElapsedMilliseconds / 1000.0f); + } + } + + static bool IsExportEnabled(string format) + { + // I couldn't figure out how to get the default value from the config file for a dictionary with a getter/setter + // so I'm handling defaults here. + var formats = App.UserConfig.Export.Formats; + if (formats == null) + { + formats = new Dictionary + { + { "fbx", true }, + { "glb", true }, + { "newglb", false }, + { "json", false }, + { "latk", false }, + { "obj", true }, + { "stl", false }, + { "usd", false }, + { "wrl", false }, + }; + } + return formats.GetValueOrDefault(format); + } + + public static void ExportScene() + { + var current = SaveLoadScript.m_Instance.SceneFile; + string safeHumanName = FileUtils.SanitizeFilename(current.HumanName); + string basename = FileUtils.SanitizeFilename( + (current.Valid && (safeHumanName != "")) ? safeHumanName : "Untitled"); + + string parent = FileUtils.GenerateNonexistentFilename(App.UserExportPath(), basename, ""); + if (!FileUtils.InitializeDirectoryWithUserError( + parent, "Failed to create export directory")) return; + + // Set up progress bar. + var progress = new Progress(); + if (App.PlatformConfig.EnableExportJson && IsExportEnabled("json")) + { + progress.SetWork("json"); + } +#if FBX_SUPPORTED + if (App.PlatformConfig.EnableExportFbx && IsExportEnabled("fbx")) + { + progress.SetWork("fbx"); + } + if (IsExportEnabled("obj")) + { + progress.SetWork("obj"); + } +#endif + +#if USD_SUPPORTED + if (App.PlatformConfig.EnableExportUsd && IsExportEnabled("usd")) + { + progress.SetWork("usd"); + } +#endif + + if (IsExportEnabled("latk")) + { + progress.SetWork("latk"); + } + + if (IsExportEnabled("wrl")) + { + progress.SetWork("wrl"); + } + + if (IsExportEnabled("stl")) + { + progress.SetWork("stl"); + } + + if (App.PlatformConfig.EnableExportGlb) + { + if (IsExportEnabled("glb")) { progress.SetWork("glb"); } + if (IsExportEnabled("newglb")) { progress.SetWork("newglb"); } + } + + string filename; + + if (App.PlatformConfig.EnableExportJson && IsExportEnabled("json") && + (filename = MakeExportPath(parent, basename, "json")) != null) + { + using (var unused = new AutoTimer("raw export")) + { + OverlayManager.m_Instance.UpdateProgress(0.1f); + ExportRaw.Export(filename); + + // Also write the metadata that would normally go in the .tilt file + SketchSnapshot.ExportMetadata(filename.Replace(".json", ".metadata.json")); + } + progress.CompleteWork("json"); + } + +#if FBX_SUPPORTED + if (App.PlatformConfig.EnableExportFbx && IsExportEnabled("fbx") && + (filename = MakeExportPath(parent, basename, "fbx")) != null) + { + using (var unused = new AutoTimer("fbx export")) + { + OverlayManager.m_Instance.UpdateProgress(0.3f); + ExportFbx.Export(filename, + App.UserConfig.Export.ExportBinaryFbx ? ExportFbx.kFbxBinary : ExportFbx.kFbxAscii, + App.UserConfig.Export.ExportFbxVersion); + OverlayManager.m_Instance.UpdateProgress(0.5f); + } + progress.CompleteWork("fbx"); + } + + if (IsExportEnabled("obj") && App.PlatformConfig.EnableExportFbx && + (filename = MakeExportPath(parent, basename, "obj")) != null) + { + // This has never been tested with the new fbx export style and may not work + ExportFbx.Export(filename, ExportFbx.kObj); + progress.CompleteWork("obj"); + } +#endif + +#if USD_SUPPORTED + if (App.PlatformConfig.EnableExportUsd && IsExportEnabled("usd") && + (filename = MakeExportPath(parent, basename, "usd")) != null) + { + using (var unused = new AutoTimer("usd export")) + { + ExportUsd.ExportPayload(filename); + } + progress.CompleteWork("usd"); + } +#endif + + if (IsExportEnabled("latk") && + (filename = MakeExportPath(parent, basename, "latk")) != null) + { + using (var unused = new AutoTimer("latk export")) + { + ExportLatk.Export(filename); + } + progress.CompleteWork("latk"); + } + + if (IsExportEnabled("wrl") && + (filename = MakeExportPath(parent, basename, "wrl")) != null) + { + ExportVrml.Export(filename); + progress.CompleteWork("wrl"); + } + + if (IsExportEnabled("stl") && + (filename = MakeExportPath(parent, basename, "stl")) != null) + { + try + { + ExportStl.Export(filename); + } + catch (ArgumentOutOfRangeException e) + { + OutputWindowScript.Error("STL export failed", e.Message); + } + progress.CompleteWork("stl"); + } + + if (App.PlatformConfig.EnableExportGlb && IsExportEnabled("glb")) + { + // Legacy GLTF export + string extension = App.Config.m_EnableGlbVersion2 ? "glb" : "glb1"; + int gltfVersion = App.Config.m_EnableGlbVersion2 ? 2 : 1; + filename = MakeExportPath(parent, basename, extension); + if (filename != null) + { + using (var unused = new AutoTimer("glb export")) + { + OverlayManager.m_Instance.UpdateProgress(0.6f); + + // TBT doesn't need (or want) brush textures in the output because it replaces all + // the materials, so it's fine to keep those http:. However, Sketchfab doesn't support + // http textures so if uploaded, this glb will have missing textures. + var exporter = new ExportGlTF(); + exporter.ExportBrushStrokes( + filename, AxisConvention.kGltf2, binary: true, doExtras: false, + includeLocalMediaContent: true, + gltfVersion: gltfVersion, + selfContained: true + ); + } + } + progress.CompleteWork("glb"); + } + + if (App.PlatformConfig.EnableExportGlb && IsExportEnabled("newglb")) + { + // 'New' GLTF export using UnityGLTF + string extension = "glb"; + using (var unused = new AutoTimer("glb export")) + { + OverlayManager.m_Instance.UpdateProgress(0.7f); + var settings = App.Config.m_UnityGLTFSettings; + var context = new ExportContext(settings); + + // Beware the two meanings of "layer" in the following code - Unity layers and Open Brush layers + + var layerCanvases = App.Scene.LayerCanvases.Select(x => x.transform).ToList(); + var layerMask = LayerMask.GetMask("MainCanvas"); + if (App.UserConfig.Export.ExportEnvironment) + { + layerCanvases.Add(App.Instance.m_EnvironmentTransform); + layerMask |= LayerMask.GetMask("Environment"); + } + context.ExportLayers = layerMask; + var unityGltfexporter = new GLTFSceneExporter(layerCanvases.ToArray(), context); + + unityGltfexporter.SaveGLB(Path.Combine(parent, $"newglb"), $"{basename}.{extension}"); + } + progress.CompleteWork("newglb"); + } + + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, basename + + $" {LocalizationSettings.StringDatabase.GetLocalizedString(kExportSuccess)}"); + ControllerConsoleScript.m_Instance.AddNewLine("Located in " + App.UserExportPath()); + + string readmeFilename = Path.Combine(App.UserExportPath(), kExportReadmeName); + if (!File.Exists(readmeFilename) && !Directory.Exists(readmeFilename)) + { + File.WriteAllText(readmeFilename, kExportReadmeBody); + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Export/ExportUtils.cs b/Assets/Scripts/Export/ExportUtils.cs index 1a4fdd42df..a704cc01e5 100644 --- a/Assets/Scripts/Export/ExportUtils.cs +++ b/Assets/Scripts/Export/ExportUtils.cs @@ -1,872 +1,872 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections.Generic; -using System; -using System.IO; -using System.Linq; -using JetBrains.Annotations; -using UnityEngine; - -namespace TiltBrush -{ - - using TexcoordInfo = GeometryPool.TexcoordInfo; - using Semantic = GeometryPool.Semantic; - using StrokeGroup = SketchGroupTag; - - public static class ExportUtils - { - // Used to refer to built-in textures that we put in Support/ - public const string kBuiltInPrefix = "tiltbrush://"; - public const string kShaderDirectory = "shaders/brushes"; - public const string kProjectRelativeBrushExportRoot = "Support/TiltBrush.com/" + kShaderDirectory; - public const string kProjectRelativeEnvironmentExportRoot = "Support/TiltBrush.com/environments"; - public const string kProjectRelativeTextureExportRoot = "Support/TiltBrush.com/textures"; - public const string kProjectRelativeSupportBrushTexturesRoot = "Support/" + kShaderDirectory; - - // -------------------------------------------------------------------------------------------- // - // Brush/Canvas Export Payloads - // -------------------------------------------------------------------------------------------- // - - /// A canvas and some strokes - [UsedImplicitly(ImplicitUseTargetFlags.Members)] - public class ExportCanvas - { - public CanvasScript m_canvas; - private List m_strokes; - - public ExportCanvas(CanvasScript canvas, IEnumerable strokes) - { - m_canvas = canvas; - m_strokes = strokes.ToList(); - } - - public ExportCanvas(IGrouping group) - { - m_canvas = group.Key; - m_strokes = group.ToList(); - } - - public IEnumerable SplitByGroup() - { - return m_strokes.GroupBy(stroke => stroke.Group) - .Select(g => new ExportGroup(g)); - } - - public IEnumerable SplitByBrush() - { - return m_strokes.GroupBy(stroke => stroke.m_BrushGuid) - .Select(g => new ExportBrush(g)); - } - } - - /// A group id and some strokes - public class ExportGroup - { - public StrokeGroup m_group; - private List m_strokes; - - public ExportGroup(IGrouping group) - { - m_group = group.Key; - m_strokes = group.ToList(); - } - - public IEnumerable SplitByBrush() - { - return m_strokes.GroupBy(stroke => stroke.m_BrushGuid) - .Select(g => new ExportBrush(g)); - } - } - - /// A brush guid and some strokes - /// This is the only grouping that can be converted to geometry - public class ExportBrush - { - public BrushDescriptor m_desc; - private List m_strokes; - - public ExportBrush(IGrouping group) - { - m_desc = BrushCatalog.m_Instance.GetBrush(group.Key); - m_strokes = group.ToList(); - } - - public struct PoolAndStrokes - { - public GeometryPool pool; - public List strokes; - } - - // Returns a pool, or null if it's not from a pool. - private static GeometryPool GetPool(Stroke stroke) - { - if (stroke.m_BatchSubset == null) { return null; } - return stroke.m_BatchSubset.m_ParentBatch.Geometry; - } - - // Yields strokes in the same order as in the input, but adds an additional side effect: - // When the user consumes the stroke they will likely force its batch to become resident. - // This wrapper undoes that, preserving the state of the Batch's residency. - private IEnumerable - PreserveBatchResidency(IEnumerable input) - { - // We could do the "make it resident, make it not resident" dance on a stroke-by-stroke - // basis, but that would be ridiculously wasteful. Optimize by grouping adjacent strokes - // together if they share the same batch. - // - // When a sketch first loads, this optimization works extremely well because batches are - // contiguous in the stroke list. The more the user mutates (select, unselect, recolor) - // the less this will be true. There's not a lot we can do about it unless we want to - // sort the strokes by batch rather than by draw time (or whatever criteria the incoming - // iterator uses), which I think would be too invasive. - foreach (IGrouping group in - input.GroupBy(stroke => GetPool(stroke))) - { - GeometryPool pool = group.Key; - - // If we have to bring the pool into memory, save off enough data so we can - // push it back out. - Mesh previousBackingMesh = null; - if (pool != null && !pool.IsGeometryResident) - { - previousBackingMesh = pool.GetBackingMesh(); - // We currently only eject batch-owned GeometryPool to Mesh, so this is unlikely to trip; - // but we eventually may want to eject them to disk to save even more memory. - if (previousBackingMesh == null) - { - Debug.LogWarning("Not yet able to eject pool back to file; leaving it in memory"); - } - pool.EnsureGeometryResident(); - } - - foreach (Stroke stroke in group) - { - yield return stroke; - } - - if (previousBackingMesh != null) - { - pool.MakeGeometryNotResident(previousBackingMesh); - } - } - } - - // Puts the passed timestamp data into pool.texcoord2. - // It's assumed that the pool does not already have anything in texcoord2. - private static void AugmentWithTimestamps(GeometryPool pool, ref List timestamps) - { - if (App.UserConfig.Export.ExportStrokeTimestamp) - { - GeometryPool.VertexLayout withTxc2 = pool.Layout; - if (withTxc2.texcoord2.size == 0 && timestamps.Count == pool.NumVerts) - { - withTxc2.texcoord2 = new TexcoordInfo { size = 3, semantic = Semantic.Timestamp }; - pool.Layout = withTxc2; - pool.m_Texcoord2.v3 = timestamps; - timestamps = null; // don't let caller reuse the list - } - else - { - Debug.LogError("Internal error; cannot add timestamps"); - } - } - } - - // Appends timestamp data to the passed List. - // - // The timestamps are laid out like so: - // x = start of stroke - // y = end of stroke - // z = Roughly interpolated between the start and end of the stroke - // I say "roughly interpolated" because we make the assumption that each control point - // creates a fixed number of vertices. It's correct only to a first approximation. - private static void AppendTimestamps( - Stroke stroke, int numVerts, - List timestamps) - { - Vector3 ts = new Vector3(stroke.HeadTimestampMs * .001f, - stroke.TailTimestampMs * .001f, - 0); - foreach (float interpolated in MathUtils.LinearResampleCurve( - stroke.m_ControlPoints.Select(cp => cp.m_TimestampMs * .001f).ToArray(), - numVerts)) - { - ts.z = interpolated; - timestamps.Add(ts); - } - } - - /// Converts strokes to multiple GeometryPools whose size is <= vertexLimit. - /// The default vertexLimit is the maximum size allowed by Unity. - /// If a single stroke exceeds the vertex limit, the stroke will be ignored. - /// TODO: dangerous! vertexLimit should be a soft limit, with a hard limit of 65k - public IEnumerable ToGeometryBatches(int vertexLimit = 65534) - { - var layout = BrushCatalog.m_Instance.GetBrush(m_desc.m_Guid).VertexLayout; - var pool = new GeometryPool(); - var strokes = new List(); - // Timestamps are kept separate and only stitched in when we emit a Pool. - // This is because GeometryPool.Append() doesn't know what to do if the - // source and dest layouts are different. - List timestamps = new List(); - pool.Layout = layout; - foreach (var stroke in PreserveBatchResidency(m_strokes)) - { - while (true) - { - if (!stroke.IsGeometryEnabled) { continue; } - int oldNumVerts = pool.NumVerts; - if (pool.Append(stroke, vertexLimit)) - { - strokes.Add(stroke); - if (App.UserConfig.Export.ExportStrokeTimestamp) - { - AppendTimestamps(stroke, pool.NumVerts - oldNumVerts, timestamps); - } - // common case: it fits - break; - } - else if (pool.m_Vertices.Count > 0) - { - // it doesn't fit, but we can make a bit of forward progress by flushing the buffer - AugmentWithTimestamps(pool, ref timestamps); - yield return new PoolAndStrokes { pool = pool, strokes = strokes }; - pool = new GeometryPool(); - pool.Layout = layout; - strokes = new List(); - timestamps = new List(); - // loop around for another go - } - else - { - // very uncommon case: stroke won't fit even in an empty buffer. - // Should never happen with the default vertexLimit. - Debug.LogWarning("Cannot export stroke that exceeds vertex limit"); - // No choice but to ignore the stroke - break; - } - } - } - if (pool.m_Vertices.Count > 0) - { - AugmentWithTimestamps(pool, ref timestamps); - yield return new PoolAndStrokes { pool = pool, strokes = strokes }; - } - } - } - - // -------------------------------------------------------------------------------------------- // - // SceneState Export Payloads - // The state objects below are pure-state populated by the ExportCollector. - // -------------------------------------------------------------------------------------------- // - - // GetInstanceID() is not determinstic. - // The order in which objects are exported is somewhat determinstic. - // This class uses the order of export to determine the id. - public class DeterministicIdGenerator - { - private Dictionary m_instanceIdToId = new Dictionary(); - private int m_nextAvailable = 1; - public int GetIdFromInstanceId(UnityEngine.Object obj) - { - int instanceId = obj.GetInstanceID(); - if (m_instanceIdToId.ContainsKey(instanceId)) - { - return m_instanceIdToId[instanceId]; - } - else - { - var ret = m_nextAvailable; - m_nextAvailable += 1; - m_instanceIdToId[instanceId] = ret; - return ret; - } - } - } - /// The current exportable SceneState of Open Brush. - public class SceneStatePayload - { - // Metadata. - public string generator = "Tilt Brush 23.3.841faedfb compatible (Actually: Open Brush {0}.{1})"; - public DeterministicIdGenerator idGenerator = new DeterministicIdGenerator(); - - // Space Bases. - public readonly AxisConvention axes; - public readonly bool reverseWinding; - public readonly float exportUnitsFromAppUnits = App.UNITS_TO_METERS; - - // Entity Manifests. - public EnvPayload env = new EnvPayload(); - public LightsPayload lights = new LightsPayload(); - public List groups = new List(); - public GroupIdMapping groupIdMapping = new GroupIdMapping(); - // These actually contain the model/image data - public List modelMeshes = new List(); - public List imageQuads = new List(); - // These are bare nodes representing things that we currently can't export - public List referenceThings = new List(); - public readonly string temporaryDirectory = null; - - // If you pass a temporary directory, it may (or may not) be used - // for memory optimizations during the export. In this case, take - // care to call Destroy() if you want the payload to clean up after itself. - public SceneStatePayload(AxisConvention axes, string temporaryDirectory) - { - this.axes = axes; - this.temporaryDirectory = temporaryDirectory; - // The determinant can be used to detect if the basis-change has a mirroring. - // This matters because a mirroring turns the triangles inside-out, requiring - // us to flip their winding to preserve the surface orientation. - this.reverseWinding = (GetFromUnity_Axes(this).determinant < 0); - } - - // Tears down the payload as safely as possible; throws no exceptions - public void Destroy() - { - if (groups != null) - { - foreach (var item in groups) - { - item.Destroy(); - } - } - if (modelMeshes != null) - { - foreach (var item in modelMeshes) - { - item.Destroy(); - } - } - } - } - - // This should be deprecated in favor of putting a group id in all the payloads - public class GroupPayload - { - public UInt32 id; - public List brushMeshes = new List(); - public void Destroy() - { - if (brushMeshes != null) - { - foreach (var item in brushMeshes) - { - item.Destroy(); - } - } - } - } - - public class EnvPayload - { - public Guid guid; - public string description; - public Cubemap skyCubemap; - public bool useGradient; - public Color skyColorA; - public Color skyColorB; - public Vector3 skyGradientDir; - public Color fogColor; - public float fogDensity; - } - - public class LightPayload - { - public LightType type; - public string legacyUniqueName; // guaranteed unique but maybe not friendly - public string name; - public Color lightColor; - public Matrix4x4 xform; - } - - [UsedImplicitly(ImplicitUseTargetFlags.Members)] - public class LightsPayload - { - public Color ambientColor; // currently unused - public List lights = new List(); - } - - // Common to all MeshPayload. - // This is an _instance_ of a mesh, and corresponds to a GameObject/MeshRenderer - public abstract class BaseMeshPayload - { - // A unique not-very-human-readable string. - // "nodeName" and "geometryName" are _mostly_ unique except in edge cases like two distinct - // widgets having the same GetExportName(). - // Keeping this around also means I can diff vs old gltf1 files for testing purposes. - public string legacyUniqueName; - - // A pleasant-looking name for the node - public string nodeName; - public Matrix4x4 xform = Matrix4x4.identity; - // Not owned by this instance; ownership is potentially shared with other BaseMeshPayloads - public GeometryPool geometry; - // A pleasant-looking name for the geometry - public string geometryName; - public IExportableMaterial exportableMaterial; - - public readonly UInt32 group; - - protected BaseMeshPayload(uint groupId) { this.group = groupId; } - - /// Returns a string used to keep different models from having overlapping names. - /// Examples uses: - /// - Keep texture names from colliding - /// - Keep material names from colliding -- in particular this is an issue for - /// the name "defaultShadingGroup" which shows up in Poly-obj and Media Library-obj - /// imports a lot. - public abstract string MeshNamespace { get; } - - // Danger! This destroys resources shared by the entire SceneStatePayload. - // It is only public so it can be called during SceneStatePayload shutdown. - public void Destroy() - { - if (geometry != null) - { - geometry.Destroy(); - geometry = null; - } - } - } - - public class ImageQuadPayload : BaseMeshPayload - { - public override string MeshNamespace => "media"; - public ImageQuadPayload(uint groupId) : base(groupId) { } - } - - // MeshPayload for a poly or media library model; might also be used for - // exporting environments from the editor? - public class ModelMeshPayload : BaseMeshPayload - { - public Model model; - - // Enumerates the instances of a Model in the scene. - // Group by (model, modelId) to collect payloads from the same instance. - public int modelId; - - // These next two are base.xform in factored form: parentXform * localXform == base.xform - - // The transform of the parent ModelWidget. - // Payloads with the same model and modelId come from the same parent, - // therefore they have the same parentXform. - public Matrix4x4 parentXform; - - // This is slightly redundant because it's the same as instanceXform.inverse * base.xform. - // It's stored explicitly for slight added precision. - public Matrix4x4 localXform; - - public override string MeshNamespace - { - get - { - if (model.GetLocation().GetLocationType() == Model.Location.Type.PolyAssetId) - { - return model.AssetId; // blows up if type is not PolyAssetId - } - else - { - string path = Path.GetFileNameWithoutExtension(model.RelativePath); - return System.Text.RegularExpressions.Regex.Replace( - path, @"[^a-zA-Z0-9_-]+", ""); - } - } - } - public ModelMeshPayload(uint groupId) : base(groupId) { } - } - - // MeshPayload for something drawn with a brush. - // Strokes are currently only used when exporting to USD - public class BrushMeshPayload : BaseMeshPayload - { - public List strokes; - public override string MeshNamespace => "brush"; - public BrushMeshPayload(uint groupId) : base(groupId) { } - } - - public class XformPayload - { - public readonly UInt32 group; - // This name isn't (currently) guaranteed to be unique among XformPayloads - public string name; - public Matrix4x4 xform; - - public XformPayload(uint groupId) { this.group = groupId; } - } - - // -------------------------------------------------------------------------------------------- // - // Data Collection Helpers - // -------------------------------------------------------------------------------------------- // - - /// Filters and returns geometry in a convenient format for export. - /// Returns geometry for the main canvas - public static ExportCanvas ExportMainCanvas() - { - // This is probably the more-useful one; it assumes we only have - // one interesting canvas (true, from a user perspective) and merges - // the selection canvas into its target canvas. - // - // It makes the assumption that the selection canvas is not playing - // fast-and-loose, and keeps its transform identical to its target - // canvas (ie, its transform is always identity). - // - // Of course, the selection canvas does play fast-and-loose, but - // we smack the canvas into place (via a deselect/reselect) before - // getting here. - var allowedBrushGuids = new HashSet( - BrushCatalog.m_Instance.AllBrushes - .Where(b => b.m_AllowExport) - .Select(b => (Guid)b.m_Guid)); - var main = App.Scene.MainCanvas; - var selection = App.Scene.SelectionCanvas; - var mainStrokes = SketchMemoryScript.AllStrokes() - .Where(stroke => allowedBrushGuids.Contains(stroke.m_BrushGuid) && - stroke.IsGeometryEnabled && - (stroke.Canvas == main || stroke.Canvas == selection)); - return new ExportCanvas(main, mainStrokes.ToList()); - } - - // Same as ExportAllCanvases but pretends all strokes are on the main canvas - // Does NOT transform strokes so ensure all canvases have identity transforms - public static ExportCanvas ExportAllCanvasesIgnoreLayers() - { - var allowedBrushGuids = new HashSet( - BrushCatalog.m_Instance.AllBrushes - .Where(b => b.m_AllowExport) - .Select(b => (Guid)b.m_Guid)); - var main = App.Scene.MainCanvas; - var selection = App.Scene.SelectionCanvas; - var mainStrokes = SketchMemoryScript - .AllStrokes() - .Where(stroke => allowedBrushGuids.Contains(stroke.m_BrushGuid) && stroke.IsGeometryEnabled); - return new ExportCanvas(main, mainStrokes.ToList()); - } - - /// Filters and returns geometry in a convenient format for export. - public static List ExportAllCanvases() - { - var allowedBrushGuids = new HashSet( - BrushCatalog.m_Instance.AllBrushes - .Where(b => b.m_AllowExport) - .Select(b => (Guid)b.m_Guid)); - - return SketchMemoryScript.AllStrokes() - .Where(stroke => allowedBrushGuids.Contains(stroke.m_BrushGuid) && - stroke.IsGeometryEnabled) - .GroupBy(stroke => stroke.Canvas) - .Select(canvasStrokes => new ExportCanvas(canvasStrokes)) - .ToList(); - } - - // -------------------------------------------------------------------------------------------- // - // Functional Conversion Helpers - // -------------------------------------------------------------------------------------------- // - - /// Applies change-of-basis conversions and distance conversions to a GeometryPool. - public static void ConvertUnitsAndChangeBasis(GeometryPool pool, SceneStatePayload payload) - { - ConvertScaleAndChangeBasis(pool, payload.exportUnitsFromAppUnits, GetFromUnity_Axes(payload)); - } - - private static void ConvertScaleAndChangeBasis( - GeometryPool pool, - float unitChange, - Matrix4x4 basisChange) - { - // If there's translation, it's ambiguous whether scale is applied before or after - // (probably the user means after, but still) - Debug.Assert((Vector3)basisChange.GetColumn(3) == Vector3.zero); - Matrix4x4 basisAndUnitChange = Matrix4x4.Scale(unitChange * Vector3.one) * basisChange; - -#if false // this code is pendantic but is useful to describe what's _really_ going on here - // xfBivector is the transform to use for normals, tangents, and other cross-products. - // Extracting rotation from a mat4 is hard, so take advantage of the fact that basisChange - // is a (maybe improper) rotation, an improper rotation being a rotation with scale -1. - // Matrix4x4 xfBivector = ExtractRotation(basisAndUnitChange); - Matrix4x4 xfBivector = basisChange; - if (basisChange.determinant < 0) { - // remove the -1 uniform scale by giving it yet more -1 uniform scale. - xfBivector = Matrix4x4.Scale(-Vector3.one) * xfBivector; - } - - // The exporter flips triangle winding when handedness flips, but it leaves the job unfinished; - // it needs to also flip the normals and tangents, since they're calculated from cross products. - // Detect that and kludge in an extra -1 scale to finish the job. - if (basisChange.determinant < 0) { - xfBivector = Matrix4x4.Scale(-Vector3.one) * xfBivector; - } -#else - Matrix4x4 xfBivector = basisChange; // The mirroring and the winding-flip cancel each other out -#endif - pool.ApplyTransform(basisAndUnitChange, xfBivector, unitChange, 0, pool.NumVerts); - } - - /// Convert the vertex colors to linear - public static List ConvertToLinearColorspace(List srgb) - { - return new List(srgb.Select(c32 => ((Color)c32).linear)); - } - - public static void ConvertToSrgbColorspace(Color[] linear) - { - for (int i = 0; i < linear.Length; i++) - { - linear[i] = linear[i].gamma; - } - } - - // Unused and untested -#if false - /// Convert the vertex Color32s to linear - /// Beware that you will lose information when quantizing down from - /// linear-float32 to linear-uint8. - public static void ConvertToLinearColorspace(MeshPayload meshes) { - for (int i = 0; i < meshes.Count; i++) { - var pool = meshes.geometry[i]; - for (int j = 0; j < pool.m_Colors.Count; j++) { - pool.m_Colors[j] = ((Color)pool.m_Colors[j]).linear; - } - } - } - /// Convert the light colors to linear - /// Beware that you will lose information when quantizing down from - /// linear-float32 to linear-uint8. - public static void ConvertToLinearColorspace(LightPayload lights) { - // XXX: these values can be > 1 because they are pre-multiplied by intensity - // Is it more appropriate to convert the base color from sRGB -> Linear, - // _then_ multiply by intensity? - lights.ambientColor = lights.ambientColor.linear; - for (int i = 0; i < lights.Count; i++) { - lights.lightColor[i] = lights.lightColor[i].linear; - } - } - /// Convert the environment (sky/fog) colors to linear - public static void ConvertToLinearColorspace(EnvPayload env) { - env.fogColor = env.fogColor.linear; - env.skyColorA = env.skyColorA.linear; - env.skyColorB = env.skyColorB.linear; - } -#endif - - - /// Flips winding order by swapping indices indexA and indexB. - /// indexA and indexB must be in the range [0, 2] and not equal to each other. - public static void ReverseTriangleWinding(GeometryPool pool, int indexA, int indexB) - { - if (indexA == indexB || indexA < 0 || indexA > 2) - { - throw new ArgumentException("indexA"); - } - if (indexB < 0 || indexB > 2) - { - throw new ArgumentException("indexB"); - } - var tris = pool.m_Tris; - int count = tris.Count; - for (int i = 0; i < count; i += 3) - { - var tmp = tris[i + indexA]; - tris[i + indexA] = tris[i + indexB]; - tris[i + indexB] = tmp; - } - } - - /// Given a transform, returns that transform in another basis. - /// The new basis is specified by outputFromInput. - public static Matrix4x4 ChangeBasis( - Matrix4x4 xfInput, - Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput) - { - return outputFromInput * xfInput * inputFromOutput; - } - - /// Given a transform, returns that transform in another basis. - /// The new basis is specified by outputFromInput. - /// - /// Not guaranteed to work if the change-of-basis matrix has non-uniform - /// scale. Otherwise, the resulting transform will not "fit" in a TrTransform. - public static TrTransform ChangeBasis( - TrTransform xfInput, - Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput) - { - // It might make this a little more accurate if outputFromInput were a TrTransform. - // Although... outputFromInput and inputFromOutput expressed as Matrix4x4 are always - // infinitely precise, for axis convention changes at least. Doing the same with a quat - // might involve sqrt(2)s. But maybe not for the common case of unity -> fbx / gltf? - Matrix4x4 m = outputFromInput * xfInput.ToMatrix4x4() * inputFromOutput; - return TrTransform.FromMatrix4x4(m); - } - - /// Given a transform, returns that transform in another basis. - /// The new basis is specified by outputFromInput. - /// - /// Not guaranteed to work if the change-of-basis matrix has non-axis-aligned - /// scale, or if the rotation portion can't be expressed as the product of 90 degree - /// rotations about an axis. Otherwise, the resulting scale will not "fit" in a Vec3. - public static void ChangeBasis( - Vector3 inputTranslation, Quaternion inputRotation, Vector3 inputScale, - out Vector3 translation, out Quaternion rotation, out Vector3 scale, - Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput) - { - TrTransform output = ChangeBasis( - TrTransform.TR(inputTranslation, inputRotation), - outputFromInput, inputFromOutput); - translation = output.translation; - rotation = output.rotation; - // Scale is a bit trickier. - Matrix4x4 m = outputFromInput * Matrix4x4.Scale(inputScale) * inputFromOutput; - scale = new Vector3(m[0, 0], m[1, 1], m[2, 2]); - m[0, 0] = m[1, 1] = m[2, 2] = 1; - Debug.Assert(m.isIdentity); - } - - /// Given a TrTransform, returns that transform as a mat4 in another basis. - /// The new basis is specified by the payload. - /// This changes both axes and units. - public static Matrix4x4 ChangeBasis( - TrTransform xfInput, SceneStatePayload payload) - { - Matrix4x4 basis = AxisConvention.GetFromUnity(payload.axes); - Matrix4x4 basisInverse = AxisConvention.GetToUnity(payload.axes); - return ChangeBasis(xfInput, basis, basisInverse) - .TransformBy(TrTransform.S(payload.exportUnitsFromAppUnits)) - .ToMatrix4x4(); - } - - /// Given a transform, returns that transform in another basis. - /// Since it's a Transform, xfInput is in Global (Room) space, Unity axes, decimeters. - /// The new basis is: Scene space, with the Payload's axes and units. - public static Matrix4x4 ChangeBasis( - Transform xfInput, SceneStatePayload payload) - { - Matrix4x4 basis = AxisConvention.GetFromUnity(payload.axes); - Matrix4x4 basisInverse = AxisConvention.GetToUnity(payload.axes); - return ChangeBasis(App.Scene.AsScene[xfInput], basis, basisInverse) - .TransformBy(TrTransform.S(payload.exportUnitsFromAppUnits)) - .ToMatrix4x4(); - } - - /// Returns a basis-change matrix that transforms from Unity axis conventions to - /// the conventions specified in the payload. - /// - /// Does *not* perform unit conversion, hence the name. - public static Matrix4x4 GetFromUnity_Axes(SceneStatePayload payload) - { - return AxisConvention.GetFromUnity(payload.axes); - } - - // -------------------------------------------------------------------------------------------- // - // Texture Collection Helper - // -------------------------------------------------------------------------------------------- // - - static public string GetTexturePath(Texture texture) - { -#if UNITY_EDITOR - // Copy the raw asset texture file and make sure it's either a PNG or JPG. - string texturePath = UnityEditor.AssetDatabase.GetAssetPath(texture); - texturePath = texturePath.Substring("Assets/".Length); - texturePath = Path.Combine(Application.dataPath, texturePath); - string extension = Path.GetExtension(texturePath).ToUpper(); - Debug.Assert(extension == ".PNG" || extension == ".JPG" || extension == ".JPEG", - String.Format("Texture {0} must be converted to png or jpg format", - texturePath)); - return texturePath; -#else - // Create an uncompressed texture from mainTex as a fallback when the asset database is - // not available. This only works on 2D textures that are readable. - string texFilename = Guid.NewGuid().ToString("D") + ".png"; - Texture2D texture2d = (Texture2D)texture; - Texture2D uncompressedTexture = new Texture2D( - texture.width, texture.height, TextureFormat.RGBA32, /*mipmap / mipChain:*/ false); - uncompressedTexture.SetPixels(texture2d.GetPixels()); - byte[] bytes = uncompressedTexture.EncodeToPNG(); - - // Save the texture file. - string texturePath = Path.Combine(Path.Combine(Path.GetDirectoryName(Application.dataPath), - ExportUtils.kProjectRelativeTextureExportRoot), - texFilename); - File.WriteAllBytes(texturePath, bytes); - return texturePath; -#endif - } - -#if UNITY_EDITOR && GAMEOBJ_EXPORT_TO_GLTF - public static SceneStatePayload GetSceneStateForGameObjectForExport( - GameObject gameObject, AxisConvention axes, Environment env) { - return ExportCollector.GetExportPayloadForGameObject(gameObject, axes, env); - } - - /// Returns a matrix: - /// - with units converted by the scale specified in the payload - /// - with basis changed by the axis convention in the payload - /// - with the scene transform subtracted out (so, converts from room to scene), but - /// only if the app is running. - /// This version also handles non-uniform scale (but stemming from what? the scene xf?) - public static Matrix4x4 ChangeBasisNonUniformScale(SceneStatePayload payload, Matrix4x4 root) { - Matrix4x4 basis = ExportUtils.GetBasisMatrix(payload); - - // Pre- and post-multiplying by the following matrix operations is the equivalent of - // .TransformBy(TrTransform.S(payload.exportUnitsFromAppUnits)). - Matrix4x4 xfScale = Matrix4x4.Scale(Vector3.one * payload.exportUnitsFromAppUnits); - Matrix4x4 xfScaleInverse = Matrix4x4.Scale(Vector3.one / payload.exportUnitsFromAppUnits); - - if (Application.isPlaying) { - // The world to scene matrix here performs the equivalent of App.Scene.AsScene[root] - // but works for non-uniform scale. - Matrix4x4 worldToSceneMatrix = App.Scene.transform.worldToLocalMatrix; - return xfScale * basis * worldToSceneMatrix * root * basis.inverse * xfScaleInverse; - } else { - return xfScale * basis * root * basis.inverse * xfScaleInverse; - } - } -#endif - - /// Returns a name based on originalName. - /// The name will be different from any names in usedNames, and will be added to usedNames. - public static string CreateUniqueName(string originalName, HashSet names) - { - if (string.IsNullOrEmpty(originalName)) - { - throw new ArgumentException("originalName"); - } - string baseName, ext; - { - // Don't use the Path functions because originalName may not be a valid path. - int dot = originalName.LastIndexOf('.'); - if (dot < 0) - { - baseName = originalName; - ext = ""; - } - else - { - baseName = originalName.Substring(0, dot); - ext = originalName.Substring(dot); - } - } - - for (int i = 0; ; ++i) - { - string subscript = (i == 0 ? "" : $"_{i}"); - string attempt = $"{baseName}{subscript}{ext}"; - if (names.Add(attempt)) - { - return attempt; - } - } - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using System; +using System.IO; +using System.Linq; +using JetBrains.Annotations; +using UnityEngine; + +namespace TiltBrush +{ + + using TexcoordInfo = GeometryPool.TexcoordInfo; + using Semantic = GeometryPool.Semantic; + using StrokeGroup = SketchGroupTag; + + public static class ExportUtils + { + // Used to refer to built-in textures that we put in Support/ + public const string kBuiltInPrefix = "tiltbrush://"; + public const string kShaderDirectory = "shaders/brushes"; + public const string kProjectRelativeBrushExportRoot = "Support/TiltBrush.com/" + kShaderDirectory; + public const string kProjectRelativeEnvironmentExportRoot = "Support/TiltBrush.com/environments"; + public const string kProjectRelativeTextureExportRoot = "Support/TiltBrush.com/textures"; + public const string kProjectRelativeSupportBrushTexturesRoot = "Support/" + kShaderDirectory; + + // -------------------------------------------------------------------------------------------- // + // Brush/Canvas Export Payloads + // -------------------------------------------------------------------------------------------- // + + /// A canvas and some strokes + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public class ExportCanvas + { + public CanvasScript m_canvas; + private List m_strokes; + + public ExportCanvas(CanvasScript canvas, IEnumerable strokes) + { + m_canvas = canvas; + m_strokes = strokes.ToList(); + } + + public ExportCanvas(IGrouping group) + { + m_canvas = group.Key; + m_strokes = group.ToList(); + } + + public IEnumerable SplitByGroup() + { + return m_strokes.GroupBy(stroke => stroke.Group) + .Select(g => new ExportGroup(g)); + } + + public IEnumerable SplitByBrush() + { + return m_strokes.GroupBy(stroke => stroke.m_BrushGuid) + .Select(g => new ExportBrush(g)); + } + } + + /// A group id and some strokes + public class ExportGroup + { + public StrokeGroup m_group; + private List m_strokes; + + public ExportGroup(IGrouping group) + { + m_group = group.Key; + m_strokes = group.ToList(); + } + + public IEnumerable SplitByBrush() + { + return m_strokes.GroupBy(stroke => stroke.m_BrushGuid) + .Select(g => new ExportBrush(g)); + } + } + + /// A brush guid and some strokes + /// This is the only grouping that can be converted to geometry + public class ExportBrush + { + public BrushDescriptor m_desc; + private List m_strokes; + + public ExportBrush(IGrouping group) + { + m_desc = BrushCatalog.m_Instance.GetBrush(group.Key); + m_strokes = group.ToList(); + } + + public struct PoolAndStrokes + { + public GeometryPool pool; + public List strokes; + } + + // Returns a pool, or null if it's not from a pool. + private static GeometryPool GetPool(Stroke stroke) + { + if (stroke.m_BatchSubset == null) { return null; } + return stroke.m_BatchSubset.m_ParentBatch.Geometry; + } + + // Yields strokes in the same order as in the input, but adds an additional side effect: + // When the user consumes the stroke they will likely force its batch to become resident. + // This wrapper undoes that, preserving the state of the Batch's residency. + private IEnumerable + PreserveBatchResidency(IEnumerable input) + { + // We could do the "make it resident, make it not resident" dance on a stroke-by-stroke + // basis, but that would be ridiculously wasteful. Optimize by grouping adjacent strokes + // together if they share the same batch. + // + // When a sketch first loads, this optimization works extremely well because batches are + // contiguous in the stroke list. The more the user mutates (select, unselect, recolor) + // the less this will be true. There's not a lot we can do about it unless we want to + // sort the strokes by batch rather than by draw time (or whatever criteria the incoming + // iterator uses), which I think would be too invasive. + foreach (IGrouping group in + input.GroupBy(stroke => GetPool(stroke))) + { + GeometryPool pool = group.Key; + + // If we have to bring the pool into memory, save off enough data so we can + // push it back out. + Mesh previousBackingMesh = null; + if (pool != null && !pool.IsGeometryResident) + { + previousBackingMesh = pool.GetBackingMesh(); + // We currently only eject batch-owned GeometryPool to Mesh, so this is unlikely to trip; + // but we eventually may want to eject them to disk to save even more memory. + if (previousBackingMesh == null) + { + Debug.LogWarning("Not yet able to eject pool back to file; leaving it in memory"); + } + pool.EnsureGeometryResident(); + } + + foreach (Stroke stroke in group) + { + yield return stroke; + } + + if (previousBackingMesh != null) + { + pool.MakeGeometryNotResident(previousBackingMesh); + } + } + } + + // Puts the passed timestamp data into pool.texcoord2. + // It's assumed that the pool does not already have anything in texcoord2. + private static void AugmentWithTimestamps(GeometryPool pool, ref List timestamps) + { + if (App.UserConfig.Export.ExportStrokeTimestamp) + { + GeometryPool.VertexLayout withTxc2 = pool.Layout; + if (withTxc2.texcoord2.size == 0 && timestamps.Count == pool.NumVerts) + { + withTxc2.texcoord2 = new TexcoordInfo { size = 3, semantic = Semantic.Timestamp }; + pool.Layout = withTxc2; + pool.m_Texcoord2.v3 = timestamps; + timestamps = null; // don't let caller reuse the list + } + else + { + Debug.LogError("Internal error; cannot add timestamps"); + } + } + } + + // Appends timestamp data to the passed List. + // + // The timestamps are laid out like so: + // x = start of stroke + // y = end of stroke + // z = Roughly interpolated between the start and end of the stroke + // I say "roughly interpolated" because we make the assumption that each control point + // creates a fixed number of vertices. It's correct only to a first approximation. + private static void AppendTimestamps( + Stroke stroke, int numVerts, + List timestamps) + { + Vector3 ts = new Vector3(stroke.HeadTimestampMs * .001f, + stroke.TailTimestampMs * .001f, + 0); + foreach (float interpolated in MathUtils.LinearResampleCurve( + stroke.m_ControlPoints.Select(cp => cp.m_TimestampMs * .001f).ToArray(), + numVerts)) + { + ts.z = interpolated; + timestamps.Add(ts); + } + } + + /// Converts strokes to multiple GeometryPools whose size is <= vertexLimit. + /// The default vertexLimit is the maximum size allowed by Unity. + /// If a single stroke exceeds the vertex limit, the stroke will be ignored. + /// TODO: dangerous! vertexLimit should be a soft limit, with a hard limit of 65k + public IEnumerable ToGeometryBatches(int vertexLimit = 65534) + { + var layout = BrushCatalog.m_Instance.GetBrush(m_desc.m_Guid).VertexLayout; + var pool = new GeometryPool(); + var strokes = new List(); + // Timestamps are kept separate and only stitched in when we emit a Pool. + // This is because GeometryPool.Append() doesn't know what to do if the + // source and dest layouts are different. + List timestamps = new List(); + pool.Layout = layout; + foreach (var stroke in PreserveBatchResidency(m_strokes)) + { + while (true) + { + if (!stroke.IsGeometryEnabled) { continue; } + int oldNumVerts = pool.NumVerts; + if (pool.Append(stroke, vertexLimit)) + { + strokes.Add(stroke); + if (App.UserConfig.Export.ExportStrokeTimestamp) + { + AppendTimestamps(stroke, pool.NumVerts - oldNumVerts, timestamps); + } + // common case: it fits + break; + } + else if (pool.m_Vertices.Count > 0) + { + // it doesn't fit, but we can make a bit of forward progress by flushing the buffer + AugmentWithTimestamps(pool, ref timestamps); + yield return new PoolAndStrokes { pool = pool, strokes = strokes }; + pool = new GeometryPool(); + pool.Layout = layout; + strokes = new List(); + timestamps = new List(); + // loop around for another go + } + else + { + // very uncommon case: stroke won't fit even in an empty buffer. + // Should never happen with the default vertexLimit. + Debug.LogWarning("Cannot export stroke that exceeds vertex limit"); + // No choice but to ignore the stroke + break; + } + } + } + if (pool.m_Vertices.Count > 0) + { + AugmentWithTimestamps(pool, ref timestamps); + yield return new PoolAndStrokes { pool = pool, strokes = strokes }; + } + } + } + + // -------------------------------------------------------------------------------------------- // + // SceneState Export Payloads + // The state objects below are pure-state populated by the ExportCollector. + // -------------------------------------------------------------------------------------------- // + + // GetInstanceID() is not determinstic. + // The order in which objects are exported is somewhat determinstic. + // This class uses the order of export to determine the id. + public class DeterministicIdGenerator + { + private Dictionary m_instanceIdToId = new Dictionary(); + private int m_nextAvailable = 1; + public int GetIdFromInstanceId(UnityEngine.Object obj) + { + int instanceId = obj.GetInstanceID(); + if (m_instanceIdToId.ContainsKey(instanceId)) + { + return m_instanceIdToId[instanceId]; + } + else + { + var ret = m_nextAvailable; + m_nextAvailable += 1; + m_instanceIdToId[instanceId] = ret; + return ret; + } + } + } + /// The current exportable SceneState of Open Brush. + public class SceneStatePayload + { + // Metadata. + public string generator = "Tilt Brush 23.3.841faedfb compatible (Actually: Open Brush {0}.{1})"; + public DeterministicIdGenerator idGenerator = new DeterministicIdGenerator(); + + // Space Bases. + public readonly AxisConvention axes; + public readonly bool reverseWinding; + public readonly float exportUnitsFromAppUnits = App.UNITS_TO_METERS; + + // Entity Manifests. + public EnvPayload env = new EnvPayload(); + public LightsPayload lights = new LightsPayload(); + public List groups = new List(); + public GroupIdMapping groupIdMapping = new GroupIdMapping(); + // These actually contain the model/image data + public List modelMeshes = new List(); + public List imageQuads = new List(); + // These are bare nodes representing things that we currently can't export + public List referenceThings = new List(); + public readonly string temporaryDirectory = null; + + // If you pass a temporary directory, it may (or may not) be used + // for memory optimizations during the export. In this case, take + // care to call Destroy() if you want the payload to clean up after itself. + public SceneStatePayload(AxisConvention axes, string temporaryDirectory) + { + this.axes = axes; + this.temporaryDirectory = temporaryDirectory; + // The determinant can be used to detect if the basis-change has a mirroring. + // This matters because a mirroring turns the triangles inside-out, requiring + // us to flip their winding to preserve the surface orientation. + this.reverseWinding = (GetFromUnity_Axes(this).determinant < 0); + } + + // Tears down the payload as safely as possible; throws no exceptions + public void Destroy() + { + if (groups != null) + { + foreach (var item in groups) + { + item.Destroy(); + } + } + if (modelMeshes != null) + { + foreach (var item in modelMeshes) + { + item.Destroy(); + } + } + } + } + + // This should be deprecated in favor of putting a group id in all the payloads + public class GroupPayload + { + public UInt32 id; + public List brushMeshes = new List(); + public void Destroy() + { + if (brushMeshes != null) + { + foreach (var item in brushMeshes) + { + item.Destroy(); + } + } + } + } + + public class EnvPayload + { + public Guid guid; + public string description; + public Cubemap skyCubemap; + public bool useGradient; + public Color skyColorA; + public Color skyColorB; + public Vector3 skyGradientDir; + public Color fogColor; + public float fogDensity; + } + + public class LightPayload + { + public LightType type; + public string legacyUniqueName; // guaranteed unique but maybe not friendly + public string name; + public Color lightColor; + public Matrix4x4 xform; + } + + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public class LightsPayload + { + public Color ambientColor; // currently unused + public List lights = new List(); + } + + // Common to all MeshPayload. + // This is an _instance_ of a mesh, and corresponds to a GameObject/MeshRenderer + public abstract class BaseMeshPayload + { + // A unique not-very-human-readable string. + // "nodeName" and "geometryName" are _mostly_ unique except in edge cases like two distinct + // widgets having the same GetExportName(). + // Keeping this around also means I can diff vs old gltf1 files for testing purposes. + public string legacyUniqueName; + + // A pleasant-looking name for the node + public string nodeName; + public Matrix4x4 xform = Matrix4x4.identity; + // Not owned by this instance; ownership is potentially shared with other BaseMeshPayloads + public GeometryPool geometry; + // A pleasant-looking name for the geometry + public string geometryName; + public IExportableMaterial exportableMaterial; + + public readonly UInt32 group; + + protected BaseMeshPayload(uint groupId) { this.group = groupId; } + + /// Returns a string used to keep different models from having overlapping names. + /// Examples uses: + /// - Keep texture names from colliding + /// - Keep material names from colliding -- in particular this is an issue for + /// the name "defaultShadingGroup" which shows up in Poly-obj and Media Library-obj + /// imports a lot. + public abstract string MeshNamespace { get; } + + // Danger! This destroys resources shared by the entire SceneStatePayload. + // It is only public so it can be called during SceneStatePayload shutdown. + public void Destroy() + { + if (geometry != null) + { + geometry.Destroy(); + geometry = null; + } + } + } + + public class ImageQuadPayload : BaseMeshPayload + { + public override string MeshNamespace => "media"; + public ImageQuadPayload(uint groupId) : base(groupId) { } + } + + // MeshPayload for a poly or media library model; might also be used for + // exporting environments from the editor? + public class ModelMeshPayload : BaseMeshPayload + { + public Model model; + + // Enumerates the instances of a Model in the scene. + // Group by (model, modelId) to collect payloads from the same instance. + public int modelId; + + // These next two are base.xform in factored form: parentXform * localXform == base.xform + + // The transform of the parent ModelWidget. + // Payloads with the same model and modelId come from the same parent, + // therefore they have the same parentXform. + public Matrix4x4 parentXform; + + // This is slightly redundant because it's the same as instanceXform.inverse * base.xform. + // It's stored explicitly for slight added precision. + public Matrix4x4 localXform; + + public override string MeshNamespace + { + get + { + if (model.GetLocation().GetLocationType() == Model.Location.Type.IcosaAssetId) + { + return model.AssetId; // blows up if type is not PolyAssetId + } + else + { + string path = Path.GetFileNameWithoutExtension(model.RelativePath); + return System.Text.RegularExpressions.Regex.Replace( + path, @"[^a-zA-Z0-9_-]+", ""); + } + } + } + public ModelMeshPayload(uint groupId) : base(groupId) { } + } + + // MeshPayload for something drawn with a brush. + // Strokes are currently only used when exporting to USD + public class BrushMeshPayload : BaseMeshPayload + { + public List strokes; + public override string MeshNamespace => "brush"; + public BrushMeshPayload(uint groupId) : base(groupId) { } + } + + public class XformPayload + { + public readonly UInt32 group; + // This name isn't (currently) guaranteed to be unique among XformPayloads + public string name; + public Matrix4x4 xform; + + public XformPayload(uint groupId) { this.group = groupId; } + } + + // -------------------------------------------------------------------------------------------- // + // Data Collection Helpers + // -------------------------------------------------------------------------------------------- // + + /// Filters and returns geometry in a convenient format for export. + /// Returns geometry for the main canvas + public static ExportCanvas ExportMainCanvas() + { + // This is probably the more-useful one; it assumes we only have + // one interesting canvas (true, from a user perspective) and merges + // the selection canvas into its target canvas. + // + // It makes the assumption that the selection canvas is not playing + // fast-and-loose, and keeps its transform identical to its target + // canvas (ie, its transform is always identity). + // + // Of course, the selection canvas does play fast-and-loose, but + // we smack the canvas into place (via a deselect/reselect) before + // getting here. + var allowedBrushGuids = new HashSet( + BrushCatalog.m_Instance.AllBrushes + .Where(b => b.m_AllowExport) + .Select(b => (Guid)b.m_Guid)); + var main = App.Scene.MainCanvas; + var selection = App.Scene.SelectionCanvas; + var mainStrokes = SketchMemoryScript.AllStrokes() + .Where(stroke => allowedBrushGuids.Contains(stroke.m_BrushGuid) && + stroke.IsGeometryEnabled && + (stroke.Canvas == main || stroke.Canvas == selection)); + return new ExportCanvas(main, mainStrokes.ToList()); + } + + // Same as ExportAllCanvases but pretends all strokes are on the main canvas + // Does NOT transform strokes so ensure all canvases have identity transforms + public static ExportCanvas ExportAllCanvasesIgnoreLayers() + { + var allowedBrushGuids = new HashSet( + BrushCatalog.m_Instance.AllBrushes + .Where(b => b.m_AllowExport) + .Select(b => (Guid)b.m_Guid)); + var main = App.Scene.MainCanvas; + var selection = App.Scene.SelectionCanvas; + var mainStrokes = SketchMemoryScript + .AllStrokes() + .Where(stroke => allowedBrushGuids.Contains(stroke.m_BrushGuid) && stroke.IsGeometryEnabled); + return new ExportCanvas(main, mainStrokes.ToList()); + } + + /// Filters and returns geometry in a convenient format for export. + public static List ExportAllCanvases() + { + var allowedBrushGuids = new HashSet( + BrushCatalog.m_Instance.AllBrushes + .Where(b => b.m_AllowExport) + .Select(b => (Guid)b.m_Guid)); + + return SketchMemoryScript.AllStrokes() + .Where(stroke => allowedBrushGuids.Contains(stroke.m_BrushGuid) && + stroke.IsGeometryEnabled) + .GroupBy(stroke => stroke.Canvas) + .Select(canvasStrokes => new ExportCanvas(canvasStrokes)) + .ToList(); + } + + // -------------------------------------------------------------------------------------------- // + // Functional Conversion Helpers + // -------------------------------------------------------------------------------------------- // + + /// Applies change-of-basis conversions and distance conversions to a GeometryPool. + public static void ConvertUnitsAndChangeBasis(GeometryPool pool, SceneStatePayload payload) + { + ConvertScaleAndChangeBasis(pool, payload.exportUnitsFromAppUnits, GetFromUnity_Axes(payload)); + } + + private static void ConvertScaleAndChangeBasis( + GeometryPool pool, + float unitChange, + Matrix4x4 basisChange) + { + // If there's translation, it's ambiguous whether scale is applied before or after + // (probably the user means after, but still) + Debug.Assert((Vector3)basisChange.GetColumn(3) == Vector3.zero); + Matrix4x4 basisAndUnitChange = Matrix4x4.Scale(unitChange * Vector3.one) * basisChange; + +#if false // this code is pendantic but is useful to describe what's _really_ going on here + // xfBivector is the transform to use for normals, tangents, and other cross-products. + // Extracting rotation from a mat4 is hard, so take advantage of the fact that basisChange + // is a (maybe improper) rotation, an improper rotation being a rotation with scale -1. + // Matrix4x4 xfBivector = ExtractRotation(basisAndUnitChange); + Matrix4x4 xfBivector = basisChange; + if (basisChange.determinant < 0) { + // remove the -1 uniform scale by giving it yet more -1 uniform scale. + xfBivector = Matrix4x4.Scale(-Vector3.one) * xfBivector; + } + + // The exporter flips triangle winding when handedness flips, but it leaves the job unfinished; + // it needs to also flip the normals and tangents, since they're calculated from cross products. + // Detect that and kludge in an extra -1 scale to finish the job. + if (basisChange.determinant < 0) { + xfBivector = Matrix4x4.Scale(-Vector3.one) * xfBivector; + } +#else + Matrix4x4 xfBivector = basisChange; // The mirroring and the winding-flip cancel each other out +#endif + pool.ApplyTransform(basisAndUnitChange, xfBivector, unitChange, 0, pool.NumVerts); + } + + /// Convert the vertex colors to linear + public static List ConvertToLinearColorspace(List srgb) + { + return new List(srgb.Select(c32 => ((Color)c32).linear)); + } + + public static void ConvertToSrgbColorspace(Color[] linear) + { + for (int i = 0; i < linear.Length; i++) + { + linear[i] = linear[i].gamma; + } + } + + // Unused and untested +#if false + /// Convert the vertex Color32s to linear + /// Beware that you will lose information when quantizing down from + /// linear-float32 to linear-uint8. + public static void ConvertToLinearColorspace(MeshPayload meshes) { + for (int i = 0; i < meshes.Count; i++) { + var pool = meshes.geometry[i]; + for (int j = 0; j < pool.m_Colors.Count; j++) { + pool.m_Colors[j] = ((Color)pool.m_Colors[j]).linear; + } + } + } + /// Convert the light colors to linear + /// Beware that you will lose information when quantizing down from + /// linear-float32 to linear-uint8. + public static void ConvertToLinearColorspace(LightPayload lights) { + // XXX: these values can be > 1 because they are pre-multiplied by intensity + // Is it more appropriate to convert the base color from sRGB -> Linear, + // _then_ multiply by intensity? + lights.ambientColor = lights.ambientColor.linear; + for (int i = 0; i < lights.Count; i++) { + lights.lightColor[i] = lights.lightColor[i].linear; + } + } + /// Convert the environment (sky/fog) colors to linear + public static void ConvertToLinearColorspace(EnvPayload env) { + env.fogColor = env.fogColor.linear; + env.skyColorA = env.skyColorA.linear; + env.skyColorB = env.skyColorB.linear; + } +#endif + + + /// Flips winding order by swapping indices indexA and indexB. + /// indexA and indexB must be in the range [0, 2] and not equal to each other. + public static void ReverseTriangleWinding(GeometryPool pool, int indexA, int indexB) + { + if (indexA == indexB || indexA < 0 || indexA > 2) + { + throw new ArgumentException("indexA"); + } + if (indexB < 0 || indexB > 2) + { + throw new ArgumentException("indexB"); + } + var tris = pool.m_Tris; + int count = tris.Count; + for (int i = 0; i < count; i += 3) + { + var tmp = tris[i + indexA]; + tris[i + indexA] = tris[i + indexB]; + tris[i + indexB] = tmp; + } + } + + /// Given a transform, returns that transform in another basis. + /// The new basis is specified by outputFromInput. + public static Matrix4x4 ChangeBasis( + Matrix4x4 xfInput, + Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput) + { + return outputFromInput * xfInput * inputFromOutput; + } + + /// Given a transform, returns that transform in another basis. + /// The new basis is specified by outputFromInput. + /// + /// Not guaranteed to work if the change-of-basis matrix has non-uniform + /// scale. Otherwise, the resulting transform will not "fit" in a TrTransform. + public static TrTransform ChangeBasis( + TrTransform xfInput, + Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput) + { + // It might make this a little more accurate if outputFromInput were a TrTransform. + // Although... outputFromInput and inputFromOutput expressed as Matrix4x4 are always + // infinitely precise, for axis convention changes at least. Doing the same with a quat + // might involve sqrt(2)s. But maybe not for the common case of unity -> fbx / gltf? + Matrix4x4 m = outputFromInput * xfInput.ToMatrix4x4() * inputFromOutput; + return TrTransform.FromMatrix4x4(m); + } + + /// Given a transform, returns that transform in another basis. + /// The new basis is specified by outputFromInput. + /// + /// Not guaranteed to work if the change-of-basis matrix has non-axis-aligned + /// scale, or if the rotation portion can't be expressed as the product of 90 degree + /// rotations about an axis. Otherwise, the resulting scale will not "fit" in a Vec3. + public static void ChangeBasis( + Vector3 inputTranslation, Quaternion inputRotation, Vector3 inputScale, + out Vector3 translation, out Quaternion rotation, out Vector3 scale, + Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput) + { + TrTransform output = ChangeBasis( + TrTransform.TR(inputTranslation, inputRotation), + outputFromInput, inputFromOutput); + translation = output.translation; + rotation = output.rotation; + // Scale is a bit trickier. + Matrix4x4 m = outputFromInput * Matrix4x4.Scale(inputScale) * inputFromOutput; + scale = new Vector3(m[0, 0], m[1, 1], m[2, 2]); + m[0, 0] = m[1, 1] = m[2, 2] = 1; + Debug.Assert(m.isIdentity); + } + + /// Given a TrTransform, returns that transform as a mat4 in another basis. + /// The new basis is specified by the payload. + /// This changes both axes and units. + public static Matrix4x4 ChangeBasis( + TrTransform xfInput, SceneStatePayload payload) + { + Matrix4x4 basis = AxisConvention.GetFromUnity(payload.axes); + Matrix4x4 basisInverse = AxisConvention.GetToUnity(payload.axes); + return ChangeBasis(xfInput, basis, basisInverse) + .TransformBy(TrTransform.S(payload.exportUnitsFromAppUnits)) + .ToMatrix4x4(); + } + + /// Given a transform, returns that transform in another basis. + /// Since it's a Transform, xfInput is in Global (Room) space, Unity axes, decimeters. + /// The new basis is: Scene space, with the Payload's axes and units. + public static Matrix4x4 ChangeBasis( + Transform xfInput, SceneStatePayload payload) + { + Matrix4x4 basis = AxisConvention.GetFromUnity(payload.axes); + Matrix4x4 basisInverse = AxisConvention.GetToUnity(payload.axes); + return ChangeBasis(App.Scene.AsScene[xfInput], basis, basisInverse) + .TransformBy(TrTransform.S(payload.exportUnitsFromAppUnits)) + .ToMatrix4x4(); + } + + /// Returns a basis-change matrix that transforms from Unity axis conventions to + /// the conventions specified in the payload. + /// + /// Does *not* perform unit conversion, hence the name. + public static Matrix4x4 GetFromUnity_Axes(SceneStatePayload payload) + { + return AxisConvention.GetFromUnity(payload.axes); + } + + // -------------------------------------------------------------------------------------------- // + // Texture Collection Helper + // -------------------------------------------------------------------------------------------- // + + static public string GetTexturePath(Texture texture) + { +#if UNITY_EDITOR + // Copy the raw asset texture file and make sure it's either a PNG or JPG. + string texturePath = UnityEditor.AssetDatabase.GetAssetPath(texture); + texturePath = texturePath.Substring("Assets/".Length); + texturePath = Path.Combine(Application.dataPath, texturePath); + string extension = Path.GetExtension(texturePath).ToUpper(); + Debug.Assert(extension == ".PNG" || extension == ".JPG" || extension == ".JPEG", + String.Format("Texture {0} must be converted to png or jpg format", + texturePath)); + return texturePath; +#else + // Create an uncompressed texture from mainTex as a fallback when the asset database is + // not available. This only works on 2D textures that are readable. + string texFilename = Guid.NewGuid().ToString("D") + ".png"; + Texture2D texture2d = (Texture2D)texture; + Texture2D uncompressedTexture = new Texture2D( + texture.width, texture.height, TextureFormat.RGBA32, /*mipmap / mipChain:*/ false); + uncompressedTexture.SetPixels(texture2d.GetPixels()); + byte[] bytes = uncompressedTexture.EncodeToPNG(); + + // Save the texture file. + string texturePath = Path.Combine(Path.Combine(Path.GetDirectoryName(Application.dataPath), + ExportUtils.kProjectRelativeTextureExportRoot), + texFilename); + File.WriteAllBytes(texturePath, bytes); + return texturePath; +#endif + } + +#if UNITY_EDITOR && GAMEOBJ_EXPORT_TO_GLTF + public static SceneStatePayload GetSceneStateForGameObjectForExport( + GameObject gameObject, AxisConvention axes, Environment env) { + return ExportCollector.GetExportPayloadForGameObject(gameObject, axes, env); + } + + /// Returns a matrix: + /// - with units converted by the scale specified in the payload + /// - with basis changed by the axis convention in the payload + /// - with the scene transform subtracted out (so, converts from room to scene), but + /// only if the app is running. + /// This version also handles non-uniform scale (but stemming from what? the scene xf?) + public static Matrix4x4 ChangeBasisNonUniformScale(SceneStatePayload payload, Matrix4x4 root) { + Matrix4x4 basis = ExportUtils.GetBasisMatrix(payload); + + // Pre- and post-multiplying by the following matrix operations is the equivalent of + // .TransformBy(TrTransform.S(payload.exportUnitsFromAppUnits)). + Matrix4x4 xfScale = Matrix4x4.Scale(Vector3.one * payload.exportUnitsFromAppUnits); + Matrix4x4 xfScaleInverse = Matrix4x4.Scale(Vector3.one / payload.exportUnitsFromAppUnits); + + if (Application.isPlaying) { + // The world to scene matrix here performs the equivalent of App.Scene.AsScene[root] + // but works for non-uniform scale. + Matrix4x4 worldToSceneMatrix = App.Scene.transform.worldToLocalMatrix; + return xfScale * basis * worldToSceneMatrix * root * basis.inverse * xfScaleInverse; + } else { + return xfScale * basis * root * basis.inverse * xfScaleInverse; + } + } +#endif + + /// Returns a name based on originalName. + /// The name will be different from any names in usedNames, and will be added to usedNames. + public static string CreateUniqueName(string originalName, HashSet names) + { + if (string.IsNullOrEmpty(originalName)) + { + throw new ArgumentException("originalName"); + } + string baseName, ext; + { + // Don't use the Path functions because originalName may not be a valid path. + int dot = originalName.LastIndexOf('.'); + if (dot < 0) + { + baseName = originalName; + ext = ""; + } + else + { + baseName = originalName.Substring(0, dot); + ext = originalName.Substring(dot); + } + } + + for (int i = 0; ; ++i) + { + string subscript = (i == 0 ? "" : $"_{i}"); + string attempt = $"{baseName}{subscript}{ext}"; + if (names.Add(attempt)) + { + return attempt; + } + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Export/TiltBrushUriLoader.cs b/Assets/Scripts/Export/TiltBrushUriLoader.cs index 438e7f8ba7..163ceea01d 100644 --- a/Assets/Scripts/Export/TiltBrushUriLoader.cs +++ b/Assets/Scripts/Export/TiltBrushUriLoader.cs @@ -1,69 +1,69 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.IO; -using TiltBrushToolkit; -using UnityEngine; -using ToolkitRawImage = TiltBrushToolkit.RawImage; - -namespace TiltBrush -{ - - public class TiltBrushUriLoader : IUriLoader - { - private string m_uriBase; - private IUriLoader m_delegate; - private bool m_loadImages; - - /// If loadImages=true, use a C# image loader which doesn't need to be run on - /// the main thread (helps avoid hitching) but is much slower than Texture2D.LoadImageData. - public TiltBrushUriLoader(string glbPath, string uriBase, bool loadImages) - { - m_loadImages = loadImages; - m_uriBase = uriBase; - m_delegate = new BufferedStreamLoader(glbPath, uriBase); - } - - public IBufferReader Load(string uri) - { - // null uri means the binary chunk of a .glb - uri = (uri == null) ? null : PolyRawAsset.GetPolySanitizedFilePath(uri); - return m_delegate.Load(uri); - } - - public bool CanLoadImages() { return m_loadImages; } - - public ToolkitRawImage LoadAsImage(string uri) - { - uri = PolyRawAsset.GetPolySanitizedFilePath(uri); - string path = Path.Combine(m_uriBase, uri); - RawImage rawImage = ImageUtils.FromImageData(File.ReadAllBytes(path), path); - return new ToolkitRawImage - { - format = TextureFormat.RGBA32, - colorData = rawImage.ColorData, - colorWidth = rawImage.ColorWidth, - colorHeight = rawImage.ColorHeight - }; - } - -#if UNITY_EDITOR - public UnityEngine.Texture2D LoadAsAsset(string uri) - { - return m_delegate.LoadAsAsset(uri); - } -#endif - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.IO; +using TiltBrushToolkit; +using UnityEngine; +using ToolkitRawImage = TiltBrushToolkit.RawImage; + +namespace TiltBrush +{ + + public class TiltBrushUriLoader : IUriLoader + { + private string m_uriBase; + private IUriLoader m_delegate; + private bool m_loadImages; + + /// If loadImages=true, use a C# image loader which doesn't need to be run on + /// the main thread (helps avoid hitching) but is much slower than Texture2D.LoadImageData. + public TiltBrushUriLoader(string glbPath, string uriBase, bool loadImages) + { + m_loadImages = loadImages; + m_uriBase = uriBase; + m_delegate = new BufferedStreamLoader(glbPath, uriBase); + } + + public IBufferReader Load(string uri) + { + // null uri means the binary chunk of a .glb + uri = (uri == null) ? null : IcosaRawAsset.GetPolySanitizedFilePath(uri); + return m_delegate.Load(uri); + } + + public bool CanLoadImages() { return m_loadImages; } + + public ToolkitRawImage LoadAsImage(string uri) + { + uri = IcosaRawAsset.GetPolySanitizedFilePath(uri); + string path = Path.Combine(m_uriBase, uri); + RawImage rawImage = ImageUtils.FromImageData(File.ReadAllBytes(path), path); + return new ToolkitRawImage + { + format = TextureFormat.RGBA32, + colorData = rawImage.ColorData, + colorWidth = rawImage.ColorWidth, + colorHeight = rawImage.ColorHeight + }; + } + +#if UNITY_EDITOR + public UnityEngine.Texture2D LoadAsAsset(string uri) + { + return m_delegate.LoadAsAsset(uri); + } +#endif + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/BasePanel.cs b/Assets/Scripts/GUI/BasePanel.cs index 5868bc41c7..7be7b1a4c1 100644 --- a/Assets/Scripts/GUI/BasePanel.cs +++ b/Assets/Scripts/GUI/BasePanel.cs @@ -1,1687 +1,1687 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.Localization; -using UnityEngine.Localization.Settings; -using TMPro; - -namespace TiltBrush -{ - - [System.Serializable] - public struct PopupMapKey - { - public GameObject m_PopUpPrefab; - public SketchControlsScript.GlobalCommands m_Command; - } - - public class BasePanel : MonoBehaviour - { - static private float GAZE_DISTANCE = 10 * App.METERS_TO_UNITS; - static private float LARGE_DISTANCE = 9999.0f * App.METERS_TO_UNITS; - - // Types etc - - protected class TextSpring - { - public float m_CurrentAngle; - public float m_DesiredAngle; - public float m_Velocity; - - public void Update(float fK, float fDampen) - { - float fToDesired = m_DesiredAngle - m_CurrentAngle; - fToDesired *= fK; - float fDampenedVel = m_Velocity * fDampen; - float fSpringForce = fToDesired - fDampenedVel; - m_Velocity += fSpringForce; - m_CurrentAngle += (m_Velocity * Time.deltaTime); - } - } - - protected enum DescriptionState - { - Open, - Closing, - Closed - } - - protected enum PanelState - { - Unused, - Unavailable, - Available, - } - - // These names are used in our player prefs, so they must be protected from obfuscation - // Do not change the names of any of them, unless they've never been released. - [Serializable] - public enum PanelType - { - SketchSurface, - Color, - Brush, - AudioReactor, - AdminPanelMobile, - ToolsBasicMobile, - ToolsBasic, - Experimental, - ToolsAdvancedMobile, - MemoryWarning, - Labs, - Sketchbook, - SketchbookMobile, - BrushMobile, - AppSettings, - Tutorials, - Reference, - Lights, - GuideTools, - Environment, - Camera, - Testing, - Poly, - BrushExperimental, - ToolsAdvanced, - AppSettingsMobile, - AdminPanel, - ExtraPanel, - ExtraMobile, - PolyMobile, - LabsMobile, - ReferenceMobile, - CameraPath, - BrushLab, - WebcamPanel = 5200, - Scripts = 6000, - SnapSettings = 8000, - StencilSettings = 20200, - LayersPanel = 15000, - TransformPanel = 12000, - } - - private enum FixedTransitionState - { - Floating, - FixedToFloating, - FloatingToFixed, - Fixed - } - - // Inspector data, tunables - [SerializeField] protected PanelType m_PanelType; - - [SerializeField] protected Collider m_Collider; - [SerializeField] public GameObject m_Mesh; - [SerializeField] protected Renderer m_Border; - [SerializeField] protected Collider m_MeshCollider; - [SerializeField] protected Vector3 m_ParticleBounds; - - [SerializeField] protected PopupMapKey[] m_PanelPopUpMap; - [SerializeField] protected string m_PanelDescription; - [SerializeField] protected LocalizedString m_LocalizedPanelDescription; - - public string PanelDescription - { - get - { - try - { - var locString = m_LocalizedPanelDescription.GetLocalizedStringAsync().Result; - return locString; - } - catch - { - return m_PanelDescription; - } - } - } - - [SerializeField] protected GameObject m_PanelDescriptionPrefab; - - [SerializeField] protected Vector3 m_PanelDescriptionOffset; - [SerializeField] protected Color m_PanelDescriptionColor; - - [SerializeField] protected GameObject m_PanelFlairPrefab; - [SerializeField] protected Vector3 m_PanelFlairOffset; - - [SerializeField] protected float m_DescriptionSpringK = 4.0f; - [SerializeField] protected float m_DescriptionSpringDampen = 0.2f; - [SerializeField] protected float m_DescriptionClosedAngle = -90.0f; - [SerializeField] protected float m_DescriptionOpenAngle = 0.0f; - [SerializeField] protected float m_DescriptionAlphaDistance = 90.0f; - - [SerializeField] protected GameObject[] m_Decor; - - [SerializeField] protected float m_GazeHighlightScaleMultiplier = 1.2f; - - [SerializeField] private float m_BorderMeshWidth = 0.02f; - [SerializeField] private float m_BorderMeshAdvWidth = 0.01f; - - [SerializeField] public float m_PanelSensitivity = 0.1f; - [SerializeField] protected bool m_ClampToBounds = false; - [SerializeField] protected Vector3 m_ReticleBounds; - - [SerializeField] public float m_BorderSphereHighlightRadius; - [SerializeField] protected Vector2 m_PositioningSpheresBounds; - [SerializeField] protected float m_PositioningSphereRadius = 0.4f; - - [SerializeField] public bool m_UseGazeRotation = false; - [SerializeField] public float m_MaxGazeRotation = 20.0f; - [SerializeField] protected float m_GazeActivateSpeed = 8.0f; - - [SerializeField] public Vector3 m_InitialSpawnPos; - [SerializeField] public Vector3 m_InitialSpawnRotEulers; - - [SerializeField] public float m_WandAttachAngle; - [SerializeField] public float m_WandAttachYOffset; - [SerializeField] public float m_WandAttachHalfHeight; - [SerializeField] private bool m_BeginFixed; - [SerializeField] private bool m_CanBeFixedToWand = true; - [SerializeField] private bool m_CanBeDetachedFromWand = true; - - [SerializeField] private float m_PopUpGazeDuration = .2f; - - [SerializeField] protected MeshRenderer[] m_PromoBorders; - - protected const float m_SwipeThreshold = 0.4f; - protected Material m_BorderMaterial; - - // Public, mutable data - - [NonSerialized] public float m_SweetSpotDistance = 1.0f; - [NonSerialized] public bool m_Fixed; - [NonSerialized] public bool m_WandPrimedForAttach; - [NonSerialized] public float m_WandAttachRadiusAdjust; - [NonSerialized] public float m_WandAttachYOffset_Target; - // This is used to store the Y offset of a fixed panel at the point where the user begins - // interacting with a panelWidget. Storing this value allows modifications to the panes to be - // undone. When the user ends interaction, _Stable is updated to be the _Target. - [NonSerialized] public float m_WandAttachYOffset_Stable; - - // Internal data - - protected PopUpWindow m_ActivePopUp; - protected SketchControlsScript.GlobalCommands m_DelayedCommand; - protected int m_DelayedCommandParam; - protected int m_DelayedCommandParam2; - - protected GameObject m_PanelDescriptionObject; - protected Renderer m_PanelDescriptionRenderer; - protected TextMeshPro m_PanelDescriptionTextMeshPro; - - protected Vector3 m_BaseScale; - protected float m_AdjustedScale; - - protected TextSpring m_PanelDescriptionTextSpring; - protected TextSpring m_PanelFlairSpring; - protected PanelFlair m_PanelFlair; - - protected Vector3 m_ReticleOffset; - protected Vector3 m_Bounds; - protected Vector3 m_WorkingReticleBounds; - - protected DescriptionState m_PanelDescriptionState; - protected DescriptionState m_PanelFlairState; - protected float m_CloseAngleThreshold; - - protected PanelState m_CurrentState; - protected PanelState m_DesiredState; - protected bool m_GazeActive; - protected bool m_GazeWasActive; - protected bool m_GazeDescriptionsActive; - - protected bool m_InputValid; - protected bool m_EatInput; - protected Ray m_ReticleSelectionRay; - - protected float m_PositioningPercent; - - protected float m_MaxGazeOffsetDistance; - protected Vector3 m_GazeRotationAxis; - protected float m_GazeRotationAngle; - protected Quaternion m_GazeRotationAmount; - protected float m_GazeActivePercent; - - protected UIComponentManager m_UIComponentManager; - protected NonScaleChild m_NonScaleChild; // potentially null - - // Private - - private List m_DecorRenderers; - private List m_DecorTextMeshes; - - private float m_ScaledPositioningSphereRadius; - private float m_PositioningExtent; - private Vector3[] m_PositioningSpheres; - private Vector3[] m_PositioningSpheresTransformed; - - private Vector3 m_PositioningHome; - private float m_PositioningK = 0.5f; - private float m_PositioningDampen = 0.1f; - private float m_DepenetrationScalar = 200.0f; - private Vector3 m_PositioningVelocity; - - private Vector3 m_GazeHitPositionCurrent; - private Vector3 m_GazeHitPositionDesired; - private float m_GazeHitPositionSpeed = 10.0f; - - protected Vector2 m_SwipeRecentMotion = Vector2.zero; - - private FixedTransitionState m_TransitionState; - private TrTransform m_WandTransitionTarget; - private static float m_WandTransitionDuration = .25f; - private float m_WandTransitionPercent; // 0 = on wand, 1 = at target - private float m_WandAttachAdjustSpeed = 16.0f; - private float m_WandAttachYOffset_Initial; - private float m_WandAttachAngle_Initial; - private PanelWidget m_WidgetSibling; - - private GameObject m_TempPopUpCollider; - private float m_PopUpGazeTimer; - - private bool m_AdvancedModePanel; - private bool m_CurrentlyVisibleInAdvancedMode; - - // TODO: The following are just to track down an elusive NRE. Delete when we've figured - // it out. - private bool m_PanelInitializationStarted; - private bool m_PanelInitializationFinished; - private float m_PanelDescriptionCounter; - public Action m_OverrideControllerMaterial; - - // Accessors/properties - - public Vector3 GetBounds() { return m_Bounds; } - public float GetPositioningExtent() { return m_PositioningExtent; } - public bool IsAvailable() { return m_CurrentState != PanelState.Unavailable; } - public bool IsActive() { return m_GazeActive; } - public bool BeginFixed { get { return m_BeginFixed; } } - public void ClearBeginFixed() { m_BeginFixed = false; } - public bool CanBeDetached { get { return m_CanBeDetachedFromWand; } } - public bool IsInInitialPosition() - { - return m_WandAttachYOffset == m_WandAttachYOffset_Initial && - m_WandAttachAngle == m_WandAttachAngle_Initial; - } - public PanelWidget WidgetSibling { get { return m_WidgetSibling; } } - public bool AdvancedModePanel { get { return m_AdvancedModePanel; } } - public bool CurrentlyVisibleInAdvancedMode { get { return m_CurrentlyVisibleInAdvancedMode; } } - public Vector3 ParticleBounds { get { return m_ParticleBounds; } } - public PopUpWindow PanelPopUp { get { return m_ActivePopUp; } } - - public Color GetGazeColorFromActiveGazePercent() - { - PanelManager pm = PanelManager.m_Instance; - return Color.Lerp(pm.PanelHighlightInactiveColor, pm.PanelHighlightActiveColor, - m_GazeActivePercent); - } - - public PanelType Type - { - get { return m_PanelType; } - } - - public virtual bool ShouldRegister { get { return true; } } - - public void InitAdvancedFlag(bool advanced) - { - m_AdvancedModePanel = advanced; - m_CurrentlyVisibleInAdvancedMode = m_BeginFixed; - } - - public float PopUpOffset - { - get { return m_ActivePopUp ? m_ActivePopUp.GetPopUpForwardOffset() : 0; } - } - - virtual protected void CalculateBounds() - { - Vector3 vCurrentScale = transform.localScale; - m_Bounds = m_ReticleBounds; - m_Bounds.x *= vCurrentScale.x * 0.5f; - m_Bounds.y *= vCurrentScale.y * 0.5f; - m_Bounds.z = 0.0f; - - if (m_ActivePopUp == null) - { - m_WorkingReticleBounds = m_Bounds; - } - else - { - Vector3 vPopUpCurrentScale = m_ActivePopUp.transform.localScale; - m_WorkingReticleBounds = m_ActivePopUp.GetReticleBounds(); - m_WorkingReticleBounds.x *= vPopUpCurrentScale.x * 0.5f; - m_WorkingReticleBounds.y *= vPopUpCurrentScale.y * 0.5f; - } - } - - public void ActivatePromoBorder(bool activate) - { - if (m_WidgetSibling) - { - if (activate) - { - m_WidgetSibling.RemovePromoMeshesFromTintable(m_PromoBorders); - } - else - { - m_WidgetSibling.AddPromoMeshesToTintable(m_PromoBorders); - } - } - foreach (var m in m_PromoBorders) - { - m.material = activate ? PromoManager.m_Instance.SharePromoMaterial : m_BorderMaterial; - } - } - - virtual public void SetInIntroMode(bool inIntro) { } - - public void SetPositioningPercent(float fPercent) - { - m_PositioningPercent = fPercent; - } - - public void SetScale(float fScale) - { - m_AdjustedScale = fScale; - transform.localScale = m_BaseScale * m_AdjustedScale; - } - - public bool PanelIsUsed() { return m_CurrentState != PanelState.Unused; } - - public Collider GetCollider() { return m_Collider; } - - public bool HasMeshCollider() - { - return m_MeshCollider != null; - } - - virtual public bool RaycastAgainstMeshCollider(Ray rRay, out RaycastHit rHitInfo, float fDist) - { - rHitInfo = new RaycastHit(); - bool bReturnValue = false; - - // Creating a popup when a popup exists will generate a temp collider. This is to prevent - // forced, accidental out of bounds user input. - if (m_TempPopUpCollider != null) - { - BoxCollider tempCollider = m_TempPopUpCollider.GetComponent(); - bReturnValue = tempCollider.Raycast(rRay, out rHitInfo, fDist); - if (!bReturnValue) - { - // If we're ever not pointing at the temp collider, destroy it. - Destroy(m_TempPopUpCollider); - m_TempPopUpCollider = null; - } - } - - // If we've got a pop-up, check collision against that guy first. - if (m_ActivePopUp != null) - { - var collider = m_ActivePopUp.GetCollider(); - if (collider == null) { throw new InvalidOperationException("No popup collider"); } - bReturnValue = bReturnValue || - m_ActivePopUp.GetCollider().Raycast(rRay, out rHitInfo, fDist); - } - - // Check custom colliders on components. - bReturnValue = bReturnValue || - m_UIComponentManager.RaycastAgainstCustomColliders(rRay, out rHitInfo, fDist); - - // If we didn't have a pop-up, or collision failed, default to base mesh. - if (m_MeshCollider == null) { throw new InvalidOperationException("No mesh collider"); } - bReturnValue = bReturnValue || m_MeshCollider.Raycast(rRay, out rHitInfo, fDist); - return bReturnValue; - } - - virtual public void AssignControllerMaterials(InputManager.ControllerName controller) - { - m_UIComponentManager.AssignControllerMaterials(controller); - - // Allows components to override the regular controller material - // without worrying about execution order etc - if (m_OverrideControllerMaterial != null) - { - m_OverrideControllerMaterial(); - // Clear afterwards. This needs to be set every frame - m_OverrideControllerMaterial = null; - } - } - - /// This function is used to determine the value to be passed in to the controller pad mesh's - /// shader for visualizing swipes and other actions. - /// It is called by SketchControls when the panel is in focus. - public float GetControllerPadShaderRatio(InputManager.ControllerName controller) - { - return m_UIComponentManager.GetControllerPadShaderRatio(controller); - } - - public bool BrushPadAnimatesOnHover() - { - return m_UIComponentManager.BrushPadAnimatesOnAnyHover(); - } - - public bool UndoRedoBlocked() - { - // This function could be generalized to allow specific UIComponents to block undo/redo, - // but we only need it in a single case right now, so it's a bit more specific. - return m_ActivePopUp == null ? false : m_ActivePopUp.BlockUndoRedo(); - } - - virtual public void OnPanelMoved() { } - - virtual protected void Awake() - { - m_WandAttachYOffset_Initial = m_WandAttachYOffset; - m_WandAttachAngle_Initial = m_WandAttachAngle; - m_WandAttachYOffset_Target = m_WandAttachYOffset; - } - - // Happens at App Start() time. - virtual public void InitPanel() - { - m_PanelInitializationStarted = true; - m_BorderMaterial = m_Border.material; - m_BaseScale = transform.localScale; - m_AdjustedScale = 1.0f; - - CalculateBounds(); - m_NonScaleChild = GetComponent(); - - if (m_PanelDescriptionPrefab != null) - { - m_PanelDescriptionObject = (GameObject)Instantiate(m_PanelDescriptionPrefab); - m_PanelDescriptionObject.transform.position = m_Mesh.transform.position; - m_PanelDescriptionObject.transform.rotation = m_Mesh.transform.rotation; - m_PanelDescriptionObject.transform.SetParent(transform); - Vector3 vScale = m_PanelDescriptionObject.transform.localScale; - vScale *= transform.localScale.x; - m_PanelDescriptionObject.transform.localScale = vScale; - - m_PanelDescriptionRenderer = m_PanelDescriptionObject.GetComponent(); - if (m_PanelDescriptionRenderer) - { - m_PanelDescriptionRenderer.enabled = false; - } - - m_PanelDescriptionTextMeshPro = m_PanelDescriptionObject.GetComponent(); - if (m_PanelDescriptionTextMeshPro) - { - m_PanelDescriptionTextMeshPro.text = PanelDescription; - m_PanelDescriptionTextMeshPro.color = m_PanelDescriptionColor; - } - LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged; - } - - if (m_PanelFlairPrefab != null) - { - GameObject go = (GameObject)Instantiate(m_PanelFlairPrefab); - m_PanelFlair = go.GetComponent(); - m_PanelFlair.ParentToPanel(transform, m_PanelFlairOffset); - } - - m_PanelDescriptionState = DescriptionState.Closing; - m_PanelFlairState = DescriptionState.Closing; - - m_CloseAngleThreshold = Mathf.Abs(m_DescriptionOpenAngle - m_DescriptionClosedAngle) * 0.01f; - - m_PanelDescriptionTextSpring = new TextSpring(); - m_PanelDescriptionTextSpring.m_CurrentAngle = m_DescriptionClosedAngle; - m_PanelDescriptionTextSpring.m_DesiredAngle = m_DescriptionClosedAngle; - m_PanelFlairSpring = new TextSpring(); - m_PanelFlairSpring.m_CurrentAngle = m_DescriptionClosedAngle; - m_PanelFlairSpring.m_DesiredAngle = m_DescriptionClosedAngle; - - PanelManager pm = PanelManager.m_Instance; - m_Border.material.SetColor("_Color", pm.PanelHighlightInactiveColor); - - m_DecorRenderers = new List(); - m_DecorTextMeshes = new List(); - - if (m_Decor.Length > 0) - { - // Cache all decor renderers. - for (int i = 0; i < m_Decor.Length; ++i) - { - Renderer[] aChildRenderers = m_Decor[i].GetComponentsInChildren(); - for (int j = 0; j < aChildRenderers.Length; ++j) - { - // Prefer to cache a text mesh pro object if we find one. - TextMeshPro tmp = aChildRenderers[j].GetComponent(); - if (tmp) - { - m_DecorTextMeshes.Add(tmp); - } - else - { - // Otherwise, the standard renderer will do. - m_DecorRenderers.Add(aChildRenderers[j]); - m_DecorRenderers[m_DecorRenderers.Count - 1].material.SetColor("_Color", - pm.PanelHighlightInactiveColor); - } - } - } - } - - m_CurrentState = PanelState.Available; - m_DesiredState = PanelState.Available; - m_GazeActive = false; - m_PositioningPercent = 0.0f; - - if (m_PositioningSpheresBounds.x > 0.0f && m_PositioningSpheresBounds.y > 0.0f && m_PositioningSphereRadius > 0.0f) - { - Vector2 vSphereBounds = m_PositioningSpheresBounds; - vSphereBounds.x -= m_PositioningSphereRadius * 0.5f; - vSphereBounds.y -= m_PositioningSphereRadius * 0.5f; - m_ScaledPositioningSphereRadius = m_PositioningSphereRadius * transform.localScale.x; - - int iNumSpheresWidth = Mathf.CeilToInt((vSphereBounds.x * 2.0f) / m_PositioningSphereRadius); - int iNumSpheresHeight = Mathf.CeilToInt((vSphereBounds.y * 2.0f) / m_PositioningSphereRadius); - int iTotalNumSpheres = (iNumSpheresWidth * 2) + ((iNumSpheresHeight - 2) * 2); - m_PositioningSpheres = new Vector3[iTotalNumSpheres]; - - float fXInterval = (vSphereBounds.x / (float)(iNumSpheresWidth - 1)) * 2.0f; - float fYInterval = (vSphereBounds.y / (float)(iNumSpheresHeight - 1)) * 2.0f; - for (int i = 0; i < iNumSpheresWidth; ++i) - { - float fX = -vSphereBounds.x + (fXInterval * (float)i); - m_PositioningSpheres[i].Set(fX, -vSphereBounds.y, 0.0f); - } - for (int i = 0; i < iNumSpheresHeight - 2; ++i) - { - float fY = -vSphereBounds.y + (fYInterval * (float)(i + 1)); - m_PositioningSpheres[iNumSpheresWidth + (i * 2)].Set(-vSphereBounds.x, fY, 0.0f); - m_PositioningSpheres[iNumSpheresWidth + (i * 2) + 1].Set(vSphereBounds.x, fY, 0.0f); - } - for (int i = iTotalNumSpheres - iNumSpheresWidth; i < iTotalNumSpheres; ++i) - { - int iIndex = i - (iTotalNumSpheres - iNumSpheresWidth); - float fX = -vSphereBounds.x + (fXInterval * (float)iIndex); - m_PositioningSpheres[i].Set(fX, vSphereBounds.y, 0.0f); - } - - m_PositioningSpheresTransformed = new Vector3[m_PositioningSpheres.Length]; - for (int i = 0; i < m_PositioningSpheresTransformed.Length; ++i) - { - m_PositioningSpheresTransformed[i] = new Vector3(); - m_PositioningExtent = Mathf.Max(m_PositioningExtent, Mathf.Max(m_PositioningSpheres[i].x, m_PositioningSpheres[i].y)); - } - m_PositioningExtent += (m_PositioningSphereRadius * 2.0f); - } - - m_MaxGazeOffsetDistance = m_Bounds.magnitude; - m_GazeRotationAmount = Quaternion.identity; - - m_UIComponentManager = GetComponent(); - m_UIComponentManager.SetColor(pm.PanelHighlightInactiveColor); - - m_WidgetSibling = GetComponent(); - if (m_Fixed) - { - m_TransitionState = FixedTransitionState.Fixed; - } - - // Bake border meshs. - float width = !m_AdvancedModePanel ? m_BorderMeshAdvWidth : m_BorderMeshWidth; - Color baseCol = !m_AdvancedModePanel ? pm.PanelBorderMeshOutlineColor : pm.PanelBorderMeshBaseColor; - Color outlineCol = !m_AdvancedModePanel ? pm.PanelBorderMeshBaseColor : pm.PanelBorderMeshOutlineColor; - BakedMeshOutline[] bakeries = GetComponentsInChildren(true); - BakedMeshOutline borderBakery = m_Border.GetComponent(); - for (int i = 0; i < bakeries.Length; ++i) - { - if (bakeries[i] == borderBakery) - { - // The border is the only bakery that gets custom treatment. - bakeries[i].Bake(baseCol, outlineCol, width); - } - else - { - bakeries[i].Bake(pm.PanelBorderMeshBaseColor, pm.PanelBorderMeshOutlineColor, m_BorderMeshWidth); - } - } - - m_PanelInitializationFinished = true; - } - - public void CloseActivePopUp(bool force) - { - if (m_ActivePopUp != null) - { - if (m_ActivePopUp.RequestClose(force)) - { - InvalidateIfActivePopup(m_ActivePopUp); - } - } - } - - public void VerifyStateForFloating() - { - m_TransitionState = FixedTransitionState.Floating; - m_WandTransitionPercent = 1; - } - - public void SetPanelStableToTarget() - { - m_WandAttachYOffset_Stable = m_WandAttachYOffset_Target; - } - - public void ResetPanelToInitialPosition() - { - m_WandAttachYOffset = m_WandAttachYOffset_Initial; - m_WandAttachYOffset_Target = m_WandAttachYOffset_Initial; - m_WandAttachYOffset_Stable = m_WandAttachYOffset_Initial; - m_WandAttachAngle = m_WandAttachAngle_Initial; - - if (m_TransitionState != FixedTransitionState.Fixed) - { - m_Fixed = true; - m_TransitionState = FixedTransitionState.Fixed; - m_WandTransitionPercent = 0; - } - } - - public void ResetPanel() - { - // Get rid of the popup as fast as possible. - if (m_ActivePopUp != null) - { - Destroy(m_ActivePopUp.gameObject); - InvalidateIfActivePopup(m_ActivePopUp); - } - Destroy(m_TempPopUpCollider); - m_TempPopUpCollider = null; - - //reset gaze animations - m_PanelDescriptionState = DescriptionState.Closed; - if (m_PanelDescriptionRenderer) - { - m_PanelDescriptionRenderer.enabled = false; - } - - m_PanelFlairState = DescriptionState.Closed; - if (m_PanelFlair != null) - { - m_PanelFlair.Hide(); - } - - UpdatePanelColor(PanelManager.m_Instance.PanelHighlightInactiveColor); - - m_GazeActive = false; - m_GazeActivePercent = 0.0001f; - m_GazeRotationAxis = Vector3.zero; - } - - public void InvalidateIfActivePopup(PopUpWindow activePopup) - { - // If this popup isn't the active one, don't bother. - if (activePopup == m_ActivePopUp) - { - m_ActivePopUp = null; - // Eat input when we close a popup so the close action doesn't carry over. - m_EatInput = true; - } - } - - private void OnSelectedLocaleChanged(Locale locale) - { - if (m_PanelDescriptionTextMeshPro) - { - m_PanelDescriptionTextMeshPro.text = PanelDescription; - } - } - - private void OnDestroy() - { - LocalizationSettings.SelectedLocaleChanged -= OnSelectedLocaleChanged; - } - - void OnEnable() - { - OnEnablePanel(); - } - - void OnDisable() - { - OnDisablePanel(); - } - - virtual protected void OnEnablePanel() - { - if (PanelManager.m_Instance != null) - { - if (PanelManager.m_Instance.AdvancedModeActive()) - { - m_CurrentlyVisibleInAdvancedMode = true; - } - } - } - - virtual protected void OnDisablePanel() - { - if (PanelManager.m_Instance != null) - { - if (PanelManager.m_Instance.AdvancedModeActive()) - { - m_CurrentlyVisibleInAdvancedMode = false; - } - } - } - - public void ResetReticleOffset() - { - m_ReticleOffset = Vector3.zero; - } - - public void UpdateReticleOffset(float fXDelta, float fYDelta) - { - m_ReticleOffset.x += fXDelta * m_PanelSensitivity; - m_ReticleOffset.y += fYDelta * m_PanelSensitivity; - - if (m_ClampToBounds) - { - m_ReticleOffset.x = Mathf.Clamp(m_ReticleOffset.x, -m_WorkingReticleBounds.x, m_WorkingReticleBounds.x); - m_ReticleOffset.y = Mathf.Clamp(m_ReticleOffset.y, -m_WorkingReticleBounds.y, m_WorkingReticleBounds.y); - m_ReticleOffset.z = m_WorkingReticleBounds.z; - } - } - - // Given a position that has been proven to be a hit point on the panel's collider and the cast - // direction that resulted in that point, determine where the reticle should be located. - // Used by wand panel method of interacting with panels. - virtual public void GetReticleTransformFromPosDir(Vector3 vInPos, Vector3 vInDir, out Vector3 vOutPos, out Vector3 vForward) - { - //by default, the collision point is ok, and the reticle's forward should be the same as the mesh - vOutPos = vInPos; - vForward = -m_Mesh.transform.forward; - - Vector3 dir = Vector3.forward; - Ray rCastRay = new Ray(vInPos - vInDir * 0.5f, vInDir); - - // If we have a ghost popup, collide with that to find our position. - if (m_TempPopUpCollider != null) - { - RaycastHit rHitInfo; - if (DoesRayHitCollider(rCastRay, m_TempPopUpCollider.GetComponent(), out rHitInfo)) - { - vOutPos = rHitInfo.point; - } - } - - // Override default and ghost with standard popup collision. - if (m_ActivePopUp != null) - { - m_ActivePopUp.CalculateReticleCollision(rCastRay, ref vOutPos, ref vForward); - } - else - { - // PopUps trump UIComponents, so if there isn't a PopUp, check against all UIComponents. - m_UIComponentManager.CalculateReticleCollision(rCastRay, ref vOutPos, ref vForward); - } - } - - // TODO : This is currently broken. Needs to be updated to use new - // m_UIComponentManager.CalculateReticleCollision style. - // Using m_ReticleOffset, determine where the reticle should be located. - // Used by gaze method of interacting with panels. - virtual public void GetReticleTransform(out Vector3 vPos, out Vector3 vForward, bool bGazeAndTap) - { - // For ViewingOnly controls, position the reticle at the gaze panel position - if (bGazeAndTap) - { - // Calculate plane intersection - Transform head = ViewpointScript.Head; - Ray ray = new Ray(head.position, head.forward); - RaycastHit rHitInfo; - if (RaycastAgainstMeshCollider(ray, out rHitInfo, GAZE_DISTANCE)) - { - vPos = rHitInfo.point; - } - else - { - vPos = new Vector3(LARGE_DISTANCE, LARGE_DISTANCE, LARGE_DISTANCE); - } - } - else - { - Vector3 vTransformedOffset = m_Mesh.transform.rotation * m_ReticleOffset; - vPos = transform.position + vTransformedOffset; - } - vForward = -m_Mesh.transform.forward; - } - - // This function should only be used when the app state changes in a way that requires - // an out-of-focus panel's visuals to become reflect a stale state. - virtual public void ForceUpdatePanelVisuals() - { - m_UIComponentManager.UpdateVisuals(); - } - - void Update() - { - BaseUpdate(); - } - - protected void BaseUpdate() - { - UpdateState(); - CalculateBounds(); - UpdateDescriptions(); - UpdateGazeBehavior(); - UpdateFixedTransition(); - } - - protected void UpdateState() - { - //check if we're switching activity - if (m_GazeWasActive != m_GazeActive) - { - if (m_GazeActive) - { - AudioManager.m_Instance.ActivatePanel(true, transform.position); - } - else - { - m_UIComponentManager.ResetInput(); - AudioManager.m_Instance.ActivatePanel(false, transform.position); - } - - m_GazeWasActive = m_GazeActive; - OnUpdateActive(); - } - - bool bGazeDescriptionsWereActive = m_GazeDescriptionsActive; - m_GazeDescriptionsActive = m_GazeActive; - - // Update components if gaze is active. - if (m_GazeDescriptionsActive) - { - m_UIComponentManager.UpdateVisuals(); - } - - //check if we're switching descriptions showing - if (bGazeDescriptionsWereActive != m_GazeDescriptionsActive) - { - if (m_GazeDescriptionsActive) - { - if (m_PanelDescriptionObject) - { - m_PanelDescriptionState = DescriptionState.Open; - m_PanelDescriptionTextSpring.m_DesiredAngle = m_DescriptionOpenAngle; - m_PanelDescriptionRenderer.enabled = true; - } - } - else - { - if (m_PanelDescriptionObject) - { - m_PanelDescriptionState = DescriptionState.Closing; - m_PanelDescriptionTextSpring.m_DesiredAngle = m_DescriptionClosedAngle; - ResetPanelFlair(); - } - m_UIComponentManager.ManagerLostFocus(); - m_UIComponentManager.Deactivate(); - if (m_ActivePopUp != null) - { - if (m_ActivePopUp.RequestClose()) - { - InvalidateIfActivePopup(m_ActivePopUp); - } - } - } - } - - //update state - m_CurrentState = m_DesiredState; - } - - virtual protected void OnUpdateActive() - { - } - - void UpdateMeshRotation() - { - m_Mesh.transform.rotation = m_GazeRotationAmount * transform.rotation; - } - - protected void UpdateDescriptions() - { - try - { - m_PanelDescriptionCounter = 0; - if (m_PanelDescriptionState != DescriptionState.Closed && m_PanelDescriptionObject != null) - { - m_PanelDescriptionCounter = 1; - m_PanelDescriptionTextSpring.Update(m_DescriptionSpringK, m_DescriptionSpringDampen); - - m_PanelDescriptionCounter = 2; - Quaternion qOrient = Quaternion.Euler(0.0f, m_PanelDescriptionTextSpring.m_CurrentAngle, 0.0f); - m_PanelDescriptionObject.transform.rotation = m_Mesh.transform.rotation * qOrient; - - m_PanelDescriptionCounter = 3; - Vector3 vPanelDescriptionOffset = m_Bounds; - vPanelDescriptionOffset.x *= m_PanelDescriptionOffset.x; - vPanelDescriptionOffset.y *= m_PanelDescriptionOffset.y; - Vector3 vTransformedOffset = m_Mesh.transform.rotation * vPanelDescriptionOffset; - m_PanelDescriptionObject.transform.position = m_Mesh.transform.position + vTransformedOffset; - - m_PanelDescriptionCounter = 4; - float fDistToClosed = Mathf.Abs(m_PanelDescriptionTextSpring.m_CurrentAngle - m_DescriptionClosedAngle); - Color descColor = m_PanelDescriptionColor; - if (m_ActivePopUp != null) - { - descColor = Color.Lerp(m_PanelDescriptionColor, - PanelManager.m_Instance.PanelHighlightInactiveColor, - m_ActivePopUp.GetTransitionRatioForVisuals()); - } - float fRatio = fDistToClosed / m_DescriptionAlphaDistance; - descColor.a = Mathf.Min(fRatio * fRatio, 1.0f); - if (m_PanelDescriptionTextMeshPro) - { - m_PanelDescriptionTextMeshPro.color = descColor; - } - - m_PanelDescriptionCounter = 5; - if (m_PanelDescriptionState == DescriptionState.Closing) - { - float fToDesired = m_PanelDescriptionTextSpring.m_DesiredAngle - m_PanelDescriptionTextSpring.m_CurrentAngle; - if (Mathf.Abs(fToDesired) <= m_CloseAngleThreshold) - { - m_PanelDescriptionRenderer.enabled = false; - m_PanelDescriptionState = DescriptionState.Closed; - } - } - } - - m_PanelDescriptionCounter = 6; - if (m_PanelFlairState != DescriptionState.Closed) - { - m_PanelDescriptionCounter = 7; - m_PanelFlairSpring.Update(m_DescriptionSpringK, m_DescriptionSpringDampen); - float fDistToClosed = Mathf.Abs(m_PanelFlairSpring.m_CurrentAngle - m_DescriptionClosedAngle); - float fRatio = fDistToClosed / m_DescriptionAlphaDistance; - float fAlpha = Mathf.Min(fRatio * fRatio, 1.0f); - - m_PanelDescriptionCounter = 8; - Quaternion qOrient = m_Mesh.transform.rotation * Quaternion.Euler(0.0f, m_PanelFlairSpring.m_CurrentAngle, 0.0f); - - m_PanelDescriptionCounter = 9; - if (m_PanelFlair != null) - { - m_PanelFlair.UpdateAnimationOnPanel(m_Bounds, qOrient, fAlpha); - } - - m_PanelDescriptionCounter = 10; - if (m_PanelFlairState == DescriptionState.Closing) - { - float fToDesired = m_PanelFlairSpring.m_DesiredAngle - m_PanelFlairSpring.m_CurrentAngle; - if (Mathf.Abs(fToDesired) <= m_CloseAngleThreshold) - { - if (m_PanelFlair != null) - { - m_PanelFlair.Hide(); - } - - m_PanelFlairState = DescriptionState.Closed; - } - } - } - } - catch (Exception ex) - { - string message = string.Format("{0}: Init State({1}, {2}), Counter({3})", - ex.Message, - m_PanelInitializationStarted, - m_PanelInitializationFinished, - m_PanelDescriptionCounter); - throw new Exception(message); - } - } - - protected virtual void UpdateGazeBehavior() - { - if (m_UseGazeRotation && m_Mesh) - { - float fPrevPercent = m_GazeActivePercent; - float fPercentStep = m_GazeActivateSpeed * Time.deltaTime; - if (IsActive()) - { - m_GazeActivePercent = Mathf.Min(m_GazeActivePercent + fPercentStep, 1.0f); - } - else - { - m_GazeActivePercent = Mathf.Max(m_GazeActivePercent - fPercentStep, 0.0f); - } - - //don't bother updating static panels - if (fPrevPercent > 0.0f) - { - float fRotationAngle = m_GazeRotationAngle * m_GazeActivePercent * (1.0f - m_PositioningPercent); - m_GazeRotationAmount = Quaternion.AngleAxis(fRotationAngle, m_GazeRotationAxis); - UpdateMeshRotation(); - - Color rPanelColor = GetGazeColor(); - if (m_Border.material == m_BorderMaterial) - { - m_Border.material.SetColor("_Color", rPanelColor); - } - - if (fPrevPercent < 1.0f) - { - float fScaleMult = m_GazeActivePercent * (m_GazeHighlightScaleMultiplier - 1.0f); - Vector3 newScale = m_BaseScale * m_AdjustedScale * (1.0f + fScaleMult); - if (m_NonScaleChild != null) - { - m_NonScaleChild.globalScale = newScale; - } - else - { - transform.localScale = newScale; - } - } - - UpdatePanelColor(rPanelColor); - m_UIComponentManager.GazeRatioChanged(m_GazeActivePercent); - } - } - } - - public Color GetGazeColor() - { - PanelManager pm = PanelManager.m_Instance; - Color targetColor = pm.PanelHighlightActiveColor; - if (m_ActivePopUp != null) - { - targetColor = Color.Lerp(pm.PanelHighlightActiveColor, - pm.PanelHighlightInactiveColor, - m_ActivePopUp.GetTransitionRatioForVisuals()); - } - - float t = m_GazeActivePercent; - if (SketchControlsScript.m_Instance.AtlasIconTextures) - { - // If we're atlasing panel textures, only send 0 and 1 through. - t = Mathf.Floor(m_GazeActivePercent + 0.5f); - } - return Color.Lerp(pm.PanelHighlightInactiveColor, targetColor, t); - } - - protected void UpdateFixedTransition() - { - // State machine for panel exploding off of wand controller. - if (m_TransitionState == FixedTransitionState.FixedToFloating) - { - m_WandTransitionPercent += Time.deltaTime / m_WandTransitionDuration; - if (m_WandTransitionPercent >= 1.0f) - { - m_WandTransitionPercent = 1.0f; - m_TransitionState = FixedTransitionState.Floating; - } - PanelManager.m_Instance.UpdateWandTransitionXf( - this, m_WandTransitionTarget, m_WandTransitionPercent); - } - else if (m_TransitionState == FixedTransitionState.FloatingToFixed) - { - m_WandTransitionPercent -= Time.deltaTime / m_WandTransitionDuration; - if (m_WandTransitionPercent <= 0.0f) - { - m_WandTransitionPercent = 0.0f; - m_TransitionState = FixedTransitionState.Fixed; - } - PanelManager.m_Instance.UpdateWandTransitionXf( - this, m_WandTransitionTarget, m_WandTransitionPercent); - } - else if (WidgetSibling && - WidgetSibling.IsUserInteracting(InputManager.ControllerName.Brush)) - { - // If the user is interacting with this panel, lock to a pane if this panel allows it. - if (m_CanBeFixedToWand) - { - PanelManager.m_Instance.AttachHeldPanelToWand(this); - } - } - else if (m_TransitionState == FixedTransitionState.Fixed) - { - // Update attach height. - float fStep = m_WandAttachAdjustSpeed * Time.deltaTime; - float fToTarget = m_WandAttachYOffset_Target - m_WandAttachYOffset; - if (Mathf.Abs(fToTarget) < fStep) - { - m_WandAttachYOffset = m_WandAttachYOffset_Target; - } - else - { - m_WandAttachYOffset += fStep * Mathf.Sign(fToTarget); - } - m_WandAttachRadiusAdjust = 0.0f; - } - } - - public void WidgetSiblingBeginInteraction() - { - PanelManager.m_Instance.InitPanesForPanelAttach(m_Fixed); - - // Initialize primed flag to off-- it'll get enabled if the attachment checks are valid. - m_WandPrimedForAttach = false; - - if (m_Fixed) - { - m_Fixed = false; - m_TransitionState = FixedTransitionState.Floating; - m_WandTransitionPercent = 1; - } - } - - public void WidgetSiblingEndInteraction() - { - if (!m_Fixed) - { - PanelManager.m_Instance.ResetPaneVisuals(); - if (m_WandPrimedForAttach || !CanBeDetached) - { - m_Fixed = true; - m_TransitionState = FixedTransitionState.Fixed; - m_WandTransitionPercent = 0; - } - - if (!m_WandPrimedForAttach) - { - // If we're releasing this panel and it's not snapping to a pane, make sure the pane - // panels are reset back to their stable positions. - PanelManager.m_Instance.SetFixedPanelsToStableOffsets(); - } - else - { - // If this panel is snapping to a pane, update it's stable position so it's sorted - // correctly when we close the gaps. - SetPanelStableToTarget(); - } - m_WandTransitionTarget = TrTransform.FromTransform(transform); - WidgetSibling.SetActiveTintToShowError(false); - - PanelManager.m_Instance.ClosePanePanelGaps(); - } - } - - // Target is ignored if the panel is reattaching to the wand - public void TransitionToWand(bool bFixed, TrTransform target) - { - if (bFixed && m_TransitionState != FixedTransitionState.Fixed) - { - m_TransitionState = FixedTransitionState.FloatingToFixed; - } - else if (!bFixed && m_TransitionState != FixedTransitionState.Floating) - { - m_WandTransitionTarget = target; - m_TransitionState = FixedTransitionState.FixedToFloating; - } - } - - void UpdatePanelColor(Color rPanelColor) - { - // Set the appropriate dim value for all our UI components. - m_UIComponentManager.SetColor(rPanelColor); - - OnUpdateGazeBehavior(rPanelColor); - - for (int i = 0; i < m_DecorRenderers.Count; ++i) - { - m_DecorRenderers[i].material.SetColor("_Color", rPanelColor); - } - for (int i = 0; i < m_DecorTextMeshes.Count; ++i) - { - m_DecorTextMeshes[i].color = rPanelColor; - } - } - - virtual protected void OnUpdateGazeBehavior(Color rPanelColor) { } - - virtual public void PanelGazeActive(bool bActive) - { - m_GazeActive = bActive; - m_GazeHitPositionCurrent = transform.position; - m_UIComponentManager.ResetInput(); - } - - void SetPanelFlairText(string sText) - { - if (m_PanelFlair != null) - { - // Interpret any message as a good message. - if (m_PanelFlair.AlwaysShow) - { - m_PanelFlair.SetText(sText); - m_PanelFlair.Show(); - m_PanelFlairState = DescriptionState.Open; - m_PanelFlairSpring.m_DesiredAngle = m_DescriptionOpenAngle; - } - else - { - if (sText != null && !sText.Equals("")) - { - m_PanelFlair.SetText(sText); - m_PanelFlair.Show(); - m_PanelFlairState = DescriptionState.Open; - m_PanelFlairSpring.m_DesiredAngle = m_DescriptionOpenAngle; - } - else if (m_PanelFlairState != DescriptionState.Closed) - { - m_PanelFlairState = DescriptionState.Closing; - m_PanelFlairSpring.m_DesiredAngle = m_DescriptionClosedAngle; - } - } - } - } - - public void ResetPanelFlair() - { - if (m_PanelFlairState != DescriptionState.Closed) - { - m_PanelFlairState = DescriptionState.Closing; - m_PanelFlairSpring.m_DesiredAngle = m_DescriptionClosedAngle; - } - } - - virtual public void OnWidgetShowAnimStart() { } - - virtual public void OnWidgetShowAnimComplete() { } - - virtual public void OnWidgetHide() { } - - /// Accumulates dpad input, and returns nonzero for a discrete swipe action. - /// Return value is 1 for "backward" swipe moving a page forward - /// and -1 for "forward" swipe moving a page backward. - protected int AccumulateSwipe() - { - int direction = 0; - if (InputManager.m_Instance.IsBrushScrollActive()) - { - // If our delta is beyond our trigger threshold, report it. - float fDelta = InputManager.m_Instance.GetAdjustedBrushScrollAmount(); - if (IncrementMotionAndCheckForSwipe(fDelta)) - { - direction = (int)Mathf.Sign(m_SwipeRecentMotion.x) * -1; - m_SwipeRecentMotion.x = 0.0f; - } - } - else - { - m_SwipeRecentMotion.x = 0.0f; - } - - return direction; - } - - /// Virtual so panels can interpret m_SwipeRecentMotion however they'd like. - virtual public bool IncrementMotionAndCheckForSwipe(float fMotion) - { - m_SwipeRecentMotion.x += fMotion; - return Mathf.Abs(m_SwipeRecentMotion.x) > m_SwipeThreshold; - } - - /// Panels are updated by SketchControls when they have focus. - public void UpdatePanel(Vector3 vToPanel, Vector3 vHitPoint) - { - // Validate input and cache it for this update. - m_InputValid = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); - if (m_EatInput) - { - m_EatInput = m_InputValid; - m_InputValid = false; - } - - // Cache input ray for this update. - Vector3 vReticlePos = SketchControlsScript.m_Instance.GetUIReticlePos(); - m_ReticleSelectionRay = new Ray(vReticlePos - transform.forward, transform.forward); - - if (m_ActivePopUp != null) - { - var collider = m_ActivePopUp.GetCollider(); - if (collider == null) { throw new InvalidOperationException("No popup collider"); } - RaycastHit hitInfo; - - // If we're pointing at a popup, increment our gaze timer. - if (BasePanel.DoesRayHitCollider(m_ReticleSelectionRay, - m_ActivePopUp.GetCollider(), out hitInfo)) - { - if (m_ActivePopUp.IsOpen()) - { - m_PopUpGazeTimer += Time.deltaTime; - } - } - else if (!m_ActivePopUp.InputObjectHasFocus() && - (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate) || - m_PopUpGazeTimer > m_PopUpGazeDuration)) - { - // If we're not pointing at the popup and the user doesn't have focus on an element - // of the popup, dismiss the popup if we've pointed at it before, or there's input. - m_ActivePopUp.RequestClose(); - } - } - - if (m_UseGazeRotation) - { - m_GazeHitPositionDesired = vHitPoint; - Vector3 vCurrToDesired = m_GazeHitPositionDesired - m_GazeHitPositionCurrent; - float fStep = m_GazeHitPositionSpeed * Time.deltaTime; - if (vCurrToDesired.sqrMagnitude < fStep * fStep) - { - m_GazeHitPositionCurrent = m_GazeHitPositionDesired; - } - else - { - vCurrToDesired.Normalize(); - vCurrToDesired *= fStep; - m_GazeHitPositionCurrent += vCurrToDesired; - } - - Vector3 vToCenter = m_GazeHitPositionCurrent - GetCollider().transform.position; - float fOffsetDist = vToCenter.magnitude; - vToCenter.Normalize(); - m_GazeRotationAxis = Vector3.Cross(-vToPanel, vToCenter); - m_GazeRotationAxis.Normalize(); - m_GazeRotationAngle = (fOffsetDist / m_MaxGazeOffsetDistance) * m_MaxGazeRotation; - } - - // Update custom logic for panel. - OnUpdatePanel(vToPanel, vHitPoint); - - // Update UIComponents, with PopUps taking priority. - if (m_ActivePopUp) - { - if (!m_EatInput && m_PopUpGazeTimer > 0) - { - m_ActivePopUp.UpdateUIComponents(m_ReticleSelectionRay, m_InputValid, m_ActivePopUp.GetCollider()); - } - } - else - { - // TODO : I'm not convinced the 3rd parameter here should be the main collider. - // I think it might need to be the mesh collider. If so, the collider is only used in - // monoscopic mode and should be renamed appropriately. - m_UIComponentManager.UpdateUIComponents(m_ReticleSelectionRay, m_InputValid, GetCollider()); - - // If a popup was just spawned, clear our active UI component, as input will now be - // directed to the popup. - if (m_ActivePopUp) - { - m_UIComponentManager.ResetInput(); - } - - if (m_UIComponentManager.ActiveInputUIComponent == null) - { - SetPanelFlairText(null); - } - else - { - SetPanelFlairText(m_UIComponentManager.ActiveInputUIComponent.Description); - } - } - } - - /// Base level logic for updating panel. This should be called from any derived class. - virtual public void OnUpdatePanel(Vector3 vToPanel, Vector3 vHitPoint) { } - - public void InitForPanelMovement() - { - m_PositioningHome = transform.position; - m_PositioningVelocity.Set(0.0f, 0.0f, 0.0f); - m_WandAttachYOffset_Stable = m_WandAttachYOffset_Target; - } - - virtual public void PanelHasStoppedMoving() - { - m_GazeHitPositionCurrent = transform.position; - } - - public void CreatePopUp(SketchControlsScript.GlobalCommands rCommand, - int iCommandParam, int iCommandParam2, string sDelayedText = "", - Action delayedClose = null) - { - CreatePopUp(rCommand, iCommandParam, iCommandParam2, Vector3.zero, sDelayedText, delayedClose); - } - - public void CreatePopUp(SketchControlsScript.GlobalCommands rCommand, - int iCommandParam, int iCommandParam2, Vector3 vPopupOffset, string sDelayedText = "", - Action delayedClose = null) - { - bool bPopUpExisted = false; - Vector3 vPrevPopUpPos = Vector3.zero; - // If we've got an active popup, send it packing. - if (m_ActivePopUp != null) - { - // Create a copy of the collider so we don't pull the rug out from under the user. - m_TempPopUpCollider = m_ActivePopUp.DuplicateCollider(); - vPrevPopUpPos = m_ActivePopUp.transform.position; - CloseActivePopUp(false); - bPopUpExisted = true; - } - - if (m_ActivePopUp == null) - { - // Look for the appropriate popup for this command. - int iPopUpIndex = -1; - for (int i = 0; i < m_PanelPopUpMap.Length; ++i) - { - if (m_PanelPopUpMap[i].m_Command == rCommand) - { - iPopUpIndex = i; - break; - } - } - - if (iPopUpIndex >= 0) - { - Vector3 position = vPopupOffset; - CreatePopUp( - m_PanelPopUpMap[iPopUpIndex].m_PopUpPrefab, position, - bPopUpExisted, bPopUpExisted, iCommandParam, - iCommandParam2, rCommand, sDelayedText, delayedClose - ); - } - } - } - - public void CreatePopUp( - GameObject prefab, Vector3 position, - bool explicitPosition, bool transition, int iCommandParam = -1, int iCommandParam2 = -1, - SketchControlsScript.GlobalCommands delayedCommand = SketchControlsScript.GlobalCommands.Null, - string sDelayedText = "", Action delayedClose = null) - { - // Create a new popup. - GameObject popUp = Instantiate(prefab, - m_Mesh.transform.position, m_Mesh.transform.rotation); - m_ActivePopUp = popUp.GetComponent(); - - if (explicitPosition) - { - popUp.transform.position = position; - } - else - { - // Treat position as an offset - popUp.transform.position = m_Mesh.transform.position + - (m_Mesh.transform.forward * m_ActivePopUp.GetPopUpForwardOffset()) + - m_Mesh.transform.TransformVector(position); - } - - popUp.transform.parent = m_Mesh.transform; - m_ActivePopUp.Init(gameObject, sDelayedText); - m_ActivePopUp.SetPopupCommandParameters(iCommandParam, iCommandParam2); - - m_ActivePopUp.m_OnClose += delayedClose; - m_PopUpGazeTimer = 0; - m_EatInput = !m_ActivePopUp.IsLongPressPopUp(); - - // If we closed a popup to create this one, we wan't don't want the standard visual - // fade that happens when a popup comes in. We're spoofing the transition value - // for visuals to avoid a pop. - if (!transition) - { - m_ActivePopUp.SpoofTransitionValue(); - } - - // Cache the intended command. - m_DelayedCommand = delayedCommand; - m_DelayedCommandParam = iCommandParam; - m_DelayedCommandParam2 = iCommandParam2; - - } - - public void PositionPopUp(Vector3 basePos) - { - m_ActivePopUp.transform.position = basePos; - } - - public void ResolveDelayedButtonCommand(bool bConfirm, bool bKeepOpen = false) - { - PopUpWindow currentPopup = m_ActivePopUp; - if (bConfirm) - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - m_DelayedCommand, m_DelayedCommandParam, m_DelayedCommandParam2); - } - // if the popup has changed since the start of the function (and it wasn't null at the start), - // that means that the global command called kicked off a new popup. In which case we shouldn't - // close the popup, or overwrite the delayed command. - bool panelChanged = (currentPopup != null) && (m_ActivePopUp != currentPopup); - if (!panelChanged) - { - m_DelayedCommand = SketchControlsScript.GlobalCommands.Null; - m_DelayedCommandParam = -1; - m_DelayedCommandParam2 = -1; - if (m_ActivePopUp != null && !bKeepOpen) - { - m_ActivePopUp.RequestClose(); - } - } - } - - virtual public void GotoPage(int iIndex) - { - } - - virtual public void AdvancePage(int iAmount) - { - } - - public void InitForCollisionDetection() - { - for (int i = 0; i < m_PositioningSpheresTransformed.Length; ++i) - { - m_PositioningSpheresTransformed[i] = transform.TransformPoint(m_PositioningSpheres[i]); - } - } - - public void CalculateDepenetration(BasePanel rOther) - { - Vector3 vThemToUs = transform.position - rOther.transform.position; - Vector3 vThemToUsNorm = vThemToUs.normalized; - float fMaxExtent = m_PositioningExtent + rOther.GetPositioningExtent(); - - if (vThemToUs.sqrMagnitude < fMaxExtent * fMaxExtent) - { - float fCombinedSphereRad = m_ScaledPositioningSphereRadius + rOther.m_ScaledPositioningSphereRadius; - float fCombinedSphereRadSq = fCombinedSphereRad * fCombinedSphereRad; - for (int i = 0; i < m_PositioningSpheresTransformed.Length; ++i) - { - for (int j = 0; j < rOther.m_PositioningSpheresTransformed.Length; ++j) - { - Vector3 vSphereToSphere = rOther.m_PositioningSpheresTransformed[j] - m_PositioningSpheresTransformed[i]; - if (vSphereToSphere.sqrMagnitude < fCombinedSphereRadSq) - { - float fSphereToSphereDist = vSphereToSphere.magnitude; - float fDepenetrationAmount = fCombinedSphereRad - fSphereToSphereDist; - m_PositioningVelocity += (vThemToUsNorm * fDepenetrationAmount * m_DepenetrationScalar * Time.deltaTime); - } - } - } - } - } - - public void CalculateDepenetration(Vector3 vOtherPos, float fCombinedRadius) - { - Vector3 vThemToUs = transform.position - vOtherPos; - if (vThemToUs.sqrMagnitude < fCombinedRadius * fCombinedRadius) - { - float fDepenetrationAmount = fCombinedRadius - vThemToUs.magnitude; - m_PositioningVelocity += (vThemToUs.normalized * fDepenetrationAmount * - m_DepenetrationScalar * Time.fixedDeltaTime); - } - } - - public void UpdatePositioningForces() - { - //use spring to bring us home - Vector3 vToHome = m_PositioningHome - transform.position; - vToHome *= m_PositioningK; - Vector3 vDampenedVel = m_PositioningVelocity * m_PositioningDampen; - Vector3 vSpringForce = vToHome - vDampenedVel; - m_PositioningVelocity += vSpringForce; - - //update position - Vector3 vPos = transform.position; - vPos += (m_PositioningVelocity * Time.deltaTime); - transform.position = vPos; - if (m_NonScaleChild) - { - m_NonScaleChild.OnPosRotChanged(); - } - } - - /// Update the delayed command parameter that is used when a popup confirmation button is pressed. - /// This can be used when something provides more context to the initial command. - public void UpdateDelayedCommandParameter(int newParam) - { - m_DelayedCommandParam = newParam; - } - - static public bool DoesRayHitCollider(Ray rRay, Collider rCollider, bool skipAngleCheck = false) - { - if (!skipAngleCheck && Vector3.Angle(rRay.direction, rCollider.transform.forward) > 90.0f) - { - return false; - } - - RaycastHit rHitInfo; - return rCollider.Raycast(rRay, out rHitInfo, 100.0f); - } - - static public bool DoesRayHitCollider(Ray rRay, Collider rCollider, out RaycastHit rHitInfo) - { - return rCollider.Raycast(rRay, out rHitInfo, 100.0f); - } - - /* - * For Debugging. - * - void OnDrawGizmos() { - if (m_PositioningSpheres != null) { - Gizmos.color = Color.yellow; - for (int i = 0; i < m_PositioningSpheres.Length; ++i) { - Gizmos.DrawWireSphere(transform.TransformPoint(m_PositioningSpheres[i]), - m_ScaledPositioningSphereRadius); - } - } - if (WidgetSibling != null) { - Gizmos.color = Color.red; - Gizmos.DrawWireSphere(transform.position, WidgetSibling.m_CollisionRadius); - } - } - /**/ - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Settings; +using TMPro; + +namespace TiltBrush +{ + + [System.Serializable] + public struct PopupMapKey + { + public GameObject m_PopUpPrefab; + public SketchControlsScript.GlobalCommands m_Command; + } + + public class BasePanel : MonoBehaviour + { + static private float GAZE_DISTANCE = 10 * App.METERS_TO_UNITS; + static private float LARGE_DISTANCE = 9999.0f * App.METERS_TO_UNITS; + + // Types etc + + protected class TextSpring + { + public float m_CurrentAngle; + public float m_DesiredAngle; + public float m_Velocity; + + public void Update(float fK, float fDampen) + { + float fToDesired = m_DesiredAngle - m_CurrentAngle; + fToDesired *= fK; + float fDampenedVel = m_Velocity * fDampen; + float fSpringForce = fToDesired - fDampenedVel; + m_Velocity += fSpringForce; + m_CurrentAngle += (m_Velocity * Time.deltaTime); + } + } + + protected enum DescriptionState + { + Open, + Closing, + Closed + } + + protected enum PanelState + { + Unused, + Unavailable, + Available, + } + + // These names are used in our player prefs, so they must be protected from obfuscation + // Do not change the names of any of them, unless they've never been released. + [Serializable] + public enum PanelType + { + SketchSurface, + Color, + Brush, + AudioReactor, + AdminPanelMobile, + ToolsBasicMobile, + ToolsBasic, + Experimental, + ToolsAdvancedMobile, + MemoryWarning, + Labs, + Sketchbook, + SketchbookMobile, + BrushMobile, + AppSettings, + Tutorials, + Reference, + Lights, + GuideTools, + Environment, + Camera, + Testing, + Icosa, + BrushExperimental, + ToolsAdvanced, + AppSettingsMobile, + AdminPanel, + ExtraPanel, + ExtraMobile, + IcosaMobile, + LabsMobile, + ReferenceMobile, + CameraPath, + BrushLab, + WebcamPanel = 5200, + Scripts = 6000, + SnapSettings = 8000, + StencilSettings = 20200, + LayersPanel = 15000, + TransformPanel = 12000, + } + + private enum FixedTransitionState + { + Floating, + FixedToFloating, + FloatingToFixed, + Fixed + } + + // Inspector data, tunables + [SerializeField] protected PanelType m_PanelType; + + [SerializeField] protected Collider m_Collider; + [SerializeField] public GameObject m_Mesh; + [SerializeField] protected Renderer m_Border; + [SerializeField] protected Collider m_MeshCollider; + [SerializeField] protected Vector3 m_ParticleBounds; + + [SerializeField] protected PopupMapKey[] m_PanelPopUpMap; + [SerializeField] protected string m_PanelDescription; + [SerializeField] protected LocalizedString m_LocalizedPanelDescription; + + public string PanelDescription + { + get + { + try + { + var locString = m_LocalizedPanelDescription.GetLocalizedStringAsync().Result; + return locString; + } + catch + { + return m_PanelDescription; + } + } + } + + [SerializeField] protected GameObject m_PanelDescriptionPrefab; + + [SerializeField] protected Vector3 m_PanelDescriptionOffset; + [SerializeField] protected Color m_PanelDescriptionColor; + + [SerializeField] protected GameObject m_PanelFlairPrefab; + [SerializeField] protected Vector3 m_PanelFlairOffset; + + [SerializeField] protected float m_DescriptionSpringK = 4.0f; + [SerializeField] protected float m_DescriptionSpringDampen = 0.2f; + [SerializeField] protected float m_DescriptionClosedAngle = -90.0f; + [SerializeField] protected float m_DescriptionOpenAngle = 0.0f; + [SerializeField] protected float m_DescriptionAlphaDistance = 90.0f; + + [SerializeField] protected GameObject[] m_Decor; + + [SerializeField] protected float m_GazeHighlightScaleMultiplier = 1.2f; + + [SerializeField] private float m_BorderMeshWidth = 0.02f; + [SerializeField] private float m_BorderMeshAdvWidth = 0.01f; + + [SerializeField] public float m_PanelSensitivity = 0.1f; + [SerializeField] protected bool m_ClampToBounds = false; + [SerializeField] protected Vector3 m_ReticleBounds; + + [SerializeField] public float m_BorderSphereHighlightRadius; + [SerializeField] protected Vector2 m_PositioningSpheresBounds; + [SerializeField] protected float m_PositioningSphereRadius = 0.4f; + + [SerializeField] public bool m_UseGazeRotation = false; + [SerializeField] public float m_MaxGazeRotation = 20.0f; + [SerializeField] protected float m_GazeActivateSpeed = 8.0f; + + [SerializeField] public Vector3 m_InitialSpawnPos; + [SerializeField] public Vector3 m_InitialSpawnRotEulers; + + [SerializeField] public float m_WandAttachAngle; + [SerializeField] public float m_WandAttachYOffset; + [SerializeField] public float m_WandAttachHalfHeight; + [SerializeField] private bool m_BeginFixed; + [SerializeField] private bool m_CanBeFixedToWand = true; + [SerializeField] private bool m_CanBeDetachedFromWand = true; + + [SerializeField] private float m_PopUpGazeDuration = .2f; + + [SerializeField] protected MeshRenderer[] m_PromoBorders; + + protected const float m_SwipeThreshold = 0.4f; + protected Material m_BorderMaterial; + + // Public, mutable data + + [NonSerialized] public float m_SweetSpotDistance = 1.0f; + [NonSerialized] public bool m_Fixed; + [NonSerialized] public bool m_WandPrimedForAttach; + [NonSerialized] public float m_WandAttachRadiusAdjust; + [NonSerialized] public float m_WandAttachYOffset_Target; + // This is used to store the Y offset of a fixed panel at the point where the user begins + // interacting with a panelWidget. Storing this value allows modifications to the panes to be + // undone. When the user ends interaction, _Stable is updated to be the _Target. + [NonSerialized] public float m_WandAttachYOffset_Stable; + + // Internal data + + protected PopUpWindow m_ActivePopUp; + protected SketchControlsScript.GlobalCommands m_DelayedCommand; + protected int m_DelayedCommandParam; + protected int m_DelayedCommandParam2; + + protected GameObject m_PanelDescriptionObject; + protected Renderer m_PanelDescriptionRenderer; + protected TextMeshPro m_PanelDescriptionTextMeshPro; + + protected Vector3 m_BaseScale; + protected float m_AdjustedScale; + + protected TextSpring m_PanelDescriptionTextSpring; + protected TextSpring m_PanelFlairSpring; + protected PanelFlair m_PanelFlair; + + protected Vector3 m_ReticleOffset; + protected Vector3 m_Bounds; + protected Vector3 m_WorkingReticleBounds; + + protected DescriptionState m_PanelDescriptionState; + protected DescriptionState m_PanelFlairState; + protected float m_CloseAngleThreshold; + + protected PanelState m_CurrentState; + protected PanelState m_DesiredState; + protected bool m_GazeActive; + protected bool m_GazeWasActive; + protected bool m_GazeDescriptionsActive; + + protected bool m_InputValid; + protected bool m_EatInput; + protected Ray m_ReticleSelectionRay; + + protected float m_PositioningPercent; + + protected float m_MaxGazeOffsetDistance; + protected Vector3 m_GazeRotationAxis; + protected float m_GazeRotationAngle; + protected Quaternion m_GazeRotationAmount; + protected float m_GazeActivePercent; + + protected UIComponentManager m_UIComponentManager; + protected NonScaleChild m_NonScaleChild; // potentially null + + // Private + + private List m_DecorRenderers; + private List m_DecorTextMeshes; + + private float m_ScaledPositioningSphereRadius; + private float m_PositioningExtent; + private Vector3[] m_PositioningSpheres; + private Vector3[] m_PositioningSpheresTransformed; + + private Vector3 m_PositioningHome; + private float m_PositioningK = 0.5f; + private float m_PositioningDampen = 0.1f; + private float m_DepenetrationScalar = 200.0f; + private Vector3 m_PositioningVelocity; + + private Vector3 m_GazeHitPositionCurrent; + private Vector3 m_GazeHitPositionDesired; + private float m_GazeHitPositionSpeed = 10.0f; + + protected Vector2 m_SwipeRecentMotion = Vector2.zero; + + private FixedTransitionState m_TransitionState; + private TrTransform m_WandTransitionTarget; + private static float m_WandTransitionDuration = .25f; + private float m_WandTransitionPercent; // 0 = on wand, 1 = at target + private float m_WandAttachAdjustSpeed = 16.0f; + private float m_WandAttachYOffset_Initial; + private float m_WandAttachAngle_Initial; + private PanelWidget m_WidgetSibling; + + private GameObject m_TempPopUpCollider; + private float m_PopUpGazeTimer; + + private bool m_AdvancedModePanel; + private bool m_CurrentlyVisibleInAdvancedMode; + + // TODO: The following are just to track down an elusive NRE. Delete when we've figured + // it out. + private bool m_PanelInitializationStarted; + private bool m_PanelInitializationFinished; + private float m_PanelDescriptionCounter; + public Action m_OverrideControllerMaterial; + + // Accessors/properties + + public Vector3 GetBounds() { return m_Bounds; } + public float GetPositioningExtent() { return m_PositioningExtent; } + public bool IsAvailable() { return m_CurrentState != PanelState.Unavailable; } + public bool IsActive() { return m_GazeActive; } + public bool BeginFixed { get { return m_BeginFixed; } } + public void ClearBeginFixed() { m_BeginFixed = false; } + public bool CanBeDetached { get { return m_CanBeDetachedFromWand; } } + public bool IsInInitialPosition() + { + return m_WandAttachYOffset == m_WandAttachYOffset_Initial && + m_WandAttachAngle == m_WandAttachAngle_Initial; + } + public PanelWidget WidgetSibling { get { return m_WidgetSibling; } } + public bool AdvancedModePanel { get { return m_AdvancedModePanel; } } + public bool CurrentlyVisibleInAdvancedMode { get { return m_CurrentlyVisibleInAdvancedMode; } } + public Vector3 ParticleBounds { get { return m_ParticleBounds; } } + public PopUpWindow PanelPopUp { get { return m_ActivePopUp; } } + + public Color GetGazeColorFromActiveGazePercent() + { + PanelManager pm = PanelManager.m_Instance; + return Color.Lerp(pm.PanelHighlightInactiveColor, pm.PanelHighlightActiveColor, + m_GazeActivePercent); + } + + public PanelType Type + { + get { return m_PanelType; } + } + + public virtual bool ShouldRegister { get { return true; } } + + public void InitAdvancedFlag(bool advanced) + { + m_AdvancedModePanel = advanced; + m_CurrentlyVisibleInAdvancedMode = m_BeginFixed; + } + + public float PopUpOffset + { + get { return m_ActivePopUp ? m_ActivePopUp.GetPopUpForwardOffset() : 0; } + } + + virtual protected void CalculateBounds() + { + Vector3 vCurrentScale = transform.localScale; + m_Bounds = m_ReticleBounds; + m_Bounds.x *= vCurrentScale.x * 0.5f; + m_Bounds.y *= vCurrentScale.y * 0.5f; + m_Bounds.z = 0.0f; + + if (m_ActivePopUp == null) + { + m_WorkingReticleBounds = m_Bounds; + } + else + { + Vector3 vPopUpCurrentScale = m_ActivePopUp.transform.localScale; + m_WorkingReticleBounds = m_ActivePopUp.GetReticleBounds(); + m_WorkingReticleBounds.x *= vPopUpCurrentScale.x * 0.5f; + m_WorkingReticleBounds.y *= vPopUpCurrentScale.y * 0.5f; + } + } + + public void ActivatePromoBorder(bool activate) + { + if (m_WidgetSibling) + { + if (activate) + { + m_WidgetSibling.RemovePromoMeshesFromTintable(m_PromoBorders); + } + else + { + m_WidgetSibling.AddPromoMeshesToTintable(m_PromoBorders); + } + } + foreach (var m in m_PromoBorders) + { + m.material = activate ? PromoManager.m_Instance.SharePromoMaterial : m_BorderMaterial; + } + } + + virtual public void SetInIntroMode(bool inIntro) { } + + public void SetPositioningPercent(float fPercent) + { + m_PositioningPercent = fPercent; + } + + public void SetScale(float fScale) + { + m_AdjustedScale = fScale; + transform.localScale = m_BaseScale * m_AdjustedScale; + } + + public bool PanelIsUsed() { return m_CurrentState != PanelState.Unused; } + + public Collider GetCollider() { return m_Collider; } + + public bool HasMeshCollider() + { + return m_MeshCollider != null; + } + + virtual public bool RaycastAgainstMeshCollider(Ray rRay, out RaycastHit rHitInfo, float fDist) + { + rHitInfo = new RaycastHit(); + bool bReturnValue = false; + + // Creating a popup when a popup exists will generate a temp collider. This is to prevent + // forced, accidental out of bounds user input. + if (m_TempPopUpCollider != null) + { + BoxCollider tempCollider = m_TempPopUpCollider.GetComponent(); + bReturnValue = tempCollider.Raycast(rRay, out rHitInfo, fDist); + if (!bReturnValue) + { + // If we're ever not pointing at the temp collider, destroy it. + Destroy(m_TempPopUpCollider); + m_TempPopUpCollider = null; + } + } + + // If we've got a pop-up, check collision against that guy first. + if (m_ActivePopUp != null) + { + var collider = m_ActivePopUp.GetCollider(); + if (collider == null) { throw new InvalidOperationException("No popup collider"); } + bReturnValue = bReturnValue || + m_ActivePopUp.GetCollider().Raycast(rRay, out rHitInfo, fDist); + } + + // Check custom colliders on components. + bReturnValue = bReturnValue || + m_UIComponentManager.RaycastAgainstCustomColliders(rRay, out rHitInfo, fDist); + + // If we didn't have a pop-up, or collision failed, default to base mesh. + if (m_MeshCollider == null) { throw new InvalidOperationException("No mesh collider"); } + bReturnValue = bReturnValue || m_MeshCollider.Raycast(rRay, out rHitInfo, fDist); + return bReturnValue; + } + + virtual public void AssignControllerMaterials(InputManager.ControllerName controller) + { + m_UIComponentManager.AssignControllerMaterials(controller); + + // Allows components to override the regular controller material + // without worrying about execution order etc + if (m_OverrideControllerMaterial != null) + { + m_OverrideControllerMaterial(); + // Clear afterwards. This needs to be set every frame + m_OverrideControllerMaterial = null; + } + } + + /// This function is used to determine the value to be passed in to the controller pad mesh's + /// shader for visualizing swipes and other actions. + /// It is called by SketchControls when the panel is in focus. + public float GetControllerPadShaderRatio(InputManager.ControllerName controller) + { + return m_UIComponentManager.GetControllerPadShaderRatio(controller); + } + + public bool BrushPadAnimatesOnHover() + { + return m_UIComponentManager.BrushPadAnimatesOnAnyHover(); + } + + public bool UndoRedoBlocked() + { + // This function could be generalized to allow specific UIComponents to block undo/redo, + // but we only need it in a single case right now, so it's a bit more specific. + return m_ActivePopUp == null ? false : m_ActivePopUp.BlockUndoRedo(); + } + + virtual public void OnPanelMoved() { } + + virtual protected void Awake() + { + m_WandAttachYOffset_Initial = m_WandAttachYOffset; + m_WandAttachAngle_Initial = m_WandAttachAngle; + m_WandAttachYOffset_Target = m_WandAttachYOffset; + } + + // Happens at App Start() time. + virtual public void InitPanel() + { + m_PanelInitializationStarted = true; + m_BorderMaterial = m_Border.material; + m_BaseScale = transform.localScale; + m_AdjustedScale = 1.0f; + + CalculateBounds(); + m_NonScaleChild = GetComponent(); + + if (m_PanelDescriptionPrefab != null) + { + m_PanelDescriptionObject = (GameObject)Instantiate(m_PanelDescriptionPrefab); + m_PanelDescriptionObject.transform.position = m_Mesh.transform.position; + m_PanelDescriptionObject.transform.rotation = m_Mesh.transform.rotation; + m_PanelDescriptionObject.transform.SetParent(transform); + Vector3 vScale = m_PanelDescriptionObject.transform.localScale; + vScale *= transform.localScale.x; + m_PanelDescriptionObject.transform.localScale = vScale; + + m_PanelDescriptionRenderer = m_PanelDescriptionObject.GetComponent(); + if (m_PanelDescriptionRenderer) + { + m_PanelDescriptionRenderer.enabled = false; + } + + m_PanelDescriptionTextMeshPro = m_PanelDescriptionObject.GetComponent(); + if (m_PanelDescriptionTextMeshPro) + { + m_PanelDescriptionTextMeshPro.text = PanelDescription; + m_PanelDescriptionTextMeshPro.color = m_PanelDescriptionColor; + } + LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged; + } + + if (m_PanelFlairPrefab != null) + { + GameObject go = (GameObject)Instantiate(m_PanelFlairPrefab); + m_PanelFlair = go.GetComponent(); + m_PanelFlair.ParentToPanel(transform, m_PanelFlairOffset); + } + + m_PanelDescriptionState = DescriptionState.Closing; + m_PanelFlairState = DescriptionState.Closing; + + m_CloseAngleThreshold = Mathf.Abs(m_DescriptionOpenAngle - m_DescriptionClosedAngle) * 0.01f; + + m_PanelDescriptionTextSpring = new TextSpring(); + m_PanelDescriptionTextSpring.m_CurrentAngle = m_DescriptionClosedAngle; + m_PanelDescriptionTextSpring.m_DesiredAngle = m_DescriptionClosedAngle; + m_PanelFlairSpring = new TextSpring(); + m_PanelFlairSpring.m_CurrentAngle = m_DescriptionClosedAngle; + m_PanelFlairSpring.m_DesiredAngle = m_DescriptionClosedAngle; + + PanelManager pm = PanelManager.m_Instance; + m_Border.material.SetColor("_Color", pm.PanelHighlightInactiveColor); + + m_DecorRenderers = new List(); + m_DecorTextMeshes = new List(); + + if (m_Decor.Length > 0) + { + // Cache all decor renderers. + for (int i = 0; i < m_Decor.Length; ++i) + { + Renderer[] aChildRenderers = m_Decor[i].GetComponentsInChildren(); + for (int j = 0; j < aChildRenderers.Length; ++j) + { + // Prefer to cache a text mesh pro object if we find one. + TextMeshPro tmp = aChildRenderers[j].GetComponent(); + if (tmp) + { + m_DecorTextMeshes.Add(tmp); + } + else + { + // Otherwise, the standard renderer will do. + m_DecorRenderers.Add(aChildRenderers[j]); + m_DecorRenderers[m_DecorRenderers.Count - 1].material.SetColor("_Color", + pm.PanelHighlightInactiveColor); + } + } + } + } + + m_CurrentState = PanelState.Available; + m_DesiredState = PanelState.Available; + m_GazeActive = false; + m_PositioningPercent = 0.0f; + + if (m_PositioningSpheresBounds.x > 0.0f && m_PositioningSpheresBounds.y > 0.0f && m_PositioningSphereRadius > 0.0f) + { + Vector2 vSphereBounds = m_PositioningSpheresBounds; + vSphereBounds.x -= m_PositioningSphereRadius * 0.5f; + vSphereBounds.y -= m_PositioningSphereRadius * 0.5f; + m_ScaledPositioningSphereRadius = m_PositioningSphereRadius * transform.localScale.x; + + int iNumSpheresWidth = Mathf.CeilToInt((vSphereBounds.x * 2.0f) / m_PositioningSphereRadius); + int iNumSpheresHeight = Mathf.CeilToInt((vSphereBounds.y * 2.0f) / m_PositioningSphereRadius); + int iTotalNumSpheres = (iNumSpheresWidth * 2) + ((iNumSpheresHeight - 2) * 2); + m_PositioningSpheres = new Vector3[iTotalNumSpheres]; + + float fXInterval = (vSphereBounds.x / (float)(iNumSpheresWidth - 1)) * 2.0f; + float fYInterval = (vSphereBounds.y / (float)(iNumSpheresHeight - 1)) * 2.0f; + for (int i = 0; i < iNumSpheresWidth; ++i) + { + float fX = -vSphereBounds.x + (fXInterval * (float)i); + m_PositioningSpheres[i].Set(fX, -vSphereBounds.y, 0.0f); + } + for (int i = 0; i < iNumSpheresHeight - 2; ++i) + { + float fY = -vSphereBounds.y + (fYInterval * (float)(i + 1)); + m_PositioningSpheres[iNumSpheresWidth + (i * 2)].Set(-vSphereBounds.x, fY, 0.0f); + m_PositioningSpheres[iNumSpheresWidth + (i * 2) + 1].Set(vSphereBounds.x, fY, 0.0f); + } + for (int i = iTotalNumSpheres - iNumSpheresWidth; i < iTotalNumSpheres; ++i) + { + int iIndex = i - (iTotalNumSpheres - iNumSpheresWidth); + float fX = -vSphereBounds.x + (fXInterval * (float)iIndex); + m_PositioningSpheres[i].Set(fX, vSphereBounds.y, 0.0f); + } + + m_PositioningSpheresTransformed = new Vector3[m_PositioningSpheres.Length]; + for (int i = 0; i < m_PositioningSpheresTransformed.Length; ++i) + { + m_PositioningSpheresTransformed[i] = new Vector3(); + m_PositioningExtent = Mathf.Max(m_PositioningExtent, Mathf.Max(m_PositioningSpheres[i].x, m_PositioningSpheres[i].y)); + } + m_PositioningExtent += (m_PositioningSphereRadius * 2.0f); + } + + m_MaxGazeOffsetDistance = m_Bounds.magnitude; + m_GazeRotationAmount = Quaternion.identity; + + m_UIComponentManager = GetComponent(); + m_UIComponentManager.SetColor(pm.PanelHighlightInactiveColor); + + m_WidgetSibling = GetComponent(); + if (m_Fixed) + { + m_TransitionState = FixedTransitionState.Fixed; + } + + // Bake border meshs. + float width = !m_AdvancedModePanel ? m_BorderMeshAdvWidth : m_BorderMeshWidth; + Color baseCol = !m_AdvancedModePanel ? pm.PanelBorderMeshOutlineColor : pm.PanelBorderMeshBaseColor; + Color outlineCol = !m_AdvancedModePanel ? pm.PanelBorderMeshBaseColor : pm.PanelBorderMeshOutlineColor; + BakedMeshOutline[] bakeries = GetComponentsInChildren(true); + BakedMeshOutline borderBakery = m_Border.GetComponent(); + for (int i = 0; i < bakeries.Length; ++i) + { + if (bakeries[i] == borderBakery) + { + // The border is the only bakery that gets custom treatment. + bakeries[i].Bake(baseCol, outlineCol, width); + } + else + { + bakeries[i].Bake(pm.PanelBorderMeshBaseColor, pm.PanelBorderMeshOutlineColor, m_BorderMeshWidth); + } + } + + m_PanelInitializationFinished = true; + } + + public void CloseActivePopUp(bool force) + { + if (m_ActivePopUp != null) + { + if (m_ActivePopUp.RequestClose(force)) + { + InvalidateIfActivePopup(m_ActivePopUp); + } + } + } + + public void VerifyStateForFloating() + { + m_TransitionState = FixedTransitionState.Floating; + m_WandTransitionPercent = 1; + } + + public void SetPanelStableToTarget() + { + m_WandAttachYOffset_Stable = m_WandAttachYOffset_Target; + } + + public void ResetPanelToInitialPosition() + { + m_WandAttachYOffset = m_WandAttachYOffset_Initial; + m_WandAttachYOffset_Target = m_WandAttachYOffset_Initial; + m_WandAttachYOffset_Stable = m_WandAttachYOffset_Initial; + m_WandAttachAngle = m_WandAttachAngle_Initial; + + if (m_TransitionState != FixedTransitionState.Fixed) + { + m_Fixed = true; + m_TransitionState = FixedTransitionState.Fixed; + m_WandTransitionPercent = 0; + } + } + + public void ResetPanel() + { + // Get rid of the popup as fast as possible. + if (m_ActivePopUp != null) + { + Destroy(m_ActivePopUp.gameObject); + InvalidateIfActivePopup(m_ActivePopUp); + } + Destroy(m_TempPopUpCollider); + m_TempPopUpCollider = null; + + //reset gaze animations + m_PanelDescriptionState = DescriptionState.Closed; + if (m_PanelDescriptionRenderer) + { + m_PanelDescriptionRenderer.enabled = false; + } + + m_PanelFlairState = DescriptionState.Closed; + if (m_PanelFlair != null) + { + m_PanelFlair.Hide(); + } + + UpdatePanelColor(PanelManager.m_Instance.PanelHighlightInactiveColor); + + m_GazeActive = false; + m_GazeActivePercent = 0.0001f; + m_GazeRotationAxis = Vector3.zero; + } + + public void InvalidateIfActivePopup(PopUpWindow activePopup) + { + // If this popup isn't the active one, don't bother. + if (activePopup == m_ActivePopUp) + { + m_ActivePopUp = null; + // Eat input when we close a popup so the close action doesn't carry over. + m_EatInput = true; + } + } + + private void OnSelectedLocaleChanged(Locale locale) + { + if (m_PanelDescriptionTextMeshPro) + { + m_PanelDescriptionTextMeshPro.text = PanelDescription; + } + } + + private void OnDestroy() + { + LocalizationSettings.SelectedLocaleChanged -= OnSelectedLocaleChanged; + } + + void OnEnable() + { + OnEnablePanel(); + } + + void OnDisable() + { + OnDisablePanel(); + } + + virtual protected void OnEnablePanel() + { + if (PanelManager.m_Instance != null) + { + if (PanelManager.m_Instance.AdvancedModeActive()) + { + m_CurrentlyVisibleInAdvancedMode = true; + } + } + } + + virtual protected void OnDisablePanel() + { + if (PanelManager.m_Instance != null) + { + if (PanelManager.m_Instance.AdvancedModeActive()) + { + m_CurrentlyVisibleInAdvancedMode = false; + } + } + } + + public void ResetReticleOffset() + { + m_ReticleOffset = Vector3.zero; + } + + public void UpdateReticleOffset(float fXDelta, float fYDelta) + { + m_ReticleOffset.x += fXDelta * m_PanelSensitivity; + m_ReticleOffset.y += fYDelta * m_PanelSensitivity; + + if (m_ClampToBounds) + { + m_ReticleOffset.x = Mathf.Clamp(m_ReticleOffset.x, -m_WorkingReticleBounds.x, m_WorkingReticleBounds.x); + m_ReticleOffset.y = Mathf.Clamp(m_ReticleOffset.y, -m_WorkingReticleBounds.y, m_WorkingReticleBounds.y); + m_ReticleOffset.z = m_WorkingReticleBounds.z; + } + } + + // Given a position that has been proven to be a hit point on the panel's collider and the cast + // direction that resulted in that point, determine where the reticle should be located. + // Used by wand panel method of interacting with panels. + virtual public void GetReticleTransformFromPosDir(Vector3 vInPos, Vector3 vInDir, out Vector3 vOutPos, out Vector3 vForward) + { + //by default, the collision point is ok, and the reticle's forward should be the same as the mesh + vOutPos = vInPos; + vForward = -m_Mesh.transform.forward; + + Vector3 dir = Vector3.forward; + Ray rCastRay = new Ray(vInPos - vInDir * 0.5f, vInDir); + + // If we have a ghost popup, collide with that to find our position. + if (m_TempPopUpCollider != null) + { + RaycastHit rHitInfo; + if (DoesRayHitCollider(rCastRay, m_TempPopUpCollider.GetComponent(), out rHitInfo)) + { + vOutPos = rHitInfo.point; + } + } + + // Override default and ghost with standard popup collision. + if (m_ActivePopUp != null) + { + m_ActivePopUp.CalculateReticleCollision(rCastRay, ref vOutPos, ref vForward); + } + else + { + // PopUps trump UIComponents, so if there isn't a PopUp, check against all UIComponents. + m_UIComponentManager.CalculateReticleCollision(rCastRay, ref vOutPos, ref vForward); + } + } + + // TODO : This is currently broken. Needs to be updated to use new + // m_UIComponentManager.CalculateReticleCollision style. + // Using m_ReticleOffset, determine where the reticle should be located. + // Used by gaze method of interacting with panels. + virtual public void GetReticleTransform(out Vector3 vPos, out Vector3 vForward, bool bGazeAndTap) + { + // For ViewingOnly controls, position the reticle at the gaze panel position + if (bGazeAndTap) + { + // Calculate plane intersection + Transform head = ViewpointScript.Head; + Ray ray = new Ray(head.position, head.forward); + RaycastHit rHitInfo; + if (RaycastAgainstMeshCollider(ray, out rHitInfo, GAZE_DISTANCE)) + { + vPos = rHitInfo.point; + } + else + { + vPos = new Vector3(LARGE_DISTANCE, LARGE_DISTANCE, LARGE_DISTANCE); + } + } + else + { + Vector3 vTransformedOffset = m_Mesh.transform.rotation * m_ReticleOffset; + vPos = transform.position + vTransformedOffset; + } + vForward = -m_Mesh.transform.forward; + } + + // This function should only be used when the app state changes in a way that requires + // an out-of-focus panel's visuals to become reflect a stale state. + virtual public void ForceUpdatePanelVisuals() + { + m_UIComponentManager.UpdateVisuals(); + } + + void Update() + { + BaseUpdate(); + } + + protected void BaseUpdate() + { + UpdateState(); + CalculateBounds(); + UpdateDescriptions(); + UpdateGazeBehavior(); + UpdateFixedTransition(); + } + + protected void UpdateState() + { + //check if we're switching activity + if (m_GazeWasActive != m_GazeActive) + { + if (m_GazeActive) + { + AudioManager.m_Instance.ActivatePanel(true, transform.position); + } + else + { + m_UIComponentManager.ResetInput(); + AudioManager.m_Instance.ActivatePanel(false, transform.position); + } + + m_GazeWasActive = m_GazeActive; + OnUpdateActive(); + } + + bool bGazeDescriptionsWereActive = m_GazeDescriptionsActive; + m_GazeDescriptionsActive = m_GazeActive; + + // Update components if gaze is active. + if (m_GazeDescriptionsActive) + { + m_UIComponentManager.UpdateVisuals(); + } + + //check if we're switching descriptions showing + if (bGazeDescriptionsWereActive != m_GazeDescriptionsActive) + { + if (m_GazeDescriptionsActive) + { + if (m_PanelDescriptionObject) + { + m_PanelDescriptionState = DescriptionState.Open; + m_PanelDescriptionTextSpring.m_DesiredAngle = m_DescriptionOpenAngle; + m_PanelDescriptionRenderer.enabled = true; + } + } + else + { + if (m_PanelDescriptionObject) + { + m_PanelDescriptionState = DescriptionState.Closing; + m_PanelDescriptionTextSpring.m_DesiredAngle = m_DescriptionClosedAngle; + ResetPanelFlair(); + } + m_UIComponentManager.ManagerLostFocus(); + m_UIComponentManager.Deactivate(); + if (m_ActivePopUp != null) + { + if (m_ActivePopUp.RequestClose()) + { + InvalidateIfActivePopup(m_ActivePopUp); + } + } + } + } + + //update state + m_CurrentState = m_DesiredState; + } + + virtual protected void OnUpdateActive() + { + } + + void UpdateMeshRotation() + { + m_Mesh.transform.rotation = m_GazeRotationAmount * transform.rotation; + } + + protected void UpdateDescriptions() + { + try + { + m_PanelDescriptionCounter = 0; + if (m_PanelDescriptionState != DescriptionState.Closed && m_PanelDescriptionObject != null) + { + m_PanelDescriptionCounter = 1; + m_PanelDescriptionTextSpring.Update(m_DescriptionSpringK, m_DescriptionSpringDampen); + + m_PanelDescriptionCounter = 2; + Quaternion qOrient = Quaternion.Euler(0.0f, m_PanelDescriptionTextSpring.m_CurrentAngle, 0.0f); + m_PanelDescriptionObject.transform.rotation = m_Mesh.transform.rotation * qOrient; + + m_PanelDescriptionCounter = 3; + Vector3 vPanelDescriptionOffset = m_Bounds; + vPanelDescriptionOffset.x *= m_PanelDescriptionOffset.x; + vPanelDescriptionOffset.y *= m_PanelDescriptionOffset.y; + Vector3 vTransformedOffset = m_Mesh.transform.rotation * vPanelDescriptionOffset; + m_PanelDescriptionObject.transform.position = m_Mesh.transform.position + vTransformedOffset; + + m_PanelDescriptionCounter = 4; + float fDistToClosed = Mathf.Abs(m_PanelDescriptionTextSpring.m_CurrentAngle - m_DescriptionClosedAngle); + Color descColor = m_PanelDescriptionColor; + if (m_ActivePopUp != null) + { + descColor = Color.Lerp(m_PanelDescriptionColor, + PanelManager.m_Instance.PanelHighlightInactiveColor, + m_ActivePopUp.GetTransitionRatioForVisuals()); + } + float fRatio = fDistToClosed / m_DescriptionAlphaDistance; + descColor.a = Mathf.Min(fRatio * fRatio, 1.0f); + if (m_PanelDescriptionTextMeshPro) + { + m_PanelDescriptionTextMeshPro.color = descColor; + } + + m_PanelDescriptionCounter = 5; + if (m_PanelDescriptionState == DescriptionState.Closing) + { + float fToDesired = m_PanelDescriptionTextSpring.m_DesiredAngle - m_PanelDescriptionTextSpring.m_CurrentAngle; + if (Mathf.Abs(fToDesired) <= m_CloseAngleThreshold) + { + m_PanelDescriptionRenderer.enabled = false; + m_PanelDescriptionState = DescriptionState.Closed; + } + } + } + + m_PanelDescriptionCounter = 6; + if (m_PanelFlairState != DescriptionState.Closed) + { + m_PanelDescriptionCounter = 7; + m_PanelFlairSpring.Update(m_DescriptionSpringK, m_DescriptionSpringDampen); + float fDistToClosed = Mathf.Abs(m_PanelFlairSpring.m_CurrentAngle - m_DescriptionClosedAngle); + float fRatio = fDistToClosed / m_DescriptionAlphaDistance; + float fAlpha = Mathf.Min(fRatio * fRatio, 1.0f); + + m_PanelDescriptionCounter = 8; + Quaternion qOrient = m_Mesh.transform.rotation * Quaternion.Euler(0.0f, m_PanelFlairSpring.m_CurrentAngle, 0.0f); + + m_PanelDescriptionCounter = 9; + if (m_PanelFlair != null) + { + m_PanelFlair.UpdateAnimationOnPanel(m_Bounds, qOrient, fAlpha); + } + + m_PanelDescriptionCounter = 10; + if (m_PanelFlairState == DescriptionState.Closing) + { + float fToDesired = m_PanelFlairSpring.m_DesiredAngle - m_PanelFlairSpring.m_CurrentAngle; + if (Mathf.Abs(fToDesired) <= m_CloseAngleThreshold) + { + if (m_PanelFlair != null) + { + m_PanelFlair.Hide(); + } + + m_PanelFlairState = DescriptionState.Closed; + } + } + } + } + catch (Exception ex) + { + string message = string.Format("{0}: Init State({1}, {2}), Counter({3})", + ex.Message, + m_PanelInitializationStarted, + m_PanelInitializationFinished, + m_PanelDescriptionCounter); + throw new Exception(message); + } + } + + protected virtual void UpdateGazeBehavior() + { + if (m_UseGazeRotation && m_Mesh) + { + float fPrevPercent = m_GazeActivePercent; + float fPercentStep = m_GazeActivateSpeed * Time.deltaTime; + if (IsActive()) + { + m_GazeActivePercent = Mathf.Min(m_GazeActivePercent + fPercentStep, 1.0f); + } + else + { + m_GazeActivePercent = Mathf.Max(m_GazeActivePercent - fPercentStep, 0.0f); + } + + //don't bother updating static panels + if (fPrevPercent > 0.0f) + { + float fRotationAngle = m_GazeRotationAngle * m_GazeActivePercent * (1.0f - m_PositioningPercent); + m_GazeRotationAmount = Quaternion.AngleAxis(fRotationAngle, m_GazeRotationAxis); + UpdateMeshRotation(); + + Color rPanelColor = GetGazeColor(); + if (m_Border.material == m_BorderMaterial) + { + m_Border.material.SetColor("_Color", rPanelColor); + } + + if (fPrevPercent < 1.0f) + { + float fScaleMult = m_GazeActivePercent * (m_GazeHighlightScaleMultiplier - 1.0f); + Vector3 newScale = m_BaseScale * m_AdjustedScale * (1.0f + fScaleMult); + if (m_NonScaleChild != null) + { + m_NonScaleChild.globalScale = newScale; + } + else + { + transform.localScale = newScale; + } + } + + UpdatePanelColor(rPanelColor); + m_UIComponentManager.GazeRatioChanged(m_GazeActivePercent); + } + } + } + + public Color GetGazeColor() + { + PanelManager pm = PanelManager.m_Instance; + Color targetColor = pm.PanelHighlightActiveColor; + if (m_ActivePopUp != null) + { + targetColor = Color.Lerp(pm.PanelHighlightActiveColor, + pm.PanelHighlightInactiveColor, + m_ActivePopUp.GetTransitionRatioForVisuals()); + } + + float t = m_GazeActivePercent; + if (SketchControlsScript.m_Instance.AtlasIconTextures) + { + // If we're atlasing panel textures, only send 0 and 1 through. + t = Mathf.Floor(m_GazeActivePercent + 0.5f); + } + return Color.Lerp(pm.PanelHighlightInactiveColor, targetColor, t); + } + + protected void UpdateFixedTransition() + { + // State machine for panel exploding off of wand controller. + if (m_TransitionState == FixedTransitionState.FixedToFloating) + { + m_WandTransitionPercent += Time.deltaTime / m_WandTransitionDuration; + if (m_WandTransitionPercent >= 1.0f) + { + m_WandTransitionPercent = 1.0f; + m_TransitionState = FixedTransitionState.Floating; + } + PanelManager.m_Instance.UpdateWandTransitionXf( + this, m_WandTransitionTarget, m_WandTransitionPercent); + } + else if (m_TransitionState == FixedTransitionState.FloatingToFixed) + { + m_WandTransitionPercent -= Time.deltaTime / m_WandTransitionDuration; + if (m_WandTransitionPercent <= 0.0f) + { + m_WandTransitionPercent = 0.0f; + m_TransitionState = FixedTransitionState.Fixed; + } + PanelManager.m_Instance.UpdateWandTransitionXf( + this, m_WandTransitionTarget, m_WandTransitionPercent); + } + else if (WidgetSibling && + WidgetSibling.IsUserInteracting(InputManager.ControllerName.Brush)) + { + // If the user is interacting with this panel, lock to a pane if this panel allows it. + if (m_CanBeFixedToWand) + { + PanelManager.m_Instance.AttachHeldPanelToWand(this); + } + } + else if (m_TransitionState == FixedTransitionState.Fixed) + { + // Update attach height. + float fStep = m_WandAttachAdjustSpeed * Time.deltaTime; + float fToTarget = m_WandAttachYOffset_Target - m_WandAttachYOffset; + if (Mathf.Abs(fToTarget) < fStep) + { + m_WandAttachYOffset = m_WandAttachYOffset_Target; + } + else + { + m_WandAttachYOffset += fStep * Mathf.Sign(fToTarget); + } + m_WandAttachRadiusAdjust = 0.0f; + } + } + + public void WidgetSiblingBeginInteraction() + { + PanelManager.m_Instance.InitPanesForPanelAttach(m_Fixed); + + // Initialize primed flag to off-- it'll get enabled if the attachment checks are valid. + m_WandPrimedForAttach = false; + + if (m_Fixed) + { + m_Fixed = false; + m_TransitionState = FixedTransitionState.Floating; + m_WandTransitionPercent = 1; + } + } + + public void WidgetSiblingEndInteraction() + { + if (!m_Fixed) + { + PanelManager.m_Instance.ResetPaneVisuals(); + if (m_WandPrimedForAttach || !CanBeDetached) + { + m_Fixed = true; + m_TransitionState = FixedTransitionState.Fixed; + m_WandTransitionPercent = 0; + } + + if (!m_WandPrimedForAttach) + { + // If we're releasing this panel and it's not snapping to a pane, make sure the pane + // panels are reset back to their stable positions. + PanelManager.m_Instance.SetFixedPanelsToStableOffsets(); + } + else + { + // If this panel is snapping to a pane, update it's stable position so it's sorted + // correctly when we close the gaps. + SetPanelStableToTarget(); + } + m_WandTransitionTarget = TrTransform.FromTransform(transform); + WidgetSibling.SetActiveTintToShowError(false); + + PanelManager.m_Instance.ClosePanePanelGaps(); + } + } + + // Target is ignored if the panel is reattaching to the wand + public void TransitionToWand(bool bFixed, TrTransform target) + { + if (bFixed && m_TransitionState != FixedTransitionState.Fixed) + { + m_TransitionState = FixedTransitionState.FloatingToFixed; + } + else if (!bFixed && m_TransitionState != FixedTransitionState.Floating) + { + m_WandTransitionTarget = target; + m_TransitionState = FixedTransitionState.FixedToFloating; + } + } + + void UpdatePanelColor(Color rPanelColor) + { + // Set the appropriate dim value for all our UI components. + m_UIComponentManager.SetColor(rPanelColor); + + OnUpdateGazeBehavior(rPanelColor); + + for (int i = 0; i < m_DecorRenderers.Count; ++i) + { + m_DecorRenderers[i].material.SetColor("_Color", rPanelColor); + } + for (int i = 0; i < m_DecorTextMeshes.Count; ++i) + { + m_DecorTextMeshes[i].color = rPanelColor; + } + } + + virtual protected void OnUpdateGazeBehavior(Color rPanelColor) { } + + virtual public void PanelGazeActive(bool bActive) + { + m_GazeActive = bActive; + m_GazeHitPositionCurrent = transform.position; + m_UIComponentManager.ResetInput(); + } + + void SetPanelFlairText(string sText) + { + if (m_PanelFlair != null) + { + // Interpret any message as a good message. + if (m_PanelFlair.AlwaysShow) + { + m_PanelFlair.SetText(sText); + m_PanelFlair.Show(); + m_PanelFlairState = DescriptionState.Open; + m_PanelFlairSpring.m_DesiredAngle = m_DescriptionOpenAngle; + } + else + { + if (sText != null && !sText.Equals("")) + { + m_PanelFlair.SetText(sText); + m_PanelFlair.Show(); + m_PanelFlairState = DescriptionState.Open; + m_PanelFlairSpring.m_DesiredAngle = m_DescriptionOpenAngle; + } + else if (m_PanelFlairState != DescriptionState.Closed) + { + m_PanelFlairState = DescriptionState.Closing; + m_PanelFlairSpring.m_DesiredAngle = m_DescriptionClosedAngle; + } + } + } + } + + public void ResetPanelFlair() + { + if (m_PanelFlairState != DescriptionState.Closed) + { + m_PanelFlairState = DescriptionState.Closing; + m_PanelFlairSpring.m_DesiredAngle = m_DescriptionClosedAngle; + } + } + + virtual public void OnWidgetShowAnimStart() { } + + virtual public void OnWidgetShowAnimComplete() { } + + virtual public void OnWidgetHide() { } + + /// Accumulates dpad input, and returns nonzero for a discrete swipe action. + /// Return value is 1 for "backward" swipe moving a page forward + /// and -1 for "forward" swipe moving a page backward. + protected int AccumulateSwipe() + { + int direction = 0; + if (InputManager.m_Instance.IsBrushScrollActive()) + { + // If our delta is beyond our trigger threshold, report it. + float fDelta = InputManager.m_Instance.GetAdjustedBrushScrollAmount(); + if (IncrementMotionAndCheckForSwipe(fDelta)) + { + direction = (int)Mathf.Sign(m_SwipeRecentMotion.x) * -1; + m_SwipeRecentMotion.x = 0.0f; + } + } + else + { + m_SwipeRecentMotion.x = 0.0f; + } + + return direction; + } + + /// Virtual so panels can interpret m_SwipeRecentMotion however they'd like. + virtual public bool IncrementMotionAndCheckForSwipe(float fMotion) + { + m_SwipeRecentMotion.x += fMotion; + return Mathf.Abs(m_SwipeRecentMotion.x) > m_SwipeThreshold; + } + + /// Panels are updated by SketchControls when they have focus. + public void UpdatePanel(Vector3 vToPanel, Vector3 vHitPoint) + { + // Validate input and cache it for this update. + m_InputValid = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); + if (m_EatInput) + { + m_EatInput = m_InputValid; + m_InputValid = false; + } + + // Cache input ray for this update. + Vector3 vReticlePos = SketchControlsScript.m_Instance.GetUIReticlePos(); + m_ReticleSelectionRay = new Ray(vReticlePos - transform.forward, transform.forward); + + if (m_ActivePopUp != null) + { + var collider = m_ActivePopUp.GetCollider(); + if (collider == null) { throw new InvalidOperationException("No popup collider"); } + RaycastHit hitInfo; + + // If we're pointing at a popup, increment our gaze timer. + if (BasePanel.DoesRayHitCollider(m_ReticleSelectionRay, + m_ActivePopUp.GetCollider(), out hitInfo)) + { + if (m_ActivePopUp.IsOpen()) + { + m_PopUpGazeTimer += Time.deltaTime; + } + } + else if (!m_ActivePopUp.InputObjectHasFocus() && + (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate) || + m_PopUpGazeTimer > m_PopUpGazeDuration)) + { + // If we're not pointing at the popup and the user doesn't have focus on an element + // of the popup, dismiss the popup if we've pointed at it before, or there's input. + m_ActivePopUp.RequestClose(); + } + } + + if (m_UseGazeRotation) + { + m_GazeHitPositionDesired = vHitPoint; + Vector3 vCurrToDesired = m_GazeHitPositionDesired - m_GazeHitPositionCurrent; + float fStep = m_GazeHitPositionSpeed * Time.deltaTime; + if (vCurrToDesired.sqrMagnitude < fStep * fStep) + { + m_GazeHitPositionCurrent = m_GazeHitPositionDesired; + } + else + { + vCurrToDesired.Normalize(); + vCurrToDesired *= fStep; + m_GazeHitPositionCurrent += vCurrToDesired; + } + + Vector3 vToCenter = m_GazeHitPositionCurrent - GetCollider().transform.position; + float fOffsetDist = vToCenter.magnitude; + vToCenter.Normalize(); + m_GazeRotationAxis = Vector3.Cross(-vToPanel, vToCenter); + m_GazeRotationAxis.Normalize(); + m_GazeRotationAngle = (fOffsetDist / m_MaxGazeOffsetDistance) * m_MaxGazeRotation; + } + + // Update custom logic for panel. + OnUpdatePanel(vToPanel, vHitPoint); + + // Update UIComponents, with PopUps taking priority. + if (m_ActivePopUp) + { + if (!m_EatInput && m_PopUpGazeTimer > 0) + { + m_ActivePopUp.UpdateUIComponents(m_ReticleSelectionRay, m_InputValid, m_ActivePopUp.GetCollider()); + } + } + else + { + // TODO : I'm not convinced the 3rd parameter here should be the main collider. + // I think it might need to be the mesh collider. If so, the collider is only used in + // monoscopic mode and should be renamed appropriately. + m_UIComponentManager.UpdateUIComponents(m_ReticleSelectionRay, m_InputValid, GetCollider()); + + // If a popup was just spawned, clear our active UI component, as input will now be + // directed to the popup. + if (m_ActivePopUp) + { + m_UIComponentManager.ResetInput(); + } + + if (m_UIComponentManager.ActiveInputUIComponent == null) + { + SetPanelFlairText(null); + } + else + { + SetPanelFlairText(m_UIComponentManager.ActiveInputUIComponent.Description); + } + } + } + + /// Base level logic for updating panel. This should be called from any derived class. + virtual public void OnUpdatePanel(Vector3 vToPanel, Vector3 vHitPoint) { } + + public void InitForPanelMovement() + { + m_PositioningHome = transform.position; + m_PositioningVelocity.Set(0.0f, 0.0f, 0.0f); + m_WandAttachYOffset_Stable = m_WandAttachYOffset_Target; + } + + virtual public void PanelHasStoppedMoving() + { + m_GazeHitPositionCurrent = transform.position; + } + + public void CreatePopUp(SketchControlsScript.GlobalCommands rCommand, + int iCommandParam, int iCommandParam2, string sDelayedText = "", + Action delayedClose = null) + { + CreatePopUp(rCommand, iCommandParam, iCommandParam2, Vector3.zero, sDelayedText, delayedClose); + } + + public void CreatePopUp(SketchControlsScript.GlobalCommands rCommand, + int iCommandParam, int iCommandParam2, Vector3 vPopupOffset, string sDelayedText = "", + Action delayedClose = null) + { + bool bPopUpExisted = false; + Vector3 vPrevPopUpPos = Vector3.zero; + // If we've got an active popup, send it packing. + if (m_ActivePopUp != null) + { + // Create a copy of the collider so we don't pull the rug out from under the user. + m_TempPopUpCollider = m_ActivePopUp.DuplicateCollider(); + vPrevPopUpPos = m_ActivePopUp.transform.position; + CloseActivePopUp(false); + bPopUpExisted = true; + } + + if (m_ActivePopUp == null) + { + // Look for the appropriate popup for this command. + int iPopUpIndex = -1; + for (int i = 0; i < m_PanelPopUpMap.Length; ++i) + { + if (m_PanelPopUpMap[i].m_Command == rCommand) + { + iPopUpIndex = i; + break; + } + } + + if (iPopUpIndex >= 0) + { + Vector3 position = vPopupOffset; + CreatePopUp( + m_PanelPopUpMap[iPopUpIndex].m_PopUpPrefab, position, + bPopUpExisted, bPopUpExisted, iCommandParam, + iCommandParam2, rCommand, sDelayedText, delayedClose + ); + } + } + } + + public void CreatePopUp( + GameObject prefab, Vector3 position, + bool explicitPosition, bool transition, int iCommandParam = -1, int iCommandParam2 = -1, + SketchControlsScript.GlobalCommands delayedCommand = SketchControlsScript.GlobalCommands.Null, + string sDelayedText = "", Action delayedClose = null) + { + // Create a new popup. + GameObject popUp = Instantiate(prefab, + m_Mesh.transform.position, m_Mesh.transform.rotation); + m_ActivePopUp = popUp.GetComponent(); + + if (explicitPosition) + { + popUp.transform.position = position; + } + else + { + // Treat position as an offset + popUp.transform.position = m_Mesh.transform.position + + (m_Mesh.transform.forward * m_ActivePopUp.GetPopUpForwardOffset()) + + m_Mesh.transform.TransformVector(position); + } + + popUp.transform.parent = m_Mesh.transform; + m_ActivePopUp.Init(gameObject, sDelayedText); + m_ActivePopUp.SetPopupCommandParameters(iCommandParam, iCommandParam2); + + m_ActivePopUp.m_OnClose += delayedClose; + m_PopUpGazeTimer = 0; + m_EatInput = !m_ActivePopUp.IsLongPressPopUp(); + + // If we closed a popup to create this one, we wan't don't want the standard visual + // fade that happens when a popup comes in. We're spoofing the transition value + // for visuals to avoid a pop. + if (!transition) + { + m_ActivePopUp.SpoofTransitionValue(); + } + + // Cache the intended command. + m_DelayedCommand = delayedCommand; + m_DelayedCommandParam = iCommandParam; + m_DelayedCommandParam2 = iCommandParam2; + + } + + public void PositionPopUp(Vector3 basePos) + { + m_ActivePopUp.transform.position = basePos; + } + + public void ResolveDelayedButtonCommand(bool bConfirm, bool bKeepOpen = false) + { + PopUpWindow currentPopup = m_ActivePopUp; + if (bConfirm) + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + m_DelayedCommand, m_DelayedCommandParam, m_DelayedCommandParam2); + } + // if the popup has changed since the start of the function (and it wasn't null at the start), + // that means that the global command called kicked off a new popup. In which case we shouldn't + // close the popup, or overwrite the delayed command. + bool panelChanged = (currentPopup != null) && (m_ActivePopUp != currentPopup); + if (!panelChanged) + { + m_DelayedCommand = SketchControlsScript.GlobalCommands.Null; + m_DelayedCommandParam = -1; + m_DelayedCommandParam2 = -1; + if (m_ActivePopUp != null && !bKeepOpen) + { + m_ActivePopUp.RequestClose(); + } + } + } + + virtual public void GotoPage(int iIndex) + { + } + + virtual public void AdvancePage(int iAmount) + { + } + + public void InitForCollisionDetection() + { + for (int i = 0; i < m_PositioningSpheresTransformed.Length; ++i) + { + m_PositioningSpheresTransformed[i] = transform.TransformPoint(m_PositioningSpheres[i]); + } + } + + public void CalculateDepenetration(BasePanel rOther) + { + Vector3 vThemToUs = transform.position - rOther.transform.position; + Vector3 vThemToUsNorm = vThemToUs.normalized; + float fMaxExtent = m_PositioningExtent + rOther.GetPositioningExtent(); + + if (vThemToUs.sqrMagnitude < fMaxExtent * fMaxExtent) + { + float fCombinedSphereRad = m_ScaledPositioningSphereRadius + rOther.m_ScaledPositioningSphereRadius; + float fCombinedSphereRadSq = fCombinedSphereRad * fCombinedSphereRad; + for (int i = 0; i < m_PositioningSpheresTransformed.Length; ++i) + { + for (int j = 0; j < rOther.m_PositioningSpheresTransformed.Length; ++j) + { + Vector3 vSphereToSphere = rOther.m_PositioningSpheresTransformed[j] - m_PositioningSpheresTransformed[i]; + if (vSphereToSphere.sqrMagnitude < fCombinedSphereRadSq) + { + float fSphereToSphereDist = vSphereToSphere.magnitude; + float fDepenetrationAmount = fCombinedSphereRad - fSphereToSphereDist; + m_PositioningVelocity += (vThemToUsNorm * fDepenetrationAmount * m_DepenetrationScalar * Time.deltaTime); + } + } + } + } + } + + public void CalculateDepenetration(Vector3 vOtherPos, float fCombinedRadius) + { + Vector3 vThemToUs = transform.position - vOtherPos; + if (vThemToUs.sqrMagnitude < fCombinedRadius * fCombinedRadius) + { + float fDepenetrationAmount = fCombinedRadius - vThemToUs.magnitude; + m_PositioningVelocity += (vThemToUs.normalized * fDepenetrationAmount * + m_DepenetrationScalar * Time.fixedDeltaTime); + } + } + + public void UpdatePositioningForces() + { + //use spring to bring us home + Vector3 vToHome = m_PositioningHome - transform.position; + vToHome *= m_PositioningK; + Vector3 vDampenedVel = m_PositioningVelocity * m_PositioningDampen; + Vector3 vSpringForce = vToHome - vDampenedVel; + m_PositioningVelocity += vSpringForce; + + //update position + Vector3 vPos = transform.position; + vPos += (m_PositioningVelocity * Time.deltaTime); + transform.position = vPos; + if (m_NonScaleChild) + { + m_NonScaleChild.OnPosRotChanged(); + } + } + + /// Update the delayed command parameter that is used when a popup confirmation button is pressed. + /// This can be used when something provides more context to the initial command. + public void UpdateDelayedCommandParameter(int newParam) + { + m_DelayedCommandParam = newParam; + } + + static public bool DoesRayHitCollider(Ray rRay, Collider rCollider, bool skipAngleCheck = false) + { + if (!skipAngleCheck && Vector3.Angle(rRay.direction, rCollider.transform.forward) > 90.0f) + { + return false; + } + + RaycastHit rHitInfo; + return rCollider.Raycast(rRay, out rHitInfo, 100.0f); + } + + static public bool DoesRayHitCollider(Ray rRay, Collider rCollider, out RaycastHit rHitInfo) + { + return rCollider.Raycast(rRay, out rHitInfo, 100.0f); + } + + /* + * For Debugging. + * + void OnDrawGizmos() { + if (m_PositioningSpheres != null) { + Gizmos.color = Color.yellow; + for (int i = 0; i < m_PositioningSpheres.Length; ++i) { + Gizmos.DrawWireSphere(transform.TransformPoint(m_PositioningSpheres[i]), + m_ScaledPositioningSphereRadius); + } + } + if (WidgetSibling != null) { + Gizmos.color = Color.red; + Gizmos.DrawWireSphere(transform.position, WidgetSibling.m_CollisionRadius); + } + } + /**/ + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/PolyModelButton.cs b/Assets/Scripts/GUI/IcosaModelButton.cs similarity index 74% rename from Assets/Scripts/GUI/PolyModelButton.cs rename to Assets/Scripts/GUI/IcosaModelButton.cs index bb3633c084..8f9985d049 100644 --- a/Assets/Scripts/GUI/PolyModelButton.cs +++ b/Assets/Scripts/GUI/IcosaModelButton.cs @@ -1,195 +1,195 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections; -using UnityEngine; - -namespace TiltBrush -{ - - public class PolyModelButton : ModelButton - { - [SerializeField] private GameObject m_LoadingOverlay; - - private PolyAssetCatalog.AssetDetails m_PacAsset; - - public PolyAssetCatalog.AssetDetails Asset - { - get { return m_PacAsset; } - } - - protected override Texture2D UnloadedTexture - { - get - { - if (m_PacAsset.Thumbnail == null) - { - return base.UnloadedTexture; - } - else - { - return m_PacAsset.Thumbnail; - } - } - } - - protected override string ModelName - { - get { return m_PacAsset.HumanName; } - } - - public override bool IsAvailable() - { - return base.IsAvailable() && m_PacAsset != null && - (!App.PolyAssetCatalog.IsLoading(m_PacAsset.AssetId)); - } - - public void SetPreset(PolyAssetCatalog.AssetDetails asset, int index) - { - if (asset != m_PacAsset) - { - m_LoadingOverlay.SetActive(false); - } - m_PacAsset = asset; - SetPreset(asset.Model, index); - - if (m_PacAsset.ModelRotation != null) - { - Quaternion publishedRotation = m_PacAsset.ModelRotation.Value; - m_PreviewBaseRotation = Quaternion.Euler(0, publishedRotation.eulerAngles.y, 0); - } - } - - override protected void RequestModelPreloadInternal(string reason) - { - App.PolyAssetCatalog.RequestModelLoad(m_PacAsset.AssetId, reason); - m_LoadingOverlay.SetActive(true); - } - - override protected void CancelRequestModelPreload() - { - if (m_PacAsset == null) { return; } - var pac = App.PolyAssetCatalog; - var assetId = m_PacAsset.AssetId; - pac.CancelRequestModelLoad(assetId); - m_LoadingOverlay.SetActive(pac.IsLoading(assetId)); - } - - protected override void OnButtonPressed() - { - // Early out if this model had errors loading. - if (m_Model != null && m_Model.Error != null) - { - return; - } - StartCoroutine(SpawnModelCoroutine("button")); - } - - // Emits user-visible error on failure - protected IEnumerator SpawnModelCoroutine(string reason) - { - if (m_Model == null) - { - // Same as calling Model.RequestModelPreload -> RequestModelLoadInternal, except - // this won't ignore the request if the load-into-memory previously failed. - App.PolyAssetCatalog.RequestModelLoad(m_PacAsset.AssetId, reason); - m_LoadingOverlay.SetActive(true); - } - // It is possible from this section forward that the user may have moved on to a different page - // on the Poly panel, which is why we use a local copy of 'model' rather than m_Model. - string assetId = m_PacAsset.AssetId; - Model model; - // A model in the catalog will become non-null once the gltf has been downloaded or is in the - // cache. - while ((model = App.PolyAssetCatalog.GetModel(assetId)) == null) - { - yield return null; - } - // We only want to disable the loading overlay if the button is still referring to the same - // asset. - if (assetId == m_PacAsset.AssetId) - { - m_LoadingOverlay.SetActive(false); - } - - // A model becomes valid once the gltf has been successfully read into a Unity mesh. - if (!model.m_Valid) - { - // The model might be in the "loaded with error" state, but it seems harmless to try again. - // If the user keeps clicking, we'll keep trying. - yield return model.LoadFullyCoroutine(reason); - Debug.Assert(model.m_Valid || model.Error != null); - } - - if (!model.m_Valid) - { - // TODO: Is there a reason not to do this unconditionally? - if (assetId == m_PacAsset.AssetId) - { - RefreshModelButton(); - } - OutputWindowScript.Error($"Couldn't load model: {model.Error?.message}", model.Error?.detail); - } - else - { - SpawnValidModel(model); - } - } - - protected override void RefreshModelButton() - { - base.RefreshModelButton(); - if (m_Model != null) - { - if (m_Model.m_Valid) - { - m_LoadingOverlay.SetActive(false); - SetButtonTexture(m_PacAsset.Thumbnail, 1); - } - else if (m_Model.Error != null) - { - m_LoadingOverlay.SetActive(false); - } - } - } - - override public string UnloadedExtraDescription() - { - PolyPanel polyp = m_Manager.GetComponent(); - Debug.AssertFormat(polyp, "PolyModelButton should be a child of the PolyPanel"); - return (polyp && polyp.ShowingUser) ? null : m_PacAsset.AccountName; - } - - override public string LoadedExtraDescription() - { - return UnloadedExtraDescription(); - } - - public override void UpdateButtonState(bool bActivateInputValid) - { - base.UpdateButtonState(bActivateInputValid); - if (IsHover()) - { - m_PreviewParent.localScale = Vector3.one; - } - } - - public override void ResetState() - { - base.ResetState(); - m_PreviewParent.localScale = Vector3.zero; - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections; +using UnityEngine; + +namespace TiltBrush +{ + + public class IcosaModelButton : ModelButton + { + [SerializeField] private GameObject m_LoadingOverlay; + + private IcosaAssetCatalog.AssetDetails m_IcosaAsset; + + public IcosaAssetCatalog.AssetDetails Asset + { + get { return m_IcosaAsset; } + } + + protected override Texture2D UnloadedTexture + { + get + { + if (m_IcosaAsset.Thumbnail == null) + { + return base.UnloadedTexture; + } + else + { + return m_IcosaAsset.Thumbnail; + } + } + } + + protected override string ModelName + { + get { return m_IcosaAsset.HumanName; } + } + + public override bool IsAvailable() + { + return base.IsAvailable() && m_IcosaAsset != null && + (!App.IcosaAssetCatalog.IsLoading(m_IcosaAsset.AssetId)); + } + + public void SetPreset(IcosaAssetCatalog.AssetDetails asset, int index) + { + if (asset != m_IcosaAsset) + { + m_LoadingOverlay.SetActive(false); + } + m_IcosaAsset = asset; + SetPreset(asset.Model, index); + + if (m_IcosaAsset.ModelRotation != null) + { + Quaternion publishedRotation = m_IcosaAsset.ModelRotation.Value; + m_PreviewBaseRotation = Quaternion.Euler(0, publishedRotation.eulerAngles.y, 0); + } + } + + override protected void RequestModelPreloadInternal(string reason) + { + App.IcosaAssetCatalog.RequestModelLoad(m_IcosaAsset.AssetId, reason); + m_LoadingOverlay.SetActive(true); + } + + override protected void CancelRequestModelPreload() + { + if (m_IcosaAsset == null) { return; } + var pac = App.IcosaAssetCatalog; + var assetId = m_IcosaAsset.AssetId; + pac.CancelRequestModelLoad(assetId); + m_LoadingOverlay.SetActive(pac.IsLoading(assetId)); + } + + protected override void OnButtonPressed() + { + // Early out if this model had errors loading. + if (m_Model != null && m_Model.Error != null) + { + return; + } + StartCoroutine(SpawnModelCoroutine("button")); + } + + // Emits user-visible error on failure + protected IEnumerator SpawnModelCoroutine(string reason) + { + if (m_Model == null) + { + // Same as calling Model.RequestModelPreload -> RequestModelLoadInternal, except + // this won't ignore the request if the load-into-memory previously failed. + App.IcosaAssetCatalog.RequestModelLoad(m_IcosaAsset.AssetId, reason); + m_LoadingOverlay.SetActive(true); + } + // It is possible from this section forward that the user may have moved on to a different page + // on the Poly panel, which is why we use a local copy of 'model' rather than m_Model. + string assetId = m_IcosaAsset.AssetId; + Model model; + // A model in the catalog will become non-null once the gltf has been downloaded or is in the + // cache. + while ((model = App.IcosaAssetCatalog.GetModel(assetId)) == null) + { + yield return null; + } + // We only want to disable the loading overlay if the button is still referring to the same + // asset. + if (assetId == m_IcosaAsset.AssetId) + { + m_LoadingOverlay.SetActive(false); + } + + // A model becomes valid once the gltf has been successfully read into a Unity mesh. + if (!model.m_Valid) + { + // The model might be in the "loaded with error" state, but it seems harmless to try again. + // If the user keeps clicking, we'll keep trying. + yield return model.LoadFullyCoroutine(reason); + Debug.Assert(model.m_Valid || model.Error != null); + } + + if (!model.m_Valid) + { + // TODO: Is there a reason not to do this unconditionally? + if (assetId == m_IcosaAsset.AssetId) + { + RefreshModelButton(); + } + OutputWindowScript.Error($"Couldn't load model: {model.Error?.message}", model.Error?.detail); + } + else + { + SpawnValidModel(model); + } + } + + protected override void RefreshModelButton() + { + base.RefreshModelButton(); + if (m_Model != null) + { + if (m_Model.m_Valid) + { + m_LoadingOverlay.SetActive(false); + SetButtonTexture(m_IcosaAsset.Thumbnail, 1); + } + else if (m_Model.Error != null) + { + m_LoadingOverlay.SetActive(false); + } + } + } + + override public string UnloadedExtraDescription() + { + IcosaPanel polyp = m_Manager.GetComponent(); + Debug.AssertFormat(polyp, "PolyModelButton should be a child of the PolyPanel"); + return (polyp && polyp.ShowingUser) ? null : m_IcosaAsset.AccountName; + } + + override public string LoadedExtraDescription() + { + return UnloadedExtraDescription(); + } + + public override void UpdateButtonState(bool bActivateInputValid) + { + base.UpdateButtonState(bActivateInputValid); + if (IsHover()) + { + m_PreviewParent.localScale = Vector3.one; + } + } + + public override void ResetState() + { + base.ResetState(); + m_PreviewParent.localScale = Vector3.zero; + } + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/PolyModelButton.cs.meta b/Assets/Scripts/GUI/IcosaModelButton.cs.meta similarity index 100% rename from Assets/Scripts/GUI/PolyModelButton.cs.meta rename to Assets/Scripts/GUI/IcosaModelButton.cs.meta diff --git a/Assets/Scripts/GUI/PolyPanel.cs b/Assets/Scripts/GUI/IcosaPanel.cs similarity index 84% rename from Assets/Scripts/GUI/PolyPanel.cs rename to Assets/Scripts/GUI/IcosaPanel.cs index 03e5f9b4b2..312dcb1fb3 100644 --- a/Assets/Scripts/GUI/PolyPanel.cs +++ b/Assets/Scripts/GUI/IcosaPanel.cs @@ -1,309 +1,309 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using UnityEngine; -using UnityEngine.Localization; -using TMPro; - -namespace TiltBrush -{ - - public enum IcosaSetType - { - User, - Liked, - Featured - } - - public class PolyPanel : ModalPanel - { - [SerializeField] private TextMeshPro m_PanelText; - [SerializeField] private TextMeshPro m_PanelTextSubtitle; - [SerializeField] private TextMeshPro m_PanelTextUserSubtitle; - [SerializeField] private LocalizedString m_PanelTextStandard; - public string PanelTextStandard { get { return m_PanelTextStandard.GetLocalizedStringAsync().Result; } } - [SerializeField] private LocalizedString m_PanelTextFeatured; - public string PanelTextFeatured { get { return m_PanelTextFeatured.GetLocalizedStringAsync().Result; } } - [SerializeField] private LocalizedString m_PanelTextLiked; // Liked Models - public string PanelTextLiked { get { return m_PanelTextLiked.GetLocalizedStringAsync().Result; } } - [SerializeField] private Renderer m_PolyGalleryRenderer; - [SerializeField] private GameObject m_NoObjectsMessage; - [SerializeField] private GameObject m_InternetError; - [SerializeField] private GameObject m_NoAuthoredModelsMessage; - [SerializeField] private GameObject m_NoLikesMessage; - [SerializeField] private GameObject m_NotLoggedInMessage; - [SerializeField] private GameObject m_OutOfDateMessage; - [SerializeField] private GameObject m_NoPolyConnectionMessage; - - private IcosaSetType m_CurrentSet; - private bool m_LoggedIn; - - // State for automatically loading models. - int m_LastPageIndexForLoad = -1; - IcosaSetType m_LastSetTypeForLoad = IcosaSetType.User; - - public bool ShowingFeatured { get { return m_CurrentSet == IcosaSetType.Featured; } } - public bool ShowingLikes { get { return m_CurrentSet == IcosaSetType.Liked; } } - public bool ShowingUser { get { return m_CurrentSet == IcosaSetType.User; } } - - - - override public void OnWidgetShowAnimComplete() - { - SetVisiblePolySet(m_CurrentSet); - } - - public override void InitPanel() - { - base.InitPanel(); - - m_LoggedIn = App.GoogleIdentity.LoggedIn; - - m_NoObjectsMessage.SetActive(false); - m_InternetError.SetActive(false); - m_NoAuthoredModelsMessage.SetActive(false); - m_NoLikesMessage.SetActive(false); - m_NotLoggedInMessage.SetActive(false); - m_OutOfDateMessage.SetActive(false); - m_NoPolyConnectionMessage.SetActive(false); - } - - public override bool IsInButtonMode(ModeButton button) - { - var polySetButton = button as PolySetButton; - return polySetButton && polySetButton.m_ButtonType == m_CurrentSet; - } - - protected override void OnStart() - { - // Initialize icons. - PolyModelButton[] rPanelButtons = m_Mesh.GetComponentsInChildren(); - foreach (PolyModelButton icon in rPanelButtons) - { - GameObject go = icon.gameObject; - icon.SetButtonGrayscale(icon); - go.SetActive(false); - Icons.Add(icon); - } - - m_CurrentSet = IcosaSetType.Featured; - - // Make sure Poly gallery button starts at greyscale when panel is initialized - m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 1); - - App.PolyAssetCatalog.RequestRefresh(m_CurrentSet); - App.PolyAssetCatalog.CatalogChanged += OnPolyAssetCatalogChanged; - } - - void SetVisiblePolySet(IcosaSetType type) - { - m_CurrentSet = type; - App.PolyAssetCatalog.RequestRefresh(m_CurrentSet); - ResetPageIndex(); - RefreshPage(); - } - - void OnPolyAssetCatalogChanged() - { - RefreshPage(); - } - - protected override void RefreshPage() - { - m_NoLikesMessage.SetActive(false); - m_NoAuthoredModelsMessage.SetActive(false); - m_NotLoggedInMessage.SetActive(false); - if (VrAssetService.m_Instance.NoConnection) - { - m_NoPolyConnectionMessage.SetActive(true); - RefreshPanelText(); - base.RefreshPage(); - return; - } - if (!VrAssetService.m_Instance.Available) - { - m_OutOfDateMessage.SetActive(true); - RefreshPanelText(); - base.RefreshPage(); - return; - } - - m_NumPages = ((App.PolyAssetCatalog.NumCloudModels(m_CurrentSet) - 1) / Icons.Count) + 1; - int numCloudModels = App.PolyAssetCatalog.NumCloudModels(m_CurrentSet); - - if (m_LastPageIndexForLoad != PageIndex || m_LastSetTypeForLoad != m_CurrentSet) - { - // Unload the previous page's models. - - // This function may be called multiple times as icons load, only unload the old models once, - // otherwise the current page's models will be thrashed. - m_LastPageIndexForLoad = PageIndex; - m_LastSetTypeForLoad = m_CurrentSet; - - // Destroy previews so only the thumbnail is visible. - for (int i = 0; i < Icons.Count; i++) - { - ((ModelButton)Icons[i]).DestroyModelPreview(); - } - - App.PolyAssetCatalog.UnloadUnusedModels(); - } - - for (int i = 0; i < Icons.Count; i++) - { - PolyModelButton icon = (PolyModelButton)Icons[i]; - // Set sketch index relative to page based index - int iMapIndex = m_IndexOffset + i; - - // Init icon according to availability of sketch - GameObject go = icon.gameObject; - if (iMapIndex < numCloudModels) - { - PolyAssetCatalog.AssetDetails asset = - App.PolyAssetCatalog.GetPolyAsset(m_CurrentSet, iMapIndex); - go.SetActive(true); - - if (icon.Asset != null && asset.AssetId != icon.Asset.AssetId) - { - icon.DestroyModelPreview(); - } - icon.SetPreset(asset, iMapIndex); - - // Note that App.UserConfig.Flags.PolyModelPreload falls through to - // App.PlatformConfig.EnablePolyPreload if it isn't set in Tilt Brush.cfg. - if (App.UserConfig.Flags.PolyModelPreload) - { - icon.RequestModelPreload(PageIndex); - } - } - else - { - go.SetActive(false); - } - } - - bool internetError = - App.PolyAssetCatalog.NumCloudModels(IcosaSetType.Featured) == 0; - m_InternetError.SetActive(internetError); - - RefreshPanelText(); - switch (m_CurrentSet) - { - case IcosaSetType.User: - if (!internetError) - { - if (App.GoogleIdentity.LoggedIn) - { - if (numCloudModels == 0) - { - m_NoAuthoredModelsMessage.SetActive(true); - } - } - else - { - m_NotLoggedInMessage.SetActive(true); - } - } - break; - case IcosaSetType.Liked: - if (!internetError) - { - if (App.GoogleIdentity.LoggedIn) - { - if (numCloudModels == 0) - { - m_NoLikesMessage.SetActive(true); - } - } - else - { - m_NotLoggedInMessage.SetActive(true); - } - } - break; - } - - base.RefreshPage(); - } - - void RefreshPanelText() - { - switch (m_CurrentSet) - { - case IcosaSetType.User: - m_PanelText.text = PanelTextStandard; - m_PanelTextSubtitle.gameObject.SetActive(false); - m_PanelTextUserSubtitle.gameObject.SetActive(true); - break; - case IcosaSetType.Featured: - m_PanelText.text = PanelTextFeatured; - m_PanelTextSubtitle.gameObject.SetActive(false); - m_PanelTextUserSubtitle.gameObject.SetActive(false); - break; - case IcosaSetType.Liked: - m_PanelText.text = PanelTextLiked; - m_PanelTextSubtitle.gameObject.SetActive(true); - m_PanelTextUserSubtitle.gameObject.SetActive(false); - break; - } - } - - void Update() - { - BaseUpdate(); - - // Update share button's text. - bool loggedIn = App.GoogleIdentity.LoggedIn; - if (loggedIn != m_LoggedIn) - { - m_LoggedIn = loggedIn; - RefreshPage(); - } - - PageFlipUpdate(); - } - - override protected void OnUpdateActive() - { - // If we're not active, hide all our preview panels - if (!m_GazeActive) - { - m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 1); - foreach (var baseButton in Icons) - { - PolyModelButton icon = (PolyModelButton)baseButton; - icon.ResetState(); - icon.SetButtonGrayscale(icon); - } - - } - else if (m_CurrentState == PanelState.Available) - { - m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 0); - foreach (var baseButton in Icons) - { - PolyModelButton icon = (PolyModelButton)baseButton; - icon.SetButtonGrayscale(false); - } - } - } - - // Works specifically with PolySetButtons. - public void ButtonPressed(IcosaSetType rType) - { - SetVisiblePolySet(rType); - } - - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using UnityEngine.Localization; +using TMPro; + +namespace TiltBrush +{ + + public enum IcosaSetType + { + User, + Liked, + Featured + } + + public class IcosaPanel : ModalPanel + { + [SerializeField] private TextMeshPro m_PanelText; + [SerializeField] private TextMeshPro m_PanelTextSubtitle; + [SerializeField] private TextMeshPro m_PanelTextUserSubtitle; + [SerializeField] private LocalizedString m_PanelTextStandard; + public string PanelTextStandard { get { return m_PanelTextStandard.GetLocalizedStringAsync().Result; } } + [SerializeField] private LocalizedString m_PanelTextFeatured; + public string PanelTextFeatured { get { return m_PanelTextFeatured.GetLocalizedStringAsync().Result; } } + [SerializeField] private LocalizedString m_PanelTextLiked; // Liked Models + public string PanelTextLiked { get { return m_PanelTextLiked.GetLocalizedStringAsync().Result; } } + [SerializeField] private Renderer m_PolyGalleryRenderer; + [SerializeField] private GameObject m_NoObjectsMessage; + [SerializeField] private GameObject m_InternetError; + [SerializeField] private GameObject m_NoAuthoredModelsMessage; + [SerializeField] private GameObject m_NoLikesMessage; + [SerializeField] private GameObject m_NotLoggedInMessage; + [SerializeField] private GameObject m_OutOfDateMessage; + [SerializeField] private GameObject m_NoPolyConnectionMessage; + + private IcosaSetType m_CurrentSet; + private bool m_LoggedIn; + + // State for automatically loading models. + int m_LastPageIndexForLoad = -1; + IcosaSetType m_LastSetTypeForLoad = IcosaSetType.User; + + public bool ShowingFeatured { get { return m_CurrentSet == IcosaSetType.Featured; } } + public bool ShowingLikes { get { return m_CurrentSet == IcosaSetType.Liked; } } + public bool ShowingUser { get { return m_CurrentSet == IcosaSetType.User; } } + + + + override public void OnWidgetShowAnimComplete() + { + SetVisiblePolySet(m_CurrentSet); + } + + public override void InitPanel() + { + base.InitPanel(); + + m_LoggedIn = App.IcosaIsLoggedIn; + + m_NoObjectsMessage.SetActive(false); + m_InternetError.SetActive(false); + m_NoAuthoredModelsMessage.SetActive(false); + m_NoLikesMessage.SetActive(false); + m_NotLoggedInMessage.SetActive(false); + m_OutOfDateMessage.SetActive(false); + m_NoPolyConnectionMessage.SetActive(false); + } + + public override bool IsInButtonMode(ModeButton button) + { + var polySetButton = button as IcosaSetButton; + return polySetButton && polySetButton.m_ButtonType == m_CurrentSet; + } + + protected override void OnStart() + { + // Initialize icons. + IcosaModelButton[] rPanelButtons = m_Mesh.GetComponentsInChildren(); + foreach (IcosaModelButton icon in rPanelButtons) + { + GameObject go = icon.gameObject; + icon.SetButtonGrayscale(icon); + go.SetActive(false); + Icons.Add(icon); + } + + m_CurrentSet = IcosaSetType.Featured; + + // Make sure Poly gallery button starts at greyscale when panel is initialized + m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 1); + + App.IcosaAssetCatalog.RequestRefresh(m_CurrentSet); + App.IcosaAssetCatalog.CatalogChanged += OnIcosaAssetCatalogChanged; + } + + void SetVisiblePolySet(IcosaSetType type) + { + m_CurrentSet = type; + App.IcosaAssetCatalog.RequestRefresh(m_CurrentSet); + ResetPageIndex(); + RefreshPage(); + } + + void OnIcosaAssetCatalogChanged() + { + RefreshPage(); + } + + protected override void RefreshPage() + { + m_NoLikesMessage.SetActive(false); + m_NoAuthoredModelsMessage.SetActive(false); + m_NotLoggedInMessage.SetActive(false); + if (VrAssetService.m_Instance.NoConnection) + { + m_NoPolyConnectionMessage.SetActive(true); + RefreshPanelText(); + base.RefreshPage(); + return; + } + if (!VrAssetService.m_Instance.Available) + { + m_OutOfDateMessage.SetActive(true); + RefreshPanelText(); + base.RefreshPage(); + return; + } + + m_NumPages = ((App.IcosaAssetCatalog.NumCloudModels(m_CurrentSet) - 1) / Icons.Count) + 1; + int numCloudModels = App.IcosaAssetCatalog.NumCloudModels(m_CurrentSet); + + if (m_LastPageIndexForLoad != PageIndex || m_LastSetTypeForLoad != m_CurrentSet) + { + // Unload the previous page's models. + + // This function may be called multiple times as icons load, only unload the old models once, + // otherwise the current page's models will be thrashed. + m_LastPageIndexForLoad = PageIndex; + m_LastSetTypeForLoad = m_CurrentSet; + + // Destroy previews so only the thumbnail is visible. + for (int i = 0; i < Icons.Count; i++) + { + ((ModelButton)Icons[i]).DestroyModelPreview(); + } + + App.IcosaAssetCatalog.UnloadUnusedModels(); + } + + for (int i = 0; i < Icons.Count; i++) + { + IcosaModelButton icon = (IcosaModelButton)Icons[i]; + // Set sketch index relative to page based index + int iMapIndex = m_IndexOffset + i; + + // Init icon according to availability of sketch + GameObject go = icon.gameObject; + if (iMapIndex < numCloudModels) + { + IcosaAssetCatalog.AssetDetails asset = + App.IcosaAssetCatalog.GetPolyAsset(m_CurrentSet, iMapIndex); + go.SetActive(true); + + if (icon.Asset != null && asset.AssetId != icon.Asset.AssetId) + { + icon.DestroyModelPreview(); + } + icon.SetPreset(asset, iMapIndex); + + // Note that App.UserConfig.Flags.PolyModelPreload falls through to + // App.PlatformConfig.EnablePolyPreload if it isn't set in Tilt Brush.cfg. + if (App.UserConfig.Flags.IcosaModelPreload) + { + icon.RequestModelPreload(PageIndex); + } + } + else + { + go.SetActive(false); + } + } + + bool internetError = + App.IcosaAssetCatalog.NumCloudModels(IcosaSetType.Featured) == 0; + m_InternetError.SetActive(internetError); + + RefreshPanelText(); + switch (m_CurrentSet) + { + case IcosaSetType.User: + if (!internetError) + { + if (App.IcosaIsLoggedIn) + { + if (numCloudModels == 0) + { + m_NoAuthoredModelsMessage.SetActive(true); + } + } + else + { + m_NotLoggedInMessage.SetActive(true); + } + } + break; + case IcosaSetType.Liked: + if (!internetError) + { + if (App.IcosaIsLoggedIn) + { + if (numCloudModels == 0) + { + m_NoLikesMessage.SetActive(true); + } + } + else + { + m_NotLoggedInMessage.SetActive(true); + } + } + break; + } + + base.RefreshPage(); + } + + void RefreshPanelText() + { + switch (m_CurrentSet) + { + case IcosaSetType.User: + m_PanelText.text = PanelTextStandard; + m_PanelTextSubtitle.gameObject.SetActive(false); + m_PanelTextUserSubtitle.gameObject.SetActive(true); + break; + case IcosaSetType.Featured: + m_PanelText.text = PanelTextFeatured; + m_PanelTextSubtitle.gameObject.SetActive(false); + m_PanelTextUserSubtitle.gameObject.SetActive(false); + break; + case IcosaSetType.Liked: + m_PanelText.text = PanelTextLiked; + m_PanelTextSubtitle.gameObject.SetActive(true); + m_PanelTextUserSubtitle.gameObject.SetActive(false); + break; + } + } + + void Update() + { + BaseUpdate(); + + // Update share button's text. + bool loggedIn = App.IcosaIsLoggedIn; + if (loggedIn != m_LoggedIn) + { + m_LoggedIn = loggedIn; + RefreshPage(); + } + + PageFlipUpdate(); + } + + override protected void OnUpdateActive() + { + // If we're not active, hide all our preview panels + if (!m_GazeActive) + { + m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 1); + foreach (var baseButton in Icons) + { + IcosaModelButton icon = (IcosaModelButton)baseButton; + icon.ResetState(); + icon.SetButtonGrayscale(icon); + } + + } + else if (m_CurrentState == PanelState.Available) + { + m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 0); + foreach (var baseButton in Icons) + { + IcosaModelButton icon = (IcosaModelButton)baseButton; + icon.SetButtonGrayscale(false); + } + } + } + + // Works specifically with PolySetButtons. + public void ButtonPressed(IcosaSetType rType) + { + SetVisiblePolySet(rType); + } + + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/PolyPanel.cs.meta b/Assets/Scripts/GUI/IcosaPanel.cs.meta similarity index 100% rename from Assets/Scripts/GUI/PolyPanel.cs.meta rename to Assets/Scripts/GUI/IcosaPanel.cs.meta diff --git a/Assets/Scripts/GUI/PolySetButton.cs b/Assets/Scripts/GUI/IcosaSetButton.cs similarity index 77% rename from Assets/Scripts/GUI/PolySetButton.cs rename to Assets/Scripts/GUI/IcosaSetButton.cs index ea87e6f455..9797c51b8d 100644 --- a/Assets/Scripts/GUI/PolySetButton.cs +++ b/Assets/Scripts/GUI/IcosaSetButton.cs @@ -1,31 +1,31 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace TiltBrush -{ - - public class PolySetButton : ModeButton - { - public IcosaSetType m_ButtonType; - - override protected void OnButtonPressed() - { - PolyPanel polyp = m_Manager.GetComponent(); - if (polyp) - { - polyp.ButtonPressed(m_ButtonType); - } - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace TiltBrush +{ + + public class IcosaSetButton : ModeButton + { + public IcosaSetType m_ButtonType; + + override protected void OnButtonPressed() + { + IcosaPanel icosaPanel = m_Manager.GetComponent(); + if (icosaPanel) + { + icosaPanel.ButtonPressed(m_ButtonType); + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/PolySetButton.cs.meta b/Assets/Scripts/GUI/IcosaSetButton.cs.meta similarity index 100% rename from Assets/Scripts/GUI/PolySetButton.cs.meta rename to Assets/Scripts/GUI/IcosaSetButton.cs.meta diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index 095d0c16d5..c10a7f22e8 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -1,421 +1,416 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Numerics; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Client; -using Org.OpenAPITools.Model; -using UnityEngine; -using TMPro; - -namespace TiltBrush -{ - - public class ProfilePopUpWindow : OptionsPopUpWindow - { - public enum Mode - { - Accounts, - TakeOffHeadset, - GoogleHelp, - DriveHelp, - SketchfabHelp, - IcosaHelp, - ConfirmLogin, - Unavailable, - } - - [SerializeField] private GameObject m_GoogleSignedInElements; - [SerializeField] private GameObject m_GoogleSignedOutElements; - [SerializeField] private GameObject m_GoogleConfirmSignOutElements; - [SerializeField] private GameObject m_SketchfabSignedInElements; - [SerializeField] private GameObject m_SketchfabSignedOutElements; - [SerializeField] private GameObject m_SketchfabConfirmSignOutElements; - [SerializeField] private GameObject m_IcosaSignedInElements; - [SerializeField] private GameObject m_IcosaSignedOutElements; - [SerializeField] private GameObject m_IcosaConfirmSignOutElements; - [SerializeField] private GameObject m_IcosaLoginElements; - [SerializeField] private Renderer m_GooglePhoto; - [SerializeField] private Renderer m_SketchfabPhoto; - [SerializeField] private Renderer m_IcosaPhoto; - [SerializeField] private TextMeshPro m_GoogleNameText; - [SerializeField] private TextMeshPro m_SketchfabNameText; - [SerializeField] private TextMeshPro m_IcosaNameText; - [SerializeField] private Texture2D m_GenericPhoto; - - [SerializeField] private GameObject m_Accounts; - [SerializeField] private GameObject m_TakeOffHeadset; - [SerializeField] private GameObject m_GoogleInfoElements; - [SerializeField] private GameObject m_DriveInfoElements; - [SerializeField] private GameObject m_SketchfabInfoElements; - [SerializeField] private GameObject m_IcosaInfoElements; - [SerializeField] private GameObject m_UnavailableElements; - [SerializeField] private GameObject m_DriveSyncEnabledElements; - [SerializeField] private GameObject m_DriveSyncDisabledElements; - [SerializeField] private GameObject m_DriveFullElements; - - [SerializeField] private GameObject m_DriveSyncIconEnabled; - [SerializeField] private GameObject m_DriveSyncIconDisabled; - [SerializeField] private GameObject m_DriveSyncIconDriveFull; - - [SerializeField] private GameObject m_BackupCompleteElements; - [SerializeField] private GameObject m_BackingUpElements; - [SerializeField] private TextMeshPro m_BackingUpProgress; - - [Header("Mobile State Members")] - [SerializeField] private GameObject m_ConfirmLoginElements; - [SerializeField] private SaveAndOptionButton m_SaveAndProceedButton; - - private Mode m_CurrentMode; - private bool m_DriveSyncing = false; - - private void Start() - { - App.DriveSync.SyncEnabledChanged += RefreshObjects; - } - - override public void Init(GameObject rParent, string sText) - { - base.Init(rParent, sText); - OAuth2Identity.ProfileUpdated += OnProfileUpdated; - RefreshObjects(); - m_IcosaLoginElements.SetActive(false); - if (App.IcosaIsLoggedIn) - { - StartCoroutine(FetchUserDataCoroutine(userData => - { - App.IcosaUserName = userData.Displayname; - App.IcosaUserId = userData.Id; - App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API - RefreshIcosaUserInfoUi(); - })); - } - - App.DriveAccess.RefreshFreeSpaceAsync().AsAsyncVoid(); - - // TODO: Make configurable by secrets/login data available at runtime. - if (App.Config.DisableAccountLogins) - { - UpdateMode(Mode.Unavailable); - } - } - - void OnDestroy() - { - OAuth2Identity.ProfileUpdated -= OnProfileUpdated; - App.DriveSync.SyncEnabledChanged -= RefreshObjects; - } - - override protected void BaseUpdate() - { - base.BaseUpdate(); - if (App.DriveSync.Syncing != m_DriveSyncing) - { - RefreshObjects(); - } - RefreshBackupProgressText(); - } - - void RefreshObjects() - { - // Google. - bool driveFull = App.DriveSync.DriveIsLowOnSpace; - bool driveSyncEnabled = App.DriveSync.SyncEnabled; - bool driveSyncing = App.DriveSync.Syncing; - - OAuth2Identity.UserInfo googleInfo = App.GoogleIdentity.Profile; - bool googleInfoValid = googleInfo != null; - m_GoogleSignedInElements.SetActive(googleInfoValid); - m_GoogleSignedOutElements.SetActive(!googleInfoValid); - m_GoogleConfirmSignOutElements.SetActive(false); - if (googleInfoValid) - { - m_GoogleNameText.text = googleInfo.name; - m_GooglePhoto.material.mainTexture = googleInfo.icon; - - m_DriveSyncIconDriveFull.SetActive(driveFull && driveSyncEnabled); - m_DriveSyncIconEnabled.SetActive(!driveFull && driveSyncEnabled); - m_DriveSyncIconDisabled.SetActive(!driveSyncEnabled); - } - - // Sketchfab. - OAuth2Identity.UserInfo sketchfabInfo = App.SketchfabIdentity.Profile; - bool sketchfabInfoValid = sketchfabInfo != null; - m_SketchfabSignedInElements.SetActive(sketchfabInfoValid); - m_SketchfabSignedOutElements.SetActive(!sketchfabInfoValid); - m_SketchfabConfirmSignOutElements.SetActive(false); - if (sketchfabInfoValid) - { - m_SketchfabNameText.text = sketchfabInfo.name; - m_SketchfabPhoto.material.mainTexture = sketchfabInfo.icon; - } - - // Icosa. - m_IcosaSignedInElements.SetActive(App.IcosaIsLoggedIn); - m_IcosaSignedOutElements.SetActive(!App.IcosaIsLoggedIn); - m_IcosaConfirmSignOutElements.SetActive(false); - RefreshIcosaUserInfoUi(); - - m_DriveFullElements.SetActive(driveFull && driveSyncEnabled); - m_DriveSyncEnabledElements.SetActive(!driveFull && driveSyncEnabled); - m_DriveSyncDisabledElements.SetActive(!driveSyncEnabled); - m_BackupCompleteElements.SetActive(!driveSyncing); - m_BackingUpElements.SetActive(driveSyncing); - m_DriveSyncing = driveSyncing; - RefreshBackupProgressText(); - } - - private void RefreshIcosaUserInfoUi() - { - m_IcosaNameText.text = App.IcosaUserName; - m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; - } - - public void HideIcosaLogin() - { - m_IcosaLoginElements.SetActive(false); - m_IcosaSignedInElements.SetActive(true); - m_IcosaSignedOutElements.SetActive(true); - m_SketchfabSignedOutElements.SetActive(true); - m_SketchfabSignedInElements.SetActive(true); - m_GoogleSignedInElements.SetActive(true); - m_GoogleSignedOutElements.SetActive(true); - m_Persistent = false; - RefreshObjects(); - } - - public void ShowIcosaLogin() - { - m_IcosaLoginElements.SetActive(true); - m_IcosaLoginElements.GetComponent().Clear(); - m_IcosaSignedInElements.SetActive(false); - m_IcosaSignedOutElements.SetActive(false); - m_SketchfabSignedOutElements.SetActive(false); - m_SketchfabSignedInElements.SetActive(false); - m_GoogleSignedInElements.SetActive(false); - m_GoogleSignedOutElements.SetActive(false); - } - - public void HandleIcosaLoginSubmit(string code) - { - if (App.IcosaIsLoggedIn) return; - StartCoroutine(LoginCoroutine(code)); - } - - private IEnumerator LoginCoroutine(string code) - { - var config = new Configuration(); - var loginApi = new LoginApi(App.ICOSA_API_URL); - loginApi.Configuration = config; - var loginTask = loginApi.DeviceLoginLoginDeviceLoginPostAsync(code); - yield return new WaitUntil(() => loginTask.IsCompleted); - - if (loginTask.Exception != null) - { - if (loginTask.Exception.Message.Contains("401 Unauthorized")) - { - // TODO: Show error message. - LoginFailure(); - AudioManager.m_Instance.PlayTrashSound(transform.position); - } - yield break; - } - - if (loginTask.Result?.AccessToken == null) - { - // TODO: Show error message. - LoginFailure(); - AudioManager.m_Instance.PlayPinSound(transform.position, AudioManager.PinSoundType.Wobble); - yield break; - } - App.Instance.IcosaToken = loginTask.Result.AccessToken; - StartCoroutine(FetchUserDataCoroutine(userData => LoginSuccess(userData))); - } - - private IEnumerator FetchUserDataCoroutine(Action onSuccess) - { - var usersApi = new UsersApi(App.ICOSA_API_URL); - var config = new Configuration { AccessToken = App.Instance.IcosaToken }; - usersApi.Configuration = config; - var getUserTask = usersApi.GetUsersMeUsersMeGetAsync(); - yield return new WaitUntil(() => getUserTask.IsCompleted); - - if (getUserTask.Exception != null) - { - if (getUserTask.Exception.Message.Contains("401 Unauthorized")) - { - // Clear user token - LoginFailure(); - } - Debug.Log($"GetUser failed with exception: {getUserTask.Exception}"); - yield break; - } - - var userData = getUserTask.Result; - if (userData == null) - { - Debug.Log($"Failure - no user data received"); - // TODO should we logout? Clear username/icon? - yield break; - } - onSuccess?.Invoke(userData); - } - - private void LoginSuccess(FullUser userData) - { - // Call the callback delegate if it's provided (which means this was called from the first coroutine) - App.IcosaUserName = userData.Displayname; - App.IcosaUserId = userData.Id; - App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API - RefreshIcosaUserInfoUi(); - HideIcosaLogin(); - } - - private void LoginFailure() - { - HideIcosaLogin(); - App.Instance.LogoutIcosa(); - } - - void RefreshBackupProgressText() - { - if (m_BackingUpElements.activeSelf) - { - m_BackingUpProgress.text = string.Format("Backing Up... {0}", - Mathf.Clamp(App.DriveSync.Progress, 0.01f, 0.99f).ToString("P0")); - } - } - - void UpdateMode(Mode newMode) - { - m_CurrentMode = newMode; - m_Accounts.SetActive(m_CurrentMode == Mode.Accounts); - m_TakeOffHeadset.SetActive(m_CurrentMode == Mode.TakeOffHeadset); - m_GoogleInfoElements.SetActive(m_CurrentMode == Mode.GoogleHelp); - m_DriveInfoElements.SetActive(m_CurrentMode == Mode.DriveHelp); - m_SketchfabInfoElements.SetActive(m_CurrentMode == Mode.SketchfabHelp); - m_IcosaInfoElements.SetActive(m_CurrentMode == Mode.IcosaHelp); - m_UnavailableElements.SetActive(m_CurrentMode == Mode.Unavailable); - if (m_ConfirmLoginElements != null) - { - m_ConfirmLoginElements.SetActive(m_CurrentMode == Mode.ConfirmLogin); - } - // Reset persistent flag when switching modes. - m_Persistent = false; - } - - void OnProfileUpdated(OAuth2Identity _) - { - // If we're currently telling the user to take of the headset to signin, - // and they've done so correctly, switch back to the accounts view. - if (m_CurrentMode == Mode.TakeOffHeadset) - { - Debug.Log($"OnProfileUpdated set AccountMode"); - UpdateMode(Mode.Accounts); - } - RefreshObjects(); - } - - // This function serves as a callback from ProfilePopUpButtons that want to - // change the mode of the popup on click. - public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) - { - switch (button.m_Command) - { - // Identifier for signaling we understand the info message. - case SketchControlsScript.GlobalCommands.Null: - case SketchControlsScript.GlobalCommands.GoogleDriveSync: - UpdateMode(Mode.Accounts); - RefreshObjects(); - break; - case SketchControlsScript.GlobalCommands.LoginToGenericCloud: - // m_CommandParam 1 is Google. m_CommandParam 2 is Sketchfab. - if (button.m_CommandParam == 1 || button.m_CommandParam == 2) - { - if (App.Config.IsMobileHardware && m_SaveAndProceedButton != null) - { - m_SaveAndProceedButton.SetCommandParameters(button.m_CommandParam, 0); - UpdateMode(Mode.ConfirmLogin); - } - else - { - OAuth2Identity.UserInfo userInfo = (button.m_CommandParam == 1) ? - App.GoogleIdentity.Profile : App.SketchfabIdentity.Profile; - if (userInfo == null) - { - UpdateMode(Mode.TakeOffHeadset); - m_Persistent = true; - } - } - } - break; - case SketchControlsScript.GlobalCommands.LoginToIcosa: - if (!App.Config.IsMobileHardware) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - SketchControlsScript.kRemoveHeadsetFyi, - fPopScalar: 0.5f - ); - } - - App.OpenURL($"{App.ICOSA_WEBSITE_URL}/device"); - ShowIcosaLogin(); - m_Persistent = true; - break; - case SketchControlsScript.GlobalCommands.AccountInfo: - // Identifier for triggering an info message. - switch (button.m_CommandParam) - { - case 0: - UpdateMode(Mode.DriveHelp); - break; - case 1: - UpdateMode(Mode.GoogleHelp); - break; - case 2: - UpdateMode(Mode.SketchfabHelp); - break; - case 3: - UpdateMode(Mode.IcosaHelp); - break; - } - break; - case SketchControlsScript.GlobalCommands.SignOutConfirm: - switch ((Cloud)button.m_CommandParam) - { - case Cloud.Poly: - m_GoogleSignedInElements.SetActive(false); - m_GoogleSignedOutElements.SetActive(false); - m_GoogleConfirmSignOutElements.SetActive(true); - break; - case Cloud.Sketchfab: - m_SketchfabSignedInElements.SetActive(false); - m_SketchfabSignedOutElements.SetActive(false); - m_SketchfabConfirmSignOutElements.SetActive(true); - break; - case Cloud.Icosa: - m_IcosaSignedInElements.SetActive(false); - m_IcosaSignedOutElements.SetActive(false); - m_IcosaConfirmSignOutElements.SetActive(true); - break; - case Cloud.None: break; - } - break; - } - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Numerics; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Client; +using Org.OpenAPITools.Model; +using UnityEngine; +using TMPro; + +namespace TiltBrush +{ + + public class ProfilePopUpWindow : OptionsPopUpWindow + { + public enum Mode + { + Accounts, + TakeOffHeadset, + GoogleHelp, + DriveHelp, + SketchfabHelp, + IcosaHelp, + ConfirmLogin, + Unavailable, + } + + [SerializeField] private GameObject m_GoogleSignedInElements; + [SerializeField] private GameObject m_GoogleSignedOutElements; + [SerializeField] private GameObject m_GoogleConfirmSignOutElements; + [SerializeField] private GameObject m_SketchfabSignedInElements; + [SerializeField] private GameObject m_SketchfabSignedOutElements; + [SerializeField] private GameObject m_SketchfabConfirmSignOutElements; + [SerializeField] private GameObject m_IcosaSignedInElements; + [SerializeField] private GameObject m_IcosaSignedOutElements; + [SerializeField] private GameObject m_IcosaConfirmSignOutElements; + [SerializeField] private GameObject m_IcosaLoginElements; + [SerializeField] private Renderer m_GooglePhoto; + [SerializeField] private Renderer m_SketchfabPhoto; + [SerializeField] private Renderer m_IcosaPhoto; + [SerializeField] private TextMeshPro m_GoogleNameText; + [SerializeField] private TextMeshPro m_SketchfabNameText; + [SerializeField] private TextMeshPro m_IcosaNameText; + [SerializeField] private Texture2D m_GenericPhoto; + + [SerializeField] private GameObject m_Accounts; + [SerializeField] private GameObject m_TakeOffHeadset; + [SerializeField] private GameObject m_GoogleInfoElements; + [SerializeField] private GameObject m_DriveInfoElements; + [SerializeField] private GameObject m_SketchfabInfoElements; + [SerializeField] private GameObject m_IcosaInfoElements; + [SerializeField] private GameObject m_UnavailableElements; + [SerializeField] private GameObject m_DriveSyncEnabledElements; + [SerializeField] private GameObject m_DriveSyncDisabledElements; + [SerializeField] private GameObject m_DriveFullElements; + + [SerializeField] private GameObject m_DriveSyncIconEnabled; + [SerializeField] private GameObject m_DriveSyncIconDisabled; + [SerializeField] private GameObject m_DriveSyncIconDriveFull; + + [SerializeField] private GameObject m_BackupCompleteElements; + [SerializeField] private GameObject m_BackingUpElements; + [SerializeField] private TextMeshPro m_BackingUpProgress; + + [Header("Mobile State Members")] + [SerializeField] private GameObject m_ConfirmLoginElements; + [SerializeField] private SaveAndOptionButton m_SaveAndProceedButton; + + private Mode m_CurrentMode; + private bool m_DriveSyncing = false; + + private void Start() + { + App.DriveSync.SyncEnabledChanged += RefreshObjects; + } + + override public void Init(GameObject rParent, string sText) + { + base.Init(rParent, sText); + OAuth2Identity.ProfileUpdated += OnProfileUpdated; + RefreshObjects(); + m_IcosaLoginElements.SetActive(false); + if (App.IcosaIsLoggedIn) + { + StartCoroutine(FetchUserDataCoroutine(userData => + { + App.IcosaUserName = userData.Displayname; + App.IcosaUserId = userData.Id; + App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API + RefreshIcosaUserInfoUi(); + })); + } + + App.DriveAccess.RefreshFreeSpaceAsync().AsAsyncVoid(); + + // TODO: Make configurable by secrets/login data available at runtime. + if (App.Config.DisableAccountLogins) + { + UpdateMode(Mode.Unavailable); + } + } + + void OnDestroy() + { + OAuth2Identity.ProfileUpdated -= OnProfileUpdated; + App.DriveSync.SyncEnabledChanged -= RefreshObjects; + } + + override protected void BaseUpdate() + { + base.BaseUpdate(); + if (App.DriveSync.Syncing != m_DriveSyncing) + { + RefreshObjects(); + } + RefreshBackupProgressText(); + } + + void RefreshObjects() + { + // Google. + bool driveFull = App.DriveSync.DriveIsLowOnSpace; + bool driveSyncEnabled = App.DriveSync.SyncEnabled; + bool driveSyncing = App.DriveSync.Syncing; + + OAuth2Identity.UserInfo googleInfo = App.GoogleIdentity.Profile; + bool googleInfoValid = googleInfo != null; + m_GoogleSignedInElements.SetActive(googleInfoValid); + m_GoogleSignedOutElements.SetActive(!googleInfoValid); + m_GoogleConfirmSignOutElements.SetActive(false); + if (googleInfoValid) + { + m_GoogleNameText.text = googleInfo.name; + m_GooglePhoto.material.mainTexture = googleInfo.icon; + + m_DriveSyncIconDriveFull.SetActive(driveFull && driveSyncEnabled); + m_DriveSyncIconEnabled.SetActive(!driveFull && driveSyncEnabled); + m_DriveSyncIconDisabled.SetActive(!driveSyncEnabled); + } + + // Sketchfab. + OAuth2Identity.UserInfo sketchfabInfo = App.SketchfabIdentity.Profile; + bool sketchfabInfoValid = sketchfabInfo != null; + m_SketchfabSignedInElements.SetActive(sketchfabInfoValid); + m_SketchfabSignedOutElements.SetActive(!sketchfabInfoValid); + m_SketchfabConfirmSignOutElements.SetActive(false); + if (sketchfabInfoValid) + { + m_SketchfabNameText.text = sketchfabInfo.name; + m_SketchfabPhoto.material.mainTexture = sketchfabInfo.icon; + } + + // Icosa. + m_IcosaSignedInElements.SetActive(App.IcosaIsLoggedIn); + m_IcosaSignedOutElements.SetActive(!App.IcosaIsLoggedIn); + m_IcosaConfirmSignOutElements.SetActive(false); + RefreshIcosaUserInfoUi(); + + m_DriveFullElements.SetActive(driveFull && driveSyncEnabled); + m_DriveSyncEnabledElements.SetActive(!driveFull && driveSyncEnabled); + m_DriveSyncDisabledElements.SetActive(!driveSyncEnabled); + m_BackupCompleteElements.SetActive(!driveSyncing); + m_BackingUpElements.SetActive(driveSyncing); + m_DriveSyncing = driveSyncing; + RefreshBackupProgressText(); + } + + private void RefreshIcosaUserInfoUi() + { + m_IcosaNameText.text = App.IcosaUserName; + m_IcosaPhoto.material.mainTexture = App.IcosaUserIcon; + } + + public void HideIcosaLogin() + { + m_IcosaLoginElements.SetActive(false); + m_IcosaSignedInElements.SetActive(true); + m_IcosaSignedOutElements.SetActive(true); + m_SketchfabSignedOutElements.SetActive(true); + m_SketchfabSignedInElements.SetActive(true); + m_GoogleSignedInElements.SetActive(true); + m_GoogleSignedOutElements.SetActive(true); + m_Persistent = false; + RefreshObjects(); + } + + public void ShowIcosaLogin() + { + m_IcosaLoginElements.SetActive(true); + m_IcosaLoginElements.GetComponent().Clear(); + m_IcosaSignedInElements.SetActive(false); + m_IcosaSignedOutElements.SetActive(false); + m_SketchfabSignedOutElements.SetActive(false); + m_SketchfabSignedInElements.SetActive(false); + m_GoogleSignedInElements.SetActive(false); + m_GoogleSignedOutElements.SetActive(false); + } + + public void HandleIcosaLoginSubmit(string code) + { + if (App.IcosaIsLoggedIn) return; + StartCoroutine(LoginCoroutine(code)); + } + + private IEnumerator LoginCoroutine(string code) + { + var config = new Configuration(); + var loginApi = new LoginApi(App.ICOSA_API_URL); + loginApi.Configuration = config; + var loginTask = loginApi.DeviceLoginLoginDeviceLoginPostAsync(code); + yield return new WaitUntil(() => loginTask.IsCompleted); + + if (loginTask.Exception != null) + { + if (loginTask.Exception.Message.Contains("401 Unauthorized")) + { + // TODO: Show error message. + LoginFailure(); + AudioManager.m_Instance.PlayTrashSound(transform.position); + } + yield break; + } + + if (loginTask.Result?.AccessToken == null) + { + // TODO: Show error message. + LoginFailure(); + AudioManager.m_Instance.PlayPinSound(transform.position, AudioManager.PinSoundType.Wobble); + yield break; + } + App.Instance.IcosaToken = loginTask.Result.AccessToken; + StartCoroutine(FetchUserDataCoroutine(userData => LoginSuccess(userData))); + } + + private IEnumerator FetchUserDataCoroutine(Action onSuccess) + { + var usersApi = new UsersApi(App.ICOSA_API_URL); + var config = new Configuration { AccessToken = App.Instance.IcosaToken }; + usersApi.Configuration = config; + var getUserTask = usersApi.GetUsersMeUsersMeGetAsync(); + yield return new WaitUntil(() => getUserTask.IsCompleted); + + if (getUserTask.Exception != null) + { + if (getUserTask.Exception.Message.Contains("401 Unauthorized")) + { + // Clear user token + LoginFailure(); + } + Debug.Log($"GetUser failed with exception: {getUserTask.Exception}"); + yield break; + } + + var userData = getUserTask.Result; + if (userData == null) + { + Debug.Log($"Failure - no user data received"); + // TODO should we logout? Clear username/icon? + yield break; + } + onSuccess?.Invoke(userData); + } + + private void LoginSuccess(FullUser userData) + { + // Call the callback delegate if it's provided (which means this was called from the first coroutine) + App.IcosaUserName = userData.Displayname; + App.IcosaUserId = userData.Id; + App.IcosaUserIcon = m_GenericPhoto; // TODO: Get icon from API + RefreshIcosaUserInfoUi(); + HideIcosaLogin(); + } + + private void LoginFailure() + { + HideIcosaLogin(); + App.Instance.LogoutIcosa(); + } + + void RefreshBackupProgressText() + { + if (m_BackingUpElements.activeSelf) + { + m_BackingUpProgress.text = string.Format("Backing Up... {0}", + Mathf.Clamp(App.DriveSync.Progress, 0.01f, 0.99f).ToString("P0")); + } + } + + void UpdateMode(Mode newMode) + { + m_CurrentMode = newMode; + m_Accounts.SetActive(m_CurrentMode == Mode.Accounts); + m_TakeOffHeadset.SetActive(m_CurrentMode == Mode.TakeOffHeadset); + m_GoogleInfoElements.SetActive(m_CurrentMode == Mode.GoogleHelp); + m_DriveInfoElements.SetActive(m_CurrentMode == Mode.DriveHelp); + m_SketchfabInfoElements.SetActive(m_CurrentMode == Mode.SketchfabHelp); + m_IcosaInfoElements.SetActive(m_CurrentMode == Mode.IcosaHelp); + m_UnavailableElements.SetActive(m_CurrentMode == Mode.Unavailable); + if (m_ConfirmLoginElements != null) + { + m_ConfirmLoginElements.SetActive(m_CurrentMode == Mode.ConfirmLogin); + } + // Reset persistent flag when switching modes. + m_Persistent = false; + } + + void OnProfileUpdated(OAuth2Identity _) + { + // If we're currently telling the user to take of the headset to signin, + // and they've done so correctly, switch back to the accounts view. + if (m_CurrentMode == Mode.TakeOffHeadset) + { + Debug.Log($"OnProfileUpdated set AccountMode"); + UpdateMode(Mode.Accounts); + } + RefreshObjects(); + } + + // This function serves as a callback from ProfilePopUpButtons that want to + // change the mode of the popup on click. + public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) + { + switch (button.m_Command) + { + // Identifier for signaling we understand the info message. + case SketchControlsScript.GlobalCommands.Null: + case SketchControlsScript.GlobalCommands.GoogleDriveSync: + UpdateMode(Mode.Accounts); + RefreshObjects(); + break; + case SketchControlsScript.GlobalCommands.LoginToGenericCloud: + // m_CommandParam 1 is Google. m_CommandParam 2 is Sketchfab. + if (button.m_CommandParam == 1 || button.m_CommandParam == 2) + { + if (App.Config.IsMobileHardware && m_SaveAndProceedButton != null) + { + m_SaveAndProceedButton.SetCommandParameters(button.m_CommandParam, 0); + UpdateMode(Mode.ConfirmLogin); + } + else + { + OAuth2Identity.UserInfo userInfo = (button.m_CommandParam == 1) ? + App.GoogleIdentity.Profile : App.SketchfabIdentity.Profile; + if (userInfo == null) + { + UpdateMode(Mode.TakeOffHeadset); + m_Persistent = true; + } + } + } + break; + case SketchControlsScript.GlobalCommands.LoginToIcosa: + if (!App.Config.IsMobileHardware) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + SketchControlsScript.kRemoveHeadsetFyi, + fPopScalar: 0.5f + ); + } + + App.OpenURL($"{App.ICOSA_WEBSITE_URL}/device"); + ShowIcosaLogin(); + m_Persistent = true; + break; + case SketchControlsScript.GlobalCommands.AccountInfo: + // Identifier for triggering an info message. + switch (button.m_CommandParam) + { + case 0: + UpdateMode(Mode.DriveHelp); + break; + case 1: + UpdateMode(Mode.GoogleHelp); + break; + case 2: + UpdateMode(Mode.SketchfabHelp); + break; + case 3: + UpdateMode(Mode.IcosaHelp); + break; + } + break; + case SketchControlsScript.GlobalCommands.SignOutConfirm: + switch ((Cloud)button.m_CommandParam) + { + case Cloud.Sketchfab: + m_SketchfabSignedInElements.SetActive(false); + m_SketchfabSignedOutElements.SetActive(false); + m_SketchfabConfirmSignOutElements.SetActive(true); + break; + case Cloud.Icosa: + m_IcosaSignedInElements.SetActive(false); + m_IcosaSignedOutElements.SetActive(false); + m_IcosaConfirmSignOutElements.SetActive(true); + break; + case Cloud.None: break; + } + break; + } + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/GUI/SketchbookPanel.cs b/Assets/Scripts/GUI/SketchbookPanel.cs index e0261a0914..6e6cde6afc 100644 --- a/Assets/Scripts/GUI/SketchbookPanel.cs +++ b/Assets/Scripts/GUI/SketchbookPanel.cs @@ -1,794 +1,792 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using UnityEngine; -using UnityEngine.Localization; -using System; -using System.Collections.Generic; -using System.Linq; -using TMPro; - -namespace TiltBrush -{ - - public class SketchbookPanel : ModalPanel - { - // Index of the "local sketches" button in m_GalleryButtons - const int kElementNumberGalleryButtonLocal = 0; - // Amount of extra space to put below the "local sketches" gallery button - const float kGalleryButtonLocalPadding = .15f; - - [SerializeField] private Texture2D m_LoadingImageTexture; - [SerializeField] private Texture2D m_UnknownImageTexture; - [SerializeField] private TextMeshPro m_PanelTextPro; - [SerializeField] private LocalizedString m_PanelTextStandard; - public string PanelTextStandard { get { return m_PanelTextStandard.GetLocalizedStringAsync().Result; } } - [SerializeField] private LocalizedString m_PanelTextShowcase; - public string PanelTextShowcase { get { return m_PanelTextShowcase.GetLocalizedStringAsync().Result; } } - - [SerializeField] private LocalizedString m_PanelTextLiked; - public string PanelTextLiked { get { return m_PanelTextLiked.GetLocalizedStringAsync().Result; } } - [SerializeField] private LocalizedString m_PanelTextDrive; - public string PanelTextDrive { get { return m_PanelTextDrive.GetLocalizedStringAsync().Result; } } - [SerializeField] private GameObject m_NoSketchesMessage; - [SerializeField] private GameObject m_NoDriveSketchesMessage; - [SerializeField] private GameObject m_NoLikesMessage; - [SerializeField] private GameObject m_NotLoggedInMessage; - [SerializeField] private GameObject m_NotLoggedInDriveMessage; - [SerializeField] private GameObject m_NoShowcaseMessage; - [SerializeField] private GameObject m_ContactingServerMessage; - [SerializeField] private GameObject m_OutOfDateMessage; - [SerializeField] private GameObject m_NoPolyConnectionMessage; - [SerializeField] private Renderer m_OnlineGalleryButtonRenderer; - [SerializeField] private GameObject[] m_IconsOnFirstPage; - [SerializeField] private GameObject[] m_IconsOnNormalPage; - [SerializeField] private GameObject m_CloseButton; - [SerializeField] private GameObject m_NewSketchButton; - // Gallery buttons will automatically reposition based on how many are visible so they must be - // added to this array in order from top to bottom. - [SerializeField] private GalleryButton[] m_GalleryButtons; - [SerializeField] private int m_ElementNumberGalleryButtonDrive = 3; - [SerializeField] private float m_GalleryButtonHeight = 0.3186f; - [SerializeField] private Renderer m_ProfileButtonRenderer; - [SerializeField] private GameObject m_LoadingGallery; - [SerializeField] private GameObject m_DriveSyncProgress; - [SerializeField] private GameObject m_SyncingDriveIcon; - [SerializeField] private GameObject m_DriveEnabledIcon; - [SerializeField] private GameObject m_DriveDisabledIcon; - [SerializeField] private GameObject m_DriveFullIcon; - [SerializeField] private Vector2 m_SketchIconUvScale = new Vector2(0.7f, 0.7f); - [SerializeField] private Vector3 m_ReadOnlyPopupOffset; - - private float m_ImageAspect; - private Vector2 m_HalfInvUvScale; - - private SceneFileInfo m_FirstSketch; - - private bool m_AllIconTexturesAssigned; - private bool m_AllSketchesAreAvailable; - private SketchSetType m_CurrentSketchSet; - private SketchSet m_SketchSet; - private OptionButton m_NewSketchButtonScript; - private OptionButton m_PaintButtonScript; - private List m_IconScriptsOnFirstPage; - private List m_IconScriptsOnNormalPage; - private bool m_DriveSetHasSketches; - private bool m_ReadOnlyShown = false; - - public float ImageAspect { get { return m_ImageAspect; } } - - override public void SetInIntroMode(bool inIntro) - { - m_NewSketchButton.SetActive(inIntro); - m_CloseButton.SetActive(!inIntro); - - // When we switch in to intro mode, make our panel colorful, even if it doesn't have focus, - // to help attract attention. - if (inIntro) - { - for (int i = 0; i < m_IconScriptsOnFirstPage.Count; ++i) - { - m_IconScriptsOnFirstPage[i].SetButtonGrayscale(false); - } - for (int i = 0; i < m_IconScriptsOnNormalPage.Count; ++i) - { - m_IconScriptsOnNormalPage[i].SetButtonGrayscale(false); - } - } - } - - protected override List Icons - { - get - { - return (PageIndex == 0 ? m_IconScriptsOnFirstPage : m_IconScriptsOnNormalPage); - } - } - - public override bool IsInButtonMode(ModeButton button) - { - GalleryButton galleryButton = button as GalleryButton; - return galleryButton && - ((galleryButton.m_ButtonType == GalleryButton.Type.Liked && m_CurrentSketchSet == SketchSetType.Liked) || - (galleryButton.m_ButtonType == GalleryButton.Type.Local && m_CurrentSketchSet == SketchSetType.User) || - (galleryButton.m_ButtonType == GalleryButton.Type.Showcase && m_CurrentSketchSet == SketchSetType.Curated) || - (galleryButton.m_ButtonType == GalleryButton.Type.Drive && m_CurrentSketchSet == SketchSetType.Drive)); - } - - override public void InitPanel() - { - base.InitPanel(); - - m_NewSketchButtonScript = m_NewSketchButton.GetComponent(); - m_PaintButtonScript = m_CloseButton.GetComponent(); - m_IconScriptsOnFirstPage = new List(); - for (int i = 0; i < m_IconsOnFirstPage.Length; ++i) - { - m_IconScriptsOnFirstPage.Add(m_IconsOnFirstPage[i].GetComponent()); - } - m_IconScriptsOnNormalPage = new List(); - for (int i = 0; i < m_IconsOnNormalPage.Length; ++i) - { - m_IconScriptsOnNormalPage.Add(m_IconsOnNormalPage[i].GetComponent()); - } - SetInIntroMode(false); - - Debug.Assert(m_SketchIconUvScale.x >= 0.0f && m_SketchIconUvScale.x <= 1.0f && - m_SketchIconUvScale.y >= 0.0f && m_SketchIconUvScale.y <= 1.0f); - m_HalfInvUvScale.Set(1.0f - m_SketchIconUvScale.x, 1.0f - m_SketchIconUvScale.y); - m_HalfInvUvScale *= 0.5f; - } - - protected override void OnStart() - { - // Initialize icons. - LoadSketchButton[] rPanelButtons = m_Mesh.GetComponentsInChildren(); - foreach (LoadSketchButton icon in rPanelButtons) - { - GameObject go = icon.gameObject; - go.SetActive(false); - } - - // GameObject is active in prefab so the button registers. - m_NoLikesMessage.SetActive(false); - m_NotLoggedInMessage.SetActive(false); - m_NotLoggedInDriveMessage.SetActive(false); - - // Dynamically position the gallery buttons. - OnDriveSetHasSketchesChanged(); - - // Set the sketch set var to Liked, then function set to force state. - m_CurrentSketchSet = SketchSetType.Liked; - SetVisibleSketchSet(SketchSetType.User); - - Action refresh = () => - { - if (m_ContactingServerMessage.activeSelf || - m_NoShowcaseMessage.activeSelf || - m_LoadingGallery.activeSelf) - { - // Update the overlays more frequently when these overlays are shown to reflect whether - // we are actively trying to get sketches from Poly. - RefreshPage(); - } - }; - SketchCatalog.m_Instance.GetSet(SketchSetType.Liked).OnSketchRefreshingChanged += refresh; - SketchCatalog.m_Instance.GetSet(SketchSetType.Curated).OnSketchRefreshingChanged += refresh; - SketchCatalog.m_Instance.GetSet(SketchSetType.Drive).OnSketchRefreshingChanged += refresh; - App.GoogleIdentity.OnLogout += refresh; - } - - void OnDestroy() - { - if (m_SketchSet != null) - { - m_SketchSet.OnChanged -= OnSketchSetDirty; - } - } - - override protected void OnEnablePanel() - { - base.OnEnablePanel(); - if (m_SketchSet != null) - { - m_SketchSet.RequestRefresh(); - } - } - - void SetVisibleSketchSet(SketchSetType type) - { - if (m_CurrentSketchSet != type) - { - // Clean up our old sketch set. - if (m_SketchSet != null) - { - m_SketchSet.OnChanged -= OnSketchSetDirty; - } - - // Cache new set. - m_SketchSet = SketchCatalog.m_Instance.GetSet(type); - m_SketchSet.OnChanged += OnSketchSetDirty; - m_SketchSet.RequestRefresh(); - - // Tell all the icons which set to reference when loading sketches. - IEnumerable allIcons = m_IconsOnFirstPage.Concat(m_IconsOnNormalPage) - .Select(icon => icon.GetComponent()) - .Where(icon => icon != null); - foreach (LoadSketchButton icon in allIcons) - { - icon.SketchSet = m_SketchSet; - } - - ComputeNumPages(); - ResetPageIndex(); - m_CurrentSketchSet = type; - RefreshPage(); - - switch (m_CurrentSketchSet) - { - case SketchSetType.User: - m_PanelTextPro.text = PanelTextStandard; - break; - case SketchSetType.Curated: - m_PanelTextPro.text = PanelTextShowcase; - break; - case SketchSetType.Liked: - m_PanelTextPro.text = PanelTextLiked; - break; - case SketchSetType.Drive: - m_PanelTextPro.text = PanelTextDrive; - break; - } - } - } - - private void ComputeNumPages() - { - if (m_SketchSet.NumSketches <= m_IconsOnFirstPage.Length) - { - m_NumPages = 1; - return; - } - int remainingSketches = m_SketchSet.NumSketches - m_IconsOnFirstPage.Length; - int normalPages = ((remainingSketches - 1) / m_IconsOnNormalPage.Length) + 1; - m_NumPages = 1 + normalPages; - } - - List GetIconLoadIndices() - { - var ret = new List(); - for (int i = 0; i < Icons.Count; i++) - { - int sketchIndex = m_IndexOffset + i; - if (sketchIndex >= m_SketchSet.NumSketches) - { - break; - } - ret.Add(sketchIndex); - } - return ret; - } - - protected override void RefreshPage() - { - m_SketchSet.RequestOnlyLoadedMetadata(GetIconLoadIndices()); - m_AllIconTexturesAssigned = false; - m_AllSketchesAreAvailable = false; - - // Disable all. - foreach (var i in m_IconsOnFirstPage) - { - i.SetActive(false); - } - foreach (var i in m_IconsOnNormalPage) - { - i.SetActive(false); - } - - // Base Refresh updates the modal parts of the panel, and we always want those refreshed. - base.RefreshPage(); - - bool requiresPoly = m_CurrentSketchSet == SketchSetType.Liked; - - bool polyDown = VrAssetService.m_Instance.NoConnection && requiresPoly; - m_NoPolyConnectionMessage.SetActive(polyDown); - - bool outOfDate = !polyDown && !VrAssetService.m_Instance.Available && requiresPoly; - m_OutOfDateMessage.SetActive(outOfDate); - - if (outOfDate || polyDown) - { - m_NoSketchesMessage.SetActive(false); - m_NoDriveSketchesMessage.SetActive(false); - m_NotLoggedInMessage.SetActive(false); - m_NoLikesMessage.SetActive(false); - m_ContactingServerMessage.SetActive(false); - m_NoShowcaseMessage.SetActive(false); - return; - } - - bool refreshIcons = m_SketchSet.NumSketches > 0; - - // Show no sketches if we don't have sketches. - m_NoSketchesMessage.SetActive( - (m_CurrentSketchSet == SketchSetType.User) && (m_SketchSet.NumSketches <= 0)); - m_NoDriveSketchesMessage.SetActive( - (m_CurrentSketchSet == SketchSetType.Drive) && (m_SketchSet.NumSketches <= 0)); - - // Show sign in popup if signed out for liked or drive sketchsets - bool showNotLoggedIn = !App.GoogleIdentity.LoggedIn && - (m_CurrentSketchSet == SketchSetType.Liked || - m_CurrentSketchSet == SketchSetType.Drive); - refreshIcons = refreshIcons && !showNotLoggedIn; - m_NotLoggedInMessage.SetActive(showNotLoggedIn && m_CurrentSketchSet == SketchSetType.Liked); - m_NotLoggedInDriveMessage.SetActive(showNotLoggedIn && - m_CurrentSketchSet == SketchSetType.Drive); - - // Show no likes text & gallery button if we don't have liked sketches. - m_NoLikesMessage.SetActive( - (m_CurrentSketchSet == SketchSetType.Liked) && - (m_SketchSet.NumSketches <= 0) && - !m_SketchSet.IsActivelyRefreshingSketches && - App.GoogleIdentity.LoggedIn); - - // Show Contacting Server if we're talking to Poly. - m_ContactingServerMessage.SetActive( - (requiresPoly || - m_CurrentSketchSet == SketchSetType.Drive) && - (m_SketchSet.NumSketches <= 0) && - (m_SketchSet.IsActivelyRefreshingSketches && App.GoogleIdentity.LoggedIn)); - - // Show Showcase error if we're in Showcase and don't have sketches. - m_NoShowcaseMessage.SetActive( - (m_CurrentSketchSet == SketchSetType.Curated) && - (m_SketchSet.NumSketches <= 0) && - !m_SketchSet.IsActivelyRefreshingSketches); - - // Refresh all icons if necessary. - if (!refreshIcons) - { - return; - } - - for (int i = 0; i < Icons.Count; i++) - { - LoadSketchButton icon = Icons[i] as LoadSketchButton; - // Default to loading image - icon.SetButtonTexture(m_LoadingImageTexture); - icon.ThumbnailLoaded = false; - - // Set sketch index relative to page based index - int iSketchIndex = m_IndexOffset + i; - if (iSketchIndex >= m_SketchSet.NumSketches) - { - iSketchIndex = -1; - } - icon.SketchIndex = iSketchIndex; - icon.ResetScale(); - - // Init icon according to availability of sketch - GameObject go = icon.gameObject; - if (m_SketchSet.IsSketchIndexValid(iSketchIndex)) - { - string sSketchName = m_SketchSet.GetSketchName(iSketchIndex); - icon.SetDescriptionText(App.ShortenForDescriptionText(sSketchName)); - SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); - if (info.Available) - { - m_SketchSet.PrecacheSketchModels(iSketchIndex); - } - - if (info.TriangleCount is int triCount) - { - icon.WarningVisible = triCount > - QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles; - } - else - { - icon.WarningVisible = false; - } - go.SetActive(true); - } - else - { - go.SetActive(false); - } - } - } - - void Update() - { - BaseUpdate(); - PageFlipUpdate(); - - // Refresh icons while they are in flux - if (m_SketchSet.IsReadyForAccess && - (!m_SketchSet.RequestedIconsAreLoaded || - !m_AllIconTexturesAssigned || !m_AllSketchesAreAvailable)) - { - UpdateIcons(); - } - - // Set icon uv offsets relative to head position. - Vector3 head_LS = m_Mesh.transform.InverseTransformPoint(ViewpointScript.Head.position); - float angleX = Vector3.Angle(Vector3.back, new Vector3(head_LS.x, 0.0f, head_LS.z)); - angleX *= (head_LS.x > 0.0f) ? -1.0f : 1.0f; - - float angleY = Vector3.Angle(Vector3.back, new Vector3(0.0f, head_LS.y, head_LS.z)); - angleY *= (head_LS.y > 0.0f) ? -1.0f : 1.0f; - - float maxAngleXRatio = angleX / 90.0f; - float maxAngleYRatio = angleY / 90.0f; - Vector2 offset = new Vector2( - m_HalfInvUvScale.x + (m_HalfInvUvScale.x * maxAngleXRatio), - m_HalfInvUvScale.y + (m_HalfInvUvScale.y * maxAngleYRatio)); - for (int i = 0; i < Icons.Count; i++) - { - LoadSketchButton icon = Icons[i] as LoadSketchButton; - icon.UpdateUvOffsetAndScale(offset, m_SketchIconUvScale); - } - - switch (m_CurrentSketchSet) - { - case SketchSetType.Curated: - m_LoadingGallery.SetActive(m_SketchSet.IsActivelyRefreshingSketches); - m_DriveSyncProgress.SetActive(false); - m_SyncingDriveIcon.SetActive(false); - m_DriveEnabledIcon.SetActive(false); - m_DriveDisabledIcon.SetActive(false); - m_DriveFullIcon.SetActive(false); - break; - case SketchSetType.Liked: - m_LoadingGallery.SetActive(false); - m_DriveSyncProgress.SetActive(false); - m_SyncingDriveIcon.SetActive(false); - m_DriveEnabledIcon.SetActive(false); - m_DriveDisabledIcon.SetActive(false); - m_DriveFullIcon.SetActive(false); - break; - case SketchSetType.User: - case SketchSetType.Drive: - bool sketchSetRefreshing = m_CurrentSketchSet == SketchSetType.Drive && - m_SketchSet.IsActivelyRefreshingSketches; - bool driveSyncing = App.DriveSync.Syncing; - bool syncEnabled = App.DriveSync.SyncEnabled; - bool googleLoggedIn = App.GoogleIdentity.LoggedIn; - bool driveFull = App.DriveSync.DriveIsLowOnSpace; - m_LoadingGallery.SetActive(sketchSetRefreshing && !driveSyncing); - m_DriveSyncProgress.SetActive(driveSyncing && !driveFull); - m_SyncingDriveIcon.SetActive(driveSyncing && !driveFull); - m_DriveEnabledIcon.SetActive(!driveFull && !driveSyncing && syncEnabled && googleLoggedIn); - m_DriveDisabledIcon.SetActive(!syncEnabled && googleLoggedIn); - m_DriveFullIcon.SetActive(driveFull && syncEnabled && googleLoggedIn); - break; - } - - // Check to see if whether "drive set has sketches" has changed. - bool driveSetHasSketches = - SketchCatalog.m_Instance.GetSet(SketchSetType.Drive).NumSketches != 0; - if (m_DriveSetHasSketches != driveSetHasSketches) - { - m_DriveSetHasSketches = driveSetHasSketches; - OnDriveSetHasSketchesChanged(); - } - } - - // Whether or not the Google Drive set has any sketches impacts how the gallery buttons are - // laid out. - private void OnDriveSetHasSketchesChanged() - { - // Only show the Google Drive gallery tab if there are sketches in there. - int galleryButtonAvailable = m_GalleryButtons.Length; - int galleryButtonN; - if (m_DriveSetHasSketches) - { - m_GalleryButtons[m_ElementNumberGalleryButtonDrive].gameObject.SetActive(true); - galleryButtonN = galleryButtonAvailable; - } - else - { - m_GalleryButtons[m_ElementNumberGalleryButtonDrive].gameObject.SetActive(false); - galleryButtonN = galleryButtonAvailable - 1; - - if (m_CurrentSketchSet == SketchSetType.Drive) - { - // We were on the Drive tab but it's gone away so switch to the local tab by simulating - // the user pressing the local tab button. - ButtonPressed(GalleryButton.Type.Local); - } - } - - // Position the gallery buttons so that they're centered. - float buttonPosY = (0.5f * (galleryButtonN - 1) * m_GalleryButtonHeight - + kGalleryButtonLocalPadding); - for (int i = 0; i < galleryButtonAvailable; i++) - { - if (i == m_ElementNumberGalleryButtonDrive && !m_DriveSetHasSketches) - { - continue; - } - Vector3 buttonPos = m_GalleryButtons[i].transform.localPosition; - buttonPos.y = buttonPosY; - m_GalleryButtons[i].transform.localPosition = buttonPos; - buttonPosY -= m_GalleryButtonHeight; - if (i == kElementNumberGalleryButtonLocal) - { - buttonPosY -= kGalleryButtonLocalPadding; - } - } - } - - // UpdateIcons() is called repeatedly by Update() until these three conditions are met: - // 1: The SketchSet has loaded all the requested icons - // 2: The textures for all the buttons have been set - // 3: (Cloud only) The SketchSet has downloaded the corresponding .tilt files. - // Until the .tilt file is downloaded we set a fade on the button, and need to keep updating - // until the file is downloaded. - private void UpdateIcons() - { - m_AllIconTexturesAssigned = true; - m_AllSketchesAreAvailable = true; - - // Poll sketch catalog until icons have loaded - foreach (BaseButton baseButton in Icons) - { - LoadSketchButton icon = baseButton as LoadSketchButton; - if (icon == null) { continue; } - int iSketchIndex = icon.SketchIndex; - if (m_SketchSet.IsSketchIndexValid(iSketchIndex)) - { - icon.FadeIn = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex).Available ? 1f : 0.5f; - - if (!icon.ThumbnailLoaded) - { - Texture2D rTexture = null; - string[] authors; - string description; - if (m_SketchSet.GetSketchIcon(iSketchIndex, out rTexture, out authors, out description)) - { - if (rTexture != null) - { - // Pass through aspect ratio of image so we don't get squished - // thumbnails from Poly - m_ImageAspect = (float)rTexture.width / rTexture.height; - float aspect = m_ImageAspect; - icon.SetButtonTexture(rTexture, aspect); - } - else - { - icon.SetButtonTexture(m_UnknownImageTexture); - } - - // Mark the texture as assigned regardless of actual bits being valid - icon.ThumbnailLoaded = true; - ; - List lines = new List(); - lines.Add(icon.Description); - - SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); - if (info is IcosaSceneFileInfo polyInfo && - polyInfo.License != VrAssetService.kCreativeCommonsLicense) - { - lines.Add(String.Format("© {0}", authors[0])); - lines.Add("All Rights Reserved"); - } - else - { - // Include primary author in description if available - if (authors != null && authors.Length > 0) - { - lines.Add(authors[0]); - } - // Include an actual description - if (description != null) - { - lines.Add(App.ShortenForDescriptionText(description)); - } - } - icon.SetDescriptionText(lines.ToArray()); - } - else - { - // While metadata has not finished loading, check if this file is valid - bool bFileValid = false; - SceneFileInfo rInfo = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); - if (rInfo != null) - { - bFileValid = rInfo.Exists; - } - - // If this file isn't valid, just keep the defaults and move on - if (!bFileValid) - { - icon.SetButtonTexture(m_UnknownImageTexture); - icon.ThumbnailLoaded = true; - } - else - { - m_AllIconTexturesAssigned = false; - } - if (!rInfo.Available) - { - m_AllSketchesAreAvailable = false; - } - } - } - } - } - } - - override public void OnUpdatePanel(Vector3 vToPanel, Vector3 vHitPoint) - { - base.OnUpdatePanel(vToPanel, vHitPoint); - - // Icons are active when animations aren't. - bool bButtonsAvailable = - (m_CurrentPageFlipState == PageFlipState.Standard) && (m_ActivePopUp == null); - - if (!PanelManager.m_Instance.IntroSketchbookMode) - { - if (bButtonsAvailable && - DoesRayHitCollider(m_ReticleSelectionRay, m_PaintButtonScript.GetCollider())) - { - m_PaintButtonScript.UpdateButtonState(m_InputValid); - } - else - { - m_PaintButtonScript.ResetState(); - } - } - else - { - if (bButtonsAvailable && - DoesRayHitCollider(m_ReticleSelectionRay, m_NewSketchButtonScript.GetCollider())) - { - m_NewSketchButtonScript.UpdateButtonState(m_InputValid); - } - else - { - m_NewSketchButtonScript.ResetState(); - } - } - } - - override protected void OnUpdateActive() - { - // If we're not active, hide all our preview panels - if (!m_GazeActive) - { - m_OnlineGalleryButtonRenderer.material.SetFloat("_Grayscale", 1); - m_ProfileButtonRenderer.material.SetFloat("_Grayscale", 1); - - for (int i = 0; i < m_IconScriptsOnFirstPage.Count; ++i) - { - m_IconScriptsOnFirstPage[i].ResetState(); - } - for (int i = 0; i < m_IconScriptsOnNormalPage.Count; ++i) - { - m_IconScriptsOnNormalPage[i].ResetState(); - } - if (m_NewSketchButtonScript) - { - m_NewSketchButtonScript.ResetState(); - } - if (m_PaintButtonScript) - { - m_PaintButtonScript.ResetState(); - } - } - else if (m_CurrentState == PanelState.Available) - { - m_OnlineGalleryButtonRenderer.material.SetFloat("_Grayscale", 0); - m_ProfileButtonRenderer.material.SetFloat("_Grayscale", 0); - m_SketchSet.RequestRefresh(); - } - } - - override protected void OnUpdateGazeBehavior(Color rPanelColor) - { - // Set the appropriate dim value for all our buttons and sliders - if (Icons != null) - { - foreach (BaseButton icon in Icons) - { - icon.SetColor(rPanelColor); - } - } - - if (m_NewSketchButtonScript != null) - { - m_NewSketchButtonScript.SetColor(rPanelColor); - } - - if (m_NavigationButtons != null) - { - for (int i = 0; i < m_NavigationButtons.Length; ++i) - { - m_NavigationButtons[i].SetColor(rPanelColor); - } - } - } - - override public bool RaycastAgainstMeshCollider(Ray rRay, out RaycastHit rHitInfo, float fDist) - { - if (m_NewSketchButton.GetComponent().Raycast(rRay, out rHitInfo, fDist)) - { - return true; - } - return base.RaycastAgainstMeshCollider(rRay, out rHitInfo, fDist); - } - - // Works specifically with GalleryButtons. - public void ButtonPressed(GalleryButton.Type rType, BaseButton button = null) - { - switch (rType) - { - case GalleryButton.Type.Exit: - SketchSurfacePanel.m_Instance.EnableDefaultTool(); - PointerManager.m_Instance.EatLineEnabledInput(); - break; - case GalleryButton.Type.Showcase: - SetVisibleSketchSet(SketchSetType.Curated); - break; - case GalleryButton.Type.Local: - SetVisibleSketchSet(SketchSetType.User); - break; - case GalleryButton.Type.Liked: - SetVisibleSketchSet(SketchSetType.Liked); - break; - case GalleryButton.Type.Drive: - SetVisibleSketchSet(SketchSetType.Drive); - if (!m_ReadOnlyShown) - { - CreatePopUp(SketchControlsScript.GlobalCommands.ReadOnlyNotice, - -1, -1, m_ReadOnlyPopupOffset); - if (button != null) - { - button.ResetState(); - } - m_ReadOnlyShown = true; - } - break; - default: - break; - } - } - - private void OnSketchSetDirty() - { - ComputeNumPages(); - - SceneFileInfo first = (m_SketchSet.NumSketches > 0) ? - m_SketchSet.GetSketchSceneFileInfo(0) : null; - // If first sketch changed, return to first page. - if (m_FirstSketch != null && !m_FirstSketch.Equals(first)) - { - PageIndex = 0; - } - else - { - PageIndex = Mathf.Min(PageIndex, m_NumPages - 1); - } - m_FirstSketch = first; - GotoPage(PageIndex); - UpdateIndexOffset(); - RefreshPage(); - } - - override protected void UpdateIndexOffset() - { - m_IndexOffset = PageIndex == 0 ? 0 : m_IconsOnFirstPage.Length + (PageIndex - 1) * Icons.Count; - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using UnityEngine.Localization; +using System; +using System.Collections.Generic; +using System.Linq; +using TMPro; + +namespace TiltBrush +{ + + public class SketchbookPanel : ModalPanel + { + // Index of the "local sketches" button in m_GalleryButtons + const int kElementNumberGalleryButtonLocal = 0; + // Amount of extra space to put below the "local sketches" gallery button + const float kGalleryButtonLocalPadding = .15f; + + [SerializeField] private Texture2D m_LoadingImageTexture; + [SerializeField] private Texture2D m_UnknownImageTexture; + [SerializeField] private TextMeshPro m_PanelTextPro; + [SerializeField] private LocalizedString m_PanelTextStandard; + public string PanelTextStandard { get { return m_PanelTextStandard.GetLocalizedStringAsync().Result; } } + [SerializeField] private LocalizedString m_PanelTextShowcase; + public string PanelTextShowcase { get { return m_PanelTextShowcase.GetLocalizedStringAsync().Result; } } + + [SerializeField] private LocalizedString m_PanelTextLiked; + public string PanelTextLiked { get { return m_PanelTextLiked.GetLocalizedStringAsync().Result; } } + [SerializeField] private LocalizedString m_PanelTextDrive; + public string PanelTextDrive { get { return m_PanelTextDrive.GetLocalizedStringAsync().Result; } } + [SerializeField] private GameObject m_NoSketchesMessage; + [SerializeField] private GameObject m_NoDriveSketchesMessage; + [SerializeField] private GameObject m_NoLikesMessage; + [SerializeField] private GameObject m_NotLoggedInMessage; + [SerializeField] private GameObject m_NotLoggedInDriveMessage; + [SerializeField] private GameObject m_NoShowcaseMessage; + [SerializeField] private GameObject m_ContactingServerMessage; + [SerializeField] private GameObject m_OutOfDateMessage; + [SerializeField] private GameObject m_NoPolyConnectionMessage; + [SerializeField] private Renderer m_OnlineGalleryButtonRenderer; + [SerializeField] private GameObject[] m_IconsOnFirstPage; + [SerializeField] private GameObject[] m_IconsOnNormalPage; + [SerializeField] private GameObject m_CloseButton; + [SerializeField] private GameObject m_NewSketchButton; + // Gallery buttons will automatically reposition based on how many are visible so they must be + // added to this array in order from top to bottom. + [SerializeField] private GalleryButton[] m_GalleryButtons; + [SerializeField] private int m_ElementNumberGalleryButtonDrive = 3; + [SerializeField] private float m_GalleryButtonHeight = 0.3186f; + [SerializeField] private Renderer m_ProfileButtonRenderer; + [SerializeField] private GameObject m_LoadingGallery; + [SerializeField] private GameObject m_DriveSyncProgress; + [SerializeField] private GameObject m_SyncingDriveIcon; + [SerializeField] private GameObject m_DriveEnabledIcon; + [SerializeField] private GameObject m_DriveDisabledIcon; + [SerializeField] private GameObject m_DriveFullIcon; + [SerializeField] private Vector2 m_SketchIconUvScale = new Vector2(0.7f, 0.7f); + [SerializeField] private Vector3 m_ReadOnlyPopupOffset; + + private float m_ImageAspect; + private Vector2 m_HalfInvUvScale; + + private SceneFileInfo m_FirstSketch; + + private bool m_AllIconTexturesAssigned; + private bool m_AllSketchesAreAvailable; + private SketchSetType m_CurrentSketchSet; + private SketchSet m_SketchSet; + private OptionButton m_NewSketchButtonScript; + private OptionButton m_PaintButtonScript; + private List m_IconScriptsOnFirstPage; + private List m_IconScriptsOnNormalPage; + private bool m_DriveSetHasSketches; + private bool m_ReadOnlyShown = false; + + public float ImageAspect { get { return m_ImageAspect; } } + + override public void SetInIntroMode(bool inIntro) + { + m_NewSketchButton.SetActive(inIntro); + m_CloseButton.SetActive(!inIntro); + + // When we switch in to intro mode, make our panel colorful, even if it doesn't have focus, + // to help attract attention. + if (inIntro) + { + for (int i = 0; i < m_IconScriptsOnFirstPage.Count; ++i) + { + m_IconScriptsOnFirstPage[i].SetButtonGrayscale(false); + } + for (int i = 0; i < m_IconScriptsOnNormalPage.Count; ++i) + { + m_IconScriptsOnNormalPage[i].SetButtonGrayscale(false); + } + } + } + + protected override List Icons + { + get + { + return (PageIndex == 0 ? m_IconScriptsOnFirstPage : m_IconScriptsOnNormalPage); + } + } + + public override bool IsInButtonMode(ModeButton button) + { + GalleryButton galleryButton = button as GalleryButton; + return galleryButton && + ((galleryButton.m_ButtonType == GalleryButton.Type.Liked && m_CurrentSketchSet == SketchSetType.Liked) || + (galleryButton.m_ButtonType == GalleryButton.Type.Local && m_CurrentSketchSet == SketchSetType.User) || + (galleryButton.m_ButtonType == GalleryButton.Type.Showcase && m_CurrentSketchSet == SketchSetType.Curated) || + (galleryButton.m_ButtonType == GalleryButton.Type.Drive && m_CurrentSketchSet == SketchSetType.Drive)); + } + + override public void InitPanel() + { + base.InitPanel(); + + m_NewSketchButtonScript = m_NewSketchButton.GetComponent(); + m_PaintButtonScript = m_CloseButton.GetComponent(); + m_IconScriptsOnFirstPage = new List(); + for (int i = 0; i < m_IconsOnFirstPage.Length; ++i) + { + m_IconScriptsOnFirstPage.Add(m_IconsOnFirstPage[i].GetComponent()); + } + m_IconScriptsOnNormalPage = new List(); + for (int i = 0; i < m_IconsOnNormalPage.Length; ++i) + { + m_IconScriptsOnNormalPage.Add(m_IconsOnNormalPage[i].GetComponent()); + } + SetInIntroMode(false); + + Debug.Assert(m_SketchIconUvScale.x >= 0.0f && m_SketchIconUvScale.x <= 1.0f && + m_SketchIconUvScale.y >= 0.0f && m_SketchIconUvScale.y <= 1.0f); + m_HalfInvUvScale.Set(1.0f - m_SketchIconUvScale.x, 1.0f - m_SketchIconUvScale.y); + m_HalfInvUvScale *= 0.5f; + } + + protected override void OnStart() + { + // Initialize icons. + LoadSketchButton[] rPanelButtons = m_Mesh.GetComponentsInChildren(); + foreach (LoadSketchButton icon in rPanelButtons) + { + GameObject go = icon.gameObject; + go.SetActive(false); + } + + // GameObject is active in prefab so the button registers. + m_NoLikesMessage.SetActive(false); + m_NotLoggedInMessage.SetActive(false); + m_NotLoggedInDriveMessage.SetActive(false); + + // Dynamically position the gallery buttons. + OnDriveSetHasSketchesChanged(); + + // Set the sketch set var to Liked, then function set to force state. + m_CurrentSketchSet = SketchSetType.Liked; + SetVisibleSketchSet(SketchSetType.User); + + Action refresh = () => + { + if (m_ContactingServerMessage.activeSelf || + m_NoShowcaseMessage.activeSelf || + m_LoadingGallery.activeSelf) + { + // Update the overlays more frequently when these overlays are shown to reflect whether + // we are actively trying to get sketches from Poly. + RefreshPage(); + } + }; + SketchCatalog.m_Instance.GetSet(SketchSetType.Liked).OnSketchRefreshingChanged += refresh; + SketchCatalog.m_Instance.GetSet(SketchSetType.Curated).OnSketchRefreshingChanged += refresh; + SketchCatalog.m_Instance.GetSet(SketchSetType.Drive).OnSketchRefreshingChanged += refresh; + App.GoogleIdentity.OnLogout += refresh; + } + + void OnDestroy() + { + if (m_SketchSet != null) + { + m_SketchSet.OnChanged -= OnSketchSetDirty; + } + } + + override protected void OnEnablePanel() + { + base.OnEnablePanel(); + if (m_SketchSet != null) + { + m_SketchSet.RequestRefresh(); + } + } + + void SetVisibleSketchSet(SketchSetType type) + { + if (m_CurrentSketchSet != type) + { + // Clean up our old sketch set. + if (m_SketchSet != null) + { + m_SketchSet.OnChanged -= OnSketchSetDirty; + } + + // Cache new set. + m_SketchSet = SketchCatalog.m_Instance.GetSet(type); + m_SketchSet.OnChanged += OnSketchSetDirty; + m_SketchSet.RequestRefresh(); + + // Tell all the icons which set to reference when loading sketches. + IEnumerable allIcons = m_IconsOnFirstPage.Concat(m_IconsOnNormalPage) + .Select(icon => icon.GetComponent()) + .Where(icon => icon != null); + foreach (LoadSketchButton icon in allIcons) + { + icon.SketchSet = m_SketchSet; + } + + ComputeNumPages(); + ResetPageIndex(); + m_CurrentSketchSet = type; + RefreshPage(); + + switch (m_CurrentSketchSet) + { + case SketchSetType.User: + m_PanelTextPro.text = PanelTextStandard; + break; + case SketchSetType.Curated: + m_PanelTextPro.text = PanelTextShowcase; + break; + case SketchSetType.Liked: + m_PanelTextPro.text = PanelTextLiked; + break; + case SketchSetType.Drive: + m_PanelTextPro.text = PanelTextDrive; + break; + } + } + } + + private void ComputeNumPages() + { + if (m_SketchSet.NumSketches <= m_IconsOnFirstPage.Length) + { + m_NumPages = 1; + return; + } + int remainingSketches = m_SketchSet.NumSketches - m_IconsOnFirstPage.Length; + int normalPages = ((remainingSketches - 1) / m_IconsOnNormalPage.Length) + 1; + m_NumPages = 1 + normalPages; + } + + List GetIconLoadIndices() + { + var ret = new List(); + for (int i = 0; i < Icons.Count; i++) + { + int sketchIndex = m_IndexOffset + i; + if (sketchIndex >= m_SketchSet.NumSketches) + { + break; + } + ret.Add(sketchIndex); + } + return ret; + } + + protected override void RefreshPage() + { + m_SketchSet.RequestOnlyLoadedMetadata(GetIconLoadIndices()); + m_AllIconTexturesAssigned = false; + m_AllSketchesAreAvailable = false; + + // Disable all. + foreach (var i in m_IconsOnFirstPage) + { + i.SetActive(false); + } + foreach (var i in m_IconsOnNormalPage) + { + i.SetActive(false); + } + + // Base Refresh updates the modal parts of the panel, and we always want those refreshed. + base.RefreshPage(); + + bool requiresPoly = m_CurrentSketchSet == SketchSetType.Liked; + + bool polyDown = VrAssetService.m_Instance.NoConnection && requiresPoly; + m_NoPolyConnectionMessage.SetActive(polyDown); + + bool outOfDate = !polyDown && !VrAssetService.m_Instance.Available && requiresPoly; + m_OutOfDateMessage.SetActive(outOfDate); + + if (outOfDate || polyDown) + { + m_NoSketchesMessage.SetActive(false); + m_NoDriveSketchesMessage.SetActive(false); + m_NotLoggedInMessage.SetActive(false); + m_NoLikesMessage.SetActive(false); + m_ContactingServerMessage.SetActive(false); + m_NoShowcaseMessage.SetActive(false); + return; + } + + bool refreshIcons = m_SketchSet.NumSketches > 0; + + // Show no sketches if we don't have sketches. + m_NoSketchesMessage.SetActive( + (m_CurrentSketchSet == SketchSetType.User) && (m_SketchSet.NumSketches <= 0)); + m_NoDriveSketchesMessage.SetActive( + (m_CurrentSketchSet == SketchSetType.Drive) && (m_SketchSet.NumSketches <= 0)); + + // Show sign in popup if signed out for liked or drive sketchsets + bool showIcosaNotLoggedIn = !App.IcosaIsLoggedIn && m_CurrentSketchSet == SketchSetType.Liked; + bool showGoogleNotLoggedIn = !App.GoogleIdentity.LoggedIn && m_CurrentSketchSet == SketchSetType.Drive; + refreshIcons = refreshIcons && !showIcosaNotLoggedIn; + m_NotLoggedInMessage.SetActive(showIcosaNotLoggedIn && m_CurrentSketchSet == SketchSetType.Liked); + m_NotLoggedInDriveMessage.SetActive(showGoogleNotLoggedIn); + + // Show no likes text & gallery button if we don't have liked sketches. + m_NoLikesMessage.SetActive( + (m_CurrentSketchSet == SketchSetType.Liked) && + (m_SketchSet.NumSketches <= 0) && + !m_SketchSet.IsActivelyRefreshingSketches && + App.IcosaIsLoggedIn); + + // Show Contacting Server if we're talking to Drive. + m_ContactingServerMessage.SetActive( + (requiresPoly || + m_CurrentSketchSet == SketchSetType.Drive) && + (m_SketchSet.NumSketches <= 0) && + (m_SketchSet.IsActivelyRefreshingSketches && App.GoogleIdentity.LoggedIn)); + + // Show Showcase error if we're in Showcase and don't have sketches. + m_NoShowcaseMessage.SetActive( + (m_CurrentSketchSet == SketchSetType.Curated) && + (m_SketchSet.NumSketches <= 0) && + !m_SketchSet.IsActivelyRefreshingSketches); + + // Refresh all icons if necessary. + if (!refreshIcons) + { + return; + } + + for (int i = 0; i < Icons.Count; i++) + { + LoadSketchButton icon = Icons[i] as LoadSketchButton; + // Default to loading image + icon.SetButtonTexture(m_LoadingImageTexture); + icon.ThumbnailLoaded = false; + + // Set sketch index relative to page based index + int iSketchIndex = m_IndexOffset + i; + if (iSketchIndex >= m_SketchSet.NumSketches) + { + iSketchIndex = -1; + } + icon.SketchIndex = iSketchIndex; + icon.ResetScale(); + + // Init icon according to availability of sketch + GameObject go = icon.gameObject; + if (m_SketchSet.IsSketchIndexValid(iSketchIndex)) + { + string sSketchName = m_SketchSet.GetSketchName(iSketchIndex); + icon.SetDescriptionText(App.ShortenForDescriptionText(sSketchName)); + SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); + if (info.Available) + { + m_SketchSet.PrecacheSketchModels(iSketchIndex); + } + + if (info.TriangleCount is int triCount) + { + icon.WarningVisible = triCount > + QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles; + } + else + { + icon.WarningVisible = false; + } + go.SetActive(true); + } + else + { + go.SetActive(false); + } + } + } + + void Update() + { + BaseUpdate(); + PageFlipUpdate(); + + // Refresh icons while they are in flux + if (m_SketchSet.IsReadyForAccess && + (!m_SketchSet.RequestedIconsAreLoaded || + !m_AllIconTexturesAssigned || !m_AllSketchesAreAvailable)) + { + UpdateIcons(); + } + + // Set icon uv offsets relative to head position. + Vector3 head_LS = m_Mesh.transform.InverseTransformPoint(ViewpointScript.Head.position); + float angleX = Vector3.Angle(Vector3.back, new Vector3(head_LS.x, 0.0f, head_LS.z)); + angleX *= (head_LS.x > 0.0f) ? -1.0f : 1.0f; + + float angleY = Vector3.Angle(Vector3.back, new Vector3(0.0f, head_LS.y, head_LS.z)); + angleY *= (head_LS.y > 0.0f) ? -1.0f : 1.0f; + + float maxAngleXRatio = angleX / 90.0f; + float maxAngleYRatio = angleY / 90.0f; + Vector2 offset = new Vector2( + m_HalfInvUvScale.x + (m_HalfInvUvScale.x * maxAngleXRatio), + m_HalfInvUvScale.y + (m_HalfInvUvScale.y * maxAngleYRatio)); + for (int i = 0; i < Icons.Count; i++) + { + LoadSketchButton icon = Icons[i] as LoadSketchButton; + icon.UpdateUvOffsetAndScale(offset, m_SketchIconUvScale); + } + + switch (m_CurrentSketchSet) + { + case SketchSetType.Curated: + m_LoadingGallery.SetActive(m_SketchSet.IsActivelyRefreshingSketches); + m_DriveSyncProgress.SetActive(false); + m_SyncingDriveIcon.SetActive(false); + m_DriveEnabledIcon.SetActive(false); + m_DriveDisabledIcon.SetActive(false); + m_DriveFullIcon.SetActive(false); + break; + case SketchSetType.Liked: + m_LoadingGallery.SetActive(false); + m_DriveSyncProgress.SetActive(false); + m_SyncingDriveIcon.SetActive(false); + m_DriveEnabledIcon.SetActive(false); + m_DriveDisabledIcon.SetActive(false); + m_DriveFullIcon.SetActive(false); + break; + case SketchSetType.User: + case SketchSetType.Drive: + bool sketchSetRefreshing = m_CurrentSketchSet == SketchSetType.Drive && + m_SketchSet.IsActivelyRefreshingSketches; + bool driveSyncing = App.DriveSync.Syncing; + bool syncEnabled = App.DriveSync.SyncEnabled; + bool googleLoggedIn = App.GoogleIdentity.LoggedIn; + bool driveFull = App.DriveSync.DriveIsLowOnSpace; + m_LoadingGallery.SetActive(sketchSetRefreshing && !driveSyncing); + m_DriveSyncProgress.SetActive(driveSyncing && !driveFull); + m_SyncingDriveIcon.SetActive(driveSyncing && !driveFull); + m_DriveEnabledIcon.SetActive(!driveFull && !driveSyncing && syncEnabled && googleLoggedIn); + m_DriveDisabledIcon.SetActive(!syncEnabled && googleLoggedIn); + m_DriveFullIcon.SetActive(driveFull && syncEnabled && googleLoggedIn); + break; + } + + // Check to see if whether "drive set has sketches" has changed. + bool driveSetHasSketches = + SketchCatalog.m_Instance.GetSet(SketchSetType.Drive).NumSketches != 0; + if (m_DriveSetHasSketches != driveSetHasSketches) + { + m_DriveSetHasSketches = driveSetHasSketches; + OnDriveSetHasSketchesChanged(); + } + } + + // Whether or not the Google Drive set has any sketches impacts how the gallery buttons are + // laid out. + private void OnDriveSetHasSketchesChanged() + { + // Only show the Google Drive gallery tab if there are sketches in there. + int galleryButtonAvailable = m_GalleryButtons.Length; + int galleryButtonN; + if (m_DriveSetHasSketches) + { + m_GalleryButtons[m_ElementNumberGalleryButtonDrive].gameObject.SetActive(true); + galleryButtonN = galleryButtonAvailable; + } + else + { + m_GalleryButtons[m_ElementNumberGalleryButtonDrive].gameObject.SetActive(false); + galleryButtonN = galleryButtonAvailable - 1; + + if (m_CurrentSketchSet == SketchSetType.Drive) + { + // We were on the Drive tab but it's gone away so switch to the local tab by simulating + // the user pressing the local tab button. + ButtonPressed(GalleryButton.Type.Local); + } + } + + // Position the gallery buttons so that they're centered. + float buttonPosY = (0.5f * (galleryButtonN - 1) * m_GalleryButtonHeight + + kGalleryButtonLocalPadding); + for (int i = 0; i < galleryButtonAvailable; i++) + { + if (i == m_ElementNumberGalleryButtonDrive && !m_DriveSetHasSketches) + { + continue; + } + Vector3 buttonPos = m_GalleryButtons[i].transform.localPosition; + buttonPos.y = buttonPosY; + m_GalleryButtons[i].transform.localPosition = buttonPos; + buttonPosY -= m_GalleryButtonHeight; + if (i == kElementNumberGalleryButtonLocal) + { + buttonPosY -= kGalleryButtonLocalPadding; + } + } + } + + // UpdateIcons() is called repeatedly by Update() until these three conditions are met: + // 1: The SketchSet has loaded all the requested icons + // 2: The textures for all the buttons have been set + // 3: (Cloud only) The SketchSet has downloaded the corresponding .tilt files. + // Until the .tilt file is downloaded we set a fade on the button, and need to keep updating + // until the file is downloaded. + private void UpdateIcons() + { + m_AllIconTexturesAssigned = true; + m_AllSketchesAreAvailable = true; + + // Poll sketch catalog until icons have loaded + foreach (BaseButton baseButton in Icons) + { + LoadSketchButton icon = baseButton as LoadSketchButton; + if (icon == null) { continue; } + int iSketchIndex = icon.SketchIndex; + if (m_SketchSet.IsSketchIndexValid(iSketchIndex)) + { + icon.FadeIn = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex).Available ? 1f : 0.5f; + + if (!icon.ThumbnailLoaded) + { + Texture2D rTexture = null; + string[] authors; + string description; + if (m_SketchSet.GetSketchIcon(iSketchIndex, out rTexture, out authors, out description)) + { + if (rTexture != null) + { + // Pass through aspect ratio of image so we don't get squished + // thumbnails from Poly + m_ImageAspect = (float)rTexture.width / rTexture.height; + float aspect = m_ImageAspect; + icon.SetButtonTexture(rTexture, aspect); + } + else + { + icon.SetButtonTexture(m_UnknownImageTexture); + } + + // Mark the texture as assigned regardless of actual bits being valid + icon.ThumbnailLoaded = true; + ; + List lines = new List(); + lines.Add(icon.Description); + + SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); + if (info is IcosaSceneFileInfo polyInfo && + polyInfo.License != VrAssetService.kCreativeCommonsLicense) + { + lines.Add(String.Format("© {0}", authors[0])); + lines.Add("All Rights Reserved"); + } + else + { + // Include primary author in description if available + if (authors != null && authors.Length > 0) + { + lines.Add(authors[0]); + } + // Include an actual description + if (description != null) + { + lines.Add(App.ShortenForDescriptionText(description)); + } + } + icon.SetDescriptionText(lines.ToArray()); + } + else + { + // While metadata has not finished loading, check if this file is valid + bool bFileValid = false; + SceneFileInfo rInfo = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); + if (rInfo != null) + { + bFileValid = rInfo.Exists; + } + + // If this file isn't valid, just keep the defaults and move on + if (!bFileValid) + { + icon.SetButtonTexture(m_UnknownImageTexture); + icon.ThumbnailLoaded = true; + } + else + { + m_AllIconTexturesAssigned = false; + } + if (!rInfo.Available) + { + m_AllSketchesAreAvailable = false; + } + } + } + } + } + } + + override public void OnUpdatePanel(Vector3 vToPanel, Vector3 vHitPoint) + { + base.OnUpdatePanel(vToPanel, vHitPoint); + + // Icons are active when animations aren't. + bool bButtonsAvailable = + (m_CurrentPageFlipState == PageFlipState.Standard) && (m_ActivePopUp == null); + + if (!PanelManager.m_Instance.IntroSketchbookMode) + { + if (bButtonsAvailable && + DoesRayHitCollider(m_ReticleSelectionRay, m_PaintButtonScript.GetCollider())) + { + m_PaintButtonScript.UpdateButtonState(m_InputValid); + } + else + { + m_PaintButtonScript.ResetState(); + } + } + else + { + if (bButtonsAvailable && + DoesRayHitCollider(m_ReticleSelectionRay, m_NewSketchButtonScript.GetCollider())) + { + m_NewSketchButtonScript.UpdateButtonState(m_InputValid); + } + else + { + m_NewSketchButtonScript.ResetState(); + } + } + } + + override protected void OnUpdateActive() + { + // If we're not active, hide all our preview panels + if (!m_GazeActive) + { + m_OnlineGalleryButtonRenderer.material.SetFloat("_Grayscale", 1); + m_ProfileButtonRenderer.material.SetFloat("_Grayscale", 1); + + for (int i = 0; i < m_IconScriptsOnFirstPage.Count; ++i) + { + m_IconScriptsOnFirstPage[i].ResetState(); + } + for (int i = 0; i < m_IconScriptsOnNormalPage.Count; ++i) + { + m_IconScriptsOnNormalPage[i].ResetState(); + } + if (m_NewSketchButtonScript) + { + m_NewSketchButtonScript.ResetState(); + } + if (m_PaintButtonScript) + { + m_PaintButtonScript.ResetState(); + } + } + else if (m_CurrentState == PanelState.Available) + { + m_OnlineGalleryButtonRenderer.material.SetFloat("_Grayscale", 0); + m_ProfileButtonRenderer.material.SetFloat("_Grayscale", 0); + m_SketchSet.RequestRefresh(); + } + } + + override protected void OnUpdateGazeBehavior(Color rPanelColor) + { + // Set the appropriate dim value for all our buttons and sliders + if (Icons != null) + { + foreach (BaseButton icon in Icons) + { + icon.SetColor(rPanelColor); + } + } + + if (m_NewSketchButtonScript != null) + { + m_NewSketchButtonScript.SetColor(rPanelColor); + } + + if (m_NavigationButtons != null) + { + for (int i = 0; i < m_NavigationButtons.Length; ++i) + { + m_NavigationButtons[i].SetColor(rPanelColor); + } + } + } + + override public bool RaycastAgainstMeshCollider(Ray rRay, out RaycastHit rHitInfo, float fDist) + { + if (m_NewSketchButton.GetComponent().Raycast(rRay, out rHitInfo, fDist)) + { + return true; + } + return base.RaycastAgainstMeshCollider(rRay, out rHitInfo, fDist); + } + + // Works specifically with GalleryButtons. + public void ButtonPressed(GalleryButton.Type rType, BaseButton button = null) + { + switch (rType) + { + case GalleryButton.Type.Exit: + SketchSurfacePanel.m_Instance.EnableDefaultTool(); + PointerManager.m_Instance.EatLineEnabledInput(); + break; + case GalleryButton.Type.Showcase: + SetVisibleSketchSet(SketchSetType.Curated); + break; + case GalleryButton.Type.Local: + SetVisibleSketchSet(SketchSetType.User); + break; + case GalleryButton.Type.Liked: + SetVisibleSketchSet(SketchSetType.Liked); + break; + case GalleryButton.Type.Drive: + SetVisibleSketchSet(SketchSetType.Drive); + if (!m_ReadOnlyShown) + { + CreatePopUp(SketchControlsScript.GlobalCommands.ReadOnlyNotice, + -1, -1, m_ReadOnlyPopupOffset); + if (button != null) + { + button.ResetState(); + } + m_ReadOnlyShown = true; + } + break; + default: + break; + } + } + + private void OnSketchSetDirty() + { + ComputeNumPages(); + + SceneFileInfo first = (m_SketchSet.NumSketches > 0) ? + m_SketchSet.GetSketchSceneFileInfo(0) : null; + // If first sketch changed, return to first page. + if (m_FirstSketch != null && !m_FirstSketch.Equals(first)) + { + PageIndex = 0; + } + else + { + PageIndex = Mathf.Min(PageIndex, m_NumPages - 1); + } + m_FirstSketch = first; + GotoPage(PageIndex); + UpdateIndexOffset(); + RefreshPage(); + } + + override protected void UpdateIndexOffset() + { + m_IndexOffset = PageIndex == 0 ? 0 : m_IconsOnFirstPage.Length + (PageIndex - 1) * Icons.Count; + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Model.cs b/Assets/Scripts/Model.cs index a01a5e2553..891e2f4353 100644 --- a/Assets/Scripts/Model.cs +++ b/Assets/Scripts/Model.cs @@ -1,961 +1,961 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using UnityEngine; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using TiltBrushToolkit; -using Unity.VectorGraphics; -using Debug = UnityEngine.Debug; -using UObject = UnityEngine.Object; - - -namespace TiltBrush -{ - - public class Model - { - public struct Location - { - public enum Type - { - Invalid, - LocalFile, - PolyAssetId - } - - private Type type; - private string path; - private string id; // Only valid when the type is PolyAssetId. - - public static Location File(string relativePath) - { - int lastIndex = relativePath.LastIndexOf('#'); - string path, fragment; - - if (lastIndex == -1) - { - path = relativePath; - fragment = null; - } - else - { - path = relativePath.Substring(0, lastIndex); - fragment = relativePath.Substring(lastIndex + 1); - } - return new Location - { - type = Type.LocalFile, - path = path, - }; - } - - public static Location PolyAsset(string assetId, string path) - { - return new Location - { - type = Type.PolyAssetId, - path = path, - id = assetId - }; - } - - /// Can return null if this is a location for a fake Model (like the ones ModelWidget - /// assigns itself while the real Model content is in progress of being loaded). - public string AbsolutePath - { - get - { - if (path == null) - { - return null; - } - switch (type) - { - case Type.LocalFile: - return Path.Combine(App.ModelLibraryPath(), path).Replace("\\", "/"); - case Type.PolyAssetId: - return path.Replace("\\", "/"); - } - return null; - } - } - - public string RelativePath - { - get - { - if (type == Type.LocalFile) { return path; } - throw new Exception("Invalid relative path request"); - } - } - - public string Extension => Path.GetExtension(AbsolutePath).ToLower(); - - public string AssetId - { - get - { - if (type == Type.PolyAssetId) { return id; } - throw new Exception("Invalid Poly asset id request"); - } - } - - public Type GetLocationType() { return type; } - - public override int GetHashCode() - { - return this.ToString().GetHashCode(); - } - - public override string ToString() - { - string str; - if (type == Type.PolyAssetId) - { - str = $"{type}:{id}"; - } - else - { - str = $"{type}:{path}"; - } - return str; - } - - public override bool Equals(object obj) - { - if (!(obj is Location)) - { - return false; - } - return this == (Location)obj; - } - - public static bool operator ==(Location a, Location b) - { - return a.type == b.type && a.path == b.path; - } - - public static bool operator !=(Location a, Location b) - { - return !(a == b); - } - } - - private static readonly float kMeshMsPerFrame = 1.0f; - - private static readonly GltfImportOptions kPolyGltfImportOptions = new GltfImportOptions - { - rescalingMode = GltfImportOptions.RescalingMode.CONVERT, - scaleFactor = App.METERS_TO_UNITS, - axisConventionOverride = AxisConvention.kGltfAccordingToIcosa, - recenter = false - }; - - private static readonly GltfImportOptions kGltfImportOptions = new GltfImportOptions - { - rescalingMode = GltfImportOptions.RescalingMode.CONVERT, - scaleFactor = App.METERS_TO_UNITS, - recenter = false - }; - - static RateLimiter sm_Limiter = new RateLimiter(maxEventsPerFrame: 1); - - // This is the object that is cloned when attached to a button or widget. - // It is the object that contains ObjModelScript. - public Transform m_ModelParent; - public Bounds m_MeshBounds; - - // Data & properties associated with current the state: - // - Unloaded - // - Trying to be loaded - // - Load finished successfully - // - Load finished unsuccessfully - // Not all of these states are modeled explicitly; this is a WIP. - - public struct LoadError - { - public LoadError(string message, string detail = null) - { - this.message = message; - this.detail = detail; - } - public readonly string message; // Human-readable short message - public readonly string detail; // Maybe non-human-readable details - // maybe? public bool transient; // true if we know for sure that this error is transient - } - - /// Is m_ModelParent assigned? - /// m_Valid = true implies m_LoadError == null - public bool m_Valid; - - /// m_LoadError != null implies m_Valid == false - private LoadError? m_LoadError; - public LoadError? Error => m_LoadError; - - // How many widgets are using this model? - public int m_UsageCount; - - private Location m_Location; - - // Can the geometry in this model be exported. - private bool m_AllowExport; - - private ImportMaterialCollector m_ImportMaterialCollector; - - // Returns the path starting after Media Library/Models - // e.g. subdirectory/example.obj - public string RelativePath - { - get { return m_Location.RelativePath; } - } - - public string AssetId - { - get { return m_Location.AssetId; } - } - - public string HumanName - { - get { return Path.GetFileNameWithoutExtension(m_Location.RelativePath); } - } - - public bool AllowExport - { - get { return m_AllowExport; } - } - - /// Only allowed if AllowExport = true - public IExportableMaterial GetExportableMaterial(Material material) - { - EnsureCollectorExists(); - return m_ImportMaterialCollector.GetExportableMaterial(material); - } - - public Model(Location location) - { - m_Location = location; - } - - public Location GetLocation() { return m_Location; } - - /// A helper class which allows import to run I/O on a background thread before producing Unity - /// GameObject(s). Usage: - /// BeginAsyncLoad() - /// TryEndAsyncLoad() repeat until it returns true; a bit of work is done each time(*) - /// CancelAsyncLoad() if you give up waiting for it to return true - /// - /// (*) Although the caller appears to have the responsibility of a scheduler, ModelBuilder - /// actually implements its own rate-limiting. After a set number of calls to TryEndAsyncLoad - /// in one frame (whether on one or many objects), further calls will be very fast no-ops. - /// So the caller can be (should be) a naive, performance-unaware scheduler that creates and - /// pumps as many ModelBuilders as it likes. - /// - /// However, note that this does not apply to the background-thread work. Nothing managages - /// that, so if N ModelBuilders are instantiated, N background threads will start running - /// and competing with each other. Your implementation may want to restrict that work to I/O. - abstract class ModelBuilder - { - protected string m_localPath; - private Future m_stateReader; - private IEnumerator m_meshEnumerator; - private ImportMaterialCollector m_ImportMaterialCollector; - private GameObject m_root; - - /// In the current implementation: - /// Before the first call to TryEndAsyncLoad, do not look at IsValid. - /// After the first call to TryEndAsyncLoad, IsValid is always true. - /// - /// TODO: semantics of IsValid = false are unclear and DoUnityThreadWork looks buggy - /// It's unclear if the intent is that the user should continue calling TryEndAsyncLoad - /// until it returns true, or if they should stop calling TryEndAsyncLoad. etc. Probably - /// we should remove this. - public bool IsValid - { - get; - protected set; - } - - public ModelBuilder(string localPath) - { - m_localPath = localPath; - IsValid = false; - } - - public void BeginAsyncLoad() - { - if (m_stateReader != null) - { - throw new ApplicationException("BeginImport should only be called once."); - } - - m_stateReader = new Future(DoBackgroundThreadWork, id => id.Dispose()); - } - - public void CancelAsyncLoad() - { - // If we have already created a mesh, we need to destroy it and the gameobject it is on so - // that we don't leave it orphaned in the heirarchy, and we don't leak meshes. - if (m_root != null) - { - foreach (var mesh in m_root.GetComponentsInChildren() - .Select(x => x.sharedMesh)) - { - UObject.Destroy(mesh); - } - UObject.Destroy(m_root); - m_root = null; - } - m_stateReader.Close(); - } - - /// Returns: - /// bool - false if incomplete, true upon successful completion. - /// GameObject - caller should check output GameObject to determine success. - /// ImportMaterialCollector - non-null upon successful completion. - /// Raises an exception on unsuccessful completion. - public bool TryEndAsyncLoad(out GameObject root, - out ImportMaterialCollector importMaterialCollector) - { - // Three things happen in this function. - // 1: It waits to try and get the result of reading the model on a background thread - // 2: It checks the rate limiter to make sure we don't have too many of these going on at once. - // 3: It enumerates through, creating meshes for the model. These are time-limited so that - // it will stop if it has taken too long in a single frame. - root = null; - importMaterialCollector = null; - if (m_meshEnumerator == null) - { - IDisposable state; - if (!m_stateReader.TryGetResult(out state)) { return false; } - - IEnumerable enumerable; - m_root = DoUnityThreadWork(state, out enumerable, out m_ImportMaterialCollector); - // TODO: Possible bugs if DoUnityThreadWork ever did fail: - // We assume the invariant that (root == null) == (IsValid == false) - // We assume the invariant that m_ImportMaterialCollector != null - // We don't dispose the GameObject or the enumerable - // If the user calls TryEndAsyncLoad again we might try to call DoUnityThreadWork again - if (m_root == null) - { - return false; - } - m_ImportMaterialCollector = new ImportMaterialCollector( - Path.GetDirectoryName(m_localPath), - uniqueSeed: m_localPath - ); - m_meshEnumerator = enumerable.GetEnumerator(); - m_root.SetActive(false); - } - // Yield until the limiter unblocks. - // Multiple calls to TryGetResult above are harmless. - if (sm_Limiter.IsBlocked()) - { - return false; - } - - // Finish constructing the actual game object. - Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); - long numTicks = (long)((Stopwatch.Frequency * kMeshMsPerFrame) / 1000); - while (true) - { - if (!m_meshEnumerator.MoveNext()) - { - m_root.SetActive(true); - root = m_root; - importMaterialCollector = m_ImportMaterialCollector; - stopwatch.Stop(); - return true; - } - if (stopwatch.ElapsedTicks > numTicks) - { - stopwatch.Stop(); - return false; - } - } - } - - // Performs whatever of the import process that can happen on a non-Unity thread. - // Returns: - // disposable - passed to DoUnityThreadWork, or disposed of if the load is canceled. - protected abstract IDisposable DoBackgroundThreadWork(); - - // Performs whatever portion of the import process that is left. - // - // Pass: - // state - the value returned from DoBackgroundThreadWork. Ownership is transferred; - // callee is responsible for Disposing it. - // Returns: - // GameObject - the root of the object hierarchy. - // ImportMaterialCollector - the materials that were created, and info about them - // IEnumerable - a coroutine that will be pumped to completion - protected abstract GameObject DoUnityThreadWork( - IDisposable state, - out IEnumerable meshCreator, - out ImportMaterialCollector importMaterialCollector); - } - - /// The glTF ModelBuilder. - class GltfModelBuilder : ModelBuilder - { - private readonly bool m_useThreadedImageLoad; - private readonly bool m_fromPoly; - - public GltfModelBuilder(Location location, bool useThreadedImageLoad) - : base(location.AbsolutePath) - { - m_useThreadedImageLoad = useThreadedImageLoad; - m_fromPoly = (location.GetLocationType() == Location.Type.PolyAssetId); - } - - protected override IDisposable DoBackgroundThreadWork() - { - var loader = new TiltBrushUriLoader( - m_localPath, Path.GetDirectoryName(m_localPath), m_useThreadedImageLoad); - var options = m_fromPoly ? kPolyGltfImportOptions : kGltfImportOptions; - return NewGltfImporter.BeginImport(m_localPath); - } - - protected override GameObject DoUnityThreadWork(IDisposable state__, - out IEnumerable meshEnumerable, - out ImportMaterialCollector - importMaterialCollector) - { - meshEnumerable = null; - importMaterialCollector = null; - GameObject rootObject = null; - using (IDisposable state_ = state__) - { - var state = state_ as NewGltfImporter.ImportState; - if (state != null) - { - string assetLocation = Path.GetDirectoryName(m_localPath); - // EndImport doesn't try to use the loadImages functionality of UriLoader anyway. - // It knows it's on the main thread, so chooses to use Unity's fast loading. - rootObject = state.root; - importMaterialCollector = new ImportMaterialCollector(assetLocation, uniqueSeed: m_localPath); - } - } - IsValid = rootObject != null; - return rootObject; - } - } // GltfModelBuilder - - GameObject LoadUsd(List warnings) - { -#if USD_SUPPORTED - return ImportUsd.Import(m_Location.AbsolutePath, out warnings); -#endif - m_LoadError = new LoadError("usd not supported"); - return null; - } - - GameObject LoadPly(List warningsOut) - { - - try - { - var reader = new PlyReader(m_Location.AbsolutePath); - var (gameObject, warnings, collector) = reader.Import(); - warningsOut.AddRange(warnings); - m_ImportMaterialCollector = collector; - m_AllowExport = (m_ImportMaterialCollector != null); - return gameObject; - } - catch (Exception ex) - { - m_LoadError = new LoadError("Invalid data", ex.Message); - m_AllowExport = false; - Debug.LogException(ex); - return null; - } - - } - - GameObject LoadSvg(List warningsOut, out SVGParser.SceneInfo sceneInfo) - { - try - { - var reader = new SvgMeshReader(m_Location.AbsolutePath); - var (gameObject, warnings, collector, si) = reader.Import(); - sceneInfo = si; - warningsOut.AddRange(warnings); - m_ImportMaterialCollector = collector; - m_AllowExport = (m_ImportMaterialCollector != null); - return gameObject; - } - catch (Exception ex) - { - m_LoadError = new LoadError("Invalid data", ex.Message); - m_AllowExport = false; - Debug.LogException(ex); - sceneInfo = new SVGParser.SceneInfo(); - return null; - } - } - - /// Load model using FBX SDK. - GameObject LoadFbx(List warningsOut) - { -#if !FBX_SUPPORTED - m_LoadError = new LoadError("fbx not supported"); - return null; -#else - try - { - var reader = new FbxReader(m_Location.AbsolutePath); - var (gameObject, warnings, collector) = reader.Import(); - warningsOut.AddRange(warnings); - m_ImportMaterialCollector = collector; - m_AllowExport = (m_ImportMaterialCollector != null); - return gameObject; - } - catch (Exception ex) - { - m_LoadError = new LoadError("Invalid data", ex.Message); - m_AllowExport = false; - Debug.LogException(ex); - return null; - } -#endif - } - - async Task LoadGltf(List warnings) - { - string localPath = m_Location.AbsolutePath; - string assetLocation = Path.GetDirectoryName(localPath); - try - { - Task t = NewGltfImporter.StartSyncImport( - localPath, - assetLocation, - this, - warnings - ); - m_AllowExport = true; - await t; - } - catch (Exception ex) - { - m_AllowExport = false; - m_LoadError = new LoadError("Invalid data", ex.Message); - Debug.LogException(ex); - } - } - - private ModelBuilder m_builder; - - public void CancelLoadModelAsync() - { - if (m_builder != null) - { - m_builder.CancelAsyncLoad(); - m_builder = null; - } - } - - /// Threaded image loading is slower, but won't block the main thread. - /// If you're running in compositor and don't care about hitching, better to turn it off. - public void LoadModelAsync(bool useThreadedImageLoad) - { - if (m_builder != null) - { - throw new ApplicationException("Load in progress"); - } - - bool allowUsd = false; -#if USD_SUPPORTED - allowUsd = true; -#endif - - // Experimental usd loading. - if (allowUsd && - m_Location.GetLocationType() == Location.Type.LocalFile && - m_Location.Extension == ".usd") - { - throw new NotImplementedException(); - } - else if (m_Location.GetLocationType() == Location.Type.PolyAssetId) - { - // If we pulled this from Poly, it's going to be a gltf file. - m_builder = new GltfModelBuilder(m_Location, useThreadedImageLoad); - } - else - { - // Assume local files load with the FbxReader. - throw new NotImplementedException(); - } - - m_builder.BeginAsyncLoad(); - } - - public bool IsLoading() - { - return m_builder != null; - } - - /// For use in conjunction with LoadModelAsync(), returns true when the async load is complete, - /// false while still loading. - /// - /// Throws if called after async load is complete or before async load started. - /// When this returns, the Model will be either Valid, or LoadError will be set. - public bool TryLoadModel() - { - GameObject go = null; - bool isValid = false; - LoadError? error = null; - try - { - if (!m_builder.TryEndAsyncLoad(out go, out m_ImportMaterialCollector)) - { - return false; - } - isValid = m_builder.IsValid; - } - catch (ObjectDisposedException ex) - { - // This is a bad exception, it means we closed the future before calling TryGetModel. - error = new LoadError("Internal error", ex.Message); - Debug.LogException(ex); - } - catch (FutureFailed ex) - { - // Something went wrong in the glTF loader on the background thread. - error = new LoadError("Invalid data", ex.InnerException?.Message); - Debug.LogException(ex); - // TODO: Temporary, for b/139759540 and b/134430318 - // Leave the other exception alone so our analytics get the aggregated results. - Debug.LogException( - new Exception(string.Format("Failed loading model {0}", m_Location), ex)); - } - - m_builder = null; - - if (!isValid) - { - m_LoadError = error ?? new LoadError("Unexpected Failure"); - } - else - { - m_AllowExport = go != null; - StartCreatePrefab(go); - } - - AssignMaterialsToCollector(m_ImportMaterialCollector); - - // Even if an exception occurs above, return true because the return value indicates async load - // is complete. - return true; - } - - public async Task LoadModelAsync() - { - Task t = StartCreatePrefab(null); - await t; - - } - public void LoadModel() - { - StartCreatePrefab(null); - - } - - /// Either synchronously load a GameObject hierarchy and convert it to a "prefab" - /// or take a previously (probably asynchronously-loaded) GameObject hierarchy and do the same. - /// - /// Sets m_ModelParent and m_MeshBounds. - /// - /// Requirements for the passed GameObject: - /// - Its transform is identity - /// - Every visible mesh also has a BoxCollider - /// - Every BoxCollider also has a visible mesh - private async Task StartCreatePrefab(GameObject go) - { - if (m_Valid) - { - // This case is handled properly but it seems wasteful. - Debug.LogWarning($"Replacing already-loaded {m_Location}: did you mean to?"); - } - - List warnings = new List(); - - // If we weren't provided a GameObject, construct one now. - if (go == null) - { - m_AllowExport = false; - // TODO: if it's not already null, why did we get here? Probably want to check for error - // and bail at a higher level, and require as a precondition that error == null - m_LoadError = null; - - string ext = m_Location.Extension; - if (m_Location.GetLocationType() == Location.Type.LocalFile && - ext == ".usd") - { - // Experimental usd loading. - go = LoadUsd(warnings); - CalcBoundsNonGltf(go); - EndCreatePrefab(go, warnings); - } - else if (m_Location.GetLocationType() == Location.Type.PolyAssetId || - ext == ".gltf2" || ext == ".gltf" || ext == ".glb") - { - // If we pulled this from Poly, it's going to be a gltf file. - Task t = LoadGltf(warnings); - await t; - } - else if (ext == ".fbx" || ext == ".obj") - { - go = LoadFbx(warnings); - CalcBoundsNonGltf(go); - EndCreatePrefab(go, warnings); - } - else if (ext == ".ply") - { - go = LoadPly(warnings); - CalcBoundsNonGltf(go); - EndCreatePrefab(go, warnings); - } - else if (ext == ".svg") - { - go = LoadSvg(warnings, out SVGParser.SceneInfo sceneInfo); - CalcBoundsNonGltf(go); - EndCreatePrefab(go, warnings); - go.GetComponent().SvgSceneInfo = sceneInfo; - } - else - { - m_LoadError = new LoadError("Unknown format", ext); - } - } - - } - - public void CalcBoundsGltf(GameObject go) - { - Bounds b = new Bounds(); - bool first = true; - var boundsList = go.GetComponentsInChildren().Select(x => x.bounds).ToList(); - var skinnedMeshRenderers = go.GetComponentsInChildren(); - boundsList.AddRange(skinnedMeshRenderers.Select(x => x.bounds)); - foreach (Bounds bounds in boundsList) - { - if (first) - { - b = bounds; - first = false; - } - else - { - b.Encapsulate(bounds); - } - } - m_MeshBounds = b; - if (first) - { - // There was no geometry - Debug.LogErrorFormat("No usable geometry in model. LoadModel({0})", go.name); - } - } - - private void CalcBoundsNonGltf(GameObject go) - { - // TODO: this list of colliders is assumed to match the modelScript.m_MeshChildren array - // This should be enforced. - - // bc.bounds is world-space; therefore this calculation requires that - // go.transform be identity - Debug.Assert(Coords.AsGlobal[go.transform] == TrTransform.identity); - Bounds b = new Bounds(); - bool first = true; - foreach (BoxCollider bc in go.GetComponentsInChildren()) - { - if (first) - { - b = new Bounds(bc.bounds.center, bc.bounds.size); - first = false; - } - else - { - b.Encapsulate(bc.bounds); - } - UnityEngine.Object.Destroy(bc); - } - m_MeshBounds = b; - if (first) - { - // There was no geometry - Debug.LogErrorFormat("No usable geometry in model. LoadModel({0})", go.name); - } - - } - - public void EndCreatePrefab(GameObject go, List warnings) - { - if (go == null) - { - m_LoadError = m_LoadError ?? new LoadError("Bad data"); - DisplayWarnings(warnings); - } - - // Adopt the GameObject - go.name = m_Location.ToString(); - go.AddComponent().Init(); - go.SetActive(false); - if (m_ModelParent != null) - { - UnityEngine.Object.Destroy(m_ModelParent.gameObject); - } - m_ModelParent = go.transform; - - // !!! Add to material dictionary here? - - m_Valid = true; - DisplayWarnings(warnings); - - } - - public void UnloadModel() - { - if (m_builder != null) - { - m_builder.CancelAsyncLoad(); - m_builder = null; - } - m_Valid = false; - m_LoadError = null; - if (m_ModelParent != null) - { - // Procedurally created meshes need to be explicitly destroyed - you can't just destroy - // the MeshFilter that references them. - foreach (var mesh in m_ModelParent.GetComponentsInChildren() - .Select(x => x.sharedMesh)) - { - UObject.Destroy(mesh); - } - UObject.Destroy(m_ModelParent.gameObject); - m_ModelParent = null; - } - } - - /// Resets this.Error and tries to load the model again. - /// Pass the reason the Model is being pulled into memory, for logging purposes. - /// - /// When this coroutine terminates, you are guaranteed that m_Valid == true - /// or m_LoadError != null. - public IEnumerator LoadFullyCoroutine(string reason) - { - m_LoadError = null; - var type = m_Location.GetLocationType(); - switch (type) - { - case Location.Type.LocalFile: - yield return OverlayManager.m_Instance.RunInCompositor( - OverlayType.LoadModel, LoadModel, 0.25f); - break; - case Location.Type.PolyAssetId: - App.PolyAssetCatalog.RequestModelLoad(this, reason); - yield return null; - while (!m_Valid && !m_LoadError.HasValue) - { - yield return null; - } - break; - default: - m_LoadError = new LoadError($"Unknown load type {type}"); - break; - } - } - - private void DisplayWarnings(List warnings) - { - if (warnings.Count > 0) - { - TiltBrush.ControllerConsoleScript.m_Instance.AddNewLine( - "Loading " + Path.GetFileName(m_Location.AbsolutePath), true); - foreach (string warning in warnings) - { - TiltBrush.ControllerConsoleScript.m_Instance.AddNewLine( - OutputWindowScript.GetShorterFileName(warning.Replace("/", @"\")), false); - } - } - } - - public bool IsCached() - { - return m_Location.GetLocationType() == Location.Type.PolyAssetId && - Directory.Exists(m_Location.AbsolutePath); - } - - public void RefreshCache() - { - Directory.SetLastAccessTimeUtc( - Path.GetDirectoryName(m_Location.AbsolutePath), System.DateTime.UtcNow); - } - - // Returns all leaf meshes which are part of the model. - // Analagous to ModelWidget.GetMeshes(). - // Do not mutate the return value. - public MeshFilter[] GetMeshes() - { - if (!m_Valid) - { - throw new InvalidOperationException(); - } - return m_ModelParent.GetComponent().m_MeshChildren; - } - - public string GetExportName() - { - switch (GetLocation().GetLocationType()) - { - case Model.Location.Type.LocalFile: - return Path.GetFileNameWithoutExtension(RelativePath); - case Model.Location.Type.PolyAssetId: - return AssetId; - } - return "Unknown"; - } - - public void AssignMaterialsToCollector(ImportMaterialCollector collector) - { - m_ImportMaterialCollector = collector; - foreach (var mf in GetMeshes()) - { - foreach (var unityMat in mf.GetComponent().materials) - { - m_ImportMaterialCollector.Add(unityMat); - } - } - } - - public void EnsureCollectorExists() - { - if (m_ImportMaterialCollector == null) - { - var localPath = GetLocation().AbsolutePath; - m_ImportMaterialCollector = new ImportMaterialCollector( - Path.GetDirectoryName(localPath), - uniqueSeed: localPath - ); - AssignMaterialsToCollector(m_ImportMaterialCollector); - } - } - } -} // namespace TiltBrush; +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using TiltBrushToolkit; +using Unity.VectorGraphics; +using Debug = UnityEngine.Debug; +using UObject = UnityEngine.Object; + + +namespace TiltBrush +{ + + public class Model + { + public struct Location + { + public enum Type + { + Invalid, + LocalFile, + IcosaAssetId + } + + private Type type; + private string path; + private string id; // Only valid when the type is IcosaAssetId. + + public static Location File(string relativePath) + { + int lastIndex = relativePath.LastIndexOf('#'); + string path, fragment; + + if (lastIndex == -1) + { + path = relativePath; + fragment = null; + } + else + { + path = relativePath.Substring(0, lastIndex); + fragment = relativePath.Substring(lastIndex + 1); + } + return new Location + { + type = Type.LocalFile, + path = path, + }; + } + + public static Location IcosaAsset(string assetId, string path) + { + return new Location + { + type = Type.IcosaAssetId, + path = path, + id = assetId + }; + } + + /// Can return null if this is a location for a fake Model (like the ones ModelWidget + /// assigns itself while the real Model content is in progress of being loaded). + public string AbsolutePath + { + get + { + if (path == null) + { + return null; + } + switch (type) + { + case Type.LocalFile: + return Path.Combine(App.ModelLibraryPath(), path).Replace("\\", "/"); + case Type.IcosaAssetId: + return path.Replace("\\", "/"); + } + return null; + } + } + + public string RelativePath + { + get + { + if (type == Type.LocalFile) { return path; } + throw new Exception("Invalid relative path request"); + } + } + + public string Extension => Path.GetExtension(AbsolutePath).ToLower(); + + public string AssetId + { + get + { + if (type == Type.IcosaAssetId) { return id; } + throw new Exception("Invalid Icosa asset id request"); + } + } + + public Type GetLocationType() { return type; } + + public override int GetHashCode() + { + return this.ToString().GetHashCode(); + } + + public override string ToString() + { + string str; + if (type == Type.IcosaAssetId) + { + str = $"{type}:{id}"; + } + else + { + str = $"{type}:{path}"; + } + return str; + } + + public override bool Equals(object obj) + { + if (!(obj is Location)) + { + return false; + } + return this == (Location)obj; + } + + public static bool operator ==(Location a, Location b) + { + return a.type == b.type && a.path == b.path; + } + + public static bool operator !=(Location a, Location b) + { + return !(a == b); + } + } + + private static readonly float kMeshMsPerFrame = 1.0f; + + private static readonly GltfImportOptions kPolyGltfImportOptions = new GltfImportOptions + { + rescalingMode = GltfImportOptions.RescalingMode.CONVERT, + scaleFactor = App.METERS_TO_UNITS, + axisConventionOverride = AxisConvention.kGltfAccordingToIcosa, + recenter = false + }; + + private static readonly GltfImportOptions kGltfImportOptions = new GltfImportOptions + { + rescalingMode = GltfImportOptions.RescalingMode.CONVERT, + scaleFactor = App.METERS_TO_UNITS, + recenter = false + }; + + static RateLimiter sm_Limiter = new RateLimiter(maxEventsPerFrame: 1); + + // This is the object that is cloned when attached to a button or widget. + // It is the object that contains ObjModelScript. + public Transform m_ModelParent; + public Bounds m_MeshBounds; + + // Data & properties associated with current the state: + // - Unloaded + // - Trying to be loaded + // - Load finished successfully + // - Load finished unsuccessfully + // Not all of these states are modeled explicitly; this is a WIP. + + public struct LoadError + { + public LoadError(string message, string detail = null) + { + this.message = message; + this.detail = detail; + } + public readonly string message; // Human-readable short message + public readonly string detail; // Maybe non-human-readable details + // maybe? public bool transient; // true if we know for sure that this error is transient + } + + /// Is m_ModelParent assigned? + /// m_Valid = true implies m_LoadError == null + public bool m_Valid; + + /// m_LoadError != null implies m_Valid == false + private LoadError? m_LoadError; + public LoadError? Error => m_LoadError; + + // How many widgets are using this model? + public int m_UsageCount; + + private Location m_Location; + + // Can the geometry in this model be exported. + private bool m_AllowExport; + + private ImportMaterialCollector m_ImportMaterialCollector; + + // Returns the path starting after Media Library/Models + // e.g. subdirectory/example.obj + public string RelativePath + { + get { return m_Location.RelativePath; } + } + + public string AssetId + { + get { return m_Location.AssetId; } + } + + public string HumanName + { + get { return Path.GetFileNameWithoutExtension(m_Location.RelativePath); } + } + + public bool AllowExport + { + get { return m_AllowExport; } + } + + /// Only allowed if AllowExport = true + public IExportableMaterial GetExportableMaterial(Material material) + { + EnsureCollectorExists(); + return m_ImportMaterialCollector.GetExportableMaterial(material); + } + + public Model(Location location) + { + m_Location = location; + } + + public Location GetLocation() { return m_Location; } + + /// A helper class which allows import to run I/O on a background thread before producing Unity + /// GameObject(s). Usage: + /// BeginAsyncLoad() + /// TryEndAsyncLoad() repeat until it returns true; a bit of work is done each time(*) + /// CancelAsyncLoad() if you give up waiting for it to return true + /// + /// (*) Although the caller appears to have the responsibility of a scheduler, ModelBuilder + /// actually implements its own rate-limiting. After a set number of calls to TryEndAsyncLoad + /// in one frame (whether on one or many objects), further calls will be very fast no-ops. + /// So the caller can be (should be) a naive, performance-unaware scheduler that creates and + /// pumps as many ModelBuilders as it likes. + /// + /// However, note that this does not apply to the background-thread work. Nothing managages + /// that, so if N ModelBuilders are instantiated, N background threads will start running + /// and competing with each other. Your implementation may want to restrict that work to I/O. + abstract class ModelBuilder + { + protected string m_localPath; + private Future m_stateReader; + private IEnumerator m_meshEnumerator; + private ImportMaterialCollector m_ImportMaterialCollector; + private GameObject m_root; + + /// In the current implementation: + /// Before the first call to TryEndAsyncLoad, do not look at IsValid. + /// After the first call to TryEndAsyncLoad, IsValid is always true. + /// + /// TODO: semantics of IsValid = false are unclear and DoUnityThreadWork looks buggy + /// It's unclear if the intent is that the user should continue calling TryEndAsyncLoad + /// until it returns true, or if they should stop calling TryEndAsyncLoad. etc. Probably + /// we should remove this. + public bool IsValid + { + get; + protected set; + } + + public ModelBuilder(string localPath) + { + m_localPath = localPath; + IsValid = false; + } + + public void BeginAsyncLoad() + { + if (m_stateReader != null) + { + throw new ApplicationException("BeginImport should only be called once."); + } + + m_stateReader = new Future(DoBackgroundThreadWork, id => id.Dispose()); + } + + public void CancelAsyncLoad() + { + // If we have already created a mesh, we need to destroy it and the gameobject it is on so + // that we don't leave it orphaned in the heirarchy, and we don't leak meshes. + if (m_root != null) + { + foreach (var mesh in m_root.GetComponentsInChildren() + .Select(x => x.sharedMesh)) + { + UObject.Destroy(mesh); + } + UObject.Destroy(m_root); + m_root = null; + } + m_stateReader.Close(); + } + + /// Returns: + /// bool - false if incomplete, true upon successful completion. + /// GameObject - caller should check output GameObject to determine success. + /// ImportMaterialCollector - non-null upon successful completion. + /// Raises an exception on unsuccessful completion. + public bool TryEndAsyncLoad(out GameObject root, + out ImportMaterialCollector importMaterialCollector) + { + // Three things happen in this function. + // 1: It waits to try and get the result of reading the model on a background thread + // 2: It checks the rate limiter to make sure we don't have too many of these going on at once. + // 3: It enumerates through, creating meshes for the model. These are time-limited so that + // it will stop if it has taken too long in a single frame. + root = null; + importMaterialCollector = null; + if (m_meshEnumerator == null) + { + IDisposable state; + if (!m_stateReader.TryGetResult(out state)) { return false; } + + IEnumerable enumerable; + m_root = DoUnityThreadWork(state, out enumerable, out m_ImportMaterialCollector); + // TODO: Possible bugs if DoUnityThreadWork ever did fail: + // We assume the invariant that (root == null) == (IsValid == false) + // We assume the invariant that m_ImportMaterialCollector != null + // We don't dispose the GameObject or the enumerable + // If the user calls TryEndAsyncLoad again we might try to call DoUnityThreadWork again + if (m_root == null) + { + return false; + } + m_ImportMaterialCollector = new ImportMaterialCollector( + Path.GetDirectoryName(m_localPath), + uniqueSeed: m_localPath + ); + m_meshEnumerator = enumerable.GetEnumerator(); + m_root.SetActive(false); + } + // Yield until the limiter unblocks. + // Multiple calls to TryGetResult above are harmless. + if (sm_Limiter.IsBlocked()) + { + return false; + } + + // Finish constructing the actual game object. + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + long numTicks = (long)((Stopwatch.Frequency * kMeshMsPerFrame) / 1000); + while (true) + { + if (!m_meshEnumerator.MoveNext()) + { + m_root.SetActive(true); + root = m_root; + importMaterialCollector = m_ImportMaterialCollector; + stopwatch.Stop(); + return true; + } + if (stopwatch.ElapsedTicks > numTicks) + { + stopwatch.Stop(); + return false; + } + } + } + + // Performs whatever of the import process that can happen on a non-Unity thread. + // Returns: + // disposable - passed to DoUnityThreadWork, or disposed of if the load is canceled. + protected abstract IDisposable DoBackgroundThreadWork(); + + // Performs whatever portion of the import process that is left. + // + // Pass: + // state - the value returned from DoBackgroundThreadWork. Ownership is transferred; + // callee is responsible for Disposing it. + // Returns: + // GameObject - the root of the object hierarchy. + // ImportMaterialCollector - the materials that were created, and info about them + // IEnumerable - a coroutine that will be pumped to completion + protected abstract GameObject DoUnityThreadWork( + IDisposable state, + out IEnumerable meshCreator, + out ImportMaterialCollector importMaterialCollector); + } + + /// The glTF ModelBuilder. + class GltfModelBuilder : ModelBuilder + { + private readonly bool m_useThreadedImageLoad; + private readonly bool m_fromIcosa; + + public GltfModelBuilder(Location location, bool useThreadedImageLoad) + : base(location.AbsolutePath) + { + m_useThreadedImageLoad = useThreadedImageLoad; + m_fromIcosa = (location.GetLocationType() == Location.Type.IcosaAssetId); + } + + protected override IDisposable DoBackgroundThreadWork() + { + var loader = new TiltBrushUriLoader( + m_localPath, Path.GetDirectoryName(m_localPath), m_useThreadedImageLoad); + var options = m_fromIcosa ? kPolyGltfImportOptions : kGltfImportOptions; + return NewGltfImporter.BeginImport(m_localPath); + } + + protected override GameObject DoUnityThreadWork(IDisposable state__, + out IEnumerable meshEnumerable, + out ImportMaterialCollector + importMaterialCollector) + { + meshEnumerable = null; + importMaterialCollector = null; + GameObject rootObject = null; + using (IDisposable state_ = state__) + { + var state = state_ as NewGltfImporter.ImportState; + if (state != null) + { + string assetLocation = Path.GetDirectoryName(m_localPath); + // EndImport doesn't try to use the loadImages functionality of UriLoader anyway. + // It knows it's on the main thread, so chooses to use Unity's fast loading. + rootObject = state.root; + importMaterialCollector = new ImportMaterialCollector(assetLocation, uniqueSeed: m_localPath); + } + } + IsValid = rootObject != null; + return rootObject; + } + } // GltfModelBuilder + + GameObject LoadUsd(List warnings) + { +#if USD_SUPPORTED + return ImportUsd.Import(m_Location.AbsolutePath, out warnings); +#endif + m_LoadError = new LoadError("usd not supported"); + return null; + } + + GameObject LoadPly(List warningsOut) + { + + try + { + var reader = new PlyReader(m_Location.AbsolutePath); + var (gameObject, warnings, collector) = reader.Import(); + warningsOut.AddRange(warnings); + m_ImportMaterialCollector = collector; + m_AllowExport = (m_ImportMaterialCollector != null); + return gameObject; + } + catch (Exception ex) + { + m_LoadError = new LoadError("Invalid data", ex.Message); + m_AllowExport = false; + Debug.LogException(ex); + return null; + } + + } + + GameObject LoadSvg(List warningsOut, out SVGParser.SceneInfo sceneInfo) + { + try + { + var reader = new SvgMeshReader(m_Location.AbsolutePath); + var (gameObject, warnings, collector, si) = reader.Import(); + sceneInfo = si; + warningsOut.AddRange(warnings); + m_ImportMaterialCollector = collector; + m_AllowExport = (m_ImportMaterialCollector != null); + return gameObject; + } + catch (Exception ex) + { + m_LoadError = new LoadError("Invalid data", ex.Message); + m_AllowExport = false; + Debug.LogException(ex); + sceneInfo = new SVGParser.SceneInfo(); + return null; + } + } + + /// Load model using FBX SDK. + GameObject LoadFbx(List warningsOut) + { +#if !FBX_SUPPORTED + m_LoadError = new LoadError("fbx not supported"); + return null; +#else + try + { + var reader = new FbxReader(m_Location.AbsolutePath); + var (gameObject, warnings, collector) = reader.Import(); + warningsOut.AddRange(warnings); + m_ImportMaterialCollector = collector; + m_AllowExport = (m_ImportMaterialCollector != null); + return gameObject; + } + catch (Exception ex) + { + m_LoadError = new LoadError("Invalid data", ex.Message); + m_AllowExport = false; + Debug.LogException(ex); + return null; + } +#endif + } + + async Task LoadGltf(List warnings) + { + string localPath = m_Location.AbsolutePath; + string assetLocation = Path.GetDirectoryName(localPath); + try + { + Task t = NewGltfImporter.StartSyncImport( + localPath, + assetLocation, + this, + warnings + ); + m_AllowExport = true; + await t; + } + catch (Exception ex) + { + m_AllowExport = false; + m_LoadError = new LoadError("Invalid data", ex.Message); + Debug.LogException(ex); + } + } + + private ModelBuilder m_builder; + + public void CancelLoadModelAsync() + { + if (m_builder != null) + { + m_builder.CancelAsyncLoad(); + m_builder = null; + } + } + + /// Threaded image loading is slower, but won't block the main thread. + /// If you're running in compositor and don't care about hitching, better to turn it off. + public void LoadModelAsync(bool useThreadedImageLoad) + { + if (m_builder != null) + { + throw new ApplicationException("Load in progress"); + } + + bool allowUsd = false; +#if USD_SUPPORTED + allowUsd = true; +#endif + + // Experimental usd loading. + if (allowUsd && + m_Location.GetLocationType() == Location.Type.LocalFile && + m_Location.Extension == ".usd") + { + throw new NotImplementedException(); + } + else if (m_Location.GetLocationType() == Location.Type.IcosaAssetId) + { + // If we pulled this from Icosa, it's going to be a gltf file. + m_builder = new GltfModelBuilder(m_Location, useThreadedImageLoad); + } + else + { + // Assume local files load with the FbxReader. + throw new NotImplementedException(); + } + + m_builder.BeginAsyncLoad(); + } + + public bool IsLoading() + { + return m_builder != null; + } + + /// For use in conjunction with LoadModelAsync(), returns true when the async load is complete, + /// false while still loading. + /// + /// Throws if called after async load is complete or before async load started. + /// When this returns, the Model will be either Valid, or LoadError will be set. + public bool TryLoadModel() + { + GameObject go = null; + bool isValid = false; + LoadError? error = null; + try + { + if (!m_builder.TryEndAsyncLoad(out go, out m_ImportMaterialCollector)) + { + return false; + } + isValid = m_builder.IsValid; + } + catch (ObjectDisposedException ex) + { + // This is a bad exception, it means we closed the future before calling TryGetModel. + error = new LoadError("Internal error", ex.Message); + Debug.LogException(ex); + } + catch (FutureFailed ex) + { + // Something went wrong in the glTF loader on the background thread. + error = new LoadError("Invalid data", ex.InnerException?.Message); + Debug.LogException(ex); + // TODO: Temporary, for b/139759540 and b/134430318 + // Leave the other exception alone so our analytics get the aggregated results. + Debug.LogException( + new Exception(string.Format("Failed loading model {0}", m_Location), ex)); + } + + m_builder = null; + + if (!isValid) + { + m_LoadError = error ?? new LoadError("Unexpected Failure"); + } + else + { + m_AllowExport = go != null; + StartCreatePrefab(go); + } + + AssignMaterialsToCollector(m_ImportMaterialCollector); + + // Even if an exception occurs above, return true because the return value indicates async load + // is complete. + return true; + } + + public async Task LoadModelAsync() + { + Task t = StartCreatePrefab(null); + await t; + + } + public void LoadModel() + { + StartCreatePrefab(null); + + } + + /// Either synchronously load a GameObject hierarchy and convert it to a "prefab" + /// or take a previously (probably asynchronously-loaded) GameObject hierarchy and do the same. + /// + /// Sets m_ModelParent and m_MeshBounds. + /// + /// Requirements for the passed GameObject: + /// - Its transform is identity + /// - Every visible mesh also has a BoxCollider + /// - Every BoxCollider also has a visible mesh + private async Task StartCreatePrefab(GameObject go) + { + if (m_Valid) + { + // This case is handled properly but it seems wasteful. + Debug.LogWarning($"Replacing already-loaded {m_Location}: did you mean to?"); + } + + List warnings = new List(); + + // If we weren't provided a GameObject, construct one now. + if (go == null) + { + m_AllowExport = false; + // TODO: if it's not already null, why did we get here? Probably want to check for error + // and bail at a higher level, and require as a precondition that error == null + m_LoadError = null; + + string ext = m_Location.Extension; + if (m_Location.GetLocationType() == Location.Type.LocalFile && + ext == ".usd") + { + // Experimental usd loading. + go = LoadUsd(warnings); + CalcBoundsNonGltf(go); + EndCreatePrefab(go, warnings); + } + else if (m_Location.GetLocationType() == Location.Type.IcosaAssetId || + ext == ".gltf2" || ext == ".gltf" || ext == ".glb") + { + // If we pulled this from Icosa, it's going to be a gltf file. + Task t = LoadGltf(warnings); + await t; + } + else if (ext == ".fbx" || ext == ".obj") + { + go = LoadFbx(warnings); + CalcBoundsNonGltf(go); + EndCreatePrefab(go, warnings); + } + else if (ext == ".ply") + { + go = LoadPly(warnings); + CalcBoundsNonGltf(go); + EndCreatePrefab(go, warnings); + } + else if (ext == ".svg") + { + go = LoadSvg(warnings, out SVGParser.SceneInfo sceneInfo); + CalcBoundsNonGltf(go); + EndCreatePrefab(go, warnings); + go.GetComponent().SvgSceneInfo = sceneInfo; + } + else + { + m_LoadError = new LoadError("Unknown format", ext); + } + } + + } + + public void CalcBoundsGltf(GameObject go) + { + Bounds b = new Bounds(); + bool first = true; + var boundsList = go.GetComponentsInChildren().Select(x => x.bounds).ToList(); + var skinnedMeshRenderers = go.GetComponentsInChildren(); + boundsList.AddRange(skinnedMeshRenderers.Select(x => x.bounds)); + foreach (Bounds bounds in boundsList) + { + if (first) + { + b = bounds; + first = false; + } + else + { + b.Encapsulate(bounds); + } + } + m_MeshBounds = b; + if (first) + { + // There was no geometry + Debug.LogErrorFormat("No usable geometry in model. LoadModel({0})", go.name); + } + } + + private void CalcBoundsNonGltf(GameObject go) + { + // TODO: this list of colliders is assumed to match the modelScript.m_MeshChildren array + // This should be enforced. + + // bc.bounds is world-space; therefore this calculation requires that + // go.transform be identity + Debug.Assert(Coords.AsGlobal[go.transform] == TrTransform.identity); + Bounds b = new Bounds(); + bool first = true; + foreach (BoxCollider bc in go.GetComponentsInChildren()) + { + if (first) + { + b = new Bounds(bc.bounds.center, bc.bounds.size); + first = false; + } + else + { + b.Encapsulate(bc.bounds); + } + UnityEngine.Object.Destroy(bc); + } + m_MeshBounds = b; + if (first) + { + // There was no geometry + Debug.LogErrorFormat("No usable geometry in model. LoadModel({0})", go.name); + } + + } + + public void EndCreatePrefab(GameObject go, List warnings) + { + if (go == null) + { + m_LoadError = m_LoadError ?? new LoadError("Bad data"); + DisplayWarnings(warnings); + } + + // Adopt the GameObject + go.name = m_Location.ToString(); + go.AddComponent().Init(); + go.SetActive(false); + if (m_ModelParent != null) + { + UnityEngine.Object.Destroy(m_ModelParent.gameObject); + } + m_ModelParent = go.transform; + + // !!! Add to material dictionary here? + + m_Valid = true; + DisplayWarnings(warnings); + + } + + public void UnloadModel() + { + if (m_builder != null) + { + m_builder.CancelAsyncLoad(); + m_builder = null; + } + m_Valid = false; + m_LoadError = null; + if (m_ModelParent != null) + { + // Procedurally created meshes need to be explicitly destroyed - you can't just destroy + // the MeshFilter that references them. + foreach (var mesh in m_ModelParent.GetComponentsInChildren() + .Select(x => x.sharedMesh)) + { + UObject.Destroy(mesh); + } + UObject.Destroy(m_ModelParent.gameObject); + m_ModelParent = null; + } + } + + /// Resets this.Error and tries to load the model again. + /// Pass the reason the Model is being pulled into memory, for logging purposes. + /// + /// When this coroutine terminates, you are guaranteed that m_Valid == true + /// or m_LoadError != null. + public IEnumerator LoadFullyCoroutine(string reason) + { + m_LoadError = null; + var type = m_Location.GetLocationType(); + switch (type) + { + case Location.Type.LocalFile: + yield return OverlayManager.m_Instance.RunInCompositor( + OverlayType.LoadModel, LoadModel, 0.25f); + break; + case Location.Type.IcosaAssetId: + App.IcosaAssetCatalog.RequestModelLoad(this, reason); + yield return null; + while (!m_Valid && !m_LoadError.HasValue) + { + yield return null; + } + break; + default: + m_LoadError = new LoadError($"Unknown load type {type}"); + break; + } + } + + private void DisplayWarnings(List warnings) + { + if (warnings.Count > 0) + { + TiltBrush.ControllerConsoleScript.m_Instance.AddNewLine( + "Loading " + Path.GetFileName(m_Location.AbsolutePath), true); + foreach (string warning in warnings) + { + TiltBrush.ControllerConsoleScript.m_Instance.AddNewLine( + OutputWindowScript.GetShorterFileName(warning.Replace("/", @"\")), false); + } + } + } + + public bool IsCached() + { + return m_Location.GetLocationType() == Location.Type.IcosaAssetId && + Directory.Exists(m_Location.AbsolutePath); + } + + public void RefreshCache() + { + Directory.SetLastAccessTimeUtc( + Path.GetDirectoryName(m_Location.AbsolutePath), System.DateTime.UtcNow); + } + + // Returns all leaf meshes which are part of the model. + // Analagous to ModelWidget.GetMeshes(). + // Do not mutate the return value. + public MeshFilter[] GetMeshes() + { + if (!m_Valid) + { + throw new InvalidOperationException(); + } + return m_ModelParent.GetComponent().m_MeshChildren; + } + + public string GetExportName() + { + switch (GetLocation().GetLocationType()) + { + case Model.Location.Type.LocalFile: + return Path.GetFileNameWithoutExtension(RelativePath); + case Model.Location.Type.IcosaAssetId: + return AssetId; + } + return "Unknown"; + } + + public void AssignMaterialsToCollector(ImportMaterialCollector collector) + { + m_ImportMaterialCollector = collector; + foreach (var mf in GetMeshes()) + { + foreach (var unityMat in mf.GetComponent().materials) + { + m_ImportMaterialCollector.Add(unityMat); + } + } + } + + public void EnsureCollectorExists() + { + if (m_ImportMaterialCollector == null) + { + var localPath = GetLocation().AbsolutePath; + m_ImportMaterialCollector = new ImportMaterialCollector( + Path.GetDirectoryName(localPath), + uniqueSeed: localPath + ); + AssignMaterialsToCollector(m_ImportMaterialCollector); + } + } + } +} // namespace TiltBrush; diff --git a/Assets/Scripts/PacDebugWindow.cs b/Assets/Scripts/PacDebugWindow.cs index 64b9960775..147c5d8bf0 100644 --- a/Assets/Scripts/PacDebugWindow.cs +++ b/Assets/Scripts/PacDebugWindow.cs @@ -1,102 +1,102 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#if UNITY_EDITOR - -using System; -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEditor; - -namespace TiltBrush -{ - - public partial class PolyAssetCatalog - { - - private class DebugWindow : EditorWindow - { - [MenuItem("Open Brush/Poly Asset Catalog Debug Window")] - private static void OpenDebugWindow() => GetWindow(); - - private static string ToString(AssetGetter ag) - { - return $"{ag.Asset.Id} {(ag.IsCanceled ? 'c' : '-')} {(ag.IsReady ? 'r' : '-')}"; - } - - private static string ToString(ModelLoadRequest m) - { - return $"{m.AssetId} {m.Reason}"; - } - - private bool m_showActiveRequests; - private bool m_showRequestQueue; - private bool m_showLoadQueue; - - private void Update() - { - if (Application.isPlaying) - { - Repaint(); - } - } - - private void OnGUI() - { - if (!Application.isPlaying) - { - EditorGUILayout.HelpBox("Only works in Play Mode.", MessageType.Info); - return; - } - var pac = App.PolyAssetCatalog; - - DrawCollection(ref m_showActiveRequests, "Downloads", pac.m_ActiveRequests, ToString); - DrawCollection(ref m_showRequestQueue, "RequestLoadQueue", pac.m_RequestLoadQueue, ToString); - DrawCollection(ref m_showLoadQueue, "LoadQueue", pac.m_LoadQueue, ToString); - } - - private void DrawCollection( - ref bool state, string label, ICollection collection, Func toString, - bool sort = false) - { - string withCount = $"{label} : {collection.Count}"; - state = EditorGUILayout.ToggleLeft(withCount, state); - if (!state || collection.Count == 0) { return; } - int i = 0; - if (sort) - { - List strings = collection.Select(toString).ToList(); - strings.Sort(); - foreach (string elt in strings) - { - EditorGUILayout.LabelField(i.ToString(), elt); - i += 1; - } - } - else - { - foreach (T elt in collection) - { - EditorGUILayout.LabelField(i.ToString(), toString(elt)); - i += 1; - } - } - } - } // class DebugWindow - } // class PolyAssetCatalog - -} // namespace TiltBrush - -#endif +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if UNITY_EDITOR + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; + +namespace TiltBrush +{ + + public partial class IcosaAssetCatalog + { + + private class DebugWindow : EditorWindow + { + [MenuItem("Open Brush/Poly Asset Catalog Debug Window")] + private static void OpenDebugWindow() => GetWindow(); + + private static string ToString(AssetGetter ag) + { + return $"{ag.Asset.Id} {(ag.IsCanceled ? 'c' : '-')} {(ag.IsReady ? 'r' : '-')}"; + } + + private static string ToString(ModelLoadRequest m) + { + return $"{m.AssetId} {m.Reason}"; + } + + private bool m_showActiveRequests; + private bool m_showRequestQueue; + private bool m_showLoadQueue; + + private void Update() + { + if (Application.isPlaying) + { + Repaint(); + } + } + + private void OnGUI() + { + if (!Application.isPlaying) + { + EditorGUILayout.HelpBox("Only works in Play Mode.", MessageType.Info); + return; + } + var pac = App.IcosaAssetCatalog; + + DrawCollection(ref m_showActiveRequests, "Downloads", pac.m_ActiveRequests, ToString); + DrawCollection(ref m_showRequestQueue, "RequestLoadQueue", pac.m_RequestLoadQueue, ToString); + DrawCollection(ref m_showLoadQueue, "LoadQueue", pac.m_LoadQueue, ToString); + } + + private void DrawCollection( + ref bool state, string label, ICollection collection, Func toString, + bool sort = false) + { + string withCount = $"{label} : {collection.Count}"; + state = EditorGUILayout.ToggleLeft(withCount, state); + if (!state || collection.Count == 0) { return; } + int i = 0; + if (sort) + { + List strings = collection.Select(toString).ToList(); + strings.Sort(); + foreach (string elt in strings) + { + EditorGUILayout.LabelField(i.ToString(), elt); + i += 1; + } + } + else + { + foreach (T elt in collection) + { + EditorGUILayout.LabelField(i.ToString(), toString(elt)); + i += 1; + } + } + } + } // class DebugWindow + } // class PolyAssetCatalog + +} // namespace TiltBrush + +#endif diff --git a/Assets/Scripts/Poly/PolyAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs similarity index 95% rename from Assets/Scripts/Poly/PolyAssetCatalog.cs rename to Assets/Scripts/Poly/IcosaAssetCatalog.cs index b118798372..5f77d5e723 100644 --- a/Assets/Scripts/Poly/PolyAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -1,1006 +1,993 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using UnityEngine; -using UnityEngine.Networking; -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json; - -namespace TiltBrush -{ - - /// Used as an accessor for files downloaded from Poly and cached on local storage. - public partial class PolyAssetCatalog : MonoBehaviour - { - const int kAssetDiskCacheSize = 1000; - const float kThumbnailFetchRate = 15; - const int kThumbnailFetchMaxCount = 30; - const int kThumbnailReadRate = 4; - - // This may be a bit broader than an asset id, but it's a safe set of - // filename characters. - // Change - added . % ~ to allow urlencoded urls - static readonly Regex sm_AssetIdPattern = new Regex(@"^[a-zA-Z0-9-_%~\.]+$"); - - public enum AssetLoadState - { - Unknown, - NotDownloaded, - Downloading, - Downloaded, // On disk but not in memory - // DownloadFailed, // We don't keep track of download errors, so this becomes "NotDownloaded" - Loading, - LoadFailed, // This shows up as a !Valid model in the model catalog - Loaded - } - - public class AssetDetails - { - // Disabled only because there isn't a pressing reason to enable it. - const bool kLazyLoadThumbnail = false; - - private readonly PolyAssetCatalog m_Owner; - private readonly Texture2D m_Thumbnail; - private string m_ThumbnailUrl; // if non-null, have not attempted to fetch it yet - - public string AssetId { get; } - public string HumanName { get; } - public string AccountName { get; } - public Quaternion? ModelRotation { get; } - - public Texture2D Thumbnail - { - get - { - if (m_ThumbnailUrl != null) - { - string url = m_ThumbnailUrl; - m_ThumbnailUrl = null; - DownloadThumbnailAsync(url); - } - return m_Thumbnail; - } - } - - public Model Model { get { return App.PolyAssetCatalog.GetModel(AssetId); } } - - public AssetDetails( - JToken json, string accountName, string thumbnailSuffix) - { - m_Owner = App.PolyAssetCatalog; - HumanName = json["displayName"].ToString(); - AssetId = json["name"].ToString().Substring(7); // strip out "assets/" - AccountName = accountName; - var rotation = json["presentationParams"]?["orientingRotation"]; - if (rotation != null) - { - ModelRotation = new Quaternion( - rotation["x"]?.Value() ?? 0, - rotation["y"]?.Value() ?? 0, - rotation["z"]?.Value() ?? 0, - rotation["w"]?.Value() ?? 0 - ); - } - else - { - ModelRotation = null; - } - - m_Thumbnail = new Texture2D(4, 4, TextureFormat.ARGB32, false); - m_ThumbnailUrl = json["thumbnail"]["url"].ToString(); - if (!string.IsNullOrEmpty(thumbnailSuffix)) - { - m_ThumbnailUrl = string.Format("{0}={1}", m_ThumbnailUrl, thumbnailSuffix); - } - if (!kLazyLoadThumbnail) - { - // Pre-emptive thumbnail fetch - _ = Thumbnail; - } - } - - /// Returns the contents of path, or null if the cache doesn't exist / can't be read. - /// It's ok to pass null. - /// Does not raise exceptions. - private static byte[] SafeReadCache(string path) - { - if (path != null && File.Exists(path)) - { - try - { - return File.ReadAllBytes(path); - } - catch (IOException e) - { - Debug.LogWarning($"Could not read cache {path}: {e}"); - } - } - return null; - } - - /// Updates the contents of path. - /// It's ok to pass null path or contents; passing contents=null clears the cache file. - /// Does not raise exceptions. - private static void SafeWriteCache(string path, byte[] contents) - { - if (path == null) { return; } - try { File.Delete(path); } - catch { } - if (contents != null) - { - string dir = Path.GetDirectoryName(path); - if (!Directory.Exists(dir)) - { - try { Directory.CreateDirectory(dir); } - catch { } - } - try { File.WriteAllBytes(path, contents); } - catch { } - } - } - - async void DownloadThumbnailAsync(string thumbnailUrl) - { - string cachePath = Path.Combine(m_Owner.m_ThumbnailCacheDir, AssetId); - byte[] thumbnailBytes = SafeReadCache(cachePath); - - if (thumbnailBytes == null) - { - await m_Owner.m_thumbnailFetchLimiter.WaitAsync(); - WebRequest www = new WebRequest(thumbnailUrl, - App.GoogleIdentity, - UnityWebRequest.kHttpVerbGET); - - await www.SendAsync(); - - while (m_Owner.m_thumbnailReadLimiter.IsBlocked()) - { - await Awaiters.NextFrame; - } - thumbnailBytes = www.ResultBytes; - SafeWriteCache(cachePath, thumbnailBytes); - } - - if (thumbnailBytes != null) - { - try - { - // TODO: fix aspect ratio of thumbnail - RawImage imageData = await new ThreadedImageReader(thumbnailBytes, thumbnailUrl); - - UnityEngine.Profiling.Profiler.BeginSample("AssetDetails.DownloadThumbnail:LoadImage"); - if (imageData != null) - { - m_Thumbnail.Resize(imageData.ColorWidth, imageData.ColorHeight, - TextureFormat.ARGB32, false); - m_Thumbnail.SetPixels32(imageData.ColorData); - m_Thumbnail.Apply(updateMipmaps: false, makeNoLongerReadable: true); - } - UnityEngine.Profiling.Profiler.EndSample(); - - // m_Thumbnail still points to the same Texture2D, so we don't need to send CatalogChanged - } - catch (Exception) - { - SafeWriteCache(cachePath, null); - throw; - } - } - } - } - - private static Vector3? GetCameraForward(JToken cameraParams) - { - if (cameraParams == null) - { - return null; - } - JToken cameraMatrix = cameraParams["matrix4x4"]; - if (cameraMatrix == null) { return null; } - // The third column holds the camera's forward. - Vector3 cameraForward = new Vector3(); - cameraForward.x = float.Parse(cameraMatrix[2].ToString()); - cameraForward.y = float.Parse(cameraMatrix[6].ToString()); - cameraForward.z = float.Parse(cameraMatrix[10].ToString()); - return cameraForward; - } - - private class AssetSet - { - public List m_Models = new List(); - public IEnumerator m_FetchMetadataCoroutine; - public bool m_RefreshRequested; - public float m_CooldownTimer; - } - - /// A request to pull a Model into memory. - /// It's assumed that the Model already exists on disk. - public class ModelLoadRequest - { - public readonly Model Model; - /// The reason for the Model being pulled into memory. - public readonly string Reason; - public string AssetId => Model.AssetId; - public ModelLoadRequest(Model model, string reason) - { - Model = model; - Reason = reason; - } - } - - public event Action CatalogChanged; - - [SerializeField] private string m_ThumbnailSuffix = "s128"; - /// Assets being downloaded to disk. - /// When done, these get moved to m_RequestLoadQueue. - private List m_ActiveRequests; - /// Assets that someone wants to bring from disk into memory. - /// Precondition: they are on disk - /// These get moved onto m_LoadQueue periodically. - /// May contain duplicates? - /// TODO: figure out why we have this intermediate stage - private List m_RequestLoadQueue; - private List m_LoadQueue; - /// Memoization data for IsLoading(). - /// Set this to null to invalidate it; or (if you are very confident) mutate it. - /// Invariant: either null, or the union of m_ActiveRequests, m_RequestLoadQueue, m_LoadQueue. - private HashSet m_IsLoadingMemo = null; - private string m_CacheDir; - private string m_ThumbnailCacheDir; - private Dictionary m_ModelsByAssetId; - private Dictionary m_AssetSetByType; - private bool m_NotifyListeners; - - private AwaitableRateLimiter m_thumbnailFetchLimiter = - new AwaitableRateLimiter(kThumbnailFetchRate, kThumbnailFetchMaxCount); - private RateLimiter m_thumbnailReadLimiter = - new RateLimiter(maxEventsPerFrame: kThumbnailReadRate); - - /// Returns true if the assetId is in any of our download or load queues - public bool IsLoading(string assetId) - { - // This needs caching because it's hammered every frame by the model buttons :-/ - if (m_IsLoadingMemo == null) - { - m_IsLoadingMemo = new HashSet(); - m_IsLoadingMemo.UnionWith(m_ActiveRequests.Select(request => request.Asset.Id)); - m_IsLoadingMemo.UnionWith(m_RequestLoadQueue.Select(request => request.AssetId)); - m_IsLoadingMemo.UnionWith(m_LoadQueue.Select(request => request.AssetId)); - } - return m_IsLoadingMemo.Contains(assetId); - } - - public void RequestRefresh(IcosaSetType type) - { - // We don't update featured except on startup. - if (type != IcosaSetType.Featured && App.GoogleIdentity.LoggedIn) - { - m_AssetSetByType[type].m_RefreshRequested = true; - } - } - - public void Init() - { - string cacheDir = Path.Combine(Application.persistentDataPath, "assetCache"); - m_CacheDir = cacheDir.Replace("\\", "/"); - // Use a different directory from m_CacheDir to avoid having to make ValidGltfCache() - // smart enough to allow directories with only a thumbnail and no asset data. - m_ThumbnailCacheDir = Path.Combine(Application.persistentDataPath, "assetThumbnail") - .Replace("\\", "/"); - m_ActiveRequests = new List(); - m_RequestLoadQueue = new List(); - m_LoadQueue = new List(); - - FileUtils.InitializeDirectoryWithUserError(m_CacheDir, "Failed to create asset cache"); - - // Create and populate model map. - m_ModelsByAssetId = new Dictionary(); - try - { - foreach (string folderPath in EnumerateCacheDirectories()) - { - string folderName = Path.GetFileName(folderPath); - string gltfFile = ValidGltfCache(folderPath, folderName); - if (gltfFile != null) - { - string path = Path.Combine(folderPath, folderName); - path = Path.Combine(path, gltfFile); - m_ModelsByAssetId[folderName] = new Model(Model.Location.PolyAsset(folderName, path)); - } - else - { - Debug.LogWarningFormat("Deleting invalid cache folder {0}", folderName); - Directory.Delete(folderPath, true); - } - } - } - catch (DirectoryNotFoundException e) - { - Debug.LogException(e); - } - catch (UnauthorizedAccessException e) - { - Debug.LogException(e); - } - - m_AssetSetByType = new Dictionary - { - { - IcosaSetType.User, - new AssetSet() - }, - { - IcosaSetType.Liked, - new AssetSet() - }, - { - IcosaSetType.Featured, - new AssetSet { m_RefreshRequested = true } - } - }; - - App.Instance.AppExit += () => - { - var models = EnumerateCacheDirectories() - .OrderBy(d => Directory.GetLastAccessTimeUtc(d)).ToArray(); - for (int excess = models.Count() - kAssetDiskCacheSize; excess > 0; excess--) - { - Directory.Delete(models[excess - 1], true); - } - }; - } - - public AssetLoadState GetAssetLoadState(string assetId) - { - if (GetModel(assetId) is Model m) - { - // A model may be present in memory but also be still loading -- eg if someone - // requested that the load be retried. In this case it's kind of in two states; - // I'm somewhat arbitrarily choosing one. - if (m.m_Valid) { return AssetLoadState.Loaded; } - else if (m.Error != null) { return AssetLoadState.LoadFailed; } - else if (IsLoading(assetId)) - { - foreach (var elt in m_RequestLoadQueue) - { - if (elt.AssetId == assetId) - { - return AssetLoadState.Loading; - } - } - foreach (var elt in m_LoadQueue) - { - if (elt.AssetId == assetId) - { - return AssetLoadState.Loading; - } - } - // This should never happen and probably indicates some bug where m_AssetLoading hasn't - // been kept in sync with m_[Request]LoadQueue. - Debug.LogWarning($"Model for {assetId} is in an indeterminate state!"); - return AssetLoadState.Unknown; - } - else - { - return AssetLoadState.Downloaded; - } - } - else - { - foreach (var downloadRequest in m_ActiveRequests) - { - if (downloadRequest.Asset.Id == assetId) - { - return AssetLoadState.Downloading; - } - } - return AssetLoadState.NotDownloaded; - } - } - - public string GetCacheDirectoryForAsset(string asset) - { - if (!sm_AssetIdPattern.IsMatch(asset)) - { - Debug.LogWarningFormat("Not an asset id: {0}", asset); - return null; - } - return Path.Combine(m_CacheDir, asset); - } - - /// On any error, returns an empty enumeration - public IEnumerable EnumerateCacheDirectories() - { - try - { - return Directory.GetDirectories(m_CacheDir); - } - catch (UnauthorizedAccessException e) { Debug.LogException(e); } - catch (DirectoryNotFoundException e) { Debug.LogException(e); } - return new string[] { }; - } - - void Start() - { - OAuth2Identity.ProfileUpdated += OnProfileUpdated; - RefreshFetchCoroutines(); - } - - public Model GetModel(string assetId) - { - Model model; - if (!m_ModelsByAssetId.TryGetValue(assetId, out model)) - { - // null is actually the default for reference types, just being explicit here. - // ReSharper disable once RedundantAssignment - model = null; - } - return model; - } - - /// Checks to see if it's time to kick off a new refresh - /// Polls any refresh coroutines going on. - void Update() - { - m_thumbnailFetchLimiter.Tick(Time.deltaTime); - if (!VrAssetService.m_Instance.Available) - { - return; - } - - foreach (var entry in m_AssetSetByType) - { - var type = entry.Key; - var set = entry.Value; - - if (set.m_FetchMetadataCoroutine != null) - { - // Pump existing update coroutine - try - { - if (!set.m_FetchMetadataCoroutine.MoveNext()) - { - set.m_FetchMetadataCoroutine = null; - } - } - catch (VrAssetServiceException e) - { - ControllerConsoleScript.m_Instance.AddNewLine(e.Message); - Debug.LogException(e); - set.m_FetchMetadataCoroutine = null; - } - } - else if (set.m_RefreshRequested) - { - // Kick off a new refresh coroutine if it is time. - if (set.m_CooldownTimer <= 0) - { - set.m_FetchMetadataCoroutine = RefreshAssetSet(type); - set.m_RefreshRequested = false; - set.m_CooldownTimer = VrAssetService.m_Instance.m_SketchbookRefreshInterval; - } - } - if (set.m_CooldownTimer >= 0) - { - set.m_CooldownTimer -= Time.deltaTime; - } - } - } - - /// Pass the reason the Model is being pulled into memory, for logging purposes. - public void RequestModelLoad(Model model, string reason) - { - // Verify assumption that byAssetId[model.asset] == model; otherwise, caller may wait - // indefinitely for model's loaded state to change and that bug will be hard to track down. - string assetId = model.GetLocation().AssetId; - m_ModelsByAssetId.TryGetValue(assetId, out Model model2); - if (model2 != model) - { - // If we pretend to try to load the model, the caller may wait infinitely for the Model - // to load. - throw new InvalidOperationException($"Duplicate {assetId}"); - } - RequestModelLoad(assetId, reason); - } - - /// Request loading a model with a given Poly Asset ID. - /// Pass the reason the Model is being pulled into memory, for logging purposes. - /// - /// Upon completion, the asset will: - /// - be in "failed download" state (don't know how to check for this) - /// - be in "download succeeded, load failed" state (check Model.Error != null) - /// - be in "download succeeded, load succeeded" state (check Model.m_Valid) - /// - /// The intent is that this method will ignore previous failures and try again. - /// If you don't want to retry a failed load-into-memory, you should check Model.Error first. - /// If you aren't trying to do a hot-reload, you should check Model.m_Valid first. - public void RequestModelLoad(string assetId, string reason) - { - // Don't attempt to load models which are already loading. - if (IsLoading(assetId)) - { - return; - } - - if (m_ModelsByAssetId.ContainsKey(assetId)) - { - // Already downloaded. - // It may be in memory already, but it's safe to ask for it to be brought in again. - // That way we get the behavior of "ignore a failed load-into-memory" - m_RequestLoadQueue.Add(new ModelLoadRequest(m_ModelsByAssetId[assetId], reason)); - m_IsLoadingMemo.Add(assetId); - } - else - { - // Not downloaded yet. - // Kick off a download; when done the load will and arrange for the download-complete to kick off the - // load-into-memory work. - string assetDir = GetCacheDirectoryForAsset(assetId); - try - { - // For the case that the folder exists, but the files were removed. - if (!Directory.Exists(assetDir)) - { - Directory.CreateDirectory(assetDir); - } - } - catch (UnauthorizedAccessException) - { - Debug.LogError("Cannot create directory for online asset download."); - } - - // Then request the asset from Poly. - AssetGetter request = VrAssetService.m_Instance.GetAsset( - assetId, VrAssetFormat.GLTF2, reason); - StartCoroutine(request.GetAssetCoroutine()); - m_ActiveRequests.Add(request); - m_IsLoadingMemo.Add(assetId); - } - } - - /// The inverse of RequestModelLoad(). - /// Returns true if the model is no longer in any load queues. - /// The current implementation is only a half-hearted effort, so it may return false. - public bool CancelRequestModelLoad(string assetId) - { - if (IsLoading(assetId)) - { - // Might be tricky to safely remove from this queue, but at least mark it so that - // it doesn't go from "downloading" to "loading into memory" - bool isInActiveRequests = false; - foreach (var elt in m_ActiveRequests) - { - if (elt.Asset.Id == assetId) - { - elt.IsCanceled = true; - isInActiveRequests = true; - } - } - - // Removing from RequestLoadQueue is easy; there's no computation associated with it (yet) - m_RequestLoadQueue.RemoveAll(elt => elt.AssetId == assetId); - bool isInRequestLoadQueue = false; - - { - bool wasInLoadQueue = false; - foreach (var elt in m_LoadQueue) - { - if (elt.AssetId == assetId) - { - elt.Model.CancelLoadModelAsync(); - wasInLoadQueue = true; - } - } - if (wasInLoadQueue) - { - m_LoadQueue = m_LoadQueue.Where(elt => elt.AssetId != assetId).ToList(); - } - } - bool isInLoadQueue = false; - - // Could just invalidate the cache, but we're going to have to rebuild it in just a moment, - // and we have enough information to mutate it properly. - if (!isInActiveRequests && !isInRequestLoadQueue && !isInLoadQueue) - { - m_IsLoadingMemo.Remove(assetId); - } - } - - return !IsLoading(assetId); - } - - /// Downloads models referenced by the passed sketch. - /// Pass the reason this is happening. - /// TODO: maybe annotate the download request so we can choose whether they turn - /// into model loads? - public void PrecacheModels(SceneFileInfo sceneFileInfo, string reason) - { - if (string.IsNullOrEmpty(App.Config.GoogleSecrets?.ApiKey)) - { - return; - } - StartCoroutine(PrecacheModelsCoroutine(sceneFileInfo, reason)); - } - - /// Waits for the json data to be read on a background thread, and then executes a precache - /// coroutine for each found asset. - private IEnumerator PrecacheModelsCoroutine(SceneFileInfo sceneFileInfo, string reason) - { - var getIdsFuture = new Future>(() => GetModelIds(sceneFileInfo)); - List ids; - while (true) - { - try - { - if (getIdsFuture.TryGetResult(out ids)) { break; } - } - catch (FutureFailed e) - { - throw new Exception($"While reading {sceneFileInfo}", e); - } - yield return null; - } - - if (ids == null) { yield break; } - List> precacheCoroutines = new List>(); - // Only trigger off one precache routine per frame. - foreach (string id in ids) - { - if (m_ModelsByAssetId.ContainsKey(id)) - { - // Already cached - continue; - } - if (!FileUtils.InitializeDirectory(GetCacheDirectoryForAsset(id))) - { - continue; - } - precacheCoroutines.Add(PrecacheCoroutine( - VrAssetService.m_Instance.GetAsset(id, VrAssetFormat.GLTF2, reason))); - yield return null; - } - - var cr = CoroutineUtil.CompleteAllCoroutines(precacheCoroutines); - while (cr.MoveNext()) - { - yield return cr.Current; - } - } - - /// Returns all non-null asset ids from the passed sketch's metadata. - /// null return value means "empty list". - /// Raises exception on error. - private static List GetModelIds(SceneFileInfo sceneFileInfo) - { - // Json deserializing is in a separate method that doesn't access Unity objects so that it - // can be called on a thread. The json deserializing can be pretty slow and can cause - // frame drops if performed on the main thread. - Stream metadata = SaveLoadScript.GetMetadataReadStream(sceneFileInfo); - if (metadata == null) - { - if (sceneFileInfo.Exists) - { - // ??? Let's try to provoke an exception to propagate to the caller - using (var dummy = File.OpenRead(sceneFileInfo.FullPath)) { } - throw new Exception($"Unknown error opening metadata {sceneFileInfo.FullPath}"); - } - else - { - throw new Exception( - "Reading metadata from nonexistent " + - $"{sceneFileInfo.InfoType} {sceneFileInfo.HumanName}"); - } - } - using (var jsonReader = new JsonTextReader(new StreamReader(metadata))) - { - var jsonData = SaveLoadScript.m_Instance.DeserializeMetadata(jsonReader); - if (SaveLoadScript.m_Instance.LastMetadataError != null) - { - throw new Exception($"Deserialize error: {SaveLoadScript.m_Instance.LastMetadataError}"); - } - if (jsonData.ModelIndex == null) { return null; } - return jsonData.ModelIndex.Select(m => m.AssetId).Where(a => a != null).ToList(); - } - } - - // The directory for the asset must have already been created - IEnumerator PrecacheCoroutine(AssetGetter request) - { - string assetId = request.Asset.Id; - var cr = request.GetAssetCoroutine(); - while (true) - { - try - { - bool result = cr.MoveNext(); - if (!result) - { - break; - } - } - catch (VrAssetServiceException e) - { - ControllerConsoleScript.m_Instance.AddNewLine(e.Message); - Debug.LogException(e); - yield break; - } - yield return cr.Current; - } - while (!request.IsReady) { yield return null; } - request.Asset.WriteToDisk(); - string path = Path.Combine(GetCacheDirectoryForAsset(assetId), request.Asset.RootFilePath); - m_ModelsByAssetId[assetId] = new Model(Model.Location.PolyAsset(assetId, path)); - } - - public void UpdateCatalog() - { - // Walk backwards so removal doesn't mess up our indexing. - for (int i = m_ActiveRequests.Count - 1; i >= 0; --i) - { - AssetGetter request = m_ActiveRequests[i]; - if (request.IsReady || request.IsCanceled) - { - if (request.Asset.ValidAsset) - { - if (request.Asset.WriteToDisk()) - { - string assetId = request.Asset.Id; - string path = - Path.Combine(GetCacheDirectoryForAsset(assetId), request.Asset.RootFilePath); - // TODO: This assumes PolyRawAssets are models. This may not be true in the - // future and should the VrAssetProtos.ElementType request parameter to - // VrAssetService.m_Instance.GetAsset to decide how to store and index the asset. - - // Populate map entry for this new model. - m_ModelsByAssetId[assetId] = new Model(Model.Location.PolyAsset(assetId, path)); - - // After download the model should be loaded too, unless the request was canceled. - // TODO: this seems a littttle suspect. Just because it finished downloading, - // does that mean we still want to bring it into memory? - if (!request.IsCanceled) - { - m_RequestLoadQueue.Add( - new ModelLoadRequest( - m_ModelsByAssetId[assetId], $"{request.Reason} fetched")); - } - else - { - // Just reset, in case asset is on one of the other queues. - m_IsLoadingMemo = null; - } - } - } - else - { - Debug.LogWarning("Downloaded asset is empty " + request.Asset.RootFilePath); - } - - m_ActiveRequests.RemoveAt(i); - m_NotifyListeners = true; - } - } - - if (m_RequestLoadQueue.Count > 0 && m_LoadQueue.Count == 0) - { - // Move a single item from "request load" to "load". Too many items on the load queue - // causes bad stuttering (at least for heavy models). - var toMove = m_RequestLoadQueue[0]; - // TODO: how is it possible for m_RequestLoadQueue to contain duplicates? - m_RequestLoadQueue = m_RequestLoadQueue - .Where(elt => elt.AssetId != toMove.AssetId) - .ToList(); - m_LoadQueue.Add(toMove); - } - - // Always call this to poll the async loader. - LoadModelsInQueueAsync(); - - // Shout from the hills. - if (m_NotifyListeners) - { - if (CatalogChanged != null) - { - CatalogChanged(); - } - m_NotifyListeners = false; - } - } - - void LoadModelsInQueueAsync() - { - UnityEngine.Profiling.Profiler.BeginSample("PAC.LoadModelsInQueueAsync"); - for (int i = m_LoadQueue.Count - 1; i >= 0; --i) - { - Model model = m_LoadQueue[i].Model; - if (!model.IsLoading()) - { - // If the overlay is up, hitching is okay; so avoid the slow threaded image load. - bool useThreadedImageLoad = - OverlayManager.m_Instance.CurrentOverlayState == OverlayState.Hidden; - model.LoadModelAsync(useThreadedImageLoad); - } - else - { - if (model.TryLoadModel()) - { - m_LoadQueue.RemoveAt(i); - m_IsLoadingMemo = null; - m_NotifyListeners = true; - } - } - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - private static HashSet SetMinus(HashSet lhs, HashSet rhs) - { - var result = new HashSet(lhs); - result.ExceptWith(rhs); - return result; - } - - private IEnumerator RefreshAssetSet(IcosaSetType type) - { - List models = new List(); - // When the list is empty, make it the actual list acted upon so that results start - // showing up immediately. - if (m_AssetSetByType[type].m_Models.Count == 0) - { - m_AssetSetByType[type].m_Models = models; - } - AssetLister lister = VrAssetService.m_Instance.ListAssets(type); - while (lister.HasMore || models.Count == 0) - { - using (var cr = lister.NextPage(models, m_ThumbnailSuffix)) - { - while (true) - { - try - { - if (!cr.MoveNext()) - { - break; - } - } - catch (VrAssetServiceException e) - { - ControllerConsoleScript.m_Instance.AddNewLine(e.Message); - Debug.LogException(e); - yield break; - } - yield return cr.Current; - } - } - if (models.Count == 0) - { - break; - } - } - // As the assets may already have models loaded into them, just add any new models and - // remove old ones. - var newIds = new HashSet(models.Select(m => m.AssetId)); - var oldIds = new HashSet(m_AssetSetByType[type].m_Models.Select(m => m.AssetId)); - // These must be reified; if they are left as lazy IEnumerables, O(n^2) behavior results - HashSet toAdd = SetMinus(newIds, oldIds); - HashSet toRemove = SetMinus(oldIds, newIds); - m_AssetSetByType[type].m_Models.RemoveAll(m => toRemove.Contains(m.AssetId)); - m_AssetSetByType[type].m_Models.InsertRange(0, models.Where(m => toAdd.Contains(m.AssetId))); - if (CatalogChanged != null) - { - CatalogChanged(); - } - } - - void RefreshFetchCoroutines() - { - if (App.GoogleIdentity.Profile != null) - { - m_AssetSetByType[IcosaSetType.User].m_RefreshRequested = true; - m_AssetSetByType[IcosaSetType.Liked].m_RefreshRequested = true; - } - else - { - AssetSet set = m_AssetSetByType[IcosaSetType.User]; - if (set.m_FetchMetadataCoroutine != null) - { - StopCoroutine(set.m_FetchMetadataCoroutine); - set.m_FetchMetadataCoroutine = null; - } - set.m_Models.Clear(); - set = m_AssetSetByType[IcosaSetType.Liked]; - if (set.m_FetchMetadataCoroutine != null) - { - StopCoroutine(set.m_FetchMetadataCoroutine); - set.m_FetchMetadataCoroutine = null; - } - set.m_Models.Clear(); - if (CatalogChanged != null) - { - CatalogChanged(); - } - } - } - - void OnProfileUpdated(OAuth2Identity _) - { - RefreshFetchCoroutines(); - } - - void OnDestroy() - { - OAuth2Identity.ProfileUpdated -= OnProfileUpdated; - } - - public int NumCloudModels(IcosaSetType type) - { - return m_AssetSetByType[type].m_Models.Count(); - } - - public AssetDetails GetPolyAsset(IcosaSetType type, int index) - { - return m_AssetSetByType[type].m_Models[index]; - } - - // Ideally we would check against the format info from Poly that we have all the required - // elements but for now we know that there should be exactly one .gltf/.gltf2 and a .bin - // Returns the filename of the .gltf/.gltf2 file, or null if not valid. - private static string ValidGltfCache(string dir, string assetId) - { - if (Directory.GetFiles(dir, "*.bin").Length == 0) - { - return null; - } - - var filesGltf1 = Directory.GetFiles(dir, "*.gltf"); - var filesGltf2 = Directory.GetFiles(dir, "*.gltf2"); - if (filesGltf1.Length + filesGltf2.Length != 1) - { - return null; - } - else if (filesGltf1.Length == 1) - { - return filesGltf1[0]; - } - else - { - return filesGltf2[0]; - } - } - - public void ClearLoadingQueue() - { - m_LoadQueue.Clear(); - m_RequestLoadQueue.Clear(); - m_IsLoadingMemo = null; - foreach (var req in m_ActiveRequests) - { - req.IsCanceled = true; - } - } - - public void UnloadUnusedModels() - { - foreach (var model in m_ModelsByAssetId.Values.Where(x => x != null && x.m_UsageCount == 0)) - { - model.UnloadModel(); - } - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using UnityEngine.Networking; +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; + +namespace TiltBrush +{ + + /// Used as an accessor for files downloaded from Poly and cached on local storage. + public partial class IcosaAssetCatalog : MonoBehaviour + { + const int kAssetDiskCacheSize = 1000; + const float kThumbnailFetchRate = 15; + const int kThumbnailFetchMaxCount = 30; + const int kThumbnailReadRate = 4; + + // This may be a bit broader than an asset id, but it's a safe set of + // filename characters. + // Change - added . % ~ to allow urlencoded urls + static readonly Regex sm_AssetIdPattern = new Regex(@"^[a-zA-Z0-9-_%~\.]+$"); + + public enum AssetLoadState + { + Unknown, + NotDownloaded, + Downloading, + Downloaded, // On disk but not in memory + // DownloadFailed, // We don't keep track of download errors, so this becomes "NotDownloaded" + Loading, + LoadFailed, // This shows up as a !Valid model in the model catalog + Loaded + } + + public class AssetDetails + { + // Disabled only because there isn't a pressing reason to enable it. + const bool kLazyLoadThumbnail = false; + + private readonly IcosaAssetCatalog m_Owner; + private readonly Texture2D m_Thumbnail; + private string m_ThumbnailUrl; // if non-null, have not attempted to fetch it yet + + public string AssetId { get; } + public string HumanName { get; } + public string AccountName { get; } + public Quaternion? ModelRotation { get; } + + public Texture2D Thumbnail + { + get + { + if (m_ThumbnailUrl != null) + { + string url = m_ThumbnailUrl; + m_ThumbnailUrl = null; + DownloadThumbnailAsync(url); + } + return m_Thumbnail; + } + } + + public Model Model { get { return App.IcosaAssetCatalog.GetModel(AssetId); } } + + public AssetDetails( + JToken json, string accountName, string thumbnailSuffix) + { + m_Owner = App.IcosaAssetCatalog; + HumanName = json["Title"].ToString(); + AssetId = json["ID"].ToString(); + AccountName = accountName; + ModelRotation = Quaternion.identity; + m_Thumbnail = new Texture2D(4, 4, TextureFormat.ARGB32, false); + // Thumbnail URL can be a .webp, but if so we rely on there also being a jpg version + m_ThumbnailUrl = json["Thumbnail"].ToString().Replace(".webp", ".jpg"); + if (!string.IsNullOrEmpty(thumbnailSuffix)) + { + m_ThumbnailUrl = string.Format("{0}{1}", m_ThumbnailUrl, thumbnailSuffix); + } + if (!kLazyLoadThumbnail) + { + // Pre-emptive thumbnail fetch + _ = Thumbnail; + } + } + + /// Returns the contents of path, or null if the cache doesn't exist / can't be read. + /// It's ok to pass null. + /// Does not raise exceptions. + private static byte[] SafeReadCache(string path) + { + if (path != null && File.Exists(path)) + { + try + { + return File.ReadAllBytes(path); + } + catch (IOException e) + { + Debug.LogWarning($"Could not read cache {path}: {e}"); + } + } + return null; + } + + /// Updates the contents of path. + /// It's ok to pass null path or contents; passing contents=null clears the cache file. + /// Does not raise exceptions. + private static void SafeWriteCache(string path, byte[] contents) + { + if (path == null) { return; } + try { File.Delete(path); } + catch { } + if (contents != null) + { + string dir = Path.GetDirectoryName(path); + if (!Directory.Exists(dir)) + { + try { Directory.CreateDirectory(dir); } + catch { } + } + try { File.WriteAllBytes(path, contents); } + catch { } + } + } + + async void DownloadThumbnailAsync(string thumbnailUrl) + { + string cachePath = Path.Combine(m_Owner.m_ThumbnailCacheDir, AssetId); + byte[] thumbnailBytes = SafeReadCache(cachePath); + + if (thumbnailBytes == null) + { + await m_Owner.m_thumbnailFetchLimiter.WaitAsync(); + WebRequest www = new WebRequest(thumbnailUrl, + App.GoogleIdentity, + UnityWebRequest.kHttpVerbGET); + + await www.SendAsync(); + + while (m_Owner.m_thumbnailReadLimiter.IsBlocked()) + { + await Awaiters.NextFrame; + } + thumbnailBytes = www.ResultBytes; + SafeWriteCache(cachePath, thumbnailBytes); + } + + if (thumbnailBytes != null) + { + try + { + // TODO: fix aspect ratio of thumbnail + RawImage imageData = await new ThreadedImageReader(thumbnailBytes, thumbnailUrl); + + UnityEngine.Profiling.Profiler.BeginSample("AssetDetails.DownloadThumbnail:LoadImage"); + if (imageData != null) + { + m_Thumbnail.Resize(imageData.ColorWidth, imageData.ColorHeight, + TextureFormat.ARGB32, false); + m_Thumbnail.SetPixels32(imageData.ColorData); + m_Thumbnail.Apply(updateMipmaps: false, makeNoLongerReadable: true); + } + UnityEngine.Profiling.Profiler.EndSample(); + + // m_Thumbnail still points to the same Texture2D, so we don't need to send CatalogChanged + } + catch (Exception) + { + SafeWriteCache(cachePath, null); + throw; + } + } + } + } + + private static Vector3? GetCameraForward(JToken cameraParams) + { + if (cameraParams == null) + { + return null; + } + JToken cameraMatrix = cameraParams["matrix4x4"]; + if (cameraMatrix == null) { return null; } + // The third column holds the camera's forward. + Vector3 cameraForward = new Vector3(); + cameraForward.x = float.Parse(cameraMatrix[2].ToString()); + cameraForward.y = float.Parse(cameraMatrix[6].ToString()); + cameraForward.z = float.Parse(cameraMatrix[10].ToString()); + return cameraForward; + } + + private class AssetSet + { + public List m_Models = new List(); + public IEnumerator m_FetchMetadataCoroutine; + public bool m_RefreshRequested; + public float m_CooldownTimer; + } + + /// A request to pull a Model into memory. + /// It's assumed that the Model already exists on disk. + public class ModelLoadRequest + { + public readonly Model Model; + /// The reason for the Model being pulled into memory. + public readonly string Reason; + public string AssetId => Model.AssetId; + public ModelLoadRequest(Model model, string reason) + { + Model = model; + Reason = reason; + } + } + + public event Action CatalogChanged; + + [SerializeField] private string m_ThumbnailSuffix = "s128"; + /// Assets being downloaded to disk. + /// When done, these get moved to m_RequestLoadQueue. + private List m_ActiveRequests; + /// Assets that someone wants to bring from disk into memory. + /// Precondition: they are on disk + /// These get moved onto m_LoadQueue periodically. + /// May contain duplicates? + /// TODO: figure out why we have this intermediate stage + private List m_RequestLoadQueue; + private List m_LoadQueue; + /// Memoization data for IsLoading(). + /// Set this to null to invalidate it; or (if you are very confident) mutate it. + /// Invariant: either null, or the union of m_ActiveRequests, m_RequestLoadQueue, m_LoadQueue. + private HashSet m_IsLoadingMemo = null; + private string m_CacheDir; + private string m_ThumbnailCacheDir; + private Dictionary m_ModelsByAssetId; + private Dictionary m_AssetSetByType; + private bool m_NotifyListeners; + + private AwaitableRateLimiter m_thumbnailFetchLimiter = + new AwaitableRateLimiter(kThumbnailFetchRate, kThumbnailFetchMaxCount); + private RateLimiter m_thumbnailReadLimiter = + new RateLimiter(maxEventsPerFrame: kThumbnailReadRate); + + /// Returns true if the assetId is in any of our download or load queues + public bool IsLoading(string assetId) + { + // This needs caching because it's hammered every frame by the model buttons :-/ + if (m_IsLoadingMemo == null) + { + m_IsLoadingMemo = new HashSet(); + m_IsLoadingMemo.UnionWith(m_ActiveRequests.Select(request => request.Asset.Id)); + m_IsLoadingMemo.UnionWith(m_RequestLoadQueue.Select(request => request.AssetId)); + m_IsLoadingMemo.UnionWith(m_LoadQueue.Select(request => request.AssetId)); + } + return m_IsLoadingMemo.Contains(assetId); + } + + public void RequestRefresh(IcosaSetType type) + { + // We don't update featured except on startup. + if (type != IcosaSetType.Featured && App.GoogleIdentity.LoggedIn) + { + m_AssetSetByType[type].m_RefreshRequested = true; + } + } + + public void Init() + { + string cacheDir = Path.Combine(Application.persistentDataPath, "assetCache"); + m_CacheDir = cacheDir.Replace("\\", "/"); + // Use a different directory from m_CacheDir to avoid having to make ValidGltfCache() + // smart enough to allow directories with only a thumbnail and no asset data. + m_ThumbnailCacheDir = Path.Combine(Application.persistentDataPath, "assetThumbnail") + .Replace("\\", "/"); + m_ActiveRequests = new List(); + m_RequestLoadQueue = new List(); + m_LoadQueue = new List(); + + FileUtils.InitializeDirectoryWithUserError(m_CacheDir, "Failed to create asset cache"); + + // Create and populate model map. + m_ModelsByAssetId = new Dictionary(); + try + { + foreach (string folderPath in EnumerateCacheDirectories()) + { + string folderName = Path.GetFileName(folderPath); + string gltfFile = ValidGltfCache(folderPath, folderName); + if (gltfFile != null) + { + string path = Path.Combine(folderPath, folderName); + path = Path.Combine(path, gltfFile); + m_ModelsByAssetId[folderName] = new Model(Model.Location.IcosaAsset(folderName, path)); + } + else + { + Debug.LogWarningFormat("Deleting invalid cache folder {0}", folderName); + Directory.Delete(folderPath, true); + } + } + } + catch (DirectoryNotFoundException e) + { + Debug.LogException(e); + } + catch (UnauthorizedAccessException e) + { + Debug.LogException(e); + } + + m_AssetSetByType = new Dictionary + { + { + IcosaSetType.User, + new AssetSet() + }, + { + IcosaSetType.Liked, + new AssetSet() + }, + { + IcosaSetType.Featured, + new AssetSet { m_RefreshRequested = true } + } + }; + + App.Instance.AppExit += () => + { + var models = EnumerateCacheDirectories() + .OrderBy(d => Directory.GetLastAccessTimeUtc(d)).ToArray(); + for (int excess = models.Count() - kAssetDiskCacheSize; excess > 0; excess--) + { + Directory.Delete(models[excess - 1], true); + } + }; + } + + public AssetLoadState GetAssetLoadState(string assetId) + { + if (GetModel(assetId) is Model m) + { + // A model may be present in memory but also be still loading -- eg if someone + // requested that the load be retried. In this case it's kind of in two states; + // I'm somewhat arbitrarily choosing one. + if (m.m_Valid) { return AssetLoadState.Loaded; } + else if (m.Error != null) { return AssetLoadState.LoadFailed; } + else if (IsLoading(assetId)) + { + foreach (var elt in m_RequestLoadQueue) + { + if (elt.AssetId == assetId) + { + return AssetLoadState.Loading; + } + } + foreach (var elt in m_LoadQueue) + { + if (elt.AssetId == assetId) + { + return AssetLoadState.Loading; + } + } + // This should never happen and probably indicates some bug where m_AssetLoading hasn't + // been kept in sync with m_[Request]LoadQueue. + Debug.LogWarning($"Model for {assetId} is in an indeterminate state!"); + return AssetLoadState.Unknown; + } + else + { + return AssetLoadState.Downloaded; + } + } + else + { + foreach (var downloadRequest in m_ActiveRequests) + { + if (downloadRequest.Asset.Id == assetId) + { + return AssetLoadState.Downloading; + } + } + return AssetLoadState.NotDownloaded; + } + } + + public string GetCacheDirectoryForAsset(string asset) + { + if (!sm_AssetIdPattern.IsMatch(asset)) + { + Debug.LogWarningFormat("Not an asset id: {0}", asset); + return null; + } + return Path.Combine(m_CacheDir, asset); + } + + /// On any error, returns an empty enumeration + public IEnumerable EnumerateCacheDirectories() + { + try + { + return Directory.GetDirectories(m_CacheDir); + } + catch (UnauthorizedAccessException e) { Debug.LogException(e); } + catch (DirectoryNotFoundException e) { Debug.LogException(e); } + return new string[] { }; + } + + void Start() + { + OAuth2Identity.ProfileUpdated += OnProfileUpdated; + RefreshFetchCoroutines(); + } + + public Model GetModel(string assetId) + { + Model model; + if (!m_ModelsByAssetId.TryGetValue(assetId, out model)) + { + // null is actually the default for reference types, just being explicit here. + // ReSharper disable once RedundantAssignment + model = null; + } + return model; + } + + /// Checks to see if it's time to kick off a new refresh + /// Polls any refresh coroutines going on. + void Update() + { + m_thumbnailFetchLimiter.Tick(Time.deltaTime); + if (!VrAssetService.m_Instance.Available) + { + return; + } + + foreach (var entry in m_AssetSetByType) + { + var type = entry.Key; + var set = entry.Value; + + if (set.m_FetchMetadataCoroutine != null) + { + // Pump existing update coroutine + try + { + if (!set.m_FetchMetadataCoroutine.MoveNext()) + { + set.m_FetchMetadataCoroutine = null; + } + } + catch (VrAssetServiceException e) + { + ControllerConsoleScript.m_Instance.AddNewLine(e.Message); + Debug.LogException(e); + set.m_FetchMetadataCoroutine = null; + } + } + else if (set.m_RefreshRequested) + { + // Kick off a new refresh coroutine if it is time. + if (set.m_CooldownTimer <= 0) + { + set.m_FetchMetadataCoroutine = RefreshAssetSet(type); + set.m_RefreshRequested = false; + set.m_CooldownTimer = VrAssetService.m_Instance.m_SketchbookRefreshInterval; + } + } + if (set.m_CooldownTimer >= 0) + { + set.m_CooldownTimer -= Time.deltaTime; + } + } + } + + /// Pass the reason the Model is being pulled into memory, for logging purposes. + public void RequestModelLoad(Model model, string reason) + { + // Verify assumption that byAssetId[model.asset] == model; otherwise, caller may wait + // indefinitely for model's loaded state to change and that bug will be hard to track down. + string assetId = model.GetLocation().AssetId; + m_ModelsByAssetId.TryGetValue(assetId, out Model model2); + if (model2 != model) + { + // If we pretend to try to load the model, the caller may wait infinitely for the Model + // to load. + throw new InvalidOperationException($"Duplicate {assetId}"); + } + RequestModelLoad(assetId, reason); + } + + /// Request loading a model with a given Poly Asset ID. + /// Pass the reason the Model is being pulled into memory, for logging purposes. + /// + /// Upon completion, the asset will: + /// - be in "failed download" state (don't know how to check for this) + /// - be in "download succeeded, load failed" state (check Model.Error != null) + /// - be in "download succeeded, load succeeded" state (check Model.m_Valid) + /// + /// The intent is that this method will ignore previous failures and try again. + /// If you don't want to retry a failed load-into-memory, you should check Model.Error first. + /// If you aren't trying to do a hot-reload, you should check Model.m_Valid first. + public void RequestModelLoad(string assetId, string reason) + { + // Don't attempt to load models which are already loading. + if (IsLoading(assetId)) + { + return; + } + + if (m_ModelsByAssetId.ContainsKey(assetId)) + { + // Already downloaded. + // It may be in memory already, but it's safe to ask for it to be brought in again. + // That way we get the behavior of "ignore a failed load-into-memory" + m_RequestLoadQueue.Add(new ModelLoadRequest(m_ModelsByAssetId[assetId], reason)); + m_IsLoadingMemo.Add(assetId); + } + else + { + // Not downloaded yet. + // Kick off a download; when done the load will and arrange for the download-complete to kick off the + // load-into-memory work. + string assetDir = GetCacheDirectoryForAsset(assetId); + try + { + // For the case that the folder exists, but the files were removed. + if (!Directory.Exists(assetDir)) + { + Directory.CreateDirectory(assetDir); + } + } + catch (UnauthorizedAccessException) + { + Debug.LogError("Cannot create directory for online asset download."); + } + + // Then request the asset from Poly. + AssetGetter request = VrAssetService.m_Instance.GetAsset( + assetId, VrAssetFormat.GLTF2, reason); + StartCoroutine(request.GetAssetCoroutine()); + m_ActiveRequests.Add(request); + m_IsLoadingMemo.Add(assetId); + } + } + + /// The inverse of RequestModelLoad(). + /// Returns true if the model is no longer in any load queues. + /// The current implementation is only a half-hearted effort, so it may return false. + public bool CancelRequestModelLoad(string assetId) + { + if (IsLoading(assetId)) + { + // Might be tricky to safely remove from this queue, but at least mark it so that + // it doesn't go from "downloading" to "loading into memory" + bool isInActiveRequests = false; + foreach (var elt in m_ActiveRequests) + { + if (elt.Asset.Id == assetId) + { + elt.IsCanceled = true; + isInActiveRequests = true; + } + } + + // Removing from RequestLoadQueue is easy; there's no computation associated with it (yet) + m_RequestLoadQueue.RemoveAll(elt => elt.AssetId == assetId); + bool isInRequestLoadQueue = false; + + { + bool wasInLoadQueue = false; + foreach (var elt in m_LoadQueue) + { + if (elt.AssetId == assetId) + { + elt.Model.CancelLoadModelAsync(); + wasInLoadQueue = true; + } + } + if (wasInLoadQueue) + { + m_LoadQueue = m_LoadQueue.Where(elt => elt.AssetId != assetId).ToList(); + } + } + bool isInLoadQueue = false; + + // Could just invalidate the cache, but we're going to have to rebuild it in just a moment, + // and we have enough information to mutate it properly. + if (!isInActiveRequests && !isInRequestLoadQueue && !isInLoadQueue) + { + m_IsLoadingMemo.Remove(assetId); + } + } + + return !IsLoading(assetId); + } + + /// Downloads models referenced by the passed sketch. + /// Pass the reason this is happening. + /// TODO: maybe annotate the download request so we can choose whether they turn + /// into model loads? + public void PrecacheModels(SceneFileInfo sceneFileInfo, string reason) + { + if (string.IsNullOrEmpty(App.Config.GoogleSecrets?.ApiKey)) + { + return; + } + StartCoroutine(PrecacheModelsCoroutine(sceneFileInfo, reason)); + } + + /// Waits for the json data to be read on a background thread, and then executes a precache + /// coroutine for each found asset. + private IEnumerator PrecacheModelsCoroutine(SceneFileInfo sceneFileInfo, string reason) + { + var getIdsFuture = new Future>(() => GetModelIds(sceneFileInfo)); + List ids; + while (true) + { + try + { + if (getIdsFuture.TryGetResult(out ids)) { break; } + } + catch (FutureFailed e) + { + throw new Exception($"While reading {sceneFileInfo}", e); + } + yield return null; + } + + if (ids == null) { yield break; } + List> precacheCoroutines = new List>(); + // Only trigger off one precache routine per frame. + foreach (string id in ids) + { + if (m_ModelsByAssetId.ContainsKey(id)) + { + // Already cached + continue; + } + if (!FileUtils.InitializeDirectory(GetCacheDirectoryForAsset(id))) + { + continue; + } + precacheCoroutines.Add(PrecacheCoroutine( + VrAssetService.m_Instance.GetAsset(id, VrAssetFormat.GLTF2, reason))); + yield return null; + } + + var cr = CoroutineUtil.CompleteAllCoroutines(precacheCoroutines); + while (cr.MoveNext()) + { + yield return cr.Current; + } + } + + /// Returns all non-null asset ids from the passed sketch's metadata. + /// null return value means "empty list". + /// Raises exception on error. + private static List GetModelIds(SceneFileInfo sceneFileInfo) + { + // Json deserializing is in a separate method that doesn't access Unity objects so that it + // can be called on a thread. The json deserializing can be pretty slow and can cause + // frame drops if performed on the main thread. + Stream metadata = SaveLoadScript.GetMetadataReadStream(sceneFileInfo); + if (metadata == null) + { + if (sceneFileInfo.Exists) + { + // ??? Let's try to provoke an exception to propagate to the caller + using (var dummy = File.OpenRead(sceneFileInfo.FullPath)) { } + throw new Exception($"Unknown error opening metadata {sceneFileInfo.FullPath}"); + } + else + { + throw new Exception( + "Reading metadata from nonexistent " + + $"{sceneFileInfo.InfoType} {sceneFileInfo.HumanName}"); + } + } + using (var jsonReader = new JsonTextReader(new StreamReader(metadata))) + { + var jsonData = SaveLoadScript.m_Instance.DeserializeMetadata(jsonReader); + if (SaveLoadScript.m_Instance.LastMetadataError != null) + { + throw new Exception($"Deserialize error: {SaveLoadScript.m_Instance.LastMetadataError}"); + } + if (jsonData.ModelIndex == null) { return null; } + return jsonData.ModelIndex.Select(m => m.AssetId).Where(a => a != null).ToList(); + } + } + + // The directory for the asset must have already been created + IEnumerator PrecacheCoroutine(AssetGetter request) + { + string assetId = request.Asset.Id; + var cr = request.GetAssetCoroutine(); + while (true) + { + try + { + bool result = cr.MoveNext(); + if (!result) + { + break; + } + } + catch (VrAssetServiceException e) + { + ControllerConsoleScript.m_Instance.AddNewLine(e.Message); + Debug.LogException(e); + yield break; + } + yield return cr.Current; + } + while (!request.IsReady) { yield return null; } + request.Asset.WriteToDisk(); + string path = Path.Combine(GetCacheDirectoryForAsset(assetId), request.Asset.RootFilePath); + m_ModelsByAssetId[assetId] = new Model(Model.Location.IcosaAsset(assetId, path)); + } + + public void UpdateCatalog() + { + // Walk backwards so removal doesn't mess up our indexing. + for (int i = m_ActiveRequests.Count - 1; i >= 0; --i) + { + AssetGetter request = m_ActiveRequests[i]; + if (request.IsReady || request.IsCanceled) + { + if (request.Asset.ValidAsset) + { + if (request.Asset.WriteToDisk()) + { + string assetId = request.Asset.Id; + string path = + Path.Combine(GetCacheDirectoryForAsset(assetId), request.Asset.RootFilePath); + // TODO: This assumes PolyRawAssets are models. This may not be true in the + // future and should the VrAssetProtos.ElementType request parameter to + // VrAssetService.m_Instance.GetAsset to decide how to store and index the asset. + + // Populate map entry for this new model. + m_ModelsByAssetId[assetId] = new Model(Model.Location.IcosaAsset(assetId, path)); + + // After download the model should be loaded too, unless the request was canceled. + // TODO: this seems a littttle suspect. Just because it finished downloading, + // does that mean we still want to bring it into memory? + if (!request.IsCanceled) + { + m_RequestLoadQueue.Add( + new ModelLoadRequest( + m_ModelsByAssetId[assetId], $"{request.Reason} fetched")); + } + else + { + // Just reset, in case asset is on one of the other queues. + m_IsLoadingMemo = null; + } + } + } + else + { + Debug.LogWarning("Downloaded asset is empty " + request.Asset.RootFilePath); + } + + m_ActiveRequests.RemoveAt(i); + m_NotifyListeners = true; + } + } + + if (m_RequestLoadQueue.Count > 0 && m_LoadQueue.Count == 0) + { + // Move a single item from "request load" to "load". Too many items on the load queue + // causes bad stuttering (at least for heavy models). + var toMove = m_RequestLoadQueue[0]; + // TODO: how is it possible for m_RequestLoadQueue to contain duplicates? + m_RequestLoadQueue = m_RequestLoadQueue + .Where(elt => elt.AssetId != toMove.AssetId) + .ToList(); + m_LoadQueue.Add(toMove); + } + + // Always call this to poll the async loader. + LoadModelsInQueueAsync(); + + // Shout from the hills. + if (m_NotifyListeners) + { + if (CatalogChanged != null) + { + CatalogChanged(); + } + m_NotifyListeners = false; + } + } + + void LoadModelsInQueueAsync() + { + UnityEngine.Profiling.Profiler.BeginSample("PAC.LoadModelsInQueueAsync"); + for (int i = m_LoadQueue.Count - 1; i >= 0; --i) + { + Model model = m_LoadQueue[i].Model; + if (!model.IsLoading()) + { + // If the overlay is up, hitching is okay; so avoid the slow threaded image load. + bool useThreadedImageLoad = + OverlayManager.m_Instance.CurrentOverlayState == OverlayState.Hidden; + model.LoadModelAsync(useThreadedImageLoad); + } + else + { + if (model.TryLoadModel()) + { + m_LoadQueue.RemoveAt(i); + m_IsLoadingMemo = null; + m_NotifyListeners = true; + } + } + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + private static HashSet SetMinus(HashSet lhs, HashSet rhs) + { + var result = new HashSet(lhs); + result.ExceptWith(rhs); + return result; + } + + private IEnumerator RefreshAssetSet(IcosaSetType type) + { + List models = new List(); + // When the list is empty, make it the actual list acted upon so that results start + // showing up immediately. + if (m_AssetSetByType[type].m_Models.Count == 0) + { + m_AssetSetByType[type].m_Models = models; + } + AssetLister lister = VrAssetService.m_Instance.ListAssets(type); + while (lister.HasMore || models.Count == 0) + { + using (var cr = lister.NextPage(models, m_ThumbnailSuffix)) + { + while (true) + { + try + { + if (!cr.MoveNext()) + { + break; + } + } + catch (VrAssetServiceException e) + { + ControllerConsoleScript.m_Instance.AddNewLine(e.Message); + Debug.LogException(e); + yield break; + } + yield return cr.Current; + } + } + if (models.Count == 0) + { + break; + } + } + // As the assets may already have models loaded into them, just add any new models and + // remove old ones. + var newIds = new HashSet(models.Select(m => m.AssetId)); + var oldIds = new HashSet(m_AssetSetByType[type].m_Models.Select(m => m.AssetId)); + // These must be reified; if they are left as lazy IEnumerables, O(n^2) behavior results + HashSet toAdd = SetMinus(newIds, oldIds); + HashSet toRemove = SetMinus(oldIds, newIds); + m_AssetSetByType[type].m_Models.RemoveAll(m => toRemove.Contains(m.AssetId)); + m_AssetSetByType[type].m_Models.InsertRange(0, models.Where(m => toAdd.Contains(m.AssetId))); + if (CatalogChanged != null) + { + CatalogChanged(); + } + } + + void RefreshFetchCoroutines() + { + if (App.GoogleIdentity.Profile != null) + { + m_AssetSetByType[IcosaSetType.User].m_RefreshRequested = true; + m_AssetSetByType[IcosaSetType.Liked].m_RefreshRequested = true; + } + else + { + AssetSet set = m_AssetSetByType[IcosaSetType.User]; + if (set.m_FetchMetadataCoroutine != null) + { + StopCoroutine(set.m_FetchMetadataCoroutine); + set.m_FetchMetadataCoroutine = null; + } + set.m_Models.Clear(); + set = m_AssetSetByType[IcosaSetType.Liked]; + if (set.m_FetchMetadataCoroutine != null) + { + StopCoroutine(set.m_FetchMetadataCoroutine); + set.m_FetchMetadataCoroutine = null; + } + set.m_Models.Clear(); + if (CatalogChanged != null) + { + CatalogChanged(); + } + } + } + + void OnProfileUpdated(OAuth2Identity _) + { + RefreshFetchCoroutines(); + } + + void OnDestroy() + { + OAuth2Identity.ProfileUpdated -= OnProfileUpdated; + } + + public int NumCloudModels(IcosaSetType type) + { + return m_AssetSetByType[type].m_Models.Count(); + } + + public AssetDetails GetPolyAsset(IcosaSetType type, int index) + { + return m_AssetSetByType[type].m_Models[index]; + } + + // Ideally we would check against the format info from Poly that we have all the required + // elements but for now we know that there should be exactly one .gltf/.gltf2 and a .bin + // Returns the filename of the .gltf/.gltf2 file, or null if not valid. + private static string ValidGltfCache(string dir, string assetId) + { + if (Directory.GetFiles(dir, "*.bin").Length == 0) + { + return null; + } + + var filesGltf1 = Directory.GetFiles(dir, "*.gltf"); + var filesGltf2 = Directory.GetFiles(dir, "*.gltf2"); + if (filesGltf1.Length + filesGltf2.Length != 1) + { + return null; + } + else if (filesGltf1.Length == 1) + { + return filesGltf1[0]; + } + else + { + return filesGltf2[0]; + } + } + + public void ClearLoadingQueue() + { + m_LoadQueue.Clear(); + m_RequestLoadQueue.Clear(); + m_IsLoadingMemo = null; + foreach (var req in m_ActiveRequests) + { + req.IsCanceled = true; + } + } + + public void UnloadUnusedModels() + { + foreach (var model in m_ModelsByAssetId.Values.Where(x => x != null && x.m_UsageCount == 0)) + { + model.UnloadModel(); + } + } + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/Poly/PolyAssetCatalog.cs.meta b/Assets/Scripts/Poly/IcosaAssetCatalog.cs.meta similarity index 100% rename from Assets/Scripts/Poly/PolyAssetCatalog.cs.meta rename to Assets/Scripts/Poly/IcosaAssetCatalog.cs.meta diff --git a/Assets/Scripts/Poly/PolyRawAsset.cs b/Assets/Scripts/Poly/IcosaRawAsset.cs similarity index 93% rename from Assets/Scripts/Poly/PolyRawAsset.cs rename to Assets/Scripts/Poly/IcosaRawAsset.cs index 549b4fdc45..b845c0e100 100644 --- a/Assets/Scripts/Poly/PolyRawAsset.cs +++ b/Assets/Scripts/Poly/IcosaRawAsset.cs @@ -1,186 +1,186 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; - -namespace TiltBrush -{ - - /// Class that assists in getting an asset from Poly. - public class PolyRawAsset - { - private static readonly Regex kWindowsRootedPath = new Regex("^[A-Za-z]:[/\\\\]"); - - private string m_AssetId; - private VrAssetFormat m_DesiredAssetType; - - public class ElementInfo - { - public string filePath; - public string dataURL; - public byte[] assetBytes; - } - private ElementInfo m_RootElement; - private List m_ResourceElements; - - public string Id - { - get { return m_AssetId; } - } - - public VrAssetFormat DesiredType - { - get { return m_DesiredAssetType; } - } - - public string RootFilePath - { - get { return m_RootElement.filePath; } - } - - public string RootDataURL - { - get { return m_RootElement.dataURL; } - } - - public List ResourceElements - { - get { return m_ResourceElements; } - } - - public bool ValidAsset - { - get { return m_RootElement.assetBytes != null; } - } - - public void SetRootElement(string filePath, string dataURL) - { - m_RootElement.filePath = filePath; - m_RootElement.dataURL = dataURL; - } - - public void CopyBytesToRootElement(byte[] bytes) - { - m_RootElement.assetBytes = bytes; - } - - public void AddResourceElement(string filePath, string dataURL) - { - ElementInfo info = new ElementInfo(); - info.filePath = filePath; - info.dataURL = dataURL; - m_ResourceElements.Add(info); - } - - public PolyRawAsset(string assetId, VrAssetFormat assetType) - { - m_AssetId = assetId; - m_DesiredAssetType = assetType; - m_RootElement = new ElementInfo(); - m_ResourceElements = new List(); - } - - private void RemoveFiles(List filesToRemove) - { - for (int i = 0; i < filesToRemove.Count; i++) - { - System.IO.File.Delete(filesToRemove[i]); - } - } - - /// The directory must already have been created - public bool WriteToDisk() - { - // First, iterate over everything that needs to be written to disk and verify it's valid. - // If any invalid elements are found, it's likely due to a download failure and we will abort - // the entire process. - if (m_RootElement.assetBytes == null) - { - return false; - } - ulong requiredDiskSpace = (ulong)m_RootElement.assetBytes.Length; - for (int j = 0; j < m_ResourceElements.Count; ++j) - { - if (m_ResourceElements[j].assetBytes == null) - { - // Download failed on one of the elements. - return false; - } - else - { - requiredDiskSpace += (ulong)m_ResourceElements[j].assetBytes.Length; - } - } - - // Next, check to see if we have enough disk space to write all the files. - string assetDir = App.PolyAssetCatalog.GetCacheDirectoryForAsset(m_AssetId); - if (!FileUtils.HasFreeSpace(assetDir, requiredDiskSpace / (1024 * 1024))) - { - OutputWindowScript.Error(String.Format("Out of disk space! {0} {1}", - requiredDiskSpace, m_AssetId)); - return false; - } - - // - // Next, begin writing to disk, remembering each file written to make the operation atomic. - // - var written = new List(); - if (!Directory.Exists(assetDir)) - { - UnityEngine.Debug.LogErrorFormat("Caller did not create directory for me: {0}", assetDir); - } - string rootFilePath = Path.Combine(assetDir, GetPolySanitizedFilePath(m_RootElement.filePath)); - if (!FileUtils.InitializeDirectory(Path.GetDirectoryName(rootFilePath)) || - !FileUtils.WriteBytesIgnoreExceptions(m_RootElement.assetBytes, rootFilePath)) - { - return false; - } - written.Add(rootFilePath); - - // Write all resources to disk - for (int j = 0; j < m_ResourceElements.Count; ++j) - { - string filePath = Path.Combine(assetDir, GetPolySanitizedFilePath(m_ResourceElements[j].filePath)); - if (!FileUtils.InitializeDirectory(Path.GetDirectoryName(filePath)) || - !FileUtils.WriteBytesIgnoreExceptions(m_ResourceElements[j].assetBytes, filePath)) - { - RemoveFiles(written); - return false; - } - written.Add(filePath); - } - return true; - } - - /// Returns a non-rooted path. - public static string GetPolySanitizedFilePath(string path) - { - // I'd like to change this to use FileUtils sanitize but that would involve - // clearing everyone's cache to force updating to the new naming - - // The kWindowsRootedPath check is there for compatibility with how this function used - // to work; it is not necessary for correctness. Even without the check, this - // function still always returns non-rooted paths. - if (Path.IsPathRooted(path) || kWindowsRootedPath.IsMatch(path)) - { - return Path.GetFileName(path); - } - return path; - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace TiltBrush +{ + + /// Class that assists in getting an asset from Icosa. + public class IcosaRawAsset + { + private static readonly Regex kWindowsRootedPath = new Regex("^[A-Za-z]:[/\\\\]"); + + private string m_AssetId; + private VrAssetFormat m_DesiredAssetType; + + public class ElementInfo + { + public string filePath; + public string dataURL; + public byte[] assetBytes; + } + private ElementInfo m_RootElement; + private List m_ResourceElements; + + public string Id + { + get { return m_AssetId; } + } + + public VrAssetFormat DesiredType + { + get { return m_DesiredAssetType; } + } + + public string RootFilePath + { + get { return m_RootElement.filePath; } + } + + public string RootDataURL + { + get { return m_RootElement.dataURL; } + } + + public List ResourceElements + { + get { return m_ResourceElements; } + } + + public bool ValidAsset + { + get { return m_RootElement.assetBytes != null; } + } + + public void SetRootElement(string filePath, string dataURL) + { + m_RootElement.filePath = filePath; + m_RootElement.dataURL = dataURL; + } + + public void CopyBytesToRootElement(byte[] bytes) + { + m_RootElement.assetBytes = bytes; + } + + public void AddResourceElement(string filePath, string dataURL) + { + ElementInfo info = new ElementInfo(); + info.filePath = filePath; + info.dataURL = dataURL; + m_ResourceElements.Add(info); + } + + public IcosaRawAsset(string assetId, VrAssetFormat assetType) + { + m_AssetId = assetId; + m_DesiredAssetType = assetType; + m_RootElement = new ElementInfo(); + m_ResourceElements = new List(); + } + + private void RemoveFiles(List filesToRemove) + { + for (int i = 0; i < filesToRemove.Count; i++) + { + System.IO.File.Delete(filesToRemove[i]); + } + } + + /// The directory must already have been created + public bool WriteToDisk() + { + // First, iterate over everything that needs to be written to disk and verify it's valid. + // If any invalid elements are found, it's likely due to a download failure and we will abort + // the entire process. + if (m_RootElement.assetBytes == null) + { + return false; + } + ulong requiredDiskSpace = (ulong)m_RootElement.assetBytes.Length; + for (int j = 0; j < m_ResourceElements.Count; ++j) + { + if (m_ResourceElements[j].assetBytes == null) + { + // Download failed on one of the elements. + return false; + } + else + { + requiredDiskSpace += (ulong)m_ResourceElements[j].assetBytes.Length; + } + } + + // Next, check to see if we have enough disk space to write all the files. + string assetDir = App.IcosaAssetCatalog.GetCacheDirectoryForAsset(m_AssetId); + if (!FileUtils.HasFreeSpace(assetDir, requiredDiskSpace / (1024 * 1024))) + { + OutputWindowScript.Error(String.Format("Out of disk space! {0} {1}", + requiredDiskSpace, m_AssetId)); + return false; + } + + // + // Next, begin writing to disk, remembering each file written to make the operation atomic. + // + var written = new List(); + if (!Directory.Exists(assetDir)) + { + UnityEngine.Debug.LogErrorFormat("Caller did not create directory for me: {0}", assetDir); + } + string rootFilePath = Path.Combine(assetDir, GetPolySanitizedFilePath(m_RootElement.filePath)); + if (!FileUtils.InitializeDirectory(Path.GetDirectoryName(rootFilePath)) || + !FileUtils.WriteBytesIgnoreExceptions(m_RootElement.assetBytes, rootFilePath)) + { + return false; + } + written.Add(rootFilePath); + + // Write all resources to disk + for (int j = 0; j < m_ResourceElements.Count; ++j) + { + string filePath = Path.Combine(assetDir, GetPolySanitizedFilePath(m_ResourceElements[j].filePath)); + if (!FileUtils.InitializeDirectory(Path.GetDirectoryName(filePath)) || + !FileUtils.WriteBytesIgnoreExceptions(m_ResourceElements[j].assetBytes, filePath)) + { + RemoveFiles(written); + return false; + } + written.Add(filePath); + } + return true; + } + + /// Returns a non-rooted path. + public static string GetPolySanitizedFilePath(string path) + { + // I'd like to change this to use FileUtils sanitize but that would involve + // clearing everyone's cache to force updating to the new naming + + // The kWindowsRootedPath check is there for compatibility with how this function used + // to work; it is not necessary for correctness. Even without the check, this + // function still always returns non-rooted paths. + if (Path.IsPathRooted(path) || kWindowsRootedPath.IsMatch(path)) + { + return Path.GetFileName(path); + } + return path; + } + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/Poly/PolyRawAsset.cs.meta b/Assets/Scripts/Poly/IcosaRawAsset.cs.meta similarity index 100% rename from Assets/Scripts/Poly/PolyRawAsset.cs.meta rename to Assets/Scripts/Poly/IcosaRawAsset.cs.meta diff --git a/Assets/Scripts/Save/FileSketchSet.cs b/Assets/Scripts/Save/FileSketchSet.cs index eea1d40fbd..0ea4071dc2 100644 --- a/Assets/Scripts/Save/FileSketchSet.cs +++ b/Assets/Scripts/Save/FileSketchSet.cs @@ -1,537 +1,537 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.IO; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using UnityEngine; - -namespace TiltBrush -{ - - public class FileSketchSet : SketchSet - { - static int ICON_LOAD_PER_FRAME = 3; - - /// Synchronously read thumbnail. Returns null on error. - public static byte[] ReadThumbnail(SceneFileInfo fileinfo) - { - using (Stream s = fileinfo.GetReadStream(TiltFile.FN_THUMBNAIL)) - { - if (s == null) { return null; } - byte[] buffer = new byte[32 * 1024]; - MemoryStream ms = new MemoryStream(); - while (true) - { - int read = s.Read(buffer, 0, buffer.Length); - if (read == 0) - { - return ms.ToArray(); - } - ms.Write(buffer, 0, read); - } - } - } - - private class FileSketch : Sketch, IComparable - { - private DiskSceneFileInfo m_FileInfo; - private Texture2D m_Icon; - private string[] m_Authors; - private bool m_bMetadataValid; - private IEnumerator m_coroutine = null; - - public SceneFileInfo SceneFileInfo - { - get { return m_FileInfo; } - } - - public string[] Authors - { - get { return m_Authors; } - } - - public Texture2D Icon - { - get { return m_Icon; } - } - - public FileSketch(DiskSceneFileInfo info) - { - m_FileInfo = info; - m_bMetadataValid = false; - - if (m_Authors == null || m_Authors.Length == 0) - { - if (m_FileInfo.HumanName.Contains(" by ")) - { - var sections = m_FileInfo.HumanName.Split(" by "); - m_Authors = new[] { sections.LastOrDefault() }; - m_FileInfo.HumanName = string.Join(" by ", sections.SkipLast(1)); - } - } - } - - public bool IconAndMetadataValid - { - get { return m_bMetadataValid; } - } - - private IEnumerable RequestLoadIconAndMetadataCoroutineThreaded() - { - var thumbFuture = new Future(() => ReadThumbnail(m_FileInfo)); - byte[] data; - while (!thumbFuture.TryGetResult(out data)) { yield return null; } - - if (data != null && data.Length > 0) - { - if (m_Icon == null) - { - m_Icon = new Texture2D(128, 128, TextureFormat.RGB24, true); - } - m_Icon.LoadImage(data); - m_Icon.Apply(); - } - else - { - // TODO: why is the icon missing/invalid? should we be noisier - // about invalid files? But RequestLoadIcon() doesn't have a - // way of indicating "not a tilt" - } - if (m_Authors == null) - { - var metadataFuture = new Future(() => m_FileInfo.ReadMetadata()); - SketchMetadata metadata; - while (!metadataFuture.TryGetResult(out metadata)) - { - yield return null; - } - if (metadata != null) - { - m_Authors = metadata.Authors; - } - else - { - if (SaveLoadScript.m_Instance.LastMetadataError != null) - { - ControllerConsoleScript.m_Instance.AddNewLine( - string.Format("Error detected in sketch '{0}'.\nTry re-saving.", - m_FileInfo.HumanName)); - Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", - m_FileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); - } - } - } - m_bMetadataValid = true; - } - - /// Returns true if the request was fully processed. - public bool RequestLoadIconAndMetadata() - { - if (m_bMetadataValid) - { - Debug.Assert(m_coroutine == null); - return true; - } - - if (m_coroutine == null) - { - if (!m_FileInfo.Exists) - { - return true; - } - m_coroutine = RequestLoadIconAndMetadataCoroutineThreaded().GetEnumerator(); - } - - if (!m_coroutine.MoveNext()) - { - m_coroutine = null; - return true; - } - else - { - return false; - } - } - - private void AbortPendingRequest() - { - if (m_coroutine != null) - { - // This case should almost never be needed, so I'm giving it - // a rubbish, but simple and safe implementation. - while (m_coroutine.MoveNext()) - { - Thread.Sleep(0); - } - m_coroutine = null; - } - } - - public void UnloadIcon() - { - AbortPendingRequest(); - UnityEngine.Object.Destroy(m_Icon); - m_Icon = null; - m_bMetadataValid = false; - } - - public int CompareTo(FileSketch rCompareSketch) - { - return rCompareSketch.m_FileInfo.CreationTime.CompareTo(m_FileInfo.CreationTime); - } - } - - protected SketchSetType m_Type; - protected bool m_ReadyForAccess; - private List m_Sketches; - private Stack m_RequestedLoads; - private FileWatcher m_FileWatcher; - // collection of sketch paths to be added / deleted from set - // Access to these containers is synchronized, but note this is not ConcurrentQueue. - // File watcher thread is producer, main thread is consumer. - private Queue m_ToAdd; - private Queue m_ToDelete; - private bool m_ReadOnly; - private string m_SketchesPath; - - public SketchSetType Type - { - get { return m_Type; } - } - - public bool IsReadyForAccess - { - get { return m_ReadyForAccess; } - } - - public bool IsActivelyRefreshingSketches - { - get { return false; } - } - - public bool RequestedIconsAreLoaded - { - get { return m_RequestedLoads.Count == 0; } - } - - public int NumSketches - { - get { return m_Sketches.Count; } - } - - public FileSketchSet() - { - m_Type = SketchSetType.User; - m_ReadyForAccess = false; - m_RequestedLoads = new Stack(); - m_Sketches = new List(); - m_ToAdd = Queue.Synchronized(new Queue()); - m_ToDelete = Queue.Synchronized(new Queue()); - m_ReadOnly = false; - m_SketchesPath = App.UserSketchPath(); - } - - public FileSketchSet(string path) - { - m_Type = SketchSetType.Curated; - m_ReadyForAccess = false; - m_RequestedLoads = new Stack(); - m_Sketches = new List(); - m_ToAdd = Queue.Synchronized(new Queue()); - m_ToDelete = Queue.Synchronized(new Queue()); - m_ReadOnly = true; - m_SketchesPath = path; - } - - public bool IsSketchIndexValid(int iIndex) - { - return (iIndex >= 0 && iIndex < m_Sketches.Count); - } - - // Returns true if metadata.json has been deserialized. - // Icon texture and SketchMetadata may be null if invalid. - public bool GetSketchIcon(int iSketchIndex, out Texture2D icon, out string[] authors, - out string description) - { - description = null; - if (!IsSketchIndexValid(iSketchIndex)) - { - Debug.Log("SketchCatalog Error: Invalid index for Sketch Metadata requested."); - icon = null; - authors = null; - return false; - } - else if (!m_Sketches[iSketchIndex].IconAndMetadataValid) - { - icon = null; - authors = null; - return false; - } - - icon = m_Sketches[iSketchIndex].Icon; - authors = m_Sketches[iSketchIndex].Authors; - return true; - } - - public SceneFileInfo GetSketchSceneFileInfo(int iSketchIndex) - { - if (!IsSketchIndexValid(iSketchIndex)) - { - Debug.Log("SketchCatalog Error: Invalid index for Sketch SceneFileInfo requested."); - return null; - } - return m_Sketches[iSketchIndex].SceneFileInfo; - } - - public string GetSketchName(int iSketchIndex) - { - if (!IsSketchIndexValid(iSketchIndex)) - { - Debug.Log("SketchCatalog Error: Invalid index for Sketch Name requested."); - return null; - } - return m_Sketches[iSketchIndex].SceneFileInfo.HumanName; - } - - public void PrecacheSketchModels(int iSketchIndex) - { - if (!IsSketchIndexValid(iSketchIndex)) - { - Debug.Log("SketchCatalog Error: Invalid index for Sketch Name requested."); - return; - } - App.PolyAssetCatalog.PrecacheModels( - m_Sketches[iSketchIndex].SceneFileInfo, $"FileSketchSet {iSketchIndex}"); - } - - public virtual void DeleteSketch(int toDelete) - { - // Not sure why we need the validity check; but if any are invalid, it - // is a serious error and we shouldn't touch anything - if (!IsSketchIndexValid(toDelete)) - { - Debug.Assert(false, "Sketch set index out of range"); - return; - } - - // Notify our file watcher to make sure it got the memo this sketch was deleted. - m_FileWatcher.NotifyDelete(m_Sketches[toDelete].SceneFileInfo.FullPath); - - // Notify the drive sketchset as the deleted file may now be visible there. - var driveSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); - if (driveSet != null) - { - driveSet.NotifySketchChanged(m_Sketches[toDelete].SceneFileInfo.FullPath); - } - - m_Sketches[toDelete].SceneFileInfo.Delete(); - } - - public virtual void RenameSketch(int toRename, string newName) - { - // Notify our file watcher to make sure it got the memo this sketch was deleted. - m_FileWatcher.NotifyDelete(m_Sketches[toRename].SceneFileInfo.FullPath); - - // Notify the drive sketchset as the deleted file may now be visible there. - var driveSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); - if (driveSet != null) - { - driveSet.NotifySketchChanged(m_Sketches[toRename].SceneFileInfo.FullPath); - } - - var newPath = m_Sketches[toRename].SceneFileInfo.Rename(newName); - - m_FileWatcher.NotifyCreated(newPath); - - } - - public virtual void Init() - { - ProcessDirectory(m_SketchesPath); - m_ReadyForAccess = true; - - // No real reason to do this; SaveLoadScript creates the directory itself - try { Directory.CreateDirectory(m_SketchesPath); } - catch (IOException) { } - catch (UnauthorizedAccessException) { } - - if (Directory.Exists(m_SketchesPath) && !m_ReadOnly) - { - m_FileWatcher = new FileWatcher(m_SketchesPath, "*" + SaveLoadScript.TILT_SUFFIX); - // TODO: improve robustness. Using Created works for typical copy and move operations, but - // doesn't handle e.g. streaming file. - // Note: Renamed event not implemented on OS X, so we rely on Deleted + Created - // If we ever start doing something special (like warning the user) with deleted or added, we - // may need to add an explicit 'changed' queue, but delete then create works fine for now. - m_FileWatcher.FileCreated += (object sender, FileSystemEventArgs e) => - { - m_ToAdd.Enqueue(e.FullPath); - }; - m_FileWatcher.FileDeleted += (object sender, FileSystemEventArgs e) => - { - m_ToDelete.Enqueue(e.FullPath); - }; - m_FileWatcher.FileChanged += (object sender, FileSystemEventArgs e) => - { - m_ToDelete.Enqueue(e.FullPath); - m_ToAdd.Enqueue(e.FullPath); - }; - m_FileWatcher.EnableRaisingEvents = true; - } - } - - /// Requests that we (as asynchronously as possible) load thumbnail icons - /// and metadata for the specified sketches. - /// - /// Takes ownership of the list. - public void RequestOnlyLoadedMetadata(List requests) - { - DumpIconTextures(); // This clears out any pending requests - m_RequestedLoads.Clear(); - - requests.Reverse(); - foreach (var iSketch in requests) - { - Debug.Assert(IsSketchIndexValid(iSketch)); - m_RequestedLoads.Push(iSketch); - } - } - - void DumpIconTextures() - { - for (int i = 0; i < m_Sketches.Count; ++i) - { - m_Sketches[i].UnloadIcon(); - } - Resources.UnloadUnusedAssets(); - } - - private Stack Update__working = new Stack(); - - public void NotifySketchCreated(string fullpath) - { - m_FileWatcher.NotifyCreated(fullpath); - } - - public void NotifySketchChanged(string fullpath) - { - m_FileWatcher.NotifyChanged(fullpath); - } - - public void RequestRefresh() - { - } - - public void Update() - { - // process async directory changes from file system watcher - // note: code here assumes we're the only consumer - bool changedEvent = false; - while (m_ToDelete.Count > 0) - { - string path = (string)m_ToDelete.Dequeue(); - if (RemoveSketchByPath(path)) - { - changedEvent = true; - } - } - // be nice and only add one sketch per frame, since we're validating header - if (m_ToAdd.Count > 0) - { - string path = (string)m_ToAdd.Dequeue(); - var fileInfo = new DiskSceneFileInfo(path, readOnly: m_ReadOnly); - if (fileInfo.IsHeaderValid()) - { - AddSketchToSet(fileInfo); - m_Sketches.Sort(); - changedEvent = true; - } - } - if (changedEvent) - { - OnChanged(); - } - - // Grab a few units of work - var working = Update__working; // = new Stack(); - Debug.Assert(working.Count == 0); - for (int i = 0; i < ICON_LOAD_PER_FRAME && m_RequestedLoads.Count > 0; ++i) - { - working.Push(m_RequestedLoads.Pop()); - } - - // Process work (perhaps generating future work) - while (working.Count > 0) - { - int iSketch = working.Pop(); - if (!m_Sketches[iSketch].RequestLoadIconAndMetadata()) - { - m_RequestedLoads.Push(iSketch); - } - } - } - - private void ProcessDirectory(string path) - { - var di = new DirectoryInfo(path); - if (!di.Exists) - { - return; - } - foreach (DiskSceneFileInfo info in SaveLoadScript.IterScenes(di, m_ReadOnly)) - { - //don't add bogus files to the catalog - if (info.IsHeaderValid()) - { - AddSketchToSet(info); - } - } - m_Sketches.Sort(); - } - - protected void AddSketchToSet(DiskSceneFileInfo rInfo) - { - m_Sketches.Add(new FileSketch(rInfo)); - } - - /// Remove sketch with given path from set, returning false if no such sketch. - private bool RemoveSketchByPath(string path) - { - // TODO: avoid this O(n) call by changing SketchSet API to not be index-based - int index = m_Sketches.FindIndex(sketch => sketch.SceneFileInfo.FullPath == path); - if (index != -1) - { - m_Sketches.RemoveAt(index); - return true; - } - else - { - return false; - } - } - - public event Action OnChanged = delegate { }; - - public event Action OnSketchRefreshingChanged - { - add { } - remove { } - } - - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using UnityEngine; + +namespace TiltBrush +{ + + public class FileSketchSet : SketchSet + { + static int ICON_LOAD_PER_FRAME = 3; + + /// Synchronously read thumbnail. Returns null on error. + public static byte[] ReadThumbnail(SceneFileInfo fileinfo) + { + using (Stream s = fileinfo.GetReadStream(TiltFile.FN_THUMBNAIL)) + { + if (s == null) { return null; } + byte[] buffer = new byte[32 * 1024]; + MemoryStream ms = new MemoryStream(); + while (true) + { + int read = s.Read(buffer, 0, buffer.Length); + if (read == 0) + { + return ms.ToArray(); + } + ms.Write(buffer, 0, read); + } + } + } + + private class FileSketch : Sketch, IComparable + { + private DiskSceneFileInfo m_FileInfo; + private Texture2D m_Icon; + private string[] m_Authors; + private bool m_bMetadataValid; + private IEnumerator m_coroutine = null; + + public SceneFileInfo SceneFileInfo + { + get { return m_FileInfo; } + } + + public string[] Authors + { + get { return m_Authors; } + } + + public Texture2D Icon + { + get { return m_Icon; } + } + + public FileSketch(DiskSceneFileInfo info) + { + m_FileInfo = info; + m_bMetadataValid = false; + + if (m_Authors == null || m_Authors.Length == 0) + { + if (m_FileInfo.HumanName.Contains(" by ")) + { + var sections = m_FileInfo.HumanName.Split(" by "); + m_Authors = new[] { sections.LastOrDefault() }; + m_FileInfo.HumanName = string.Join(" by ", sections.SkipLast(1)); + } + } + } + + public bool IconAndMetadataValid + { + get { return m_bMetadataValid; } + } + + private IEnumerable RequestLoadIconAndMetadataCoroutineThreaded() + { + var thumbFuture = new Future(() => ReadThumbnail(m_FileInfo)); + byte[] data; + while (!thumbFuture.TryGetResult(out data)) { yield return null; } + + if (data != null && data.Length > 0) + { + if (m_Icon == null) + { + m_Icon = new Texture2D(128, 128, TextureFormat.RGB24, true); + } + m_Icon.LoadImage(data); + m_Icon.Apply(); + } + else + { + // TODO: why is the icon missing/invalid? should we be noisier + // about invalid files? But RequestLoadIcon() doesn't have a + // way of indicating "not a tilt" + } + if (m_Authors == null) + { + var metadataFuture = new Future(() => m_FileInfo.ReadMetadata()); + SketchMetadata metadata; + while (!metadataFuture.TryGetResult(out metadata)) + { + yield return null; + } + if (metadata != null) + { + m_Authors = metadata.Authors; + } + else + { + if (SaveLoadScript.m_Instance.LastMetadataError != null) + { + ControllerConsoleScript.m_Instance.AddNewLine( + string.Format("Error detected in sketch '{0}'.\nTry re-saving.", + m_FileInfo.HumanName)); + Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", + m_FileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); + } + } + } + m_bMetadataValid = true; + } + + /// Returns true if the request was fully processed. + public bool RequestLoadIconAndMetadata() + { + if (m_bMetadataValid) + { + Debug.Assert(m_coroutine == null); + return true; + } + + if (m_coroutine == null) + { + if (!m_FileInfo.Exists) + { + return true; + } + m_coroutine = RequestLoadIconAndMetadataCoroutineThreaded().GetEnumerator(); + } + + if (!m_coroutine.MoveNext()) + { + m_coroutine = null; + return true; + } + else + { + return false; + } + } + + private void AbortPendingRequest() + { + if (m_coroutine != null) + { + // This case should almost never be needed, so I'm giving it + // a rubbish, but simple and safe implementation. + while (m_coroutine.MoveNext()) + { + Thread.Sleep(0); + } + m_coroutine = null; + } + } + + public void UnloadIcon() + { + AbortPendingRequest(); + UnityEngine.Object.Destroy(m_Icon); + m_Icon = null; + m_bMetadataValid = false; + } + + public int CompareTo(FileSketch rCompareSketch) + { + return rCompareSketch.m_FileInfo.CreationTime.CompareTo(m_FileInfo.CreationTime); + } + } + + protected SketchSetType m_Type; + protected bool m_ReadyForAccess; + private List m_Sketches; + private Stack m_RequestedLoads; + private FileWatcher m_FileWatcher; + // collection of sketch paths to be added / deleted from set + // Access to these containers is synchronized, but note this is not ConcurrentQueue. + // File watcher thread is producer, main thread is consumer. + private Queue m_ToAdd; + private Queue m_ToDelete; + private bool m_ReadOnly; + private string m_SketchesPath; + + public SketchSetType Type + { + get { return m_Type; } + } + + public bool IsReadyForAccess + { + get { return m_ReadyForAccess; } + } + + public bool IsActivelyRefreshingSketches + { + get { return false; } + } + + public bool RequestedIconsAreLoaded + { + get { return m_RequestedLoads.Count == 0; } + } + + public int NumSketches + { + get { return m_Sketches.Count; } + } + + public FileSketchSet() + { + m_Type = SketchSetType.User; + m_ReadyForAccess = false; + m_RequestedLoads = new Stack(); + m_Sketches = new List(); + m_ToAdd = Queue.Synchronized(new Queue()); + m_ToDelete = Queue.Synchronized(new Queue()); + m_ReadOnly = false; + m_SketchesPath = App.UserSketchPath(); + } + + public FileSketchSet(string path) + { + m_Type = SketchSetType.Curated; + m_ReadyForAccess = false; + m_RequestedLoads = new Stack(); + m_Sketches = new List(); + m_ToAdd = Queue.Synchronized(new Queue()); + m_ToDelete = Queue.Synchronized(new Queue()); + m_ReadOnly = true; + m_SketchesPath = path; + } + + public bool IsSketchIndexValid(int iIndex) + { + return (iIndex >= 0 && iIndex < m_Sketches.Count); + } + + // Returns true if metadata.json has been deserialized. + // Icon texture and SketchMetadata may be null if invalid. + public bool GetSketchIcon(int iSketchIndex, out Texture2D icon, out string[] authors, + out string description) + { + description = null; + if (!IsSketchIndexValid(iSketchIndex)) + { + Debug.Log("SketchCatalog Error: Invalid index for Sketch Metadata requested."); + icon = null; + authors = null; + return false; + } + else if (!m_Sketches[iSketchIndex].IconAndMetadataValid) + { + icon = null; + authors = null; + return false; + } + + icon = m_Sketches[iSketchIndex].Icon; + authors = m_Sketches[iSketchIndex].Authors; + return true; + } + + public SceneFileInfo GetSketchSceneFileInfo(int iSketchIndex) + { + if (!IsSketchIndexValid(iSketchIndex)) + { + Debug.Log("SketchCatalog Error: Invalid index for Sketch SceneFileInfo requested."); + return null; + } + return m_Sketches[iSketchIndex].SceneFileInfo; + } + + public string GetSketchName(int iSketchIndex) + { + if (!IsSketchIndexValid(iSketchIndex)) + { + Debug.Log("SketchCatalog Error: Invalid index for Sketch Name requested."); + return null; + } + return m_Sketches[iSketchIndex].SceneFileInfo.HumanName; + } + + public void PrecacheSketchModels(int iSketchIndex) + { + if (!IsSketchIndexValid(iSketchIndex)) + { + Debug.Log("SketchCatalog Error: Invalid index for Sketch Name requested."); + return; + } + App.IcosaAssetCatalog.PrecacheModels( + m_Sketches[iSketchIndex].SceneFileInfo, $"FileSketchSet {iSketchIndex}"); + } + + public virtual void DeleteSketch(int toDelete) + { + // Not sure why we need the validity check; but if any are invalid, it + // is a serious error and we shouldn't touch anything + if (!IsSketchIndexValid(toDelete)) + { + Debug.Assert(false, "Sketch set index out of range"); + return; + } + + // Notify our file watcher to make sure it got the memo this sketch was deleted. + m_FileWatcher.NotifyDelete(m_Sketches[toDelete].SceneFileInfo.FullPath); + + // Notify the drive sketchset as the deleted file may now be visible there. + var driveSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); + if (driveSet != null) + { + driveSet.NotifySketchChanged(m_Sketches[toDelete].SceneFileInfo.FullPath); + } + + m_Sketches[toDelete].SceneFileInfo.Delete(); + } + + public virtual void RenameSketch(int toRename, string newName) + { + // Notify our file watcher to make sure it got the memo this sketch was deleted. + m_FileWatcher.NotifyDelete(m_Sketches[toRename].SceneFileInfo.FullPath); + + // Notify the drive sketchset as the deleted file may now be visible there. + var driveSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); + if (driveSet != null) + { + driveSet.NotifySketchChanged(m_Sketches[toRename].SceneFileInfo.FullPath); + } + + var newPath = m_Sketches[toRename].SceneFileInfo.Rename(newName); + + m_FileWatcher.NotifyCreated(newPath); + + } + + public virtual void Init() + { + ProcessDirectory(m_SketchesPath); + m_ReadyForAccess = true; + + // No real reason to do this; SaveLoadScript creates the directory itself + try { Directory.CreateDirectory(m_SketchesPath); } + catch (IOException) { } + catch (UnauthorizedAccessException) { } + + if (Directory.Exists(m_SketchesPath) && !m_ReadOnly) + { + m_FileWatcher = new FileWatcher(m_SketchesPath, "*" + SaveLoadScript.TILT_SUFFIX); + // TODO: improve robustness. Using Created works for typical copy and move operations, but + // doesn't handle e.g. streaming file. + // Note: Renamed event not implemented on OS X, so we rely on Deleted + Created + // If we ever start doing something special (like warning the user) with deleted or added, we + // may need to add an explicit 'changed' queue, but delete then create works fine for now. + m_FileWatcher.FileCreated += (object sender, FileSystemEventArgs e) => + { + m_ToAdd.Enqueue(e.FullPath); + }; + m_FileWatcher.FileDeleted += (object sender, FileSystemEventArgs e) => + { + m_ToDelete.Enqueue(e.FullPath); + }; + m_FileWatcher.FileChanged += (object sender, FileSystemEventArgs e) => + { + m_ToDelete.Enqueue(e.FullPath); + m_ToAdd.Enqueue(e.FullPath); + }; + m_FileWatcher.EnableRaisingEvents = true; + } + } + + /// Requests that we (as asynchronously as possible) load thumbnail icons + /// and metadata for the specified sketches. + /// + /// Takes ownership of the list. + public void RequestOnlyLoadedMetadata(List requests) + { + DumpIconTextures(); // This clears out any pending requests + m_RequestedLoads.Clear(); + + requests.Reverse(); + foreach (var iSketch in requests) + { + Debug.Assert(IsSketchIndexValid(iSketch)); + m_RequestedLoads.Push(iSketch); + } + } + + void DumpIconTextures() + { + for (int i = 0; i < m_Sketches.Count; ++i) + { + m_Sketches[i].UnloadIcon(); + } + Resources.UnloadUnusedAssets(); + } + + private Stack Update__working = new Stack(); + + public void NotifySketchCreated(string fullpath) + { + m_FileWatcher.NotifyCreated(fullpath); + } + + public void NotifySketchChanged(string fullpath) + { + m_FileWatcher.NotifyChanged(fullpath); + } + + public void RequestRefresh() + { + } + + public void Update() + { + // process async directory changes from file system watcher + // note: code here assumes we're the only consumer + bool changedEvent = false; + while (m_ToDelete.Count > 0) + { + string path = (string)m_ToDelete.Dequeue(); + if (RemoveSketchByPath(path)) + { + changedEvent = true; + } + } + // be nice and only add one sketch per frame, since we're validating header + if (m_ToAdd.Count > 0) + { + string path = (string)m_ToAdd.Dequeue(); + var fileInfo = new DiskSceneFileInfo(path, readOnly: m_ReadOnly); + if (fileInfo.IsHeaderValid()) + { + AddSketchToSet(fileInfo); + m_Sketches.Sort(); + changedEvent = true; + } + } + if (changedEvent) + { + OnChanged(); + } + + // Grab a few units of work + var working = Update__working; // = new Stack(); + Debug.Assert(working.Count == 0); + for (int i = 0; i < ICON_LOAD_PER_FRAME && m_RequestedLoads.Count > 0; ++i) + { + working.Push(m_RequestedLoads.Pop()); + } + + // Process work (perhaps generating future work) + while (working.Count > 0) + { + int iSketch = working.Pop(); + if (!m_Sketches[iSketch].RequestLoadIconAndMetadata()) + { + m_RequestedLoads.Push(iSketch); + } + } + } + + private void ProcessDirectory(string path) + { + var di = new DirectoryInfo(path); + if (!di.Exists) + { + return; + } + foreach (DiskSceneFileInfo info in SaveLoadScript.IterScenes(di, m_ReadOnly)) + { + //don't add bogus files to the catalog + if (info.IsHeaderValid()) + { + AddSketchToSet(info); + } + } + m_Sketches.Sort(); + } + + protected void AddSketchToSet(DiskSceneFileInfo rInfo) + { + m_Sketches.Add(new FileSketch(rInfo)); + } + + /// Remove sketch with given path from set, returning false if no such sketch. + private bool RemoveSketchByPath(string path) + { + // TODO: avoid this O(n) call by changing SketchSet API to not be index-based + int index = m_Sketches.FindIndex(sketch => sketch.SceneFileInfo.FullPath == path); + if (index != -1) + { + m_Sketches.RemoveAt(index); + return true; + } + else + { + return false; + } + } + + public event Action OnChanged = delegate { }; + + public event Action OnSketchRefreshingChanged + { + add { } + remove { } + } + + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Save/MetadataUtils.cs b/Assets/Scripts/Save/MetadataUtils.cs index cbd034561d..9ac4232c5a 100644 --- a/Assets/Scripts/Save/MetadataUtils.cs +++ b/Assets/Scripts/Save/MetadataUtils.cs @@ -1,446 +1,446 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using UnityEngine; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace TiltBrush -{ - public static class MetadataUtils - { - public struct WidgetMetadata - { - public TrTransform xf; - public string subtree; - public bool pinned; - public bool tinted; - public uint groupId; - public int layerId; - public bool twoSided; - } - - /// Sanitizes potentially-invalid data coming from the .tilt file. - /// Returns an array of valid TrTransforms; may return the input array. - public static TrTransform[] Sanitize(TrTransform[] data) - { - if (data != null) - { - for (int i = 0; i < data.Length; ++i) - { - if (!data[i].IsFinite()) - { - Debug.LogWarningFormat("Found non-finite TrTransform: {0}", data[i]); - return data.Where(xf => xf.IsFinite()).ToArray(); - } - } - } - - return data; - } - - private static float ByTranslation(WidgetMetadata meta) - { - return Vector3.Dot(meta.xf.translation, new Vector3(256 * 256, 256, 1)); - } - - private static float ByTranslation(TrTransform xf) - { - return Vector3.Dot(xf.translation, new Vector3(256 * 256, 256, 1)); - } - - private static string ByModelLocation(TiltModels75 models) - { - if (models.AssetId != null) - { - return "AssetId:" + models.AssetId; - } - else if (models.FilePath != null) - { - return "FilePath:" + models.FilePath; - } - Debug.LogWarning("Attempted to save model without asset id or filepath"); - return ""; - } - - public static CameraPathMetadata[] GetCameraPaths() - { - return WidgetManager.m_Instance.CameraPathWidgets - .Where(cpw => cpw.WidgetScript.ShouldSerialize()) - .Select(cpw => cpw.WidgetScript.AsSerializable()) - .ToArray(); - } - - public static LayerMetadata[] GetLayers() - { - return App.Scene.LayerCanvasesSerialized(); - } - - public static TiltModels75[] GetTiltModels(GroupIdMapping groupIdMapping) - { - var widgets = - WidgetManager.m_Instance.ModelWidgets.Where(w => w.gameObject.activeSelf).ToArray(); - if (widgets.Length == 0 && !ModelCatalog.m_Instance.MissingModels.Any()) - { - return null; - } - var widgetModels = widgets.Select(w => w.Model).Distinct(); - - Dictionary> modelLocationMap = - new Dictionary>(); - foreach (var model in widgetModels) - { - modelLocationMap[model.GetLocation()] = new List(); - } - foreach (var widget in widgets) - { - WidgetMetadata newEntry = new WidgetMetadata(); - newEntry.xf = widget.GetSaveTransform(); - newEntry.subtree = widget.Subtree; - newEntry.pinned = widget.Pinned; - newEntry.groupId = groupIdMapping.GetId(widget.Group); - newEntry.layerId = App.Scene.GetIndexOfCanvas(widget.Canvas); - modelLocationMap[widget.Model.GetLocation()].Add(newEntry); - } - - List models = new List(); - foreach (var elem in modelLocationMap) - { - var val = new TiltModels75 - { - Location = elem.Key, - }; - - // Order and align the metadata. - WidgetMetadata[] ordered = elem.Value.OrderBy(ByTranslation).ToArray(); - val.PinStates = new bool[ordered.Length]; - val.Subtrees = new string[ordered.Length]; - val.RawTransforms = new TrTransform[ordered.Length]; - val.GroupIds = new uint[ordered.Length]; - val.LayerIds = new int[ordered.Length]; - for (int i = 0; i < ordered.Length; ++i) - { - val.Subtrees[i] = ordered[i].subtree; - val.PinStates[i] = ordered[i].pinned; - val.RawTransforms[i] = ordered[i].xf; - val.GroupIds[i] = ordered[i].groupId; - val.LayerIds[i] = ordered[i].layerId; - } - models.Add(val); - } - - return models - .Concat(ModelCatalog.m_Instance.MissingModels) - .OrderBy(ByModelLocation).ToArray(); - } - - public static TiltVideo[] GetTiltVideos(GroupIdMapping groupIdMapping) - { - return WidgetManager.m_Instance.VideoWidgets.Where(x => x.gameObject.activeSelf).Select(x => ConvertVideoToTiltVideo(x)).ToArray(); - - TiltVideo ConvertVideoToTiltVideo(VideoWidget widget) - { - TiltVideo video = new TiltVideo - { - // Annoyingly Images now use forward slash and a leading dot. So this is inconsistent. - // Switching videos would have led to backwards incompatible changes in .tilt files - // or an annoying legacy - FilePath = widget.Video.PersistentPath, - AspectRatio = widget.Video.Aspect, - Pinned = widget.Pinned, - Transform = widget.SaveTransform, - GroupId = groupIdMapping.GetId(widget.Group), - LayerId = App.Scene.GetIndexOfCanvas(widget.Canvas), - TwoSided = widget.TwoSided - }; - if (widget.VideoController != null) - { - video.Paused = !widget.VideoController.Playing; - video.Time = widget.VideoController.Time; - video.Volume = widget.VideoController.Volume; - } - return video; - } - } - - public static Guides[] GetGuideIndex(GroupIdMapping groupIdMapping) - { - var stencils = - WidgetManager.m_Instance.StencilWidgets.Where(s => s.gameObject.activeSelf).ToList(); - if (stencils.Count == 0) - { - return null; - } - Dictionary> guides = - new Dictionary>(); - foreach (var stencil in stencils) - { - if (!guides.ContainsKey(stencil.Type)) - { - guides[stencil.Type] = new List(); - } - guides[stencil.Type].Add(stencil.GetSaveState(groupIdMapping)); - } - List guideIndex = new List(); - foreach (var elem in guides) - { - guideIndex.Add(new Guides - { - Type = elem.Key, - States = elem.Value.OrderBy(s => ByTranslation(s.Transform)).ToArray() - }); - } - return guideIndex.OrderBy(g => g.Type).ToArray(); - } - - public static TiltLights[] GetTiltLights(GroupIdMapping groupIdMapping) - { - var imports = WidgetManager.m_Instance.LightWidgets - .Where(w => w.gameObject.activeSelf).ToArray(); - if (imports.Length == 0) - { - return null; - } - - var lightIndex = new List(); - foreach (var lightWidget in imports) - { - var light = lightWidget.GetComponentInChildren(); - var newEntry = new TiltLights(); - newEntry.Transform = lightWidget.GetSaveTransform(); - newEntry.Pinned = lightWidget.Pinned; - newEntry.GroupId = groupIdMapping.GetId(lightWidget.Group); - newEntry.LayerId = App.Scene.GetIndexOfCanvas(lightWidget.Canvas); - - newEntry.PunctualLightType = light.type; - newEntry.Intensity = light.intensity; - newEntry.LightColor = light.color; - - if (light.type == LightType.Spot) - { - newEntry.InnerConeAngle = light.innerSpotAngle; - newEntry.OuterConeAngle = light.spotAngle; - } - - if (light.type == LightType.Point || light.type == LightType.Spot) - { - newEntry.Range = light.range; - } - - lightIndex.Add(newEntry); - } - return lightIndex.ToArray(); - } - - public static TiltImages75[] GetTiltImages(GroupIdMapping groupIdMapping) - { - var imports = WidgetManager.m_Instance.ImageWidgets - .Where(w => w.gameObject.activeSelf).ToArray(); - if (imports.Length == 0) - { - return null; - } - - // From the list of image widgets in the sketch, create a map that contains a unique - // entry per image, with associated metadata (transform and pin state) stored as arrays. - Dictionary> imagesByPath = - new Dictionary>(); - Dictionary aspectRatios = new Dictionary(); - foreach (var image in imports) - { - string path = image.RelativePath; - if (image.AspectRatio == null) - { - Debug.LogError("Trying to save partially-initialized image {fileName}"); - } - if (!imagesByPath.ContainsKey(path)) - { - imagesByPath[path] = new List(); - aspectRatios[path] = image.AspectRatio ?? 1; - } - WidgetMetadata newEntry = new WidgetMetadata(); - newEntry.xf = image.SaveTransform; - newEntry.pinned = image.Pinned; - newEntry.tinted = image.UseLegacyTint; - newEntry.groupId = groupIdMapping.GetId(image.Group); - newEntry.layerId = App.Scene.GetIndexOfCanvas(image.Canvas); - newEntry.twoSided = image.TwoSided; - imagesByPath[path].Add(newEntry); - } - - // Build the save metadata from our unique map. - List imageIndex = new List(); - foreach (var elem in imagesByPath) - { - var val = new TiltImages75 - { - FilePath = elem.Key, - FileName = Path.GetFileName(elem.Key), - AspectRatio = aspectRatios[elem.Key] - }; - - // Order and align the metadata. - WidgetMetadata[] ordered = - elem.Value.OrderBy(ByTranslation).ToArray(); - val.PinStates = new bool[ordered.Length]; - val.TintStates = new bool[ordered.Length]; - val.Transforms = new TrTransform[ordered.Length]; - val.GroupIds = new uint[ordered.Length]; - val.LayerIds = new int[ordered.Length]; - val.TwoSidedFlags = new bool[ordered.Length]; - for (int i = 0; i < ordered.Length; ++i) - { - val.PinStates[i] = ordered[i].pinned; - val.TintStates[i] = ordered[i].tinted; - val.Transforms[i] = ordered[i].xf; - val.GroupIds[i] = ordered[i].groupId; - val.LayerIds[i] = ordered[i].layerId; - val.TwoSidedFlags[i] = ordered[i].twoSided; - } - imageIndex.Add(val); - } - return imageIndex.OrderBy(i => i.FileName).ToArray(); - } - - public static void VerifyMetadataVersion(SketchMetadata data) - { - Upgrade_Set_ModelIndexInSet(data); - - if (data.SchemaVersion < 1) - { - UpgradeSchema_0to1(data); - } - if (data.SchemaVersion < 2) - { - UpgradeSchema_1to2(data); - } - } - - // Converts data.Set_deprecated[] to data.ModelIndex[].InSet - static void Upgrade_Set_ModelIndexInSet(SketchMetadata data) - { - if (data == null || data.Set_deprecated == null) - { - return; - } - - TiltModels75[] index = data.ModelIndex; - - string[] set = data.Set_deprecated; - if (index == null) { index = new TiltModels75[] { }; } - List setOnly = new List(set); - foreach (TiltModels75 m in index) - { - if (set.Contains(m.FilePath)) - { - m.InSet_deprecated = true; - setOnly.Remove(m.FilePath); - } - } - index = index.Concat(setOnly.Select(s => new TiltModels75 - { - FilePath = s, - InSet_deprecated = true - })).ToArray(); - - data.ModelIndex = index; - data.Set_deprecated = null; - } - - /// SchemaVersion 1 was released in M15. - /// It adds bool[] TiltModels75.PinStates, bool[] TiltModels75.TintStates, bool Guides.State.Pinned - static void UpgradeSchema_0to1(SketchMetadata data) - { - // Pin flags were not written out previous to v15, so default guides to pinned. - if (data.GuideIndex != null) - { - for (int i = 0; i < data.GuideIndex.Length; ++i) - { - for (int j = 0; j < data.GuideIndex[i].States.Length; ++j) - { - data.GuideIndex[i].States[j].Pinned = true; - } - } - } - - // Default images to pinned, tinted, and not grouped. - if (data.ImageIndex != null) - { - for (int i = 0; i < data.ImageIndex.Length; ++i) - { - int numXfs = data.ImageIndex[i].Transforms.Length; - data.ImageIndex[i].PinStates = Enumerable.Repeat(true, numXfs).ToArray(); - data.ImageIndex[i].TintStates = Enumerable.Repeat(true, numXfs).ToArray(); - data.ImageIndex[i].GroupIds = Enumerable.Repeat(0u, numXfs).ToArray(); - } - } - - // Default models to pinned, if they're local. Poly assets are unpinned. - if (data.ModelIndex != null) - { - for (int i = 0; i < data.ModelIndex.Length; ++i) - { - if (data.ModelIndex[i].PinStates == null) - { - int numXfs = (data.ModelIndex[i].Transforms != null) ? - data.ModelIndex[i].Transforms.Length : - data.ModelIndex[i].RawTransforms.Length; - data.ModelIndex[i].PinStates = new bool[numXfs]; - } - - for (int j = 0; j < data.ModelIndex[i].PinStates.Length; ++j) - { - data.ModelIndex[i].PinStates[j] = (data.ModelIndex[i].Location.GetLocationType() != - Model.Location.Type.PolyAssetId); - } - } - } - - data.SchemaVersion = 1; - } - - // Append value to array, creating array if necessary - static T[] SafeAppend(T[] array, T value) - { - T[] asArray = { value }; - return (array == null) ? asArray : array.Concat(asArray).ToArray(); - } - - // SchemaVersion 2: M19 - // Converts data.ModelIndex[].InSet to data.ModelIndex[].RawTransforms[] - // This changes the behavior slightly: sets are immovable, but RawTransforms[] can be unpinned. - // This essentially removes the experimental Set feature. - static void UpgradeSchema_1to2(SketchMetadata data) - { - Debug.Assert(data.SchemaVersion == 1); - if (data.ModelIndex == null) { return; } - foreach (TiltModels75 tm75 in data.ModelIndex) - { - if (!tm75.InSet_deprecated) { continue; } - // Only one of Transforms[] or RawTransforms[] can be non-null. - // Therefore, we can only append to RawTransforms[] if Transforms[] is null. - if (tm75.Transforms != null) - { - Debug.LogError("Cannot upgrade InSet if Transforms[] is non-null"); - continue; - } - tm75.InSet_deprecated = false; - tm75.m_rawTransforms = SafeAppend(tm75.m_rawTransforms, TrTransform.identity); - tm75.PinStates = SafeAppend(tm75.PinStates, true); - } - data.SchemaVersion = 2; - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace TiltBrush +{ + public static class MetadataUtils + { + public struct WidgetMetadata + { + public TrTransform xf; + public string subtree; + public bool pinned; + public bool tinted; + public uint groupId; + public int layerId; + public bool twoSided; + } + + /// Sanitizes potentially-invalid data coming from the .tilt file. + /// Returns an array of valid TrTransforms; may return the input array. + public static TrTransform[] Sanitize(TrTransform[] data) + { + if (data != null) + { + for (int i = 0; i < data.Length; ++i) + { + if (!data[i].IsFinite()) + { + Debug.LogWarningFormat("Found non-finite TrTransform: {0}", data[i]); + return data.Where(xf => xf.IsFinite()).ToArray(); + } + } + } + + return data; + } + + private static float ByTranslation(WidgetMetadata meta) + { + return Vector3.Dot(meta.xf.translation, new Vector3(256 * 256, 256, 1)); + } + + private static float ByTranslation(TrTransform xf) + { + return Vector3.Dot(xf.translation, new Vector3(256 * 256, 256, 1)); + } + + private static string ByModelLocation(TiltModels75 models) + { + if (models.AssetId != null) + { + return "AssetId:" + models.AssetId; + } + else if (models.FilePath != null) + { + return "FilePath:" + models.FilePath; + } + Debug.LogWarning("Attempted to save model without asset id or filepath"); + return ""; + } + + public static CameraPathMetadata[] GetCameraPaths() + { + return WidgetManager.m_Instance.CameraPathWidgets + .Where(cpw => cpw.WidgetScript.ShouldSerialize()) + .Select(cpw => cpw.WidgetScript.AsSerializable()) + .ToArray(); + } + + public static LayerMetadata[] GetLayers() + { + return App.Scene.LayerCanvasesSerialized(); + } + + public static TiltModels75[] GetTiltModels(GroupIdMapping groupIdMapping) + { + var widgets = + WidgetManager.m_Instance.ModelWidgets.Where(w => w.gameObject.activeSelf).ToArray(); + if (widgets.Length == 0 && !ModelCatalog.m_Instance.MissingModels.Any()) + { + return null; + } + var widgetModels = widgets.Select(w => w.Model).Distinct(); + + Dictionary> modelLocationMap = + new Dictionary>(); + foreach (var model in widgetModels) + { + modelLocationMap[model.GetLocation()] = new List(); + } + foreach (var widget in widgets) + { + WidgetMetadata newEntry = new WidgetMetadata(); + newEntry.xf = widget.GetSaveTransform(); + newEntry.subtree = widget.Subtree; + newEntry.pinned = widget.Pinned; + newEntry.groupId = groupIdMapping.GetId(widget.Group); + newEntry.layerId = App.Scene.GetIndexOfCanvas(widget.Canvas); + modelLocationMap[widget.Model.GetLocation()].Add(newEntry); + } + + List models = new List(); + foreach (var elem in modelLocationMap) + { + var val = new TiltModels75 + { + Location = elem.Key, + }; + + // Order and align the metadata. + WidgetMetadata[] ordered = elem.Value.OrderBy(ByTranslation).ToArray(); + val.PinStates = new bool[ordered.Length]; + val.Subtrees = new string[ordered.Length]; + val.RawTransforms = new TrTransform[ordered.Length]; + val.GroupIds = new uint[ordered.Length]; + val.LayerIds = new int[ordered.Length]; + for (int i = 0; i < ordered.Length; ++i) + { + val.Subtrees[i] = ordered[i].subtree; + val.PinStates[i] = ordered[i].pinned; + val.RawTransforms[i] = ordered[i].xf; + val.GroupIds[i] = ordered[i].groupId; + val.LayerIds[i] = ordered[i].layerId; + } + models.Add(val); + } + + return models + .Concat(ModelCatalog.m_Instance.MissingModels) + .OrderBy(ByModelLocation).ToArray(); + } + + public static TiltVideo[] GetTiltVideos(GroupIdMapping groupIdMapping) + { + return WidgetManager.m_Instance.VideoWidgets.Where(x => x.gameObject.activeSelf).Select(x => ConvertVideoToTiltVideo(x)).ToArray(); + + TiltVideo ConvertVideoToTiltVideo(VideoWidget widget) + { + TiltVideo video = new TiltVideo + { + // Annoyingly Images now use forward slash and a leading dot. So this is inconsistent. + // Switching videos would have led to backwards incompatible changes in .tilt files + // or an annoying legacy + FilePath = widget.Video.PersistentPath, + AspectRatio = widget.Video.Aspect, + Pinned = widget.Pinned, + Transform = widget.SaveTransform, + GroupId = groupIdMapping.GetId(widget.Group), + LayerId = App.Scene.GetIndexOfCanvas(widget.Canvas), + TwoSided = widget.TwoSided + }; + if (widget.VideoController != null) + { + video.Paused = !widget.VideoController.Playing; + video.Time = widget.VideoController.Time; + video.Volume = widget.VideoController.Volume; + } + return video; + } + } + + public static Guides[] GetGuideIndex(GroupIdMapping groupIdMapping) + { + var stencils = + WidgetManager.m_Instance.StencilWidgets.Where(s => s.gameObject.activeSelf).ToList(); + if (stencils.Count == 0) + { + return null; + } + Dictionary> guides = + new Dictionary>(); + foreach (var stencil in stencils) + { + if (!guides.ContainsKey(stencil.Type)) + { + guides[stencil.Type] = new List(); + } + guides[stencil.Type].Add(stencil.GetSaveState(groupIdMapping)); + } + List guideIndex = new List(); + foreach (var elem in guides) + { + guideIndex.Add(new Guides + { + Type = elem.Key, + States = elem.Value.OrderBy(s => ByTranslation(s.Transform)).ToArray() + }); + } + return guideIndex.OrderBy(g => g.Type).ToArray(); + } + + public static TiltLights[] GetTiltLights(GroupIdMapping groupIdMapping) + { + var imports = WidgetManager.m_Instance.LightWidgets + .Where(w => w.gameObject.activeSelf).ToArray(); + if (imports.Length == 0) + { + return null; + } + + var lightIndex = new List(); + foreach (var lightWidget in imports) + { + var light = lightWidget.GetComponentInChildren(); + var newEntry = new TiltLights(); + newEntry.Transform = lightWidget.GetSaveTransform(); + newEntry.Pinned = lightWidget.Pinned; + newEntry.GroupId = groupIdMapping.GetId(lightWidget.Group); + newEntry.LayerId = App.Scene.GetIndexOfCanvas(lightWidget.Canvas); + + newEntry.PunctualLightType = light.type; + newEntry.Intensity = light.intensity; + newEntry.LightColor = light.color; + + if (light.type == LightType.Spot) + { + newEntry.InnerConeAngle = light.innerSpotAngle; + newEntry.OuterConeAngle = light.spotAngle; + } + + if (light.type == LightType.Point || light.type == LightType.Spot) + { + newEntry.Range = light.range; + } + + lightIndex.Add(newEntry); + } + return lightIndex.ToArray(); + } + + public static TiltImages75[] GetTiltImages(GroupIdMapping groupIdMapping) + { + var imports = WidgetManager.m_Instance.ImageWidgets + .Where(w => w.gameObject.activeSelf).ToArray(); + if (imports.Length == 0) + { + return null; + } + + // From the list of image widgets in the sketch, create a map that contains a unique + // entry per image, with associated metadata (transform and pin state) stored as arrays. + Dictionary> imagesByPath = + new Dictionary>(); + Dictionary aspectRatios = new Dictionary(); + foreach (var image in imports) + { + string path = image.RelativePath; + if (image.AspectRatio == null) + { + Debug.LogError("Trying to save partially-initialized image {fileName}"); + } + if (!imagesByPath.ContainsKey(path)) + { + imagesByPath[path] = new List(); + aspectRatios[path] = image.AspectRatio ?? 1; + } + WidgetMetadata newEntry = new WidgetMetadata(); + newEntry.xf = image.SaveTransform; + newEntry.pinned = image.Pinned; + newEntry.tinted = image.UseLegacyTint; + newEntry.groupId = groupIdMapping.GetId(image.Group); + newEntry.layerId = App.Scene.GetIndexOfCanvas(image.Canvas); + newEntry.twoSided = image.TwoSided; + imagesByPath[path].Add(newEntry); + } + + // Build the save metadata from our unique map. + List imageIndex = new List(); + foreach (var elem in imagesByPath) + { + var val = new TiltImages75 + { + FilePath = elem.Key, + FileName = Path.GetFileName(elem.Key), + AspectRatio = aspectRatios[elem.Key] + }; + + // Order and align the metadata. + WidgetMetadata[] ordered = + elem.Value.OrderBy(ByTranslation).ToArray(); + val.PinStates = new bool[ordered.Length]; + val.TintStates = new bool[ordered.Length]; + val.Transforms = new TrTransform[ordered.Length]; + val.GroupIds = new uint[ordered.Length]; + val.LayerIds = new int[ordered.Length]; + val.TwoSidedFlags = new bool[ordered.Length]; + for (int i = 0; i < ordered.Length; ++i) + { + val.PinStates[i] = ordered[i].pinned; + val.TintStates[i] = ordered[i].tinted; + val.Transforms[i] = ordered[i].xf; + val.GroupIds[i] = ordered[i].groupId; + val.LayerIds[i] = ordered[i].layerId; + val.TwoSidedFlags[i] = ordered[i].twoSided; + } + imageIndex.Add(val); + } + return imageIndex.OrderBy(i => i.FileName).ToArray(); + } + + public static void VerifyMetadataVersion(SketchMetadata data) + { + Upgrade_Set_ModelIndexInSet(data); + + if (data.SchemaVersion < 1) + { + UpgradeSchema_0to1(data); + } + if (data.SchemaVersion < 2) + { + UpgradeSchema_1to2(data); + } + } + + // Converts data.Set_deprecated[] to data.ModelIndex[].InSet + static void Upgrade_Set_ModelIndexInSet(SketchMetadata data) + { + if (data == null || data.Set_deprecated == null) + { + return; + } + + TiltModels75[] index = data.ModelIndex; + + string[] set = data.Set_deprecated; + if (index == null) { index = new TiltModels75[] { }; } + List setOnly = new List(set); + foreach (TiltModels75 m in index) + { + if (set.Contains(m.FilePath)) + { + m.InSet_deprecated = true; + setOnly.Remove(m.FilePath); + } + } + index = index.Concat(setOnly.Select(s => new TiltModels75 + { + FilePath = s, + InSet_deprecated = true + })).ToArray(); + + data.ModelIndex = index; + data.Set_deprecated = null; + } + + /// SchemaVersion 1 was released in M15. + /// It adds bool[] TiltModels75.PinStates, bool[] TiltModels75.TintStates, bool Guides.State.Pinned + static void UpgradeSchema_0to1(SketchMetadata data) + { + // Pin flags were not written out previous to v15, so default guides to pinned. + if (data.GuideIndex != null) + { + for (int i = 0; i < data.GuideIndex.Length; ++i) + { + for (int j = 0; j < data.GuideIndex[i].States.Length; ++j) + { + data.GuideIndex[i].States[j].Pinned = true; + } + } + } + + // Default images to pinned, tinted, and not grouped. + if (data.ImageIndex != null) + { + for (int i = 0; i < data.ImageIndex.Length; ++i) + { + int numXfs = data.ImageIndex[i].Transforms.Length; + data.ImageIndex[i].PinStates = Enumerable.Repeat(true, numXfs).ToArray(); + data.ImageIndex[i].TintStates = Enumerable.Repeat(true, numXfs).ToArray(); + data.ImageIndex[i].GroupIds = Enumerable.Repeat(0u, numXfs).ToArray(); + } + } + + // Default models to pinned, if they're local. Poly assets are unpinned. + if (data.ModelIndex != null) + { + for (int i = 0; i < data.ModelIndex.Length; ++i) + { + if (data.ModelIndex[i].PinStates == null) + { + int numXfs = (data.ModelIndex[i].Transforms != null) ? + data.ModelIndex[i].Transforms.Length : + data.ModelIndex[i].RawTransforms.Length; + data.ModelIndex[i].PinStates = new bool[numXfs]; + } + + for (int j = 0; j < data.ModelIndex[i].PinStates.Length; ++j) + { + data.ModelIndex[i].PinStates[j] = (data.ModelIndex[i].Location.GetLocationType() != + Model.Location.Type.IcosaAssetId); + } + } + } + + data.SchemaVersion = 1; + } + + // Append value to array, creating array if necessary + static T[] SafeAppend(T[] array, T value) + { + T[] asArray = { value }; + return (array == null) ? asArray : array.Concat(asArray).ToArray(); + } + + // SchemaVersion 2: M19 + // Converts data.ModelIndex[].InSet to data.ModelIndex[].RawTransforms[] + // This changes the behavior slightly: sets are immovable, but RawTransforms[] can be unpinned. + // This essentially removes the experimental Set feature. + static void UpgradeSchema_1to2(SketchMetadata data) + { + Debug.Assert(data.SchemaVersion == 1); + if (data.ModelIndex == null) { return; } + foreach (TiltModels75 tm75 in data.ModelIndex) + { + if (!tm75.InSet_deprecated) { continue; } + // Only one of Transforms[] or RawTransforms[] can be non-null. + // Therefore, we can only append to RawTransforms[] if Transforms[] is null. + if (tm75.Transforms != null) + { + Debug.LogError("Cannot upgrade InSet if Transforms[] is non-null"); + continue; + } + tm75.InSet_deprecated = false; + tm75.m_rawTransforms = SafeAppend(tm75.m_rawTransforms, TrTransform.identity); + tm75.PinStates = SafeAppend(tm75.PinStates, true); + } + data.SchemaVersion = 2; + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Save/SketchCatalog.cs b/Assets/Scripts/Save/SketchCatalog.cs index e813aeed74..e2146ad33d 100644 --- a/Assets/Scripts/Save/SketchCatalog.cs +++ b/Assets/Scripts/Save/SketchCatalog.cs @@ -1,123 +1,123 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.IO; -using UnityEngine; - -namespace TiltBrush -{ - - public enum SketchSetType - { - User, - Curated, - Liked, - Drive, - } - - // SketchCatalog.Awake must come after App.Awake - public class SketchCatalog : MonoBehaviour - { - static public SketchCatalog m_Instance; - - // This folder contains json files which define where to pull the sketch thumbnail and data - // from Poly. These are used to populate the showcase when we can't query Poly. - // Obviously, if Poly as a database is deleted or moved, accessing these files will fail. - public const string kDefaultShowcaseSketchesFolder = "DefaultShowcaseSketches"; - - private SketchSet[] m_Sets; - - public SketchSet GetSet(SketchSetType eType) - { - return m_Sets[(int)eType]; - } - - void Awake() - { - m_Instance = this; - - if (Application.platform == RuntimePlatform.OSXEditor || - Application.platform == RuntimePlatform.OSXPlayer) - { - // force KEvents implementation of FileSystemWatcher - // source: https://github.com/mono/mono/blob/master/mcs/class/System/System.IO/FileSystemWatcher.cs - // Unity bug: https://fogbugz.unity3d.com/default.asp?778750_fncnl0np45at4mq1 - System.Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "3"); - } - - int maxTriangles = QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles; - - InitFeaturedSketchesPath(); - - m_Sets = new SketchSet[] - { - new FileSketchSet(), - new FileSketchSet(App.FeaturedSketchesPath()), - new PolySketchSet(this, SketchSetType.Liked, maxTriangles, needsLogin: true), - new GoogleDriveSketchSet(), - }; - } - - public static bool InitFeaturedSketchesPath() - { - string featuredPath = App.FeaturedSketchesPath(); - if (!App.InitDirectoryAtPath(featuredPath)) { return false; } - - TextAsset[] textAssets = - Resources.LoadAll(SketchCatalog.kDefaultShowcaseSketchesFolder); - foreach (var asset in textAssets) - { - if (asset.name.EndsWith(".tilt")) - { - string filePath = Path.Combine(App.FeaturedSketchesPath(), asset.name); - if (!File.Exists(filePath)) - { - File.WriteAllBytes(filePath, asset.bytes); - } - } - Resources.UnloadAsset(asset); - } - - return true; - } - - void Start() - { - foreach (SketchSet s in m_Sets) - { - s.Init(); - } - } - - void Update() - { - foreach (SketchSet s in m_Sets) - { - s.Update(); - } - } - - public void NotifyUserFileCreated(string fullpath) - { - m_Sets[(int)SketchSetType.User].NotifySketchCreated(fullpath); - } - - public void NotifyUserFileChanged(string fullpath) - { - m_Sets[(int)SketchSetType.User].NotifySketchChanged(fullpath); - } - } - - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.IO; +using UnityEngine; + +namespace TiltBrush +{ + + public enum SketchSetType + { + User, + Curated, + Liked, + Drive, + } + + // SketchCatalog.Awake must come after App.Awake + public class SketchCatalog : MonoBehaviour + { + static public SketchCatalog m_Instance; + + // This folder contains json files which define where to pull the sketch thumbnail and data + // from Poly. These are used to populate the showcase when we can't query Poly. + // Obviously, if Poly as a database is deleted or moved, accessing these files will fail. + public const string kDefaultShowcaseSketchesFolder = "DefaultShowcaseSketches"; + + private SketchSet[] m_Sets; + + public SketchSet GetSet(SketchSetType eType) + { + return m_Sets[(int)eType]; + } + + void Awake() + { + m_Instance = this; + + if (Application.platform == RuntimePlatform.OSXEditor || + Application.platform == RuntimePlatform.OSXPlayer) + { + // force KEvents implementation of FileSystemWatcher + // source: https://github.com/mono/mono/blob/master/mcs/class/System/System.IO/FileSystemWatcher.cs + // Unity bug: https://fogbugz.unity3d.com/default.asp?778750_fncnl0np45at4mq1 + System.Environment.SetEnvironmentVariable("MONO_MANAGED_WATCHER", "3"); + } + + int maxTriangles = QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles; + + InitFeaturedSketchesPath(); + + m_Sets = new SketchSet[] + { + new FileSketchSet(), + new FileSketchSet(App.FeaturedSketchesPath()), + new IcosaSketchSet(this, SketchSetType.Liked, maxTriangles, needsLogin: true), + new GoogleDriveSketchSet(), + }; + } + + public static bool InitFeaturedSketchesPath() + { + string featuredPath = App.FeaturedSketchesPath(); + if (!App.InitDirectoryAtPath(featuredPath)) { return false; } + + TextAsset[] textAssets = + Resources.LoadAll(SketchCatalog.kDefaultShowcaseSketchesFolder); + foreach (var asset in textAssets) + { + if (asset.name.EndsWith(".tilt")) + { + string filePath = Path.Combine(App.FeaturedSketchesPath(), asset.name); + if (!File.Exists(filePath)) + { + File.WriteAllBytes(filePath, asset.bytes); + } + } + Resources.UnloadAsset(asset); + } + + return true; + } + + void Start() + { + foreach (SketchSet s in m_Sets) + { + s.Init(); + } + } + + void Update() + { + foreach (SketchSet s in m_Sets) + { + s.Update(); + } + } + + public void NotifyUserFileCreated(string fullpath) + { + m_Sets[(int)SketchSetType.User].NotifySketchCreated(fullpath); + } + + public void NotifyUserFileChanged(string fullpath) + { + m_Sets[(int)SketchSetType.User].NotifySketchChanged(fullpath); + } + } + + +} // namespace TiltBrush diff --git a/Assets/Scripts/Save/SketchMetadata.cs b/Assets/Scripts/Save/SketchMetadata.cs index b20cfe80c3..f92d74eda5 100644 --- a/Assets/Scripts/Save/SketchMetadata.cs +++ b/Assets/Scripts/Save/SketchMetadata.cs @@ -1,820 +1,820 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Newtonsoft.Json; -using System; -using System.Linq; -using UnityEngine; - -namespace TiltBrush -{ - /* metadata.json format changes and additions - - - == v7.0: Single models introduced - - New list added called Models - This list could theoretically contain as many models as the user liked, but we cut it back. - In the wild, this list should only ever be of length zero or one, never null. - - "Models": [ - { - "FilePath": "C:\\Users\\Person\\Documents\\Tilt Brush\\Models\\Lili.obj", - "Position": [ -6.38644, 12.7471752, 8.778034 ], - "Rotation": [ 0.018738009, -0.861053467, -0.00643645832, -0.5081283 ], - "Scale": [ 0.649798400, 0.649798400, 0.649798400 ] - } - ] - - - == v7.5: Multiple models and images introduced - - ModelIndex replaces Models - ImageIndex added - These lists contained one entry for every unique object. Each entry in the list contained - a transform for each instance of the object in the sketch. - Both ModelIndex and ImageIndex can be null, and will never be zero length. - - "ImageIndex": [ - { - "FileName": "Blue And teal gradient.png", - "AspectRatio": 1.77777779, - "Transforms": [ [ [ 86.13458, 78.30929, 58.5603828 ], [ 0.0259972736, -0.291850924, -0.00451487023, -0.956099868 ], 198.431992 ] ] - } - ] - - "ModelIndex": [ - { - "FilePath": "Media Library\\Models\\3a. limbs 3\\5.obj", - "Transforms": [ [ [ -3.953864, 3.135213, -9.598504 ], [ 0.06734027, 0.287244231, -0.08482468, -0.9517147 ], 1.37809682 ] - ] - }, - ... - ] - - - == v7.5-experimental: Guides introduced - - Guides are saved as a list of all models, one tranform per instance. - Their shapes are saved as indices since we can guarantee consistency in those. - - "Guides": [ - { - "Type": 0, - "Transform": [ [ -3.60909033, 9.355544, -0.7895982 ], [ 0.161795944, 0.100251839, -0.007717509, 0.9816884 ], 2.0 ] - } - ] - - - == v7.5b: Guides updated. - - List turned into an index. - Type stored as a string. - Size stored as transform.scale + Custom. - - "GuideIndex": [ - { - "Type": "Capsule", - "States": [ - { - "Transform": [ [ 4.216275, 19.4874744, -0.2948996 ], [ 0.101220131, 0.190763831, -0.1001101, 0.971257746 ], 4 ], - "Custom": [ .5, 2, .5 ] - } - ] - } - ] - - - == v8: Guides updated - - Extents stored explicitly. - Transform.scale is always 0. - Backwards-compatibility for 7.5b guides (finally removed in v19) - Type accidentally written out in hashed format :-P - - "GuideIndex": [ - { - "Type": "pmhghhkcdmp", - "States": [ - { - "Transform": [ [ 4.216275, 19.4874744, -0.2948996 ], [ 0.101220131, 0.190763831, -0.1001101, 0.971257746 ], 0.0 ], - "Extents": [ 2.50599027, 9.098843, 2.50599027 ] - } - ] - }, - ] - - == v8.2: Prepare for fixing Guides.Type - - Guides.Type supports human-readable values, although hashed values are still written - - - == v9.0b: Added initial version of Palette - - "Palette": { - "Colors": [ - { - "r": 50, - "g": 50, - "b": 230, - "a": 255 - } - ] - } - - - == v9.0: Removed Palette from save file until 9.1 - - == v9.1: Changed Palette format to use alpha-less and more-compact color values - - "Palette": { "Entries": [ [ 50, 50, 230, 255 ] ] } - - The 9.0b-style "Colors" field is ignored. - The alpha value is preserved when serializing, but will always be 255. - The alpha value is forced to 255 when deserializing. - - - == v10.0: Added CustomLights and CustomEnvironment to save file - - "Environment": { - "GradientColors": [ [ 100, 100, 100, 255 ], [ 60, 64, 90, 255 ] - ], - "GradientSkew": [ 0.0, 0.0, 0.0, 1.0 ], - "FogColor": [ 31, 31, 78, 255 ], - "FogDensity": 0.00141058769, - "ReflectionIntensity": 0.3 - } - - "Lights": { - "Ambient": [ 173, 173, 173, 255 ], - "Shadow": { - "Orientation": [ 0.5, 0.0, 0.0, 0.8660254 ], - "Color": [ 32.0, 31.8180237, 3.81469727E-06, 1.0 ] - }, - "NoShadow": { - "Orientation": [ 0.883022249, -0.321393818, 0.116977736, 0.321393818 ], - "Color": [ 0.03125, 0.000550092547, 0.0, 1.0 ] - } - } - - The "GradientColors" field of Environment is null if the gradient has not been accessed. - Orientation of lights is stored in scene space. - Colors stored as integer RGB are guaranteed to be LDR. Those as floating point RGB may be HDR. - - "SourceId": "abcdefg" - - If this sketch is derived from a Poly asset then SourceId is the id of that original asset. - - - == v12.0: Added Set to save file - - The set of a sketch comprises of props (models) that have been brought in unscaled and have - the root pivot placed at the origin. - File paths are relative to the Models directory. - - "Set": [ - "Andy/Andy.obj", - "Tiltasaurus/Tiltasaurus.obj" - ] - - - == v13.0: Added AssetId to Models - - Models can be loaded via AssetId, a lookup value for assets stored in Poly. - FilePath and AssetId should never both be valid, but AssetId is preferred in that errant case. - - Transforms are maintained for backward compatability for models. They can be read in but are no - longer written out. RawTransforms replaces it, where the new translation represents the pivot of - the models as specified in the original file instead of the center of the mesh bounds, and the - new scale represents the multiplier on the size of the original mesh instead of the normalization. - - M13 was never actually released to the public. - - "ModelIndex": [ - { - "FilePath": null, - "AssetId": "bzolM7RH0n6", - "InSet": false, - "Transforms": null, - "RawTransforms": [ [ [ -3.953864, 3.135213, -9.598504 ], [ 0.06734027, 0.287244231, -0.08482468, -0.9517147 ], 1.37809682 ] - ] - }, - - If a sketch has been uploaded to Poly we store the asset id so that future uploads can update - the existing asset rather than creating a new one. - - Controls for adding a model to a set were hidden behind a flag in M12 and disabled in M14. - - == v14.2: Deprecate Set - - Models that are in the Set of the v12 metadata will be written out in ModelIndex with InSet = true. - - == v15.0: Added Version, PinStates for models, images, and guides, TintStates for images - - SchemaVersion = 1 - - TiltModels75: Added PinStates[] and TintStates[]. Each is a bool[] with the same length as Transforms[]. - Files with SchemaVersion < 1 are upgraded to have PinStates and TintStates (both set to true) - ... - "PinStates": [ - true, - true, - false - ], - "TintStates": [ - true, - true, - false - ], - "Transforms": [ [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], - [ [ 3.466383, 5.71923733, 6.45537853 ], [ -0.0334184356, -0.237726957, 0.0122604668, -0.9706796 ], 0.800769 ], - [ [ 4.00411844, 5.71402025, 6.12121439 ], [ -0.03800745, -0.354895175, 0.00206097914, -0.9341309 ], 0.760676444 ] - ] - ... - - Guide.State: added bool Pinned. - Files with SchemaVersion < 1 are upgraded with Pinned = true. - - == v19.0: Remove Guides.State.Custom, TiltModels75.InSet - - Guides.State.Custom only existed in the 7.5b file format, and was never released to the public. - Should have been removed long ago! - - InSet is converted to a pinned model with identity transform. - - == v22.0: Reference videos added. (TiltVideo[] Videos) - Camera paths added. (CameraPathMetadata[] CameraPaths) - Added GroupIds for TiltModels75, Guides.State, TiltImage75, and TiltVideo. - - "CameraPaths": [ - { - "PathKnots": [ - { - "Xf": [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], - "Speed": 2.81081581 - }, - { - ... - } - ], - "RotationKnots": [ - { - "Xf": [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], - "KnotIndex": 0, - "KnotT": 0.2, - }, - { - ... - } - "SpeedKnots": [ - { - "Xf": [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], - "KnotIndex": 0, - "KnotT": 0.2, - "Speed": 2.81081581 - }, - { - ... - } - ], - }, - { - ... - } - ], - - PathKnots, RotationKnots, and SpeedKnots may all have 0 elements. RotationKnots and SpeedKnots - will not have >0 elements if PathKnots has 0 elements. - */ - - - // From v7.0 - [Serializable] - public class TiltModels70 - { - /// Absolute path to model. Relative paths are not supported. - public string FilePath { get; set; } - public Vector3 Position { get; set; } - public Quaternion Rotation { get; set; } - public Vector3 Scale { get; set; } - - public TiltModels75 Upgrade() - { - string relativePath; - try - { - relativePath = WidgetManager.GetModelSubpath(FilePath); - } - catch (ArgumentException) - { - relativePath = null; - } - // I guess keep it around, so we don't lose information. - if (relativePath == null) - { - relativePath = FilePath; - } - - return new TiltModels75 - { - FilePath = relativePath, - PinStates = new[] { true }, - Transforms = new[] { TrTransform.TRS(Position, Rotation, Scale.x) } - }; - } - } - - // Use for v7.5 and on - // Missing models are normally preserved, with these exceptions: - // - lost if a pre-M13 .tilt is saved in M13+ - // - InSet models are lost if saved, at least through M18 (b/65633544) - [Serializable] - public class TiltModels75 - { - /// Relative path to model from Media Library. - /// e.g. Media Library/Models/subdirectory/model.obj - /// With 14.0 on, this is unused if AssetId is valid. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string FilePath { get; set; } - - /// AssetId for Poly. - /// Added in 14.0, this is preferred over FilePath. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string AssetId { get; set; } - - // True if showing the untransformed, non-interactable model. - // Added in M13 in 97e210f041e20b87c72c87bafb71d7d399d46c13. Never released to public. - // Turned into RawTransforms in M19. - [JsonProperty( - PropertyName = "InSet", // Used to be called InSet - DefaultValueHandling = DefaultValueHandling.Ignore // Don't write "false" values into the .tilt any more - )] - [System.ComponentModel.DefaultValue(false)] - public bool InSet_deprecated { get; set; } - - // True if model should be pinned on load. Added in M15. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public bool[] PinStates { get; set; } - - /// Prior to M13, never null or empty; but an empty array is allowed on read. - /// Post M13, always null. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TrTransform[] Transforms { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string[] Subtrees { get; set; } - - /// Prior to M13, always null. - /// Post M13, never null or empty; but an empty array is allowed on read. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TrTransform[] RawTransforms - { - get { return m_rawTransforms; } - set { m_rawTransforms = MetadataUtils.Sanitize(value); } - } - // Only for use by MetadataUtils.cs - [JsonIgnore] - public TrTransform[] m_rawTransforms; - - /// used to bridge the gap between strict Tilt Brush and not-so-strict json - [JsonIgnore] - public Model.Location Location - { - get - { - if (AssetId != null) - { - return Model.Location.PolyAsset(AssetId, null); - } - else if (FilePath != null) - { - return Model.Location.File(FilePath); - } - else - { - return new Model.Location(); // invalid location - } - } - set - { - if (value.GetLocationType() == Model.Location.Type.LocalFile) - { - FilePath = value.RelativePath; - AssetId = null; - } - else if (value.GetLocationType() == Model.Location.Type.PolyAssetId) - { - FilePath = null; - AssetId = value.AssetId; - } - } - } - - // Group IDs for widgets. 0 for ungrouped items. Added in M22. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public uint[] GroupIds { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int[] LayerIds { get; set; } - } - - [Serializable] - public class Guides - { - // In 8.x we mistakenly wrote out hashed names for these shapes. - const string kHashedCube = "ldeocipaedc"; - const string kHashedSphere = "jminadgooco"; - const string kHashedCapsule = "pmhghhkcdmp"; - - // Custom dimensions for non-uniformly scaled stencils - [Serializable] - public class State - { - public TrTransform Transform { get; set; } - public Vector3 Extents { get; set; } - // True if guide should be pinned on load. Added in M15. - public bool Pinned { get; set; } - // Group ID for widget. 0 for ungrouped items. Added in M22. - public uint GroupId { get; set; } - public int LayerId { get; set; } - } - - // This is the accessor used by Json.NET for reading/writing the "Type" field. - // The getter is used for writing; the setter for reading. - [JsonProperty(PropertyName = "Type")] - private string SerializedType - { - get - { - string ret = Type.ToString(); - if (!Char.IsUpper(ret[0])) - { - // Must be an obfuscated value, or a numeric value (ie, an int that doesn't map - // to a valid enum name), neither of which is expected. Die early rather than - // generate garbage output, so we can catch it in testing. - Debug.LogErrorFormat("Writing bad stencil value {0}", ret); - return "Cube"; - } - return ret; - } - - set - { - try - { - Type = (StencilType)Enum.Parse(typeof(StencilType), value, true); - } - catch (ArgumentException e) - { - // Support the 8.x names for these - switch (value) - { - case kHashedCube: - Type = StencilType.Cube; - break; - case kHashedSphere: - Type = StencilType.Sphere; - break; - case kHashedCapsule: - Type = StencilType.Capsule; - break; - default: - // TODO: log a user visible warning? - Debug.LogException(e); - Type = StencilType.Cube; - break; - } - } - } - } - - [JsonIgnore] - public StencilType Type = StencilType.Cube; - - public State[] States { get; set; } - } - - [Serializable] - public class Palette - { - // Protect Tilt Brush from getting bad alpha values from the user. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - private Color32[] Entries - { - get { return Colors; } - set - { - if (value != null) - { - for (int i = 0; i < value.Length; ++i) - { - value[i].a = 255; - } - } - Colors = value; - } - } - - [JsonIgnore] - public Color32[] Colors { get; set; } - } - - [Serializable] - public class CustomLights - { - [Serializable] - public class DirectionalLight - { - public Quaternion Orientation { get; set; } - public Color Color { get; set; } - } - - public Color32 Ambient { get; set; } - public DirectionalLight Shadow { get; set; } - public DirectionalLight NoShadow { get; set; } - } - - [Serializable] - public class CustomEnvironment - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public Color32[] GradientColors { get; set; } - public Quaternion GradientSkew { get; set; } - public Color32 FogColor { get; set; } - public float FogDensity { get; set; } - public float ReflectionIntensity { get; set; } - - public string Skybox { get; set; } - } - - [Serializable] - public class CameraPathPositionKnotMetadata - { - public TrTransform Xf; - public float TangentMagnitude; - } - - [Serializable] - public class CameraPathRotationKnotMetadata - { - public TrTransform Xf; - public float PathTValue; - } - - [Serializable] - public class CameraPathSpeedKnotMetadata - { - public TrTransform Xf; - public float PathTValue; - public float Speed; - } - - [Serializable] - public class CameraPathFovKnotMetadata - { - public TrTransform Xf; - public float PathTValue; - public float Fov; - } - - [Serializable] - public class CameraPathMetadata - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public CameraPathPositionKnotMetadata[] PathKnots { get; set; } - public CameraPathRotationKnotMetadata[] RotationKnots { get; set; } - public CameraPathSpeedKnotMetadata[] SpeedKnots { get; set; } - public CameraPathFovKnotMetadata[] FovKnots { get; set; } - } - - [Serializable] - public class LayerMetadata - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Name; - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public bool Visible; - } - - // TODO: deprecate (7.5b-only) - // Left just to avoid breaking trusted testers' art - [Serializable] - public class TiltImages75b - { - /// Absolute path to image; any path ending in .png or .jpg will work though. - public string FilePath { get; set; } - public TrTransform Transform { get; set; } - /// width / height - public float AspectRatio { get; set; } - - public TiltImages75 Upgrade() - { - return new TiltImages75 - { - FileName = System.IO.Path.GetFileName(FilePath), - AspectRatio = AspectRatio, - PinStates = new[] { true }, - TintStates = new[] { true }, - Transforms = new[] { Transform }, - GroupIds = new[] { 0u } - }; - } - } - - [Serializable] - public class TiltLights - { - public Color? LightColor; - public float? Intensity; - public LightType PunctualLightType; - public float? Range; - public float? InnerConeAngle; - public float? OuterConeAngle; - - public bool Pinned; - public TrTransform Transform; - // Group ID for widget. 0 for ungrouped items. - public uint GroupId { get; set; } - public int LayerId { get; set; } - } - - [Serializable] - public class TiltImages75 - { - /// *.png or *.jpg, should have no path - public string FileName { get; set; } - /// FileName plus path relative to images directory - public string FilePath { get; set; } - /// width / height - public float AspectRatio { get; set; } - // True if image should be pinned on load. Added in M15. - public bool[] PinStates { get; set; } - // True if image should use legacy tinting. Added in M15. - public bool[] TintStates { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TrTransform[] Transforms { get; set; } - // Group IDs for widgets. 0 for ungrouped items. Added in M22. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public uint[] GroupIds { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int[] LayerIds { get; set; } - public bool[] TwoSidedFlags { get; set; } - } - - [Serializable] - public class Mirror - { - public TrTransform Transform { get; set; } - } - - [Serializable] - public class TiltVideo - { - public string FilePath { get; set; } // relative to Media Library folder - public float AspectRatio { get; set; } - public bool Pinned; - public TrTransform Transform; - public bool Paused { get; set; } - public float Time { get; set; } - public float Volume { get; set; } - // Group ID for widget. 0 for ungrouped items. - public uint GroupId { get; set; } - public int LayerId { get; set; } - public bool TwoSided { get; set; } - } - - [Serializable] - // Serializable protects data members obfuscator, but we need to also protect - // method names like ShouldSerializeXxx(...) that are used by Json.NET - [System.Reflection.Obfuscation(Exclude = true)] - public class SketchMetadata - { - static public int kSchemaVersion = 2; - - // Reference to environment GUID. - public string EnvironmentPreset; - // Reference to sketch audio GUID. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string AudioPreset { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string[] Authors { get; set; } - public Guid[] BrushIndex { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string[] RequiredCapabilities { get; set; } - public TrTransform ThumbnailCameraTransformInRoomSpace = TrTransform.identity; - public TrTransform SceneTransformInRoomSpace = TrTransform.identity; - // Callback for JSON.net (name is magic and special) - public bool ShouldSerializeSceneTransformInRoomSpace() - { - return SceneTransformInRoomSpace != TrTransform.identity; - } - public TrTransform CanvasTransformInSceneSpace = TrTransform.identity; - // Callback for JSON.net (name is magic and special) - public bool ShouldSerializeCanvasTransformInSceneSpace() - { - return CanvasTransformInSceneSpace != TrTransform.identity; - } - - // This was the old name of ThumbnailCameraTransformInRoomSpace. - [Serializable] - public struct UnusedSketchTransform - { - public Vector3 position; - public Quaternion orientation; - } - // This is write-only to keep it from being serialized out - public UnusedSketchTransform ThumbnailCameraTransform - { - set - { - var xf = TrTransform.TR(value.position, value.orientation); - ThumbnailCameraTransformInRoomSpace = xf; - } - } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public int SchemaVersion { get; set; } - - /// Deprecated - /// Only written in 7.0-7.2 - /// Only should ever contains a single model but will create multiples if they are in the list - /// Write-only so it gets serialized in but not serialized out. - /// Models and ModelIndex will never coexist in the same .tilt, so we can upgrade in place. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TiltModels70[] Models - { - set - { - ModelIndex = value.Select(m70 => m70.Upgrade()).ToArray(); - } - } - - /// Added in 7.5 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TiltModels75[] ModelIndex { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TiltLights[] LightIndex { get; set; } - - // Added in 7.5b; never released to public. - // Write-only so it gets serialized in but not serialized out. - // Images and ImageIndex will never coexist in the same .tilt, so we can upgrade in place. - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TiltImages75b[] Images - { - set - { - ImageIndex = value.Select(i75b => i75b.Upgrade()).ToArray(); - } - } - - // Added in 7.5 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TiltImages75[] ImageIndex { get; set; } - - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public Mirror Mirror { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public Guides[] GuideIndex { get; set; } - // Added in 9.1 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public Palette Palette { get; set; } - // Added in 10.0 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public CustomLights Lights { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public CustomEnvironment Environment { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string SourceId { get; set; } - // Added in 12.0, deprecated in 13.0 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "Set")] - public string[] Set_deprecated { get; set; } - // Added in 13.0 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string AssetId { get; set; } - // Added in 22.0 - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TiltVideo[] Videos { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public LayerMetadata[] Layers { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public CameraPathMetadata[] CameraPaths { get; set; } - - // Added for 24.0b Open-source edition - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ApplicationName { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ApplicationVersion { get; set; } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Newtonsoft.Json; +using System; +using System.Linq; +using UnityEngine; + +namespace TiltBrush +{ + /* metadata.json format changes and additions + + + == v7.0: Single models introduced + + New list added called Models + This list could theoretically contain as many models as the user liked, but we cut it back. + In the wild, this list should only ever be of length zero or one, never null. + + "Models": [ + { + "FilePath": "C:\\Users\\Person\\Documents\\Tilt Brush\\Models\\Lili.obj", + "Position": [ -6.38644, 12.7471752, 8.778034 ], + "Rotation": [ 0.018738009, -0.861053467, -0.00643645832, -0.5081283 ], + "Scale": [ 0.649798400, 0.649798400, 0.649798400 ] + } + ] + + + == v7.5: Multiple models and images introduced + + ModelIndex replaces Models + ImageIndex added + These lists contained one entry for every unique object. Each entry in the list contained + a transform for each instance of the object in the sketch. + Both ModelIndex and ImageIndex can be null, and will never be zero length. + + "ImageIndex": [ + { + "FileName": "Blue And teal gradient.png", + "AspectRatio": 1.77777779, + "Transforms": [ [ [ 86.13458, 78.30929, 58.5603828 ], [ 0.0259972736, -0.291850924, -0.00451487023, -0.956099868 ], 198.431992 ] ] + } + ] + + "ModelIndex": [ + { + "FilePath": "Media Library\\Models\\3a. limbs 3\\5.obj", + "Transforms": [ [ [ -3.953864, 3.135213, -9.598504 ], [ 0.06734027, 0.287244231, -0.08482468, -0.9517147 ], 1.37809682 ] + ] + }, + ... + ] + + + == v7.5-experimental: Guides introduced + + Guides are saved as a list of all models, one tranform per instance. + Their shapes are saved as indices since we can guarantee consistency in those. + + "Guides": [ + { + "Type": 0, + "Transform": [ [ -3.60909033, 9.355544, -0.7895982 ], [ 0.161795944, 0.100251839, -0.007717509, 0.9816884 ], 2.0 ] + } + ] + + + == v7.5b: Guides updated. + + List turned into an index. + Type stored as a string. + Size stored as transform.scale + Custom. + + "GuideIndex": [ + { + "Type": "Capsule", + "States": [ + { + "Transform": [ [ 4.216275, 19.4874744, -0.2948996 ], [ 0.101220131, 0.190763831, -0.1001101, 0.971257746 ], 4 ], + "Custom": [ .5, 2, .5 ] + } + ] + } + ] + + + == v8: Guides updated + + Extents stored explicitly. + Transform.scale is always 0. + Backwards-compatibility for 7.5b guides (finally removed in v19) + Type accidentally written out in hashed format :-P + + "GuideIndex": [ + { + "Type": "pmhghhkcdmp", + "States": [ + { + "Transform": [ [ 4.216275, 19.4874744, -0.2948996 ], [ 0.101220131, 0.190763831, -0.1001101, 0.971257746 ], 0.0 ], + "Extents": [ 2.50599027, 9.098843, 2.50599027 ] + } + ] + }, + ] + + == v8.2: Prepare for fixing Guides.Type + + Guides.Type supports human-readable values, although hashed values are still written + + + == v9.0b: Added initial version of Palette + + "Palette": { + "Colors": [ + { + "r": 50, + "g": 50, + "b": 230, + "a": 255 + } + ] + } + + + == v9.0: Removed Palette from save file until 9.1 + + == v9.1: Changed Palette format to use alpha-less and more-compact color values + + "Palette": { "Entries": [ [ 50, 50, 230, 255 ] ] } + + The 9.0b-style "Colors" field is ignored. + The alpha value is preserved when serializing, but will always be 255. + The alpha value is forced to 255 when deserializing. + + + == v10.0: Added CustomLights and CustomEnvironment to save file + + "Environment": { + "GradientColors": [ [ 100, 100, 100, 255 ], [ 60, 64, 90, 255 ] + ], + "GradientSkew": [ 0.0, 0.0, 0.0, 1.0 ], + "FogColor": [ 31, 31, 78, 255 ], + "FogDensity": 0.00141058769, + "ReflectionIntensity": 0.3 + } + + "Lights": { + "Ambient": [ 173, 173, 173, 255 ], + "Shadow": { + "Orientation": [ 0.5, 0.0, 0.0, 0.8660254 ], + "Color": [ 32.0, 31.8180237, 3.81469727E-06, 1.0 ] + }, + "NoShadow": { + "Orientation": [ 0.883022249, -0.321393818, 0.116977736, 0.321393818 ], + "Color": [ 0.03125, 0.000550092547, 0.0, 1.0 ] + } + } + + The "GradientColors" field of Environment is null if the gradient has not been accessed. + Orientation of lights is stored in scene space. + Colors stored as integer RGB are guaranteed to be LDR. Those as floating point RGB may be HDR. + + "SourceId": "abcdefg" + + If this sketch is derived from a Poly asset then SourceId is the id of that original asset. + + + == v12.0: Added Set to save file + + The set of a sketch comprises of props (models) that have been brought in unscaled and have + the root pivot placed at the origin. + File paths are relative to the Models directory. + + "Set": [ + "Andy/Andy.obj", + "Tiltasaurus/Tiltasaurus.obj" + ] + + + == v13.0: Added AssetId to Models + + Models can be loaded via AssetId, a lookup value for assets stored in Poly. + FilePath and AssetId should never both be valid, but AssetId is preferred in that errant case. + + Transforms are maintained for backward compatability for models. They can be read in but are no + longer written out. RawTransforms replaces it, where the new translation represents the pivot of + the models as specified in the original file instead of the center of the mesh bounds, and the + new scale represents the multiplier on the size of the original mesh instead of the normalization. + + M13 was never actually released to the public. + + "ModelIndex": [ + { + "FilePath": null, + "AssetId": "bzolM7RH0n6", + "InSet": false, + "Transforms": null, + "RawTransforms": [ [ [ -3.953864, 3.135213, -9.598504 ], [ 0.06734027, 0.287244231, -0.08482468, -0.9517147 ], 1.37809682 ] + ] + }, + + If a sketch has been uploaded to Poly we store the asset id so that future uploads can update + the existing asset rather than creating a new one. + + Controls for adding a model to a set were hidden behind a flag in M12 and disabled in M14. + + == v14.2: Deprecate Set + + Models that are in the Set of the v12 metadata will be written out in ModelIndex with InSet = true. + + == v15.0: Added Version, PinStates for models, images, and guides, TintStates for images + + SchemaVersion = 1 + + TiltModels75: Added PinStates[] and TintStates[]. Each is a bool[] with the same length as Transforms[]. + Files with SchemaVersion < 1 are upgraded to have PinStates and TintStates (both set to true) + ... + "PinStates": [ + true, + true, + false + ], + "TintStates": [ + true, + true, + false + ], + "Transforms": [ [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], + [ [ 3.466383, 5.71923733, 6.45537853 ], [ -0.0334184356, -0.237726957, 0.0122604668, -0.9706796 ], 0.800769 ], + [ [ 4.00411844, 5.71402025, 6.12121439 ], [ -0.03800745, -0.354895175, 0.00206097914, -0.9341309 ], 0.760676444 ] + ] + ... + + Guide.State: added bool Pinned. + Files with SchemaVersion < 1 are upgraded with Pinned = true. + + == v19.0: Remove Guides.State.Custom, TiltModels75.InSet + + Guides.State.Custom only existed in the 7.5b file format, and was never released to the public. + Should have been removed long ago! + + InSet is converted to a pinned model with identity transform. + + == v22.0: Reference videos added. (TiltVideo[] Videos) + Camera paths added. (CameraPathMetadata[] CameraPaths) + Added GroupIds for TiltModels75, Guides.State, TiltImage75, and TiltVideo. + + "CameraPaths": [ + { + "PathKnots": [ + { + "Xf": [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], + "Speed": 2.81081581 + }, + { + ... + } + ], + "RotationKnots": [ + { + "Xf": [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], + "KnotIndex": 0, + "KnotT": 0.2, + }, + { + ... + } + "SpeedKnots": [ + { + "Xf": [ [ 2.81081581, 5.47280025, 6.80550051 ], [ -0.0380054265, -0.219284073, 0.00313296262, -0.9749155 ], 2.41691542 ], + "KnotIndex": 0, + "KnotT": 0.2, + "Speed": 2.81081581 + }, + { + ... + } + ], + }, + { + ... + } + ], + + PathKnots, RotationKnots, and SpeedKnots may all have 0 elements. RotationKnots and SpeedKnots + will not have >0 elements if PathKnots has 0 elements. + */ + + + // From v7.0 + [Serializable] + public class TiltModels70 + { + /// Absolute path to model. Relative paths are not supported. + public string FilePath { get; set; } + public Vector3 Position { get; set; } + public Quaternion Rotation { get; set; } + public Vector3 Scale { get; set; } + + public TiltModels75 Upgrade() + { + string relativePath; + try + { + relativePath = WidgetManager.GetModelSubpath(FilePath); + } + catch (ArgumentException) + { + relativePath = null; + } + // I guess keep it around, so we don't lose information. + if (relativePath == null) + { + relativePath = FilePath; + } + + return new TiltModels75 + { + FilePath = relativePath, + PinStates = new[] { true }, + Transforms = new[] { TrTransform.TRS(Position, Rotation, Scale.x) } + }; + } + } + + // Use for v7.5 and on + // Missing models are normally preserved, with these exceptions: + // - lost if a pre-M13 .tilt is saved in M13+ + // - InSet models are lost if saved, at least through M18 (b/65633544) + [Serializable] + public class TiltModels75 + { + /// Relative path to model from Media Library. + /// e.g. Media Library/Models/subdirectory/model.obj + /// With 14.0 on, this is unused if AssetId is valid. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string FilePath { get; set; } + + /// AssetId for Poly. + /// Added in 14.0, this is preferred over FilePath. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string AssetId { get; set; } + + // True if showing the untransformed, non-interactable model. + // Added in M13 in 97e210f041e20b87c72c87bafb71d7d399d46c13. Never released to public. + // Turned into RawTransforms in M19. + [JsonProperty( + PropertyName = "InSet", // Used to be called InSet + DefaultValueHandling = DefaultValueHandling.Ignore // Don't write "false" values into the .tilt any more + )] + [System.ComponentModel.DefaultValue(false)] + public bool InSet_deprecated { get; set; } + + // True if model should be pinned on load. Added in M15. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public bool[] PinStates { get; set; } + + /// Prior to M13, never null or empty; but an empty array is allowed on read. + /// Post M13, always null. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TrTransform[] Transforms { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string[] Subtrees { get; set; } + + /// Prior to M13, always null. + /// Post M13, never null or empty; but an empty array is allowed on read. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TrTransform[] RawTransforms + { + get { return m_rawTransforms; } + set { m_rawTransforms = MetadataUtils.Sanitize(value); } + } + // Only for use by MetadataUtils.cs + [JsonIgnore] + public TrTransform[] m_rawTransforms; + + /// used to bridge the gap between strict Tilt Brush and not-so-strict json + [JsonIgnore] + public Model.Location Location + { + get + { + if (AssetId != null) + { + return Model.Location.IcosaAsset(AssetId, null); + } + else if (FilePath != null) + { + return Model.Location.File(FilePath); + } + else + { + return new Model.Location(); // invalid location + } + } + set + { + if (value.GetLocationType() == Model.Location.Type.LocalFile) + { + FilePath = value.RelativePath; + AssetId = null; + } + else if (value.GetLocationType() == Model.Location.Type.IcosaAssetId) + { + FilePath = null; + AssetId = value.AssetId; + } + } + } + + // Group IDs for widgets. 0 for ungrouped items. Added in M22. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public uint[] GroupIds { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int[] LayerIds { get; set; } + } + + [Serializable] + public class Guides + { + // In 8.x we mistakenly wrote out hashed names for these shapes. + const string kHashedCube = "ldeocipaedc"; + const string kHashedSphere = "jminadgooco"; + const string kHashedCapsule = "pmhghhkcdmp"; + + // Custom dimensions for non-uniformly scaled stencils + [Serializable] + public class State + { + public TrTransform Transform { get; set; } + public Vector3 Extents { get; set; } + // True if guide should be pinned on load. Added in M15. + public bool Pinned { get; set; } + // Group ID for widget. 0 for ungrouped items. Added in M22. + public uint GroupId { get; set; } + public int LayerId { get; set; } + } + + // This is the accessor used by Json.NET for reading/writing the "Type" field. + // The getter is used for writing; the setter for reading. + [JsonProperty(PropertyName = "Type")] + private string SerializedType + { + get + { + string ret = Type.ToString(); + if (!Char.IsUpper(ret[0])) + { + // Must be an obfuscated value, or a numeric value (ie, an int that doesn't map + // to a valid enum name), neither of which is expected. Die early rather than + // generate garbage output, so we can catch it in testing. + Debug.LogErrorFormat("Writing bad stencil value {0}", ret); + return "Cube"; + } + return ret; + } + + set + { + try + { + Type = (StencilType)Enum.Parse(typeof(StencilType), value, true); + } + catch (ArgumentException e) + { + // Support the 8.x names for these + switch (value) + { + case kHashedCube: + Type = StencilType.Cube; + break; + case kHashedSphere: + Type = StencilType.Sphere; + break; + case kHashedCapsule: + Type = StencilType.Capsule; + break; + default: + // TODO: log a user visible warning? + Debug.LogException(e); + Type = StencilType.Cube; + break; + } + } + } + } + + [JsonIgnore] + public StencilType Type = StencilType.Cube; + + public State[] States { get; set; } + } + + [Serializable] + public class Palette + { + // Protect Tilt Brush from getting bad alpha values from the user. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + private Color32[] Entries + { + get { return Colors; } + set + { + if (value != null) + { + for (int i = 0; i < value.Length; ++i) + { + value[i].a = 255; + } + } + Colors = value; + } + } + + [JsonIgnore] + public Color32[] Colors { get; set; } + } + + [Serializable] + public class CustomLights + { + [Serializable] + public class DirectionalLight + { + public Quaternion Orientation { get; set; } + public Color Color { get; set; } + } + + public Color32 Ambient { get; set; } + public DirectionalLight Shadow { get; set; } + public DirectionalLight NoShadow { get; set; } + } + + [Serializable] + public class CustomEnvironment + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public Color32[] GradientColors { get; set; } + public Quaternion GradientSkew { get; set; } + public Color32 FogColor { get; set; } + public float FogDensity { get; set; } + public float ReflectionIntensity { get; set; } + + public string Skybox { get; set; } + } + + [Serializable] + public class CameraPathPositionKnotMetadata + { + public TrTransform Xf; + public float TangentMagnitude; + } + + [Serializable] + public class CameraPathRotationKnotMetadata + { + public TrTransform Xf; + public float PathTValue; + } + + [Serializable] + public class CameraPathSpeedKnotMetadata + { + public TrTransform Xf; + public float PathTValue; + public float Speed; + } + + [Serializable] + public class CameraPathFovKnotMetadata + { + public TrTransform Xf; + public float PathTValue; + public float Fov; + } + + [Serializable] + public class CameraPathMetadata + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public CameraPathPositionKnotMetadata[] PathKnots { get; set; } + public CameraPathRotationKnotMetadata[] RotationKnots { get; set; } + public CameraPathSpeedKnotMetadata[] SpeedKnots { get; set; } + public CameraPathFovKnotMetadata[] FovKnots { get; set; } + } + + [Serializable] + public class LayerMetadata + { + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Name; + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public bool Visible; + } + + // TODO: deprecate (7.5b-only) + // Left just to avoid breaking trusted testers' art + [Serializable] + public class TiltImages75b + { + /// Absolute path to image; any path ending in .png or .jpg will work though. + public string FilePath { get; set; } + public TrTransform Transform { get; set; } + /// width / height + public float AspectRatio { get; set; } + + public TiltImages75 Upgrade() + { + return new TiltImages75 + { + FileName = System.IO.Path.GetFileName(FilePath), + AspectRatio = AspectRatio, + PinStates = new[] { true }, + TintStates = new[] { true }, + Transforms = new[] { Transform }, + GroupIds = new[] { 0u } + }; + } + } + + [Serializable] + public class TiltLights + { + public Color? LightColor; + public float? Intensity; + public LightType PunctualLightType; + public float? Range; + public float? InnerConeAngle; + public float? OuterConeAngle; + + public bool Pinned; + public TrTransform Transform; + // Group ID for widget. 0 for ungrouped items. + public uint GroupId { get; set; } + public int LayerId { get; set; } + } + + [Serializable] + public class TiltImages75 + { + /// *.png or *.jpg, should have no path + public string FileName { get; set; } + /// FileName plus path relative to images directory + public string FilePath { get; set; } + /// width / height + public float AspectRatio { get; set; } + // True if image should be pinned on load. Added in M15. + public bool[] PinStates { get; set; } + // True if image should use legacy tinting. Added in M15. + public bool[] TintStates { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TrTransform[] Transforms { get; set; } + // Group IDs for widgets. 0 for ungrouped items. Added in M22. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public uint[] GroupIds { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int[] LayerIds { get; set; } + public bool[] TwoSidedFlags { get; set; } + } + + [Serializable] + public class Mirror + { + public TrTransform Transform { get; set; } + } + + [Serializable] + public class TiltVideo + { + public string FilePath { get; set; } // relative to Media Library folder + public float AspectRatio { get; set; } + public bool Pinned; + public TrTransform Transform; + public bool Paused { get; set; } + public float Time { get; set; } + public float Volume { get; set; } + // Group ID for widget. 0 for ungrouped items. + public uint GroupId { get; set; } + public int LayerId { get; set; } + public bool TwoSided { get; set; } + } + + [Serializable] + // Serializable protects data members obfuscator, but we need to also protect + // method names like ShouldSerializeXxx(...) that are used by Json.NET + [System.Reflection.Obfuscation(Exclude = true)] + public class SketchMetadata + { + static public int kSchemaVersion = 2; + + // Reference to environment GUID. + public string EnvironmentPreset; + // Reference to sketch audio GUID. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string AudioPreset { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string[] Authors { get; set; } + public Guid[] BrushIndex { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string[] RequiredCapabilities { get; set; } + public TrTransform ThumbnailCameraTransformInRoomSpace = TrTransform.identity; + public TrTransform SceneTransformInRoomSpace = TrTransform.identity; + // Callback for JSON.net (name is magic and special) + public bool ShouldSerializeSceneTransformInRoomSpace() + { + return SceneTransformInRoomSpace != TrTransform.identity; + } + public TrTransform CanvasTransformInSceneSpace = TrTransform.identity; + // Callback for JSON.net (name is magic and special) + public bool ShouldSerializeCanvasTransformInSceneSpace() + { + return CanvasTransformInSceneSpace != TrTransform.identity; + } + + // This was the old name of ThumbnailCameraTransformInRoomSpace. + [Serializable] + public struct UnusedSketchTransform + { + public Vector3 position; + public Quaternion orientation; + } + // This is write-only to keep it from being serialized out + public UnusedSketchTransform ThumbnailCameraTransform + { + set + { + var xf = TrTransform.TR(value.position, value.orientation); + ThumbnailCameraTransformInRoomSpace = xf; + } + } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public int SchemaVersion { get; set; } + + /// Deprecated + /// Only written in 7.0-7.2 + /// Only should ever contains a single model but will create multiples if they are in the list + /// Write-only so it gets serialized in but not serialized out. + /// Models and ModelIndex will never coexist in the same .tilt, so we can upgrade in place. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TiltModels70[] Models + { + set + { + ModelIndex = value.Select(m70 => m70.Upgrade()).ToArray(); + } + } + + /// Added in 7.5 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TiltModels75[] ModelIndex { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TiltLights[] LightIndex { get; set; } + + // Added in 7.5b; never released to public. + // Write-only so it gets serialized in but not serialized out. + // Images and ImageIndex will never coexist in the same .tilt, so we can upgrade in place. + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TiltImages75b[] Images + { + set + { + ImageIndex = value.Select(i75b => i75b.Upgrade()).ToArray(); + } + } + + // Added in 7.5 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TiltImages75[] ImageIndex { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public Mirror Mirror { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public Guides[] GuideIndex { get; set; } + // Added in 9.1 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public Palette Palette { get; set; } + // Added in 10.0 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public CustomLights Lights { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public CustomEnvironment Environment { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string SourceId { get; set; } + // Added in 12.0, deprecated in 13.0 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "Set")] + public string[] Set_deprecated { get; set; } + // Added in 13.0 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string AssetId { get; set; } + // Added in 22.0 + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TiltVideo[] Videos { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public LayerMetadata[] Layers { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public CameraPathMetadata[] CameraPaths { get; set; } + + // Added for 24.0b Open-source edition + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ApplicationName { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ApplicationVersion { get; set; } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/AssetGetter.cs b/Assets/Scripts/Sharing/AssetGetter.cs index 2ae2d31284..4cc6fbd4ee 100644 --- a/Assets/Scripts/Sharing/AssetGetter.cs +++ b/Assets/Scripts/Sharing/AssetGetter.cs @@ -1,251 +1,250 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using Newtonsoft.Json.Linq; -using UnityEngine; -using UnityEngine.Networking; - -namespace TiltBrush -{ - - /// Class that assists in getting a PolyRawAsset from Poly. - public class AssetGetter - { - private bool m_Ready; - private string m_URI; - private PolyRawAsset m_Asset; - private JsonSerializer m_JsonSerializer; - - /// Converts a property from snake case to camel case. - public class SnakeToCamelPropertyNameContractResolver : DefaultContractResolver - { - - protected override JsonProperty CreateProperty(MemberInfo member, - MemberSerialization memberSerialization) - { - JsonProperty jsonProperty = base.CreateProperty(member, memberSerialization); - jsonProperty.PropertyName = SnakeToCamelCase(jsonProperty.PropertyName); - return jsonProperty; - } - - string SnakeToCamelCase(string s) - { - int index = s.IndexOf("_"); - while (index != -1) - { - string toHere = s.Substring(0, index); - string upper = s.Substring(index + 1, 1); - string theRest = s.Substring(index + 2); - - s = toHere + upper.ToUpper() + theRest; - index = s.IndexOf("_"); - } - return s; - } - } - - public bool IsCanceled - { - get; - set; - } - - public bool IsReady - { - get { return m_Ready; } - } - - public PolyRawAsset Asset - { - get { return m_Asset; } - } - - public string Reason { get; } - - public AssetGetter(string uri, string assetId, VrAssetFormat assetType, - string reason) - { - m_URI = uri; - m_Asset = new PolyRawAsset(assetId, assetType); - m_JsonSerializer = new JsonSerializer(); - m_JsonSerializer.ContractResolver = new SnakeToCamelPropertyNameContractResolver(); - Reason = reason; - } - - // Initiates the contact with Poly. - public IEnumerator GetAssetCoroutine() - { - - OAuth2Identity identity = null; - - if (!m_URI.StartsWith(VrAssetService.m_Instance.ApiHost)) - { - m_Asset.SetRootElement(UnityWebRequest.EscapeURL(m_URI), m_URI); - } - else - { - - identity = App.GoogleIdentity; - - if (string.IsNullOrEmpty(App.Config.GoogleSecrets?.ApiKey)) - { - IsCanceled = true; - yield break; - } - m_Ready = false; - - WebRequest initialRequest = new WebRequest(m_URI, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); - using (var cr = initialRequest.SendAsync().AsIeNull()) - { - while (!initialRequest.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException e) - { - Debug.LogException(e); - IsCanceled = true; - yield break; - } - yield return cr.Current; - } - } - - // Deserialize request string in to an Asset class. - MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(initialRequest.Result)); - using (var jsonReader = new JsonTextReader(new StreamReader(memStream))) - { - Future f = new Future(() => JObject.Parse(initialRequest.Result)); - JObject json; - while (!f.TryGetResult(out json)) { yield return null; } - - if (json.Count == 0) - { - Debug.LogErrorFormat("Failed to deserialize response for {0}", m_URI); - yield break; - } - - // Find the asset by looking through the format list for the specified type. - VrAssetFormat requestedType = m_Asset.DesiredType; - - while (true) - { - var format = json["formats"]?.FirstOrDefault(x => - x["formatType"]?.ToString() == requestedType.ToString()); - if (format != null) - { - string internalRootFilePath = format["root"]?["relativePath"].ToString(); - // If we successfully get a gltf2 format file, internally change the extension to - // "gltf2" so that the cache knows that it is a gltf2 file. - if (requestedType == VrAssetFormat.GLTF2) - { - internalRootFilePath = Path.ChangeExtension(internalRootFilePath, "gltf2"); - } - - // Get root element info. - m_Asset.SetRootElement( - internalRootFilePath, - format["root"]?["url"].ToString()); - - // Get all resource infos. There may be zero. - foreach (var r in format["resources"]) - { - string path = r["relativePath"].ToString(); - m_Asset.AddResourceElement(path, r["url"].ToString()); - - // The root element should be the only gltf file. - Debug.Assert(!path.EndsWith(".gltf") && !path.EndsWith(".gltf2"), - string.Format("Found extra gltf resource: {0}", path)); - } - break; - } - else - { - // We asked for an asset in a format that it doesn't have. - // In some cases, we should look for a different format as backup. - if (requestedType == VrAssetFormat.GLTF2) - { - requestedType = VrAssetFormat.GLTF; - } - else - { - // In other cases, we should fail and get out. - Debug.LogErrorFormat("Can't download {0} in {1} format.", m_Asset.Id, m_Asset.DesiredType); - yield break; - } - } - } - } - } - - // Download root asset. - var request = new WebRequest(m_Asset.RootDataURL, identity); - using (var cr = request.SendAsync().AsIeNull()) - { - while (!request.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException e) - { - Debug.LogErrorFormat("Error downloading {0} at {1}\n{2}", - m_Asset.Id, m_Asset.RootDataURL, e); - yield break; - } - yield return cr.Current; - } - } - m_Asset.CopyBytesToRootElement(request.ResultBytes); - - // Download all resource assets. - foreach (var e in m_Asset.ResourceElements) - { - request = new WebRequest(e.dataURL, App.GoogleIdentity); - using (var cr = request.SendAsync().AsIeNull()) - { - while (!request.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException ex) - { - Debug.LogErrorFormat("Error downloading {0} at {1}\n{2}", - m_Asset.Id, m_Asset.RootDataURL, ex); - e.assetBytes = null; - yield break; - } - yield return cr.Current; - } - } - e.assetBytes = request.ResultBytes; - } - - m_Ready = true; - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json.Linq; +using UnityEngine; +using UnityEngine.Networking; + +namespace TiltBrush +{ + + /// Class that assists in getting a PolyRawAsset from Poly. + public class AssetGetter + { + private bool m_Ready; + private string m_URI; + private IcosaRawAsset m_Asset; + private JsonSerializer m_JsonSerializer; + + /// Converts a property from snake case to camel case. + public class SnakeToCamelPropertyNameContractResolver : DefaultContractResolver + { + + protected override JsonProperty CreateProperty(MemberInfo member, + MemberSerialization memberSerialization) + { + JsonProperty jsonProperty = base.CreateProperty(member, memberSerialization); + jsonProperty.PropertyName = SnakeToCamelCase(jsonProperty.PropertyName); + return jsonProperty; + } + + string SnakeToCamelCase(string s) + { + int index = s.IndexOf("_"); + while (index != -1) + { + string toHere = s.Substring(0, index); + string upper = s.Substring(index + 1, 1); + string theRest = s.Substring(index + 2); + + s = toHere + upper.ToUpper() + theRest; + index = s.IndexOf("_"); + } + return s; + } + } + + public bool IsCanceled + { + get; + set; + } + + public bool IsReady + { + get { return m_Ready; } + } + + public IcosaRawAsset Asset + { + get { return m_Asset; } + } + + public string Reason { get; } + + public AssetGetter(string uri, string assetId, VrAssetFormat assetType, + string reason) + { + m_URI = uri; + m_Asset = new IcosaRawAsset(assetId, assetType); + m_JsonSerializer = new JsonSerializer(); + m_JsonSerializer.ContractResolver = new SnakeToCamelPropertyNameContractResolver(); + Reason = reason; + } + + // Initiates the contact with Poly. + public IEnumerator GetAssetCoroutine() + { + + OAuth2Identity identity = null; + + if (!m_URI.StartsWith(VrAssetService.m_Instance.ApiHost)) + { + m_Asset.SetRootElement(UnityWebRequest.EscapeURL(m_URI), m_URI); + } + else + { + m_Ready = false; + + WebRequest initialRequest = new WebRequest(m_URI, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + using (var cr = initialRequest.SendAsync().AsIeNull()) + { + while (!initialRequest.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException e) + { + Debug.LogException(e); + IsCanceled = true; + yield break; + } + yield return cr.Current; + } + } + + // Deserialize request string in to an Asset class. + MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(initialRequest.Result)); + using (var jsonReader = new JsonTextReader(new StreamReader(memStream))) + { + Future f = new Future(() => JObject.Parse(initialRequest.Result)); + JObject json; + while (!f.TryGetResult(out json)) { yield return null; } + + var polyPizzaUrl = json["Download"]; + if (polyPizzaUrl == null) + { + Debug.LogErrorFormat("Failed to find download url for {0}", m_URI); + yield break; + } + + if (json.Count == 0) + { + Debug.LogErrorFormat("Failed to deserialize response for {0}", m_URI); + yield break; + } + + // Find the asset by looking through the format list for the specified type. + VrAssetFormat requestedType = m_Asset.DesiredType; + + while (true) + { + var format = json["formats"]?.FirstOrDefault(x => + x["formatType"]?.ToString() == requestedType.ToString()); + if (format != null) + { + string internalRootFilePath = format["root"]?["relativePath"].ToString(); + // If we successfully get a gltf2 format file, internally change the extension to + // "gltf2" so that the cache knows that it is a gltf2 file. + if (requestedType == VrAssetFormat.GLTF2) + { + internalRootFilePath = Path.ChangeExtension(internalRootFilePath, "gltf2"); + } + + // Get root element info. + m_Asset.SetRootElement( + internalRootFilePath, + format["root"]?["url"].ToString()); + + // Get all resource infos. There may be zero. + foreach (var r in format["resources"]) + { + string path = r["relativePath"].ToString(); + m_Asset.AddResourceElement(path, r["url"].ToString()); + + // The root element should be the only gltf file. + Debug.Assert(!path.EndsWith(".gltf") && !path.EndsWith(".gltf2"), + string.Format("Found extra gltf resource: {0}", path)); + } + break; + } + else + { + // We asked for an asset in a format that it doesn't have. + // In some cases, we should look for a different format as backup. + if (requestedType == VrAssetFormat.GLTF2) + { + requestedType = VrAssetFormat.GLTF; + } + else + { + // In other cases, we should fail and get out. + Debug.LogErrorFormat("Can't download {0} in {1} format.", m_Asset.Id, m_Asset.DesiredType); + yield break; + } + } + } + } + } + + // Download root asset. + var request = new WebRequest(m_Asset.RootDataURL, identity); + using (var cr = request.SendAsync().AsIeNull()) + { + while (!request.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException e) + { + Debug.LogErrorFormat("Error downloading {0} at {1}\n{2}", + m_Asset.Id, m_Asset.RootDataURL, e); + yield break; + } + yield return cr.Current; + } + } + m_Asset.CopyBytesToRootElement(request.ResultBytes); + + // Download all resource assets. + foreach (var e in m_Asset.ResourceElements) + { + request = new WebRequest(e.dataURL, App.Instance.IcosaToken); + using (var cr = request.SendAsync().AsIeNull()) + { + while (!request.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException ex) + { + Debug.LogErrorFormat("Error downloading {0} at {1}\n{2}", + m_Asset.Id, m_Asset.RootDataURL, ex); + e.assetBytes = null; + yield break; + } + yield return cr.Current; + } + } + e.assetBytes = request.ResultBytes; + } + + m_Ready = true; + } + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index 7e3e48f7c7..a891a1b79f 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -1,159 +1,161 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json.Linq; -using UnityEngine.Networking; - -namespace TiltBrush -{ - - /// Class that holds page state while calling Poly ListAssets. - public class AssetLister - { - private string m_Uri; - private string m_ErrorMessage; - private string m_PageToken; - - public bool HasMore { get { return m_PageToken != null; } } - - public AssetLister(string uri, string errorMessage) - { - m_Uri = uri; - m_ErrorMessage = errorMessage; - } - - public IEnumerator NextPage(List files) - { - string uri = m_PageToken == null ? m_Uri - : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); - - WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); - using (var cr = request.SendAsync().AsIeNull()) - { - while (!request.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException e) - { - e.UserFriendly = m_ErrorMessage; - throw; - } - yield return cr.Current; - } - } - - Future f = new Future(() => JObject.Parse(request.Result)); - JObject json; - while (!f.TryGetResult(out json)) { yield return null; } - - var assets = json["assets"]; - if (assets != null) - { - foreach (var asset in assets) - { - var info = new IcosaSceneFileInfo(asset); - info.Author = asset["displayName"].ToString(); - ; - files.Add(info); - } - } - JToken jPageToken = json["nextPageToken"]; - m_PageToken = jPageToken != null ? jPageToken.ToString() : null; - } - - public IEnumerator NextPage(List files, - string thumbnailSuffix) - { - string uri = m_PageToken == null ? m_Uri - : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); - - WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); - using (var cr = request.SendAsync().AsIeNull()) - { - while (!request.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException e) - { - e.UserFriendly = m_ErrorMessage; - throw; - } - yield return cr.Current; - } - } - Future f = new Future(() => JObject.Parse(request.Result)); - JObject json; - while (!f.TryGetResult(out json)) { yield return null; } - - if (json.Count == 0) { yield break; } - - JToken lastAsset = null; - var assets = json["assets"] ?? json["userAssets"]; - foreach (JToken possibleAsset in assets) - { - try - { - // User assets are nested in an 'asset' node. - JToken asset = possibleAsset["asset"] ?? possibleAsset; - if (asset["visibility"].ToString() == "PRIVATE") - { - continue; - } - - // We now don't filter the liked Poly objects, but we don't want to return liked Tilt Brush - // sketches so in this section we filter out anything with a Tilt file in it. - // Also, although currently all Poly objects have a GLTF representation we should probably - // not rely on that continuing, so we discard anything that doesn't have a GLTF (1) - // representation. We look for PGLTF and GLTF as for a lot of objects Poly is returning - // PGLTF without GLTF. - bool skipObject = false; - foreach (var format in asset["formats"]) - { - var formatType = format["formatType"].ToString(); - if (formatType == "TILT") - { - skipObject = true; - break; - } - } - if (skipObject) - { - continue; - } - lastAsset = asset; - string accountName = asset["authorName"]?.ToString() ?? "Unknown"; - files.Add(new PolyAssetCatalog.AssetDetails(asset, accountName, thumbnailSuffix)); - } - catch (NullReferenceException) - { - UnityEngine.Debug.LogErrorFormat("Failed to load asset: {0}", - lastAsset == null ? "NULL" : lastAsset.ToString()); - } - yield return null; - } - - JToken jPageToken = json["nextPageToken"]; - m_PageToken = jPageToken != null ? jPageToken.ToString() : null; - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Linq; +using UnityEngine.Networking; + +namespace TiltBrush +{ + + /// Class that holds page state while calling Poly ListAssets. + public class AssetLister + { + private string m_Uri; + private string m_ErrorMessage; + private int m_PageIndex = -1; + + public bool HasMore { get { return m_PageIndex != -1; } } + + public AssetLister(string uri, string errorMessage) + { + m_Uri = uri; + m_ErrorMessage = errorMessage; + m_PageIndex = 0; + } + + public IEnumerator NextPage(List files) + { + if (m_PageIndex == -1) { yield break; } + m_PageIndex++; + string uri = m_PageIndex == 0 ? m_Uri + : String.Format("{0}&page={1}", m_Uri, m_PageIndex); + + WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + using (var cr = request.SendAsync().AsIeNull()) + { + while (!request.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException e) + { + e.UserFriendly = m_ErrorMessage; + throw; + } + yield return cr.Current; + } + } + + Future f = new Future(() => JArray.Parse(request.Result)); + JArray assets; + while (!f.TryGetResult(out assets)) { yield return null; } + + if (assets != null) + { + foreach (var asset in assets) + { + var info = new IcosaSceneFileInfo(asset); + info.Author = asset["displayName"].ToString(); + files.Add(info); + } + } + + // Set page token to -1 when we hit the last page + m_PageIndex = assets.Count > 0 ? m_PageIndex : -1; + } + + public IEnumerator NextPage(List files, + string thumbnailSuffix) + { + if (m_PageIndex == -1) { yield break; } + string uri = m_PageIndex == 0 ? m_Uri + : String.Format("{0}&page_token={1}", m_Uri, m_PageIndex); + + WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); + using (var cr = request.SendAsync().AsIeNull()) + { + while (!request.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException e) + { + e.UserFriendly = m_ErrorMessage; + throw; + } + yield return cr.Current; + } + } + Future f = new Future(() => JObject.Parse(request.Result)); + JObject json; + while (!f.TryGetResult(out json)) { yield return null; } + + if (json.Count == 0) { yield break; } + + JToken lastAsset = null; + var assets = json["assets"] ?? json["userAssets"]; + foreach (JToken possibleAsset in assets) + { + try + { + // User assets are nested in an 'asset' node. + JToken asset = possibleAsset["asset"] ?? possibleAsset; + if (asset["visibility"].ToString() == "PRIVATE") + { + continue; + } + + // We now don't filter the liked Poly objects, but we don't want to return liked Tilt Brush + // sketches so in this section we filter out anything with a Tilt file in it. + // Also, although currently all Poly objects have a GLTF representation we should probably + // not rely on that continuing, so we discard anything that doesn't have a GLTF (1) + // representation. We look for PGLTF and GLTF as for a lot of objects Poly is returning + // PGLTF without GLTF. + bool skipObject = false; + foreach (var format in asset["formats"]) + { + var formatType = format["formatType"].ToString(); + if (formatType == "TILT") + { + skipObject = true; + break; + } + } + if (skipObject) + { + continue; + } + lastAsset = asset; + string accountName = asset["authorName"]?.ToString() ?? "Unknown"; + files.Add(new IcosaAssetCatalog.AssetDetails(asset, accountName, thumbnailSuffix)); + } + catch (NullReferenceException) + { + UnityEngine.Debug.LogErrorFormat("Failed to load asset: {0}", + lastAsset == null ? "NULL" : lastAsset.ToString()); + } + yield return null; + } + + m_PageIndex++; + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/GoogleDriveSketchSet.cs b/Assets/Scripts/Sharing/GoogleDriveSketchSet.cs index 520238f3a6..fd8db78ed4 100644 --- a/Assets/Scripts/Sharing/GoogleDriveSketchSet.cs +++ b/Assets/Scripts/Sharing/GoogleDriveSketchSet.cs @@ -1,441 +1,441 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using UnityEngine; -using UnityEngine.Networking; -using DriveData = Google.Apis.Drive.v3.Data; -using static TiltBrush.DriveAccess; - -namespace TiltBrush -{ - /// A sketchset made up of the sketches stored on Google drive, excepting the ones that are copies - /// of the sketches on the local device. - public class GoogleDriveSketchSet : SketchSet - { - - public class GoogleDriveFileInfo : SceneFileInfo - { - private const string kCachePath = "DriveCache"; - private DriveData.File m_File; - private Texture2D m_Thumbnail; - private bool m_AbortLoad; - private string m_FileName; - private TiltFile m_TiltFile; - private FileStream m_DownloadStream; - private string m_SourceId; // If this is a derivative work of a poly asset, that asset id. - private string m_Source; - - public Texture2D Thumbnail => m_Thumbnail; - - public FileInfoType InfoType => FileInfoType.Cloud; - - public string HumanName => m_File?.Name ?? "Untitled"; - - public bool Valid => true; - - public bool Available => m_TiltFile != null; - - public string FullPath => Path.Combine(App.UserSketchPath(), HumanName); - - public bool Exists => true; - - public bool ReadOnly => true; - - public string AssetId => null; - - public string SourceId - { - get { return m_SourceId; } - set { m_SourceId = value; } - } - - public int? TriangleCount => null; - - public float Progress - { - get - { - if (m_DownloadStream == null) - { - return 1f; - } - return m_DownloadStream.Position / (float)m_File.Size.Value; - } - } - - public DateTime LastModifiedTime => m_File.ModifiedTime.Value; - - public string Source => m_Source; - - public string Description => m_File.Description; - - private string CachePath => Path.Combine(Application.temporaryCachePath, kCachePath); - - public GoogleDriveFileInfo(DriveData.File file, string source) - { - m_File = file; - m_FileName = Path.Combine(CachePath, m_File.Id + ".tilt"); - if (File.Exists(m_FileName)) - { - var fileInfo = new FileInfo(m_FileName); - if (DriveAccess.IsNewer(m_File, fileInfo)) - { - File.Delete(m_FileName); - } - else - { - m_TiltFile = new TiltFile(m_FileName); - } - } - - m_Source = source; - } - - public void Delete() - { - throw new NotImplementedException(); - } - - public string Rename(string newName) - { - throw new NotImplementedException(); - } - - public bool IsHeaderValid() - { - return true; // TODO - } - - public Stream GetReadStream(string subfileName) - { - return m_TiltFile.GetReadStream(subfileName); - } - - public IEnumerator LoadThumbnail() - { - UnityWebRequest request = UnityWebRequestTexture.GetTexture(m_File.ThumbnailLink, - nonReadable: true); - var operation = request.SendWebRequest(); - m_AbortLoad = false; - while (!operation.isDone && !m_AbortLoad) - { - yield return null; - } - if (m_AbortLoad) { yield break; } - if (!request.isNetworkError) - { - m_Thumbnail = DownloadHandlerTexture.GetContent(request); - } - // TODO: show broken texture url here? - } - - public void UnloadThumbnail() - { - m_AbortLoad = true; - UnityEngine.Object.Destroy(m_Thumbnail); - m_Thumbnail = null; - } - - public async Task DownloadAsync(CancellationToken token) - { - Directory.CreateDirectory(CachePath); - using (m_DownloadStream = new FileStream(m_FileName, FileMode.Create)) - { - try - { - await Retry(() => App.DriveAccess.DownloadFileAsync(m_File, m_DownloadStream, token)); - } - catch (OperationCanceledException) - { - m_DownloadStream.Close(); - File.Delete(m_FileName); - } - finally - { - m_DownloadStream = null; - } - } - File.SetLastWriteTime(m_FileName, m_File.ModifiedTime.Value); - m_TiltFile = new TiltFile(m_FileName); - } - } - - - private bool m_Refreshing; - private GoogleDriveFileInfo[] m_Sketches; - private Coroutine m_ThumbnailLoadingCoroutine; - private bool m_Changed; - - public SketchSetType Type => SketchSetType.Drive; - - public bool IsReadyForAccess => m_Sketches != null; - - public bool IsActivelyRefreshingSketches => m_Refreshing || App.DriveAccess.Initializing; - - public bool RequestedIconsAreLoaded => m_ThumbnailLoadingCoroutine == null; - - public int NumSketches => m_Sketches?.Length ?? 0; - - public void Init() - { - App.DriveAccess.OnReadyChanged += OnDriveEnabledChanged; - EnumerateTiltFilesWhenReady(); - } - - public bool IsSketchIndexValid(int iIndex) - { - return iIndex >= 0 && iIndex < NumSketches; - } - - public void RequestOnlyLoadedMetadata(List requests) - { - // Get rid of existing icons - for (int i = 0; i < NumSketches; ++i) - { - m_Sketches[i].UnloadThumbnail(); - } - Resources.UnloadUnusedAssets(); - - if (m_ThumbnailLoadingCoroutine != null) - { - App.Instance.StopCoroutine(m_ThumbnailLoadingCoroutine); - m_Refreshing = false; - } - - m_ThumbnailLoadingCoroutine = App.Instance.StartCoroutine(LoadThumbnailsCoroutine(requests)); - } - - public bool GetSketchIcon(int i, out Texture2D icon, out string[] authors, - out string description) - { - if (i < 0 || i >= NumSketches) - { - icon = null; - authors = null; - description = null; - return false; - } - var sketch = m_Sketches[i]; - icon = sketch.Thumbnail; - authors = new[] { $"From {sketch.Source}" }; - description = string.IsNullOrEmpty(sketch.Description) ? null : sketch.Description; - return icon != null; - } - - public SceneFileInfo GetSketchSceneFileInfo(int i) - { - if (i < 0 || i >= NumSketches) - { - return null; - } - return m_Sketches[i]; - } - - public string GetSketchName(int i) - { - return GetSketchSceneFileInfo(i)?.HumanName; - } - - public void DeleteSketch(int toDelete) - { - throw new NotImplementedException(); - } - - public void RenameSketch(int toRename, string newName) - { - throw new NotImplementedException(); - } - - public void PrecacheSketchModels(int i) - { - if (i >= 0 && i < NumSketches) - { - // TODO: this currently causes the models to also be loaded into memory - App.PolyAssetCatalog.PrecacheModels(m_Sketches[i], $"GoogleDriveSketchSet {i}"); - } - } - - public void NotifySketchCreated(string fullpath) - { - m_Changed = true; - } - - public void NotifySketchChanged(string fullpath) - { - m_Changed = true; - } - - public void RequestRefresh() - { - // nothing - } - - public void Update() - { - if (!App.GoogleIdentity.LoggedIn || !App.GoogleUserSettings.Initialized) - { - return; - } - if (!m_Refreshing && m_Changed) - { - m_Changed = false; - EnumerateTiltFilesAsync().AsAsyncVoid(); - } - } - - public event Action OnChanged; - - public event Action OnSketchRefreshingChanged; - - private void OnDriveEnabledChanged() - { - if (App.DriveSync.SyncEnabled) - { - EnumerateTiltFilesWhenReady(); - OnSketchRefreshingChanged?.Invoke(); - } - else - { - if (m_Sketches != null && m_Sketches.Length != 0) - { - foreach (var sketch in m_Sketches) - { - sketch.UnloadThumbnail(); - } - m_Sketches = null; - OnChanged?.Invoke(); - } - } - } - - /// This will enumerate tilt files if the Drive access is ready - but otherwise will add itself - /// the DriveAccess.OnReadyChanged event to get called when that happens. It removes itself from - /// the event afterwards. Multiple calls will only result in a single enumeration. - private void EnumerateTiltFilesWhenReady() - { - if (App.DriveAccess.Ready) - { - if (!m_Refreshing) - { - EnumerateTiltFilesAsync().AsAsyncVoid(); - } - App.DriveAccess.OnReadyChanged -= EnumerateTiltFilesWhenReady; - } - else - { - App.DriveAccess.OnReadyChanged += EnumerateTiltFilesWhenReady; - } - } - - private async Task EnumerateTiltFilesAsync() - { - m_Refreshing = true; - OnSketchRefreshingChanged?.Invoke(); - try - { - // Gets all the 'Sketches' folders within each device folder on Google Drive - List deviceFolders = - (await Retry(() => App.DriveAccess.GetFoldersInFolderAsync( - App.GoogleUserSettings.DriveSyncFolderId, CancellationToken.None))).ToList(); - - var sketchTasks = deviceFolders - .Select(x => EnumerateTiltFilesForDevice(x, CancellationToken.None)).ToArray(); - await Task.WhenAll(sketchTasks); - // If the sketch is a backup of something that came from the local machine, only show it if - // the file is no longer present on the local machine. - var sketchList = new List(); - for (int i = 0; i < deviceFolders.Count; ++i) - { - if (deviceFolders[i].Id == App.DriveAccess.DeviceFolder) - { - sketchList.AddRange(sketchTasks[i].Result.Where(x => !File.Exists(x.FullPath))); - } - else - { - sketchList.AddRange(sketchTasks[i].Result); - } - } - m_Sketches = sketchList.OrderByDescending(x => x.LastModifiedTime).ToArray(); - } - finally - { - await new WaitForUpdate(); - m_Refreshing = false; - OnSketchRefreshingChanged?.Invoke(); - OnChanged?.Invoke(); - } - } - - private async Task> EnumerateTiltFilesForDevice( - DriveData.File folder, CancellationToken token) - { - var sketchFolder = - await Retry(() => App.DriveAccess.GetFolderAsync("Sketches", folder.Id, token)); - if (sketchFolder == null) - { - return Enumerable.Empty(); - } - var files = await Retry(() => App.DriveAccess.GetFilesInFolderAsync(sketchFolder.Id, token)); - var tiltFiles = files.Where(x => Path.GetExtension(x.Name) == ".tilt"); - return tiltFiles.Select(x => new GoogleDriveFileInfo(x, folder.Name)); - } - - private IEnumerator LoadThumbnailsCoroutine(List requests) - { - if (requests.Count == 0) - { - yield break; - } - var loadingCoroutines = requests.Select(x => m_Sketches[x].LoadThumbnail()).ToArray(); - - // Stagger kicking off each loading coroutine by a frame - foreach (var coroutine in loadingCoroutines) - { - coroutine.MoveNext(); - yield return null; - } - - bool stillLoading; - do - { - stillLoading = false; - for (int i = 0; i < loadingCoroutines.Length; ++i) - { - var coroutine = loadingCoroutines[i]; - if (coroutine == null) - { - continue; - } - if (coroutine.MoveNext()) - { - stillLoading = true; - } - else - { - loadingCoroutines[i] = null; - } - } - yield return null; - } while (stillLoading); - m_ThumbnailLoadingCoroutine = null; - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; +using DriveData = Google.Apis.Drive.v3.Data; +using static TiltBrush.DriveAccess; + +namespace TiltBrush +{ + /// A sketchset made up of the sketches stored on Google drive, excepting the ones that are copies + /// of the sketches on the local device. + public class GoogleDriveSketchSet : SketchSet + { + + public class GoogleDriveFileInfo : SceneFileInfo + { + private const string kCachePath = "DriveCache"; + private DriveData.File m_File; + private Texture2D m_Thumbnail; + private bool m_AbortLoad; + private string m_FileName; + private TiltFile m_TiltFile; + private FileStream m_DownloadStream; + private string m_SourceId; // If this is a derivative work of a poly asset, that asset id. + private string m_Source; + + public Texture2D Thumbnail => m_Thumbnail; + + public FileInfoType InfoType => FileInfoType.Cloud; + + public string HumanName => m_File?.Name ?? "Untitled"; + + public bool Valid => true; + + public bool Available => m_TiltFile != null; + + public string FullPath => Path.Combine(App.UserSketchPath(), HumanName); + + public bool Exists => true; + + public bool ReadOnly => true; + + public string AssetId => null; + + public string SourceId + { + get { return m_SourceId; } + set { m_SourceId = value; } + } + + public int? TriangleCount => null; + + public float Progress + { + get + { + if (m_DownloadStream == null) + { + return 1f; + } + return m_DownloadStream.Position / (float)m_File.Size.Value; + } + } + + public DateTime LastModifiedTime => m_File.ModifiedTime.Value; + + public string Source => m_Source; + + public string Description => m_File.Description; + + private string CachePath => Path.Combine(Application.temporaryCachePath, kCachePath); + + public GoogleDriveFileInfo(DriveData.File file, string source) + { + m_File = file; + m_FileName = Path.Combine(CachePath, m_File.Id + ".tilt"); + if (File.Exists(m_FileName)) + { + var fileInfo = new FileInfo(m_FileName); + if (DriveAccess.IsNewer(m_File, fileInfo)) + { + File.Delete(m_FileName); + } + else + { + m_TiltFile = new TiltFile(m_FileName); + } + } + + m_Source = source; + } + + public void Delete() + { + throw new NotImplementedException(); + } + + public string Rename(string newName) + { + throw new NotImplementedException(); + } + + public bool IsHeaderValid() + { + return true; // TODO + } + + public Stream GetReadStream(string subfileName) + { + return m_TiltFile.GetReadStream(subfileName); + } + + public IEnumerator LoadThumbnail() + { + UnityWebRequest request = UnityWebRequestTexture.GetTexture(m_File.ThumbnailLink, + nonReadable: true); + var operation = request.SendWebRequest(); + m_AbortLoad = false; + while (!operation.isDone && !m_AbortLoad) + { + yield return null; + } + if (m_AbortLoad) { yield break; } + if (!request.isNetworkError) + { + m_Thumbnail = DownloadHandlerTexture.GetContent(request); + } + // TODO: show broken texture url here? + } + + public void UnloadThumbnail() + { + m_AbortLoad = true; + UnityEngine.Object.Destroy(m_Thumbnail); + m_Thumbnail = null; + } + + public async Task DownloadAsync(CancellationToken token) + { + Directory.CreateDirectory(CachePath); + using (m_DownloadStream = new FileStream(m_FileName, FileMode.Create)) + { + try + { + await Retry(() => App.DriveAccess.DownloadFileAsync(m_File, m_DownloadStream, token)); + } + catch (OperationCanceledException) + { + m_DownloadStream.Close(); + File.Delete(m_FileName); + } + finally + { + m_DownloadStream = null; + } + } + File.SetLastWriteTime(m_FileName, m_File.ModifiedTime.Value); + m_TiltFile = new TiltFile(m_FileName); + } + } + + + private bool m_Refreshing; + private GoogleDriveFileInfo[] m_Sketches; + private Coroutine m_ThumbnailLoadingCoroutine; + private bool m_Changed; + + public SketchSetType Type => SketchSetType.Drive; + + public bool IsReadyForAccess => m_Sketches != null; + + public bool IsActivelyRefreshingSketches => m_Refreshing || App.DriveAccess.Initializing; + + public bool RequestedIconsAreLoaded => m_ThumbnailLoadingCoroutine == null; + + public int NumSketches => m_Sketches?.Length ?? 0; + + public void Init() + { + App.DriveAccess.OnReadyChanged += OnDriveEnabledChanged; + EnumerateTiltFilesWhenReady(); + } + + public bool IsSketchIndexValid(int iIndex) + { + return iIndex >= 0 && iIndex < NumSketches; + } + + public void RequestOnlyLoadedMetadata(List requests) + { + // Get rid of existing icons + for (int i = 0; i < NumSketches; ++i) + { + m_Sketches[i].UnloadThumbnail(); + } + Resources.UnloadUnusedAssets(); + + if (m_ThumbnailLoadingCoroutine != null) + { + App.Instance.StopCoroutine(m_ThumbnailLoadingCoroutine); + m_Refreshing = false; + } + + m_ThumbnailLoadingCoroutine = App.Instance.StartCoroutine(LoadThumbnailsCoroutine(requests)); + } + + public bool GetSketchIcon(int i, out Texture2D icon, out string[] authors, + out string description) + { + if (i < 0 || i >= NumSketches) + { + icon = null; + authors = null; + description = null; + return false; + } + var sketch = m_Sketches[i]; + icon = sketch.Thumbnail; + authors = new[] { $"From {sketch.Source}" }; + description = string.IsNullOrEmpty(sketch.Description) ? null : sketch.Description; + return icon != null; + } + + public SceneFileInfo GetSketchSceneFileInfo(int i) + { + if (i < 0 || i >= NumSketches) + { + return null; + } + return m_Sketches[i]; + } + + public string GetSketchName(int i) + { + return GetSketchSceneFileInfo(i)?.HumanName; + } + + public void DeleteSketch(int toDelete) + { + throw new NotImplementedException(); + } + + public void RenameSketch(int toRename, string newName) + { + throw new NotImplementedException(); + } + + public void PrecacheSketchModels(int i) + { + if (i >= 0 && i < NumSketches) + { + // TODO: this currently causes the models to also be loaded into memory + App.IcosaAssetCatalog.PrecacheModels(m_Sketches[i], $"GoogleDriveSketchSet {i}"); + } + } + + public void NotifySketchCreated(string fullpath) + { + m_Changed = true; + } + + public void NotifySketchChanged(string fullpath) + { + m_Changed = true; + } + + public void RequestRefresh() + { + // nothing + } + + public void Update() + { + if (!App.GoogleIdentity.LoggedIn || !App.GoogleUserSettings.Initialized) + { + return; + } + if (!m_Refreshing && m_Changed) + { + m_Changed = false; + EnumerateTiltFilesAsync().AsAsyncVoid(); + } + } + + public event Action OnChanged; + + public event Action OnSketchRefreshingChanged; + + private void OnDriveEnabledChanged() + { + if (App.DriveSync.SyncEnabled) + { + EnumerateTiltFilesWhenReady(); + OnSketchRefreshingChanged?.Invoke(); + } + else + { + if (m_Sketches != null && m_Sketches.Length != 0) + { + foreach (var sketch in m_Sketches) + { + sketch.UnloadThumbnail(); + } + m_Sketches = null; + OnChanged?.Invoke(); + } + } + } + + /// This will enumerate tilt files if the Drive access is ready - but otherwise will add itself + /// the DriveAccess.OnReadyChanged event to get called when that happens. It removes itself from + /// the event afterwards. Multiple calls will only result in a single enumeration. + private void EnumerateTiltFilesWhenReady() + { + if (App.DriveAccess.Ready) + { + if (!m_Refreshing) + { + EnumerateTiltFilesAsync().AsAsyncVoid(); + } + App.DriveAccess.OnReadyChanged -= EnumerateTiltFilesWhenReady; + } + else + { + App.DriveAccess.OnReadyChanged += EnumerateTiltFilesWhenReady; + } + } + + private async Task EnumerateTiltFilesAsync() + { + m_Refreshing = true; + OnSketchRefreshingChanged?.Invoke(); + try + { + // Gets all the 'Sketches' folders within each device folder on Google Drive + List deviceFolders = + (await Retry(() => App.DriveAccess.GetFoldersInFolderAsync( + App.GoogleUserSettings.DriveSyncFolderId, CancellationToken.None))).ToList(); + + var sketchTasks = deviceFolders + .Select(x => EnumerateTiltFilesForDevice(x, CancellationToken.None)).ToArray(); + await Task.WhenAll(sketchTasks); + // If the sketch is a backup of something that came from the local machine, only show it if + // the file is no longer present on the local machine. + var sketchList = new List(); + for (int i = 0; i < deviceFolders.Count; ++i) + { + if (deviceFolders[i].Id == App.DriveAccess.DeviceFolder) + { + sketchList.AddRange(sketchTasks[i].Result.Where(x => !File.Exists(x.FullPath))); + } + else + { + sketchList.AddRange(sketchTasks[i].Result); + } + } + m_Sketches = sketchList.OrderByDescending(x => x.LastModifiedTime).ToArray(); + } + finally + { + await new WaitForUpdate(); + m_Refreshing = false; + OnSketchRefreshingChanged?.Invoke(); + OnChanged?.Invoke(); + } + } + + private async Task> EnumerateTiltFilesForDevice( + DriveData.File folder, CancellationToken token) + { + var sketchFolder = + await Retry(() => App.DriveAccess.GetFolderAsync("Sketches", folder.Id, token)); + if (sketchFolder == null) + { + return Enumerable.Empty(); + } + var files = await Retry(() => App.DriveAccess.GetFilesInFolderAsync(sketchFolder.Id, token)); + var tiltFiles = files.Where(x => Path.GetExtension(x.Name) == ".tilt"); + return tiltFiles.Select(x => new GoogleDriveFileInfo(x, folder.Name)); + } + + private IEnumerator LoadThumbnailsCoroutine(List requests) + { + if (requests.Count == 0) + { + yield break; + } + var loadingCoroutines = requests.Select(x => m_Sketches[x].LoadThumbnail()).ToArray(); + + // Stagger kicking off each loading coroutine by a frame + foreach (var coroutine in loadingCoroutines) + { + coroutine.MoveNext(); + yield return null; + } + + bool stillLoading; + do + { + stillLoading = false; + for (int i = 0; i < loadingCoroutines.Length; ++i) + { + var coroutine = loadingCoroutines[i]; + if (coroutine == null) + { + continue; + } + if (coroutine.MoveNext()) + { + stillLoading = true; + } + else + { + loadingCoroutines[i] = null; + } + } + yield return null; + } while (stillLoading); + m_ThumbnailLoadingCoroutine = null; + } + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/PolySketchSet.cs b/Assets/Scripts/Sharing/IcosaSketchSet.cs similarity index 87% rename from Assets/Scripts/Sharing/PolySketchSet.cs rename to Assets/Scripts/Sharing/IcosaSketchSet.cs index 974b5da7cb..c9b6da1a66 100644 --- a/Assets/Scripts/Sharing/PolySketchSet.cs +++ b/Assets/Scripts/Sharing/IcosaSketchSet.cs @@ -1,923 +1,927 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using UnityEngine; -using UnityEngine.Networking; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Newtonsoft.Json.Linq; - -namespace TiltBrush -{ - - // TODO: Specify tag for which sketches to query (curated, liked etc.) - public class PolySketchSet : SketchSet - { - - const int kDownloadBufferSize = 1024 * 1024; // 1MB - - // Downloading is handled by PolySketchSet which will set the local paths - - private class PolySketch : Sketch - { - // This value holds the count of sketches that were downloaded by the sketch set - // before this one. It's used during our sort to retain order from Poly, while - // allowing a custom sort on top. - public int m_DownloadIndex; - private IcosaSceneFileInfo m_FileInfo; - private Texture2D m_Icon; - - public SceneFileInfo SceneFileInfo - { - get { return m_FileInfo; } - } - - public string[] Authors - { - get - { - return m_FileInfo.Author != null - ? new string[] { m_FileInfo.Author } - : new string[] { }; - } - } - - public Texture2D Icon - { - get { return m_Icon; } - set { m_Icon = value; } - } - - public bool IconAndMetadataValid - { - get { return m_Icon != null; } - } - - public PolySketch(IcosaSceneFileInfo info) - { - m_FileInfo = info; - } - - public void UnloadIcon() - { - UnityEngine.Object.Destroy(m_Icon); - m_Icon = null; - } - - // Not part of the interface - public IcosaSceneFileInfo IcosaSceneFileInfo - { - get { return m_FileInfo; } - } - } - - private const string kIntroSketchAssetId = "agqmaia62KE"; - private const int kIntroSketchIndex = 1; - - private List m_Sketches; - private Dictionary m_AssetIds; - private int m_TotalCount = 0; - private VrAssetService m_AssetService; - private MonoBehaviour m_Parent; - private bool m_Ready; - private bool m_RefreshRequested; - private bool m_NeedsLogin; - private bool m_LoggedIn; - private bool m_ActivelyRefreshingSketches; - private int m_MaximumSceneTriangles; - - private SketchSetType m_Type; - private string m_CacheDir; - private Coroutine m_RefreshCoroutine; - private float m_CooldownTimer; - private List m_RequestedIcons = new List(); - private Coroutine m_TextureLoaderCoroutine; - - public SketchSetType Type { get { return m_Type; } } - - public VrAssetService VrAssetService - { - set { m_AssetService = value; } - } - - public PolySketchSet(MonoBehaviour parent, SketchSetType type, int maxSceneTriangles, - bool needsLogin = false) - { - m_Parent = parent; - m_Sketches = new List(); - m_AssetIds = new Dictionary(); - m_Ready = false; - m_RefreshRequested = false; - m_Type = type; - m_NeedsLogin = needsLogin; - m_MaximumSceneTriangles = maxSceneTriangles; - } - - public void Init() - { - VrAssetService = VrAssetService.m_Instance; - m_LoggedIn = false; - m_RefreshRequested = true; - m_CooldownTimer = VrAssetService.m_Instance.m_SketchbookRefreshInterval; - } - - public bool IsReadyForAccess - { - get { return m_Ready; } - } - - public bool IsActivelyRefreshingSketches - { - get { return m_ActivelyRefreshingSketches; } - private set - { - m_ActivelyRefreshingSketches = value; - OnSketchRefreshingChanged(); - } - } - - public bool RequestedIconsAreLoaded - { - get { return false; } - } - - public int NumSketches - { - get { return m_TotalCount; } - } - - public void NotifySketchCreated(string fullpath) { } - - public void NotifySketchChanged(string fullpath) { } - - public void RequestRefresh() - { - m_RefreshRequested = true; - } - - public bool IsSketchIndexValid(int index) - { - return index >= 0 && index < m_TotalCount; - } - - public void RequestOnlyLoadedMetadata(List requests) - { - // Stop any current texture loading - if (m_TextureLoaderCoroutine != null) - { - m_Parent.StopCoroutine(m_TextureLoaderCoroutine); - m_TextureLoaderCoroutine = null; - } - // Nuke textures on all icons - foreach (var sketch in m_Sketches) - { - sketch.UnloadIcon(); - } - m_RequestedIcons.Clear(); - m_RequestedIcons.AddRange(requests); - m_TextureLoaderCoroutine = m_Parent.StartCoroutine(TextureLoaderCoroutine()); - } - - public bool GetSketchIcon(int iSketchIndex, out Texture2D icon, out string[] authors, - out string description) - { - description = null; - if (iSketchIndex >= m_Sketches.Count) - { - icon = null; - authors = null; - return false; - } - PolySketch sketch = m_Sketches[iSketchIndex]; - icon = sketch.Icon; - authors = sketch.Authors; - return icon != null; - } - - public SceneFileInfo GetSketchSceneFileInfo(int i) - { - return i < m_Sketches.Count ? m_Sketches[i].SceneFileInfo : null; - } - - public string GetSketchName(int i) - { - return i < m_Sketches.Count ? m_Sketches[i].SceneFileInfo.HumanName : null; - } - - public void RenameSketch(int toRename, string newName) - { - throw new NotImplementedException(); - } - - public void PrecacheSketchModels(int i) - { - if (i < m_Sketches.Count) - { - App.PolyAssetCatalog.PrecacheModels(m_Sketches[i].SceneFileInfo, $"PolySketchSet {i}"); - } - } - - public void DeleteSketch(int toDelete) - { - throw new NotImplementedException(); - } - - public void Update() - { - if (!VrAssetService.m_Instance.Available) - { - return; - } - - if (m_NeedsLogin) - { - bool loggedIn = App.GoogleIdentity.LoggedIn; - if (loggedIn != m_LoggedIn) - { - m_LoggedIn = loggedIn; - if (!loggedIn) - { - if (m_RefreshCoroutine != null) - { - m_Parent.StopCoroutine(m_RefreshCoroutine); - IsActivelyRefreshingSketches = false; - m_RefreshCoroutine = null; - m_Sketches.Clear(); - m_AssetIds.Clear(); - m_TotalCount = 0; - m_CacheDir = null; - OnChanged(); - } - } - else - { - // Lie about refreshing. The refresh coroutine will happen in time, but - // for the user, we want them to know something will happen [soon]. - IsActivelyRefreshingSketches = true; - m_RefreshCoroutine = m_Parent.StartCoroutine(PeriodicRefreshCoroutine()); - } - } - } - else if (m_RefreshRequested && m_RefreshCoroutine == null) - { - m_RefreshCoroutine = m_Parent.StartCoroutine(PeriodicRefreshCoroutine()); - } - } - - public event Action OnChanged = delegate { }; - - public event Action OnSketchRefreshingChanged = delegate { }; - - private IEnumerator PeriodicRefreshCoroutine() - { - while (true) - { - if (m_RefreshRequested) - { - yield return Refresh(); - m_RefreshRequested = false; - - // Don't poll the showcase - if (Type == SketchSetType.Curated) - { - yield break; - } - m_CooldownTimer = VrAssetService.m_Instance.m_SketchbookRefreshInterval; - } - else - { - yield return null; - } - while (m_CooldownTimer > 0.0f) - { - m_CooldownTimer -= Time.deltaTime; - yield return null; - } - } - } - - // Update our state from the cloud. - // There are three stages to this, each with a separate coroutine: - // 1: Pull all the metadata from the server and populate m_Sketches - // If there are changes: - // 2: Download any thumbnails and tilt files we don't already have - // 3: Clean up any cached files that are no longer referenced - private IEnumerator Refresh() - { - if (m_NeedsLogin && !m_LoggedIn) - { - m_Sketches.Clear(); - m_AssetIds.Clear(); - m_TotalCount = 0; - m_CacheDir = null; - OnChanged(); - yield break; - } - - if (m_CacheDir == null) - { - m_CacheDir = CacheDir(Type); - try - { - Directory.CreateDirectory(m_CacheDir); - } - catch (Exception ex) - { - // Most of the system exceptions returned by CreateDirectory are just things the user needs - // to fix, and may well be sorted by reinstalling. If it's an ArgumentNullExeption or not a - // SystemException, we need to know about it, so log the exception. - if (!(ex is SystemException) || ex is ArgumentNullException) - { - Debug.LogException(ex); - } - ControllerConsoleScript.m_Instance.AddNewLine( - $"Error creating cache directory. Try restarting {App.kAppDisplayName}.\n" + - $"If this error persists, try reinstalling {App.kAppDisplayName}.", true); - yield break; - } - } - - // While we're fetching metadata, hold a flag so we can message state in the UI. - IsActivelyRefreshingSketches = true; - yield return PopulateSketchesCoroutine(); - IsActivelyRefreshingSketches = false; - } - - // Fetch asset metadata from server and populate m_Sketches - private IEnumerator PopulateSketchesCoroutine() - { - // Replacement data structures that will be populated with incoming data. These - // temporary structures will be copied to the "live" structures in chunks, so - // beware of modifications that may reference incomplete data. - List sketches = new List(); - Dictionary assetIds = new Dictionary(); - - bool fromEmpty = m_AssetIds.Count == 0; - int loadSketchCount = 0; - - AssetLister lister = null; - List infos = new List(); - - // If we don't have a connection to Poly and we're querying the Showcase, use - // the json metadatas stored in resources, instead of trying to get them from Poly. - if (VrAssetService.m_Instance.NoConnection && m_Type == SketchSetType.Curated) - { - TextAsset[] textAssets = - Resources.LoadAll(SketchCatalog.kDefaultShowcaseSketchesFolder); - for (int i = 0; i < textAssets.Length; ++i) - { - JObject jo = JObject.Parse(textAssets[i].text); - infos.Add(new IcosaSceneFileInfo(jo)); - } - } - else - { - lister = m_AssetService.ListAssets(Type); - } - - bool changed = false; - int pagesFetched = 0; - while (lister == null || lister.HasMore || assetIds.Count == 0) - { - if (sketches.Count >= 180) - { - break; // Don't allow the sketchbook to become too big. - } - - // lister will be null if we can't connect to Poly and we've pre-populated infos - // with data from Resources. - if (lister != null) - { - using (var cr = lister.NextPage(infos)) - { - while (true) - { - try - { - if (!cr.MoveNext()) - { - break; - } - } - catch (VrAssetServiceException e) - { - ControllerConsoleScript.m_Instance.AddNewLine(e.UserFriendly); - Debug.LogException(e); - yield break; - } - yield return cr.Current; - } - } - } - if (infos.Count == 0) - { - break; - } - if (m_CacheDir == null) - { - yield break; - } - // Only cull the curated sketches. If a user likes a sketch that's very high poly count, - // there's no feedback to tell them why it didn't show up in their list. b/123649719 - if (m_Type == SketchSetType.Curated) - { - infos = infos.Where(x => x.GltfTriangleCount < m_MaximumSceneTriangles).ToList(); - } - if (m_Type == SketchSetType.Curated && !assetIds.Keys.Contains(kIntroSketchAssetId)) - { - yield return VrAssetService.m_Instance.InsertSketchInfo( - kIntroSketchAssetId, kIntroSketchIndex, infos); - } - for (int i = 0; i < infos.Count; i++) - { - IcosaSceneFileInfo info = infos[i]; - PolySketch sketch; - if (m_AssetIds.TryGetValue(info.AssetId, out sketch)) - { - // We already have this sketch - } - else - { - sketch = new PolySketch(info); - sketch.m_DownloadIndex = loadSketchCount++; - // Set local paths - info.TiltPath = Path.Combine(m_CacheDir, String.Format("{0}.tilt", info.AssetId)); - info.IconPath = Path.Combine(m_CacheDir, String.Format("{0}.png", info.AssetId)); - changed = true; - } - if (assetIds.ContainsKey(info.AssetId)) - { - Debug.LogErrorFormat("VR Asset Service has returned two objects with asset id '{0}'.", - info.AssetId); - } - else - { - sketches.Add(sketch); - assetIds.Add(info.AssetId, sketch); - } - } - infos.Clear(); - - // Only download the files with every other page, otherwise the sketch pages change too often, - // Which results in a bad user experience. - if ((++pagesFetched & 1) == 0 || lister == null || !lister.HasMore) - { - if (Type == SketchSetType.Curated) - { - sketches.Sort(CompareSketchesByTriangleCountAndDownloadIndex); - } - - // If populating the set from new then show stuff as it comes in. - // (We don't have to worry about anything being removed) - if (fromEmpty) - { - yield return DownloadFilesCoroutine(sketches); - RemoveFailedDownloads(sketches); - // Copying sketches to m_Sketches before sketches has completed populating is a bit - // dangerous, but as long as they're copied and then listeners are notified - // immediately afterward with OnChanged(), there data should be stable. - m_TotalCount = sketches.Count; - m_Sketches = new List(sketches); - m_AssetIds = assetIds; - m_Ready = true; - OnChanged(); - } - } - } - - if (!fromEmpty) - { - // Find anything that was removed - int removed = m_Sketches.Count(s => !assetIds.ContainsKey(s.IcosaSceneFileInfo.AssetId)); - if (removed > 0) - { - changed = true; - } - - // Download new files before we notify our listeners that we've got new stuff for them. - if (changed) - { - yield return DownloadFilesCoroutine(sketches); - RemoveFailedDownloads(sketches); - } - - // DeleteOldSketchesCoroutine relies on m_AssetIds being up to date, so set these before - // we try to cull the herd. - m_AssetIds = assetIds; - if (changed) - { - yield return DeleteOldSketchesCoroutine(); - } - - // Set new data live - m_TotalCount = sketches.Count; - m_Sketches = new List(sketches); - m_Ready = true; - if (changed) - { - OnChanged(); - } - } - else - { - // On first run populate, take the time to clean out any cobwebs. - // Note that this is particularly important for the curated sketches, which do not have - // a periodic refresh, meaning old sketches will never been deleted in the other path. - yield return DeleteOldSketchesCoroutine(); - OnChanged(); - } - } - - // If we have not managed to download a tilt file or its icon, we should remove it from the - // sketches list so as not to confuse the user. - private void RemoveFailedDownloads(List sketches) - { - sketches.RemoveAll(x => !x.IcosaSceneFileInfo.TiltDownloaded || - !x.IcosaSceneFileInfo.IconDownloaded); - } - - // Download tilt files and thumbnails (that we don't already have) - private IEnumerator DownloadFilesCoroutine(List sketches) - { - bool notifyOnError = true; - void NotifyCreateError(IcosaSceneFileInfo sceneFileInfo, string type, Exception ex) - { - string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}."; - ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); - notifyOnError = false; - Debug.LogException(ex); - Debug.LogError($"{sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); - } - - void NotifyWriteError(IcosaSceneFileInfo sceneFileInfo, string type, UnityWebRequest www) - { - string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}.\n" + - "Out of disk space?"; - ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); - notifyOnError = false; - Debug.LogError($"{www.error} {sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); - } - - byte[] downloadBuffer = new byte[kDownloadBufferSize]; - // Load the icons first, then the thumbnails - foreach (PolySketch sketch in sketches) - { - IcosaSceneFileInfo sceneFileInfo = sketch.IcosaSceneFileInfo; - // TODO(b/36270116): Check filesizes when Poly can give it to us to detect incomplete downloads - if (!sceneFileInfo.IconDownloaded) - { - if (File.Exists(sceneFileInfo.IconPath)) - { - sceneFileInfo.IconDownloaded = true; - } - else - { - using (UnityWebRequest www = UnityWebRequest.Get(sceneFileInfo.IconUrl)) - { - DownloadHandlerFastFile downloadHandler; - try - { - downloadHandler = new DownloadHandlerFastFile(sceneFileInfo.IconPath, downloadBuffer); - } - catch (Exception ex) - { - NotifyCreateError(sceneFileInfo, "icon", ex); - continue; - } - www.downloadHandler = downloadHandler; - yield return www.SendWebRequest(); - if (www.isNetworkError || www.responseCode >= 400 || !string.IsNullOrEmpty(www.error)) - { - NotifyWriteError(sceneFileInfo, "icon", www); - } - else - { - sceneFileInfo.IconDownloaded = true; - } - } - } - } - yield return null; - } - - foreach (PolySketch sketch in sketches) - { - IcosaSceneFileInfo sceneFileInfo = sketch.IcosaSceneFileInfo; - if (!sceneFileInfo.TiltDownloaded) - { - if (File.Exists(sceneFileInfo.TiltPath)) - { - sceneFileInfo.TiltDownloaded = true; - } - else - { - using (UnityWebRequest www = UnityWebRequest.Get(sceneFileInfo.TiltFileUrl)) - { - DownloadHandlerFastFile downloadHandler; - try - { - downloadHandler = new DownloadHandlerFastFile(sceneFileInfo.TiltPath, downloadBuffer); - } - catch (Exception ex) - { - NotifyCreateError(sceneFileInfo, "sketch", ex); - continue; - } - www.downloadHandler = downloadHandler; - yield return www.SendWebRequest(); - if (www.isNetworkError || www.responseCode >= 400 || !string.IsNullOrEmpty(www.error)) - { - NotifyWriteError(sceneFileInfo, "sketch", www); - } - else - { - sceneFileInfo.TiltDownloaded = true; - } - } - } - } - yield return null; - } - yield return null; - } - - // Delete any files that aren't referenced in m_Sketches (actually m_AssetIds) - // Does this on a background thread so prevent hitches. - private IEnumerator DeleteOldSketchesCoroutine() - { - if (m_CacheDir != null) - { - var task = new Future(() => - { - var unknown = new DirectoryInfo(m_CacheDir).GetFiles().Where( - f => !m_AssetIds.ContainsKey(Path.GetFileNameWithoutExtension(f.Name))); - foreach (var f in unknown) - { - f.Delete(); - } - return true; - }); - - bool unused; - while (!task.TryGetResult(out unused)) - { - yield return null; - } - } - } - - // Read the icon textures for all sketches in m_RequestedIcons - private IEnumerator TextureLoaderCoroutine() - { - while (m_RequestedIcons.Count > 0) - { - foreach (int i in m_RequestedIcons) - { - PolySketch sketch = m_Sketches[i]; - string path = sketch.IcosaSceneFileInfo.IconPath; - if (sketch.IcosaSceneFileInfo.IconDownloaded) - { - byte[] data = File.ReadAllBytes(path); - Texture2D t = new Texture2D(2, 2); - t.LoadImage(data); - sketch.Icon = t; - } - yield return null; - } - m_RequestedIcons.RemoveAll(i => m_Sketches[i].Icon != null); - } - } - - private static string CacheDir(SketchSetType type) - { - switch (type) - { - case SketchSetType.Liked: - { - // Ids are in the format "people/123456" so just pull out the numeric part - string id = App.GoogleIdentity.Profile.id; - id = id.Substring(id.LastIndexOf('/') + 1); - return Path.Combine(Application.persistentDataPath, String.Format("users/{0}/liked", id)); - } - case SketchSetType.Curated: - return Path.Combine(Application.persistentDataPath, "Curated Sketches"); - default: - return null; - } - } - - // When we sort the sketches, we sort them into buckets while retaining order within those - // buckets. We bucket the sketches using the gltf triangle count, but how we bucket depends on - // whether we are sorting the liked sketches or the curated sketches. - // Liked sketches get split into normal, complex (requires a warning), and impossible. - // Curated sketches get bucketed by the nearest 100,000 triangles. - private static int CompareSketchesByTriangleCountAndDownloadIndex(PolySketch a, PolySketch b) - { - int compareResult = CloudSketchComplexityBucket(a).CompareTo(CloudSketchComplexityBucket(b)); - - // If both sketches are in the same grouping, sort them relative to download index. - if (compareResult == 0) - { - return a.m_DownloadIndex.CompareTo(b.m_DownloadIndex); - } - - return compareResult; - } - - // Buckets the sketches into buckets 100000 tris in size. - private static int CloudSketchComplexityBucket(PolySketch s) - { - return s.IcosaSceneFileInfo.GltfTriangleCount / 100000; - } - } - - public class IcosaSceneFileInfo : SceneFileInfo - { - - // Asset - private string m_AssetId; - private string m_HumanName; - private string m_License; - - private string m_localTiltFile; - private string m_localIcon; - private string m_TiltFileUrl; - private string m_IconUrl; - private int m_GltfTriangleCount; - - private TiltFile m_DownloadedFile; - private bool m_IconDownloaded; - - // Populate metadata from the JSON returned by Poly for a single asset - // See go/vr-assets-service-api - public IcosaSceneFileInfo(JToken json) - { - m_AssetId = json["name"].ToString().Substring(7); // strip 'assets/' from start - m_HumanName = json["displayName"].ToString(); - - var format = json["formats"].First(x => x["formatType"].ToString() == "TILT")["root"]; - m_TiltFileUrl = format["url"].ToString(); - m_IconUrl = json["thumbnail"]?["url"]?.ToString(); - m_License = json["license"]?.ToString(); - - - // Some assets (old ones? broken ones?) are missing the "formatComplexity" field - var gltfFormat = json["formats"].First(x => x["formatType"].ToString() == "GLTF"); - string gltfTriCount = gltfFormat?["formatComplexity"]?["triangleCount"]?.ToString(); - if (gltfTriCount == null) - { - Debug.Log($"{m_AssetId} has no tricount"); - } - m_GltfTriangleCount = Int32.Parse(gltfTriCount ?? "1"); - - m_DownloadedFile = null; - m_IconDownloaded = false; - } - - public override string ToString() - { - return $"CloudFile {AssetId} file {TiltPath}"; - } - - public FileInfoType InfoType - { - get { return FileInfoType.Cloud; } - } - - public string HumanName - { - get { return m_HumanName; } - } - - // Allow setting since it is not in the asset json object itself - public string Author { get; set; } - - public bool Valid - { - get { return true; } - } - - public bool Available - { - get { return m_DownloadedFile != null; } - } - - public string FullPath - { - get { return m_localTiltFile; } - } - - public bool Exists - { - get { return true; } - } - - public bool ReadOnly - { - get { return true; } - } - - public string SourceId - { - get { return null; } - } - - public void Delete() - { - throw new NotImplementedException(); - } - - public string Rename(string newName) - { - throw new NotImplementedException(); - } - - public bool IsHeaderValid() - { - // Assume it's valid until we download it - return true; - } - - // Cloud specific stuff - public string AssetId - { - get { return m_AssetId; } - } - - public string TiltFileUrl - { - get { return m_TiltFileUrl; } - } - - public string IconUrl - { - get { return m_IconUrl; } - } - - public string License - { - get { return m_License; } - } - - // Path to the locally downloaded .tilt file - public string TiltPath - { - get { return m_localTiltFile; } - set { m_localTiltFile = value; } - } - - // Path to the locally downloaded icon - public string IconPath - { - get { return m_localIcon; } - set { m_localIcon = value; } - } - - public bool IconDownloaded - { - get { return m_IconDownloaded; } - set { m_IconDownloaded = value; } - } - - public bool TiltDownloaded - { - get { return m_DownloadedFile != null; } - set - { - if (value) - { - m_DownloadedFile = new TiltFile(m_localTiltFile); - } - else - { - m_DownloadedFile = null; - } - } - } - - // Not part of the interface - public int? TriangleCount => m_GltfTriangleCount; - - public Stream GetReadStream(string subfileName) - { - return m_DownloadedFile.GetReadStream(subfileName); - } - - // Not part of the interface - public int GltfTriangleCount => m_GltfTriangleCount; - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using UnityEngine; +using UnityEngine.Networking; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Newtonsoft.Json.Linq; + +namespace TiltBrush +{ + + // TODO: Specify tag for which sketches to query (curated, liked etc.) + public class IcosaSketchSet : SketchSet + { + + const int kDownloadBufferSize = 1024 * 1024; // 1MB + + // Downloading is handled by IcosaSketchSet which will set the local paths + + private class IcosaSketch : Sketch + { + // This value holds the count of sketches that were downloaded by the sketch set + // before this one. It's used during our sort to retain order from Icosa, while + // allowing a custom sort on top. + public int m_DownloadIndex; + private IcosaSceneFileInfo m_FileInfo; + private Texture2D m_Icon; + + public SceneFileInfo SceneFileInfo + { + get { return m_FileInfo; } + } + + public string[] Authors + { + get + { + return m_FileInfo.Author != null + ? new string[] { m_FileInfo.Author } + : new string[] { }; + } + } + + public Texture2D Icon + { + get { return m_Icon; } + set { m_Icon = value; } + } + + public bool IconAndMetadataValid + { + get { return m_Icon != null; } + } + + public IcosaSketch(IcosaSceneFileInfo info) + { + m_FileInfo = info; + } + + public void UnloadIcon() + { + UnityEngine.Object.Destroy(m_Icon); + m_Icon = null; + } + + // Not part of the interface + public IcosaSceneFileInfo IcosaSceneFileInfo + { + get { return m_FileInfo; } + } + } + + private const string kIntroSketchAssetId = "agqmaia62KE"; + private const int kIntroSketchIndex = 1; + + private List m_Sketches; + private Dictionary m_AssetIds; + private int m_TotalCount = 0; + private VrAssetService m_AssetService; + private MonoBehaviour m_Parent; + private bool m_Ready; + private bool m_RefreshRequested; + private bool m_NeedsLogin; + private bool m_LoggedIn; + private bool m_ActivelyRefreshingSketches; + private int m_MaximumSceneTriangles; + + private SketchSetType m_Type; + private string m_CacheDir; + private Coroutine m_RefreshCoroutine; + private float m_CooldownTimer; + private List m_RequestedIcons = new List(); + private Coroutine m_TextureLoaderCoroutine; + + public SketchSetType Type { get { return m_Type; } } + + public VrAssetService VrAssetService + { + set { m_AssetService = value; } + } + + public IcosaSketchSet(MonoBehaviour parent, SketchSetType type, int maxSceneTriangles, + bool needsLogin = false) + { + m_Parent = parent; + m_Sketches = new List(); + m_AssetIds = new Dictionary(); + m_Ready = false; + m_RefreshRequested = false; + m_Type = type; + m_NeedsLogin = needsLogin; + m_MaximumSceneTriangles = maxSceneTriangles; + } + + public void Init() + { + VrAssetService = VrAssetService.m_Instance; + m_LoggedIn = false; + m_RefreshRequested = true; + m_CooldownTimer = VrAssetService.m_Instance.m_SketchbookRefreshInterval; + } + + public bool IsReadyForAccess + { + get { return m_Ready; } + } + + public bool IsActivelyRefreshingSketches + { + get { return m_ActivelyRefreshingSketches; } + private set + { + m_ActivelyRefreshingSketches = value; + OnSketchRefreshingChanged(); + } + } + + public bool RequestedIconsAreLoaded + { + get { return false; } + } + + public int NumSketches + { + get { return m_TotalCount; } + } + + public void NotifySketchCreated(string fullpath) { } + + public void NotifySketchChanged(string fullpath) { } + + public void RequestRefresh() + { + m_RefreshRequested = true; + } + + public bool IsSketchIndexValid(int index) + { + return index >= 0 && index < m_TotalCount; + } + + public void RequestOnlyLoadedMetadata(List requests) + { + // Stop any current texture loading + if (m_TextureLoaderCoroutine != null) + { + m_Parent.StopCoroutine(m_TextureLoaderCoroutine); + m_TextureLoaderCoroutine = null; + } + // Nuke textures on all icons + foreach (var sketch in m_Sketches) + { + sketch.UnloadIcon(); + } + m_RequestedIcons.Clear(); + m_RequestedIcons.AddRange(requests); + m_TextureLoaderCoroutine = m_Parent.StartCoroutine(TextureLoaderCoroutine()); + } + + public bool GetSketchIcon(int iSketchIndex, out Texture2D icon, out string[] authors, + out string description) + { + description = null; + if (iSketchIndex >= m_Sketches.Count) + { + icon = null; + authors = null; + return false; + } + IcosaSketch sketch = m_Sketches[iSketchIndex]; + icon = sketch.Icon; + authors = sketch.Authors; + return icon != null; + } + + public SceneFileInfo GetSketchSceneFileInfo(int i) + { + return i < m_Sketches.Count ? m_Sketches[i].SceneFileInfo : null; + } + + public string GetSketchName(int i) + { + return i < m_Sketches.Count ? m_Sketches[i].SceneFileInfo.HumanName : null; + } + + public void RenameSketch(int toRename, string newName) + { + throw new NotImplementedException(); + } + + public void PrecacheSketchModels(int i) + { + if (i < m_Sketches.Count) + { + App.IcosaAssetCatalog.PrecacheModels(m_Sketches[i].SceneFileInfo, $"IcosaSketchSet {i}"); + } + } + + public void DeleteSketch(int toDelete) + { + throw new NotImplementedException(); + } + + public void Update() + { + if (!VrAssetService.m_Instance.Available) + { + return; + } + + if (m_NeedsLogin) + { + bool loggedIn = App.IcosaIsLoggedIn; + if (loggedIn != m_LoggedIn) + { + m_LoggedIn = loggedIn; + if (!loggedIn) + { + if (m_RefreshCoroutine != null) + { + m_Parent.StopCoroutine(m_RefreshCoroutine); + IsActivelyRefreshingSketches = false; + m_RefreshCoroutine = null; + m_Sketches.Clear(); + m_AssetIds.Clear(); + m_TotalCount = 0; + m_CacheDir = null; + OnChanged(); + } + } + else + { + // Lie about refreshing. The refresh coroutine will happen in time, but + // for the user, we want them to know something will happen [soon]. + IsActivelyRefreshingSketches = true; + m_RefreshCoroutine = m_Parent.StartCoroutine(PeriodicRefreshCoroutine()); + } + } + } + else if (m_RefreshRequested && m_RefreshCoroutine == null) + { + m_RefreshCoroutine = m_Parent.StartCoroutine(PeriodicRefreshCoroutine()); + } + } + + public event Action OnChanged = delegate { }; + + public event Action OnSketchRefreshingChanged = delegate { }; + + private IEnumerator PeriodicRefreshCoroutine() + { + while (true) + { + if (m_RefreshRequested) + { + yield return Refresh(); + m_RefreshRequested = false; + + // Don't poll the showcase + if (Type == SketchSetType.Curated) + { + yield break; + } + m_CooldownTimer = VrAssetService.m_Instance.m_SketchbookRefreshInterval; + } + else + { + yield return null; + } + while (m_CooldownTimer > 0.0f) + { + m_CooldownTimer -= Time.deltaTime; + yield return null; + } + } + } + + // Update our state from the cloud. + // There are three stages to this, each with a separate coroutine: + // 1: Pull all the metadata from the server and populate m_Sketches + // If there are changes: + // 2: Download any thumbnails and tilt files we don't already have + // 3: Clean up any cached files that are no longer referenced + private IEnumerator Refresh() + { + if (m_NeedsLogin && !m_LoggedIn) + { + m_Sketches.Clear(); + m_AssetIds.Clear(); + m_TotalCount = 0; + m_CacheDir = null; + OnChanged(); + yield break; + } + + if (m_CacheDir == null) + { + m_CacheDir = CacheDir(Type); + try + { + Directory.CreateDirectory(m_CacheDir); + } + catch (Exception ex) + { + // Most of the system exceptions returned by CreateDirectory are just things the user needs + // to fix, and may well be sorted by reinstalling. If it's an ArgumentNullExeption or not a + // SystemException, we need to know about it, so log the exception. + if (!(ex is SystemException) || ex is ArgumentNullException) + { + Debug.LogException(ex); + } + ControllerConsoleScript.m_Instance.AddNewLine( + $"Error creating cache directory. Try restarting {App.kAppDisplayName}.\n" + + $"If this error persists, try reinstalling {App.kAppDisplayName}.", true); + yield break; + } + } + + // While we're fetching metadata, hold a flag so we can message state in the UI. + IsActivelyRefreshingSketches = true; + yield return PopulateSketchesCoroutine(); + IsActivelyRefreshingSketches = false; + } + + // Fetch asset metadata from server and populate m_Sketches + private IEnumerator PopulateSketchesCoroutine() + { + // Replacement data structures that will be populated with incoming data. These + // temporary structures will be copied to the "live" structures in chunks, so + // beware of modifications that may reference incomplete data. + List sketches = new List(); + Dictionary assetIds = new Dictionary(); + + bool fromEmpty = m_AssetIds.Count == 0; + int loadSketchCount = 0; + + AssetLister lister = null; + List infos = new List(); + + // If we don't have a connection to Icosa and we're querying the Showcase, use + // the json metadatas stored in resources, instead of trying to get them from Icosa. + if (VrAssetService.m_Instance.m_UseLocalFeaturedSketches && m_Type == SketchSetType.Curated) + { + TextAsset[] textAssets = + Resources.LoadAll(SketchCatalog.kDefaultShowcaseSketchesFolder); + for (int i = 0; i < textAssets.Length; ++i) + { + JObject jo = JObject.Parse(textAssets[i].text); + infos.Add(new IcosaSceneFileInfo(jo)); + } + } + else + { + lister = m_AssetService.ListAssets(Type); + } + + bool changed = false; + int pagesFetched = 0; + while (lister == null || lister.HasMore) + { + if (sketches.Count >= 180) + { + break; // Don't allow the sketchbook to become too big. + } + + // lister will be null if we can't connect to Icosa and we've pre-populated infos + // with data from Resources. + if (lister != null) + { + using (var cr = lister.NextPage(infos)) + { + while (true) + { + try + { + if (!cr.MoveNext()) + { + break; + } + } + catch (VrAssetServiceException e) + { + ControllerConsoleScript.m_Instance.AddNewLine(e.UserFriendly); + Debug.LogException(e); + yield break; + } + yield return cr.Current; + } + } + } + if (infos.Count == 0) + { + break; + } + if (m_CacheDir == null) + { + yield break; + } + // Only cull the curated sketches. If a user likes a sketch that's very high poly count, + // there's no feedback to tell them why it didn't show up in their list. b/123649719 + if (m_Type == SketchSetType.Curated) + { + infos = infos.Where(x => x.GltfTriangleCount < m_MaximumSceneTriangles).ToList(); + } + if (m_Type == SketchSetType.Curated && !assetIds.Keys.Contains(kIntroSketchAssetId)) + { + yield return VrAssetService.m_Instance.InsertSketchInfo( + kIntroSketchAssetId, kIntroSketchIndex, infos); + } + for (int i = 0; i < infos.Count; i++) + { + IcosaSceneFileInfo info = infos[i]; + IcosaSketch sketch; + if (m_AssetIds.TryGetValue(info.AssetId, out sketch)) + { + // We already have this sketch + } + else + { + sketch = new IcosaSketch(info); + sketch.m_DownloadIndex = loadSketchCount++; + // Set local paths + info.TiltPath = Path.Combine(m_CacheDir, String.Format("{0}.tilt", info.AssetId)); + info.IconPath = Path.Combine(m_CacheDir, String.Format("{0}.png", info.AssetId)); + changed = true; + } + if (assetIds.ContainsKey(info.AssetId)) + { + Debug.Log($"Duplicate {info.HumanName} {info.AssetId}"); + Debug.LogErrorFormat("VR Asset Service has returned two objects with asset id '{0}'.", + info.AssetId); + } + else + { + Debug.Log($"Adding {info.HumanName} {info.AssetId}"); + sketches.Add(sketch); + assetIds.Add(info.AssetId, sketch); + } + } + infos.Clear(); + + // Only download the files with every other page, otherwise the sketch pages change too often, + // Which results in a bad user experience. + if ((++pagesFetched & 1) == 0 || lister == null || !lister.HasMore) + { + if (Type == SketchSetType.Curated) + { + sketches.Sort(CompareSketchesByTriangleCountAndDownloadIndex); + } + + // If populating the set from new then show stuff as it comes in. + // (We don't have to worry about anything being removed) + if (fromEmpty) + { + yield return DownloadFilesCoroutine(sketches); + RemoveFailedDownloads(sketches); + // Copying sketches to m_Sketches before sketches has completed populating is a bit + // dangerous, but as long as they're copied and then listeners are notified + // immediately afterward with OnChanged(), there data should be stable. + m_TotalCount = sketches.Count; + m_Sketches = new List(sketches); + m_AssetIds = assetIds; + m_Ready = true; + OnChanged(); + } + } + } + + if (!fromEmpty) + { + // Find anything that was removed + int removed = m_Sketches.Count(s => !assetIds.ContainsKey(s.IcosaSceneFileInfo.AssetId)); + if (removed > 0) + { + changed = true; + } + + // Download new files before we notify our listeners that we've got new stuff for them. + if (changed) + { + yield return DownloadFilesCoroutine(sketches); + RemoveFailedDownloads(sketches); + } + + // DeleteOldSketchesCoroutine relies on m_AssetIds being up to date, so set these before + // we try to cull the herd. + m_AssetIds = assetIds; + if (changed) + { + yield return DeleteOldSketchesCoroutine(); + } + + // Set new data live + m_TotalCount = sketches.Count; + m_Sketches = new List(sketches); + m_Ready = true; + if (changed) + { + OnChanged(); + } + } + else + { + // On first run populate, take the time to clean out any cobwebs. + // Note that this is particularly important for the curated sketches, which do not have + // a periodic refresh, meaning old sketches will never been deleted in the other path. + yield return DeleteOldSketchesCoroutine(); + OnChanged(); + } + } + + // If we have not managed to download a tilt file or its icon, we should remove it from the + // sketches list so as not to confuse the user. + private void RemoveFailedDownloads(List sketches) + { + sketches.RemoveAll(x => !x.IcosaSceneFileInfo.TiltDownloaded || + !x.IcosaSceneFileInfo.IconDownloaded); + } + + // Download tilt files and thumbnails (that we don't already have) + private IEnumerator DownloadFilesCoroutine(List sketches) + { + bool notifyOnError = true; + void NotifyCreateError(IcosaSceneFileInfo sceneFileInfo, string type, Exception ex) + { + string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}."; + ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); + notifyOnError = false; + Debug.LogException(ex); + Debug.LogError($"{sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); + } + + void NotifyWriteError(IcosaSceneFileInfo sceneFileInfo, string type, UnityWebRequest www) + { + string error = $"Error downloading {type} file for {sceneFileInfo.HumanName}.\n" + + "Out of disk space?"; + ControllerConsoleScript.m_Instance.AddNewLine(error, notifyOnError); + notifyOnError = false; + Debug.LogError($"{www.error} {sceneFileInfo.HumanName} {sceneFileInfo.TiltPath}"); + } + + byte[] downloadBuffer = new byte[kDownloadBufferSize]; + // Load the icons first, then the thumbnails + foreach (IcosaSketch sketch in sketches) + { + IcosaSceneFileInfo sceneFileInfo = sketch.IcosaSceneFileInfo; + // TODO(b/36270116): Check filesizes when Icosa can give it to us to detect incomplete downloads + if (!sceneFileInfo.IconDownloaded) + { + if (File.Exists(sceneFileInfo.IconPath)) + { + sceneFileInfo.IconDownloaded = true; + } + else + { + using (UnityWebRequest www = UnityWebRequest.Get(sceneFileInfo.IconUrl)) + { + DownloadHandlerFastFile downloadHandler; + try + { + downloadHandler = new DownloadHandlerFastFile(sceneFileInfo.IconPath, downloadBuffer); + } + catch (Exception ex) + { + NotifyCreateError(sceneFileInfo, "icon", ex); + continue; + } + www.downloadHandler = downloadHandler; + yield return www.SendWebRequest(); + if (www.isNetworkError || www.responseCode >= 400 || !string.IsNullOrEmpty(www.error)) + { + NotifyWriteError(sceneFileInfo, "icon", www); + } + else + { + sceneFileInfo.IconDownloaded = true; + } + } + } + } + yield return null; + } + + foreach (IcosaSketch sketch in sketches) + { + IcosaSceneFileInfo sceneFileInfo = sketch.IcosaSceneFileInfo; + if (!sceneFileInfo.TiltDownloaded) + { + if (File.Exists(sceneFileInfo.TiltPath)) + { + sceneFileInfo.TiltDownloaded = true; + } + else + { + using (UnityWebRequest www = UnityWebRequest.Get(sceneFileInfo.TiltFileUrl)) + { + DownloadHandlerFastFile downloadHandler; + try + { + downloadHandler = new DownloadHandlerFastFile(sceneFileInfo.TiltPath, downloadBuffer); + } + catch (Exception ex) + { + NotifyCreateError(sceneFileInfo, "sketch", ex); + continue; + } + www.downloadHandler = downloadHandler; + yield return www.SendWebRequest(); + if (www.isNetworkError || www.responseCode >= 400 || !string.IsNullOrEmpty(www.error)) + { + NotifyWriteError(sceneFileInfo, "sketch", www); + } + else + { + sceneFileInfo.TiltDownloaded = true; + } + } + } + } + yield return null; + } + yield return null; + } + + // Delete any files that aren't referenced in m_Sketches (actually m_AssetIds) + // Does this on a background thread so prevent hitches. + private IEnumerator DeleteOldSketchesCoroutine() + { + if (m_CacheDir != null) + { + var task = new Future(() => + { + var unknown = new DirectoryInfo(m_CacheDir).GetFiles().Where( + f => !m_AssetIds.ContainsKey(Path.GetFileNameWithoutExtension(f.Name))); + foreach (var f in unknown) + { + f.Delete(); + } + return true; + }); + + bool unused; + while (!task.TryGetResult(out unused)) + { + yield return null; + } + } + } + + // Read the icon textures for all sketches in m_RequestedIcons + private IEnumerator TextureLoaderCoroutine() + { + while (m_RequestedIcons.Count > 0) + { + foreach (int i in m_RequestedIcons) + { + IcosaSketch sketch = m_Sketches[i]; + string path = sketch.IcosaSceneFileInfo.IconPath; + if (sketch.IcosaSceneFileInfo.IconDownloaded) + { + byte[] data = File.ReadAllBytes(path); + Texture2D t = new Texture2D(2, 2); + t.LoadImage(data); + sketch.Icon = t; + } + yield return null; + } + m_RequestedIcons.RemoveAll(i => m_Sketches[i].Icon != null); + } + } + + private static string CacheDir(SketchSetType type) + { + switch (type) + { + case SketchSetType.Liked: + { + string id = App.IcosaUserId; + return Path.Combine(Application.persistentDataPath, String.Format("users/{0}/liked", id)); + } + case SketchSetType.Curated: + return Path.Combine(Application.persistentDataPath, "Curated Sketches"); + default: + return null; + } + } + + // When we sort the sketches, we sort them into buckets while retaining order within those + // buckets. We bucket the sketches using the gltf triangle count, but how we bucket depends on + // whether we are sorting the liked sketches or the curated sketches. + // Liked sketches get split into normal, complex (requires a warning), and impossible. + // Curated sketches get bucketed by the nearest 100,000 triangles. + private static int CompareSketchesByTriangleCountAndDownloadIndex(IcosaSketch a, IcosaSketch b) + { + int compareResult = CloudSketchComplexityBucket(a).CompareTo(CloudSketchComplexityBucket(b)); + + // If both sketches are in the same grouping, sort them relative to download index. + if (compareResult == 0) + { + return a.m_DownloadIndex.CompareTo(b.m_DownloadIndex); + } + + return compareResult; + } + + // Buckets the sketches into buckets 100000 tris in size. + private static int CloudSketchComplexityBucket(IcosaSketch s) + { + return s.IcosaSceneFileInfo.GltfTriangleCount / 100000; + } + } + + public class IcosaSceneFileInfo : SceneFileInfo + { + + // Asset + private string m_AssetId; + private string m_HumanName; + private string m_License; + + private string m_localTiltFile; + private string m_localIcon; + private string m_TiltFileUrl; + private string m_IconUrl; + private int m_GltfTriangleCount; + + private TiltFile m_DownloadedFile; + private bool m_IconDownloaded; + + public bool IsValid => m_TiltFileUrl != null; + + // Populate metadata from the JSON returned by Icosa for a single asset + // See go/vr-assets-service-api + public IcosaSceneFileInfo(JToken json) + { + m_AssetId = json["id"].ToString(); + m_HumanName = json["name"]?.ToString() ?? "Untitled"; + + var format = json["formats"]?.FirstOrDefault(x => x["format"].ToString() == "TILT")?["url"]; + m_TiltFileUrl = format?.ToString(); + m_IconUrl = json["thumbnail"]?.ToString(); + m_License = json["visibility"]?.ToString() ?? "PRIVATE"; + Author = json["ownername"]?.ToString() ?? "Unknown Author"; + + // TODO + m_GltfTriangleCount = 1; + // // Some assets (old ones? broken ones?) are missing the "formatComplexity" field + // var gltfFormat = json["formats"].First(x => x["format"].ToString() == "GLTF"); + // string gltfTriCount = gltfFormat?["formatComplexity"]?["triangleCount"]?.ToString(); + // if (gltfTriCount == null) + // { + // Debug.Log($"{m_AssetId} has no tricount"); + // } + // m_GltfTriangleCount = Int32.Parse(gltfTriCount ?? "1"); + + m_DownloadedFile = null; + m_IconDownloaded = false; + } + + public override string ToString() + { + return $"CloudFile {AssetId} file {TiltPath}"; + } + + public FileInfoType InfoType + { + get { return FileInfoType.Cloud; } + } + + public string HumanName + { + get { return m_HumanName; } + } + + // Allow setting since it is not in the asset json object itself + public string Author { get; set; } + + public bool Valid + { + get { return true; } + } + + public bool Available + { + get { return m_DownloadedFile != null; } + } + + public string FullPath + { + get { return m_localTiltFile; } + } + + public bool Exists + { + get { return true; } + } + + public bool ReadOnly + { + get { return true; } + } + + public string SourceId + { + get { return null; } + } + + public void Delete() + { + throw new NotImplementedException(); + } + + public string Rename(string newName) + { + throw new NotImplementedException(); + } + + public bool IsHeaderValid() + { + // Assume it's valid until we download it + return true; + } + + // Cloud specific stuff + public string AssetId + { + get { return m_AssetId; } + } + + public string TiltFileUrl + { + get { return m_TiltFileUrl; } + } + + public string IconUrl + { + get { return m_IconUrl; } + } + + public string License + { + get { return m_License; } + } + + // Path to the locally downloaded .tilt file + public string TiltPath + { + get { return m_localTiltFile; } + set { m_localTiltFile = value; } + } + + // Path to the locally downloaded icon + public string IconPath + { + get { return m_localIcon; } + set { m_localIcon = value; } + } + + public bool IconDownloaded + { + get { return m_IconDownloaded; } + set { m_IconDownloaded = value; } + } + + public bool TiltDownloaded + { + get { return m_DownloadedFile != null; } + set + { + if (value) + { + m_DownloadedFile = new TiltFile(m_localTiltFile); + } + else + { + m_DownloadedFile = null; + } + } + } + + // Not part of the interface + public int? TriangleCount => m_GltfTriangleCount; + + public Stream GetReadStream(string subfileName) + { + return m_DownloadedFile.GetReadStream(subfileName); + } + + // Not part of the interface + public int GltfTriangleCount => m_GltfTriangleCount; + } +} // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/PolySketchSet.cs.meta b/Assets/Scripts/Sharing/IcosaSketchSet.cs.meta similarity index 100% rename from Assets/Scripts/Sharing/PolySketchSet.cs.meta rename to Assets/Scripts/Sharing/IcosaSketchSet.cs.meta diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 42a463c579..bf3884cc9f 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -1,952 +1,951 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using Org.OpenAPITools.Api; -using Org.OpenAPITools.Client; -using UnityEngine; -using UnityEngine.Networking; - -namespace TiltBrush -{ - - /// Back-end for cloud upload - /// These values might be serialized into prefabs; do not change them! - /// Make serializable so they don't get mangled by obfuscation. - [Serializable] - public enum Cloud - { - None, - Poly, - Sketchfab, - Icosa - } - - [Serializable] - public enum VrAssetFormat - { - Unknown, - TILT, - GLTF, - GLTF2, - } - - public class VrAssetService : MonoBehaviour - { - // Constants - - const string kDefaultName = "sketch"; - - private string kApiHost => App.ICOSA_API_URL; - private string kAssetLandingPage => $"{App.ICOSA_WEBSITE_URL}/uploads"; - - private const string kListAssetsUri = "/v1/assets"; - private const string kUserAssetsUri = "/v1/users/me/assets"; - private const string kUserLikesUri = "/v1/users/me/likedassets"; - private const string kGetVersionUri = "/$discovery/rest?version=v1"; - - public static string kIcosaApiKey => App.Config.GoogleSecrets?.ApiKey; - - public const string kCreativeCommonsLicense = "CREATIVE_COMMONS_BY"; - - // Icosa API used by Tilt Brush. - // If Icosa doesn't support this version, don't try to talk to Icosa and prompt the user to upgrade. - private const string kIcosaApiVersion = "v1"; - - /// Change-of-basis transform - public static readonly TrTransform kIcosaFromUnity; - - private static Dictionary kGltfMimetypes = new Dictionary - { - { ".gltf", "model/gltf+json" }, - { ".bin", "application/octet-stream" }, - { ".glsl", "text/plain" }, - { ".bmp", "image/bmp" }, - { ".jpeg", "image/jpeg" }, - { ".jpg", "image/jpeg" }, - { ".png", "image/png" }, - { ".tilt", "application/octet-stream" } - }; - - // For progress reporting - private enum UploadStep - { - CreateGltf, - CreateTilt, - ZipElements, - UploadElements, - UpdateAssetData, - Done - } - - // These are progress values at the start of each step. - // TODO(b/146892613): have a different set for Icosa vs Sketchfab? - private static double[] kProgressSteps = - { - 0.01, // UploadStep.CreateGltf -- progress > 0 means we have begun - 0.25, // UploadStep.CreateTilt - 0.3, // UploadStep.ZipElements - 0.5, // UploadStep.UploadElements - 0.95, // UploadStep.UpdateAssetData - 1, // UploadStep.Done - 1 // - }; - - // Classes and types - - class StreamWithReadProgress : WrappedStream - { - public int TotalRead { get; private set; } - public StreamWithReadProgress(string filename) - { - SetWrapped(File.OpenRead(filename), ownsStream: true); - } - public override int Read(byte[] buffer, int offset, int count) - { - var amountRead = base.Read(buffer, offset, count); - TotalRead += amountRead; - return amountRead; - } - public override int ReadByte() - { - var amountRead = base.ReadByte(); - TotalRead += amountRead; - return amountRead; - } - } - - /// A bag of information about a pending upload - class UploadInfo : IProgress - { - // Only one of sourceFile or sourceData is valid - public readonly FileInfo m_sourceFile; - public readonly byte[] m_sourceData; - public readonly string m_remoteName; - public readonly long m_length; - public string m_elementId; - public double m_uploadPercent = 0; - - public UploadInfo(string sourceFile, string remoteName) - { - m_sourceFile = new FileInfo(sourceFile); - m_remoteName = remoteName; - m_length = m_sourceFile.Length; - } - - public UploadInfo(byte[] data, string remoteName) - { - m_sourceData = data; - m_remoteName = remoteName; - m_length = data.Length; - } - - public void SetUploaded(string elementId) - { - m_elementId = elementId; - } - - /// Allocates and returns a new Stream. - /// Caller is responsible for disposing of it. - public Stream OpenStream() - { - if (m_sourceData != null) - { - return new MemoryStream(m_sourceData); - } - else - { - return m_sourceFile.OpenRead(); - } - } - - void IProgress.Report(double value) - { - m_uploadPercent = value; - } - } - - /// Creates and then cleans up a uniquely-named temporary directory - class TemporaryUploadDirectory : IDisposable - { - public string Value { get; } - - public TemporaryUploadDirectory() - { -#if UNITY_EDITOR - if (App.Config && App.Config.m_DebugUpload) - { - // Delay deleting the directory until the next upload - string dirName = Path.Combine(Application.temporaryCachePath, "Upload"); - if (Directory.Exists(dirName)) - { - try { Directory.Delete(dirName, true); } - catch (Exception e) { Debug.LogException(e); } - } - } -#endif - Value = FileUtils.GenerateNonexistentFilename( - Application.temporaryCachePath, "Upload", ""); - string failureMessage = $"Can't create upload directory: {Value}"; - bool dirCreated = FileUtils.InitializeDirectoryWithUserError(Value, failureMessage); - if (!dirCreated) - { - throw new VrAssetServiceException(failureMessage); - } - } - - public void Dispose() - { -#if UNITY_EDITOR - if (App.Config && App.Config.m_DebugUpload) - { - // Delay deleting the directory until the next upload - return; - } -#endif - if (Directory.Exists(Value)) - { - Directory.Delete(Value, true); - } - } - } - - // Static API - - public static VrAssetService m_Instance; - - // Currently this always returns the standard API host when running unit tests - public string ApiHost - { - get - { - string cfg = App.UserConfig?.Sharing.VrAssetServiceHostOverride; - if (!string.IsNullOrEmpty(cfg)) { return cfg; } - return kApiHost; - } - } - - /// Returns true if Icosa would accept a PATCH of the specified asset - /// from the specified user. - /// - /// Pass: - /// type - - /// Where you found the assetId. Necessary because of some shortcuts - /// taken by the implementation. - /// userId - Icosa user id of the currently-logged-in OAuth user. Get it - /// with GetAccountIdAsync(). - public static async Task IsMutableAssetIdAsync( - FileInfoType type, string assetId, string userId, string apiHost) - { - if (assetId == null) { return false; } - // There are too many assumptions here -- for both cloud and disk, the logic - // should be "is it owned by me and unpublished? then it's mutable" - if (type == FileInfoType.Cloud) - { - // Assumption: cloud files are immutable. - // You can't "Like" an unpublished asset; and published assets are immutable. - return false; - } - else if (type == FileInfoType.Disk) - { - // Assumption: this asset is mutable because it's unlikely for a cloud-based .tilt - // to be in the user's Sketches/ folder. Local sketches always become un-published - // and mutable assets when uploaded (remember, publishing makes a copy). - // If someone grabbed a .tilt from their Icosa asset cache and put it in Sketches/ - // that would break this assumption and I'm not sure what would happen. - if (userId == null) { return false; } - // It's mutable, but check whether it's mutable by _us_. - try - { - // The null == null case is handled earlier - WebRequest request = new WebRequest( - $"{apiHost}{kListAssetsUri}/{assetId}?key={kIcosaApiKey}", - App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); - return (await request.SendAsync()).JObject?["accountId"].ToString() == userId; - } - catch (VrAssetServiceException) - { - return false; - } - } - else - { - throw new InvalidOperationException($"Unknown FileInfoType {type}"); - } - } - - // Instance API - - static VrAssetService() - { - Matrix4x4 polyFromUnity = AxisConvention.GetFromUnity(AxisConvention.kGltfAccordingToIcosa); - - // Provably non-lossy: the mat4 is purely TRS, and the S is uniform - kIcosaFromUnity = TrTransform.FromMatrix4x4(polyFromUnity); - } - - // Instance API - - [SerializeField] private int m_AssetsPerPage; - [SerializeField] public float m_SketchbookRefreshInterval; - - private float m_UploadProgress; - private bool m_LastUploadFailed; - private string m_LastUploadErrorMessage; - private string m_LastUploadErrorDetails; - private bool m_UserCanceledLastUpload; - private string m_LastUploadCompleteUrl; - TaskAndCts<(string url, long bytes)> m_UploadTask = null; - - // Icosa account id associated with the Google identity - private string m_IcosaAccountId; - - private enum IcosaStatus - { - Ok, - Disabled, - NoConnection - } - private IcosaStatus m_IcosaStatus; - - public bool Available => m_IcosaStatus == IcosaStatus.Ok; - - public bool NoConnection => m_IcosaStatus == IcosaStatus.NoConnection; - - public float UploadProgress => m_UploadProgress; - - public bool LastUploadFailed - { - get { return m_LastUploadFailed; } - } - - public string LastUploadErrorMessage - { - get { return m_LastUploadErrorMessage; } - } - - public string LastUploadErrorDetails - { - get { return m_LastUploadErrorDetails; } - } - - public bool UserCanceledLastUpload - { - get { return m_UserCanceledLastUpload; } - } - - public string LastUploadCompleteUrl - { - get { return m_LastUploadCompleteUrl; } - } - - private string AssetLandingPage - { - get - { - string cfg = App.UserConfig.Sharing.VrAssetServiceUrlOverride; - if (!string.IsNullOrEmpty(cfg)) { return cfg; } - return kAssetLandingPage; - } - } - - // Cannot be an UploadProgress setter because the getter's type is different. - // pct is how much of that step has been completed. - private void SetUploadProgress(UploadStep step, double pct) - { - var step0 = (float)kProgressSteps[Mathf.Min(kProgressSteps.Length - 1, (int)step)]; - var step1 = (float)kProgressSteps[Mathf.Min(kProgressSteps.Length - 1, (int)step + 1)]; - m_UploadProgress = Mathf.Lerp(step0, step1, (float)pct); - } - - void Awake() - { - m_Instance = this; - } - - void Start() - { - if (!string.IsNullOrEmpty(App.UserConfig.Sharing.VrAssetServiceHostOverride) || - !string.IsNullOrEmpty(App.UserConfig.Sharing.VrAssetServiceUrlOverride)) - { - Debug.LogFormat("Overriding VrAssetService Api Host: {0} Landing Page: {1}", - ApiHost, AssetLandingPage); - } - - // If auto profiling is enabled, disable automatic Icosa downloading. - if (!App.UserConfig.Profiling.AutoProfile) - { - VerifyIcosaConnectionAndCheckApiVersionAsync(); - } - else - { - m_IcosaStatus = IcosaStatus.Disabled; - } - } - - /// Consume the result of the previous upload (if any) - public void ConsumeUploadResults() - { - // Our UI interprets "progress >= 1.0" as "there are results and we must display them!" - // The UI should really be internally stateful, and only display these results when new results - // appear that haven't yet been shown to the user. The issue with this design is that there - // are other consumers of upload results. Thankfully, they are less important and don't - // care about progress; so the hack is that we only "consume" the progress. - if (m_UploadProgress >= 1) - { - m_UploadProgress = 0; - } - } - - /// If you try and upload a sketch while a sketch is already uploading, - /// the existing upload will be canceled. - public async Task UploadCurrentSketchAsync(Cloud backend, bool isDemoUpload) - { - // This function handles most of the setup and cleanup. - // The heavy lifting is split out into UploadCurrentSketchInternal. - - // Generic to all kinds of failures - void ReportFailure(string userFriendly, string fullMessage = "") - { - m_LastUploadFailed = true; - m_LastUploadErrorMessage = userFriendly; - m_LastUploadErrorDetails = fullMessage; - ControllerConsoleScript.m_Instance.AddNewLine(LastUploadErrorMessage, skipLog: true); - ControllerConsoleScript.m_Instance.AddNewLine(LastUploadErrorDetails, skipLog: true); - AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); - } - - Debug.LogFormat("UploadCurrentSketch(demo: {0})", isDemoUpload); - - // Cancel previous upload coroutine if necessary. - if (m_UploadTask != null) - { - CancelUpload(); - if (await Task.WhenAny(m_UploadTask.Task, Task.Delay(TimeSpan.FromSeconds(5))) != - m_UploadTask.Task) - { - // Definitely a bug we should fix; make it noisy because otherwise it just looks - // like nothing is happening. - OutputWindowScript.Error("Timed out waiting for canceled upload"); - return; - } - } - - - m_LastUploadFailed = false; - m_LastUploadErrorMessage = ""; - m_LastUploadErrorDetails = ""; - m_UserCanceledLastUpload = false; - m_UploadProgress = 0.0f; - - using (var tempUploadDir = new TemporaryUploadDirectory()) - try - { - if (!isDemoUpload) - { - App.Instance.SetDesiredState(App.AppState.Uploading); - } - AudioManager.m_Instance.UploadLoop(true); - var timer = System.Diagnostics.Stopwatch.StartNew(); - m_UploadTask = new TaskAndCts<(string url, long bytes)>(); - - switch (backend) - { - case Cloud.Icosa: - m_UploadTask.Task = UploadCurrentSketchIcosaAsync(m_UploadTask.Token, tempUploadDir.Value, - isDemoUpload); - break; - case Cloud.Sketchfab: - m_UploadTask.Task = UploadCurrentSketchSketchfabAsync(m_UploadTask.Token, tempUploadDir.Value, - isDemoUpload); - break; - } - var (url, totalUploadLength) = await m_UploadTask.Task; - m_LastUploadCompleteUrl = url; - ControllerConsoleScript.m_Instance.AddNewLine("Upload succeeded!"); - AudioManager.m_Instance.PlayUploadCompleteSound(InputManager.Wand.Transform.position); - PanelManager.m_Instance.GetAdminPanel().ActivatePromoBorder(true); - // Don't auto-open the URL on mobile because it steals focus from the user. - if (!isDemoUpload && !App.Config.IsMobileHardware && m_LastUploadCompleteUrl != null) - { - // Can't pass a string param because this is also called from mobile GUI - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.ViewLastUpload); - } - } - catch (VrAssetServiceException exception) - { - // "Expected" failures (40x, 50x, etc) - Debug.LogWarning("UploadCurrentSketch external error"); - Debug.LogException(exception); - ReportFailure(exception.UserFriendly, exception.Message); - } - catch (OperationCanceledException) - { - m_UserCanceledLastUpload = true; - ReportFailure("Upload canceled."); - } - catch (Exception exception) - { - // Unexpected failures -- ie, bugs on our part - Debug.LogError("UploadCurrentSketch internal error"); - ReportFailure("Upload failed.", exception.Message); - throw; - } - finally - { - // Cleanup - if (App.CurrentState == App.AppState.Uploading) - { - App.Instance.SetDesiredState(App.AppState.Standard); - } - - m_UploadTask = null; - AudioManager.m_Instance.UploadLoop(false); - // This is how the upload popup knows we're complete - m_UploadProgress = 1.0f; - } - } - - /// Request to cancel the upload -- does not happen synchronously. - public void CancelUpload() - { - m_UploadTask?.Cancel(); - } - - private async void VerifyIcosaConnectionAndCheckApiVersionAsync() - { - m_IcosaStatus = await GetIcosaStatus(); - } - - private async Task GetIcosaStatus() - { - // UserConfig override - if (App.UserConfig.Flags.DisableIcosa || App.Instance.IcosaToken == null) - { - return IcosaStatus.Disabled; - } - - string uri = ApiHost; - try - { - var api = new LoginApi($"{App.ICOSA_API_URL}"); - var result = new Dictionary { { "version", "v1" } }; // TODO: get version from API - string version = result["version"]; - if (version == kIcosaApiVersion) - { - return IcosaStatus.Ok; - } - else - { - Debug.LogWarning($"Icosa requires API {version} > {kIcosaApiVersion}"); - return IcosaStatus.Disabled; - } - } - catch (VrAssetServiceException e) - { - Debug.LogWarning($"Error connecting to Icosa: {e}"); - return IcosaStatus.NoConnection; - } - catch (Exception e) - { - Debug.LogError($"Internal error connecting to Icosa: {e}"); - return IcosaStatus.NoConnection; - } - } - - /// Returns a writable SceneFileInfo - private DiskSceneFileInfo GetWritableFile() - { - // hermetic gltf files currently don't work with AccessLevel.PRIVATE - SceneFileInfo currentFileInfo = SaveLoadScript.m_Instance.SceneFile; - - DiskSceneFileInfo fileInfo; - if (currentFileInfo.Valid) - { - if (currentFileInfo is DiskSceneFileInfo) - { - fileInfo = (DiskSceneFileInfo)currentFileInfo; - } - else - { - // This is a cloud sketch not saved before - fileInfo = SaveLoadScript.m_Instance.GetNewNameSceneFileInfo(); - } - } - else - { - // Save as a new file - fileInfo = SaveLoadScript.m_Instance.GetNewNameSceneFileInfo(); - } - return fileInfo; - } - - /// Returns a relative path R such that Join(fromDir, R) refers to toFile, or null on error. - /// Does not handle ".." paths. - public static string GetRelativePath(string fromDir, string toFile) - { - // Normalize paths so we can use plain string compares - var alt = Path.AltDirectorySeparatorChar; - var standard = Path.DirectorySeparatorChar; - if (alt != standard) - { - fromDir = fromDir.Replace(alt, standard); - toFile = toFile.Replace(alt, standard); - } - - int baseLen = fromDir.Length; - if (!toFile.StartsWith(fromDir)) { return null; } - if (toFile.Length <= baseLen) { return null; } - var sep = toFile[baseLen]; - if (sep != Path.DirectorySeparatorChar) - { - return null; - } - return toFile.Substring(baseLen + 1); - } - - private async Task CreateZipFileAsync( - string zipName, string rootDir, string[] paths, - CancellationToken token) - { - long totalLength = paths.Aggregate(0L, (acc, elt) => acc + new FileInfo(elt).Length) + 1; - long read = 1; - - using (var zip = File.OpenWrite(zipName)) - { - using (var archive = new ZipArchive(zip, ZipArchiveMode.Create)) - { - foreach (var path in paths) - { - string archivedName = GetRelativePath(rootDir, path); - if (archivedName == null) - { - Debug.LogWarning($"Ignoring {path} not under {rootDir}"); - continue; - } - ZipArchiveEntry entry = archive.CreateEntry(archivedName); - using (Stream writer = entry.Open()) - { - using (var reader = new StreamWithReadProgress(path)) - { - var task = reader.CopyToAsync(writer, 0x1_0000, token); - while (!task.IsCompleted) - { - long prev = reader.TotalRead; - await Awaiters.NextFrame; - read += reader.TotalRead - prev; - SetUploadProgress(UploadStep.ZipElements, read / (double)(totalLength)); - } - await task; - } - } - } - } - } - } - - // TODO: Refactor. This is largely the same as UploadCurrentSketchSketchFabAsync aside from a few url changes and the response. - private async Task<(string, long)> UploadCurrentSketchIcosaAsync( - CancellationToken token, string tempUploadDir, bool _) - { - DiskSceneFileInfo fileInfo = GetWritableFile(); - - var currentScene = SaveLoadScript.m_Instance.SceneFile; - string uploadName = currentScene.Valid ? currentScene.HumanName : kDefaultName; - string gltfUploadName = $"{uploadName}.gltf"; - - SetUploadProgress(UploadStep.CreateGltf, 0); - // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. - string gltfFile = Path.Combine(tempUploadDir, gltfUploadName); - var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( - OverlayType.Export, fadeDuration: 0.5f, - action: () => new ExportGlTF().ExportBrushStrokes( - gltfFile, - AxisConvention.kGltf2, binary: false, doExtras: true, - includeLocalMediaContent: true, gltfVersion: 2, - selfContained: true)); - if (!exportResults.success) - { - throw new VrAssetServiceException("Internal error creating upload data."); - } - - // Construct options to set the background color to the current environment's clear color. - Color bgColor = SceneSettings.m_Instance.CurrentEnvironment.m_RenderSettings.m_ClearColor; - IcosaService.Options options = null; - // options.SetBackgroundColor(bgColor); - - // TODO(b/146892613): we're not uploading this at the moment. Should we be? - // If we don't, we can probably remove this step...? - SetUploadProgress(UploadStep.CreateTilt, 0); - var thumbnail = await CreateTiltForUploadAsync(fileInfo); - token.ThrowIfCancellationRequested(); - - // Create a copy of the .tilt file in tempUploadDir. - string tempTiltPath = Path.Combine(tempUploadDir, $"{uploadName}.tilt"); - File.Copy(fileInfo.FullPath, tempTiltPath); - - // Save thumbnail as a png to temp path - string tempThumbnailPath = Path.Combine(tempUploadDir, "thumbnail.png"); - File.WriteAllBytes(tempThumbnailPath, thumbnail); - - // Collect files into a .zip file, including the .tilt file and thumbnail - string zipName = Path.Combine(tempUploadDir, "archive.zip"); - var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath).Append(tempThumbnailPath); - await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); - - var service = new IcosaService(App.Instance.IcosaToken); - var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); - var response = await service.CreateModel( - zipName, progress, token, options, tempUploadDir); - // TODO(b/146892613): return the UID and stick it into the .tilt file? - // Or do we not care since we aren't recording provenance and remixing - - // TODO(b/146892613): figure out this flow - // response.uri is not very useful; it is an API uri that gives you json of asset details. - // Also, the 3d-models URI might show that the asset is still processing. We can poll their - // API and find out when it's done and pop up the window then? - string uri = $"{App.ICOSA_WEBSITE_URL}/edit/{response.upload_job}"; - return (uri, 0); - } - - private async Task<(string, long)> UploadCurrentSketchSketchfabAsync( - CancellationToken token, string tempUploadDir, bool _) - { - DiskSceneFileInfo fileInfo = GetWritableFile(); - - SetUploadProgress(UploadStep.CreateGltf, 0); - // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. - string gltfFile = Path.Combine(tempUploadDir, $"{kDefaultName}.gltf"); - var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( - OverlayType.Export, fadeDuration: 0.5f, - action: () => new ExportGlTF().ExportBrushStrokes( - gltfFile, - AxisConvention.kGltf2, binary: false, doExtras: false, - includeLocalMediaContent: true, gltfVersion: 2, - // Sketchfab doesn't support absolute texture URIs - selfContained: true)); - if (!exportResults.success) - { - throw new VrAssetServiceException("Internal error creating upload data."); - } - - // Construct options to set the background color to the current environment's clear color. - Color bgColor = SceneSettings.m_Instance.CurrentEnvironment.m_RenderSettings.m_ClearColor; - SketchfabService.Options options = new SketchfabService.Options(); - options.SetBackgroundColor(bgColor); - - // TODO(b/146892613): we're not uploading this at the moment. Should we be? - // If we don't, we can probably remove this step...? - SetUploadProgress(UploadStep.CreateTilt, 0); - await CreateTiltForUploadAsync(fileInfo); - token.ThrowIfCancellationRequested(); - - // Create a copy of the .tilt file in tempUploadDir. - string tempTiltPath = Path.Combine(tempUploadDir, "sketch.tilt"); - File.Copy(fileInfo.FullPath, tempTiltPath); - - // Collect files into a .zip file, including the .tilt file. - string zipName = Path.Combine(tempUploadDir, "archive.zip"); - var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath); - await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); - var uploadLength = new FileInfo(zipName).Length; - - var service = new SketchfabService(App.SketchfabIdentity); - var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); - var response = await service.CreateModel( - fileInfo.HumanName, zipName, progress, token, options, tempUploadDir); - // TODO(b/146892613): return the UID and stick it into the .tilt file? - // Or do we not care since we aren't recording provenance and remixing - - // TODO(b/146892613): figure out this flow - // response.uri is not very useful; it is an API uri that gives you json of asset details. - // Also, the 3d-models URI might show that the asset is still processing. We can poll their - // API and find out when it's done and pop up the window then? - string uri = $"{SketchfabService.kModelLandingPage}{response.uid}"; - return (uri, uploadLength); - } - - /// Helper for UploadCurrentSketchXxxAsync - /// Writes the sketch to the passed fileInfo and returns a sketch thumbnail. - private async Task CreateTiltForUploadAsync(DiskSceneFileInfo fileInfo) - { - // Create and save snapshot. - SetUploadProgress(UploadStep.CreateTilt, 0); - SketchControlsScript.m_Instance.GenerateReplacementSaveIcon(); - SketchSnapshot snapshot = await SaveLoadScript.m_Instance.CreateSnapshotWithIconsAsync(); - snapshot.AssetId = fileInfo.AssetId; // FileInfo and snapshot must match - await SaveLoadScript.m_Instance.SaveSnapshot(fileInfo, snapshot: snapshot); - if (!File.Exists(fileInfo.FullPath)) - { - string exceptionMessage = "Internal error uploading .tilt."; - if (SaveLoadScript.m_Instance.LastWriteSnapshotError != null) - { - exceptionMessage += " Error: " + SaveLoadScript.m_Instance.LastWriteSnapshotError; - } - else - { - exceptionMessage += " No error message"; - } - - throw new VrAssetServiceException(exceptionMessage); - } - - byte[] thumbnail = SaveLoadScript.m_Instance.GetLastThumbnailBytes(); - if (thumbnail == null) - { - thumbnail = FileSketchSet.ReadThumbnail(fileInfo) ?? new byte[0]; - } - - return thumbnail; - } - - public AssetGetter GetAsset(string assetId, VrAssetFormat type, string reason) - { - string uri; - if (assetId.ToLower().StartsWith("https%3a%2f%2f") || assetId.ToLower().StartsWith("http%3a%2f%2f")) - { - uri = UnityWebRequest.UnEscapeURL(assetId); - } - else - { - uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, assetId, kIcosaApiKey); - } - return new AssetGetter(uri, assetId, type, reason); - } - - public AssetLister ListAssets(SketchSetType type) - { - string filter = null; - string errorMessage = null; - switch (type) - { - case SketchSetType.Liked: - if (!App.GoogleIdentity.LoggedIn) - { - return null; - } - filter = $"{kUserLikesUri}?format=TILT&orderBy=LIKED_TIME&key={kIcosaApiKey}"; - errorMessage = "Failed to access your liked sketches."; - break; - case SketchSetType.Curated: - if (string.IsNullOrEmpty(kIcosaApiKey)) - { - return null; - } - filter = $"{kListAssetsUri}?format=TILT&curated=true&orderBy=NEWEST&key={kIcosaApiKey}"; - errorMessage = "Failed to access featured sketches."; - break; - } - - string uri = $"{ApiHost}{filter}&pageSize={m_AssetsPerPage}"; - return new AssetLister(uri, errorMessage); - } - - // Get a specific sketch and insert it into the listed sketches at the specified index. - public IEnumerator InsertSketchInfo( - string assetId, int index, List infos) - { - string uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, assetId, kIcosaApiKey); - WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); - using (var cr = request.SendAsync().AsIeNull()) - { - while (!request.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException e) - { - Debug.LogException(e); - Debug.LogError("Failed to fetch sketch " + assetId); - yield break; - } - yield return cr.Current; - } - } - - Future f = new Future(() => JObject.Parse(request.Result)); - JObject json; - while (!f.TryGetResult(out json)) { yield return null; } - infos.Insert(index, new IcosaSceneFileInfo(json.Root)); - } - - public AssetLister ListAssets(IcosaSetType type) - { - string uri = null; - switch (type) - { - case IcosaSetType.Liked: - uri = $"{ApiHost}{kUserLikesUri}?format=GLTF2&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; - break; - case IcosaSetType.User: - uri = $"{ApiHost}{kUserAssetsUri}?format=GLTF2&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; - break; - case IcosaSetType.Featured: - uri = $"{ApiHost}{kListAssetsUri}?key={kIcosaApiKey}" + - $"&format=GLTF2&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; - break; - } - return new AssetLister(uri, "Failed to connect to Icosa."); - } - - // Download a tilt file to a temporary file and load it - public IEnumerator LoadTiltFile(string id) - { - string path = Path.GetTempFileName(); - string uri = String.Format("{0}{1}/{2}?key={3}", ApiHost, kListAssetsUri, id, kIcosaApiKey); - WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); - using (var cr = request.SendAsync().AsIeNull()) - { - while (!request.Done) - { - try - { - cr.MoveNext(); - } - catch (VrAssetServiceException) - { - yield break; - } - yield return cr.Current; - } - } - JObject json = JObject.Parse(request.Result); - var info = new IcosaSceneFileInfo(json); - using (UnityWebRequest www = UnityWebRequest.Get(info.TiltFileUrl)) - { - yield return www.SendWebRequest(); - while (!www.downloadHandler.isDone) { yield return null; } - FileStream stream = File.Create(path); - byte[] data = www.downloadHandler.data; - stream.Write(data, 0, data.Length); - stream.Close(); - } - - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.LoadNamedFile, sParam: path); - File.Delete(path); - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using Newtonsoft.Json.Linq; +using Org.OpenAPITools.Api; +using Org.OpenAPITools.Client; +using UnityEngine; +using UnityEngine.Networking; + +namespace TiltBrush +{ + + /// Back-end for cloud upload + /// These values might be serialized into prefabs; do not change them! + /// Make serializable so they don't get mangled by obfuscation. + [Serializable] + public enum Cloud + { + None = 0, + // Poly = 1, + Sketchfab = 2, + Icosa = 3, + } + + [Serializable] + public enum VrAssetFormat + { + Unknown, + TILT, + GLTF, + GLTF2, + } + + public class VrAssetService : MonoBehaviour + { + // Constants + + const string kDefaultName = "sketch"; + + private string kApiHost => App.ICOSA_API_URL; + private string kAssetLandingPage => $"{App.ICOSA_WEBSITE_URL}/uploads"; + + private const string kListAssetsUri = "/poly/assets"; + private const string kUserAssetsUri = "/users/me/assets"; + private const string kUserLikesUri = "/users/me/likedassets"; + + public const string kCreativeCommonsLicense = "CREATIVE_COMMONS_BY"; + + // Icosa API used by Open Brush. + // If Icosa doesn't support this version, don't try to talk to Icosa and prompt the user to upgrade. + private const string kIcosaApiVersion = "v1"; + + /// Change-of-basis transform + public static readonly TrTransform kIcosaFromUnity; + + private static Dictionary kGltfMimetypes = new Dictionary + { + { ".gltf", "model/gltf+json" }, + { ".bin", "application/octet-stream" }, + { ".glsl", "text/plain" }, + { ".bmp", "image/bmp" }, + { ".jpeg", "image/jpeg" }, + { ".jpg", "image/jpeg" }, + { ".png", "image/png" }, + { ".tilt", "application/octet-stream" } + }; + + // For progress reporting + private enum UploadStep + { + CreateGltf, + CreateTilt, + ZipElements, + UploadElements, + UpdateAssetData, + Done + } + + // These are progress values at the start of each step. + // TODO(b/146892613): have a different set for Icosa vs Sketchfab? + private static double[] kProgressSteps = + { + 0.01, // UploadStep.CreateGltf -- progress > 0 means we have begun + 0.25, // UploadStep.CreateTilt + 0.3, // UploadStep.ZipElements + 0.5, // UploadStep.UploadElements + 0.95, // UploadStep.UpdateAssetData + 1, // UploadStep.Done + 1 // + }; + + // Classes and types + + class StreamWithReadProgress : WrappedStream + { + public int TotalRead { get; private set; } + public StreamWithReadProgress(string filename) + { + SetWrapped(File.OpenRead(filename), ownsStream: true); + } + public override int Read(byte[] buffer, int offset, int count) + { + var amountRead = base.Read(buffer, offset, count); + TotalRead += amountRead; + return amountRead; + } + public override int ReadByte() + { + var amountRead = base.ReadByte(); + TotalRead += amountRead; + return amountRead; + } + } + + /// A bag of information about a pending upload + class UploadInfo : IProgress + { + // Only one of sourceFile or sourceData is valid + public readonly FileInfo m_sourceFile; + public readonly byte[] m_sourceData; + public readonly string m_remoteName; + public readonly long m_length; + public string m_elementId; + public double m_uploadPercent = 0; + + public UploadInfo(string sourceFile, string remoteName) + { + m_sourceFile = new FileInfo(sourceFile); + m_remoteName = remoteName; + m_length = m_sourceFile.Length; + } + + public UploadInfo(byte[] data, string remoteName) + { + m_sourceData = data; + m_remoteName = remoteName; + m_length = data.Length; + } + + public void SetUploaded(string elementId) + { + m_elementId = elementId; + } + + /// Allocates and returns a new Stream. + /// Caller is responsible for disposing of it. + public Stream OpenStream() + { + if (m_sourceData != null) + { + return new MemoryStream(m_sourceData); + } + else + { + return m_sourceFile.OpenRead(); + } + } + + void IProgress.Report(double value) + { + m_uploadPercent = value; + } + } + + /// Creates and then cleans up a uniquely-named temporary directory + class TemporaryUploadDirectory : IDisposable + { + public string Value { get; } + + public TemporaryUploadDirectory() + { +#if UNITY_EDITOR + if (App.Config && App.Config.m_DebugUpload) + { + // Delay deleting the directory until the next upload + string dirName = Path.Combine(Application.temporaryCachePath, "Upload"); + if (Directory.Exists(dirName)) + { + try { Directory.Delete(dirName, true); } + catch (Exception e) { Debug.LogException(e); } + } + } +#endif + Value = FileUtils.GenerateNonexistentFilename( + Application.temporaryCachePath, "Upload", ""); + string failureMessage = $"Can't create upload directory: {Value}"; + bool dirCreated = FileUtils.InitializeDirectoryWithUserError(Value, failureMessage); + if (!dirCreated) + { + throw new VrAssetServiceException(failureMessage); + } + } + + public void Dispose() + { +#if UNITY_EDITOR + if (App.Config && App.Config.m_DebugUpload) + { + // Delay deleting the directory until the next upload + return; + } +#endif + if (Directory.Exists(Value)) + { + Directory.Delete(Value, true); + } + } + } + + // Static API + + public static VrAssetService m_Instance; + + // Currently this always returns the standard API host when running unit tests + public string ApiHost + { + get + { + string cfg = App.UserConfig?.Sharing.VrAssetServiceHostOverride; + if (!string.IsNullOrEmpty(cfg)) { return cfg; } + return kApiHost; + } + } + + /// Returns true if Icosa would accept a PATCH of the specified asset + /// from the specified user. + /// + /// Pass: + /// type - + /// Where you found the assetId. Necessary because of some shortcuts + /// taken by the implementation. + /// userId - Icosa user id of the currently-logged-in OAuth user. Get it + /// with GetAccountIdAsync(). + public static async Task IsMutableAssetIdAsync( + FileInfoType type, string assetId, string userId, string apiHost) + { + if (assetId == null) { return false; } + // There are too many assumptions here -- for both cloud and disk, the logic + // should be "is it owned by me and unpublished? then it's mutable" + if (type == FileInfoType.Cloud) + { + // Assumption: cloud files are immutable. + // You can't "Like" an unpublished asset; and published assets are immutable. + return false; + } + else if (type == FileInfoType.Disk) + { + // Assumption: this asset is mutable because it's unlikely for a cloud-based .tilt + // to be in the user's Sketches/ folder. Local sketches always become un-published + // and mutable assets when uploaded (remember, publishing makes a copy). + // If someone grabbed a .tilt from their Icosa asset cache and put it in Sketches/ + // that would break this assumption and I'm not sure what would happen. + if (userId == null) { return false; } + // It's mutable, but check whether it's mutable by _us_. + try + { + // The null == null case is handled earlier + WebRequest request = new WebRequest( + $"{apiHost}{kListAssetsUri}/{assetId}", + App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + return (await request.SendAsync()).JObject?["accountId"].ToString() == userId; + } + catch (VrAssetServiceException) + { + return false; + } + } + else + { + throw new InvalidOperationException($"Unknown FileInfoType {type}"); + } + } + + // Instance API + + static VrAssetService() + { + Matrix4x4 polyFromUnity = AxisConvention.GetFromUnity(AxisConvention.kGltfAccordingToIcosa); + + // Provably non-lossy: the mat4 is purely TRS, and the S is uniform + kIcosaFromUnity = TrTransform.FromMatrix4x4(polyFromUnity); + } + + // Instance API + + [SerializeField] private int m_AssetsPerPage; + [SerializeField] public float m_SketchbookRefreshInterval; + public bool m_UseLocalFeaturedSketches = true; + + private float m_UploadProgress; + private bool m_LastUploadFailed; + private string m_LastUploadErrorMessage; + private string m_LastUploadErrorDetails; + private bool m_UserCanceledLastUpload; + private string m_LastUploadCompleteUrl; + TaskAndCts<(string url, long bytes)> m_UploadTask = null; + private string m_IcosaAccountId; + + private enum IcosaStatus + { + Ok, + Disabled, + NoConnection + } + private IcosaStatus m_IcosaStatus; + + public bool Available => m_IcosaStatus == IcosaStatus.Ok; + + public bool NoConnection => m_IcosaStatus == IcosaStatus.NoConnection; + + public float UploadProgress => m_UploadProgress; + + public bool LastUploadFailed + { + get { return m_LastUploadFailed; } + } + + public string LastUploadErrorMessage + { + get { return m_LastUploadErrorMessage; } + } + + public string LastUploadErrorDetails + { + get { return m_LastUploadErrorDetails; } + } + + public bool UserCanceledLastUpload + { + get { return m_UserCanceledLastUpload; } + } + + public string LastUploadCompleteUrl + { + get { return m_LastUploadCompleteUrl; } + } + + private string AssetLandingPage + { + get + { + string cfg = App.UserConfig.Sharing.VrAssetServiceUrlOverride; + if (!string.IsNullOrEmpty(cfg)) { return cfg; } + return kAssetLandingPage; + } + } + + // Cannot be an UploadProgress setter because the getter's type is different. + // pct is how much of that step has been completed. + private void SetUploadProgress(UploadStep step, double pct) + { + var step0 = (float)kProgressSteps[Mathf.Min(kProgressSteps.Length - 1, (int)step)]; + var step1 = (float)kProgressSteps[Mathf.Min(kProgressSteps.Length - 1, (int)step + 1)]; + m_UploadProgress = Mathf.Lerp(step0, step1, (float)pct); + } + + void Awake() + { + m_Instance = this; + } + + void Start() + { + if (!string.IsNullOrEmpty(App.UserConfig.Sharing.VrAssetServiceHostOverride) || + !string.IsNullOrEmpty(App.UserConfig.Sharing.VrAssetServiceUrlOverride)) + { + Debug.LogFormat("Overriding VrAssetService Api Host: {0} Landing Page: {1}", + ApiHost, AssetLandingPage); + } + + // If auto profiling is enabled, disable automatic Icosa downloading. + if (!App.UserConfig.Profiling.AutoProfile) + { + VerifyIcosaConnectionAndCheckApiVersionAsync(); + } + else + { + m_IcosaStatus = IcosaStatus.Disabled; + } + } + + /// Consume the result of the previous upload (if any) + public void ConsumeUploadResults() + { + // Our UI interprets "progress >= 1.0" as "there are results and we must display them!" + // The UI should really be internally stateful, and only display these results when new results + // appear that haven't yet been shown to the user. The issue with this design is that there + // are other consumers of upload results. Thankfully, they are less important and don't + // care about progress; so the hack is that we only "consume" the progress. + if (m_UploadProgress >= 1) + { + m_UploadProgress = 0; + } + } + + /// If you try and upload a sketch while a sketch is already uploading, + /// the existing upload will be canceled. + public async Task UploadCurrentSketchAsync(Cloud backend, bool isDemoUpload) + { + // This function handles most of the setup and cleanup. + // The heavy lifting is split out into UploadCurrentSketchInternal. + + // Generic to all kinds of failures + void ReportFailure(string userFriendly, string fullMessage = "") + { + m_LastUploadFailed = true; + m_LastUploadErrorMessage = userFriendly; + m_LastUploadErrorDetails = fullMessage; + ControllerConsoleScript.m_Instance.AddNewLine(LastUploadErrorMessage, skipLog: true); + ControllerConsoleScript.m_Instance.AddNewLine(LastUploadErrorDetails, skipLog: true); + AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); + } + + Debug.LogFormat("UploadCurrentSketch(demo: {0})", isDemoUpload); + + // Cancel previous upload coroutine if necessary. + if (m_UploadTask != null) + { + CancelUpload(); + if (await Task.WhenAny(m_UploadTask.Task, Task.Delay(TimeSpan.FromSeconds(5))) != + m_UploadTask.Task) + { + // Definitely a bug we should fix; make it noisy because otherwise it just looks + // like nothing is happening. + OutputWindowScript.Error("Timed out waiting for canceled upload"); + return; + } + } + + + m_LastUploadFailed = false; + m_LastUploadErrorMessage = ""; + m_LastUploadErrorDetails = ""; + m_UserCanceledLastUpload = false; + m_UploadProgress = 0.0f; + + using (var tempUploadDir = new TemporaryUploadDirectory()) + try + { + if (!isDemoUpload) + { + App.Instance.SetDesiredState(App.AppState.Uploading); + } + AudioManager.m_Instance.UploadLoop(true); + var timer = System.Diagnostics.Stopwatch.StartNew(); + m_UploadTask = new TaskAndCts<(string url, long bytes)>(); + + switch (backend) + { + case Cloud.Icosa: + m_UploadTask.Task = UploadCurrentSketchIcosaAsync(m_UploadTask.Token, tempUploadDir.Value, + isDemoUpload); + break; + case Cloud.Sketchfab: + m_UploadTask.Task = UploadCurrentSketchSketchfabAsync(m_UploadTask.Token, tempUploadDir.Value, + isDemoUpload); + break; + } + var (url, totalUploadLength) = await m_UploadTask.Task; + m_LastUploadCompleteUrl = url; + ControllerConsoleScript.m_Instance.AddNewLine("Upload succeeded!"); + AudioManager.m_Instance.PlayUploadCompleteSound(InputManager.Wand.Transform.position); + PanelManager.m_Instance.GetAdminPanel().ActivatePromoBorder(true); + // Don't auto-open the URL on mobile because it steals focus from the user. + if (!isDemoUpload && !App.Config.IsMobileHardware && m_LastUploadCompleteUrl != null) + { + // Can't pass a string param because this is also called from mobile GUI + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.ViewLastUpload); + } + } + catch (VrAssetServiceException exception) + { + // "Expected" failures (40x, 50x, etc) + Debug.LogWarning("UploadCurrentSketch external error"); + Debug.LogException(exception); + ReportFailure(exception.UserFriendly, exception.Message); + } + catch (OperationCanceledException) + { + m_UserCanceledLastUpload = true; + ReportFailure("Upload canceled."); + } + catch (Exception exception) + { + // Unexpected failures -- ie, bugs on our part + Debug.LogError("UploadCurrentSketch internal error"); + ReportFailure("Upload failed.", exception.Message); + throw; + } + finally + { + // Cleanup + if (App.CurrentState == App.AppState.Uploading) + { + App.Instance.SetDesiredState(App.AppState.Standard); + } + + m_UploadTask = null; + AudioManager.m_Instance.UploadLoop(false); + // This is how the upload popup knows we're complete + m_UploadProgress = 1.0f; + } + } + + /// Request to cancel the upload -- does not happen synchronously. + public void CancelUpload() + { + m_UploadTask?.Cancel(); + } + + private async void VerifyIcosaConnectionAndCheckApiVersionAsync() + { + m_IcosaStatus = await GetIcosaStatus(); + } + + private async Task GetIcosaStatus() + { + // UserConfig override + if (App.UserConfig.Flags.DisableIcosa) + { + return IcosaStatus.Disabled; + } + + string uri = ApiHost; + try + { + var api = new LoginApi($"{App.ICOSA_API_URL}"); + var result = new Dictionary { { "version", "v1" } }; // TODO: get version from API + string version = result["version"]; + if (version == kIcosaApiVersion) + { + return IcosaStatus.Ok; + } + else + { + Debug.LogWarning($"Icosa requires API {version} > {kIcosaApiVersion}"); + return IcosaStatus.Disabled; + } + } + catch (VrAssetServiceException e) + { + Debug.LogWarning($"Error connecting to Icosa: {e}"); + return IcosaStatus.NoConnection; + } + catch (Exception e) + { + Debug.LogError($"Internal error connecting to Icosa: {e}"); + return IcosaStatus.NoConnection; + } + } + + /// Returns a writable SceneFileInfo + private DiskSceneFileInfo GetWritableFile() + { + // hermetic gltf files currently don't work with AccessLevel.PRIVATE + SceneFileInfo currentFileInfo = SaveLoadScript.m_Instance.SceneFile; + + DiskSceneFileInfo fileInfo; + if (currentFileInfo.Valid) + { + if (currentFileInfo is DiskSceneFileInfo) + { + fileInfo = (DiskSceneFileInfo)currentFileInfo; + } + else + { + // This is a cloud sketch not saved before + fileInfo = SaveLoadScript.m_Instance.GetNewNameSceneFileInfo(); + } + } + else + { + // Save as a new file + fileInfo = SaveLoadScript.m_Instance.GetNewNameSceneFileInfo(); + } + return fileInfo; + } + + /// Returns a relative path R such that Join(fromDir, R) refers to toFile, or null on error. + /// Does not handle ".." paths. + public static string GetRelativePath(string fromDir, string toFile) + { + // Normalize paths so we can use plain string compares + var alt = Path.AltDirectorySeparatorChar; + var standard = Path.DirectorySeparatorChar; + if (alt != standard) + { + fromDir = fromDir.Replace(alt, standard); + toFile = toFile.Replace(alt, standard); + } + + int baseLen = fromDir.Length; + if (!toFile.StartsWith(fromDir)) { return null; } + if (toFile.Length <= baseLen) { return null; } + var sep = toFile[baseLen]; + if (sep != Path.DirectorySeparatorChar) + { + return null; + } + return toFile.Substring(baseLen + 1); + } + + private async Task CreateZipFileAsync( + string zipName, string rootDir, string[] paths, + CancellationToken token) + { + long totalLength = paths.Aggregate(0L, (acc, elt) => acc + new FileInfo(elt).Length) + 1; + long read = 1; + + using (var zip = File.OpenWrite(zipName)) + { + using (var archive = new ZipArchive(zip, ZipArchiveMode.Create)) + { + foreach (var path in paths) + { + string archivedName = GetRelativePath(rootDir, path); + if (archivedName == null) + { + Debug.LogWarning($"Ignoring {path} not under {rootDir}"); + continue; + } + ZipArchiveEntry entry = archive.CreateEntry(archivedName); + using (Stream writer = entry.Open()) + { + using (var reader = new StreamWithReadProgress(path)) + { + var task = reader.CopyToAsync(writer, 0x1_0000, token); + while (!task.IsCompleted) + { + long prev = reader.TotalRead; + await Awaiters.NextFrame; + read += reader.TotalRead - prev; + SetUploadProgress(UploadStep.ZipElements, read / (double)(totalLength)); + } + await task; + } + } + } + } + } + } + + // TODO: Refactor. This is largely the same as UploadCurrentSketchSketchFabAsync aside from a few url changes and the response. + private async Task<(string, long)> UploadCurrentSketchIcosaAsync( + CancellationToken token, string tempUploadDir, bool _) + { + DiskSceneFileInfo fileInfo = GetWritableFile(); + + var currentScene = SaveLoadScript.m_Instance.SceneFile; + string uploadName = currentScene.Valid ? currentScene.HumanName : kDefaultName; + string gltfUploadName = $"{uploadName}.gltf"; + + SetUploadProgress(UploadStep.CreateGltf, 0); + // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. + string gltfFile = Path.Combine(tempUploadDir, gltfUploadName); + var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( + OverlayType.Export, fadeDuration: 0.5f, + action: () => new ExportGlTF().ExportBrushStrokes( + gltfFile, + AxisConvention.kGltf2, binary: false, doExtras: true, + includeLocalMediaContent: true, gltfVersion: 2, + selfContained: true)); + if (!exportResults.success) + { + throw new VrAssetServiceException("Internal error creating upload data."); + } + + // Construct options to set the background color to the current environment's clear color. + Color bgColor = SceneSettings.m_Instance.CurrentEnvironment.m_RenderSettings.m_ClearColor; + IcosaService.Options options = null; + // options.SetBackgroundColor(bgColor); + + // TODO(b/146892613): we're not uploading this at the moment. Should we be? + // If we don't, we can probably remove this step...? + SetUploadProgress(UploadStep.CreateTilt, 0); + var thumbnail = await CreateTiltForUploadAsync(fileInfo); + token.ThrowIfCancellationRequested(); + + // Create a copy of the .tilt file in tempUploadDir. + string tempTiltPath = Path.Combine(tempUploadDir, $"{uploadName}.tilt"); + File.Copy(fileInfo.FullPath, tempTiltPath); + + // Save thumbnail as a png to temp path + string tempThumbnailPath = Path.Combine(tempUploadDir, "thumbnail.png"); + File.WriteAllBytes(tempThumbnailPath, thumbnail); + + // Collect files into a .zip file, including the .tilt file and thumbnail + string zipName = Path.Combine(tempUploadDir, "archive.zip"); + var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath).Append(tempThumbnailPath); + await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); + + var service = new IcosaService(App.Instance.IcosaToken); + var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); + var response = await service.CreateModel( + zipName, progress, token, options, tempUploadDir); + // TODO(b/146892613): return the UID and stick it into the .tilt file? + // Or do we not care since we aren't recording provenance and remixing + + // TODO(b/146892613): figure out this flow + // response.uri is not very useful; it is an API uri that gives you json of asset details. + // Also, the 3d-models URI might show that the asset is still processing. We can poll their + // API and find out when it's done and pop up the window then? + string uri = $"{App.ICOSA_WEBSITE_URL}/edit/{response.upload_job}"; + return (uri, 0); + } + + private async Task<(string, long)> UploadCurrentSketchSketchfabAsync( + CancellationToken token, string tempUploadDir, bool _) + { + DiskSceneFileInfo fileInfo = GetWritableFile(); + + SetUploadProgress(UploadStep.CreateGltf, 0); + // Do the glTF straight away as it relies on the meshes, not the stroke descriptions. + string gltfFile = Path.Combine(tempUploadDir, $"{kDefaultName}.gltf"); + var exportResults = await OverlayManager.m_Instance.RunInCompositorAsync( + OverlayType.Export, fadeDuration: 0.5f, + action: () => new ExportGlTF().ExportBrushStrokes( + gltfFile, + AxisConvention.kGltf2, binary: false, doExtras: false, + includeLocalMediaContent: true, gltfVersion: 2, + // Sketchfab doesn't support absolute texture URIs + selfContained: true)); + if (!exportResults.success) + { + throw new VrAssetServiceException("Internal error creating upload data."); + } + + // Construct options to set the background color to the current environment's clear color. + Color bgColor = SceneSettings.m_Instance.CurrentEnvironment.m_RenderSettings.m_ClearColor; + SketchfabService.Options options = new SketchfabService.Options(); + options.SetBackgroundColor(bgColor); + + // TODO(b/146892613): we're not uploading this at the moment. Should we be? + // If we don't, we can probably remove this step...? + SetUploadProgress(UploadStep.CreateTilt, 0); + await CreateTiltForUploadAsync(fileInfo); + token.ThrowIfCancellationRequested(); + + // Create a copy of the .tilt file in tempUploadDir. + string tempTiltPath = Path.Combine(tempUploadDir, "sketch.tilt"); + File.Copy(fileInfo.FullPath, tempTiltPath); + + // Collect files into a .zip file, including the .tilt file. + string zipName = Path.Combine(tempUploadDir, "archive.zip"); + var filesToZip = exportResults.exportedFiles.ToList().Append(tempTiltPath); + await CreateZipFileAsync(zipName, tempUploadDir, filesToZip.ToArray(), token); + var uploadLength = new FileInfo(zipName).Length; + + var service = new SketchfabService(App.SketchfabIdentity); + var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); + var response = await service.CreateModel( + fileInfo.HumanName, zipName, progress, token, options, tempUploadDir); + // TODO(b/146892613): return the UID and stick it into the .tilt file? + // Or do we not care since we aren't recording provenance and remixing + + // TODO(b/146892613): figure out this flow + // response.uri is not very useful; it is an API uri that gives you json of asset details. + // Also, the 3d-models URI might show that the asset is still processing. We can poll their + // API and find out when it's done and pop up the window then? + string uri = $"{SketchfabService.kModelLandingPage}{response.uid}"; + return (uri, uploadLength); + } + + /// Helper for UploadCurrentSketchXxxAsync + /// Writes the sketch to the passed fileInfo and returns a sketch thumbnail. + private async Task CreateTiltForUploadAsync(DiskSceneFileInfo fileInfo) + { + // Create and save snapshot. + SetUploadProgress(UploadStep.CreateTilt, 0); + SketchControlsScript.m_Instance.GenerateReplacementSaveIcon(); + SketchSnapshot snapshot = await SaveLoadScript.m_Instance.CreateSnapshotWithIconsAsync(); + snapshot.AssetId = fileInfo.AssetId; // FileInfo and snapshot must match + await SaveLoadScript.m_Instance.SaveSnapshot(fileInfo, snapshot: snapshot); + if (!File.Exists(fileInfo.FullPath)) + { + string exceptionMessage = "Internal error uploading .tilt."; + if (SaveLoadScript.m_Instance.LastWriteSnapshotError != null) + { + exceptionMessage += " Error: " + SaveLoadScript.m_Instance.LastWriteSnapshotError; + } + else + { + exceptionMessage += " No error message"; + } + + throw new VrAssetServiceException(exceptionMessage); + } + + byte[] thumbnail = SaveLoadScript.m_Instance.GetLastThumbnailBytes(); + if (thumbnail == null) + { + thumbnail = FileSketchSet.ReadThumbnail(fileInfo) ?? new byte[0]; + } + + return thumbnail; + } + + public AssetGetter GetAsset(string assetId, VrAssetFormat type, string reason) + { + string uri; + if (assetId.ToLower().StartsWith("https%3a%2f%2f") || assetId.ToLower().StartsWith("http%3a%2f%2f")) + { + uri = UnityWebRequest.UnEscapeURL(assetId); + } + else + { + uri = String.Format("{0}{1}/{2}", ApiHost, kListAssetsUri, assetId); + } + return new AssetGetter(uri, assetId, type, reason); + } + + private string CombineQueryParams(string uriPath, string additionalParams) + { + string separator = uriPath.Contains("?") ? "&" : "?"; + return $"{uriPath}{separator}{additionalParams}"; + } + + public AssetLister ListAssets(SketchSetType type) + { + string filteredUriPath = null; + string errorMessage = null; + switch (type) + { + case SketchSetType.Liked: + if (!App.IcosaIsLoggedIn) + { + return null; + } + filteredUriPath = CombineQueryParams(kUserLikesUri, "format=TILT&orderBy=LIKED_TIME"); + errorMessage = "Failed to access your liked sketches."; + break; + case SketchSetType.Curated: + filteredUriPath = CombineQueryParams(kListAssetsUri, "format=TILT&curated=true&orderBy=NEWEST"); + errorMessage = "Failed to access featured sketches."; + break; + } + + string uri = $"{ApiHost}{filteredUriPath}&pageSize={m_AssetsPerPage}"; + return new AssetLister(uri, errorMessage); + } + + // Get a specific sketch and insert it into the listed sketches at the specified index. + public IEnumerator InsertSketchInfo( + string assetId, int index, List infos) + { + string uri = String.Format("{0}{1}/{2}", ApiHost, kListAssetsUri, assetId); + WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + using (var cr = request.SendAsync().AsIeNull()) + { + while (!request.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException e) + { + Debug.LogException(e); + Debug.LogError("Failed to fetch sketch " + assetId); + yield break; + } + yield return cr.Current; + } + } + + Future f = new Future(() => JObject.Parse(request.Result)); + JObject json; + while (!f.TryGetResult(out json)) { yield return null; } + infos.Insert(index, new IcosaSceneFileInfo(json.Root)); + } + + public AssetLister ListAssets(IcosaSetType type) + { + string uri = null; + switch (type) + { + case IcosaSetType.Liked: + uri = $"{ApiHost}{kUserLikesUri}?format=GLTF2&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; + break; + case IcosaSetType.User: + uri = $"{ApiHost}{kUserAssetsUri}?format=GLTF2&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + break; + case IcosaSetType.Featured: + uri = $"{ApiHost}{kListAssetsUri}" + + $"?format=GLTF2&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + break; + } + return new AssetLister(uri, "Failed to connect to Icosa."); + } + + // Download a tilt file to a temporary file and load it + public IEnumerator LoadTiltFile(string id) + { + string path = Path.GetTempFileName(); + string uri = String.Format("{0}{1}/{2}", ApiHost, kListAssetsUri, id); + WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + using (var cr = request.SendAsync().AsIeNull()) + { + while (!request.Done) + { + try + { + cr.MoveNext(); + } + catch (VrAssetServiceException) + { + yield break; + } + yield return cr.Current; + } + } + JObject json = JObject.Parse(request.Result); + var info = new IcosaSceneFileInfo(json); + using (UnityWebRequest www = UnityWebRequest.Get(info.TiltFileUrl)) + { + yield return www.SendWebRequest(); + while (!www.downloadHandler.isDone) { yield return null; } + FileStream stream = File.Create(path); + byte[] data = www.downloadHandler.data; + stream.Write(data, 0, data.Length); + stream.Close(); + } + + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.LoadNamedFile, sParam: path); + File.Delete(path); + } + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 165eab31d3..4f9ffabb6f 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -1,5225 +1,5225 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using TiltBrush.Layers; -using UnityEngine; -using UnityEngine.InputSystem; -using SymmetryMode = TiltBrush.PointerManager.SymmetryMode; - -namespace TiltBrush -{ - - public class SketchControlsScript : MonoBehaviour - { - public const string kRemoveHeadsetFyi = "Remove headset to view."; - const string kTiltBrushGalleryUrl = "https://icosa.gallery"; - const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; - const string kPolyMainPageUri = "https://poly.google.com"; - - static public SketchControlsScript m_Instance; - static bool sm_enableGrabHaptics = true; - - // ------------------------------------------------------------ - // Constants and types - // ------------------------------------------------------------ - - public enum GlobalCommands - { - Null, - Save, - SaveNew, - Load, - NewSketch, - StraightEdge, - AutoOrient, - Undo, - Redo, - Tiltasaurus, - LightingHdr, - AudioVisualization, - ResetAllPanels, - SketchOrigin, - SymmetryPlane, - MultiMirror, - ViewOnly, - SaveGallery, - LightingLdr, - ShowSketchFolder, - About, - LoadNamedFile, // iParam1 : (optional) - send through a LoadSpeed as int - DropCam, - CuratedGallery, - Unused_UploadToCloud, - AnalyticsEnabled_Deprecated, - Credits, - LogOutOfGenericCloud, - DraftingVisibility, - DeleteSketch, - ShowWindowGUI, - MorePanels, - Cameras, - FAQ, - ExportRaw, - IRC, - YouTubeChat, - CameraOptions, - StencilsDisabled, - AdvancedTools, - FloatingPanelsMode, - StraightEdgeMeterDisplay, - Sketchbook, - ExportAll, - Lights, - SaveAndUpload, - StraightEdgeShape, - SaveOptions, - SketchbookMenu, - Disco, - ViewOnlineGallery, - CancelUpload, - AdvancedPanelsToggle, - Music, - Duplicate, - ToggleGroupStrokesAndWidgets, - SaveModel, - ViewPolyPage, - ViewPolyGallery, - ExportListed, - RenderCameraPath, - ToggleProfiling, - DoAutoProfile, - DoAutoProfileAndQuit, - ToggleSettings, - SummonMirror, - InvertSelection, - SelectAll, - FlipSelection, - ToggleBrushLab, - ReleaseNotes, - ToggleCameraPostEffects, - ToggleWatermark, - AccountInfo, - // LoadConfirmUnsaved -> LoadWaitOnDownload -> LoadConfirmComplex -> LoadComplexHigh -> Load - LoadConfirmUnsaved, - LoadConfirmComplex, - MemoryWarning, - MemoryExceeded, - ViewLastUpload, - LoadConfirmComplexHigh, - ShowTos, - ShowPrivacy, - ShowQuestSideLoading, - AshleysSketch, - UnloadReferenceImageCatalog, - SaveOnLocalChanges, - ToggleCameraPathVisuals, - ToggleCameraPathPreview, - DeleteCameraPath, - RecordCameraPath, - SelectCameraPath, - ToggleAutosimplification, - ShowGoogleDrive, - GoogleDriveSync_Folder, // iParam1: folder id as DriveSync.SyncedFolderType - GoogleDriveSync, - LoginToGenericCloud, // iParam1: Cloud enum - UploadToGenericCloud, // iParam1: Cloud enum - LoadWaitOnDownload, - SignOutConfirm, - ReadOnlyNotice, - ShowContribution, - - // Open Brush Reserved Enums 1000-1999 - LanguagePopup = 1000, - - RenameSketch = 5200, - OpenLayerOptionsPopup = 5201, - RenameLayer = 5202, - LoginToIcosa = 5600, - OpenDirectorChooserPopup = 5800, - OpenScriptsCommandsList = 6000, - OpenScriptsList = 6001, - OpenExampleScriptsList = 6002, - SymmetryTwoHanded = 6003, - OpenColorOptionsPopup = 7000, - ChangeSnapAngle = 8000, - MergeBrushStrokes = 10000, - RepaintOptions = 11500, - OpenNumericInputPopup = 12000 - } - - public enum ControlsType - { - KeyboardMouse, - SixDofControllers, - ViewingOnly - } - - public enum DraftingVisibilityOption - { - Visible, - Transparent, - Hidden - } - - public enum InputState - { - Standard, - Pan, - Rotation, - HeadLock, - ControllerLock, - PushPull, - BrushSize, - Save, - Load, - Num - } - - public enum LoadSpeed - { - Normal = -1, - Quick = 1, - } - - const float kControlPointHistoryMaxTime = 0.1f; - - class GazeResult - { - public bool m_HitWithGaze; - public bool m_HitWithController; - // ReSharper disable once NotAccessedField.Local - public bool m_WithinView; - public float m_ControllerDistance; - public Vector3 m_GazePosition; - public Vector3 m_ControllerPosition; - public InputManager.ControllerName m_ControllerName; - } - - class GrabWidgetControllerInfo - { - public InputManager.ControllerName m_Name; - /// Transform of controller at the time the grab started - public TrTransform m_BaseControllerXf; - /// "local" transform of widget (relative to controller), at the time the grab started. - /// The widget isn't parented to the controller, but if it were, this would be its transform. - public TrTransform m_BaseWidgetXf_LS; - } - - struct GrabWidgetHoldPoint - { - // ReSharper disable once NotAccessedField.Local - public InputManager.ControllerName m_Name; - public float m_BirthTime; - public Vector3 m_Pos; // where controller is holding the widget - public Quaternion m_Rot; - } - - class InputStateConfig - { - public bool m_AllowDrawing; - public bool m_AllowMovement; - public bool m_ShowGizmo; - } - - enum FadeState - { - None, - FadeOn, - FadeOff - } - - enum GrabWidgetState - { - None, - OneHand, - TwoHands - } - - enum GrabWorldState - { - Normal, - ResettingTransform, - ResetDone - } - - private enum WorldTransformResetState - { - Default, - Requested, - FadingToBlack, - FadingToScene, - } - - enum RotationType - { - All, - RollOnly - } - - enum GrabIntersectionState - { - RequestIntersections, - ReadBrush, - ReadWand - } - - // ------------------------------------------------------------ - // Inspector data (read-only even if public) - // ------------------------------------------------------------ - - public GameObject m_SketchSurface; - public SketchMemoryScript.PlaybackMode m_DefaultSketchPlaybackMode; - public float m_GazeMaxAngleFromPointing = 85.0f; - public float m_GazeMaxAngleFacingToForward = 80.0f; - - [SerializeField] bool m_AtlasIconTextures; - - [SerializeField] SaveIconTool m_SaveIconTool; - [SerializeField] DropCamWidget m_DropCam; - [SerializeField] string m_CreditsSketchFilename; - [SerializeField] string m_AshleysSketchFilename; - [SerializeField] float m_DefaultSketchLoadSpeed; - [SerializeField] GameObject m_TransformGizmoPrefab; - - [SerializeField] GameObject m_RotationIconPrefab; - [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; - [SerializeField] float m_GazeMaxDistance = 10.0f; - [SerializeField] float m_GazeControllerPointingDistance; - [SerializeField] float m_GazePanelDectivationDelay = 0.25f; - - [SerializeField] GameObject m_UIReticle; - [SerializeField] GameObject m_UIReticleMobile; - [SerializeField] GameObject m_UIReticleSixDofController; - - [SerializeField] float m_DoubleTapWindow; - [SerializeField] float m_PushPullScale; - [SerializeField] RotationCursorScript m_RotationCursor; - [SerializeField] float m_RotationMaxAngle; - - [SerializeField] float m_RotationScalar; - [SerializeField] float m_RotationRollScalar; - [SerializeField] float m_PanScalar; - - [SerializeField] float m_AdjustToolSizeScalar; - - [SerializeField] GameObject m_IRCChatPrefab; - [SerializeField] GameObject m_YouTubeChatPrefab; - [SerializeField] GameObject m_Decor; - [SerializeField] BaseTool.ToolType m_InitialTool = BaseTool.ToolType.SketchSurface; - [SerializeField] string m_ReleaseNotesURL; - [SerializeField] string m_HelpCenterURL; - [SerializeField] string m_ThirdPartyNoticesURL; - [SerializeField] string m_TosURL; - [SerializeField] string m_PrivacyURL; - [SerializeField] string m_QuestSideLoadingHowToURL; - - [Multiline] - [SerializeField] string m_ContributionPromoText; - [SerializeField] string m_ContributionURL; - - [SerializeField] float m_WorldTransformMinScale = .1f; - [SerializeField] float m_WorldTransformMaxScale = 10.0f; - - [Header("Undo/Redo Hold")] - [SerializeField] float m_UndoRedoHold_DurationBeforeStart; - [SerializeField] float m_UndoRedoHold_RepeatInterval; - - [Header("Pin Cushion")] - [SerializeField] GameObject m_PinCushionPrefab; - - [Header("Grabbing and tossing")] - [SerializeField] float m_GrabWorldFadeSpeed = 8.0f; - [SerializeField] Color m_GrabWorldGridColor = new Color(0.0f, 1.0f, 1.0f, 0.2f); - [SerializeField] ControllerGrabVisuals m_ControllerGrabVisuals; - [SerializeField] float m_WidgetGpuIntersectionRadius; - - [Header("Saving")] - [SerializeField] int m_NumStrokesForSaveIcon = 50; - - [NonSerialized] public Color m_GrabHighlightActiveColor; - [NonSerialized] public bool m_DisableWorldGrabbing = false; - - /// Throwing an object faster than this means it's a "toss". Units are m/s. - public float m_TossThresholdMeters = 3f; - /// Angular motion contributes more towards the toss velocity the larger the object is; - /// or rather, the larger the distance between the grab point and the object's center. - /// To prevent large objects from being too-easily-tossed, bound that distance. - public float m_TossMaxPivotDistMeters = 0.33f; - - // ------------------------------------------------------------ - // Internal data - // ------------------------------------------------------------ - - private SketchSurfacePanel m_SketchSurfacePanel; - private SketchMemoryScript.PlaybackMode m_SketchPlaybackMode; - private GameObject m_TransformGizmo; - private TransformGizmoScript m_TransformGizmoScript; - private GameObject m_RotationIcon; - private float m_MouseDeltaX; - private float m_MouseDeltaY; - private float m_MouseDeltaXScaled; - private float m_MouseDeltaYScaled; - private float m_PositionOffsetResetTapTime; - private bool m_EatToolScaleInput; - - private PanelManager m_PanelManager; - private WidgetManager m_WidgetManager; - private PinCushion m_PinCushion; - private bool m_EatPinCushionInput; - - // This is the gaze that was used to compute m_CurrentGazeHitPoint. - // It is not a general substitute for ViewpointScript.Gaze. - private Ray m_CurrentGazeRay; - private Quaternion m_CurrentHeadOrientation; - private GazeResult[] m_GazeResults; - private int m_CurrentGazeObject; - private bool m_EatInputGazeObject; - private Vector3 m_CurrentGazeHitPoint; - private Ray m_GazeControllerRay; - private Ray m_GazeControllerRayActivePanel; - private bool m_ForcePanelActivation = false; - private float m_GazePanelDectivationCountdown; - private bool m_PanelsVisibilityRequested; - - // Previously Experimental-Model only - private bool m_HeadOffset; - - float m_UndoHold_Timer; - float m_RedoHold_Timer; - - // Grab world member variables. - struct GrabState - { - public InputManager.ControllerName name; - public TrTransform grabTransform; - public bool grabbingWorld; - public bool grabbingGroup; - public bool startedGrabInsideWidget; - public bool eatInput; - private GrabWidget lastWidgetIntersect; - - public void SetHadBestGrabAndTriggerHaptics(GrabWidgetData data) - { - bool dormant = WidgetManager.m_Instance.WidgetsDormant; - if (data != null && !data.m_WidgetScript.AllowDormancy) - { - dormant = false; - } - GrabWidget newInsideWidget = (data != null && !dormant) ? data.m_WidgetScript : null; - if (sm_enableGrabHaptics && newInsideWidget != lastWidgetIntersect) - { - // state changed - if (newInsideWidget != null) - { - // transitioning in - InputManager.m_Instance.TriggerHaptics(name, data.m_WidgetScript.HapticDuration); - } - else - { - // transitioning out - InputManager.m_Instance.TriggerHaptics(name, 0.03f); - } - } - lastWidgetIntersect = newInsideWidget; - } - - public void ClearInsideWidget() - { - lastWidgetIntersect = null; - } - } - private GrabState m_GrabBrush = new GrabState { name = InputManager.ControllerName.Brush }; - private GrabState m_GrabWand = new GrabState { name = InputManager.ControllerName.Wand }; - - private WorldTransformResetState m_WorldTransformResetState = WorldTransformResetState.Default; - private TrTransform m_WorldTransformResetXf = TrTransform.identity; // set when reset requested - private GrabWorldState m_GrabWorldState = GrabWorldState.Normal; - private float m_WorldTransformFadeAmount; - private bool m_AllowWorldTransformLastFrame = false; - private bool m_WorldBeingGrabbed; - private TrTransform m_xfDropCamReset_RS; - - struct GpuIntersectionResult - { - public GpuIntersector.FutureModelResult result; - public List resultList; - } - private Queue m_BrushResults; - private Queue m_WandResults; - private int m_WidgetGpuIntersectionLayer; - - private GrabWidget m_CurrentGrabWidget; - private GrabWidget m_MaybeDriftingGrabWidget; // use only to clear drift - - // References to widgets, cached in the UpdateGrab_None, to be used by helper functions - // for the remainder of the frame. - private GrabWidget m_PotentialGrabWidgetBrush; - private GrabWidget m_PotentialGrabWidgetWand; - - // Flags for the explaining if the m_PotentialGrabWidget_x widgets are able to be interacted with. - // Cached in the UpdateGrab_None, used for the remainder of the frame. - private bool m_PotentialGrabWidgetBrushValid; - private bool m_PotentialGrabWidgetWandValid; - - // References to widget metadata, cached in UpdateGrab_None, to be re-used on "off frames" - // when the GPU intersector is not refreshing the nearest widget to the respective controller. - private GrabWidgetData m_BackupBrushGrabData; - private GrabWidgetData m_BackupWandGrabData; - - private GrabWidgetState m_GrabWidgetState; - private GrabWidgetControllerInfo m_GrabWidgetOneHandInfo; - private TrTransform m_GrabWidgetTwoHandBrushPrev; - private TrTransform m_GrabWidgetTwoHandWandPrev; - private Queue m_GrabWidgetHoldHistory; - - private Quaternion m_RotationOrigin; - private Vector2 m_RotationCursorOffset; - - private bool m_RotationRollActive; - private float m_RotationResetTapTime; - - private RotationType m_CurrentRotationType; - private bool m_AutoOrientAfterRotation; - - private Vector3 m_SurfaceForward; - private Vector3 m_SurfaceRight; - private Vector3 m_SurfaceUp; - - private Vector3 m_SurfaceLockOffset; - private Vector3 m_SurfaceLockBaseSurfacePosition; - private Vector3 m_SurfaceLockBaseControllerPosition; - private Quaternion m_SurfaceLockBaseHeadRotation; - private Quaternion m_SurfaceLockBaseControllerRotation; - private Quaternion m_SurfaceLockBaseSurfaceRotation; - private InputManager.ControllerName m_SurfaceLockActingController; - private float m_SurfaceLockControllerBaseScalar; - private float m_SurfaceLockControllerScalar; - - private bool m_PositioningPanelWithHead; - private Quaternion m_PositioningPanelBaseHeadRotation; - private Vector3 m_PositioningPanelOffset; - private float m_PositioningTimer; - private float m_PositioningSpeed; - - private DraftingVisibilityOption m_DraftingVisibility = DraftingVisibilityOption.Visible; - - private Vector3 m_SketchOrigin; - - private ControlsType m_ControlsType; - private GrabWidget m_IRCChatWidget; - private GrabWidget m_YouTubeChatWidget; - private MultiCamCaptureRig m_MultiCamCaptureRig; - private CameraPathCaptureRig m_CameraPathCaptureRig; - - private bool m_ViewOnly = false; - - private InputState m_CurrentInputState; - private InputStateConfig[] m_InputStateConfigs; - - private GrabIntersectionState m_CurrentGrabIntersectionState; - - private float m_WorldTransformSpeedSmoothed; - - // ------------------------------------------------------------ - // Properties and events - // ------------------------------------------------------------ - - public MultiCamCaptureRig MultiCamCaptureRig - { - get { return m_MultiCamCaptureRig; } - } - - public CameraPathCaptureRig CameraPathCaptureRig - { - get { return m_CameraPathCaptureRig; } - } - - public ControllerGrabVisuals ControllerGrabVisuals - { - get { return m_ControllerGrabVisuals; } - } - - public SketchMemoryScript.PlaybackMode SketchPlaybackMode - { - get { return m_SketchPlaybackMode; } - set { m_SketchPlaybackMode = value; } - } - - public Transform m_Canvas - { - get { return App.Instance.m_CanvasTransform; } - } - - public ControlsType ActiveControlsType - { - get { return m_ControlsType; } - set { m_ControlsType = value; } - } - - public float WorldTransformMinScale - { - get - { - return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMinScale * 0.01f : - m_WorldTransformMinScale; - } - } - - public float WorldTransformMaxScale - { - get - { - return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMaxScale * 10.0f : - m_WorldTransformMaxScale; - } - } - - public void SetInitialTool(BaseTool.ToolType rType) - { - m_InitialTool = rType; - } - - public void SetInFreePaintMode(bool bFreePaint) - { - m_SketchSurfacePanel.SetInFreePaintMode(bFreePaint); - } - - public float GazeMaxDistance - { - get { return m_GazeMaxDistance; } - } - - public InputManager.ControllerName OneHandGrabController - { - get - { - return m_CurrentGrabWidget != null ? - m_GrabWidgetOneHandInfo.m_Name : - InputManager.ControllerName.None; - } - } - - public InputManager.ControllerName PotentialOneHandGrabController(GrabWidget widget) - { - if (m_PotentialGrabWidgetBrush == widget) - { - return InputManager.ControllerName.Brush; - } - else if (m_PotentialGrabWidgetWand == widget) - { - return InputManager.ControllerName.Wand; - } - return OneHandGrabController; - } - - public Vector3 GetSurfaceForward() { return m_SurfaceForward; } - public Vector3 GetSurfaceUp() { return m_SurfaceUp; } - public Vector3 GetSurfaceRight() { return m_SurfaceRight; } - public Vector3 GetSketchOrigin() { return m_SketchOrigin; } - public float GetDefaultSketchLoadSpeed() { return m_DefaultSketchLoadSpeed; } - public Quaternion GetCurrentHeadOrientation() { return m_CurrentHeadOrientation; } - public Vector3 GetUIReticlePos() { return m_UIReticle.transform.position; } - public Vector3 GetSweetSpotPos() { return m_PanelManager.m_SweetSpot.transform.position; } - public void SetSketchOrigin(Vector3 vOrigin) { m_SketchOrigin = vOrigin; } - - public void EatGazeObjectInput() - { - m_EatInputGazeObject = true; - m_GazePanelDectivationCountdown = 0.0f; - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - } - public void EatToolScaleInput() { m_EatToolScaleInput = true; } - public void EatGrabInput() - { - m_GrabWand.eatInput = true; - m_GrabBrush.eatInput = true; - } - - public bool ShouldRespondToPadInput(InputManager.ControllerName name) - { - if (name == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).BrushPadAnimatesOnHover(); - } - return !m_EatToolScaleInput && SketchSurfacePanel.m_Instance.CanAdjustToolSize(); - } - public void ForcePanelActivation(bool bForce) - { - m_ForcePanelActivation = bForce; - if (m_ForcePanelActivation) - { - m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; - } - } - public bool IsUserInteractingWithUI() - { - return (m_CurrentGazeObject != -1) || (m_GazePanelDectivationCountdown > 0.0f); - } - public bool IsUIBlockingUndoRedo() - { - if (m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).UndoRedoBlocked(); - } - return false; - } - public bool IsUserAbleToInteractWithAnyWidget() - { - return IsUserInteractingWithAnyWidget() || - (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || - (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid); - } - public bool IsUserInteractingWithAnyWidget() { return m_CurrentGrabWidget != null; } - public bool IsUserGrabbingAnyPanel() - { - return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is PanelWidget); - } - public bool IsUsersBrushIntersectingWithSelectionWidget() - { - return (m_PotentialGrabWidgetBrush != null && - m_PotentialGrabWidgetBrushValid && - m_PotentialGrabWidgetBrush is SelectionWidget); - } - public bool IsUserIntersectingWithSelectionWidget() - { - return IsUsersBrushIntersectingWithSelectionWidget() || - (m_PotentialGrabWidgetWand != null && - m_PotentialGrabWidgetWandValid && - m_PotentialGrabWidgetWand is SelectionWidget); - } - public bool IsUserInteractingWithSelectionWidget() - { - return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is SelectionWidget); - } - - public bool IsUserGrabbingWorld() { return m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld; } - public bool IsUserGrabbingWorldWithBrushHand() { return m_GrabBrush.grabbingWorld; } - public bool IsUserTransformingWorld() { return m_GrabWand.grabbingWorld && m_GrabBrush.grabbingWorld; } - public float GetGazePanelActivationRatio() { return m_GazePanelDectivationCountdown / m_GazePanelDectivationDelay; } - public bool IsCurrentGrabWidgetPinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.Pinned; } - public bool CanCurrentGrabWidgetBePinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.AllowPinning; } - public bool DidUserGrabWithBothInside() { return m_GrabBrush.startedGrabInsideWidget && m_GrabWand.startedGrabInsideWidget; } - public bool IsUserGrabbingWidget(GrabWidget widget) { return widget == m_CurrentGrabWidget; } - public bool IsUserTwoHandGrabbingWidget() { return m_GrabWidgetState == GrabWidgetState.TwoHands; } - public bool IsPinCushionShowing() { return m_PinCushion.IsShowing(); } - public bool IsUserLookingAtPanel(BasePanel panel) - { - return m_CurrentGazeObject > -1 && - m_PanelManager.GetAllPanels()[m_CurrentGazeObject].m_Panel == panel; - } - - public SaveIconTool GetSaveIconTool() - { - return m_SaveIconTool; - } - - public DropCamWidget GetDropCampWidget() - { - return m_DropCam; - } - - public bool IsGrabWorldStateStable() - { - return m_GrabWorldState == GrabWorldState.Normal; - } - - // Internal: modify Coords.ScenePose or Coords.CanvasPose depending on the - // state of m_InTransformCanvasMode - TrTransform GrabbedPose - { - get - { - return App.Scene.Pose; - } - set - { - App.Scene.Pose = value; - } - } - - public Transform GazeObjectTransform() - { - if (m_CurrentGazeObject != -1) - { - return m_PanelManager.GetPanel(m_CurrentGazeObject).transform; - } - return null; - } - - public void ForceShowUIReticle(bool bVisible) - { - m_UIReticle.SetActive(bVisible); - } - - public void SetUIReticleTransform(Vector3 vPos, Vector3 vForward) - { - m_UIReticle.transform.position = vPos; - m_UIReticle.transform.forward = vForward; - } - - public bool AtlasIconTextures - { - get { return m_AtlasIconTextures; } - } - - public IconTextureAtlas IconTextureAtlas - { - get { return GetComponent(); } - } - public GrabWidget CurrentGrabWidget => m_CurrentGrabWidget; - - void DismissPopupOnCurrentGazeObject(bool force) - { - if (m_CurrentGazeObject != -1) - { - m_PanelManager.GetPanel(m_CurrentGazeObject).CloseActivePopUp(force); - } - } - - void Awake() - { - m_Instance = this; - - BrushController.m_Instance.BrushSetToDefault += OnBrushSetToDefault; - - IconTextureAtlas.Init(); - - m_MultiCamCaptureRig = GetComponentInChildren(true); - m_MultiCamCaptureRig.Init(); - - m_CameraPathCaptureRig = GetComponentInChildren(true); - m_CameraPathCaptureRig.Init(); - - m_SketchSurfacePanel = m_SketchSurface.GetComponent(); - m_PanelManager = GetComponent(); - m_PanelManager.Init(); - InitGazePanels(); - - m_WidgetManager = GetComponent(); - m_WidgetManager.Init(); - - m_InputStateConfigs = new InputStateConfig[(int)InputState.Num]; - for (int i = 0; i < (int)InputState.Num; ++i) - { - m_InputStateConfigs[i] = new InputStateConfig(); - m_InputStateConfigs[i].m_AllowDrawing = false; - m_InputStateConfigs[i].m_AllowMovement = true; - m_InputStateConfigs[i].m_ShowGizmo = false; - } - - m_InputStateConfigs[(int)InputState.Standard].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.Pan].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.HeadLock].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowDrawing = true; - m_InputStateConfigs[(int)InputState.PushPull].m_AllowDrawing = true; - - m_InputStateConfigs[(int)InputState.Pan].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.Rotation].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.PushPull].m_AllowMovement = false; - m_InputStateConfigs[(int)InputState.BrushSize].m_AllowMovement = false; - - m_InputStateConfigs[(int)InputState.Pan].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.Rotation].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.HeadLock].m_ShowGizmo = true; - m_InputStateConfigs[(int)InputState.PushPull].m_ShowGizmo = true; - - m_CurrentGazeRay = new Ray(Vector3.zero, Vector3.forward); - m_GazeControllerRay = new Ray(Vector3.zero, Vector3.forward); - m_GazeControllerRayActivePanel = new Ray(Vector3.zero, Vector3.forward); - - m_GrabWidgetHoldHistory = new Queue(); - m_GrabWidgetOneHandInfo = new GrabWidgetControllerInfo(); - - // Initialize world grip members. - m_GrabBrush.grabTransform = TrTransform.identity; - m_GrabWand.grabTransform = TrTransform.identity; - - m_BrushResults = new Queue(); - m_WandResults = new Queue(); - m_WidgetGpuIntersectionLayer = LayerMask.NameToLayer("GpuIntersection"); - m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; - } - - public void InitGazePanels() - { - // Find all gaze panels. - int iNumGazePanels = m_PanelManager.GetAllPanels().Count; - m_GazeResults = new GazeResult[iNumGazePanels]; - for (int i = 0; i < iNumGazePanels; ++i) - { - m_GazeResults[i] = new GazeResult(); - m_GazeResults[i].m_HitWithGaze = false; - m_GazeResults[i].m_HitWithController = false; - m_GazeResults[i].m_WithinView = false; - m_GazeResults[i].m_GazePosition = new Vector3(); - } - } - - public void OnEnable() - { - // This needs to run before other tools initialize, which is why it's running in OnEnable. - // The sequence is Awake(), OnEnable(), Start(). - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) - { - SetInFreePaintMode(true); - SetInitialTool(BaseTool.ToolType.FreePaintTool); - } - } - - void Start() - { - m_TransformGizmo = (GameObject)Instantiate(m_TransformGizmoPrefab); - m_TransformGizmo.transform.parent = transform; - m_TransformGizmoScript = m_TransformGizmo.GetComponent(); - m_TransformGizmo.SetActive(false); - - m_RotationIcon = (GameObject)Instantiate(m_RotationIconPrefab); - m_RotationIcon.transform.position = m_SketchSurface.transform.position; - m_RotationIcon.transform.parent = m_SketchSurface.transform; - m_RotationIcon.SetActive(false); - - GameObject pinCushionObj = (GameObject)Instantiate(m_PinCushionPrefab); - m_PinCushion = pinCushionObj.GetComponent(); - - m_PositionOffsetResetTapTime = 0.0f; - - m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - - m_AutoOrientAfterRotation = true; - m_RotationCursor.gameObject.SetActive(false); - - ResetGrabbedPose(); - m_SketchOrigin = m_SketchSurface.transform.position; - - m_PanelManager.InitPanels(m_ControlsType == ControlsType.SixDofControllers); - - m_UIReticleMobile.SetActive(m_ControlsType == ControlsType.ViewingOnly); - m_UIReticleSixDofController.SetActive(m_ControlsType != ControlsType.ViewingOnly); - - m_PositioningPanelWithHead = false; - m_PositioningSpeed = 16.0f; - - m_CurrentRotationType = RotationType.All; - m_RotationResetTapTime = 0.0f; - - m_CurrentInputState = InputState.Standard; - - m_SketchSurfacePanel.EnableSpecificTool(m_InitialTool); - m_SurfaceLockControllerBaseScalar = m_SketchSurfacePanel.m_PanelSensitivity; - - //after initializing, start with gaze objects hidden - m_CurrentGazeObject = -1; - m_EatInputGazeObject = false; - - // Previously set to 0 in experimental builds - int hidePanelsDelay = 1; - - StartCoroutine(DelayedHidePanels(hidePanelsDelay)); - - m_DropCam.Show(false); - - m_GrabWidgetState = GrabWidgetState.None; - - UpdateDraftingVisibility(); - - m_DisableWorldGrabbing = false; - } - - private IEnumerator DelayedHidePanels(int frames) - { - int stall = frames; - while (stall-- > 0) - { - yield return null; - } - - m_PanelManager.HidePanelsForStartup(); - RequestPanelsVisibility(false); - } - - void Update() - { - // TODO: we need to figure out what transform to pass in here! - // Maybe best _just for now_ to use the scene transform? - TrTransform scenePose = App.Scene.Pose; - Shader.SetGlobalMatrix("xf_CS", scenePose.ToMatrix4x4()); - Shader.SetGlobalMatrix("xf_I_CS", scenePose.inverse.ToMatrix4x4()); - } - - void LateUpdate() - { - // Gracefully exits if we're not recording a video. - VideoRecorderUtils.SerializerNewUsdFrame(); - } - - public bool IsFreepaintToolReady() - { - return - !m_PinCushion.IsShowing() && - !PointerManager.m_Instance.IsStraightEdgeProxyActive() && - !InputManager.m_Instance.ControllersAreSwapping() && - (m_SketchSurfacePanel.IsSketchSurfaceToolActive() || - (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.FreePaintTool)) - ; - } - - public void UpdateControls() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlsScript.UpdateControls"); - m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; - - // Verify controllers are available and prune state if they're not. - if ((App.VrSdk.GetControllerDof() == VrSdk.DoF.Six && - App.VrSdk.IsInitializingUnityXR) && App.VrSdk.IsHmdInitialized()) - { - m_PanelManager.SetVisible(false); - PointerManager.m_Instance.RequestPointerRendering(false); - return; - } - - //mouse movement - Vector2 mv = InputManager.m_Instance.GetMouseMoveDelta(); - m_MouseDeltaX = mv.x; - m_MouseDeltaY = mv.y; - - UpdateGazeObjectsAnimationState(); - UpdateCurrentGazeRay(); - m_SketchSurfacePanel.SetBacksideActive(m_CurrentGazeRay.origin); - m_PanelManager.UpdatePanels(); - - m_MouseDeltaXScaled = m_MouseDeltaX * GetAppropriateMovementScalar(); - m_MouseDeltaYScaled = m_MouseDeltaY * GetAppropriateMovementScalar(); - - //this is used for one-shot inputs that don't require state and do not change state - UpdateBaseInput(); - - UpdatePinCushionVisibility(); - - //if the pointer manager is processing, we don't want to respond to input - if (!PointerManager.m_Instance.IsMainPointerProcessingLine()) - { - - //see if we're grabbing a widget - UpdateGrab(); - - //see if we're looking at a gaze object - RefreshCurrentGazeObject(); - - // Tools allowed when widgets aren't grabbed. - bool bWidgetGrabOK = m_GrabWidgetState == GrabWidgetState.None; - - // If we don't have a widget held and we're not grabbing the world with the brush controller, - // update tools. - if (bWidgetGrabOK && !m_GrabBrush.grabbingWorld) - { - if (m_CurrentGazeObject != -1 && !m_WorldBeingGrabbed) - { - UpdateActiveGazeObject(); - - // Allow for standard input (like Undo / Redo) even when gazing at a panel. - if (m_CurrentInputState == InputState.Standard) - { - UpdateStandardInput(); - } - } - else - { - //standard input, no gaze object - if (m_InputStateConfigs[(int)m_CurrentInputState].m_AllowMovement) - { - m_SketchSurfacePanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); - } - - switch (m_CurrentInputState) - { - case InputState.Standard: - UpdateStandardInput(); - break; - case InputState.Pan: - UpdatePanInput(); - break; - case InputState.Rotation: - UpdateRotationInput(); - break; - case InputState.HeadLock: - UpdateHeadLockInput(); - break; - case InputState.ControllerLock: - UpdateControllerLock(); - break; - case InputState.PushPull: - UpdatePushPullInput(); - break; - case InputState.Save: - UpdateSaveInput(); - break; - case InputState.Load: - UpdateLoadInput(); - break; - } - - //keep pointer locked in the right spot, even if it's hidden - if (m_SketchSurfacePanel.ActiveTool.LockPointerToSketchSurface()) - { - Vector3 vPointerPos = Vector3.zero; - Vector3 vPointerForward = Vector3.zero; - m_SketchSurfacePanel.GetReticleTransform(out vPointerPos, out vPointerForward, - (m_ControlsType == ControlsType.ViewingOnly)); - PointerManager.m_Instance.SetMainPointerPosition(vPointerPos); - PointerManager.m_Instance.SetMainPointerForward(vPointerForward); - } - - m_SketchSurfacePanel.AllowDrawing(m_InputStateConfigs[(int)m_CurrentInputState].m_AllowDrawing); - m_SketchSurfacePanel.UpdateCurrentTool(); - - PointerManager.m_Instance.AllowPointerPreviewLine(IsFreepaintToolReady()); - //keep transform gizmo at sketch surface pos - m_TransformGizmo.transform.position = m_SketchSurface.transform.position; - bool bGizmoActive = m_InputStateConfigs[(int)m_CurrentInputState].m_ShowGizmo && m_SketchSurfacePanel.ShouldShowTransformGizmo(); - m_TransformGizmo.SetActive(bGizmoActive); - } - } - } - - // Update any transition to a scene transform reset. - UpdateWorldTransformReset(); - - //update our line after all input and tools have chimed in on the state of it - PointerManager.m_Instance.UpdateLine(); - UnityEngine.Profiling.Profiler.EndSample(); - } - - public void UpdateControlsPostIntro() - { - m_PanelManager.UpdatePanels(); - UpdateCurrentGazeRay(); - UpdateGazeObjectsAnimationState(); - RefreshCurrentGazeObject(); - UpdateSwapControllers(); - if (m_CurrentGazeObject > -1) - { - UpdateActiveGazeObject(); - } - } - - public void UpdateControlsForLoading() - { - UpdateCurrentGazeRay(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - UpdateGrab(); - UpdateWorldTransformReset(); - - if (m_GrabWidgetState == GrabWidgetState.None && m_CurrentGazeObject == -1 && - m_SketchSurfacePanel.ActiveTool.AvailableDuringLoading() && - !m_GrabBrush.grabbingWorld) - { - m_SketchSurfacePanel.UpdateCurrentTool(); - } - } - - public void UpdateControlsForReset() - { - UpdateGrab(); - UpdateCurrentGazeRay(); - UpdatePinCushionVisibility(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - PointerManager.m_Instance.UpdateLine(); - } - - public void UpdateControlsForUploading() - { - UpdateCurrentGazeRay(); - UpdatePinCushionVisibility(); - m_PanelManager.UpdatePanels(); - UpdateGazeObjectsAnimationState(); - } - - public void UpdateControlsForMemoryExceeded() - { - UpdateGrab(); - m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; - m_PanelManager.UpdatePanels(); - UpdateCurrentGazeRay(); - UpdateGazeObjectsAnimationState(); - RefreshCurrentGazeObject(); - if (m_CurrentGazeObject > -1) - { - UpdateActiveGazeObject(); - } - } - - void UpdatePinCushionVisibility() - { - // If the pin cushion is showing and the user cancels, eat the input. - // if (m_PinCushion.IsShowing()) - // { - // if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) || - // InputManager.Brush.GetControllerGrip() || - // InputManager.Wand.GetControllerGrip() || - // IsUserInteractingWithAnyWidget() || - // IsUserInteractingWithUI()) - // { - // m_EatPinCushionInput = true; - // } - // } - - // If our tool wants the input blocked, maintain the input eat state until - // after the user has let off input. - if (m_SketchSurfacePanel.ActiveTool.BlockPinCushion() || !CanUsePinCushion()) - { - m_EatPinCushionInput = true; - } - - bool show = - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.ShowPinCushion); - m_PinCushion.ShowPinCushion(show && !m_EatPinCushionInput); - m_EatPinCushionInput = m_EatPinCushionInput && show; - } - - bool CanUsePinCushion() - { - return (m_ControlsType == ControlsType.SixDofControllers) && - m_PanelManager.AdvancedModeActive() && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !InputManager.Brush.GetControllerGrip() && - !InputManager.Wand.GetControllerGrip() && - !IsUserInteractingWithAnyWidget() && - !IsUserInteractingWithUI() && - !m_SketchSurfacePanel.ActiveTool.BlockPinCushion() && - App.Instance.IsInStateThatAllowsPainting(); - } - - void UpdateCurrentGazeRay() - { - var head = ViewpointScript.Head; - m_CurrentGazeRay = new Ray(head.position, head.forward); - m_CurrentHeadOrientation = head.rotation; - - // We use the gaze ray for certain shader effects - like edge falloff. - Shader.SetGlobalVector("_WorldSpaceRootCameraPosition", m_CurrentGazeRay.origin); - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - if (hasController) - { - if (InputManager.Brush.IsTrackedObjectValid) - { - Transform rAttachPoint = InputManager.m_Instance.GetBrushControllerAttachPoint(); - m_GazeControllerRay.direction = rAttachPoint.forward; - m_GazeControllerRay.origin = rAttachPoint.position; - } - else - { - // If the brush controller isn't tracked, put our controller ray out of the way. - float fBig = 9999999.0f; - m_GazeControllerRay.direction = Vector3.one; - m_GazeControllerRay.origin = new Vector3(fBig, fBig, fBig); - } - - m_GazeControllerRayActivePanel.direction = m_GazeControllerRay.direction; - m_GazeControllerRayActivePanel.origin = m_GazeControllerRay.origin; - m_GazeControllerRayActivePanel.origin -= (m_GazeControllerRayActivePanel.direction * 0.5f); - } - } - - public void UpdateGazeObjectsAnimationState() - { - // Are the panels allowed to be visible? - bool isSixDof = m_ControlsType == ControlsType.SixDofControllers; - if ((!isSixDof) || - (InputManager.Wand.IsTrackedObjectValid && - !m_SketchSurfacePanel.ActiveTool.HidePanels() && - !App.Instance.IsLoading())) - { - // Transition panels according to requested visibility. - m_PanelManager.SetVisible(m_PanelsVisibilityRequested); - } - else - { - // Transition out. - m_PanelManager.SetVisible(false); - } - } - - void UpdateBaseInput() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateBaseInput"); - if (m_ControlsType == ControlsType.SixDofControllers) - { - m_PanelManager.UpdateWandOrientationControls(); - } - - //allow tool scaling if we're not drawing and our input device is active - bool bScaleInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Scale); - bool bScaleCommandActive = - bScaleInputActive - && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) - && m_GrabBrush.grabbingWorld == false - && m_CurrentGazeObject == -1 // free up swipe for use by gaze object - && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) - // TODO:Mikesky - very hacky - && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; - - if (m_EatToolScaleInput) - { - m_EatToolScaleInput = bScaleInputActive; - } - - if (bScaleCommandActive && !m_EatToolScaleInput) - { - if (m_GrabWidgetState == GrabWidgetState.None) - { - //send scale command down to current tool - m_SketchSurfacePanel.UpdateToolSize( - m_AdjustToolSizeScalar * InputManager.m_Instance.GetAdjustedBrushScrollAmount()); - } - - //ugly, but brush size is becoming not an input state - m_MouseDeltaX = 0.0f; - m_MouseDeltaY = 0.0f; - } - - UpdateSwapControllers(); - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateSwapControllers() - { - // Don't allow controller swap in first run intro. - // Don't allow controller swap if we're grabbing a widget. - // Don't allow controller swap if a Logitech pen is present. - if (!TutorialManager.m_Instance.TutorialActive() && - m_GrabWidgetState == GrabWidgetState.None && - !App.VrSdk.VrControls.LogitechPenIsPresent()) - { - if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.SwapControls)) - { - DoSwapControls(); - } - } - } - - public static void DoSwapControls() - { - InputManager.m_Instance.WandOnRight = !InputManager.m_Instance.WandOnRight; - InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Brush) - .DisplayControllerSwapAnimation(); - InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Wand) - .DisplayControllerSwapAnimation(); - AudioManager.m_Instance.PlayControllerSwapSound( - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); - } - - void UpdateStandardInput() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateStandardInput"); - //debug keys - if (App.UserConfig.Flags.AdvancedKeyboardShortcuts) - { - var camTool = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; - - if (InputManager.m_Instance.GetKeyboardShortcutDown(InputManager.KeyboardShortcut.SaveNew)) - { - IssueGlobalCommand(GlobalCommands.SaveNew, 1); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.SwitchCamera) && camTool != null) - { - camTool.ExternalObjectNextCameraStyle(); // For monoscopic mode - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ViewOnly)) - { - IssueGlobalCommand(GlobalCommands.ViewOnly); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleScreenMirroring)) - { - ViewpointScript.m_Instance.ToggleScreenMirroring(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.PreviousTool)) - { - m_SketchSurfacePanel.PreviousTool(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.NextTool)) - { - m_SketchSurfacePanel.NextTool(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.CycleSymmetryMode)) - { - var cur = PointerManager.m_Instance.CurrentSymmetryMode; - var next = (cur == SymmetryMode.None) ? SymmetryMode.SinglePlane - : (cur == SymmetryMode.SinglePlane) ? SymmetryMode.DebugMultiple - : (cur == SymmetryMode.DebugMultiple) ? SymmetryMode.MultiMirror - : (cur == SymmetryMode.MultiMirror) ? SymmetryMode.TwoHanded - : SymmetryMode.None; - PointerManager.m_Instance.CurrentSymmetryMode = next; - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.Export)) - { - StartCoroutine(ExportCoroutine()); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.StoreHeadTransform) && - InputManager.m_Instance.GetAnyShift()) - { - Transform head = ViewpointScript.Head; - PlayerPrefs.SetFloat("HeadOffset_localPositionX", head.localPosition.x); - PlayerPrefs.SetFloat("HeadOffset_localPositionY", head.localPosition.y); - PlayerPrefs.SetFloat("HeadOffset_localPositionZ", head.localPosition.z); - PlayerPrefs.SetFloat("HeadOffset_localRotationX", head.localRotation.x); - PlayerPrefs.SetFloat("HeadOffset_localRotationY", head.localRotation.y); - PlayerPrefs.SetFloat("HeadOffset_localRotationZ", head.localRotation.z); - PlayerPrefs.SetFloat("HeadOffset_localRotationW", head.localRotation.w); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.RecallHeadTransform)) - { - Transform head = ViewpointScript.Head; - // Toggle the head offset. - if (m_HeadOffset) - { - // Remove the offset. - Transform originalParent = head.parent; - head.SetParent(head.parent.parent); - GameObject.DestroyImmediate(originalParent.gameObject); - m_HeadOffset = false; - } - else - { - // Add the offset. - GameObject newParent = new GameObject(); - newParent.transform.SetParent(head.parent); - newParent.transform.localPosition = Vector3.zero; - newParent.transform.localRotation = Quaternion.identity; - newParent.transform.localScale = Vector3.one; - head.SetParent(newParent.transform); - TrTransform offsetTransform = TrTransform.TR( - new Vector3( - PlayerPrefs.GetFloat("HeadOffset_localPositionX", 0), - PlayerPrefs.GetFloat("HeadOffset_localPositionY", 1.5f), - PlayerPrefs.GetFloat("HeadOffset_localPositionZ", 0)), - new Quaternion( - PlayerPrefs.GetFloat("HeadOffset_localRotationX", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationY", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationZ", 0), - PlayerPrefs.GetFloat("HeadOffset_localRotationW", 1))); - TrTransform originalTransformInverse = TrTransform.FromLocalTransform(head).inverse; - TrTransform newParentTransform = offsetTransform * originalTransformInverse; - newParent.transform.localPosition = newParentTransform.translation; - newParent.transform.localRotation = newParentTransform.rotation; - m_HeadOffset = true; - } - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleLightType)) - { - // Toggle between per-pixel & SH lighting on the secondary directional light - Light secondaryLight = App.Scene.GetLight((1)); - if (LightRenderMode.ForceVertex == secondaryLight.renderMode) - { - secondaryLight.renderMode = LightRenderMode.ForcePixel; - } - else - { - secondaryLight.renderMode = LightRenderMode.ForceVertex; - } - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.TossWidget)) - { - m_WidgetManager.TossNearestWidget(); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.Reset)) - { - App.Instance.SetDesiredState(App.AppState.LoadingBrushesAndLighting); - } - else if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.FlyMode)) - { - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.FlyTool); - } - else if (App.Config.m_ToggleProfileOnAppButton && - (InputManager.Wand.GetVrInputDown(VrInput.Button03) || - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ToggleProfile))) - { - IssueGlobalCommand(GlobalCommands.ToggleProfiling); - } - } - -#if DEBUG - if (InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.CheckStrokes)) - { - bool value = !SketchMemoryScript.m_Instance.m_SanityCheckStrokes; - string feature = "Stroke determinism checking"; - SketchMemoryScript.m_Instance.m_SanityCheckStrokes = value; - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - feature + (value ? ": On" : ": Off")); - } -#endif - - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - var mouse = Mouse.current; - - // Toggle default tool. - if (!m_PanelManager.AdvancedModeActive() && - InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && - !m_SketchSurfacePanel.IsDefaultToolEnabled() && - m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && - // don't allow tool to change while pointing at panel because there is no visual indication - m_CurrentGazeObject == -1) - { - m_SketchSurfacePanel.EnableDefaultTool(); - AudioManager.m_Instance.PlayPinCushionSound(true); - } - // Pan. - else if (!hasController && mouse.rightButton.isPressed) - { - SwitchState(InputState.Pan); - } - // Controller lock (this must be before rotate/head lock!). - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - SwitchState(InputState.ControllerLock); - } - // Rotate. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) - { - SwitchState(InputState.Rotation); - } - // Head lock. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) - { - SwitchState(InputState.HeadLock); - } - // Push pull. - else if (!hasController && - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate)) - { - SwitchState(InputState.PushPull); - } - else if (!PointerManager.m_Instance.IsMainPointerCreatingStroke()) - { - // Reset surface. - if (!hasController && - InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Reset)) - { - ResetGrabbedPose(); - } - // Undo. - else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Undo) && - CanUndo()) - { - IssueGlobalCommand(GlobalCommands.Undo); - } - else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo) && - CanUndo() && ShouldRepeatUndo()) - { - m_UndoHold_Timer = m_UndoRedoHold_RepeatInterval; - IssueGlobalCommand(GlobalCommands.Undo); - } - // Redo. - else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Redo) && - CanRedo()) - { - IssueGlobalCommand(GlobalCommands.Redo); - } - else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo) && - CanRedo() && ShouldRepeatRedo()) - { - m_RedoHold_Timer = m_UndoRedoHold_RepeatInterval; - IssueGlobalCommand(GlobalCommands.Redo); - } - // Reset scene. - else if (!hasController && - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.ResetScene)) - { - // TODO: Should thsi go away? Seems like the "sweetspot" may no longer be used. - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Two) - { - m_PanelManager.SetSweetSpotPosition(m_CurrentGazeRay.origin); - ResetGrabbedPose(); - } - } - // Straight edge. - else if (!hasController && - InputManager.m_Instance.GetKeyboardShortcutDown( - InputManager.KeyboardShortcut.StraightEdge)) - { - IssueGlobalCommand(GlobalCommands.StraightEdge); - } - // Always fall back on switching tools. - else - { - m_SketchSurfacePanel.CheckForToolSelection(); - } - } - - // Reset undo/redo hold timers. - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo)) - { - m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - } - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo)) - { - m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - bool CanUndo() - { - return SketchMemoryScript.m_Instance.CanUndo() && - !IsUIBlockingUndoRedo() && - m_PanelManager.GazePanelsAreVisible() && - !m_GrabWand.grabbingWorld && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - bool CanRedo() - { - return SketchMemoryScript.m_Instance.CanRedo() && - !IsUIBlockingUndoRedo() && - m_PanelManager.GazePanelsAreVisible() && - !m_GrabBrush.grabbingWorld && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; - } - - bool ShouldRepeatUndo() - { - m_UndoHold_Timer -= Time.deltaTime; - return (m_UndoHold_Timer <= 0.0f); - } - - bool ShouldRepeatRedo() - { - m_RedoHold_Timer -= Time.deltaTime; - return (m_RedoHold_Timer <= 0.0f); - } - - // Updates the global state: - // m_CurrentGrabWidget - void UpdateGrab() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateGrab"); - if (m_ControlsType != ControlsType.SixDofControllers) - { - UnityEngine.Profiling.Profiler.EndSample(); - return; - } - - GrabWidget rPrevGrabWidget = m_CurrentGrabWidget; - GrabWidget rPrevPotentialBrush = m_PotentialGrabWidgetBrush; - GrabWidget rPrevPotentialWand = m_PotentialGrabWidgetWand; - if (m_CurrentGrabWidget) - { - m_CurrentGrabWidget.Activate(false); - } - if (m_PotentialGrabWidgetBrush) - { - m_PotentialGrabWidgetBrush.Activate(false); - } - if (m_PotentialGrabWidgetWand) - { - m_PotentialGrabWidgetWand.Activate(false); - } - m_CurrentGrabWidget = null; - m_PotentialGrabWidgetBrush = null; - m_PotentialGrabWidgetWand = null; - m_PotentialGrabWidgetBrushValid = false; - m_PotentialGrabWidgetWandValid = false; - - m_WidgetManager.RefreshNearestWidgetLists(m_CurrentGazeRay, m_CurrentGazeObject); - - if (m_GrabWidgetState == GrabWidgetState.None) - { - UpdateGrab_WasNone(rPrevPotentialBrush, rPrevPotentialWand); - } - else if (m_GrabWidgetState == GrabWidgetState.OneHand) - { - UpdateGrab_WasOneHand(rPrevGrabWidget); - } - else if (m_GrabWidgetState == GrabWidgetState.TwoHands) - { - UpdateGrab_WasTwoHands(rPrevGrabWidget); - } - - // Update grab intersection state. - switch (m_CurrentGrabIntersectionState) - { - case GrabIntersectionState.RequestIntersections: - m_CurrentGrabIntersectionState = GrabIntersectionState.ReadBrush; - break; - case GrabIntersectionState.ReadBrush: - m_CurrentGrabIntersectionState = GrabIntersectionState.ReadWand; - break; - case GrabIntersectionState.ReadWand: - m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; - break; - } - - if (!TutorialManager.m_Instance.TutorialActive() && m_CurrentGrabWidget == null) - { - UpdateGrab_World(); - } - - App.Instance.SelectionEffect.HighlightForGrab( - m_GrabWidgetState != GrabWidgetState.None || - (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || - (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid)); - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateGrab_WasNone(GrabWidget rPrevPotentialBrush, GrabWidget rPrevPotentialWand) - { - // if a panel isn't in focus, allow for widget grab - // We can grab a widget as long as we aren't trying to draw with that hand. - bool bActiveInput = - (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && - App.Instance.IsInStateThatAllowsPainting()); - - //certain tools don't allow us to mess with widgets - bool bWidgetManipOK = m_SketchSurfacePanel.DoesCurrentToolAllowWidgetManipulation() && - !m_GrabWand.grabbingWorld && !m_GrabBrush.grabbingWorld && IsGrabWorldStateStable() && - App.Instance.IsInStateThatAllowsAnyGrabbing(); - - // Update EatInput flags if they're valid. - if (m_GrabBrush.eatInput) - { - m_GrabBrush.eatInput = InputManager.Brush.GetControllerGrip(); - } - if (m_GrabWand.eatInput) - { - m_GrabWand.eatInput = InputManager.Wand.GetControllerGrip(); - } - - bool bShouldClearWandInside = false; - if (m_CurrentInputState == InputState.Standard && bWidgetManipOK) - { - // If we're in the intersection request state, fire off a new intersection request. If - // we're in the read brush state, update our brush grab data structure. - List brushBests = m_WidgetManager.WidgetsNearBrush; - if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) - { - RequestWidgetIntersection(brushBests, InputManager.ControllerName.Brush); - } - else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadBrush) - { - m_BackupBrushGrabData = GetBestWidget(brushBests, m_BrushResults); - } - - if (m_BackupBrushGrabData != null) - { - m_PotentialGrabWidgetBrush = m_BackupBrushGrabData.m_WidgetScript; - - // Allow widget grab if we're not painting. - if (!bActiveInput) - { - m_PotentialGrabWidgetBrush.Activate(true); - m_PotentialGrabWidgetBrushValid = true; - m_PotentialGrabWidgetBrush.VisualizePinState(); - - if (!m_GrabBrush.eatInput && InputManager.Brush.GetControllerGrip()) - { - m_CurrentGrabWidget = m_PotentialGrabWidgetBrush; - if (m_CurrentGrabWidget.Group != SketchGroupTag.None) - { - m_GrabBrush.grabbingGroup = true; - m_CurrentGrabWidget = - SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); - } - UpdateGrab_NoneToOne(InputManager.ControllerName.Brush); - bShouldClearWandInside = true; - m_GrabBrush.startedGrabInsideWidget = true; - } - } - } - m_GrabBrush.SetHadBestGrabAndTriggerHaptics(m_BackupBrushGrabData); - m_ControllerGrabVisuals.BrushInWidgetRange = m_BackupBrushGrabData != null; - - // If we're in the intersection request state, fire off a new intersection request. If - // we're in the read wand state, update our wand grab data structure. - List wandBests = m_WidgetManager.WidgetsNearWand; - if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) - { - RequestWidgetIntersection(wandBests, InputManager.ControllerName.Wand); - } - else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadWand) - { - m_BackupWandGrabData = GetBestWidget(wandBests, m_WandResults); - } - - if (m_BackupWandGrabData != null) - { - m_PotentialGrabWidgetWand = m_BackupWandGrabData.m_WidgetScript; - // Allow wand widget grab if brush grab failed. - bool bGrabAllowed = (m_GrabWidgetState == GrabWidgetState.None) && !bActiveInput; - if (bGrabAllowed) - { - m_PotentialGrabWidgetWand.Activate(true); - m_PotentialGrabWidgetWandValid = true; - m_PotentialGrabWidgetWand.VisualizePinState(); - - if (!m_GrabWand.eatInput && InputManager.Wand.GetControllerGrip()) - { - m_CurrentGrabWidget = m_PotentialGrabWidgetWand; - if (m_CurrentGrabWidget.Group != SketchGroupTag.None) - { - m_GrabWand.grabbingGroup = true; - m_CurrentGrabWidget = - SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); - } - UpdateGrab_NoneToOne(InputManager.ControllerName.Wand); - m_GrabBrush.ClearInsideWidget(); - m_GrabWand.startedGrabInsideWidget = true; - } - } - } - m_GrabWand.SetHadBestGrabAndTriggerHaptics(m_BackupWandGrabData); - m_ControllerGrabVisuals.WandInWidgetRange = m_BackupWandGrabData != null; - - // Account for asymmetry in controller processing by clearing after wand has updated - // GrabState.insideWidget according to bestWandGrab. - if (bShouldClearWandInside) - { - m_GrabWand.ClearInsideWidget(); - } - } - - // Update widget collisions if we've got a drifter. - if (m_GrabWidgetState == GrabWidgetState.None) - { - if (m_WidgetManager.ShouldUpdateCollisions()) - { - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - } - } - } - - void UpdateGrab_WasOneHand(GrabWidget rPrevGrabWidget) - { - var controller = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; - bool shouldRelease = !App.Instance.IsInStateThatAllowsAnyGrabbing(); - if (!InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetControllerGrip() || - shouldRelease) - { - if (shouldRelease) - { - EatGrabInput(); - } - - Vector3 vLinearVelocity; - Vector3 vAngularVelocity; - if (GetGrabWidgetHoldHistory(out vLinearVelocity, out vAngularVelocity)) - { - rPrevGrabWidget.SetVelocities( - vLinearVelocity, vAngularVelocity, - controller.Transform.position); - } - // One -> None - UpdateGrab_ToNone(rPrevGrabWidget); - } - else - { - // Keep holding on to our widget. - m_CurrentGrabWidget = rPrevGrabWidget; - m_CurrentGrabWidget.Activate(true); - m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); - - if (!m_CurrentGrabWidget.Pinned) - { - var info = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; - var controllerXf = Coords.AsGlobal[info.Transform]; - var newWidgetXf = controllerXf * m_GrabWidgetOneHandInfo.m_BaseWidgetXf_LS; - m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); - - UpdateGrabWidgetHoldHistory(m_GrabWidgetOneHandInfo.m_Name); - } - - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - - // Check for widget pinning. - if (m_CurrentGrabWidget.AllowPinning) - { - if (InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( - InputManager.SketchCommands.PinWidget)) - { - // If the user initiates a pin action, buzz a bit. - if (!m_CurrentGrabWidget.Pinned) - { - InputManager.m_Instance.TriggerHapticsPulse( - m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); - } - m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); - SketchSurfacePanel.m_Instance.EatToolsInput(); - m_WidgetManager.RefreshPinAndUnpinLists(); - } - } - - if (m_CurrentGrabWidget is SelectionWidget) - { - if (InputManager.m_Instance.GetCommandDown( - InputManager.SketchCommands.DuplicateSelection)) - { - controller.LastHeldInput = - controller.GetCommandHoldInput(InputManager.SketchCommands.DuplicateSelection); - } - - if (controller.LastHeldInput != null && - InputManager.m_Instance.GetCommandHeld(InputManager.SketchCommands.DuplicateSelection)) - { - SketchControlsScript.m_Instance.IssueGlobalCommand( - SketchControlsScript.GlobalCommands.Duplicate); - } - } - - InputManager.ControllerName otherName = - (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? - InputManager.ControllerName.Wand : InputManager.ControllerName.Brush; - bool otherInputEaten = - (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? - m_GrabWand.eatInput : m_GrabBrush.eatInput; - - // See if the other controller decides to grab the widget (unless we're pinned). - if (!m_CurrentGrabWidget.Pinned) - { - if (m_CurrentGrabWidget.AllowTwoHandGrab) - { - if (InputManager.Controllers[(int)otherName].GetControllerGrip()) - { - RequestPanelsVisibility(false); - m_GrabWidgetState = GrabWidgetState.TwoHands; - // Figure out if the new grab starts inside the widget. - Vector3 vOtherGrabPos = TrTransform.FromTransform( - InputManager.m_Instance.GetController(otherName)).translation; - bool bOtherGrabInBounds = m_CurrentGrabWidget.GetActivationScore( - vOtherGrabPos, otherName) >= 0; - m_CurrentGrabWidget.SetUserTwoHandGrabbing( - true, m_GrabWidgetOneHandInfo.m_Name, otherName, bOtherGrabInBounds); - - if (otherName == InputManager.ControllerName.Brush) - { - m_GrabBrush.startedGrabInsideWidget = bOtherGrabInBounds; - } - else - { - m_GrabWand.startedGrabInsideWidget = bOtherGrabInBounds; - } - - m_GrabWidgetTwoHandBrushPrev = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); - m_GrabWidgetTwoHandWandPrev = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); - } - } - } - else if (!otherInputEaten && InputManager.Controllers[(int)otherName].GetControllerGrip()) - { - // If it's a two hand grab but the current grab widget is pinned, grab the world. - UpdateGrab_ToNone(m_CurrentGrabWidget); - m_CurrentGrabWidget = null; - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - } - } - } - - // Previous frame was a two-handed grab. - // Handles all the cases where this frame's grab is zero, one, or two hands. - void UpdateGrab_WasTwoHands(GrabWidget rPrevGrabWidget) - { - //keep holding on to our widget - m_CurrentGrabWidget = rPrevGrabWidget; - m_CurrentGrabWidget.Activate(true); - m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); - - if (!App.Instance.IsInStateThatAllowsAnyGrabbing()) - { - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - UpdateGrab_ToNone(rPrevGrabWidget); - } - else if (!InputManager.Wand.GetControllerGrip()) - { // Look for button release. - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - // See if our Brush hand is still within grab range of the widget. - if (m_GrabBrush.startedGrabInsideWidget || - IsControllerNearWidget(InputManager.ControllerName.Brush, m_CurrentGrabWidget)) - { - m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Brush; - RequestPanelsVisibility(true); - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - } - else - { - // If the Brush hand is beyond the widget, we're not holding it anymore. - UpdateGrab_ToNone(rPrevGrabWidget); - - // Eat input on the brush grip until we release the button. - m_GrabBrush.eatInput = true; - } - } - else if (!InputManager.Brush.GetControllerGrip()) - { - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - if (m_GrabWand.startedGrabInsideWidget || - IsControllerNearWidget(InputManager.ControllerName.Wand, m_CurrentGrabWidget)) - { - m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Wand; - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - } - else - { - UpdateGrab_ToNone(rPrevGrabWidget); - m_GrabWand.eatInput = true; - } - } - else - { - // Both hands still grabbing. - // Check for pin, which forcibly releases one of the hands. - if (m_CurrentGrabWidget.AllowPinning && - InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( - InputManager.SketchCommands.PinWidget)) - { - // If the user initiates a pin action, buzz a bit. - if (!m_CurrentGrabWidget.Pinned) - { - InputManager.m_Instance.TriggerHapticsPulse( - m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); - } - - m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); - SketchSurfacePanel.m_Instance.EatToolsInput(); - m_WidgetManager.RefreshPinAndUnpinLists(); - - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - m_GrabWidgetState = GrabWidgetState.OneHand; - m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); - - // Eat input on the off hand so we don't immediately jump in to world transform. - if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) - { - RequestPanelsVisibility(true); - m_GrabWand.eatInput = true; - } - else - { - m_GrabBrush.eatInput = true; - } - } - - if (!m_CurrentGrabWidget.Pinned) - { - UpdateGrab_ContinuesTwoHands(); - } - } - ClearGrabWidgetHoldHistory(); - m_PanelManager.DoCollisionSimulationForWidgetPanels(); - } - - // Common case for two-handed grab: both the previous and current frames are two-handed. - private void UpdateGrab_ContinuesTwoHands() - { - //holding with two hands, transform accordingly - TrTransform xfBrush = TrTransform.FromTransform(InputManager.Brush.Transform); - TrTransform xfWand = TrTransform.FromTransform(InputManager.Wand.Transform); - Vector2 vSizeRange = m_CurrentGrabWidget.GetWidgetSizeRange(); - - GrabWidget.Axis axis = m_CurrentGrabWidget.GetScaleAxis( - xfWand.translation, xfBrush.translation, - out Vector3 axisDirection, out float axisExtent); - - TrTransform newWidgetXf; - if (axis != GrabWidget.Axis.Invalid) - { - // Scale along a single axis - float deltaScale; - if (App.Config.m_AxisManipulationIsResize) - { - newWidgetXf = MathUtils.TwoPointObjectTransformationAxisResize( - axisDirection, axisExtent, - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - GetWorkingTransform(m_CurrentGrabWidget), - out deltaScale, - deltaScaleMin: vSizeRange.x / axisExtent, - deltaScaleMax: vSizeRange.y / axisExtent); - } - else - { - newWidgetXf = MathUtils.TwoPointObjectTransformationNonUniformScale( - axisDirection, - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - GetWorkingTransform(m_CurrentGrabWidget), - out deltaScale, - finalScaleMin: vSizeRange.x, - deltaScaleMin: vSizeRange.x / axisExtent, - deltaScaleMax: vSizeRange.y / axisExtent); - } - - // The above functions return undefined values in newWidgetXf.scale; but that's - // okay because RecordAndSetPosRot ignores xf.scale. - // TODO: do this more cleanly - m_CurrentGrabWidget.RecordAndApplyScaleToAxis(deltaScale, axis); - } - else - { - // Uniform scaling - TrTransform xfObject = GetWorkingTransform(m_CurrentGrabWidget); - Vector3 extents = (m_CurrentGrabWidget is StencilWidget) - ? (m_CurrentGrabWidget as StencilWidget).Extents - : Vector3.one * Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); - - // Delta-scale bounds should be based on the smallest/largest extent. - // Irritatingly, the API wants absolute rather than relative scale bounds, - // so they need even more conversion. - float deltaScaleMin = vSizeRange.x / extents.Min(); - float deltaScaleMax = vSizeRange.y / extents.Max(); - if (m_GrabWand.startedGrabInsideWidget && m_GrabBrush.startedGrabInsideWidget) - { - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); - } - else if (m_GrabWand.startedGrabInsideWidget) - { - // keep the wand inside the object - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, - xfWand, xfBrush, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, - bUseLeftAsPivot: true); - } - else - { - // keep the brush inside the object (note the brush is the left hand) - newWidgetXf = MathUtils.TwoPointObjectTransformation( - m_GrabWidgetTwoHandBrushPrev, m_GrabWidgetTwoHandWandPrev, - xfBrush, xfWand, - xfObject, - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, - bUseLeftAsPivot: true); - } - - // Must do separately becvause RecordAndSetPosRot ignores newWidgetXf.scale - m_CurrentGrabWidget.RecordAndSetSize(newWidgetXf.scale); - - float currentSize = Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); - if (currentSize == vSizeRange.x || currentSize == vSizeRange.y) - { - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.05f); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); - } - } - - // Ignores TrTransform.scale - m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); - - m_GrabWidgetTwoHandBrushPrev = xfBrush; - m_GrabWidgetTwoHandWandPrev = xfWand; - } - - void UpdateGrab_NoneToOne(InputManager.ControllerName controllerName) - { - if (m_MaybeDriftingGrabWidget != null && - m_MaybeDriftingGrabWidget.IsMoving() && - !m_MaybeDriftingGrabWidget.IsSpinningFreely) - { - // If a new widget is grabbed but the previous one is still drifting, end the drift. - // TODO: Simplify in the widget animation cleanup. - if (m_MaybeDriftingGrabWidget == m_CurrentGrabWidget) - { - SketchMemoryScript.m_Instance.PerformAndRecordCommand( - new MoveWidgetCommand(m_MaybeDriftingGrabWidget, - m_MaybeDriftingGrabWidget.LocalTransform, m_MaybeDriftingGrabWidget.CustomDimension, - final: true), - discardIfNotMerged: true); - } - m_MaybeDriftingGrabWidget.ClearVelocities(); - } - - // UserInteracting should be the first thing that happens here so OnUserBeginInteracting can - // be called before everything else. - m_CurrentGrabWidget.UserInteracting(true, controllerName); - m_CurrentGrabWidget.ClearVelocities(); - ClearGrabWidgetHoldHistory(); - - //set our info names according to this controller's name - m_GrabWidgetOneHandInfo.m_Name = controllerName; - InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); - - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.RequestPointerRendering(false); - m_SketchSurfacePanel.RequestHideActiveTool(true); - if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Wand) - { - RequestPanelsVisibility(false); - } - - // Notify visuals. - ControllerGrabVisuals.VisualState visualState = - m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush ? - ControllerGrabVisuals.VisualState.WidgetBrushGrip : - ControllerGrabVisuals.VisualState.WidgetWandGrip; - m_ControllerGrabVisuals.SetDesiredVisualState(visualState); - m_ControllerGrabVisuals.SetHeldWidget(m_CurrentGrabWidget.transform); - - //if a gaze object had focus when we grabbed this widget, take focus off the object - ResetActivePanel(); - m_UIReticle.SetActive(false); - - // Prep all other grab widgets for collision. - m_PanelManager.PrimeCollisionSimForWidgets(m_CurrentGrabWidget); - - m_GrabWidgetState = GrabWidgetState.OneHand; - m_WidgetManager.WidgetsDormant = false; - PointerManager.m_Instance.EatLineEnabledInput(); - - m_BackupWandGrabData = null; - m_BackupBrushGrabData = null; - } - - void UpdateGrab_ToNone(GrabWidget rPrevGrabWidget) - { - m_MaybeDriftingGrabWidget = rPrevGrabWidget; - - m_GrabWidgetState = GrabWidgetState.None; - PointerManager.m_Instance.RequestPointerRendering(!App.Instance.IsLoading() && - m_SketchSurfacePanel.ShouldShowPointer()); - RequestPanelsVisibility(true); - m_SketchSurfacePanel.RequestHideActiveTool(false); - rPrevGrabWidget.UserInteracting(false); - - // Disable grab visuals. - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - m_ControllerGrabVisuals.SetHeldWidget(null); - - if (m_GrabBrush.grabbingGroup || m_GrabWand.grabbingGroup) - { - SelectionManager.m_Instance.EndGrabbingGroupWithWidget(); - m_GrabBrush.grabbingGroup = false; - m_GrabWand.grabbingGroup = false; - } - } - - void RequestWidgetIntersection(List candidates, - InputManager.ControllerName controllerName) - { - // Get locals based off what controller we're using. - Queue resultQueue = null; - Vector3 controllerPos = Vector3.zero; - if (controllerName == InputManager.ControllerName.Brush) - { - resultQueue = m_BrushResults; - controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; - } - else - { - resultQueue = m_WandResults; - controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; - } - - // If we don't have a candidate that has a GPU object, don't bother firing off a GPU request. - bool requestGpuIntersection = false; - - // Fire off a new GPU intersection with all widgets that can use it. - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - candidates[i].m_WidgetScript.SetGPUIntersectionObjectLayer(m_WidgetGpuIntersectionLayer); - requestGpuIntersection = true; - } - } - - if (requestGpuIntersection) - { - GpuIntersectionResult newRequest = new GpuIntersectionResult(); - newRequest.resultList = new List(); - newRequest.result = App.Instance.GpuIntersector.RequestModelIntersections( - controllerPos, m_WidgetGpuIntersectionRadius, newRequest.resultList, 8, - (1 << m_WidgetGpuIntersectionLayer)); - - // The new result will only be null when the intersector is disabled. - if (newRequest.result != null) - { - resultQueue.Enqueue(newRequest); - } - - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - candidates[i].m_WidgetScript.RestoreGPUIntersectionObjectLayer(); - } - } - } - } - - GrabWidgetData GetBestWidget(List candidates, - Queue resultQueue) - { - // Discard futures that are too old. - while (resultQueue.Count > 0) - { - if (Time.frameCount - resultQueue.Peek().result.StartFrame < 5) - { - break; - } - resultQueue.Dequeue(); - } - - // If the oldest future is ready, use its intersection result to update the candidates. - GpuIntersectionResult finishedResult; - if (resultQueue.Count > 0 && resultQueue.Peek().result.IsReady) - { - finishedResult = resultQueue.Dequeue(); - } - else - { - finishedResult.resultList = new List(); - } - - // TODO: Speed this up. - for (int i = 0; i < candidates.Count; ++i) - { - if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) - { - // If a candidate can't find itself in the finished results list, it's not eligible. - bool candidateValid = false; - for (int j = 0; j < finishedResult.resultList.Count; ++j) - { - if (candidates[i].m_WidgetScript.Equals(finishedResult.resultList[j].widget)) - { - candidateValid = true; - break; - } - } - - if (candidateValid) - { - // If a candidate has a GPU intersection object and we found it in this list, - // not only is it valid, but it's as valid as it can be. - candidates[i].m_ControllerScore = 1.0f; - } - else - { - candidates[i].m_NearController = false; - } - } - } - - // Run through the candidates and pick - GrabWidgetData best = null; - for (int i = 0; i < candidates.Count; ++i) - { - var candidate = candidates[i]; - if (!candidate.m_NearController) continue; - - // For media widgets - only select from the active layer - if (candidate.m_WidgetScript is MediaWidget - && candidate.m_WidgetScript.Canvas != App.Scene.ActiveCanvas) continue; - - if (best == null || candidate.m_ControllerScore > best.m_ControllerScore) - { - best = candidate; - } - } - return best; - } - - void InitializeGrabWidgetControllerInfo(GrabWidgetControllerInfo info) - { - Transform controller = InputManager.Controllers[(int)info.m_Name].Transform; - Transform widget = m_CurrentGrabWidget.GrabTransform_GS; - TrTransform newWidgetXf = Coords.AsGlobal[widget]; - - info.m_BaseControllerXf = Coords.AsGlobal[controller]; - info.m_BaseWidgetXf_LS = info.m_BaseControllerXf.inverse * newWidgetXf; - } - - // returns the transform of the true widget (not the snapped one for those that can be) - private TrTransform GetWorkingTransform(GrabWidget w) - { - TrTransform ret = w.GetGrabbedTrTransform(); - ret.scale = w.GetSignedWidgetSize(); - return ret; - } - - // Initiate the world transform reset animation. - public void RequestWorldTransformReset(bool toSavedXf = false) - { - if (WorldIsReset(toSavedXf)) - { - return; - } - - m_WorldTransformResetXf = - toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity; - m_WorldTransformResetState = WorldTransformResetState.Requested; - - App.Scene.disableTiltProtection = false; - } - - void UpdateWorldTransformReset() - { - switch (m_WorldTransformResetState) - { - case WorldTransformResetState.Requested: - ViewpointScript.m_Instance.FadeToColor(Color.black, m_GrabWorldFadeSpeed); - m_WorldTransformResetState = WorldTransformResetState.FadingToBlack; - m_xfDropCamReset_RS = Coords.AsRoom[m_DropCam.transform]; - PointerManager.m_Instance.EatLineEnabledInput(); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - break; - case WorldTransformResetState.FadingToBlack: - m_WorldTransformFadeAmount += m_GrabWorldFadeSpeed * Time.deltaTime; - if (m_WorldTransformFadeAmount >= 1.0f) - { - App.Scene.Pose = m_WorldTransformResetXf; - m_WorldTransformFadeAmount = 1.0f; - m_WorldTransformResetState = WorldTransformResetState.FadingToScene; - ViewpointScript.m_Instance.FadeToScene(m_GrabWorldFadeSpeed); - m_DropCam.transform.position = m_xfDropCamReset_RS.translation; - m_DropCam.transform.rotation = m_xfDropCamReset_RS.rotation; - PointerManager.m_Instance.AllowPointerPreviewLine(true); - } - break; - case WorldTransformResetState.FadingToScene: - m_WorldTransformFadeAmount -= m_GrabWorldFadeSpeed * Time.deltaTime; - if (m_WorldTransformFadeAmount <= 0.0f) - { - m_WorldTransformFadeAmount = 0.0f; - m_WorldTransformResetState = WorldTransformResetState.Default; - } - break; - } - } - - bool CheckToggleTiltProtection() - { - if ( - InputManager.Wand.GetCommandDown(InputManager.SketchCommands.Redo) || - InputManager.Brush.GetCommandDown(InputManager.SketchCommands.Redo) - ) - { - App.Scene.disableTiltProtection = !App.Scene.disableTiltProtection; - - return !App.Scene.disableTiltProtection; - } - - return false; - - } - - void UpdateGrab_World() - { - bool bAllowWorldTransform = m_SketchSurfacePanel.ActiveTool.AllowWorldTransformation() && - (m_GrabWorldState != GrabWorldState.ResetDone) && - (!PointerManager.m_Instance.IsMainPointerCreatingStroke() || App.Instance.IsLoading()) && - App.Instance.IsInStateThatAllowsAnyGrabbing() && - !m_DisableWorldGrabbing; - - bool bWorldGrabWandPrev = m_GrabWand.grabbingWorld; - bool bWorldGrabBrushPrev = m_GrabBrush.grabbingWorld; - m_GrabWand.grabbingWorld = bAllowWorldTransform && !m_GrabWand.eatInput && - InputManager.Wand.GetControllerGrip(); - m_GrabBrush.grabbingWorld = bAllowWorldTransform && !m_GrabBrush.eatInput && - InputManager.Brush.GetControllerGrip() && - (m_CurrentGazeObject == -1); - - bool grabsChanged = (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) || - (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld); - bool bAllowWorldTransformChanged = - bAllowWorldTransform != m_AllowWorldTransformLastFrame; - int nGrabs = m_GrabWand.grabbingWorld ? 1 : 0; - nGrabs += m_GrabBrush.grabbingWorld ? 1 : 0; - - // Allow grabbing again if grabs have changed and we're done resetting. - if (m_GrabWorldState == GrabWorldState.ResetDone && grabsChanged) - { - m_GrabWorldState = GrabWorldState.Normal; - } - - // Update panels visibility if brush grip has changed. - if (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) - { - RequestPanelsVisibility(!m_GrabWand.grabbingWorld); - } - - // Update tool visibility if brush grip has changed. - if (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld) - { - m_SketchSurfacePanel.RequestHideActiveTool(m_GrabBrush.grabbingWorld); - PointerManager.m_Instance.AllowPointerPreviewLine(!m_GrabBrush.grabbingWorld); - PointerManager.m_Instance.RequestPointerRendering(!m_GrabBrush.grabbingWorld - && m_SketchSurfacePanel.ShouldShowPointer() && !App.Instance.IsLoading()); - } - - // Reset m_WorldBeingGrabbed and only set it when world is actually being grabbed. - bool bWorldBeingGrabbedPrev = m_WorldBeingGrabbed; - m_WorldBeingGrabbed = false; - - // Move the world if it has been grabbed. - if (m_GrabWorldState == GrabWorldState.Normal && bAllowWorldTransform) - { - if (nGrabs == 2) - { - // Two-handed world movement. - m_WorldBeingGrabbed = true; - TrTransform grabXfWand = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); - TrTransform grabXfBrush = TrTransform.FromTransform( - InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); - - // Offset the controller positions so that they're centered on the grips. - Vector3 gripPos = InputManager.Controllers[(int)InputManager.ControllerName.Brush].Geometry.GripAttachPoint.localPosition; - gripPos.x = 0.0f; - grabXfWand.translation += grabXfWand.MultiplyVector(gripPos); - grabXfBrush.translation += grabXfBrush.MultiplyVector(gripPos); - - // Are we initiating two hand transform this frame? - if (!bWorldGrabWandPrev || !bWorldGrabBrushPrev) - { - PointerManager.m_Instance.EnableLine(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - PointerManager.m_Instance.RequestPointerRendering(false); - // Initiate audio loop - m_WorldTransformSpeedSmoothed = 0.0f; - AudioManager.m_Instance.WorldGrabLoop(true); - } - else - { - TrTransform xfOld = GrabbedPose; - TrTransform xfNew; - float deltaScaleMin = WorldTransformMinScale / xfOld.scale; - float deltaScaleMax = WorldTransformMaxScale / xfOld.scale; - bool fixOffset = false; - fixOffset = CheckToggleTiltProtection(); - xfNew = MathUtils.TwoPointObjectTransformation( - m_GrabBrush.grabTransform, m_GrabWand.grabTransform, - grabXfBrush, grabXfWand, - xfOld, - rotationAxisConstraint: (App.Scene.disableTiltProtection ? default(Vector3) : Vector3.up), - deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); - float fCurrentWorldTransformSpeed = - Mathf.Abs((xfNew.scale - xfOld.scale) / Time.deltaTime); - m_WorldTransformSpeedSmoothed = - Mathf.Lerp(m_WorldTransformSpeedSmoothed, fCurrentWorldTransformSpeed, - AudioManager.m_Instance.m_WorldGrabLoopSmoothSpeed * Time.deltaTime); - AudioManager.m_Instance.ChangeLoopVolume("WorldGrab", - Mathf.Clamp(m_WorldTransformSpeedSmoothed / - AudioManager.m_Instance.m_WorldGrabLoopAttenuation, 0f, - AudioManager.m_Instance.m_WorldGrabLoopMaxVolume)); - - if (fixOffset) - { - Vector3 midPoint = Vector3.Lerp(grabXfBrush.translation, grabXfWand.translation, 0.5f); - - Vector3 localMidPointOldXF = xfOld.inverse * midPoint; - - // assign this to force the axial protection - GrabbedPose = xfNew; - xfNew = GrabbedPose; - - Vector3 midPointXFNew = xfNew * localMidPointOldXF; - - TrTransform xfDelta1 = TrTransform.T(midPoint - midPointXFNew); - xfNew = xfDelta1 * xfNew; - } - GrabbedPose = xfNew; - } - - // Update last states. - m_GrabBrush.grabTransform = grabXfBrush; - m_GrabWand.grabTransform = grabXfWand; - } - } - else if (m_GrabWorldState == GrabWorldState.ResettingTransform) - { - if (m_WorldTransformResetState == WorldTransformResetState.FadingToScene) - { - ResetGrabbedPose(); - PanelManager.m_Instance.ExecuteOnPanel(x => x.OnPanelMoved()); - - // World can't be transformed right after a reset until grab states have changed. - if (bAllowWorldTransform) - { - bAllowWorldTransform = false; - bAllowWorldTransformChanged = - bAllowWorldTransform != m_AllowWorldTransformLastFrame; - } - - // Set the grab world state on exit. - if (nGrabs == 0) - { - m_GrabWorldState = GrabWorldState.Normal; - } - else - { - m_GrabWorldState = GrabWorldState.ResetDone; - } - } - } - - if (grabsChanged || bAllowWorldTransformChanged) - { - // Fade in grid when doing two handed spin. - if (nGrabs == 2 && !bAllowWorldTransformChanged) - { - ViewpointScript.m_Instance.FadeGroundPlaneIn(m_GrabWorldGridColor, m_GrabWorldFadeSpeed); - } - else - { - ViewpointScript.m_Instance.FadeGroundPlaneOut(m_GrabWorldFadeSpeed); - } - } - - // Update visuals for world transform - if (grabsChanged) - { - bool bDoubleGrip = m_GrabBrush.grabbingWorld && m_GrabWand.grabbingWorld; - bool bSingleGrip = m_GrabBrush.grabbingWorld || m_GrabWand.grabbingWorld; - Vector3 vControllersMidpoint = - (InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush) + - InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Wand)) * 0.5f; - - // Update transform line visuals - if (bDoubleGrip) - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldDoubleGrip); - AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); - } - else if (bSingleGrip) - { - if (m_GrabWand.grabbingWorld) - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldWandGrip); - } - else - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldBrushGrip); - } - - if (!bWorldGrabWandPrev && !bWorldGrabBrushPrev) - { - AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); - } - else - { - AudioManager.m_Instance.WorldGrabLoop(false); - } - } - else - { - m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); - AudioManager.m_Instance.WorldGrabLoop(false); - } - - if (m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld) - { - m_WidgetManager.WidgetsDormant = false; - PointerManager.m_Instance.EatLineEnabledInput(); - } - } - - // Reset scene transform if we're gripping and press the track pad. - bool wandReset = m_GrabWand.grabbingWorld && - InputManager.Wand.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); - bool brushReset = m_GrabBrush.grabbingWorld && - InputManager.Brush.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); - if ((wandReset || brushReset) && !WorldIsReset(toSavedXf: false)) - { - m_GrabBrush.eatInput = true; - m_GrabWand.eatInput = true; - m_EatToolScaleInput = true; - m_GrabWorldState = GrabWorldState.ResettingTransform; - RequestWorldTransformReset(); - AudioManager.m_Instance.PlayTransformResetSound(); - } - - // Update the skybox rotation with the new scene rotation. - if (RenderSettings.skybox) - { - Quaternion sceneQuaternion = App.Instance.m_SceneTransform.rotation; - RenderSettings.skybox.SetVector( - "_SkyboxRotation", - new Vector4(sceneQuaternion.x, sceneQuaternion.y, sceneQuaternion.z, sceneQuaternion.w)); - } - - // Update last frame members. - m_AllowWorldTransformLastFrame = bAllowWorldTransform; - } - - /// If lhs and rhs are overlapping, return the smallest vector that would - /// cause rhs to stop overlapping; otherwise, return 0. - /// lhs: an antisphere (solid outside, empty inside) - /// rhs: a sphere (empty outside, solid inside) - private static Vector3 GetOverlap_Antisphere_Sphere( - Vector3 lhsCenter, float lhsRadius, - Vector3 rhsCenter, float rhsRadius) - { - // If anyone passes negative values, they are a bad person - lhsRadius = Mathf.Abs(lhsRadius); - rhsRadius = Mathf.Abs(rhsRadius); - // Without loss of generality, can recenter on lhs - rhsCenter -= lhsCenter; - lhsCenter -= lhsCenter; - - float maxDistance = lhsRadius - rhsRadius; - - // Edge case: sphere does not fit in antisphere - if (maxDistance <= 0) - { - return -rhsCenter; - } - - float penetrationDistance = Mathf.Max(0, rhsCenter.magnitude - maxDistance); - return -penetrationDistance * rhsCenter.normalized; - } - - public static bool IsValidScenePose(TrTransform xf, float radialBounds) - { - // Simple and dumb implementation for now. - return xf == MakeValidScenePose(xf, radialBounds); - } - - /// This is like MakeValidScenePose, but it guarantees that: - /// - The return value is a valid result of Lerp(scene0, scene1, t), - /// for some handwavy definition of "lerp" - /// - The lerp "t" is in [0, 1] - /// - IsValidScenePose(return value) is true, subject to the previous constraints. - /// - /// Think of it as doing a cast from scene0 to scene1. - public static TrTransform MakeValidSceneMove( - TrTransform scene0, TrTransform scene1, float radialBounds) - { - if (IsValidScenePose(scene1, radialBounds)) - { - return scene1; - } - if (!IsValidScenePose(scene0, radialBounds)) - { - Debug.LogError("Invalid scene cast start"); - return scene0; - } - - // We don't support lerping either of these - Debug.Assert(scene0.rotation == scene1.rotation); - Debug.Assert(scene0.scale == scene1.scale); - - Vector3 vRoom0 = -scene0.translation; - Vector3 vRoom1 = -scene1.translation; - float radius = (scene0.scale - * radialBounds - * App.METERS_TO_UNITS) - App.Instance.RoomRadius; - - float t0, t1; - bool success = MathUtils.RaySphereIntersection( - vRoom0, vRoom1 - vRoom0, - Vector3.zero, radius, out t0, out t1); - if (!success) - { - // If this were more important, we could solve for the t of the closest approach - return scene0; - } - - // t0 is expected to be < 0 (room starts inside the fence) - // t1 is expected to be in [0, 1] (room ends outside the fence) - - // Constraints: - // - Lerp t must be in [0, 1]. (Do not move past the requested endpoint) - // - Lerp t should be as high as possible but < t1. (Do not exit the sphere) - float t = Mathf.Clamp(t1, 0, 1); - - TrTransform sceneT = TrTransform.TRS( - Vector3.Lerp(scene0.translation, scene1.translation, t), - scene0.rotation, - scene0.scale); - return MakeValidScenePose(sceneT, radialBounds); - } - - /// Returns a new ScenePose TrTransform that does not cause the room - /// to violate the hard scene bounds. - /// - /// scenePose - The current, possibly invalid scene pose - public static TrTransform MakeValidScenePose(TrTransform scenePose, float radialBounds) - { - scenePose.scale = Mathf.Clamp( - scenePose.scale, - SketchControlsScript.m_Instance.WorldTransformMinScale, - SketchControlsScript.m_Instance.WorldTransformMaxScale); - - // Anything not explicitly qualified is in room space. - - float roomRadius = App.Instance.RoomRadius; - Vector3 roomCenter = Vector3.zero; - - float fenceRadius = scenePose.scale * radialBounds - * App.METERS_TO_UNITS; - Vector3 fenceCenter = scenePose.translation; - - Vector3 moveRoom = GetOverlap_Antisphere_Sphere( - fenceCenter, fenceRadius, roomCenter, roomRadius); - Vector3 moveFence = -moveRoom; - - scenePose.translation += moveFence; - return scenePose; - } - - /// Clears data used by GetGrabWidgetHoldHistory() - /// Should be called any time m_GrabWidgetOneHandInfo changes - void ClearGrabWidgetHoldHistory() - { - m_GrabWidgetHoldHistory.Clear(); - } - - /// Collects data for use with GetGrabWidgetHoldHistory() - void UpdateGrabWidgetHoldHistory(InputManager.ControllerName name) - { - float t = Time.realtimeSinceStartup; - var info = InputManager.Controllers[(int)name]; - m_GrabWidgetHoldHistory.Enqueue(new GrabWidgetHoldPoint - { - m_Name = name, - m_BirthTime = t, - m_Pos = info.Transform.position, - m_Rot = info.Transform.rotation - }); - - // Trim the fat off our widget history - while (m_GrabWidgetHoldHistory.Count > 0 && - t - m_GrabWidgetHoldHistory.Peek().m_BirthTime >= kControlPointHistoryMaxTime) - { - m_GrabWidgetHoldHistory.Dequeue(); - } - } - - /// Returns possibly-smoothed linear and angular velocities. May fail. - /// Angular velocity is returned as an axial vector whose length() is degrees/second - bool GetGrabWidgetHoldHistory(out Vector3 vLinearVelocity, out Vector3 vAngularVelocity) - { - vLinearVelocity = vAngularVelocity = Vector3.zero; - if (m_GrabWidgetHoldHistory.Count < 2) - { - return false; - } - - // We need pairs of elements, so a simple foreach() won't quite work. - // Maybe using linq .First() and .Skip() would be okay. - using (IEnumerator enumerator = m_GrabWidgetHoldHistory.GetEnumerator()) - { - if (!enumerator.MoveNext()) - { - return false; - } - - // Infinitesimal rotations commute, and scaled-axis-angle rotations commute - // "better" than other rotation formats. - Vector3 totalDeltaTheta = Vector3.zero; - - GrabWidgetHoldPoint first = enumerator.Current; - GrabWidgetHoldPoint prev = first; - GrabWidgetHoldPoint current = first; - while (enumerator.MoveNext()) - { - current = enumerator.Current; - - // For our quaternion, find the difference, convert it to angle/axis, and sum it - // Find delta such that delta * prev = cur - // left-multiply because we want it in world-space. - // multiply vs prev since we want the delta that takes us forward in time - // rather than backward in time. - Quaternion dtheta = current.m_Rot * Quaternion.Inverse(prev.m_Rot); - // Assume the rotation took the shorter path - if (dtheta.w < 0) - { - dtheta.Set(-dtheta.x, -dtheta.y, -dtheta.z, -dtheta.w); - } - - float degrees; - Vector3 axis; - dtheta.ToAngleAxis(out degrees, out axis); - totalDeltaTheta += (axis * degrees); - prev = current; - } - - // Linear velocity calculation doesn't need to look at intermediate points - Vector3 totalDeltaPosition = current.m_Pos - first.m_Pos; - float totalDeltaTime = current.m_BirthTime - first.m_BirthTime; - if (totalDeltaTime == 0) - { - return false; - } - - vLinearVelocity = totalDeltaPosition / totalDeltaTime; - vAngularVelocity = totalDeltaTheta / totalDeltaTime; - return true; - } - } - - bool IsControllerNearWidget(InputManager.ControllerName name, GrabWidget widget) - { - Vector3 vControllerPos = InputManager.m_Instance.GetControllerAttachPointPosition(name); - return widget.GetActivationScore(vControllerPos, name) >= 0.0f; - } - - void RefreshCurrentGazeObject() - { - UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.RefreshCurrentGazeObject"); - int iPrevGazeObject = m_CurrentGazeObject; - m_CurrentGazeObject = -1; - bool bGazeAllowed = (m_CurrentInputState == InputState.Standard) - && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) - && !m_SketchSurfacePanel.ActiveTool.InputBlocked() - && (m_GrabWidgetState == GrabWidgetState.None) - && !m_GrabBrush.grabbingWorld - && !m_PinCushion.IsShowing() - && !PointerManager.MainPointerIsPainting() - ; - - bool bGazeDeactivationOverrideWithInput = false; - List aAllPanels = m_PanelManager.GetAllPanels(); - - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - - //if we're re-positioning a panel, keep it active - if (m_PositioningPanelWithHead) - { - m_CurrentGazeObject = iPrevGazeObject; - } - // Only activate gaze objects if we're in standard input mode, and if we don't have the 'draw' - // button held. - else if ((bGazeAllowed || (iPrevGazeObject != -1))) - { - //reset hit flags - for (int i = 0; i < m_GazeResults.Length; ++i) - { - m_GazeResults[i].m_HitWithGaze = false; - m_GazeResults[i].m_HitWithController = false; - m_GazeResults[i].m_WithinView = false; - } - - // If we're in controller mode, find the nearest colliding widget that might get in our way. - float fNearestWidget = 99999.0f; - if (hasController) - { - fNearestWidget = m_WidgetManager.DistanceToNearestWidget(m_GazeControllerRay); - } - - //check all panels for gaze hit - bool bRequireVisibilityCheck = !hasController || (iPrevGazeObject == -1); - if (m_PanelManager.PanelsAreStable()) - { - RaycastHit rHitInfo; - bool bRayHit = false; - int panelsHit = 0; - for (int i = 0; i < aAllPanels.Count; ++i) - { - // Ignore fixed panels when they are not visible. - if (!m_PanelManager.GazePanelsAreVisible() && aAllPanels[i].m_Panel.m_Fixed) - { - continue; - } - - if (aAllPanels[i].m_Panel.gameObject.activeSelf && aAllPanels[i].m_Panel.IsAvailable()) - { - //make sure this b-snap is in view - Vector3 vToPanel = aAllPanels[i].m_Panel.transform.position - m_CurrentGazeRay.origin; - vToPanel.Normalize(); - if (!bRequireVisibilityCheck || Vector3.Angle(vToPanel, m_CurrentGazeRay.direction) < m_GazeMaxAngleFromFacing) - { - if (hasController) - { - if (aAllPanels[i].m_Panel.HasMeshCollider()) - { - //make sure the angle between the pointer and the panel forward is below our max angle - if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, m_GazeControllerRay.direction) < m_GazeMaxAngleFromPointing) - { - //make sure the angle between the user-to-panel and the panel forward is reasonable - if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, vToPanel) < m_GazeMaxAngleFacingToForward) - { - m_GazeResults[i].m_WithinView = true; - - bRayHit = false; - bRayHit = aAllPanels[i].m_Panel.RaycastAgainstMeshCollider( - m_GazeControllerRay, out rHitInfo, m_GazeControllerPointingDistance); - - if (bRayHit) - { - //if the ray starts inside the panel, we won't get a good hit point, it'll just be zero - if (rHitInfo.point.sqrMagnitude > 0.1f) - { - if (rHitInfo.distance < fNearestWidget) - { - m_GazeResults[i].m_ControllerDistance = rHitInfo.distance; - m_GazeResults[i].m_ControllerPosition = rHitInfo.point; - m_GazeResults[i].m_HitWithController = true; - panelsHit++; - } - } - } - } - } - } - } - else - { - m_GazeResults[i].m_WithinView = true; - if (aAllPanels[i].m_Panel.GetCollider().Raycast(m_CurrentGazeRay, out rHitInfo, m_GazeMaxDistance)) - { - m_GazeResults[i].m_GazePosition = rHitInfo.point; - m_GazeResults[i].m_HitWithGaze = true; - } - } - } - } - } - - // No panels hit within normal ray distance. - // Check if previous panel still pointed to. - if (panelsHit == 0) - { - if (iPrevGazeObject != -1) - { - // Don't allow any panel to hold focus if it's facing away from the user. - Vector3 vToPanel = aAllPanels[iPrevGazeObject].m_Panel.transform.position - - m_CurrentGazeRay.origin; - vToPanel.Normalize(); - if (Vector3.Angle(aAllPanels[iPrevGazeObject].m_Panel.transform.forward, vToPanel) < - m_GazeMaxAngleFacingToForward) - { - float fDist = m_GazeControllerPointingDistance * 1.5f; - bRayHit = aAllPanels[iPrevGazeObject].m_Panel.RaycastAgainstMeshCollider( - m_GazeControllerRayActivePanel, out rHitInfo, fDist); - if (bRayHit) - { - if (rHitInfo.point.sqrMagnitude > 0.1f) - { - if (rHitInfo.distance < fNearestWidget) - { - m_GazeResults[iPrevGazeObject].m_ControllerDistance = rHitInfo.distance; - m_GazeResults[iPrevGazeObject].m_ControllerPosition = rHitInfo.point; - m_GazeResults[iPrevGazeObject].m_HitWithController = true; - } - } - } - } - } - } - } - - //determine what panel we hit, take the one with the lowest controller distance - float fControllerDist = 999.0f; - int iControllerIndex = -1; - if (hasController) - { - for (int i = 0; i < m_GazeResults.Length; ++i) - { - if (m_GazeResults[i].m_HitWithController) - { - if (m_GazeResults[i].m_ControllerDistance < fControllerDist) - { - iControllerIndex = i; - fControllerDist = m_GazeResults[i].m_ControllerDistance; - } - } - } - } - - //if we found something near our controller, take it - if (iControllerIndex != -1) - { - m_CurrentGazeObject = iControllerIndex; - m_CurrentGazeHitPoint = m_GazeResults[iControllerIndex].m_ControllerPosition; - - // TODO: This should not be hardcoded once multiple pointers are allowed. - m_GazeResults[m_CurrentGazeObject].m_ControllerName = InputManager.ControllerName.Brush; - if (m_GazeResults[m_CurrentGazeObject].m_HitWithGaze) - { - //average with the gaze position if we hit that too - m_CurrentGazeHitPoint += m_GazeResults[m_CurrentGazeObject].m_GazePosition; - m_CurrentGazeHitPoint *= 0.5f; - } - } - else - { - //nothing near the controller, see if we're looking at the previous - if (iPrevGazeObject != -1 && m_GazeResults[iPrevGazeObject].m_HitWithGaze) - { - m_CurrentGazeObject = iPrevGazeObject; - m_CurrentGazeHitPoint = m_GazeResults[m_CurrentGazeObject].m_GazePosition; - } - else - { - //controller and gaze not near panel, pick the first panel we're looking at - for (int i = 0; i < m_GazeResults.Length; ++i) - { - if (m_GazeResults[i].m_HitWithGaze) - { - m_CurrentGazeObject = i; - m_CurrentGazeHitPoint = m_GazeResults[i].m_GazePosition; - break; - } - } - } - } - - //forcing users to look away from gaze panel - if (m_EatInputGazeObject && m_CurrentGazeObject != -1) - { - m_CurrentGazeObject = -1; - } - else if (m_CurrentGazeObject == -1) - { - m_EatInputGazeObject = false; - } - } - - //if we're staring at a panel, keep our countdown fresh - if (m_CurrentGazeObject != -1 || m_ForcePanelActivation) - { - m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; - } - else - { - if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate)) - { - bGazeDeactivationOverrideWithInput = true; - m_GazePanelDectivationCountdown = 0.0f; - } - else - { - m_GazePanelDectivationCountdown -= Time.deltaTime; - } - if (m_GazePanelDectivationCountdown > 0.0f) - { - m_CurrentGazeObject = iPrevGazeObject; - } - } - - //update our positioning timer - if (m_PositioningPanelWithHead) - { - m_PositioningTimer += m_PositioningSpeed * Time.deltaTime; - m_PositioningTimer = Mathf.Min(m_PositioningTimer, 1.0f); - } - else - { - m_PositioningTimer -= m_PositioningSpeed * Time.deltaTime; - m_PositioningTimer = Mathf.Max(m_PositioningTimer, 0.0f); - } - - //prime objects if we change targets - if (iPrevGazeObject != m_CurrentGazeObject) - { - //if we're switching panels, make sure the pointer doesn't streak - PointerManager.m_Instance.DisablePointerPreviewLine(); - - if (iPrevGazeObject != -1) - { - aAllPanels[iPrevGazeObject].m_Panel.PanelGazeActive(false); - aAllPanels[iPrevGazeObject].m_Panel.SetPositioningPercent(0.0f); - } - if (m_CurrentGazeObject != -1) - { - //make sure our line is disabled - if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) - { - PointerManager.m_Instance.EnableLine(false); - PointerManager.m_Instance.AllowPointerPreviewLine(false); - } - - aAllPanels[m_CurrentGazeObject].m_Panel.PanelGazeActive(true); - aAllPanels[m_CurrentGazeObject].m_Panel.SetPositioningPercent(0.0f); - - if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) - { - m_SketchSurfacePanel.RequestHideActiveTool(true); - } - } - else - { - //if we don't have a panel, we need to enable the pointer according to the current tool - PointerManager.m_Instance.RefreshFreePaintPointerAngle(); - PointerManager.m_Instance.RequestPointerRendering(m_SketchSurfacePanel.ShouldShowPointer()); - m_UIReticle.SetActive(false); - m_SketchSurfacePanel.RequestHideActiveTool(false); - if (!bGazeDeactivationOverrideWithInput) - { - m_SketchSurfacePanel.EatToolsInput(); - } - } - - m_PositioningPanelWithHead = false; - } - UnityEngine.Profiling.Profiler.EndSample(); - } - - void UpdateActiveGazeObject() - { - BasePanel currentPanel = m_PanelManager.GetPanel(m_CurrentGazeObject); - currentPanel.SetPositioningPercent(m_PositioningTimer); - bool hasController = m_ControlsType == ControlsType.SixDofControllers; - // Update positioning behavior. - if (m_PositioningPanelWithHead) - { - if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) && - !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - // No more positioning. - m_PositioningPanelWithHead = false; - m_PanelManager.m_SweetSpot.EnableBorderSphere(false, Vector3.zero, 0.0f); - currentPanel.PanelHasStoppedMoving(); - } - else - { - //lock the panel to the sweet spot bounds in the direction the user is looking - Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_PositioningPanelBaseHeadRotation); - Vector3 vAdjustedOffset = qDiff * m_PositioningPanelOffset; - - Vector3 vNewPos = m_PanelManager.m_SweetSpot.transform.position + vAdjustedOffset; - currentPanel.transform.position = vNewPos; - - vAdjustedOffset.Normalize(); - currentPanel.transform.forward = vAdjustedOffset; - - float fHighlightRadius = currentPanel.m_BorderSphereHighlightRadius; - m_PanelManager.m_SweetSpot.EnableBorderSphere(true, vNewPos, fHighlightRadius * m_PositioningTimer); - - //once we've moved this panel, run the simulation on the other panels to resolve collisions - m_PanelManager.DoCollisionSimulationForKeyboardMouse(currentPanel); - } - } - else - { - // It's possible that, on this frame, before this function was called, active gaze was pulled - // from this panel. In this case, we want to skip updating this frame. - // This happens when a panel has gaze and world grab dismisses all panels, for example. - if (currentPanel.IsActive()) - { - //orient to gaze - if (hasController) - { - currentPanel.UpdatePanel(m_GazeControllerRay.direction, m_CurrentGazeHitPoint); - } - else - { - currentPanel.UpdatePanel(m_CurrentGazeRay.direction, m_CurrentGazeHitPoint); - } - } - - if (!hasController) - { - //lock to head if we're holding a lock button.. - bool bLockToHead = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) || - InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController); - - if (bLockToHead) - { - m_PositioningPanelWithHead = true; - m_PositioningPanelBaseHeadRotation = m_CurrentHeadOrientation; - m_PositioningPanelOffset = currentPanel.transform.position - - m_PanelManager.m_SweetSpot.transform.position; - - currentPanel.ResetPanelFlair(); - - //prime all other panels for movement - m_PanelManager.PrimeCollisionSimForKeyboardMouse(); - } - } - - PointerManager.m_Instance.RequestPointerRendering(false); - currentPanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); - } - - // Keep reticle locked in the right spot. - Vector3 reticlePos = Vector3.zero; - Vector3 reticleForward = Vector3.zero; - if (hasController) - { - currentPanel.GetReticleTransformFromPosDir(m_CurrentGazeHitPoint, - m_GazeControllerRay.direction, out reticlePos, out reticleForward); - } - else - { - currentPanel.GetReticleTransform(out reticlePos, out reticleForward, - (m_ControlsType == ControlsType.ViewingOnly)); - } - - SetUIReticleTransform(reticlePos, -reticleForward); - m_UIReticle.SetActive(GetGazePanelActivationRatio() >= 1.0f); - } - - public void ResetActivePanel() - { - m_PanelManager.ResetPanel(m_CurrentGazeObject); - PointerManager.m_Instance.DisablePointerPreviewLine(); - m_PositioningPanelWithHead = false; - m_CurrentGazeObject = -1; - } - - void UpdatePanInput() - { - if (Mouse.current.rightButton.isPressed) - { - Vector3 vPanDiff = Vector3.zero; - vPanDiff += (Vector3.right * m_MouseDeltaXScaled); - vPanDiff += (Vector3.up * m_MouseDeltaYScaled); - Vector3 vSurfacePos = m_SketchSurface.transform.position; - m_SketchSurface.transform.position = vSurfacePos + vPanDiff; - } - else - { - float fCurrentTime = Time.realtimeSinceStartup; - if (fCurrentTime - m_PositionOffsetResetTapTime < m_DoubleTapWindow) - { - if (m_CurrentGazeObject == -1) - { - ResetGrabbedPose(); - } - } - m_PositionOffsetResetTapTime = fCurrentTime; - - SwitchState(InputState.Standard); - } - } - - void UpdateRotationInput() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) - { - bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); - bool bRollRotation = m_RotationRollActive || bAltInputActive || m_CurrentRotationType == RotationType.RollOnly; - m_RotationIcon.SetActive(bRollRotation); - if (bRollRotation) - { - m_RotationCursorOffset.x += m_MouseDeltaXScaled; - float fRotationAmount = m_RotationCursorOffset.x * -m_RotationRollScalar; - - Quaternion qOffsetRotation = Quaternion.AngleAxis(fRotationAmount, m_SurfaceForward); - Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; - m_SketchSurface.transform.rotation = qNewRotation; - - m_RotationRollActive = true; - m_RotationCursor.gameObject.SetActive(false); - } - else - { - //update offset with mouse movement - m_RotationCursorOffset.x += m_MouseDeltaXScaled; - m_RotationCursorOffset.y += m_MouseDeltaYScaled; - - //get offset in model space - Vector3 vSurfaceBounds = m_SketchSurface.transform.localScale * 0.5f; - m_RotationCursorOffset.x = Mathf.Clamp(m_RotationCursorOffset.x, -vSurfaceBounds.x, vSurfaceBounds.x); - m_RotationCursorOffset.y = Mathf.Clamp(m_RotationCursorOffset.y, -vSurfaceBounds.y, vSurfaceBounds.y); - float fCursorOffsetDist = m_RotationCursorOffset.magnitude; - float fMaxCursorOffsetDist = vSurfaceBounds.x; - - //transform offset in to world space - Vector3 vTransformedOffset = m_RotationOrigin * m_RotationCursorOffset; - vTransformedOffset.Normalize(); - - //get world space rotation axis - Vector3 vSketchSurfaceRotationAxis = Vector3.Cross(vTransformedOffset, m_SurfaceForward); - vSketchSurfaceRotationAxis.Normalize(); - - //amount to rotate is determined by offset distance from origin - float fSketchSurfaceRotationAngle = Mathf.Min(fCursorOffsetDist / fMaxCursorOffsetDist, 1.0f); - fSketchSurfaceRotationAngle *= m_RotationMaxAngle; - - //set new surface rotation by combining base rotation with angle/axis rotation - Quaternion qOffsetRotation = Quaternion.AngleAxis(fSketchSurfaceRotationAngle, vSketchSurfaceRotationAxis); - Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; - m_SketchSurface.transform.rotation = qNewRotation; - - //set position of rotation cursor - Vector3 vNewTransformedOffset = qNewRotation * m_RotationCursorOffset; - m_RotationCursor.transform.position = m_SketchSurface.transform.position + vNewTransformedOffset; - m_RotationCursor.transform.rotation = qNewRotation; - - //set position of guide lines - Vector2 vToCenter = m_RotationCursorOffset; - vToCenter.Normalize(); - float fOffsetAngle = Vector2.Angle(vToCenter, Vector2.up); - m_RotationCursor.PositionCursorLines(m_SketchSurface.transform.position, m_SketchSurface.transform.forward, fOffsetAngle, vSurfaceBounds.x * 2.0f); - } - } - else - { - float fCurrentTime = Time.realtimeSinceStartup; - if (fCurrentTime - m_RotationResetTapTime < m_DoubleTapWindow) - { - //reset drawing surface rotation - m_SketchSurface.transform.rotation = Quaternion.identity; - } - m_RotationResetTapTime = fCurrentTime; - - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - if (!m_RotationRollActive && m_AutoOrientAfterRotation && m_SketchSurfacePanel.IsSketchSurfaceToolActive()) - { - //get possible auto rotations - Quaternion qQuatUp = OrientSketchSurfaceToUp(); - Quaternion qQuatForward = OrientSketchSurfaceToForward(); - - //get the angle between our current and desired auto-rotation - float toUpAngle = Quaternion.Angle(qQuatUp, m_SketchSurface.transform.rotation); - float toForwardAngle = Quaternion.Angle(qQuatForward, m_SketchSurface.transform.rotation); - - //set our new rotation to be whichever autorotation is closeset - Quaternion qNewRotation; - if (Mathf.Abs(toUpAngle) < Mathf.Abs(toForwardAngle)) - { - qNewRotation = qQuatUp; - } - else - { - qNewRotation = qQuatForward; - } - - //update the sketch surface - m_SketchSurface.transform.rotation = qNewRotation; - - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - } - - SwitchState(InputState.Standard); - } - } - - void UpdateHeadLockInput() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) - { - //compute new position/orientation of sketch surface - Vector3 vTransformedOffset = m_CurrentHeadOrientation * m_SurfaceLockOffset; - Vector3 vSurfacePos = m_CurrentGazeRay.origin + vTransformedOffset; - - Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_SurfaceLockBaseHeadRotation); - Quaternion qNewSurfaceRot = qDiff * m_SurfaceLockBaseSurfaceRotation; - - m_SketchSurface.transform.position = vSurfacePos; - m_SketchSurface.transform.rotation = qNewSurfaceRot; - } - else - { - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - SwitchState(InputState.Standard); - } - } - - void UpdateControllerLock() - { - if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) - { - //compute new position/orientation of sketch surface - Vector3 vControllerDiff = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController) - m_SurfaceLockBaseControllerPosition; - m_SketchSurface.transform.position = m_SurfaceLockBaseSurfacePosition + (vControllerDiff * m_SurfaceLockControllerScalar); - - Quaternion qDiff = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController) * Quaternion.Inverse(m_SurfaceLockBaseControllerRotation); - m_SketchSurface.transform.rotation = qDiff * m_SurfaceLockBaseSurfaceRotation; - } - else - { - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - - SwitchState(InputState.Standard); - } - } - - void UpdatePushPullInput() - { - bool bRotationActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation); - bool bInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); - bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); - - if (bRotationActive && bInputActive) - { - SwitchState(InputState.Rotation); - } - else if (bAltInputActive) - { - Vector3 vPos = m_SketchSurface.transform.position; - float fBigDiff = Mathf.Abs(m_MouseDeltaXScaled) > Mathf.Abs(m_MouseDeltaYScaled) ? -m_MouseDeltaXScaled : m_MouseDeltaYScaled; - vPos += Vector3.forward * fBigDiff; - - m_SketchSurface.transform.position = vPos; - } - else - { - SwitchState(InputState.Standard); - } - } - - void UpdateSaveInput() - { - if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Save)) - { - SwitchState(InputState.Standard); - } - } - - void UpdateLoadInput() - { - if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Load)) - { - SwitchState(InputState.Standard); - } - } - - void OnBrushSetToDefault() - { - BrushDescriptor rDefaultBrush = BrushCatalog.m_Instance.DefaultBrush; - PointerManager.m_Instance.SetBrushForAllPointers(rDefaultBrush); - PointerManager.m_Instance.SetAllPointersBrushSize01(0.5f); - PointerManager.m_Instance.MarkAllBrushSizeUsed(); - } - - public void AssignControllerMaterials(InputManager.ControllerName controller) - { - ControllerGeometry geometry = InputManager.GetControllerGeometry(controller); - - // Start from a clean state - geometry.ResetAll(); - - // If the tutorial is enabled, override all materials. - if (TutorialManager.m_Instance.TutorialActive()) - { - InputManager.m_Instance - .GetControllerTutorial(controller) - ?.AssignControllerMaterials(controller); - return; - } - - // If we're grabbing the world, get the materials from the world transform panel. - if (m_GrabBrush.grabbingWorld && controller == InputManager.ControllerName.Brush) - { - TrTransform scenePose = App.Scene.Pose; - if (scenePose.scale != 1 || scenePose.translation != Vector3.zero - || scenePose.rotation != Quaternion.identity) - { - geometry.ShowWorldTransformReset(); - } - return; - } - else if (m_GrabWand.grabbingWorld && controller == InputManager.ControllerName.Wand) - { - TrTransform scenePose = App.Scene.Pose; - if (scenePose.scale != 1 || scenePose.translation != Vector3.zero - || scenePose.rotation != Quaternion.identity) - { - geometry.ShowWorldTransformReset(); - } - return; - } - - // Not grabbing the world, so see if we're grabbing a widget. - if (m_GrabWidgetState != GrabWidgetState.None) - { - m_CurrentGrabWidget.AssignControllerMaterials(controller); - return; - } - - // See if we're highlighting a widget and if that matters. - if (m_CurrentGrabWidget != null && m_CurrentGrabWidget.HasHoverInteractions()) - { - m_CurrentGrabWidget.AssignHoverControllerMaterials(controller); - return; - } - - // Not grabbing the world or a widget, see if we're interacting with a panel. - if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); - panel.AssignControllerMaterials(controller); - return; - } - - // Defaults. - if (controller == InputManager.ControllerName.Wand) - { - if (App.CurrentState != App.AppState.Standard || m_PanelManager.IntroSketchbookMode) - { - // If app is not in standard mode, the actions represented by subsequent material - // assigments cannot be taken. - return; - } - bool creatingStroke = PointerManager.m_Instance.IsMainPointerCreatingStroke(); - bool allowPainting = App.Instance.IsInStateThatAllowsPainting(); - - InputManager.Wand.Geometry.ShowRotatePanels(); - InputManager.Wand.Geometry.ShowUndoRedo(CanUndo() && !creatingStroke && allowPainting, - CanRedo() && !creatingStroke && allowPainting); - } - - // Show the pin cushion icon on the button if it's available. - if (controller == InputManager.ControllerName.Brush && CanUsePinCushion()) - { - InputManager.Brush.Geometry.ShowPinCushion(); - } - - // Finally, override with tools. - m_SketchSurfacePanel.AssignControllerMaterials(controller); - } - - public float GetControllerPadShaderRatio( - InputManager.ControllerName controller, VrInput input) - { - // If we're interacting with a panel, get touch ratio from the panel. - if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) - { - BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); - return panel.GetControllerPadShaderRatio(controller); - } - return SketchSurfacePanel.m_Instance.GetCurrentToolSizeRatio(controller, input); - } - - void SwitchState(InputState rDesiredState) - { - //exit current state - switch (m_CurrentInputState) - { - case InputState.Pan: - m_TransformGizmoScript.ResetTransform(); - break; - case InputState.PushPull: - m_TransformGizmoScript.ResetTransform(); - break; - case InputState.Rotation: - m_RotationRollActive = false; - m_RotationIcon.SetActive(false); - m_RotationCursor.gameObject.SetActive(false); - break; - } - - bool bSketchSurfaceToolActive = m_SketchSurfacePanel.IsSketchSurfaceToolActive(); - - //enter new state - switch (rDesiredState) - { - case InputState.Pan: - m_TransformGizmoScript.SetTransformForPan(); - break; - case InputState.PushPull: - m_TransformGizmoScript.SetTransformForPushPull(); - break; - case InputState.Rotation: - if (bSketchSurfaceToolActive) - { - m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; - m_SketchSurfacePanel.ResetReticleOffset(); - } - m_RotationOrigin = m_SketchSurface.transform.rotation; - m_RotationCursorOffset = Vector2.zero; - m_RotationCursor.transform.position = m_SketchSurface.transform.position; - m_RotationCursor.transform.rotation = m_SketchSurface.transform.rotation; - m_RotationCursor.ClearCursorLines(m_SketchSurface.transform.position); - m_RotationCursor.gameObject.SetActive(bSketchSurfaceToolActive); - break; - case InputState.HeadLock: - m_SurfaceLockBaseHeadRotation = m_CurrentHeadOrientation; - m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; - m_SurfaceLockOffset = m_SketchSurface.transform.position - m_CurrentGazeRay.origin; - m_SurfaceLockOffset = Quaternion.Inverse(m_SurfaceLockBaseHeadRotation) * m_SurfaceLockOffset; - break; - case InputState.ControllerLock: - if (bSketchSurfaceToolActive) - { - m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; - m_SketchSurfacePanel.ResetReticleOffset(); - } - m_SurfaceLockActingController = InputManager.m_Instance.GetDominantController(InputManager.SketchCommands.LockToController); - m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; - m_SurfaceLockBaseControllerRotation = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController); - m_SurfaceLockBaseSurfacePosition = m_SketchSurface.transform.position; - m_SurfaceLockBaseControllerPosition = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController); - m_SurfaceLockControllerScalar = m_SketchSurfacePanel.m_PanelSensitivity / m_SurfaceLockControllerBaseScalar; - break; - case InputState.Save: - IssueGlobalCommand(GlobalCommands.Save); - break; - case InputState.Load: - IssueGlobalCommand(GlobalCommands.Load); - break; - } - - m_CurrentInputState = rDesiredState; - } - - public void RequestPanelsVisibility(bool bVisible) - { - // Always false in viewonly mode - bVisible = m_ViewOnly ? false : bVisible; - m_PanelsVisibilityRequested = bVisible; - } - - Quaternion OrientSketchSurfaceToUp() - { - //project the world up vector on to the surface plane - Vector3 vUpOnSurfacePlane = Vector3.up - (Vector3.Dot(Vector3.up, m_SurfaceForward) * m_SurfaceForward); - vUpOnSurfacePlane.Normalize(); - - //get the angle between the surface up and the projected world up - float fUpOnSurfacePlaneAngle = Vector3.Angle(vUpOnSurfacePlane, m_SurfaceUp); - Vector3 vUpCross = Vector3.Cross(vUpOnSurfacePlane, m_SurfaceUp); - vUpCross.Normalize(); - if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) - { - fUpOnSurfacePlaneAngle *= -1.0f; - } - - //rotate around the surface foward by the angle diff - Quaternion qOrientToUp = Quaternion.AngleAxis(fUpOnSurfacePlaneAngle, m_SurfaceForward); - Quaternion qNewRotation = qOrientToUp * m_SketchSurface.transform.rotation; - return qNewRotation; - } - - Quaternion OrientSketchSurfaceToForward() - { - //project the world forward vector on to the surface plane - Vector3 vForwardOnSurfacePlane = Vector3.forward - (Vector3.Dot(Vector3.forward, m_SurfaceForward) * m_SurfaceForward); - vForwardOnSurfacePlane.Normalize(); - - //get the angle between the surface up and the projected world forward - float fForwardOnSurfacePlaneAngle = Vector3.Angle(vForwardOnSurfacePlane, m_SurfaceUp); - Vector3 vUpCross = Vector3.Cross(vForwardOnSurfacePlane, m_SurfaceUp); - vUpCross.Normalize(); - if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) - { - fForwardOnSurfacePlaneAngle *= -1.0f; - } - - //rotate around the surface foward by the angle diff - Quaternion qOrientToForward = Quaternion.AngleAxis(fForwardOnSurfacePlaneAngle, m_SurfaceForward); - Quaternion qNewRotation = qOrientToForward * m_SketchSurface.transform.rotation; - return qNewRotation; - } - - /// Reset the scene or the canvas, depending on the current mode - public void ResetGrabbedPose(bool everything = false) - { - //update sketch surface position with offset to sweet spot - m_SketchSurface.transform.position = m_PanelManager.GetSketchSurfaceResetPos(); - if (everything) - { - App.Scene.Pose = TrTransform.identity; - Coords.CanvasLocalPose = TrTransform.identity; - } - App.Scene.Pose = TrTransform.identity; - - //reset orientation and pointer - ResetSketchSurfaceOrientation(); - m_SketchSurfacePanel.ResetReticleOffset(); - PointerManager.m_Instance.DisablePointerPreviewLine(); - PointerManager.m_Instance.SetPointerPreviewLineDelayTimer(); - } - - public void ResetSketchSurfaceOrientation() - { - m_SketchSurface.transform.rotation = Quaternion.identity; - m_SurfaceForward = m_SketchSurface.transform.forward; - m_SurfaceRight = m_SketchSurface.transform.right; - m_SurfaceUp = m_SketchSurface.transform.up; - } - - float GetAppropriateMovementScalar() - { - switch (m_CurrentInputState) - { - case InputState.Pan: return m_PanScalar; - case InputState.Rotation: return m_RotationScalar; - case InputState.PushPull: return m_PushPullScale; - } - - return 1.0f; - } - - // TODO - it'd be great if we could disentangle this from the multicam. - IEnumerator RenderPathAndQuit() - { -#if USD_SUPPORTED - App.Instance.SetDesiredState(App.AppState.OfflineRendering); - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); - MultiCamTool multiCam = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; - Debug.Assert(multiCam != null); // Something's gone wrong if we've been unable to find multicam! - if (multiCam == null) - { - yield break; - } - multiCam.ExternalObjectForceCameraStyle(MultiCamStyle.Video); - MultiCamCaptureRig.ForceClippingPlanes(MultiCamStyle.Video); - // Give the video tool time to switch - TODO - be a little more graceful here - yield return new WaitForSeconds(2); - // Make sure the videos have had time to load, and set playing ones to start - while (VideoCatalog.Instance.IsScanning) - { - yield return null; - } - foreach (var widget in WidgetManager.m_Instance.VideoWidgets) - { - if (widget.VideoController.Playing) - { - widget.VideoController.Position = 0; - } - } - yield return null; - var ssMgr = MultiCamCaptureRig.ManagerFromStyle(MultiCamStyle.Video); - ssMgr.SetScreenshotResolution(App.UserConfig.Video.OfflineResolution); - multiCam.StartVideoCapture(MultiCamTool.GetSaveName(MultiCamStyle.Video), offlineRender: true); - App.Instance.FrameCountDisplay.gameObject.SetActive(true); - App.Instance.FrameCountDisplay.SetFramesTotal(VideoRecorderUtils.NumFramesInUsdSerializer); - while (VideoRecorderUtils.ActiveVideoRecording != null) - { - App.Instance.FrameCountDisplay.SetCurrentFrame( - VideoRecorderUtils.ActiveVideoRecording.FrameCount); - yield return null; - } - ssMgr.SetScreenshotResolution(App.UserConfig.Video.Resolution); -#else - Debug.LogError("Render path requires USD support"); - yield return null; -#endif - QuitApp(); - } - - IEnumerator ExportListAndQuit() - { - App.Config.m_ForceDeterministicBirthTimeForExport = true; - List filesToExport = new List(); - foreach (string filePattern in App.Config.m_FilePatternsToExport) - { - bool absolute = Path.IsPathRooted(filePattern); - string directory = absolute ? Path.GetDirectoryName(filePattern) : App.UserSketchPath(); - string filename = Path.GetFileName(filePattern); - var tiltFiles = Directory.GetFiles(directory, filename); - filesToExport.AddRange(tiltFiles); - // Also look at .tilt files which have been unzipped into directory format - var tiltDirs = Directory.GetDirectories(directory, filename) - .Where(n => n.EndsWith(".tilt")); - filesToExport.AddRange(tiltDirs); - } - - using (var coroutine = LoadAndExportList(filesToExport)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - QuitApp(); - } - - void QuitApp() - { - // We're done! Quit! -#if UNITY_EDITOR - UnityEditor.EditorApplication.isPlaying = false; -#else - Application.Quit(); -#endif - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExportList(List filenames) - { - foreach (var filename in filenames) - { - using (var coroutine = LoadAndExport(filename)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExportAll() - { - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.User); - for (int i = 0; i < SketchCatalog.m_Instance.GetSet(SketchSetType.User).NumSketches; ++i) - { - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(i); - using (var coroutine = LoadAndExport(rInfo.FullPath)) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - } - - /// Loads a .tilt file completely. - /// This may be slightly buggy; it's not currently used for production. - /// This coroutine must be run to completion or disposed. - public IEnumerable LoadTiltFile(string filename) - { - using (var unused = new SceneSettings.RequestInstantSceneSwitch()) - { - IssueGlobalCommand( - GlobalCommands.LoadNamedFile, - iParam1: (int)LoadSpeed.Quick, sParam: filename); - yield return null; - while (App.Instance.IsLoading()) - { - yield return null; - } - - // I don't know why App.Instance.IsLoading() doesn't cover this, but it doesn't. - while (m_WidgetManager.CreatingMediaWidgets) - { - yield return null; - } - while (WidgetManager.m_Instance.AreMediaWidgetsStillLoading()) - { - yield return null; - } - - // This is kind of a hack. - // Despite the RequestInstantSceneSwitch above, I think scene colors still require - // a few frames to settle; also, GrabWidgets need to register themselves on the - // first frame, etc. - for (int i = 0; i < 10; ++i) - { - yield return null; - } - } - } - - // This coroutine must be run to completion or disposed. - IEnumerator LoadAndExport(string filename) - { - foreach (var val in LoadTiltFile(filename)) - { - yield return val; - } - using (var coroutine = ExportCoroutine()) - { - while (coroutine.MoveNext()) - { - yield return coroutine.Current; - } - } - } - - IEnumerator ExportCoroutine() - { - return OverlayManager.m_Instance.RunInCompositor( - OverlayType.Export, () => - { - // Sort of a kludge: put stuff back into the main canvas - SelectionManager.m_Instance.ClearActiveSelection(); - Export.ExportScene(); - }, 0.25f, false, true); - } - - private void SaveModel() - { -#if USD_SUPPORTED - var current = SaveLoadScript.m_Instance.SceneFile; - string basename = (current.Valid) - ? Path.GetFileNameWithoutExtension(current.FullPath) - : "Untitled"; - string directoryName = FileUtils.GenerateNonexistentFilename( - App.ModelLibraryPath(), basename, ""); - - string usdname = Path.Combine(directoryName, basename + ".usd"); - // TODO: export selection only, though this is still only experimental. The blocking - // issue to implement this is that the export collector needs to expose this as an option. - // - // SelectionManager.m_Instance.HasSelection - // ? SelectionManager.m_Instance.SelectedStrokes - // : null - ExportUsd.ExportPayload(usdname); - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, "Model created!"); -#endif - } - - /// Generates a view from the previous thumbnail viewpoint. - public void GenerateReplacementSaveIcon() - { - if (SaveLoadScript.m_Instance.LastThumbnail_SS.HasValue) - { - TrTransform thumbnailInGlobalSpace = App.Scene.Pose * - SaveLoadScript.m_Instance.LastThumbnail_SS.Value; - - m_SaveIconTool.ProgrammaticCaptureSaveIcon(thumbnailInGlobalSpace.translation, - thumbnailInGlobalSpace.rotation); - } - else - { - GenerateBestGuessSaveIcon(); - } - } - - public void GenerateBestGuessSaveIcon() - { - TrTransform camXform = GenerateBestGuessSaveIconTransform(); - m_SaveIconTool.ProgrammaticCaptureSaveIcon(camXform.translation, camXform.rotation); - } - - /// This positions the save icon camera at the user's head position, and faces it towards - /// the most recent strokes the user has created. - /// If there are no strokes, it faces towards the 'most recent' models. - /// Sadly we cannot really mix the two as we don't know when the models were instantiated. - public TrTransform GenerateBestGuessSaveIconTransform(int itemsToEnumerate = 0) - { - if (itemsToEnumerate == 0) - { - itemsToEnumerate = m_NumStrokesForSaveIcon; - } - int startIndex = Mathf.Max(0, SketchMemoryScript.AllStrokesCount() - itemsToEnumerate); - var lastFewStrokes = SketchMemoryScript.AllStrokes().Skip(startIndex).ToArray(); - - Bounds bounds; - if (lastFewStrokes.Length > 0) - { - bounds = new Bounds(lastFewStrokes.First().m_ControlPoints.First().m_Pos, Vector3.zero); - foreach (var stroke in lastFewStrokes.Skip(1)) - { - bounds.Encapsulate(stroke.m_ControlPoints.First().m_Pos); - bounds.Encapsulate(stroke.m_ControlPoints.Last().m_Pos); - } - } - else - { - // If we have no strokes, just use the aggregates bounding boxes of the blocks models. - var models = m_WidgetManager.ModelWidgets.ToArray(); - // we should always have models to get here, but just in case... - if (models.Length > 0) - { - startIndex = Mathf.Max(0, models.Length - itemsToEnumerate); - bounds = models[startIndex].WorldSpaceBounds; - for (int i = startIndex + 1; i < models.Length; ++i) - { - bounds.Encapsulate(models[i].WorldSpaceBounds); - } - } - else - { - bounds = new Bounds(new Vector3(0, 1, -100000), Vector3.one); // some point in the distance - } - } - - Vector3 camPos = ViewpointScript.Head.position; - Vector3 worldPos = App.Scene.Pose.MultiplyPoint(bounds.center); - Quaternion direction = Quaternion.LookRotation(worldPos - camPos); - return TrTransform.TR(camPos, direction); - } - - - public void GenerateBoundingBoxSaveIcon() - { - Vector3 vNewCamPos; - { - Bounds rCanvasBounds = App.Scene.AllCanvases - .Select(canvas => canvas.GetCanvasBoundingBox()) - .Aggregate((b1, b2) => - { - b1.Encapsulate(b2); - return b1; - }); - - //position the camera at the center of the canvas bounds - vNewCamPos = rCanvasBounds.center; - - //back the camera up, along -z until we can see the extent of the bounds - float fCanvasWidth = rCanvasBounds.max.x - rCanvasBounds.min.x; - float fCanvasHeight = rCanvasBounds.max.y - rCanvasBounds.min.y; - float fLargerExtent = Mathf.Max(fCanvasHeight, fCanvasWidth); - - //half fov for camera - float fHalfFOV = m_SaveIconTool.ScreenshotManager.LeftEye.fieldOfView * 0.5f; - - //TODO: find the real reason this isn't working as it should - float fMagicNumber = 1.375f; - - //set new cam position and zero out orientation - float fBackupDistance = (fLargerExtent * 0.5f) - * Mathf.Tan(Mathf.Deg2Rad * fHalfFOV) * fMagicNumber; - vNewCamPos.z = rCanvasBounds.min.z - fBackupDistance; - } - - m_SaveIconTool.ProgrammaticCaptureSaveIcon(vNewCamPos, Quaternion.identity); - } - - private void MergeBrushStrokes(SceneFileInfo fileInfo) - { - m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - if (SaveLoadScript.m_Instance.Load(fileInfo, true)) - { - SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); - SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true, false, false); - // the order of these two lines are important as ExitIntroSketch is setting the - // color of the pointer and we need the color to be set before we go to the Loading - // state. App script's ShouldTintControllers allow the controller to be tinted only - // when the app is in the standard mode. That was there to prevent the controller color - // from flickering while in the intro mode. - App.Instance.ExitIntroSketch(); - App.Instance.SetDesiredState(App.AppState.QuickLoad); - } - } - - public void LoadSketch(SceneFileInfo fileInfo, bool quickload = false, bool additive = false) - { - LightsControlScript.m_Instance.DiscoMode = false; - m_WidgetManager.FollowingPath = false; - m_WidgetManager.CameraPathsVisible = false; - m_WidgetManager.DestroyAllWidgets(); - m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); - ResetGrabbedPose(everything: true); - PointerManager.m_Instance.EnablePointerStrokeGeneration(true); - if (SaveLoadScript.m_Instance.Load(fileInfo, additive)) - { - SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); - SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true); - // the order of these two lines are important as ExitIntroSketch is setting the - // color of the pointer and we need the color to be set before we go to the Loading - // state. App script's ShouldTintControllers allow the controller to be tinted only - // when the app is in the standard mode. That was there to prevent the controller color - // from flickering while in the intro mode. - App.Instance.ExitIntroSketch(); - App.Instance.SetDesiredState(quickload ? App.AppState.QuickLoad : App.AppState.Loading); - } - QualityControls.m_Instance.ResetAutoQuality(); - m_WidgetManager.ValidateCurrentCameraPath(); - } - - public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, - int iParam2 = -1, string sParam = null) - { - switch (rEnum) - { - - // Keyboard command, for debugging and emergency use. - case GlobalCommands.Save: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - return; - } - // Disable active selection before saving. - // This looks fishy, here's what's going on: When an object is selected and it has moved, the - // the user is observing the selection canvas in the HMD, but we will be saving the main canvas. - // Because they haven't deselected yet, the selection canvas and the main canvas are out of sync - // so the strokes that will be saved will not match what the user sees. - // - // Here we deselect to force the main canvas to sync with the selection canvas, which is more - // correct from the user's perspective. Push the deselect operation onto the stack so the user - // can undo it after save, if desired. - SelectionManager.m_Instance.ClearActiveSelection(); - GenerateReplacementSaveIcon(); - if (iParam1 == -1) - { - if (iParam2 == 1) - { - // Do a save in Tiltasaurus mode, which creates a new filename prefixed with - // "Tiltasaurus_" and the current prompt. Also, don't eat gaze input so that the - // Tiltasaurus prompt stays open. - StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite(tiltasaurusMode: true)); - } - else - { - StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite()); - EatGazeObjectInput(); - } - } - else - { - StartCoroutine(SaveLoadScript.m_Instance.SaveMonoscopic(iParam1)); - } - break; - } - case GlobalCommands.SaveNew: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - return; - } - if (iParam1 == 1) - { - GenerateBoundingBoxSaveIcon(); - } - StartCoroutine(SaveLoadScript.m_Instance.SaveNewName()); - EatGazeObjectInput(); - break; - } - case GlobalCommands.SaveAndUpload: - { - if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) - { - Debug.LogError("SaveAndUpload: Disk space error"); - return; - } - SelectionManager.m_Instance.ClearActiveSelection(); - m_PanelManager.GetPanel(m_CurrentGazeObject).CreatePopUp( - GlobalCommands.UploadToGenericCloud, (int)Cloud.None, -1); - EatGazeObjectInput(); - break; - } - case GlobalCommands.ExportAll: - { - StartCoroutine(LoadAndExportAll()); - break; - } - // Glen Keane request: a way to draw guidelines that can be toggled on and off - // at runtime. - case GlobalCommands.DraftingVisibility: - { - if (!Enum.IsDefined(typeof(DraftingVisibilityOption), iParam1)) - { - Debug.LogError("Unknown draft visibility value: " + iParam1); - return; - } - DraftingVisibilityOption option = (DraftingVisibilityOption)iParam1; - if (option != m_DraftingVisibility) - { - m_DraftingVisibility = option; - UpdateDraftingVisibility(); - } - break; - } - case GlobalCommands.MergeBrushStrokes: - { - // TODO Refactor with Load below - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - if (rInfo != null) - { - MergeBrushStrokes(rInfo); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - break; - } - case GlobalCommands.Load: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - if (rInfo != null) - { - LoadSketch(rInfo); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - break; - } - case GlobalCommands.LoadNamedFile: - LoadNamed(sParam, iParam1 == (int)LoadSpeed.Quick, iParam2 != -1); - break; - case GlobalCommands.NewSketch: - NewSketch(fade: true); - Vector3 vTrashSoundPos = m_CurrentGazeRay.origin; - if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) - { - vTrashSoundPos = InputManager.m_Instance.GetControllerPosition( - InputManager.ControllerName.Wand); - } - AudioManager.m_Instance.PlayTrashSound(vTrashSoundPos); - PromoManager.m_Instance.RequestAdvancedPanelsPromo(); - break; - case GlobalCommands.SymmetryPlane: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.SinglePlane) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.SinglePlane); - ControllerConsoleScript.m_Instance.AddNewLine("Mirror Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Mirror Off"); - } - break; - case GlobalCommands.MultiMirror: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.MultiMirror) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.MultiMirror); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); - } - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - break; - case GlobalCommands.SymmetryTwoHanded: - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.TwoHanded) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.TwoHanded); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); - } - else - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); - ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); - } - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - break; - case GlobalCommands.StraightEdge: - PointerManager.m_Instance.StraightEdgeModeEnabled = !PointerManager.m_Instance.StraightEdgeModeEnabled; - if (PointerManager.m_Instance.StraightEdgeModeEnabled) - { - ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge On"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge Off"); - } - break; - case GlobalCommands.AutoOrient: - m_AutoOrientAfterRotation = !m_AutoOrientAfterRotation; - if (m_AutoOrientAfterRotation) - { - ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient On"); - } - else - { - ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient Off"); - } - break; - case GlobalCommands.Undo: - SketchMemoryScript.m_Instance.StepBack(); - break; - case GlobalCommands.Redo: - SketchMemoryScript.m_Instance.StepForward(); - break; - case GlobalCommands.AudioVisualization: // Intentionally blank. - break; - case GlobalCommands.ResetAllPanels: - m_PanelManager.ResetWandPanelsConfiguration(); - EatGazeObjectInput(); - break; - case GlobalCommands.SketchOrigin: - m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SketchOrigin); - EatGazeObjectInput(); - break; - case GlobalCommands.ViewOnly: - m_ViewOnly = !m_ViewOnly; - RequestPanelsVisibility(!m_ViewOnly); - PointerManager.m_Instance.RequestPointerRendering(!m_ViewOnly); - // TODO - decide if this is a permanent change - // With this line, you can't set a tool such as fly or teleport - // and switch to View Only mode as the mode change disables all tools - //m_SketchSurface.SetActive(!m_ViewOnly); - m_Decor.SetActive(!m_ViewOnly); - break; - case GlobalCommands.SaveGallery: - m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SaveIconTool); - break; - case GlobalCommands.DropCam: - // Want to enable this if in monoscopic or VR modes. - // TODO: seems odd to tie this switch to the controller type, should be based on some - // other build-time configuration setting. - if (App.VrSdk.GetControllerDof() != VrSdk.DoF.None) - { - m_DropCam.Show(!m_DropCam.gameObject.activeSelf); - } - break; - case GlobalCommands.AnalyticsEnabled_Deprecated: - break; - case GlobalCommands.ToggleAutosimplification: - QualityControls.AutosimplifyEnabled = !QualityControls.AutosimplifyEnabled; - break; - case GlobalCommands.Credits: - LoadSketch(new DiskSceneFileInfo(m_CreditsSketchFilename, embedded: true, readOnly: true)); - EatGazeObjectInput(); - break; - case GlobalCommands.AshleysSketch: - LoadSketch(new DiskSceneFileInfo(m_AshleysSketchFilename, embedded: true, readOnly: true)); - EatGazeObjectInput(); - break; - case GlobalCommands.FAQ: - OpenURLAndInformUser(m_HelpCenterURL); - break; - case GlobalCommands.ReleaseNotes: - OpenURLAndInformUser(m_ReleaseNotesURL); - break; - case GlobalCommands.ExportRaw: - if (!FileUtils.CheckDiskSpaceWithError(App.UserExportPath())) - { - return; - } - EatGazeObjectInput(); - StartCoroutine(ExportCoroutine()); - break; - case GlobalCommands.IRC: - if (m_IRCChatWidget == null) - { - GameObject widgetobject = (GameObject)Instantiate(m_IRCChatPrefab); - widgetobject.transform.parent = App.Instance.m_RoomTransform; - m_IRCChatWidget = widgetobject.GetComponent(); - m_IRCChatWidget.Show(true); - } - else - { - m_IRCChatWidget.Show(false); - m_IRCChatWidget = null; - } - break; - case GlobalCommands.YouTubeChat: - if (m_YouTubeChatWidget == null) - { - GameObject widgetobject = (GameObject)Instantiate(m_YouTubeChatPrefab); - widgetobject.transform.parent = App.Instance.m_RoomTransform; - m_YouTubeChatWidget = widgetobject.GetComponent(); - m_YouTubeChatWidget.Show(true); - } - else - { - m_YouTubeChatWidget.Show(false); - m_YouTubeChatWidget = null; - } - break; - case GlobalCommands.CameraOptions: - // If we're switching in to Camera mode, make sure Multicam is selected. - if (!m_PanelManager.CameraActive()) - { - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); - } - m_PanelManager.ToggleCameraPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.ShowSketchFolder: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); - EatGazeObjectInput(); - //launch external window and tell the user we did so - //this call is windows only - if ((Application.platform == RuntimePlatform.WindowsPlayer) || - (Application.platform == RuntimePlatform.WindowsEditor)) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - kRemoveHeadsetFyi, fPopScalar: 0.5f); - System.Diagnostics.Process.Start("explorer.exe", - "/select," + rInfo.FullPath); - } - break; - } - case GlobalCommands.About: - OpenURLAndInformUser(m_ThirdPartyNoticesURL); - break; - case GlobalCommands.StencilsDisabled: - SketchMemoryScript.m_Instance.PerformAndRecordCommand(new StencilsVisibleCommand()); - break; - case GlobalCommands.StraightEdgeMeterDisplay: - PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); - break; - case GlobalCommands.Sketchbook: - m_PanelManager.ToggleSketchbookPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.StraightEdgeShape: - // Previously experimental mode only. - // Untested and currently untriggerable. - PointerManager.m_Instance.StraightEdgeGuide.SetTempShape( - (StraightEdgeGuideScript.Shape)iParam1); - break; - case GlobalCommands.DeleteSketch: - { - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - sketchSet.DeleteSketch(iParam1); - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.RenameSketch: - { - var sketchSetType = (SketchSetType)iParam2; - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - string newName = sParam; - if (string.IsNullOrEmpty(newName)) - { - newName = KeyboardPopUpWindow.m_LastInput; - if (string.IsNullOrEmpty(newName)) - { - break; - } - } - - if (sketchSetType == SketchSetType.User) - { - sketchSet.RenameSketch(iParam1, newName); - } - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.RenameLayer: - { - var layer = App.Scene.GetCanvasByLayerIndex(iParam1); - App.Scene.RenameLayer(layer, KeyboardPopUpWindow.m_LastInput); - DismissPopupOnCurrentGazeObject(false); - break; - } - case GlobalCommands.ShowWindowGUI: - break; - case GlobalCommands.Disco: - LightsControlScript.m_Instance.DiscoMode = !LightsControlScript.m_Instance.DiscoMode; - break; - case GlobalCommands.AccountInfo: break; // Intentionally blank. - case GlobalCommands.LoginToGenericCloud: - { - var ident = App.GetIdentity((Cloud)iParam1); - if (!ident.LoggedIn) { ident.LoginAsync(); } - // iParam2 is being used as a UX flag. If not set to the default, it will cause the UI - // to lose focus. - if (iParam2 != -1) { EatGazeObjectInput(); } - break; - } - case GlobalCommands.LogOutOfGenericCloud: - { - Cloud cloud = (Cloud)iParam1; - if (cloud == Cloud.Icosa) - { - App.Instance.IcosaToken = null; - App.IcosaUserName = ""; - App.IcosaUserIcon = null; - } - else - { - var ident = App.GetIdentity(cloud); - if (ident.LoggedIn) { ident.Logout(); } - } - break; - } - case GlobalCommands.UploadToGenericCloud: - { - Cloud cloud = (Cloud)iParam1; - if (cloud == Cloud.Icosa) - { - if (App.Instance.IcosaToken == null) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - "Not logged in", fPopScalar: 0.5f); - } - } - else - { - var ident = App.GetIdentity(cloud); - if (!ident.LoggedIn) - { - ident.LoginAsync(); - break; - } - } - SelectionManager.m_Instance.ClearActiveSelection(); - VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); - EatGazeObjectInput(); - break; - } - case GlobalCommands.ViewOnlineGallery: - OpenURLAndInformUser(kTiltBrushGalleryUrl); - break; - case GlobalCommands.CancelUpload: - VrAssetService.m_Instance.CancelUpload(); - break; - case GlobalCommands.ViewLastUpload: - if (VrAssetService.m_Instance.LastUploadCompleteUrl != null) - { - var url = VrAssetService.m_Instance.LastUploadCompleteUrl; - App.OpenURL(url); - - // The upload flow is different on mobile and requires the user to manually accept - // that they'll go to the browser for publishing. In that case, we want to reset - // state when the leave to publish. This is automatically part of the - // UploadPopUpWindow state flow on PC. - if (App.Config.IsMobileHardware) - { - DismissPopupOnCurrentGazeObject(true); - } - } - break; - case GlobalCommands.ShowGoogleDrive: - string baseDriveUrl = "https://drive.google.com"; - string driveURL = !App.GoogleIdentity.LoggedIn ? baseDriveUrl : - string.Format( - "http://accounts.google.com/AccountChooser?Email={0}&continue={1}", - App.GoogleIdentity.Profile.email, baseDriveUrl); - OpenURLAndInformUser(driveURL); - break; - case GlobalCommands.GoogleDriveSync: - App.DriveSync.SyncEnabled = !App.DriveSync.SyncEnabled; - break; - case GlobalCommands.GoogleDriveSync_Folder: - App.DriveSync.ToggleSyncOnFolderOfType((DriveSync.SyncedFolderType)iParam1); - break; - case GlobalCommands.Duplicate: - { - int selectedVerts = SelectionManager.m_Instance.NumVertsInSelection; - - // TODO - this code has never taken imported models etc into account - if (PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror) - { - selectedVerts *= PointerManager.m_Instance.CustomMirrorMatrices.Count; - } - - if (!SketchMemoryScript.m_Instance.MemoryWarningAccepted && - SketchMemoryScript.m_Instance.WillVertCountPutUsOverTheMemoryLimit(selectedVerts)) - { - AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); - if (!m_PanelManager.MemoryWarningActive()) - { - m_PanelManager.ToggleMemoryWarningMode(); - } - } - else - { - ClipboardManager.Instance.DuplicateSelection( - stampMode: IsUserInteractingWithSelectionWidget()); - } - EatToolScaleInput(); - break; - } - case GlobalCommands.AdvancedPanelsToggle: - m_PanelManager.ToggleAdvancedPanels(); - // If we're now in basic mode, ensure we don't have advanced abilities. - if (!m_PanelManager.AdvancedModeActive()) - { - m_WidgetManager.StencilsDisabled = true; - m_WidgetManager.CameraPathsVisible = false; - App.Switchboard.TriggerStencilModeChanged(); - m_SketchSurfacePanel.EnsureUserHasBasicToolEnabled(); - if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.None) - { - PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None, false); - } - } - PromoManager.m_Instance.RecordCompletion(PromoType.AdvancedPanels); - EatGazeObjectInput(); - break; - case GlobalCommands.Music: break; // Intentionally blank. - case GlobalCommands.ToggleGroupStrokesAndWidgets: - SelectionManager.m_Instance.ToggleGroupSelectedStrokesAndWidgets(); - EatToolScaleInput(); - break; - case GlobalCommands.SaveModel: - SaveModel(); - break; - case GlobalCommands.ViewPolyPage: - OpenURLAndInformUser(kPolyMainPageUri); - break; - case GlobalCommands.ViewPolyGallery: - OpenURLAndInformUser(kBlocksGalleryUrl); - break; - case GlobalCommands.ExportListed: - StartCoroutine(ExportListAndQuit()); - break; - case GlobalCommands.RenderCameraPath: - StartCoroutine(RenderPathAndQuit()); - break; - case GlobalCommands.ToggleProfiling: - ToggleProfiling(); - break; - case GlobalCommands.DoAutoProfile: - DoAutoProfile(); - break; - case GlobalCommands.DoAutoProfileAndQuit: - DoAutoProfileAndQuit(); - break; - case GlobalCommands.ToggleSettings: - m_PanelManager.ToggleSettingsPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.SummonMirror: - PointerManager.m_Instance.BringSymmetryToUser(); - break; - case GlobalCommands.InvertSelection: - SelectionManager.m_Instance.InvertSelection(); - break; - case GlobalCommands.SelectAll: - SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); - SelectionManager.m_Instance.SelectAll(); - EatGazeObjectInput(); - break; - case GlobalCommands.FlipSelection: - SelectionManager.m_Instance.FlipSelection(); - break; - case GlobalCommands.ToggleBrushLab: - m_PanelManager.ToggleBrushLabPanels(); - PointerManager.m_Instance.EatLineEnabledInput(); - SketchSurfacePanel.m_Instance.EatToolsInput(); - break; - case GlobalCommands.ToggleCameraPostEffects: - CameraConfig.PostEffects = !CameraConfig.PostEffects; - break; - case GlobalCommands.ToggleWatermark: - if (PlayerPrefs.GetInt("Promo_Contribution", 0) == 0) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Wand, - m_ContributionPromoText, fPopScalar: 1.0f); - PlayerPrefs.SetInt("Promo_Contribution", 1); - } - CameraConfig.Watermark = !CameraConfig.Watermark; - break; - case GlobalCommands.LoadConfirmComplexHigh: - IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); - break; - case GlobalCommands.LoadConfirmComplex: - { - var index = iParam1; - var sketchSetType = (SketchSetType)iParam2; - bool loadSketch = true; - - // If the sketchbook is active, we may want to show a popup instead of load. - if (m_PanelManager.SketchbookActive()) - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - if (sketchBook != null) - { - // Get triangle count from cloud scene file info. - SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); - SceneFileInfo sfi = sketchSet.GetSketchSceneFileInfo(index); - int tris = sfi.TriangleCount ?? -1; - - // Show "this is bad" popup if we're over the triangle limit. - if (tris > QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles) - { - loadSketch = false; - sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplexHigh, iParam1, iParam2); - } - else if (tris > - QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles) - { - // Show, "this could be bad" popup if we're over the warning limit. - loadSketch = false; - sketchBook.CreatePopUp(GlobalCommands.Load, iParam1, iParam2); - } - } - } - - if (loadSketch) - { - IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.LoadConfirmUnsaved: - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - if ((sketchBook != null) && SketchMemoryScript.m_Instance.IsMemoryDirty()) - { - sketchBook.CreatePopUp(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); - } - else - { - IssueGlobalCommand(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.LoadWaitOnDownload: - { - bool download = false; - if (iParam2 == (int)SketchSetType.Drive) - { - BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); - var googleSketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); - if (sketchBook != null - && googleSketchSet != null - && googleSketchSet.IsSketchIndexValid(iParam1) - && !googleSketchSet.GetSketchSceneFileInfo(iParam1).Available) - { - sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); - download = true; - } - } - if (!download) - { - IssueGlobalCommand(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); - } - } - break; - case GlobalCommands.MemoryWarning: - if (iParam1 > 0) - { - SketchMemoryScript.m_Instance.MemoryWarningAccepted = true; - } - m_PanelManager.ToggleMemoryWarningMode(); - break; - case GlobalCommands.MemoryExceeded: - // If we're in the memory exceeded app state, exit. - if (App.CurrentState == App.AppState.MemoryExceeded) - { - App.Instance.SetDesiredState(App.AppState.Standard); - } - else - { - // If we're not in the full app state, just switch our panel mode. - m_PanelManager.ToggleMemoryWarningMode(); - } - break; - case GlobalCommands.ShowTos: - OpenURLAndInformUser(m_TosURL); - break; - case GlobalCommands.ShowPrivacy: - OpenURLAndInformUser(m_PrivacyURL); - break; - case GlobalCommands.ShowQuestSideLoading: - OpenURLAndInformUser(m_QuestSideLoadingHowToURL); - break; - case GlobalCommands.ShowContribution: - OpenURLAndInformUser(m_ContributionURL); - break; - case GlobalCommands.UnloadReferenceImageCatalog: - ReferenceImageCatalog.m_Instance.UnloadAllImages(); - break; - case GlobalCommands.ToggleCameraPathVisuals: - m_WidgetManager.CameraPathsVisible = !m_WidgetManager.CameraPathsVisible; - break; - case GlobalCommands.ToggleCameraPathPreview: - m_WidgetManager.FollowingPath = !m_WidgetManager.FollowingPath; - break; - case GlobalCommands.DeleteCameraPath: - { - var cameraPath = m_WidgetManager.GetCurrentCameraPath(); - GrabWidget cameraPathWidget = cameraPath == null ? null : cameraPath.m_WidgetScript; - m_WidgetManager.DeleteCameraPath(cameraPathWidget); - } - break; - case GlobalCommands.RecordCameraPath: - // Turn off MultiCam if we're going to record the camera path. - if (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.MultiCamTool) - { - m_SketchSurfacePanel.EnableDefaultTool(); - } - CameraPathCaptureRig.RecordPath(); - EatGazeObjectInput(); - break; - case GlobalCommands.OpenScriptsCommandsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/help/commands"); - break; - case GlobalCommands.OpenScriptsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/scripts"); - break; - case GlobalCommands.OpenExampleScriptsList: - OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); - break; - case GlobalCommands.RepaintOptions: break; // Intentionally blank. - case GlobalCommands.LoginToIcosa: break; // Intentionally blank. - case GlobalCommands.Null: break; // Intentionally blank. - default: - Debug.LogError($"Unrecognized command {rEnum}"); - break; - } - } - - private void LoadNamed(string path, bool quickload, bool additive) - { - var fileInfo = new DiskSceneFileInfo(path); - fileInfo.ReadMetadata(); - if (SaveLoadScript.m_Instance.LastMetadataError != null) - { - ControllerConsoleScript.m_Instance.AddNewLine( - string.Format("Error detected in sketch '{0}'.\nTry re-saving.", - fileInfo.HumanName)); - Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", - fileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); - } - LoadSketch(fileInfo, quickload, additive); - if (m_ControlsType != ControlsType.ViewingOnly) - { - EatGazeObjectInput(); - } - } - - public void OpenURLAndInformUser(string url) - { - // On desktop - launch external browser and inform the user - // On mobile - the browser appears over the app - if (!App.Config.IsMobileHardware) - { - OutputWindowScript.m_Instance.CreateInfoCardAtController( - InputManager.ControllerName.Brush, - kRemoveHeadsetFyi, fPopScalar: 0.5f); - } - App.OpenURL(url); - EatGazeObjectInput(); - } - - public bool IsCommandActive(GlobalCommands rEnum, int iParam = -1) - { - switch (rEnum) - { - case GlobalCommands.StraightEdge: return PointerManager.m_Instance.StraightEdgeModeEnabled; - case GlobalCommands.StraightEdgeMeterDisplay: return PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter(); - case GlobalCommands.SymmetryPlane: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.SinglePlane; - case GlobalCommands.MultiMirror: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror; - case GlobalCommands.SymmetryTwoHanded: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.TwoHanded; - case GlobalCommands.AutoOrient: return m_AutoOrientAfterRotation; - case GlobalCommands.AudioVisualization: return VisualizerManager.m_Instance.VisualsRequested; - case GlobalCommands.AdvancedPanelsToggle: return m_PanelManager.AdvancedModeActive(); - case GlobalCommands.Music: return VisualizerManager.m_Instance.VisualsRequested; - case GlobalCommands.DropCam: return m_DropCam.gameObject.activeSelf; - case GlobalCommands.ToggleAutosimplification: return QualityControls.AutosimplifyEnabled; - case GlobalCommands.DraftingVisibility: return m_DraftingVisibility == (DraftingVisibilityOption)iParam; - case GlobalCommands.Cameras: - return SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.AutoGif || - SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.ScreenshotTool; - case GlobalCommands.IRC: return m_IRCChatWidget != null; - case GlobalCommands.YouTubeChat: return m_YouTubeChatWidget != null; - case GlobalCommands.StencilsDisabled: return m_WidgetManager.StencilsDisabled; - case GlobalCommands.StraightEdgeShape: - // Previously experimental mode only. - // Untested and currently untriggerable. - return PointerManager.m_Instance.StraightEdgeGuide.TempShape == (StraightEdgeGuideScript.Shape)iParam || - (PointerManager.m_Instance.StraightEdgeGuide.TempShape == StraightEdgeGuideScript.Shape.None - && PointerManager.m_Instance.StraightEdgeGuide.CurrentShape == (StraightEdgeGuideScript.Shape)iParam); - case GlobalCommands.Disco: return LightsControlScript.m_Instance.DiscoMode; - case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.UngroupingAllowed; - case GlobalCommands.ToggleProfiling: return UnityEngine.Profiling.Profiler.enabled; - case GlobalCommands.ToggleCameraPostEffects: return CameraConfig.PostEffects; - case GlobalCommands.ToggleWatermark: return CameraConfig.Watermark; - case GlobalCommands.ToggleCameraPathVisuals: return m_WidgetManager.CameraPathsVisible; - case GlobalCommands.ToggleCameraPathPreview: return m_WidgetManager.FollowingPath; - case GlobalCommands.SelectCameraPath: - return m_WidgetManager.IsCameraPathAtIndexCurrent(iParam) && - m_WidgetManager.CameraPathsVisible; - case GlobalCommands.GoogleDriveSync_Folder: - return App.DriveSync.IsFolderOfTypeSynced((DriveSync.SyncedFolderType)iParam); - case GlobalCommands.GoogleDriveSync: return App.DriveSync.SyncEnabled; - case GlobalCommands.RecordCameraPath: return VideoRecorderUtils.ActiveVideoRecording != null; - } - return false; - } - - public void NewSketch(bool fade) - { - LightsControlScript.m_Instance.DiscoMode = false; - m_WidgetManager.FollowingPath = false; - SketchMemoryScript.m_Instance.ClearMemory(); - ControllerConsoleScript.m_Instance.AddNewLine("Sketch Cleared"); - ResetGrabbedPose(everything: true); - QualityControls.m_Instance.ResetAutoQuality(); - InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); - SaveLoadScript.m_Instance.ResetLastFilename(); - SelectionManager.m_Instance.RemoveFromSelection(false); - PointerManager.m_Instance.ResetSymmetryToHome(); - App.Scene.ResetLayers(notify: true); - ApiManager.Instance.ResetBrushTransform(); - - // If we've got the camera path tool active, switch back to the default tool. - // I'm doing this because if we leave the camera path tool active, the camera path - // panel shows the button highlighted, which affects the user's flow for being - // invited to start a path. It looks weird. - if (m_SketchSurfacePanel.ActiveToolType == BaseTool.ToolType.CameraPathTool) - { - m_SketchSurfacePanel.EnableDefaultTool(); - } - - m_WidgetManager.DestroyAllWidgets(); - if (LightsControlScript.m_Instance.LightsChanged || - SceneSettings.m_Instance.EnvironmentChanged) - { - SceneSettings.m_Instance.RecordSkyColorsForFading(); - SceneSettings.m_Instance.SetDesiredPreset( - SceneSettings.m_Instance.GetDesiredPreset(), skipFade: !fade); - } - // Blank the thumbnail position so that autosave won't save the thumbnail position to be - // the one from the old sketch. - SaveLoadScript.m_Instance.LastThumbnail_SS = null; - - // Re-set the quality level to reset simplification level - QualityControls.m_Instance.QualityLevel = QualityControls.m_Instance.QualityLevel; - - App.PolyAssetCatalog.ClearLoadingQueue(); - App.PolyAssetCatalog.UnloadUnusedModels(); - } - - private bool WorldIsReset(bool toSavedXf) - { - return App.Scene.Pose == - (toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity); - } - - public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) - { - // TODO: hide gallery view / publish if there are no saved sketches - switch (rEnum) - { - case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); - case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); - case GlobalCommands.Save: - bool canSave = - SaveLoadScript.m_Instance.SceneFile.Valid && - SaveLoadScript.m_Instance.IsSavingAllowed(); - return canSave && (!WorldIsReset(toSavedXf: true) || - (SketchHasChanges() && SketchMemoryScript.m_Instance.IsMemoryDirty())); - case GlobalCommands.SaveOptions: - case GlobalCommands.SaveNew: - case GlobalCommands.SaveGallery: - return SketchHasChanges(); - case GlobalCommands.SaveOnLocalChanges: - if (!SaveLoadScript.m_Instance.SceneFile.Valid) - { - // No save file, but something has changed. - return SketchHasChanges(); - } - else - { - if (SaveLoadScript.m_Instance.CanOverwriteSource) - { - // Save file, and it's our file. Whether we have changes is irrelevant. - return true; - } - // Save file, but it's not our file. Only make a copy if there are local changes. - return SketchMemoryScript.m_Instance.IsMemoryDirty(); - } - case GlobalCommands.UploadToGenericCloud: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || - VrAssetService.m_Instance.UploadProgress >= 1.0f || - VrAssetService.m_Instance.LastUploadFailed; - case GlobalCommands.SaveAndUpload: - return App.GoogleIdentity.LoggedIn && - (VrAssetService.m_Instance.UploadProgress <= 0.0f) && - IsCommandAvailable(GlobalCommands.UploadToGenericCloud); - case GlobalCommands.NewSketch: - return SketchHasChanges(); - case GlobalCommands.Credits: - case GlobalCommands.AshleysSketch: - return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); - case GlobalCommands.Tiltasaurus: return TiltBrush.Tiltasaurus.m_Instance.TiltasaurusAvailable(); - case GlobalCommands.ExportRaw: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf); - case GlobalCommands.ResetAllPanels: return m_PanelManager.PanelsHaveBeenCustomized(); - case GlobalCommands.Duplicate: return ClipboardManager.Instance.CanCopy; - case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.SelectionCanBeGrouped; - case GlobalCommands.SaveModel: return SelectionManager.m_Instance.HasSelection; - case GlobalCommands.SummonMirror: - return PointerManager.m_Instance.CurrentSymmetryMode != - SymmetryMode.None; - case GlobalCommands.InvertSelection: - case GlobalCommands.FlipSelection: - return SelectionManager.m_Instance.HasSelection; - case GlobalCommands.SelectAll: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - m_WidgetManager.HasSelectableWidgets(); - case GlobalCommands.UnloadReferenceImageCatalog: - return ReferenceImageCatalog.m_Instance.AnyImageValid(); - case GlobalCommands.ToggleCameraPathPreview: - return m_WidgetManager.CanRecordCurrentCameraPath(); - case GlobalCommands.DeleteCameraPath: - return CameraPathCaptureRig.Enabled && m_WidgetManager.AnyActivePathHasAKnot(); - case GlobalCommands.ToggleCameraPathVisuals: - return m_WidgetManager.AnyActivePathHasAKnot(); - case GlobalCommands.GoogleDriveSync: - return App.GoogleIdentity.LoggedIn; - case GlobalCommands.RecordCameraPath: return m_WidgetManager.CameraPathsVisible; - } - return true; - } - - public bool SketchHasChanges() - { - if (SceneSettings.m_Instance.IsTransitioning) { return false; } - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - SceneSettings.m_Instance.EnvironmentChanged || - LightsControlScript.m_Instance.LightsChanged || - m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.LightWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.StencilWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.VideoWidgets.Any(w => w.gameObject.activeSelf) || - m_WidgetManager.AnyCameraPathWidgetsActive; - } - - public void OpenPanelOfType(BasePanel.PanelType type, TrTransform trSpawnXf) - { - m_PanelManager.OpenPanel(type, trSpawnXf); - EatGazeObjectInput(); - } - - public void RestoreFloatingPanels() - { - if (!m_SketchSurfacePanel.ActiveTool.HidePanels()) - { - m_PanelManager.RestoreHiddenPanels(); - } - } - - public void UpdateDraftingVisibility() - { - float value = 0; - switch (m_DraftingVisibility) - { - case DraftingVisibilityOption.Visible: - value = 1; - break; - case DraftingVisibilityOption.Transparent: - value = .5f; - break; - case DraftingVisibilityOption.Hidden: - value = 0; - break; - } - Shader.SetGlobalFloat("_DraftingVisibility01", value); - } - - private void ToggleProfiling() - { - if (Debug.isDebugBuild && ProfileDisplay.Instance != null) - { - ProfileDisplay.Instance.gameObject.SetActive(UnityEngine.Profiling.Profiler.enabled); - } - if (UnityEngine.Profiling.Profiler.enabled) - { - ProfilingManager.Instance.StopProfiling(); - } - else - { - ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProflingMode); - } - } - - private void DoAutoProfile() - { - StartCoroutine(DoProfiling()); - } - - private void DoAutoProfileAndQuit() - { - StartCoroutine(DoProfiling(andQuit: true)); - } - - private IEnumerator DoProfiling(bool andQuit = false) - { - TrTransform oldWandPose = TrTransform.FromTransform(InputManager.Wand.Geometry.transform); - TrTransform oldBrushPose = TrTransform.FromTransform(InputManager.Brush.Geometry.transform); - - App.AppState oldState = App.CurrentState; - App.Instance.SetDesiredState(App.AppState.AutoProfiling); - while (App.CurrentState != App.AppState.AutoProfiling) - { - yield return null; - } - - TrTransform camPose = App.Scene.Pose * SaveLoadScript.m_Instance.ReasonableThumbnail_SS; - camPose.ToTransform(App.VrSdk.GetVrCamera().transform); - float controllerDirection = App.UserConfig.Profiling.ShowControllers ? 1f : -1f; - Vector3 roffset = Camera.main.transform.right * 2f; - Vector3 fOffset = Camera.main.transform.forward * 4f * controllerDirection; - InputManager.Brush.Geometry.transform.position = Camera.main.transform.position + roffset + fOffset; - InputManager.Brush.Geometry.transform.rotation = Camera.main.transform.rotation; - InputManager.Wand.Geometry.transform.position = Camera.main.transform.position - roffset + fOffset; - InputManager.Wand.Geometry.transform.rotation = Camera.main.transform.rotation; - m_PanelManager.LockPanelsToController(); - - ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProflingMode); - yield return new WaitForSeconds(App.UserConfig.Profiling.Duration); - ProfilingManager.Instance.StopProfiling(); - - if (App.UserConfig.Profiling.TakeScreenshot) - { - GameObject camObj = new GameObject("ScreenShotter"); - Camera cam = camObj.AddComponent(); - cam.CopyFrom(App.VrSdk.GetVrCamera()); - cam.stereoTargetEye = StereoTargetEyeMask.None; - cam.clearFlags = CameraClearFlags.SolidColor; - camPose.ToTransform(camObj.transform); - int res = App.UserConfig.Profiling.ScreenshotResolution; - RenderTexture renderTexture = RenderTexture.GetTemporary(res, res, 24); - try - { - cam.targetTexture = renderTexture; - cam.Render(); - RenderTexture prev = RenderTexture.active; - RenderTexture.active = renderTexture; - var texture = new Texture2D(res, res, TextureFormat.RGB24, false); - texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); - RenderTexture.active = prev; - byte[] jpegBytes = texture.EncodeToJPG(); - string filename = - Path.GetFileNameWithoutExtension(SaveLoadScript.m_Instance.SceneFile.FullPath); - File.WriteAllBytes(Path.Combine(App.UserPath(), filename + ".jpg"), jpegBytes); - } - finally - { - Destroy(camObj); - RenderTexture.ReleaseTemporary(renderTexture); - } - } - - oldWandPose.ToTransform(InputManager.Wand.Geometry.transform); - oldBrushPose.ToTransform(InputManager.Brush.Geometry.transform); - App.Instance.SetDesiredState(oldState); - - if (andQuit) - { - QuitApp(); - } - } - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using TiltBrush.Layers; +using UnityEngine; +using UnityEngine.InputSystem; +using SymmetryMode = TiltBrush.PointerManager.SymmetryMode; + +namespace TiltBrush +{ + + public class SketchControlsScript : MonoBehaviour + { + public const string kRemoveHeadsetFyi = "Remove headset to view."; + const string kTiltBrushGalleryUrl = "https://icosa.gallery"; + const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; + const string kPolyMainPageUri = "https://poly.google.com"; + + static public SketchControlsScript m_Instance; + static bool sm_enableGrabHaptics = true; + + // ------------------------------------------------------------ + // Constants and types + // ------------------------------------------------------------ + + public enum GlobalCommands + { + Null, + Save, + SaveNew, + Load, + NewSketch, + StraightEdge, + AutoOrient, + Undo, + Redo, + Tiltasaurus, + LightingHdr, + AudioVisualization, + ResetAllPanels, + SketchOrigin, + SymmetryPlane, + MultiMirror, + ViewOnly, + SaveGallery, + LightingLdr, + ShowSketchFolder, + About, + LoadNamedFile, // iParam1 : (optional) - send through a LoadSpeed as int + DropCam, + CuratedGallery, + Unused_UploadToCloud, + AnalyticsEnabled_Deprecated, + Credits, + LogOutOfGenericCloud, + DraftingVisibility, + DeleteSketch, + ShowWindowGUI, + MorePanels, + Cameras, + FAQ, + ExportRaw, + IRC, + YouTubeChat, + CameraOptions, + StencilsDisabled, + AdvancedTools, + FloatingPanelsMode, + StraightEdgeMeterDisplay, + Sketchbook, + ExportAll, + Lights, + SaveAndUpload, + StraightEdgeShape, + SaveOptions, + SketchbookMenu, + Disco, + ViewOnlineGallery, + CancelUpload, + AdvancedPanelsToggle, + Music, + Duplicate, + ToggleGroupStrokesAndWidgets, + SaveModel, + ViewPolyPage, + ViewPolyGallery, + ExportListed, + RenderCameraPath, + ToggleProfiling, + DoAutoProfile, + DoAutoProfileAndQuit, + ToggleSettings, + SummonMirror, + InvertSelection, + SelectAll, + FlipSelection, + ToggleBrushLab, + ReleaseNotes, + ToggleCameraPostEffects, + ToggleWatermark, + AccountInfo, + // LoadConfirmUnsaved -> LoadWaitOnDownload -> LoadConfirmComplex -> LoadComplexHigh -> Load + LoadConfirmUnsaved, + LoadConfirmComplex, + MemoryWarning, + MemoryExceeded, + ViewLastUpload, + LoadConfirmComplexHigh, + ShowTos, + ShowPrivacy, + ShowQuestSideLoading, + AshleysSketch, + UnloadReferenceImageCatalog, + SaveOnLocalChanges, + ToggleCameraPathVisuals, + ToggleCameraPathPreview, + DeleteCameraPath, + RecordCameraPath, + SelectCameraPath, + ToggleAutosimplification, + ShowGoogleDrive, + GoogleDriveSync_Folder, // iParam1: folder id as DriveSync.SyncedFolderType + GoogleDriveSync, + LoginToGenericCloud, // iParam1: Cloud enum + UploadToGenericCloud, // iParam1: Cloud enum + LoadWaitOnDownload, + SignOutConfirm, + ReadOnlyNotice, + ShowContribution, + + // Open Brush Reserved Enums 1000-1999 + LanguagePopup = 1000, + + RenameSketch = 5200, + OpenLayerOptionsPopup = 5201, + RenameLayer = 5202, + LoginToIcosa = 5600, + OpenDirectorChooserPopup = 5800, + OpenScriptsCommandsList = 6000, + OpenScriptsList = 6001, + OpenExampleScriptsList = 6002, + SymmetryTwoHanded = 6003, + OpenColorOptionsPopup = 7000, + ChangeSnapAngle = 8000, + MergeBrushStrokes = 10000, + RepaintOptions = 11500, + OpenNumericInputPopup = 12000 + } + + public enum ControlsType + { + KeyboardMouse, + SixDofControllers, + ViewingOnly + } + + public enum DraftingVisibilityOption + { + Visible, + Transparent, + Hidden + } + + public enum InputState + { + Standard, + Pan, + Rotation, + HeadLock, + ControllerLock, + PushPull, + BrushSize, + Save, + Load, + Num + } + + public enum LoadSpeed + { + Normal = -1, + Quick = 1, + } + + const float kControlPointHistoryMaxTime = 0.1f; + + class GazeResult + { + public bool m_HitWithGaze; + public bool m_HitWithController; + // ReSharper disable once NotAccessedField.Local + public bool m_WithinView; + public float m_ControllerDistance; + public Vector3 m_GazePosition; + public Vector3 m_ControllerPosition; + public InputManager.ControllerName m_ControllerName; + } + + class GrabWidgetControllerInfo + { + public InputManager.ControllerName m_Name; + /// Transform of controller at the time the grab started + public TrTransform m_BaseControllerXf; + /// "local" transform of widget (relative to controller), at the time the grab started. + /// The widget isn't parented to the controller, but if it were, this would be its transform. + public TrTransform m_BaseWidgetXf_LS; + } + + struct GrabWidgetHoldPoint + { + // ReSharper disable once NotAccessedField.Local + public InputManager.ControllerName m_Name; + public float m_BirthTime; + public Vector3 m_Pos; // where controller is holding the widget + public Quaternion m_Rot; + } + + class InputStateConfig + { + public bool m_AllowDrawing; + public bool m_AllowMovement; + public bool m_ShowGizmo; + } + + enum FadeState + { + None, + FadeOn, + FadeOff + } + + enum GrabWidgetState + { + None, + OneHand, + TwoHands + } + + enum GrabWorldState + { + Normal, + ResettingTransform, + ResetDone + } + + private enum WorldTransformResetState + { + Default, + Requested, + FadingToBlack, + FadingToScene, + } + + enum RotationType + { + All, + RollOnly + } + + enum GrabIntersectionState + { + RequestIntersections, + ReadBrush, + ReadWand + } + + // ------------------------------------------------------------ + // Inspector data (read-only even if public) + // ------------------------------------------------------------ + + public GameObject m_SketchSurface; + public SketchMemoryScript.PlaybackMode m_DefaultSketchPlaybackMode; + public float m_GazeMaxAngleFromPointing = 85.0f; + public float m_GazeMaxAngleFacingToForward = 80.0f; + + [SerializeField] bool m_AtlasIconTextures; + + [SerializeField] SaveIconTool m_SaveIconTool; + [SerializeField] DropCamWidget m_DropCam; + [SerializeField] string m_CreditsSketchFilename; + [SerializeField] string m_AshleysSketchFilename; + [SerializeField] float m_DefaultSketchLoadSpeed; + [SerializeField] GameObject m_TransformGizmoPrefab; + + [SerializeField] GameObject m_RotationIconPrefab; + [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; + [SerializeField] float m_GazeMaxDistance = 10.0f; + [SerializeField] float m_GazeControllerPointingDistance; + [SerializeField] float m_GazePanelDectivationDelay = 0.25f; + + [SerializeField] GameObject m_UIReticle; + [SerializeField] GameObject m_UIReticleMobile; + [SerializeField] GameObject m_UIReticleSixDofController; + + [SerializeField] float m_DoubleTapWindow; + [SerializeField] float m_PushPullScale; + [SerializeField] RotationCursorScript m_RotationCursor; + [SerializeField] float m_RotationMaxAngle; + + [SerializeField] float m_RotationScalar; + [SerializeField] float m_RotationRollScalar; + [SerializeField] float m_PanScalar; + + [SerializeField] float m_AdjustToolSizeScalar; + + [SerializeField] GameObject m_IRCChatPrefab; + [SerializeField] GameObject m_YouTubeChatPrefab; + [SerializeField] GameObject m_Decor; + [SerializeField] BaseTool.ToolType m_InitialTool = BaseTool.ToolType.SketchSurface; + [SerializeField] string m_ReleaseNotesURL; + [SerializeField] string m_HelpCenterURL; + [SerializeField] string m_ThirdPartyNoticesURL; + [SerializeField] string m_TosURL; + [SerializeField] string m_PrivacyURL; + [SerializeField] string m_QuestSideLoadingHowToURL; + + [Multiline] + [SerializeField] string m_ContributionPromoText; + [SerializeField] string m_ContributionURL; + + [SerializeField] float m_WorldTransformMinScale = .1f; + [SerializeField] float m_WorldTransformMaxScale = 10.0f; + + [Header("Undo/Redo Hold")] + [SerializeField] float m_UndoRedoHold_DurationBeforeStart; + [SerializeField] float m_UndoRedoHold_RepeatInterval; + + [Header("Pin Cushion")] + [SerializeField] GameObject m_PinCushionPrefab; + + [Header("Grabbing and tossing")] + [SerializeField] float m_GrabWorldFadeSpeed = 8.0f; + [SerializeField] Color m_GrabWorldGridColor = new Color(0.0f, 1.0f, 1.0f, 0.2f); + [SerializeField] ControllerGrabVisuals m_ControllerGrabVisuals; + [SerializeField] float m_WidgetGpuIntersectionRadius; + + [Header("Saving")] + [SerializeField] int m_NumStrokesForSaveIcon = 50; + + [NonSerialized] public Color m_GrabHighlightActiveColor; + [NonSerialized] public bool m_DisableWorldGrabbing = false; + + /// Throwing an object faster than this means it's a "toss". Units are m/s. + public float m_TossThresholdMeters = 3f; + /// Angular motion contributes more towards the toss velocity the larger the object is; + /// or rather, the larger the distance between the grab point and the object's center. + /// To prevent large objects from being too-easily-tossed, bound that distance. + public float m_TossMaxPivotDistMeters = 0.33f; + + // ------------------------------------------------------------ + // Internal data + // ------------------------------------------------------------ + + private SketchSurfacePanel m_SketchSurfacePanel; + private SketchMemoryScript.PlaybackMode m_SketchPlaybackMode; + private GameObject m_TransformGizmo; + private TransformGizmoScript m_TransformGizmoScript; + private GameObject m_RotationIcon; + private float m_MouseDeltaX; + private float m_MouseDeltaY; + private float m_MouseDeltaXScaled; + private float m_MouseDeltaYScaled; + private float m_PositionOffsetResetTapTime; + private bool m_EatToolScaleInput; + + private PanelManager m_PanelManager; + private WidgetManager m_WidgetManager; + private PinCushion m_PinCushion; + private bool m_EatPinCushionInput; + + // This is the gaze that was used to compute m_CurrentGazeHitPoint. + // It is not a general substitute for ViewpointScript.Gaze. + private Ray m_CurrentGazeRay; + private Quaternion m_CurrentHeadOrientation; + private GazeResult[] m_GazeResults; + private int m_CurrentGazeObject; + private bool m_EatInputGazeObject; + private Vector3 m_CurrentGazeHitPoint; + private Ray m_GazeControllerRay; + private Ray m_GazeControllerRayActivePanel; + private bool m_ForcePanelActivation = false; + private float m_GazePanelDectivationCountdown; + private bool m_PanelsVisibilityRequested; + + // Previously Experimental-Model only + private bool m_HeadOffset; + + float m_UndoHold_Timer; + float m_RedoHold_Timer; + + // Grab world member variables. + struct GrabState + { + public InputManager.ControllerName name; + public TrTransform grabTransform; + public bool grabbingWorld; + public bool grabbingGroup; + public bool startedGrabInsideWidget; + public bool eatInput; + private GrabWidget lastWidgetIntersect; + + public void SetHadBestGrabAndTriggerHaptics(GrabWidgetData data) + { + bool dormant = WidgetManager.m_Instance.WidgetsDormant; + if (data != null && !data.m_WidgetScript.AllowDormancy) + { + dormant = false; + } + GrabWidget newInsideWidget = (data != null && !dormant) ? data.m_WidgetScript : null; + if (sm_enableGrabHaptics && newInsideWidget != lastWidgetIntersect) + { + // state changed + if (newInsideWidget != null) + { + // transitioning in + InputManager.m_Instance.TriggerHaptics(name, data.m_WidgetScript.HapticDuration); + } + else + { + // transitioning out + InputManager.m_Instance.TriggerHaptics(name, 0.03f); + } + } + lastWidgetIntersect = newInsideWidget; + } + + public void ClearInsideWidget() + { + lastWidgetIntersect = null; + } + } + private GrabState m_GrabBrush = new GrabState { name = InputManager.ControllerName.Brush }; + private GrabState m_GrabWand = new GrabState { name = InputManager.ControllerName.Wand }; + + private WorldTransformResetState m_WorldTransformResetState = WorldTransformResetState.Default; + private TrTransform m_WorldTransformResetXf = TrTransform.identity; // set when reset requested + private GrabWorldState m_GrabWorldState = GrabWorldState.Normal; + private float m_WorldTransformFadeAmount; + private bool m_AllowWorldTransformLastFrame = false; + private bool m_WorldBeingGrabbed; + private TrTransform m_xfDropCamReset_RS; + + struct GpuIntersectionResult + { + public GpuIntersector.FutureModelResult result; + public List resultList; + } + private Queue m_BrushResults; + private Queue m_WandResults; + private int m_WidgetGpuIntersectionLayer; + + private GrabWidget m_CurrentGrabWidget; + private GrabWidget m_MaybeDriftingGrabWidget; // use only to clear drift + + // References to widgets, cached in the UpdateGrab_None, to be used by helper functions + // for the remainder of the frame. + private GrabWidget m_PotentialGrabWidgetBrush; + private GrabWidget m_PotentialGrabWidgetWand; + + // Flags for the explaining if the m_PotentialGrabWidget_x widgets are able to be interacted with. + // Cached in the UpdateGrab_None, used for the remainder of the frame. + private bool m_PotentialGrabWidgetBrushValid; + private bool m_PotentialGrabWidgetWandValid; + + // References to widget metadata, cached in UpdateGrab_None, to be re-used on "off frames" + // when the GPU intersector is not refreshing the nearest widget to the respective controller. + private GrabWidgetData m_BackupBrushGrabData; + private GrabWidgetData m_BackupWandGrabData; + + private GrabWidgetState m_GrabWidgetState; + private GrabWidgetControllerInfo m_GrabWidgetOneHandInfo; + private TrTransform m_GrabWidgetTwoHandBrushPrev; + private TrTransform m_GrabWidgetTwoHandWandPrev; + private Queue m_GrabWidgetHoldHistory; + + private Quaternion m_RotationOrigin; + private Vector2 m_RotationCursorOffset; + + private bool m_RotationRollActive; + private float m_RotationResetTapTime; + + private RotationType m_CurrentRotationType; + private bool m_AutoOrientAfterRotation; + + private Vector3 m_SurfaceForward; + private Vector3 m_SurfaceRight; + private Vector3 m_SurfaceUp; + + private Vector3 m_SurfaceLockOffset; + private Vector3 m_SurfaceLockBaseSurfacePosition; + private Vector3 m_SurfaceLockBaseControllerPosition; + private Quaternion m_SurfaceLockBaseHeadRotation; + private Quaternion m_SurfaceLockBaseControllerRotation; + private Quaternion m_SurfaceLockBaseSurfaceRotation; + private InputManager.ControllerName m_SurfaceLockActingController; + private float m_SurfaceLockControllerBaseScalar; + private float m_SurfaceLockControllerScalar; + + private bool m_PositioningPanelWithHead; + private Quaternion m_PositioningPanelBaseHeadRotation; + private Vector3 m_PositioningPanelOffset; + private float m_PositioningTimer; + private float m_PositioningSpeed; + + private DraftingVisibilityOption m_DraftingVisibility = DraftingVisibilityOption.Visible; + + private Vector3 m_SketchOrigin; + + private ControlsType m_ControlsType; + private GrabWidget m_IRCChatWidget; + private GrabWidget m_YouTubeChatWidget; + private MultiCamCaptureRig m_MultiCamCaptureRig; + private CameraPathCaptureRig m_CameraPathCaptureRig; + + private bool m_ViewOnly = false; + + private InputState m_CurrentInputState; + private InputStateConfig[] m_InputStateConfigs; + + private GrabIntersectionState m_CurrentGrabIntersectionState; + + private float m_WorldTransformSpeedSmoothed; + + // ------------------------------------------------------------ + // Properties and events + // ------------------------------------------------------------ + + public MultiCamCaptureRig MultiCamCaptureRig + { + get { return m_MultiCamCaptureRig; } + } + + public CameraPathCaptureRig CameraPathCaptureRig + { + get { return m_CameraPathCaptureRig; } + } + + public ControllerGrabVisuals ControllerGrabVisuals + { + get { return m_ControllerGrabVisuals; } + } + + public SketchMemoryScript.PlaybackMode SketchPlaybackMode + { + get { return m_SketchPlaybackMode; } + set { m_SketchPlaybackMode = value; } + } + + public Transform m_Canvas + { + get { return App.Instance.m_CanvasTransform; } + } + + public ControlsType ActiveControlsType + { + get { return m_ControlsType; } + set { m_ControlsType = value; } + } + + public float WorldTransformMinScale + { + get + { + return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMinScale * 0.01f : + m_WorldTransformMinScale; + } + } + + public float WorldTransformMaxScale + { + get + { + return App.UserConfig.Flags.UnlockScale ? m_WorldTransformMaxScale * 10.0f : + m_WorldTransformMaxScale; + } + } + + public void SetInitialTool(BaseTool.ToolType rType) + { + m_InitialTool = rType; + } + + public void SetInFreePaintMode(bool bFreePaint) + { + m_SketchSurfacePanel.SetInFreePaintMode(bFreePaint); + } + + public float GazeMaxDistance + { + get { return m_GazeMaxDistance; } + } + + public InputManager.ControllerName OneHandGrabController + { + get + { + return m_CurrentGrabWidget != null ? + m_GrabWidgetOneHandInfo.m_Name : + InputManager.ControllerName.None; + } + } + + public InputManager.ControllerName PotentialOneHandGrabController(GrabWidget widget) + { + if (m_PotentialGrabWidgetBrush == widget) + { + return InputManager.ControllerName.Brush; + } + else if (m_PotentialGrabWidgetWand == widget) + { + return InputManager.ControllerName.Wand; + } + return OneHandGrabController; + } + + public Vector3 GetSurfaceForward() { return m_SurfaceForward; } + public Vector3 GetSurfaceUp() { return m_SurfaceUp; } + public Vector3 GetSurfaceRight() { return m_SurfaceRight; } + public Vector3 GetSketchOrigin() { return m_SketchOrigin; } + public float GetDefaultSketchLoadSpeed() { return m_DefaultSketchLoadSpeed; } + public Quaternion GetCurrentHeadOrientation() { return m_CurrentHeadOrientation; } + public Vector3 GetUIReticlePos() { return m_UIReticle.transform.position; } + public Vector3 GetSweetSpotPos() { return m_PanelManager.m_SweetSpot.transform.position; } + public void SetSketchOrigin(Vector3 vOrigin) { m_SketchOrigin = vOrigin; } + + public void EatGazeObjectInput() + { + m_EatInputGazeObject = true; + m_GazePanelDectivationCountdown = 0.0f; + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + } + public void EatToolScaleInput() { m_EatToolScaleInput = true; } + public void EatGrabInput() + { + m_GrabWand.eatInput = true; + m_GrabBrush.eatInput = true; + } + + public bool ShouldRespondToPadInput(InputManager.ControllerName name) + { + if (name == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).BrushPadAnimatesOnHover(); + } + return !m_EatToolScaleInput && SketchSurfacePanel.m_Instance.CanAdjustToolSize(); + } + public void ForcePanelActivation(bool bForce) + { + m_ForcePanelActivation = bForce; + if (m_ForcePanelActivation) + { + m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; + } + } + public bool IsUserInteractingWithUI() + { + return (m_CurrentGazeObject != -1) || (m_GazePanelDectivationCountdown > 0.0f); + } + public bool IsUIBlockingUndoRedo() + { + if (m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).UndoRedoBlocked(); + } + return false; + } + public bool IsUserAbleToInteractWithAnyWidget() + { + return IsUserInteractingWithAnyWidget() || + (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || + (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid); + } + public bool IsUserInteractingWithAnyWidget() { return m_CurrentGrabWidget != null; } + public bool IsUserGrabbingAnyPanel() + { + return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is PanelWidget); + } + public bool IsUsersBrushIntersectingWithSelectionWidget() + { + return (m_PotentialGrabWidgetBrush != null && + m_PotentialGrabWidgetBrushValid && + m_PotentialGrabWidgetBrush is SelectionWidget); + } + public bool IsUserIntersectingWithSelectionWidget() + { + return IsUsersBrushIntersectingWithSelectionWidget() || + (m_PotentialGrabWidgetWand != null && + m_PotentialGrabWidgetWandValid && + m_PotentialGrabWidgetWand is SelectionWidget); + } + public bool IsUserInteractingWithSelectionWidget() + { + return (m_CurrentGrabWidget != null && m_CurrentGrabWidget is SelectionWidget); + } + + public bool IsUserGrabbingWorld() { return m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld; } + public bool IsUserGrabbingWorldWithBrushHand() { return m_GrabBrush.grabbingWorld; } + public bool IsUserTransformingWorld() { return m_GrabWand.grabbingWorld && m_GrabBrush.grabbingWorld; } + public float GetGazePanelActivationRatio() { return m_GazePanelDectivationCountdown / m_GazePanelDectivationDelay; } + public bool IsCurrentGrabWidgetPinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.Pinned; } + public bool CanCurrentGrabWidgetBePinned() { return IsUserInteractingWithAnyWidget() && m_CurrentGrabWidget.AllowPinning; } + public bool DidUserGrabWithBothInside() { return m_GrabBrush.startedGrabInsideWidget && m_GrabWand.startedGrabInsideWidget; } + public bool IsUserGrabbingWidget(GrabWidget widget) { return widget == m_CurrentGrabWidget; } + public bool IsUserTwoHandGrabbingWidget() { return m_GrabWidgetState == GrabWidgetState.TwoHands; } + public bool IsPinCushionShowing() { return m_PinCushion.IsShowing(); } + public bool IsUserLookingAtPanel(BasePanel panel) + { + return m_CurrentGazeObject > -1 && + m_PanelManager.GetAllPanels()[m_CurrentGazeObject].m_Panel == panel; + } + + public SaveIconTool GetSaveIconTool() + { + return m_SaveIconTool; + } + + public DropCamWidget GetDropCampWidget() + { + return m_DropCam; + } + + public bool IsGrabWorldStateStable() + { + return m_GrabWorldState == GrabWorldState.Normal; + } + + // Internal: modify Coords.ScenePose or Coords.CanvasPose depending on the + // state of m_InTransformCanvasMode + TrTransform GrabbedPose + { + get + { + return App.Scene.Pose; + } + set + { + App.Scene.Pose = value; + } + } + + public Transform GazeObjectTransform() + { + if (m_CurrentGazeObject != -1) + { + return m_PanelManager.GetPanel(m_CurrentGazeObject).transform; + } + return null; + } + + public void ForceShowUIReticle(bool bVisible) + { + m_UIReticle.SetActive(bVisible); + } + + public void SetUIReticleTransform(Vector3 vPos, Vector3 vForward) + { + m_UIReticle.transform.position = vPos; + m_UIReticle.transform.forward = vForward; + } + + public bool AtlasIconTextures + { + get { return m_AtlasIconTextures; } + } + + public IconTextureAtlas IconTextureAtlas + { + get { return GetComponent(); } + } + public GrabWidget CurrentGrabWidget => m_CurrentGrabWidget; + + void DismissPopupOnCurrentGazeObject(bool force) + { + if (m_CurrentGazeObject != -1) + { + m_PanelManager.GetPanel(m_CurrentGazeObject).CloseActivePopUp(force); + } + } + + void Awake() + { + m_Instance = this; + + BrushController.m_Instance.BrushSetToDefault += OnBrushSetToDefault; + + IconTextureAtlas.Init(); + + m_MultiCamCaptureRig = GetComponentInChildren(true); + m_MultiCamCaptureRig.Init(); + + m_CameraPathCaptureRig = GetComponentInChildren(true); + m_CameraPathCaptureRig.Init(); + + m_SketchSurfacePanel = m_SketchSurface.GetComponent(); + m_PanelManager = GetComponent(); + m_PanelManager.Init(); + InitGazePanels(); + + m_WidgetManager = GetComponent(); + m_WidgetManager.Init(); + + m_InputStateConfigs = new InputStateConfig[(int)InputState.Num]; + for (int i = 0; i < (int)InputState.Num; ++i) + { + m_InputStateConfigs[i] = new InputStateConfig(); + m_InputStateConfigs[i].m_AllowDrawing = false; + m_InputStateConfigs[i].m_AllowMovement = true; + m_InputStateConfigs[i].m_ShowGizmo = false; + } + + m_InputStateConfigs[(int)InputState.Standard].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.Pan].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.HeadLock].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowDrawing = true; + m_InputStateConfigs[(int)InputState.PushPull].m_AllowDrawing = true; + + m_InputStateConfigs[(int)InputState.Pan].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.Rotation].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.ControllerLock].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.PushPull].m_AllowMovement = false; + m_InputStateConfigs[(int)InputState.BrushSize].m_AllowMovement = false; + + m_InputStateConfigs[(int)InputState.Pan].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.Rotation].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.HeadLock].m_ShowGizmo = true; + m_InputStateConfigs[(int)InputState.PushPull].m_ShowGizmo = true; + + m_CurrentGazeRay = new Ray(Vector3.zero, Vector3.forward); + m_GazeControllerRay = new Ray(Vector3.zero, Vector3.forward); + m_GazeControllerRayActivePanel = new Ray(Vector3.zero, Vector3.forward); + + m_GrabWidgetHoldHistory = new Queue(); + m_GrabWidgetOneHandInfo = new GrabWidgetControllerInfo(); + + // Initialize world grip members. + m_GrabBrush.grabTransform = TrTransform.identity; + m_GrabWand.grabTransform = TrTransform.identity; + + m_BrushResults = new Queue(); + m_WandResults = new Queue(); + m_WidgetGpuIntersectionLayer = LayerMask.NameToLayer("GpuIntersection"); + m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; + } + + public void InitGazePanels() + { + // Find all gaze panels. + int iNumGazePanels = m_PanelManager.GetAllPanels().Count; + m_GazeResults = new GazeResult[iNumGazePanels]; + for (int i = 0; i < iNumGazePanels; ++i) + { + m_GazeResults[i] = new GazeResult(); + m_GazeResults[i].m_HitWithGaze = false; + m_GazeResults[i].m_HitWithController = false; + m_GazeResults[i].m_WithinView = false; + m_GazeResults[i].m_GazePosition = new Vector3(); + } + } + + public void OnEnable() + { + // This needs to run before other tools initialize, which is why it's running in OnEnable. + // The sequence is Awake(), OnEnable(), Start(). + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) + { + SetInFreePaintMode(true); + SetInitialTool(BaseTool.ToolType.FreePaintTool); + } + } + + void Start() + { + m_TransformGizmo = (GameObject)Instantiate(m_TransformGizmoPrefab); + m_TransformGizmo.transform.parent = transform; + m_TransformGizmoScript = m_TransformGizmo.GetComponent(); + m_TransformGizmo.SetActive(false); + + m_RotationIcon = (GameObject)Instantiate(m_RotationIconPrefab); + m_RotationIcon.transform.position = m_SketchSurface.transform.position; + m_RotationIcon.transform.parent = m_SketchSurface.transform; + m_RotationIcon.SetActive(false); + + GameObject pinCushionObj = (GameObject)Instantiate(m_PinCushionPrefab); + m_PinCushion = pinCushionObj.GetComponent(); + + m_PositionOffsetResetTapTime = 0.0f; + + m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + + m_AutoOrientAfterRotation = true; + m_RotationCursor.gameObject.SetActive(false); + + ResetGrabbedPose(); + m_SketchOrigin = m_SketchSurface.transform.position; + + m_PanelManager.InitPanels(m_ControlsType == ControlsType.SixDofControllers); + + m_UIReticleMobile.SetActive(m_ControlsType == ControlsType.ViewingOnly); + m_UIReticleSixDofController.SetActive(m_ControlsType != ControlsType.ViewingOnly); + + m_PositioningPanelWithHead = false; + m_PositioningSpeed = 16.0f; + + m_CurrentRotationType = RotationType.All; + m_RotationResetTapTime = 0.0f; + + m_CurrentInputState = InputState.Standard; + + m_SketchSurfacePanel.EnableSpecificTool(m_InitialTool); + m_SurfaceLockControllerBaseScalar = m_SketchSurfacePanel.m_PanelSensitivity; + + //after initializing, start with gaze objects hidden + m_CurrentGazeObject = -1; + m_EatInputGazeObject = false; + + // Previously set to 0 in experimental builds + int hidePanelsDelay = 1; + + StartCoroutine(DelayedHidePanels(hidePanelsDelay)); + + m_DropCam.Show(false); + + m_GrabWidgetState = GrabWidgetState.None; + + UpdateDraftingVisibility(); + + m_DisableWorldGrabbing = false; + } + + private IEnumerator DelayedHidePanels(int frames) + { + int stall = frames; + while (stall-- > 0) + { + yield return null; + } + + m_PanelManager.HidePanelsForStartup(); + RequestPanelsVisibility(false); + } + + void Update() + { + // TODO: we need to figure out what transform to pass in here! + // Maybe best _just for now_ to use the scene transform? + TrTransform scenePose = App.Scene.Pose; + Shader.SetGlobalMatrix("xf_CS", scenePose.ToMatrix4x4()); + Shader.SetGlobalMatrix("xf_I_CS", scenePose.inverse.ToMatrix4x4()); + } + + void LateUpdate() + { + // Gracefully exits if we're not recording a video. + VideoRecorderUtils.SerializerNewUsdFrame(); + } + + public bool IsFreepaintToolReady() + { + return + !m_PinCushion.IsShowing() && + !PointerManager.m_Instance.IsStraightEdgeProxyActive() && + !InputManager.m_Instance.ControllersAreSwapping() && + (m_SketchSurfacePanel.IsSketchSurfaceToolActive() || + (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.FreePaintTool)) + ; + } + + public void UpdateControls() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlsScript.UpdateControls"); + m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; + + // Verify controllers are available and prune state if they're not. + if ((App.VrSdk.GetControllerDof() == VrSdk.DoF.Six && + App.VrSdk.IsInitializingUnityXR) && App.VrSdk.IsHmdInitialized()) + { + m_PanelManager.SetVisible(false); + PointerManager.m_Instance.RequestPointerRendering(false); + return; + } + + //mouse movement + Vector2 mv = InputManager.m_Instance.GetMouseMoveDelta(); + m_MouseDeltaX = mv.x; + m_MouseDeltaY = mv.y; + + UpdateGazeObjectsAnimationState(); + UpdateCurrentGazeRay(); + m_SketchSurfacePanel.SetBacksideActive(m_CurrentGazeRay.origin); + m_PanelManager.UpdatePanels(); + + m_MouseDeltaXScaled = m_MouseDeltaX * GetAppropriateMovementScalar(); + m_MouseDeltaYScaled = m_MouseDeltaY * GetAppropriateMovementScalar(); + + //this is used for one-shot inputs that don't require state and do not change state + UpdateBaseInput(); + + UpdatePinCushionVisibility(); + + //if the pointer manager is processing, we don't want to respond to input + if (!PointerManager.m_Instance.IsMainPointerProcessingLine()) + { + + //see if we're grabbing a widget + UpdateGrab(); + + //see if we're looking at a gaze object + RefreshCurrentGazeObject(); + + // Tools allowed when widgets aren't grabbed. + bool bWidgetGrabOK = m_GrabWidgetState == GrabWidgetState.None; + + // If we don't have a widget held and we're not grabbing the world with the brush controller, + // update tools. + if (bWidgetGrabOK && !m_GrabBrush.grabbingWorld) + { + if (m_CurrentGazeObject != -1 && !m_WorldBeingGrabbed) + { + UpdateActiveGazeObject(); + + // Allow for standard input (like Undo / Redo) even when gazing at a panel. + if (m_CurrentInputState == InputState.Standard) + { + UpdateStandardInput(); + } + } + else + { + //standard input, no gaze object + if (m_InputStateConfigs[(int)m_CurrentInputState].m_AllowMovement) + { + m_SketchSurfacePanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); + } + + switch (m_CurrentInputState) + { + case InputState.Standard: + UpdateStandardInput(); + break; + case InputState.Pan: + UpdatePanInput(); + break; + case InputState.Rotation: + UpdateRotationInput(); + break; + case InputState.HeadLock: + UpdateHeadLockInput(); + break; + case InputState.ControllerLock: + UpdateControllerLock(); + break; + case InputState.PushPull: + UpdatePushPullInput(); + break; + case InputState.Save: + UpdateSaveInput(); + break; + case InputState.Load: + UpdateLoadInput(); + break; + } + + //keep pointer locked in the right spot, even if it's hidden + if (m_SketchSurfacePanel.ActiveTool.LockPointerToSketchSurface()) + { + Vector3 vPointerPos = Vector3.zero; + Vector3 vPointerForward = Vector3.zero; + m_SketchSurfacePanel.GetReticleTransform(out vPointerPos, out vPointerForward, + (m_ControlsType == ControlsType.ViewingOnly)); + PointerManager.m_Instance.SetMainPointerPosition(vPointerPos); + PointerManager.m_Instance.SetMainPointerForward(vPointerForward); + } + + m_SketchSurfacePanel.AllowDrawing(m_InputStateConfigs[(int)m_CurrentInputState].m_AllowDrawing); + m_SketchSurfacePanel.UpdateCurrentTool(); + + PointerManager.m_Instance.AllowPointerPreviewLine(IsFreepaintToolReady()); + //keep transform gizmo at sketch surface pos + m_TransformGizmo.transform.position = m_SketchSurface.transform.position; + bool bGizmoActive = m_InputStateConfigs[(int)m_CurrentInputState].m_ShowGizmo && m_SketchSurfacePanel.ShouldShowTransformGizmo(); + m_TransformGizmo.SetActive(bGizmoActive); + } + } + } + + // Update any transition to a scene transform reset. + UpdateWorldTransformReset(); + + //update our line after all input and tools have chimed in on the state of it + PointerManager.m_Instance.UpdateLine(); + UnityEngine.Profiling.Profiler.EndSample(); + } + + public void UpdateControlsPostIntro() + { + m_PanelManager.UpdatePanels(); + UpdateCurrentGazeRay(); + UpdateGazeObjectsAnimationState(); + RefreshCurrentGazeObject(); + UpdateSwapControllers(); + if (m_CurrentGazeObject > -1) + { + UpdateActiveGazeObject(); + } + } + + public void UpdateControlsForLoading() + { + UpdateCurrentGazeRay(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + UpdateGrab(); + UpdateWorldTransformReset(); + + if (m_GrabWidgetState == GrabWidgetState.None && m_CurrentGazeObject == -1 && + m_SketchSurfacePanel.ActiveTool.AvailableDuringLoading() && + !m_GrabBrush.grabbingWorld) + { + m_SketchSurfacePanel.UpdateCurrentTool(); + } + } + + public void UpdateControlsForReset() + { + UpdateGrab(); + UpdateCurrentGazeRay(); + UpdatePinCushionVisibility(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + PointerManager.m_Instance.UpdateLine(); + } + + public void UpdateControlsForUploading() + { + UpdateCurrentGazeRay(); + UpdatePinCushionVisibility(); + m_PanelManager.UpdatePanels(); + UpdateGazeObjectsAnimationState(); + } + + public void UpdateControlsForMemoryExceeded() + { + UpdateGrab(); + m_SketchSurfacePanel.m_UpdatedToolThisFrame = false; + m_PanelManager.UpdatePanels(); + UpdateCurrentGazeRay(); + UpdateGazeObjectsAnimationState(); + RefreshCurrentGazeObject(); + if (m_CurrentGazeObject > -1) + { + UpdateActiveGazeObject(); + } + } + + void UpdatePinCushionVisibility() + { + // If the pin cushion is showing and the user cancels, eat the input. + // if (m_PinCushion.IsShowing()) + // { + // if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) || + // InputManager.Brush.GetControllerGrip() || + // InputManager.Wand.GetControllerGrip() || + // IsUserInteractingWithAnyWidget() || + // IsUserInteractingWithUI()) + // { + // m_EatPinCushionInput = true; + // } + // } + + // If our tool wants the input blocked, maintain the input eat state until + // after the user has let off input. + if (m_SketchSurfacePanel.ActiveTool.BlockPinCushion() || !CanUsePinCushion()) + { + m_EatPinCushionInput = true; + } + + bool show = + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.ShowPinCushion); + m_PinCushion.ShowPinCushion(show && !m_EatPinCushionInput); + m_EatPinCushionInput = m_EatPinCushionInput && show; + } + + bool CanUsePinCushion() + { + return (m_ControlsType == ControlsType.SixDofControllers) && + m_PanelManager.AdvancedModeActive() && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !InputManager.Brush.GetControllerGrip() && + !InputManager.Wand.GetControllerGrip() && + !IsUserInteractingWithAnyWidget() && + !IsUserInteractingWithUI() && + !m_SketchSurfacePanel.ActiveTool.BlockPinCushion() && + App.Instance.IsInStateThatAllowsPainting(); + } + + void UpdateCurrentGazeRay() + { + var head = ViewpointScript.Head; + m_CurrentGazeRay = new Ray(head.position, head.forward); + m_CurrentHeadOrientation = head.rotation; + + // We use the gaze ray for certain shader effects - like edge falloff. + Shader.SetGlobalVector("_WorldSpaceRootCameraPosition", m_CurrentGazeRay.origin); + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + if (hasController) + { + if (InputManager.Brush.IsTrackedObjectValid) + { + Transform rAttachPoint = InputManager.m_Instance.GetBrushControllerAttachPoint(); + m_GazeControllerRay.direction = rAttachPoint.forward; + m_GazeControllerRay.origin = rAttachPoint.position; + } + else + { + // If the brush controller isn't tracked, put our controller ray out of the way. + float fBig = 9999999.0f; + m_GazeControllerRay.direction = Vector3.one; + m_GazeControllerRay.origin = new Vector3(fBig, fBig, fBig); + } + + m_GazeControllerRayActivePanel.direction = m_GazeControllerRay.direction; + m_GazeControllerRayActivePanel.origin = m_GazeControllerRay.origin; + m_GazeControllerRayActivePanel.origin -= (m_GazeControllerRayActivePanel.direction * 0.5f); + } + } + + public void UpdateGazeObjectsAnimationState() + { + // Are the panels allowed to be visible? + bool isSixDof = m_ControlsType == ControlsType.SixDofControllers; + if ((!isSixDof) || + (InputManager.Wand.IsTrackedObjectValid && + !m_SketchSurfacePanel.ActiveTool.HidePanels() && + !App.Instance.IsLoading())) + { + // Transition panels according to requested visibility. + m_PanelManager.SetVisible(m_PanelsVisibilityRequested); + } + else + { + // Transition out. + m_PanelManager.SetVisible(false); + } + } + + void UpdateBaseInput() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateBaseInput"); + if (m_ControlsType == ControlsType.SixDofControllers) + { + m_PanelManager.UpdateWandOrientationControls(); + } + + //allow tool scaling if we're not drawing and our input device is active + bool bScaleInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Scale); + bool bScaleCommandActive = + bScaleInputActive + && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) + && m_GrabBrush.grabbingWorld == false + && m_CurrentGazeObject == -1 // free up swipe for use by gaze object + && (m_ControlsType != ControlsType.SixDofControllers || InputManager.Brush.IsTrackedObjectValid) + // TODO:Mikesky - very hacky + && SketchSurfacePanel.m_Instance.ActiveTool.m_Type != BaseTool.ToolType.MultiCamTool; + + if (m_EatToolScaleInput) + { + m_EatToolScaleInput = bScaleInputActive; + } + + if (bScaleCommandActive && !m_EatToolScaleInput) + { + if (m_GrabWidgetState == GrabWidgetState.None) + { + //send scale command down to current tool + m_SketchSurfacePanel.UpdateToolSize( + m_AdjustToolSizeScalar * InputManager.m_Instance.GetAdjustedBrushScrollAmount()); + } + + //ugly, but brush size is becoming not an input state + m_MouseDeltaX = 0.0f; + m_MouseDeltaY = 0.0f; + } + + UpdateSwapControllers(); + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateSwapControllers() + { + // Don't allow controller swap in first run intro. + // Don't allow controller swap if we're grabbing a widget. + // Don't allow controller swap if a Logitech pen is present. + if (!TutorialManager.m_Instance.TutorialActive() && + m_GrabWidgetState == GrabWidgetState.None && + !App.VrSdk.VrControls.LogitechPenIsPresent()) + { + if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.SwapControls)) + { + DoSwapControls(); + } + } + } + + public static void DoSwapControls() + { + InputManager.m_Instance.WandOnRight = !InputManager.m_Instance.WandOnRight; + InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Brush) + .DisplayControllerSwapAnimation(); + InputManager.m_Instance.GetControllerBehavior(InputManager.ControllerName.Wand) + .DisplayControllerSwapAnimation(); + AudioManager.m_Instance.PlayControllerSwapSound( + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush)); + } + + void UpdateStandardInput() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateStandardInput"); + //debug keys + if (App.UserConfig.Flags.AdvancedKeyboardShortcuts) + { + var camTool = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; + + if (InputManager.m_Instance.GetKeyboardShortcutDown(InputManager.KeyboardShortcut.SaveNew)) + { + IssueGlobalCommand(GlobalCommands.SaveNew, 1); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.SwitchCamera) && camTool != null) + { + camTool.ExternalObjectNextCameraStyle(); // For monoscopic mode + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ViewOnly)) + { + IssueGlobalCommand(GlobalCommands.ViewOnly); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleScreenMirroring)) + { + ViewpointScript.m_Instance.ToggleScreenMirroring(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.PreviousTool)) + { + m_SketchSurfacePanel.PreviousTool(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.NextTool)) + { + m_SketchSurfacePanel.NextTool(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.CycleSymmetryMode)) + { + var cur = PointerManager.m_Instance.CurrentSymmetryMode; + var next = (cur == SymmetryMode.None) ? SymmetryMode.SinglePlane + : (cur == SymmetryMode.SinglePlane) ? SymmetryMode.DebugMultiple + : (cur == SymmetryMode.DebugMultiple) ? SymmetryMode.MultiMirror + : (cur == SymmetryMode.MultiMirror) ? SymmetryMode.TwoHanded + : SymmetryMode.None; + PointerManager.m_Instance.CurrentSymmetryMode = next; + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.Export)) + { + StartCoroutine(ExportCoroutine()); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.StoreHeadTransform) && + InputManager.m_Instance.GetAnyShift()) + { + Transform head = ViewpointScript.Head; + PlayerPrefs.SetFloat("HeadOffset_localPositionX", head.localPosition.x); + PlayerPrefs.SetFloat("HeadOffset_localPositionY", head.localPosition.y); + PlayerPrefs.SetFloat("HeadOffset_localPositionZ", head.localPosition.z); + PlayerPrefs.SetFloat("HeadOffset_localRotationX", head.localRotation.x); + PlayerPrefs.SetFloat("HeadOffset_localRotationY", head.localRotation.y); + PlayerPrefs.SetFloat("HeadOffset_localRotationZ", head.localRotation.z); + PlayerPrefs.SetFloat("HeadOffset_localRotationW", head.localRotation.w); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.RecallHeadTransform)) + { + Transform head = ViewpointScript.Head; + // Toggle the head offset. + if (m_HeadOffset) + { + // Remove the offset. + Transform originalParent = head.parent; + head.SetParent(head.parent.parent); + GameObject.DestroyImmediate(originalParent.gameObject); + m_HeadOffset = false; + } + else + { + // Add the offset. + GameObject newParent = new GameObject(); + newParent.transform.SetParent(head.parent); + newParent.transform.localPosition = Vector3.zero; + newParent.transform.localRotation = Quaternion.identity; + newParent.transform.localScale = Vector3.one; + head.SetParent(newParent.transform); + TrTransform offsetTransform = TrTransform.TR( + new Vector3( + PlayerPrefs.GetFloat("HeadOffset_localPositionX", 0), + PlayerPrefs.GetFloat("HeadOffset_localPositionY", 1.5f), + PlayerPrefs.GetFloat("HeadOffset_localPositionZ", 0)), + new Quaternion( + PlayerPrefs.GetFloat("HeadOffset_localRotationX", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationY", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationZ", 0), + PlayerPrefs.GetFloat("HeadOffset_localRotationW", 1))); + TrTransform originalTransformInverse = TrTransform.FromLocalTransform(head).inverse; + TrTransform newParentTransform = offsetTransform * originalTransformInverse; + newParent.transform.localPosition = newParentTransform.translation; + newParent.transform.localRotation = newParentTransform.rotation; + m_HeadOffset = true; + } + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleLightType)) + { + // Toggle between per-pixel & SH lighting on the secondary directional light + Light secondaryLight = App.Scene.GetLight((1)); + if (LightRenderMode.ForceVertex == secondaryLight.renderMode) + { + secondaryLight.renderMode = LightRenderMode.ForcePixel; + } + else + { + secondaryLight.renderMode = LightRenderMode.ForceVertex; + } + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.TossWidget)) + { + m_WidgetManager.TossNearestWidget(); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.Reset)) + { + App.Instance.SetDesiredState(App.AppState.LoadingBrushesAndLighting); + } + else if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.FlyMode)) + { + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.FlyTool); + } + else if (App.Config.m_ToggleProfileOnAppButton && + (InputManager.Wand.GetVrInputDown(VrInput.Button03) || + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ToggleProfile))) + { + IssueGlobalCommand(GlobalCommands.ToggleProfiling); + } + } + +#if DEBUG + if (InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.CheckStrokes)) + { + bool value = !SketchMemoryScript.m_Instance.m_SanityCheckStrokes; + string feature = "Stroke determinism checking"; + SketchMemoryScript.m_Instance.m_SanityCheckStrokes = value; + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + feature + (value ? ": On" : ": Off")); + } +#endif + + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + var mouse = Mouse.current; + + // Toggle default tool. + if (!m_PanelManager.AdvancedModeActive() && + InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.ToggleDefaultTool) && + !m_SketchSurfacePanel.IsDefaultToolEnabled() && + m_SketchSurfacePanel.ActiveTool.AllowDefaultToolToggle() && + // don't allow tool to change while pointing at panel because there is no visual indication + m_CurrentGazeObject == -1) + { + m_SketchSurfacePanel.EnableDefaultTool(); + AudioManager.m_Instance.PlayPinCushionSound(true); + } + // Pan. + else if (!hasController && mouse.rightButton.isPressed) + { + SwitchState(InputState.Pan); + } + // Controller lock (this must be before rotate/head lock!). + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + SwitchState(InputState.ControllerLock); + } + // Rotate. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) + { + SwitchState(InputState.Rotation); + } + // Head lock. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) + { + SwitchState(InputState.HeadLock); + } + // Push pull. + else if (!hasController && + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate)) + { + SwitchState(InputState.PushPull); + } + else if (!PointerManager.m_Instance.IsMainPointerCreatingStroke()) + { + // Reset surface. + if (!hasController && + InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Reset)) + { + ResetGrabbedPose(); + } + // Undo. + else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Undo) && + CanUndo()) + { + IssueGlobalCommand(GlobalCommands.Undo); + } + else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo) && + CanUndo() && ShouldRepeatUndo()) + { + m_UndoHold_Timer = m_UndoRedoHold_RepeatInterval; + IssueGlobalCommand(GlobalCommands.Undo); + } + // Redo. + else if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Redo) && + CanRedo()) + { + IssueGlobalCommand(GlobalCommands.Redo); + } + else if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo) && + CanRedo() && ShouldRepeatRedo()) + { + m_RedoHold_Timer = m_UndoRedoHold_RepeatInterval; + IssueGlobalCommand(GlobalCommands.Redo); + } + // Reset scene. + else if (!hasController && + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.ResetScene)) + { + // TODO: Should thsi go away? Seems like the "sweetspot" may no longer be used. + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Two) + { + m_PanelManager.SetSweetSpotPosition(m_CurrentGazeRay.origin); + ResetGrabbedPose(); + } + } + // Straight edge. + else if (!hasController && + InputManager.m_Instance.GetKeyboardShortcutDown( + InputManager.KeyboardShortcut.StraightEdge)) + { + IssueGlobalCommand(GlobalCommands.StraightEdge); + } + // Always fall back on switching tools. + else + { + m_SketchSurfacePanel.CheckForToolSelection(); + } + } + + // Reset undo/redo hold timers. + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Undo)) + { + m_UndoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + } + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Redo)) + { + m_RedoHold_Timer = m_UndoRedoHold_DurationBeforeStart; + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + bool CanUndo() + { + return SketchMemoryScript.m_Instance.CanUndo() && + !IsUIBlockingUndoRedo() && + m_PanelManager.GazePanelsAreVisible() && + !m_GrabWand.grabbingWorld && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + bool CanRedo() + { + return SketchMemoryScript.m_Instance.CanRedo() && + !IsUIBlockingUndoRedo() && + m_PanelManager.GazePanelsAreVisible() && + !m_GrabBrush.grabbingWorld && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + !SelectionManager.m_Instance.IsAnimatingTossFromGrabbingGroup; + } + + bool ShouldRepeatUndo() + { + m_UndoHold_Timer -= Time.deltaTime; + return (m_UndoHold_Timer <= 0.0f); + } + + bool ShouldRepeatRedo() + { + m_RedoHold_Timer -= Time.deltaTime; + return (m_RedoHold_Timer <= 0.0f); + } + + // Updates the global state: + // m_CurrentGrabWidget + void UpdateGrab() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.UpdateGrab"); + if (m_ControlsType != ControlsType.SixDofControllers) + { + UnityEngine.Profiling.Profiler.EndSample(); + return; + } + + GrabWidget rPrevGrabWidget = m_CurrentGrabWidget; + GrabWidget rPrevPotentialBrush = m_PotentialGrabWidgetBrush; + GrabWidget rPrevPotentialWand = m_PotentialGrabWidgetWand; + if (m_CurrentGrabWidget) + { + m_CurrentGrabWidget.Activate(false); + } + if (m_PotentialGrabWidgetBrush) + { + m_PotentialGrabWidgetBrush.Activate(false); + } + if (m_PotentialGrabWidgetWand) + { + m_PotentialGrabWidgetWand.Activate(false); + } + m_CurrentGrabWidget = null; + m_PotentialGrabWidgetBrush = null; + m_PotentialGrabWidgetWand = null; + m_PotentialGrabWidgetBrushValid = false; + m_PotentialGrabWidgetWandValid = false; + + m_WidgetManager.RefreshNearestWidgetLists(m_CurrentGazeRay, m_CurrentGazeObject); + + if (m_GrabWidgetState == GrabWidgetState.None) + { + UpdateGrab_WasNone(rPrevPotentialBrush, rPrevPotentialWand); + } + else if (m_GrabWidgetState == GrabWidgetState.OneHand) + { + UpdateGrab_WasOneHand(rPrevGrabWidget); + } + else if (m_GrabWidgetState == GrabWidgetState.TwoHands) + { + UpdateGrab_WasTwoHands(rPrevGrabWidget); + } + + // Update grab intersection state. + switch (m_CurrentGrabIntersectionState) + { + case GrabIntersectionState.RequestIntersections: + m_CurrentGrabIntersectionState = GrabIntersectionState.ReadBrush; + break; + case GrabIntersectionState.ReadBrush: + m_CurrentGrabIntersectionState = GrabIntersectionState.ReadWand; + break; + case GrabIntersectionState.ReadWand: + m_CurrentGrabIntersectionState = GrabIntersectionState.RequestIntersections; + break; + } + + if (!TutorialManager.m_Instance.TutorialActive() && m_CurrentGrabWidget == null) + { + UpdateGrab_World(); + } + + App.Instance.SelectionEffect.HighlightForGrab( + m_GrabWidgetState != GrabWidgetState.None || + (m_PotentialGrabWidgetBrush != null && m_PotentialGrabWidgetBrushValid) || + (m_PotentialGrabWidgetWand != null && m_PotentialGrabWidgetWandValid)); + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateGrab_WasNone(GrabWidget rPrevPotentialBrush, GrabWidget rPrevPotentialWand) + { + // if a panel isn't in focus, allow for widget grab + // We can grab a widget as long as we aren't trying to draw with that hand. + bool bActiveInput = + (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) && + App.Instance.IsInStateThatAllowsPainting()); + + //certain tools don't allow us to mess with widgets + bool bWidgetManipOK = m_SketchSurfacePanel.DoesCurrentToolAllowWidgetManipulation() && + !m_GrabWand.grabbingWorld && !m_GrabBrush.grabbingWorld && IsGrabWorldStateStable() && + App.Instance.IsInStateThatAllowsAnyGrabbing(); + + // Update EatInput flags if they're valid. + if (m_GrabBrush.eatInput) + { + m_GrabBrush.eatInput = InputManager.Brush.GetControllerGrip(); + } + if (m_GrabWand.eatInput) + { + m_GrabWand.eatInput = InputManager.Wand.GetControllerGrip(); + } + + bool bShouldClearWandInside = false; + if (m_CurrentInputState == InputState.Standard && bWidgetManipOK) + { + // If we're in the intersection request state, fire off a new intersection request. If + // we're in the read brush state, update our brush grab data structure. + List brushBests = m_WidgetManager.WidgetsNearBrush; + if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) + { + RequestWidgetIntersection(brushBests, InputManager.ControllerName.Brush); + } + else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadBrush) + { + m_BackupBrushGrabData = GetBestWidget(brushBests, m_BrushResults); + } + + if (m_BackupBrushGrabData != null) + { + m_PotentialGrabWidgetBrush = m_BackupBrushGrabData.m_WidgetScript; + + // Allow widget grab if we're not painting. + if (!bActiveInput) + { + m_PotentialGrabWidgetBrush.Activate(true); + m_PotentialGrabWidgetBrushValid = true; + m_PotentialGrabWidgetBrush.VisualizePinState(); + + if (!m_GrabBrush.eatInput && InputManager.Brush.GetControllerGrip()) + { + m_CurrentGrabWidget = m_PotentialGrabWidgetBrush; + if (m_CurrentGrabWidget.Group != SketchGroupTag.None) + { + m_GrabBrush.grabbingGroup = true; + m_CurrentGrabWidget = + SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); + } + UpdateGrab_NoneToOne(InputManager.ControllerName.Brush); + bShouldClearWandInside = true; + m_GrabBrush.startedGrabInsideWidget = true; + } + } + } + m_GrabBrush.SetHadBestGrabAndTriggerHaptics(m_BackupBrushGrabData); + m_ControllerGrabVisuals.BrushInWidgetRange = m_BackupBrushGrabData != null; + + // If we're in the intersection request state, fire off a new intersection request. If + // we're in the read wand state, update our wand grab data structure. + List wandBests = m_WidgetManager.WidgetsNearWand; + if (m_CurrentGrabIntersectionState == GrabIntersectionState.RequestIntersections) + { + RequestWidgetIntersection(wandBests, InputManager.ControllerName.Wand); + } + else if (m_CurrentGrabIntersectionState == GrabIntersectionState.ReadWand) + { + m_BackupWandGrabData = GetBestWidget(wandBests, m_WandResults); + } + + if (m_BackupWandGrabData != null) + { + m_PotentialGrabWidgetWand = m_BackupWandGrabData.m_WidgetScript; + // Allow wand widget grab if brush grab failed. + bool bGrabAllowed = (m_GrabWidgetState == GrabWidgetState.None) && !bActiveInput; + if (bGrabAllowed) + { + m_PotentialGrabWidgetWand.Activate(true); + m_PotentialGrabWidgetWandValid = true; + m_PotentialGrabWidgetWand.VisualizePinState(); + + if (!m_GrabWand.eatInput && InputManager.Wand.GetControllerGrip()) + { + m_CurrentGrabWidget = m_PotentialGrabWidgetWand; + if (m_CurrentGrabWidget.Group != SketchGroupTag.None) + { + m_GrabWand.grabbingGroup = true; + m_CurrentGrabWidget = + SelectionManager.m_Instance.StartGrabbingGroupWithWidget(m_CurrentGrabWidget); + } + UpdateGrab_NoneToOne(InputManager.ControllerName.Wand); + m_GrabBrush.ClearInsideWidget(); + m_GrabWand.startedGrabInsideWidget = true; + } + } + } + m_GrabWand.SetHadBestGrabAndTriggerHaptics(m_BackupWandGrabData); + m_ControllerGrabVisuals.WandInWidgetRange = m_BackupWandGrabData != null; + + // Account for asymmetry in controller processing by clearing after wand has updated + // GrabState.insideWidget according to bestWandGrab. + if (bShouldClearWandInside) + { + m_GrabWand.ClearInsideWidget(); + } + } + + // Update widget collisions if we've got a drifter. + if (m_GrabWidgetState == GrabWidgetState.None) + { + if (m_WidgetManager.ShouldUpdateCollisions()) + { + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + } + } + } + + void UpdateGrab_WasOneHand(GrabWidget rPrevGrabWidget) + { + var controller = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; + bool shouldRelease = !App.Instance.IsInStateThatAllowsAnyGrabbing(); + if (!InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetControllerGrip() || + shouldRelease) + { + if (shouldRelease) + { + EatGrabInput(); + } + + Vector3 vLinearVelocity; + Vector3 vAngularVelocity; + if (GetGrabWidgetHoldHistory(out vLinearVelocity, out vAngularVelocity)) + { + rPrevGrabWidget.SetVelocities( + vLinearVelocity, vAngularVelocity, + controller.Transform.position); + } + // One -> None + UpdateGrab_ToNone(rPrevGrabWidget); + } + else + { + // Keep holding on to our widget. + m_CurrentGrabWidget = rPrevGrabWidget; + m_CurrentGrabWidget.Activate(true); + m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); + + if (!m_CurrentGrabWidget.Pinned) + { + var info = InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name]; + var controllerXf = Coords.AsGlobal[info.Transform]; + var newWidgetXf = controllerXf * m_GrabWidgetOneHandInfo.m_BaseWidgetXf_LS; + m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); + + UpdateGrabWidgetHoldHistory(m_GrabWidgetOneHandInfo.m_Name); + } + + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + + // Check for widget pinning. + if (m_CurrentGrabWidget.AllowPinning) + { + if (InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( + InputManager.SketchCommands.PinWidget)) + { + // If the user initiates a pin action, buzz a bit. + if (!m_CurrentGrabWidget.Pinned) + { + InputManager.m_Instance.TriggerHapticsPulse( + m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); + } + m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); + SketchSurfacePanel.m_Instance.EatToolsInput(); + m_WidgetManager.RefreshPinAndUnpinLists(); + } + } + + if (m_CurrentGrabWidget is SelectionWidget) + { + if (InputManager.m_Instance.GetCommandDown( + InputManager.SketchCommands.DuplicateSelection)) + { + controller.LastHeldInput = + controller.GetCommandHoldInput(InputManager.SketchCommands.DuplicateSelection); + } + + if (controller.LastHeldInput != null && + InputManager.m_Instance.GetCommandHeld(InputManager.SketchCommands.DuplicateSelection)) + { + SketchControlsScript.m_Instance.IssueGlobalCommand( + SketchControlsScript.GlobalCommands.Duplicate); + } + } + + InputManager.ControllerName otherName = + (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? + InputManager.ControllerName.Wand : InputManager.ControllerName.Brush; + bool otherInputEaten = + (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) ? + m_GrabWand.eatInput : m_GrabBrush.eatInput; + + // See if the other controller decides to grab the widget (unless we're pinned). + if (!m_CurrentGrabWidget.Pinned) + { + if (m_CurrentGrabWidget.AllowTwoHandGrab) + { + if (InputManager.Controllers[(int)otherName].GetControllerGrip()) + { + RequestPanelsVisibility(false); + m_GrabWidgetState = GrabWidgetState.TwoHands; + // Figure out if the new grab starts inside the widget. + Vector3 vOtherGrabPos = TrTransform.FromTransform( + InputManager.m_Instance.GetController(otherName)).translation; + bool bOtherGrabInBounds = m_CurrentGrabWidget.GetActivationScore( + vOtherGrabPos, otherName) >= 0; + m_CurrentGrabWidget.SetUserTwoHandGrabbing( + true, m_GrabWidgetOneHandInfo.m_Name, otherName, bOtherGrabInBounds); + + if (otherName == InputManager.ControllerName.Brush) + { + m_GrabBrush.startedGrabInsideWidget = bOtherGrabInBounds; + } + else + { + m_GrabWand.startedGrabInsideWidget = bOtherGrabInBounds; + } + + m_GrabWidgetTwoHandBrushPrev = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); + m_GrabWidgetTwoHandWandPrev = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); + } + } + } + else if (!otherInputEaten && InputManager.Controllers[(int)otherName].GetControllerGrip()) + { + // If it's a two hand grab but the current grab widget is pinned, grab the world. + UpdateGrab_ToNone(m_CurrentGrabWidget); + m_CurrentGrabWidget = null; + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + } + } + } + + // Previous frame was a two-handed grab. + // Handles all the cases where this frame's grab is zero, one, or two hands. + void UpdateGrab_WasTwoHands(GrabWidget rPrevGrabWidget) + { + //keep holding on to our widget + m_CurrentGrabWidget = rPrevGrabWidget; + m_CurrentGrabWidget.Activate(true); + m_CurrentGrabWidget.UserInteracting(true, m_GrabWidgetOneHandInfo.m_Name); + + if (!App.Instance.IsInStateThatAllowsAnyGrabbing()) + { + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + UpdateGrab_ToNone(rPrevGrabWidget); + } + else if (!InputManager.Wand.GetControllerGrip()) + { // Look for button release. + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + // See if our Brush hand is still within grab range of the widget. + if (m_GrabBrush.startedGrabInsideWidget || + IsControllerNearWidget(InputManager.ControllerName.Brush, m_CurrentGrabWidget)) + { + m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Brush; + RequestPanelsVisibility(true); + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + } + else + { + // If the Brush hand is beyond the widget, we're not holding it anymore. + UpdateGrab_ToNone(rPrevGrabWidget); + + // Eat input on the brush grip until we release the button. + m_GrabBrush.eatInput = true; + } + } + else if (!InputManager.Brush.GetControllerGrip()) + { + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + if (m_GrabWand.startedGrabInsideWidget || + IsControllerNearWidget(InputManager.ControllerName.Wand, m_CurrentGrabWidget)) + { + m_GrabWidgetOneHandInfo.m_Name = InputManager.ControllerName.Wand; + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + } + else + { + UpdateGrab_ToNone(rPrevGrabWidget); + m_GrabWand.eatInput = true; + } + } + else + { + // Both hands still grabbing. + // Check for pin, which forcibly releases one of the hands. + if (m_CurrentGrabWidget.AllowPinning && + InputManager.Controllers[(int)m_GrabWidgetOneHandInfo.m_Name].GetCommandDown( + InputManager.SketchCommands.PinWidget)) + { + // If the user initiates a pin action, buzz a bit. + if (!m_CurrentGrabWidget.Pinned) + { + InputManager.m_Instance.TriggerHapticsPulse( + m_GrabWidgetOneHandInfo.m_Name, 3, 0.10f, 0.07f); + } + + m_CurrentGrabWidget.Pin(!m_CurrentGrabWidget.Pinned); + SketchSurfacePanel.m_Instance.EatToolsInput(); + m_WidgetManager.RefreshPinAndUnpinLists(); + + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + m_GrabWidgetState = GrabWidgetState.OneHand; + m_CurrentGrabWidget.SetUserTwoHandGrabbing(false); + + // Eat input on the off hand so we don't immediately jump in to world transform. + if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush) + { + RequestPanelsVisibility(true); + m_GrabWand.eatInput = true; + } + else + { + m_GrabBrush.eatInput = true; + } + } + + if (!m_CurrentGrabWidget.Pinned) + { + UpdateGrab_ContinuesTwoHands(); + } + } + ClearGrabWidgetHoldHistory(); + m_PanelManager.DoCollisionSimulationForWidgetPanels(); + } + + // Common case for two-handed grab: both the previous and current frames are two-handed. + private void UpdateGrab_ContinuesTwoHands() + { + //holding with two hands, transform accordingly + TrTransform xfBrush = TrTransform.FromTransform(InputManager.Brush.Transform); + TrTransform xfWand = TrTransform.FromTransform(InputManager.Wand.Transform); + Vector2 vSizeRange = m_CurrentGrabWidget.GetWidgetSizeRange(); + + GrabWidget.Axis axis = m_CurrentGrabWidget.GetScaleAxis( + xfWand.translation, xfBrush.translation, + out Vector3 axisDirection, out float axisExtent); + + TrTransform newWidgetXf; + if (axis != GrabWidget.Axis.Invalid) + { + // Scale along a single axis + float deltaScale; + if (App.Config.m_AxisManipulationIsResize) + { + newWidgetXf = MathUtils.TwoPointObjectTransformationAxisResize( + axisDirection, axisExtent, + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + GetWorkingTransform(m_CurrentGrabWidget), + out deltaScale, + deltaScaleMin: vSizeRange.x / axisExtent, + deltaScaleMax: vSizeRange.y / axisExtent); + } + else + { + newWidgetXf = MathUtils.TwoPointObjectTransformationNonUniformScale( + axisDirection, + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + GetWorkingTransform(m_CurrentGrabWidget), + out deltaScale, + finalScaleMin: vSizeRange.x, + deltaScaleMin: vSizeRange.x / axisExtent, + deltaScaleMax: vSizeRange.y / axisExtent); + } + + // The above functions return undefined values in newWidgetXf.scale; but that's + // okay because RecordAndSetPosRot ignores xf.scale. + // TODO: do this more cleanly + m_CurrentGrabWidget.RecordAndApplyScaleToAxis(deltaScale, axis); + } + else + { + // Uniform scaling + TrTransform xfObject = GetWorkingTransform(m_CurrentGrabWidget); + Vector3 extents = (m_CurrentGrabWidget is StencilWidget) + ? (m_CurrentGrabWidget as StencilWidget).Extents + : Vector3.one * Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); + + // Delta-scale bounds should be based on the smallest/largest extent. + // Irritatingly, the API wants absolute rather than relative scale bounds, + // so they need even more conversion. + float deltaScaleMin = vSizeRange.x / extents.Min(); + float deltaScaleMax = vSizeRange.y / extents.Max(); + if (m_GrabWand.startedGrabInsideWidget && m_GrabBrush.startedGrabInsideWidget) + { + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); + } + else if (m_GrabWand.startedGrabInsideWidget) + { + // keep the wand inside the object + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandWandPrev, m_GrabWidgetTwoHandBrushPrev, + xfWand, xfBrush, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, + bUseLeftAsPivot: true); + } + else + { + // keep the brush inside the object (note the brush is the left hand) + newWidgetXf = MathUtils.TwoPointObjectTransformation( + m_GrabWidgetTwoHandBrushPrev, m_GrabWidgetTwoHandWandPrev, + xfBrush, xfWand, + xfObject, + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax, + bUseLeftAsPivot: true); + } + + // Must do separately becvause RecordAndSetPosRot ignores newWidgetXf.scale + m_CurrentGrabWidget.RecordAndSetSize(newWidgetXf.scale); + + float currentSize = Mathf.Abs(m_CurrentGrabWidget.GetSignedWidgetSize()); + if (currentSize == vSizeRange.x || currentSize == vSizeRange.y) + { + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.05f); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Wand, 0.05f); + } + } + + // Ignores TrTransform.scale + m_CurrentGrabWidget.RecordAndSetPosRot(newWidgetXf); + + m_GrabWidgetTwoHandBrushPrev = xfBrush; + m_GrabWidgetTwoHandWandPrev = xfWand; + } + + void UpdateGrab_NoneToOne(InputManager.ControllerName controllerName) + { + if (m_MaybeDriftingGrabWidget != null && + m_MaybeDriftingGrabWidget.IsMoving() && + !m_MaybeDriftingGrabWidget.IsSpinningFreely) + { + // If a new widget is grabbed but the previous one is still drifting, end the drift. + // TODO: Simplify in the widget animation cleanup. + if (m_MaybeDriftingGrabWidget == m_CurrentGrabWidget) + { + SketchMemoryScript.m_Instance.PerformAndRecordCommand( + new MoveWidgetCommand(m_MaybeDriftingGrabWidget, + m_MaybeDriftingGrabWidget.LocalTransform, m_MaybeDriftingGrabWidget.CustomDimension, + final: true), + discardIfNotMerged: true); + } + m_MaybeDriftingGrabWidget.ClearVelocities(); + } + + // UserInteracting should be the first thing that happens here so OnUserBeginInteracting can + // be called before everything else. + m_CurrentGrabWidget.UserInteracting(true, controllerName); + m_CurrentGrabWidget.ClearVelocities(); + ClearGrabWidgetHoldHistory(); + + //set our info names according to this controller's name + m_GrabWidgetOneHandInfo.m_Name = controllerName; + InitializeGrabWidgetControllerInfo(m_GrabWidgetOneHandInfo); + + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.RequestPointerRendering(false); + m_SketchSurfacePanel.RequestHideActiveTool(true); + if (m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Wand) + { + RequestPanelsVisibility(false); + } + + // Notify visuals. + ControllerGrabVisuals.VisualState visualState = + m_GrabWidgetOneHandInfo.m_Name == InputManager.ControllerName.Brush ? + ControllerGrabVisuals.VisualState.WidgetBrushGrip : + ControllerGrabVisuals.VisualState.WidgetWandGrip; + m_ControllerGrabVisuals.SetDesiredVisualState(visualState); + m_ControllerGrabVisuals.SetHeldWidget(m_CurrentGrabWidget.transform); + + //if a gaze object had focus when we grabbed this widget, take focus off the object + ResetActivePanel(); + m_UIReticle.SetActive(false); + + // Prep all other grab widgets for collision. + m_PanelManager.PrimeCollisionSimForWidgets(m_CurrentGrabWidget); + + m_GrabWidgetState = GrabWidgetState.OneHand; + m_WidgetManager.WidgetsDormant = false; + PointerManager.m_Instance.EatLineEnabledInput(); + + m_BackupWandGrabData = null; + m_BackupBrushGrabData = null; + } + + void UpdateGrab_ToNone(GrabWidget rPrevGrabWidget) + { + m_MaybeDriftingGrabWidget = rPrevGrabWidget; + + m_GrabWidgetState = GrabWidgetState.None; + PointerManager.m_Instance.RequestPointerRendering(!App.Instance.IsLoading() && + m_SketchSurfacePanel.ShouldShowPointer()); + RequestPanelsVisibility(true); + m_SketchSurfacePanel.RequestHideActiveTool(false); + rPrevGrabWidget.UserInteracting(false); + + // Disable grab visuals. + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + m_ControllerGrabVisuals.SetHeldWidget(null); + + if (m_GrabBrush.grabbingGroup || m_GrabWand.grabbingGroup) + { + SelectionManager.m_Instance.EndGrabbingGroupWithWidget(); + m_GrabBrush.grabbingGroup = false; + m_GrabWand.grabbingGroup = false; + } + } + + void RequestWidgetIntersection(List candidates, + InputManager.ControllerName controllerName) + { + // Get locals based off what controller we're using. + Queue resultQueue = null; + Vector3 controllerPos = Vector3.zero; + if (controllerName == InputManager.ControllerName.Brush) + { + resultQueue = m_BrushResults; + controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; + } + else + { + resultQueue = m_WandResults; + controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; + } + + // If we don't have a candidate that has a GPU object, don't bother firing off a GPU request. + bool requestGpuIntersection = false; + + // Fire off a new GPU intersection with all widgets that can use it. + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + candidates[i].m_WidgetScript.SetGPUIntersectionObjectLayer(m_WidgetGpuIntersectionLayer); + requestGpuIntersection = true; + } + } + + if (requestGpuIntersection) + { + GpuIntersectionResult newRequest = new GpuIntersectionResult(); + newRequest.resultList = new List(); + newRequest.result = App.Instance.GpuIntersector.RequestModelIntersections( + controllerPos, m_WidgetGpuIntersectionRadius, newRequest.resultList, 8, + (1 << m_WidgetGpuIntersectionLayer)); + + // The new result will only be null when the intersector is disabled. + if (newRequest.result != null) + { + resultQueue.Enqueue(newRequest); + } + + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + candidates[i].m_WidgetScript.RestoreGPUIntersectionObjectLayer(); + } + } + } + } + + GrabWidgetData GetBestWidget(List candidates, + Queue resultQueue) + { + // Discard futures that are too old. + while (resultQueue.Count > 0) + { + if (Time.frameCount - resultQueue.Peek().result.StartFrame < 5) + { + break; + } + resultQueue.Dequeue(); + } + + // If the oldest future is ready, use its intersection result to update the candidates. + GpuIntersectionResult finishedResult; + if (resultQueue.Count > 0 && resultQueue.Peek().result.IsReady) + { + finishedResult = resultQueue.Dequeue(); + } + else + { + finishedResult.resultList = new List(); + } + + // TODO: Speed this up. + for (int i = 0; i < candidates.Count; ++i) + { + if (candidates[i].m_WidgetScript.HasGPUIntersectionObject()) + { + // If a candidate can't find itself in the finished results list, it's not eligible. + bool candidateValid = false; + for (int j = 0; j < finishedResult.resultList.Count; ++j) + { + if (candidates[i].m_WidgetScript.Equals(finishedResult.resultList[j].widget)) + { + candidateValid = true; + break; + } + } + + if (candidateValid) + { + // If a candidate has a GPU intersection object and we found it in this list, + // not only is it valid, but it's as valid as it can be. + candidates[i].m_ControllerScore = 1.0f; + } + else + { + candidates[i].m_NearController = false; + } + } + } + + // Run through the candidates and pick + GrabWidgetData best = null; + for (int i = 0; i < candidates.Count; ++i) + { + var candidate = candidates[i]; + if (!candidate.m_NearController) continue; + + // For media widgets - only select from the active layer + if (candidate.m_WidgetScript is MediaWidget + && candidate.m_WidgetScript.Canvas != App.Scene.ActiveCanvas) continue; + + if (best == null || candidate.m_ControllerScore > best.m_ControllerScore) + { + best = candidate; + } + } + return best; + } + + void InitializeGrabWidgetControllerInfo(GrabWidgetControllerInfo info) + { + Transform controller = InputManager.Controllers[(int)info.m_Name].Transform; + Transform widget = m_CurrentGrabWidget.GrabTransform_GS; + TrTransform newWidgetXf = Coords.AsGlobal[widget]; + + info.m_BaseControllerXf = Coords.AsGlobal[controller]; + info.m_BaseWidgetXf_LS = info.m_BaseControllerXf.inverse * newWidgetXf; + } + + // returns the transform of the true widget (not the snapped one for those that can be) + private TrTransform GetWorkingTransform(GrabWidget w) + { + TrTransform ret = w.GetGrabbedTrTransform(); + ret.scale = w.GetSignedWidgetSize(); + return ret; + } + + // Initiate the world transform reset animation. + public void RequestWorldTransformReset(bool toSavedXf = false) + { + if (WorldIsReset(toSavedXf)) + { + return; + } + + m_WorldTransformResetXf = + toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity; + m_WorldTransformResetState = WorldTransformResetState.Requested; + + App.Scene.disableTiltProtection = false; + } + + void UpdateWorldTransformReset() + { + switch (m_WorldTransformResetState) + { + case WorldTransformResetState.Requested: + ViewpointScript.m_Instance.FadeToColor(Color.black, m_GrabWorldFadeSpeed); + m_WorldTransformResetState = WorldTransformResetState.FadingToBlack; + m_xfDropCamReset_RS = Coords.AsRoom[m_DropCam.transform]; + PointerManager.m_Instance.EatLineEnabledInput(); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + break; + case WorldTransformResetState.FadingToBlack: + m_WorldTransformFadeAmount += m_GrabWorldFadeSpeed * Time.deltaTime; + if (m_WorldTransformFadeAmount >= 1.0f) + { + App.Scene.Pose = m_WorldTransformResetXf; + m_WorldTransformFadeAmount = 1.0f; + m_WorldTransformResetState = WorldTransformResetState.FadingToScene; + ViewpointScript.m_Instance.FadeToScene(m_GrabWorldFadeSpeed); + m_DropCam.transform.position = m_xfDropCamReset_RS.translation; + m_DropCam.transform.rotation = m_xfDropCamReset_RS.rotation; + PointerManager.m_Instance.AllowPointerPreviewLine(true); + } + break; + case WorldTransformResetState.FadingToScene: + m_WorldTransformFadeAmount -= m_GrabWorldFadeSpeed * Time.deltaTime; + if (m_WorldTransformFadeAmount <= 0.0f) + { + m_WorldTransformFadeAmount = 0.0f; + m_WorldTransformResetState = WorldTransformResetState.Default; + } + break; + } + } + + bool CheckToggleTiltProtection() + { + if ( + InputManager.Wand.GetCommandDown(InputManager.SketchCommands.Redo) || + InputManager.Brush.GetCommandDown(InputManager.SketchCommands.Redo) + ) + { + App.Scene.disableTiltProtection = !App.Scene.disableTiltProtection; + + return !App.Scene.disableTiltProtection; + } + + return false; + + } + + void UpdateGrab_World() + { + bool bAllowWorldTransform = m_SketchSurfacePanel.ActiveTool.AllowWorldTransformation() && + (m_GrabWorldState != GrabWorldState.ResetDone) && + (!PointerManager.m_Instance.IsMainPointerCreatingStroke() || App.Instance.IsLoading()) && + App.Instance.IsInStateThatAllowsAnyGrabbing() && + !m_DisableWorldGrabbing; + + bool bWorldGrabWandPrev = m_GrabWand.grabbingWorld; + bool bWorldGrabBrushPrev = m_GrabBrush.grabbingWorld; + m_GrabWand.grabbingWorld = bAllowWorldTransform && !m_GrabWand.eatInput && + InputManager.Wand.GetControllerGrip(); + m_GrabBrush.grabbingWorld = bAllowWorldTransform && !m_GrabBrush.eatInput && + InputManager.Brush.GetControllerGrip() && + (m_CurrentGazeObject == -1); + + bool grabsChanged = (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) || + (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld); + bool bAllowWorldTransformChanged = + bAllowWorldTransform != m_AllowWorldTransformLastFrame; + int nGrabs = m_GrabWand.grabbingWorld ? 1 : 0; + nGrabs += m_GrabBrush.grabbingWorld ? 1 : 0; + + // Allow grabbing again if grabs have changed and we're done resetting. + if (m_GrabWorldState == GrabWorldState.ResetDone && grabsChanged) + { + m_GrabWorldState = GrabWorldState.Normal; + } + + // Update panels visibility if brush grip has changed. + if (bWorldGrabWandPrev != m_GrabWand.grabbingWorld) + { + RequestPanelsVisibility(!m_GrabWand.grabbingWorld); + } + + // Update tool visibility if brush grip has changed. + if (bWorldGrabBrushPrev != m_GrabBrush.grabbingWorld) + { + m_SketchSurfacePanel.RequestHideActiveTool(m_GrabBrush.grabbingWorld); + PointerManager.m_Instance.AllowPointerPreviewLine(!m_GrabBrush.grabbingWorld); + PointerManager.m_Instance.RequestPointerRendering(!m_GrabBrush.grabbingWorld + && m_SketchSurfacePanel.ShouldShowPointer() && !App.Instance.IsLoading()); + } + + // Reset m_WorldBeingGrabbed and only set it when world is actually being grabbed. + bool bWorldBeingGrabbedPrev = m_WorldBeingGrabbed; + m_WorldBeingGrabbed = false; + + // Move the world if it has been grabbed. + if (m_GrabWorldState == GrabWorldState.Normal && bAllowWorldTransform) + { + if (nGrabs == 2) + { + // Two-handed world movement. + m_WorldBeingGrabbed = true; + TrTransform grabXfWand = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Wand)); + TrTransform grabXfBrush = TrTransform.FromTransform( + InputManager.m_Instance.GetController(InputManager.ControllerName.Brush)); + + // Offset the controller positions so that they're centered on the grips. + Vector3 gripPos = InputManager.Controllers[(int)InputManager.ControllerName.Brush].Geometry.GripAttachPoint.localPosition; + gripPos.x = 0.0f; + grabXfWand.translation += grabXfWand.MultiplyVector(gripPos); + grabXfBrush.translation += grabXfBrush.MultiplyVector(gripPos); + + // Are we initiating two hand transform this frame? + if (!bWorldGrabWandPrev || !bWorldGrabBrushPrev) + { + PointerManager.m_Instance.EnableLine(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + PointerManager.m_Instance.RequestPointerRendering(false); + // Initiate audio loop + m_WorldTransformSpeedSmoothed = 0.0f; + AudioManager.m_Instance.WorldGrabLoop(true); + } + else + { + TrTransform xfOld = GrabbedPose; + TrTransform xfNew; + float deltaScaleMin = WorldTransformMinScale / xfOld.scale; + float deltaScaleMax = WorldTransformMaxScale / xfOld.scale; + bool fixOffset = false; + fixOffset = CheckToggleTiltProtection(); + xfNew = MathUtils.TwoPointObjectTransformation( + m_GrabBrush.grabTransform, m_GrabWand.grabTransform, + grabXfBrush, grabXfWand, + xfOld, + rotationAxisConstraint: (App.Scene.disableTiltProtection ? default(Vector3) : Vector3.up), + deltaScaleMin: deltaScaleMin, deltaScaleMax: deltaScaleMax); + float fCurrentWorldTransformSpeed = + Mathf.Abs((xfNew.scale - xfOld.scale) / Time.deltaTime); + m_WorldTransformSpeedSmoothed = + Mathf.Lerp(m_WorldTransformSpeedSmoothed, fCurrentWorldTransformSpeed, + AudioManager.m_Instance.m_WorldGrabLoopSmoothSpeed * Time.deltaTime); + AudioManager.m_Instance.ChangeLoopVolume("WorldGrab", + Mathf.Clamp(m_WorldTransformSpeedSmoothed / + AudioManager.m_Instance.m_WorldGrabLoopAttenuation, 0f, + AudioManager.m_Instance.m_WorldGrabLoopMaxVolume)); + + if (fixOffset) + { + Vector3 midPoint = Vector3.Lerp(grabXfBrush.translation, grabXfWand.translation, 0.5f); + + Vector3 localMidPointOldXF = xfOld.inverse * midPoint; + + // assign this to force the axial protection + GrabbedPose = xfNew; + xfNew = GrabbedPose; + + Vector3 midPointXFNew = xfNew * localMidPointOldXF; + + TrTransform xfDelta1 = TrTransform.T(midPoint - midPointXFNew); + xfNew = xfDelta1 * xfNew; + } + GrabbedPose = xfNew; + } + + // Update last states. + m_GrabBrush.grabTransform = grabXfBrush; + m_GrabWand.grabTransform = grabXfWand; + } + } + else if (m_GrabWorldState == GrabWorldState.ResettingTransform) + { + if (m_WorldTransformResetState == WorldTransformResetState.FadingToScene) + { + ResetGrabbedPose(); + PanelManager.m_Instance.ExecuteOnPanel(x => x.OnPanelMoved()); + + // World can't be transformed right after a reset until grab states have changed. + if (bAllowWorldTransform) + { + bAllowWorldTransform = false; + bAllowWorldTransformChanged = + bAllowWorldTransform != m_AllowWorldTransformLastFrame; + } + + // Set the grab world state on exit. + if (nGrabs == 0) + { + m_GrabWorldState = GrabWorldState.Normal; + } + else + { + m_GrabWorldState = GrabWorldState.ResetDone; + } + } + } + + if (grabsChanged || bAllowWorldTransformChanged) + { + // Fade in grid when doing two handed spin. + if (nGrabs == 2 && !bAllowWorldTransformChanged) + { + ViewpointScript.m_Instance.FadeGroundPlaneIn(m_GrabWorldGridColor, m_GrabWorldFadeSpeed); + } + else + { + ViewpointScript.m_Instance.FadeGroundPlaneOut(m_GrabWorldFadeSpeed); + } + } + + // Update visuals for world transform + if (grabsChanged) + { + bool bDoubleGrip = m_GrabBrush.grabbingWorld && m_GrabWand.grabbingWorld; + bool bSingleGrip = m_GrabBrush.grabbingWorld || m_GrabWand.grabbingWorld; + Vector3 vControllersMidpoint = + (InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Brush) + + InputManager.m_Instance.GetControllerPosition(InputManager.ControllerName.Wand)) * 0.5f; + + // Update transform line visuals + if (bDoubleGrip) + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldDoubleGrip); + AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); + } + else if (bSingleGrip) + { + if (m_GrabWand.grabbingWorld) + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldWandGrip); + } + else + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.WorldBrushGrip); + } + + if (!bWorldGrabWandPrev && !bWorldGrabBrushPrev) + { + AudioManager.m_Instance.WorldGrabbed(vControllersMidpoint); + } + else + { + AudioManager.m_Instance.WorldGrabLoop(false); + } + } + else + { + m_ControllerGrabVisuals.SetDesiredVisualState(ControllerGrabVisuals.VisualState.Off); + AudioManager.m_Instance.WorldGrabLoop(false); + } + + if (m_GrabWand.grabbingWorld || m_GrabBrush.grabbingWorld) + { + m_WidgetManager.WidgetsDormant = false; + PointerManager.m_Instance.EatLineEnabledInput(); + } + } + + // Reset scene transform if we're gripping and press the track pad. + bool wandReset = m_GrabWand.grabbingWorld && + InputManager.Wand.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); + bool brushReset = m_GrabBrush.grabbingWorld && + InputManager.Brush.GetCommandDown(InputManager.SketchCommands.WorldTransformReset); + if ((wandReset || brushReset) && !WorldIsReset(toSavedXf: false)) + { + m_GrabBrush.eatInput = true; + m_GrabWand.eatInput = true; + m_EatToolScaleInput = true; + m_GrabWorldState = GrabWorldState.ResettingTransform; + RequestWorldTransformReset(); + AudioManager.m_Instance.PlayTransformResetSound(); + } + + // Update the skybox rotation with the new scene rotation. + if (RenderSettings.skybox) + { + Quaternion sceneQuaternion = App.Instance.m_SceneTransform.rotation; + RenderSettings.skybox.SetVector( + "_SkyboxRotation", + new Vector4(sceneQuaternion.x, sceneQuaternion.y, sceneQuaternion.z, sceneQuaternion.w)); + } + + // Update last frame members. + m_AllowWorldTransformLastFrame = bAllowWorldTransform; + } + + /// If lhs and rhs are overlapping, return the smallest vector that would + /// cause rhs to stop overlapping; otherwise, return 0. + /// lhs: an antisphere (solid outside, empty inside) + /// rhs: a sphere (empty outside, solid inside) + private static Vector3 GetOverlap_Antisphere_Sphere( + Vector3 lhsCenter, float lhsRadius, + Vector3 rhsCenter, float rhsRadius) + { + // If anyone passes negative values, they are a bad person + lhsRadius = Mathf.Abs(lhsRadius); + rhsRadius = Mathf.Abs(rhsRadius); + // Without loss of generality, can recenter on lhs + rhsCenter -= lhsCenter; + lhsCenter -= lhsCenter; + + float maxDistance = lhsRadius - rhsRadius; + + // Edge case: sphere does not fit in antisphere + if (maxDistance <= 0) + { + return -rhsCenter; + } + + float penetrationDistance = Mathf.Max(0, rhsCenter.magnitude - maxDistance); + return -penetrationDistance * rhsCenter.normalized; + } + + public static bool IsValidScenePose(TrTransform xf, float radialBounds) + { + // Simple and dumb implementation for now. + return xf == MakeValidScenePose(xf, radialBounds); + } + + /// This is like MakeValidScenePose, but it guarantees that: + /// - The return value is a valid result of Lerp(scene0, scene1, t), + /// for some handwavy definition of "lerp" + /// - The lerp "t" is in [0, 1] + /// - IsValidScenePose(return value) is true, subject to the previous constraints. + /// + /// Think of it as doing a cast from scene0 to scene1. + public static TrTransform MakeValidSceneMove( + TrTransform scene0, TrTransform scene1, float radialBounds) + { + if (IsValidScenePose(scene1, radialBounds)) + { + return scene1; + } + if (!IsValidScenePose(scene0, radialBounds)) + { + Debug.LogError("Invalid scene cast start"); + return scene0; + } + + // We don't support lerping either of these + Debug.Assert(scene0.rotation == scene1.rotation); + Debug.Assert(scene0.scale == scene1.scale); + + Vector3 vRoom0 = -scene0.translation; + Vector3 vRoom1 = -scene1.translation; + float radius = (scene0.scale + * radialBounds + * App.METERS_TO_UNITS) - App.Instance.RoomRadius; + + float t0, t1; + bool success = MathUtils.RaySphereIntersection( + vRoom0, vRoom1 - vRoom0, + Vector3.zero, radius, out t0, out t1); + if (!success) + { + // If this were more important, we could solve for the t of the closest approach + return scene0; + } + + // t0 is expected to be < 0 (room starts inside the fence) + // t1 is expected to be in [0, 1] (room ends outside the fence) + + // Constraints: + // - Lerp t must be in [0, 1]. (Do not move past the requested endpoint) + // - Lerp t should be as high as possible but < t1. (Do not exit the sphere) + float t = Mathf.Clamp(t1, 0, 1); + + TrTransform sceneT = TrTransform.TRS( + Vector3.Lerp(scene0.translation, scene1.translation, t), + scene0.rotation, + scene0.scale); + return MakeValidScenePose(sceneT, radialBounds); + } + + /// Returns a new ScenePose TrTransform that does not cause the room + /// to violate the hard scene bounds. + /// + /// scenePose - The current, possibly invalid scene pose + public static TrTransform MakeValidScenePose(TrTransform scenePose, float radialBounds) + { + scenePose.scale = Mathf.Clamp( + scenePose.scale, + SketchControlsScript.m_Instance.WorldTransformMinScale, + SketchControlsScript.m_Instance.WorldTransformMaxScale); + + // Anything not explicitly qualified is in room space. + + float roomRadius = App.Instance.RoomRadius; + Vector3 roomCenter = Vector3.zero; + + float fenceRadius = scenePose.scale * radialBounds + * App.METERS_TO_UNITS; + Vector3 fenceCenter = scenePose.translation; + + Vector3 moveRoom = GetOverlap_Antisphere_Sphere( + fenceCenter, fenceRadius, roomCenter, roomRadius); + Vector3 moveFence = -moveRoom; + + scenePose.translation += moveFence; + return scenePose; + } + + /// Clears data used by GetGrabWidgetHoldHistory() + /// Should be called any time m_GrabWidgetOneHandInfo changes + void ClearGrabWidgetHoldHistory() + { + m_GrabWidgetHoldHistory.Clear(); + } + + /// Collects data for use with GetGrabWidgetHoldHistory() + void UpdateGrabWidgetHoldHistory(InputManager.ControllerName name) + { + float t = Time.realtimeSinceStartup; + var info = InputManager.Controllers[(int)name]; + m_GrabWidgetHoldHistory.Enqueue(new GrabWidgetHoldPoint + { + m_Name = name, + m_BirthTime = t, + m_Pos = info.Transform.position, + m_Rot = info.Transform.rotation + }); + + // Trim the fat off our widget history + while (m_GrabWidgetHoldHistory.Count > 0 && + t - m_GrabWidgetHoldHistory.Peek().m_BirthTime >= kControlPointHistoryMaxTime) + { + m_GrabWidgetHoldHistory.Dequeue(); + } + } + + /// Returns possibly-smoothed linear and angular velocities. May fail. + /// Angular velocity is returned as an axial vector whose length() is degrees/second + bool GetGrabWidgetHoldHistory(out Vector3 vLinearVelocity, out Vector3 vAngularVelocity) + { + vLinearVelocity = vAngularVelocity = Vector3.zero; + if (m_GrabWidgetHoldHistory.Count < 2) + { + return false; + } + + // We need pairs of elements, so a simple foreach() won't quite work. + // Maybe using linq .First() and .Skip() would be okay. + using (IEnumerator enumerator = m_GrabWidgetHoldHistory.GetEnumerator()) + { + if (!enumerator.MoveNext()) + { + return false; + } + + // Infinitesimal rotations commute, and scaled-axis-angle rotations commute + // "better" than other rotation formats. + Vector3 totalDeltaTheta = Vector3.zero; + + GrabWidgetHoldPoint first = enumerator.Current; + GrabWidgetHoldPoint prev = first; + GrabWidgetHoldPoint current = first; + while (enumerator.MoveNext()) + { + current = enumerator.Current; + + // For our quaternion, find the difference, convert it to angle/axis, and sum it + // Find delta such that delta * prev = cur + // left-multiply because we want it in world-space. + // multiply vs prev since we want the delta that takes us forward in time + // rather than backward in time. + Quaternion dtheta = current.m_Rot * Quaternion.Inverse(prev.m_Rot); + // Assume the rotation took the shorter path + if (dtheta.w < 0) + { + dtheta.Set(-dtheta.x, -dtheta.y, -dtheta.z, -dtheta.w); + } + + float degrees; + Vector3 axis; + dtheta.ToAngleAxis(out degrees, out axis); + totalDeltaTheta += (axis * degrees); + prev = current; + } + + // Linear velocity calculation doesn't need to look at intermediate points + Vector3 totalDeltaPosition = current.m_Pos - first.m_Pos; + float totalDeltaTime = current.m_BirthTime - first.m_BirthTime; + if (totalDeltaTime == 0) + { + return false; + } + + vLinearVelocity = totalDeltaPosition / totalDeltaTime; + vAngularVelocity = totalDeltaTheta / totalDeltaTime; + return true; + } + } + + bool IsControllerNearWidget(InputManager.ControllerName name, GrabWidget widget) + { + Vector3 vControllerPos = InputManager.m_Instance.GetControllerAttachPointPosition(name); + return widget.GetActivationScore(vControllerPos, name) >= 0.0f; + } + + void RefreshCurrentGazeObject() + { + UnityEngine.Profiling.Profiler.BeginSample("SketchControlScript.RefreshCurrentGazeObject"); + int iPrevGazeObject = m_CurrentGazeObject; + m_CurrentGazeObject = -1; + bool bGazeAllowed = (m_CurrentInputState == InputState.Standard) + && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate) + && !m_SketchSurfacePanel.ActiveTool.InputBlocked() + && (m_GrabWidgetState == GrabWidgetState.None) + && !m_GrabBrush.grabbingWorld + && !m_PinCushion.IsShowing() + && !PointerManager.MainPointerIsPainting() + ; + + bool bGazeDeactivationOverrideWithInput = false; + List aAllPanels = m_PanelManager.GetAllPanels(); + + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + + //if we're re-positioning a panel, keep it active + if (m_PositioningPanelWithHead) + { + m_CurrentGazeObject = iPrevGazeObject; + } + // Only activate gaze objects if we're in standard input mode, and if we don't have the 'draw' + // button held. + else if ((bGazeAllowed || (iPrevGazeObject != -1))) + { + //reset hit flags + for (int i = 0; i < m_GazeResults.Length; ++i) + { + m_GazeResults[i].m_HitWithGaze = false; + m_GazeResults[i].m_HitWithController = false; + m_GazeResults[i].m_WithinView = false; + } + + // If we're in controller mode, find the nearest colliding widget that might get in our way. + float fNearestWidget = 99999.0f; + if (hasController) + { + fNearestWidget = m_WidgetManager.DistanceToNearestWidget(m_GazeControllerRay); + } + + //check all panels for gaze hit + bool bRequireVisibilityCheck = !hasController || (iPrevGazeObject == -1); + if (m_PanelManager.PanelsAreStable()) + { + RaycastHit rHitInfo; + bool bRayHit = false; + int panelsHit = 0; + for (int i = 0; i < aAllPanels.Count; ++i) + { + // Ignore fixed panels when they are not visible. + if (!m_PanelManager.GazePanelsAreVisible() && aAllPanels[i].m_Panel.m_Fixed) + { + continue; + } + + if (aAllPanels[i].m_Panel.gameObject.activeSelf && aAllPanels[i].m_Panel.IsAvailable()) + { + //make sure this b-snap is in view + Vector3 vToPanel = aAllPanels[i].m_Panel.transform.position - m_CurrentGazeRay.origin; + vToPanel.Normalize(); + if (!bRequireVisibilityCheck || Vector3.Angle(vToPanel, m_CurrentGazeRay.direction) < m_GazeMaxAngleFromFacing) + { + if (hasController) + { + if (aAllPanels[i].m_Panel.HasMeshCollider()) + { + //make sure the angle between the pointer and the panel forward is below our max angle + if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, m_GazeControllerRay.direction) < m_GazeMaxAngleFromPointing) + { + //make sure the angle between the user-to-panel and the panel forward is reasonable + if (Vector3.Angle(aAllPanels[i].m_Panel.transform.forward, vToPanel) < m_GazeMaxAngleFacingToForward) + { + m_GazeResults[i].m_WithinView = true; + + bRayHit = false; + bRayHit = aAllPanels[i].m_Panel.RaycastAgainstMeshCollider( + m_GazeControllerRay, out rHitInfo, m_GazeControllerPointingDistance); + + if (bRayHit) + { + //if the ray starts inside the panel, we won't get a good hit point, it'll just be zero + if (rHitInfo.point.sqrMagnitude > 0.1f) + { + if (rHitInfo.distance < fNearestWidget) + { + m_GazeResults[i].m_ControllerDistance = rHitInfo.distance; + m_GazeResults[i].m_ControllerPosition = rHitInfo.point; + m_GazeResults[i].m_HitWithController = true; + panelsHit++; + } + } + } + } + } + } + } + else + { + m_GazeResults[i].m_WithinView = true; + if (aAllPanels[i].m_Panel.GetCollider().Raycast(m_CurrentGazeRay, out rHitInfo, m_GazeMaxDistance)) + { + m_GazeResults[i].m_GazePosition = rHitInfo.point; + m_GazeResults[i].m_HitWithGaze = true; + } + } + } + } + } + + // No panels hit within normal ray distance. + // Check if previous panel still pointed to. + if (panelsHit == 0) + { + if (iPrevGazeObject != -1) + { + // Don't allow any panel to hold focus if it's facing away from the user. + Vector3 vToPanel = aAllPanels[iPrevGazeObject].m_Panel.transform.position - + m_CurrentGazeRay.origin; + vToPanel.Normalize(); + if (Vector3.Angle(aAllPanels[iPrevGazeObject].m_Panel.transform.forward, vToPanel) < + m_GazeMaxAngleFacingToForward) + { + float fDist = m_GazeControllerPointingDistance * 1.5f; + bRayHit = aAllPanels[iPrevGazeObject].m_Panel.RaycastAgainstMeshCollider( + m_GazeControllerRayActivePanel, out rHitInfo, fDist); + if (bRayHit) + { + if (rHitInfo.point.sqrMagnitude > 0.1f) + { + if (rHitInfo.distance < fNearestWidget) + { + m_GazeResults[iPrevGazeObject].m_ControllerDistance = rHitInfo.distance; + m_GazeResults[iPrevGazeObject].m_ControllerPosition = rHitInfo.point; + m_GazeResults[iPrevGazeObject].m_HitWithController = true; + } + } + } + } + } + } + } + + //determine what panel we hit, take the one with the lowest controller distance + float fControllerDist = 999.0f; + int iControllerIndex = -1; + if (hasController) + { + for (int i = 0; i < m_GazeResults.Length; ++i) + { + if (m_GazeResults[i].m_HitWithController) + { + if (m_GazeResults[i].m_ControllerDistance < fControllerDist) + { + iControllerIndex = i; + fControllerDist = m_GazeResults[i].m_ControllerDistance; + } + } + } + } + + //if we found something near our controller, take it + if (iControllerIndex != -1) + { + m_CurrentGazeObject = iControllerIndex; + m_CurrentGazeHitPoint = m_GazeResults[iControllerIndex].m_ControllerPosition; + + // TODO: This should not be hardcoded once multiple pointers are allowed. + m_GazeResults[m_CurrentGazeObject].m_ControllerName = InputManager.ControllerName.Brush; + if (m_GazeResults[m_CurrentGazeObject].m_HitWithGaze) + { + //average with the gaze position if we hit that too + m_CurrentGazeHitPoint += m_GazeResults[m_CurrentGazeObject].m_GazePosition; + m_CurrentGazeHitPoint *= 0.5f; + } + } + else + { + //nothing near the controller, see if we're looking at the previous + if (iPrevGazeObject != -1 && m_GazeResults[iPrevGazeObject].m_HitWithGaze) + { + m_CurrentGazeObject = iPrevGazeObject; + m_CurrentGazeHitPoint = m_GazeResults[m_CurrentGazeObject].m_GazePosition; + } + else + { + //controller and gaze not near panel, pick the first panel we're looking at + for (int i = 0; i < m_GazeResults.Length; ++i) + { + if (m_GazeResults[i].m_HitWithGaze) + { + m_CurrentGazeObject = i; + m_CurrentGazeHitPoint = m_GazeResults[i].m_GazePosition; + break; + } + } + } + } + + //forcing users to look away from gaze panel + if (m_EatInputGazeObject && m_CurrentGazeObject != -1) + { + m_CurrentGazeObject = -1; + } + else if (m_CurrentGazeObject == -1) + { + m_EatInputGazeObject = false; + } + } + + //if we're staring at a panel, keep our countdown fresh + if (m_CurrentGazeObject != -1 || m_ForcePanelActivation) + { + m_GazePanelDectivationCountdown = m_GazePanelDectivationDelay; + } + else + { + if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate)) + { + bGazeDeactivationOverrideWithInput = true; + m_GazePanelDectivationCountdown = 0.0f; + } + else + { + m_GazePanelDectivationCountdown -= Time.deltaTime; + } + if (m_GazePanelDectivationCountdown > 0.0f) + { + m_CurrentGazeObject = iPrevGazeObject; + } + } + + //update our positioning timer + if (m_PositioningPanelWithHead) + { + m_PositioningTimer += m_PositioningSpeed * Time.deltaTime; + m_PositioningTimer = Mathf.Min(m_PositioningTimer, 1.0f); + } + else + { + m_PositioningTimer -= m_PositioningSpeed * Time.deltaTime; + m_PositioningTimer = Mathf.Max(m_PositioningTimer, 0.0f); + } + + //prime objects if we change targets + if (iPrevGazeObject != m_CurrentGazeObject) + { + //if we're switching panels, make sure the pointer doesn't streak + PointerManager.m_Instance.DisablePointerPreviewLine(); + + if (iPrevGazeObject != -1) + { + aAllPanels[iPrevGazeObject].m_Panel.PanelGazeActive(false); + aAllPanels[iPrevGazeObject].m_Panel.SetPositioningPercent(0.0f); + } + if (m_CurrentGazeObject != -1) + { + //make sure our line is disabled + if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) + { + PointerManager.m_Instance.EnableLine(false); + PointerManager.m_Instance.AllowPointerPreviewLine(false); + } + + aAllPanels[m_CurrentGazeObject].m_Panel.PanelGazeActive(true); + aAllPanels[m_CurrentGazeObject].m_Panel.SetPositioningPercent(0.0f); + + if (m_GazeResults[m_CurrentGazeObject].m_ControllerName == InputManager.ControllerName.Brush) + { + m_SketchSurfacePanel.RequestHideActiveTool(true); + } + } + else + { + //if we don't have a panel, we need to enable the pointer according to the current tool + PointerManager.m_Instance.RefreshFreePaintPointerAngle(); + PointerManager.m_Instance.RequestPointerRendering(m_SketchSurfacePanel.ShouldShowPointer()); + m_UIReticle.SetActive(false); + m_SketchSurfacePanel.RequestHideActiveTool(false); + if (!bGazeDeactivationOverrideWithInput) + { + m_SketchSurfacePanel.EatToolsInput(); + } + } + + m_PositioningPanelWithHead = false; + } + UnityEngine.Profiling.Profiler.EndSample(); + } + + void UpdateActiveGazeObject() + { + BasePanel currentPanel = m_PanelManager.GetPanel(m_CurrentGazeObject); + currentPanel.SetPositioningPercent(m_PositioningTimer); + bool hasController = m_ControlsType == ControlsType.SixDofControllers; + // Update positioning behavior. + if (m_PositioningPanelWithHead) + { + if (!InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) && + !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + // No more positioning. + m_PositioningPanelWithHead = false; + m_PanelManager.m_SweetSpot.EnableBorderSphere(false, Vector3.zero, 0.0f); + currentPanel.PanelHasStoppedMoving(); + } + else + { + //lock the panel to the sweet spot bounds in the direction the user is looking + Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_PositioningPanelBaseHeadRotation); + Vector3 vAdjustedOffset = qDiff * m_PositioningPanelOffset; + + Vector3 vNewPos = m_PanelManager.m_SweetSpot.transform.position + vAdjustedOffset; + currentPanel.transform.position = vNewPos; + + vAdjustedOffset.Normalize(); + currentPanel.transform.forward = vAdjustedOffset; + + float fHighlightRadius = currentPanel.m_BorderSphereHighlightRadius; + m_PanelManager.m_SweetSpot.EnableBorderSphere(true, vNewPos, fHighlightRadius * m_PositioningTimer); + + //once we've moved this panel, run the simulation on the other panels to resolve collisions + m_PanelManager.DoCollisionSimulationForKeyboardMouse(currentPanel); + } + } + else + { + // It's possible that, on this frame, before this function was called, active gaze was pulled + // from this panel. In this case, we want to skip updating this frame. + // This happens when a panel has gaze and world grab dismisses all panels, for example. + if (currentPanel.IsActive()) + { + //orient to gaze + if (hasController) + { + currentPanel.UpdatePanel(m_GazeControllerRay.direction, m_CurrentGazeHitPoint); + } + else + { + currentPanel.UpdatePanel(m_CurrentGazeRay.direction, m_CurrentGazeHitPoint); + } + } + + if (!hasController) + { + //lock to head if we're holding a lock button.. + bool bLockToHead = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead) || + InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController); + + if (bLockToHead) + { + m_PositioningPanelWithHead = true; + m_PositioningPanelBaseHeadRotation = m_CurrentHeadOrientation; + m_PositioningPanelOffset = currentPanel.transform.position - + m_PanelManager.m_SweetSpot.transform.position; + + currentPanel.ResetPanelFlair(); + + //prime all other panels for movement + m_PanelManager.PrimeCollisionSimForKeyboardMouse(); + } + } + + PointerManager.m_Instance.RequestPointerRendering(false); + currentPanel.UpdateReticleOffset(m_MouseDeltaX, m_MouseDeltaY); + } + + // Keep reticle locked in the right spot. + Vector3 reticlePos = Vector3.zero; + Vector3 reticleForward = Vector3.zero; + if (hasController) + { + currentPanel.GetReticleTransformFromPosDir(m_CurrentGazeHitPoint, + m_GazeControllerRay.direction, out reticlePos, out reticleForward); + } + else + { + currentPanel.GetReticleTransform(out reticlePos, out reticleForward, + (m_ControlsType == ControlsType.ViewingOnly)); + } + + SetUIReticleTransform(reticlePos, -reticleForward); + m_UIReticle.SetActive(GetGazePanelActivationRatio() >= 1.0f); + } + + public void ResetActivePanel() + { + m_PanelManager.ResetPanel(m_CurrentGazeObject); + PointerManager.m_Instance.DisablePointerPreviewLine(); + m_PositioningPanelWithHead = false; + m_CurrentGazeObject = -1; + } + + void UpdatePanInput() + { + if (Mouse.current.rightButton.isPressed) + { + Vector3 vPanDiff = Vector3.zero; + vPanDiff += (Vector3.right * m_MouseDeltaXScaled); + vPanDiff += (Vector3.up * m_MouseDeltaYScaled); + Vector3 vSurfacePos = m_SketchSurface.transform.position; + m_SketchSurface.transform.position = vSurfacePos + vPanDiff; + } + else + { + float fCurrentTime = Time.realtimeSinceStartup; + if (fCurrentTime - m_PositionOffsetResetTapTime < m_DoubleTapWindow) + { + if (m_CurrentGazeObject == -1) + { + ResetGrabbedPose(); + } + } + m_PositionOffsetResetTapTime = fCurrentTime; + + SwitchState(InputState.Standard); + } + } + + void UpdateRotationInput() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation)) + { + bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); + bool bRollRotation = m_RotationRollActive || bAltInputActive || m_CurrentRotationType == RotationType.RollOnly; + m_RotationIcon.SetActive(bRollRotation); + if (bRollRotation) + { + m_RotationCursorOffset.x += m_MouseDeltaXScaled; + float fRotationAmount = m_RotationCursorOffset.x * -m_RotationRollScalar; + + Quaternion qOffsetRotation = Quaternion.AngleAxis(fRotationAmount, m_SurfaceForward); + Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; + m_SketchSurface.transform.rotation = qNewRotation; + + m_RotationRollActive = true; + m_RotationCursor.gameObject.SetActive(false); + } + else + { + //update offset with mouse movement + m_RotationCursorOffset.x += m_MouseDeltaXScaled; + m_RotationCursorOffset.y += m_MouseDeltaYScaled; + + //get offset in model space + Vector3 vSurfaceBounds = m_SketchSurface.transform.localScale * 0.5f; + m_RotationCursorOffset.x = Mathf.Clamp(m_RotationCursorOffset.x, -vSurfaceBounds.x, vSurfaceBounds.x); + m_RotationCursorOffset.y = Mathf.Clamp(m_RotationCursorOffset.y, -vSurfaceBounds.y, vSurfaceBounds.y); + float fCursorOffsetDist = m_RotationCursorOffset.magnitude; + float fMaxCursorOffsetDist = vSurfaceBounds.x; + + //transform offset in to world space + Vector3 vTransformedOffset = m_RotationOrigin * m_RotationCursorOffset; + vTransformedOffset.Normalize(); + + //get world space rotation axis + Vector3 vSketchSurfaceRotationAxis = Vector3.Cross(vTransformedOffset, m_SurfaceForward); + vSketchSurfaceRotationAxis.Normalize(); + + //amount to rotate is determined by offset distance from origin + float fSketchSurfaceRotationAngle = Mathf.Min(fCursorOffsetDist / fMaxCursorOffsetDist, 1.0f); + fSketchSurfaceRotationAngle *= m_RotationMaxAngle; + + //set new surface rotation by combining base rotation with angle/axis rotation + Quaternion qOffsetRotation = Quaternion.AngleAxis(fSketchSurfaceRotationAngle, vSketchSurfaceRotationAxis); + Quaternion qNewRotation = qOffsetRotation * m_RotationOrigin; + m_SketchSurface.transform.rotation = qNewRotation; + + //set position of rotation cursor + Vector3 vNewTransformedOffset = qNewRotation * m_RotationCursorOffset; + m_RotationCursor.transform.position = m_SketchSurface.transform.position + vNewTransformedOffset; + m_RotationCursor.transform.rotation = qNewRotation; + + //set position of guide lines + Vector2 vToCenter = m_RotationCursorOffset; + vToCenter.Normalize(); + float fOffsetAngle = Vector2.Angle(vToCenter, Vector2.up); + m_RotationCursor.PositionCursorLines(m_SketchSurface.transform.position, m_SketchSurface.transform.forward, fOffsetAngle, vSurfaceBounds.x * 2.0f); + } + } + else + { + float fCurrentTime = Time.realtimeSinceStartup; + if (fCurrentTime - m_RotationResetTapTime < m_DoubleTapWindow) + { + //reset drawing surface rotation + m_SketchSurface.transform.rotation = Quaternion.identity; + } + m_RotationResetTapTime = fCurrentTime; + + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + if (!m_RotationRollActive && m_AutoOrientAfterRotation && m_SketchSurfacePanel.IsSketchSurfaceToolActive()) + { + //get possible auto rotations + Quaternion qQuatUp = OrientSketchSurfaceToUp(); + Quaternion qQuatForward = OrientSketchSurfaceToForward(); + + //get the angle between our current and desired auto-rotation + float toUpAngle = Quaternion.Angle(qQuatUp, m_SketchSurface.transform.rotation); + float toForwardAngle = Quaternion.Angle(qQuatForward, m_SketchSurface.transform.rotation); + + //set our new rotation to be whichever autorotation is closeset + Quaternion qNewRotation; + if (Mathf.Abs(toUpAngle) < Mathf.Abs(toForwardAngle)) + { + qNewRotation = qQuatUp; + } + else + { + qNewRotation = qQuatForward; + } + + //update the sketch surface + m_SketchSurface.transform.rotation = qNewRotation; + + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + } + + SwitchState(InputState.Standard); + } + } + + void UpdateHeadLockInput() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToHead)) + { + //compute new position/orientation of sketch surface + Vector3 vTransformedOffset = m_CurrentHeadOrientation * m_SurfaceLockOffset; + Vector3 vSurfacePos = m_CurrentGazeRay.origin + vTransformedOffset; + + Quaternion qDiff = m_CurrentHeadOrientation * Quaternion.Inverse(m_SurfaceLockBaseHeadRotation); + Quaternion qNewSurfaceRot = qDiff * m_SurfaceLockBaseSurfaceRotation; + + m_SketchSurface.transform.position = vSurfacePos; + m_SketchSurface.transform.rotation = qNewSurfaceRot; + } + else + { + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + SwitchState(InputState.Standard); + } + } + + void UpdateControllerLock() + { + if (InputManager.m_Instance.GetCommand(InputManager.SketchCommands.LockToController)) + { + //compute new position/orientation of sketch surface + Vector3 vControllerDiff = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController) - m_SurfaceLockBaseControllerPosition; + m_SketchSurface.transform.position = m_SurfaceLockBaseSurfacePosition + (vControllerDiff * m_SurfaceLockControllerScalar); + + Quaternion qDiff = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController) * Quaternion.Inverse(m_SurfaceLockBaseControllerRotation); + m_SketchSurface.transform.rotation = qDiff * m_SurfaceLockBaseSurfaceRotation; + } + else + { + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + + SwitchState(InputState.Standard); + } + } + + void UpdatePushPullInput() + { + bool bRotationActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.PivotRotation); + bool bInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); + bool bAltInputActive = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.AltActivate); + + if (bRotationActive && bInputActive) + { + SwitchState(InputState.Rotation); + } + else if (bAltInputActive) + { + Vector3 vPos = m_SketchSurface.transform.position; + float fBigDiff = Mathf.Abs(m_MouseDeltaXScaled) > Mathf.Abs(m_MouseDeltaYScaled) ? -m_MouseDeltaXScaled : m_MouseDeltaYScaled; + vPos += Vector3.forward * fBigDiff; + + m_SketchSurface.transform.position = vPos; + } + else + { + SwitchState(InputState.Standard); + } + } + + void UpdateSaveInput() + { + if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Save)) + { + SwitchState(InputState.Standard); + } + } + + void UpdateLoadInput() + { + if (!InputManager.m_Instance.GetKeyboardShortcut(InputManager.KeyboardShortcut.Load)) + { + SwitchState(InputState.Standard); + } + } + + void OnBrushSetToDefault() + { + BrushDescriptor rDefaultBrush = BrushCatalog.m_Instance.DefaultBrush; + PointerManager.m_Instance.SetBrushForAllPointers(rDefaultBrush); + PointerManager.m_Instance.SetAllPointersBrushSize01(0.5f); + PointerManager.m_Instance.MarkAllBrushSizeUsed(); + } + + public void AssignControllerMaterials(InputManager.ControllerName controller) + { + ControllerGeometry geometry = InputManager.GetControllerGeometry(controller); + + // Start from a clean state + geometry.ResetAll(); + + // If the tutorial is enabled, override all materials. + if (TutorialManager.m_Instance.TutorialActive()) + { + InputManager.m_Instance + .GetControllerTutorial(controller) + ?.AssignControllerMaterials(controller); + return; + } + + // If we're grabbing the world, get the materials from the world transform panel. + if (m_GrabBrush.grabbingWorld && controller == InputManager.ControllerName.Brush) + { + TrTransform scenePose = App.Scene.Pose; + if (scenePose.scale != 1 || scenePose.translation != Vector3.zero + || scenePose.rotation != Quaternion.identity) + { + geometry.ShowWorldTransformReset(); + } + return; + } + else if (m_GrabWand.grabbingWorld && controller == InputManager.ControllerName.Wand) + { + TrTransform scenePose = App.Scene.Pose; + if (scenePose.scale != 1 || scenePose.translation != Vector3.zero + || scenePose.rotation != Quaternion.identity) + { + geometry.ShowWorldTransformReset(); + } + return; + } + + // Not grabbing the world, so see if we're grabbing a widget. + if (m_GrabWidgetState != GrabWidgetState.None) + { + m_CurrentGrabWidget.AssignControllerMaterials(controller); + return; + } + + // See if we're highlighting a widget and if that matters. + if (m_CurrentGrabWidget != null && m_CurrentGrabWidget.HasHoverInteractions()) + { + m_CurrentGrabWidget.AssignHoverControllerMaterials(controller); + return; + } + + // Not grabbing the world or a widget, see if we're interacting with a panel. + if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); + panel.AssignControllerMaterials(controller); + return; + } + + // Defaults. + if (controller == InputManager.ControllerName.Wand) + { + if (App.CurrentState != App.AppState.Standard || m_PanelManager.IntroSketchbookMode) + { + // If app is not in standard mode, the actions represented by subsequent material + // assigments cannot be taken. + return; + } + bool creatingStroke = PointerManager.m_Instance.IsMainPointerCreatingStroke(); + bool allowPainting = App.Instance.IsInStateThatAllowsPainting(); + + InputManager.Wand.Geometry.ShowRotatePanels(); + InputManager.Wand.Geometry.ShowUndoRedo(CanUndo() && !creatingStroke && allowPainting, + CanRedo() && !creatingStroke && allowPainting); + } + + // Show the pin cushion icon on the button if it's available. + if (controller == InputManager.ControllerName.Brush && CanUsePinCushion()) + { + InputManager.Brush.Geometry.ShowPinCushion(); + } + + // Finally, override with tools. + m_SketchSurfacePanel.AssignControllerMaterials(controller); + } + + public float GetControllerPadShaderRatio( + InputManager.ControllerName controller, VrInput input) + { + // If we're interacting with a panel, get touch ratio from the panel. + if (controller == InputManager.ControllerName.Brush && m_CurrentGazeObject != -1) + { + BasePanel panel = m_PanelManager.GetPanel(m_CurrentGazeObject); + return panel.GetControllerPadShaderRatio(controller); + } + return SketchSurfacePanel.m_Instance.GetCurrentToolSizeRatio(controller, input); + } + + void SwitchState(InputState rDesiredState) + { + //exit current state + switch (m_CurrentInputState) + { + case InputState.Pan: + m_TransformGizmoScript.ResetTransform(); + break; + case InputState.PushPull: + m_TransformGizmoScript.ResetTransform(); + break; + case InputState.Rotation: + m_RotationRollActive = false; + m_RotationIcon.SetActive(false); + m_RotationCursor.gameObject.SetActive(false); + break; + } + + bool bSketchSurfaceToolActive = m_SketchSurfacePanel.IsSketchSurfaceToolActive(); + + //enter new state + switch (rDesiredState) + { + case InputState.Pan: + m_TransformGizmoScript.SetTransformForPan(); + break; + case InputState.PushPull: + m_TransformGizmoScript.SetTransformForPushPull(); + break; + case InputState.Rotation: + if (bSketchSurfaceToolActive) + { + m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; + m_SketchSurfacePanel.ResetReticleOffset(); + } + m_RotationOrigin = m_SketchSurface.transform.rotation; + m_RotationCursorOffset = Vector2.zero; + m_RotationCursor.transform.position = m_SketchSurface.transform.position; + m_RotationCursor.transform.rotation = m_SketchSurface.transform.rotation; + m_RotationCursor.ClearCursorLines(m_SketchSurface.transform.position); + m_RotationCursor.gameObject.SetActive(bSketchSurfaceToolActive); + break; + case InputState.HeadLock: + m_SurfaceLockBaseHeadRotation = m_CurrentHeadOrientation; + m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; + m_SurfaceLockOffset = m_SketchSurface.transform.position - m_CurrentGazeRay.origin; + m_SurfaceLockOffset = Quaternion.Inverse(m_SurfaceLockBaseHeadRotation) * m_SurfaceLockOffset; + break; + case InputState.ControllerLock: + if (bSketchSurfaceToolActive) + { + m_SketchSurface.transform.position = PointerManager.m_Instance.MainPointer.transform.position; + m_SketchSurfacePanel.ResetReticleOffset(); + } + m_SurfaceLockActingController = InputManager.m_Instance.GetDominantController(InputManager.SketchCommands.LockToController); + m_SurfaceLockBaseSurfaceRotation = m_SketchSurface.transform.rotation; + m_SurfaceLockBaseControllerRotation = InputManager.m_Instance.GetControllerRotation(m_SurfaceLockActingController); + m_SurfaceLockBaseSurfacePosition = m_SketchSurface.transform.position; + m_SurfaceLockBaseControllerPosition = InputManager.m_Instance.GetControllerPosition(m_SurfaceLockActingController); + m_SurfaceLockControllerScalar = m_SketchSurfacePanel.m_PanelSensitivity / m_SurfaceLockControllerBaseScalar; + break; + case InputState.Save: + IssueGlobalCommand(GlobalCommands.Save); + break; + case InputState.Load: + IssueGlobalCommand(GlobalCommands.Load); + break; + } + + m_CurrentInputState = rDesiredState; + } + + public void RequestPanelsVisibility(bool bVisible) + { + // Always false in viewonly mode + bVisible = m_ViewOnly ? false : bVisible; + m_PanelsVisibilityRequested = bVisible; + } + + Quaternion OrientSketchSurfaceToUp() + { + //project the world up vector on to the surface plane + Vector3 vUpOnSurfacePlane = Vector3.up - (Vector3.Dot(Vector3.up, m_SurfaceForward) * m_SurfaceForward); + vUpOnSurfacePlane.Normalize(); + + //get the angle between the surface up and the projected world up + float fUpOnSurfacePlaneAngle = Vector3.Angle(vUpOnSurfacePlane, m_SurfaceUp); + Vector3 vUpCross = Vector3.Cross(vUpOnSurfacePlane, m_SurfaceUp); + vUpCross.Normalize(); + if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) + { + fUpOnSurfacePlaneAngle *= -1.0f; + } + + //rotate around the surface foward by the angle diff + Quaternion qOrientToUp = Quaternion.AngleAxis(fUpOnSurfacePlaneAngle, m_SurfaceForward); + Quaternion qNewRotation = qOrientToUp * m_SketchSurface.transform.rotation; + return qNewRotation; + } + + Quaternion OrientSketchSurfaceToForward() + { + //project the world forward vector on to the surface plane + Vector3 vForwardOnSurfacePlane = Vector3.forward - (Vector3.Dot(Vector3.forward, m_SurfaceForward) * m_SurfaceForward); + vForwardOnSurfacePlane.Normalize(); + + //get the angle between the surface up and the projected world forward + float fForwardOnSurfacePlaneAngle = Vector3.Angle(vForwardOnSurfacePlane, m_SurfaceUp); + Vector3 vUpCross = Vector3.Cross(vForwardOnSurfacePlane, m_SurfaceUp); + vUpCross.Normalize(); + if (Vector3.Dot(vUpCross, m_SurfaceForward) > 0.0f) + { + fForwardOnSurfacePlaneAngle *= -1.0f; + } + + //rotate around the surface foward by the angle diff + Quaternion qOrientToForward = Quaternion.AngleAxis(fForwardOnSurfacePlaneAngle, m_SurfaceForward); + Quaternion qNewRotation = qOrientToForward * m_SketchSurface.transform.rotation; + return qNewRotation; + } + + /// Reset the scene or the canvas, depending on the current mode + public void ResetGrabbedPose(bool everything = false) + { + //update sketch surface position with offset to sweet spot + m_SketchSurface.transform.position = m_PanelManager.GetSketchSurfaceResetPos(); + if (everything) + { + App.Scene.Pose = TrTransform.identity; + Coords.CanvasLocalPose = TrTransform.identity; + } + App.Scene.Pose = TrTransform.identity; + + //reset orientation and pointer + ResetSketchSurfaceOrientation(); + m_SketchSurfacePanel.ResetReticleOffset(); + PointerManager.m_Instance.DisablePointerPreviewLine(); + PointerManager.m_Instance.SetPointerPreviewLineDelayTimer(); + } + + public void ResetSketchSurfaceOrientation() + { + m_SketchSurface.transform.rotation = Quaternion.identity; + m_SurfaceForward = m_SketchSurface.transform.forward; + m_SurfaceRight = m_SketchSurface.transform.right; + m_SurfaceUp = m_SketchSurface.transform.up; + } + + float GetAppropriateMovementScalar() + { + switch (m_CurrentInputState) + { + case InputState.Pan: return m_PanScalar; + case InputState.Rotation: return m_RotationScalar; + case InputState.PushPull: return m_PushPullScale; + } + + return 1.0f; + } + + // TODO - it'd be great if we could disentangle this from the multicam. + IEnumerator RenderPathAndQuit() + { +#if USD_SUPPORTED + App.Instance.SetDesiredState(App.AppState.OfflineRendering); + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); + MultiCamTool multiCam = SketchSurfacePanel.m_Instance.ActiveTool as MultiCamTool; + Debug.Assert(multiCam != null); // Something's gone wrong if we've been unable to find multicam! + if (multiCam == null) + { + yield break; + } + multiCam.ExternalObjectForceCameraStyle(MultiCamStyle.Video); + MultiCamCaptureRig.ForceClippingPlanes(MultiCamStyle.Video); + // Give the video tool time to switch - TODO - be a little more graceful here + yield return new WaitForSeconds(2); + // Make sure the videos have had time to load, and set playing ones to start + while (VideoCatalog.Instance.IsScanning) + { + yield return null; + } + foreach (var widget in WidgetManager.m_Instance.VideoWidgets) + { + if (widget.VideoController.Playing) + { + widget.VideoController.Position = 0; + } + } + yield return null; + var ssMgr = MultiCamCaptureRig.ManagerFromStyle(MultiCamStyle.Video); + ssMgr.SetScreenshotResolution(App.UserConfig.Video.OfflineResolution); + multiCam.StartVideoCapture(MultiCamTool.GetSaveName(MultiCamStyle.Video), offlineRender: true); + App.Instance.FrameCountDisplay.gameObject.SetActive(true); + App.Instance.FrameCountDisplay.SetFramesTotal(VideoRecorderUtils.NumFramesInUsdSerializer); + while (VideoRecorderUtils.ActiveVideoRecording != null) + { + App.Instance.FrameCountDisplay.SetCurrentFrame( + VideoRecorderUtils.ActiveVideoRecording.FrameCount); + yield return null; + } + ssMgr.SetScreenshotResolution(App.UserConfig.Video.Resolution); +#else + Debug.LogError("Render path requires USD support"); + yield return null; +#endif + QuitApp(); + } + + IEnumerator ExportListAndQuit() + { + App.Config.m_ForceDeterministicBirthTimeForExport = true; + List filesToExport = new List(); + foreach (string filePattern in App.Config.m_FilePatternsToExport) + { + bool absolute = Path.IsPathRooted(filePattern); + string directory = absolute ? Path.GetDirectoryName(filePattern) : App.UserSketchPath(); + string filename = Path.GetFileName(filePattern); + var tiltFiles = Directory.GetFiles(directory, filename); + filesToExport.AddRange(tiltFiles); + // Also look at .tilt files which have been unzipped into directory format + var tiltDirs = Directory.GetDirectories(directory, filename) + .Where(n => n.EndsWith(".tilt")); + filesToExport.AddRange(tiltDirs); + } + + using (var coroutine = LoadAndExportList(filesToExport)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + QuitApp(); + } + + void QuitApp() + { + // We're done! Quit! +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExportList(List filenames) + { + foreach (var filename in filenames) + { + using (var coroutine = LoadAndExport(filename)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExportAll() + { + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.User); + for (int i = 0; i < SketchCatalog.m_Instance.GetSet(SketchSetType.User).NumSketches; ++i) + { + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(i); + using (var coroutine = LoadAndExport(rInfo.FullPath)) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + } + + /// Loads a .tilt file completely. + /// This may be slightly buggy; it's not currently used for production. + /// This coroutine must be run to completion or disposed. + public IEnumerable LoadTiltFile(string filename) + { + using (var unused = new SceneSettings.RequestInstantSceneSwitch()) + { + IssueGlobalCommand( + GlobalCommands.LoadNamedFile, + iParam1: (int)LoadSpeed.Quick, sParam: filename); + yield return null; + while (App.Instance.IsLoading()) + { + yield return null; + } + + // I don't know why App.Instance.IsLoading() doesn't cover this, but it doesn't. + while (m_WidgetManager.CreatingMediaWidgets) + { + yield return null; + } + while (WidgetManager.m_Instance.AreMediaWidgetsStillLoading()) + { + yield return null; + } + + // This is kind of a hack. + // Despite the RequestInstantSceneSwitch above, I think scene colors still require + // a few frames to settle; also, GrabWidgets need to register themselves on the + // first frame, etc. + for (int i = 0; i < 10; ++i) + { + yield return null; + } + } + } + + // This coroutine must be run to completion or disposed. + IEnumerator LoadAndExport(string filename) + { + foreach (var val in LoadTiltFile(filename)) + { + yield return val; + } + using (var coroutine = ExportCoroutine()) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + } + } + + IEnumerator ExportCoroutine() + { + return OverlayManager.m_Instance.RunInCompositor( + OverlayType.Export, () => + { + // Sort of a kludge: put stuff back into the main canvas + SelectionManager.m_Instance.ClearActiveSelection(); + Export.ExportScene(); + }, 0.25f, false, true); + } + + private void SaveModel() + { +#if USD_SUPPORTED + var current = SaveLoadScript.m_Instance.SceneFile; + string basename = (current.Valid) + ? Path.GetFileNameWithoutExtension(current.FullPath) + : "Untitled"; + string directoryName = FileUtils.GenerateNonexistentFilename( + App.ModelLibraryPath(), basename, ""); + + string usdname = Path.Combine(directoryName, basename + ".usd"); + // TODO: export selection only, though this is still only experimental. The blocking + // issue to implement this is that the export collector needs to expose this as an option. + // + // SelectionManager.m_Instance.HasSelection + // ? SelectionManager.m_Instance.SelectedStrokes + // : null + ExportUsd.ExportPayload(usdname); + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, "Model created!"); +#endif + } + + /// Generates a view from the previous thumbnail viewpoint. + public void GenerateReplacementSaveIcon() + { + if (SaveLoadScript.m_Instance.LastThumbnail_SS.HasValue) + { + TrTransform thumbnailInGlobalSpace = App.Scene.Pose * + SaveLoadScript.m_Instance.LastThumbnail_SS.Value; + + m_SaveIconTool.ProgrammaticCaptureSaveIcon(thumbnailInGlobalSpace.translation, + thumbnailInGlobalSpace.rotation); + } + else + { + GenerateBestGuessSaveIcon(); + } + } + + public void GenerateBestGuessSaveIcon() + { + TrTransform camXform = GenerateBestGuessSaveIconTransform(); + m_SaveIconTool.ProgrammaticCaptureSaveIcon(camXform.translation, camXform.rotation); + } + + /// This positions the save icon camera at the user's head position, and faces it towards + /// the most recent strokes the user has created. + /// If there are no strokes, it faces towards the 'most recent' models. + /// Sadly we cannot really mix the two as we don't know when the models were instantiated. + public TrTransform GenerateBestGuessSaveIconTransform(int itemsToEnumerate = 0) + { + if (itemsToEnumerate == 0) + { + itemsToEnumerate = m_NumStrokesForSaveIcon; + } + int startIndex = Mathf.Max(0, SketchMemoryScript.AllStrokesCount() - itemsToEnumerate); + var lastFewStrokes = SketchMemoryScript.AllStrokes().Skip(startIndex).ToArray(); + + Bounds bounds; + if (lastFewStrokes.Length > 0) + { + bounds = new Bounds(lastFewStrokes.First().m_ControlPoints.First().m_Pos, Vector3.zero); + foreach (var stroke in lastFewStrokes.Skip(1)) + { + bounds.Encapsulate(stroke.m_ControlPoints.First().m_Pos); + bounds.Encapsulate(stroke.m_ControlPoints.Last().m_Pos); + } + } + else + { + // If we have no strokes, just use the aggregates bounding boxes of the blocks models. + var models = m_WidgetManager.ModelWidgets.ToArray(); + // we should always have models to get here, but just in case... + if (models.Length > 0) + { + startIndex = Mathf.Max(0, models.Length - itemsToEnumerate); + bounds = models[startIndex].WorldSpaceBounds; + for (int i = startIndex + 1; i < models.Length; ++i) + { + bounds.Encapsulate(models[i].WorldSpaceBounds); + } + } + else + { + bounds = new Bounds(new Vector3(0, 1, -100000), Vector3.one); // some point in the distance + } + } + + Vector3 camPos = ViewpointScript.Head.position; + Vector3 worldPos = App.Scene.Pose.MultiplyPoint(bounds.center); + Quaternion direction = Quaternion.LookRotation(worldPos - camPos); + return TrTransform.TR(camPos, direction); + } + + + public void GenerateBoundingBoxSaveIcon() + { + Vector3 vNewCamPos; + { + Bounds rCanvasBounds = App.Scene.AllCanvases + .Select(canvas => canvas.GetCanvasBoundingBox()) + .Aggregate((b1, b2) => + { + b1.Encapsulate(b2); + return b1; + }); + + //position the camera at the center of the canvas bounds + vNewCamPos = rCanvasBounds.center; + + //back the camera up, along -z until we can see the extent of the bounds + float fCanvasWidth = rCanvasBounds.max.x - rCanvasBounds.min.x; + float fCanvasHeight = rCanvasBounds.max.y - rCanvasBounds.min.y; + float fLargerExtent = Mathf.Max(fCanvasHeight, fCanvasWidth); + + //half fov for camera + float fHalfFOV = m_SaveIconTool.ScreenshotManager.LeftEye.fieldOfView * 0.5f; + + //TODO: find the real reason this isn't working as it should + float fMagicNumber = 1.375f; + + //set new cam position and zero out orientation + float fBackupDistance = (fLargerExtent * 0.5f) + * Mathf.Tan(Mathf.Deg2Rad * fHalfFOV) * fMagicNumber; + vNewCamPos.z = rCanvasBounds.min.z - fBackupDistance; + } + + m_SaveIconTool.ProgrammaticCaptureSaveIcon(vNewCamPos, Quaternion.identity); + } + + private void MergeBrushStrokes(SceneFileInfo fileInfo) + { + m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + if (SaveLoadScript.m_Instance.Load(fileInfo, true)) + { + SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); + SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true, false, false); + // the order of these two lines are important as ExitIntroSketch is setting the + // color of the pointer and we need the color to be set before we go to the Loading + // state. App script's ShouldTintControllers allow the controller to be tinted only + // when the app is in the standard mode. That was there to prevent the controller color + // from flickering while in the intro mode. + App.Instance.ExitIntroSketch(); + App.Instance.SetDesiredState(App.AppState.QuickLoad); + } + } + + public void LoadSketch(SceneFileInfo fileInfo, bool quickload = false, bool additive = false) + { + LightsControlScript.m_Instance.DiscoMode = false; + m_WidgetManager.FollowingPath = false; + m_WidgetManager.CameraPathsVisible = false; + m_WidgetManager.DestroyAllWidgets(); + m_PanelManager.ToggleSketchbookPanels(isLoadingSketch: true); + ResetGrabbedPose(everything: true); + PointerManager.m_Instance.EnablePointerStrokeGeneration(true); + if (SaveLoadScript.m_Instance.Load(fileInfo, additive)) + { + SketchMemoryScript.m_Instance.SetPlaybackMode(m_SketchPlaybackMode, m_DefaultSketchLoadSpeed); + SketchMemoryScript.m_Instance.BeginDrawingFromMemory(bDrawFromStart: true); + // the order of these two lines are important as ExitIntroSketch is setting the + // color of the pointer and we need the color to be set before we go to the Loading + // state. App script's ShouldTintControllers allow the controller to be tinted only + // when the app is in the standard mode. That was there to prevent the controller color + // from flickering while in the intro mode. + App.Instance.ExitIntroSketch(); + App.Instance.SetDesiredState(quickload ? App.AppState.QuickLoad : App.AppState.Loading); + } + QualityControls.m_Instance.ResetAutoQuality(); + m_WidgetManager.ValidateCurrentCameraPath(); + } + + public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, + int iParam2 = -1, string sParam = null) + { + switch (rEnum) + { + + // Keyboard command, for debugging and emergency use. + case GlobalCommands.Save: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + return; + } + // Disable active selection before saving. + // This looks fishy, here's what's going on: When an object is selected and it has moved, the + // the user is observing the selection canvas in the HMD, but we will be saving the main canvas. + // Because they haven't deselected yet, the selection canvas and the main canvas are out of sync + // so the strokes that will be saved will not match what the user sees. + // + // Here we deselect to force the main canvas to sync with the selection canvas, which is more + // correct from the user's perspective. Push the deselect operation onto the stack so the user + // can undo it after save, if desired. + SelectionManager.m_Instance.ClearActiveSelection(); + GenerateReplacementSaveIcon(); + if (iParam1 == -1) + { + if (iParam2 == 1) + { + // Do a save in Tiltasaurus mode, which creates a new filename prefixed with + // "Tiltasaurus_" and the current prompt. Also, don't eat gaze input so that the + // Tiltasaurus prompt stays open. + StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite(tiltasaurusMode: true)); + } + else + { + StartCoroutine(SaveLoadScript.m_Instance.SaveOverwrite()); + EatGazeObjectInput(); + } + } + else + { + StartCoroutine(SaveLoadScript.m_Instance.SaveMonoscopic(iParam1)); + } + break; + } + case GlobalCommands.SaveNew: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + return; + } + if (iParam1 == 1) + { + GenerateBoundingBoxSaveIcon(); + } + StartCoroutine(SaveLoadScript.m_Instance.SaveNewName()); + EatGazeObjectInput(); + break; + } + case GlobalCommands.SaveAndUpload: + { + if (!FileUtils.CheckDiskSpaceWithError(App.UserSketchPath())) + { + Debug.LogError("SaveAndUpload: Disk space error"); + return; + } + SelectionManager.m_Instance.ClearActiveSelection(); + m_PanelManager.GetPanel(m_CurrentGazeObject).CreatePopUp( + GlobalCommands.UploadToGenericCloud, (int)Cloud.None, -1); + EatGazeObjectInput(); + break; + } + case GlobalCommands.ExportAll: + { + StartCoroutine(LoadAndExportAll()); + break; + } + // Glen Keane request: a way to draw guidelines that can be toggled on and off + // at runtime. + case GlobalCommands.DraftingVisibility: + { + if (!Enum.IsDefined(typeof(DraftingVisibilityOption), iParam1)) + { + Debug.LogError("Unknown draft visibility value: " + iParam1); + return; + } + DraftingVisibilityOption option = (DraftingVisibilityOption)iParam1; + if (option != m_DraftingVisibility) + { + m_DraftingVisibility = option; + UpdateDraftingVisibility(); + } + break; + } + case GlobalCommands.MergeBrushStrokes: + { + // TODO Refactor with Load below + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + if (rInfo != null) + { + MergeBrushStrokes(rInfo); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + break; + } + case GlobalCommands.Load: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + if (rInfo != null) + { + LoadSketch(rInfo); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + break; + } + case GlobalCommands.LoadNamedFile: + LoadNamed(sParam, iParam1 == (int)LoadSpeed.Quick, iParam2 != -1); + break; + case GlobalCommands.NewSketch: + NewSketch(fade: true); + Vector3 vTrashSoundPos = m_CurrentGazeRay.origin; + if (App.VrSdk.GetControllerDof() == VrSdk.DoF.Six) + { + vTrashSoundPos = InputManager.m_Instance.GetControllerPosition( + InputManager.ControllerName.Wand); + } + AudioManager.m_Instance.PlayTrashSound(vTrashSoundPos); + PromoManager.m_Instance.RequestAdvancedPanelsPromo(); + break; + case GlobalCommands.SymmetryPlane: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.SinglePlane) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.SinglePlane); + ControllerConsoleScript.m_Instance.AddNewLine("Mirror Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Mirror Off"); + } + break; + case GlobalCommands.MultiMirror: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.MultiMirror) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.MultiMirror); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); + } + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + break; + case GlobalCommands.SymmetryTwoHanded: + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.TwoHanded) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.TwoHanded); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Enabled"); + } + else + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None); + ControllerConsoleScript.m_Instance.AddNewLine("Symmetry Off"); + } + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + break; + case GlobalCommands.StraightEdge: + PointerManager.m_Instance.StraightEdgeModeEnabled = !PointerManager.m_Instance.StraightEdgeModeEnabled; + if (PointerManager.m_Instance.StraightEdgeModeEnabled) + { + ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge On"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Straight Edge Off"); + } + break; + case GlobalCommands.AutoOrient: + m_AutoOrientAfterRotation = !m_AutoOrientAfterRotation; + if (m_AutoOrientAfterRotation) + { + ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient On"); + } + else + { + ControllerConsoleScript.m_Instance.AddNewLine("Auto-Orient Off"); + } + break; + case GlobalCommands.Undo: + SketchMemoryScript.m_Instance.StepBack(); + break; + case GlobalCommands.Redo: + SketchMemoryScript.m_Instance.StepForward(); + break; + case GlobalCommands.AudioVisualization: // Intentionally blank. + break; + case GlobalCommands.ResetAllPanels: + m_PanelManager.ResetWandPanelsConfiguration(); + EatGazeObjectInput(); + break; + case GlobalCommands.SketchOrigin: + m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SketchOrigin); + EatGazeObjectInput(); + break; + case GlobalCommands.ViewOnly: + m_ViewOnly = !m_ViewOnly; + RequestPanelsVisibility(!m_ViewOnly); + PointerManager.m_Instance.RequestPointerRendering(!m_ViewOnly); + // TODO - decide if this is a permanent change + // With this line, you can't set a tool such as fly or teleport + // and switch to View Only mode as the mode change disables all tools + //m_SketchSurface.SetActive(!m_ViewOnly); + m_Decor.SetActive(!m_ViewOnly); + break; + case GlobalCommands.SaveGallery: + m_SketchSurfacePanel.EnableSpecificTool(BaseTool.ToolType.SaveIconTool); + break; + case GlobalCommands.DropCam: + // Want to enable this if in monoscopic or VR modes. + // TODO: seems odd to tie this switch to the controller type, should be based on some + // other build-time configuration setting. + if (App.VrSdk.GetControllerDof() != VrSdk.DoF.None) + { + m_DropCam.Show(!m_DropCam.gameObject.activeSelf); + } + break; + case GlobalCommands.AnalyticsEnabled_Deprecated: + break; + case GlobalCommands.ToggleAutosimplification: + QualityControls.AutosimplifyEnabled = !QualityControls.AutosimplifyEnabled; + break; + case GlobalCommands.Credits: + LoadSketch(new DiskSceneFileInfo(m_CreditsSketchFilename, embedded: true, readOnly: true)); + EatGazeObjectInput(); + break; + case GlobalCommands.AshleysSketch: + LoadSketch(new DiskSceneFileInfo(m_AshleysSketchFilename, embedded: true, readOnly: true)); + EatGazeObjectInput(); + break; + case GlobalCommands.FAQ: + OpenURLAndInformUser(m_HelpCenterURL); + break; + case GlobalCommands.ReleaseNotes: + OpenURLAndInformUser(m_ReleaseNotesURL); + break; + case GlobalCommands.ExportRaw: + if (!FileUtils.CheckDiskSpaceWithError(App.UserExportPath())) + { + return; + } + EatGazeObjectInput(); + StartCoroutine(ExportCoroutine()); + break; + case GlobalCommands.IRC: + if (m_IRCChatWidget == null) + { + GameObject widgetobject = (GameObject)Instantiate(m_IRCChatPrefab); + widgetobject.transform.parent = App.Instance.m_RoomTransform; + m_IRCChatWidget = widgetobject.GetComponent(); + m_IRCChatWidget.Show(true); + } + else + { + m_IRCChatWidget.Show(false); + m_IRCChatWidget = null; + } + break; + case GlobalCommands.YouTubeChat: + if (m_YouTubeChatWidget == null) + { + GameObject widgetobject = (GameObject)Instantiate(m_YouTubeChatPrefab); + widgetobject.transform.parent = App.Instance.m_RoomTransform; + m_YouTubeChatWidget = widgetobject.GetComponent(); + m_YouTubeChatWidget.Show(true); + } + else + { + m_YouTubeChatWidget.Show(false); + m_YouTubeChatWidget = null; + } + break; + case GlobalCommands.CameraOptions: + // If we're switching in to Camera mode, make sure Multicam is selected. + if (!m_PanelManager.CameraActive()) + { + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.MultiCamTool); + } + m_PanelManager.ToggleCameraPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.ShowSketchFolder: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo rInfo = sketchSet.GetSketchSceneFileInfo(index); + EatGazeObjectInput(); + //launch external window and tell the user we did so + //this call is windows only + if ((Application.platform == RuntimePlatform.WindowsPlayer) || + (Application.platform == RuntimePlatform.WindowsEditor)) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + kRemoveHeadsetFyi, fPopScalar: 0.5f); + System.Diagnostics.Process.Start("explorer.exe", + "/select," + rInfo.FullPath); + } + break; + } + case GlobalCommands.About: + OpenURLAndInformUser(m_ThirdPartyNoticesURL); + break; + case GlobalCommands.StencilsDisabled: + SketchMemoryScript.m_Instance.PerformAndRecordCommand(new StencilsVisibleCommand()); + break; + case GlobalCommands.StraightEdgeMeterDisplay: + PointerManager.m_Instance.StraightEdgeGuide.FlipMeter(); + break; + case GlobalCommands.Sketchbook: + m_PanelManager.ToggleSketchbookPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.StraightEdgeShape: + // Previously experimental mode only. + // Untested and currently untriggerable. + PointerManager.m_Instance.StraightEdgeGuide.SetTempShape( + (StraightEdgeGuideScript.Shape)iParam1); + break; + case GlobalCommands.DeleteSketch: + { + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + sketchSet.DeleteSketch(iParam1); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.RenameSketch: + { + var sketchSetType = (SketchSetType)iParam2; + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + string newName = sParam; + if (string.IsNullOrEmpty(newName)) + { + newName = KeyboardPopUpWindow.m_LastInput; + if (string.IsNullOrEmpty(newName)) + { + break; + } + } + + if (sketchSetType == SketchSetType.User) + { + sketchSet.RenameSketch(iParam1, newName); + } + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.RenameLayer: + { + var layer = App.Scene.GetCanvasByLayerIndex(iParam1); + App.Scene.RenameLayer(layer, KeyboardPopUpWindow.m_LastInput); + DismissPopupOnCurrentGazeObject(false); + break; + } + case GlobalCommands.ShowWindowGUI: + break; + case GlobalCommands.Disco: + LightsControlScript.m_Instance.DiscoMode = !LightsControlScript.m_Instance.DiscoMode; + break; + case GlobalCommands.AccountInfo: break; // Intentionally blank. + case GlobalCommands.LoginToGenericCloud: + { + var ident = App.GetIdentity((Cloud)iParam1); + if (!ident.LoggedIn) { ident.LoginAsync(); } + // iParam2 is being used as a UX flag. If not set to the default, it will cause the UI + // to lose focus. + if (iParam2 != -1) { EatGazeObjectInput(); } + break; + } + case GlobalCommands.LogOutOfGenericCloud: + { + Cloud cloud = (Cloud)iParam1; + if (cloud == Cloud.Icosa) + { + App.Instance.IcosaToken = null; + App.IcosaUserName = ""; + App.IcosaUserIcon = null; + } + else + { + var ident = App.GetIdentity(cloud); + if (ident.LoggedIn) { ident.Logout(); } + } + break; + } + case GlobalCommands.UploadToGenericCloud: + { + Cloud cloud = (Cloud)iParam1; + if (cloud == Cloud.Icosa) + { + if (App.Instance.IcosaToken == null) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + "Not logged in", fPopScalar: 0.5f); + } + } + else + { + var ident = App.GetIdentity(cloud); + if (!ident.LoggedIn) + { + ident.LoginAsync(); + break; + } + } + SelectionManager.m_Instance.ClearActiveSelection(); + VrAssetService.m_Instance.UploadCurrentSketchAsync(cloud, isDemoUpload: false).AsAsyncVoid(); + EatGazeObjectInput(); + break; + } + case GlobalCommands.ViewOnlineGallery: + OpenURLAndInformUser(kTiltBrushGalleryUrl); + break; + case GlobalCommands.CancelUpload: + VrAssetService.m_Instance.CancelUpload(); + break; + case GlobalCommands.ViewLastUpload: + if (VrAssetService.m_Instance.LastUploadCompleteUrl != null) + { + var url = VrAssetService.m_Instance.LastUploadCompleteUrl; + App.OpenURL(url); + + // The upload flow is different on mobile and requires the user to manually accept + // that they'll go to the browser for publishing. In that case, we want to reset + // state when the leave to publish. This is automatically part of the + // UploadPopUpWindow state flow on PC. + if (App.Config.IsMobileHardware) + { + DismissPopupOnCurrentGazeObject(true); + } + } + break; + case GlobalCommands.ShowGoogleDrive: + string baseDriveUrl = "https://drive.google.com"; + string driveURL = !App.GoogleIdentity.LoggedIn ? baseDriveUrl : + string.Format( + "http://accounts.google.com/AccountChooser?Email={0}&continue={1}", + App.GoogleIdentity.Profile.email, baseDriveUrl); + OpenURLAndInformUser(driveURL); + break; + case GlobalCommands.GoogleDriveSync: + App.DriveSync.SyncEnabled = !App.DriveSync.SyncEnabled; + break; + case GlobalCommands.GoogleDriveSync_Folder: + App.DriveSync.ToggleSyncOnFolderOfType((DriveSync.SyncedFolderType)iParam1); + break; + case GlobalCommands.Duplicate: + { + int selectedVerts = SelectionManager.m_Instance.NumVertsInSelection; + + // TODO - this code has never taken imported models etc into account + if (PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror) + { + selectedVerts *= PointerManager.m_Instance.CustomMirrorMatrices.Count; + } + + if (!SketchMemoryScript.m_Instance.MemoryWarningAccepted && + SketchMemoryScript.m_Instance.WillVertCountPutUsOverTheMemoryLimit(selectedVerts)) + { + AudioManager.m_Instance.PlayUploadCanceledSound(InputManager.Wand.Transform.position); + if (!m_PanelManager.MemoryWarningActive()) + { + m_PanelManager.ToggleMemoryWarningMode(); + } + } + else + { + ClipboardManager.Instance.DuplicateSelection( + stampMode: IsUserInteractingWithSelectionWidget()); + } + EatToolScaleInput(); + break; + } + case GlobalCommands.AdvancedPanelsToggle: + m_PanelManager.ToggleAdvancedPanels(); + // If we're now in basic mode, ensure we don't have advanced abilities. + if (!m_PanelManager.AdvancedModeActive()) + { + m_WidgetManager.StencilsDisabled = true; + m_WidgetManager.CameraPathsVisible = false; + App.Switchboard.TriggerStencilModeChanged(); + m_SketchSurfacePanel.EnsureUserHasBasicToolEnabled(); + if (PointerManager.m_Instance.CurrentSymmetryMode != SymmetryMode.None) + { + PointerManager.m_Instance.SetSymmetryMode(SymmetryMode.None, false); + } + } + PromoManager.m_Instance.RecordCompletion(PromoType.AdvancedPanels); + EatGazeObjectInput(); + break; + case GlobalCommands.Music: break; // Intentionally blank. + case GlobalCommands.ToggleGroupStrokesAndWidgets: + SelectionManager.m_Instance.ToggleGroupSelectedStrokesAndWidgets(); + EatToolScaleInput(); + break; + case GlobalCommands.SaveModel: + SaveModel(); + break; + case GlobalCommands.ViewPolyPage: + OpenURLAndInformUser(kPolyMainPageUri); + break; + case GlobalCommands.ViewPolyGallery: + OpenURLAndInformUser(kBlocksGalleryUrl); + break; + case GlobalCommands.ExportListed: + StartCoroutine(ExportListAndQuit()); + break; + case GlobalCommands.RenderCameraPath: + StartCoroutine(RenderPathAndQuit()); + break; + case GlobalCommands.ToggleProfiling: + ToggleProfiling(); + break; + case GlobalCommands.DoAutoProfile: + DoAutoProfile(); + break; + case GlobalCommands.DoAutoProfileAndQuit: + DoAutoProfileAndQuit(); + break; + case GlobalCommands.ToggleSettings: + m_PanelManager.ToggleSettingsPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.SummonMirror: + PointerManager.m_Instance.BringSymmetryToUser(); + break; + case GlobalCommands.InvertSelection: + SelectionManager.m_Instance.InvertSelection(); + break; + case GlobalCommands.SelectAll: + SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); + SelectionManager.m_Instance.SelectAll(); + EatGazeObjectInput(); + break; + case GlobalCommands.FlipSelection: + SelectionManager.m_Instance.FlipSelection(); + break; + case GlobalCommands.ToggleBrushLab: + m_PanelManager.ToggleBrushLabPanels(); + PointerManager.m_Instance.EatLineEnabledInput(); + SketchSurfacePanel.m_Instance.EatToolsInput(); + break; + case GlobalCommands.ToggleCameraPostEffects: + CameraConfig.PostEffects = !CameraConfig.PostEffects; + break; + case GlobalCommands.ToggleWatermark: + if (PlayerPrefs.GetInt("Promo_Contribution", 0) == 0) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Wand, + m_ContributionPromoText, fPopScalar: 1.0f); + PlayerPrefs.SetInt("Promo_Contribution", 1); + } + CameraConfig.Watermark = !CameraConfig.Watermark; + break; + case GlobalCommands.LoadConfirmComplexHigh: + IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); + break; + case GlobalCommands.LoadConfirmComplex: + { + var index = iParam1; + var sketchSetType = (SketchSetType)iParam2; + bool loadSketch = true; + + // If the sketchbook is active, we may want to show a popup instead of load. + if (m_PanelManager.SketchbookActive()) + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + if (sketchBook != null) + { + // Get triangle count from cloud scene file info. + SketchSet sketchSet = SketchCatalog.m_Instance.GetSet(sketchSetType); + SceneFileInfo sfi = sketchSet.GetSketchSceneFileInfo(index); + int tris = sfi.TriangleCount ?? -1; + + // Show "this is bad" popup if we're over the triangle limit. + if (tris > QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles) + { + loadSketch = false; + sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplexHigh, iParam1, iParam2); + } + else if (tris > + QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles) + { + // Show, "this could be bad" popup if we're over the warning limit. + loadSketch = false; + sketchBook.CreatePopUp(GlobalCommands.Load, iParam1, iParam2); + } + } + } + + if (loadSketch) + { + IssueGlobalCommand(GlobalCommands.Load, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.LoadConfirmUnsaved: + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + if ((sketchBook != null) && SketchMemoryScript.m_Instance.IsMemoryDirty()) + { + sketchBook.CreatePopUp(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); + } + else + { + IssueGlobalCommand(GlobalCommands.LoadWaitOnDownload, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.LoadWaitOnDownload: + { + bool download = false; + if (iParam2 == (int)SketchSetType.Drive) + { + BasePanel sketchBook = m_PanelManager.GetSketchBookPanel(); + var googleSketchSet = SketchCatalog.m_Instance.GetSet(SketchSetType.Drive); + if (sketchBook != null + && googleSketchSet != null + && googleSketchSet.IsSketchIndexValid(iParam1) + && !googleSketchSet.GetSketchSceneFileInfo(iParam1).Available) + { + sketchBook.CreatePopUp(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); + download = true; + } + } + if (!download) + { + IssueGlobalCommand(GlobalCommands.LoadConfirmComplex, iParam1, iParam2, null); + } + } + break; + case GlobalCommands.MemoryWarning: + if (iParam1 > 0) + { + SketchMemoryScript.m_Instance.MemoryWarningAccepted = true; + } + m_PanelManager.ToggleMemoryWarningMode(); + break; + case GlobalCommands.MemoryExceeded: + // If we're in the memory exceeded app state, exit. + if (App.CurrentState == App.AppState.MemoryExceeded) + { + App.Instance.SetDesiredState(App.AppState.Standard); + } + else + { + // If we're not in the full app state, just switch our panel mode. + m_PanelManager.ToggleMemoryWarningMode(); + } + break; + case GlobalCommands.ShowTos: + OpenURLAndInformUser(m_TosURL); + break; + case GlobalCommands.ShowPrivacy: + OpenURLAndInformUser(m_PrivacyURL); + break; + case GlobalCommands.ShowQuestSideLoading: + OpenURLAndInformUser(m_QuestSideLoadingHowToURL); + break; + case GlobalCommands.ShowContribution: + OpenURLAndInformUser(m_ContributionURL); + break; + case GlobalCommands.UnloadReferenceImageCatalog: + ReferenceImageCatalog.m_Instance.UnloadAllImages(); + break; + case GlobalCommands.ToggleCameraPathVisuals: + m_WidgetManager.CameraPathsVisible = !m_WidgetManager.CameraPathsVisible; + break; + case GlobalCommands.ToggleCameraPathPreview: + m_WidgetManager.FollowingPath = !m_WidgetManager.FollowingPath; + break; + case GlobalCommands.DeleteCameraPath: + { + var cameraPath = m_WidgetManager.GetCurrentCameraPath(); + GrabWidget cameraPathWidget = cameraPath == null ? null : cameraPath.m_WidgetScript; + m_WidgetManager.DeleteCameraPath(cameraPathWidget); + } + break; + case GlobalCommands.RecordCameraPath: + // Turn off MultiCam if we're going to record the camera path. + if (m_SketchSurfacePanel.GetCurrentToolType() == BaseTool.ToolType.MultiCamTool) + { + m_SketchSurfacePanel.EnableDefaultTool(); + } + CameraPathCaptureRig.RecordPath(); + EatGazeObjectInput(); + break; + case GlobalCommands.OpenScriptsCommandsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/help/commands"); + break; + case GlobalCommands.OpenScriptsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/scripts"); + break; + case GlobalCommands.OpenExampleScriptsList: + OpenURLAndInformUser($"http://localhost:{App.HttpServer.HttpPort}/examplescripts"); + break; + case GlobalCommands.RepaintOptions: break; // Intentionally blank. + case GlobalCommands.LoginToIcosa: break; // Intentionally blank. + case GlobalCommands.Null: break; // Intentionally blank. + default: + Debug.LogError($"Unrecognized command {rEnum}"); + break; + } + } + + private void LoadNamed(string path, bool quickload, bool additive) + { + var fileInfo = new DiskSceneFileInfo(path); + fileInfo.ReadMetadata(); + if (SaveLoadScript.m_Instance.LastMetadataError != null) + { + ControllerConsoleScript.m_Instance.AddNewLine( + string.Format("Error detected in sketch '{0}'.\nTry re-saving.", + fileInfo.HumanName)); + Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", + fileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); + } + LoadSketch(fileInfo, quickload, additive); + if (m_ControlsType != ControlsType.ViewingOnly) + { + EatGazeObjectInput(); + } + } + + public void OpenURLAndInformUser(string url) + { + // On desktop - launch external browser and inform the user + // On mobile - the browser appears over the app + if (!App.Config.IsMobileHardware) + { + OutputWindowScript.m_Instance.CreateInfoCardAtController( + InputManager.ControllerName.Brush, + kRemoveHeadsetFyi, fPopScalar: 0.5f); + } + App.OpenURL(url); + EatGazeObjectInput(); + } + + public bool IsCommandActive(GlobalCommands rEnum, int iParam = -1) + { + switch (rEnum) + { + case GlobalCommands.StraightEdge: return PointerManager.m_Instance.StraightEdgeModeEnabled; + case GlobalCommands.StraightEdgeMeterDisplay: return PointerManager.m_Instance.StraightEdgeGuide.IsShowingMeter(); + case GlobalCommands.SymmetryPlane: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.SinglePlane; + case GlobalCommands.MultiMirror: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.MultiMirror; + case GlobalCommands.SymmetryTwoHanded: return PointerManager.m_Instance.CurrentSymmetryMode == SymmetryMode.TwoHanded; + case GlobalCommands.AutoOrient: return m_AutoOrientAfterRotation; + case GlobalCommands.AudioVisualization: return VisualizerManager.m_Instance.VisualsRequested; + case GlobalCommands.AdvancedPanelsToggle: return m_PanelManager.AdvancedModeActive(); + case GlobalCommands.Music: return VisualizerManager.m_Instance.VisualsRequested; + case GlobalCommands.DropCam: return m_DropCam.gameObject.activeSelf; + case GlobalCommands.ToggleAutosimplification: return QualityControls.AutosimplifyEnabled; + case GlobalCommands.DraftingVisibility: return m_DraftingVisibility == (DraftingVisibilityOption)iParam; + case GlobalCommands.Cameras: + return SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.AutoGif || + SketchSurfacePanel.m_Instance.GetCurrentToolType() == BaseTool.ToolType.ScreenshotTool; + case GlobalCommands.IRC: return m_IRCChatWidget != null; + case GlobalCommands.YouTubeChat: return m_YouTubeChatWidget != null; + case GlobalCommands.StencilsDisabled: return m_WidgetManager.StencilsDisabled; + case GlobalCommands.StraightEdgeShape: + // Previously experimental mode only. + // Untested and currently untriggerable. + return PointerManager.m_Instance.StraightEdgeGuide.TempShape == (StraightEdgeGuideScript.Shape)iParam || + (PointerManager.m_Instance.StraightEdgeGuide.TempShape == StraightEdgeGuideScript.Shape.None + && PointerManager.m_Instance.StraightEdgeGuide.CurrentShape == (StraightEdgeGuideScript.Shape)iParam); + case GlobalCommands.Disco: return LightsControlScript.m_Instance.DiscoMode; + case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.UngroupingAllowed; + case GlobalCommands.ToggleProfiling: return UnityEngine.Profiling.Profiler.enabled; + case GlobalCommands.ToggleCameraPostEffects: return CameraConfig.PostEffects; + case GlobalCommands.ToggleWatermark: return CameraConfig.Watermark; + case GlobalCommands.ToggleCameraPathVisuals: return m_WidgetManager.CameraPathsVisible; + case GlobalCommands.ToggleCameraPathPreview: return m_WidgetManager.FollowingPath; + case GlobalCommands.SelectCameraPath: + return m_WidgetManager.IsCameraPathAtIndexCurrent(iParam) && + m_WidgetManager.CameraPathsVisible; + case GlobalCommands.GoogleDriveSync_Folder: + return App.DriveSync.IsFolderOfTypeSynced((DriveSync.SyncedFolderType)iParam); + case GlobalCommands.GoogleDriveSync: return App.DriveSync.SyncEnabled; + case GlobalCommands.RecordCameraPath: return VideoRecorderUtils.ActiveVideoRecording != null; + } + return false; + } + + public void NewSketch(bool fade) + { + LightsControlScript.m_Instance.DiscoMode = false; + m_WidgetManager.FollowingPath = false; + SketchMemoryScript.m_Instance.ClearMemory(); + ControllerConsoleScript.m_Instance.AddNewLine("Sketch Cleared"); + ResetGrabbedPose(everything: true); + QualityControls.m_Instance.ResetAutoQuality(); + InputManager.m_Instance.TriggerHaptics(InputManager.ControllerName.Brush, 0.1f); + SaveLoadScript.m_Instance.ResetLastFilename(); + SelectionManager.m_Instance.RemoveFromSelection(false); + PointerManager.m_Instance.ResetSymmetryToHome(); + App.Scene.ResetLayers(notify: true); + ApiManager.Instance.ResetBrushTransform(); + + // If we've got the camera path tool active, switch back to the default tool. + // I'm doing this because if we leave the camera path tool active, the camera path + // panel shows the button highlighted, which affects the user's flow for being + // invited to start a path. It looks weird. + if (m_SketchSurfacePanel.ActiveToolType == BaseTool.ToolType.CameraPathTool) + { + m_SketchSurfacePanel.EnableDefaultTool(); + } + + m_WidgetManager.DestroyAllWidgets(); + if (LightsControlScript.m_Instance.LightsChanged || + SceneSettings.m_Instance.EnvironmentChanged) + { + SceneSettings.m_Instance.RecordSkyColorsForFading(); + SceneSettings.m_Instance.SetDesiredPreset( + SceneSettings.m_Instance.GetDesiredPreset(), skipFade: !fade); + } + // Blank the thumbnail position so that autosave won't save the thumbnail position to be + // the one from the old sketch. + SaveLoadScript.m_Instance.LastThumbnail_SS = null; + + // Re-set the quality level to reset simplification level + QualityControls.m_Instance.QualityLevel = QualityControls.m_Instance.QualityLevel; + + App.IcosaAssetCatalog.ClearLoadingQueue(); + App.IcosaAssetCatalog.UnloadUnusedModels(); + } + + private bool WorldIsReset(bool toSavedXf) + { + return App.Scene.Pose == + (toSavedXf ? SketchMemoryScript.m_Instance.InitialSketchTransform : TrTransform.identity); + } + + public bool IsCommandAvailable(GlobalCommands rEnum, int iParam = -1) + { + // TODO: hide gallery view / publish if there are no saved sketches + switch (rEnum) + { + case GlobalCommands.Undo: return SketchMemoryScript.m_Instance.CanUndo(); + case GlobalCommands.Redo: return SketchMemoryScript.m_Instance.CanRedo(); + case GlobalCommands.Save: + bool canSave = + SaveLoadScript.m_Instance.SceneFile.Valid && + SaveLoadScript.m_Instance.IsSavingAllowed(); + return canSave && (!WorldIsReset(toSavedXf: true) || + (SketchHasChanges() && SketchMemoryScript.m_Instance.IsMemoryDirty())); + case GlobalCommands.SaveOptions: + case GlobalCommands.SaveNew: + case GlobalCommands.SaveGallery: + return SketchHasChanges(); + case GlobalCommands.SaveOnLocalChanges: + if (!SaveLoadScript.m_Instance.SceneFile.Valid) + { + // No save file, but something has changed. + return SketchHasChanges(); + } + else + { + if (SaveLoadScript.m_Instance.CanOverwriteSource) + { + // Save file, and it's our file. Whether we have changes is irrelevant. + return true; + } + // Save file, but it's not our file. Only make a copy if there are local changes. + return SketchMemoryScript.m_Instance.IsMemoryDirty(); + } + case GlobalCommands.UploadToGenericCloud: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || + VrAssetService.m_Instance.UploadProgress >= 1.0f || + VrAssetService.m_Instance.LastUploadFailed; + case GlobalCommands.SaveAndUpload: + return App.GoogleIdentity.LoggedIn && + (VrAssetService.m_Instance.UploadProgress <= 0.0f) && + IsCommandAvailable(GlobalCommands.UploadToGenericCloud); + case GlobalCommands.NewSketch: + return SketchHasChanges(); + case GlobalCommands.Credits: + case GlobalCommands.AshleysSketch: + return !SketchHasChanges() && !SketchMemoryScript.m_Instance.IsMemoryDirty(); + case GlobalCommands.Tiltasaurus: return TiltBrush.Tiltasaurus.m_Instance.TiltasaurusAvailable(); + case GlobalCommands.ExportRaw: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf); + case GlobalCommands.ResetAllPanels: return m_PanelManager.PanelsHaveBeenCustomized(); + case GlobalCommands.Duplicate: return ClipboardManager.Instance.CanCopy; + case GlobalCommands.ToggleGroupStrokesAndWidgets: return SelectionManager.m_Instance.SelectionCanBeGrouped; + case GlobalCommands.SaveModel: return SelectionManager.m_Instance.HasSelection; + case GlobalCommands.SummonMirror: + return PointerManager.m_Instance.CurrentSymmetryMode != + SymmetryMode.None; + case GlobalCommands.InvertSelection: + case GlobalCommands.FlipSelection: + return SelectionManager.m_Instance.HasSelection; + case GlobalCommands.SelectAll: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + m_WidgetManager.HasSelectableWidgets(); + case GlobalCommands.UnloadReferenceImageCatalog: + return ReferenceImageCatalog.m_Instance.AnyImageValid(); + case GlobalCommands.ToggleCameraPathPreview: + return m_WidgetManager.CanRecordCurrentCameraPath(); + case GlobalCommands.DeleteCameraPath: + return CameraPathCaptureRig.Enabled && m_WidgetManager.AnyActivePathHasAKnot(); + case GlobalCommands.ToggleCameraPathVisuals: + return m_WidgetManager.AnyActivePathHasAKnot(); + case GlobalCommands.GoogleDriveSync: + return App.GoogleIdentity.LoggedIn; + case GlobalCommands.RecordCameraPath: return m_WidgetManager.CameraPathsVisible; + } + return true; + } + + public bool SketchHasChanges() + { + if (SceneSettings.m_Instance.IsTransitioning) { return false; } + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + SceneSettings.m_Instance.EnvironmentChanged || + LightsControlScript.m_Instance.LightsChanged || + m_WidgetManager.ModelWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.LightWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.StencilWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.ImageWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.VideoWidgets.Any(w => w.gameObject.activeSelf) || + m_WidgetManager.AnyCameraPathWidgetsActive; + } + + public void OpenPanelOfType(BasePanel.PanelType type, TrTransform trSpawnXf) + { + m_PanelManager.OpenPanel(type, trSpawnXf); + EatGazeObjectInput(); + } + + public void RestoreFloatingPanels() + { + if (!m_SketchSurfacePanel.ActiveTool.HidePanels()) + { + m_PanelManager.RestoreHiddenPanels(); + } + } + + public void UpdateDraftingVisibility() + { + float value = 0; + switch (m_DraftingVisibility) + { + case DraftingVisibilityOption.Visible: + value = 1; + break; + case DraftingVisibilityOption.Transparent: + value = .5f; + break; + case DraftingVisibilityOption.Hidden: + value = 0; + break; + } + Shader.SetGlobalFloat("_DraftingVisibility01", value); + } + + private void ToggleProfiling() + { + if (Debug.isDebugBuild && ProfileDisplay.Instance != null) + { + ProfileDisplay.Instance.gameObject.SetActive(UnityEngine.Profiling.Profiler.enabled); + } + if (UnityEngine.Profiling.Profiler.enabled) + { + ProfilingManager.Instance.StopProfiling(); + } + else + { + ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProflingMode); + } + } + + private void DoAutoProfile() + { + StartCoroutine(DoProfiling()); + } + + private void DoAutoProfileAndQuit() + { + StartCoroutine(DoProfiling(andQuit: true)); + } + + private IEnumerator DoProfiling(bool andQuit = false) + { + TrTransform oldWandPose = TrTransform.FromTransform(InputManager.Wand.Geometry.transform); + TrTransform oldBrushPose = TrTransform.FromTransform(InputManager.Brush.Geometry.transform); + + App.AppState oldState = App.CurrentState; + App.Instance.SetDesiredState(App.AppState.AutoProfiling); + while (App.CurrentState != App.AppState.AutoProfiling) + { + yield return null; + } + + TrTransform camPose = App.Scene.Pose * SaveLoadScript.m_Instance.ReasonableThumbnail_SS; + camPose.ToTransform(App.VrSdk.GetVrCamera().transform); + float controllerDirection = App.UserConfig.Profiling.ShowControllers ? 1f : -1f; + Vector3 roffset = Camera.main.transform.right * 2f; + Vector3 fOffset = Camera.main.transform.forward * 4f * controllerDirection; + InputManager.Brush.Geometry.transform.position = Camera.main.transform.position + roffset + fOffset; + InputManager.Brush.Geometry.transform.rotation = Camera.main.transform.rotation; + InputManager.Wand.Geometry.transform.position = Camera.main.transform.position - roffset + fOffset; + InputManager.Wand.Geometry.transform.rotation = Camera.main.transform.rotation; + m_PanelManager.LockPanelsToController(); + + ProfilingManager.Instance.StartProfiling(App.UserConfig.Profiling.ProflingMode); + yield return new WaitForSeconds(App.UserConfig.Profiling.Duration); + ProfilingManager.Instance.StopProfiling(); + + if (App.UserConfig.Profiling.TakeScreenshot) + { + GameObject camObj = new GameObject("ScreenShotter"); + Camera cam = camObj.AddComponent(); + cam.CopyFrom(App.VrSdk.GetVrCamera()); + cam.stereoTargetEye = StereoTargetEyeMask.None; + cam.clearFlags = CameraClearFlags.SolidColor; + camPose.ToTransform(camObj.transform); + int res = App.UserConfig.Profiling.ScreenshotResolution; + RenderTexture renderTexture = RenderTexture.GetTemporary(res, res, 24); + try + { + cam.targetTexture = renderTexture; + cam.Render(); + RenderTexture prev = RenderTexture.active; + RenderTexture.active = renderTexture; + var texture = new Texture2D(res, res, TextureFormat.RGB24, false); + texture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); + RenderTexture.active = prev; + byte[] jpegBytes = texture.EncodeToJPG(); + string filename = + Path.GetFileNameWithoutExtension(SaveLoadScript.m_Instance.SceneFile.FullPath); + File.WriteAllBytes(Path.Combine(App.UserPath(), filename + ".jpg"), jpegBytes); + } + finally + { + Destroy(camObj); + RenderTexture.ReleaseTemporary(renderTexture); + } + } + + oldWandPose.ToTransform(InputManager.Wand.Geometry.transform); + oldBrushPose.ToTransform(InputManager.Brush.Geometry.transform); + App.Instance.SetDesiredState(oldState); + + if (andQuit) + { + QuitApp(); + } + } + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index 8b670c540a..53d8e18bf8 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -1,695 +1,695 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.Collections.Generic; -using Newtonsoft.Json; -using UnityEngine.Serialization; - -namespace TiltBrush -{ - - // Use "struct" instead of "class" to prohibit the use of default values, - // which won't work the way you want. Use Nullable<> instead. - [Serializable] - public class UserConfig - { - [Serializable] - public struct YouTubeConfig - { - public string ChannelID; - } - public YouTubeConfig YouTube; - - [Serializable] - public struct FlagsConfig - { - public bool DisableAudio; - public bool DisableAutosave; - [FormerlySerializedAs("DisablePoly")] public bool DisableIcosa; - public bool UnlockScale; - public bool GuideToggleVisiblityOnly; - public bool HighResolutionSnapshots; // Deprecated - public bool ShowDroppedFrames; - public bool LargeMeshSupport; - public bool EnableMonoscopicMode; - - private bool? m_DisableXrMode; - public bool DisableXrMode - { - get - { -#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX - return true; -#else - return m_DisableXrMode ?? false; -#endif - } - set { m_DisableXrMode = value; } - } - - public bool EnableApiRemoteCalls; - public bool EnableApiCorsHeaders; - - bool? m_AdvancedKeyboardShortcuts; - public bool AdvancedKeyboardShortcuts - { - get - { - return m_AdvancedKeyboardShortcuts ?? false; - } - set { m_AdvancedKeyboardShortcuts = value; } - } - - bool? m_PostEffectsOnCapture; - public bool PostEffectsOnCaptureValid { get { return m_PostEffectsOnCapture != null; } } - public bool PostEffectsOnCapture - { - get { return m_PostEffectsOnCapture ?? true; } - set { m_PostEffectsOnCapture = value; } - } - - // Spectator-cam watermark doesn't update on the fly - // The snapshot/video watermark does, though - bool? m_ShowWatermark; - public bool ShowWatermarkValid { get { return m_ShowWatermark != null; } } - public bool ShowWatermark - { - get { return m_ShowWatermark ?? true; } - set { m_ShowWatermark = value; } - } - - // This doesn't update on the fly - bool? m_ShowHeadset; - public bool ShowHeadset - { - get { return m_ShowHeadset ?? true; } - set { m_ShowHeadset = value; } - } - - // This doesn't update on the fly - bool? m_ShowControllers; - public bool ShowControllers - { - get { return m_ShowControllers ?? true; } - set { m_ShowControllers = value; } - } - - bool? m_SkipIntro; - public bool SkipIntro - { - get { return m_SkipIntro ?? false; } - set { m_SkipIntro = value; } - } - - int? m_SnapshotHeight; - public int SnapshotHeight - { - get { return m_SnapshotHeight ?? -1; } - set - { - int max = App.Config.PlatformConfig.MaxSnapshotDimension; - if (value > max) - { - OutputWindowScript.Error( - $"Snapshot height of {value} is not supported. Set to {max} pixels."); - m_SnapshotHeight = max; - } - else - { - m_SnapshotHeight = value; - } - } - } - - int? m_SnapshotWidth; - public int SnapshotWidth - { - get { return m_SnapshotWidth ?? -1; } - set - { - int max = App.Config.PlatformConfig.MaxSnapshotDimension; - if (value > max) - { - OutputWindowScript.Error( - $"Snapshot width of {value} is not supported. Set to {max} pixels."); - m_SnapshotWidth = max; - } - else - { - m_SnapshotWidth = value; - } - } - } - - float? m_Fov; - public bool FovValid { get { return m_Fov != null; } } - public float Fov - { - get { return m_Fov ?? CameraConfig.kFovDefault; } - set - { - m_Fov = UnityEngine.Mathf.Clamp(value, CameraConfig.kFovMin, CameraConfig.kFovMax); - if (m_Fov != value) - { - OutputWindowScript.Error(string.Format("FOV of '{0}' not supported.", value), - string.Format("FOV must be between {0} and {1}.\nFOV set to {2}.", - CameraConfig.kFovMin, CameraConfig.kFovMax, m_Fov)); - } - } - } - - private bool? m_PolyModelPreload; - public bool PolyModelPreloadValid => m_PolyModelPreload.HasValue; - public bool PolyModelPreload - { - get - { - return m_PolyModelPreload ?? App.PlatformConfig.EnablePolyPreload; - } - set { m_PolyModelPreload = value; } - } - } - - public FlagsConfig Flags; - - [Serializable] - public struct DemoConfig - { - public bool Enabled; - public uint? Duration; - public bool PublishAutomatically; - - private string m_PublishTitle; - public string PublishTitle - { - get { return m_PublishTitle ?? $"Sketch from {App.kAppDisplayName} demo"; } - set { m_PublishTitle = value; } - } - - private string m_PublishDescription; - public string PublishDescription - { - get { return m_PublishDescription ?? ""; } - set { m_PublishDescription = value; } - } - } - public DemoConfig Demo; - - [Serializable] - public struct BrushConfig - { - private Dictionary m_AddTagsToBrushes; - private Dictionary m_RemoveTagsFromBrushes; - private string[] m_IncludeTags; - private string[] m_ExcludeTags; - - public Dictionary AddTagsToBrushes - { - get => m_AddTagsToBrushes ?? (m_AddTagsToBrushes = new Dictionary()); - set => m_AddTagsToBrushes = value; - } - - public Dictionary RemoveTagsFromBrushes - { - get => m_RemoveTagsFromBrushes ?? (m_RemoveTagsFromBrushes = new Dictionary()); - set => m_RemoveTagsFromBrushes = value; - } - - public string[] IncludeTags - { - get - { - if (m_IncludeTags == null) - { - if (App.Config.GetIsExperimental()) - { - m_IncludeTags = new[] { "default", "experimental" }; - } - else - { - m_IncludeTags = new[] { "default" }; - } - } - return m_IncludeTags; - } - set => m_IncludeTags = value; - } - - public string[] ExcludeTags - { - get => m_ExcludeTags ?? (ExcludeTags = Array.Empty()); - set => m_ExcludeTags = value; - } - } - public BrushConfig Brushes; - - [Serializable] - public struct ImportConfig - { - bool? m_UseUnityGltf; - public bool UseUnityGltf - { - get { return m_UseUnityGltf ?? false; } - set { m_UseUnityGltf = value; } - } - } - - [Serializable] - public struct ExportConfig - { - bool? m_ExportBinaryFbx; - public bool ExportBinaryFbx - { - get { return m_ExportBinaryFbx ?? true; } - set { m_ExportBinaryFbx = value; } - } - - string m_ExportFbxVersion; - public string ExportFbxVersion - { - get { return m_ExportFbxVersion ?? "FBX201400"; } - set { m_ExportFbxVersion = value; } - } - - bool? m_ExportStrokeTimestamp; - public bool ExportStrokeTimestamp - { - get { return m_ExportStrokeTimestamp ?? true; } - set { m_ExportStrokeTimestamp = value; } - } - - // Used by UnityGLTF exporter - bool? m_ExportStrokeMetadata; - public bool ExportStrokeMetadata - { - get { return m_ExportStrokeMetadata ?? false; } - set { m_ExportStrokeMetadata = value; } - } - - // Used by UnityGLTF exporter - bool? m_KeepStrokes; - public bool KeepStrokes - { - get { return m_KeepStrokes ?? false; } - set { m_KeepStrokes = value; } - } - - // Used by UnityGLTF exporter - bool? m_KeepGroups; - public bool KeepGroups - { - get { return m_KeepGroups ?? true; } - set { m_KeepGroups = value; } - } - - // Used by UnityGLTF exporter - private bool? m_ExportEnvironment; - public bool ExportEnvironment - { - get { return m_ExportEnvironment ?? false; } - set { m_ExportEnvironment = value; } - } - - private Dictionary m_Formats; - [JsonProperty] - public Dictionary Formats - { - get { return m_Formats ?? null; } - set => m_Formats = value; - } - } - - public ImportConfig Import; - public ExportConfig Export; - - [Serializable] - public struct SharingConfig - { - // For Poly testing allow us to use a different API host and landing page URL. - [JsonProperty("VrAssetServiceHost")] public string VrAssetServiceHostOverride; - [JsonProperty("VrAssetServiceUrl")] public string VrAssetServiceUrlOverride; - } - public SharingConfig Sharing; - - [Serializable] - public struct IdentityConfig - { - public string Author; - } - public IdentityConfig User; - - [Serializable] - public struct VideoConfig - { - // Default values and limits. - private const float kDefaultFps = 30f; - private const float kDefaultOfflineFps = 60f; - private const float kMinFps = 1f; - private const float kMaxFps = 60f; - private const float kMaxOfflineFps = 1000f; - private const int kDefaultRes = 1280; - private const int kDefaultOfflineRes = 1920; - private const int kMinRes = 640; - private const int kMaxRes = 2560; - private const int kMaxOfflineRes = 8000; - private const string kDefaultContainer = "mp4"; - private static readonly List kSupportedContainers = new List - { - "mp4", "mov", "avi", "mpeg", "ogv", "ogx", - }; - - private const string kDefaultVideoEncoder = "h.264"; - private static readonly List kSupportedVideoEncoders = new List - { - "h.264", "h.265", - }; - - private const float kDefaultSmoothing = 0.98f; - private const float kDefaultOdsPoleCollapsing = 1.0f; - - float? m_FPS; - public float FPS - { - get { return m_FPS ?? kDefaultFps; } - set - { - m_FPS = UnityEngine.Mathf.Clamp(value, kMinFps, kMaxFps); - if (m_FPS != value) - { - OutputWindowScript.Error(string.Format("Video FPS of '{0}' not supported.", value), - string.Format("FPS must be between {0} and {1}.\nFPS set to {2}.", - UnityEngine.Mathf.RoundToInt(kMinFps), - UnityEngine.Mathf.RoundToInt(kMaxFps), m_FPS)); - } - } - } - - float? m_OfflineFps; - public float OfflineFPS - { - get { return m_OfflineFps ?? kDefaultOfflineFps; } - set - { - m_OfflineFps = UnityEngine.Mathf.Clamp(value, kMinFps, kMaxOfflineFps); - if (OfflineFPS != value) - { - OutputWindowScript.Error(string.Format("Offline Video FPS of '{0}' not supported.", value), - string.Format("FPS must be between {0} and {1}.\nFPS set to {2}.", - UnityEngine.Mathf.RoundToInt(kMinFps), - UnityEngine.Mathf.RoundToInt(kMaxOfflineFps), OfflineFPS)); - } - } - } - - float? m_Fov; - public bool FovValid { get { return m_Fov != null; } } - public float Fov - { - get { return m_Fov ?? CameraConfig.kFovDefault; } - set - { - m_Fov = UnityEngine.Mathf.Clamp(value, CameraConfig.kFovMin, CameraConfig.kFovMax); - if (m_Fov != value) - { - OutputWindowScript.Error(string.Format("FOV of '{0}' not supported.", value), - string.Format("FOV must be between {0} and {1}.\nFOV set to {2}.", - CameraConfig.kFovMin, CameraConfig.kFovMax, m_Fov)); - } - } - } - - int? m_Resolution; - public int Resolution - { - get { return m_Resolution ?? kDefaultRes; } - set - { - m_Resolution = UnityEngine.Mathf.Clamp(value, kMinRes, kMaxRes); - if (m_Resolution != value) - { - OutputWindowScript.Error(string.Format("Video Resolution of '{0}' not supported.", value), - string.Format("Resolution must be between {0} and {1}.\nResolution set to {2}.", - kMinRes, kMaxRes, m_Resolution)); - } - } - } - - int? m_OfflineResolution; - public int OfflineResolution - { - get { return m_OfflineResolution ?? kDefaultOfflineRes; } - set - { - m_OfflineResolution = UnityEngine.Mathf.Clamp(value, 640, kMaxOfflineRes); - if (m_OfflineResolution != value) - { - OutputWindowScript.Error(string.Format("Video Resolution of '{0}' not supported.", value), - string.Format("Resolution must be between {0} and {1}.\nResolution set to {2}.", - kMinRes, kMaxOfflineRes, m_OfflineResolution)); - } - } - } - - private bool? m_SaveCameraPath; - public bool SaveCameraPath - { - get { return m_SaveCameraPath ?? true; } - set - { - m_SaveCameraPath = value; - } - } - - string m_VideoEncoder; - public string Encoder - { - get { return m_VideoEncoder ?? kDefaultVideoEncoder; } - set - { - string lowered = value.ToLowerInvariant(); - if (kSupportedVideoEncoders.Contains(lowered)) - { - m_VideoEncoder = lowered; - } - else - { - m_VideoEncoder = null; - OutputWindowScript.Error( - $"VideoEncoder '{lowered}' not supported in {App.kConfigFileName}", - string.Format("Supported: {0}.\nContainer type set to {1}.", - string.Join(", ", kSupportedVideoEncoders.ToArray()), - kDefaultVideoEncoder)); - } - } - } - - string m_ContainerType; - public string ContainerType - { - get { return m_ContainerType ?? kDefaultContainer; } - set - { - string lowered = value.ToLowerInvariant(); - if (kSupportedContainers.Contains(lowered)) - { - m_ContainerType = lowered; - } - else - { - m_ContainerType = null; - OutputWindowScript.Error( - $"ContainerType '{lowered}' not supported in {App.kConfigFileName}", - string.Format("Supported: {0}.\nContainer type set to {1}.", - string.Join(", ", kSupportedContainers.ToArray()), kDefaultContainer)); - } - } - } - - float? m_CameraSmoothing; - public bool CameraSmoothingValid { get { return m_CameraSmoothing != null; } } - public float CameraSmoothing - { - get { return m_CameraSmoothing ?? kDefaultSmoothing; } - set - { - m_CameraSmoothing = UnityEngine.Mathf.Clamp01(value); - if (m_CameraSmoothing != value) - { - OutputWindowScript.Error(string.Format("Camera smoothing of '{0}' not supported.", value), - string.Format("Smoothing must be between 0 and 1.\nSmoothing set to {0}.", - m_CameraSmoothing)); - } - } - } - - float? m_OdsPoleCollapsing; - public float OdsPoleCollapsing - { - get { return m_OdsPoleCollapsing ?? kDefaultOdsPoleCollapsing; } - set - { - m_OdsPoleCollapsing = UnityEngine.Mathf.Clamp01(value); - if (m_OdsPoleCollapsing != value) - { - OutputWindowScript.Error(string.Format("Pole Collapsing of '{0}' not supported.", value), - string.Format("Smoothing must be between 0 and 1.\nPole Collapsing set to {0}.", - m_OdsPoleCollapsing)); - } - } - } - } - - public VideoConfig Video; - - // Settings for the QA testing panel. - [Serializable] - public struct TestingConfig - { - public Dictionary BrushReplacementMap - { - get - { - Dictionary results = new Dictionary(); - if (Config.IsExperimental) - { - if (string.IsNullOrEmpty(BrushReplacements)) - { - return results; - } - var replacements = BrushReplacements.Split(','); - foreach (string replacement in replacements) - { - string[] pair = replacement.Split('='); - if (pair.Length == 2) - { - if (pair[0] == "*") - { - Guid guid = new Guid(pair[1]); - foreach (var brush in App.Instance.m_Manifest.Brushes) - { - results.Add(brush.m_Guid, guid); - } - } - else - { - results.Add(new Guid(pair[0]), new Guid(pair[1])); - } - } - else - { - OutputWindowScript.Error("BrushReplacement should be of the form:\n" + - "brushguidA=brushguidB,brushguidC=brushguidD"); - } - } - } - return results; - } - } - - public bool Enabled; - public string InputFile; - public string OutputFile; - public bool ResetPromos; - public bool FirstRun; - public string BrushReplacements; - } - public TestingConfig Testing; - - // Profiling Settings - [Serializable] - public struct ProfilingConfig - { - public const int kDefaultScreenshotResolution = 1000; - public string[] ProfilingFunctions { get; private set; } - public ProfilingManager.Mode ProflingMode { get; private set; } - - public string Mode - { - set - { - try - { - ProflingMode = (ProfilingManager.Mode)Enum.Parse(typeof(ProfilingManager.Mode), value); - } - catch (ArgumentException) - { - OutputWindowScript.Error(string.Format("'{0}' is not a valid profiling mode.", value)); - } - } - } - - public string Functions - { - set { ProfilingFunctions = value.Split(','); } - } - - public string ProfileName; - public string ProfileFilename; - public string SketchToLoad; - public bool AutoProfile; - public bool ShowControllers; - - public float Duration - { - get { return m_Duration.HasValue ? m_Duration.Value : 5f; } - set { m_Duration = value; } - } - private float? m_Duration; - - public bool Csv; - - // Any invalid quality level is ignored. - private int? m_QualityLevel; - public int QualityLevel - { - get { return m_QualityLevel.HasValue ? m_QualityLevel.Value : -1; } - set { m_QualityLevel = value; } - } - - public float ViewportScaling { get; set; } - public float EyeTextureScaling { get; set; } - public int GlobalMaximumLOD { get; set; } - public int MsaaLevel { get; set; } - public bool TakeScreenshot { get; set; } - private int? m_screenshotResolution; - - public int ScreenshotResolution - { - get - { - return m_screenshotResolution.HasValue ? - m_screenshotResolution.Value : kDefaultScreenshotResolution; - } - set { m_screenshotResolution = value; } - } - public bool PerfgateOutput { get; set; } - - private float? m_StrokeSimplification; - - public float StrokeSimplification - { - get { return m_StrokeSimplification.HasValue ? m_StrokeSimplification.Value : 0f; } - set { m_StrokeSimplification = value; } - } - - public bool HasStrokeSimplification - { - get { return m_StrokeSimplification.HasValue; } - } - } - public ProfilingConfig Profiling; - } - -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using UnityEngine.Serialization; + +namespace TiltBrush +{ + + // Use "struct" instead of "class" to prohibit the use of default values, + // which won't work the way you want. Use Nullable<> instead. + [Serializable] + public class UserConfig + { + [Serializable] + public struct YouTubeConfig + { + public string ChannelID; + } + public YouTubeConfig YouTube; + + [Serializable] + public struct FlagsConfig + { + public bool DisableAudio; + public bool DisableAutosave; + [FormerlySerializedAs("DisablePoly")] public bool DisableIcosa; + public bool UnlockScale; + public bool GuideToggleVisiblityOnly; + public bool HighResolutionSnapshots; // Deprecated + public bool ShowDroppedFrames; + public bool LargeMeshSupport; + public bool EnableMonoscopicMode; + + private bool? m_DisableXrMode; + public bool DisableXrMode + { + get + { +#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + return true; +#else + return m_DisableXrMode ?? false; +#endif + } + set { m_DisableXrMode = value; } + } + + public bool EnableApiRemoteCalls; + public bool EnableApiCorsHeaders; + + bool? m_AdvancedKeyboardShortcuts; + public bool AdvancedKeyboardShortcuts + { + get + { + return m_AdvancedKeyboardShortcuts ?? false; + } + set { m_AdvancedKeyboardShortcuts = value; } + } + + bool? m_PostEffectsOnCapture; + public bool PostEffectsOnCaptureValid { get { return m_PostEffectsOnCapture != null; } } + public bool PostEffectsOnCapture + { + get { return m_PostEffectsOnCapture ?? true; } + set { m_PostEffectsOnCapture = value; } + } + + // Spectator-cam watermark doesn't update on the fly + // The snapshot/video watermark does, though + bool? m_ShowWatermark; + public bool ShowWatermarkValid { get { return m_ShowWatermark != null; } } + public bool ShowWatermark + { + get { return m_ShowWatermark ?? true; } + set { m_ShowWatermark = value; } + } + + // This doesn't update on the fly + bool? m_ShowHeadset; + public bool ShowHeadset + { + get { return m_ShowHeadset ?? true; } + set { m_ShowHeadset = value; } + } + + // This doesn't update on the fly + bool? m_ShowControllers; + public bool ShowControllers + { + get { return m_ShowControllers ?? true; } + set { m_ShowControllers = value; } + } + + bool? m_SkipIntro; + public bool SkipIntro + { + get { return m_SkipIntro ?? false; } + set { m_SkipIntro = value; } + } + + int? m_SnapshotHeight; + public int SnapshotHeight + { + get { return m_SnapshotHeight ?? -1; } + set + { + int max = App.Config.PlatformConfig.MaxSnapshotDimension; + if (value > max) + { + OutputWindowScript.Error( + $"Snapshot height of {value} is not supported. Set to {max} pixels."); + m_SnapshotHeight = max; + } + else + { + m_SnapshotHeight = value; + } + } + } + + int? m_SnapshotWidth; + public int SnapshotWidth + { + get { return m_SnapshotWidth ?? -1; } + set + { + int max = App.Config.PlatformConfig.MaxSnapshotDimension; + if (value > max) + { + OutputWindowScript.Error( + $"Snapshot width of {value} is not supported. Set to {max} pixels."); + m_SnapshotWidth = max; + } + else + { + m_SnapshotWidth = value; + } + } + } + + float? m_Fov; + public bool FovValid { get { return m_Fov != null; } } + public float Fov + { + get { return m_Fov ?? CameraConfig.kFovDefault; } + set + { + m_Fov = UnityEngine.Mathf.Clamp(value, CameraConfig.kFovMin, CameraConfig.kFovMax); + if (m_Fov != value) + { + OutputWindowScript.Error(string.Format("FOV of '{0}' not supported.", value), + string.Format("FOV must be between {0} and {1}.\nFOV set to {2}.", + CameraConfig.kFovMin, CameraConfig.kFovMax, m_Fov)); + } + } + } + + private bool? m_IcosaModelPreload; + public bool PolyModelPreloadValid => m_IcosaModelPreload.HasValue; + public bool IcosaModelPreload + { + get + { + return m_IcosaModelPreload ?? App.PlatformConfig.EnablePolyPreload; + } + set { m_IcosaModelPreload = value; } + } + } + + public FlagsConfig Flags; + + [Serializable] + public struct DemoConfig + { + public bool Enabled; + public uint? Duration; + public bool PublishAutomatically; + + private string m_PublishTitle; + public string PublishTitle + { + get { return m_PublishTitle ?? $"Sketch from {App.kAppDisplayName} demo"; } + set { m_PublishTitle = value; } + } + + private string m_PublishDescription; + public string PublishDescription + { + get { return m_PublishDescription ?? ""; } + set { m_PublishDescription = value; } + } + } + public DemoConfig Demo; + + [Serializable] + public struct BrushConfig + { + private Dictionary m_AddTagsToBrushes; + private Dictionary m_RemoveTagsFromBrushes; + private string[] m_IncludeTags; + private string[] m_ExcludeTags; + + public Dictionary AddTagsToBrushes + { + get => m_AddTagsToBrushes ?? (m_AddTagsToBrushes = new Dictionary()); + set => m_AddTagsToBrushes = value; + } + + public Dictionary RemoveTagsFromBrushes + { + get => m_RemoveTagsFromBrushes ?? (m_RemoveTagsFromBrushes = new Dictionary()); + set => m_RemoveTagsFromBrushes = value; + } + + public string[] IncludeTags + { + get + { + if (m_IncludeTags == null) + { + if (App.Config.GetIsExperimental()) + { + m_IncludeTags = new[] { "default", "experimental" }; + } + else + { + m_IncludeTags = new[] { "default" }; + } + } + return m_IncludeTags; + } + set => m_IncludeTags = value; + } + + public string[] ExcludeTags + { + get => m_ExcludeTags ?? (ExcludeTags = Array.Empty()); + set => m_ExcludeTags = value; + } + } + public BrushConfig Brushes; + + [Serializable] + public struct ImportConfig + { + bool? m_UseUnityGltf; + public bool UseUnityGltf + { + get { return m_UseUnityGltf ?? false; } + set { m_UseUnityGltf = value; } + } + } + + [Serializable] + public struct ExportConfig + { + bool? m_ExportBinaryFbx; + public bool ExportBinaryFbx + { + get { return m_ExportBinaryFbx ?? true; } + set { m_ExportBinaryFbx = value; } + } + + string m_ExportFbxVersion; + public string ExportFbxVersion + { + get { return m_ExportFbxVersion ?? "FBX201400"; } + set { m_ExportFbxVersion = value; } + } + + bool? m_ExportStrokeTimestamp; + public bool ExportStrokeTimestamp + { + get { return m_ExportStrokeTimestamp ?? true; } + set { m_ExportStrokeTimestamp = value; } + } + + // Used by UnityGLTF exporter + bool? m_ExportStrokeMetadata; + public bool ExportStrokeMetadata + { + get { return m_ExportStrokeMetadata ?? false; } + set { m_ExportStrokeMetadata = value; } + } + + // Used by UnityGLTF exporter + bool? m_KeepStrokes; + public bool KeepStrokes + { + get { return m_KeepStrokes ?? false; } + set { m_KeepStrokes = value; } + } + + // Used by UnityGLTF exporter + bool? m_KeepGroups; + public bool KeepGroups + { + get { return m_KeepGroups ?? true; } + set { m_KeepGroups = value; } + } + + // Used by UnityGLTF exporter + private bool? m_ExportEnvironment; + public bool ExportEnvironment + { + get { return m_ExportEnvironment ?? false; } + set { m_ExportEnvironment = value; } + } + + private Dictionary m_Formats; + [JsonProperty] + public Dictionary Formats + { + get { return m_Formats ?? null; } + set => m_Formats = value; + } + } + + public ImportConfig Import; + public ExportConfig Export; + + [Serializable] + public struct SharingConfig + { + // For Poly testing allow us to use a different API host and landing page URL. + [JsonProperty("VrAssetServiceHost")] public string VrAssetServiceHostOverride; + [JsonProperty("VrAssetServiceUrl")] public string VrAssetServiceUrlOverride; + } + public SharingConfig Sharing; + + [Serializable] + public struct IdentityConfig + { + public string Author; + } + public IdentityConfig User; + + [Serializable] + public struct VideoConfig + { + // Default values and limits. + private const float kDefaultFps = 30f; + private const float kDefaultOfflineFps = 60f; + private const float kMinFps = 1f; + private const float kMaxFps = 60f; + private const float kMaxOfflineFps = 1000f; + private const int kDefaultRes = 1280; + private const int kDefaultOfflineRes = 1920; + private const int kMinRes = 640; + private const int kMaxRes = 2560; + private const int kMaxOfflineRes = 8000; + private const string kDefaultContainer = "mp4"; + private static readonly List kSupportedContainers = new List + { + "mp4", "mov", "avi", "mpeg", "ogv", "ogx", + }; + + private const string kDefaultVideoEncoder = "h.264"; + private static readonly List kSupportedVideoEncoders = new List + { + "h.264", "h.265", + }; + + private const float kDefaultSmoothing = 0.98f; + private const float kDefaultOdsPoleCollapsing = 1.0f; + + float? m_FPS; + public float FPS + { + get { return m_FPS ?? kDefaultFps; } + set + { + m_FPS = UnityEngine.Mathf.Clamp(value, kMinFps, kMaxFps); + if (m_FPS != value) + { + OutputWindowScript.Error(string.Format("Video FPS of '{0}' not supported.", value), + string.Format("FPS must be between {0} and {1}.\nFPS set to {2}.", + UnityEngine.Mathf.RoundToInt(kMinFps), + UnityEngine.Mathf.RoundToInt(kMaxFps), m_FPS)); + } + } + } + + float? m_OfflineFps; + public float OfflineFPS + { + get { return m_OfflineFps ?? kDefaultOfflineFps; } + set + { + m_OfflineFps = UnityEngine.Mathf.Clamp(value, kMinFps, kMaxOfflineFps); + if (OfflineFPS != value) + { + OutputWindowScript.Error(string.Format("Offline Video FPS of '{0}' not supported.", value), + string.Format("FPS must be between {0} and {1}.\nFPS set to {2}.", + UnityEngine.Mathf.RoundToInt(kMinFps), + UnityEngine.Mathf.RoundToInt(kMaxOfflineFps), OfflineFPS)); + } + } + } + + float? m_Fov; + public bool FovValid { get { return m_Fov != null; } } + public float Fov + { + get { return m_Fov ?? CameraConfig.kFovDefault; } + set + { + m_Fov = UnityEngine.Mathf.Clamp(value, CameraConfig.kFovMin, CameraConfig.kFovMax); + if (m_Fov != value) + { + OutputWindowScript.Error(string.Format("FOV of '{0}' not supported.", value), + string.Format("FOV must be between {0} and {1}.\nFOV set to {2}.", + CameraConfig.kFovMin, CameraConfig.kFovMax, m_Fov)); + } + } + } + + int? m_Resolution; + public int Resolution + { + get { return m_Resolution ?? kDefaultRes; } + set + { + m_Resolution = UnityEngine.Mathf.Clamp(value, kMinRes, kMaxRes); + if (m_Resolution != value) + { + OutputWindowScript.Error(string.Format("Video Resolution of '{0}' not supported.", value), + string.Format("Resolution must be between {0} and {1}.\nResolution set to {2}.", + kMinRes, kMaxRes, m_Resolution)); + } + } + } + + int? m_OfflineResolution; + public int OfflineResolution + { + get { return m_OfflineResolution ?? kDefaultOfflineRes; } + set + { + m_OfflineResolution = UnityEngine.Mathf.Clamp(value, 640, kMaxOfflineRes); + if (m_OfflineResolution != value) + { + OutputWindowScript.Error(string.Format("Video Resolution of '{0}' not supported.", value), + string.Format("Resolution must be between {0} and {1}.\nResolution set to {2}.", + kMinRes, kMaxOfflineRes, m_OfflineResolution)); + } + } + } + + private bool? m_SaveCameraPath; + public bool SaveCameraPath + { + get { return m_SaveCameraPath ?? true; } + set + { + m_SaveCameraPath = value; + } + } + + string m_VideoEncoder; + public string Encoder + { + get { return m_VideoEncoder ?? kDefaultVideoEncoder; } + set + { + string lowered = value.ToLowerInvariant(); + if (kSupportedVideoEncoders.Contains(lowered)) + { + m_VideoEncoder = lowered; + } + else + { + m_VideoEncoder = null; + OutputWindowScript.Error( + $"VideoEncoder '{lowered}' not supported in {App.kConfigFileName}", + string.Format("Supported: {0}.\nContainer type set to {1}.", + string.Join(", ", kSupportedVideoEncoders.ToArray()), + kDefaultVideoEncoder)); + } + } + } + + string m_ContainerType; + public string ContainerType + { + get { return m_ContainerType ?? kDefaultContainer; } + set + { + string lowered = value.ToLowerInvariant(); + if (kSupportedContainers.Contains(lowered)) + { + m_ContainerType = lowered; + } + else + { + m_ContainerType = null; + OutputWindowScript.Error( + $"ContainerType '{lowered}' not supported in {App.kConfigFileName}", + string.Format("Supported: {0}.\nContainer type set to {1}.", + string.Join(", ", kSupportedContainers.ToArray()), kDefaultContainer)); + } + } + } + + float? m_CameraSmoothing; + public bool CameraSmoothingValid { get { return m_CameraSmoothing != null; } } + public float CameraSmoothing + { + get { return m_CameraSmoothing ?? kDefaultSmoothing; } + set + { + m_CameraSmoothing = UnityEngine.Mathf.Clamp01(value); + if (m_CameraSmoothing != value) + { + OutputWindowScript.Error(string.Format("Camera smoothing of '{0}' not supported.", value), + string.Format("Smoothing must be between 0 and 1.\nSmoothing set to {0}.", + m_CameraSmoothing)); + } + } + } + + float? m_OdsPoleCollapsing; + public float OdsPoleCollapsing + { + get { return m_OdsPoleCollapsing ?? kDefaultOdsPoleCollapsing; } + set + { + m_OdsPoleCollapsing = UnityEngine.Mathf.Clamp01(value); + if (m_OdsPoleCollapsing != value) + { + OutputWindowScript.Error(string.Format("Pole Collapsing of '{0}' not supported.", value), + string.Format("Smoothing must be between 0 and 1.\nPole Collapsing set to {0}.", + m_OdsPoleCollapsing)); + } + } + } + } + + public VideoConfig Video; + + // Settings for the QA testing panel. + [Serializable] + public struct TestingConfig + { + public Dictionary BrushReplacementMap + { + get + { + Dictionary results = new Dictionary(); + if (Config.IsExperimental) + { + if (string.IsNullOrEmpty(BrushReplacements)) + { + return results; + } + var replacements = BrushReplacements.Split(','); + foreach (string replacement in replacements) + { + string[] pair = replacement.Split('='); + if (pair.Length == 2) + { + if (pair[0] == "*") + { + Guid guid = new Guid(pair[1]); + foreach (var brush in App.Instance.m_Manifest.Brushes) + { + results.Add(brush.m_Guid, guid); + } + } + else + { + results.Add(new Guid(pair[0]), new Guid(pair[1])); + } + } + else + { + OutputWindowScript.Error("BrushReplacement should be of the form:\n" + + "brushguidA=brushguidB,brushguidC=brushguidD"); + } + } + } + return results; + } + } + + public bool Enabled; + public string InputFile; + public string OutputFile; + public bool ResetPromos; + public bool FirstRun; + public string BrushReplacements; + } + public TestingConfig Testing; + + // Profiling Settings + [Serializable] + public struct ProfilingConfig + { + public const int kDefaultScreenshotResolution = 1000; + public string[] ProfilingFunctions { get; private set; } + public ProfilingManager.Mode ProflingMode { get; private set; } + + public string Mode + { + set + { + try + { + ProflingMode = (ProfilingManager.Mode)Enum.Parse(typeof(ProfilingManager.Mode), value); + } + catch (ArgumentException) + { + OutputWindowScript.Error(string.Format("'{0}' is not a valid profiling mode.", value)); + } + } + } + + public string Functions + { + set { ProfilingFunctions = value.Split(','); } + } + + public string ProfileName; + public string ProfileFilename; + public string SketchToLoad; + public bool AutoProfile; + public bool ShowControllers; + + public float Duration + { + get { return m_Duration.HasValue ? m_Duration.Value : 5f; } + set { m_Duration = value; } + } + private float? m_Duration; + + public bool Csv; + + // Any invalid quality level is ignored. + private int? m_QualityLevel; + public int QualityLevel + { + get { return m_QualityLevel.HasValue ? m_QualityLevel.Value : -1; } + set { m_QualityLevel = value; } + } + + public float ViewportScaling { get; set; } + public float EyeTextureScaling { get; set; } + public int GlobalMaximumLOD { get; set; } + public int MsaaLevel { get; set; } + public bool TakeScreenshot { get; set; } + private int? m_screenshotResolution; + + public int ScreenshotResolution + { + get + { + return m_screenshotResolution.HasValue ? + m_screenshotResolution.Value : kDefaultScreenshotResolution; + } + set { m_screenshotResolution = value; } + } + public bool PerfgateOutput { get; set; } + + private float? m_StrokeSimplification; + + public float StrokeSimplification + { + get { return m_StrokeSimplification.HasValue ? m_StrokeSimplification.Value : 0f; } + set { m_StrokeSimplification = value; } + } + + public bool HasStrokeSimplification + { + get { return m_StrokeSimplification.HasValue; } + } + } + public ProfilingConfig Profiling; + } + +} // namespace TiltBrush diff --git a/Assets/Scripts/Util/ImageUtils.cs b/Assets/Scripts/Util/ImageUtils.cs index 926e8459fb..13783d3d45 100644 --- a/Assets/Scripts/Util/ImageUtils.cs +++ b/Assets/Scripts/Util/ImageUtils.cs @@ -1,467 +1,467 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using System.IO; -using System.Threading.Tasks; -using UnityEngine; -using UnityEngine.Networking; - -namespace TiltBrush -{ - public class ImageLoadError : Exception - { - public ImageLoadError(string message) : base(message) { } - public ImageLoadError(string fmt, params System.Object[] args) - : base(string.Format(fmt, args)) - { - } - public ImageLoadError(Exception inner, string fmt, params System.Object[] args) - : base(string.Format(fmt, args), inner) - { - } - } - - static public class ImageUtils - { - const byte kJpegMarkerSOF0 = (byte)0xc0; - const byte kJpegMarkerSOF2 = (byte)0xc2; - - /// Raises TiltBrush.ImageDecodeError if the data is not in a supported image format. - /// Raises TiltBrush.ImageTooLargeError if abortSize > 0 and the image width or height - /// are larger than abortSize. - static public RawImage FromImageData( - byte[] data, string filename, int abortDimension = -1) - { - if (abortDimension > 0) - { - ValidateDimensions(data, abortDimension); - } - - if (IsJpeg(data)) - { - return FromJpeg(data, filename); - } - else if (IsPng(data)) - { - return FromPng(data, filename); - } - else - { - throw new ImageLoadError("Unknown/unsupported format"); - } - } - - static public void ValidateDimensions(byte[] data, int maxDimension) - { - int imageWidth = 0; - int imageHeight = 0; - using (var stream = new MemoryStream(data)) - { - if (IsJpeg(data)) - { - bool ok = GetJpegWidthAndHeight(stream, out imageWidth, out imageHeight); - if (!ok) - { - throw new ImageLoadError("Failed to get dimensions from jpeg"); - } - } - else if (IsPng(data)) - { - GetPngWidthAndHeight(stream, out imageWidth, out imageHeight); - } - else - { - throw new ImageLoadError("Can't get dimensions from unknown image format!"); - } - } - - // Cast to long as maxDimension is big enough on desktop to overflow - if (imageWidth * imageHeight > ((long)maxDimension * (long)maxDimension)) - { - throw new ImageLoadError( - String.Format("Image dimensions {0}x{1} are greater than max dimensions of {2}x{2}!", - imageWidth, imageHeight, maxDimension)); - } - } - - /// Returns true if the data looks like it might be a jpeg - static public bool IsJpeg(byte[] data) - { - return ( - data.Length >= 3 && - data[0] == 0xff && data[1] == 0xd8 && /* SOI tag */ - data[2] == 0xff /* Start of some other tag */); - } - - // [MustUseReturnValue] -- Unity doesn't seem to contain this annotation?! - // Should probably return (int, int)? instead - static public bool GetJpegWidthAndHeight(Stream stream, - out int width, out int height) - { - width = 0; - height = 0; - - byte marker = 0; - byte[] twoBytes = new byte[2]; - JPEGBinaryReader reader = new JPEGBinaryReader(stream); - - // Run through all the markers in the jpeg and look for the SOF, or start of frames. - // Most .jpegs have one SOF, but some have many. - // We're concerned with the size of this image, so take the largest of any frames we find. - while (true) - { - switch (marker) - { - case kJpegMarkerSOF0: - case kJpegMarkerSOF2: - // Skip the frame length. - reader.ReadInt16(); - // Bits percision. - reader.ReadByte(); - // Scan lines (height) - twoBytes = reader.ReadBytes(2); - height = Mathf.Max(height, (twoBytes[0] << 8) + twoBytes[1]); - // Scan samples per line (width) - twoBytes = reader.ReadBytes(2); - width = Mathf.Max(width, (twoBytes[0] << 8) + twoBytes[1]); - return true; - } - - try - { - marker = reader.GetNextMarker(); - } - catch (System.IO.EndOfStreamException) - { - break; /* done reading the file */ - } - } - return false; - } - - /// Raises TiltBrush.ImageDecodeError if the data is not a valid jpeg - static public RawImage FromJpeg(byte[] jpegData, string filename) - { - using (var stream = new MemoryStream(jpegData)) - { - FluxJpeg.Core.Image image; - try - { - FluxJpeg.Core.DecodedJpeg decoded = - new FluxJpeg.Core.Decoder.JpegDecoder(stream).Decode(); - image = decoded.Image; - image.ChangeColorSpace(FluxJpeg.Core.ColorSpace.RGB); - } - catch (Exception e) - { - // Library throws bare Exception :-P - throw new ImageLoadError(e, "JPEG decode error"); - } - - var reds = image.Raster[0]; - var greens = image.Raster[1]; - var blues = image.Raster[2]; - int _width = image.Width; - int _height = image.Height; - var buf = new Color32[_width * _height]; - unsafe - { - unchecked - { - fixed (Color32* pBuf = buf) - { - byte* cur = (byte*)pBuf; - for (int y = _height - 1; y >= 0; --y) - { - for (int x = 0; x < _width; ++x) - { - cur[0] = reds[x, y]; - cur[1] = greens[x, y]; - cur[2] = blues[x, y]; - cur[3] = 0xff; - cur += 4; - } - } - } - } - } - return new RawImage - { - ColorData = buf, - ColorWidth = _width, - ColorHeight = _height, - ColorAspect = _height == 0 ? 1f : ((float)_width / _height) - }; - } - } - - /// Returns true if the data look slike it might be a png - static public bool IsPng(byte[] data) - { - return ( - data.Length >= 8 && - data[0] == 0x89 && - data[1] == 'P' && data[2] == 'N' && data[3] == 'G' && // "PNG" - data[4] == '\r' && data[5] == '\n' && // CRLF - data[6] == 0x1a && // DOS EOF - data[7] == '\n'); - } - - static public void GetPngWidthAndHeight(System.IO.Stream stream, - out int width, out int height) - { - BinaryReader br = new BinaryReader(stream); - // Skip signature. - br.ReadBytes(16); - - byte[] fourBytes = new byte[4]; - fourBytes = br.ReadBytes(4); - width = (fourBytes[0] << 24) + (fourBytes[1] << 16) + - (fourBytes[2] << 8) + fourBytes[3]; - - fourBytes = br.ReadBytes(4); - height = (fourBytes[0] << 24) + (fourBytes[1] << 16) + - (fourBytes[2] << 8) + fourBytes[3]; - } - - /// Raises TiltBrush.ImageDecodeError if the data is not a valid png. - static public RawImage FromPng(byte[] pngData, string filename) - { - try - { - return _FromPng(pngData, filename); - } - catch (Exception e) - { - // There are a ton of different exceptions it can throw, and there - // is no convenient base class - throw new ImageLoadError(e, "PNG decode error"); - } - } - - static RawImage _FromPng(byte[] pngData, string filename) - { - // TODO: test the untested branches - using (var stream = new MemoryStream(pngData)) - { - var png = new Hjg.Pngcs.PngReader(stream, filename); - png.SetUnpackedMode(true); - - int rows = png.ImgInfo.Rows; - int cols = png.ImgInfo.Cols; - int chans = png.ImgInfo.Channels; - - Color32[] buf = new Color32[rows * cols]; - - if (png.ImgInfo.Indexed) - { - var plte = png.GetMetadata().GetPLTE(); - - byte[] alphas = new byte[256]; - { - for (int i = 0; i < 256; ++i) - { - alphas[i] = 255; - } - var trns = png.GetMetadata().GetTRNS(); - if (trns != null) - { - // might be smaller than 256 - int[] palette = trns.GetPalletteAlpha(); - for (int i = 0; i < palette.Length; ++i) - { - alphas[i] = (byte)palette[i]; - } - } - } - - byte[] line = null; - int[] rgb = new int[3]; - for (int r = 0; r < rows; ++r) - { - line = png.ReadRowByte(line, r); - for (int c = 0; c < cols; ++c) - { - int iEntry = line[c]; - plte.GetEntryRgb(iEntry, rgb); - byte alpha = alphas[iEntry]; - buf[(rows - r - 1) * cols + c] = new Color32((byte)rgb[0], (byte)rgb[1], (byte)rgb[2], alpha); - } - } - } - else if (png.ImgInfo.Greyscale && !png.ImgInfo.Alpha) - { - Debug.Assert(chans == 1); - Debug.Assert(png.ImgInfo.BitDepth <= 8, "Unsupported: 16-bit grey"); - - byte[] line = null; - for (int r = 0; r < rows; ++r) - { - line = png.ReadRowByte(line, r); - for (int c = 0; c < cols; ++c) - { - buf[(rows - r - 1) * cols + c] = new Color32(line[c], line[c], line[c], 255); - } - } - } - else if (png.ImgInfo.Greyscale && png.ImgInfo.Alpha) - { - Debug.Assert(chans == 2); - Debug.Assert(png.ImgInfo.BitDepth <= 8, "Unsupported: 16-bit grey"); - Debug.Assert(false, "currently unsupported: grayscale alpha"); - - byte[] line = null; - for (int r = 0; r < rows; ++r) - { - line = png.ReadRowByte(line, r); - for (int c = 0; c < cols; ++c) - { - int i = c * chans; - buf[(rows - r - 1) * cols + c] = new Color32(line[i], line[i], line[i], line[i + 1]); - } - } - } - else if (chans == 3 || chans == 4) - { - // Can use ReadRowByte() if bitDepth <= 8 - if (png.ImgInfo.BitDepth <= 8) - { - byte[] line = null; - for (int r = 0; r < rows; ++r) - { - line = png.ReadRowByte(line, r); - for (int c = 0; c < cols; ++c) - { - int ichan = c * chans; - buf[(rows - r - 1) * cols + c] = new Color32( - line[ichan], line[ichan + 1], line[ichan + 2], - (chans == 3) ? (byte)0xff : line[ichan + 3]); - } - } - } - else - { - Debug.Assert(png.ImgInfo.BitDepth == 16); - Debug.Assert(false, "Untested: 16-bit rgb"); - var lines = png.ReadRowsInt(0, png.ImgInfo.Rows, 1); - for (int r = 0; r < rows; ++r) - { - int[] line = lines.Scanlines[r]; - for (int c = 0; c < cols; ++c) - { - int ichan = c * chans; - buf[(rows - r - 1) * cols + c] = new Color32( - (byte)line[ichan], (byte)line[ichan + 1], (byte)line[ichan + 2], - (byte)((chans == 3) ? 0xff : line[ichan + 3])); - } - } - } - } - else - { - Debug.Assert(false, "Weird format"); - } - - return new RawImage - { - ColorData = buf, - ColorWidth = cols, - ColorHeight = rows, - ColorAspect = (rows == 0) ? 1f : ((float)cols / rows) - }; - } - } - - /// Fetches the url and returns a Texture2D or null. - public static async Task DownloadTextureAsync(string uri) - { - using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(uri)) - { - await www.SendWebRequest(); - // If we don't do this, downloadHandler.data sometimes (always?) returns garbage - while (!www.downloadHandler.isDone) - { - await Awaiters.NextFrame; - } - - if (www.isNetworkError || www.responseCode >= 400) - { - Debug.LogErrorFormat("ImageUtils: Error downloading {0}, error {1}", uri, www.responseCode); - return null; - } - - - // Try LoadImage first, because it's faster - { - Texture2D dest = new Texture2D(2, 2); - if (dest.LoadImage(www.downloadHandler.data)) - { - if (dest.width == 8) - { - // Detect "false success" from LoadImage -- if we see this at all, we should - // consider falling through to ThreadedImageReader? - Debug.LogError("Got 8x8 from LoadImage!"); - } - return dest; - } - else - { - Debug.LogError("DownloadTextureAsync: LoadImage failed"); // Unexpected - } - UnityEngine.Object.Destroy(dest); - } - - // This case probably won't get hit any more - { - RawImage im; - try - { - im = await new ThreadedImageReader(www.downloadHandler.data, uri); - } - catch (ImageLoadError e) - { - Console.WriteLine($"ImageUtils: cannot hand-create {uri}: {e}"); - im = null; - } - if (im != null) - { - Console.WriteLine($"ImageUtils: hand-creating {uri}: {www.isDone}"); - Texture2D dest = new Texture2D(im.ColorWidth, im.ColorHeight, TextureFormat.RGBA32, true); - dest.SetPixels32(im.ColorData); - dest.Apply(); - Console.WriteLine("ImageUtils: hand-created icon dimensions: " + - $"{dest.width}, {dest.height}"); - return dest; - } - } - - // b/37256058: Unity's DownloadHandlerTexture is buggy in 5.4.4 and returns Textures - // which hard-crash Unity (for some URLs, sometimes). - // That bug may be fixed by now, but since it doesn't show up in analytics and is - // quite severeTry creating the texture ourselves. - // Fall through to doing it the old, maybe b/37256058 buggy way. - // This case should get hit even less than the previous case. - { - Console.WriteLine("ImageUtils: downloaded and handled {0}: {1} {2}", - uri, www.isDone, www.downloadHandler.isDone); - // b/62269743: sometimes this returns an ugly 8x8 texture. - Texture2D dest = DownloadHandlerTexture.GetContent(www); - Console.WriteLine($"ImageUtils: icon dimensions: {dest.width}, {dest.height}"); - return dest; - } - } - } - } // ImageUtils -} // TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; + +namespace TiltBrush +{ + public class ImageLoadError : Exception + { + public ImageLoadError(string message) : base(message) { } + public ImageLoadError(string fmt, params System.Object[] args) + : base(string.Format(fmt, args)) + { + } + public ImageLoadError(Exception inner, string fmt, params System.Object[] args) + : base(string.Format(fmt, args), inner) + { + } + } + + static public class ImageUtils + { + const byte kJpegMarkerSOF0 = (byte)0xc0; + const byte kJpegMarkerSOF2 = (byte)0xc2; + + /// Raises TiltBrush.ImageDecodeError if the data is not in a supported image format. + /// Raises TiltBrush.ImageTooLargeError if abortSize > 0 and the image width or height + /// are larger than abortSize. + static public RawImage FromImageData( + byte[] data, string filename, int abortDimension = -1) + { + if (abortDimension > 0) + { + ValidateDimensions(data, abortDimension); + } + + if (IsJpeg(data)) + { + return FromJpeg(data, filename); + } + else if (IsPng(data)) + { + return FromPng(data, filename); + } + else + { + throw new ImageLoadError("Unknown/unsupported format"); + } + } + + static public void ValidateDimensions(byte[] data, int maxDimension) + { + int imageWidth = 0; + int imageHeight = 0; + using (var stream = new MemoryStream(data)) + { + if (IsJpeg(data)) + { + bool ok = GetJpegWidthAndHeight(stream, out imageWidth, out imageHeight); + if (!ok) + { + throw new ImageLoadError("Failed to get dimensions from jpeg"); + } + } + else if (IsPng(data)) + { + GetPngWidthAndHeight(stream, out imageWidth, out imageHeight); + } + else + { + throw new ImageLoadError("Can't get dimensions from unknown image format!"); + } + } + + // Cast to long as maxDimension is big enough on desktop to overflow + if (imageWidth * imageHeight > ((long)maxDimension * (long)maxDimension)) + { + throw new ImageLoadError( + String.Format("Image dimensions {0}x{1} are greater than max dimensions of {2}x{2}!", + imageWidth, imageHeight, maxDimension)); + } + } + + /// Returns true if the data looks like it might be a jpeg + static public bool IsJpeg(byte[] data) + { + return ( + data.Length >= 3 && + data[0] == 0xff && data[1] == 0xd8 && /* SOI tag */ + data[2] == 0xff /* Start of some other tag */); + } + + // [MustUseReturnValue] -- Unity doesn't seem to contain this annotation?! + // Should probably return (int, int)? instead + static public bool GetJpegWidthAndHeight(Stream stream, + out int width, out int height) + { + width = 0; + height = 0; + + byte marker = 0; + byte[] twoBytes = new byte[2]; + JPEGBinaryReader reader = new JPEGBinaryReader(stream); + + // Run through all the markers in the jpeg and look for the SOF, or start of frames. + // Most .jpegs have one SOF, but some have many. + // We're concerned with the size of this image, so take the largest of any frames we find. + while (true) + { + switch (marker) + { + case kJpegMarkerSOF0: + case kJpegMarkerSOF2: + // Skip the frame length. + reader.ReadInt16(); + // Bits percision. + reader.ReadByte(); + // Scan lines (height) + twoBytes = reader.ReadBytes(2); + height = Mathf.Max(height, (twoBytes[0] << 8) + twoBytes[1]); + // Scan samples per line (width) + twoBytes = reader.ReadBytes(2); + width = Mathf.Max(width, (twoBytes[0] << 8) + twoBytes[1]); + return true; + } + + try + { + marker = reader.GetNextMarker(); + } + catch (System.IO.EndOfStreamException) + { + break; /* done reading the file */ + } + } + return false; + } + + /// Raises TiltBrush.ImageDecodeError if the data is not a valid jpeg + static public RawImage FromJpeg(byte[] jpegData, string filename) + { + using (var stream = new MemoryStream(jpegData)) + { + FluxJpeg.Core.Image image; + try + { + FluxJpeg.Core.DecodedJpeg decoded = + new FluxJpeg.Core.Decoder.JpegDecoder(stream).Decode(); + image = decoded.Image; + image.ChangeColorSpace(FluxJpeg.Core.ColorSpace.RGB); + } + catch (Exception e) + { + // Library throws bare Exception :-P + throw new ImageLoadError(e, "JPEG decode error"); + } + + var reds = image.Raster[0]; + var greens = image.Raster[1]; + var blues = image.Raster[2]; + int _width = image.Width; + int _height = image.Height; + var buf = new Color32[_width * _height]; + unsafe + { + unchecked + { + fixed (Color32* pBuf = buf) + { + byte* cur = (byte*)pBuf; + for (int y = _height - 1; y >= 0; --y) + { + for (int x = 0; x < _width; ++x) + { + cur[0] = reds[x, y]; + cur[1] = greens[x, y]; + cur[2] = blues[x, y]; + cur[3] = 0xff; + cur += 4; + } + } + } + } + } + return new RawImage + { + ColorData = buf, + ColorWidth = _width, + ColorHeight = _height, + ColorAspect = _height == 0 ? 1f : ((float)_width / _height) + }; + } + } + + /// Returns true if the data look slike it might be a png + static public bool IsPng(byte[] data) + { + return ( + data.Length >= 8 && + data[0] == 0x89 && + data[1] == 'P' && data[2] == 'N' && data[3] == 'G' && // "PNG" + data[4] == '\r' && data[5] == '\n' && // CRLF + data[6] == 0x1a && // DOS EOF + data[7] == '\n'); + } + + static public void GetPngWidthAndHeight(System.IO.Stream stream, + out int width, out int height) + { + BinaryReader br = new BinaryReader(stream); + // Skip signature. + br.ReadBytes(16); + + byte[] fourBytes = new byte[4]; + fourBytes = br.ReadBytes(4); + width = (fourBytes[0] << 24) + (fourBytes[1] << 16) + + (fourBytes[2] << 8) + fourBytes[3]; + + fourBytes = br.ReadBytes(4); + height = (fourBytes[0] << 24) + (fourBytes[1] << 16) + + (fourBytes[2] << 8) + fourBytes[3]; + } + + /// Raises TiltBrush.ImageDecodeError if the data is not a valid png. + static public RawImage FromPng(byte[] pngData, string filename) + { + try + { + return _FromPng(pngData, filename); + } + catch (Exception e) + { + // There are a ton of different exceptions it can throw, and there + // is no convenient base class + throw new ImageLoadError(e, "PNG decode error"); + } + } + + static RawImage _FromPng(byte[] pngData, string filename) + { + // TODO: test the untested branches + using (var stream = new MemoryStream(pngData)) + { + var png = new Hjg.Pngcs.PngReader(stream, filename); + png.SetUnpackedMode(true); + + int rows = png.ImgInfo.Rows; + int cols = png.ImgInfo.Cols; + int chans = png.ImgInfo.Channels; + + Color32[] buf = new Color32[rows * cols]; + + if (png.ImgInfo.Indexed) + { + var plte = png.GetMetadata().GetPLTE(); + + byte[] alphas = new byte[256]; + { + for (int i = 0; i < 256; ++i) + { + alphas[i] = 255; + } + var trns = png.GetMetadata().GetTRNS(); + if (trns != null) + { + // might be smaller than 256 + int[] palette = trns.GetPalletteAlpha(); + for (int i = 0; i < palette.Length; ++i) + { + alphas[i] = (byte)palette[i]; + } + } + } + + byte[] line = null; + int[] rgb = new int[3]; + for (int r = 0; r < rows; ++r) + { + line = png.ReadRowByte(line, r); + for (int c = 0; c < cols; ++c) + { + int iEntry = line[c]; + plte.GetEntryRgb(iEntry, rgb); + byte alpha = alphas[iEntry]; + buf[(rows - r - 1) * cols + c] = new Color32((byte)rgb[0], (byte)rgb[1], (byte)rgb[2], alpha); + } + } + } + else if (png.ImgInfo.Greyscale && !png.ImgInfo.Alpha) + { + Debug.Assert(chans == 1); + Debug.Assert(png.ImgInfo.BitDepth <= 8, "Unsupported: 16-bit grey"); + + byte[] line = null; + for (int r = 0; r < rows; ++r) + { + line = png.ReadRowByte(line, r); + for (int c = 0; c < cols; ++c) + { + buf[(rows - r - 1) * cols + c] = new Color32(line[c], line[c], line[c], 255); + } + } + } + else if (png.ImgInfo.Greyscale && png.ImgInfo.Alpha) + { + Debug.Assert(chans == 2); + Debug.Assert(png.ImgInfo.BitDepth <= 8, "Unsupported: 16-bit grey"); + Debug.Assert(false, "currently unsupported: grayscale alpha"); + + byte[] line = null; + for (int r = 0; r < rows; ++r) + { + line = png.ReadRowByte(line, r); + for (int c = 0; c < cols; ++c) + { + int i = c * chans; + buf[(rows - r - 1) * cols + c] = new Color32(line[i], line[i], line[i], line[i + 1]); + } + } + } + else if (chans == 3 || chans == 4) + { + // Can use ReadRowByte() if bitDepth <= 8 + if (png.ImgInfo.BitDepth <= 8) + { + byte[] line = null; + for (int r = 0; r < rows; ++r) + { + line = png.ReadRowByte(line, r); + for (int c = 0; c < cols; ++c) + { + int ichan = c * chans; + buf[(rows - r - 1) * cols + c] = new Color32( + line[ichan], line[ichan + 1], line[ichan + 2], + (chans == 3) ? (byte)0xff : line[ichan + 3]); + } + } + } + else + { + Debug.Assert(png.ImgInfo.BitDepth == 16); + Debug.Assert(false, "Untested: 16-bit rgb"); + var lines = png.ReadRowsInt(0, png.ImgInfo.Rows, 1); + for (int r = 0; r < rows; ++r) + { + int[] line = lines.Scanlines[r]; + for (int c = 0; c < cols; ++c) + { + int ichan = c * chans; + buf[(rows - r - 1) * cols + c] = new Color32( + (byte)line[ichan], (byte)line[ichan + 1], (byte)line[ichan + 2], + (byte)((chans == 3) ? 0xff : line[ichan + 3])); + } + } + } + } + else + { + Debug.Assert(false, "Weird format"); + } + + return new RawImage + { + ColorData = buf, + ColorWidth = cols, + ColorHeight = rows, + ColorAspect = (rows == 0) ? 1f : ((float)cols / rows) + }; + } + } + + /// Fetches the url and returns a Texture2D or null. + public static async Task DownloadTextureAsync(string uri) + { + using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(uri)) + { + await www.SendWebRequest(); + // If we don't do this, downloadHandler.data sometimes (always?) returns garbage + while (!www.downloadHandler.isDone) + { + await Awaiters.NextFrame; + } + + if (www.isNetworkError || www.responseCode >= 400) + { + Debug.LogErrorFormat("ImageUtils: Error downloading {0}, error {1}", uri, www.responseCode); + return null; + } + + + // Try LoadImage first, because it's faster + { + Texture2D dest = new Texture2D(2, 2); + if (dest.LoadImage(www.downloadHandler.data)) + { + if (dest.width == 8) + { + // Detect "false success" from LoadImage -- if we see this at all, we should + // consider falling through to ThreadedImageReader? + Debug.LogError("Got 8x8 from LoadImage!"); + } + return dest; + } + else + { + Debug.LogError("DownloadTextureAsync: LoadImage failed"); // Unexpected + } + UnityEngine.Object.Destroy(dest); + } + + // This case probably won't get hit any more + { + RawImage im; + try + { + im = await new ThreadedImageReader(www.downloadHandler.data, uri); + } + catch (ImageLoadError e) + { + Console.WriteLine($"ImageUtils: cannot hand-create {uri}: {e}"); + im = null; + } + if (im != null) + { + Console.WriteLine($"ImageUtils: hand-creating {uri}: {www.isDone}"); + Texture2D dest = new Texture2D(im.ColorWidth, im.ColorHeight, TextureFormat.RGBA32, true); + dest.SetPixels32(im.ColorData); + dest.Apply(); + Console.WriteLine("ImageUtils: hand-created icon dimensions: " + + $"{dest.width}, {dest.height}"); + return dest; + } + } + + // b/37256058: Unity's DownloadHandlerTexture is buggy in 5.4.4 and returns Textures + // which hard-crash Unity (for some URLs, sometimes). + // That bug may be fixed by now, but since it doesn't show up in analytics and is + // quite severeTry creating the texture ourselves. + // Fall through to doing it the old, maybe b/37256058 buggy way. + // This case should get hit even less than the previous case. + { + Console.WriteLine("ImageUtils: downloaded and handled {0}: {1} {2}", + uri, www.isDone, www.downloadHandler.isDone); + // b/62269743: sometimes this returns an ugly 8x8 texture. + Texture2D dest = DownloadHandlerTexture.GetContent(www); + Console.WriteLine($"ImageUtils: icon dimensions: {dest.width}, {dest.height}"); + return dest; + } + } + } + } // ImageUtils +} // TiltBrush diff --git a/Assets/Scripts/WidgetManager.cs b/Assets/Scripts/WidgetManager.cs index e9b5e445fe..7169e8124e 100644 --- a/Assets/Scripts/WidgetManager.cs +++ b/Assets/Scripts/WidgetManager.cs @@ -1,1559 +1,1559 @@ -//// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -using System; -using System.Linq; -using System.Collections.Generic; -using UnityEngine; - -namespace TiltBrush -{ - - // These names are used in our save format, so they must be protected from obfuscation - // Do not change the names of any of them, unless they've never been released. - [Serializable] - public enum StencilType - { - Plane, - Cube, - Sphere, - Capsule, - Cone, - Cylinder, - InteriorDome, - Pyramid, - Ellipsoid - } - - [Serializable] - public struct StencilMapKey - { - public StencilType m_Type; - public StencilWidget m_StencilPrefab; - } - - public struct StencilContactInfo - { - public StencilWidget widget; - public Vector3 pos; - public Vector3 normal; - } - - public class GrabWidgetData - { - public readonly GameObject m_WidgetObject; - public readonly GrabWidget m_WidgetScript; - - // These fields are only valid during a call to GetNearestGrabWidget, - // and are undefined afterwards. Do not use them. - - public bool m_NearController; - // only valid if m_NearController == true - public float m_ControllerScore; - - public GrabWidgetData(GrabWidget widget) - { - m_WidgetScript = widget; - m_WidgetObject = widget.gameObject; - } - // Could maybe get by without this since all users of Clone() don't care if they - // receive a plain old GrabWidgetData or not. - public virtual GrabWidgetData Clone() - { - return new GrabWidgetData(m_WidgetScript) - { - m_NearController = m_NearController, - m_ControllerScore = m_ControllerScore - }; - } - } - - public class TypedWidgetData : GrabWidgetData where T : GrabWidget - { - private readonly T m_typedWidget; - public new T WidgetScript => m_typedWidget; - public TypedWidgetData(T widget) : base(widget) - { - m_typedWidget = widget; - } - public override GrabWidgetData Clone() - { - return new TypedWidgetData(m_typedWidget) - { - m_NearController = m_NearController, - m_ControllerScore = m_ControllerScore - }; - } - } - - public class WidgetManager : MonoBehaviour - { - static public WidgetManager m_Instance; - - [SerializeField] ModelWidget m_ModelWidgetPrefab; - [SerializeField] GameObject m_WidgetPinPrefab; - [SerializeField] ImageWidget m_ImageWidgetPrefab; - [SerializeField] VideoWidget m_VideoWidgetPrefab; - [SerializeField] LightWidget m_LightWidgetPrefab; - [SerializeField] SceneLightGizmo m_SceneLightGizmoPrefab; - [SerializeField] CameraPathWidget m_CameraPathWidgetPrefab; - [SerializeField] private GameObject m_CameraPathPositionKnotPrefab; - [SerializeField] private GameObject m_CameraPathRotationKnotPrefab; - [SerializeField] private GameObject m_CameraPathSpeedKnotPrefab; - [SerializeField] private GameObject m_CameraPathFovKnotPrefab; - [SerializeField] private GameObject m_CameraPathKnotSegmentPrefab; - [SerializeField] private GrabWidgetHome m_Home; - [SerializeField] private GameObject m_HomeHintLinePrefab; - [SerializeField] float m_WidgetSnapAngle = 15.0f; - [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; - [SerializeField] private float m_PanelFocusActivationScore; - [SerializeField] private float m_ModelVertCountScalar = 1.0f; - - [Header("Stencils")] - [SerializeField] StencilMapKey[] m_StencilMap; - [SerializeField] private float m_StencilAttractDist = 0.5f; - [SerializeField] private float m_StencilAttachHysteresis = 0.1f; - [SerializeField] private string m_StencilLayerName; - [SerializeField] private string m_PinnedStencilLayerName; - - private bool m_WidgetsDormant; - private bool m_InhibitGrabWhileLoading; - - private GameObject m_HomeHintLine; - private MeshFilter m_HomeHintLineMeshFilter; - private Vector3 m_HomeHintLineBaseScale; - - private StencilContactInfo[] m_StencilContactInfos; - private const int m_StencilBucketSize = 16; - private StencilWidget m_ActiveStencil; - private bool m_StencilsDisabled; - - // All grabbable widgets should be in exactly one of these lists. - // Widgets will be in the most specific list. - private List m_GrabWidgets; - private List> m_ModelWidgets; - private List> m_LightWidgets; - private List> m_StencilWidgets; - private List> m_ImageWidgets; - private List> m_VideoWidgets; - private List> m_CameraPathWidgets; - - // These lists are used by the PinTool. They're kept in sync by the - // widget manager, but the PinTool is responsible for their coherency. - private List m_CanBePinnedWidgets; - private List m_CanBeUnpinnedWidgets; - public event Action RefreshPinAndUnpinAction; - - private TiltModels75[] m_loadingTiltModels75; - private TiltLights[] m_loadingTiltLights; - private TiltImages75[] m_loadingTiltImages75; - private TiltVideo[] m_loadingTiltVideos; - - private List m_WidgetsNearBrush; - private List m_WidgetsNearWand; - - // This value is used by SketchMemoryScript to check the sketch against memory limits. - // It's incremented when a model is registered, and decremented when a model is - // unregistered. - private int m_ModelVertCount; - // Similar to above, this value is used to check against memory limits. Images are always - // the same number of verts, however, so this number is scaled by texture size. It's a - // hand-wavey calculation. - private int m_ImageVertCount; - - // Camera path. - [NonSerialized] public bool FollowingPath; - private TypedWidgetData m_CurrentCameraPath; - private bool m_CameraPathsVisible; - private CameraPathTinter m_CameraPathTinter; - - static private Dictionary sm_BatchMap = new Dictionary(); - public bool m_EnableSnapToGuides; - - public StencilWidget ActiveStencil - { - get { return m_ActiveStencil; } - } - - public void ResetActiveStencil() - { - m_ActiveStencil = null; - } - - public int StencilLayerIndex - { - get { return LayerMask.NameToLayer(m_StencilLayerName); } - } - - public int PinnedStencilLayerIndex - { - get { return LayerMask.NameToLayer(m_PinnedStencilLayerName); } - } - - public LayerMask PinnedStencilLayerMask - { - get { return LayerMask.GetMask(m_PinnedStencilLayerName); } - } - - public LayerMask StencilLayerMask - { - get { return LayerMask.GetMask(m_StencilLayerName); } - } - - public float StencilAttractDist - { - get => m_StencilAttractDist; - set - { - m_StencilAttractDist = value; - App.Switchboard.TriggerStencilAttractDistChanged(); - } - } - - public List WidgetsNearBrush - { - get { return m_WidgetsNearBrush; } - } - - public List WidgetsNearWand - { - get { return m_WidgetsNearWand; } - } - - public bool AnyWidgetsToPin - { - get { return m_CanBePinnedWidgets.Count > 0; } - } - - public bool AnyWidgetsToUnpin - { - get { return m_CanBeUnpinnedWidgets.Count > 0; } - } - - public float ModelVertCountScalar - { - get { return m_ModelVertCountScalar; } - } - - public int ImageVertCount - { - get { return m_ImageVertCount; } - } - - public void AdjustModelVertCount(int amount) - { - m_ModelVertCount += amount; - } - - public void AdjustImageVertCount(int amount) - { - m_ImageVertCount += amount; - } - - public int WidgetsVertCount - { - get { return m_ModelVertCount + m_ImageVertCount; } - } - - public bool AnyVideoWidgetActive => m_VideoWidgets.Any(x => x.m_WidgetObject.activeSelf); - - public bool AnyCameraPathWidgetsActive => - m_CameraPathWidgets.Any(x => x.m_WidgetObject.activeSelf); - - public CameraPathTinter PathTinter { get => m_CameraPathTinter; } - - // Returns the associated widget for the given batchId. - // Returns null if key doesn't exist. - public GrabWidget GetBatch(ushort batchId) - { - if (sm_BatchMap.ContainsKey(batchId)) - { - return sm_BatchMap[batchId]; - } - return null; - } - - public void AddWidgetToBatchMap(GrabWidget widget, ushort batchId) - { - Debug.Assert(!sm_BatchMap.ContainsKey(batchId)); - sm_BatchMap.Add(batchId, widget); - } - - void Awake() - { - m_Instance = this; - } - - public void Init() - { - m_CameraPathTinter = gameObject.AddComponent(); - - m_GrabWidgets = new List(); - m_ModelWidgets = new List>(); - m_LightWidgets = new List>(); - m_StencilWidgets = new List>(); - m_ImageWidgets = new List>(); - m_VideoWidgets = new List>(); - m_CameraPathWidgets = new List>(); - - m_CanBePinnedWidgets = new List(); - m_CanBeUnpinnedWidgets = new List(); - - m_WidgetsNearBrush = new List(); - m_WidgetsNearWand = new List(); - - m_Home.Init(); - m_Home.SetFixedPosition(Vector3.zero); - m_HomeHintLine = (GameObject)Instantiate(m_HomeHintLinePrefab); - m_HomeHintLineMeshFilter = m_HomeHintLine.GetComponent(); - m_HomeHintLineBaseScale = m_HomeHintLine.transform.localScale; - m_HomeHintLine.transform.parent = transform; - m_HomeHintLine.SetActive(false); - - m_StencilContactInfos = new StencilContactInfo[m_StencilBucketSize]; - m_StencilsDisabled = false; - - FollowingPath = false; - m_CameraPathsVisible = false; - } - - public ModelWidget ModelWidgetPrefab { get { return m_ModelWidgetPrefab; } } - public ImageWidget ImageWidgetPrefab { get { return m_ImageWidgetPrefab; } } - public VideoWidget VideoWidgetPrefab { get { return m_VideoWidgetPrefab; } } - public LightWidget LightWidgetPrefab { get { return m_LightWidgetPrefab; } } - public SceneLightGizmo SceneLightGizmoPrefab { get { return m_SceneLightGizmoPrefab; } } - public CameraPathWidget CameraPathWidgetPrefab { get { return m_CameraPathWidgetPrefab; } } - public GameObject CameraPathPositionKnotPrefab { get { return m_CameraPathPositionKnotPrefab; } } - public GameObject CameraPathRotationKnotPrefab { get { return m_CameraPathRotationKnotPrefab; } } - public GameObject CameraPathSpeedKnotPrefab { get { return m_CameraPathSpeedKnotPrefab; } } - public GameObject CameraPathFovKnotPrefab { get { return m_CameraPathFovKnotPrefab; } } - public GameObject CameraPathKnotSegmentPrefab { get { return m_CameraPathKnotSegmentPrefab; } } - - public IEnumerable ActiveGrabWidgets - { - get - { - if (m_InhibitGrabWhileLoading) - { - // Returns only widgets that are not part of the sketch - return m_GrabWidgets.Where(x => x.m_WidgetObject.activeSelf); - } - return GetAllActiveGrabWidgets(); - } - } - - private IEnumerable GetAllActiveGrabWidgets() - { - for (int i = 0; i < m_GrabWidgets.Count; ++i) - { - if (m_GrabWidgets[i].m_WidgetObject.activeSelf) - { - yield return m_GrabWidgets[i]; - } - } - for (int i = 0; i < m_ModelWidgets.Count; ++i) - { - if (m_ModelWidgets[i].m_WidgetObject.activeSelf) - { - yield return m_ModelWidgets[i]; - } - } - for (int i = 0; i < m_LightWidgets.Count; ++i) - { - if (m_LightWidgets[i].m_WidgetObject.activeSelf) - { - yield return m_LightWidgets[i]; - } - } - for (int i = 0; i < m_StencilWidgets.Count; ++i) - { - if (m_StencilWidgets[i].m_WidgetObject.activeSelf) - { - yield return m_StencilWidgets[i]; - } - } - for (int i = 0; i < m_ImageWidgets.Count; ++i) - { - if (m_ImageWidgets[i].m_WidgetObject.activeSelf) - { - yield return m_ImageWidgets[i]; - } - } - for (int i = 0; i < m_VideoWidgets.Count; ++i) - { - if (m_VideoWidgets[i].m_WidgetObject.activeSelf) - { - yield return m_VideoWidgets[i]; - } - } - for (int i = 0; i < m_CameraPathWidgets.Count; ++i) - { - if (m_CameraPathWidgets[i].m_WidgetObject.activeInHierarchy) - { - yield return m_CameraPathWidgets[i]; - } - } - } - - public IEnumerable MediaWidgets - { - get - { - IEnumerable ret = m_ModelWidgets; - return ret - .Concat(m_ImageWidgets) - .Concat(m_VideoWidgets) - .Concat(m_LightWidgets); - } - } - - public IEnumerable> CameraPathWidgets => - m_CameraPathWidgets.Where(x => x.m_WidgetObject.activeSelf); - - public TypedWidgetData GetCurrentCameraPath() => m_CurrentCameraPath; - - public void SetCurrentCameraPath(CameraPathWidget path) - { - // Early out if we're trying to set the path to the already current path. - if (m_CurrentCameraPath != null && m_CurrentCameraPath.WidgetScript == path) - { - return; - } - - for (int i = 0; i < m_CameraPathWidgets.Count; ++i) - { - if (m_CameraPathWidgets[i].m_WidgetScript == path) - { - FollowingPath = false; - SetCurrentCameraPath_Internal(m_CameraPathWidgets[i]); - App.Switchboard.TriggerCurrentCameraPathChanged(); - return; - } - } - } - - public void ValidateCurrentCameraPath() - { - if (m_CurrentCameraPath == null || - m_CurrentCameraPath.WidgetScript == null || - !m_CurrentCameraPath.m_WidgetObject.activeSelf) - { - var prevPath = m_CurrentCameraPath; - for (int i = 0; i < m_CameraPathWidgets.Count; ++i) - { - if (m_CameraPathWidgets[i] != prevPath && - m_CameraPathWidgets[i].m_WidgetObject.activeSelf) - { - SetCurrentCameraPath_Internal(m_CameraPathWidgets[i]); - return; - } - } - } - } - - void SetCurrentCameraPath_Internal(TypedWidgetData cp) - { - if (m_CurrentCameraPath != null && m_CurrentCameraPath.WidgetScript != null) - { - m_CurrentCameraPath.WidgetScript.SetAsActivePath(false); - } - m_CurrentCameraPath = cp; - if (m_CurrentCameraPath != null && m_CurrentCameraPath.WidgetScript != null) - { - m_CurrentCameraPath.WidgetScript.SetAsActivePath(true); - } - } - - public bool CanRecordCurrentCameraPath() - { - if (SketchSurfacePanel.m_Instance.GetCurrentToolType() != BaseTool.ToolType.MultiCamTool) - { - if (m_CameraPathsVisible && m_CurrentCameraPath != null) - { - CameraPathWidget cpw = m_CurrentCameraPath.WidgetScript; - return (cpw != null) && cpw.gameObject.activeSelf && cpw.Path.NumPositionKnots > 1; - } - } - return false; - } - - // The following methods use indexes to reference camera paths. Because the list of - // camera path widgets can have inactive paths (if they're deleted), these lists may - // have holes. Use caution when using these methods. - // The reason we need these methods is because our UI buttons work with SketchControls - // global commands, which can be modified with generic integer parameters. In those - // cases, we can't pass a CameraPathWidget object. - public CameraPathWidget GetNthActiveCameraPath(int nth) - { - var activeCameraPathWidgets = m_CameraPathWidgets.Where(x => x.m_WidgetObject.activeSelf); - foreach (var cpw in activeCameraPathWidgets) - { - if (nth == 0) - { - return cpw.WidgetScript; - } - --nth; - } - return null; - } - - public bool IsCameraPathAtIndexCurrent(int pathIndex) - { - CameraPathWidget cpw = (m_CurrentCameraPath != null) ? cpw = m_CurrentCameraPath.WidgetScript : - null; - if (cpw == null) { return false; } - return GetNthActiveCameraPath(pathIndex) == m_CurrentCameraPath.WidgetScript; - } - - public int? GetIndexOfCameraPath(CameraPathWidget path) - { - int index = 0; - for (int i = 0; i < m_CameraPathWidgets.Count; ++i) - { - if (m_CameraPathWidgets[i].m_WidgetObject.activeSelf) - { - if (m_CameraPathWidgets[i].WidgetScript == path) - { - return index; - } - ++index; - } - } - return null; - } - - public CameraPathWidget CreatePathWidget() - { - CreateWidgetCommand command = - new CreateWidgetCommand(m_CameraPathWidgetPrefab, TrTransform.identity); - SketchMemoryScript.m_Instance.PerformAndRecordCommand(command); - return m_CameraPathWidgets.Last().WidgetScript; - } - - public bool AnyActivePathHasAKnot() - { - var datas = CameraPathWidgets; - foreach (TypedWidgetData data in datas) - { - if (data.WidgetScript.Path.NumPositionKnots > 0) - { - return true; - } - } - return false; - } - - public void DeleteCameraPath(GrabWidget cameraPathWidgetScript) - { - if (cameraPathWidgetScript != null) - { - // We don't *really* delete it, we just hide it. - SketchMemoryScript.m_Instance.PerformAndRecordCommand( - new HideWidgetCommand(cameraPathWidgetScript)); - FollowingPath = false; - } - - // If our current path is null or inactive, it means we have no camera paths. In that - // instance, if the camera path tool is active, reset back to our default tool. - // I'm doing this because if we leave the camera path tool active, the camera path - // panel shows the button highlighted, which affects the user's flow for being - // invited to start a path. It looks weird. - if (m_CurrentCameraPath == null || !m_CurrentCameraPath.WidgetScript.gameObject.activeSelf) - { - if (SketchSurfacePanel.m_Instance.ActiveToolType == BaseTool.ToolType.CameraPathTool) - { - SketchSurfacePanel.m_Instance.EnableDefaultTool(); - } - } - } - - public bool CameraPathsVisible - { - get { return m_CameraPathsVisible; } - set - { - if (value != m_CameraPathsVisible) - { - m_CameraPathsVisible = value; - - // Camera paths. - for (int i = 0; i < m_CameraPathWidgets.Count; ++i) - { - CameraPathWidget cpw = m_CameraPathWidgets[i].m_WidgetScript as CameraPathWidget; - if (cpw.gameObject.activeSelf) - { - cpw.Path.SetKnotsActive(m_CameraPathsVisible); - } - } - - if (!m_CameraPathsVisible) - { - // Flip back to default tool if we turned off paths. - if (SketchSurfacePanel.m_Instance.ActiveToolType == BaseTool.ToolType.CameraPathTool) - { - SketchSurfacePanel.m_Instance.EnableDefaultTool(); - } - FollowingPath = false; - } - - App.Switchboard.TriggerCameraPathVisibilityChanged(); - } - } - } - - public bool HasSelectableWidgets() - { - return - (m_ModelWidgets.Count > 0) || - (m_LightWidgets.Count > 0) || - (m_ImageWidgets.Count > 0) || - (m_VideoWidgets.Count > 0) || - (!m_StencilsDisabled && m_StencilWidgets.Count > 0); - } - - public bool HasExportableContent(Cloud cloud) - { - switch (cloud) - { - case Cloud.Sketchfab: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || - ImageWidgets.Any(w => w.gameObject.activeSelf); - default: - return SketchMemoryScript.m_Instance.HasVisibleObjects() || - App.Config.m_EnableReferenceModelExport && - ExportableModelWidgets.Any( - w => w.gameObject.activeSelf && - w.Model.GetLocation().GetLocationType() == Model.Location.Type.PolyAssetId); - } - } - - public bool HasNonExportableContent(Cloud cloud) - { - switch (cloud) - { - case Cloud.Sketchfab: - return VideoWidgets.Any(w => w.gameObject.activeSelf); - default: - return NonExportableModelWidgets.Any(w => w.gameObject.activeSelf) || - ImageWidgets.Any(w => w.gameObject.activeSelf) || - VideoWidgets.Any(w => w.gameObject.activeSelf); - } - } - - public bool StencilsDisabled - { - get { return m_StencilsDisabled; } - set - { - if (value != m_StencilsDisabled) - { - // Flip flag and visuals for all stencils. - for (int i = 0; i < m_StencilWidgets.Count; ++i) - { - StencilWidget sw = m_StencilWidgets[i].WidgetScript; - if (sw) - { - sw.RefreshVisibility(value); - } - } - m_ActiveStencil = null; - } - m_StencilsDisabled = value; - RefreshPinAndUnpinLists(); - } - } - - private static string CanonicalizeForCompare(string path) - { - return path.ToLower().Replace("\\", "/"); - } - - // Shortens full file paths to "Media Library/[Models | Images]/thing" - // Input: absolute path - // Returns: path starting from Media Library/ (e.g. "Media Library/[Models | Images]/thing") - // or null if path does not lead to Media Library - // Throws: ArgumentException if path is not full - public static string GetPathRootedAtMedia(string path) - { - if (!System.IO.Path.IsPathRooted(path)) - { - throw new ArgumentException("Path is not rooted"); - } - var media = App.MediaLibraryPath(); - if (CanonicalizeForCompare(path).StartsWith(CanonicalizeForCompare(media))) - { - return "Media Library" + path.Substring(media.Length); - } - return null; - } - - // Returns path after Media Library/Models for models only - // Input: absolute path - // Returns: path starting after Models/ or null if the path is not to the Models directory - public static string GetModelSubpath(string fullPath) - { - string media = GetPathRootedAtMedia(fullPath); - string modelPath = "Media Library/Models/"; - if (media == null || !media.StartsWith(modelPath)) - { - return null; - } - return media.Substring(modelPath.Length); - } - - // Used only at .tilt-loading time - public void SetDataFromTilt(TiltModels75[] value) - { - m_loadingTiltModels75 = value; - } - - // Used only at .tilt-loading time - public void SetDataFromTilt(TiltImages75[] value) - { - m_loadingTiltImages75 = value; - } - - // Used only at .tilt-loading time - public void SetDataFromTilt(TiltLights[] value) - { - m_loadingTiltLights = value; - } - - public void SetDataFromTilt(CameraPathMetadata[] cameraPaths) - { - for (int i = 0; i < cameraPaths.Length; ++i) - { - CameraPathWidget.CreateFromSaveData(cameraPaths[i]); - } - } - - public void SetDataFromTilt(TiltVideo[] value) - { - m_loadingTiltVideos = value; - } - - public WidgetPinScript GetWidgetPin() - { - GameObject pinObj = Instantiate(m_WidgetPinPrefab); - pinObj.transform.parent = transform; - return pinObj.GetComponent(); - } - - public void DestroyWidgetPin(WidgetPinScript pin) - { - if (pin != null) - { - Destroy(pin.gameObject); - } - } - - // Set the position that widgets can snap to in the current environment - public void SetHomePosition(Vector3 position) - { - m_Home.SetFixedPosition(position); - } - - // Dormant models are still grabbable but visuals/haptics are disabled - public bool WidgetsDormant - { - get { return m_WidgetsDormant; } - set - { - m_WidgetsDormant = value; - Shader.SetGlobalFloat("_WidgetsDormant", value ? 0 : 1); - } - } - - public float WidgetSnapAngle - { - get { return m_WidgetSnapAngle; } - } - - public bool IsOriginHomeWithinSnapRange(Vector3 pos) - { - return m_Home.WithinRange(pos); - } - - public Transform GetHomeXf() - { - return m_Home.transform; - } - - public void SetHomeOwner(Transform owner) - { - m_Home.SetOwner(owner); - m_Home.Reset(); - } - - public void ClearHomeOwner() - { - m_Home.SetOwner(null); - m_Home.gameObject.SetActive(false); - m_HomeHintLine.SetActive(false); - } - - public void EnableHome(bool bEnable) - { - m_Home.gameObject.SetActive(bEnable); - if (!bEnable) - { - m_HomeHintLine.SetActive(false); - } - } - - public void LoadingState(bool bEnter) - { - m_InhibitGrabWhileLoading = bEnter; - } - - public void UpdateHomeHintLine(Vector3 vModelSnapPos) - { - if (!m_Home.WithinRange(vModelSnapPos) && m_Home.WithinHintRange(vModelSnapPos)) - { - // Enable, position, and scale hint line. - m_HomeHintLine.SetActive(true); - Vector3 vHomeToModel = vModelSnapPos - m_Home.transform.position; - m_HomeHintLine.transform.position = m_Home.transform.position + - (vHomeToModel * 0.5f); - m_HomeHintLine.transform.up = vHomeToModel.normalized; - - Vector3 vScale = m_HomeHintLineBaseScale; - vScale.y = vHomeToModel.magnitude * 0.5f; - m_HomeHintLine.transform.localScale = vScale; - - App.Instance.SelectionEffect.RegisterMesh(m_HomeHintLineMeshFilter); - m_Home.RenderHighlight(); - } - else - { - // Disable the line. - m_HomeHintLine.SetActive(false); - } - } - - public bool MagnetizeToStencils(ref Vector3 pos, ref Quaternion rot, IEnumerable stencilsToIgnore = null) - { - // Early out if stencils are disabled. - if (m_StencilsDisabled && !App.UserConfig.Flags.GuideToggleVisiblityOnly) - { - return false; - } - - Vector3 samplePos = pos; - - bool stencilWasUsed = false; - - // If we're painting, we have a different path for magnetization that relies on the - // previous frame. - if (PointerManager.m_Instance.IsLineEnabled()) - { - // If we don't have an active stencil, we're done here. - if (m_ActiveStencil == null) - { - return false; - } - - // Using the 0 index of m_StencilContactInfos as a shortcut. - m_StencilContactInfos[0].widget = m_ActiveStencil; - FindClosestPointOnWidgetSurface(pos, ref m_StencilContactInfos[0]); - - m_ActiveStencil.SetInUse(true); - pos = m_StencilContactInfos[0].pos; - rot = Quaternion.LookRotation(m_StencilContactInfos[0].normal); - stencilWasUsed = true; - } - else - { - StencilWidget prevStencil = m_ActiveStencil; - float fPrevScore = -m_StencilAttachHysteresis; - int iPrevIndex = -1; - m_ActiveStencil = null; - - // Run through the overlap list and find the best stencil to stick to. - int iPrimaryIndex = -1; - float fBestScore = 0; - int sIndex = 0; - - IEnumerable widgetsToCheck = m_StencilWidgets.Select(w => w.WidgetScript); - if (stencilsToIgnore != null) widgetsToCheck = widgetsToCheck.Except(stencilsToIgnore); - foreach (var sw in widgetsToCheck) - { - Debug.Assert(sw != null); - - // Reset tint - sw.SetInUse(false); - - // Does a rough check to see if the stencil might overlap. OverlapSphereNonAlloc is - // shockingly slow, which is why we don't use it. - Collider collider = sw.GrabCollider; - float centerDist = (collider.bounds.center - samplePos).sqrMagnitude; - if (centerDist > - (StencilAttractDist * StencilAttractDist + collider.bounds.extents.sqrMagnitude)) - { - continue; - } - - m_StencilContactInfos[sIndex].widget = sw; - - FindClosestPointOnWidgetSurface(samplePos, ref m_StencilContactInfos[sIndex]); - - // Find out how far we are from this point and save it as a score. - float distToSurfactPoint = (m_StencilContactInfos[sIndex].pos - samplePos).magnitude; - float score = 1.0f - (distToSurfactPoint / StencilAttractDist); - if (score > fBestScore) - { - iPrimaryIndex = sIndex; - fBestScore = score; - m_ActiveStencil = m_StencilContactInfos[sIndex].widget; - } - - if (m_StencilContactInfos[sIndex].widget == prevStencil) - { - fPrevScore = score; - iPrevIndex = sIndex; - } - - if (++sIndex == m_StencilBucketSize) - { - break; - } - } - - // If we are switching between stencils, check to see if we're switching "enough". - if (iPrevIndex != -1 && m_ActiveStencil != null && prevStencil != m_ActiveStencil) - { - if (fPrevScore + m_StencilAttachHysteresis > fBestScore) - { - m_ActiveStencil = prevStencil; - iPrimaryIndex = iPrevIndex; - } - } - - // If we found a good stencil, return the surface collision transform. - if (m_ActiveStencil != null) - { - m_ActiveStencil.SetInUse(true); - pos = m_StencilContactInfos[iPrimaryIndex].pos; - var up = rot * Vector3.up; - rot = Quaternion.LookRotation(m_StencilContactInfos[iPrimaryIndex].normal, up); - stencilWasUsed = true; - } - - if (prevStencil != m_ActiveStencil) - { - PointerManager.m_Instance.DisablePointerPreviewLine(); - } - } - - return stencilWasUsed; - } - - bool FindClosestPointOnCollider( - Ray rRay, Collider collider, out RaycastHit rHitInfo, float fDist) - { - rHitInfo = new RaycastHit(); - return collider.Raycast(rRay, out rHitInfo, fDist); - } - - void FindClosestPointOnWidgetSurface(Vector3 pos, ref StencilContactInfo info) - { - info.widget.FindClosestPointOnSurface(pos, out info.pos, out info.normal); - } - - public bool ShouldUpdateCollisions() - { - return ActiveGrabWidgets.Any(elt => elt.m_WidgetScript.IsCollisionEnabled()); - } - - public IEnumerable ModelWidgets - { - get - { - return m_ModelWidgets - .Select(w => w == null ? null : w.WidgetScript) - .Where(w => w != null); - } - } - - public IEnumerable LightWidgets - { - get - { - return m_LightWidgets - .Select(w => w == null ? null : w.WidgetScript) - .Where(w => w != null); - } - } - - public IEnumerable VideoWidgets - { - get - { - return m_VideoWidgets - .Select(w => w == null ? null : w.WidgetScript) - .Where(w => w != null); - } - } - - public IEnumerable NonExportableModelWidgets - { - get - { - return m_ModelWidgets - .Select(w => w == null ? null : w.WidgetScript) - .Where(w => w != null).Where(w => !w.Model.AllowExport); - } - } - - public IEnumerable ExportableModelWidgets - { - get - { - return m_ModelWidgets - .Select(w => w == null ? null : w.WidgetScript) - .Where(w => w != null).Where(w => w.Model.AllowExport); - } - } - - public IEnumerable StencilWidgets - { - get - { - return m_StencilWidgets - .Select(d => d == null ? null : d.WidgetScript) - .Where(w => w != null); - } - } - - public StencilWidget GetStencilPrefab(StencilType type) - { - for (int i = 0; i < m_StencilMap.Length; ++i) - { - if (m_StencilMap[i].m_Type == type) - { - return m_StencilMap[i].m_StencilPrefab; - } - } - throw new ArgumentException(type.ToString()); - } - - public IEnumerable ImageWidgets - { - get - { - return m_ImageWidgets - .Select(d => d == null ? null : d.m_WidgetScript as ImageWidget) - .Where(w => w != null); - } - } - - public List GetAllUnselectedActiveWidgets() - { - List widgets = new List(); - GetUnselectedActiveWidgetsInList(m_ModelWidgets); - GetUnselectedActiveWidgetsInList(m_LightWidgets); - GetUnselectedActiveWidgetsInList(m_ImageWidgets); - GetUnselectedActiveWidgetsInList(m_VideoWidgets); - if (!m_StencilsDisabled) - { - GetUnselectedActiveWidgetsInList(m_StencilWidgets); - } - return widgets; - - void GetUnselectedActiveWidgetsInList(List> list) where T : GrabWidget - { - for (int i = 0; i < list.Count; ++i) - { - GrabWidget w = list[i].m_WidgetScript; - if (!w.Pinned && w.transform.parent == App.Scene.ActiveCanvas.transform && - w.gameObject.activeSelf) - { - widgets.Add(w); - } - } - } - } - - public void RefreshPinAndUnpinLists() - { - if (RefreshPinAndUnpinAction != null) - { - m_CanBePinnedWidgets.Clear(); - m_CanBeUnpinnedWidgets.Clear(); - - RefreshPinUnpinWidgetList(m_ModelWidgets); - RefreshPinUnpinWidgetList(m_LightWidgets); - RefreshPinUnpinWidgetList(m_ImageWidgets); - RefreshPinUnpinWidgetList(m_VideoWidgets); - RefreshPinUnpinWidgetList(m_StencilWidgets); - - RefreshPinAndUnpinAction(); - } - - // New in C# 7 - local functions! - void RefreshPinUnpinWidgetList(List> widgetList) where T : GrabWidget - { - foreach (var widgetData in widgetList) - { - var widget = widgetData.WidgetScript; - if (widget.gameObject.activeSelf && widget.AllowPinning) - { - if (widget.Pinned) - { - m_CanBeUnpinnedWidgets.Add(widget); - } - else - { - m_CanBePinnedWidgets.Add(widget); - } - } - } - } - } - - public void RegisterHighlightsForPinnableWidgets(bool pinnable) - { - List widgets = pinnable ? m_CanBePinnedWidgets : m_CanBeUnpinnedWidgets; - for (int i = 0; i < widgets.Count; ++i) - { - GrabWidget w = widgets[i]; - // If stencils are disabled, don't highlight them, cause we can interact with 'em. - if (WidgetManager.m_Instance.StencilsDisabled) - { - if (w is StencilWidget) - { - continue; - } - } - w.RegisterHighlight(); - } - } - - public void RegisterGrabWidget(GameObject rWidget) - { - // Find b/29514616 - if (ReferenceEquals(rWidget, null)) - { - throw new ArgumentNullException("rWidget"); - } - else if (rWidget == null) - { - throw new ArgumentNullException("rWidget(2)"); - } - GrabWidget generic = rWidget.GetComponent(); - if (generic == null) - { - throw new InvalidOperationException($"Object {rWidget.name} is not a GrabWidget"); - } - - if (generic is ModelWidget mw) - { - m_ModelWidgets.Add(new TypedWidgetData(mw)); - } - else if (generic is LightWidget light) - { - m_LightWidgets.Add(new TypedWidgetData(light)); - } - else if (generic is StencilWidget stencil) - { - m_StencilWidgets.Add(new TypedWidgetData(stencil)); - } - else if (generic is ImageWidget image) - { - m_ImageWidgets.Add(new TypedWidgetData(image)); - } - else if (generic is VideoWidget video) - { - m_VideoWidgets.Add(new TypedWidgetData(video)); - } - else if (generic is CameraPathWidget cpw) - { - m_CameraPathWidgets.Add(new TypedWidgetData(cpw)); - } - else - { - m_GrabWidgets.Add(new GrabWidgetData(generic)); - } - - RefreshPinAndUnpinLists(); - } - - // Returns true if a widget was removed - static bool RemoveFrom(List list, GameObject rWidget) - where T : GrabWidgetData - { - int idx = list.FindIndex((data) => data.m_WidgetObject == rWidget); - if (idx != -1) - { - list.RemoveAt(idx); - return true; - } - return false; - } - - public void UnregisterGrabWidget(GameObject rWidget) - { - // Get this widget's batchId out of the map. - sm_BatchMap.Remove(rWidget.GetComponent().BatchId); - - // Pull out of pin tool lists. - RefreshPinAndUnpinLists(); - - // Decrement model vert count if we're a model widget. - ModelWidget mw = rWidget.GetComponent() as ModelWidget; - if (mw != null) - { - m_ModelVertCount -= mw.NumVertsTrackedByWidgetManager; - } - // Same with image widget. - ImageWidget iw = rWidget.GetComponent() as ImageWidget; - if (iw != null) - { - m_ImageVertCount -= iw.NumVertsTrackedByWidgetManager; - } - - if (RemoveFrom(m_ModelWidgets, rWidget)) { return; } - if (RemoveFrom(m_LightWidgets, rWidget)) { return; } - if (RemoveFrom(m_StencilWidgets, rWidget)) { return; } - if (RemoveFrom(m_ImageWidgets, rWidget)) { return; } - if (RemoveFrom(m_VideoWidgets, rWidget)) { return; } - if (RemoveFrom(m_CameraPathWidgets, rWidget)) { return; } - RemoveFrom(m_GrabWidgets, rWidget); - } - - public ImageWidget GetNearestImage(Vector3 pos, float maxDepth, ref Vector3 sampleLoc) - { - ImageWidget bestImage = null; - float leastDistance = float.MaxValue; - foreach (var im in ImageWidgets.Where(i => i.gameObject.activeSelf)) - { - Vector3 dropper_QS; - Vector3 dropper_GS = pos; - Matrix4x4 xfQuadFromGlobal = im.m_ImageQuad.transform.worldToLocalMatrix; - dropper_QS = xfQuadFromGlobal.MultiplyPoint3x4(dropper_GS); - if (Mathf.Abs(dropper_QS.z) < leastDistance - && Mathf.Abs(dropper_QS.x) <= 0.5f - && Mathf.Abs(dropper_QS.y) <= 0.5f - && Mathf.Abs(dropper_QS.z) <= maxDepth / Mathf.Abs(im.GetSignedWidgetSize())) - { - bestImage = im; - leastDistance = Mathf.Abs(dropper_QS.z); - sampleLoc = dropper_QS; - } - } - return bestImage; - } - - public void RefreshNearestWidgetLists(Ray currentGazeRay, int currentGazeObject) - { - m_WidgetsNearBrush.Clear(); - UpdateNearestGrabsFor(InputManager.ControllerName.Brush, currentGazeRay, currentGazeObject); - foreach (GrabWidgetData widget in ActiveGrabWidgets) - { - if (widget.m_NearController) - { - // Deep copy. - m_WidgetsNearBrush.Add(widget.Clone()); - } - } - - m_WidgetsNearWand.Clear(); - UpdateNearestGrabsFor(InputManager.ControllerName.Wand, currentGazeRay, currentGazeObject); - foreach (GrabWidgetData widget in ActiveGrabWidgets) - { - if (widget.m_NearController) - { - m_WidgetsNearWand.Add(widget.Clone()); - } - } - } - - // Helper for RefreshNearestWidgetLists - void UpdateNearestGrabsFor( - InputManager.ControllerName name, Ray currentGazeRay, int currentGazeObject) - { - // Reset hit flags. - foreach (var elt in ActiveGrabWidgets) - { - elt.m_NearController = false; - elt.m_ControllerScore = -1.0f; - } - - Vector3 controllerPos = Vector3.zero; - if (name == InputManager.ControllerName.Brush) - { - controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; - } - else if (name == InputManager.ControllerName.Wand) - { - controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; - } - else - { - Debug.LogError("UpdateNearestGrabsFor() only supports Brush and Wand controller types."); - } - - // Figure out if controller is in view frustum. If it isn't, don't allow widget grabs. - Vector3 vToController = controllerPos - currentGazeRay.origin; - vToController.Normalize(); - if (Vector3.Angle(vToController, currentGazeRay.direction) > m_GazeMaxAngleFromFacing) - { - return; - } - - BasePanel gazePanel = null; - if (currentGazeObject > -1) - { - gazePanel = PanelManager.m_Instance.GetPanel(currentGazeObject); - } - - foreach (var data in ActiveGrabWidgets) - { - if (!data.m_WidgetScript.enabled) - { - continue; - } - if (m_StencilsDisabled && data.m_WidgetScript is StencilWidget) - { - continue; - } - if (SelectionManager.m_Instance.ShouldRemoveFromSelection() && - !data.m_WidgetScript.CanGrabDuringDeselection()) - { - continue; - } - if (SelectionManager.m_Instance.IsWidgetSelected(data.m_WidgetScript)) - { - continue; - } - float score = data.m_WidgetScript.GetActivationScore(controllerPos, name); - if (score < m_PanelFocusActivationScore && name == InputManager.ControllerName.Brush && - gazePanel && data.m_WidgetObject == gazePanel.gameObject) - { - // If the brush is pointing at a panel, make sure that the panel will be the widget grabbed - score = m_PanelFocusActivationScore; - } - if (score < 0) - { - continue; - } - - data.m_NearController = true; - data.m_ControllerScore = score; - } - } - - public float DistanceToNearestWidget(Ray ray) - { - // If we're in controller mode, find the nearest colliding widget that might get in our way. - float fNearestWidget = 99999.0f; - foreach (var elt in ActiveGrabWidgets) - { - float fWidgetDist = 0.0f; - if (elt.m_WidgetScript.DistanceToCollider(ray, out fWidgetDist)) - { - fNearestWidget = Mathf.Min(fNearestWidget, fWidgetDist); - } - } - return fNearestWidget; - } - - public void DestroyAllWidgets() - { - DestroyWidgetList(m_ModelWidgets); - DestroyWidgetList(m_LightWidgets); - DestroyWidgetList(m_ImageWidgets); - DestroyWidgetList(m_VideoWidgets); - DestroyWidgetList(m_StencilWidgets); - DestroyWidgetList(m_CameraPathWidgets, false); - SetCurrentCameraPath_Internal(null); - App.Switchboard.TriggerAllWidgetsDestroyed(); - - void DestroyWidgetList(List> widgetList, - bool hideBeforeDestroy = true) where T : GrabWidget - { - while (widgetList.Count > 0) - { - GrabWidget widget = widgetList[0].m_WidgetScript; - GameObject obj = widgetList[0].m_WidgetObject; - if (hideBeforeDestroy) { widget.Hide(); } - widget.OnPreDestroy(); - UnregisterGrabWidget(obj); - Destroy(obj); - } - } - } - - /// Upon return: - /// - ImageWidgets have at least an icon-sized Texture2D; it will be mutated with - /// the fullres texture data some time later. - /// - ModelWidgets may have a dummy, invisible Model instead of the actual Model they want; - /// the Model will be automatically replaced with the loaded Model some time later. - public IEnumerator CreateMediaWidgetsFromLoadDataCoroutine() - { - if (m_loadingTiltModels75 != null) - { - OverlayManager.m_Instance.RefuseProgressBarChanges(true); - - if (App.Config.kModelWidgetsWaitForLoad) - { - var assetIds = m_loadingTiltModels75 - .Select(tm => tm.AssetId).Where(aid => aid != null).ToArray(); - // Kick off a bunch of loads... - foreach (var assetId in assetIds) - { - if (App.PolyAssetCatalog.GetAssetLoadState(assetId) - != PolyAssetCatalog.AssetLoadState.Loaded) - { - App.PolyAssetCatalog.RequestModelLoad(assetId, "tiltload"); - } - } - // ... and wait for them to complete - // No widgets have been created yet, so we can't use AreMediaWidgetsStillLoading. - bool IsLoading(string assetId) - { - var state = App.PolyAssetCatalog.GetAssetLoadState(assetId); - return (state == PolyAssetCatalog.AssetLoadState.Downloading || - state == PolyAssetCatalog.AssetLoadState.Loading); - } - while (assetIds.Any(IsLoading)) - { - yield return null; - } - } - - for (int i = 0; i < m_loadingTiltModels75.Length; i++) - { - ModelWidget.CreateFromSaveData(m_loadingTiltModels75[i]); - OverlayManager.m_Instance.UpdateProgress( - (float)(i + 1) / m_loadingTiltModels75.Length, true); - } - OverlayManager.m_Instance.RefuseProgressBarChanges(false); - m_loadingTiltModels75 = null; - } - ModelCatalog.m_Instance.PrintMissingModelWarnings(); - if (m_loadingTiltLights != null) - { - foreach (var light in m_loadingTiltLights) - { - LightWidget.FromTiltLight(light); - } - m_loadingTiltLights = null; - } - if (m_loadingTiltImages75 != null) - { - foreach (TiltImages75 import in m_loadingTiltImages75) - { - // TODO: FromTiltImage should take advantage of being called by a coroutine - // so it can avoid calling ReferenceImage.SynchronousLoad() - ImageWidget.FromTiltImage(import); - } - m_loadingTiltImages75 = null; - } - if (m_loadingTiltVideos != null) - { - foreach (var video in m_loadingTiltVideos) - { - VideoWidget.FromTiltVideo(video); - } - m_loadingTiltVideos = null; - } - yield break; - } - - /// Returns true when all media widgets have finished getting created. - /// Note that: - /// - ImageWidgets may have low-res textures - /// - ModelWidgets may not have a model yet (depending on Config.kModelWidgetsWaitForLoad) - public bool CreatingMediaWidgets => - m_loadingTiltModels75 != null || - m_loadingTiltImages75 != null || - m_loadingTiltVideos != null; - - /// Returns true if any widgets are still waiting for their data. - public bool AreMediaWidgetsStillLoading() - { - if (CreatingMediaWidgets) { return true; } - // Widgets have been created, but some may not have their data yet - PolyAssetCatalog pac = App.PolyAssetCatalog; - foreach (var gwd in m_ModelWidgets) - { - Model.Location loc = gwd.WidgetScript.Model.GetLocation(); - if (loc.GetLocationType() == Model.Location.Type.PolyAssetId) - { - switch (pac.GetAssetLoadState(loc.AssetId)) - { - case PolyAssetCatalog.AssetLoadState.Downloading: - case PolyAssetCatalog.AssetLoadState.Loading: - return true; - } - } - } - return false; - } - - // Smaller is better. Invalid objects get negative values. - static float ScoreByAngleAndDistance(GrabWidgetData data) - { - if (!data.m_WidgetScript.Showing) { return -1; } - Transform source = ViewpointScript.Head; - Transform dest = data.m_WidgetObject.transform; - Vector3 delta = (dest.position - source.position); - float dist = delta.magnitude; - return dist / Vector3.Dot(delta.normalized, source.forward); - } - - // Simulate a grab and toss. Use for debugging in monoscopic only. - public void TossNearestWidget() - { - var widget = MediaWidgets.Where(w => w.m_WidgetScript.Showing) - .Select(w => new { score = ScoreByAngleAndDistance(w), widget = w.m_WidgetScript }) - .Where(data => data.score > 0) - .OrderBy(data => data.score) - .FirstOrDefault().widget; - - if (widget != null) - { - if (widget.Pinned) - { - SketchMemoryScript.m_Instance.PerformAndRecordCommand(new PinWidgetCommand(widget, false)); - } - // All media widgets record their movements. - SketchMemoryScript.m_Instance.PerformAndRecordCommand( - new MoveWidgetCommand(widget, widget.LocalTransform, widget.CustomDimension)); - float speed = (App.METERS_TO_UNITS * SketchControlsScript.m_Instance.m_TossThresholdMeters); - Vector3 vLinVel = ViewpointScript.Head.forward * speed; - Vector3 vAngVel = Quaternion.AngleAxis(UnityEngine.Random.Range(0, 360), Vector3.forward) - * ViewpointScript.Head.up * 500; - widget.SetVelocities(vLinVel, vAngVel, widget.transform.position); - } - else - { - Debug.Log("No media in sketch"); - } - } - - public List> ActiveImageWidgets => - m_ImageWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); - public List> ActiveLightWidgets => - m_LightWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); - public List> ActiveModelWidgets => - m_ModelWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); - public List> ActiveVideoWidgets => - m_VideoWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); - public List> ActiveCameraPathWidgets => - m_CameraPathWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); - - } -} +//// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; + +namespace TiltBrush +{ + + // These names are used in our save format, so they must be protected from obfuscation + // Do not change the names of any of them, unless they've never been released. + [Serializable] + public enum StencilType + { + Plane, + Cube, + Sphere, + Capsule, + Cone, + Cylinder, + InteriorDome, + Pyramid, + Ellipsoid + } + + [Serializable] + public struct StencilMapKey + { + public StencilType m_Type; + public StencilWidget m_StencilPrefab; + } + + public struct StencilContactInfo + { + public StencilWidget widget; + public Vector3 pos; + public Vector3 normal; + } + + public class GrabWidgetData + { + public readonly GameObject m_WidgetObject; + public readonly GrabWidget m_WidgetScript; + + // These fields are only valid during a call to GetNearestGrabWidget, + // and are undefined afterwards. Do not use them. + + public bool m_NearController; + // only valid if m_NearController == true + public float m_ControllerScore; + + public GrabWidgetData(GrabWidget widget) + { + m_WidgetScript = widget; + m_WidgetObject = widget.gameObject; + } + // Could maybe get by without this since all users of Clone() don't care if they + // receive a plain old GrabWidgetData or not. + public virtual GrabWidgetData Clone() + { + return new GrabWidgetData(m_WidgetScript) + { + m_NearController = m_NearController, + m_ControllerScore = m_ControllerScore + }; + } + } + + public class TypedWidgetData : GrabWidgetData where T : GrabWidget + { + private readonly T m_typedWidget; + public new T WidgetScript => m_typedWidget; + public TypedWidgetData(T widget) : base(widget) + { + m_typedWidget = widget; + } + public override GrabWidgetData Clone() + { + return new TypedWidgetData(m_typedWidget) + { + m_NearController = m_NearController, + m_ControllerScore = m_ControllerScore + }; + } + } + + public class WidgetManager : MonoBehaviour + { + static public WidgetManager m_Instance; + + [SerializeField] ModelWidget m_ModelWidgetPrefab; + [SerializeField] GameObject m_WidgetPinPrefab; + [SerializeField] ImageWidget m_ImageWidgetPrefab; + [SerializeField] VideoWidget m_VideoWidgetPrefab; + [SerializeField] LightWidget m_LightWidgetPrefab; + [SerializeField] SceneLightGizmo m_SceneLightGizmoPrefab; + [SerializeField] CameraPathWidget m_CameraPathWidgetPrefab; + [SerializeField] private GameObject m_CameraPathPositionKnotPrefab; + [SerializeField] private GameObject m_CameraPathRotationKnotPrefab; + [SerializeField] private GameObject m_CameraPathSpeedKnotPrefab; + [SerializeField] private GameObject m_CameraPathFovKnotPrefab; + [SerializeField] private GameObject m_CameraPathKnotSegmentPrefab; + [SerializeField] private GrabWidgetHome m_Home; + [SerializeField] private GameObject m_HomeHintLinePrefab; + [SerializeField] float m_WidgetSnapAngle = 15.0f; + [SerializeField] float m_GazeMaxAngleFromFacing = 70.0f; + [SerializeField] private float m_PanelFocusActivationScore; + [SerializeField] private float m_ModelVertCountScalar = 1.0f; + + [Header("Stencils")] + [SerializeField] StencilMapKey[] m_StencilMap; + [SerializeField] private float m_StencilAttractDist = 0.5f; + [SerializeField] private float m_StencilAttachHysteresis = 0.1f; + [SerializeField] private string m_StencilLayerName; + [SerializeField] private string m_PinnedStencilLayerName; + + private bool m_WidgetsDormant; + private bool m_InhibitGrabWhileLoading; + + private GameObject m_HomeHintLine; + private MeshFilter m_HomeHintLineMeshFilter; + private Vector3 m_HomeHintLineBaseScale; + + private StencilContactInfo[] m_StencilContactInfos; + private const int m_StencilBucketSize = 16; + private StencilWidget m_ActiveStencil; + private bool m_StencilsDisabled; + + // All grabbable widgets should be in exactly one of these lists. + // Widgets will be in the most specific list. + private List m_GrabWidgets; + private List> m_ModelWidgets; + private List> m_LightWidgets; + private List> m_StencilWidgets; + private List> m_ImageWidgets; + private List> m_VideoWidgets; + private List> m_CameraPathWidgets; + + // These lists are used by the PinTool. They're kept in sync by the + // widget manager, but the PinTool is responsible for their coherency. + private List m_CanBePinnedWidgets; + private List m_CanBeUnpinnedWidgets; + public event Action RefreshPinAndUnpinAction; + + private TiltModels75[] m_loadingTiltModels75; + private TiltLights[] m_loadingTiltLights; + private TiltImages75[] m_loadingTiltImages75; + private TiltVideo[] m_loadingTiltVideos; + + private List m_WidgetsNearBrush; + private List m_WidgetsNearWand; + + // This value is used by SketchMemoryScript to check the sketch against memory limits. + // It's incremented when a model is registered, and decremented when a model is + // unregistered. + private int m_ModelVertCount; + // Similar to above, this value is used to check against memory limits. Images are always + // the same number of verts, however, so this number is scaled by texture size. It's a + // hand-wavey calculation. + private int m_ImageVertCount; + + // Camera path. + [NonSerialized] public bool FollowingPath; + private TypedWidgetData m_CurrentCameraPath; + private bool m_CameraPathsVisible; + private CameraPathTinter m_CameraPathTinter; + + static private Dictionary sm_BatchMap = new Dictionary(); + public bool m_EnableSnapToGuides; + + public StencilWidget ActiveStencil + { + get { return m_ActiveStencil; } + } + + public void ResetActiveStencil() + { + m_ActiveStencil = null; + } + + public int StencilLayerIndex + { + get { return LayerMask.NameToLayer(m_StencilLayerName); } + } + + public int PinnedStencilLayerIndex + { + get { return LayerMask.NameToLayer(m_PinnedStencilLayerName); } + } + + public LayerMask PinnedStencilLayerMask + { + get { return LayerMask.GetMask(m_PinnedStencilLayerName); } + } + + public LayerMask StencilLayerMask + { + get { return LayerMask.GetMask(m_StencilLayerName); } + } + + public float StencilAttractDist + { + get => m_StencilAttractDist; + set + { + m_StencilAttractDist = value; + App.Switchboard.TriggerStencilAttractDistChanged(); + } + } + + public List WidgetsNearBrush + { + get { return m_WidgetsNearBrush; } + } + + public List WidgetsNearWand + { + get { return m_WidgetsNearWand; } + } + + public bool AnyWidgetsToPin + { + get { return m_CanBePinnedWidgets.Count > 0; } + } + + public bool AnyWidgetsToUnpin + { + get { return m_CanBeUnpinnedWidgets.Count > 0; } + } + + public float ModelVertCountScalar + { + get { return m_ModelVertCountScalar; } + } + + public int ImageVertCount + { + get { return m_ImageVertCount; } + } + + public void AdjustModelVertCount(int amount) + { + m_ModelVertCount += amount; + } + + public void AdjustImageVertCount(int amount) + { + m_ImageVertCount += amount; + } + + public int WidgetsVertCount + { + get { return m_ModelVertCount + m_ImageVertCount; } + } + + public bool AnyVideoWidgetActive => m_VideoWidgets.Any(x => x.m_WidgetObject.activeSelf); + + public bool AnyCameraPathWidgetsActive => + m_CameraPathWidgets.Any(x => x.m_WidgetObject.activeSelf); + + public CameraPathTinter PathTinter { get => m_CameraPathTinter; } + + // Returns the associated widget for the given batchId. + // Returns null if key doesn't exist. + public GrabWidget GetBatch(ushort batchId) + { + if (sm_BatchMap.ContainsKey(batchId)) + { + return sm_BatchMap[batchId]; + } + return null; + } + + public void AddWidgetToBatchMap(GrabWidget widget, ushort batchId) + { + Debug.Assert(!sm_BatchMap.ContainsKey(batchId)); + sm_BatchMap.Add(batchId, widget); + } + + void Awake() + { + m_Instance = this; + } + + public void Init() + { + m_CameraPathTinter = gameObject.AddComponent(); + + m_GrabWidgets = new List(); + m_ModelWidgets = new List>(); + m_LightWidgets = new List>(); + m_StencilWidgets = new List>(); + m_ImageWidgets = new List>(); + m_VideoWidgets = new List>(); + m_CameraPathWidgets = new List>(); + + m_CanBePinnedWidgets = new List(); + m_CanBeUnpinnedWidgets = new List(); + + m_WidgetsNearBrush = new List(); + m_WidgetsNearWand = new List(); + + m_Home.Init(); + m_Home.SetFixedPosition(Vector3.zero); + m_HomeHintLine = (GameObject)Instantiate(m_HomeHintLinePrefab); + m_HomeHintLineMeshFilter = m_HomeHintLine.GetComponent(); + m_HomeHintLineBaseScale = m_HomeHintLine.transform.localScale; + m_HomeHintLine.transform.parent = transform; + m_HomeHintLine.SetActive(false); + + m_StencilContactInfos = new StencilContactInfo[m_StencilBucketSize]; + m_StencilsDisabled = false; + + FollowingPath = false; + m_CameraPathsVisible = false; + } + + public ModelWidget ModelWidgetPrefab { get { return m_ModelWidgetPrefab; } } + public ImageWidget ImageWidgetPrefab { get { return m_ImageWidgetPrefab; } } + public VideoWidget VideoWidgetPrefab { get { return m_VideoWidgetPrefab; } } + public LightWidget LightWidgetPrefab { get { return m_LightWidgetPrefab; } } + public SceneLightGizmo SceneLightGizmoPrefab { get { return m_SceneLightGizmoPrefab; } } + public CameraPathWidget CameraPathWidgetPrefab { get { return m_CameraPathWidgetPrefab; } } + public GameObject CameraPathPositionKnotPrefab { get { return m_CameraPathPositionKnotPrefab; } } + public GameObject CameraPathRotationKnotPrefab { get { return m_CameraPathRotationKnotPrefab; } } + public GameObject CameraPathSpeedKnotPrefab { get { return m_CameraPathSpeedKnotPrefab; } } + public GameObject CameraPathFovKnotPrefab { get { return m_CameraPathFovKnotPrefab; } } + public GameObject CameraPathKnotSegmentPrefab { get { return m_CameraPathKnotSegmentPrefab; } } + + public IEnumerable ActiveGrabWidgets + { + get + { + if (m_InhibitGrabWhileLoading) + { + // Returns only widgets that are not part of the sketch + return m_GrabWidgets.Where(x => x.m_WidgetObject.activeSelf); + } + return GetAllActiveGrabWidgets(); + } + } + + private IEnumerable GetAllActiveGrabWidgets() + { + for (int i = 0; i < m_GrabWidgets.Count; ++i) + { + if (m_GrabWidgets[i].m_WidgetObject.activeSelf) + { + yield return m_GrabWidgets[i]; + } + } + for (int i = 0; i < m_ModelWidgets.Count; ++i) + { + if (m_ModelWidgets[i].m_WidgetObject.activeSelf) + { + yield return m_ModelWidgets[i]; + } + } + for (int i = 0; i < m_LightWidgets.Count; ++i) + { + if (m_LightWidgets[i].m_WidgetObject.activeSelf) + { + yield return m_LightWidgets[i]; + } + } + for (int i = 0; i < m_StencilWidgets.Count; ++i) + { + if (m_StencilWidgets[i].m_WidgetObject.activeSelf) + { + yield return m_StencilWidgets[i]; + } + } + for (int i = 0; i < m_ImageWidgets.Count; ++i) + { + if (m_ImageWidgets[i].m_WidgetObject.activeSelf) + { + yield return m_ImageWidgets[i]; + } + } + for (int i = 0; i < m_VideoWidgets.Count; ++i) + { + if (m_VideoWidgets[i].m_WidgetObject.activeSelf) + { + yield return m_VideoWidgets[i]; + } + } + for (int i = 0; i < m_CameraPathWidgets.Count; ++i) + { + if (m_CameraPathWidgets[i].m_WidgetObject.activeInHierarchy) + { + yield return m_CameraPathWidgets[i]; + } + } + } + + public IEnumerable MediaWidgets + { + get + { + IEnumerable ret = m_ModelWidgets; + return ret + .Concat(m_ImageWidgets) + .Concat(m_VideoWidgets) + .Concat(m_LightWidgets); + } + } + + public IEnumerable> CameraPathWidgets => + m_CameraPathWidgets.Where(x => x.m_WidgetObject.activeSelf); + + public TypedWidgetData GetCurrentCameraPath() => m_CurrentCameraPath; + + public void SetCurrentCameraPath(CameraPathWidget path) + { + // Early out if we're trying to set the path to the already current path. + if (m_CurrentCameraPath != null && m_CurrentCameraPath.WidgetScript == path) + { + return; + } + + for (int i = 0; i < m_CameraPathWidgets.Count; ++i) + { + if (m_CameraPathWidgets[i].m_WidgetScript == path) + { + FollowingPath = false; + SetCurrentCameraPath_Internal(m_CameraPathWidgets[i]); + App.Switchboard.TriggerCurrentCameraPathChanged(); + return; + } + } + } + + public void ValidateCurrentCameraPath() + { + if (m_CurrentCameraPath == null || + m_CurrentCameraPath.WidgetScript == null || + !m_CurrentCameraPath.m_WidgetObject.activeSelf) + { + var prevPath = m_CurrentCameraPath; + for (int i = 0; i < m_CameraPathWidgets.Count; ++i) + { + if (m_CameraPathWidgets[i] != prevPath && + m_CameraPathWidgets[i].m_WidgetObject.activeSelf) + { + SetCurrentCameraPath_Internal(m_CameraPathWidgets[i]); + return; + } + } + } + } + + void SetCurrentCameraPath_Internal(TypedWidgetData cp) + { + if (m_CurrentCameraPath != null && m_CurrentCameraPath.WidgetScript != null) + { + m_CurrentCameraPath.WidgetScript.SetAsActivePath(false); + } + m_CurrentCameraPath = cp; + if (m_CurrentCameraPath != null && m_CurrentCameraPath.WidgetScript != null) + { + m_CurrentCameraPath.WidgetScript.SetAsActivePath(true); + } + } + + public bool CanRecordCurrentCameraPath() + { + if (SketchSurfacePanel.m_Instance.GetCurrentToolType() != BaseTool.ToolType.MultiCamTool) + { + if (m_CameraPathsVisible && m_CurrentCameraPath != null) + { + CameraPathWidget cpw = m_CurrentCameraPath.WidgetScript; + return (cpw != null) && cpw.gameObject.activeSelf && cpw.Path.NumPositionKnots > 1; + } + } + return false; + } + + // The following methods use indexes to reference camera paths. Because the list of + // camera path widgets can have inactive paths (if they're deleted), these lists may + // have holes. Use caution when using these methods. + // The reason we need these methods is because our UI buttons work with SketchControls + // global commands, which can be modified with generic integer parameters. In those + // cases, we can't pass a CameraPathWidget object. + public CameraPathWidget GetNthActiveCameraPath(int nth) + { + var activeCameraPathWidgets = m_CameraPathWidgets.Where(x => x.m_WidgetObject.activeSelf); + foreach (var cpw in activeCameraPathWidgets) + { + if (nth == 0) + { + return cpw.WidgetScript; + } + --nth; + } + return null; + } + + public bool IsCameraPathAtIndexCurrent(int pathIndex) + { + CameraPathWidget cpw = (m_CurrentCameraPath != null) ? cpw = m_CurrentCameraPath.WidgetScript : + null; + if (cpw == null) { return false; } + return GetNthActiveCameraPath(pathIndex) == m_CurrentCameraPath.WidgetScript; + } + + public int? GetIndexOfCameraPath(CameraPathWidget path) + { + int index = 0; + for (int i = 0; i < m_CameraPathWidgets.Count; ++i) + { + if (m_CameraPathWidgets[i].m_WidgetObject.activeSelf) + { + if (m_CameraPathWidgets[i].WidgetScript == path) + { + return index; + } + ++index; + } + } + return null; + } + + public CameraPathWidget CreatePathWidget() + { + CreateWidgetCommand command = + new CreateWidgetCommand(m_CameraPathWidgetPrefab, TrTransform.identity); + SketchMemoryScript.m_Instance.PerformAndRecordCommand(command); + return m_CameraPathWidgets.Last().WidgetScript; + } + + public bool AnyActivePathHasAKnot() + { + var datas = CameraPathWidgets; + foreach (TypedWidgetData data in datas) + { + if (data.WidgetScript.Path.NumPositionKnots > 0) + { + return true; + } + } + return false; + } + + public void DeleteCameraPath(GrabWidget cameraPathWidgetScript) + { + if (cameraPathWidgetScript != null) + { + // We don't *really* delete it, we just hide it. + SketchMemoryScript.m_Instance.PerformAndRecordCommand( + new HideWidgetCommand(cameraPathWidgetScript)); + FollowingPath = false; + } + + // If our current path is null or inactive, it means we have no camera paths. In that + // instance, if the camera path tool is active, reset back to our default tool. + // I'm doing this because if we leave the camera path tool active, the camera path + // panel shows the button highlighted, which affects the user's flow for being + // invited to start a path. It looks weird. + if (m_CurrentCameraPath == null || !m_CurrentCameraPath.WidgetScript.gameObject.activeSelf) + { + if (SketchSurfacePanel.m_Instance.ActiveToolType == BaseTool.ToolType.CameraPathTool) + { + SketchSurfacePanel.m_Instance.EnableDefaultTool(); + } + } + } + + public bool CameraPathsVisible + { + get { return m_CameraPathsVisible; } + set + { + if (value != m_CameraPathsVisible) + { + m_CameraPathsVisible = value; + + // Camera paths. + for (int i = 0; i < m_CameraPathWidgets.Count; ++i) + { + CameraPathWidget cpw = m_CameraPathWidgets[i].m_WidgetScript as CameraPathWidget; + if (cpw.gameObject.activeSelf) + { + cpw.Path.SetKnotsActive(m_CameraPathsVisible); + } + } + + if (!m_CameraPathsVisible) + { + // Flip back to default tool if we turned off paths. + if (SketchSurfacePanel.m_Instance.ActiveToolType == BaseTool.ToolType.CameraPathTool) + { + SketchSurfacePanel.m_Instance.EnableDefaultTool(); + } + FollowingPath = false; + } + + App.Switchboard.TriggerCameraPathVisibilityChanged(); + } + } + } + + public bool HasSelectableWidgets() + { + return + (m_ModelWidgets.Count > 0) || + (m_LightWidgets.Count > 0) || + (m_ImageWidgets.Count > 0) || + (m_VideoWidgets.Count > 0) || + (!m_StencilsDisabled && m_StencilWidgets.Count > 0); + } + + public bool HasExportableContent(Cloud cloud) + { + switch (cloud) + { + case Cloud.Sketchfab: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + ExportableModelWidgets.Any(w => w.gameObject.activeSelf) || + ImageWidgets.Any(w => w.gameObject.activeSelf); + default: + return SketchMemoryScript.m_Instance.HasVisibleObjects() || + App.Config.m_EnableReferenceModelExport && + ExportableModelWidgets.Any( + w => w.gameObject.activeSelf && + w.Model.GetLocation().GetLocationType() == Model.Location.Type.IcosaAssetId); + } + } + + public bool HasNonExportableContent(Cloud cloud) + { + switch (cloud) + { + case Cloud.Sketchfab: + return VideoWidgets.Any(w => w.gameObject.activeSelf); + default: + return NonExportableModelWidgets.Any(w => w.gameObject.activeSelf) || + ImageWidgets.Any(w => w.gameObject.activeSelf) || + VideoWidgets.Any(w => w.gameObject.activeSelf); + } + } + + public bool StencilsDisabled + { + get { return m_StencilsDisabled; } + set + { + if (value != m_StencilsDisabled) + { + // Flip flag and visuals for all stencils. + for (int i = 0; i < m_StencilWidgets.Count; ++i) + { + StencilWidget sw = m_StencilWidgets[i].WidgetScript; + if (sw) + { + sw.RefreshVisibility(value); + } + } + m_ActiveStencil = null; + } + m_StencilsDisabled = value; + RefreshPinAndUnpinLists(); + } + } + + private static string CanonicalizeForCompare(string path) + { + return path.ToLower().Replace("\\", "/"); + } + + // Shortens full file paths to "Media Library/[Models | Images]/thing" + // Input: absolute path + // Returns: path starting from Media Library/ (e.g. "Media Library/[Models | Images]/thing") + // or null if path does not lead to Media Library + // Throws: ArgumentException if path is not full + public static string GetPathRootedAtMedia(string path) + { + if (!System.IO.Path.IsPathRooted(path)) + { + throw new ArgumentException("Path is not rooted"); + } + var media = App.MediaLibraryPath(); + if (CanonicalizeForCompare(path).StartsWith(CanonicalizeForCompare(media))) + { + return "Media Library" + path.Substring(media.Length); + } + return null; + } + + // Returns path after Media Library/Models for models only + // Input: absolute path + // Returns: path starting after Models/ or null if the path is not to the Models directory + public static string GetModelSubpath(string fullPath) + { + string media = GetPathRootedAtMedia(fullPath); + string modelPath = "Media Library/Models/"; + if (media == null || !media.StartsWith(modelPath)) + { + return null; + } + return media.Substring(modelPath.Length); + } + + // Used only at .tilt-loading time + public void SetDataFromTilt(TiltModels75[] value) + { + m_loadingTiltModels75 = value; + } + + // Used only at .tilt-loading time + public void SetDataFromTilt(TiltImages75[] value) + { + m_loadingTiltImages75 = value; + } + + // Used only at .tilt-loading time + public void SetDataFromTilt(TiltLights[] value) + { + m_loadingTiltLights = value; + } + + public void SetDataFromTilt(CameraPathMetadata[] cameraPaths) + { + for (int i = 0; i < cameraPaths.Length; ++i) + { + CameraPathWidget.CreateFromSaveData(cameraPaths[i]); + } + } + + public void SetDataFromTilt(TiltVideo[] value) + { + m_loadingTiltVideos = value; + } + + public WidgetPinScript GetWidgetPin() + { + GameObject pinObj = Instantiate(m_WidgetPinPrefab); + pinObj.transform.parent = transform; + return pinObj.GetComponent(); + } + + public void DestroyWidgetPin(WidgetPinScript pin) + { + if (pin != null) + { + Destroy(pin.gameObject); + } + } + + // Set the position that widgets can snap to in the current environment + public void SetHomePosition(Vector3 position) + { + m_Home.SetFixedPosition(position); + } + + // Dormant models are still grabbable but visuals/haptics are disabled + public bool WidgetsDormant + { + get { return m_WidgetsDormant; } + set + { + m_WidgetsDormant = value; + Shader.SetGlobalFloat("_WidgetsDormant", value ? 0 : 1); + } + } + + public float WidgetSnapAngle + { + get { return m_WidgetSnapAngle; } + } + + public bool IsOriginHomeWithinSnapRange(Vector3 pos) + { + return m_Home.WithinRange(pos); + } + + public Transform GetHomeXf() + { + return m_Home.transform; + } + + public void SetHomeOwner(Transform owner) + { + m_Home.SetOwner(owner); + m_Home.Reset(); + } + + public void ClearHomeOwner() + { + m_Home.SetOwner(null); + m_Home.gameObject.SetActive(false); + m_HomeHintLine.SetActive(false); + } + + public void EnableHome(bool bEnable) + { + m_Home.gameObject.SetActive(bEnable); + if (!bEnable) + { + m_HomeHintLine.SetActive(false); + } + } + + public void LoadingState(bool bEnter) + { + m_InhibitGrabWhileLoading = bEnter; + } + + public void UpdateHomeHintLine(Vector3 vModelSnapPos) + { + if (!m_Home.WithinRange(vModelSnapPos) && m_Home.WithinHintRange(vModelSnapPos)) + { + // Enable, position, and scale hint line. + m_HomeHintLine.SetActive(true); + Vector3 vHomeToModel = vModelSnapPos - m_Home.transform.position; + m_HomeHintLine.transform.position = m_Home.transform.position + + (vHomeToModel * 0.5f); + m_HomeHintLine.transform.up = vHomeToModel.normalized; + + Vector3 vScale = m_HomeHintLineBaseScale; + vScale.y = vHomeToModel.magnitude * 0.5f; + m_HomeHintLine.transform.localScale = vScale; + + App.Instance.SelectionEffect.RegisterMesh(m_HomeHintLineMeshFilter); + m_Home.RenderHighlight(); + } + else + { + // Disable the line. + m_HomeHintLine.SetActive(false); + } + } + + public bool MagnetizeToStencils(ref Vector3 pos, ref Quaternion rot, IEnumerable stencilsToIgnore = null) + { + // Early out if stencils are disabled. + if (m_StencilsDisabled && !App.UserConfig.Flags.GuideToggleVisiblityOnly) + { + return false; + } + + Vector3 samplePos = pos; + + bool stencilWasUsed = false; + + // If we're painting, we have a different path for magnetization that relies on the + // previous frame. + if (PointerManager.m_Instance.IsLineEnabled()) + { + // If we don't have an active stencil, we're done here. + if (m_ActiveStencil == null) + { + return false; + } + + // Using the 0 index of m_StencilContactInfos as a shortcut. + m_StencilContactInfos[0].widget = m_ActiveStencil; + FindClosestPointOnWidgetSurface(pos, ref m_StencilContactInfos[0]); + + m_ActiveStencil.SetInUse(true); + pos = m_StencilContactInfos[0].pos; + rot = Quaternion.LookRotation(m_StencilContactInfos[0].normal); + stencilWasUsed = true; + } + else + { + StencilWidget prevStencil = m_ActiveStencil; + float fPrevScore = -m_StencilAttachHysteresis; + int iPrevIndex = -1; + m_ActiveStencil = null; + + // Run through the overlap list and find the best stencil to stick to. + int iPrimaryIndex = -1; + float fBestScore = 0; + int sIndex = 0; + + IEnumerable widgetsToCheck = m_StencilWidgets.Select(w => w.WidgetScript); + if (stencilsToIgnore != null) widgetsToCheck = widgetsToCheck.Except(stencilsToIgnore); + foreach (var sw in widgetsToCheck) + { + Debug.Assert(sw != null); + + // Reset tint + sw.SetInUse(false); + + // Does a rough check to see if the stencil might overlap. OverlapSphereNonAlloc is + // shockingly slow, which is why we don't use it. + Collider collider = sw.GrabCollider; + float centerDist = (collider.bounds.center - samplePos).sqrMagnitude; + if (centerDist > + (StencilAttractDist * StencilAttractDist + collider.bounds.extents.sqrMagnitude)) + { + continue; + } + + m_StencilContactInfos[sIndex].widget = sw; + + FindClosestPointOnWidgetSurface(samplePos, ref m_StencilContactInfos[sIndex]); + + // Find out how far we are from this point and save it as a score. + float distToSurfactPoint = (m_StencilContactInfos[sIndex].pos - samplePos).magnitude; + float score = 1.0f - (distToSurfactPoint / StencilAttractDist); + if (score > fBestScore) + { + iPrimaryIndex = sIndex; + fBestScore = score; + m_ActiveStencil = m_StencilContactInfos[sIndex].widget; + } + + if (m_StencilContactInfos[sIndex].widget == prevStencil) + { + fPrevScore = score; + iPrevIndex = sIndex; + } + + if (++sIndex == m_StencilBucketSize) + { + break; + } + } + + // If we are switching between stencils, check to see if we're switching "enough". + if (iPrevIndex != -1 && m_ActiveStencil != null && prevStencil != m_ActiveStencil) + { + if (fPrevScore + m_StencilAttachHysteresis > fBestScore) + { + m_ActiveStencil = prevStencil; + iPrimaryIndex = iPrevIndex; + } + } + + // If we found a good stencil, return the surface collision transform. + if (m_ActiveStencil != null) + { + m_ActiveStencil.SetInUse(true); + pos = m_StencilContactInfos[iPrimaryIndex].pos; + var up = rot * Vector3.up; + rot = Quaternion.LookRotation(m_StencilContactInfos[iPrimaryIndex].normal, up); + stencilWasUsed = true; + } + + if (prevStencil != m_ActiveStencil) + { + PointerManager.m_Instance.DisablePointerPreviewLine(); + } + } + + return stencilWasUsed; + } + + bool FindClosestPointOnCollider( + Ray rRay, Collider collider, out RaycastHit rHitInfo, float fDist) + { + rHitInfo = new RaycastHit(); + return collider.Raycast(rRay, out rHitInfo, fDist); + } + + void FindClosestPointOnWidgetSurface(Vector3 pos, ref StencilContactInfo info) + { + info.widget.FindClosestPointOnSurface(pos, out info.pos, out info.normal); + } + + public bool ShouldUpdateCollisions() + { + return ActiveGrabWidgets.Any(elt => elt.m_WidgetScript.IsCollisionEnabled()); + } + + public IEnumerable ModelWidgets + { + get + { + return m_ModelWidgets + .Select(w => w == null ? null : w.WidgetScript) + .Where(w => w != null); + } + } + + public IEnumerable LightWidgets + { + get + { + return m_LightWidgets + .Select(w => w == null ? null : w.WidgetScript) + .Where(w => w != null); + } + } + + public IEnumerable VideoWidgets + { + get + { + return m_VideoWidgets + .Select(w => w == null ? null : w.WidgetScript) + .Where(w => w != null); + } + } + + public IEnumerable NonExportableModelWidgets + { + get + { + return m_ModelWidgets + .Select(w => w == null ? null : w.WidgetScript) + .Where(w => w != null).Where(w => !w.Model.AllowExport); + } + } + + public IEnumerable ExportableModelWidgets + { + get + { + return m_ModelWidgets + .Select(w => w == null ? null : w.WidgetScript) + .Where(w => w != null).Where(w => w.Model.AllowExport); + } + } + + public IEnumerable StencilWidgets + { + get + { + return m_StencilWidgets + .Select(d => d == null ? null : d.WidgetScript) + .Where(w => w != null); + } + } + + public StencilWidget GetStencilPrefab(StencilType type) + { + for (int i = 0; i < m_StencilMap.Length; ++i) + { + if (m_StencilMap[i].m_Type == type) + { + return m_StencilMap[i].m_StencilPrefab; + } + } + throw new ArgumentException(type.ToString()); + } + + public IEnumerable ImageWidgets + { + get + { + return m_ImageWidgets + .Select(d => d == null ? null : d.m_WidgetScript as ImageWidget) + .Where(w => w != null); + } + } + + public List GetAllUnselectedActiveWidgets() + { + List widgets = new List(); + GetUnselectedActiveWidgetsInList(m_ModelWidgets); + GetUnselectedActiveWidgetsInList(m_LightWidgets); + GetUnselectedActiveWidgetsInList(m_ImageWidgets); + GetUnselectedActiveWidgetsInList(m_VideoWidgets); + if (!m_StencilsDisabled) + { + GetUnselectedActiveWidgetsInList(m_StencilWidgets); + } + return widgets; + + void GetUnselectedActiveWidgetsInList(List> list) where T : GrabWidget + { + for (int i = 0; i < list.Count; ++i) + { + GrabWidget w = list[i].m_WidgetScript; + if (!w.Pinned && w.transform.parent == App.Scene.ActiveCanvas.transform && + w.gameObject.activeSelf) + { + widgets.Add(w); + } + } + } + } + + public void RefreshPinAndUnpinLists() + { + if (RefreshPinAndUnpinAction != null) + { + m_CanBePinnedWidgets.Clear(); + m_CanBeUnpinnedWidgets.Clear(); + + RefreshPinUnpinWidgetList(m_ModelWidgets); + RefreshPinUnpinWidgetList(m_LightWidgets); + RefreshPinUnpinWidgetList(m_ImageWidgets); + RefreshPinUnpinWidgetList(m_VideoWidgets); + RefreshPinUnpinWidgetList(m_StencilWidgets); + + RefreshPinAndUnpinAction(); + } + + // New in C# 7 - local functions! + void RefreshPinUnpinWidgetList(List> widgetList) where T : GrabWidget + { + foreach (var widgetData in widgetList) + { + var widget = widgetData.WidgetScript; + if (widget.gameObject.activeSelf && widget.AllowPinning) + { + if (widget.Pinned) + { + m_CanBeUnpinnedWidgets.Add(widget); + } + else + { + m_CanBePinnedWidgets.Add(widget); + } + } + } + } + } + + public void RegisterHighlightsForPinnableWidgets(bool pinnable) + { + List widgets = pinnable ? m_CanBePinnedWidgets : m_CanBeUnpinnedWidgets; + for (int i = 0; i < widgets.Count; ++i) + { + GrabWidget w = widgets[i]; + // If stencils are disabled, don't highlight them, cause we can interact with 'em. + if (WidgetManager.m_Instance.StencilsDisabled) + { + if (w is StencilWidget) + { + continue; + } + } + w.RegisterHighlight(); + } + } + + public void RegisterGrabWidget(GameObject rWidget) + { + // Find b/29514616 + if (ReferenceEquals(rWidget, null)) + { + throw new ArgumentNullException("rWidget"); + } + else if (rWidget == null) + { + throw new ArgumentNullException("rWidget(2)"); + } + GrabWidget generic = rWidget.GetComponent(); + if (generic == null) + { + throw new InvalidOperationException($"Object {rWidget.name} is not a GrabWidget"); + } + + if (generic is ModelWidget mw) + { + m_ModelWidgets.Add(new TypedWidgetData(mw)); + } + else if (generic is LightWidget light) + { + m_LightWidgets.Add(new TypedWidgetData(light)); + } + else if (generic is StencilWidget stencil) + { + m_StencilWidgets.Add(new TypedWidgetData(stencil)); + } + else if (generic is ImageWidget image) + { + m_ImageWidgets.Add(new TypedWidgetData(image)); + } + else if (generic is VideoWidget video) + { + m_VideoWidgets.Add(new TypedWidgetData(video)); + } + else if (generic is CameraPathWidget cpw) + { + m_CameraPathWidgets.Add(new TypedWidgetData(cpw)); + } + else + { + m_GrabWidgets.Add(new GrabWidgetData(generic)); + } + + RefreshPinAndUnpinLists(); + } + + // Returns true if a widget was removed + static bool RemoveFrom(List list, GameObject rWidget) + where T : GrabWidgetData + { + int idx = list.FindIndex((data) => data.m_WidgetObject == rWidget); + if (idx != -1) + { + list.RemoveAt(idx); + return true; + } + return false; + } + + public void UnregisterGrabWidget(GameObject rWidget) + { + // Get this widget's batchId out of the map. + sm_BatchMap.Remove(rWidget.GetComponent().BatchId); + + // Pull out of pin tool lists. + RefreshPinAndUnpinLists(); + + // Decrement model vert count if we're a model widget. + ModelWidget mw = rWidget.GetComponent() as ModelWidget; + if (mw != null) + { + m_ModelVertCount -= mw.NumVertsTrackedByWidgetManager; + } + // Same with image widget. + ImageWidget iw = rWidget.GetComponent() as ImageWidget; + if (iw != null) + { + m_ImageVertCount -= iw.NumVertsTrackedByWidgetManager; + } + + if (RemoveFrom(m_ModelWidgets, rWidget)) { return; } + if (RemoveFrom(m_LightWidgets, rWidget)) { return; } + if (RemoveFrom(m_StencilWidgets, rWidget)) { return; } + if (RemoveFrom(m_ImageWidgets, rWidget)) { return; } + if (RemoveFrom(m_VideoWidgets, rWidget)) { return; } + if (RemoveFrom(m_CameraPathWidgets, rWidget)) { return; } + RemoveFrom(m_GrabWidgets, rWidget); + } + + public ImageWidget GetNearestImage(Vector3 pos, float maxDepth, ref Vector3 sampleLoc) + { + ImageWidget bestImage = null; + float leastDistance = float.MaxValue; + foreach (var im in ImageWidgets.Where(i => i.gameObject.activeSelf)) + { + Vector3 dropper_QS; + Vector3 dropper_GS = pos; + Matrix4x4 xfQuadFromGlobal = im.m_ImageQuad.transform.worldToLocalMatrix; + dropper_QS = xfQuadFromGlobal.MultiplyPoint3x4(dropper_GS); + if (Mathf.Abs(dropper_QS.z) < leastDistance + && Mathf.Abs(dropper_QS.x) <= 0.5f + && Mathf.Abs(dropper_QS.y) <= 0.5f + && Mathf.Abs(dropper_QS.z) <= maxDepth / Mathf.Abs(im.GetSignedWidgetSize())) + { + bestImage = im; + leastDistance = Mathf.Abs(dropper_QS.z); + sampleLoc = dropper_QS; + } + } + return bestImage; + } + + public void RefreshNearestWidgetLists(Ray currentGazeRay, int currentGazeObject) + { + m_WidgetsNearBrush.Clear(); + UpdateNearestGrabsFor(InputManager.ControllerName.Brush, currentGazeRay, currentGazeObject); + foreach (GrabWidgetData widget in ActiveGrabWidgets) + { + if (widget.m_NearController) + { + // Deep copy. + m_WidgetsNearBrush.Add(widget.Clone()); + } + } + + m_WidgetsNearWand.Clear(); + UpdateNearestGrabsFor(InputManager.ControllerName.Wand, currentGazeRay, currentGazeObject); + foreach (GrabWidgetData widget in ActiveGrabWidgets) + { + if (widget.m_NearController) + { + m_WidgetsNearWand.Add(widget.Clone()); + } + } + } + + // Helper for RefreshNearestWidgetLists + void UpdateNearestGrabsFor( + InputManager.ControllerName name, Ray currentGazeRay, int currentGazeObject) + { + // Reset hit flags. + foreach (var elt in ActiveGrabWidgets) + { + elt.m_NearController = false; + elt.m_ControllerScore = -1.0f; + } + + Vector3 controllerPos = Vector3.zero; + if (name == InputManager.ControllerName.Brush) + { + controllerPos = InputManager.m_Instance.GetBrushControllerAttachPoint().position; + } + else if (name == InputManager.ControllerName.Wand) + { + controllerPos = InputManager.m_Instance.GetWandControllerAttachPoint().position; + } + else + { + Debug.LogError("UpdateNearestGrabsFor() only supports Brush and Wand controller types."); + } + + // Figure out if controller is in view frustum. If it isn't, don't allow widget grabs. + Vector3 vToController = controllerPos - currentGazeRay.origin; + vToController.Normalize(); + if (Vector3.Angle(vToController, currentGazeRay.direction) > m_GazeMaxAngleFromFacing) + { + return; + } + + BasePanel gazePanel = null; + if (currentGazeObject > -1) + { + gazePanel = PanelManager.m_Instance.GetPanel(currentGazeObject); + } + + foreach (var data in ActiveGrabWidgets) + { + if (!data.m_WidgetScript.enabled) + { + continue; + } + if (m_StencilsDisabled && data.m_WidgetScript is StencilWidget) + { + continue; + } + if (SelectionManager.m_Instance.ShouldRemoveFromSelection() && + !data.m_WidgetScript.CanGrabDuringDeselection()) + { + continue; + } + if (SelectionManager.m_Instance.IsWidgetSelected(data.m_WidgetScript)) + { + continue; + } + float score = data.m_WidgetScript.GetActivationScore(controllerPos, name); + if (score < m_PanelFocusActivationScore && name == InputManager.ControllerName.Brush && + gazePanel && data.m_WidgetObject == gazePanel.gameObject) + { + // If the brush is pointing at a panel, make sure that the panel will be the widget grabbed + score = m_PanelFocusActivationScore; + } + if (score < 0) + { + continue; + } + + data.m_NearController = true; + data.m_ControllerScore = score; + } + } + + public float DistanceToNearestWidget(Ray ray) + { + // If we're in controller mode, find the nearest colliding widget that might get in our way. + float fNearestWidget = 99999.0f; + foreach (var elt in ActiveGrabWidgets) + { + float fWidgetDist = 0.0f; + if (elt.m_WidgetScript.DistanceToCollider(ray, out fWidgetDist)) + { + fNearestWidget = Mathf.Min(fNearestWidget, fWidgetDist); + } + } + return fNearestWidget; + } + + public void DestroyAllWidgets() + { + DestroyWidgetList(m_ModelWidgets); + DestroyWidgetList(m_LightWidgets); + DestroyWidgetList(m_ImageWidgets); + DestroyWidgetList(m_VideoWidgets); + DestroyWidgetList(m_StencilWidgets); + DestroyWidgetList(m_CameraPathWidgets, false); + SetCurrentCameraPath_Internal(null); + App.Switchboard.TriggerAllWidgetsDestroyed(); + + void DestroyWidgetList(List> widgetList, + bool hideBeforeDestroy = true) where T : GrabWidget + { + while (widgetList.Count > 0) + { + GrabWidget widget = widgetList[0].m_WidgetScript; + GameObject obj = widgetList[0].m_WidgetObject; + if (hideBeforeDestroy) { widget.Hide(); } + widget.OnPreDestroy(); + UnregisterGrabWidget(obj); + Destroy(obj); + } + } + } + + /// Upon return: + /// - ImageWidgets have at least an icon-sized Texture2D; it will be mutated with + /// the fullres texture data some time later. + /// - ModelWidgets may have a dummy, invisible Model instead of the actual Model they want; + /// the Model will be automatically replaced with the loaded Model some time later. + public IEnumerator CreateMediaWidgetsFromLoadDataCoroutine() + { + if (m_loadingTiltModels75 != null) + { + OverlayManager.m_Instance.RefuseProgressBarChanges(true); + + if (App.Config.kModelWidgetsWaitForLoad) + { + var assetIds = m_loadingTiltModels75 + .Select(tm => tm.AssetId).Where(aid => aid != null).ToArray(); + // Kick off a bunch of loads... + foreach (var assetId in assetIds) + { + if (App.IcosaAssetCatalog.GetAssetLoadState(assetId) + != IcosaAssetCatalog.AssetLoadState.Loaded) + { + App.IcosaAssetCatalog.RequestModelLoad(assetId, "tiltload"); + } + } + // ... and wait for them to complete + // No widgets have been created yet, so we can't use AreMediaWidgetsStillLoading. + bool IsLoading(string assetId) + { + var state = App.IcosaAssetCatalog.GetAssetLoadState(assetId); + return (state == IcosaAssetCatalog.AssetLoadState.Downloading || + state == IcosaAssetCatalog.AssetLoadState.Loading); + } + while (assetIds.Any(IsLoading)) + { + yield return null; + } + } + + for (int i = 0; i < m_loadingTiltModels75.Length; i++) + { + ModelWidget.CreateFromSaveData(m_loadingTiltModels75[i]); + OverlayManager.m_Instance.UpdateProgress( + (float)(i + 1) / m_loadingTiltModels75.Length, true); + } + OverlayManager.m_Instance.RefuseProgressBarChanges(false); + m_loadingTiltModels75 = null; + } + ModelCatalog.m_Instance.PrintMissingModelWarnings(); + if (m_loadingTiltLights != null) + { + foreach (var light in m_loadingTiltLights) + { + LightWidget.FromTiltLight(light); + } + m_loadingTiltLights = null; + } + if (m_loadingTiltImages75 != null) + { + foreach (TiltImages75 import in m_loadingTiltImages75) + { + // TODO: FromTiltImage should take advantage of being called by a coroutine + // so it can avoid calling ReferenceImage.SynchronousLoad() + ImageWidget.FromTiltImage(import); + } + m_loadingTiltImages75 = null; + } + if (m_loadingTiltVideos != null) + { + foreach (var video in m_loadingTiltVideos) + { + VideoWidget.FromTiltVideo(video); + } + m_loadingTiltVideos = null; + } + yield break; + } + + /// Returns true when all media widgets have finished getting created. + /// Note that: + /// - ImageWidgets may have low-res textures + /// - ModelWidgets may not have a model yet (depending on Config.kModelWidgetsWaitForLoad) + public bool CreatingMediaWidgets => + m_loadingTiltModels75 != null || + m_loadingTiltImages75 != null || + m_loadingTiltVideos != null; + + /// Returns true if any widgets are still waiting for their data. + public bool AreMediaWidgetsStillLoading() + { + if (CreatingMediaWidgets) { return true; } + // Widgets have been created, but some may not have their data yet + IcosaAssetCatalog pac = App.IcosaAssetCatalog; + foreach (var gwd in m_ModelWidgets) + { + Model.Location loc = gwd.WidgetScript.Model.GetLocation(); + if (loc.GetLocationType() == Model.Location.Type.IcosaAssetId) + { + switch (pac.GetAssetLoadState(loc.AssetId)) + { + case IcosaAssetCatalog.AssetLoadState.Downloading: + case IcosaAssetCatalog.AssetLoadState.Loading: + return true; + } + } + } + return false; + } + + // Smaller is better. Invalid objects get negative values. + static float ScoreByAngleAndDistance(GrabWidgetData data) + { + if (!data.m_WidgetScript.Showing) { return -1; } + Transform source = ViewpointScript.Head; + Transform dest = data.m_WidgetObject.transform; + Vector3 delta = (dest.position - source.position); + float dist = delta.magnitude; + return dist / Vector3.Dot(delta.normalized, source.forward); + } + + // Simulate a grab and toss. Use for debugging in monoscopic only. + public void TossNearestWidget() + { + var widget = MediaWidgets.Where(w => w.m_WidgetScript.Showing) + .Select(w => new { score = ScoreByAngleAndDistance(w), widget = w.m_WidgetScript }) + .Where(data => data.score > 0) + .OrderBy(data => data.score) + .FirstOrDefault().widget; + + if (widget != null) + { + if (widget.Pinned) + { + SketchMemoryScript.m_Instance.PerformAndRecordCommand(new PinWidgetCommand(widget, false)); + } + // All media widgets record their movements. + SketchMemoryScript.m_Instance.PerformAndRecordCommand( + new MoveWidgetCommand(widget, widget.LocalTransform, widget.CustomDimension)); + float speed = (App.METERS_TO_UNITS * SketchControlsScript.m_Instance.m_TossThresholdMeters); + Vector3 vLinVel = ViewpointScript.Head.forward * speed; + Vector3 vAngVel = Quaternion.AngleAxis(UnityEngine.Random.Range(0, 360), Vector3.forward) + * ViewpointScript.Head.up * 500; + widget.SetVelocities(vLinVel, vAngVel, widget.transform.position); + } + else + { + Debug.Log("No media in sketch"); + } + } + + public List> ActiveImageWidgets => + m_ImageWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); + public List> ActiveLightWidgets => + m_LightWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); + public List> ActiveModelWidgets => + m_ModelWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); + public List> ActiveVideoWidgets => + m_VideoWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); + public List> ActiveCameraPathWidgets => + m_CameraPathWidgets.Where(w => w.WidgetScript.gameObject.activeSelf).ToList(); + + } +} diff --git a/Assets/Scripts/Widgets/ModelWidget.cs b/Assets/Scripts/Widgets/ModelWidget.cs index d26af7f43c..0f17bae0b4 100644 --- a/Assets/Scripts/Widgets/ModelWidget.cs +++ b/Assets/Scripts/Widgets/ModelWidget.cs @@ -1,842 +1,842 @@ -// Copyright 2020 The Tilt Brush Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; -using UnityEngine; -using System.Linq; -using System.Threading.Tasks; - -namespace TiltBrush -{ - - public class ModelWidget : MediaWidget - { - // Do not change otherwise will break backward compatibility with 7.5 save format. - private const float kInitialSizeMeters_RS = 0.25f; - - [SerializeField] private float m_MinContainerRatio; // [0, 1] - [SerializeField] private float m_MaxBloat; - - private Model m_Model; - private string m_Subtree; - public string Subtree - { - get => m_Subtree; - set => m_Subtree = value; - } - - public event Action ScaleChanged; - - private Transform m_ModelInstance; - private ObjModelScript m_ObjModelScript; - private float m_InitSize_CS; - private float m_HideSize_CS; - private bool m_PolyCallbackActive; - - private int m_NumVertsTrackedByWidgetManager; - - /// Returns null if there's no model. - /// Note that it's an error to ask for a model's AssetId unless location type is PolyAssetId. - public string AssetId => m_Model?.AssetId; - - // Returns all leaf meshes which are part of the model, not including those created for auxillary - // purposes, such as ghosting or snapping. - // Analagous to Model.GetMeshes(). - // Do not mutate the return value. - public MeshFilter[] GetMeshes() - { - return m_ObjModelScript.m_MeshChildren; - } - - public Model Model - { - get { return m_Model; } - set - { - // Reduce usage count on old model. - if (m_Model != null) - { - m_Model.m_UsageCount--; - } - m_Model = value; - // Increment usage count on new model. - if (m_Model != null) - { - m_Model.m_UsageCount++; - } - LoadModel(); - } - } - - protected override Vector3 HomeSnapOffset - { - get - { - Vector3 box = m_BoxCollider.size - m_ContainerBloat * App.Scene.Pose.scale; - return m_Size * box * 0.5f * App.Scene.Pose.scale; - } - } - - protected override Vector3 GetHomeSnapLocation(Quaternion snapOrient) - { - return base.GetHomeSnapLocation(snapOrient) - - snapOrient * (App.Scene.Pose.scale * m_Size * m_Model.m_MeshBounds.center); - } - - public override float MaxAxisScale - { - get - { - return Mathf.Max(transform.localScale.x * m_BoxCollider.size.x, - Mathf.Max(transform.localScale.y * m_BoxCollider.size.y, - transform.localScale.z * m_BoxCollider.size.z)); - } - } - - public Bounds WorldSpaceBounds - { - get { return m_BoxCollider.bounds; } - } - - public int NumVertsTrackedByWidgetManager - { - get { return m_NumVertsTrackedByWidgetManager; } - } - - override public int GetTiltMeterCost() - { - return (m_ObjModelScript != null) ? m_ObjModelScript.NumMeshes : 0; - } - - public int GetNumVertsInModel() - { - return (m_ObjModelScript != null) ? m_ObjModelScript.GetNumVertsInMeshes() : 0; - } - - override protected void Awake() - { - base.Awake(); - transform.parent = App.ActiveCanvas.transform; - transform.localScale = Vector3.one; - - // Custom pin scalar for models. - m_PinScalar = 0.5f; - } - - override public void OnPreDestroy() - { - base.OnPreDestroy(); - - if (m_PolyCallbackActive) - { - App.PolyAssetCatalog.CatalogChanged -= OnPacCatalogChanged; - m_PolyCallbackActive = false; - } - // Set our model to null so its usage count is decremented. - Model = null; - } - public override GrabWidget Clone() - { - return Clone(transform.position, transform.rotation, m_Size); - } - - public override GrabWidget Clone(Vector3 position, Quaternion rotation, float size) - { - ModelWidget clone = Instantiate(WidgetManager.m_Instance.ModelWidgetPrefab); - clone.m_PreviousCanvas = m_PreviousCanvas; - clone.transform.position = position; - clone.transform.rotation = rotation; - clone.Model = Model; - // We're obviously not loading from a sketch. This is to prevent the intro animation. - // TODO: Change variable name to something more explicit of what this flag does. - clone.m_LoadingFromSketch = true; - clone.Show(true, false); - clone.AddSceneLightGizmos(); - clone.transform.parent = transform.parent; - clone.SetSignedWidgetSize(size); - clone.m_Subtree = m_Subtree; - clone.SyncHierarchyToSubtree(); - HierarchyUtils.RecursivelySetLayer(clone.transform, gameObject.layer); - TiltMeterScript.m_Instance.AdjustMeterWithWidget(clone.GetTiltMeterCost(), up: true); - - CanvasScript canvas = transform.parent.GetComponent(); - if (canvas != null) - { - var materials = clone.GetComponentsInChildren().SelectMany(x => x.materials); - foreach (var material in materials) - { - foreach (string keyword in canvas.BatchManager.MaterialKeywords) - { - material.EnableKeyword(keyword); - } - } - } - - if (!clone.Model.m_Valid) - { - App.PolyAssetCatalog.CatalogChanged += clone.OnPacCatalogChanged; - clone.m_PolyCallbackActive = true; - } - clone.CloneInitialMaterials(this); - clone.TrySetCanvasKeywordsFromObject(transform); - return clone; - } - - protected override void OnHideStart() - { - m_HideSize_CS = m_Size; - } - - public void OnPacCatalogChanged() - { - Model model = App.PolyAssetCatalog.GetModel(AssetId); - if (model != null && model.m_Valid) - { - Model = model; - SetSignedWidgetSize(m_Size); - - // TODO: We may not want to do this, eventually. Perhaps we continue to receive messages, - // get our asset each time, and do a diff to see if we should reload it. - App.PolyAssetCatalog.CatalogChanged -= OnPacCatalogChanged; - m_PolyCallbackActive = false; - } - } - - override protected void SetWidgetSizeInternal(float fScale) - { - base.SetWidgetSizeInternal(fScale); - if (Mathf.Abs(fScale - m_Size) > float.Epsilon) - { - ScaleChanged?.Invoke(); - } - } - - public override string GetExportName() - { - return Model.GetExportName(); - } - - void LoadModel() - { - // Clean up existing model - if (m_ModelInstance != null) - { - GameObject.Destroy(m_ModelInstance.gameObject); - } - - // Early out if we don't have a model to clone. - // This can happen if model loading is deferred. - if (m_Model == null || m_Model.m_ModelParent == null) - { - return; - } - - m_ModelInstance = Instantiate(m_Model.m_ModelParent); - m_ModelInstance.gameObject.SetActive(true); - m_ModelInstance.parent = this.transform; - - Coords.AsLocal[m_ModelInstance] = TrTransform.identity; - float maxExtent = 2 * Mathf.Max(m_Model.m_MeshBounds.extents.x, - Mathf.Max(m_Model.m_MeshBounds.extents.y, m_Model.m_MeshBounds.extents.z)); - float size; - if (maxExtent == 0.0f) - { - // If we created a widget with a model that doesn't have geo, we won't have calculated a - // bounds worth much. In that case, give us a default size. - size = 1.0f; - } - else - { - size = kInitialSizeMeters_RS * App.METERS_TO_UNITS / maxExtent; - } - - m_InitSize_CS = size / Coords.CanvasPose.scale; - - // Models are created in the main canvas. Cache model layer in case it's overridden later. - HierarchyUtils.RecursivelySetLayer(transform, App.Scene.MainCanvas.gameObject.layer); - m_BackupLayer = m_ModelInstance.gameObject.layer; - - // Set a new batchId on this model so it can be picked up in GPU intersections. - m_BatchId = GpuIntersector.GetNextBatchId(); - HierarchyUtils.RecursivelySetMaterialBatchID(m_ModelInstance, m_BatchId); - WidgetManager.m_Instance.AddWidgetToBatchMap(this, m_BatchId); - - Vector3 ratios = GetBoundsRatios(m_Model.m_MeshBounds); - m_ContainerBloat.x = Mathf.Max(0, m_MinContainerRatio - ratios.x); - m_ContainerBloat.y = Mathf.Max(0, m_MinContainerRatio - ratios.y); - m_ContainerBloat.z = Mathf.Max(0, m_MinContainerRatio - ratios.z); - m_ContainerBloat /= m_MinContainerRatio; // Normalize for the min ratio. - m_ContainerBloat *= m_MaxBloat / App.Scene.Pose.scale; // Apply bloat to appropriate axes. - - m_BoxCollider.size = m_Model.m_MeshBounds.size + m_ContainerBloat; - m_BoxCollider.transform.localPosition = m_Model.m_MeshBounds.center; - - InitSnapGhost(m_Model.m_ModelParent, m_ModelInstance); - - // Remove previous model vertex recording. - WidgetManager.m_Instance.AdjustModelVertCount(-m_NumVertsTrackedByWidgetManager); - m_NumVertsTrackedByWidgetManager = 0; - - m_ObjModelScript = GetComponentInChildren(); - m_ObjModelScript.Init(); - if (m_ObjModelScript.NumMeshes == 0) - { - OutputWindowScript.Error("No usable geometry in model"); - } - else - { - m_NumVertsTrackedByWidgetManager = m_ObjModelScript.GetNumVertsInMeshes(); - WidgetManager.m_Instance.AdjustModelVertCount(m_NumVertsTrackedByWidgetManager); - } - - if (m_Model.IsCached()) - { - m_Model.RefreshCache(); - } - } - - public bool HasSubModels() - { - string ext = Model.GetLocation().Extension; - if (ext == ".gltf" || ext == ".glb") - { - int lightCount = m_ObjModelScript.GetComponentsInChildren().Length; - int meshCount = GetMeshes().Length; - return lightCount + meshCount > 1; - } - else if (m_Model.GetLocation().Extension == ".svg") - { - return m_ObjModelScript.SvgSceneInfo.HasSubShapes(); - } - return false; - } - - public void SyncHierarchyToSubtree(string previousSubtree = null) - { - if (string.IsNullOrEmpty(Subtree)) return; - // Walk the hierarchy and find the matching node - Transform oldRoot = m_ObjModelScript.transform; - Transform node = oldRoot; - - // We only want to walk the new part of the hierarchy - string subpathToTraverse; - if (!string.IsNullOrEmpty(previousSubtree)) - { - subpathToTraverse = m_Subtree.Substring(previousSubtree.Length); - } - else - { - subpathToTraverse = m_Subtree; - } - subpathToTraverse = subpathToTraverse.Trim('/'); - - bool excludeChildren = false; - if (subpathToTraverse.EndsWith(".mesh")) - { - subpathToTraverse = subpathToTraverse.Substring(0, subpathToTraverse.Length - 5); - excludeChildren = true; - } - if (node.name == subpathToTraverse) - { - // We're already at the right node - // No need to do anything - Debug.LogWarning($"Didn't expect to get here..."); - } - else - { - // node will be null if not found - node = node.Find(subpathToTraverse); - } - - if (node != null) - { - if (excludeChildren) - { - foreach (Transform child in node) - { - Destroy(child.gameObject); - } - } - var newRoot = new GameObject(); - newRoot.transform.SetParent(transform); - newRoot.name = $"LocalFile:{m_Model.RelativePath}#{m_Subtree}"; - m_ObjModelScript = newRoot.AddComponent(); - node.SetParent(newRoot.transform, worldPositionStays: true); - - oldRoot.gameObject.SetActive(false); // TODO destroy might fail on first load so also hide - Destroy(oldRoot.gameObject); - - m_ObjModelScript.Init(); - if (excludeChildren) - { - // Destroyed children aren't destroyed immediately, so we need to assign them manually - var mf = node.GetComponent(); - var smr = node.GetComponent(); - m_ObjModelScript.m_MeshChildren = mf != null ? new[] { mf } : Array.Empty(); - m_ObjModelScript.m_SkinnedMeshChildren = smr != null ? new[] { smr } : Array.Empty(); - } - - CloneInitialMaterials(null); - RecalculateColliderBounds(); - } - } - - public void RecalculateColliderBounds() - { - var widgetTransform = m_ObjModelScript.transform.parent; - - // Save the widget's original transform - var oldParent = widgetTransform.parent; - var oldPosition = widgetTransform.localPosition; - var oldRotation = widgetTransform.localRotation; - var oldScale = widgetTransform.localScale; - - // Move it to the origin - widgetTransform.SetParent(null); - widgetTransform.localPosition = Vector3.zero; - widgetTransform.localRotation = Quaternion.identity; - widgetTransform.localScale = Vector3.one; - - // Reset the collider gameobject transform - m_BoxCollider.transform.localPosition = Vector3.zero; - m_BoxCollider.transform.localRotation = Quaternion.identity; - m_BoxCollider.transform.localScale = Vector3.one; - - // Collect the renderers - var meshRenderers = m_ObjModelScript - .m_MeshChildren - .Select(x => x.GetComponent()); - var skinnedMeshRenderers = m_ObjModelScript.m_SkinnedMeshChildren; - - // Calculate the bounds - Bounds b = new Bounds(); - bool first = true; - var boundsList = meshRenderers.Select(x => x.bounds).ToList(); - boundsList.AddRange(skinnedMeshRenderers.Select(x => x.bounds)); - - for (var i = 0; i < boundsList.Count; i++) - { - var bounds = boundsList[i]; - - if (first) - { - b = bounds; - first = false; - } - else - { - b.Encapsulate(bounds); - } - } - - m_MeshBounds = b; - m_BoxCollider.transform.localPosition = m_MeshBounds.center; - m_BoxCollider.size = m_MeshBounds.size; - - // Restore the widget's original transform - widgetTransform.SetParent(oldParent); - widgetTransform.localPosition = oldPosition; - widgetTransform.localRotation = oldRotation; - widgetTransform.localScale = oldScale; - } - - public override float GetActivationScore(Vector3 vControllerPos, InputManager.ControllerName name) - { - Vector3 vInvTransformedPos = m_BoxCollider.transform.InverseTransformPoint(vControllerPos); - Vector3 vSize = m_BoxCollider.size * 0.5f; - float xDiff = vSize.x - Mathf.Abs(vInvTransformedPos.x); - float yDiff = vSize.y - Mathf.Abs(vInvTransformedPos.y); - float zDiff = vSize.z - Mathf.Abs(vInvTransformedPos.z); - if (xDiff > 0.0f && yDiff > 0.0f && zDiff > 0.0f) - { - float minSize = Mathf.Abs(m_Size) * - Mathf.Min(m_BoxCollider.size.x, Mathf.Min(m_BoxCollider.size.y, m_BoxCollider.size.z)); - return (xDiff / vSize.x + yDiff / vSize.y + zDiff / vSize.z) / 3 / (minSize + 1); - } - return -1.0f; - } - - private static Vector3 GetBoundsRatios(Bounds bounds) - { - float maxExtent = 0.001f; // epsilon to avoid division by zero - maxExtent = Mathf.Max(maxExtent, bounds.extents.x); - maxExtent = Mathf.Max(maxExtent, bounds.extents.y); - maxExtent = Mathf.Max(maxExtent, bounds.extents.z); - - return new Vector3( - bounds.extents.x / maxExtent, - bounds.extents.y / maxExtent, - bounds.extents.z / maxExtent); - } - - protected override void OnShow() - { - base.OnShow(); - - if (m_Model != null && m_Model.m_Valid) - { - SetSignedWidgetSize(0.0f); - } - - if (!m_LoadingFromSketch) - { - m_IntroAnimState = IntroAnimState.In; - Debug.Assert(!IsMoving(), "Shouldn't have velocity!"); - ClearVelocities(); - m_IntroAnimValue = 0.0f; - UpdateIntroAnim(); - } - else - { - m_IntroAnimState = IntroAnimState.On; - } - } - - protected override void OnUpdate() - { - // During transitions, scale up and down. - if (m_CurrentState == State.Hiding) - { - SetWidgetSizeAboutCenterOfMass(m_HideSize_CS * GetShowRatio()); - } - } - - protected override void UpdateIntroAnim() - { - base.UpdateIntroAnim(); - if (!m_LoadingFromSketch) - { - SetWidgetSizeAboutCenterOfMass(m_InitSize_CS * m_IntroAnimValue); - } - } - - protected override void UpdateScale() - { - if (this == null) return; // BreakModelApartCommand can destroy us - transform.localScale = Vector3.one * m_Size; - if (m_Model != null && m_Model.m_Valid) - { - m_BoxCollider.size = MeshBounds.size + m_ContainerBloat; - } - } - - private Bounds m_MeshBounds; - public Bounds MeshBounds - { - get - { - if (string.IsNullOrEmpty(m_Subtree)) - { - return m_Model.m_MeshBounds; - } - return m_MeshBounds; - } - } - - override protected void InitPin() - { - base.InitPin(); - // Move the pin closer to the center since the bounds around the mesh might not be very tight. - m_Pin.SetPenetrationScalar(.25f); - } - - public override void RegisterHighlight() - { -#if !(UNITY_ANDROID || UNITY_IOS) - if (m_ObjModelScript != null) - { - m_ObjModelScript.RegisterHighlight(); - return; - } -#endif - base.RegisterHighlight(); - } - - protected override void UnregisterHighlight() - { -#if !(UNITY_ANDROID || UNITY_IOS) - if (m_ObjModelScript != null) - { - m_ObjModelScript.UnregisterHighlight(); - return; - } -#endif - base.UnregisterHighlight(); - } - - public TrTransform GetSaveTransform() - { - var xf = TrTransform.FromLocalTransform(transform); - xf.scale = GetSignedWidgetSize(); - return xf; - } - - public override Vector2 GetWidgetSizeRange() - { - // m_Model can be null in the event we're pulling the model from Poly. - if (m_Model == null || !m_Model.m_Valid) - { - // Don't enforce a size range if we don't know the extents yet. - // It will be contrained when the model loads in and the bounds are known. - return new Vector2(float.MinValue, float.MaxValue); - } - float maxExtent = 2 * Mathf.Max(m_Model.m_MeshBounds.extents.x, - Mathf.Max(m_Model.m_MeshBounds.extents.y, m_Model.m_MeshBounds.extents.z)); - // If we created a widget with a model that doesn't have geo, we won't have calculated a - // bounds with great data. Protect against divide by zero. - if (maxExtent == 0.0f) - { - maxExtent = 1.0f; - } - return new Vector2(m_MinSize_CS / maxExtent, m_MaxSize_CS / maxExtent); - } - - /// This method is for use when loading widgets that use the pre-M13 file format, - /// which is normalized with respect to min and max size (ie: the transform is not "raw") - public void SetWidgetSizeNonRaw(float fScale) - { - Vector3 extents = m_Model.m_MeshBounds.extents; - float maxExtent = Mathf.Max(extents.x, Mathf.Max(extents.y, extents.z)); - // If we created a widget with a model that doesn't have geo, we won't have calculated a - // bounds with great data. Protect against divide by zero. - if (maxExtent == 0.0f) - { - maxExtent = 1.0f; - } - float sizeRatio = kInitialSizeMeters_RS / 2 / maxExtent; - SetSignedWidgetSize(fScale * sizeRatio * 10); - } - - - /// Updates the scale of the object, but with the center-of-scale - /// being the center of mass's origin, as opposed to the widget's origin. - /// Postconditions: - /// - GetWidgetSize() == size - /// - CenterOfMassPose_LS is unchanged (modulo precision issues) - protected void SetWidgetSizeAboutCenterOfMass(float size) - { - if (m_Size == size) { return; } - - // Use WithUnitScale because we want only the pos/rot difference - // Find delta such that delta * new = old - var oldCm_LS = WithUnitScale(CenterOfMassPose_LS); - m_Size = size; - UpdateScale(); - var newCm_LS = WithUnitScale(CenterOfMassPose_LS); - var delta_LS = oldCm_LS * newCm_LS.inverse; - if (CenterOfMassTransform == transform) - { - // Edge case when they are equal: delta_LS will only be approximately identity. - // Make it exactly identity for a smidge more accuracy - delta_LS = TrTransform.identity; - } - - LocalTransform = delta_LS * LocalTransform; - } - - /// I believe (but am not sure) that Media Library content loads synchronously, - /// and PAC content loads asynchronously. - public static async void CreateFromSaveData(TiltModels75 modelDatas) - { - Debug.AssertFormat(modelDatas.AssetId == null || modelDatas.FilePath == null, - "Model Data should not have an AssetID *and* a File Path"); - Debug.AssertFormat(!modelDatas.InSet_deprecated, - "InSet should have been removed at load time"); - - bool ok; - if (modelDatas.FilePath != null) - { - - Task okTask = CreateModelsFromRelativePath( - modelDatas.FilePath, modelDatas.Subtrees, - modelDatas.Transforms, modelDatas.RawTransforms, modelDatas.PinStates, - modelDatas.GroupIds, modelDatas.LayerIds); - ok = await okTask; - - } - else if (modelDatas.AssetId != null) - { - CreateModelsFromAssetId( - modelDatas.AssetId, modelDatas.Subtrees, - modelDatas.RawTransforms, modelDatas.PinStates, modelDatas.GroupIds, modelDatas.LayerIds); - ok = true; - } - else - { - Debug.LogError("Model Data doesn't contain an AssetID or File Path."); - ok = false; - } - - if (!ok) - { - ModelCatalog.m_Instance.AddMissingModel( - modelDatas.FilePath, modelDatas.Transforms, modelDatas.RawTransforms); - } - } - - /// I believe (but am not sure) that this is synchronous. - /// Returns false if the model can't be loaded -- in this case, caller is responsible - /// for creating the missing-model placeholder. - public static async Task CreateModelsFromRelativePath( - string relativePath, string[] subtrees, TrTransform[] xfs, TrTransform[] rawXfs, bool[] pinStates, uint[] groupIds, int[] layerIds) - { - // Verify model is loaded. Or, at least, has been tried to be loaded. - Model model = ModelCatalog.m_Instance.GetModel(relativePath); - if (model == null) { return false; } - - if (!model.m_Valid) - { - // Reload the model if it's not valid or if we're loading a subtree. - Task t = model.LoadModelAsync(); - await t; - } - if (!model.m_Valid) - { - return false; - } - - if (xfs != null) - { - // Pre M13 format - for (int i = 0; i < xfs.Length; ++i) - { - bool pin = (pinStates != null && i < pinStates.Length) ? pinStates[i] : true; - uint groupId = (groupIds != null && i < groupIds.Length) ? groupIds[i] : 0; - CreateModel(model, subtrees[i], xfs[i], pin, isNonRawTransform: true, groupId, 0); - } - } - if (rawXfs != null) - { - // Post M13 format - for (int i = 0; i < rawXfs.Length; ++i) - { - bool pin = (pinStates != null && i < pinStates.Length) ? pinStates[i] : true; - uint groupId = (groupIds != null && i < groupIds.Length) ? groupIds[i] : 0; - int layerId = (layerIds != null && i < layerIds.Length) ? layerIds[i] : 0; - CreateModel(model, subtrees[i], rawXfs[i], pin, isNonRawTransform: false, groupId, layerId); - } - } - return true; - } - - /// isNonRawTransform - true if the transform uses the pre-M13 meaning of transform.scale. - static void CreateModel(Model model, string subtree, TrTransform xf, bool pin, - bool isNonRawTransform, uint groupId, int layerId, string assetId = null) - { - - var modelWidget = Instantiate(WidgetManager.m_Instance.ModelWidgetPrefab) as ModelWidget; - modelWidget.transform.localPosition = xf.translation; - modelWidget.transform.localRotation = xf.rotation; - modelWidget.Model = model; - modelWidget.m_Subtree = subtree; - modelWidget.SyncHierarchyToSubtree(); - modelWidget.m_LoadingFromSketch = true; - modelWidget.Show(true, false); - if (isNonRawTransform) - { - modelWidget.SetWidgetSizeNonRaw(xf.scale); - modelWidget.transform.localPosition -= - xf.rotation * modelWidget.Model.m_MeshBounds.center * modelWidget.GetSignedWidgetSize(); - } - else - { - modelWidget.SetSignedWidgetSize(xf.scale); - } - TiltMeterScript.m_Instance.AdjustMeterWithWidget(modelWidget.GetTiltMeterCost(), up: true); - if (pin) - { - modelWidget.PinFromSave(); - } - - if (assetId != null && !model.m_Valid) - { - App.PolyAssetCatalog.CatalogChanged += modelWidget.OnPacCatalogChanged; - modelWidget.m_PolyCallbackActive = true; - } - modelWidget.Group = App.GroupManager.GetGroupFromId(groupId); - modelWidget.SetCanvas(App.Scene.GetOrCreateLayer(layerId)); - } - - // Used when loading model assetIds from a serialized format (e.g. Tilt file). - static void CreateModelsFromAssetId(string assetId, string[] subtrees, TrTransform[] rawXfs, - bool[] pinStates, uint[] groupIds, int[] layerIds) - { - // Request model from Poly and if it doesn't exist, ask to load it. - Model model = App.PolyAssetCatalog.GetModel(assetId); - if (model == null) - { - // This Model is transient; the Widget will replace it with a good Model from the PAC - // as soon as the PAC loads it. - model = new Model(Model.Location.PolyAsset(assetId, null)); - } - if (!model.m_Valid) - { - App.PolyAssetCatalog.RequestModelLoad(assetId, "widget"); - } - - // Create a widget for each transform. - for (int i = 0; i < rawXfs.Length; ++i) - { - bool pin = (i < pinStates.Length) ? pinStates[i] : true; - uint groupId = (groupIds != null && i < groupIds.Length) ? groupIds[i] : 0; - int layerId = (layerIds != null && i < layerIds.Length) ? layerIds[i] : 0; - CreateModel(model, subtrees[i], rawXfs[i], pin, isNonRawTransform: false, groupId, layerId, assetId); - } - } - - override public bool HasGPUIntersectionObject() - { - return m_ModelInstance != null; - } - - override public void SetGPUIntersectionObjectLayer(int layer) - { - HierarchyUtils.RecursivelySetLayer(m_ModelInstance, layer); - } - - override public void RestoreGPUIntersectionObjectLayer() - { - HierarchyUtils.RecursivelySetLayer(m_ModelInstance, m_BackupLayer); - } - - override public bool CanSnapToHome() - { - return m_Model.m_MeshBounds.center == Vector3.zero; - } - - public void AddSceneLightGizmos() - { - var lights = m_ObjModelScript.transform.GetComponentsInChildren(); - foreach (var light in lights) - { - // Probably not the right place to do it but we have to it somewhere - light.renderMode = LightRenderMode.ForceVertex; - Transform tr = Instantiate( - WidgetManager.m_Instance.SceneLightGizmoPrefab.transform, - light.transform - ); - var gizmo = tr.GetComponent(); - gizmo.SetupLightGizmos(light); - } - } - - public void UpdateBatchInfo() - { - // Set a new batchId on this model so it can be picked up in GPU intersections. - m_BatchId = GpuIntersector.GetNextBatchId(); - HierarchyUtils.RecursivelySetMaterialBatchID(m_ModelInstance, m_BatchId); - WidgetManager.m_Instance.AddWidgetToBatchMap(this, m_BatchId); - } - } -} // namespace TiltBrush +// Copyright 2020 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using UnityEngine; +using System.Linq; +using System.Threading.Tasks; + +namespace TiltBrush +{ + + public class ModelWidget : MediaWidget + { + // Do not change otherwise will break backward compatibility with 7.5 save format. + private const float kInitialSizeMeters_RS = 0.25f; + + [SerializeField] private float m_MinContainerRatio; // [0, 1] + [SerializeField] private float m_MaxBloat; + + private Model m_Model; + private string m_Subtree; + public string Subtree + { + get => m_Subtree; + set => m_Subtree = value; + } + + public event Action ScaleChanged; + + private Transform m_ModelInstance; + private ObjModelScript m_ObjModelScript; + private float m_InitSize_CS; + private float m_HideSize_CS; + private bool m_PolyCallbackActive; + + private int m_NumVertsTrackedByWidgetManager; + + /// Returns null if there's no model. + /// Note that it's an error to ask for a model's AssetId unless location type is PolyAssetId. + public string AssetId => m_Model?.AssetId; + + // Returns all leaf meshes which are part of the model, not including those created for auxillary + // purposes, such as ghosting or snapping. + // Analagous to Model.GetMeshes(). + // Do not mutate the return value. + public MeshFilter[] GetMeshes() + { + return m_ObjModelScript.m_MeshChildren; + } + + public Model Model + { + get { return m_Model; } + set + { + // Reduce usage count on old model. + if (m_Model != null) + { + m_Model.m_UsageCount--; + } + m_Model = value; + // Increment usage count on new model. + if (m_Model != null) + { + m_Model.m_UsageCount++; + } + LoadModel(); + } + } + + protected override Vector3 HomeSnapOffset + { + get + { + Vector3 box = m_BoxCollider.size - m_ContainerBloat * App.Scene.Pose.scale; + return m_Size * box * 0.5f * App.Scene.Pose.scale; + } + } + + protected override Vector3 GetHomeSnapLocation(Quaternion snapOrient) + { + return base.GetHomeSnapLocation(snapOrient) - + snapOrient * (App.Scene.Pose.scale * m_Size * m_Model.m_MeshBounds.center); + } + + public override float MaxAxisScale + { + get + { + return Mathf.Max(transform.localScale.x * m_BoxCollider.size.x, + Mathf.Max(transform.localScale.y * m_BoxCollider.size.y, + transform.localScale.z * m_BoxCollider.size.z)); + } + } + + public Bounds WorldSpaceBounds + { + get { return m_BoxCollider.bounds; } + } + + public int NumVertsTrackedByWidgetManager + { + get { return m_NumVertsTrackedByWidgetManager; } + } + + override public int GetTiltMeterCost() + { + return (m_ObjModelScript != null) ? m_ObjModelScript.NumMeshes : 0; + } + + public int GetNumVertsInModel() + { + return (m_ObjModelScript != null) ? m_ObjModelScript.GetNumVertsInMeshes() : 0; + } + + override protected void Awake() + { + base.Awake(); + transform.parent = App.ActiveCanvas.transform; + transform.localScale = Vector3.one; + + // Custom pin scalar for models. + m_PinScalar = 0.5f; + } + + override public void OnPreDestroy() + { + base.OnPreDestroy(); + + if (m_PolyCallbackActive) + { + App.IcosaAssetCatalog.CatalogChanged -= OnPacCatalogChanged; + m_PolyCallbackActive = false; + } + // Set our model to null so its usage count is decremented. + Model = null; + } + public override GrabWidget Clone() + { + return Clone(transform.position, transform.rotation, m_Size); + } + + public override GrabWidget Clone(Vector3 position, Quaternion rotation, float size) + { + ModelWidget clone = Instantiate(WidgetManager.m_Instance.ModelWidgetPrefab); + clone.m_PreviousCanvas = m_PreviousCanvas; + clone.transform.position = position; + clone.transform.rotation = rotation; + clone.Model = Model; + // We're obviously not loading from a sketch. This is to prevent the intro animation. + // TODO: Change variable name to something more explicit of what this flag does. + clone.m_LoadingFromSketch = true; + clone.Show(true, false); + clone.AddSceneLightGizmos(); + clone.transform.parent = transform.parent; + clone.SetSignedWidgetSize(size); + clone.m_Subtree = m_Subtree; + clone.SyncHierarchyToSubtree(); + HierarchyUtils.RecursivelySetLayer(clone.transform, gameObject.layer); + TiltMeterScript.m_Instance.AdjustMeterWithWidget(clone.GetTiltMeterCost(), up: true); + + CanvasScript canvas = transform.parent.GetComponent(); + if (canvas != null) + { + var materials = clone.GetComponentsInChildren().SelectMany(x => x.materials); + foreach (var material in materials) + { + foreach (string keyword in canvas.BatchManager.MaterialKeywords) + { + material.EnableKeyword(keyword); + } + } + } + + if (!clone.Model.m_Valid) + { + App.IcosaAssetCatalog.CatalogChanged += clone.OnPacCatalogChanged; + clone.m_PolyCallbackActive = true; + } + clone.CloneInitialMaterials(this); + clone.TrySetCanvasKeywordsFromObject(transform); + return clone; + } + + protected override void OnHideStart() + { + m_HideSize_CS = m_Size; + } + + public void OnPacCatalogChanged() + { + Model model = App.IcosaAssetCatalog.GetModel(AssetId); + if (model != null && model.m_Valid) + { + Model = model; + SetSignedWidgetSize(m_Size); + + // TODO: We may not want to do this, eventually. Perhaps we continue to receive messages, + // get our asset each time, and do a diff to see if we should reload it. + App.IcosaAssetCatalog.CatalogChanged -= OnPacCatalogChanged; + m_PolyCallbackActive = false; + } + } + + override protected void SetWidgetSizeInternal(float fScale) + { + base.SetWidgetSizeInternal(fScale); + if (Mathf.Abs(fScale - m_Size) > float.Epsilon) + { + ScaleChanged?.Invoke(); + } + } + + public override string GetExportName() + { + return Model.GetExportName(); + } + + void LoadModel() + { + // Clean up existing model + if (m_ModelInstance != null) + { + GameObject.Destroy(m_ModelInstance.gameObject); + } + + // Early out if we don't have a model to clone. + // This can happen if model loading is deferred. + if (m_Model == null || m_Model.m_ModelParent == null) + { + return; + } + + m_ModelInstance = Instantiate(m_Model.m_ModelParent); + m_ModelInstance.gameObject.SetActive(true); + m_ModelInstance.parent = this.transform; + + Coords.AsLocal[m_ModelInstance] = TrTransform.identity; + float maxExtent = 2 * Mathf.Max(m_Model.m_MeshBounds.extents.x, + Mathf.Max(m_Model.m_MeshBounds.extents.y, m_Model.m_MeshBounds.extents.z)); + float size; + if (maxExtent == 0.0f) + { + // If we created a widget with a model that doesn't have geo, we won't have calculated a + // bounds worth much. In that case, give us a default size. + size = 1.0f; + } + else + { + size = kInitialSizeMeters_RS * App.METERS_TO_UNITS / maxExtent; + } + + m_InitSize_CS = size / Coords.CanvasPose.scale; + + // Models are created in the main canvas. Cache model layer in case it's overridden later. + HierarchyUtils.RecursivelySetLayer(transform, App.Scene.MainCanvas.gameObject.layer); + m_BackupLayer = m_ModelInstance.gameObject.layer; + + // Set a new batchId on this model so it can be picked up in GPU intersections. + m_BatchId = GpuIntersector.GetNextBatchId(); + HierarchyUtils.RecursivelySetMaterialBatchID(m_ModelInstance, m_BatchId); + WidgetManager.m_Instance.AddWidgetToBatchMap(this, m_BatchId); + + Vector3 ratios = GetBoundsRatios(m_Model.m_MeshBounds); + m_ContainerBloat.x = Mathf.Max(0, m_MinContainerRatio - ratios.x); + m_ContainerBloat.y = Mathf.Max(0, m_MinContainerRatio - ratios.y); + m_ContainerBloat.z = Mathf.Max(0, m_MinContainerRatio - ratios.z); + m_ContainerBloat /= m_MinContainerRatio; // Normalize for the min ratio. + m_ContainerBloat *= m_MaxBloat / App.Scene.Pose.scale; // Apply bloat to appropriate axes. + + m_BoxCollider.size = m_Model.m_MeshBounds.size + m_ContainerBloat; + m_BoxCollider.transform.localPosition = m_Model.m_MeshBounds.center; + + InitSnapGhost(m_Model.m_ModelParent, m_ModelInstance); + + // Remove previous model vertex recording. + WidgetManager.m_Instance.AdjustModelVertCount(-m_NumVertsTrackedByWidgetManager); + m_NumVertsTrackedByWidgetManager = 0; + + m_ObjModelScript = GetComponentInChildren(); + m_ObjModelScript.Init(); + if (m_ObjModelScript.NumMeshes == 0) + { + OutputWindowScript.Error("No usable geometry in model"); + } + else + { + m_NumVertsTrackedByWidgetManager = m_ObjModelScript.GetNumVertsInMeshes(); + WidgetManager.m_Instance.AdjustModelVertCount(m_NumVertsTrackedByWidgetManager); + } + + if (m_Model.IsCached()) + { + m_Model.RefreshCache(); + } + } + + public bool HasSubModels() + { + string ext = Model.GetLocation().Extension; + if (ext == ".gltf" || ext == ".glb") + { + int lightCount = m_ObjModelScript.GetComponentsInChildren().Length; + int meshCount = GetMeshes().Length; + return lightCount + meshCount > 1; + } + else if (m_Model.GetLocation().Extension == ".svg") + { + return m_ObjModelScript.SvgSceneInfo.HasSubShapes(); + } + return false; + } + + public void SyncHierarchyToSubtree(string previousSubtree = null) + { + if (string.IsNullOrEmpty(Subtree)) return; + // Walk the hierarchy and find the matching node + Transform oldRoot = m_ObjModelScript.transform; + Transform node = oldRoot; + + // We only want to walk the new part of the hierarchy + string subpathToTraverse; + if (!string.IsNullOrEmpty(previousSubtree)) + { + subpathToTraverse = m_Subtree.Substring(previousSubtree.Length); + } + else + { + subpathToTraverse = m_Subtree; + } + subpathToTraverse = subpathToTraverse.Trim('/'); + + bool excludeChildren = false; + if (subpathToTraverse.EndsWith(".mesh")) + { + subpathToTraverse = subpathToTraverse.Substring(0, subpathToTraverse.Length - 5); + excludeChildren = true; + } + if (node.name == subpathToTraverse) + { + // We're already at the right node + // No need to do anything + Debug.LogWarning($"Didn't expect to get here..."); + } + else + { + // node will be null if not found + node = node.Find(subpathToTraverse); + } + + if (node != null) + { + if (excludeChildren) + { + foreach (Transform child in node) + { + Destroy(child.gameObject); + } + } + var newRoot = new GameObject(); + newRoot.transform.SetParent(transform); + newRoot.name = $"LocalFile:{m_Model.RelativePath}#{m_Subtree}"; + m_ObjModelScript = newRoot.AddComponent(); + node.SetParent(newRoot.transform, worldPositionStays: true); + + oldRoot.gameObject.SetActive(false); // TODO destroy might fail on first load so also hide + Destroy(oldRoot.gameObject); + + m_ObjModelScript.Init(); + if (excludeChildren) + { + // Destroyed children aren't destroyed immediately, so we need to assign them manually + var mf = node.GetComponent(); + var smr = node.GetComponent(); + m_ObjModelScript.m_MeshChildren = mf != null ? new[] { mf } : Array.Empty(); + m_ObjModelScript.m_SkinnedMeshChildren = smr != null ? new[] { smr } : Array.Empty(); + } + + CloneInitialMaterials(null); + RecalculateColliderBounds(); + } + } + + public void RecalculateColliderBounds() + { + var widgetTransform = m_ObjModelScript.transform.parent; + + // Save the widget's original transform + var oldParent = widgetTransform.parent; + var oldPosition = widgetTransform.localPosition; + var oldRotation = widgetTransform.localRotation; + var oldScale = widgetTransform.localScale; + + // Move it to the origin + widgetTransform.SetParent(null); + widgetTransform.localPosition = Vector3.zero; + widgetTransform.localRotation = Quaternion.identity; + widgetTransform.localScale = Vector3.one; + + // Reset the collider gameobject transform + m_BoxCollider.transform.localPosition = Vector3.zero; + m_BoxCollider.transform.localRotation = Quaternion.identity; + m_BoxCollider.transform.localScale = Vector3.one; + + // Collect the renderers + var meshRenderers = m_ObjModelScript + .m_MeshChildren + .Select(x => x.GetComponent()); + var skinnedMeshRenderers = m_ObjModelScript.m_SkinnedMeshChildren; + + // Calculate the bounds + Bounds b = new Bounds(); + bool first = true; + var boundsList = meshRenderers.Select(x => x.bounds).ToList(); + boundsList.AddRange(skinnedMeshRenderers.Select(x => x.bounds)); + + for (var i = 0; i < boundsList.Count; i++) + { + var bounds = boundsList[i]; + + if (first) + { + b = bounds; + first = false; + } + else + { + b.Encapsulate(bounds); + } + } + + m_MeshBounds = b; + m_BoxCollider.transform.localPosition = m_MeshBounds.center; + m_BoxCollider.size = m_MeshBounds.size; + + // Restore the widget's original transform + widgetTransform.SetParent(oldParent); + widgetTransform.localPosition = oldPosition; + widgetTransform.localRotation = oldRotation; + widgetTransform.localScale = oldScale; + } + + public override float GetActivationScore(Vector3 vControllerPos, InputManager.ControllerName name) + { + Vector3 vInvTransformedPos = m_BoxCollider.transform.InverseTransformPoint(vControllerPos); + Vector3 vSize = m_BoxCollider.size * 0.5f; + float xDiff = vSize.x - Mathf.Abs(vInvTransformedPos.x); + float yDiff = vSize.y - Mathf.Abs(vInvTransformedPos.y); + float zDiff = vSize.z - Mathf.Abs(vInvTransformedPos.z); + if (xDiff > 0.0f && yDiff > 0.0f && zDiff > 0.0f) + { + float minSize = Mathf.Abs(m_Size) * + Mathf.Min(m_BoxCollider.size.x, Mathf.Min(m_BoxCollider.size.y, m_BoxCollider.size.z)); + return (xDiff / vSize.x + yDiff / vSize.y + zDiff / vSize.z) / 3 / (minSize + 1); + } + return -1.0f; + } + + private static Vector3 GetBoundsRatios(Bounds bounds) + { + float maxExtent = 0.001f; // epsilon to avoid division by zero + maxExtent = Mathf.Max(maxExtent, bounds.extents.x); + maxExtent = Mathf.Max(maxExtent, bounds.extents.y); + maxExtent = Mathf.Max(maxExtent, bounds.extents.z); + + return new Vector3( + bounds.extents.x / maxExtent, + bounds.extents.y / maxExtent, + bounds.extents.z / maxExtent); + } + + protected override void OnShow() + { + base.OnShow(); + + if (m_Model != null && m_Model.m_Valid) + { + SetSignedWidgetSize(0.0f); + } + + if (!m_LoadingFromSketch) + { + m_IntroAnimState = IntroAnimState.In; + Debug.Assert(!IsMoving(), "Shouldn't have velocity!"); + ClearVelocities(); + m_IntroAnimValue = 0.0f; + UpdateIntroAnim(); + } + else + { + m_IntroAnimState = IntroAnimState.On; + } + } + + protected override void OnUpdate() + { + // During transitions, scale up and down. + if (m_CurrentState == State.Hiding) + { + SetWidgetSizeAboutCenterOfMass(m_HideSize_CS * GetShowRatio()); + } + } + + protected override void UpdateIntroAnim() + { + base.UpdateIntroAnim(); + if (!m_LoadingFromSketch) + { + SetWidgetSizeAboutCenterOfMass(m_InitSize_CS * m_IntroAnimValue); + } + } + + protected override void UpdateScale() + { + if (this == null) return; // BreakModelApartCommand can destroy us + transform.localScale = Vector3.one * m_Size; + if (m_Model != null && m_Model.m_Valid) + { + m_BoxCollider.size = MeshBounds.size + m_ContainerBloat; + } + } + + private Bounds m_MeshBounds; + public Bounds MeshBounds + { + get + { + if (string.IsNullOrEmpty(m_Subtree)) + { + return m_Model.m_MeshBounds; + } + return m_MeshBounds; + } + } + + override protected void InitPin() + { + base.InitPin(); + // Move the pin closer to the center since the bounds around the mesh might not be very tight. + m_Pin.SetPenetrationScalar(.25f); + } + + public override void RegisterHighlight() + { +#if !(UNITY_ANDROID || UNITY_IOS) + if (m_ObjModelScript != null) + { + m_ObjModelScript.RegisterHighlight(); + return; + } +#endif + base.RegisterHighlight(); + } + + protected override void UnregisterHighlight() + { +#if !(UNITY_ANDROID || UNITY_IOS) + if (m_ObjModelScript != null) + { + m_ObjModelScript.UnregisterHighlight(); + return; + } +#endif + base.UnregisterHighlight(); + } + + public TrTransform GetSaveTransform() + { + var xf = TrTransform.FromLocalTransform(transform); + xf.scale = GetSignedWidgetSize(); + return xf; + } + + public override Vector2 GetWidgetSizeRange() + { + // m_Model can be null in the event we're pulling the model from Poly. + if (m_Model == null || !m_Model.m_Valid) + { + // Don't enforce a size range if we don't know the extents yet. + // It will be contrained when the model loads in and the bounds are known. + return new Vector2(float.MinValue, float.MaxValue); + } + float maxExtent = 2 * Mathf.Max(m_Model.m_MeshBounds.extents.x, + Mathf.Max(m_Model.m_MeshBounds.extents.y, m_Model.m_MeshBounds.extents.z)); + // If we created a widget with a model that doesn't have geo, we won't have calculated a + // bounds with great data. Protect against divide by zero. + if (maxExtent == 0.0f) + { + maxExtent = 1.0f; + } + return new Vector2(m_MinSize_CS / maxExtent, m_MaxSize_CS / maxExtent); + } + + /// This method is for use when loading widgets that use the pre-M13 file format, + /// which is normalized with respect to min and max size (ie: the transform is not "raw") + public void SetWidgetSizeNonRaw(float fScale) + { + Vector3 extents = m_Model.m_MeshBounds.extents; + float maxExtent = Mathf.Max(extents.x, Mathf.Max(extents.y, extents.z)); + // If we created a widget with a model that doesn't have geo, we won't have calculated a + // bounds with great data. Protect against divide by zero. + if (maxExtent == 0.0f) + { + maxExtent = 1.0f; + } + float sizeRatio = kInitialSizeMeters_RS / 2 / maxExtent; + SetSignedWidgetSize(fScale * sizeRatio * 10); + } + + + /// Updates the scale of the object, but with the center-of-scale + /// being the center of mass's origin, as opposed to the widget's origin. + /// Postconditions: + /// - GetWidgetSize() == size + /// - CenterOfMassPose_LS is unchanged (modulo precision issues) + protected void SetWidgetSizeAboutCenterOfMass(float size) + { + if (m_Size == size) { return; } + + // Use WithUnitScale because we want only the pos/rot difference + // Find delta such that delta * new = old + var oldCm_LS = WithUnitScale(CenterOfMassPose_LS); + m_Size = size; + UpdateScale(); + var newCm_LS = WithUnitScale(CenterOfMassPose_LS); + var delta_LS = oldCm_LS * newCm_LS.inverse; + if (CenterOfMassTransform == transform) + { + // Edge case when they are equal: delta_LS will only be approximately identity. + // Make it exactly identity for a smidge more accuracy + delta_LS = TrTransform.identity; + } + + LocalTransform = delta_LS * LocalTransform; + } + + /// I believe (but am not sure) that Media Library content loads synchronously, + /// and PAC content loads asynchronously. + public static async void CreateFromSaveData(TiltModels75 modelDatas) + { + Debug.AssertFormat(modelDatas.AssetId == null || modelDatas.FilePath == null, + "Model Data should not have an AssetID *and* a File Path"); + Debug.AssertFormat(!modelDatas.InSet_deprecated, + "InSet should have been removed at load time"); + + bool ok; + if (modelDatas.FilePath != null) + { + + Task okTask = CreateModelsFromRelativePath( + modelDatas.FilePath, modelDatas.Subtrees, + modelDatas.Transforms, modelDatas.RawTransforms, modelDatas.PinStates, + modelDatas.GroupIds, modelDatas.LayerIds); + ok = await okTask; + + } + else if (modelDatas.AssetId != null) + { + CreateModelsFromAssetId( + modelDatas.AssetId, modelDatas.Subtrees, + modelDatas.RawTransforms, modelDatas.PinStates, modelDatas.GroupIds, modelDatas.LayerIds); + ok = true; + } + else + { + Debug.LogError("Model Data doesn't contain an AssetID or File Path."); + ok = false; + } + + if (!ok) + { + ModelCatalog.m_Instance.AddMissingModel( + modelDatas.FilePath, modelDatas.Transforms, modelDatas.RawTransforms); + } + } + + /// I believe (but am not sure) that this is synchronous. + /// Returns false if the model can't be loaded -- in this case, caller is responsible + /// for creating the missing-model placeholder. + public static async Task CreateModelsFromRelativePath( + string relativePath, string[] subtrees, TrTransform[] xfs, TrTransform[] rawXfs, bool[] pinStates, uint[] groupIds, int[] layerIds) + { + // Verify model is loaded. Or, at least, has been tried to be loaded. + Model model = ModelCatalog.m_Instance.GetModel(relativePath); + if (model == null) { return false; } + + if (!model.m_Valid) + { + // Reload the model if it's not valid or if we're loading a subtree. + Task t = model.LoadModelAsync(); + await t; + } + if (!model.m_Valid) + { + return false; + } + + if (xfs != null) + { + // Pre M13 format + for (int i = 0; i < xfs.Length; ++i) + { + bool pin = (pinStates != null && i < pinStates.Length) ? pinStates[i] : true; + uint groupId = (groupIds != null && i < groupIds.Length) ? groupIds[i] : 0; + CreateModel(model, subtrees[i], xfs[i], pin, isNonRawTransform: true, groupId, 0); + } + } + if (rawXfs != null) + { + // Post M13 format + for (int i = 0; i < rawXfs.Length; ++i) + { + bool pin = (pinStates != null && i < pinStates.Length) ? pinStates[i] : true; + uint groupId = (groupIds != null && i < groupIds.Length) ? groupIds[i] : 0; + int layerId = (layerIds != null && i < layerIds.Length) ? layerIds[i] : 0; + CreateModel(model, subtrees[i], rawXfs[i], pin, isNonRawTransform: false, groupId, layerId); + } + } + return true; + } + + /// isNonRawTransform - true if the transform uses the pre-M13 meaning of transform.scale. + static void CreateModel(Model model, string subtree, TrTransform xf, bool pin, + bool isNonRawTransform, uint groupId, int layerId, string assetId = null) + { + + var modelWidget = Instantiate(WidgetManager.m_Instance.ModelWidgetPrefab) as ModelWidget; + modelWidget.transform.localPosition = xf.translation; + modelWidget.transform.localRotation = xf.rotation; + modelWidget.Model = model; + modelWidget.m_Subtree = subtree; + modelWidget.SyncHierarchyToSubtree(); + modelWidget.m_LoadingFromSketch = true; + modelWidget.Show(true, false); + if (isNonRawTransform) + { + modelWidget.SetWidgetSizeNonRaw(xf.scale); + modelWidget.transform.localPosition -= + xf.rotation * modelWidget.Model.m_MeshBounds.center * modelWidget.GetSignedWidgetSize(); + } + else + { + modelWidget.SetSignedWidgetSize(xf.scale); + } + TiltMeterScript.m_Instance.AdjustMeterWithWidget(modelWidget.GetTiltMeterCost(), up: true); + if (pin) + { + modelWidget.PinFromSave(); + } + + if (assetId != null && !model.m_Valid) + { + App.IcosaAssetCatalog.CatalogChanged += modelWidget.OnPacCatalogChanged; + modelWidget.m_PolyCallbackActive = true; + } + modelWidget.Group = App.GroupManager.GetGroupFromId(groupId); + modelWidget.SetCanvas(App.Scene.GetOrCreateLayer(layerId)); + } + + // Used when loading model assetIds from a serialized format (e.g. Tilt file). + static void CreateModelsFromAssetId(string assetId, string[] subtrees, TrTransform[] rawXfs, + bool[] pinStates, uint[] groupIds, int[] layerIds) + { + // Request model from Poly and if it doesn't exist, ask to load it. + Model model = App.IcosaAssetCatalog.GetModel(assetId); + if (model == null) + { + // This Model is transient; the Widget will replace it with a good Model from the PAC + // as soon as the PAC loads it. + model = new Model(Model.Location.IcosaAsset(assetId, null)); + } + if (!model.m_Valid) + { + App.IcosaAssetCatalog.RequestModelLoad(assetId, "widget"); + } + + // Create a widget for each transform. + for (int i = 0; i < rawXfs.Length; ++i) + { + bool pin = (i < pinStates.Length) ? pinStates[i] : true; + uint groupId = (groupIds != null && i < groupIds.Length) ? groupIds[i] : 0; + int layerId = (layerIds != null && i < layerIds.Length) ? layerIds[i] : 0; + CreateModel(model, subtrees[i], rawXfs[i], pin, isNonRawTransform: false, groupId, layerId, assetId); + } + } + + override public bool HasGPUIntersectionObject() + { + return m_ModelInstance != null; + } + + override public void SetGPUIntersectionObjectLayer(int layer) + { + HierarchyUtils.RecursivelySetLayer(m_ModelInstance, layer); + } + + override public void RestoreGPUIntersectionObjectLayer() + { + HierarchyUtils.RecursivelySetLayer(m_ModelInstance, m_BackupLayer); + } + + override public bool CanSnapToHome() + { + return m_Model.m_MeshBounds.center == Vector3.zero; + } + + public void AddSceneLightGizmos() + { + var lights = m_ObjModelScript.transform.GetComponentsInChildren(); + foreach (var light in lights) + { + // Probably not the right place to do it but we have to it somewhere + light.renderMode = LightRenderMode.ForceVertex; + Transform tr = Instantiate( + WidgetManager.m_Instance.SceneLightGizmoPrefab.transform, + light.transform + ); + var gizmo = tr.GetComponent(); + gizmo.SetupLightGizmos(light); + } + } + + public void UpdateBatchInfo() + { + // Set a new batchId on this model so it can be picked up in GPU intersections. + m_BatchId = GpuIntersector.GetNextBatchId(); + HierarchyUtils.RecursivelySetMaterialBatchID(m_ModelInstance, m_BatchId); + WidgetManager.m_Instance.AddWidgetToBatchMap(this, m_BatchId); + } + } +} // namespace TiltBrush From 3d8fb2f55d917311a029f8d3029038ef3e25e65c Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 15 Jun 2024 17:08:28 +0100 Subject: [PATCH 042/105] Create cameras and animation clips for GLTF export (WIP) --- .../Scripts/Export/CreateCamerasForExport.cs | 80 +++++++++++++++++++ .../Export/CreateCamerasForExport.cs.meta | 3 + 2 files changed, 83 insertions(+) create mode 100644 Assets/Scripts/Export/CreateCamerasForExport.cs create mode 100644 Assets/Scripts/Export/CreateCamerasForExport.cs.meta diff --git a/Assets/Scripts/Export/CreateCamerasForExport.cs b/Assets/Scripts/Export/CreateCamerasForExport.cs new file mode 100644 index 0000000000..b9b6e56e1f --- /dev/null +++ b/Assets/Scripts/Export/CreateCamerasForExport.cs @@ -0,0 +1,80 @@ +// Copyright 2024 The Open Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace TiltBrush +{ + public class CreateAnimationClips + { + private static List temporaryCameras; + static void Create() + { + temporaryCameras = new List(); + var cameraPathWidgets = WidgetManager.m_Instance.CameraPathWidgets.ToArray(); + foreach (var widget in cameraPathWidgets) + { + var layer = widget.m_WidgetScript.Canvas; + var cam = GameObject.Instantiate(new GameObject(), layer.transform); + cam.AddComponent(); + AnimatorOverrideController overrideController = new AnimatorOverrideController(); + Animator animator = cam.AddComponent(); + overrideController.runtimeAnimatorController = animator.runtimeAnimatorController; + animator.runtimeAnimatorController = overrideController; + + var clip = new AnimationClip {frameRate = 90}; // TODO What's the correct value? + var posCurves = new AnimationCurve[] { new (), new(), new() }; + var rotCurves = new AnimationCurve[] { new (), new(), new(), new() }; + foreach (var knot in widget.WidgetScript.Path.PositionKnots) + { + var xf = knot.KnotXf; + var t = knot.PathT.T; + posCurves[0].AddKey(t, xf.position.x); + posCurves[1].AddKey(t, xf.position.y); + posCurves[2].AddKey(t, xf.position.z); + } + clip.SetCurve("", typeof(Transform), "localPosition.x", posCurves[0]); + clip.SetCurve("", typeof(Transform), "localPosition.y", posCurves[1]); + clip.SetCurve("", typeof(Transform), "localPosition.z", posCurves[2]); + foreach (var knot in widget.WidgetScript.Path.RotationKnots) + { + var xf = knot.KnotXf; + var t = knot.PathT.T; + rotCurves[0].AddKey(t, xf.rotation.x); + rotCurves[1].AddKey(t, xf.rotation.y); + rotCurves[2].AddKey(t, xf.rotation.z); + rotCurves[3].AddKey(t, xf.rotation.w); + } + clip.SetCurve("", typeof(Transform), "localRotation.x", posCurves[0]); + clip.SetCurve("", typeof(Transform), "localRotation.y", posCurves[1]); + clip.SetCurve("", typeof(Transform), "localRotation.z", posCurves[2]); + clip.SetCurve("", typeof(Transform), "localRotation.w", posCurves[3]); + + overrideController["DefaultAnimation"] = clip; + temporaryCameras.Add(cam); + } + } + + static void Destroy() + { + foreach (var cam in temporaryCameras) + { + GameObject.Destroy(cam); + } + } + } +} + diff --git a/Assets/Scripts/Export/CreateCamerasForExport.cs.meta b/Assets/Scripts/Export/CreateCamerasForExport.cs.meta new file mode 100644 index 0000000000..46c28027a4 --- /dev/null +++ b/Assets/Scripts/Export/CreateCamerasForExport.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 14919aff2e114a19b65a5e58509e1d81 +timeCreated: 1718461146 \ No newline at end of file From 0ad431e8478895e399a2f8984e2bd10b8672106e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 17 Jun 2024 08:57:01 +0100 Subject: [PATCH 043/105] Switch to official UnityGLTF repo --- Packages/manifest.json | 2 +- Packages/packages-lock.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/manifest.json b/Packages/manifest.json index a9581a0223..b57de99478 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -28,7 +28,7 @@ "com.unity.xr.oculus": "3.3.0", "com.unity.xr.openxr": "1.8.2", "com.zappar.xr.zapbox": "https://github.com/zappar-xr/zapbox-xr-sdk.git#3296cbf5046369801027a821fe9ff6082431a605", - "org.khronos.unitygltf": "https://github.com/icosa-mirror/UnityGLTF.git", + "org.khronos.unitygltf": "https://github.com/KhronosGroup/UnityGLTF.git", "org.nuget.google.apis": "1.64.0", "org.nuget.google.apis.auth": "1.64.0", "org.nuget.google.apis.core": "https://github.com/icosa-mirror/org.nuget.google.apis.core.git#1.64.0-openbrush", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 11d7a818cf..11e21b4633 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -357,7 +357,7 @@ "hash": "3296cbf5046369801027a821fe9ff6082431a605" }, "org.khronos.unitygltf": { - "version": "https://github.com/icosa-mirror/UnityGLTF.git", + "version": "https://github.com/KhronosGroup/UnityGLTF.git", "depth": 0, "source": "git", "dependencies": { @@ -366,7 +366,7 @@ "com.unity.shadergraph": "10.0.0", "com.unity.mathematics": "1.0.0" }, - "hash": "99c96d9029d4ce02e5f729a5e069a44aef6faa99" + "hash": "dbd863412a2807fea00b4433d28ec3682eac6bda" }, "org.nuget.google.apis": { "version": "1.64.0", From 9e11bf9b2914f8816f4d0dab3c9807e5d5de9376 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 17 Jun 2024 08:57:47 +0100 Subject: [PATCH 044/105] Update UnityGLTFSettings.asset --- Assets/Resources/UnityGLTFSettings.asset | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Assets/Resources/UnityGLTFSettings.asset b/Assets/Resources/UnityGLTFSettings.asset index b7aaddc95f..78cef5a7f4 100644 --- a/Assets/Resources/UnityGLTFSettings.asset +++ b/Assets/Resources/UnityGLTFSettings.asset @@ -112,6 +112,18 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 5066402b4fbd41e79ab3d7023cca96c5, type: 3} m_Name: MaterialVariantsPlugin m_EditorClassIdentifier: +--- !u!114 &-2913368442722201512 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 473c2b5b3de547139304b32beee641e9, type: 3} + m_Name: AnimationPointerImport + m_EditorClassIdentifier: --- !u!114 &-1701294651066981149 MonoBehaviour: m_ObjectHideFlags: 3 @@ -183,6 +195,7 @@ MonoBehaviour: - {fileID: 8372111537548844026} - {fileID: -7716978867629807533} - {fileID: 6916234453510156686} + - {fileID: -2913368442722201512} ExportPlugins: - {fileID: 242952683485160214} - {fileID: -5728475199642485532} @@ -194,7 +207,6 @@ MonoBehaviour: - {fileID: -7373113640993280472} - {fileID: 7420168740226561727} - {fileID: -6755212205620999988} - - {fileID: 8207833083978872644} - {fileID: -682178813687408182} exportNames: 1 exportFullPath: 0 From c0aa661a0e63e27d053709e98ea5e14475859c2b Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 17 Jun 2024 08:58:49 +0100 Subject: [PATCH 045/105] Move GLTF export standin handling to prefab. Stub out sky export logic --- .../Prefabs/GltfExportStandinManager.prefab | 131 ++++++++++++++++++ .../GltfExportStandinManager.prefab.meta | 7 + Assets/Scenes/Main.unity | 81 ++++++++++- ...rExport.cs => GltfExportStandinManager.cs} | 40 ++++-- ....meta => GltfExportStandinManager.cs.meta} | 0 .../Scripts/Export/OpenBrushExportPlugin.cs | 11 ++ 6 files changed, 259 insertions(+), 11 deletions(-) create mode 100644 Assets/Prefabs/GltfExportStandinManager.prefab create mode 100644 Assets/Prefabs/GltfExportStandinManager.prefab.meta rename Assets/Scripts/Export/{CreateCamerasForExport.cs => GltfExportStandinManager.cs} (73%) rename Assets/Scripts/Export/{CreateCamerasForExport.cs.meta => GltfExportStandinManager.cs.meta} (100%) diff --git a/Assets/Prefabs/GltfExportStandinManager.prefab b/Assets/Prefabs/GltfExportStandinManager.prefab new file mode 100644 index 0000000000..c213b88970 --- /dev/null +++ b/Assets/Prefabs/GltfExportStandinManager.prefab @@ -0,0 +1,131 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &5512400623774099615 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2455894836569734632} + - component: {fileID: 7950399880331140669} + m_Layer: 0 + m_Name: GltfExportStandinManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2455894836569734632 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5512400623774099615} + 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: + - {fileID: 6935990006228621101} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &7950399880331140669 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5512400623774099615} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 14919aff2e114a19b65a5e58509e1d81, type: 3} + m_Name: + m_EditorClassIdentifier: + m_TemporarySkySphere: {fileID: 6935990006228621101} +--- !u!1001 &6935990006228749741 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 2455894836569734632} + m_Modifications: + - target: {fileID: 100000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_Name + value: InvertedSphere + objectReference: {fileID: 0} + - target: {fileID: 100000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalScale.x + value: 100 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalScale.y + value: 100 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalScale.z + value: 100 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalPosition.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalPosition.z + value: 0.03281074 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2300000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} + propertyPath: m_Materials.Array.data[0] + value: + objectReference: {fileID: 2100000, guid: 77b09cb82a5ffa94fb37bccf98d2fc84, type: 2} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, type: 3} +--- !u!4 &6935990006228621101 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 400000, guid: 9c85be13d3f1fd84f94cc7551c16c7cd, + type: 3} + m_PrefabInstance: {fileID: 6935990006228749741} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Prefabs/GltfExportStandinManager.prefab.meta b/Assets/Prefabs/GltfExportStandinManager.prefab.meta new file mode 100644 index 0000000000..a9848cdc60 --- /dev/null +++ b/Assets/Prefabs/GltfExportStandinManager.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fd97be770c9e84eb1b5fcc5c06bd0adf +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 41e5ef778d..61fadd1f3a 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -26,7 +26,7 @@ RenderSettings: m_AmbientIntensity: 1 m_AmbientMode: 3 m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} - m_SkyboxMaterial: {fileID: 0} + m_SkyboxMaterial: {fileID: 2100000, guid: 77b09cb82a5ffa94fb37bccf98d2fc84, type: 2} m_HaloStrength: 0.5 m_FlareStrength: 1 m_FlareFadeSpeed: 3 @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 8900000, guid: 378efb751ea39e14cb1fd93f49ead278, type: 3} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_IndirectSpecularColor: {r: 0.49780262, g: 0.49780262, b: 0.49780262, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &4 LightmapSettings: @@ -10750,6 +10750,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_AssetsPerPage: 20 m_SketchbookRefreshInterval: 10 + m_UseLocalFeaturedSketches: 1 --- !u!114 &652605567 MonoBehaviour: m_ObjectHideFlags: 0 @@ -18431,6 +18432,7 @@ Transform: - {fileID: 1065755966} - {fileID: 1843608204} - {fileID: 1830711222} + - {fileID: 8494983682236033548} m_Father: {fileID: 0} m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -36448,6 +36450,81 @@ PrefabInstance: objectReference: {fileID: 94678610} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: e26dd31bb70849e4da9f0b1230a62278, type: 3} +--- !u!1001 &8494983682236033547 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1370550523} + m_Modifications: + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_RootOrder + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 5512400623774099615, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + propertyPath: m_Name + value: GltfExportStandinManager + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, type: 3} +--- !u!4 &8494983682236033548 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 2455894836569734632, guid: fd97be770c9e84eb1b5fcc5c06bd0adf, + type: 3} + m_PrefabInstance: {fileID: 8494983682236033547} + m_PrefabAsset: {fileID: 0} --- !u!4 &9123593761565215834 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Export/CreateCamerasForExport.cs b/Assets/Scripts/Export/GltfExportStandinManager.cs similarity index 73% rename from Assets/Scripts/Export/CreateCamerasForExport.cs rename to Assets/Scripts/Export/GltfExportStandinManager.cs index b9b6e56e1f..376b48d10f 100644 --- a/Assets/Scripts/Export/CreateCamerasForExport.cs +++ b/Assets/Scripts/Export/GltfExportStandinManager.cs @@ -12,23 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEngine.Serialization; namespace TiltBrush { - public class CreateAnimationClips + public class GltfExportStandinManager : MonoBehaviour { - private static List temporaryCameras; - static void Create() + [SerializeField] public Transform m_TemporarySkySphere; + [NonSerialized] public static GltfExportStandinManager m_Instance; + private List m_TemporaryCameras; + + void Awake() { - temporaryCameras = new List(); + m_Instance = this; + } + + public void CreateCameraStandins() + { + m_TemporaryCameras = new List(); var cameraPathWidgets = WidgetManager.m_Instance.CameraPathWidgets.ToArray(); foreach (var widget in cameraPathWidgets) { var layer = widget.m_WidgetScript.Canvas; - var cam = GameObject.Instantiate(new GameObject(), layer.transform); + var cam = Instantiate(new GameObject(), layer.transform); cam.AddComponent(); AnimatorOverrideController overrideController = new AnimatorOverrideController(); Animator animator = cam.AddComponent(); @@ -64,17 +74,29 @@ static void Create() clip.SetCurve("", typeof(Transform), "localRotation.w", posCurves[3]); overrideController["DefaultAnimation"] = clip; - temporaryCameras.Add(cam); + m_TemporaryCameras.Add(cam); } } - static void Destroy() + public void DestroyCameraStandins() { - foreach (var cam in temporaryCameras) + foreach (var cam in m_TemporaryCameras) { - GameObject.Destroy(cam); + Destroy(cam); } } + + public void CreateSkyStandin() + { + // TODO check if we need box vs sphere etc + m_TemporarySkySphere.GetComponent().material = RenderSettings.skybox; + m_TemporarySkySphere.gameObject.SetActive(false); + } + + public void DestroySkyStandin() + { + m_TemporarySkySphere.gameObject.SetActive(false); + } } } diff --git a/Assets/Scripts/Export/CreateCamerasForExport.cs.meta b/Assets/Scripts/Export/GltfExportStandinManager.cs.meta similarity index 100% rename from Assets/Scripts/Export/CreateCamerasForExport.cs.meta rename to Assets/Scripts/Export/GltfExportStandinManager.cs.meta diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index 977d02101c..f4512523c1 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -35,6 +35,11 @@ public class OpenBrushExportPluginConfig : GLTFExportPluginContext public override void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { + GltfExportStandinManager.m_Instance.CreateCameraStandins(); + if (App.UserConfig.Export.ExportCustomSkybox) + { + GltfExportStandinManager.m_Instance.CreateSkyStandin(); + } SelectionManager.m_Instance?.ClearActiveSelection(); _meshesToBatches = new Dictionary(); } @@ -288,6 +293,12 @@ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gl public override void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { if (!Application.isPlaying) return; + GltfExportStandinManager.m_Instance.DestroyCameraStandins(); + if (App.UserConfig.Export.ExportCustomSkybox) + { + GltfExportStandinManager.m_Instance.DestroySkyStandin(); + } + gltfRoot.Asset.Generator = $"Open Brush UnityGLTF Exporter {App.Config.m_VersionNumber}.{App.Config.m_BuildStamp})"; JToken ColorToJArray(Color c) => JToken.FromObject(new { c.r, c.g, c.b, c.a }); From 55c03cee88a6048bfb06b45199e51a279a427bd1 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 17 Jun 2024 09:57:50 +0100 Subject: [PATCH 046/105] Missing user config entry --- Assets/Scripts/UserConfig.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index 53d8e18bf8..7bc1746237 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -322,6 +322,14 @@ public bool ExportEnvironment set { m_ExportEnvironment = value; } } + // Used by UnityGLTF exporter + private bool? m_ExportCustomSkybox; + public bool ExportCustomSkybox + { + get { return m_ExportCustomSkybox ?? false; } + set { m_ExportCustomSkybox = value; } + } + private Dictionary m_Formats; [JsonProperty] public Dictionary Formats From d0c39b3545ddf5df078f9f416d081221caed449e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 17 Jun 2024 10:11:11 +0100 Subject: [PATCH 047/105] Unused import --- Assets/Scripts/Export/OpenBrushExportPlugin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index f4512523c1..fb48738b7a 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -3,7 +3,6 @@ using System.Linq; using GLTF.Schema; using Newtonsoft.Json.Linq; -using Unity.VectorGraphics; using UnityEngine; using UnityGLTF; using UnityGLTF.Plugins; From 4ec1567597cbf89e6bbf2531221d72ee47e5523e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 18 Jun 2024 08:36:40 +0100 Subject: [PATCH 048/105] Move camera path export logic to the export plugin --- .../Export/GltfExportStandinManager.cs | 55 ------------- .../Scripts/Export/OpenBrushExportPlugin.cs | 80 ++++++++++++++++++- 2 files changed, 77 insertions(+), 58 deletions(-) diff --git a/Assets/Scripts/Export/GltfExportStandinManager.cs b/Assets/Scripts/Export/GltfExportStandinManager.cs index 376b48d10f..0703a64491 100644 --- a/Assets/Scripts/Export/GltfExportStandinManager.cs +++ b/Assets/Scripts/Export/GltfExportStandinManager.cs @@ -31,61 +31,6 @@ void Awake() m_Instance = this; } - public void CreateCameraStandins() - { - m_TemporaryCameras = new List(); - var cameraPathWidgets = WidgetManager.m_Instance.CameraPathWidgets.ToArray(); - foreach (var widget in cameraPathWidgets) - { - var layer = widget.m_WidgetScript.Canvas; - var cam = Instantiate(new GameObject(), layer.transform); - cam.AddComponent(); - AnimatorOverrideController overrideController = new AnimatorOverrideController(); - Animator animator = cam.AddComponent(); - overrideController.runtimeAnimatorController = animator.runtimeAnimatorController; - animator.runtimeAnimatorController = overrideController; - - var clip = new AnimationClip {frameRate = 90}; // TODO What's the correct value? - var posCurves = new AnimationCurve[] { new (), new(), new() }; - var rotCurves = new AnimationCurve[] { new (), new(), new(), new() }; - foreach (var knot in widget.WidgetScript.Path.PositionKnots) - { - var xf = knot.KnotXf; - var t = knot.PathT.T; - posCurves[0].AddKey(t, xf.position.x); - posCurves[1].AddKey(t, xf.position.y); - posCurves[2].AddKey(t, xf.position.z); - } - clip.SetCurve("", typeof(Transform), "localPosition.x", posCurves[0]); - clip.SetCurve("", typeof(Transform), "localPosition.y", posCurves[1]); - clip.SetCurve("", typeof(Transform), "localPosition.z", posCurves[2]); - foreach (var knot in widget.WidgetScript.Path.RotationKnots) - { - var xf = knot.KnotXf; - var t = knot.PathT.T; - rotCurves[0].AddKey(t, xf.rotation.x); - rotCurves[1].AddKey(t, xf.rotation.y); - rotCurves[2].AddKey(t, xf.rotation.z); - rotCurves[3].AddKey(t, xf.rotation.w); - } - clip.SetCurve("", typeof(Transform), "localRotation.x", posCurves[0]); - clip.SetCurve("", typeof(Transform), "localRotation.y", posCurves[1]); - clip.SetCurve("", typeof(Transform), "localRotation.z", posCurves[2]); - clip.SetCurve("", typeof(Transform), "localRotation.w", posCurves[3]); - - overrideController["DefaultAnimation"] = clip; - m_TemporaryCameras.Add(cam); - } - } - - public void DestroyCameraStandins() - { - foreach (var cam in m_TemporaryCameras) - { - Destroy(cam); - } - } - public void CreateSkyStandin() { // TODO check if we need box vs sphere etc diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index fb48738b7a..712a951809 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -26,21 +26,94 @@ public class OpenBrushExportPluginConfig : GLTFExportPluginContext { private Dictionary _meshesToBatches; + // List of gameobject names to ignore during export private List _ignoreList = new() { "SnapGrid3D", - "Preview Light" + "Preview Light", + }; + private List m_CameraPathsCameras; public override void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { - GltfExportStandinManager.m_Instance.CreateCameraStandins(); if (App.UserConfig.Export.ExportCustomSkybox) { GltfExportStandinManager.m_Instance.CreateSkyStandin(); } SelectionManager.m_Instance?.ClearActiveSelection(); _meshesToBatches = new Dictionary(); + GenerateCameraPathsCameras(); + } + + private void GenerateCameraPathsCameras() + { + m_CameraPathsCameras = new List(); + var cameraPathWidgets = WidgetManager.m_Instance.CameraPathWidgets.ToArray(); + for (var i = 0; i < cameraPathWidgets.Length; i++) + { + var widget = cameraPathWidgets[i]; + var layer = widget.m_WidgetScript.Canvas; + var go = GameObject.Instantiate(new GameObject(), layer.transform); + go.name = $"CameraPath_{i}_{widget.m_WidgetScript.name}"; + var cam = go.AddComponent(); + m_CameraPathsCameras.Add(cam); + } + } + + private void ExportCameraPaths(GLTFSceneExporter exporter) + { + var cameraPathWidgets = WidgetManager.m_Instance.CameraPathWidgets.ToArray(); + for (var i = 0; i < cameraPathWidgets.Length; i++) + { + var cam = m_CameraPathsCameras[i]; + var widget = cameraPathWidgets[i]; + + GLTFAnimation anim = new GLTFAnimation(); + anim.Name = cam.gameObject.name; + + var posKnots = widget.WidgetScript.Path.PositionKnots; + var posTimes = new float[posKnots.Count]; + var posValues = new object[posKnots.Count]; + for (var j = 0; j < posKnots.Count; j++) + { + var knot = posKnots[j]; + var xf = knot.KnotXf; + var t = knot.PathT.T; + posTimes[j] = t; + posValues[j] = xf.position; + } + exporter.AddAnimationData(cam.gameObject, "translation", anim, posTimes, posValues); + + var rotKnots = widget.WidgetScript.Path.RotationKnots; + var rotTimes = new float[rotKnots.Count]; + var rotValues = new object[rotKnots.Count]; + for (var j = 0; j < rotKnots.Count; j++) + { + var knot = rotKnots[j]; + var xf = knot.KnotXf; + var t = knot.PathT.T; + posTimes[j] = t; + posValues[j] = xf.rotation; + } + exporter.AddAnimationData(cam.gameObject, "rotation", anim, posTimes, posValues); + + var fovKnots = widget.WidgetScript.Path.FovKnots; + var fovTimes = new float[fovKnots.Count]; + var fovValues = new object[fovKnots.Count]; + for (var j = 0; j < fovKnots.Count; j++) + { + var knot = fovKnots[j]; + var xf = knot.KnotXf; + var t = knot.PathT.T; + posTimes[j] = t; + posValues[j] = xf.rotation; + } + exporter.AddAnimationData(cam, "field of view", anim, fovTimes, fovValues); + + exporter.GetRoot().Animations.Add(anim); + GameObject.Destroy(cam); + } } private Transform GetOrCreateGroupTransform(CanvasScript layer, int group) @@ -292,7 +365,8 @@ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gl public override void AfterSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) { if (!Application.isPlaying) return; - GltfExportStandinManager.m_Instance.DestroyCameraStandins(); + + ExportCameraPaths(exporter); if (App.UserConfig.Export.ExportCustomSkybox) { GltfExportStandinManager.m_Instance.DestroySkyStandin(); From 28abbd19eb9bf7b93fd33028bb2fd6ef25c96e85 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 20 Jun 2024 23:39:09 +0100 Subject: [PATCH 049/105] "Camera Path" mode for the spectator cam. (via HTTP API only for now) --- .../Resources/ScriptExamples/spectator.html | 3 +- .../Scripts/API/ApiMethods.GlobalCommands.cs | 7 +++ Assets/Scripts/API/ApiMethods.cs | 3 ++ Assets/Scripts/Widgets/DropCamWidget.cs | 48 +++++++++++++++++-- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/Assets/Resources/ScriptExamples/spectator.html b/Assets/Resources/ScriptExamples/spectator.html index 8fb1773f90..3fce6da574 100644 --- a/Assets/Resources/ScriptExamples/spectator.html +++ b/Assets/Resources/ScriptExamples/spectator.html @@ -51,10 +51,11 @@

Spectator Camera:

+


-
+
   diff --git a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs index ef55f955c2..707eff8d52 100644 --- a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs +++ b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs @@ -592,6 +592,13 @@ public static void RecordCameraPath() var rEnum = SketchControlsScript.GlobalCommands.RecordCameraPath; SketchControlsScript.m_Instance.IssueGlobalCommand(rEnum); } + + [ApiEndpoint("camerapath.setactive", "Sets the active camera path")] + public static void SetActiveCameraPath(int index) + { + var widget = _GetActiveCameraPath(index); + WidgetManager.m_Instance.SetCurrentCameraPath(widget); + } } } diff --git a/Assets/Scripts/API/ApiMethods.cs b/Assets/Scripts/API/ApiMethods.cs index bfde2c3f77..4d00ea88c6 100644 --- a/Assets/Scripts/API/ApiMethods.cs +++ b/Assets/Scripts/API/ApiMethods.cs @@ -206,6 +206,9 @@ public static void SpectatorMode(string mode) case "circular": cam.SetMode(DropCamWidget.Mode.Circular); break; + case "camerapath": + cam.SetMode(DropCamWidget.Mode.CameraPath); + break; } } diff --git a/Assets/Scripts/Widgets/DropCamWidget.cs b/Assets/Scripts/Widgets/DropCamWidget.cs index 6e0e799240..cb40b37209 100644 --- a/Assets/Scripts/Widgets/DropCamWidget.cs +++ b/Assets/Scripts/Widgets/DropCamWidget.cs @@ -31,10 +31,11 @@ public class DropCamWidget : GrabWidget { public enum Mode { - SlowFollow, - Stationary, - Wobble, - Circular + SlowFollow = 0, + Stationary = 1, + Wobble = 2, + Circular = 3, + CameraPath = 5600 } public const Mode kDefaultMode = Mode.Stationary; @@ -167,6 +168,7 @@ static public string GetModeName(Mode mode) case Mode.Stationary: return "Stationary"; case Mode.Wobble: return "Figure 8"; case Mode.Circular: return "Circular"; + case Mode.CameraPath: return "Camera Path"; } return ""; } @@ -327,10 +329,48 @@ override protected void OnUpdate() m_GuideCircleObject.SetActive(false); } break; + case Mode.CameraPath: + if (m_UserInteracting) + { + ResetCam(); + } + else + { + m_AnimatedPathTime += Time.deltaTime * (m_CircleSpeed * 20); + FollowCameraPath(); + } + break; } } } + private void FollowCameraPath() + { + var currentPathWidget = WidgetManager.m_Instance.GetCurrentCameraPath().WidgetScript; // TODO Cache + var pathT = new PathT(m_AnimatedPathTime); + pathT.Clamp(currentPathWidget.Path.PositionKnots.Count); + + if (currentPathWidget != null && currentPathWidget.Path.NumPositionKnots > 1) + { + float speed = Mathf.Max(currentPathWidget.Path.GetSpeed(pathT), + CameraPathSpeedKnot.kMinSpeed); + bool completed = currentPathWidget.Path.MoveAlongPath(speed * Time.deltaTime, + pathT, out pathT); + if (completed) + { + // TODO optional looping? + m_AnimatedPathTime = 0; + } + + PathT t = pathT; + float fov = currentPathWidget.Path.GetFov(t); + var cam = GetComponentInChildren(); // TODO Cache + cam.fieldOfView = fov; + transform.position = currentPathWidget.Path.GetPosition(t) - cam.transform.localPosition; + transform.rotation = currentPathWidget.Path.GetRotation(t) * cam.transform.localRotation.Negated(); + } + } + override public void Activate(bool bActive) { base.Activate(bActive); From d65cdac5ca1a35338475897cb9018b5bfba79765 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Fri, 21 Jun 2024 00:09:02 +0100 Subject: [PATCH 050/105] ShouldNodeExport method (needs custom fork of UnityGLTF) --- .../Scripts/Export/OpenBrushExportPlugin.cs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index 712a951809..61a6631ea7 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -25,14 +25,6 @@ public override GLTFExportPluginContext CreateInstance(ExportContext context) public class OpenBrushExportPluginConfig : GLTFExportPluginContext { private Dictionary _meshesToBatches; - - // List of gameobject names to ignore during export - private List _ignoreList = new() - { - "SnapGrid3D", - "Preview Light", - - }; private List m_CameraPathsCameras; public override void BeforeSceneExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) @@ -148,18 +140,6 @@ private Transform GetOrCreateGroupTransform(CanvasScript layer, int group) public void BeforeLayerExport(Transform transform) { var canvas = transform.GetComponent(); - foreach (Transform child in transform) - { - if (_ignoreList.Contains(child.name)) - { - child.tag = "EditorOnly"; - } - - if (child.GetComponent() != null) - { - child.tag = "EditorOnly"; - } - } if (App.UserConfig.Export.KeepStrokes) { @@ -193,6 +173,18 @@ public void BeforeLayerExport(Transform transform) } } + public override bool ShouldNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform) + { + Type[] excludedTypes = + { + typeof(BaseBrushScript), // Brush preview strokes (does this break non-batched exports?) + typeof(SnapGrid3D), + typeof(StencilWidget), + typeof(CameraPathWidget) + }; + return excludedTypes.Any(t => transform.GetComponent(t) != null); + } + public override void BeforeNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node) { if (transform.GetComponent() != null) From e5c46a8abcbdeded0d20b412b28f65b46cdc1bf9 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 30 Jun 2024 12:20:27 +0100 Subject: [PATCH 051/105] Set alphamode and doublesided based on brush manifest. --- .../Scripts/Export/OpenBrushExportPlugin.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index 61a6631ea7..50f3f27150 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -348,9 +348,26 @@ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gl var brush = BrushCatalog.m_Instance.AllBrushes.FirstOrDefault(b => b.DurableName == materialNode.Name); - if (brush != null && brush.BlendMode == ExportableMaterialBlendMode.AdditiveBlend) + if (brush != null) { - AddExtension(materialNode, EXT_blend_operations.Add); + var manifest = BrushCatalog.m_Instance.GetBrush(brush.m_Guid); + + switch (manifest.m_BlendMode) + { + case ExportableMaterialBlendMode.AdditiveBlend: + AddExtension(materialNode, EXT_blend_operations.Add); + materialNode.AlphaMode = AlphaMode.BLEND; + materialNode.DoubleSided = true; + break; + case ExportableMaterialBlendMode.AlphaMask: + materialNode.AlphaMode = AlphaMode.MASK; + materialNode.DoubleSided = true; + break; + case ExportableMaterialBlendMode.AlphaBlend: + materialNode.AlphaMode = AlphaMode.BLEND; + materialNode.DoubleSided = true; + break; + } } } From 09bd47102c121af486a177dcc38435dc55048015 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 30 Jun 2024 14:39:17 +0100 Subject: [PATCH 052/105] switch to our fork of UnityGLTF --- Packages/manifest.json | 2 +- Packages/packages-lock.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Packages/manifest.json b/Packages/manifest.json index b57de99478..a6762a5628 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -28,7 +28,7 @@ "com.unity.xr.oculus": "3.3.0", "com.unity.xr.openxr": "1.8.2", "com.zappar.xr.zapbox": "https://github.com/zappar-xr/zapbox-xr-sdk.git#3296cbf5046369801027a821fe9ff6082431a605", - "org.khronos.unitygltf": "https://github.com/KhronosGroup/UnityGLTF.git", + "org.khronos.unitygltf": "https://github.com/icosa-mirror/UnityGLTF.git#feature/should-node-export-hook", "org.nuget.google.apis": "1.64.0", "org.nuget.google.apis.auth": "1.64.0", "org.nuget.google.apis.core": "https://github.com/icosa-mirror/org.nuget.google.apis.core.git#1.64.0-openbrush", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 11e21b4633..f5d44ab635 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -357,7 +357,7 @@ "hash": "3296cbf5046369801027a821fe9ff6082431a605" }, "org.khronos.unitygltf": { - "version": "https://github.com/KhronosGroup/UnityGLTF.git", + "version": "https://github.com/icosa-mirror/UnityGLTF.git#feature/should-node-export-hook", "depth": 0, "source": "git", "dependencies": { @@ -366,7 +366,7 @@ "com.unity.shadergraph": "10.0.0", "com.unity.mathematics": "1.0.0" }, - "hash": "dbd863412a2807fea00b4433d28ec3682eac6bda" + "hash": "0dba6d0dde6177c83039b3e1e6153889da40d257" }, "org.nuget.google.apis": { "version": "1.64.0", @@ -425,7 +425,7 @@ "depth": 0, "source": "git", "dependencies": {}, - "hash": "cff83f2ffcb95787572f951e7c7ea6e8958f2f65" + "hash": "731ce73482c74c1bccb84d53c1771f9c4a0d511b" }, "com.unity.modules.ai": { "version": "1.0.0", From 6c7f2bc0a59e672477f307fc722e3ed1f850e652 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 30 Jun 2024 14:53:53 +0100 Subject: [PATCH 053/105] Bool check was inverted --- Assets/Scripts/Export/OpenBrushExportPlugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index 50f3f27150..a5db5754f6 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -182,7 +182,7 @@ public override bool ShouldNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfR typeof(StencilWidget), typeof(CameraPathWidget) }; - return excludedTypes.Any(t => transform.GetComponent(t) != null); + return !excludedTypes.Any(t => transform.GetComponent(t) != null); } public override void BeforeNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node) From 69ce46a0d1f42f62987a234f75be672e717a0ae4 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 30 Jun 2024 16:20:01 +0100 Subject: [PATCH 054/105] Give ConcaveHull it's own copy of the MatteHull material to ease matching of material to manifest --- .../Basic/ConcaveHull/ConcaveHull.asset | 3 +- .../Brushes/Basic/ConcaveHull/ConcaveHull.mat | 102 ++++++++++++++++++ .../Basic/ConcaveHull/ConcaveHull.mat.meta | 8 ++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat create mode 100644 Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta diff --git a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset index c27d0d1000..e71b0e6745 100644 --- a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset +++ b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset @@ -24,7 +24,6 @@ MonoBehaviour: m_Supersedes: {fileID: 0} m_LooksIdentical: 0 m_ButtonTexture: {fileID: 2800000, guid: b54f6c68561104a4a94d4830df924aa5, type: 3} - m_Description: BRUSH_CONCAVEHULL m_LocalizedDescription: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 @@ -47,7 +46,7 @@ MonoBehaviour: m_VolumeVelocityRangeMultiplier: 1.5 m_AudioReactive: 0 m_ButtonAudio: {fileID: 0} - m_Material: {fileID: 2100000, guid: 4eecef89d4dfcf243b2ceb7a6249ec47, type: 2} + m_Material: {fileID: 2100000, guid: 6021fb85ff462234e99f3510d9525d24, type: 2} m_TextureAtlasV: 1 m_TileRate: 0.01 m_UseBloomSwatchOnColorPicker: 0 diff --git a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat new file mode 100644 index 0000000000..9b71008488 --- /dev/null +++ b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat @@ -0,0 +1,102 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: ConcaveHull + m_Shader: {fileID: 4800000, guid: 76dc2f7fe973ede4bbbedfc291269c84, type: 3} + m_ShaderKeywords: _EMISSION + m_LightmapFlags: 1 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _Bottom: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _Side: + m_Texture: {fileID: 2800000, guid: 006eb4ee45e4eee4b84cf30d54b01889, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _Top: + m_Texture: {fileID: 2800000, guid: 640eb58bac1e8c349b8aef55f817627b, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BottomScale: 2 + - _Bulge: 2.25 + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _EmissionGain: 0.5 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _NumSides: 5 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _ScrollJitterFrequency: 1 + - _ScrollJitterIntensity: 1 + - _ScrollRate: 1 + - _Shininess: 0.078125 + - _SideScale: 0.2 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _Speed: 1 + - _SpreadRate: 1.539 + - _SrcBlend: 1 + - _TopScale: 0.3 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0} diff --git a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta new file mode 100644 index 0000000000..253b7b45e4 --- /dev/null +++ b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6021fb85ff462234e99f3510d9525d24 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: From 194fe2a055dff9ca5f470d9f84cfe66544780b10 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 30 Jun 2024 16:21:03 +0100 Subject: [PATCH 055/105] manifest and alpha mode logic for new GLTF export --- .../Scripts/Export/OpenBrushExportPlugin.cs | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index a5db5754f6..f84b242df1 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -342,32 +342,41 @@ public override void AfterMaterialExport(GLTFSceneExporter exporter, GLTFRoot gl // Only handle brush materials if (!material.shader.name.StartsWith("Brush/")) return; - // Strip the (Instance) suffix from the material node name - materialNode.Name = materialNode.Name.Replace("(Instance)", "").Trim(); - materialNode.Name = $"ob-{materialNode.Name}"; + // TODO - This assumes that every brush has a unique material with a unique name + // Currently, this is true, but it may not always be the case + var brushes = BrushCatalog.m_Instance.AllBrushes + .Where(b => b.Material.name == material.name) + .ToList(); - var brush = BrushCatalog.m_Instance.AllBrushes.FirstOrDefault(b => b.DurableName == materialNode.Name); - - if (brush != null) + switch (brushes.Count) { - var manifest = BrushCatalog.m_Instance.GetBrush(brush.m_Guid); + case 0: + Debug.LogError($"No matching brush found for material {material.name}"); + return; + case > 1: + Debug.LogError($"Multiple brushes with the same material name: {material.name}"); + return; + } - switch (manifest.m_BlendMode) - { - case ExportableMaterialBlendMode.AdditiveBlend: - AddExtension(materialNode, EXT_blend_operations.Add); - materialNode.AlphaMode = AlphaMode.BLEND; - materialNode.DoubleSided = true; - break; - case ExportableMaterialBlendMode.AlphaMask: - materialNode.AlphaMode = AlphaMode.MASK; - materialNode.DoubleSided = true; - break; - case ExportableMaterialBlendMode.AlphaBlend: - materialNode.AlphaMode = AlphaMode.BLEND; - materialNode.DoubleSided = true; - break; - } + var brush = brushes[0]; + var manifest = BrushCatalog.m_Instance.GetBrush(brush.m_Guid); + + materialNode.Name = $"ob-{manifest.DurableName}"; + // Do we need to override the regular UnityGLTF logic here? + materialNode.DoubleSided = manifest.m_RenderBackfaces; + + switch (manifest.m_BlendMode) + { + case ExportableMaterialBlendMode.AdditiveBlend: + AddExtension(materialNode, EXT_blend_operations.Add); + materialNode.AlphaMode = AlphaMode.BLEND; + break; + case ExportableMaterialBlendMode.AlphaMask: + materialNode.AlphaMode = AlphaMode.MASK; + break; + case ExportableMaterialBlendMode.AlphaBlend: + materialNode.AlphaMode = AlphaMode.BLEND; + break; } } From f7233f6a6388acc75dc010f203a3e3153bce82a6 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 30 Jun 2024 16:21:42 +0100 Subject: [PATCH 056/105] Can't exclude BaseBrushScript gameobjects if we want to support unbatched strokes --- Assets/Scripts/Export/OpenBrushExportPlugin.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Export/OpenBrushExportPlugin.cs b/Assets/Scripts/Export/OpenBrushExportPlugin.cs index f84b242df1..1868e40805 100644 --- a/Assets/Scripts/Export/OpenBrushExportPlugin.cs +++ b/Assets/Scripts/Export/OpenBrushExportPlugin.cs @@ -177,12 +177,13 @@ public override bool ShouldNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfR { Type[] excludedTypes = { - typeof(BaseBrushScript), // Brush preview strokes (does this break non-batched exports?) typeof(SnapGrid3D), typeof(StencilWidget), typeof(CameraPathWidget) }; - return !excludedTypes.Any(t => transform.GetComponent(t) != null); + bool hasExcludedComponent = excludedTypes.Any(t => transform.GetComponent(t) != null); + bool excludedName = false; // TODO + return !hasExcludedComponent && !excludedName; } public override void BeforeNodeExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot, Transform transform, Node node) From b2f54b0ed555ae56a5f7f315ee01123ae89c4dcc Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 1 Jul 2024 12:02:41 +0100 Subject: [PATCH 057/105] API Brush json was included invalid brushes and name matching was buggy --- Assets/Scripts/API/ApiManager.cs | 43 ++++++++++++++------ Assets/Scripts/API/ApiMethods.DrawStrokes.cs | 15 ++----- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Assets/Scripts/API/ApiManager.cs b/Assets/Scripts/API/ApiManager.cs index 10f178c8ee..b65686450f 100644 --- a/Assets/Scripts/API/ApiManager.cs +++ b/Assets/Scripts/API/ApiManager.cs @@ -68,6 +68,9 @@ public static ApiManager Instance [NonSerialized] public Dictionary CommandExamples; public string m_startupScriptName = "startup.sketchscript"; + // Need to set this on the main thread because of localiztion + private string m_BrushesJson; + public string UserScriptsPath() { return m_UserScriptsPath; } void Awake() @@ -79,6 +82,7 @@ void Awake() App.HttpServer.AddHttpHandler($"/help/brushes", InfoCallback); App.HttpServer.AddRawHttpHandler("/cameraview", CameraViewCallback); PopulateApi(); + m_UserScripts = new Dictionary(); m_ExampleScripts = new Dictionary(); m_CommandStatuses = new Dictionary(); @@ -86,6 +90,7 @@ void Awake() PopulateUserScripts(); BrushTransformStack = new Stack<(Vector3, Quaternion)>(); ResetBrushTransform(); + if (!Directory.Exists(m_UserScriptsPath)) { Directory.CreateDirectory(m_UserScriptsPath); @@ -159,6 +164,16 @@ void Awake() App.Instance.StateChanged += RunStartupScript; } + void Start() + { + // HTTP API String substitutions + // Don't move to Awake() as that runs too early + string[] brushNameList = BrushCatalog.m_Instance.GetTagFilteredBrushList() + .Select(ApiFriendlyBrushName) + .ToArray(); + m_BrushesJson = JsonConvert.SerializeObject(brushNameList, Formatting.Indented); + } + public void ResetBrushTransform() { // Resets the "turtle" transform back to it's original values @@ -524,33 +539,35 @@ private string ScriptTemplateSubstitution(string html) { // TODO Document these + html = html.Replace("{{brushesJson}}", m_BrushesJson); - string[] brushNameList = BrushCatalog.m_Instance.AllBrushes - .Where(x => x.Description != "") - .Where(x => x.m_SupersededBy == null) - .Select(x => x.Description.Replace(" ", "").Replace(".", "").Replace("(", "").Replace(")", "")) - .ToArray(); - string brushesJson = JsonConvert.SerializeObject(brushNameList); - html = html.Replace("{{brushesJson}}", brushesJson); - - string pointFamilies = JsonConvert.SerializeObject(Enum.GetNames(typeof(SymmetryGroup.R))); + string pointFamilies = JsonConvert.SerializeObject(Enum.GetNames(typeof(SymmetryGroup.R)), Formatting.Indented); html = html.Replace("{{pointFamiliesJson}}", pointFamilies); - string wallpaperGroups = JsonConvert.SerializeObject(Enum.GetNames(typeof(PointSymmetry.Family))); + string wallpaperGroups = JsonConvert.SerializeObject(Enum.GetNames(typeof(PointSymmetry.Family)), Formatting.Indented); html = html.Replace("{{wallpaperGroupsJson}}", wallpaperGroups); string[] environmentNameList = EnvironmentCatalog.m_Instance.AllEnvironments .Select(x => x.Description.Replace(" ", "")) .ToArray(); - string environmentsJson = JsonConvert.SerializeObject(environmentNameList); + string environmentsJson = JsonConvert.SerializeObject(environmentNameList, Formatting.Indented); html = html.Replace("{{environmentsJson}}", environmentsJson); - string commandsJson = JsonConvert.SerializeObject(ListApiCommands()); + string commandsJson = JsonConvert.SerializeObject(ListApiCommands(), Formatting.Indented); html = html.Replace("{{commandsJson}}", commandsJson); return html; } + public static string ApiFriendlyBrushName(BrushDescriptor brush) + { + return brush.Description + .Replace(" ", "") + .Replace(".", "") + .Replace("(", "") + .Replace(")", ""); + } + public void ReceiveWebSocketMessage(WebSocketMessage message) { foreach (var cmd in message.data.Split("&")) @@ -662,7 +679,7 @@ private void OutgoingApiCommand() foreach (var listenerUrl in m_OutgoingApiListeners) { string getUri = $"{listenerUrl}?{command.Key}={command.Value}"; - if (getUri.Length < 512) // Actually limit is 2083 but let's be conservative + if (getUri.Length < 512) // Actually limit is 2083 but let's be conservative { StartCoroutine(GetRequest(getUri)); } diff --git a/Assets/Scripts/API/ApiMethods.DrawStrokes.cs b/Assets/Scripts/API/ApiMethods.DrawStrokes.cs index 895b7b362c..054c705eb5 100644 --- a/Assets/Scripts/API/ApiMethods.DrawStrokes.cs +++ b/Assets/Scripts/API/ApiMethods.DrawStrokes.cs @@ -26,7 +26,7 @@ public static partial class ApiMethods [ApiEndpoint("draw.paths", "Draws a series of paths at the current brush position [[[x1,y1,z1],[x2,y2,z2], etc...]]. Does not move the brush position")] public static void DrawPaths(string jsonString) { - // TODO Use brush rotation + // TODO Use brush rotation var origin = ApiManager.Instance.BrushPosition; var paths = JsonConvert.DeserializeObject>>>($"[{jsonString}]"); DrawStrokes.MultiPathsToStrokes(paths, origin); @@ -108,14 +108,9 @@ public static void Brush(string brushType) try { brushDescriptor = BrushCatalog.m_Instance.AllBrushes - .First(x => x.Description - .Replace(" ", "") - .Replace(".", "") - .Replace("(", "") - .Replace(")", "") - .ToLower() == brushType); + .First(x => ApiManager.ApiFriendlyBrushName(x).ToLower() == brushType); } - catch (InvalidOperationException e) + catch (InvalidOperationException) { Debug.LogError($"No brush found called: {brushType}"); } @@ -125,10 +120,6 @@ public static void Brush(string brushType) { PointerManager.m_Instance.SetBrushForAllPointers(brushDescriptor); } - else - { - Debug.LogError($"No brush found with the name or guid: {brushType}"); - } } [ApiEndpoint("color.add.hsv", "Adds the supplied values to the current color. Values are hue, saturation and value")] From a1419c3eae592a050e34a0164ab87cdae0e2b23e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 1 Jul 2024 12:36:52 +0100 Subject: [PATCH 058/105] Revert "Give ConcaveHull it's own copy of the MatteHull material to ease matching of material to manifest" This reverts commit 69ce46a0d1f42f62987a234f75be672e717a0ae4. --- .../Basic/ConcaveHull/ConcaveHull.asset | 3 +- .../Brushes/Basic/ConcaveHull/ConcaveHull.mat | 102 ------------------ .../Basic/ConcaveHull/ConcaveHull.mat.meta | 8 -- 3 files changed, 2 insertions(+), 111 deletions(-) delete mode 100644 Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat delete mode 100644 Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta diff --git a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset index e71b0e6745..c27d0d1000 100644 --- a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset +++ b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.asset @@ -24,6 +24,7 @@ MonoBehaviour: m_Supersedes: {fileID: 0} m_LooksIdentical: 0 m_ButtonTexture: {fileID: 2800000, guid: b54f6c68561104a4a94d4830df924aa5, type: 3} + m_Description: BRUSH_CONCAVEHULL m_LocalizedDescription: m_TableReference: m_TableCollectionName: GUID:c84355079ab3f3e4f8f3812258805f86 @@ -46,7 +47,7 @@ MonoBehaviour: m_VolumeVelocityRangeMultiplier: 1.5 m_AudioReactive: 0 m_ButtonAudio: {fileID: 0} - m_Material: {fileID: 2100000, guid: 6021fb85ff462234e99f3510d9525d24, type: 2} + m_Material: {fileID: 2100000, guid: 4eecef89d4dfcf243b2ceb7a6249ec47, type: 2} m_TextureAtlasV: 1 m_TileRate: 0.01 m_UseBloomSwatchOnColorPicker: 0 diff --git a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat deleted file mode 100644 index 9b71008488..0000000000 --- a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat +++ /dev/null @@ -1,102 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 6 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: ConcaveHull - m_Shader: {fileID: 4800000, guid: 76dc2f7fe973ede4bbbedfc291269c84, type: 3} - m_ShaderKeywords: _EMISSION - m_LightmapFlags: 1 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _Bottom: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Side: - m_Texture: {fileID: 2800000, guid: 006eb4ee45e4eee4b84cf30d54b01889, type: 3} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _Top: - m_Texture: {fileID: 2800000, guid: 640eb58bac1e8c349b8aef55f817627b, type: 3} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Floats: - - _BottomScale: 2 - - _Bulge: 2.25 - - _BumpScale: 1 - - _Cutoff: 0.5 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _EmissionGain: 0.5 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _NumSides: 5 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _ScrollJitterFrequency: 1 - - _ScrollJitterIntensity: 1 - - _ScrollRate: 1 - - _Shininess: 0.078125 - - _SideScale: 0.2 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _Speed: 1 - - _SpreadRate: 1.539 - - _SrcBlend: 1 - - _TopScale: 0.3 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - - _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0} diff --git a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta b/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta deleted file mode 100644 index 253b7b45e4..0000000000 --- a/Assets/Resources/Brushes/Basic/ConcaveHull/ConcaveHull.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 6021fb85ff462234e99f3510d9525d24 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: From b8cdc017cea725e4b6d32c297ed73f5681d43e82 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 10 Jul 2024 15:27:41 +0100 Subject: [PATCH 059/105] Updated urls --- Assets/Scripts/App.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index 3aa2fd3c9d..c9b8e0e39d 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -132,8 +132,8 @@ public enum AppState public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; // TODO Make these overridable - public static string ICOSA_WEBSITE_URL = "https://icosa.ixxy.co.uk"; - public static string ICOSA_API_URL = "https://icosa-api.ixxy.co.uk"; + public static string ICOSA_WEBSITE_URL = "https://icosa-api-django.ixxy.co.uk"; + public static string ICOSA_API_URL = "https://icosa-api-django.ixxy.co.uk/api/v1"; public string IcosaToken { From d530368e9927866fadfd90811ddb9178109f33d2 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 10 Jul 2024 15:38:52 +0100 Subject: [PATCH 060/105] Fix assets api url. remove useless property --- Assets/Scripts/Sharing/VrAssetService.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index bf3884cc9f..c88412087f 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -57,10 +57,9 @@ public class VrAssetService : MonoBehaviour const string kDefaultName = "sketch"; - private string kApiHost => App.ICOSA_API_URL; private string kAssetLandingPage => $"{App.ICOSA_WEBSITE_URL}/uploads"; - private const string kListAssetsUri = "/poly/assets"; + private const string kListAssetsUri = "/assets"; private const string kUserAssetsUri = "/users/me/assets"; private const string kUserLikesUri = "/users/me/likedassets"; @@ -238,7 +237,7 @@ public string ApiHost { string cfg = App.UserConfig?.Sharing.VrAssetServiceHostOverride; if (!string.IsNullOrEmpty(cfg)) { return cfg; } - return kApiHost; + return App.ICOSA_API_URL; } } From d7112ac40f744d45e02217b9588068e0d741526e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 11 Jul 2024 09:06:22 +0100 Subject: [PATCH 061/105] Editor utility to ease testing of logged in actions --- Assets/Editor/SetIcosaToken.cs | 58 +++++++++++++++++++++++++++++ Assets/Editor/SetIcosaToken.cs.meta | 3 ++ 2 files changed, 61 insertions(+) create mode 100644 Assets/Editor/SetIcosaToken.cs create mode 100644 Assets/Editor/SetIcosaToken.cs.meta diff --git a/Assets/Editor/SetIcosaToken.cs b/Assets/Editor/SetIcosaToken.cs new file mode 100644 index 0000000000..eb7f7b3132 --- /dev/null +++ b/Assets/Editor/SetIcosaToken.cs @@ -0,0 +1,58 @@ +// Copyright 2021 The Tilt Brush Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace TiltBrush +{ + + using UnityEngine; + using UnityEditor; + + public class SetIcosaToken : EditorWindow + { + private static string key = "IcosaToken"; + private static string value = ""; + + [MenuItem("Open Brush/Icosa/Set Login Token")] + public static void ShowWindow() + { + value = PlayerPrefs.GetString(key); + GetWindow("Set Icosa Login Token"); + } + + void OnGUI() + { + GUILayout.Label("Enter Token", EditorStyles.boldLabel); + value = EditorGUILayout.TextField("Value", value); + + if (GUILayout.Button("Save")) + { + PlayerPrefs.SetString(key, value); + PlayerPrefs.Save(); + Debug.Log($"Saved: {key} = {value}"); + } + + if (GUILayout.Button("Load")) + { + value = PlayerPrefs.GetString(key, ""); + Debug.Log($"Loaded: {key} = {value}"); + } + + if (GUILayout.Button("Delete")) + { + PlayerPrefs.DeleteKey(key); + Debug.Log($"Deleted: {key}"); + } + } + } +} diff --git a/Assets/Editor/SetIcosaToken.cs.meta b/Assets/Editor/SetIcosaToken.cs.meta new file mode 100644 index 0000000000..eff53debf6 --- /dev/null +++ b/Assets/Editor/SetIcosaToken.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5918c754a3d344139ef0469bc3e3fbce +timeCreated: 1720622791 \ No newline at end of file From 14baac56f739e45ac98ae64a7c298ecb2b179f04 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 11 Jul 2024 09:24:19 +0100 Subject: [PATCH 062/105] Unused method --- Assets/Scripts/Sharing/WebRequest.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Assets/Scripts/Sharing/WebRequest.cs b/Assets/Scripts/Sharing/WebRequest.cs index 3abec3dd88..dd4cc6fd7c 100644 --- a/Assets/Scripts/Sharing/WebRequest.cs +++ b/Assets/Scripts/Sharing/WebRequest.cs @@ -221,13 +221,6 @@ public static string RedactUriForError(string uriString) #endif } - /// Convenience method - public static Task GetAsync(string uri) - { - return new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET) - .SendAsync(); - } - private string m_Uri; private string m_Method; From ba2d85dcdb59ead825be8869ae802c423e673ade Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 11 Jul 2024 11:46:32 +0100 Subject: [PATCH 063/105] Allow unauthenticated logins --- Assets/Scripts/Sharing/WebRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/WebRequest.cs b/Assets/Scripts/Sharing/WebRequest.cs index dd4cc6fd7c..f9f43450c7 100644 --- a/Assets/Scripts/Sharing/WebRequest.cs +++ b/Assets/Scripts/Sharing/WebRequest.cs @@ -299,7 +299,7 @@ public WebRequest(string uri, OAuth2Identity identity, } // identity may be null, in which case no authentication takes place - public WebRequest(string uri, string loginToken, + public WebRequest(string uri, string loginToken = null, string method = UnityWebRequest.kHttpVerbGET, bool compress = false) { if (string.IsNullOrEmpty(uri)) From 2f39e33dbf68945cd276eedab4e89291d7b6623c Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 11 Jul 2024 11:47:27 +0100 Subject: [PATCH 064/105] Use Icosa icon instead of Poly --- Assets/Prefabs/Panels/IcosaPanel.prefab | 376 ++++++--- Assets/Prefabs/Panels/SketchbookPanel.prefab | 770 ++++++++++++++----- 2 files changed, 835 insertions(+), 311 deletions(-) diff --git a/Assets/Prefabs/Panels/IcosaPanel.prefab b/Assets/Prefabs/Panels/IcosaPanel.prefab index ac8ccd224a..43f3ee9a87 100644 --- a/Assets/Prefabs/Panels/IcosaPanel.prefab +++ b/Assets/Prefabs/Panels/IcosaPanel.prefab @@ -27,13 +27,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 106270} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.666, y: -0.927, z: 0.05} m_LocalScale: {x: 0.27, y: 0.27, z: 0.27} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3345126 MeshFilter: @@ -93,9 +93,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 106270} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11464876 @@ -177,6 +185,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 141494} + serializedVersion: 2 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} @@ -186,7 +195,6 @@ Transform: - {fileID: 4000011255173414} - {fileID: 465812} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114000010862122888 MonoBehaviour: @@ -391,13 +399,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 146636} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.1} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3338058 MeshFilter: @@ -430,6 +438,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 167476} + serializedVersion: 2 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} @@ -463,7 +472,6 @@ Transform: - {fileID: 4000013357198230} - {fileID: 1728879999287929109} m_Father: {fileID: 471112} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &170558 GameObject: @@ -489,13 +497,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 170558} + serializedVersion: 2 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: 471112} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6578658 BoxCollider: @@ -505,9 +513,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 170558} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.3084, y: 2.8000002, z: 0.5} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &177738 @@ -534,13 +550,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 177738} + serializedVersion: 2 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: 465812} - m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6508354 BoxCollider: @@ -550,9 +566,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 177738} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.8083999, y: 2.3000002, z: 0.02} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &199414 @@ -582,13 +606,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 199414} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.247, y: -0.927, z: 0.05} m_LocalScale: {x: 0.27, y: 0.27, z: 0.27} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3323192 MeshFilter: @@ -648,9 +672,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 199414} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11463628 @@ -733,6 +765,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010087135480} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.627, y: -0.108, z: 0.08} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} @@ -740,7 +773,6 @@ Transform: m_Children: - {fileID: 4000012374902596} m_Father: {fileID: 465812} - m_RootOrder: 15 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010907512276 MeshFilter: @@ -800,9 +832,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010087135480} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000010337963734 @@ -894,13 +934,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010298050724} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010071936162} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011346989110 MeshFilter: @@ -977,13 +1017,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010313941914} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012902286828} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011347804352 MeshFilter: @@ -1058,13 +1098,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010339999408} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013774678408} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000010474425972 GameObject: @@ -1091,13 +1131,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010474425972} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012676471016} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012427647034 MeshFilter: @@ -1174,13 +1214,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010592129534} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010583109832} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013295006944 MeshFilter: @@ -1258,13 +1298,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010887400618} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.129, y: 0.3911, z: -0.041999996} m_LocalScale: {x: 0.17, y: 0.17, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010258256146} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010323384624 MeshFilter: @@ -1282,9 +1322,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010887400618} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -1354,6 +1402,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010933935756} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.1, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -1362,7 +1411,6 @@ Transform: - {fileID: 2631587096066588542} - {fileID: 4000010274953216} m_Father: {fileID: 465812} - m_RootOrder: 25 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000011207303254 MeshRenderer: @@ -1429,13 +1477,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010957668132} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013774678408} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000010961947472 GameObject: @@ -1463,13 +1511,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010961947472} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.05999999} m_LocalScale: {x: 2, y: 1, z: 0.8783332} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013940156938} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011003021764 MeshFilter: @@ -1487,9 +1535,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010961947472} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -1560,13 +1616,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011102024434} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013851072734} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012816624386 MeshFilter: @@ -1644,13 +1700,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011118790432} + serializedVersion: 2 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: 465812} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011393229142 MeshFilter: @@ -1742,6 +1798,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011266074316} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.207, y: -0.525, z: 0.08} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} @@ -1749,7 +1806,6 @@ Transform: m_Children: - {fileID: 4000010573672924} m_Father: {fileID: 465812} - m_RootOrder: 17 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012808144226 MeshFilter: @@ -1809,9 +1865,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011266074316} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000013156484394 @@ -1901,13 +1965,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011318793836} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013774678408} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000011418975696 GameObject: @@ -1936,6 +2000,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011418975696} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.627, y: -0.525, z: 0.08} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} @@ -1943,7 +2008,6 @@ Transform: m_Children: - {fileID: 4000012573971770} m_Father: {fileID: 465812} - m_RootOrder: 18 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010171189952 MeshFilter: @@ -2003,9 +2067,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011418975696} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000010451906298 @@ -2097,13 +2169,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011603093424} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.68239236, y: 0.002720356, z: 0.014} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 19 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012418862958 MeshFilter: @@ -2182,13 +2254,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011686355436} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.693, y: 0.942, z: 0} m_LocalScale: {x: 0.27, y: 0.27, z: 0.27} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011749087192 MeshFilter: @@ -2248,9 +2320,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011686355436} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000011188303058 @@ -2289,7 +2369,7 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: da8fcd82368826b4cad505e8249bd63c, type: 3} + m_ButtonTexture: {fileID: 2800000, guid: ca2b2689a21743448be1a5306c61e90b, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 @@ -2358,6 +2438,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011746198504} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: -0.406, z: -0.054} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -2366,7 +2447,6 @@ Transform: - {fileID: 2459190327082045982} - {fileID: 4000012173239436} m_Father: {fileID: 4000014190517824} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012570074730 MeshFilter: @@ -2426,9 +2506,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011746198504} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000013674408190 @@ -2536,6 +2624,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011782189388} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.21, y: -0.406, z: -0.054000005} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -2544,7 +2633,6 @@ Transform: - {fileID: 2001505504518591488} - {fileID: 4000011038848668} m_Father: {fileID: 4000010713138772} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010848474370 MeshFilter: @@ -2682,9 +2770,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011782189388} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1000011854602082 @@ -2710,6 +2806,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011854602082} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.209, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -2718,7 +2815,6 @@ Transform: - {fileID: 4000014150265184} - {fileID: 4000011110165888} m_Father: {fileID: 4000010185127306} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000011887412796 GameObject: @@ -2745,13 +2841,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011887412796} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012505868838} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012418927610 MeshFilter: @@ -2828,13 +2924,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011916187790} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012676471016} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012203188556 MeshFilter: @@ -2909,6 +3005,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011934193470} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -2917,7 +3014,6 @@ Transform: - {fileID: 4000010681971296} - {fileID: 4000013032411082} m_Father: {fileID: 4000013357198230} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000011998859986 GameObject: @@ -2943,6 +3039,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011998859986} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.1, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -2952,7 +3049,6 @@ Transform: - {fileID: 4000013940156938} - {fileID: 4000010583109832} m_Father: {fileID: 465812} - m_RootOrder: 24 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000010342337010 MeshRenderer: @@ -3023,6 +3119,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012065099252} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.206, y: -0.525, z: 0.08} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} @@ -3030,7 +3127,6 @@ Transform: m_Children: - {fileID: 4000011549167550} m_Father: {fileID: 465812} - m_RootOrder: 16 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010156527234 MeshFilter: @@ -3090,9 +3186,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012065099252} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000013205858318 @@ -3186,13 +3290,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012109970132} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.68226534, y: 0.289, z: 0.003} m_LocalScale: {x: 0.393, y: 0.395, z: 0.395} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000014007660200 MeshFilter: @@ -3302,9 +3406,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012109970132} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.1, y: 0.8, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1000012134891236 @@ -3332,13 +3444,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012134891236} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013437124302} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012148575110 MeshFilter: @@ -3415,13 +3527,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012156671066} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010274953216} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013878566190 MeshFilter: @@ -3496,6 +3608,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012254481722} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -3504,7 +3617,6 @@ Transform: - {fileID: 4000012695734804} - {fileID: 4000011402503074} m_Father: {fileID: 4000014190517824} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000012257498920 GameObject: @@ -3530,6 +3642,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012257498920} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.1, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -3540,7 +3653,6 @@ Transform: - {fileID: 4000013275467134} - {fileID: 4000013851072734} m_Father: {fileID: 465812} - m_RootOrder: 22 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000013438199350 MeshRenderer: @@ -3609,13 +3721,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012433234120} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013851072734} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011902349082 MeshFilter: @@ -3690,6 +3802,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012535319180} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -3698,7 +3811,6 @@ Transform: - {fileID: 4000013572391784} - {fileID: 4000010847277736} m_Father: {fileID: 4000010713138772} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000012632648426 GameObject: @@ -3725,13 +3837,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012632648426} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010583109832} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010560099168 MeshFilter: @@ -3809,13 +3921,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012648007250} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.05999999} m_LocalScale: {x: 2, y: 1, z: 0.8783332} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012309134638} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011299344062 MeshFilter: @@ -3833,9 +3945,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012648007250} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -3905,6 +4025,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012677157260} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.1, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -3914,7 +4035,6 @@ Transform: - {fileID: 4000012135202400} - {fileID: 4000012676471016} m_Father: {fileID: 465812} - m_RootOrder: 23 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000013656731088 MeshRenderer: @@ -3983,13 +4103,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012700630038} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012135202400} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010348517304 MeshFilter: @@ -4068,6 +4188,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012840250180} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: -0.406, z: -0.054000005} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -4076,7 +4197,6 @@ Transform: - {fileID: 944172704919589472} - {fileID: 4000010958123902} m_Father: {fileID: 4000010185127306} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011317273212 MeshFilter: @@ -4136,9 +4256,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012840250180} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000012803435128 @@ -4243,6 +4371,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012895512256} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.1, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -4252,7 +4381,6 @@ Transform: - {fileID: 4000010287137376} - {fileID: 4000012902286828} m_Father: {fileID: 465812} - m_RootOrder: 20 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000013360289746 MeshRenderer: @@ -4323,6 +4451,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013033931112} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.206, y: -0.108, z: 0.08} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} @@ -4330,7 +4459,6 @@ Transform: m_Children: - {fileID: 4000012934970154} m_Father: {fileID: 465812} - m_RootOrder: 13 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010877679830 MeshFilter: @@ -4390,9 +4518,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013033931112} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000012757722782 @@ -4482,6 +4618,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013045178456} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.2, y: 0.44, z: 0.08} m_LocalScale: {x: 0.59999996, y: 0.59999996, z: 0.59999996} @@ -4494,7 +4631,6 @@ Transform: - {fileID: 4000012973175376} - {fileID: 4000011998283800} m_Father: {fileID: 465812} - m_RootOrder: 12 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013164525870 GameObject: @@ -4519,6 +4655,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013164525870} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -4527,7 +4664,6 @@ Transform: - {fileID: 4000014152566328} - {fileID: 4000012457610374} m_Father: {fileID: 4000010258256146} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013194522416 GameObject: @@ -4554,13 +4690,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013194522416} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012902286828} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000014091131168 MeshFilter: @@ -4635,13 +4771,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013227438200} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013774678408} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013244881560 GameObject: @@ -4666,13 +4802,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013244881560} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013774678408} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013669222526 GameObject: @@ -4699,13 +4835,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013669222526} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1.9083999, y: 2.4674253, z: 2.158998} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 471112} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012204931114 MeshFilter: @@ -4784,13 +4920,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013717588074} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.68226534, y: 0, z: 0.003} m_LocalScale: {x: 0.393, y: 0.393, z: 0.393} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010703057562 MeshFilter: @@ -4850,9 +4986,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013717588074} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.1, y: 0.8, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114020894668800226 @@ -4928,13 +5072,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013727554954} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013774678408} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013820106702 GameObject: @@ -4963,13 +5107,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013820106702} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.68226534, y: -0.287, z: 0.003} m_LocalScale: {x: 0.393, y: 0.393, z: 0.393} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012084355200 MeshFilter: @@ -5079,9 +5223,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013820106702} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.1, y: 0.8, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1000013864904278 @@ -5109,13 +5261,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013864904278} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010375171238} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011183412372 MeshFilter: @@ -5193,13 +5345,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013965171982} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.05999999} m_LocalScale: {x: 2, y: 1, z: 0.8783332} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010287137376} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013560793154 MeshFilter: @@ -5217,9 +5369,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013965171982} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -5290,13 +5450,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014019108166} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012918810746} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012744993406 MeshFilter: @@ -5373,13 +5533,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014078913134} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010274953216} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011738738920 MeshFilter: @@ -5458,6 +5618,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014142736920} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.207, y: -0.108, z: 0.08} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} @@ -5465,7 +5626,6 @@ Transform: m_Children: - {fileID: 4000011362327722} m_Father: {fileID: 465812} - m_RootOrder: 14 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010874363134 MeshFilter: @@ -5525,9 +5685,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014142736920} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000010401740614 @@ -5619,13 +5787,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014145540092} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0, z: -0.01} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010489297534} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013079542252 MeshFilter: @@ -5704,6 +5872,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014265003938} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: -0.406, z: -0.054000005} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -5712,7 +5881,6 @@ Transform: - {fileID: 332452329397556464} - {fileID: 4000011628422548} m_Father: {fileID: 4000010258256146} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010574217940 MeshFilter: @@ -5772,9 +5940,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014265003938} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000012405969980 @@ -5879,6 +6055,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1021259386128138} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.16, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -5887,7 +6064,6 @@ Transform: - {fileID: 224789936601444018} - {fileID: 4882862779753528} m_Father: {fileID: 465812} - m_RootOrder: 21 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23005740735915826 MeshRenderer: @@ -5956,13 +6132,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1584545852907080} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4882862779753528} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33710099620813864 MeshFilter: @@ -6046,7 +6222,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4932507317400606} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6253,6 +6428,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1906655395918520} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: 0.06400013, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -6261,7 +6437,6 @@ Transform: - {fileID: 4855091873576358} - {fileID: 4101333333006384} m_Father: {fileID: 4932507317400606} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1950131066522348 GameObject: @@ -6288,13 +6463,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1950131066522348} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4882862779753528} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33966215123458588 MeshFilter: @@ -6378,7 +6553,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000014190517824} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6595,7 +6769,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010713138772} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6812,7 +6985,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012135202400} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -7025,7 +7197,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010287137376} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -7229,6 +7400,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1816486902914083968} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.209, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -7237,7 +7409,6 @@ Transform: - {fileID: 1170610089143318067} - {fileID: 8020598781474349598} m_Father: {fileID: 1728879999287929109} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2345216971250741339 GameObject: @@ -7264,13 +7435,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2345216971250741339} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7047693795861342256} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &4058447100505187227 MeshFilter: @@ -7346,6 +7517,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2503533423454172181} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.1, z: -0.05} m_LocalScale: {x: 1, y: 1, z: 1} @@ -7354,7 +7526,6 @@ Transform: - {fileID: 5036818937176549477} - {fileID: 7047693795861342256} m_Father: {fileID: 465812} - m_RootOrder: 26 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &6343569852496349475 MeshRenderer: @@ -7430,7 +7601,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1728879999287929109} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -7644,7 +7814,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -7817,7 +7986,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013357198230} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8038,7 +8206,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8251,7 +8418,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010185127306} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8461,13 +8627,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8549073727040760058} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7047693795861342256} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5826445933073688365 MeshFilter: @@ -8551,7 +8717,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 465812} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8764,7 +8929,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010258256146} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8981,7 +9145,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012309134638} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -9194,7 +9357,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013940156938} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} diff --git a/Assets/Prefabs/Panels/SketchbookPanel.prefab b/Assets/Prefabs/Panels/SketchbookPanel.prefab index 4030ed258e..2e0735cbe4 100644 --- a/Assets/Prefabs/Panels/SketchbookPanel.prefab +++ b/Assets/Prefabs/Panels/SketchbookPanel.prefab @@ -28,6 +28,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 101820} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.7678, y: -0.5287, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -36,7 +37,6 @@ Transform: - {fileID: 4000012287575696} - {fileID: 4044106463973608} m_Father: {fileID: 473678} - m_RootOrder: 12 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3348596 MeshFilter: @@ -96,9 +96,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 101820} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11403980 @@ -193,13 +201,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 102468} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.42, y: -0.991, z: -0.04} m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 17 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3392684 MeshFilter: @@ -259,9 +267,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 102468} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11400036 @@ -342,13 +358,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 109144} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.5, z: -0.0025} m_LocalScale: {x: 1, y: 0.02857143, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 447228} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3375698 MeshFilter: @@ -425,6 +441,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 109932} + serializedVersion: 2 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} @@ -433,7 +450,6 @@ Transform: - {fileID: 400638} - {fileID: 473678} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &2478559423573741591 MonoBehaviour: @@ -611,7 +627,7 @@ MonoBehaviour: m_NoShowcaseMessage: {fileID: 1147890328312932} m_ContactingServerMessage: {fileID: 1000011835935838} m_OutOfDateMessage: {fileID: 1000012586849750} - m_NoPolyConnectionMessage: {fileID: 4374687820491222719} + m_NoIcosaConnectionMessage: {fileID: 4374687820491222719} m_OnlineGalleryButtonRenderer: {fileID: 23175542353181748} m_IconsOnFirstPage: - {fileID: 142432} @@ -688,13 +704,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 116946} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0, z: -0.1} m_LocalScale: {x: 2, y: 2, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 475426} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2321406 MeshRenderer: @@ -785,13 +801,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 125268} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 458322} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3362976 MeshFilter: @@ -867,13 +883,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 126012} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.1, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 405018} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6504288 BoxCollider: @@ -883,9 +899,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 126012} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.44, y: 2.7, z: 0.5} m_Center: {x: 0, y: -0.06, z: 0} --- !u!1 &127584 @@ -915,6 +939,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 127584} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.84, y: 0.4779, z: -0.0301} m_LocalScale: {x: 0.436, y: 0.436, z: 0.436} @@ -924,7 +949,6 @@ Transform: - {fileID: 458322} - {fileID: 431352} m_Father: {fileID: 473678} - m_RootOrder: 13 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3366994 MeshFilter: @@ -984,9 +1008,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 127584} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.12, y: 0.73, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11443276 @@ -1066,6 +1098,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 129824} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.132, y: -0.991, z: -0.04} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} @@ -1073,7 +1106,6 @@ Transform: m_Children: - {fileID: 440846} m_Father: {fileID: 473678} - m_RootOrder: 18 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3302166 MeshFilter: @@ -1133,9 +1165,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 129824} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: -0.1} --- !u!114 &11474338 @@ -1216,13 +1256,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 130496} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 479382} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3319264 MeshFilter: @@ -1299,13 +1339,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 131562} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0, z: -0.1} m_LocalScale: {x: 2, y: 2, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 423072} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2305932 MeshRenderer: @@ -1396,6 +1436,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 135162} + serializedVersion: 2 m_LocalRotation: {x: 0.5000001, y: 0.5, z: -0.5, w: 0.49999994} m_LocalPosition: {x: 0, y: 1.2142847, z: 0.4594286} m_LocalScale: {x: 0.9142857, y: 1, z: 1} @@ -1404,7 +1445,6 @@ Transform: - {fileID: 432950} - {fileID: 471736} m_Father: {fileID: 490548} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3351754 MeshFilter: @@ -1481,13 +1521,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 136488} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 458322} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3389112 MeshFilter: @@ -1566,6 +1606,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 136962} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.63, y: -0.991, z: -0.04} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} @@ -1573,7 +1614,6 @@ Transform: m_Children: - {fileID: 440778} m_Father: {fileID: 473678} - m_RootOrder: 22 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3357382 MeshFilter: @@ -1633,9 +1673,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 136962} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: -0.1} --- !u!114 &11499286 @@ -1714,6 +1762,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 138864} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.1, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -1763,7 +1812,6 @@ Transform: - {fileID: 4604395735417884} - {fileID: 4000012925136122} m_Father: {fileID: 405018} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &142258 GameObject: @@ -1792,6 +1840,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 142258} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.436, y: -0.991, z: -0.04} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} @@ -1799,7 +1848,6 @@ Transform: m_Children: - {fileID: 408284} m_Father: {fileID: 473678} - m_RootOrder: 21 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3356862 MeshFilter: @@ -1859,9 +1907,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 142258} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: -0.1} --- !u!114 &11408728 @@ -1945,6 +2001,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 142432} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.012, y: 0.249, z: -0.03} m_LocalScale: {x: 0.935, y: 0.935, z: 0.45} @@ -1953,7 +2010,6 @@ Transform: - {fileID: 4000012999570806} - {fileID: 4819910167133682} m_Father: {fileID: 473678} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3345518 MeshFilter: @@ -2013,9 +2069,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 142432} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11452860 @@ -2108,6 +2172,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 148956} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: 0.35714287, z: 0.002285719} m_LocalScale: {x: 1.7142856, y: 1, z: 1} @@ -2116,7 +2181,6 @@ Transform: - {fileID: 434670} - {fileID: 454538} m_Father: {fileID: 490548} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3365804 MeshFilter: @@ -2196,6 +2260,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 153576} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.247, y: -0.008, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -2204,7 +2269,6 @@ Transform: - {fileID: 4000013461333742} - {fileID: 4192096235684524} m_Father: {fileID: 473678} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3354416 MeshFilter: @@ -2264,9 +2328,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 153576} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11475750 @@ -2362,6 +2434,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 155428} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.7698, y: -0.012, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -2370,7 +2443,6 @@ Transform: - {fileID: 4000011053558040} - {fileID: 4969141364180978} m_Father: {fileID: 473678} - m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3388248 MeshFilter: @@ -2430,9 +2502,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 155428} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11413066 @@ -2527,6 +2607,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 155754} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.84, y: -0.1593, z: -0.0301} m_LocalScale: {x: 0.43600002, y: 0.43600002, z: 0.43600002} @@ -2536,7 +2617,6 @@ Transform: - {fileID: 479382} - {fileID: 499714} m_Father: {fileID: 473678} - m_RootOrder: 15 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3306764 MeshFilter: @@ -2596,9 +2676,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 155754} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.12, y: 0.73, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11456692 @@ -2676,13 +2764,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162048} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: 0.00228548} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 431352} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3335230 MeshFilter: @@ -2762,6 +2850,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 166396} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.247, y: -0.5287, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -2770,7 +2859,6 @@ Transform: - {fileID: 4000013138638754} - {fileID: 4916153691783676} m_Father: {fileID: 473678} - m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3390222 MeshFilter: @@ -2830,9 +2918,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 166396} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11452280 @@ -2928,6 +3024,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 167238} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.7698, y: 0.5129, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -2936,7 +3033,6 @@ Transform: - {fileID: 4000013478852842} - {fileID: 4648845093172990} m_Father: {fileID: 473678} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3309702 MeshFilter: @@ -2996,9 +3092,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 167238} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11486740 @@ -3091,13 +3195,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 167948} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0, z: -0.1} m_LocalScale: {x: 2, y: 2, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 416688} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2384872 MeshRenderer: @@ -3188,13 +3292,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 168646} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -0.1} m_LocalScale: {x: 2, y: 2, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499328} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2380942 MeshRenderer: @@ -3288,6 +3392,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 169670} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.2737, y: -0.5287, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -3296,7 +3401,6 @@ Transform: - {fileID: 4000011500092316} - {fileID: 4069340866196592} m_Father: {fileID: 473678} - m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3348150 MeshFilter: @@ -3356,9 +3460,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 169670} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11490938 @@ -3453,6 +3565,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 171976} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.245, y: -0.991, z: -0.04} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} @@ -3460,7 +3573,6 @@ Transform: m_Children: - {fileID: 420674} m_Father: {fileID: 473678} - m_RootOrder: 20 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3376740 MeshFilter: @@ -3520,9 +3632,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 171976} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: -0.1} --- !u!114 &11406912 @@ -3603,13 +3723,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 172220} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: 0.00228548} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499714} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3364482 MeshFilter: @@ -3686,13 +3806,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 173116} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: -0.0024999883} m_LocalScale: {x: 1, y: 0.028571432, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 447228} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3359794 MeshFilter: @@ -3771,6 +3891,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 174376} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.058, y: -0.991, z: -0.04} m_LocalScale: {x: 0.15, y: 0.15, z: 0.15} @@ -3778,7 +3899,6 @@ Transform: m_Children: - {fileID: 494418} m_Father: {fileID: 473678} - m_RootOrder: 19 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3373530 MeshFilter: @@ -3838,9 +3958,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 174376} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: -0.1} --- !u!114 &11468240 @@ -3921,6 +4049,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 175552} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: -1.0697142, z: 0.9165714} m_LocalScale: {x: 4.5714283, y: 1, z: 1} @@ -3929,7 +4058,6 @@ Transform: - {fileID: 441124} - {fileID: 427536} m_Father: {fileID: 490548} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3319242 MeshFilter: @@ -4006,13 +4134,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 176914} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: -0.0024999883} m_LocalScale: {x: 1, y: 0.028571432, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 412038} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3313266 MeshFilter: @@ -4092,6 +4220,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 177278} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.249, y: 0.5129, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -4100,7 +4229,6 @@ Transform: - {fileID: 4000012910955364} - {fileID: 4840805683560546} m_Father: {fileID: 473678} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3327974 MeshFilter: @@ -4160,9 +4288,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 177278} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11431774 @@ -4258,6 +4394,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 178250} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.2737, y: -0.008, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -4266,7 +4403,6 @@ Transform: - {fileID: 4000012616996494} - {fileID: 4284873399471688} m_Father: {fileID: 473678} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3363168 MeshFilter: @@ -4326,9 +4462,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 178250} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11477404 @@ -4421,6 +4565,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 178916} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: 0.35714287, z: 0.002285719} m_LocalScale: {x: 1.7142856, y: 1, z: 1} @@ -4429,7 +4574,6 @@ Transform: - {fileID: 465110} - {fileID: 484996} m_Father: {fileID: 468632} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3314012 MeshFilter: @@ -4506,13 +4650,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 183508} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 479382} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3395878 MeshFilter: @@ -4589,13 +4733,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 183652} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0, z: -0.1} m_LocalScale: {x: 2, y: 2, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 494534} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2397020 MeshRenderer: @@ -4686,13 +4830,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 188732} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.5, z: -0.0025} m_LocalScale: {x: 1, y: 0.02857143, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 412038} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3342042 MeshFilter: @@ -4769,6 +4913,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 192230} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: -1.0697142, z: 0.9165714} m_LocalScale: {x: 4.5714283, y: 1, z: 1} @@ -4777,7 +4922,6 @@ Transform: - {fileID: 441958} - {fileID: 498352} m_Father: {fileID: 468632} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3374636 MeshFilter: @@ -4854,13 +4998,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 194474} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: 0.0022855483} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 431352} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3334046 MeshFilter: @@ -4937,6 +5081,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 195798} + serializedVersion: 2 m_LocalRotation: {x: 0.5000001, y: 0.5, z: -0.5, w: 0.49999994} m_LocalPosition: {x: 0, y: 1.2142847, z: 0.4594286} m_LocalScale: {x: 0.9142857, y: 1, z: 1} @@ -4945,7 +5090,6 @@ Transform: - {fileID: 453004} - {fileID: 467276} m_Father: {fileID: 468632} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3326064 MeshFilter: @@ -5021,13 +5165,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 196078} + serializedVersion: 2 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: 473678} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6509770 BoxCollider: @@ -5037,9 +5181,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 196078} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.44, y: 2.7, z: 0.042} m_Center: {x: 0, y: -0.06, z: -0.01} --- !u!1 &196312 @@ -5069,13 +5221,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 196312} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.912, y: -0.991, z: -0.04} m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 23 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3342126 MeshFilter: @@ -5135,9 +5287,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 196312} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &11459642 @@ -5218,13 +5378,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 197350} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: 0.0022855483} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 499714} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3369242 MeshFilter: @@ -5301,13 +5461,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010118716738} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000011206878738} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011125244486 MeshFilter: @@ -5384,6 +5544,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010134702830} + serializedVersion: 2 m_LocalRotation: {x: 0.5000001, y: 0.5, z: -0.5, w: 0.49999994} m_LocalPosition: {x: 0, y: 1.2142847, z: 0.4594286} m_LocalScale: {x: 0.9142857, y: 1, z: 1} @@ -5392,7 +5553,6 @@ Transform: - {fileID: 4000010892569700} - {fileID: 4000013078219060} m_Father: {fileID: 4000010470004098} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011206434982 MeshFilter: @@ -5472,6 +5632,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010241860486} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.2717, y: 0.5129, z: -0.03} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} @@ -5480,7 +5641,6 @@ Transform: - {fileID: 4000011338142394} - {fileID: 4758236918301830} m_Father: {fileID: 473678} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012812584494 MeshFilter: @@ -5540,9 +5700,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010241860486} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000013156271550 @@ -5642,7 +5810,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013760615668} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -5852,13 +6019,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010365406468} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013868811186} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011204178654 MeshFilter: @@ -5933,6 +6100,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010401472448} + serializedVersion: 2 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} @@ -5942,7 +6110,6 @@ Transform: - {fileID: 4000014046535472} - {fileID: 4000013013276346} m_Father: {fileID: 473678} - m_RootOrder: 42 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000010567837980 GameObject: @@ -5970,13 +6137,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010567837980} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.1212, y: 0.383, z: -0.041999996} m_LocalScale: {x: 0.17, y: 0.17, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013760615668} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011881333540 MeshFilter: @@ -5994,9 +6161,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010567837980} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -6065,6 +6240,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010667475366} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.000000014901161, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -6073,7 +6249,6 @@ Transform: - {fileID: 4000011744107576} - {fileID: 4000011886445864} m_Father: {fileID: 4000011114460008} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000010683355590 GameObject: @@ -6102,13 +6277,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010683355590} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.839, y: -0.991, z: -0.0311} m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 25 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012255737950 MeshFilter: @@ -6168,9 +6343,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010683355590} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114911010639464544 @@ -6297,13 +6480,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010744527238} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013175241986} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012057045804 MeshFilter: @@ -6379,6 +6562,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010821915206} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -6389,7 +6573,6 @@ Transform: - {fileID: 4000012999227018} - {fileID: 4000013454894384} m_Father: {fileID: 473678} - m_RootOrder: 34 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000010582741524 MeshRenderer: @@ -6460,13 +6643,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010881089914} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 476782} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011808528658 MeshFilter: @@ -6526,9 +6709,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010881089914} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114095498643358690 @@ -6605,13 +6796,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011094475738} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012925136122} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011121147842 MeshFilter: @@ -6688,13 +6879,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011216287484} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013454894384} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012369206972 MeshFilter: @@ -6771,13 +6962,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011517226962} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000011206878738} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011120581400 MeshFilter: @@ -6856,13 +7047,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011551410764} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010333377928} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011412534472 MeshFilter: @@ -6922,9 +7113,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011551410764} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114335706120377868 @@ -7003,6 +7202,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011622452938} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.84, y: 0.1593, z: -0.0301} m_LocalScale: {x: 0.436, y: 0.436, z: 0.436} @@ -7012,7 +7212,6 @@ Transform: - {fileID: 4000011206878738} - {fileID: 4000011225904734} m_Father: {fileID: 473678} - m_RootOrder: 14 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010882293456 MeshFilter: @@ -7072,9 +7271,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011622452938} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.12, y: 0.73, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000011006960986 @@ -7150,6 +7357,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011692107830} + serializedVersion: 2 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} @@ -7158,7 +7366,6 @@ Transform: - {fileID: 4000010769280002} - {fileID: 4000012312614358} m_Father: {fileID: 4000013760615668} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000011797134698 GameObject: @@ -7185,13 +7392,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011797134698} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.5, z: -0.0025} m_LocalScale: {x: 1, y: 0.02857143, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000014044617982} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011351091714 MeshFilter: @@ -7267,6 +7474,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011835935838} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -7276,7 +7484,6 @@ Transform: - {fileID: 4000010399852960} - {fileID: 4000013175241986} m_Father: {fileID: 473678} - m_RootOrder: 35 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000010716088472 MeshRenderer: @@ -7347,13 +7554,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012110338046} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 461906} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012807620068 MeshFilter: @@ -7413,9 +7620,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012110338046} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114866655278054368 @@ -7492,13 +7707,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012149669276} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: 0.00228548} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000011225904734} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010883985424 MeshFilter: @@ -7575,13 +7790,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012311522496} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: -0.0024999883} m_LocalScale: {x: 1, y: 0.028571432, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000014044617982} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012851111020 MeshFilter: @@ -7658,6 +7873,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012495519762} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: 0.35714287, z: 0.002285719} m_LocalScale: {x: 1.7142856, y: 1, z: 1} @@ -7666,7 +7882,6 @@ Transform: - {fileID: 4000012956736620} - {fileID: 4000010013684490} m_Father: {fileID: 4000010470004098} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013472049802 MeshFilter: @@ -7745,13 +7960,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012517480522} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 408880} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013108224718 MeshFilter: @@ -7811,9 +8026,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012517480522} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114323545490698212 @@ -7889,6 +8112,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012586849750} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24000001, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -7897,7 +8121,6 @@ Transform: - {fileID: 6005845494309965052} - {fileID: 4000013868811186} m_Father: {fileID: 473678} - m_RootOrder: 39 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23000010319935078 MeshRenderer: @@ -7973,7 +8196,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010586237508} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8181,13 +8403,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012646663936} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 493402} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010800557860 MeshFilter: @@ -8247,9 +8469,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012646663936} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114549399520814884 @@ -8328,13 +8558,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012671655106} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.4113, y: -0.4082, z: -0.05} m_LocalScale: {x: 0.26, y: 0.26, z: 0.55} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 411214} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013219408624 MeshFilter: @@ -8394,9 +8624,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012671655106} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114871902584000098 @@ -8474,13 +8712,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012741200280} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.337, z: -0.025} m_LocalScale: {x: 0.45, y: 0.45, z: 0.45} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012758470188} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010754946242 MeshFilter: @@ -8572,13 +8810,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012752427380} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 406124} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013646666634 MeshFilter: @@ -8638,9 +8876,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012752427380} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114362806798608418 @@ -8717,13 +8963,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012812392858} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013868811186} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012714006372 MeshFilter: @@ -8809,7 +9055,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012758470188} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -9037,13 +9282,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013014429140} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 474082} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011491718626 MeshFilter: @@ -9103,9 +9348,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013014429140} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114195161851537796 @@ -9182,13 +9435,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013171469736} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: 0.0022855483} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000011225904734} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010406950982 MeshFilter: @@ -9266,13 +9519,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013208288780} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.05999999} m_LocalScale: {x: 2, y: 1, z: 0.8783332} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010586237508} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012686066634 MeshFilter: @@ -9290,9 +9543,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013208288780} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -9363,13 +9624,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013281891730} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012925136122} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010839572548 MeshFilter: @@ -9446,6 +9707,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013345196914} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: -1.0697142, z: 0.9165714} m_LocalScale: {x: 4.5714283, y: 1, z: 1} @@ -9454,7 +9716,6 @@ Transform: - {fileID: 4000013753592404} - {fileID: 4000013563491830} m_Father: {fileID: 4000010470004098} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010676958356 MeshFilter: @@ -9529,6 +9790,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013409032268} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.000000014901161, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -9537,7 +9799,6 @@ Transform: - {fileID: 4000012486041186} - {fileID: 4000013191658744} m_Father: {fileID: 4000012758470188} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1000013456164478 GameObject: @@ -9564,13 +9825,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013456164478} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013175241986} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013506293760 MeshFilter: @@ -9649,13 +9910,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013560234246} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 401916} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012247514248 MeshFilter: @@ -9715,9 +9976,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013560234246} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114244469609186990 @@ -9796,13 +10065,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013664674502} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.3, y: -0.25, z: -0.05} m_LocalScale: {x: 0.55, y: 0.55, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473330} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012106556998 MeshFilter: @@ -9862,9 +10131,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013664674502} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.7, y: 1, z: 0.025} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114635763778407954 @@ -9943,6 +10220,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013782811050} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.44, z: -0.054} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -9951,7 +10229,6 @@ Transform: - {fileID: 2996170910482353195} - {fileID: 4000012865260200} m_Father: {fileID: 4000013760615668} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013863582604 MeshFilter: @@ -10011,9 +10288,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013782811050} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114000010491166222 @@ -10119,13 +10404,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013804988808} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000012925136122} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011914095804 MeshFilter: @@ -10202,13 +10487,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013953183086} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000013454894384} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000014113340520 MeshFilter: @@ -10285,13 +10570,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1003691897808460} + serializedVersion: 2 m_LocalRotation: {x: -0, y: 0.7073474, z: 0.7068661, w: 0} m_LocalPosition: {x: 0, y: 0, z: 0.01} m_LocalScale: {x: 95.2381, y: 100, z: 40.000046} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4604395735417884} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: -89.961006, y: 180, z: 0} --- !u!33 &33682943879407966 MeshFilter: @@ -10368,13 +10653,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1023390386316022} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.29999998, y: -0.2999997, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 474082} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33767155316608232 MeshFilter: @@ -10451,13 +10736,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1037653607876102} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.30000007, y: -0.29999998, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000010333377928} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33256049024245930 MeshFilter: @@ -10541,7 +10826,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4405259647245308} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -10749,13 +11033,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1063686457530790} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.30000007, y: -0.3, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 493402} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33405629311793028 MeshFilter: @@ -10832,13 +11116,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1122766985615510} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.30000007, y: -0.2999997, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 461906} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33373790172941974 MeshFilter: @@ -10914,6 +11198,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1147890328312932} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -10922,7 +11207,6 @@ Transform: - {fileID: 224259994149328172} - {fileID: 4299097812957920} m_Father: {fileID: 473678} - m_RootOrder: 36 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23762962104057700 MeshRenderer: @@ -10991,13 +11275,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1157290024452308} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4299097812957920} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33340641278957148 MeshFilter: @@ -11081,7 +11365,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4227232315394708} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -11287,13 +11570,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1194264369200220} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.30000025, y: -0.29999998, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 476782} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33315681194821888 MeshFilter: @@ -11372,6 +11655,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1227842993942742} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -1.65, y: 0, z: 0} m_LocalScale: {x: 1.05, y: 2.5, z: 1} @@ -11382,7 +11666,6 @@ Transform: - {fileID: 4160166080439610} - {fileID: 4088058456074744} m_Father: {fileID: 473678} - m_RootOrder: 41 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &65181151135621986 BoxCollider: @@ -11392,9 +11675,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1227842993942742} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.02} m_Center: {x: 0, y: 0, z: 0} --- !u!33 &33728303746786244 @@ -11552,13 +11843,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1236945777315276} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.29999998, y: -0.3, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 401916} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33461801393607834 MeshFilter: @@ -11635,13 +11926,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1329085538968134} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4572210679967688} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33281479349217826 MeshFilter: @@ -11718,13 +12009,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1336920247228296} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4299097812957920} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33306568108413466 MeshFilter: @@ -11801,13 +12092,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1352538921561852} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4653590090589872} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33291731134228232 MeshFilter: @@ -11885,13 +12176,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1353537369857522} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.30000004, y: -0.30000004, z: -0.04} m_LocalScale: {x: 0.3, y: 0.3, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 411214} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33552357186652048 MeshFilter: @@ -11909,9 +12200,17 @@ MeshCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1353537369857522} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 4 + serializedVersion: 5 m_Convex: 0 m_CookingOptions: 30 m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} @@ -11982,13 +12281,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1357195914620088} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4572210679967688} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33408476018355782 MeshFilter: @@ -12073,7 +12372,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4405002968423422} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -12292,13 +12590,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1585279513007384} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4227232315394708} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33595363077444256 MeshFilter: @@ -12375,13 +12673,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1593948112818424} + serializedVersion: 2 m_LocalRotation: {x: -0.7068661, y: 0, z: 0, w: 0.7073474} m_LocalPosition: {x: 0.00000011353266, y: 0, z: 0.005} m_LocalScale: {x: 95.2381, y: 100.000046, z: 40.000057} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4604395735417884} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: -89.961006, y: 0, z: 0} --- !u!33 &33685971061773632 MeshFilter: @@ -12460,6 +12758,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1631327297680716} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.44, z: -0.054000005} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -12468,7 +12767,6 @@ Transform: - {fileID: 8468573391107282333} - {fileID: 4483479252455118} m_Father: {fileID: 4405259647245308} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33174227262776470 MeshFilter: @@ -12606,9 +12904,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1631327297680716} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1637582873204310 @@ -12636,13 +12942,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1637582873204310} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 95.2381, y: 99.99982, z: 39.999924} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4604395735417884} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33530010535052012 MeshFilter: @@ -12720,13 +13026,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1644973727690454} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.847, y: 1.001, z: -0.05} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 26 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33567874645767864 MeshFilter: @@ -12814,6 +13120,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1672029381864338} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.000000014901161, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -12822,7 +13129,6 @@ Transform: - {fileID: 4776209461862024} - {fileID: 4299019526169640} m_Father: {fileID: 4405002968423422} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1676764474539712 GameObject: @@ -12848,6 +13154,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1676764474539712} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -12857,7 +13164,6 @@ Transform: - {fileID: 4227232315394708} - {fileID: 4572210679967688} m_Father: {fileID: 473678} - m_RootOrder: 37 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &23211508628126822 MeshRenderer: @@ -12933,7 +13239,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4000011114460008} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -13147,13 +13452,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1718400279081948} + serializedVersion: 2 m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4653590090589872} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!33 &33957633259298626 MeshFilter: @@ -13230,13 +13535,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1806376406434594} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.29999998, y: -0.29999998, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 406124} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33436210745481492 MeshFilter: @@ -13311,6 +13616,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1838418458797118} + serializedVersion: 2 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} @@ -13319,7 +13625,6 @@ Transform: - {fileID: 4434180684794314} - {fileID: 4908000352793634} m_Father: {fileID: 4405259647245308} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1839306045669634 GameObject: @@ -13348,6 +13653,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1839306045669634} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.082, y: -1.252, z: -0.01} m_LocalScale: {x: 0.3, y: 0.3, z: 0.3} @@ -13356,7 +13662,6 @@ Transform: - {fileID: 4594768142946388} - {fileID: 4485662548378186} m_Father: {fileID: 473678} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33573308757238834 MeshFilter: @@ -13416,9 +13721,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1839306045669634} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &114786666466332882 @@ -13526,13 +13839,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1842065821143492} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.839, y: 1.013, z: -0.0311} m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 24 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33422040586648110 MeshFilter: @@ -13592,9 +13905,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1842065821143492} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &114501524827504004 @@ -13633,7 +13954,7 @@ MonoBehaviour: m_LocalVariables: [] m_DescriptionActivateSpeed: 12 m_DescriptionZScale: 1 - m_ButtonTexture: {fileID: 2800000, guid: da8fcd82368826b4cad505e8249bd63c, type: 3} + m_ButtonTexture: {fileID: 2800000, guid: ca2b2689a21743448be1a5306c61e90b, type: 3} m_AtlasTexture: 1 m_ToggleButton: 0 m_LongPressReleaseButton: 0 @@ -13700,13 +14021,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1855919676530468} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.29999998, y: -0.3, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 408880} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33999473819885868 MeshFilter: @@ -13783,13 +14104,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1914095689519454} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.29999998, y: -0.29999998, z: -0.04} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473330} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33810070024719510 MeshFilter: @@ -13868,13 +14189,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 894190226255371916} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.875, y: 1.03, z: -0.025} m_LocalScale: {x: 0.175, y: 0.175, z: 0.175} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 31 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &9181308231173954030 MeshFilter: @@ -13934,9 +14255,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 894190226255371916} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &4188583716324234656 @@ -14040,6 +14369,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 915226954648437342} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.000000014901161, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -14048,7 +14378,6 @@ Transform: - {fileID: 7463230845054887281} - {fileID: 3442742383225157293} m_Father: {fileID: 9117108218266893509} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1322492417976703408 GameObject: @@ -14075,13 +14404,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1322492417976703408} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.5, z: -0.0024999883} m_LocalScale: {x: 1, y: 0.028571432, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8668434879085770626} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2500334484848516744 MeshFilter: @@ -14165,7 +14494,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1524240171334712824} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -14373,6 +14701,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2158335996185070942} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: -1.0697142, z: 0.9165714} m_LocalScale: {x: 4.5714283, y: 1, z: 1} @@ -14381,7 +14710,6 @@ Transform: - {fileID: 8835613076674131173} - {fileID: 1681593874138847941} m_Father: {fileID: 7687396309078903398} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1248259070182587467 MeshFilter: @@ -14456,6 +14784,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2316682789993501927} + serializedVersion: 2 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} @@ -14464,7 +14793,6 @@ Transform: - {fileID: 1126899405721930507} - {fileID: 2349666135276550853} m_Father: {fileID: 1524240171334712824} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2564330845180575454 GameObject: @@ -14491,13 +14819,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2564330845180575454} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3702262655533381092} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3786255149657314558 MeshFilter: @@ -14581,7 +14909,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 9117108218266893509} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -14789,6 +15116,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3489518439063806035} + serializedVersion: 2 m_LocalRotation: {x: 0.5000001, y: 0.5, z: -0.5, w: 0.49999994} m_LocalPosition: {x: 0, y: 1.2142847, z: 0.4594286} m_LocalScale: {x: 0.9142857, y: 1, z: 1} @@ -14797,7 +15125,6 @@ Transform: - {fileID: 321097991674213930} - {fileID: 6474380007388265344} m_Father: {fileID: 7687396309078903398} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3710666160644817266 MeshFilter: @@ -14874,13 +15201,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3711792098476514597} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.5, z: -0.0025} m_LocalScale: {x: 1, y: 0.02857143, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8668434879085770626} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6232630873404181854 MeshFilter: @@ -14956,6 +15283,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4374687820491222719} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24000001, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -14964,7 +15292,6 @@ Transform: - {fileID: 5273980064973361335} - {fileID: 6471389067300543686} m_Father: {fileID: 473678} - m_RootOrder: 40 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &3762244675612984118 MeshRenderer: @@ -15040,7 +15367,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3702262655533381092} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -15246,13 +15572,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4778719595247149710} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 550194924522150295} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6533685272076450346 MeshFilter: @@ -15330,13 +15656,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4987445825603729169} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071068} m_LocalPosition: {x: 0.847, y: 1.001, z: -0.05} m_LocalScale: {x: 0.25, y: 0.25, z: 0.25} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 27 m_LocalEulerAnglesHint: {x: 0, y: 0, z: -90} --- !u!33 &2139868953206300193 MeshFilter: @@ -15425,13 +15751,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5708433634109880750} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6471389067300543686} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7528090674439679421 MeshFilter: @@ -15514,7 +15840,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -15679,6 +16004,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6839353153389732464} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.24000001, y: 0, z: -0.1} m_LocalScale: {x: 1, y: 1, z: 1} @@ -15688,7 +16014,6 @@ Transform: - {fileID: 3702262655533381092} - {fileID: 1817565260503090077} m_Father: {fileID: 473678} - m_RootOrder: 38 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2320526944858906914 MeshRenderer: @@ -15759,13 +16084,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6937020573975178444} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.875, y: 1.03, z: -0.025} m_LocalScale: {x: 0.175, y: 0.175, z: 0.175} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 29 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2502232274484467176 MeshFilter: @@ -15825,9 +16150,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6937020573975178444} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &6670325552642882511 @@ -15933,13 +16266,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6967533768856939298} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6471389067300543686} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7509040775308887635 MeshFilter: @@ -16018,13 +16351,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7072846544151351213} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.875, y: 1.03, z: -0.025} m_LocalScale: {x: 0.175, y: 0.175, z: 0.175} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 30 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7617378168068650857 MeshFilter: @@ -16084,9 +16417,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7072846544151351213} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &7544911188538389732 @@ -16199,7 +16540,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 32 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -16407,13 +16747,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7514244858002170337} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1817565260503090077} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2312766600907746419 MeshFilter: @@ -16492,6 +16832,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7675354702135526188} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.44, z: -0.054000005} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -16500,7 +16841,6 @@ Transform: - {fileID: 863212687757741380} - {fileID: 6818748104769445831} m_Father: {fileID: 1524240171334712824} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6394529472826107509 MeshFilter: @@ -16638,9 +16978,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7675354702135526188} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &7809439681076855981 @@ -16675,7 +17023,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4604395735417884} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -16883,6 +17230,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8051552516459751659} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.84, y: -0.4779, z: -0.0301} m_LocalScale: {x: 0.43600002, y: 0.43600002, z: 0.43600002} @@ -16892,7 +17240,6 @@ Transform: - {fileID: 550194924522150295} - {fileID: 8773122714945324681} m_Father: {fileID: 473678} - m_RootOrder: 16 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2565729595090453201 MeshFilter: @@ -16952,9 +17299,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8051552516459751659} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.12, y: 0.73, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &4985083439257374453 @@ -17032,13 +17387,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8156343948566993514} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: 0.00228548} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8773122714945324681} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7850102019527573647 MeshFilter: @@ -17115,13 +17470,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8334431517929914044} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1817565260503090077} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1779665805953307883 MeshFilter: @@ -17198,13 +17553,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8425099743687099145} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.49999997, z: -0.002} m_LocalScale: {x: 1, y: 0.028571434, z: 2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 550194924522150295} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5384255025845176638 MeshFilter: @@ -17283,13 +17638,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8515080880137723669} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.922, y: 0.956, z: -0.075} m_LocalScale: {x: 0.1, y: 0.1, z: 0.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 28 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &830589215893783279 MeshFilter: @@ -17349,9 +17704,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8515080880137723669} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.75, y: 2.75, z: 0.1} m_Center: {x: -0.75, y: 0.5, z: 0} --- !u!114 &5055648481581678162 @@ -17457,6 +17820,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8643510522094066947} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071067} m_LocalPosition: {x: 0, y: 0.35714287, z: 0.002285719} m_LocalScale: {x: 1.7142856, y: 1, z: 1} @@ -17465,7 +17829,6 @@ Transform: - {fileID: 8235492235284656829} - {fileID: 5169543984843292650} m_Father: {fileID: 7687396309078903398} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3909138286404281281 MeshFilter: @@ -17549,7 +17912,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 473678} - m_RootOrder: 33 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -17759,13 +18121,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8955674742366199644} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.49999997, z: 0.0022855483} m_LocalScale: {x: 1, y: 0.028571434, z: 0.9142854} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8773122714945324681} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &870282709681677666 MeshFilter: From fe41fd4f91f24a63bde4690d8df337fb13d5289b Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Fri, 12 Jul 2024 15:50:24 +0100 Subject: [PATCH 065/105] Fixes and changes for Icosa API integration --- Assets/Scripts/GUI/AdminPanel.cs | 4 +- Assets/Scripts/GUI/SketchbookPanel.cs | 28 ++++++++------ Assets/Scripts/GUI/SketchesPanel.cs | 4 +- Assets/Scripts/Model.cs | 49 ++++++++++++++++++++---- Assets/Scripts/Poly/IcosaAssetCatalog.cs | 37 +++++++++++------- Assets/Scripts/Sharing/AssetGetter.cs | 17 ++------ Assets/Scripts/Sharing/AssetLister.cs | 44 ++++++++------------- Assets/Scripts/Sharing/IcosaSketchSet.cs | 38 +++++++++--------- Assets/Scripts/Sharing/VrAssetService.cs | 2 +- Assets/Scripts/SketchControlsScript.cs | 12 +++--- 10 files changed, 133 insertions(+), 102 deletions(-) diff --git a/Assets/Scripts/GUI/AdminPanel.cs b/Assets/Scripts/GUI/AdminPanel.cs index d46b99c9c0..be6b251156 100644 --- a/Assets/Scripts/GUI/AdminPanel.cs +++ b/Assets/Scripts/GUI/AdminPanel.cs @@ -59,7 +59,9 @@ public class AdminPanel : BasePanel void UpdateShareButtonText() { // Skip redundant updates - bool currentLoggedIn = App.GoogleIdentity.LoggedIn || App.SketchfabIdentity.LoggedIn; + bool currentLoggedIn = App.GoogleIdentity.LoggedIn + || App.SketchfabIdentity.LoggedIn + || App.IcosaIsLoggedIn; if (currentLoggedIn == m_UpdateShareButtonState) { return; } m_UpdateShareButtonState = currentLoggedIn; diff --git a/Assets/Scripts/GUI/SketchbookPanel.cs b/Assets/Scripts/GUI/SketchbookPanel.cs index 6e6cde6afc..31bd0b2829 100644 --- a/Assets/Scripts/GUI/SketchbookPanel.cs +++ b/Assets/Scripts/GUI/SketchbookPanel.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Linq; using TMPro; +using UnityEngine.Serialization; namespace TiltBrush { @@ -49,7 +50,7 @@ public class SketchbookPanel : ModalPanel [SerializeField] private GameObject m_NoShowcaseMessage; [SerializeField] private GameObject m_ContactingServerMessage; [SerializeField] private GameObject m_OutOfDateMessage; - [SerializeField] private GameObject m_NoPolyConnectionMessage; + [FormerlySerializedAs("m_NoPolyConnectionMessage")][SerializeField] private GameObject m_NoIcosaConnectionMessage; [SerializeField] private Renderer m_OnlineGalleryButtonRenderer; [SerializeField] private GameObject[] m_IconsOnFirstPage; [SerializeField] private GameObject[] m_IconsOnNormalPage; @@ -299,15 +300,18 @@ protected override void RefreshPage() // Base Refresh updates the modal parts of the panel, and we always want those refreshed. base.RefreshPage(); - bool requiresPoly = m_CurrentSketchSet == SketchSetType.Liked; + bool requiresIcosa = m_CurrentSketchSet == SketchSetType.Liked; + bool requiresGoogle = m_CurrentSketchSet == SketchSetType.Drive; - bool polyDown = VrAssetService.m_Instance.NoConnection && requiresPoly; - m_NoPolyConnectionMessage.SetActive(polyDown); + bool icosaDown = VrAssetService.m_Instance.NoConnection && requiresIcosa; + m_NoIcosaConnectionMessage.SetActive(icosaDown); - bool outOfDate = !polyDown && !VrAssetService.m_Instance.Available && requiresPoly; + m_NoIcosaConnectionMessage.SetActive(icosaDown); + + bool outOfDate = !icosaDown && !VrAssetService.m_Instance.Available && requiresIcosa; m_OutOfDateMessage.SetActive(outOfDate); - if (outOfDate || polyDown) + if (outOfDate || icosaDown) { m_NoSketchesMessage.SetActive(false); m_NoDriveSketchesMessage.SetActive(false); @@ -340,12 +344,14 @@ protected override void RefreshPage() !m_SketchSet.IsActivelyRefreshingSketches && App.IcosaIsLoggedIn); - // Show Contacting Server if we're talking to Drive. + // Show Contacting Server if we're talking to Drive or Icosa m_ContactingServerMessage.SetActive( - (requiresPoly || - m_CurrentSketchSet == SketchSetType.Drive) && - (m_SketchSet.NumSketches <= 0) && - (m_SketchSet.IsActivelyRefreshingSketches && App.GoogleIdentity.LoggedIn)); + m_SketchSet.NumSketches <= 0 + && m_SketchSet.IsActivelyRefreshingSketches + && ( + (requiresIcosa && App.IcosaIsLoggedIn) || + (requiresGoogle && App.GoogleIdentity.LoggedIn) + )); // Show Showcase error if we're in Showcase and don't have sketches. m_NoShowcaseMessage.SetActive( diff --git a/Assets/Scripts/GUI/SketchesPanel.cs b/Assets/Scripts/GUI/SketchesPanel.cs index 7598c8e4bf..cce072b1cf 100644 --- a/Assets/Scripts/GUI/SketchesPanel.cs +++ b/Assets/Scripts/GUI/SketchesPanel.cs @@ -37,7 +37,7 @@ public override void InitPanel() base.InitPanel(); m_LastUploadProgress = -1.0f; - m_LoggedIn = App.GoogleIdentity.LoggedIn; + m_LoggedIn = App.GoogleIdentity.LoggedIn || App.IcosaIsLoggedIn; RefreshLoginButtonText(m_LoggedIn); } @@ -52,7 +52,7 @@ void Update() m_SaveOptionsButton.SetActive(alreadySaved); // Update share button's text. - bool loggedIn = App.GoogleIdentity.LoggedIn; + bool loggedIn = App.GoogleIdentity.LoggedIn || App.IcosaIsLoggedIn; if (loggedIn != m_LoggedIn) { RefreshLoginButtonText(loggedIn); diff --git a/Assets/Scripts/Model.cs b/Assets/Scripts/Model.cs index 891e2f4353..38a7d8b19a 100644 --- a/Assets/Scripts/Model.cs +++ b/Assets/Scripts/Model.cs @@ -430,6 +430,10 @@ protected override IDisposable DoBackgroundThreadWork() var loader = new TiltBrushUriLoader( m_localPath, Path.GetDirectoryName(m_localPath), m_useThreadedImageLoad); var options = m_fromIcosa ? kPolyGltfImportOptions : kGltfImportOptions; + if (m_fromIcosa) + { + return ImportGltf.BeginImport(m_localPath, loader, options); + } return NewGltfImporter.BeginImport(m_localPath); } @@ -438,23 +442,52 @@ protected override GameObject DoUnityThreadWork(IDisposable state__, out ImportMaterialCollector importMaterialCollector) { - meshEnumerable = null; - importMaterialCollector = null; GameObject rootObject = null; - using (IDisposable state_ = state__) + if (m_fromIcosa) { - var state = state_ as NewGltfImporter.ImportState; + var state = state__ as ImportGltf.ImportState; if (state != null) { string assetLocation = Path.GetDirectoryName(m_localPath); // EndImport doesn't try to use the loadImages functionality of UriLoader anyway. // It knows it's on the main thread, so chooses to use Unity's fast loading. - rootObject = state.root; - importMaterialCollector = new ImportMaterialCollector(assetLocation, uniqueSeed: m_localPath); + var loader = new TiltBrushUriLoader(m_localPath, assetLocation, loadImages: false); + ImportGltf.GltfImportResult result = + ImportGltf.EndImport( + state, loader, + new ImportMaterialCollector(assetLocation, uniqueSeed: m_localPath), + out meshEnumerable); + + if (result != null) + { + rootObject = result.root; + importMaterialCollector = (ImportMaterialCollector)result.materialCollector; + } + } + IsValid = rootObject != null; + meshEnumerable = null; + importMaterialCollector = null; + return rootObject; + } + else + { + meshEnumerable = null; + importMaterialCollector = null; + using (IDisposable state_ = state__) + { + var state = state_ as NewGltfImporter.ImportState; + if (state != null) + { + string assetLocation = Path.GetDirectoryName(m_localPath); + // EndImport doesn't try to use the loadImages functionality of UriLoader anyway. + // It knows it's on the main thread, so chooses to use Unity's fast loading. + rootObject = state.root; + importMaterialCollector = new ImportMaterialCollector(assetLocation, uniqueSeed: m_localPath); + } } + IsValid = rootObject != null; + return rootObject; } - IsValid = rootObject != null; - return rootObject; } } // GltfModelBuilder diff --git a/Assets/Scripts/Poly/IcosaAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs index 5f77d5e723..89ec0deb36 100644 --- a/Assets/Scripts/Poly/IcosaAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -13,7 +13,6 @@ // limitations under the License. using UnityEngine; -using UnityEngine.Networking; using System; using System.Linq; using System.Collections.Generic; @@ -84,16 +83,31 @@ public AssetDetails( JToken json, string accountName, string thumbnailSuffix) { m_Owner = App.IcosaAssetCatalog; - HumanName = json["Title"].ToString(); - AssetId = json["ID"].ToString(); + HumanName = json["displayName"].ToString(); + // AssetId = json["name"].ToString().Substring(7); // strip out "assets/" + // AssetId = json["ID"].ToString(); + AssetId = json["url"].ToString(); AccountName = accountName; - ModelRotation = Quaternion.identity; + var rotation = json["presentationParams"]?["orientingRotation"]; + if (rotation != null) + { + ModelRotation = new Quaternion( + rotation["x"]?.Value() ?? 0, + rotation["y"]?.Value() ?? 0, + rotation["z"]?.Value() ?? 0, + rotation["w"]?.Value() ?? 0 + ); + } + else + { + ModelRotation = null; + } + m_Thumbnail = new Texture2D(4, 4, TextureFormat.ARGB32, false); - // Thumbnail URL can be a .webp, but if so we rely on there also being a jpg version - m_ThumbnailUrl = json["Thumbnail"].ToString().Replace(".webp", ".jpg"); + m_ThumbnailUrl = json["thumbnail"]["url"].ToString(); if (!string.IsNullOrEmpty(thumbnailSuffix)) { - m_ThumbnailUrl = string.Format("{0}{1}", m_ThumbnailUrl, thumbnailSuffix); + m_ThumbnailUrl = string.Format("{0}={1}", m_ThumbnailUrl, thumbnailSuffix); } if (!kLazyLoadThumbnail) { @@ -150,10 +164,7 @@ async void DownloadThumbnailAsync(string thumbnailUrl) if (thumbnailBytes == null) { await m_Owner.m_thumbnailFetchLimiter.WaitAsync(); - WebRequest www = new WebRequest(thumbnailUrl, - App.GoogleIdentity, - UnityWebRequest.kHttpVerbGET); - + WebRequest www = new WebRequest(thumbnailUrl); await www.SendAsync(); while (m_Owner.m_thumbnailReadLimiter.IsBlocked()) @@ -276,7 +287,7 @@ public bool IsLoading(string assetId) public void RequestRefresh(IcosaSetType type) { // We don't update featured except on startup. - if (type != IcosaSetType.Featured && App.GoogleIdentity.LoggedIn) + if (type != IcosaSetType.Featured && (App.IcosaIsLoggedIn || App.GoogleIdentity.LoggedIn)) { m_AssetSetByType[type].m_RefreshRequested = true; } @@ -896,7 +907,7 @@ private IEnumerator RefreshAssetSet(IcosaSetType type) void RefreshFetchCoroutines() { - if (App.GoogleIdentity.Profile != null) + if (App.IcosaIsLoggedIn) { m_AssetSetByType[IcosaSetType.User].m_RefreshRequested = true; m_AssetSetByType[IcosaSetType.Liked].m_RefreshRequested = true; diff --git a/Assets/Scripts/Sharing/AssetGetter.cs b/Assets/Scripts/Sharing/AssetGetter.cs index 4cc6fbd4ee..fac4907054 100644 --- a/Assets/Scripts/Sharing/AssetGetter.cs +++ b/Assets/Scripts/Sharing/AssetGetter.cs @@ -90,12 +90,10 @@ public AssetGetter(string uri, string assetId, VrAssetFormat assetType, Reason = reason; } - // Initiates the contact with Poly. + // Initiates the contact with Icosa public IEnumerator GetAssetCoroutine() { - OAuth2Identity identity = null; - if (!m_URI.StartsWith(VrAssetService.m_Instance.ApiHost)) { m_Asset.SetRootElement(UnityWebRequest.EscapeURL(m_URI), m_URI); @@ -104,7 +102,7 @@ public IEnumerator GetAssetCoroutine() { m_Ready = false; - WebRequest initialRequest = new WebRequest(m_URI, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + WebRequest initialRequest = new WebRequest(m_URI); using (var cr = initialRequest.SendAsync().AsIeNull()) { while (!initialRequest.Done) @@ -131,13 +129,6 @@ public IEnumerator GetAssetCoroutine() JObject json; while (!f.TryGetResult(out json)) { yield return null; } - var polyPizzaUrl = json["Download"]; - if (polyPizzaUrl == null) - { - Debug.LogErrorFormat("Failed to find download url for {0}", m_URI); - yield break; - } - if (json.Count == 0) { Debug.LogErrorFormat("Failed to deserialize response for {0}", m_URI); @@ -198,7 +189,7 @@ public IEnumerator GetAssetCoroutine() } // Download root asset. - var request = new WebRequest(m_Asset.RootDataURL, identity); + var request = new WebRequest(m_Asset.RootDataURL); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) @@ -221,7 +212,7 @@ public IEnumerator GetAssetCoroutine() // Download all resource assets. foreach (var e in m_Asset.ResourceElements) { - request = new WebRequest(e.dataURL, App.Instance.IcosaToken); + request = new WebRequest(e.dataURL); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index a891a1b79f..9729a2ed1e 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -14,7 +14,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Newtonsoft.Json.Linq; using UnityEngine.Networking; @@ -26,23 +25,20 @@ public class AssetLister { private string m_Uri; private string m_ErrorMessage; - private int m_PageIndex = -1; + private string m_PageToken; - public bool HasMore { get { return m_PageIndex != -1; } } + public bool HasMore { get { return m_PageToken != null; } } public AssetLister(string uri, string errorMessage) { m_Uri = uri; m_ErrorMessage = errorMessage; - m_PageIndex = 0; } public IEnumerator NextPage(List files) { - if (m_PageIndex == -1) { yield break; } - m_PageIndex++; - string uri = m_PageIndex == 0 ? m_Uri - : String.Format("{0}&page={1}", m_Uri, m_PageIndex); + string uri = m_PageToken == null ? m_Uri + : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) @@ -62,10 +58,11 @@ public IEnumerator NextPage(List files) } } - Future f = new Future(() => JArray.Parse(request.Result)); - JArray assets; - while (!f.TryGetResult(out assets)) { yield return null; } + Future f = new Future(() => JObject.Parse(request.Result)); + JObject json; + while (!f.TryGetResult(out json)) { yield return null; } + var assets = json["assets"]; if (assets != null) { foreach (var asset in assets) @@ -75,19 +72,17 @@ public IEnumerator NextPage(List files) files.Add(info); } } - - // Set page token to -1 when we hit the last page - m_PageIndex = assets.Count > 0 ? m_PageIndex : -1; + JToken jPageToken = json["nextPageToken"]; + m_PageToken = jPageToken != null ? jPageToken.ToString() : null; } public IEnumerator NextPage(List files, string thumbnailSuffix) { - if (m_PageIndex == -1) { yield break; } - string uri = m_PageIndex == 0 ? m_Uri - : String.Format("{0}&page_token={1}", m_Uri, m_PageIndex); + string uri = m_PageToken == null ? m_Uri + : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); - WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); + WebRequest request = new WebRequest(uri, App.Instance.IcosaToken); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) @@ -111,13 +106,11 @@ public IEnumerator NextPage(List files, if (json.Count == 0) { yield break; } JToken lastAsset = null; - var assets = json["assets"] ?? json["userAssets"]; - foreach (JToken possibleAsset in assets) + var assets = json["assets"]; + foreach (JObject asset in assets) { try { - // User assets are nested in an 'asset' node. - JToken asset = possibleAsset["asset"] ?? possibleAsset; if (asset["visibility"].ToString() == "PRIVATE") { continue; @@ -125,10 +118,6 @@ public IEnumerator NextPage(List files, // We now don't filter the liked Poly objects, but we don't want to return liked Tilt Brush // sketches so in this section we filter out anything with a Tilt file in it. - // Also, although currently all Poly objects have a GLTF representation we should probably - // not rely on that continuing, so we discard anything that doesn't have a GLTF (1) - // representation. We look for PGLTF and GLTF as for a lot of objects Poly is returning - // PGLTF without GLTF. bool skipObject = false; foreach (var format in asset["formats"]) { @@ -155,7 +144,8 @@ public IEnumerator NextPage(List files, yield return null; } - m_PageIndex++; + JToken jPageToken = json["nextPageToken"]; + m_PageToken = jPageToken != null ? jPageToken.ToString() : null; } } } // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/IcosaSketchSet.cs b/Assets/Scripts/Sharing/IcosaSketchSet.cs index c9b6da1a66..f01b3186c6 100644 --- a/Assets/Scripts/Sharing/IcosaSketchSet.cs +++ b/Assets/Scripts/Sharing/IcosaSketchSet.cs @@ -391,7 +391,7 @@ private IEnumerator PopulateSketchesCoroutine() bool changed = false; int pagesFetched = 0; - while (lister == null || lister.HasMore) + while (lister == null || lister.HasMore || assetIds.Count == 0) { if (sketches.Count >= 180) { @@ -768,25 +768,23 @@ public class IcosaSceneFileInfo : SceneFileInfo // See go/vr-assets-service-api public IcosaSceneFileInfo(JToken json) { - m_AssetId = json["id"].ToString(); - m_HumanName = json["name"]?.ToString() ?? "Untitled"; - - var format = json["formats"]?.FirstOrDefault(x => x["format"].ToString() == "TILT")?["url"]; - m_TiltFileUrl = format?.ToString(); - m_IconUrl = json["thumbnail"]?.ToString(); - m_License = json["visibility"]?.ToString() ?? "PRIVATE"; - Author = json["ownername"]?.ToString() ?? "Unknown Author"; - - // TODO - m_GltfTriangleCount = 1; - // // Some assets (old ones? broken ones?) are missing the "formatComplexity" field - // var gltfFormat = json["formats"].First(x => x["format"].ToString() == "GLTF"); - // string gltfTriCount = gltfFormat?["formatComplexity"]?["triangleCount"]?.ToString(); - // if (gltfTriCount == null) - // { - // Debug.Log($"{m_AssetId} has no tricount"); - // } - // m_GltfTriangleCount = Int32.Parse(gltfTriCount ?? "1"); + m_AssetId = json["name"].ToString().Substring(7); // strip 'assets/' from start + m_HumanName = json["displayName"].ToString(); + + var format = json["formats"].First(x => x["formatType"].ToString() == "TILT")["root"]; + m_TiltFileUrl = format["url"].ToString(); + m_IconUrl = json["thumbnail"]?["url"]?.ToString(); + m_License = json["license"]?.ToString(); + + + // Some assets (old ones? broken ones?) are missing the "formatComplexity" field + var gltfFormat = json["formats"].First(x => x["formatType"].ToString() == "GLTF"); + string gltfTriCount = gltfFormat?["formatComplexity"]?["triangleCount"]?.ToString(); + if (gltfTriCount == null) + { + Debug.Log($"{m_AssetId} has no tricount"); + } + m_GltfTriangleCount = Int32.Parse(gltfTriCount ?? "1"); m_DownloadedFile = null; m_IconDownloaded = false; diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index c88412087f..021af82863 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -304,7 +304,7 @@ static VrAssetService() [SerializeField] private int m_AssetsPerPage; [SerializeField] public float m_SketchbookRefreshInterval; - public bool m_UseLocalFeaturedSketches = true; + public bool m_UseLocalFeaturedSketches = false; private float m_UploadProgress; private bool m_LastUploadFailed; diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 4f9ffabb6f..d5902f6869 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -27,10 +27,10 @@ namespace TiltBrush public class SketchControlsScript : MonoBehaviour { + // TODO L10n public const string kRemoveHeadsetFyi = "Remove headset to view."; - const string kTiltBrushGalleryUrl = "https://icosa.gallery"; - const string kBlocksGalleryUrl = "https://poly.google.com/blocks"; - const string kPolyMainPageUri = "https://poly.google.com"; + private string m_OpenBrushGalleryUrl = $"{App.ICOSA_WEBSITE_URL}/openbrush"; + private string m_BlocksGalleryUrl = $"{App.ICOSA_WEBSITE_URL}/blocks"; static public SketchControlsScript m_Instance; static bool sm_enableGrabHaptics = true; @@ -4576,7 +4576,7 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, break; } case GlobalCommands.ViewOnlineGallery: - OpenURLAndInformUser(kTiltBrushGalleryUrl); + OpenURLAndInformUser(m_OpenBrushGalleryUrl); break; case GlobalCommands.CancelUpload: VrAssetService.m_Instance.CancelUpload(); @@ -4664,10 +4664,10 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, SaveModel(); break; case GlobalCommands.ViewPolyPage: - OpenURLAndInformUser(kPolyMainPageUri); + OpenURLAndInformUser(App.ICOSA_WEBSITE_URL); break; case GlobalCommands.ViewPolyGallery: - OpenURLAndInformUser(kBlocksGalleryUrl); + OpenURLAndInformUser(m_BlocksGalleryUrl); break; case GlobalCommands.ExportListed: StartCoroutine(ExportListAndQuit()); From 7c8911c1e0e6ba91269fc013382ebc60606c85de Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Fri, 12 Jul 2024 15:51:33 +0100 Subject: [PATCH 066/105] Handle weird situations where we init API too early. --- Assets/Scripts/API/ApiManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/Scripts/API/ApiManager.cs b/Assets/Scripts/API/ApiManager.cs index b65686450f..228cd26b22 100644 --- a/Assets/Scripts/API/ApiManager.cs +++ b/Assets/Scripts/API/ApiManager.cs @@ -561,6 +561,11 @@ private string ScriptTemplateSubstitution(string html) public static string ApiFriendlyBrushName(BrushDescriptor brush) { + if (brush.Description == null) + { + Debug.LogWarning($"Brush {brush.m_DurableName} has no description"); + return ""; + } return brush.Description .Replace(" ", "") .Replace(".", "") From 20a49524b46eb9ca9f0132bcafc94313b88c78f1 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 13 Jul 2024 15:05:55 +0100 Subject: [PATCH 067/105] Icosa model loading is working finally --- Assets/Scripts/Poly/IcosaAssetCatalog.cs | 41 ++++++++++++++---------- Assets/Scripts/Sharing/AssetLister.cs | 13 +++----- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Assets/Scripts/Poly/IcosaAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs index 89ec0deb36..bbab647698 100644 --- a/Assets/Scripts/Poly/IcosaAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -826,22 +826,27 @@ void LoadModelsInQueueAsync() for (int i = m_LoadQueue.Count - 1; i >= 0; --i) { Model model = m_LoadQueue[i].Model; - if (!model.IsLoading()) - { - // If the overlay is up, hitching is okay; so avoid the slow threaded image load. - bool useThreadedImageLoad = - OverlayManager.m_Instance.CurrentOverlayState == OverlayState.Hidden; - model.LoadModelAsync(useThreadedImageLoad); - } - else - { - if (model.TryLoadModel()) - { - m_LoadQueue.RemoveAt(i); - m_IsLoadingMemo = null; - m_NotifyListeners = true; - } - } + model.LoadModel(); + m_LoadQueue.RemoveAt(i); + m_IsLoadingMemo = null; + m_NotifyListeners = true; + // if (!model.IsLoading()) + // { + // // If the overlay is up, hitching is okay; so avoid the slow threaded image load. + // bool useThreadedImageLoad = + // OverlayManager.m_Instance.CurrentOverlayState == OverlayState.Hidden; + // // model.LoadModelAsync(useThreadedImageLoad); + // model.LoadModel(); + // } + // else + // { + // if (model.TryLoadModel(true)) + // { + // m_LoadQueue.RemoveAt(i); + // m_IsLoadingMemo = null; + // m_NotifyListeners = true; + // } + // } } UnityEngine.Profiling.Profiler.EndSample(); } @@ -863,8 +868,10 @@ private IEnumerator RefreshAssetSet(IcosaSetType type) m_AssetSetByType[type].m_Models = models; } AssetLister lister = VrAssetService.m_Instance.ListAssets(type); - while (lister.HasMore || models.Count == 0) + bool firstPass = true; + while (lister.HasMore || firstPass) { + firstPass = false; using (var cr = lister.NextPage(models, m_ThumbnailSuffix)) { while (true) diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index 9729a2ed1e..3e93d202f5 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json.Linq; +using UnityEngine; using UnityEngine.Networking; namespace TiltBrush @@ -37,8 +38,7 @@ public AssetLister(string uri, string errorMessage) public IEnumerator NextPage(List files) { - string uri = m_PageToken == null ? m_Uri - : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); + string uri = m_PageToken == null ? m_Uri : $"{m_Uri}&pageToken={m_PageToken}"; WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) @@ -72,15 +72,13 @@ public IEnumerator NextPage(List files) files.Add(info); } } - JToken jPageToken = json["nextPageToken"]; - m_PageToken = jPageToken != null ? jPageToken.ToString() : null; + m_PageToken = json["nextPageToken"]?.ToString(); } public IEnumerator NextPage(List files, string thumbnailSuffix) { - string uri = m_PageToken == null ? m_Uri - : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); + string uri = m_PageToken == null ? m_Uri : $"{m_Uri}&pageToken={m_PageToken}"; WebRequest request = new WebRequest(uri, App.Instance.IcosaToken); using (var cr = request.SendAsync().AsIeNull()) @@ -144,8 +142,7 @@ public IEnumerator NextPage(List files, yield return null; } - JToken jPageToken = json["nextPageToken"]; - m_PageToken = jPageToken != null ? jPageToken.ToString() : null; + m_PageToken = json["nextPageToken"]?.ToString(); } } } // namespace TiltBrush From 3bcfd2804df7ba2ca1890792c431726b45d00f2a Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 11:27:56 +0100 Subject: [PATCH 068/105] Fix login base urls and make Icosa urls overrideable --- Assets/Editor/DummyCommandRefs.cs | 8 +-- .../Scripts/API/ApiMethods.GlobalCommands.cs | 7 ++- Assets/Scripts/App.cs | 4 -- Assets/Scripts/GUI/ProfilePopUpWindow.cs | 9 +-- Assets/Scripts/Sharing/AssetGetter.cs | 2 +- Assets/Scripts/Sharing/IcosaService.cs | 2 +- Assets/Scripts/Sharing/VrAssetService.cs | 58 +++++++++---------- Assets/Scripts/SketchControlsScript.cs | 15 ++--- Assets/Scripts/UserConfig.cs | 4 +- 9 files changed, 53 insertions(+), 56 deletions(-) diff --git a/Assets/Editor/DummyCommandRefs.cs b/Assets/Editor/DummyCommandRefs.cs index a0aee72faa..4a0bf27bac 100644 --- a/Assets/Editor/DummyCommandRefs.cs +++ b/Assets/Editor/DummyCommandRefs.cs @@ -179,15 +179,15 @@ public static void Prefabs_Panels_MemoryWarningPanel() public static void Prefabs_Panels_PolyPanel() { Use(GlobalCommands.LoginToGenericCloud); - Use(GlobalCommands.ViewPolyGallery); - Use(GlobalCommands.ViewPolyPage); + Use(GlobalCommands.ViewBlocksGallery); + Use(GlobalCommands.ViewIcosaHomePage); } public static void Prefabs_Panels_PolyPanel_Mobile() { Use(GlobalCommands.LoginToGenericCloud); - Use(GlobalCommands.ViewPolyGallery); - Use(GlobalCommands.ViewPolyPage); + Use(GlobalCommands.ViewBlocksGallery); + Use(GlobalCommands.ViewIcosaHomePage); } public static void Prefabs_Panels_ReferencePanel_ReferencePanel_Mobile() diff --git a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs index 707eff8d52..91bf488778 100644 --- a/Assets/Scripts/API/ApiMethods.GlobalCommands.cs +++ b/Assets/Scripts/API/ApiMethods.GlobalCommands.cs @@ -15,7 +15,6 @@ using System.IO; using Org.OpenAPITools.Api; using Org.OpenAPITools.Client; -using UnityEngine; namespace TiltBrush { @@ -56,15 +55,17 @@ public static void SaveAs(string filename) public static void IcosaLogin(string username, string password) { var config = new Configuration(); - var loginApi = new LoginApi(App.ICOSA_API_URL); + var loginApi = new LoginApi(VrAssetService.m_Instance.IcosaApiRoot); + config.BasePath = VrAssetService.m_Instance.IcosaApiRoot; loginApi.Configuration = config; var token = loginApi.LoginLoginPost(username, password); App.Instance.IcosaToken = token.AccessToken; if (token != null) { - var usersApi = new UsersApi(App.ICOSA_API_URL); + var usersApi = new UsersApi(VrAssetService.m_Instance.IcosaApiRoot); config = new Configuration { AccessToken = App.Instance.IcosaToken }; + config.BasePath = VrAssetService.m_Instance.IcosaApiRoot; usersApi.Configuration = config; var userData = usersApi.GetUsersMeUsersMeGet(); diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index c9b8e0e39d..d425bdd779 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -131,10 +131,6 @@ public enum AppState public static OAuth2Identity GoogleIdentity => m_Instance.m_GoogleIdentity; public static OAuth2Identity SketchfabIdentity => m_Instance.m_SketchfabIdentity; - // TODO Make these overridable - public static string ICOSA_WEBSITE_URL = "https://icosa-api-django.ixxy.co.uk"; - public static string ICOSA_API_URL = "https://icosa-api-django.ixxy.co.uk/api/v1"; - public string IcosaToken { get => PlayerPrefs.HasKey("IcosaToken") ? PlayerPrefs.GetString("IcosaToken") : null; diff --git a/Assets/Scripts/GUI/ProfilePopUpWindow.cs b/Assets/Scripts/GUI/ProfilePopUpWindow.cs index c10a7f22e8..d2684a7679 100644 --- a/Assets/Scripts/GUI/ProfilePopUpWindow.cs +++ b/Assets/Scripts/GUI/ProfilePopUpWindow.cs @@ -14,7 +14,6 @@ using System; using System.Collections; -using System.Numerics; using Org.OpenAPITools.Api; using Org.OpenAPITools.Client; using Org.OpenAPITools.Model; @@ -218,7 +217,8 @@ public void HandleIcosaLoginSubmit(string code) private IEnumerator LoginCoroutine(string code) { var config = new Configuration(); - var loginApi = new LoginApi(App.ICOSA_API_URL); + var loginApi = new LoginApi(VrAssetService.m_Instance.IcosaApiRoot); + config.BasePath = VrAssetService.m_Instance.IcosaApiRoot; loginApi.Configuration = config; var loginTask = loginApi.DeviceLoginLoginDeviceLoginPostAsync(code); yield return new WaitUntil(() => loginTask.IsCompleted); @@ -247,8 +247,9 @@ private IEnumerator LoginCoroutine(string code) private IEnumerator FetchUserDataCoroutine(Action onSuccess) { - var usersApi = new UsersApi(App.ICOSA_API_URL); + var usersApi = new UsersApi(VrAssetService.m_Instance.IcosaApiRoot); var config = new Configuration { AccessToken = App.Instance.IcosaToken }; + config.BasePath = VrAssetService.m_Instance.IcosaApiRoot; usersApi.Configuration = config; var getUserTask = usersApi.GetUsersMeUsersMeGetAsync(); yield return new WaitUntil(() => getUserTask.IsCompleted); @@ -372,7 +373,7 @@ public void OnProfilePopUpButtonPressed(ProfilePopUpButton button) ); } - App.OpenURL($"{App.ICOSA_WEBSITE_URL}/device"); + App.OpenURL($"{VrAssetService.m_Instance.IcosaHomePage}/device"); ShowIcosaLogin(); m_Persistent = true; break; diff --git a/Assets/Scripts/Sharing/AssetGetter.cs b/Assets/Scripts/Sharing/AssetGetter.cs index fac4907054..0baa2827c4 100644 --- a/Assets/Scripts/Sharing/AssetGetter.cs +++ b/Assets/Scripts/Sharing/AssetGetter.cs @@ -94,7 +94,7 @@ public AssetGetter(string uri, string assetId, VrAssetFormat assetType, public IEnumerator GetAssetCoroutine() { - if (!m_URI.StartsWith(VrAssetService.m_Instance.ApiHost)) + if (!m_URI.StartsWith(VrAssetService.m_Instance.IcosaApiRoot)) { m_Asset.SetRootElement(UnityWebRequest.EscapeURL(m_URI), m_URI); } diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index 104236e3cf..cccde3eb55 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -218,7 +218,7 @@ public async Task CreateModel( // No compression because it's a compressed .zip already WebRequest uploader = new WebRequest( - $"{App.ICOSA_API_URL}/assets", m_accessToken, "POST", compress: false); + $"{VrAssetService.m_Instance.IcosaApiRoot}/assets", m_accessToken, "POST", compress: false); var moreParams = new List<(string, string)>(); diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 021af82863..cff654fb57 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -20,10 +20,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Web; using Newtonsoft.Json.Linq; using Org.OpenAPITools.Api; -using Org.OpenAPITools.Client; using UnityEngine; using UnityEngine.Networking; @@ -57,8 +55,6 @@ public class VrAssetService : MonoBehaviour const string kDefaultName = "sketch"; - private string kAssetLandingPage => $"{App.ICOSA_WEBSITE_URL}/uploads"; - private const string kListAssetsUri = "/assets"; private const string kUserAssetsUri = "/users/me/assets"; private const string kUserLikesUri = "/users/me/likedassets"; @@ -231,16 +227,28 @@ public void Dispose() public static VrAssetService m_Instance; // Currently this always returns the standard API host when running unit tests - public string ApiHost + public string IcosaApiRoot + { + get + { + string cfg = App.UserConfig?.Sharing.IcosaApiRoot; + if (!string.IsNullOrEmpty(cfg)) { return cfg; } + return "https://icosa.gallery/api/v1"; + } + } + + public string IcosaHomePage { get { - string cfg = App.UserConfig?.Sharing.VrAssetServiceHostOverride; + string cfg = App.UserConfig.Sharing.IcosaHomePage; if (!string.IsNullOrEmpty(cfg)) { return cfg; } - return App.ICOSA_API_URL; + return "https://icosa.gallery"; } } + private string IcosaUploadPage => $"{IcosaHomePage}/uploads"; + /// Returns true if Icosa would accept a PATCH of the specified asset /// from the specified user. /// @@ -354,16 +362,6 @@ public string LastUploadCompleteUrl get { return m_LastUploadCompleteUrl; } } - private string AssetLandingPage - { - get - { - string cfg = App.UserConfig.Sharing.VrAssetServiceUrlOverride; - if (!string.IsNullOrEmpty(cfg)) { return cfg; } - return kAssetLandingPage; - } - } - // Cannot be an UploadProgress setter because the getter's type is different. // pct is how much of that step has been completed. private void SetUploadProgress(UploadStep step, double pct) @@ -380,11 +378,11 @@ void Awake() void Start() { - if (!string.IsNullOrEmpty(App.UserConfig.Sharing.VrAssetServiceHostOverride) || - !string.IsNullOrEmpty(App.UserConfig.Sharing.VrAssetServiceUrlOverride)) + if (!string.IsNullOrEmpty(App.UserConfig.Sharing.IcosaApiRoot) || + !string.IsNullOrEmpty(App.UserConfig.Sharing.IcosaHomePage)) { Debug.LogFormat("Overriding VrAssetService Api Host: {0} Landing Page: {1}", - ApiHost, AssetLandingPage); + IcosaApiRoot, IcosaHomePage); } // If auto profiling is enabled, disable automatic Icosa downloading. @@ -541,10 +539,10 @@ private async Task GetIcosaStatus() return IcosaStatus.Disabled; } - string uri = ApiHost; + string uri = IcosaApiRoot; try { - var api = new LoginApi($"{App.ICOSA_API_URL}"); + var api = new LoginApi(IcosaApiRoot); var result = new Dictionary { { "version", "v1" } }; // TODO: get version from API string version = result["version"]; if (version == kIcosaApiVersion) @@ -720,7 +718,7 @@ private async Task CreateZipFileAsync( // response.uri is not very useful; it is an API uri that gives you json of asset details. // Also, the 3d-models URI might show that the asset is still processing. We can poll their // API and find out when it's done and pop up the window then? - string uri = $"{App.ICOSA_WEBSITE_URL}/edit/{response.upload_job}"; + string uri = $"{VrAssetService.m_Instance.IcosaHomePage}/edit/{response.upload_job}"; return (uri, 0); } @@ -824,7 +822,7 @@ public AssetGetter GetAsset(string assetId, VrAssetFormat type, string reason) } else { - uri = String.Format("{0}{1}/{2}", ApiHost, kListAssetsUri, assetId); + uri = String.Format("{0}{1}/{2}", IcosaApiRoot, kListAssetsUri, assetId); } return new AssetGetter(uri, assetId, type, reason); } @@ -855,7 +853,7 @@ public AssetLister ListAssets(SketchSetType type) break; } - string uri = $"{ApiHost}{filteredUriPath}&pageSize={m_AssetsPerPage}"; + string uri = $"{IcosaApiRoot}{filteredUriPath}&pageSize={m_AssetsPerPage}"; return new AssetLister(uri, errorMessage); } @@ -863,7 +861,7 @@ public AssetLister ListAssets(SketchSetType type) public IEnumerator InsertSketchInfo( string assetId, int index, List infos) { - string uri = String.Format("{0}{1}/{2}", ApiHost, kListAssetsUri, assetId); + string uri = String.Format("{0}{1}/{2}", IcosaApiRoot, kListAssetsUri, assetId); WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { @@ -895,13 +893,13 @@ public AssetLister ListAssets(IcosaSetType type) switch (type) { case IcosaSetType.Liked: - uri = $"{ApiHost}{kUserLikesUri}?format=GLTF2&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; + uri = $"{IcosaApiRoot}{kUserLikesUri}?format=GLTF2&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; break; case IcosaSetType.User: - uri = $"{ApiHost}{kUserAssetsUri}?format=GLTF2&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + uri = $"{IcosaApiRoot}{kUserAssetsUri}?format=GLTF2&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; break; case IcosaSetType.Featured: - uri = $"{ApiHost}{kListAssetsUri}" + + uri = $"{IcosaApiRoot}{kListAssetsUri}" + $"?format=GLTF2&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; break; } @@ -912,7 +910,7 @@ public AssetLister ListAssets(IcosaSetType type) public IEnumerator LoadTiltFile(string id) { string path = Path.GetTempFileName(); - string uri = String.Format("{0}{1}/{2}", ApiHost, kListAssetsUri, id); + string uri = String.Format("{0}{1}/{2}", IcosaApiRoot, kListAssetsUri, id); WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index d5902f6869..339b34fcdd 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -29,8 +29,9 @@ public class SketchControlsScript : MonoBehaviour { // TODO L10n public const string kRemoveHeadsetFyi = "Remove headset to view."; - private string m_OpenBrushGalleryUrl = $"{App.ICOSA_WEBSITE_URL}/openbrush"; - private string m_BlocksGalleryUrl = $"{App.ICOSA_WEBSITE_URL}/blocks"; + + private string m_OpenBrushGalleryUrl = $"{VrAssetService.m_Instance.IcosaHomePage}/openbrush"; + private string m_BlocksGalleryUrl = $"{VrAssetService.m_Instance.IcosaHomePage}/blocks"; static public SketchControlsScript m_Instance; static bool sm_enableGrabHaptics = true; @@ -98,8 +99,8 @@ public enum GlobalCommands Duplicate, ToggleGroupStrokesAndWidgets, SaveModel, - ViewPolyPage, - ViewPolyGallery, + ViewIcosaHomePage, + ViewBlocksGallery, ExportListed, RenderCameraPath, ToggleProfiling, @@ -4663,10 +4664,10 @@ public void IssueGlobalCommand(GlobalCommands rEnum, int iParam1 = -1, case GlobalCommands.SaveModel: SaveModel(); break; - case GlobalCommands.ViewPolyPage: - OpenURLAndInformUser(App.ICOSA_WEBSITE_URL); + case GlobalCommands.ViewIcosaHomePage: + OpenURLAndInformUser(VrAssetService.m_Instance.IcosaHomePage); break; - case GlobalCommands.ViewPolyGallery: + case GlobalCommands.ViewBlocksGallery: OpenURLAndInformUser(m_BlocksGalleryUrl); break; case GlobalCommands.ExportListed: diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index 7bc1746237..02b94eb43a 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -346,8 +346,8 @@ public Dictionary Formats public struct SharingConfig { // For Poly testing allow us to use a different API host and landing page URL. - [JsonProperty("VrAssetServiceHost")] public string VrAssetServiceHostOverride; - [JsonProperty("VrAssetServiceUrl")] public string VrAssetServiceUrlOverride; + [FormerlySerializedAs("VrAssetServiceHostOverride")][JsonProperty("VrAssetServiceHost")] public string IcosaApiRoot; + [FormerlySerializedAs("VrAssetServiceUrlOverride")][JsonProperty("VrAssetServiceUrl")] public string IcosaHomePage; } public SharingConfig Sharing; From c7cbd64c68a97d8fdb05d3963c51c6d4b130ee71 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 12:13:43 +0100 Subject: [PATCH 069/105] Minor changes --- Assets/Scripts/Sharing/VrAssetService.cs | 2 +- Assets/Scripts/SketchControlsScript.cs | 4 ++-- Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index cff654fb57..9711be6ad8 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -539,9 +539,9 @@ private async Task GetIcosaStatus() return IcosaStatus.Disabled; } - string uri = IcosaApiRoot; try { + // TODO need a do-nothing endpoint we can use for this var api = new LoginApi(IcosaApiRoot); var result = new Dictionary { { "version", "v1" } }; // TODO: get version from API string version = result["version"]; diff --git a/Assets/Scripts/SketchControlsScript.cs b/Assets/Scripts/SketchControlsScript.cs index 339b34fcdd..ee21d2fbb9 100644 --- a/Assets/Scripts/SketchControlsScript.cs +++ b/Assets/Scripts/SketchControlsScript.cs @@ -30,8 +30,8 @@ public class SketchControlsScript : MonoBehaviour // TODO L10n public const string kRemoveHeadsetFyi = "Remove headset to view."; - private string m_OpenBrushGalleryUrl = $"{VrAssetService.m_Instance.IcosaHomePage}/openbrush"; - private string m_BlocksGalleryUrl = $"{VrAssetService.m_Instance.IcosaHomePage}/blocks"; + private string m_OpenBrushGalleryUrl => $"{VrAssetService.m_Instance.IcosaHomePage}/openbrush"; + private string m_BlocksGalleryUrl => $"{VrAssetService.m_Instance.IcosaHomePage}/blocks"; static public SketchControlsScript m_Instance; static bool sm_enableGrabHaptics = true; diff --git a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs index b98bfedcdc..e29296628c 100644 --- a/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs +++ b/Assets/ThirdParty/Org.OpenAPITools/Client/ApiClient.cs @@ -147,7 +147,7 @@ internal object Deserialize(UnityWebRequest request, Type type) throw new ApiException((int)request.responseCode, request.error, text); } } - + if (type != typeof(System.Object) && request.responseCode >= 200 && request.responseCode < 300) { throw new UnexpectedResponseException(request, type); @@ -417,7 +417,7 @@ private async Task> ExecAsync( { await tsc.Task; } - + if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.DataProcessingError) { From be018e2307889537089ce64550c88f4215137cdc Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 14:33:16 +0100 Subject: [PATCH 070/105] Unbreak Google login --- Assets/Scripts/App.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index d425bdd779..1d10827d2e 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -164,6 +164,7 @@ public static OAuth2Identity GetIdentity(Cloud cloud) { switch (cloud) { + case Cloud.Google: return GoogleIdentity; case Cloud.Sketchfab: return SketchfabIdentity; case Cloud.Icosa: throw new InvalidOperationException("Icosa does not use OAuth2"); default: throw new InvalidOperationException($"No OAuth2 identity for {cloud}"); From 292bce3ad454856dba0e0a0dacafe9b8945e5e88 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 14:33:50 +0100 Subject: [PATCH 071/105] Redundant default param --- Assets/Scripts/Sharing/AssetLister.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index 3e93d202f5..e2f2ea4c46 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -40,7 +40,7 @@ public IEnumerator NextPage(List files) { string uri = m_PageToken == null ? m_Uri : $"{m_Uri}&pageToken={m_PageToken}"; - WebRequest request = new WebRequest(uri, App.Instance.IcosaToken, UnityWebRequest.kHttpVerbGET); + WebRequest request = new WebRequest(uri, App.Instance.IcosaToken); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) From 7cc489018add80329d6ffe402bff16636d86194d Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 14:34:19 +0100 Subject: [PATCH 072/105] Google login enum. Remove redundant code --- Assets/Scripts/Sharing/VrAssetService.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 9711be6ad8..1391dc81dc 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -35,7 +35,7 @@ namespace TiltBrush public enum Cloud { None = 0, - // Poly = 1, + Google = 1, Sketchfab = 2, Icosa = 3, } @@ -378,13 +378,6 @@ void Awake() void Start() { - if (!string.IsNullOrEmpty(App.UserConfig.Sharing.IcosaApiRoot) || - !string.IsNullOrEmpty(App.UserConfig.Sharing.IcosaHomePage)) - { - Debug.LogFormat("Overriding VrAssetService Api Host: {0} Landing Page: {1}", - IcosaApiRoot, IcosaHomePage); - } - // If auto profiling is enabled, disable automatic Icosa downloading. if (!App.UserConfig.Profiling.AutoProfile) { From bcd668e74e1a8163a2d1d1268d245ea0f5f46cf7 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 14:35:39 +0100 Subject: [PATCH 073/105] Fix monoscopic reticle bounds --- .../PopUps/PopUpWindow_Accounts.prefab | 455 ++++++++++++------ 1 file changed, 318 insertions(+), 137 deletions(-) diff --git a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab index 3501fdb33a..8a49196286 100644 --- a/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab +++ b/Assets/Prefabs/PopUps/PopUpWindow_Accounts.prefab @@ -23,6 +23,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162347472523419310} + serializedVersion: 2 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} @@ -33,7 +34,6 @@ Transform: - {fileID: 2434770951962534735} - {fileID: 5914045499585322770} m_Father: {fileID: 7706333168622570552} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &217738510970157188 GameObject: @@ -60,13 +60,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 217738510970157188} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.37, y: -0.324, z: -0.025} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625732761162234} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &597686372283234627 MeshFilter: @@ -149,7 +149,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1000721931266647293} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -315,6 +314,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 271835898861049686} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.621, y: 0.11, z: -0.010000025} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} @@ -323,7 +323,6 @@ Transform: - {fileID: 3392034205142368857} - {fileID: 6661346418304443125} m_Father: {fileID: 529827201699571093} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1135707697623452651 MeshFilter: @@ -400,13 +399,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 281059589699444468} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0, y: 0.09399994, z: 0.000999992} m_LocalScale: {x: 88.50999, y: 88.51, z: 88.51} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 282387885870267606} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &256765526658491878 MeshFilter: @@ -484,6 +483,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 281059592884163862} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.67, y: 0.39, z: -0.212} m_LocalScale: {x: 1.5, y: 1.4999999, z: 1.4999999} @@ -494,7 +494,6 @@ Transform: - {fileID: 282387882118496818} - {fileID: 8121802616684643442} m_Father: {fileID: 7706333168622570552} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &217270776815681370 BoxCollider: @@ -504,9 +503,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 281059592884163862} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2.5, y: 1.5, z: 0.02} m_Center: {x: 0, y: 0, z: 0.06} --- !u!23 &3134156942944013523 @@ -604,13 +611,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 281059592966504990} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0, y: 0.094, z: 0.001} m_LocalScale: {x: 88.50999, y: 88.51, z: 88.51} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 282387885870267606} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &256765527167650962 MeshFilter: @@ -694,7 +701,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6373137851377895237} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -904,13 +910,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 440723707841846920} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.845, y: -0.242, z: -0.06} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7741920746089690377} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2121377069448007116 MeshFilter: @@ -1049,9 +1055,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 440723707841846920} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &710086474055226122 @@ -1081,6 +1095,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 710086474055226122} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.634, y: -0.242, z: -0.06} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} @@ -1089,7 +1104,6 @@ Transform: - {fileID: 1583684570998085433} - {fileID: 5208561973859313428} m_Father: {fileID: 1432546188030660464} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8398224192364770992 MeshFilter: @@ -1228,9 +1242,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 710086474055226122} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &805844845446197852 @@ -1260,6 +1282,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 805844845446197852} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.434, z: -0.08} m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} @@ -1268,7 +1291,6 @@ Transform: - {fileID: 6454029799957143386} - {fileID: 8006143071345660080} m_Father: {fileID: 3457603883277620212} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6941474665913055402 MeshFilter: @@ -1328,9 +1350,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 805844845446197852} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &5581805715966274588 @@ -1437,13 +1467,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1206425102053680919} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.0021666286, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7500850895307564819} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5694728525219628291 MeshFilter: @@ -1522,13 +1552,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1420984185611099820} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.845, y: -0.242, z: -0.06} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6423827612944397798} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3587958868994276479 MeshFilter: @@ -1666,9 +1696,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1420984185611099820} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &1595746536817818034 @@ -1698,13 +1736,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1595746536817818034} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.845, y: -0.242, z: -0.06} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1675920430848132177} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8449302079573380106 MeshFilter: @@ -1842,9 +1880,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1595746536817818034} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &1666263515128110337 @@ -1872,13 +1918,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1666263515128110337} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0.53499997, y: 0.2550001, z: -0.005} m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1000721931266647293} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2034091918340690625 MeshFilter: @@ -1953,6 +1999,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2027372909421917180} + serializedVersion: 2 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} @@ -1963,7 +2010,6 @@ Transform: - {fileID: 9030048754727846959} - {fileID: 1153672243062602464} m_Father: {fileID: 7706333168622570552} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2114749234407238233 GameObject: @@ -1992,13 +2038,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2114749234407238233} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.041, y: 0.247, z: -0.02499998} m_LocalScale: {x: 0.25, y: 0.24999991, z: 0.2499999} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 529827201699571093} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3237075444271635601 MeshFilter: @@ -2137,9 +2183,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2114749234407238233} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &2130932365216558288 @@ -2174,7 +2228,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3457603883277620212} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2378,6 +2431,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2181120061148835783} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.13499999, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -2387,7 +2441,6 @@ Transform: - {fileID: 4704522903857296483} - {fileID: 7338107887349877872} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2524197670113838527 GameObject: @@ -2421,7 +2474,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735159627316} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2627,13 +2679,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2539912369590534119} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0.7238, y: 0.2550001, z: -0.005} m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1432546188030660464} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2328370020170245272 MeshFilter: @@ -2710,13 +2762,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2541333487600552761} + serializedVersion: 2 m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5521146392998997215} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!33 &7900905264382626932 MeshFilter: @@ -2793,13 +2845,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2615357070484673490} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0.622, y: 0.2550001, z: -0.005} m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6423827612944397798} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8539932158365628172 MeshFilter: @@ -2882,7 +2934,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 529827201699571093} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -3048,13 +3099,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2727968127303647375} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1741904442304953456} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3359518694211327191 MeshFilter: @@ -3133,6 +3184,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2777064628006719104} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.627, y: -0.242, z: -0.06} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} @@ -3141,7 +3193,6 @@ Transform: - {fileID: 3376029733079660750} - {fileID: 7580573523043848490} m_Father: {fileID: 534589999851858800} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &4757421857253066679 MeshFilter: @@ -3280,9 +3331,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2777064628006719104} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &3011044884306177928 @@ -3310,13 +3369,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3011044884306177928} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0.622, y: 0.2550001, z: -0.005} m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1675920430848132177} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1159919028371824209 MeshFilter: @@ -3400,7 +3459,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6423827612944397798} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -3613,7 +3671,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7741920746089690377} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -3826,7 +3883,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7338107887349877872} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -4034,6 +4090,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3270184853410304968} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.627, y: -0.242, z: -0.06} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} @@ -4042,7 +4099,6 @@ Transform: - {fileID: 2729332839612941327} - {fileID: 906844544403122228} m_Father: {fileID: 1000721931266647293} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5249507884336548187 MeshFilter: @@ -4181,9 +4237,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3270184853410304968} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 2, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &3292774118003760337 @@ -4213,6 +4277,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3292774118003760337} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.434, z: -0.08} m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} @@ -4221,7 +4286,6 @@ Transform: - {fileID: 4750169147585981226} - {fileID: 2257200555475620437} m_Father: {fileID: 6373137851377895237} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6391502674584969794 MeshFilter: @@ -4281,9 +4345,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3292774118003760337} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &4927931040837201254 @@ -4390,13 +4462,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3350558357556252257} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.045} m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 181638217984195686} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8140081930800988506 MeshFilter: @@ -4473,13 +4545,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3364245950019763117} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0.621, y: -0.151, z: -0.005} m_LocalScale: {x: 47, y: 49.999924, z: 74.2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8994260351568061772} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7765932664411523162 MeshFilter: @@ -4556,13 +4628,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3364253099164516464} + serializedVersion: 2 m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} m_LocalPosition: {x: 0, y: 0, z: 0.025} m_LocalScale: {x: 0.79999995, y: 0.02, z: 0.8} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8121802616684643442} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} --- !u!33 &2437227728573446938 MeshFilter: @@ -4639,13 +4711,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3387414818891217864} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 534589999851858800} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1555734638323418344 MeshFilter: @@ -4724,6 +4796,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3674036727267325941} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.251, y: -0.75199986, z: -0.018} m_LocalScale: {x: 0.29999998, y: 0.29999998, z: 0.29999998} @@ -4732,7 +4805,6 @@ Transform: - {fileID: 6855474239171570967} - {fileID: 4100876239645834569} m_Father: {fileID: 3576301794080265196} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3994049754972082256 MeshFilter: @@ -4792,9 +4864,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3674036727267325941} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &4355862344388155723 @@ -4901,13 +4981,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3793285321450572269} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5521146392998997215} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &4593108259088090395 MeshFilter: @@ -4984,13 +5064,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3883720175326442003} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0.621, y: -0.151, z: -0.005} m_LocalScale: {x: 47, y: 49.999924, z: 74.2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1411852402157678472} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1080627186782362197 MeshFilter: @@ -5065,6 +5145,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3893413551512185020} + serializedVersion: 2 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} @@ -5076,7 +5157,6 @@ Transform: - {fileID: 2403478784508930027} - {fileID: 5520953697447238279} m_Father: {fileID: 7706333168622570552} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &4082436277880791083 GameObject: @@ -5101,6 +5181,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4082436277880791083} + serializedVersion: 2 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} @@ -5110,7 +5191,6 @@ Transform: - {fileID: 1432546188030660464} - {fileID: 7741920746089690377} m_Father: {fileID: 8645625735416266758} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &4103205528941609404 GameObject: @@ -5139,13 +5219,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4103205528941609404} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.046, y: 0.2620001, z: -0.025} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 534589999851858800} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3380311651882997193 MeshFilter: @@ -5284,9 +5364,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4103205528941609404} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &4105427794534146856 @@ -5316,13 +5404,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4105427794534146856} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.398, y: -0.242, z: -0.06} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6423827612944397798} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &4957999687296029088 MeshFilter: @@ -5461,9 +5549,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4105427794534146856} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &4148507089762079577 @@ -5498,7 +5594,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5520953697447238279} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -5704,13 +5799,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4316944982740412120} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1741904442304953456} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &390951240049198343 MeshFilter: @@ -5789,13 +5884,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4516902404884197280} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.398, y: -0.242, z: -0.06} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1675920430848132177} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3316034999128707532 MeshFilter: @@ -5934,9 +6029,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4516902404884197280} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &4539008613640430378 @@ -5964,13 +6067,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4539008613640430378} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.37, y: -0.324, z: -0.025} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5621771514230647440} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8976936997367820456 MeshFilter: @@ -6045,6 +6148,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4671315024118560739} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.5611764, y: -1.1, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -6055,7 +6159,6 @@ Transform: - {fileID: 1675920430848132177} - {fileID: 282387885870267606} m_Father: {fileID: 8645625735416266758} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &4884541372955623633 GameObject: @@ -6089,7 +6192,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1741904442304953456} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6294,6 +6396,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5048206632594256402} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.13499999, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -6304,7 +6407,6 @@ Transform: - {fileID: 7566473728865432861} - {fileID: 5521146392998997215} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &480163635885148388 MonoBehaviour: @@ -6350,7 +6452,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4825048073929942150} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6556,13 +6657,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5123262806756250226} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.045} m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7893921961113671483} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7929569316632562742 MeshFilter: @@ -6639,13 +6740,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5301811399408650331} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.151, z: -0.005} m_LocalScale: {x: 0.01, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735416266758} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6161329202685458175 MeshFilter: @@ -6729,7 +6830,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7893921961113671483} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -6942,7 +7042,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 3457603883277620212} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -7152,13 +7251,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5419396676487616149} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.398, y: -0.242, z: -0.06} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7741920746089690377} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5255334288434809734 MeshFilter: @@ -7296,9 +7395,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5419396676487616149} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &5427861527217224720 @@ -7328,6 +7435,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5427861527217224720} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.434, z: -0.08} m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} @@ -7336,7 +7444,6 @@ Transform: - {fileID: 3215585481372348197} - {fileID: 5384230387207399056} m_Father: {fileID: 4825048073929942150} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &744816621245303203 MeshFilter: @@ -7396,9 +7503,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5427861527217224720} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &5696691115232969362 @@ -7505,6 +7620,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5598400511041364639} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.621, y: 0.11, z: -0.010000025} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} @@ -7513,7 +7629,6 @@ Transform: - {fileID: 8639010961277564811} - {fileID: 8146115410073256732} m_Father: {fileID: 8994260351568061772} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3899858871660736573 MeshFilter: @@ -7588,6 +7703,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5649685825589421528} + serializedVersion: 2 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} @@ -7598,7 +7714,6 @@ Transform: - {fileID: 6124434747938532598} - {fileID: 4120900520909079457} m_Father: {fileID: 149137880941958609} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &5782945728072830494 GameObject: @@ -7625,13 +7740,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5782945728072830494} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.65, y: 0.65, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8121802616684643442} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7835309114794309555 MeshFilter: @@ -7706,6 +7821,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5866976969575595662} + serializedVersion: 2 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} @@ -7716,7 +7832,6 @@ Transform: - {fileID: 90735575297890764} - {fileID: 5753424830644711893} m_Father: {fileID: 149137880941958609} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &5921053250823053932 GameObject: @@ -7743,13 +7858,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5921053250823053932} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.33714277, y: -0.011110305, z: -0.024999999} m_LocalScale: {x: 0.21428569, y: 0.8333332, z: 0.15} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1741904442304953456} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1551091070195428418 MeshFilter: @@ -7828,13 +7943,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5978289847150237173} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.046, y: 0.2620001, z: -0.025} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1000721931266647293} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &899810967416512585 MeshFilter: @@ -7973,9 +8088,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 5978289847150237173} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &6286143077313149806 @@ -8003,13 +8126,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6286143077313149806} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.278, y: 0.26200008, z: -0.01} m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1000721931266647293} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7928834966209772352 MeshFilter: @@ -8093,7 +8216,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 4825048073929942150} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8303,13 +8425,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6569154546886767970} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.213, y: 0.2620001, z: -0.025} m_LocalScale: {x: 0.2, y: 0.2, z: 0.2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1432546188030660464} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3857003002993330428 MeshFilter: @@ -8448,9 +8570,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6569154546886767970} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &6655431747887542890 @@ -8484,7 +8614,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1432546188030660464} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -8652,13 +8781,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6696247793707346302} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.041, y: 0.247, z: -0.02499998} m_LocalScale: {x: 0.25, y: 0.24999991, z: 0.2499999} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8994260351568061772} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1778902779340506197 MeshFilter: @@ -8797,9 +8926,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6696247793707346302} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &6877995767082855847 @@ -8827,13 +8964,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6877995767082855847} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0.53499997, y: 0.2550001, z: -0.005} m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 534589999851858800} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2124419336652034620 MeshFilter: @@ -8916,7 +9053,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8994260351568061772} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -9089,7 +9225,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1675920430848132177} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -9295,13 +9430,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7241757417913730636} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0.622, y: 0.2550001, z: -0.005} m_LocalScale: {x: 32, y: 32, z: 20} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7741920746089690377} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7967378821529834859 MeshFilter: @@ -9376,6 +9511,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7292193688806996409} + serializedVersion: 2 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} @@ -9387,7 +9523,6 @@ Transform: - {fileID: 1741904442304953456} - {fileID: 6692607038900794975} m_Father: {fileID: 5854179938074699431} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &7389499574749059798 GameObject: @@ -9421,7 +9556,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1766577842652504206} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -9634,7 +9768,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7500850895307564819} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -9840,13 +9973,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7464697824948226506} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071067} m_LocalPosition: {x: 0.621, y: -0.151, z: -0.005} m_LocalScale: {x: 47, y: 49.999924, z: 74.2} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 529827201699571093} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &5785604700358356680 MeshFilter: @@ -9923,13 +10056,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7600327135675325717} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.0022857143} m_LocalScale: {x: 1.1, y: 1.1, z: 1.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5621771514230647440} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6946978540861440256 MeshFilter: @@ -10004,6 +10137,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7620355729290749329} + serializedVersion: 2 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} @@ -10015,7 +10149,6 @@ Transform: - {fileID: 4700142148089133932} - {fileID: 181638217984195686} m_Father: {fileID: 149137880941958609} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &7725770829448276136 GameObject: @@ -10048,7 +10181,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 534589999851858800} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -10214,13 +10346,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7739595941385861960} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.0022857143} m_LocalScale: {x: 1.1, y: 1.1, z: 1.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 9214833072643761372} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &751210734990785936 MeshFilter: @@ -10304,7 +10436,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6373137851377895237} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -10510,13 +10641,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7844204848832429112} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.0021666286, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7338107887349877872} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8207801876785030697 MeshFilter: @@ -10593,13 +10724,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7987582482775903940} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.37, y: -0.324, z: -0.025} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 9214833072643761372} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1622672940972688331 MeshFilter: @@ -10674,6 +10805,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8025577052483214110} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.13499999, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -10683,7 +10815,6 @@ Transform: - {fileID: 8844894437526037228} - {fileID: 6721629502030059466} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8071707898958835644 GameObject: @@ -10712,6 +10843,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8071707898958835644} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 1.102, y: -0.576, z: -0.054} m_LocalScale: {x: 0.29999995, y: 0.29999998, z: 0.29999998} @@ -10720,7 +10852,6 @@ Transform: - {fileID: 7015353133639046746} - {fileID: 7380447630107943821} m_Father: {fileID: 282387885870267606} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2977604093551169432 MeshFilter: @@ -10780,9 +10911,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8071707898958835644} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.0000001, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &4597543144213941713 @@ -10881,7 +11020,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 181638217984195686} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -11087,13 +11225,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8203317146764388448} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.0021666286, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6721629502030059466} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1440642196620937798 MeshFilter: @@ -11170,13 +11308,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8230375728890308425} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.9828, y: 0.26200008, z: -0.01} m_LocalScale: {x: 0.2649999, y: 0.2649999, z: 0.2649999} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1432546188030660464} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1482784536010354649 MeshFilter: @@ -11260,7 +11398,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 6721629502030059466} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -11464,6 +11601,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8372598602804526943} + serializedVersion: 2 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} @@ -11475,7 +11613,6 @@ Transform: - {fileID: 6952797202003472198} - {fileID: 7893921961113671483} m_Father: {fileID: 5854179938074699431} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8374217926999613278 GameObject: @@ -11500,6 +11637,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8374217926999613278} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.477, y: 0.135, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -11508,7 +11646,6 @@ Transform: - {fileID: 9062749716723118463} - {fileID: 1698918594952708344} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8485803142575213209 GameObject: @@ -11542,7 +11679,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1766577842652504206} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -11750,6 +11886,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8579567244815457455} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.62, y: -0.576, z: -0.02500001} m_LocalScale: {x: 0.7, y: 0.18, z: 1} @@ -11760,7 +11897,6 @@ Transform: - {fileID: 4153333614772809210} - {fileID: 7758143906596955047} m_Father: {fileID: 1411852402157678472} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &1069378607617687420 MeshFilter: @@ -11899,9 +12035,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8579567244815457455} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &8644312780941313480 @@ -11935,7 +12079,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1411852402157678472} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -12101,6 +12244,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332586413500038} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.621, y: 0.11, z: -0.010000025} m_LocalScale: {x: 0.4, y: 0.4, z: 0.4} @@ -12109,7 +12253,6 @@ Transform: - {fileID: 8645625732756982244} - {fileID: 3135609562369966535} m_Father: {fileID: 1411852402157678472} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995069649507040 MeshFilter: @@ -12186,13 +12329,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332586423700726} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.0022857143} m_LocalScale: {x: 1.1, y: 1.1, z: 1.1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625732761162234} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995080555659526 MeshFilter: @@ -12269,13 +12412,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587409793350} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735778197046} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995081492312010 MeshFilter: @@ -12353,6 +12496,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587479430734} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -12367,7 +12511,6 @@ Transform: - {fileID: 1766577842652504206} - {fileID: 8645625735778197046} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &8529915923179660036 MonoBehaviour: @@ -12389,7 +12532,7 @@ MonoBehaviour: m_SubtitleCharacterWidth: 0.05625 m_ButtonWidth: 0.5 m_BaseButtonOffset: {x: 0, y: 0, z: 0} - m_ReticleBounds: {x: 1.6, y: 3, z: -0.35} + m_ReticleBounds: {x: 3.4, y: 3, z: -0.35} m_PopUpForwardOffset: -0.25 m_AutoPlaceButtons: [] m_TransitionDuration: 0.1 @@ -12445,9 +12588,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587479430734} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 3.4, y: 2.8, z: 0.01} m_Center: {x: 0.2, y: -0.6, z: -0.01} --- !u!114 &8533537593755566908 @@ -12485,6 +12636,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332587709734578} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -0.349, z: 0} m_LocalScale: {x: 1.21, y: 1.21, z: 1.21} @@ -12493,7 +12645,6 @@ Transform: - {fileID: 8645625733637814676} - {fileID: 8645625740661137770} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8644332588524666284 GameObject: @@ -12520,13 +12671,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332588524666284} + serializedVersion: 2 m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} m_LocalPosition: {x: -0, y: 0, z: 0} m_LocalScale: {x: 100, y: 100, z: 100} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735778197046} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995081988753086 MeshFilter: @@ -12601,6 +12752,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332588631379036} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.13499999, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -12610,7 +12762,6 @@ Transform: - {fileID: 8645625734146380956} - {fileID: 8645625735159627316} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8644332588836208674 GameObject: @@ -12644,7 +12795,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625734681381188} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -12848,6 +12998,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332589018115638} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0.13499999, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -12859,7 +13010,6 @@ Transform: - {fileID: 149137880941958609} - {fileID: 7706333168622570552} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &8644332589239274156 GameObject: @@ -12888,6 +13038,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332589239274156} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.434, z: -0.08} m_LocalScale: {x: 0.6, y: 0.29999998, z: 0.29999998} @@ -12896,7 +13047,6 @@ Transform: - {fileID: 1829480651769887961} - {fileID: 8645625732950447588} m_Father: {fileID: 8645625734681381188} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995070916950296 MeshFilter: @@ -12956,9 +13106,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332589239274156} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.01} --- !u!114 &853645773711861975 @@ -13065,13 +13223,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332589642346024} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.099, z: -0.0025} m_LocalScale: {x: 0.7, y: 0.7, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625734681381188} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995080664749698 MeshFilter: @@ -13148,13 +13306,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332589849401822} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.0021666286, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735159627316} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995080173436830 MeshFilter: @@ -13238,7 +13396,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 8645625735416266758} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -13446,13 +13603,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332590165761558} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.193, y: 0.247, z: -0.02499998} m_LocalScale: {x: 0.25, y: 0.24999991, z: 0.2499999} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1411852402157678472} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &8610995070142662328 MeshFilter: @@ -13591,9 +13748,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8644332590165761558} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1.0000001, z: 0.1} m_Center: {x: 0, y: 0, z: -0.01} --- !u!1 &8854873750265266201 @@ -13619,6 +13784,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8854873750265266201} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.135, z: 0} m_LocalScale: {x: 0.85, y: 0.8500001, z: 0.8500001} @@ -13628,7 +13794,6 @@ Transform: - {fileID: 7219417712597752172} - {fileID: 7500850895307564819} m_Father: {fileID: 8645625734765961102} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &9067465071141378777 GameObject: @@ -13653,6 +13818,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 9067465071141378777} + serializedVersion: 2 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} @@ -13662,7 +13828,6 @@ Transform: - {fileID: 534589999851858800} - {fileID: 6423827612944397798} m_Father: {fileID: 8645625735416266758} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &9078849995330061814 GameObject: @@ -13687,6 +13852,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 9078849995330061814} + serializedVersion: 2 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} @@ -13697,7 +13863,6 @@ Transform: - {fileID: 5446221535905318599} - {fileID: 4590447671262214820} m_Father: {fileID: 5854179938074699431} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &9188801593698870886 GameObject: @@ -13724,13 +13889,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 9188801593698870886} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.045} m_LocalScale: {x: 2, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5520953697447238279} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2379879017139536075 MeshFilter: @@ -13787,6 +13952,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 282387885870267606} m_Modifications: - target: {fileID: 1934243545717122, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} @@ -14810,6 +14976,9 @@ PrefabInstance: value: 4 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: ef7eb6dda2db50c4bbf6c682e4a21736, type: 3} --- !u!224 &3194813434804236579 stripped RectTransform: @@ -14822,6 +14991,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 3576301794080265196} m_Modifications: - target: {fileID: 5108924410514366489, guid: cc0d72d82ebc4604eb11a2b2aae17547, @@ -14885,6 +15055,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: cc0d72d82ebc4604eb11a2b2aae17547, type: 3} --- !u!4 &6679582736177907422 stripped Transform: @@ -14903,6 +15076,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 3576301794080265196} m_Modifications: - target: {fileID: 8058103854604790177, guid: bc0454c142b400c469bd0d54ce226a24, @@ -14966,6 +15140,9 @@ PrefabInstance: value: EnabledElements objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: bc0454c142b400c469bd0d54ce226a24, type: 3} --- !u!114 &554234529220568430 stripped MonoBehaviour: @@ -15008,6 +15185,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 3576301794080265196} m_Modifications: - target: {fileID: 229422388496447405, guid: 99a38de63f18d524c91733042928d80a, @@ -15071,6 +15249,9 @@ PrefabInstance: value: DriveFullElements objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 99a38de63f18d524c91733042928d80a, type: 3} --- !u!1 &5684969559238275584 stripped GameObject: From 45557e7b4de55ea7e7556db75275027fa4d01da9 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 14:36:01 +0100 Subject: [PATCH 074/105] Remove formerly serialized attributes --- Assets/Scripts/UserConfig.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index 02b94eb43a..d9b71cf97f 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -346,8 +346,8 @@ public Dictionary Formats public struct SharingConfig { // For Poly testing allow us to use a different API host and landing page URL. - [FormerlySerializedAs("VrAssetServiceHostOverride")][JsonProperty("VrAssetServiceHost")] public string IcosaApiRoot; - [FormerlySerializedAs("VrAssetServiceUrlOverride")][JsonProperty("VrAssetServiceUrl")] public string IcosaHomePage; + public string IcosaApiRoot; + public string IcosaHomePage; } public SharingConfig Sharing; From c26aeb33d12d0784349f26cb8e516bb5bc7d4fa6 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 14:36:22 +0100 Subject: [PATCH 075/105] Allow breaking apart on icosa models --- Assets/Scripts/Widgets/ModelWidget.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Widgets/ModelWidget.cs b/Assets/Scripts/Widgets/ModelWidget.cs index 0f17bae0b4..ae11487cf3 100644 --- a/Assets/Scripts/Widgets/ModelWidget.cs +++ b/Assets/Scripts/Widgets/ModelWidget.cs @@ -309,7 +309,7 @@ void LoadModel() public bool HasSubModels() { string ext = Model.GetLocation().Extension; - if (ext == ".gltf" || ext == ".glb") + if (ext == ".gltf" || ext == ".gltf2" || ext == ".glb") { int lightCount = m_ObjModelScript.GetComponentsInChildren().Length; int meshCount = GetMeshes().Length; From 8f85801232cde694a53298f99280ceaba8491053 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 14 Jul 2024 15:02:23 +0100 Subject: [PATCH 076/105] Fix error when breaking up icosa models --- Assets/Scripts/Widgets/ModelWidget.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Widgets/ModelWidget.cs b/Assets/Scripts/Widgets/ModelWidget.cs index ae11487cf3..6dc6d938ef 100644 --- a/Assets/Scripts/Widgets/ModelWidget.cs +++ b/Assets/Scripts/Widgets/ModelWidget.cs @@ -370,7 +370,17 @@ public void SyncHierarchyToSubtree(string previousSubtree = null) } var newRoot = new GameObject(); newRoot.transform.SetParent(transform); - newRoot.name = $"LocalFile:{m_Model.RelativePath}#{m_Subtree}"; + switch (m_Model.GetLocation().GetLocationType()) + { + case Model.Location.Type.LocalFile: + newRoot.name = $"LocalFile:{m_Model.RelativePath}#{m_Subtree}"; + break; + case Model.Location.Type.IcosaAssetId: + newRoot.name = $"RemoteFile:{m_Model.AssetId}#{m_Subtree}"; + break; + case Model.Location.Type.Invalid: + throw new InvalidOperationException("Invalid model location type"); + } m_ObjModelScript = newRoot.AddComponent(); node.SetParent(newRoot.transform, worldPositionStays: true); From 35df6106d2a2ff074c91d9aa6273fc7e38620f34 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 15 Jul 2024 13:06:28 +0100 Subject: [PATCH 077/105] Use "blocks" pseudo-format --- Assets/Scripts/Sharing/VrAssetService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 1391dc81dc..d03dfc764d 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -886,14 +886,14 @@ public AssetLister ListAssets(IcosaSetType type) switch (type) { case IcosaSetType.Liked: - uri = $"{IcosaApiRoot}{kUserLikesUri}?format=GLTF2&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; + uri = $"{IcosaApiRoot}{kUserLikesUri}?format=BLOCKS&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; break; case IcosaSetType.User: - uri = $"{IcosaApiRoot}{kUserAssetsUri}?format=GLTF2&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + uri = $"{IcosaApiRoot}{kUserAssetsUri}?format=BLOCKS&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; break; case IcosaSetType.Featured: uri = $"{IcosaApiRoot}{kListAssetsUri}" + - $"?format=GLTF2&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + $"?format=BLOCKS&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; break; } return new AssetLister(uri, "Failed to connect to Icosa."); From 88dc001016dd8e9f6d5ad6d47b2dca0baa9a5812 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 15 Jul 2024 16:01:01 +0100 Subject: [PATCH 078/105] More room for title on sketchbook panel --- Assets/Prefabs/Panels/SketchbookPanel.prefab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Prefabs/Panels/SketchbookPanel.prefab b/Assets/Prefabs/Panels/SketchbookPanel.prefab index 2e0735cbe4..9c4dfab59b 100644 --- a/Assets/Prefabs/Panels/SketchbookPanel.prefab +++ b/Assets/Prefabs/Panels/SketchbookPanel.prefab @@ -15971,7 +15971,7 @@ MonoBehaviour: m_VertexBufferAutoSizeReduction: 1 m_useMaxVisibleDescender: 1 m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: -0.5816542, w: 0} + m_margin: {x: 0, y: 0, z: -0.91909444, w: 0} m_isUsingLegacyAnimationComponent: 0 m_isVolumetricText: 0 _SortingLayer: 0 From b4c6fc48867b76c8b75a1e59839cbbf06287af32 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Tue, 16 Jul 2024 09:09:12 +0100 Subject: [PATCH 079/105] Use edit_url from upload response instead of job id --- Assets/Scripts/Sharing/IcosaService.cs | 4 +++- Assets/Scripts/Sharing/VrAssetService.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaService.cs b/Assets/Scripts/Sharing/IcosaService.cs index cccde3eb55..3d76b76539 100644 --- a/Assets/Scripts/Sharing/IcosaService.cs +++ b/Assets/Scripts/Sharing/IcosaService.cs @@ -229,15 +229,17 @@ public async Task CreateModel( } uploader.ProgressObject = progress; - var reply = await uploader.SendNamedDataAsync( + WebRequest.Reply reply = await uploader.SendNamedDataAsync( "files", File.OpenRead(zipPath), Path.GetFileName(zipPath), "application/zip", moreParams: moreParams, token, temporaryDirectory); return reply.Deserialize(); } + [Serializable, UsedImplicitly] public struct CreateResponse { public string upload_job; + public string edit_url; } } } // namespace TiltBrush diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index d03dfc764d..f45ff09a68 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -702,7 +702,7 @@ private async Task CreateZipFileAsync( var service = new IcosaService(App.Instance.IcosaToken); var progress = new Progress(d => SetUploadProgress(UploadStep.UploadElements, d)); - var response = await service.CreateModel( + IcosaService.CreateResponse response = await service.CreateModel( zipName, progress, token, options, tempUploadDir); // TODO(b/146892613): return the UID and stick it into the .tilt file? // Or do we not care since we aren't recording provenance and remixing @@ -711,7 +711,7 @@ private async Task CreateZipFileAsync( // response.uri is not very useful; it is an API uri that gives you json of asset details. // Also, the 3d-models URI might show that the asset is still processing. We can poll their // API and find out when it's done and pop up the window then? - string uri = $"{VrAssetService.m_Instance.IcosaHomePage}/edit/{response.upload_job}"; + string uri = $"{response.edit_url}"; return (uri, 0); } From 7fab9b2793423ab71d44a831aa3ced4c94e333c9 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Mon, 5 Aug 2024 16:43:48 +0100 Subject: [PATCH 080/105] Remove out of date comment --- Assets/Scripts/UserConfig.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/UserConfig.cs b/Assets/Scripts/UserConfig.cs index d9b71cf97f..26d37d9dad 100644 --- a/Assets/Scripts/UserConfig.cs +++ b/Assets/Scripts/UserConfig.cs @@ -345,7 +345,6 @@ public Dictionary Formats [Serializable] public struct SharingConfig { - // For Poly testing allow us to use a different API host and landing page URL. public string IcosaApiRoot; public string IcosaHomePage; } From a8eab36a1b62729447a0c79719252a4e78205f45 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 12:36:51 +0100 Subject: [PATCH 081/105] Icosa panel shouldn't begin fixed --- Assets/Prefabs/Panels/IcosaPanel.prefab | 2 +- Assets/Settings/Localization/Strings/Strings_de.asset | 2 +- Assets/Settings/Localization/Strings/Strings_en.asset | 4 ++-- Assets/Settings/Localization/Strings/Strings_es.asset | 2 +- Assets/Settings/Localization/Strings/Strings_fr.asset | 2 +- Assets/Settings/Localization/Strings/Strings_ja.asset | 2 +- Assets/Settings/Localization/Strings/Strings_ko.asset | 2 +- Assets/Settings/Localization/Strings/Strings_zh.asset | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Assets/Prefabs/Panels/IcosaPanel.prefab b/Assets/Prefabs/Panels/IcosaPanel.prefab index 43f3ee9a87..d172bc1a18 100644 --- a/Assets/Prefabs/Panels/IcosaPanel.prefab +++ b/Assets/Prefabs/Panels/IcosaPanel.prefab @@ -277,7 +277,7 @@ MonoBehaviour: m_WandAttachAngle: 0 m_WandAttachYOffset: 0 m_WandAttachHalfHeight: 1.3 - m_BeginFixed: 1 + m_BeginFixed: 0 m_CanBeFixedToWand: 1 m_CanBeDetachedFromWand: 1 m_PopUpGazeDuration: 0.2 diff --git a/Assets/Settings/Localization/Strings/Strings_de.asset b/Assets/Settings/Localization/Strings/Strings_de.asset index e0259b2546..14d275f270 100644 --- a/Assets/Settings/Localization/Strings/Strings_de.asset +++ b/Assets/Settings/Localization/Strings/Strings_de.asset @@ -505,7 +505,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: Poly-Bibliothek + m_Localized: Icosa-Bibliothek m_Metadata: m_Items: [] - m_Id: 7691821452599296 diff --git a/Assets/Settings/Localization/Strings/Strings_en.asset b/Assets/Settings/Localization/Strings/Strings_en.asset index 3ce5d5ef3f..62428df361 100644 --- a/Assets/Settings/Localization/Strings/Strings_en.asset +++ b/Assets/Settings/Localization/Strings/Strings_en.asset @@ -501,7 +501,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: Poly Library + m_Localized: Icosa Library m_Metadata: m_Items: [] - m_Id: 7691821452599296 @@ -3465,7 +3465,7 @@ MonoBehaviour: - m_Id: 164911761917042688 m_Localized: 'Sign in to an Icosa account to allow - uploading to Icosa.' + uploading to Icosa.' m_Metadata: m_Items: [] - m_Id: 164911966586494976 diff --git a/Assets/Settings/Localization/Strings/Strings_es.asset b/Assets/Settings/Localization/Strings/Strings_es.asset index 0aa94db064..ec0d2c56c0 100644 --- a/Assets/Settings/Localization/Strings/Strings_es.asset +++ b/Assets/Settings/Localization/Strings/Strings_es.asset @@ -501,7 +501,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: Biblioteca de Poly + m_Localized: Biblioteca de Icosa m_Metadata: m_Items: [] - m_Id: 7691821452599296 diff --git a/Assets/Settings/Localization/Strings/Strings_fr.asset b/Assets/Settings/Localization/Strings/Strings_fr.asset index c0c7d234f0..98b9741f5c 100644 --- a/Assets/Settings/Localization/Strings/Strings_fr.asset +++ b/Assets/Settings/Localization/Strings/Strings_fr.asset @@ -503,7 +503,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: "Biblioth\xE8que Poly" + m_Localized: "Biblioth\xE8que Icosa" m_Metadata: m_Items: [] - m_Id: 7691821452599296 diff --git a/Assets/Settings/Localization/Strings/Strings_ja.asset b/Assets/Settings/Localization/Strings/Strings_ja.asset index 4175e9eeb7..1fa386568b 100644 --- a/Assets/Settings/Localization/Strings/Strings_ja.asset +++ b/Assets/Settings/Localization/Strings/Strings_ja.asset @@ -500,7 +500,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: "Poly\u30E9\u30A4\u30D6\u30E9\u30EA" + m_Localized: "Icosa\u30E9\u30A4\u30D6\u30E9\u30EA" m_Metadata: m_Items: [] - m_Id: 7691821452599296 diff --git a/Assets/Settings/Localization/Strings/Strings_ko.asset b/Assets/Settings/Localization/Strings/Strings_ko.asset index 1fdf5c1bfe..b2fa9793c0 100644 --- a/Assets/Settings/Localization/Strings/Strings_ko.asset +++ b/Assets/Settings/Localization/Strings/Strings_ko.asset @@ -497,7 +497,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: "Poly \uB77C\uC774\uBE0C\uB7EC\uB9AC" + m_Localized: "Icosa \uB77C\uC774\uBE0C\uB7EC\uB9AC" m_Metadata: m_Items: [] - m_Id: 7691821452599296 diff --git a/Assets/Settings/Localization/Strings/Strings_zh.asset b/Assets/Settings/Localization/Strings/Strings_zh.asset index 801374490a..d9aa0ddef2 100644 --- a/Assets/Settings/Localization/Strings/Strings_zh.asset +++ b/Assets/Settings/Localization/Strings/Strings_zh.asset @@ -495,7 +495,7 @@ MonoBehaviour: m_Metadata: m_Items: [] - m_Id: 7691576060649472 - m_Localized: Poly Library + m_Localized: Icosa Library m_Metadata: m_Items: [] - m_Id: 7691821452599296 From 68c819d0c9f0637b5cb7179988168ebc9f0f8aba Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 12:37:11 +0100 Subject: [PATCH 082/105] Renable poly button and rename to Icosa --- Assets/Prefabs/Panels/LabsPanel.prefab | 226 ++++++++++++++---- Assets/Prefabs/Panels/LabsPanel_Mobile.prefab | 136 ++++++++--- 2 files changed, 283 insertions(+), 79 deletions(-) diff --git a/Assets/Prefabs/Panels/LabsPanel.prefab b/Assets/Prefabs/Panels/LabsPanel.prefab index 95ae0b3ae7..90f31902df 100644 --- a/Assets/Prefabs/Panels/LabsPanel.prefab +++ b/Assets/Prefabs/Panels/LabsPanel.prefab @@ -26,13 +26,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 111906} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.1962, y: 0, z: 0} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3327986 MeshFilter: @@ -121,13 +121,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 132602} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.29, y: 0.1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6572796 BoxCollider: @@ -137,9 +137,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 132602} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 0.65, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &151652 @@ -167,13 +175,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 151652} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.1083, y: 0, z: 0.0002105044} m_LocalScale: {x: 0.0375, y: 0.0375, z: 0.0375} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3363688 MeshFilter: @@ -249,13 +257,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 153348} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.1} m_LocalScale: {x: 1, y: 1, z: 0.01} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 16 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3364774 MeshFilter: @@ -288,6 +296,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 160772} + serializedVersion: 2 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} @@ -311,7 +320,6 @@ Transform: - {fileID: 499404} - {fileID: 415298} m_Father: {fileID: 485948} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &162404 GameObject: @@ -337,6 +345,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162404} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: -0.65, z: 0.05} m_LocalScale: {x: 2.5000002, y: 2.4999995, z: 2.5} @@ -350,7 +359,6 @@ Transform: - {fileID: 468986} - {fileID: 430698} m_Father: {fileID: 402684} - m_RootOrder: 13 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &11452096 MonoBehaviour: @@ -417,13 +425,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 164352} + serializedVersion: 2 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: 485948} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6531256 BoxCollider: @@ -433,9 +441,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 164352} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.9, y: 2.4, z: 0.5} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &168032 @@ -462,13 +478,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 168032} + serializedVersion: 2 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: 402684} - m_RootOrder: 15 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6547610 BoxCollider: @@ -478,9 +494,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 168032} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.4, y: 1.9, z: 0.02} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &190538 @@ -508,13 +532,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 190538} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.181, y: 0.0375, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2394778 MeshRenderer: @@ -592,13 +616,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 193010} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -0.08, y: 0, z: 0} m_LocalScale: {x: 0.01, y: 0.03, z: 0.01} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3387922 MeshFilter: @@ -689,13 +713,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 197878} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0.1965, y: 0, z: 0} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3300622 MeshFilter: @@ -785,13 +809,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 199026} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 1, w: 0} m_LocalPosition: {x: 0.1088, y: 0, z: 0.00021062419} m_LocalScale: {x: 0.0375, y: 0.0375, z: 0.0375} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 434412} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3316150 MeshFilter: @@ -869,6 +893,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 199434} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 10, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -878,7 +903,6 @@ Transform: - {fileID: 442914} - {fileID: 402684} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114019824221785570 MonoBehaviour: @@ -1028,13 +1052,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010690061500} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.207, y: -0.3, z: 0.05} + m_LocalPosition: {x: 0, y: -0.3, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 11 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013755556650 MeshFilter: @@ -1094,9 +1118,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010690061500} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114000011226708614 @@ -1204,13 +1236,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010708462674} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.6000004, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010418119032 MeshFilter: @@ -1270,9 +1302,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010708462674} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114000012887931942 @@ -1380,13 +1420,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011220362354} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.415, y: 0.6, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010384743742 MeshFilter: @@ -1446,9 +1486,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000011220362354} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114000013288942322 @@ -1556,13 +1604,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012054711126} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.415, y: 0.15, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013414599442 MeshFilter: @@ -1622,9 +1670,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012054711126} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114000011947978538 @@ -1731,13 +1787,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012292180826} + serializedVersion: 2 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: 402684} - m_RootOrder: 14 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013181835044 MeshFilter: @@ -1829,13 +1885,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013682514104} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.415, y: 0.15, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012285989520 MeshFilter: @@ -1946,9 +2002,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013682514104} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1000013998383226 @@ -1976,13 +2040,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013998383226} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1.5, y: 1.9, z: 2.4} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 485948} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011026480406 MeshFilter: @@ -2061,13 +2125,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014165885050} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.6, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012292458500 MeshFilter: @@ -2127,9 +2191,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000014165885050} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114000012043531378 @@ -2237,13 +2309,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1634642417305556} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.15, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33062622632853984 MeshFilter: @@ -2303,9 +2375,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1634642417305556} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.05} --- !u!114 &114328378359043972 @@ -2386,13 +2466,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1016131236980122382} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: 0.20799999, y: -0.3, z: 0.05} + m_LocalPosition: {x: 0.415, y: -0.3, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 12 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7459773723550717888 MeshFilter: @@ -2503,9 +2583,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1016131236980122382} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &2720949118896594763 @@ -2522,12 +2610,12 @@ GameObject: - component: {fileID: 4847028611873093419} - component: {fileID: 8302968001735879957} m_Layer: 16 - m_Name: PolyLibrary + m_Name: IcosaLibrary m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &1643836230823162033 Transform: m_ObjectHideFlags: 0 @@ -2535,13 +2623,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2720949118896594763} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.415, y: 0.15, z: 0.05} + m_LocalPosition: {x: -0.415, y: -0.3, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2078078264413487613 MeshFilter: @@ -2652,9 +2740,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2720949118896594763} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.05} --- !u!1 &4011871447704911063 @@ -2684,13 +2780,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4011871447704911063} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.415, y: 0.15, z: 0.049999237} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &4510907067803236372 MeshFilter: @@ -2801,9 +2897,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4011871447704911063} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.05} --- !u!1 &6294566030148222674 @@ -2833,13 +2937,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6294566030148222674} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.415, y: 0.6000004, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6280797664358514420 MeshFilter: @@ -2899,9 +3003,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6294566030148222674} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &6254052937444237984 @@ -3009,13 +3121,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6295169059849678530} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.415, y: 0.14999962, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6280936243828533384 MeshFilter: @@ -3075,9 +3187,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6295169059849678530} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &6253917691903006574 @@ -3168,13 +3288,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6464453085985281487} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.415, y: 0.6000004, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &6468953577034300013 MeshFilter: @@ -3234,9 +3354,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6464453085985281487} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &6351870043360636743 diff --git a/Assets/Prefabs/Panels/LabsPanel_Mobile.prefab b/Assets/Prefabs/Panels/LabsPanel_Mobile.prefab index ea2fc049ca..32ce3de82f 100644 --- a/Assets/Prefabs/Panels/LabsPanel_Mobile.prefab +++ b/Assets/Prefabs/Panels/LabsPanel_Mobile.prefab @@ -24,13 +24,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 153348} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0.1} m_LocalScale: {x: 0.68, y: 0.48000002, z: 0.01} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 10 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3364774 MeshFilter: @@ -63,6 +63,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 160772} + serializedVersion: 2 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} @@ -70,9 +71,9 @@ Transform: m_Children: - {fileID: 4000012490214276} - {fileID: 3219221680988349883} - - {fileID: 1139538046860859100} - {fileID: 4626429713668410} - {fileID: 4000011596321798} + - {fileID: 1139538046860859100} - {fileID: 2468152952838901612} - {fileID: 8809421544085413876} - {fileID: 2468055462235645184} @@ -80,7 +81,6 @@ Transform: - {fileID: 4000011486688576} - {fileID: 415298} m_Father: {fileID: 485948} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &164352 GameObject: @@ -106,13 +106,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 164352} + serializedVersion: 2 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: 485948} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6531256 BoxCollider: @@ -122,9 +122,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 164352} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.9, y: 1.6, z: 0.5} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &168032 @@ -151,13 +159,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 168032} + serializedVersion: 2 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: 402684} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!65 &6547610 BoxCollider: @@ -167,9 +175,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 168032} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1.4, y: 1.1, z: 0.02} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &199434 @@ -198,6 +214,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 199434} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 1.788, y: 10, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -207,7 +224,6 @@ Transform: - {fileID: 442914} - {fileID: 402684} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &114019824221785570 MonoBehaviour: @@ -357,13 +373,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010708462674} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.415, y: 0.212, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000010418119032 MeshFilter: @@ -423,9 +439,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000010708462674} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &114000012887931942 @@ -532,13 +556,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000012292180826} + serializedVersion: 2 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: 402684} - m_RootOrder: 9 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000013181835044 MeshFilter: @@ -630,13 +654,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013682514104} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.415, y: 0.212, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000012285989520 MeshFilter: @@ -747,9 +771,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013682514104} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1000013998383226 @@ -777,13 +809,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1000013998383226} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1.5, y: 1.1, z: 2.4} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 485948} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33000011026480406 MeshFilter: @@ -862,13 +894,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1634642417305556} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0.212, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &33062622632853984 MeshFilter: @@ -928,9 +960,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1634642417305556} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.05} --- !u!114 &114328378359043972 @@ -1011,13 +1051,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2471131391549106940} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0.6149999, y: -0.2119999, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2465741338722790106 MeshFilter: @@ -1077,9 +1117,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2471131391549106940} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &2583710071636260494 @@ -1187,13 +1235,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2471663056078174956} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.215, y: -0.2119999, z: 0.05} + m_LocalPosition: {x: 0, y: -0.212, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &2466755025299766438 MeshFilter: @@ -1253,9 +1301,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2471663056078174956} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.1} m_Center: {x: -0.000000074505806, y: 0.000000022351742, z: 0} --- !u!114 &2583821116712190784 @@ -1346,13 +1402,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3217728787759546849} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -0.41499996, y: 0.2119999, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &3231233352384081475 MeshFilter: @@ -1412,9 +1468,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 3217728787759546849} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: 0} --- !u!114 &3258234186096769897 @@ -1510,12 +1574,12 @@ GameObject: - component: {fileID: 1408412152701508224} - component: {fileID: 769398838496988592} m_Layer: 16 - m_Name: PolyLibrary + m_Name: IcosaLibrary m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &1139538046860859100 Transform: m_ObjectHideFlags: 0 @@ -1523,13 +1587,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7776948797728914220} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} - m_LocalPosition: {x: -0.415, y: 0.212, z: 0.05} + m_LocalPosition: {x: -0.415, y: -0.212, z: 0.05} m_LocalScale: {x: 0.35, y: 0.35, z: 0.35} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 402684} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!33 &7705200013302925820 MeshFilter: @@ -1627,7 +1691,7 @@ MonoBehaviour: m_HoverScale: 1.1 m_HoverBoxColliderGrow: 0.2 m_AddOverlay: 0 - m_Type: 29 + m_Type: 22 m_AlwaysSpawn: 0 references: version: 2 @@ -1640,9 +1704,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7776948797728914220} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 0.01} m_Center: {x: 0, y: 0, z: -0.05} --- !u!1001 &1958996772086488720 @@ -1650,6 +1722,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 402684} m_Modifications: - target: {fileID: 3143605885771001294, guid: 6e69b3bc22681fb44ac931399886cd69, @@ -1665,7 +1738,7 @@ PrefabInstance: - target: {fileID: 7020719392467468644, guid: 6e69b3bc22681fb44ac931399886cd69, type: 3} propertyPath: m_LocalPosition.x - value: 0.19999993 + value: 0.415 objectReference: {fileID: 0} - target: {fileID: 7020719392467468644, guid: 6e69b3bc22681fb44ac931399886cd69, type: 3} @@ -1713,6 +1786,9 @@ PrefabInstance: value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 6e69b3bc22681fb44ac931399886cd69, type: 3} --- !u!4 &8809421544085413876 stripped Transform: From 2e83abcd3635404cac908b906ee0fb15e3f7913f Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 12:37:58 +0100 Subject: [PATCH 083/105] Disable m_UseLocalFeaturedSketches in the asset catalog --- Assets/Scenes/Main.unity | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 41a5d244ee..8b8622177d 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -10856,7 +10856,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_AssetsPerPage: 20 m_SketchbookRefreshInterval: 10 - m_UseLocalFeaturedSketches: 1 + m_UseLocalFeaturedSketches: 0 --- !u!114 &652605567 MonoBehaviour: m_ObjectHideFlags: 0 From e9c01c089d63b91485f07a8511ede9bbeb969fba Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 12:38:45 +0100 Subject: [PATCH 084/105] Curated also requiresIcosa now. Remove duplicate code. --- Assets/Scripts/GUI/SketchbookPanel.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Assets/Scripts/GUI/SketchbookPanel.cs b/Assets/Scripts/GUI/SketchbookPanel.cs index 31bd0b2829..ff33e9cb53 100644 --- a/Assets/Scripts/GUI/SketchbookPanel.cs +++ b/Assets/Scripts/GUI/SketchbookPanel.cs @@ -300,14 +300,12 @@ protected override void RefreshPage() // Base Refresh updates the modal parts of the panel, and we always want those refreshed. base.RefreshPage(); - bool requiresIcosa = m_CurrentSketchSet == SketchSetType.Liked; + bool requiresIcosa = m_CurrentSketchSet == SketchSetType.Liked || m_CurrentSketchSet == SketchSetType.Curated; bool requiresGoogle = m_CurrentSketchSet == SketchSetType.Drive; bool icosaDown = VrAssetService.m_Instance.NoConnection && requiresIcosa; m_NoIcosaConnectionMessage.SetActive(icosaDown); - m_NoIcosaConnectionMessage.SetActive(icosaDown); - bool outOfDate = !icosaDown && !VrAssetService.m_Instance.Available && requiresIcosa; m_OutOfDateMessage.SetActive(outOfDate); From a862b9996f73b7246708111218687bc591ceae4a Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:33:49 +0100 Subject: [PATCH 085/105] Add a comment --- Assets/Scripts/GUI/IcosaPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/GUI/IcosaPanel.cs b/Assets/Scripts/GUI/IcosaPanel.cs index 312dcb1fb3..da3371bb22 100644 --- a/Assets/Scripts/GUI/IcosaPanel.cs +++ b/Assets/Scripts/GUI/IcosaPanel.cs @@ -193,8 +193,8 @@ protected override void RefreshPage() } } - bool internetError = - App.IcosaAssetCatalog.NumCloudModels(IcosaSetType.Featured) == 0; + // Use featured model count as a proxy for "icosa is working" + bool internetError = App.IcosaAssetCatalog.NumCloudModels(IcosaSetType.Featured) == 0; m_InternetError.SetActive(internetError); RefreshPanelText(); From 10326222c977e6b60183deb65ab478b2fbf1c104 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:35:26 +0100 Subject: [PATCH 086/105] We now have assetId directly in the api results --- Assets/Scripts/Sharing/IcosaSketchSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/IcosaSketchSet.cs b/Assets/Scripts/Sharing/IcosaSketchSet.cs index f01b3186c6..d26370c945 100644 --- a/Assets/Scripts/Sharing/IcosaSketchSet.cs +++ b/Assets/Scripts/Sharing/IcosaSketchSet.cs @@ -768,7 +768,7 @@ public class IcosaSceneFileInfo : SceneFileInfo // See go/vr-assets-service-api public IcosaSceneFileInfo(JToken json) { - m_AssetId = json["name"].ToString().Substring(7); // strip 'assets/' from start + m_AssetId = json["assetId"].ToString(); // strip 'assets/' from start m_HumanName = json["displayName"].ToString(); var format = json["formats"].First(x => x["formatType"].ToString() == "TILT")["root"]; From 698ea97ab5791f72cf982183dd02fe4268355541 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:35:44 +0100 Subject: [PATCH 087/105] Better logging --- Assets/Scripts/Sharing/IcosaSketchSet.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaSketchSet.cs b/Assets/Scripts/Sharing/IcosaSketchSet.cs index d26370c945..68f8fdd443 100644 --- a/Assets/Scripts/Sharing/IcosaSketchSet.cs +++ b/Assets/Scripts/Sharing/IcosaSketchSet.cs @@ -461,9 +461,7 @@ private IEnumerator PopulateSketchesCoroutine() } if (assetIds.ContainsKey(info.AssetId)) { - Debug.Log($"Duplicate {info.HumanName} {info.AssetId}"); - Debug.LogErrorFormat("VR Asset Service has returned two objects with asset id '{0}'.", - info.AssetId); + Debug.LogWarning($"VR Asset Service has returned two objects for: {info.AssetId} {info.HumanName}"); } else { From be6f11e9a52c8ad872b05594fd4d8efcf307bd21 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:36:03 +0100 Subject: [PATCH 088/105] Remove some logging noise --- Assets/Scripts/Sharing/IcosaSketchSet.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Assets/Scripts/Sharing/IcosaSketchSet.cs b/Assets/Scripts/Sharing/IcosaSketchSet.cs index 68f8fdd443..d4a8478902 100644 --- a/Assets/Scripts/Sharing/IcosaSketchSet.cs +++ b/Assets/Scripts/Sharing/IcosaSketchSet.cs @@ -778,10 +778,6 @@ public IcosaSceneFileInfo(JToken json) // Some assets (old ones? broken ones?) are missing the "formatComplexity" field var gltfFormat = json["formats"].First(x => x["formatType"].ToString() == "GLTF"); string gltfTriCount = gltfFormat?["formatComplexity"]?["triangleCount"]?.ToString(); - if (gltfTriCount == null) - { - Debug.Log($"{m_AssetId} has no tricount"); - } m_GltfTriangleCount = Int32.Parse(gltfTriCount ?? "1"); m_DownloadedFile = null; From 426a05d89ff050933aa4b66b543c0107fe612601 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:36:25 +0100 Subject: [PATCH 089/105] Update a comment --- Assets/Scripts/Sharing/AssetLister.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index e2f2ea4c46..23cf56a401 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -114,7 +114,7 @@ public IEnumerator NextPage(List files, continue; } - // We now don't filter the liked Poly objects, but we don't want to return liked Tilt Brush + // We now don't filter the liked Icosa objects, but we don't want to return liked Tilt Brush // sketches so in this section we filter out anything with a Tilt file in it. bool skipObject = false; foreach (var format in asset["formats"]) From 33b539473b9903659d7efcda180538f755bab755 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:37:36 +0100 Subject: [PATCH 090/105] Some code prepping for dynamic switching between online/offline features sketches --- Assets/Scripts/Save/SketchCatalog.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Save/SketchCatalog.cs b/Assets/Scripts/Save/SketchCatalog.cs index e2146ad33d..cd995df4d2 100644 --- a/Assets/Scripts/Save/SketchCatalog.cs +++ b/Assets/Scripts/Save/SketchCatalog.cs @@ -58,12 +58,21 @@ void Awake() int maxTriangles = QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles; - InitFeaturedSketchesPath(); + SketchSet featuredSketchSet = null; + if (false) // TODO this fails because of initialization order: (VrAssetService.m_Instance.m_UseLocalFeaturedSketches) + { + featuredSketchSet = new FileSketchSet(App.FeaturedSketchesPath()); + InitFeaturedSketchesPath(); + } + else + { + featuredSketchSet = new IcosaSketchSet(this, SketchSetType.Curated, maxTriangles); + } m_Sets = new SketchSet[] { new FileSketchSet(), - new FileSketchSet(App.FeaturedSketchesPath()), + featuredSketchSet, new IcosaSketchSet(this, SketchSetType.Liked, maxTriangles, needsLogin: true), new GoogleDriveSketchSet(), }; From 67ff241f5f032e81ee18df42f6477f168ce1b3c3 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:38:00 +0100 Subject: [PATCH 091/105] We now have assetid available --- Assets/Scripts/Poly/IcosaAssetCatalog.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Assets/Scripts/Poly/IcosaAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs index bbab647698..5056028dcc 100644 --- a/Assets/Scripts/Poly/IcosaAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -84,9 +84,7 @@ public AssetDetails( { m_Owner = App.IcosaAssetCatalog; HumanName = json["displayName"].ToString(); - // AssetId = json["name"].ToString().Substring(7); // strip out "assets/" - // AssetId = json["ID"].ToString(); - AssetId = json["url"].ToString(); + AssetId = json["assetId"].ToString(); AccountName = accountName; var rotation = json["presentationParams"]?["orientingRotation"]; if (rotation != null) From ade568ea828bdbd2f54cbd66614186e810abbdbb Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:38:19 +0100 Subject: [PATCH 092/105] Handle missing thumbnails better --- Assets/Scripts/Poly/IcosaAssetCatalog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/Poly/IcosaAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs index 5056028dcc..5564886024 100644 --- a/Assets/Scripts/Poly/IcosaAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -102,7 +102,7 @@ public AssetDetails( } m_Thumbnail = new Texture2D(4, 4, TextureFormat.ARGB32, false); - m_ThumbnailUrl = json["thumbnail"]["url"].ToString(); + m_ThumbnailUrl = json?["thumbnail"]?["url"]?.ToString(); if (!string.IsNullOrEmpty(thumbnailSuffix)) { m_ThumbnailUrl = string.Format("{0}={1}", m_ThumbnailUrl, thumbnailSuffix); From ccfd9d30fcf14cbc783d51ef047d499783af67ee Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:39:08 +0100 Subject: [PATCH 093/105] No need to check GoogleIdentity here --- Assets/Scripts/Poly/IcosaAssetCatalog.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Poly/IcosaAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs index 5564886024..f2291f9003 100644 --- a/Assets/Scripts/Poly/IcosaAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -284,8 +284,8 @@ public bool IsLoading(string assetId) public void RequestRefresh(IcosaSetType type) { - // We don't update featured except on startup. - if (type != IcosaSetType.Featured && (App.IcosaIsLoggedIn || App.GoogleIdentity.LoggedIn)) + // We don't update featured except on startup + if (type != IcosaSetType.Featured && App.IcosaIsLoggedIn) { m_AssetSetByType[type].m_RefreshRequested = true; } From 4ab872d9f7d3132b32813d3d9c907c26a824d64d Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:44:41 +0100 Subject: [PATCH 094/105] Make user models panel the default --- Assets/Scripts/GUI/IcosaPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Scripts/GUI/IcosaPanel.cs b/Assets/Scripts/GUI/IcosaPanel.cs index da3371bb22..5bfd5a445a 100644 --- a/Assets/Scripts/GUI/IcosaPanel.cs +++ b/Assets/Scripts/GUI/IcosaPanel.cs @@ -97,7 +97,7 @@ protected override void OnStart() Icons.Add(icon); } - m_CurrentSet = IcosaSetType.Featured; + m_CurrentSet = IcosaSetType.User; // Make sure Poly gallery button starts at greyscale when panel is initialized m_PolyGalleryRenderer.material.SetFloat("_Grayscale", 1); From a7c74345c6b76882dd6c60d3e00c18ad34dcec08 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 16:45:38 +0100 Subject: [PATCH 095/105] Disable m_InternetError as well before figuring out general panel visibility (for consistency) --- Assets/Scripts/GUI/IcosaPanel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Assets/Scripts/GUI/IcosaPanel.cs b/Assets/Scripts/GUI/IcosaPanel.cs index 5bfd5a445a..8f65164fca 100644 --- a/Assets/Scripts/GUI/IcosaPanel.cs +++ b/Assets/Scripts/GUI/IcosaPanel.cs @@ -124,6 +124,7 @@ protected override void RefreshPage() m_NoLikesMessage.SetActive(false); m_NoAuthoredModelsMessage.SetActive(false); m_NotLoggedInMessage.SetActive(false); + m_InternetError.SetActive(false); if (VrAssetService.m_Instance.NoConnection) { m_NoPolyConnectionMessage.SetActive(true); From 41a7ef444a4716f47b8631c30c2b486f2b72c50e Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Thu, 12 Sep 2024 19:18:11 +0100 Subject: [PATCH 096/105] Remove debug log --- Assets/Scripts/Sharing/IcosaSketchSet.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/Scripts/Sharing/IcosaSketchSet.cs b/Assets/Scripts/Sharing/IcosaSketchSet.cs index d4a8478902..510f937572 100644 --- a/Assets/Scripts/Sharing/IcosaSketchSet.cs +++ b/Assets/Scripts/Sharing/IcosaSketchSet.cs @@ -465,7 +465,6 @@ private IEnumerator PopulateSketchesCoroutine() } else { - Debug.Log($"Adding {info.HumanName} {info.AssetId}"); sketches.Add(sketch); assetIds.Add(info.AssetId, sketch); } From 653e3a6351c5517e40e29ac1f7c8687266de148f Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 14 Sep 2024 15:22:55 +0100 Subject: [PATCH 097/105] Tweak default lister params --- Assets/Scripts/Sharing/VrAssetService.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index f45ff09a68..9a265d93b7 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -882,21 +882,26 @@ public IEnumerator InsertSketchInfo( public AssetLister ListAssets(IcosaSetType type) { - string uri = null; + string uri = IcosaApiRoot; + switch (type) { case IcosaSetType.Liked: - uri = $"{IcosaApiRoot}{kUserLikesUri}?format=BLOCKS&orderBy=LIKED_TIME&pageSize={m_AssetsPerPage}"; + uri += $"{kUserLikesUri}?orderBy=LIKED_TIME"; break; case IcosaSetType.User: - uri = $"{IcosaApiRoot}{kUserAssetsUri}?format=BLOCKS&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + uri += $"{kUserAssetsUri}?orderBy=NEWEST"; break; case IcosaSetType.Featured: - uri = $"{IcosaApiRoot}{kListAssetsUri}" + - $"?format=BLOCKS&curated=true&orderBy=NEWEST&pageSize={m_AssetsPerPage}"; + // Old way - newest curated + // uri += $"{kListAssetsUri}" + $"?curated=true&orderBy=NEWEST"; + // For now try just sorting by "best" + uri += $"{kListAssetsUri}" + $"?orderBy=BEST"; + // Something like orderBy=TRENDING would be good - BEST but weighted by recency break; } - return new AssetLister(uri, "Failed to connect to Icosa."); + uri += $"&format=GLTF2&pageSize={m_AssetsPerPage}"; + return new AssetLister(uri, errorMessage: "Failed to connect to Icosa."); } // Download a tilt file to a temporary file and load it From 97c5097461066c6fbe9388f8ac179c7fb4c415eb Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 14 Sep 2024 15:23:45 +0100 Subject: [PATCH 098/105] Limit the number of pages we retrieve --- Assets/Scripts/Sharing/AssetLister.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/AssetLister.cs b/Assets/Scripts/Sharing/AssetLister.cs index 23cf56a401..dde91ad82b 100644 --- a/Assets/Scripts/Sharing/AssetLister.cs +++ b/Assets/Scripts/Sharing/AssetLister.cs @@ -27,8 +27,9 @@ public class AssetLister private string m_Uri; private string m_ErrorMessage; private string m_PageToken; + private int m_pageLimit = 10; - public bool HasMore { get { return m_PageToken != null; } } + public bool HasMore { get { return m_PageToken != null && Int16.Parse(m_PageToken) < m_pageLimit; } } public AssetLister(string uri, string errorMessage) { From ac0121ec57820bbf116439365496383f949175ec Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 14 Sep 2024 15:24:07 +0100 Subject: [PATCH 099/105] Not sure what changed the main scene --- Assets/Scenes/Main.unity | 1 + 1 file changed, 1 insertion(+) diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity index 8b8622177d..4acc7d73da 100644 --- a/Assets/Scenes/Main.unity +++ b/Assets/Scenes/Main.unity @@ -37003,3 +37003,4 @@ SceneRoots: - {fileID: 1802399861} - {fileID: 106206546} - {fileID: 1848773840} + - {fileID: 1611595291} From 0f89153bb15f75e9d6f1bbd11522f25967a16cd7 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sat, 14 Sep 2024 15:59:12 +0100 Subject: [PATCH 100/105] Avoid errors when author is missing --- Assets/Scripts/GUI/SketchbookPanel.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/GUI/SketchbookPanel.cs b/Assets/Scripts/GUI/SketchbookPanel.cs index ff33e9cb53..8b4224479a 100644 --- a/Assets/Scripts/GUI/SketchbookPanel.cs +++ b/Assets/Scripts/GUI/SketchbookPanel.cs @@ -579,10 +579,12 @@ private void UpdateIcons() lines.Add(icon.Description); SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); - if (info is IcosaSceneFileInfo polyInfo && - polyInfo.License != VrAssetService.kCreativeCommonsLicense) + if (info is IcosaSceneFileInfo polyInfo && polyInfo.License != VrAssetService.kCreativeCommonsLicense) { - lines.Add(String.Format("© {0}", authors[0])); + if (authors != null && authors.Length > 0) + { + lines.Add($"© {authors[0]}"); + } lines.Add("All Rights Reserved"); } else From 193bb31f56093bc4160e0c3feeeab7f60b0228d2 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 15 Sep 2024 16:21:36 +0100 Subject: [PATCH 101/105] Log a warning if we can't find a GLTF2 --- Assets/Scripts/Sharing/AssetGetter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Assets/Scripts/Sharing/AssetGetter.cs b/Assets/Scripts/Sharing/AssetGetter.cs index 0baa2827c4..9594ff06e0 100644 --- a/Assets/Scripts/Sharing/AssetGetter.cs +++ b/Assets/Scripts/Sharing/AssetGetter.cs @@ -175,6 +175,7 @@ public IEnumerator GetAssetCoroutine() // In some cases, we should look for a different format as backup. if (requestedType == VrAssetFormat.GLTF2) { + Debug.LogWarning($"No GLTF2 format found for {m_Asset.Id}. Trying GLTF1."); requestedType = VrAssetFormat.GLTF; } else From 60e864c14dc5656d12ac143882dae5ba8f97be6f Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Sun, 15 Sep 2024 16:22:21 +0100 Subject: [PATCH 102/105] Sort featured sketches by "best" --- Assets/Scripts/Sharing/VrAssetService.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 9a265d93b7..0bf9d3e8fa 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -841,7 +841,10 @@ public AssetLister ListAssets(SketchSetType type) errorMessage = "Failed to access your liked sketches."; break; case SketchSetType.Curated: - filteredUriPath = CombineQueryParams(kListAssetsUri, "format=TILT&curated=true&orderBy=NEWEST"); + // Old way - newest curated + // filteredUriPath = CombineQueryParams(kListAssetsUri, "format=TILT&curated=true&orderBy=NEWEST"); + // For now try just sorting by "best" + filteredUriPath = CombineQueryParams(kListAssetsUri, "format=TILT&orderBy=BEST"); errorMessage = "Failed to access featured sketches."; break; } From cb24340c5ec0663b1789ddca218071a022c4bf64 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 25 Sep 2024 17:21:48 +0100 Subject: [PATCH 103/105] Update packages-lock.json --- Packages/packages-lock.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index eb5486d154..962c99dd08 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -50,11 +50,11 @@ "depth": 1, "source": "registry", "dependencies": { - "com.unity.scriptablebuildpipeline": "1.21.23", "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.scriptablebuildpipeline": "1.21.23", "com.unity.modules.unitywebrequestassetbundle": "1.0.0" }, "url": "https://packages.unity.com" @@ -84,10 +84,10 @@ "depth": 0, "source": "registry", "dependencies": { - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.burst": "1.8.4", "com.unity.mathematics": "1.3.1", - "com.unity.burst": "1.8.4" + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" }, "url": "https://packages.unity.com" }, @@ -172,8 +172,8 @@ "depth": 0, "source": "registry", "dependencies": { - "com.unity.mathematics": "1.2.1", - "com.unity.burst": "1.4.11" + "com.unity.burst": "1.4.11", + "com.unity.mathematics": "1.2.1" }, "url": "https://packages.unity.com" }, @@ -273,9 +273,9 @@ "depth": 0, "source": "registry", "dependencies": { + "com.unity.modules.audio": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.animation": "1.0.0", - "com.unity.modules.audio": "1.0.0", "com.unity.modules.particlesystem": "1.0.0" }, "url": "https://packages.unity.com" @@ -332,9 +332,9 @@ "depth": 0, "source": "registry", "dependencies": { - "com.unity.modules.subsystems": "1.0.0", "com.unity.modules.vr": "1.0.0", "com.unity.modules.xr": "1.0.0", + "com.unity.modules.subsystems": "1.0.0", "com.unity.xr.legacyinputhelpers": "2.1.7" }, "url": "https://packages.unity.com" @@ -353,10 +353,10 @@ "depth": 0, "source": "registry", "dependencies": { - "com.unity.xr.management": "4.4.0", - "com.unity.xr.legacyinputhelpers": "2.1.2", "com.unity.inputsystem": "1.6.3", - "com.unity.xr.core-utils": "2.1.1" + "com.unity.xr.core-utils": "2.1.1", + "com.unity.xr.management": "4.4.0", + "com.unity.xr.legacyinputhelpers": "2.1.2" }, "url": "https://packages.unity.com" }, @@ -380,7 +380,7 @@ "com.unity.shadergraph": "10.0.0", "com.unity.mathematics": "1.0.0" }, - "hash": "0dba6d0dde6177c83039b3e1e6153889da40d257" + "hash": "8427244b73c19eb121ca59dc98efacb732301712" }, "org.nuget.google.apis": { "version": "1.64.0", From f0b4547da9d670626c10517163a3156caa2c2d65 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 25 Sep 2024 21:41:59 +0100 Subject: [PATCH 104/105] Comment --- Assets/Scripts/Sharing/VrAssetService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/Scripts/Sharing/VrAssetService.cs b/Assets/Scripts/Sharing/VrAssetService.cs index 0bf9d3e8fa..24546e0557 100644 --- a/Assets/Scripts/Sharing/VrAssetService.cs +++ b/Assets/Scripts/Sharing/VrAssetService.cs @@ -832,6 +832,8 @@ public AssetLister ListAssets(SketchSetType type) string errorMessage = null; switch (type) { + // TODO Add User sketches + // TODO Allow non-CC-BY sketches to be loaded as read-only case SketchSetType.Liked: if (!App.IcosaIsLoggedIn) { From 08bb3d44cd8c447b25494a18b39e81f6f5931704 Mon Sep 17 00:00:00 2001 From: Andy Baker Date: Wed, 25 Sep 2024 21:43:10 +0100 Subject: [PATCH 105/105] Comment --- Assets/Scripts/Poly/IcosaAssetCatalog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Assets/Scripts/Poly/IcosaAssetCatalog.cs b/Assets/Scripts/Poly/IcosaAssetCatalog.cs index f2291f9003..6aa2570765 100644 --- a/Assets/Scripts/Poly/IcosaAssetCatalog.cs +++ b/Assets/Scripts/Poly/IcosaAssetCatalog.cs @@ -825,6 +825,7 @@ void LoadModelsInQueueAsync() { Model model = m_LoadQueue[i].Model; model.LoadModel(); + // TODO Back to async loading m_LoadQueue.RemoveAt(i); m_IsLoadingMemo = null; m_NotifyListeners = true;