Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for point clouds in Unity #197

Merged
merged 3 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

### v0.3.0

##### Additions :tada:

- Added support for rendering point clouds (`pnts`).

### v0.2.0

##### Breaking Changes :mega:
Expand Down
7 changes: 5 additions & 2 deletions Editor/Cesium3DTilesetEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
157 changes: 93 additions & 64 deletions native~/Runtime/src/UnityPrepareRendererResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,32 @@ using namespace DotNet;
namespace {

template <typename TDest, typename TSource>
void setTriangles(
void setIndices(
const Unity::Collections::NativeArray1<TDest>& dest,
const AccessorView<TSource>& source) {
assert(dest.Length() == source.size());

TDest* triangles = static_cast<TDest*>(
TDest* indices = static_cast<TDest*>(
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 <typename T>
void generateIndices(
const Unity::Collections::NativeArray1<T>& dest,
const int32_t count) {
assert(dest.Length() == count);

T* indices = static_cast<T*>(
Unity::Collections::LowLevel::Unsafe::NativeArrayUnsafeUtility::
GetUnsafeBufferPointerWithoutChecks(dest));

for (int64_t i = 0; i < count; ++i) {
indices[i] = static_cast<T>(i);
}
}

Expand Down Expand Up @@ -250,11 +265,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.
Expand Down Expand Up @@ -465,33 +475,53 @@ void populateMeshDataArray(

int32_t indexCount = 0;

AccessorView<uint8_t> indices8(gltf, primitive.indices);
if (indices8.status() == AccessorViewStatus::Valid) {
indexCount = indices8.size();
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16);
setTriangles(meshData.GetIndexData<std::uint16_t>(), indices8);
}
if (primitive.indices >= 0) {
AccessorView<uint8_t> indices8(gltf, primitive.indices);
if (indices8.status() == AccessorViewStatus::Valid) {
indexCount = indices8.size();
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16);
setIndices(meshData.GetIndexData<std::uint16_t>(), indices8);
}

AccessorView<uint16_t> indices16(gltf, primitive.indices);
if (indices16.status() == AccessorViewStatus::Valid) {
indexCount = indices16.size();
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16);
setTriangles(meshData.GetIndexData<std::uint16_t>(), indices16);
}
AccessorView<uint16_t> indices16(gltf, primitive.indices);
if (indices16.status() == AccessorViewStatus::Valid) {
indexCount = indices16.size();
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16);
setIndices(meshData.GetIndexData<std::uint16_t>(), indices16);
}

AccessorView<uint32_t> indices32(gltf, primitive.indices);
if (indices32.status() == AccessorViewStatus::Valid) {
indexCount = indices32.size();
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt32);
setIndices(meshData.GetIndexData<std::uint32_t>(), indices32);
}
} else {
// Generate indices for primitives without them.
indexCount = positionView.size();

AccessorView<uint32_t> indices32(gltf, primitive.indices);
if (indices32.status() == AccessorViewStatus::Valid) {
indexCount = indices32.size();
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt32);
setTriangles(meshData.GetIndexData<std::uint32_t>(), indices32);
if (indexCount > std::numeric_limits<uint16_t>::max()) {
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt32);
generateIndices(meshData.GetIndexData<std::uint32_t>(), indexCount);
} else {
meshData.SetIndexBufferParams(indexCount, IndexFormat::UInt16);
generateIndices(meshData.GetIndexData<std::uint16_t>(), indexCount);
}
}

meshData.subMeshCount(1);

// 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;
Expand All @@ -501,12 +531,6 @@ void populateMeshDataArray(
subMeshDescriptor.vertexCount = 0;

meshData.SetSubMesh(0, subMeshDescriptor, MeshUpdateFlags::Default);

// if (createPhysicsMeshes) {
// UnityEngine::MeshCollider meshCollider =
// primitiveGameObject.AddComponent<UnityEngine::MeshCollider>();
// meshCollider.sharedMesh(unityMesh);
// }
});
}

Expand Down Expand Up @@ -582,6 +606,8 @@ UnityPrepareRendererResources::prepareInLoadThread(

const UnityEngine::MeshDataArray& meshDataArray =
workerResult.meshDataResult.meshDataArray;
const std::vector<CesiumPrimitiveInfo>& 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
Expand Down Expand Up @@ -620,36 +646,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<std::int32_t> instanceIDs(meshes.Length());
const std::int32_t len = meshes.Length();
std::vector<std::int32_t> 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});
});
}

Expand Down Expand Up @@ -757,11 +790,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.
Expand Down Expand Up @@ -1015,7 +1043,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 =
Expand Down
6 changes: 6 additions & 0 deletions native~/Runtime/src/UnityPrepareRendererResources.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_<i>) to the
* corresponding Unity texture coordinate index.
Expand Down
2 changes: 1 addition & 1 deletion native~/extern/cesium-native
Submodule cesium-native updated 27 files
+6 −0 CHANGES.md
+1 −0 Cesium3DTilesSelection/CMakeLists.txt
+1 −1 Cesium3DTilesSelection/src/B3dmToGltfConverter.cpp
+133 −40 Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.cpp
+7 −1 Cesium3DTilesSelection/src/BatchTableToGltfFeatureMetadata.h
+1,636 −0 Cesium3DTilesSelection/src/PntsToGltfConverter.cpp
+17 −0 Cesium3DTilesSelection/src/PntsToGltfConverter.h
+2 −21 Cesium3DTilesSelection/src/QuantizedMeshLoader.cpp
+2 −0 Cesium3DTilesSelection/src/registerAllTileContentTypes.cpp
+21 −0 Cesium3DTilesSelection/test/ConvertTileToGltf.h
+1,245 −0 Cesium3DTilesSelection/test/TestPntsToGltfConverter.cpp
+480 −16 Cesium3DTilesSelection/test/TestUpgradeBatchTableToExtFeatureMetadata.cpp
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudBatched.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudConstantRGBA.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudDraco.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudDracoBatched.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudDracoPartial.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudNormals.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudNormalsOctEncoded.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudPositionsOnly.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudQuantized.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudRGB.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudRGB565.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudRGBA.pnts
+ Cesium3DTilesSelection/test/data/PointCloud/pointCloudWithPerPointProperties.pnts
+82 −0 CesiumUtility/include/CesiumUtility/AttributeCompression.h
+69 −0 CesiumUtility/test/TestAttributeCompression.cpp