Skip to content
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 {#changes}

## ? - ?

##### Additions :tada:

- Added support for rendering glTFs with line primitives.

## v1.23.3 - 2026-06-01

##### Fixes :wrench:
Expand Down
2 changes: 1 addition & 1 deletion native~/extern/cesium-native
Submodule cesium-native updated 38 files
+3 −0 CHANGES.md
+61 −0 Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetContentOptions.h
+1 −41 Cesium3DTilesSelection/include/Cesium3DTilesSelection/TilesetOptions.h
+1 −1 Cesium3DTilesSelection/src/CesiumIonTilesetLoader.cpp
+1 −1 Cesium3DTilesSelection/src/ITwinCesiumCuratedContentLoader.cpp
+6 −10 Cesium3DTilesSelection/src/ImplicitOctreeLoader.cpp
+6 −10 Cesium3DTilesSelection/src/ImplicitQuadtreeLoader.cpp
+1 −1 Cesium3DTilesSelection/src/LayerJsonTerrainLoader.cpp
+1 −1 Cesium3DTilesSelection/src/TileContentLoadInfo.cpp
+1 −1 Cesium3DTilesSelection/src/TileContentLoadInfo.h
+1 −1 Cesium3DTilesSelection/src/TilesetContentLoader.cpp
+15 −0 Cesium3DTilesSelection/src/TilesetContentOptions.cpp
+3 −5 Cesium3DTilesSelection/src/TilesetJsonLoader.cpp
+85 −0 CesiumGltf/generated/include/CesiumGltf/ExtensionKhrBillboard.h
+9 −3 CesiumGltf/include/CesiumGltf/AccessorUtility.h
+11 −6 CesiumGltf/src/AccessorUtility.cpp
+75 −0 CesiumGltfReader/generated/include/CesiumGltfReader/ExtensionKhrBillboardReader.h
+60 −0 CesiumGltfReader/generated/src/ExtensionKhrBillboardJsonHandler.h
+130 −0 CesiumGltfReader/generated/src/GeneratedJsonHandlers.cpp
+3 −0 CesiumGltfReader/generated/src/registerReaderExtensions.cpp
+38 −2 CesiumGltfReader/include/CesiumGltfReader/GltfReader.h
+5 −0 CesiumGltfReader/src/GltfReader.cpp
+386 −0 CesiumGltfReader/src/convertPrimitiveModes.cpp
+15 −0 CesiumGltfReader/src/convertPrimitiveModes.h
+467 −3 CesiumGltfReader/test/TestGltfReader.cpp
+0 −0 CesiumGltfReader/test/data/BoxTextured/BoxTextured.gltf
+ CesiumGltfReader/test/data/BoxTexturedWebp/BoxTextured0.bin
+0 −0 CesiumGltfReader/test/data/BoxTexturedWebp/BoxTexturedWebp.gltf
+ CesiumGltfReader/test/data/BoxTexturedWebp/CesiumLogoFlat.webp
+ CesiumGltfReader/test/data/CesiumBalloon.glb
+ CesiumGltfReader/test/data/CesiumBalloon/CesiumBalloonKTX2.glb
+ CesiumGltfReader/test/data/CesiumBalloon/CesiumBalloonKTX2Hacky.glb
+233 −0 CesiumGltfReader/test/data/MeshPrimitiveModes/MeshPrimitiveModes.gltf
+52 −0 CesiumGltfWriter/generated/src/ModelJsonWriter.cpp
+14 −0 CesiumGltfWriter/generated/src/ModelJsonWriter.h
+3 −0 CesiumGltfWriter/generated/src/registerWriterExtensions.cpp
+7 −0 tools/generate-classes/glTF.json
+48 −7 tools/generate-classes/resolveProperty.js
111 changes: 62 additions & 49 deletions native~/src/Runtime/Cesium3DTilesetImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,53 +581,9 @@ void Cesium3DTilesetImpl::DestroyTileset(
this->_destroyTilesetOnNextUpdate = false;
}

void Cesium3DTilesetImpl::LoadTileset(
const DotNet::CesiumForUnity::Cesium3DTileset& tileset) {
TilesetOptions options{};
options.rendererOptions = std::make_any<CreateModelOptions>(tileset);
options.maximumScreenSpaceError = tileset.maximumScreenSpaceError();
options.preloadAncestors = tileset.preloadAncestors();
options.preloadSiblings = tileset.preloadSiblings();
options.forbidHoles = tileset.forbidHoles();
options.maximumSimultaneousTileLoads = tileset.maximumSimultaneousTileLoads();
options.maximumCachedBytes = tileset.maximumCachedBytes();
options.loadingDescendantLimit = tileset.loadingDescendantLimit();
options.enableFrustumCulling = tileset.enableFrustumCulling();
options.enableFogCulling = tileset.enableFogCulling();
options.enforceCulledScreenSpaceError =
tileset.enforceCulledScreenSpaceError();
options.culledScreenSpaceError = tileset.culledScreenSpaceError();
// options.enableLodTransitionPeriod = tileset.useLodTransitions();
// options.lodTransitionLength = tileset.lodTransitionLength();
options.showCreditsOnScreen = tileset.showCreditsOnScreen();
options.loadErrorCallback =
[tileset](const TilesetLoadFailureDetails& details) {
int typeValue = (int)details.type;
CesiumForUnity::Cesium3DTilesetLoadFailureDetails unityDetails(
tileset,
CesiumForUnity::Cesium3DTilesetLoadType(typeValue),
details.statusCode,
System::String(details.message));

CesiumForUnity::Cesium3DTileset::BroadcastCesium3DTilesetLoadFailure(
unityDetails);
};

// Generous per-frame time limits for loading / unloading on main thread.
options.mainThreadLoadingTimeLimit = 5.0;
options.tileCacheUnloadTimeLimit = 5.0;

DotNet::CesiumForUnity::CesiumGeoreference georeferenceComponent =
tileset.gameObject()
.GetComponentInParent<DotNet::CesiumForUnity::CesiumGeoreference>();
if (georeferenceComponent != nullptr) {
options.ellipsoid =
georeferenceComponent.ellipsoid().NativeImplementation().GetEllipsoid();
}

TilesetContentOptions contentOptions{};
contentOptions.generateMissingNormalsSmooth = tileset.generateSmoothNormals();

namespace {
CesiumImage::SupportedGpuCompressedPixelFormats
getSupportedGpuCompressedPixelFormats() {
CesiumImage::SupportedGpuCompressedPixelFormats supportedFormats;
supportedFormats.ETC2_RGBA = UnityEngine::SystemInfo::IsFormatSupported(
DotNet::UnityEngine::Experimental::Rendering::GraphicsFormat::
Expand Down Expand Up @@ -682,12 +638,69 @@ void Cesium3DTilesetImpl::LoadTileset(
DotNet::UnityEngine::Experimental::Rendering::GraphicsFormat::
RG_EAC_UNorm,
DotNet::UnityEngine::Experimental::Rendering::FormatUsage::Sample);
return supportedFormats;
}
} // namespace

void Cesium3DTilesetImpl::LoadTileset(
const DotNet::CesiumForUnity::Cesium3DTileset& tileset) {
TilesetOptions options{};
options.rendererOptions = std::make_any<CreateModelOptions>(tileset);
options.maximumScreenSpaceError = tileset.maximumScreenSpaceError();
options.preloadAncestors = tileset.preloadAncestors();
options.preloadSiblings = tileset.preloadSiblings();
options.forbidHoles = tileset.forbidHoles();
options.maximumSimultaneousTileLoads = tileset.maximumSimultaneousTileLoads();
options.maximumCachedBytes = tileset.maximumCachedBytes();
options.loadingDescendantLimit = tileset.loadingDescendantLimit();
options.enableFrustumCulling = tileset.enableFrustumCulling();
options.enableFogCulling = tileset.enableFogCulling();
options.enforceCulledScreenSpaceError =
tileset.enforceCulledScreenSpaceError();
options.culledScreenSpaceError = tileset.culledScreenSpaceError();
// options.enableLodTransitionPeriod = tileset.useLodTransitions();
// options.lodTransitionLength = tileset.lodTransitionLength();
options.showCreditsOnScreen = tileset.showCreditsOnScreen();
options.loadErrorCallback =
[tileset](const TilesetLoadFailureDetails& details) {
int typeValue = (int)details.type;
CesiumForUnity::Cesium3DTilesetLoadFailureDetails unityDetails(
tileset,
CesiumForUnity::Cesium3DTilesetLoadType(typeValue),
details.statusCode,
System::String(details.message));

CesiumForUnity::Cesium3DTileset::BroadcastCesium3DTilesetLoadFailure(
unityDetails);
};

// Generous per-frame time limits for loading / unloading on main thread.
options.mainThreadLoadingTimeLimit = 5.0;
options.tileCacheUnloadTimeLimit = 5.0;

contentOptions.ktx2TranscodeTargets =
CesiumImage::Ktx2TranscodeTargets(supportedFormats, false);
DotNet::CesiumForUnity::CesiumGeoreference georeferenceComponent =
tileset.gameObject()
.GetComponentInParent<DotNet::CesiumForUnity::CesiumGeoreference>();
if (georeferenceComponent != nullptr) {
options.ellipsoid =
georeferenceComponent.ellipsoid().NativeImplementation().GetEllipsoid();
}

TilesetContentOptions contentOptions{};
contentOptions.generateMissingNormalsSmooth = tileset.generateSmoothNormals();
contentOptions.ktx2TranscodeTargets = CesiumImage::Ktx2TranscodeTargets(
getSupportedGpuCompressedPixelFormats(),
false);
contentOptions.applyTextureTransform = false;

// Although Unity provides MeshTopology.LineStrip, it crashes when given an
// index buffer with a primitive restart constant. Therefore, ensure all
// primitive modes are converted before they are processed by Unity.
contentOptions.primitiveModeOptions.convertLineLoop = true;
contentOptions.primitiveModeOptions.convertLineStrip = true;
contentOptions.primitiveModeOptions.convertTriangleFan = true;
contentOptions.primitiveModeOptions.convertTriangleStrip = true;

options.contentOptions = contentOptions;

CesiumForUnity::CesiumCameraManager cameraManager =
Expand Down
107 changes: 51 additions & 56 deletions native~/src/Runtime/UnityPrepareRendererResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,22 +331,24 @@ void loadPrimitive(
using namespace DotNet::Unity::Collections::LowLevel::Unsafe;

CESIUM_TRACE("Cesium::loadPrimitive<T>");
int32_t indexCount = 0;
int32_t indexCount = static_cast<int32_t>(indicesView.size());
switch (primitive.mode) {
case MeshPrimitive::Mode::TRIANGLES:
case MeshPrimitive::Mode::POINTS:
indexCount = static_cast<int32_t>(indicesView.size());
break;
case MeshPrimitive::Mode::TRIANGLE_STRIP:
case MeshPrimitive::Mode::TRIANGLE_FAN:
indexCount = static_cast<int32_t>(3 * (indicesView.size() - 2));
case MeshPrimitive::Mode::LINES:
if (indexCount < 2) {
return;
}
break;
case MeshPrimitive::Mode::TRIANGLES:
if (indexCount < 3) {
return;
}
break;
default:
// TODO: add support for other primitive types.
return;
}

if (indexCount < 3 && primitive.mode != MeshPrimitive::Mode::POINTS) {
// The options set in Cesium3DTilesetImpl::LoadTileset should ensure we only
// receive supported primitive modes.
CESIUM_ASSERT(false);
return;
}

Expand All @@ -367,11 +369,12 @@ void loadPrimitive(
AccessorView<UnityEngine::Vector3>(gltf, normalAccessorIt->second);
hasNormals = normalView.status() == AccessorViewStatus::Valid;
} else if (
!primitiveInfo.isUnlit && primitive.mode != MeshPrimitive::Mode::POINTS) {
!primitiveInfo.isUnlit &&
primitive.mode == MeshPrimitive::Mode::TRIANGLES) {
computeFlatNormals = hasNormals = true;
}

// Check if we need to upgrade to a large index type to accommodate the
// Check if we need to upgrade to a large index type to accommodate the
// larger number of vertices we need for flat normals.
if (computeFlatNormals && indexFormat == IndexFormat::UInt16 &&
indexCount >= std::numeric_limits<uint16_t>::max()) {
Expand All @@ -397,34 +400,8 @@ void loadPrimitive(
Unity::Collections::LowLevel::Unsafe::NativeArrayUnsafeUtility::
GetUnsafeBufferPointerWithoutChecks(dest));

switch (primitive.mode) {
case MeshPrimitive::Mode::TRIANGLE_STRIP:
for (int64_t i = 0; i < indicesView.size() - 2; ++i) {
if (i % 2) {
indices[3 * i] = indicesView[i];
indices[3 * i + 1] = indicesView[i + 2];
indices[3 * i + 2] = indicesView[i + 1];
} else {
indices[3 * i] = indicesView[i];
indices[3 * i + 1] = indicesView[i + 1];
indices[3 * i + 2] = indicesView[i + 2];
}
}
break;
case MeshPrimitive::Mode::TRIANGLE_FAN:
for (int64_t i = 0; i < indicesView.size() - 2; ++i) {
indices[3 * i] = indicesView[0];
indices[3 * i + 1] = indicesView[i + 1];
indices[3 * i + 2] = indicesView[i + 2];
}
break;
case MeshPrimitive::Mode::TRIANGLES:
case MeshPrimitive::Mode::POINTS:
default:
for (int64_t i = 0; i < indicesView.size(); ++i) {
indices[i] = indicesView[i];
}
break;
for (int64_t i = 0; i < indicesView.size(); ++i) {
indices[i] = indicesView[i];
}

// Max attribute count supported by Unity, see VertexAttribute.
Expand Down Expand Up @@ -664,12 +641,19 @@ void loadPrimitive(
// for each.
SubMeshDescriptor subMeshDescriptor{};

if (primitive.mode == MeshPrimitive::Mode::POINTS) {
switch (primitive.mode) {
case MeshPrimitive::Mode::POINTS:
subMeshDescriptor.topology = MeshTopology::Points;
primitiveInfo.containsPoints = true;
} else {
break;
case MeshPrimitive::Mode::LINES:
subMeshDescriptor.topology = MeshTopology::Lines;
break;
case MeshPrimitive::Mode::TRIANGLES:
default:
subMeshDescriptor.topology = MeshTopology::Triangles;
break;
}
primitiveInfo.mode = primitive.mode;

subMeshDescriptor.indexStart = 0;
subMeshDescriptor.indexCount = indexCount;
Expand Down Expand Up @@ -1010,11 +994,16 @@ UnityPrepareRendererResources::prepareInLoadThread(
const std::int32_t len = meshes.Length();
std::vector<std::int32_t> instanceIDs;
for (int32_t i = 0; i < len; ++i) {
// Don't attempt to bake a physics mesh from a point cloud or
// from an invalid triangle mesh.
if (primitiveInfos[i].containsPoints ||
isDegenerateTriangleMesh(meshes[i])) {
// Don't attempt to bake a physics mesh from points, lines, or
// an invalid triangle mesh.
switch (primitiveInfos[i].mode) {
case CesiumGltf::MeshPrimitive::Mode::POINTS:
case CesiumGltf::MeshPrimitive::Mode::LINES:
continue;
default:
if (isDegenerateTriangleMesh(meshes[i])) {
continue;
}
}

instanceIDs.push_back(meshes[i].GetInstanceID());
Expand Down Expand Up @@ -1588,7 +1577,7 @@ void* UnityPrepareRendererResources::prepareInMainThread(
materialProperties);
}

if (primitiveInfo.containsPoints) {
if (primitiveInfo.mode == CesiumGltf::MeshPrimitive::Mode::POINTS) {
CesiumForUnity::CesiumPointCloudRenderer pointCloudRenderer =
primitiveGameObject
.AddComponent<CesiumForUnity::CesiumPointCloudRenderer>();
Expand Down Expand Up @@ -1619,13 +1608,19 @@ void* UnityPrepareRendererResources::prepareInMainThread(
}

if (createPhysicsMeshes) {
if (!primitiveInfo.containsPoints &&
!isDegenerateTriangleMesh(unityMesh)) {
// This should not trigger mesh baking for physics, because the
// meshes were already baked in the worker thread.
UnityEngine::MeshCollider meshCollider =
primitiveGameObject.AddComponent<UnityEngine::MeshCollider>();
meshCollider.sharedMesh(unityMesh);
switch (primitiveInfo.mode) {
case CesiumGltf::MeshPrimitive::Mode::POINTS:
case CesiumGltf::MeshPrimitive::Mode::LINES:
break;
default:
if (!isDegenerateTriangleMesh(unityMesh)) {
// This should not trigger mesh baking for physics, because the
// meshes were already baked in the worker thread.
UnityEngine::MeshCollider meshCollider =
primitiveGameObject.AddComponent<UnityEngine::MeshCollider>();
meshCollider.sharedMesh(unityMesh);
}
break;
}
}

Expand Down
5 changes: 2 additions & 3 deletions native~/src/Runtime/UnityPrepareRendererResources.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@ struct CreateModelOptions {
*/
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.
* @brief The mode of the glTF primitive.
*/
bool containsPoints = false;
int32_t mode = CesiumGltf::MeshPrimitive::Mode::TRIANGLES;

/**
* @brief Whether or not the primitive contains translucent vertex
Expand Down
Loading