From 6443579b51a715f0fca74db07427eee1d1f7da1e Mon Sep 17 00:00:00 2001 From: Ghislain Cottat Date: Thu, 3 Jul 2025 17:55:52 +0200 Subject: [PATCH] Enhancement: basic support for glTF LINES primitive mode ie list of segments: wouldn't be hard to support line loops and strips I guess, but I'd need a dataset to test that. No support for rendering with "attenuation" like point clouds: haven't looked at the custom vertex factory and shader yet to see how hard it would be to support lines. Probably need to change the name of CesiumGltfPointsComponent and proxy, unless it makes more sense to duplicate them. --- .../Private/CesiumGltfComponent.cpp | 24 +++++++++++++------ .../Private/CesiumGltfPointsComponent.cpp | 2 +- .../Private/CesiumGltfPointsComponent.h | 2 ++ .../Private/CesiumGltfPointsSceneProxy.cpp | 20 +++++++++++----- .../Private/CesiumGltfPointsSceneProxy.h | 4 +++- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp index e1036fd7b..d5ad8b3b4 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfComponent.cpp @@ -1328,6 +1328,7 @@ getIndices(const TIndexAccessor& indicesView, int32 primitiveMode) { break; case CesiumGltf::MeshPrimitive::Mode::TRIANGLES: case CesiumGltf::MeshPrimitive::Mode::POINTS: + case CesiumGltf::MeshPrimitive::Mode::LINES: default: indices.SetNum(static_cast::SizeType>(indicesView.size())); for (int32 i = 0; i < indicesView.size(); ++i) { @@ -1360,6 +1361,7 @@ static void loadPrimitive( switch (primitive.mode) { case CesiumGltf::MeshPrimitive::Mode::POINTS: + case CesiumGltf::MeshPrimitive::Mode::LINES: case CesiumGltf::MeshPrimitive::Mode::TRIANGLES: case CesiumGltf::MeshPrimitive::Mode::TRIANGLE_STRIP: case CesiumGltf::MeshPrimitive::Mode::TRIANGLE_FAN: @@ -1540,8 +1542,10 @@ static void loadPrimitive( bool needToGenerateFlatNormals = normalsAreRequired && !hasNormals; bool needToGenerateTangents = needsTangents && !hasTangents; bool duplicateVertices = needToGenerateFlatNormals || needToGenerateTangents; - duplicateVertices = duplicateVertices && - primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS; + duplicateVertices = + duplicateVertices && + primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS && + primitive.mode != CesiumGltf::MeshPrimitive::Mode::LINES; uint32 numVertices = duplicateVertices ? uint32(indices.Num()) : uint32(positionView.size()); @@ -1759,7 +1763,8 @@ static void loadPrimitive( section.MinVertexIndex = 0; section.MaxVertexIndex = numVertices - 1; section.bEnableCollision = - primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS; + primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS && + primitive.mode != CesiumGltf::MeshPrimitive::Mode::LINES; section.bCastShadow = true; section.MaterialIndex = 0; @@ -1786,7 +1791,8 @@ static void loadPrimitive( #if ENGINE_VERSION_5_5_OR_HIGHER // UE 5.5 requires that we do this in order to avoid a crash when ray // tracing is enabled. - if (primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS) { + if (primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS && + primitive.mode != CesiumGltf::MeshPrimitive::Mode::LINES) { // UE 5.5 requires that we do this in order to avoid a crash when ray // tracing is enabled. RenderData->InitializeRayTracingRepresentationFromRenderingLODs(); @@ -1800,7 +1806,7 @@ static void loadPrimitive( primitiveResult.transform = transform * yInvertMatrix * scaleMatrix; - if (primitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS && + if (section.bEnableCollision && options.pMeshOptions->pNodeOptions->pModelOptions->createPhysicsMeshes) { if (numVertices != 0 && indices.Num() != 0) { TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::ChaosCook) @@ -3040,9 +3046,12 @@ static void loadPrimitiveGameThreadPart( UStaticMeshComponent* pMesh = nullptr; ICesiumPrimitive* pCesiumPrimitive = nullptr; - if (meshPrimitive.mode == CesiumGltf::MeshPrimitive::Mode::POINTS) { + if (meshPrimitive.mode == CesiumGltf::MeshPrimitive::Mode::POINTS || + meshPrimitive.mode == CesiumGltf::MeshPrimitive::Mode::LINES) { UCesiumGltfPointsComponent* pPointMesh = NewObject(pGltf, componentName); + pPointMesh->bLinesList = + (meshPrimitive.mode == CesiumGltf::MeshPrimitive::Mode::LINES); pPointMesh->UsesAdditiveRefinement = tile.getRefine() == Cesium3DTilesSelection::TileRefine::Add; pPointMesh->GeometricError = static_cast(tile.getGeometricError()); @@ -3114,7 +3123,8 @@ static void loadPrimitiveGameThreadPart( // sense, but if Unreal will crash trying to generate ray tracing // information for a static mesh without triangles. pStaticMesh->bSupportRayTracing = - meshPrimitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS; + meshPrimitive.mode != CesiumGltf::MeshPrimitive::Mode::POINTS && + meshPrimitive.mode != CesiumGltf::MeshPrimitive::Mode::LINES; pMesh->SetStaticMesh(pStaticMesh); pStaticMesh->SetFlags( diff --git a/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.cpp b/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.cpp index a7e9fec3f..87611064d 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.cpp @@ -18,7 +18,7 @@ FPrimitiveSceneProxy* UCesiumGltfPointsComponent::CreateSceneProxy() { } FCesiumGltfPointsSceneProxy* Proxy = - new FCesiumGltfPointsSceneProxy(this, GetScene()->GetFeatureLevel()); + new FCesiumGltfPointsSceneProxy(this, GetScene()->GetFeatureLevel(), bLinesList); FCesiumGltfPointsSceneProxyTilesetData TilesetData; TilesetData.UpdateFromComponent(this); diff --git a/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.h b/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.h index 1feeb96d6..433fcc9f4 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.h +++ b/Source/CesiumRuntime/Private/CesiumGltfPointsComponent.h @@ -28,6 +28,8 @@ class UCesiumGltfPointsComponent : public UCesiumGltfPrimitiveComponent { // error. glm::vec3 Dimensions; + bool bLinesList = false; + // Override UPrimitiveComponent interface. virtual FPrimitiveSceneProxy* CreateSceneProxy() override; }; diff --git a/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.cpp b/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.cpp index f0aaa9379..e06e6447b 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.cpp +++ b/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.cpp @@ -26,6 +26,7 @@ void FCesiumGltfPointsSceneProxyTilesetData::UpdateFromComponent( UsesAdditiveRefinement = Component->UsesAdditiveRefinement; GeometricError = Component->GeometricError; Dimensions = Component->Dimensions; + bLinesList = Component->bLinesList; } SIZE_T FCesiumGltfPointsSceneProxy::GetTypeHash() const { @@ -35,11 +36,12 @@ SIZE_T FCesiumGltfPointsSceneProxy::GetTypeHash() const { FCesiumGltfPointsSceneProxy::FCesiumGltfPointsSceneProxy( UCesiumGltfPointsComponent* InComponent, - ERHIFeatureLevel::Type InFeatureLevel) + ERHIFeatureLevel::Type InFeatureLevel, + bool bLinesList) : FPrimitiveSceneProxy(InComponent), RenderData(InComponent->GetStaticMesh()->GetRenderData()), NumPoints(RenderData->LODResources[0].IndexBuffer.GetNumIndices()), - bAttenuationSupported( + bAttenuationSupported(!bLinesList && RHISupportsManualVertexFetch(GetScene().GetShaderPlatform())), TilesetData(), AttenuationVertexFactory( @@ -116,6 +118,7 @@ uint32 FCesiumGltfPointsSceneProxy::GetMemoryFootprint(void) const { void FCesiumGltfPointsSceneProxy::UpdateTilesetData( const FCesiumGltfPointsSceneProxyTilesetData& InTilesetData) { TilesetData = InTilesetData; + ensure(!TilesetData.bLinesList || !bAttenuationSupported); // see ctor } float FCesiumGltfPointsSceneProxy::GetGeometricError() const { @@ -208,17 +211,22 @@ void FCesiumGltfPointsSceneProxy::CreateMesh(FMeshBatch& Mesh) const { Mesh.VertexFactory = &RenderData->LODVertexFactories[0].VertexFactory; Mesh.MaterialRenderProxy = Material->GetRenderProxy(); Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative(); - Mesh.Type = PT_PointList; + Mesh.Type = TilesetData.bLinesList ? PT_LineList : PT_PointList; Mesh.DepthPriorityGroup = SDPG_World; Mesh.LODIndex = 0; Mesh.bCanApplyViewModeOverrides = false; Mesh.bUseAsOccluder = false; - Mesh.bWireframe = false; + Mesh.bWireframe = TilesetData.bLinesList; FMeshBatchElement& BatchElement = Mesh.Elements[0]; BatchElement.IndexBuffer = &RenderData->LODResources[0].IndexBuffer; - BatchElement.NumPrimitives = NumPoints; BatchElement.FirstIndex = 0; BatchElement.MinVertexIndex = 0; - BatchElement.MaxVertexIndex = BatchElement.NumPrimitives - 1; + BatchElement.MaxVertexIndex = NumPoints - 1; + if (TilesetData.bLinesList) { + ensure((NumPoints % 2) == 0); + BatchElement.NumPrimitives = NumPoints / 2; + } else { + BatchElement.NumPrimitives = NumPoints; + } } diff --git a/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.h b/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.h index 0b4295490..5df5482be 100644 --- a/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.h +++ b/Source/CesiumRuntime/Private/CesiumGltfPointsSceneProxy.h @@ -19,6 +19,7 @@ struct FCesiumGltfPointsSceneProxyTilesetData { bool UsesAdditiveRefinement; float GeometricError; glm::vec3 Dimensions; + bool bLinesList; FCesiumGltfPointsSceneProxyTilesetData(); @@ -36,7 +37,8 @@ class FCesiumGltfPointsSceneProxy final : public FPrimitiveSceneProxy { FCesiumGltfPointsSceneProxy( UCesiumGltfPointsComponent* InComponent, - ERHIFeatureLevel::Type InFeatureLevel); + ERHIFeatureLevel::Type InFeatureLevel, + bool bLinesList); virtual ~FCesiumGltfPointsSceneProxy();