diff --git a/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp b/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp index 5cdc7b3a6e1a739767650fb0e31daf6f3d3ce1ed..939db5dccab26c21de6ebf36f370415428318f1a 100644 --- a/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp +++ b/Source/InstancedMeshLineRendering/Private/GPUInstancedLineComponent.cpp @@ -55,8 +55,8 @@ void UGPUInstancedLineComponent::UpdateWholeTexture() { TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdateWholeTexture")) - // todo optimize height computation - FUpdateTextureRegion2D* Region = new FUpdateTextureRegion2D( + // todo optimize height computation + FUpdateTextureRegion2D* Region = new FUpdateTextureRegion2D( 0, 0, 0, @@ -271,11 +271,18 @@ void UGPUInstancedLineComponent::PostEditChangeChainProperty(FPropertyChangedCha check(AddedAtIndex != INDEX_NONE); check(LineIndex != INDEX_NONE); - // Set the point to the previous one instead of defaulting to 0,0,0 - if (AddedAtIndex > 0) // this *should* be always true + // Set the point to the previous one instead of defaulting to 0,0,0 - default to 0 if we insert it at the front + if (AddedAtIndex == 0) + EditorLines[LineIndex].Points[AddedAtIndex].Point = EditorLines[LineIndex].Points[AddedAtIndex + 1].Point; + else if(AddedAtIndex == EditorLines[LineIndex].Points.Num() - 1) EditorLines[LineIndex].Points[AddedAtIndex].Point = EditorLines[LineIndex].Points[AddedAtIndex - 1].Point; - - AddPoint(EditorLines[LineIndex].RespectiveLineId, EditorLines[LineIndex].Points[AddedAtIndex].Point); + else + { + EditorLines[LineIndex].Points[AddedAtIndex].Point = 0.5 * EditorLines[LineIndex].Points[AddedAtIndex - 1].Point + + 0.5 * EditorLines[LineIndex].Points[AddedAtIndex + 1].Point; + } + + InsertPointWithSameColor(EditorLines[LineIndex].RespectiveLineId, AddedAtIndex, EditorLines[LineIndex].Points[AddedAtIndex].Point); } else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Duplicate) @@ -341,9 +348,9 @@ void UGPUInstancedLineComponent::PostEditChangeChainProperty(FPropertyChangedCha } } } - else if(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName() == GET_MEMBER_NAME_CHECKED(FEditorPoint, Point)) + else if (PropertyChangedEvent.Property->GetName() == "X" || PropertyChangedEvent.Property->GetName() == "Y" || PropertyChangedEvent.Property->GetName() == "Z") { - if (PropertyChangedEvent.Property->GetName() == "X" || PropertyChangedEvent.Property->GetName() == "Y" || PropertyChangedEvent.Property->GetName() == "Z") + if (PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode() != nullptr && PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName() == GET_MEMBER_NAME_CHECKED(FEditorPoint, Point)) { if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet || PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive) { @@ -362,14 +369,20 @@ void UGPUInstancedLineComponent::PostEditChangeChainProperty(FPropertyChangedCha UpdatePoint(EditorLines[LineIndex].RespectiveLineId, PointIndex, EditorLines[LineIndex].Points[PointIndex].Point); } } - } - else if (PropertyChangedEvent.Property->GetFName() == "TextureWidth") + } + else if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(FEditorLineData, Color)) { - + const int32 LineIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName().ToString()); + SetLineColor(EditorLines[LineIndex].RespectiveLineId, EditorLines[LineIndex].Color); } - else if (PropertyChangedEvent.Property->GetFName() == "TextureHeight") + else if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(FEditorLineData, Width)) { - + const int32 LineIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName().ToString()); + SetLineWidth(EditorLines[LineIndex].RespectiveLineId, EditorLines[LineIndex].Width); + } + else if (PropertyChangedEvent.Property->GetFName() == "TextureWidth" || PropertyChangedEvent.Property->GetFName() == "TextureHeight") + { + ResizeTexture(TextureWidth, TextureHeight); } } Super::PostEditChangeChainProperty(PropertyChangedEvent); @@ -443,8 +456,54 @@ void UGPUInstancedLineComponent::ReserveMemory(int32 NumberOfSegments, int32 Num } +bool UGPUInstancedLineComponent::ResizeTexture(int32 Width, int32 Height) +{ + if (LinearLineData.Num() > Width * Height) + return false; + // what basically needs to be done is a complete re-calculation of the texture indices + + CurrentTextureMarker = { 0, 0 }; + CurrentTextureIndex = 0; + + TextureWidth = Width; + TextureHeight = Height; + + //int32 TextureIndex = 0; + + // Sequentially update the custom data and the line map. + // 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, GPULineArray>& Line : LineMap) + { + for (GPULineIndices& LineIndices : Line.Value) + { + // Key is instance id, value is texture index. + if(LineIndices.Key >= 0) + SetCustomDataValue(LineIndices.Key, 4, CurrentTextureIndex, false); + LineIndices.Value = CurrentTextureIndex; + CurrentTextureIndex++; + MoveTextureMarker(); + } + } + + // Recreate texture + UE_LOG(LogTemp, Display, TEXT("UGPUInstancedLineComponent::ResizeTexture - Creating New Texture")); + PositionTexture = UTexture2D::CreateTransient(TextureWidth, TextureHeight, PF_A32B32G32R32F); + // Allocate the texture RHI + PositionTexture->UpdateResource(); + + UpdateWholeTexture(); + + // Update material parameters and texture + DynamicLineMaterial->SetTextureParameterValue("PositionTexture", PositionTexture); + DynamicLineMaterial->SetScalarParameterValue("TextureWidth", TextureWidth); + + return true; +} + void UGPUInstancedLineComponent::InitializeLinesInBulk(int32 NumberOfLines, int32 NumberOfSegmentsPerLine, - TArray<FVector4>& Points, const TArray<FLinearColor>& Colors, const TArray<float>& Widths) + TArray<FVector4>& Points, const TArray<FLinearColor>& Colors, const TArray<float>& Widths) { if (LinearLineData.Num() != 0 || LineMap.Num() != 0 || GetInstanceCount() != 0) { @@ -1349,4 +1408,59 @@ bool UGPUInstancedLineComponent::RemovePoints(int32 LineId, int32 StartingPointI return false; } + +bool UGPUInstancedLineComponent::SetLineColor(int32 LineId, const FLinearColor& Color) +{ + const GPULineArray& Line = LineMap[LineId]; + + for (const GPULineIndices& LineIndices: Line) + { + const int32 InstanceId = LineIndices.Key; + if (InstanceId >= 0) + { + SetCustomDataValue(InstanceId, 0, Color.R, false); + SetCustomDataValue(InstanceId, 1, Color.G, false); + SetCustomDataValue(InstanceId, 2, Color.B, false); + } + } + MarkRenderStateDirty(); + return true; +} + +bool UGPUInstancedLineComponent::SetSegmentColor(int32 LineId, int32 SegmentId, const FLinearColor& Color) +{ + // Get Instance Id: + + const int32 InstanceId = LineMap[LineId][SegmentId].Key; + + SetCustomDataValue(InstanceId, 0, Color.R, false); + SetCustomDataValue(InstanceId, 1, Color.G, false); + SetCustomDataValue(InstanceId, 2, Color.B, true); + + return true; +} + +bool UGPUInstancedLineComponent::SetLineWidth(int32 LineId, float Width) +{ + const GPULineArray& Line = LineMap[LineId]; + + for (const GPULineIndices& LineIndices : Line) + { + const int32 InstanceId = LineIndices.Key; + if (InstanceId >= 0) + { + SetCustomDataValue(InstanceId, 3, Width, false); + } + } + MarkRenderStateDirty(); + return true; +} + +bool UGPUInstancedLineComponent::SetSegmentWidth(int32 LineId, int32 SegmentId, float Width) +{ + const int32 InstanceId = LineMap[LineId][SegmentId].Key; + + SetCustomDataValue(InstanceId, 3, Width, false); + return true; +} //#pragma optimize("", on) diff --git a/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h b/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h index 712c07014229e0bda72df50de6ae415d290e59b1..9c87a461585bfb09f7d268f69f863b466586d7e6 100644 --- a/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h +++ b/Source/InstancedMeshLineRendering/Public/GPUInstancedLineComponent.h @@ -145,6 +145,18 @@ public: UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") void ReserveMemory(int32 NumberOfSegments, int32 NumberOfLines); + /** + * 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. + * + * @param Width [in] New texture width. + * @param Height [in] New texture height. + * + * @return bool Returns true on success, false on failure. + */ + UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") + bool ResizeTexture(int32 Width, int32 Height); + /** * Initializes the component with a number of lines with the same length. Existing data will be cleared. The data with which to initialize the texture needs to be already in a consistent format and @@ -335,6 +347,54 @@ public: // todo UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") bool RemovePoints(int32 LineId, int32 StartingPointId, int32 NumberOfPoints); + + + /** + * This function sets the color of a whole line. + * + * @param LineId [in] The id of the line. + * @param Color [in] The new color. + * + * @return bool Returns true on successful update. + */ + UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") + bool SetLineColor(int32 LineId, const FLinearColor& Color); + + /** + * This function sets the color of a single segment in a line. + * + * @param LineId [in] The id of the line. + * @param SegmentId [in] The index of the segment. + * @param Color [in] The new color. + * + * @return bool Returns true on successful update. + */ + UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") + bool SetSegmentColor(int32 LineId, int32 SegmentId, const FLinearColor& Color); + + /** + * This function sets the width of a whole line. + * + * @param LineId [in] The id of the line. + * @param Width [in] The new width. + * + * @return bool Returns true on successful update. + */ + UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") + bool SetLineWidth(int32 LineId, float Width); + + /** + * This function sets the width of a single segment in a line. + * + * @param LineId [in] The id of the line. + * @param SegmentId [in] The index of the segment. + * @param Width [in] The new width. + * + * @return bool Returns true on successful update. + */ + UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") + bool SetSegmentWidth(int32 LineId, int32 SegmentId, float Width); + // todo UFUNCTION(BlueprintCallable, Category = "Components|InstancedLineComponent") @@ -349,6 +409,14 @@ public: return LineMap.Num(); } + + /** + * Returns the number of points (not segments) of a given line. + * + * @param LineId [in] Id of the line + * + * @return int32 Number of points. + */ UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Components|InstancedLineComponent") int32 GetNumberOfPointsInLine(int32 LineId) const { @@ -361,10 +429,10 @@ public: UPROPERTY(BlueprintReadOnly, VisibleAnywhere) UTexture2D* PositionTexture; - UPROPERTY(BlueprintReadWrite, EditAnywhere) + UPROPERTY(BlueprintReadOnly, EditAnywhere) int32 TextureWidth; - UPROPERTY(BlueprintReadWrite, EditAnywhere) + UPROPERTY(BlueprintReadOnly, EditAnywhere) int32 TextureHeight; UPROPERTY(BlueprintReadOnly, VisibleAnywhere)