diff --git a/Content/DynamicLineMaterial-Unoptimized.uasset b/Content/DynamicLineMaterial-Unoptimized.uasset new file mode 100644 index 0000000000000000000000000000000000000000..00cd4efcf8507ee0abbf45cf2ad43e44494c54b2 Binary files /dev/null and b/Content/DynamicLineMaterial-Unoptimized.uasset differ diff --git a/Content/DynamicLineMaterial.uasset b/Content/DynamicLineMaterial.uasset index e4529ef79a4492ebb3bb941980aab51f06487134..caea7d8737eb5a2668617a54dfc47b98154aecdf 100644 Binary files a/Content/DynamicLineMaterial.uasset and b/Content/DynamicLineMaterial.uasset differ diff --git a/Content/DynamicLineMaterialNewBackup.uasset b/Content/DynamicLineMaterialNewBackup.uasset index 8941aaff1f87b80799abf485b6497bb4d5b034be..cd0056d098d43cf45c8aafbcf08c79609961a3f3 100644 Binary files a/Content/DynamicLineMaterialNewBackup.uasset and b/Content/DynamicLineMaterialNewBackup.uasset differ diff --git a/Content/DynamicLineMaterialOldBackup.uasset b/Content/DynamicLineMaterialOldBackup.uasset index 91d3640c2277496afadde2c9499505066c57455e..2187dc645856e8d11f7368184d65fe2f9fc3932c 100644 Binary files a/Content/DynamicLineMaterialOldBackup.uasset and b/Content/DynamicLineMaterialOldBackup.uasset differ diff --git a/Content/DynamicLineMaterialTesting.uasset b/Content/DynamicLineMaterialTesting.uasset index 9b4c6fd06cc86ca8e0d37fe60f303c7ef5892f7d..79d7b52111d4169ebbaba1e1eef3c472ce6f4c40 100644 Binary files a/Content/DynamicLineMaterialTesting.uasset and b/Content/DynamicLineMaterialTesting.uasset differ diff --git a/Content/line.uasset b/Content/line.uasset index e53730b60727f78b76728d091c3f56474d3dc599..5eef0bfd3997b72daff8b796b43dfe1f06c9375d 100644 Binary files a/Content/line.uasset and b/Content/line.uasset differ diff --git a/Content/optimized_line.uasset b/Content/optimized_line.uasset new file mode 100644 index 0000000000000000000000000000000000000000..d03b0c31a0886d08d020f9d0b27d847d777b0140 Binary files /dev/null and b/Content/optimized_line.uasset differ diff --git a/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp b/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp index 687d43f5644036086f2697aab03c51a2cc180176..17a5b81849fd5ec8ad14418ae59962b3027835de 100644 --- a/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp +++ b/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp @@ -13,7 +13,7 @@ UGPUInstancedLineComponent::UGPUInstancedLineComponent(const FObjectInitializer& CurrentTextureMarker = FIntPoint(0, 0); CurrentTextureIndex = 0, - NextFreeId = 0; + //NextFreeId = 0; TextureWidth = 2048; TextureHeight = 2048; @@ -27,7 +27,7 @@ UGPUInstancedLineComponent::UGPUInstancedLineComponent(const FObjectInitializer& SetCanEverAffectNavigation(false); - static ConstructorHelpers::FObjectFinder<UStaticMesh>LineMeshAsset(TEXT("StaticMesh'/InstancedMeshLineRendering/line.line'")); + static ConstructorHelpers::FObjectFinder<UStaticMesh>LineMeshAsset(TEXT("StaticMesh'/InstancedMeshLineRendering/optimized_line.optimized_line'")); static ConstructorHelpers::FObjectFinder<UMaterial>LineMaterialAsset(TEXT("Material'/InstancedMeshLineRendering/DynamicLineMaterial.DynamicLineMaterial'")); UStaticMesh* LineAsset = LineMeshAsset.Object; UMaterial* LineMaterial = LineMaterialAsset.Object; @@ -513,6 +513,13 @@ void UGPUInstancedLineComponent::ReserveMemory(int32 NumberOfSegments, int32 Num LinearLineData.Reserve(NumberOfLines * (NumberOfSegments + 1)); } +void UGPUInstancedLineComponent::ReserveMemoryWithoutSegments(int32 NumberOfLines, int32 NumberOfTotalPoints) +{ + PreAllocateInstancesMemory(NumberOfTotalPoints - NumberOfLines); // Allocates a bit too much + LineMap.Reserve(NumberOfTotalPoints - NumberOfLines); + LinearLineData.Reserve(NumberOfTotalPoints); +} + bool UGPUInstancedLineComponent::ResizeTexture(int32 Width, int32 Height) { if (LinearLineData.Num() > Width * Height) @@ -531,9 +538,9 @@ bool UGPUInstancedLineComponent::ResizeTexture(int32 Width, int32 Height) // This might be a bit awkward as the line map is a *map*, and not just an array. // Some lines might be re-ordered in the texture, but that should be fine. - for(TPair<int32, FGPULineArray>& Line : LineMap) + for(int32 LineId = 0; LineId < LineMap.Num(); ++LineId)// TPair<int32, FGPULineArray>& Line : LineMap) { - for (FGPULineIndices& LineIndices : Line.Value.IndexArray) + for (FGPULineIndices& LineIndices : LineMap[LineId].IndexArray) { // Key is instance id, value is texture index. if(LineIndices.InstanceIndex >= 0) @@ -567,7 +574,7 @@ void UGPUInstancedLineComponent::InitializeLinesInBulk(int32 NumberOfLines, int3 LinearLineData.Empty(); // Reset LineMap.Empty(); - NextFreeId = 0; + //NextFreeId = 0; CurrentTextureIndex = 0; CurrentTextureMarker = FIntPoint(0, 0); ClearInstances(); @@ -590,8 +597,8 @@ void UGPUInstancedLineComponent::InitializeLinesInBulk(int32 NumberOfLines, int3 { const FLinearColor Color = UniformColor ? Colors[0] : Colors[LineIndex]; const float Width = UniformWidth ? Widths[0] : Widths[LineIndex]; - - FGPULineArray& NewLineArray = LineMap.Add(NextFreeId, FGPULineArray()); + int32 Idx = LineMap.Add(FGPULineArray()); + FGPULineArray& NewLineArray = LineMap[Idx]; NewLineArray.IndexArray.Reserve(NumberOfSegmentsPerLine + 1); for (int32 PointIndex = 0; PointIndex < NumberOfSegmentsPerLine; ++PointIndex) @@ -601,7 +608,7 @@ void UGPUInstancedLineComponent::InitializeLinesInBulk(int32 NumberOfLines, int3 SetCustomDataValue(InstanceId, 0, Color.R, false); SetCustomDataValue(InstanceId, 1, Color.G, false); SetCustomDataValue(InstanceId, 2, Color.B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); SetCustomDataValue(InstanceId, 4, static_cast<float>(CurrentTextureIndex), false); // Segment Start NewLineArray.IndexArray.Add({InstanceId, LineIndex * (NumberOfSegmentsPerLine + 1) + PointIndex}); CurrentTextureIndex++; @@ -611,7 +618,7 @@ void UGPUInstancedLineComponent::InitializeLinesInBulk(int32 NumberOfLines, int3 NewLineArray.IndexArray.Add({-1, LineIndex * (NumberOfSegmentsPerLine + 1) + NumberOfSegmentsPerLine}); MoveTextureMarker(); CurrentTextureIndex++; - NextFreeId++; + //NextFreeId++; } MarkRenderStateDirty(); @@ -627,11 +634,12 @@ int32 UGPUInstancedLineComponent::AddLine(const TArray<FVector>& Line, FLinearCo return -1; } // 1. - FGPULineArray& NewLineArray = LineMap.Add(NextFreeId, FGPULineArray()); + const int32 LineId = LineMap.Add(FGPULineArray()); + FGPULineArray& NewLineArray = LineMap[LineId]; NewLineArray.IndexArray.Reserve(Line.Num()); - const int32 LineId = NextFreeId; - NextFreeId++; // TODO Fix Id system as this can possibly overflow. + //const int32 LineId = NextFreeId; + //NextFreeId++; // TODO Fix Id system as this can possibly overflow. @@ -646,7 +654,7 @@ int32 UGPUInstancedLineComponent::AddLine(const TArray<FVector>& Line, FLinearCo SetCustomDataValue(InstanceId, 0, Color.R, false); SetCustomDataValue(InstanceId, 1, Color.G, false); SetCustomDataValue(InstanceId, 2, Color.B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); SetCustomDataValue(InstanceId, 4, static_cast<float>(CurrentTextureIndex), false); // Segment Start NewLineArray.IndexArray.Add({InstanceId, LinearLineData.Num() + PointIndex}); CurrentTextureIndex++; @@ -685,6 +693,70 @@ int32 UGPUInstancedLineComponent::AddLine(const TArray<FVector>& Line, FLinearCo return LineId; } +int32 UGPUInstancedLineComponent::AddLine(TArray<FVector4>& Line, FLinearColor Color, float Width) +{ + if (Line.Num() < 2) + { + UE_LOG(LogLineRendering, Error, TEXT("UGPUInstancedLineComponent::AddLine : Can't add line with less than 2 points.")); // Actually we can! + return -1; + } + // 1. + const int32 LineId = LineMap.Add(FGPULineArray()); + FGPULineArray& NewLineArray = LineMap[LineId]; + NewLineArray.IndexArray.Reserve(Line.Num()); + + + + const int32 NumberSegments = Line.Num() - 1; + + const FIntPoint InitialTextureMarker = CurrentTextureMarker; + + for (int32 PointIndex = 0; PointIndex < NumberSegments; ++PointIndex) + { + // Add with a dummy transform + int32 InstanceId = AddInstance(FTransform::Identity); + SetCustomDataValue(InstanceId, 0, Color.R, false); + SetCustomDataValue(InstanceId, 1, Color.G, false); + SetCustomDataValue(InstanceId, 2, Color.B, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); + SetCustomDataValue(InstanceId, 4, static_cast<float>(CurrentTextureIndex), false); // Segment Start + NewLineArray.IndexArray.Add({ InstanceId, LinearLineData.Num() + PointIndex }); + CurrentTextureIndex++; + + MoveTextureMarker(); + } + // Add the last point which does not correspond to a transform/instance, but needs to be added to the texture still such that the second last instance can read it: + NewLineArray.IndexArray.Add({ -1, LinearLineData.Num() + NumberSegments }); + MoveTextureMarker(); + CurrentTextureIndex++; + + // Recreate the data on the heap to allow asynchronous texture update. + TArray<FVector4>* TextureData = new TArray<FVector4>(MoveTemp(Line)); + + + // Store the points in a linear array here + LinearLineData.Append(*TextureData); + + int32 NumRegions = 0; + FUpdateTextureRegion2D* Regions = CalculateTextureRegions(InitialTextureMarker, TextureData->Num(), NumRegions); + + const uint32 Pitch = TextureWidth; + PositionTexture->UpdateTextureRegions(0, NumRegions, Regions, Pitch * sizeof(FVector4), sizeof(FVector4), (uint8*)TextureData->GetData(), + [](auto InTextureData, auto InRegions) + { + // Clean up the copied data + delete InTextureData; + delete InRegions; + }); + + //UE_LOG(LogLineRendering, Display, TEXT("Added Line #%d"), LineId); // Actually we can! + + + return LineId; + + +} + int32 UGPUInstancedLineComponent::AddLineFromEditorData(FEditorLineData& LineData) { const TArray<FEditorPoint>* EditorLineDataPtr = &LineData.Points; @@ -721,7 +793,7 @@ bool UGPUInstancedLineComponent::AddPoint(int32 LineId, const FVector& Point) SetCustomDataValue(InstanceId, 0, R, false); SetCustomDataValue(InstanceId, 1, G, false); SetCustomDataValue(InstanceId, 2, B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); // Update the latest dummy entry to point to the actual segment. Line.IndexArray.Last().InstanceIndex = InstanceId; @@ -731,7 +803,7 @@ bool UGPUInstancedLineComponent::AddPoint(int32 LineId, const FVector& Point) FVector4& PreviousLastPoint = LinearLineData[Indices.TextureIndex]; PreviousLastPoint.W = 1.0; // This isn't the line end anymore. - if(LineId == NextFreeId - 1) + if(LineId == LineMap.Num() - 1) { LinearLineData.Add(FVector4(Point, -1)); // This is the last point now. @@ -793,10 +865,10 @@ bool UGPUInstancedLineComponent::AddPoint(int32 LineId, const FVector& Point) const int32 NewPointTextureIndex = Indices.TextureIndex + 1; - // Update all LineMap Indices that have a texture index larger than the newly inserted point. Todo: Make more efficient, maybe remove map and swap to array: - for (TPair<int32, FGPULineArray>& Pair : LineMap) + // Update all LineMap Indices that have a texture index larger than the newly inserted point. + //Todo: Make more efficient + for (FGPULineArray& LineArray : LineMap) { - FGPULineArray& LineArray = Pair.Value; if (LineArray.IndexArray[0].TextureIndex >= NewPointTextureIndex) { // Need to increase all indices by 1 and adjust the custom data @@ -842,7 +914,7 @@ bool UGPUInstancedLineComponent::InsertPoint(int32 LineId, int32 SegmentId, cons SetCustomDataValue(InstanceId, 0, Color.R, false); SetCustomDataValue(InstanceId, 1, Color.G, false); SetCustomDataValue(InstanceId, 2, Color.B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); LinearLineData.Insert(FVector4(Point), LinearDataIndex); @@ -865,9 +937,8 @@ bool UGPUInstancedLineComponent::InsertPoint(int32 LineId, int32 SegmentId, cons } // Update all following lines - for (TPair<int32, FGPULineArray>& Pair : LineMap) + for (FGPULineArray& LineArray : LineMap) { - FGPULineArray& LineArray = Pair.Value; if (LineArray.IndexArray[0].TextureIndex > LinearDataIndex) { // Need to increase all indices by 1 and adjust the custom data @@ -927,7 +998,7 @@ bool UGPUInstancedLineComponent::AddPoints(int32 LineId, const TArray<FVector>& FVector4& PreviousLastPoint = LinearLineData[Indices.TextureIndex]; PreviousLastPoint.W = 1.0; // This isn't the line end anymore. - if (LineId == NextFreeId - 1) + if (LineId == LineMap.Num() - 1) { for (int32 PointIndex = 0; PointIndex < Points.Num(); ++PointIndex) { @@ -936,7 +1007,7 @@ bool UGPUInstancedLineComponent::AddPoints(int32 LineId, const TArray<FVector>& SetCustomDataValue(InstanceId, 0, R, false); SetCustomDataValue(InstanceId, 1, G, false); SetCustomDataValue(InstanceId, 2, B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); SetCustomDataValue(InstanceId, 4, static_cast<float>(PointTextureIndex + PointIndex), false); // Update the latest dummy entry to point to the actual segment. @@ -987,9 +1058,8 @@ bool UGPUInstancedLineComponent::AddPoints(int32 LineId, const TArray<FVector>& const int32 NewPointTextureIndex = Indices.TextureIndex + 1; // Update all LineMap Indices that have a texture index larger than the newly inserted point. Todo: Make more efficient, maybe remove map and swap to array: - for (TPair<int32, FGPULineArray>& Pair : LineMap) + for (FGPULineArray& LineArray : LineMap) { - FGPULineArray& LineArray = Pair.Value; if (LineArray.IndexArray[0].TextureIndex >= NewPointTextureIndex) { // Need to increase all indices by 1 and adjust the custom data @@ -1008,7 +1078,7 @@ bool UGPUInstancedLineComponent::AddPoints(int32 LineId, const TArray<FVector>& SetCustomDataValue(InstanceId, 0, R, false); SetCustomDataValue(InstanceId, 1, G, false); SetCustomDataValue(InstanceId, 2, B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); SetCustomDataValue(InstanceId, 4, static_cast<float>(NewPointTextureIndex - 1 + PointIndex), false); // Update the latest dummy entry to point to the actual segment. @@ -1073,7 +1143,7 @@ bool UGPUInstancedLineComponent::InsertPoints(int32 LineId, int32 SegmentId, con SetCustomDataValue(InstanceId, 0, R, false); SetCustomDataValue(InstanceId, 1, G, false); SetCustomDataValue(InstanceId, 2, B, false); - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); Line.IndexArray.Insert({ InstanceId, LinearDataIndex + PointIndex }, SegmentId + PointIndex); SetCustomDataValue(InstanceId, 4, LinearDataIndex + PointIndex, false); @@ -1089,9 +1159,9 @@ bool UGPUInstancedLineComponent::InsertPoints(int32 LineId, int32 SegmentId, con } // Update all following lines - for (TPair<int32, FGPULineArray>& Pair : LineMap) + for (FGPULineArray& LineArray : LineMap) { - FGPULineArray& LineArray = Pair.Value; + //FGPULineArray& LineArray = Pair.Value; if (LineArray.IndexArray[0].TextureIndex > LinearDataIndex) { // Need to increase all indices by 1 and adjust the custom data @@ -1376,12 +1446,12 @@ bool UGPUInstancedLineComponent::RemoveLine(int32 LineId) // Remove linear data: LinearLineData.RemoveAt(LineTextureIndex, LineLength); // Remove line from map, this invalidates above Line reference. - LineMap.Remove(LineId); + LineMap.RemoveAt(LineId); // Update all following lines - for (TPair<int32, FGPULineArray>& Pair : LineMap) + for (FGPULineArray& LineArray : LineMap) { - FGPULineArray& LineArray = Pair.Value; + //FGPULineArray& LineArray = Pair.Value; for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i) { if (LineArray.IndexArray[i].InstanceIndex >= InstancesToRemove[0]) @@ -1480,9 +1550,8 @@ bool UGPUInstancedLineComponent::RemovePoint(int32 LineId, int32 PointId) Line.IndexArray.RemoveAt(PointId); // Update all following lines - for (TPair<int32, FGPULineArray>& Pair : LineMap) + for (FGPULineArray& LineArray : LineMap) { - FGPULineArray& LineArray = Pair.Value; for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i) { if (LineArray.IndexArray[i].InstanceIndex >= RemovedInstanceId) @@ -1558,7 +1627,7 @@ bool UGPUInstancedLineComponent::SetLineWidth(int32 LineId, float Width) const int32 InstanceId = LineIndices.InstanceIndex; if (InstanceId >= 0) { - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); } } MarkRenderStateDirty(); @@ -1569,7 +1638,7 @@ bool UGPUInstancedLineComponent::SetSegmentWidth(int32 LineId, int32 SegmentId, { const int32 InstanceId = LineMap[LineId].IndexArray[SegmentId].InstanceIndex; - SetCustomDataValue(InstanceId, 3, Width, false); + SetCustomDataValue(InstanceId, 3, Width / 2.0f, false); return true; } //#pragma optimize("", on) diff --git a/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h b/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h index 2dbfe14ec283739aa35f2573988c2d2cc931052d..a949141c4c7a7e15b5fe14dff31c0c2e31038f81 100644 --- a/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h +++ b/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h @@ -155,6 +155,10 @@ public: UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") void ReserveMemory(int32 NumberOfSegments, int32 NumberOfLines); + + void ReserveMemoryWithoutSegments(int32 NumberOfLines, int32 NumberOfTotalPoints); + + /** * Manually resized the texture to the specified width and height. Returns false if the texture couldn't be resized, e.g. if more lines are * being rendered than can be displayed in the texture. @@ -198,6 +202,9 @@ public: UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") int32 AddLine(const TArray<FVector>& Line, FLinearColor Color, float Width = 1.0); + + int32 AddLine(TArray<FVector4>& Line, FLinearColor Color, float Width = 1.0); + /** * Adds a line and returns the respective ID of the line, which can be used to identify it for updating, modifying and removing. * @@ -462,7 +469,7 @@ public: UMaterialInterface* LineMaterialInterface; UPROPERTY() - TMap<int32, FGPULineArray> LineMap; + TArray<FGPULineArray> LineMap; //private: @@ -475,8 +482,8 @@ public: UPROPERTY() TArray<int32> EditorLineIds; - UPROPERTY() - int32 NextFreeId; + //UPROPERTY() + //int32 NextFreeId; UPROPERTY() int32 CurrentTextureIndex;