From 9cce944705f0a95b818cc9c076d2e9faad4297e8 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 2 Feb 2023 11:19:45 -0500 Subject: [PATCH 1/3] Basic support for rendering point clouds in Unity --- Editor/Cesium3DTilesetEditor.cs | 7 +- .../src/UnityPrepareRendererResources.cpp | 156 +++++++++++------- .../src/UnityPrepareRendererResources.h | 6 + native~/extern/cesium-native | 2 +- 4 files changed, 104 insertions(+), 67 deletions(-) diff --git a/Editor/Cesium3DTilesetEditor.cs b/Editor/Cesium3DTilesetEditor.cs index ecdb58be..4883e853 100644 --- a/Editor/Cesium3DTilesetEditor.cs +++ b/Editor/Cesium3DTilesetEditor.cs @@ -444,10 +444,13 @@ private void DrawPhysicsProperties() GUIContent createPhysicsMeshesContent = new GUIContent( "Create Physics Meshes", - "Whether to generate physics meshes for this tileset.\n\n" + + "Whether to generate physics meshes for this tileset." + + "\n\n" + "Disabling this option will improve the performance of tile loading, " + "but it will no longer be possible to collide with the tileset since " + - "the physics meshes will not be created."); + "the physics meshes will not be created." + + "\n\n" + + "Physics meshes cannot be generated for primitives containing points."); EditorGUILayout.PropertyField(this._createPhysicsMeshes, createPhysicsMeshesContent); } } diff --git a/native~/Runtime/src/UnityPrepareRendererResources.cpp b/native~/Runtime/src/UnityPrepareRendererResources.cpp index 7def266d..97484296 100644 --- a/native~/Runtime/src/UnityPrepareRendererResources.cpp +++ b/native~/Runtime/src/UnityPrepareRendererResources.cpp @@ -73,17 +73,31 @@ using namespace DotNet; namespace { template -void setTriangles( +void setIndices( const Unity::Collections::NativeArray1& dest, const AccessorView& source) { assert(dest.Length() == source.size()); - TDest* triangles = static_cast( + TDest* indices = static_cast( Unity::Collections::LowLevel::Unsafe::NativeArrayUnsafeUtility:: GetUnsafeBufferPointerWithoutChecks(dest)); for (int64_t i = 0; i < source.size(); ++i) { - triangles[i] = source[i]; + indices[i] = source[i]; + } +} + +template +void generateIndices( + const Unity::Collections::NativeArray1& dest, + const int32_t count) { + assert(dest.Length() == count); + T* indices = static_cast( + Unity::Collections::LowLevel::Unsafe::NativeArrayUnsafeUtility:: + GetUnsafeBufferPointerWithoutChecks(dest)); + + for (int64_t i = 0; i < count; ++i) { + indices[i] = static_cast(i); } } @@ -250,11 +264,6 @@ void populateMeshDataArray( std::int32_t numberOfAttributes = 0; std::int32_t streamIndex = 0; - if (primitive.indices < 0) { - // TODO: support non-indexed primitives. - return; - } - auto positionAccessorIt = primitive.attributes.find("POSITION"); if (positionAccessorIt == primitive.attributes.end()) { // This primitive doesn't have a POSITION semantic, ignore it. @@ -465,25 +474,38 @@ void populateMeshDataArray( int32_t indexCount = 0; - AccessorView indices8(gltf, primitive.indices); - if (indices8.status() == AccessorViewStatus::Valid) { - indexCount = indices8.size(); - meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16); - setTriangles(meshData.GetIndexData(), indices8); - } + if (primitive.indices >= 0) { + AccessorView indices8(gltf, primitive.indices); + if (indices8.status() == AccessorViewStatus::Valid) { + indexCount = indices8.size(); + meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16); + setIndices(meshData.GetIndexData(), indices8); + } - AccessorView indices16(gltf, primitive.indices); - if (indices16.status() == AccessorViewStatus::Valid) { - indexCount = indices16.size(); - meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16); - setTriangles(meshData.GetIndexData(), indices16); - } + AccessorView indices16(gltf, primitive.indices); + if (indices16.status() == AccessorViewStatus::Valid) { + indexCount = indices16.size(); + meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16); + setIndices(meshData.GetIndexData(), indices16); + } - AccessorView indices32(gltf, primitive.indices); - if (indices32.status() == AccessorViewStatus::Valid) { - indexCount = indices32.size(); - meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt32); - setTriangles(meshData.GetIndexData(), indices32); + AccessorView indices32(gltf, primitive.indices); + if (indices32.status() == AccessorViewStatus::Valid) { + indexCount = indices32.size(); + meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt32); + setIndices(meshData.GetIndexData(), indices32); + } + } else { + // Generate indices for primitives without them. + indexCount = positionView.size(); + + if (indexCount > std::numeric_limits::max()) { + meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt32); + generateIndices(meshData.GetIndexData(), indexCount); + } else { + meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16); + generateIndices(meshData.GetIndexData(), indexCount); + } } meshData.subMeshCount(1); @@ -491,7 +513,14 @@ void populateMeshDataArray( // TODO: use sub-meshes for glTF primitives, instead of a separate mesh // for each. SubMeshDescriptor subMeshDescriptor{}; - subMeshDescriptor.topology = MeshTopology::Triangles; + + if (primitive.mode == MeshPrimitive::Mode::POINTS) { + subMeshDescriptor.topology = MeshTopology::Points; + primitiveInfo.containsPoints = true; + } else { + subMeshDescriptor.topology = MeshTopology::Triangles; + } + subMeshDescriptor.indexStart = 0; subMeshDescriptor.indexCount = indexCount; subMeshDescriptor.baseVertex = 0; @@ -501,12 +530,6 @@ void populateMeshDataArray( subMeshDescriptor.vertexCount = 0; meshData.SetSubMesh(0, subMeshDescriptor, MeshUpdateFlags::Default); - - // if (createPhysicsMeshes) { - // UnityEngine::MeshCollider meshCollider = - // primitiveGameObject.AddComponent(); - // meshCollider.sharedMesh(unityMesh); - // } }); } @@ -582,6 +605,8 @@ UnityPrepareRendererResources::prepareInLoadThread( const UnityEngine::MeshDataArray& meshDataArray = workerResult.meshDataResult.meshDataArray; + const std::vector& primitiveInfos = + workerResult.meshDataResult.primitiveInfos; // Create meshes and populate them from the MeshData created in // the worker thread. Sadly, this must be done in the main @@ -620,36 +645,43 @@ UnityPrepareRendererResources::prepareInLoadThread( if (shouldCreatePhysicsMeshes) { // Baking physics meshes takes awhile, so do that in a // worker thread. - std::int32_t len = meshes.Length(); - std::vector instanceIDs(meshes.Length()); + const std::int32_t len = meshes.Length(); + std::vector instanceIDs; for (int32_t i = 0; i < len; ++i) { - instanceIDs[i] = meshes[i].GetInstanceID(); + // Don't attempt to bake a physics mesh from a point cloud. + if (primitiveInfos[i].containsPoints) { + continue; + } + instanceIDs.push_back(meshes[i].GetInstanceID()); } - return asyncSystem.runInWorkerThread( - [workerResult = std::move(workerResult), - instanceIDs = std::move(instanceIDs), - meshes = std::move(meshes)]() mutable { - for (std::int32_t instanceID : instanceIDs) { - UnityEngine::Physics::BakeMesh(instanceID, false); - } - - LoadThreadResult* pResult = new LoadThreadResult{ - std::move(meshes), - std::move(workerResult.meshDataResult.primitiveInfos)}; - return TileLoadResultAndRenderResources{ - std::move(workerResult.tileLoadResult), - pResult}; - }); - } else { - LoadThreadResult* pResult = new LoadThreadResult{ - std::move(meshes), - std::move(workerResult.meshDataResult.primitiveInfos)}; - return asyncSystem.createResolvedFuture( - TileLoadResultAndRenderResources{ - std::move(workerResult.tileLoadResult), - pResult}); + if (instanceIDs.size() > 0) { + return asyncSystem.runInWorkerThread( + [workerResult = std::move(workerResult), + instanceIDs = std::move(instanceIDs), + meshes = std::move(meshes)]() mutable { + for (std::int32_t instanceID : instanceIDs) { + UnityEngine::Physics::BakeMesh(instanceID, false); + } + + LoadThreadResult* pResult = new LoadThreadResult{ + std::move(meshes), + std::move( + workerResult.meshDataResult.primitiveInfos)}; + return TileLoadResultAndRenderResources{ + std::move(workerResult.tileLoadResult), + pResult}; + }); + } } + + LoadThreadResult* pResult = new LoadThreadResult{ + std::move(meshes), + std::move(workerResult.meshDataResult.primitiveInfos)}; + return asyncSystem.createResolvedFuture( + TileLoadResultAndRenderResources{ + std::move(workerResult.tileLoadResult), + pResult}); }); } @@ -757,11 +789,6 @@ void* UnityPrepareRendererResources::prepareInMainThread( return; } - if (primitive.indices < 0) { - // TODO: support non-indexed primitives. - return; - } - auto positionAccessorIt = primitive.attributes.find("POSITION"); if (positionAccessorIt == primitive.attributes.end()) { // This primitive doesn't have a POSITION semantic, ignore it. @@ -1015,7 +1042,8 @@ void* UnityPrepareRendererResources::prepareInMainThread( meshFilter.sharedMesh(unityMesh); - if (createPhysicsMeshes) { + if (createPhysicsMeshes && + primitive.mode != MeshPrimitive::Mode::POINTS) { // This should not trigger mesh baking for physics, because the meshes // were already baked in the worker thread. UnityEngine::MeshCollider meshCollider = diff --git a/native~/Runtime/src/UnityPrepareRendererResources.h b/native~/Runtime/src/UnityPrepareRendererResources.h index aef9ab03..ee3e2e44 100644 --- a/native~/Runtime/src/UnityPrepareRendererResources.h +++ b/native~/Runtime/src/UnityPrepareRendererResources.h @@ -11,6 +11,12 @@ namespace CesiumForUnityNative { * Unity MeshData. */ struct CesiumPrimitiveInfo { + /** + * @brief Whether or not the primitive's mode is set to POINTS. + * This affects whether or not it can be baked into a physics mesh. + */ + bool containsPoints = false; + /** * @brief Maps a texture coordinate index i (TEXCOORD_) to the * corresponding Unity texture coordinate index. diff --git a/native~/extern/cesium-native b/native~/extern/cesium-native index f5f1baec..cb77693b 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit f5f1baece2daa168b03fba10c68a4a6ca8348523 +Subproject commit cb77693b4de0815cd8bc3cd5ee6a4529094d4569 From ad9c1e347c794aac9c7049885466dea405df141c Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Thu, 2 Feb 2023 17:31:58 -0500 Subject: [PATCH 2/3] Update changelog --- CHANGES.md | 6 ++++++ native~/Runtime/src/UnityPrepareRendererResources.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 013c714c..36f29306 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +### v0.3.0 + +##### Additions :tada: + +- Added support for rendering point clouds (`pnts`). + ### v0.2.0 ##### Breaking Changes :mega: diff --git a/native~/Runtime/src/UnityPrepareRendererResources.cpp b/native~/Runtime/src/UnityPrepareRendererResources.cpp index 97484296..3e1a8c95 100644 --- a/native~/Runtime/src/UnityPrepareRendererResources.cpp +++ b/native~/Runtime/src/UnityPrepareRendererResources.cpp @@ -92,6 +92,7 @@ void generateIndices( const Unity::Collections::NativeArray1& dest, const int32_t count) { assert(dest.Length() == count); + T* indices = static_cast( Unity::Collections::LowLevel::Unsafe::NativeArrayUnsafeUtility:: GetUnsafeBufferPointerWithoutChecks(dest)); From 20e092dcddd4510a072f77d3ff872c554b0dd2c3 Mon Sep 17 00:00:00 2001 From: Janine Liu Date: Fri, 3 Feb 2023 16:15:06 -0500 Subject: [PATCH 3/3] Update cesium-native commit --- 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 cb77693b..34e209af 160000 --- a/native~/extern/cesium-native +++ b/native~/extern/cesium-native @@ -1 +1 @@ -Subproject commit cb77693b4de0815cd8bc3cd5ee6a4529094d4569 +Subproject commit 34e209af460b55a41f884009032f6226b3655cc9