From a134330ad1736cedfc56ff32692495cd682500f5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Mon, 23 Sep 2024 23:00:28 +1000 Subject: [PATCH 01/16] Basic Unity height query API. --- Reinterop~/MethodsImplementedInCpp.cs | 10 +-- Runtime/Cesium3DTileset.cs | 15 ++++ Runtime/ConfigureReinterop.cs | 15 ++++ Tests/TestCesium3DTileset.cs | 73 +++++++++++++++++++ Tests/TestCesium3DTileset.cs.meta | 11 +++ native~/Runtime/src/Cesium3DTilesetImpl.cpp | 77 +++++++++++++++++++++ native~/Runtime/src/Cesium3DTilesetImpl.h | 13 ++++ native~/extern/cesium-native | 2 +- 8 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 Tests/TestCesium3DTileset.cs create mode 100644 Tests/TestCesium3DTileset.cs.meta diff --git a/Reinterop~/MethodsImplementedInCpp.cs b/Reinterop~/MethodsImplementedInCpp.cs index 5493a77d..807f3d0d 100644 --- a/Reinterop~/MethodsImplementedInCpp.cs +++ b/Reinterop~/MethodsImplementedInCpp.cs @@ -344,7 +344,7 @@ private static void GenerateMethod(CppGenerationContext context, TypeToGenerate CSharpType csWrapperType = CSharpType.FromSymbol(context, item.Type); CSharpType csReturnType = CSharpType.FromSymbol(context, method.ReturnType); - var csParameters = method.Parameters.Select(parameter => (Name: parameter.Name, CallName: parameter.Name, Type: CSharpType.FromSymbol(context, parameter.Type))); + var csParameters = method.Parameters.Select(parameter => (Name: parameter.Name, CallName: parameter.Name, Type: CSharpType.FromSymbol(context, parameter.Type), IsParams: parameter.IsParams)); var csParametersInterop = csParameters; var implementationPointer = new CSharpType(context, InteropTypeKind.Primitive, csWrapperType.Namespaces, csWrapperType.Name + ".ImplementationHandle", csWrapperType.SpecialType, null); @@ -354,13 +354,13 @@ private static void GenerateMethod(CppGenerationContext context, TypeToGenerate { csParametersInterop = new[] { - (Name: "implementation", CallName: "_implementation", Type: implementationPointer) + (Name: "implementation", CallName: "_implementation", Type: implementationPointer, IsParams: false) }.Concat(csParametersInterop); } csParametersInterop = new[] { - (Name: "thiz", CallName: "this", Type: csWrapperType), + (Name: "thiz", CallName: "this", Type: csWrapperType, IsParams: false), }.Concat(csParametersInterop); } @@ -370,7 +370,7 @@ private static void GenerateMethod(CppGenerationContext context, TypeToGenerate { csParametersInterop = csParametersInterop.Concat(new[] { - (Name: "pReturnValue", CallName: "&returnValue", Type: csInteropReturnType.AsPointer()) + (Name : "pReturnValue", CallName : "&returnValue", Type : csInteropReturnType.AsPointer(), IsParams : false) }); csInteropReturnType = CSharpType.FromSymbol(context, returnType.Kind == InteropTypeKind.Nullable ? context.Compilation.GetSpecialType(SpecialType.System_Byte) : context.Compilation.GetSpecialType(SpecialType.System_Void)); } @@ -429,7 +429,7 @@ private static void GenerateMethod(CppGenerationContext context, TypeToGenerate result.CSharpPartialMethodDefinitions.Methods.Add(new( methodDefinition: $$""" - {{modifiers}} partial {{csReturnType.GetFullyQualifiedName()}} {{method.Name}}({{string.Join(", ", csParameters.Select(parameter => $"{parameter.Type.GetFullyQualifiedName()} {parameter.Name}"))}}) + {{modifiers}} partial {{csReturnType.GetFullyQualifiedName()}} {{method.Name}}({{string.Join(", ", csParameters.Select(parameter => $"{(parameter.IsParams ? "params " : "")}{parameter.Type.GetFullyQualifiedName()} {parameter.Name}"))}}) { unsafe { diff --git a/Runtime/Cesium3DTileset.cs b/Runtime/Cesium3DTileset.cs index 80bf9a4a..c46f39d2 100644 --- a/Runtime/Cesium3DTileset.cs +++ b/Runtime/Cesium3DTileset.cs @@ -1,5 +1,8 @@ using Reinterop; using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Unity.Mathematics; using UnityEngine; namespace CesiumForUnity @@ -20,6 +23,13 @@ public enum CesiumDataSource FromUrl } + public class SampleHeightResult + { + public double3[] longitudeLatitudeHeightPositions { get; set; } + public bool[] heightSampled { get; set; } + public string[] warnings { get; set; } + } + /// /// A tileset in the 3D Tiles format. 3D Tiles /// is an open specification for sharing, visualizing, fusing, and interacting with massive @@ -714,6 +724,8 @@ public bool createPhysicsMeshes /// public partial void FocusTileset(); + public partial Task SampleHeightMostDetailed(params double3[] longitudeLatitudeHeightPositions); + #endregion #region Private Methods @@ -731,6 +743,8 @@ public bool createPhysicsMeshes #endregion + #region Backward Compatibility + void ISerializationCallbackReceiver.OnBeforeSerialize() { } @@ -748,5 +762,6 @@ void ISerializationCallbackReceiver.OnAfterDeserialize() #if UNITY_EDITOR private bool _useDefaultServer = false; #endif + #endregion } } diff --git a/Runtime/ConfigureReinterop.cs b/Runtime/ConfigureReinterop.cs index 5c536437..25628c8e 100644 --- a/Runtime/ConfigureReinterop.cs +++ b/Runtime/ConfigureReinterop.cs @@ -890,6 +890,21 @@ Cesium3DTilesetLoadFailureDetails tilesetDetails camera = manager.additionalCameras[i]; } + TaskCompletionSource promise = new TaskCompletionSource(); + promise.SetException(new Exception("message")); + SampleHeightResult result = new SampleHeightResult(); + result.longitudeLatitudeHeightPositions = null; + result.heightSampled = null; + result.warnings = null; + promise.SetResult(result); + Task task = promise.Task; + + double3[] positions = null; + for (int i = 0; i < positions.Length; ++i) + { + positions[i] = positions[i]; + } + #if UNITY_EDITOR SceneView sv = SceneView.lastActiveSceneView; sv.pivot = sv.pivot; diff --git a/Tests/TestCesium3DTileset.cs b/Tests/TestCesium3DTileset.cs new file mode 100644 index 00000000..639d4b70 --- /dev/null +++ b/Tests/TestCesium3DTileset.cs @@ -0,0 +1,73 @@ +using CesiumForUnity; +using NUnit.Framework; +using System; +using System.Collections; +using System.Threading.Tasks; +using Unity.Mathematics; +using UnityEngine; +using UnityEngine.TestTools; + +public class TestCesium3DTileset +{ + [UnityTest] + public IEnumerator SampleHeightMostDetailedWorksWithAnEmptyArrayOfPositions() + { + GameObject go = new GameObject(); + go.name = "Cesium World Terrain"; + Cesium3DTileset tileset = go.AddComponent(); + tileset.ionAssetID = 1; + + // TODO: remove this + yield return null; + + Task task = tileset.SampleHeightMostDetailed(); + + while (!task.IsCompleted) + { + yield return null; + } + + SampleHeightResult result = task.Result; + Assert.IsNotNull(result); + Assert.IsNotNull(result.longitudeLatitudeHeightPositions); + Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.warnings); + Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 0); + Assert.AreEqual(result.heightSampled.Length, 0); + Assert.AreEqual(result.warnings.Length, 0); + } + + [UnityTest] + public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() + { + GameObject go = new GameObject(); + go.name = "Cesium World Terrain"; + Cesium3DTileset tileset = go.AddComponent(); + tileset.ionAssetID = 1; + + // TODO: remove this + yield return null; + + Task task = tileset.SampleHeightMostDetailed(new double3(-105.1, 40.1, 1.0)); + + while (!task.IsCompleted) + { + yield return null; + } + + SampleHeightResult result = task.Result; + Assert.IsNotNull(result); + Assert.IsNotNull(result.longitudeLatitudeHeightPositions); + Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.warnings); + Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 1); + Assert.AreEqual(result.heightSampled.Length, 1); + Assert.AreEqual(result.warnings.Length, 0); + + Assert.AreEqual(result.heightSampled[0], true); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].x, -105.1, 1e-12); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].y, 40.1, 1e-12); + // Returned height should be different from the original height (1.0) by at least one meter. + Assert.IsTrue(math.abs(result.longitudeLatitudeHeightPositions[0].z - 1.0) > 1.0); + } +} \ No newline at end of file diff --git a/Tests/TestCesium3DTileset.cs.meta b/Tests/TestCesium3DTileset.cs.meta new file mode 100644 index 00000000..2f4f2a12 --- /dev/null +++ b/Tests/TestCesium3DTileset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b05a8ea3c2209fe4d8ccecb0dd98ce71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.cpp b/native~/Runtime/src/Cesium3DTilesetImpl.cpp index 7df9cff5..b61ca4d3 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.cpp +++ b/native~/Runtime/src/Cesium3DTilesetImpl.cpp @@ -22,8 +22,13 @@ #include #include #include +#include +#include #include #include +#include +#include +#include #include #include #include @@ -381,6 +386,78 @@ float Cesium3DTilesetImpl::ComputeLoadProgress( return getTileset()->computeLoadProgress(); } +System::Threading::Tasks::Task1 +Cesium3DTilesetImpl::SampleHeightMostDetailed( + const CesiumForUnity::Cesium3DTileset& tileset, + const System::Array1& + longitudeLatitudeHeightPositions) { + System::Threading::Tasks::TaskCompletionSource1< + CesiumForUnity::SampleHeightResult> + promise{}; + + Tileset* pTileset = this->getTileset(); + if (pTileset == nullptr) { + // TODO: wait a tick for the tileset to be created. + promise.SetException( + System::Exception(System::String("Tileset not created yet."))); + return promise.Task(); + } + + std::vector positions; + positions.reserve(longitudeLatitudeHeightPositions.Length()); + + for (int32_t i = 0, len = longitudeLatitudeHeightPositions.Length(); i < len; + ++i) { + Unity::Mathematics::double3 position = longitudeLatitudeHeightPositions[i]; + positions.emplace_back(CesiumGeospatial::Cartographic::fromDegrees( + position.x, + position.y, + position.z)); + } + + pTileset->sampleHeightMostDetailed(positions) + .thenImmediately( + [promise](Cesium3DTilesSelection::SampleHeightResult&& result) { + System::Array1 positions( + result.positions.size()); + for (size_t i = 0; i < result.positions.size(); ++i) { + const CesiumGeospatial::Cartographic& positionRadians = + result.positions[i]; + positions.Item( + i, + Unity::Mathematics::double3{ + CesiumUtility::Math::radiansToDegrees( + positionRadians.longitude), + CesiumUtility::Math::radiansToDegrees( + positionRadians.latitude), + positionRadians.height}); + } + + System::Array1 heightSampled(result.heightSampled.size()); + for (size_t i = 0; i < result.heightSampled.size(); ++i) { + heightSampled.Item(i, result.heightSampled[i]); + } + + System::Array1 warnings(result.warnings.size()); + for (size_t i = 0; i < result.warnings.size(); ++i) { + warnings.Item(i, System::String(result.warnings[i])); + } + + CesiumForUnity::SampleHeightResult unityResult; + unityResult.longitudeLatitudeHeightPositions(positions); + unityResult.heightSampled(heightSampled); + unityResult.warnings(warnings); + + promise.SetResult(unityResult); + }) + .catchImmediately([promise](std::exception&& exception) { + promise.SetException( + System::Exception(System::String(exception.what()))); + }); + + return promise.Task(); +} + Tileset* Cesium3DTilesetImpl::getTileset() { return this->_pTileset.get(); } const Tileset* Cesium3DTilesetImpl::getTileset() const { diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.h b/native~/Runtime/src/Cesium3DTilesetImpl.h index 5ada3e23..0c55590b 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.h +++ b/native~/Runtime/src/Cesium3DTilesetImpl.h @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -19,8 +20,13 @@ namespace DotNet::CesiumForUnity { class Cesium3DTileset; class CesiumRasterOverlay; +class SampleHeightResult; } // namespace DotNet::CesiumForUnity +namespace DotNet::Unity::Mathematics { +struct double3; +} + namespace Cesium3DTilesSelection { class Tileset; } @@ -49,6 +55,13 @@ class Cesium3DTilesetImpl : public CesiumImpl { float ComputeLoadProgress(const DotNet::CesiumForUnity::Cesium3DTileset& tileset); + DotNet::System::Threading::Tasks::Task1< + DotNet::CesiumForUnity::SampleHeightResult> + SampleHeightMostDetailed( + const DotNet::CesiumForUnity::Cesium3DTileset& tileset, + const DotNet::System::Array1& + longitudeLatitudeHeightPositions); + Cesium3DTilesSelection::Tileset* getTileset(); const Cesium3DTilesSelection::Tileset* getTileset() const; diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index f64fa6af..bc0770a1 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit f64fa6af9b067c631b6c71b7c7a8a39eaeeec1f2 +Subproject commit bc0770a146e3f2a52e10c17ac19c9a7bff454327 From ddffb0c4a5732f3145d7a0dada3761f88e13bbc0 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 07:49:10 +1000 Subject: [PATCH 02/16] Update cesium-native to fix a test hang. It will still fail, though, because of lack of a valid ion token while running the tests. --- native~/extern/cesium-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index bc0770a1..4850751a 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit bc0770a146e3f2a52e10c17ac19c9a7bff454327 +Subproject commit 4850751a25c3cc95e8b9458a904b043d98bbd284 From d40bdffa15c71d1a5691b335a25a5683e1a32d61 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 08:31:06 +1000 Subject: [PATCH 03/16] Use ion token on CI, add more tests. --- Tests/TestCesium3DTileset.cs | 80 ++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/Tests/TestCesium3DTileset.cs b/Tests/TestCesium3DTileset.cs index 639d4b70..fa22c4f8 100644 --- a/Tests/TestCesium3DTileset.cs +++ b/Tests/TestCesium3DTileset.cs @@ -15,6 +15,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithAnEmptyArrayOfPositions() GameObject go = new GameObject(); go.name = "Cesium World Terrain"; Cesium3DTileset tileset = go.AddComponent(); + tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; tileset.ionAssetID = 1; // TODO: remove this @@ -43,6 +44,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() GameObject go = new GameObject(); go.name = "Cesium World Terrain"; Cesium3DTileset tileset = go.AddComponent(); + tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; tileset.ionAssetID = 1; // TODO: remove this @@ -70,4 +72,82 @@ public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() // Returned height should be different from the original height (1.0) by at least one meter. Assert.IsTrue(math.abs(result.longitudeLatitudeHeightPositions[0].z - 1.0) > 1.0); } + + [UnityTest] + public IEnumerator SampleHeightMostDetailedWorksWithMultiplePositions() + { + GameObject go = new GameObject(); + go.name = "Cesium World Terrain"; + Cesium3DTileset tileset = go.AddComponent(); + tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; + tileset.ionAssetID = 1; + + // TODO: remove this + yield return null; + + Task task = tileset.SampleHeightMostDetailed( + new double3(-105.1, 40.1, 1.0), + new double3(105.1, -40.1, 1.0)); + + while (!task.IsCompleted) + { + yield return null; + } + + SampleHeightResult result = task.Result; + Assert.IsNotNull(result); + Assert.IsNotNull(result.longitudeLatitudeHeightPositions); + Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.warnings); + Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 2); + Assert.AreEqual(result.heightSampled.Length, 2); + Assert.AreEqual(result.warnings.Length, 0); + + Assert.AreEqual(result.heightSampled[0], true); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].x, -105.1, 1e-12); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].y, 40.1, 1e-12); + // Returned height should be different from the original height (1.0) by at least one meter. + Assert.IsTrue(math.abs(result.longitudeLatitudeHeightPositions[0].z - 1.0) > 1.0); + + Assert.AreEqual(result.heightSampled[1], true); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[1].x, 105.1, 1e-12); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[1].y, -40.1, 1e-12); + // Returned height should be different from the original height (1.0) by at least one meter. + Assert.IsTrue(math.abs(result.longitudeLatitudeHeightPositions[1].z - 1.0) > 1.0); + } + + [UnityTest] + public IEnumerator SampleHeightMostDetailedIndicatesNotSampledForPositionOutsideTileset() + { + GameObject go = new GameObject(); + go.name = "Melbourne Photogrammetry"; + Cesium3DTileset tileset = go.AddComponent(); + tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; + tileset.ionAssetID = 69380; + + // TODO: remove this + yield return null; + + // Somewhere in Sydney, not Melbourne + Task task = tileset.SampleHeightMostDetailed(new double3(151.20972, -33.87100, 1.0)); + + while (!task.IsCompleted) + { + yield return null; + } + + SampleHeightResult result = task.Result; + Assert.IsNotNull(result); + Assert.IsNotNull(result.longitudeLatitudeHeightPositions); + Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.warnings); + Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 1); + Assert.AreEqual(result.heightSampled.Length, 1); + Assert.AreEqual(result.warnings.Length, 0); + + Assert.AreEqual(result.heightSampled[0], false); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].x, 151.20972, 1e-12); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].y, -33.87100, 1e-12); + Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].z, 1.0, 1e-12); + } } \ No newline at end of file From 50bf57afdd0fb9a92604e1b9ebb0340172fac5ca Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 09:04:43 +1000 Subject: [PATCH 04/16] SampleHeightResult -> CesiumSampleHeightResult --- Runtime/Cesium3DTileset.cs | 10 ++-------- Runtime/CesiumSampleHeightResult.cs | 11 +++++++++++ Runtime/CesiumSampleHeightResult.cs.meta | 11 +++++++++++ Runtime/ConfigureReinterop.cs | 6 +++--- Tests/TestCesium3DTileset.cs | 16 ++++++++-------- native~/Runtime/src/Cesium3DTilesetImpl.cpp | 8 ++++---- native~/Runtime/src/Cesium3DTilesetImpl.h | 4 ++-- 7 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 Runtime/CesiumSampleHeightResult.cs create mode 100644 Runtime/CesiumSampleHeightResult.cs.meta diff --git a/Runtime/Cesium3DTileset.cs b/Runtime/Cesium3DTileset.cs index c46f39d2..c3f9a920 100644 --- a/Runtime/Cesium3DTileset.cs +++ b/Runtime/Cesium3DTileset.cs @@ -1,5 +1,6 @@ using Reinterop; using System; +using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using Unity.Mathematics; @@ -23,13 +24,6 @@ public enum CesiumDataSource FromUrl } - public class SampleHeightResult - { - public double3[] longitudeLatitudeHeightPositions { get; set; } - public bool[] heightSampled { get; set; } - public string[] warnings { get; set; } - } - /// /// A tileset in the 3D Tiles format. 3D Tiles /// is an open specification for sharing, visualizing, fusing, and interacting with massive @@ -724,7 +718,7 @@ public bool createPhysicsMeshes /// public partial void FocusTileset(); - public partial Task SampleHeightMostDetailed(params double3[] longitudeLatitudeHeightPositions); + public partial Task SampleHeightMostDetailed(params double3[] longitudeLatitudeHeightPositions); #endregion diff --git a/Runtime/CesiumSampleHeightResult.cs b/Runtime/CesiumSampleHeightResult.cs new file mode 100644 index 00000000..e1808c8b --- /dev/null +++ b/Runtime/CesiumSampleHeightResult.cs @@ -0,0 +1,11 @@ +using Unity.Mathematics; + +namespace CesiumForUnity +{ + public class CesiumSampleHeightResult + { + public double3[] longitudeLatitudeHeightPositions { get; set; } + public bool[] heightSampled { get; set; } + public string[] warnings { get; set; } + } +} diff --git a/Runtime/CesiumSampleHeightResult.cs.meta b/Runtime/CesiumSampleHeightResult.cs.meta new file mode 100644 index 00000000..1b1fda83 --- /dev/null +++ b/Runtime/CesiumSampleHeightResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 153f2af389348c149a9695eeed7bb016 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/ConfigureReinterop.cs b/Runtime/ConfigureReinterop.cs index 25628c8e..1bbe21ac 100644 --- a/Runtime/ConfigureReinterop.cs +++ b/Runtime/ConfigureReinterop.cs @@ -890,14 +890,14 @@ Cesium3DTilesetLoadFailureDetails tilesetDetails camera = manager.additionalCameras[i]; } - TaskCompletionSource promise = new TaskCompletionSource(); + TaskCompletionSource promise = new TaskCompletionSource(); promise.SetException(new Exception("message")); - SampleHeightResult result = new SampleHeightResult(); + CesiumSampleHeightResult result = new CesiumSampleHeightResult(); result.longitudeLatitudeHeightPositions = null; result.heightSampled = null; result.warnings = null; promise.SetResult(result); - Task task = promise.Task; + Task task = promise.Task; double3[] positions = null; for (int i = 0; i < positions.Length; ++i) diff --git a/Tests/TestCesium3DTileset.cs b/Tests/TestCesium3DTileset.cs index fa22c4f8..9cab8e47 100644 --- a/Tests/TestCesium3DTileset.cs +++ b/Tests/TestCesium3DTileset.cs @@ -21,14 +21,14 @@ public IEnumerator SampleHeightMostDetailedWorksWithAnEmptyArrayOfPositions() // TODO: remove this yield return null; - Task task = tileset.SampleHeightMostDetailed(); + Task task = tileset.SampleHeightMostDetailed(); while (!task.IsCompleted) { yield return null; } - SampleHeightResult result = task.Result; + CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); Assert.IsNotNull(result.heightSampled); @@ -50,14 +50,14 @@ public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() // TODO: remove this yield return null; - Task task = tileset.SampleHeightMostDetailed(new double3(-105.1, 40.1, 1.0)); + Task task = tileset.SampleHeightMostDetailed(new double3(-105.1, 40.1, 1.0)); while (!task.IsCompleted) { yield return null; } - SampleHeightResult result = task.Result; + CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); Assert.IsNotNull(result.heightSampled); @@ -85,7 +85,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithMultiplePositions() // TODO: remove this yield return null; - Task task = tileset.SampleHeightMostDetailed( + Task task = tileset.SampleHeightMostDetailed( new double3(-105.1, 40.1, 1.0), new double3(105.1, -40.1, 1.0)); @@ -94,7 +94,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithMultiplePositions() yield return null; } - SampleHeightResult result = task.Result; + CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); Assert.IsNotNull(result.heightSampled); @@ -129,14 +129,14 @@ public IEnumerator SampleHeightMostDetailedIndicatesNotSampledForPositionOutside yield return null; // Somewhere in Sydney, not Melbourne - Task task = tileset.SampleHeightMostDetailed(new double3(151.20972, -33.87100, 1.0)); + Task task = tileset.SampleHeightMostDetailed(new double3(151.20972, -33.87100, 1.0)); while (!task.IsCompleted) { yield return null; } - SampleHeightResult result = task.Result; + CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); Assert.IsNotNull(result.heightSampled); diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.cpp b/native~/Runtime/src/Cesium3DTilesetImpl.cpp index b61ca4d3..d9a017f8 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.cpp +++ b/native~/Runtime/src/Cesium3DTilesetImpl.cpp @@ -21,8 +21,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -386,13 +386,13 @@ float Cesium3DTilesetImpl::ComputeLoadProgress( return getTileset()->computeLoadProgress(); } -System::Threading::Tasks::Task1 +System::Threading::Tasks::Task1 Cesium3DTilesetImpl::SampleHeightMostDetailed( const CesiumForUnity::Cesium3DTileset& tileset, const System::Array1& longitudeLatitudeHeightPositions) { System::Threading::Tasks::TaskCompletionSource1< - CesiumForUnity::SampleHeightResult> + CesiumForUnity::CesiumSampleHeightResult> promise{}; Tileset* pTileset = this->getTileset(); @@ -443,7 +443,7 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( warnings.Item(i, System::String(result.warnings[i])); } - CesiumForUnity::SampleHeightResult unityResult; + CesiumForUnity::CesiumSampleHeightResult unityResult; unityResult.longitudeLatitudeHeightPositions(positions); unityResult.heightSampled(heightSampled); unityResult.warnings(warnings); diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.h b/native~/Runtime/src/Cesium3DTilesetImpl.h index 0c55590b..f980b780 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.h +++ b/native~/Runtime/src/Cesium3DTilesetImpl.h @@ -20,7 +20,7 @@ namespace DotNet::CesiumForUnity { class Cesium3DTileset; class CesiumRasterOverlay; -class SampleHeightResult; +class CesiumSampleHeightResult; } // namespace DotNet::CesiumForUnity namespace DotNet::Unity::Mathematics { @@ -56,7 +56,7 @@ class Cesium3DTilesetImpl : public CesiumImpl { ComputeLoadProgress(const DotNet::CesiumForUnity::Cesium3DTileset& tileset); DotNet::System::Threading::Tasks::Task1< - DotNet::CesiumForUnity::SampleHeightResult> + DotNet::CesiumForUnity::CesiumSampleHeightResult> SampleHeightMostDetailed( const DotNet::CesiumForUnity::Cesium3DTileset& tileset, const DotNet::System::Array1& From c741e27ef1f4810df4ffc0a17eb2de23a2873861 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 09:39:37 +1000 Subject: [PATCH 05/16] Add WaitForTask. --- Runtime/WaitForTask.cs | 25 +++++++++++++++++++++++++ Runtime/WaitForTask.cs.meta | 11 +++++++++++ Tests/TestCesium3DTileset.cs | 20 ++++---------------- 3 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 Runtime/WaitForTask.cs create mode 100644 Runtime/WaitForTask.cs.meta diff --git a/Runtime/WaitForTask.cs b/Runtime/WaitForTask.cs new file mode 100644 index 00000000..94a94efa --- /dev/null +++ b/Runtime/WaitForTask.cs @@ -0,0 +1,25 @@ +using System; +using UnityEngine; + +namespace CesiumForUnity +{ + /// + /// A YieldInstruction that can be yielded from a coroutine in order to wait + /// until a given task completes. + /// + public class WaitForTask : CustomYieldInstruction + { + private IAsyncResult _task; + + /// + /// Initializes a new instance. + /// + /// The task to wait for. + public WaitForTask(IAsyncResult task) + { + this._task = task; + } + + public override bool keepWaiting => !this._task.IsCompleted; + } +} diff --git a/Runtime/WaitForTask.cs.meta b/Runtime/WaitForTask.cs.meta new file mode 100644 index 00000000..60234ff5 --- /dev/null +++ b/Runtime/WaitForTask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ca29eff4c876de44b957b0121d454856 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/TestCesium3DTileset.cs b/Tests/TestCesium3DTileset.cs index 9cab8e47..bb25b82a 100644 --- a/Tests/TestCesium3DTileset.cs +++ b/Tests/TestCesium3DTileset.cs @@ -23,10 +23,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithAnEmptyArrayOfPositions() Task task = tileset.SampleHeightMostDetailed(); - while (!task.IsCompleted) - { - yield return null; - } + yield return new WaitForTask(task); CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); @@ -52,10 +49,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() Task task = tileset.SampleHeightMostDetailed(new double3(-105.1, 40.1, 1.0)); - while (!task.IsCompleted) - { - yield return null; - } + yield return new WaitForTask(task); CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); @@ -89,10 +83,7 @@ public IEnumerator SampleHeightMostDetailedWorksWithMultiplePositions() new double3(-105.1, 40.1, 1.0), new double3(105.1, -40.1, 1.0)); - while (!task.IsCompleted) - { - yield return null; - } + yield return new WaitForTask(task); CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); @@ -131,10 +122,7 @@ public IEnumerator SampleHeightMostDetailedIndicatesNotSampledForPositionOutside // Somewhere in Sydney, not Melbourne Task task = tileset.SampleHeightMostDetailed(new double3(151.20972, -33.87100, 1.0)); - while (!task.IsCompleted) - { - yield return null; - } + yield return new WaitForTask(task); CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); From 782a96648fb8ce59ee74f20b7b7b1d1b3a134779 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 09:59:59 +1000 Subject: [PATCH 06/16] Add documentation. --- Runtime/Cesium3DTileset.cs | 21 +++++++++++++++++ Runtime/CesiumSampleHeightResult.cs | 36 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/Runtime/Cesium3DTileset.cs b/Runtime/Cesium3DTileset.cs index c3f9a920..a3a17aee 100644 --- a/Runtime/Cesium3DTileset.cs +++ b/Runtime/Cesium3DTileset.cs @@ -718,6 +718,27 @@ public bool createPhysicsMeshes /// public partial void FocusTileset(); + /// + /// Initiates an asynchronous query for the height of this tileset at a list of positions, + /// expressed as longitude and latitude. The most detailed available tiles are used to + /// determine each height. + /// + /// + /// + /// The height of the input positions is ignored. On output, the height is + /// expressed in meters above the ellipsoid (usually WGS84), which should not + /// be confused with a height above mean sea level. + /// + /// + /// Use inside a coroutine to wait for the asynchronous height + /// query to complete. + /// + /// + /// + /// The positions for which to sample heights. The X component is the Longitude (degrees), + /// the Y component is the Latitude (degrees), and the Z component is the Height (meters). + /// + /// An asynchronous task that will provide the requested heights when complete. public partial Task SampleHeightMostDetailed(params double3[] longitudeLatitudeHeightPositions); #endregion diff --git a/Runtime/CesiumSampleHeightResult.cs b/Runtime/CesiumSampleHeightResult.cs index e1808c8b..590bd33c 100644 --- a/Runtime/CesiumSampleHeightResult.cs +++ b/Runtime/CesiumSampleHeightResult.cs @@ -2,10 +2,46 @@ namespace CesiumForUnity { + /// + /// The asynchronous result of a call to . + /// public class CesiumSampleHeightResult { + /// + /// The positions and sampled heights. The X component is Longitude (degrees), the + /// Y component is Latitude (degrees), and the Z component is Height (meters) above + /// the ellipsoid (usually WGS84). + /// + /// + /// + /// The longitudes and latitudes will match the values at the same index in the + /// original input positions. Each height will either be the height sampled + /// from the tileset at that position, or the original input height if the + /// height could not be sampled. To determine which, look at the value of + /// at the same index. + /// + /// + /// The returned height is is measured from the ellipsoid, which is usually WGS84. + /// It should not be confused with a height about Mean Sea Level. + /// + /// public double3[] longitudeLatitudeHeightPositions { get; set; } + + /// + /// Specifies whether the height for the position at this index was sampled successfully. + /// + /// + /// If true, has + /// a valid height sampled from the tileset at this index. If false, the height + /// could not be sampled for the position at this index, and so the height in + /// is unchanged + /// from the original input height. + /// public bool[] heightSampled { get; set; } + + /// + /// Any warnings that occurred while sampling heights. + /// public string[] warnings { get; set; } } } From 19757ebe155b48474f2fb738b0f40d75e6da2927 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 10:05:06 +1000 Subject: [PATCH 07/16] Update CHANGES.md. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 51b023c0..67142b78 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ##### Additions :tada: - Added a new `CesiumCameraManager` component. It allows configuration of the cameras to use for Cesium3DTileset culling and level-of-detail. +- Added `SampleHeightMostDetailed` method to `Cesium3DTileset`. It asynchronously queries the height of a tileset at a list of positions. ##### Fixes :wrench: From dc19682028b7bbf33cf568df175a3a566a4db25b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Tue, 24 Sep 2024 10:08:53 +1000 Subject: [PATCH 08/16] Pass through ion token for tests. --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff7070b7..ab397f2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -138,6 +138,8 @@ jobs: name: Windows Package path: d:\cesium\CesiumForUnityBuildProject\*.tgz - name: Run Tests + env: + CESIUM_ION_TOKEN_FOR_TESTS: ${{ secrets.CESIUM_ION_TOKEN_FOR_TESTS }} run: | start -FilePath "C:\Program Files\Unity\Hub\Editor\2022.3.41f1\Editor\Unity.exe" -ArgumentList "-runTests -batchmode -projectPath d:\cesium\CesiumForUnityBuildProject -testResults d:\cesium\temp\TestResults.xml -testPlatform PlayMode -logFile d:\cesium\temp\test-log.txt" -Wait cat d:\cesium\temp\test-log.txt @@ -273,6 +275,8 @@ jobs: name: macOS Package path: ~/cesium/CesiumForUnityBuildProject/*.tgz - name: Run Tests + env: + CESIUM_ION_TOKEN_FOR_TESTS: ${{ secrets.CESIUM_ION_TOKEN_FOR_TESTS }} run: | /Applications/Unity/Hub/Editor/2022.3.41f1/Unity.app/Contents/MacOS/Unity -runTests -batchmode -projectPath ~/cesium/CesiumForUnityBuildProject -testResults ~/cesium/CesiumForUnityBuildProject/TestResults.xml -testPlatform PlayMode -logFile ~/cesium/CesiumForUnityBuildProject/test-log.txt cat ~/cesium/CesiumForUnityBuildProject/test-log.txt From d9b8693bfd6b87c3a1ed96ce2a9b712513536c68 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 26 Sep 2024 15:36:33 -0400 Subject: [PATCH 09/16] Doc adjustments --- Runtime/Cesium3DTileset.cs | 17 ++++++++-------- Runtime/CesiumSampleHeightResult.cs | 30 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Runtime/Cesium3DTileset.cs b/Runtime/Cesium3DTileset.cs index a3a17aee..f1e53241 100644 --- a/Runtime/Cesium3DTileset.cs +++ b/Runtime/Cesium3DTileset.cs @@ -719,15 +719,15 @@ public bool createPhysicsMeshes public partial void FocusTileset(); /// - /// Initiates an asynchronous query for the height of this tileset at a list of positions, - /// expressed as longitude and latitude. The most detailed available tiles are used to - /// determine each height. + /// Initiates an asynchronous query for the height of this tileset at a list of + /// cartographic positions, where the longitude (X) and latitude (Y) are given in degrees. + /// The most detailed available tiles are used to determine each height. /// /// /// - /// The height of the input positions is ignored. On output, the height is - /// expressed in meters above the ellipsoid (usually WGS84), which should not - /// be confused with a height above mean sea level. + /// The height of the input positions is ignored, unless height sampling fails + /// at that location. The output height is expressed in meters above the ellipsoid + /// (usually WGS84), which should not be confused with a height above mean sea level. /// /// /// Use inside a coroutine to wait for the asynchronous height @@ -735,8 +735,9 @@ public bool createPhysicsMeshes /// /// /// - /// The positions for which to sample heights. The X component is the Longitude (degrees), - /// the Y component is the Latitude (degrees), and the Z component is the Height (meters). + /// The cartographic positions for which to sample heights. The X component is the + /// Longitude (degrees), the Y component is the Latitude (degrees), and the Z component + /// is the Height (meters). /// /// An asynchronous task that will provide the requested heights when complete. public partial Task SampleHeightMostDetailed(params double3[] longitudeLatitudeHeightPositions); diff --git a/Runtime/CesiumSampleHeightResult.cs b/Runtime/CesiumSampleHeightResult.cs index 590bd33c..0e813393 100644 --- a/Runtime/CesiumSampleHeightResult.cs +++ b/Runtime/CesiumSampleHeightResult.cs @@ -8,34 +8,34 @@ namespace CesiumForUnity public class CesiumSampleHeightResult { /// - /// The positions and sampled heights. The X component is Longitude (degrees), the - /// Y component is Latitude (degrees), and the Z component is Height (meters) above - /// the ellipsoid (usually WGS84). + /// The positions and their sampled heights. The X component is Longitude (degrees), + /// the Y component is Latitude (degrees), and the Z component is Height (meters) + /// above the ellipsoid (usually WGS84). /// /// /// - /// The longitudes and latitudes will match the values at the same index in the - /// original input positions. Each height will either be the height sampled - /// from the tileset at that position, or the original input height if the - /// height could not be sampled. To determine which, look at the value of + /// For each resulting position, its longitude and latitude values will match + /// values from its input. Its height will either be the height sampled from + /// the tileset at that position, or the original input height if the sample + /// was unsuccessful. To determine which, look at the value of /// at the same index. /// /// - /// The returned height is is measured from the ellipsoid, which is usually WGS84. - /// It should not be confused with a height about Mean Sea Level. + /// The returned height is measured from the ellipsoid (usually WGS84) and + /// should not be confused with a height above Mean Sea Level. /// /// public double3[] longitudeLatitudeHeightPositions { get; set; } /// - /// Specifies whether the height for the position at this index was sampled successfully. + /// Indicates whether the height for the position at the corresponding index was sampled + /// successfully. /// /// - /// If true, has - /// a valid height sampled from the tileset at this index. If false, the height - /// could not be sampled for the position at this index, and so the height in - /// is unchanged - /// from the original input height. + /// If true, then the corresponding position in + /// uses + /// the height sampled from the tileset. If false, the height could not be sampled for + /// the position, so its height is the same as the original input height. /// public bool[] heightSampled { get; set; } From f23191f3257404d05a17b68797cad6bdbf929e1b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 11:22:32 +1000 Subject: [PATCH 10/16] heightSampled -> sampleSuccess. --- Runtime/CesiumSampleHeightResult.cs | 4 ++-- Runtime/ConfigureReinterop.cs | 2 +- Tests/TestCesium3DTileset.cs | 24 ++++++++++----------- native~/Runtime/src/Cesium3DTilesetImpl.cpp | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Runtime/CesiumSampleHeightResult.cs b/Runtime/CesiumSampleHeightResult.cs index 0e813393..a1d63ce4 100644 --- a/Runtime/CesiumSampleHeightResult.cs +++ b/Runtime/CesiumSampleHeightResult.cs @@ -18,7 +18,7 @@ public class CesiumSampleHeightResult /// values from its input. Its height will either be the height sampled from /// the tileset at that position, or the original input height if the sample /// was unsuccessful. To determine which, look at the value of - /// at the same index. + /// at the same index. /// /// /// The returned height is measured from the ellipsoid (usually WGS84) and @@ -37,7 +37,7 @@ public class CesiumSampleHeightResult /// the height sampled from the tileset. If false, the height could not be sampled for /// the position, so its height is the same as the original input height. /// - public bool[] heightSampled { get; set; } + public bool[] sampleSuccess { get; set; } /// /// Any warnings that occurred while sampling heights. diff --git a/Runtime/ConfigureReinterop.cs b/Runtime/ConfigureReinterop.cs index 1bbe21ac..c746a914 100644 --- a/Runtime/ConfigureReinterop.cs +++ b/Runtime/ConfigureReinterop.cs @@ -894,7 +894,7 @@ Cesium3DTilesetLoadFailureDetails tilesetDetails promise.SetException(new Exception("message")); CesiumSampleHeightResult result = new CesiumSampleHeightResult(); result.longitudeLatitudeHeightPositions = null; - result.heightSampled = null; + result.sampleSuccess = null; result.warnings = null; promise.SetResult(result); Task task = promise.Task; diff --git a/Tests/TestCesium3DTileset.cs b/Tests/TestCesium3DTileset.cs index bb25b82a..a5abd41a 100644 --- a/Tests/TestCesium3DTileset.cs +++ b/Tests/TestCesium3DTileset.cs @@ -28,10 +28,10 @@ public IEnumerator SampleHeightMostDetailedWorksWithAnEmptyArrayOfPositions() CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); - Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.sampleSuccess); Assert.IsNotNull(result.warnings); Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 0); - Assert.AreEqual(result.heightSampled.Length, 0); + Assert.AreEqual(result.sampleSuccess.Length, 0); Assert.AreEqual(result.warnings.Length, 0); } @@ -54,13 +54,13 @@ public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); - Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.sampleSuccess); Assert.IsNotNull(result.warnings); Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 1); - Assert.AreEqual(result.heightSampled.Length, 1); + Assert.AreEqual(result.sampleSuccess.Length, 1); Assert.AreEqual(result.warnings.Length, 0); - Assert.AreEqual(result.heightSampled[0], true); + Assert.AreEqual(result.sampleSuccess[0], true); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].x, -105.1, 1e-12); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].y, 40.1, 1e-12); // Returned height should be different from the original height (1.0) by at least one meter. @@ -88,19 +88,19 @@ public IEnumerator SampleHeightMostDetailedWorksWithMultiplePositions() CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); - Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.sampleSuccess); Assert.IsNotNull(result.warnings); Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 2); - Assert.AreEqual(result.heightSampled.Length, 2); + Assert.AreEqual(result.sampleSuccess.Length, 2); Assert.AreEqual(result.warnings.Length, 0); - Assert.AreEqual(result.heightSampled[0], true); + Assert.AreEqual(result.sampleSuccess[0], true); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].x, -105.1, 1e-12); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].y, 40.1, 1e-12); // Returned height should be different from the original height (1.0) by at least one meter. Assert.IsTrue(math.abs(result.longitudeLatitudeHeightPositions[0].z - 1.0) > 1.0); - Assert.AreEqual(result.heightSampled[1], true); + Assert.AreEqual(result.sampleSuccess[1], true); Assert.AreEqual(result.longitudeLatitudeHeightPositions[1].x, 105.1, 1e-12); Assert.AreEqual(result.longitudeLatitudeHeightPositions[1].y, -40.1, 1e-12); // Returned height should be different from the original height (1.0) by at least one meter. @@ -127,13 +127,13 @@ public IEnumerator SampleHeightMostDetailedIndicatesNotSampledForPositionOutside CesiumSampleHeightResult result = task.Result; Assert.IsNotNull(result); Assert.IsNotNull(result.longitudeLatitudeHeightPositions); - Assert.IsNotNull(result.heightSampled); + Assert.IsNotNull(result.sampleSuccess); Assert.IsNotNull(result.warnings); Assert.AreEqual(result.longitudeLatitudeHeightPositions.Length, 1); - Assert.AreEqual(result.heightSampled.Length, 1); + Assert.AreEqual(result.sampleSuccess.Length, 1); Assert.AreEqual(result.warnings.Length, 0); - Assert.AreEqual(result.heightSampled[0], false); + Assert.AreEqual(result.sampleSuccess[0], false); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].x, 151.20972, 1e-12); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].y, -33.87100, 1e-12); Assert.AreEqual(result.longitudeLatitudeHeightPositions[0].z, 1.0, 1e-12); diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.cpp b/native~/Runtime/src/Cesium3DTilesetImpl.cpp index d9a017f8..bbae8ef4 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.cpp +++ b/native~/Runtime/src/Cesium3DTilesetImpl.cpp @@ -445,7 +445,7 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( CesiumForUnity::CesiumSampleHeightResult unityResult; unityResult.longitudeLatitudeHeightPositions(positions); - unityResult.heightSampled(heightSampled); + unityResult.sampleSuccess(heightSampled); unityResult.warnings(warnings); promise.SetResult(unityResult); From c8f5ba17c9a7e273cd629d2a87afab5bff8e0b9c Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 21:34:32 +1000 Subject: [PATCH 11/16] Handle sampling before Native Tileset is created. --- Reinterop~/CSharpObjectHandleUtility.cs | 9 +++++- Tests/TestCesium3DTileset.cs | 12 -------- native~/Runtime/src/Cesium3DTilesetImpl.cpp | 29 ++++++++++++------- native~/Runtime/src/UnityTilesetExternals.cpp | 2 +- native~/extern/cesium-native | 2 +- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Reinterop~/CSharpObjectHandleUtility.cs b/Reinterop~/CSharpObjectHandleUtility.cs index 77d31264..93c58fb0 100644 --- a/Reinterop~/CSharpObjectHandleUtility.cs +++ b/Reinterop~/CSharpObjectHandleUtility.cs @@ -46,7 +46,14 @@ public static void FreeHandle(IntPtr handle) if (handle == IntPtr.Zero) return; - GCHandle.FromIntPtr(handle).Free(); + try + { + GCHandle.FromIntPtr(handle).Free(); + } + catch (ArgumentException e) + { + System.Console.WriteLine(e.ToString()); + } } public static object GetObjectFromHandle(IntPtr handle) diff --git a/Tests/TestCesium3DTileset.cs b/Tests/TestCesium3DTileset.cs index a5abd41a..74c5adfc 100644 --- a/Tests/TestCesium3DTileset.cs +++ b/Tests/TestCesium3DTileset.cs @@ -18,9 +18,6 @@ public IEnumerator SampleHeightMostDetailedWorksWithAnEmptyArrayOfPositions() tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; tileset.ionAssetID = 1; - // TODO: remove this - yield return null; - Task task = tileset.SampleHeightMostDetailed(); yield return new WaitForTask(task); @@ -44,9 +41,6 @@ public IEnumerator SampleHeightMostDetailedWorksWithASinglePosition() tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; tileset.ionAssetID = 1; - // TODO: remove this - yield return null; - Task task = tileset.SampleHeightMostDetailed(new double3(-105.1, 40.1, 1.0)); yield return new WaitForTask(task); @@ -76,9 +70,6 @@ public IEnumerator SampleHeightMostDetailedWorksWithMultiplePositions() tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; tileset.ionAssetID = 1; - // TODO: remove this - yield return null; - Task task = tileset.SampleHeightMostDetailed( new double3(-105.1, 40.1, 1.0), new double3(105.1, -40.1, 1.0)); @@ -116,9 +107,6 @@ public IEnumerator SampleHeightMostDetailedIndicatesNotSampledForPositionOutside tileset.ionAccessToken = Environment.GetEnvironmentVariable("CESIUM_ION_TOKEN_FOR_TESTS") ?? ""; tileset.ionAssetID = 69380; - // TODO: remove this - yield return null; - // Somewhere in Sydney, not Melbourne Task task = tileset.SampleHeightMostDetailed(new double3(151.20972, -33.87100, 1.0)); diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.cpp b/native~/Runtime/src/Cesium3DTilesetImpl.cpp index bbae8ef4..8550bc05 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.cpp +++ b/native~/Runtime/src/Cesium3DTilesetImpl.cpp @@ -94,7 +94,6 @@ void Cesium3DTilesetImpl::Update( } if (this->_destroyTilesetOnNextUpdate) { - this->_destroyTilesetOnNextUpdate = false; this->DestroyTileset(tileset); } @@ -391,18 +390,16 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( const CesiumForUnity::Cesium3DTileset& tileset, const System::Array1& longitudeLatitudeHeightPositions) { + if (this->getTileset() == nullptr) { + // Calling DestroyTileset ensures _destroyTilesetOnNextUpdate is reset. + this->DestroyTileset(tileset); + this->LoadTileset(tileset); + } + System::Threading::Tasks::TaskCompletionSource1< CesiumForUnity::CesiumSampleHeightResult> promise{}; - Tileset* pTileset = this->getTileset(); - if (pTileset == nullptr) { - // TODO: wait a tick for the tileset to be created. - promise.SetException( - System::Exception(System::String("Tileset not created yet."))); - return promise.Task(); - } - std::vector positions; positions.reserve(longitudeLatitudeHeightPositions.Length()); @@ -415,7 +412,17 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( position.z)); } - pTileset->sampleHeightMostDetailed(positions) + CesiumAsync::Future future = + this->getTileset() + ? this->getTileset()->sampleHeightMostDetailed(positions) + : getAsyncSystem().createResolvedFuture( + Cesium3DTilesSelection::SampleHeightResult{ + positions, + std::vector(positions.size(), false), + {"Could not sample heights from tileset because it has not " + "been created."}}); + + std::move(future) .thenImmediately( [promise](Cesium3DTilesSelection::SampleHeightResult&& result) { System::Array1 positions( @@ -531,6 +538,8 @@ void Cesium3DTilesetImpl::DestroyTileset( } this->_pTileset.reset(); + + this->_destroyTilesetOnNextUpdate = false; } void Cesium3DTilesetImpl::LoadTileset( diff --git a/native~/Runtime/src/UnityTilesetExternals.cpp b/native~/Runtime/src/UnityTilesetExternals.cpp index 45001fb8..b9615b20 100644 --- a/native~/Runtime/src/UnityTilesetExternals.cpp +++ b/native~/Runtime/src/UnityTilesetExternals.cpp @@ -95,7 +95,7 @@ createTilesetExternals(const CesiumForUnity::Cesium3DTileset& tileset) { return TilesetExternals{ getAssetAccessor(), std::make_shared(tileset.gameObject()), - AsyncSystem(getTaskProcessor()), + getAsyncSystem(), getOrCreateCreditSystem(tileset), spdlog::default_logger()}; } diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index 4850751a..abd3347e 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit 4850751a25c3cc95e8b9458a904b043d98bbd284 +Subproject commit abd3347e29d07a99c8b1fc596ed79bde22b52c8d From fcab7cd90367a2728beeb8ac19caaa747a12f8ac Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 21:39:05 +1000 Subject: [PATCH 12/16] Add comment explaining catch. --- Reinterop~/CSharpObjectHandleUtility.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Reinterop~/CSharpObjectHandleUtility.cs b/Reinterop~/CSharpObjectHandleUtility.cs index 93c58fb0..d11d95e9 100644 --- a/Reinterop~/CSharpObjectHandleUtility.cs +++ b/Reinterop~/CSharpObjectHandleUtility.cs @@ -52,6 +52,11 @@ public static void FreeHandle(IntPtr handle) } catch (ArgumentException e) { + // The "GCHandle value belongs to a different domain" exception tends + // to happen on AppDomain reload, which is common in Unity. + // Catch the exception to prevent it propagating through our native + // code and blowing things up. + // See: https://github.com/CesiumGS/cesium-unity/issues/18 System.Console.WriteLine(e.ToString()); } } From 7682003f117d6702299d29ba127a9ddc0d1cc2a5 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 21:42:42 +1000 Subject: [PATCH 13/16] Update cesium-native. --- native~/extern/cesium-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index abd3347e..fb38ac9f 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit abd3347e29d07a99c8b1fc596ed79bde22b52c8d +Subproject commit fb38ac9f2cf3a4baec39e0b73616391258181e82 From 432cd1e11af290a33cd324be68d93a3bd8a116b6 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 22:29:06 +1000 Subject: [PATCH 14/16] heightSampled -> sampleSuccess. --- native~/Runtime/src/Cesium3DTilesetImpl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.cpp b/native~/Runtime/src/Cesium3DTilesetImpl.cpp index 8550bc05..80991b80 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.cpp +++ b/native~/Runtime/src/Cesium3DTilesetImpl.cpp @@ -440,9 +440,9 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( positionRadians.height}); } - System::Array1 heightSampled(result.heightSampled.size()); - for (size_t i = 0; i < result.heightSampled.size(); ++i) { - heightSampled.Item(i, result.heightSampled[i]); + System::Array1 sampleSuccess(result.sampleSuccess.size()); + for (size_t i = 0; i < result.sampleSuccess.size(); ++i) { + sampleSuccess.Item(i, result.sampleSuccess[i]); } System::Array1 warnings(result.warnings.size()); @@ -452,7 +452,7 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( CesiumForUnity::CesiumSampleHeightResult unityResult; unityResult.longitudeLatitudeHeightPositions(positions); - unityResult.sampleSuccess(heightSampled); + unityResult.sampleSuccess(sampleSuccess); unityResult.warnings(warnings); promise.SetResult(unityResult); From ca2c2ba8c54e7117cf3b0649cac5e5fdd338e22b Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 23:10:06 +1000 Subject: [PATCH 15/16] std::move positions on error, because we can. --- native~/Runtime/src/Cesium3DTilesetImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native~/Runtime/src/Cesium3DTilesetImpl.cpp b/native~/Runtime/src/Cesium3DTilesetImpl.cpp index 80991b80..b42c8082 100644 --- a/native~/Runtime/src/Cesium3DTilesetImpl.cpp +++ b/native~/Runtime/src/Cesium3DTilesetImpl.cpp @@ -417,7 +417,7 @@ Cesium3DTilesetImpl::SampleHeightMostDetailed( ? this->getTileset()->sampleHeightMostDetailed(positions) : getAsyncSystem().createResolvedFuture( Cesium3DTilesSelection::SampleHeightResult{ - positions, + std::move(positions), std::vector(positions.size(), false), {"Could not sample heights from tileset because it has not " "been created."}}); From e17c287039a6464c193955a6374e37d657e830f4 Mon Sep 17 00:00:00 2001 From: Kevin Ring Date: Fri, 27 Sep 2024 23:10:57 +1000 Subject: [PATCH 16/16] Update native. --- native~/extern/cesium-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index fb38ac9f..b56b1a2e 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit fb38ac9f2cf3a4baec39e0b73616391258181e82 +Subproject commit b56b1a2ee709e1c9b53996a6073c66d9cf01955b