Skip to content
Snippets Groups Projects
Select Git revision
  • b641236291267ff77128a2fcea57a0de910ec0ac
  • 4.26 default protected
  • dev/5.1
  • fix/packaging-friendly
  • develop
  • feature/tesselation
  • cleanup/feature
7 results

GPUInstancedLineComponent.cpp

Blame
  • GPUInstancedLineComponent.cpp 53.10 KiB
    // Fill out your copyright notice in the Description page of Project Settings.
    
    
    #include "GPUInstancedLineComponent.h"
    #include "InstancedMeshLineRendering.h" // Only needed for logging
    
    //#pragma optimize("", on)
    
    
    UGPUInstancedLineComponent::UGPUInstancedLineComponent(const FObjectInitializer& ObjectInitializer)
    {
    	NumCustomDataFloats = 5;
    	
    	CurrentTextureMarker = FIntPoint(0, 0);
    	CurrentTextureIndex = 0,
    	//NextFreeId = 0;
    
    	TextureWidth = 2048;
    	TextureHeight = 2048;
    
    	SetWorldTransform(FTransform::Identity);
    	SetUsingAbsoluteLocation(true);
    	
    	SetCastShadow(false);
    	SetCollisionEnabled(ECollisionEnabled::NoCollision);
    	SetGenerateOverlapEvents(false);
    
    	SetCanEverAffectNavigation(false);
    	
    	static ConstructorHelpers::FObjectFinder<UStaticMesh>LineMeshAsset(TEXT("StaticMesh'/InstancedMeshLineRendering/line_subdivided5.line_subdivided5'"));
    	static ConstructorHelpers::FObjectFinder<UMaterial>LineMaterialAsset(TEXT("Material'/InstancedMeshLineRendering/DynamicLineMaterial.DynamicLineMaterial'"));
    	UStaticMesh* LineAsset = LineMeshAsset.Object;
    	UMaterial* LineMaterial = LineMaterialAsset.Object;
    	SetStaticMesh(LineAsset);
    	SetMaterial(0, LineMaterial);
    
    	
    	LineMaterialInterface = LoadObject<UMaterialInterface>(NULL, TEXT("/InstancedMeshLineRendering/DynamicLineMaterial.DynamicLineMaterial"), NULL, LOAD_None, NULL);
    	//DynamicLineMaterial = UMaterialInstanceDynamic::Create(LineMaterialInterface, GetTransientPackage());
    	SetMaterial(0, DynamicLineMaterial);
    	//SetMobility(EComponentMobility::Static);
    
    #if WITH_EDITOR
    	bAutoActivate = true;
    	PrimaryComponentTick.bCanEverTick = true;
    	bTickInEditor = true;
    	SetComponentTickEnabled(true);
    #else
    	PrimaryComponentTick.bCanEverTick = false;
    	bTickInEditor = false;
    	SetComponentTickEnabled(false);
    #endif
    
    }
    
    UGPUInstancedLineComponent::~UGPUInstancedLineComponent()
    {
    	ReleaseData();
    }
    
    // Not implemented yet because the data structure isn't clear yet.
    void UGPUInstancedLineComponent::ReleaseData()
    {
    }
    
    void UGPUInstancedLineComponent::UpdateWholeTexture() const
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdateWholeTexture"))
    
    	// todo optimize height computation
    	FUpdateTextureRegion2D* Region = new FUpdateTextureRegion2D(
    			0,
    			0,
    			0,
    			0, FMath::Min(LinearLineData.Num(), TextureWidth), FMath::CeilToInt(static_cast<float>(LinearLineData.Num()) / static_cast<float>(TextureWidth)));
    	
    	TArray<FVector4>* TextureData = new TArray<FVector4>(LinearLineData);
    
    	// Needed for packaging - works without this in the editor but crashes when packaging.
    	TextureData->SetNumUninitialized(TextureWidth * TextureHeight, false);
    
    	PositionTexture->UpdateTextureRegions(0, 1, Region, TextureWidth * sizeof(FVector4), sizeof(FVector4), (uint8*)TextureData->GetData(),
    		[](auto InTextureData, auto InRegions)
    	{	
    		delete InTextureData;
    		delete InRegions;
    	});
    }
    
    FLinearColor UGPUInstancedLineComponent::GetLineColor(int32 LineId)
    {
    	const float R = PerInstanceSMCustomData[LineMap[LineId].IndexArray[0].InstanceIndex * NumCustomDataFloats];
    	const float G = PerInstanceSMCustomData[LineMap[LineId].IndexArray[0].InstanceIndex * NumCustomDataFloats + 1];
    	const float B = PerInstanceSMCustomData[LineMap[LineId].IndexArray[0].InstanceIndex * NumCustomDataFloats + 2];
    	return { R, G, B };
    }
    
    int32 UGPUInstancedLineComponent::AddNewSegmentInstance(const FLinearColor& Color, float Width, int32 Index)
    {
    	// Add with a dummy transform
    	const 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>(Index), false); // Segment Start
    	return InstanceId;
    }
    
    void UGPUInstancedLineComponent::UpdateTexture(const FIntPoint& StartIndex, int32 NumberOfPoints, uint8* SrcData, bool bMarkRenderStateDirty)
    {
    	int32 NumRegions = 0;
    	FUpdateTextureRegion2D* Regions = CalculateTextureRegions(StartIndex, NumberOfPoints, NumRegions);
    
    	const uint32 Pitch = TextureWidth;
    	PositionTexture->UpdateTextureRegions(0, NumRegions, Regions, Pitch * sizeof(FVector4), sizeof(FVector4), SrcData,
    		[](auto InTextureData, auto InRegions)
    	{
    		// Clean up the copied data
    		delete InTextureData;
    		delete InRegions;
    	});
    
    	// Probably not a good place to call this
    	if(bMarkRenderStateDirty)
    		MarkRenderStateDirty();
    }
    
    void UGPUInstancedLineComponent::Init()
    {	
    	if (PositionTexture == nullptr)
    	{
    		UE_LOG(LogLineRendering, Display, TEXT("UGPUInstancedLineComponent::Init Creating Texture"));
    		PositionTexture = UTexture2D::CreateTransient(TextureWidth, TextureHeight, PF_A32B32G32R32F);
    		//// Allocate the texture RHI
    		PositionTexture->UpdateResource();
    		UpdateWholeTexture();
    	}
    	if (DynamicLineMaterial == nullptr) // FOR WHATEVER REASON I HAVE NO IDEA
    	{
    		//UE_LOG(LogLineRendering, Display, TEXT("UGPUInstancedLineComponent::Init MID was nullptr!"));
    		DynamicLineMaterial = UMaterialInstanceDynamic::Create(LineMaterialInterface, GetTransientPackage());
    		SetMaterial(0, DynamicLineMaterial);
    	}
    
    	DynamicLineMaterial->SetTextureParameterValue("PositionTexture", PositionTexture);
    	DynamicLineMaterial->SetScalarParameterValue("TextureWidth", TextureWidth);
    	//UpdateWholeTexture();
    
    	bIsInitialized = true;
    	
    }
    
    void UGPUInstancedLineComponent::UpdateAllEditorLines()
    {
    	for (const FEditorLineData& EditorLine : EditorLines)
    	{
    		if (EditorLine.RespectiveLineId != -1)
    		{
    			UpdateLineFromEditorData(EditorLine);
    		}
    		else
    		{
    			UE_LOG(LogLineRendering, Error, TEXT("UGPUInstancedLineComponent::UpdateAllEditorLines : An editor line wasn't registered before updating it, ignoring!"));
    		}
    	}
    }
    
    FUpdateTextureRegion2D* UGPUInstancedLineComponent::CalculateTextureRegions(const FIntPoint& StartIndex,
                                                                                int32 NumberOfPoints, int32& NumberOfRegionsOut)
    {	
    	// Calculate the first consecutive region:
    	int32 RemainingPoints = NumberOfPoints;
    	const uint32 TextureWidthU = static_cast<uint32>(TextureWidth);
    	
    	TArray<FUpdateTextureRegion2D>* Regions = new TArray<FUpdateTextureRegion2D>();
    	Regions->AddZeroed(1);
    
    	(*Regions)[0].DestX = StartIndex.X;
    	(*Regions)[0].DestY = StartIndex.Y;
    	(*Regions)[0].SrcX = 0; //InitialTextureMarker.X;
    	(*Regions)[0].SrcY = 0; // InitialTextureMarker.Y;
    	(*Regions)[0].Width = FMath::Min(NumberOfPoints, TextureWidth - static_cast<int32>((*Regions)[0].DestX));
    	(*Regions)[0].Height = StartIndex.X == 0 ? (NumberOfPoints / TextureWidth) : 1;
    
    	checkf((*Regions)[0].DestX + (*Regions)[0].Width <= TextureWidthU, TEXT("Region[0] out of bounds on X. Texture: %i, %i, %i"), (*Regions)[0].DestX, (*Regions)[0].Width, PositionTexture->GetSizeX());
    	
    	RemainingPoints -= (*Regions)[0].Height == 1 ? (*Regions)[0].Width : (*Regions)[0].Width * (*Regions)[0].Height;
    	if(RemainingPoints == 0)
    	{
    		NumberOfRegionsOut = 1;
    		return Regions->GetData();
    	}
    
    	Regions->AddZeroed(1);
    	(*Regions)[1].DestX = 0;
    	(*Regions)[1].DestY = (*Regions)[0].DestY + (*Regions)[0].Height;
    	(*Regions)[1].SrcX = NumberOfPoints - RemainingPoints; // InitialTextureMarker.Y;
    	(*Regions)[1].SrcY = 0; //InitialTextureMarker.X;
    	(*Regions)[1].Width = FMath::Min(RemainingPoints,TextureWidth - static_cast<int32>((*Regions)[1].DestX));
    	RemainingPoints -= (*Regions)[1].Width;
    	(*Regions)[1].Height = RemainingPoints == 0 ? 1 : (RemainingPoints / TextureWidth);
    	RemainingPoints -= (*Regions)[1].Width * ((*Regions)[1].Height - 1);
    
    	checkf((*Regions)[1].DestX + (*Regions)[1].Width <= TextureWidthU, TEXT("Region[1] out of bounds on X. Texture: %i, %i, %i"), (*Regions)[1].DestX, (*Regions)[1].Width, PositionTexture->GetSizeX());
    
    	if (RemainingPoints == 0)
    	{
    		NumberOfRegionsOut = 2;
    		
    		return Regions->GetData();
    	}
    	else if (RemainingPoints >= TextureWidth)
    		UE_LOG(LogLineRendering, Fatal, TEXT("UGPUInstancedLineComponent::CalculateTextureRegions : Calculation went wrong on second region, fix code!"));
    	
    	Regions->AddZeroed(1);
    	(*Regions)[2].DestX = 0;
    	(*Regions)[2].DestY = (*Regions)[1].DestY + (*Regions)[1].Height;
    	(*Regions)[2].SrcX = NumberOfPoints - RemainingPoints; // InitialTextureMarker.Y;
    	(*Regions)[2].SrcY = 0;
    	(*Regions)[2].Width = FMath::Min(RemainingPoints, TextureWidth - static_cast<int32>((*Regions)[2].DestX));
    	RemainingPoints -= (*Regions)[2].Width;
    	(*Regions)[2].Height = RemainingPoints == 0 ? 1 : (RemainingPoints / TextureWidth);
    	RemainingPoints -= (*Regions)[2].Width * ((*Regions)[1].Height - 1);
    
    	checkf((*Regions)[2].DestX + (*Regions)[2].Width <= TextureWidthU, TEXT("Region[2] out of bounds on X. Texture: %i, %i, %i"), (*Regions)[2].DestX, (*Regions)[2].Width, PositionTexture->GetSizeX());
    
    	
    	if (RemainingPoints > 0)
    		UE_LOG(LogLineRendering, Fatal, TEXT("UGPUInstancedLineComponent::CalculateTextureRegions : Calculation went wrong on third region, fix code!"));
    
    	NumberOfRegionsOut = 3;
    	return Regions->GetData();	
    }
    
    void UGPUInstancedLineComponent::BeginPlay()
    {
    	Super::BeginPlay();
    	
    	if(!PositionTexture)
    	{
    		UE_LOG(LogLineRendering, Warning, TEXT("UGPUInstancedLineComponent::BeginPlay: Component is not initialized on BeginPlay, Initializing..."));
    		Init();
    	}
    }
    
    
    #if WITH_EDITOR
    void UGPUInstancedLineComponent::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
    {
    	Init();
    	
    	if (PropertyChangedEvent.Property != nullptr)
    	{
    		if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGPUInstancedLineComponent, EditorLines))
    		{
    			// A new entry has been added
    			if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
    			{
    				const int32 AddedAtIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.Property->GetFName().ToString());
    				check(AddedAtIndex != INDEX_NONE);
    				AddLineFromEditorData(EditorLines[AddedAtIndex]);
    			}
    			// An entry has been duplicated
    			else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Duplicate)
    			{
    				const int32 AddedAtIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.Property->GetFName().ToString());
    				check(AddedAtIndex != INDEX_NONE);
    				AddLineFromEditorData(EditorLines[AddedAtIndex]);				
    			}
    			else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayRemove)
    			{
    				const int32 RemovedAtIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.Property->GetFName().ToString());
    				check(RemovedAtIndex != INDEX_NONE);
    
    				RemoveLineFromEditorData(EditorLineIds[RemovedAtIndex]);
    				//RemoveLine(EditorLines[RemovedAtIndex].RespectiveLineId);
    				// todo test me, depends on if the data was removed already or not - the data seems to be already removed
    			}
    			else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayClear)
    			{
    				RemoveAllEditorLines();
    			}
    		}
    		else if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(FEditorLineData, Points))
    		{
    			if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayAdd)
    			{
    				const int32 AddedAtIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.Property->GetFName().ToString());
    				const int32 LineIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName().ToString());
    				
    				check(AddedAtIndex != INDEX_NONE);
    				check(LineIndex != INDEX_NONE);
    
    				// 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;
    				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)
    			{
    				const int32 AddedAtIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.Property->GetFName().ToString());
    				const int32 LineIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName().ToString());
    
    				check(AddedAtIndex != INDEX_NONE);
    				check(LineIndex != INDEX_NONE);
    
    				AddPoint(EditorLines[LineIndex].RespectiveLineId, EditorLines[LineIndex].Points[AddedAtIndex].Point);
    			}			
    			else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayRemove)
    			{
    				// Get Indices
    				const int32 RemovedAtIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.Property->GetFName().ToString());
    				const int32 EditorLineIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName().ToString());
    
    				check(RemovedAtIndex != INDEX_NONE);
    				check(EditorLineIndex != INDEX_NONE);
    				
    				// This isn't allowed to happen, but cannot be explicitly forbidden in the engine. For now, just hack it by inserting a point at 0,0,0.
    				// Alternatively, just delete the line? TODO
    				if(EditorLines[EditorLineIndex].Points.Num() < 2)
    				{
    					UE_LOG(LogLineRendering, Error, TEXT("UGPUInstancedLineComponent: A Line needs to consist of at least two points, trying to remove one of them doesn't work. Defaulted point inserted again!"));
    					EditorLines[EditorLineIndex].Points.AddZeroed(1);
    					UpdatePoint(EditorLines[EditorLineIndex].RespectiveLineId, RemovedAtIndex, EditorLines[EditorLineIndex].Points[RemovedAtIndex].Point);
    				}
    				else
    				{
    					RemovePoint(EditorLines[EditorLineIndex].RespectiveLineId, RemovedAtIndex);
    				}
    				
    			}
    			else if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ArrayClear) // Tricky one because we need at least 2 points for a valid line, else the backend breaks.
    			{
    				// GetArrayIndex somehow fails unfortunately...
    				//const int32 LineIndex = PropertyChangedEvent.GetArrayIndex(PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetValue()->GetFName().ToString());
    				//check(LineIndex != INDEX_NONE);
    				//
    				// For now, find the cleared array manually TODO
    				//
    
    				for (int32 EditorLineIndex = 0; EditorLineIndex < EditorLines.Num(); ++EditorLineIndex)
    				{
    					if(EditorLines[EditorLineIndex].Points.Num() == 0)
    					{
    						EditorLines[EditorLineIndex].Points.SetNumZeroed(2);
    						// For simplicity's sake: (TODO)
    						const int32 NumberOfPoints = GetNumberOfPointsInLine(EditorLines[EditorLineIndex].RespectiveLineId);
    						const int32 NumberOfPointsToRemove = FMath::Max(NumberOfPoints - 2, 0);
    						for(int32 PointToRemove = 0; PointToRemove < NumberOfPointsToRemove; PointToRemove++)
    						{
    							RemovePoint(EditorLines[EditorLineIndex].RespectiveLineId, 2); // just repeatedly delete point 2
    						}
    
    						TArray<FVector> Pts = { FVector(0, 0, 0), FVector(0, 0, 0) };
    						UpdatePoints(EditorLines[EditorLineIndex].RespectiveLineId, 0, Pts);
    						
    						break;
    					}
    				}	
    			}			
    		}
    		else 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)
    				{
    					// Find the correct updated point in editor line data and propagate the update.
    
    					
    					TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* PointsNode = PropertyChangedEvent.PropertyChain.GetActiveNode()->GetPrevNode()->GetPrevNode();
    					TDoubleLinkedList<FProperty*>::TDoubleLinkedListNode* LinesNode = PointsNode->GetPrevNode();					
    					
    					const int32 PointIndex = PropertyChangedEvent.GetArrayIndex(PointsNode->GetValue()->GetFName().ToString());
    					const int32 LineIndex = PropertyChangedEvent.GetArrayIndex(LinesNode->GetValue()->GetFName().ToString());
    
    					check(PointIndex != INDEX_NONE);
    					check(LineIndex != INDEX_NONE);
    					
    					UpdatePoint(EditorLines[LineIndex].RespectiveLineId, PointIndex, EditorLines[LineIndex].Points[PointIndex].Point);
    				}
    			}
    		}
    		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() == 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);
    }
    
    void UGPUInstancedLineComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
    {
    	Init();
    	const FName PropertyName = PropertyChangedEvent.GetPropertyName();
    
    	if (PropertyChangedEvent.Property != nullptr)
    	{
    		if (PropertyName == GET_MEMBER_NAME_CHECKED(FEditorPoint, Point))
    		{
    			if(PropertyChangedEvent.ChangeType == EPropertyChangeType::Unspecified)
    			{
    				// Due to not having access to the chain whenever the widget is used, we need to just update ALL the editor lines every time
    				// the widget is dragged around. This is incredibly annoying but there doesn't seem to be another way, as the widget drag doesn't fire
    				// the PostEditChangeChainProperty callback. 
    				
    				UpdateAllEditorLines();
    			}
    		}
    		if (PropertyName == "LineMaterialInterface")
    		{
    			DynamicLineMaterial = UMaterialInstanceDynamic::Create(LineMaterialInterface, GetTransientPackage());
    			SetMaterial(0, DynamicLineMaterial);
    
    			DynamicLineMaterial->SetTextureParameterValue("PositionTexture", PositionTexture);
    			DynamicLineMaterial->SetScalarParameterValue("TextureWidth", TextureWidth);
    		}
    	}
    	
    	Super::PostEditChangeProperty(PropertyChangedEvent);
    }
    
    void UGPUInstancedLineComponent::TickComponent(float DeltaTime, ELevelTick TickType,
    	FActorComponentTickFunction* ThisTickFunction)
    {
    	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
    	if(TickCount < MaxTicksUntilInit)
    	{
    		TickCount++;
    	}
    	else
    	{
    		Init();
    		bTickInEditor = false;
    		SetComponentTickEnabled(false);
    	}
    }
    
    #endif
    
    void UGPUInstancedLineComponent::BeginDestroy()
    {
    	Super::BeginDestroy();
    	ReleaseData();
    }
    
    
    void UGPUInstancedLineComponent::ReserveMemory(int32 NumberOfSegments, int32 NumberOfLines)
    {
    	const int32 Total = NumberOfSegments * NumberOfLines;
    	PreAllocateInstancesMemory(Total);
    	LineMap.Reserve(Total);
    	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)
    		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(int32 LineId = 0; LineId < LineMap.Num(); ++LineId)//  TPair<int32, FGPULineArray>& Line : LineMap)
    	{
    		for (FGPULineIndices& LineIndices : LineMap[LineId].IndexArray)
    		{
    			// Key is instance id, value is texture index.
    			if(LineIndices.InstanceIndex >= 0)
    				SetCustomDataValue(LineIndices.InstanceIndex, 4, CurrentTextureIndex, false);
    			LineIndices.TextureIndex = CurrentTextureIndex;
    			CurrentTextureIndex++;
    			MoveTextureMarker();
    		}
    	}
    
    	// Recreate texture
    	UE_LOG(LogLineRendering, 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)
    {
    	if (LinearLineData.Num() != 0 || LineMap.Num() != 0 || GetInstanceCount() != 0)
    	{
    		LinearLineData.Empty(); // Reset
    		LineMap.Empty();
    
    		//NextFreeId = 0;
    		CurrentTextureIndex = 0;
    		CurrentTextureMarker = FIntPoint(0, 0);
    		ClearInstances();
    	}
    
    	if (Points.Num() != NumberOfLines * (NumberOfSegmentsPerLine + 1))
    	{
    		UE_LOG(LogLineRendering, Fatal, TEXT("UGPUInstancedLineComponent::InitializeLinesInBulk : Inconsistent number of points and lines!"));
    	}
    
    	LinearLineData = MoveTemp(Points);
    
    
    	// Add the number of instances - sadly this requires the for loop
    
    	const bool UniformColor = Colors.Num() == 1;
    	const bool UniformWidth = Widths.Num() == 1;
    	
    	for (int32 LineIndex = 0; LineIndex < NumberOfLines; ++LineIndex)
    	{
    		const FLinearColor Color = UniformColor ? Colors[0] : Colors[LineIndex];
    		const float Width = UniformWidth ? Widths[0] : Widths[LineIndex];
    		const int32 Idx = LineMap.Add(FGPULineArray());
    		FGPULineArray& NewLineArray = LineMap[Idx];
    		NewLineArray.IndexArray.Reserve(NumberOfSegmentsPerLine + 1);
    		
    		for (int32 PointIndex = 0; PointIndex < NumberOfSegmentsPerLine; ++PointIndex)
    		{
    			const int32 InstanceId = AddNewSegmentInstance(Color, Width, CurrentTextureIndex);			
    			NewLineArray.IndexArray.Add({InstanceId, LineIndex * (NumberOfSegmentsPerLine + 1) + PointIndex});
    			CurrentTextureIndex++;
    
    			MoveTextureMarker();
    		}
    		NewLineArray.IndexArray.Add({-1, LineIndex * (NumberOfSegmentsPerLine + 1) + NumberOfSegmentsPerLine});
    		MoveTextureMarker();
    		CurrentTextureIndex++;
    		//NextFreeId++;
    	}
    
    	MarkRenderStateDirty();
    	UpdateWholeTexture();	
    }
    
    void UGPUInstancedLineComponent::InitializeLinesInBulkTesselate(int32 NumberOfLines, int32 NumberOfSegmentsPerLine,
    	TArray<FVector4>& Points, const TArray<FLinearColor>& Colors, const TArray<float>& Widths)
    {
    	// Just as a test
    	const int32 NumOriginalSegments = NumberOfSegmentsPerLine;
    	NumberOfSegmentsPerLine += 9;
    	LinearLineData.Reserve(NumberOfLines * (NumberOfSegmentsPerLine + 1));
    
    	
    	if (LinearLineData.Num() != 0 || LineMap.Num() != 0 || GetInstanceCount() != 0)
    	{
    		LinearLineData.Empty(); // Reset
    		LineMap.Empty();
    
    		//NextFreeId = 0;
    		CurrentTextureIndex = 0;
    		CurrentTextureMarker = FIntPoint(0, 0);
    		ClearInstances();
    	}
    
    	LinearLineData = MoveTemp(Points);
    	
    	// Add tesselated points from back to front	
    	for (int LineIdx = NumberOfLines - 1; LineIdx >= 0; --LineIdx)
    	{
    		// Get start and end of line
    		const FVector4 Start = LinearLineData[LineIdx * (NumOriginalSegments + 1)];
    		const FVector4 End = LinearLineData[LineIdx * (NumOriginalSegments + 1) + 1];
    		const FVector4 Dist = End - Start;
    		// Tesselate
    		const FVector4 First = Start + 0.1 * Dist;
    		const FVector4 Second = Start + 0.2 * Dist;
    		const FVector4 Third = Start + 0.3 * Dist;
    		const FVector4 Fourth = Start + 0.4 * Dist;
    		const FVector4 Fith = Start + 0.5 * Dist;
    		const FVector4 Sixth = Start + 0.6 * Dist;
    		const FVector4 Seventh = Start + 0.7 * Dist;
    		const FVector4 Eigth = Start + 0.8 * Dist;
    		const FVector4 Nineth = Start + 0.9 * Dist;
    
    		LinearLineData.Insert({ First, Second, Third, Fourth, Fith, Sixth, Seventh, Eigth, Nineth }, LineIdx * (NumOriginalSegments + 1) + 1);
    		
    	}
    
    	// Add the number of instances - sadly this requires the for loop
    
    	const bool UniformColor = Colors.Num() == 1;
    	const bool UniformWidth = Widths.Num() == 1;
    
    	for (int32 LineIndex = 0; LineIndex < NumberOfLines; ++LineIndex)
    	{
    		const FLinearColor Color = UniformColor ? Colors[0] : Colors[LineIndex];
    		const float Width = UniformWidth ? Widths[0] : Widths[LineIndex];
    		const int32 Idx = LineMap.Add(FGPULineArray());
    		FGPULineArray& NewLineArray = LineMap[Idx];
    		NewLineArray.IndexArray.Reserve(NumberOfSegmentsPerLine + 1);
    
    		for (int32 PointIndex = 0; PointIndex < NumberOfSegmentsPerLine; ++PointIndex)
    		{
    			const int32 InstanceId = AddNewSegmentInstance(Color, Width, CurrentTextureIndex);
    			NewLineArray.IndexArray.Add({ InstanceId, LineIndex * (NumberOfSegmentsPerLine + 1) + PointIndex });
    			CurrentTextureIndex++;
    
    			MoveTextureMarker();
    		}
    		NewLineArray.IndexArray.Add({ -1, LineIndex * (NumberOfSegmentsPerLine + 1) + NumberOfSegmentsPerLine });
    		MoveTextureMarker();
    		CurrentTextureIndex++;
    		//NextFreeId++;
    	}
    
    	MarkRenderStateDirty();
    	UpdateWholeTexture();
    	
    }
    
    
    int32 UGPUInstancedLineComponent::AddLine(const TArray<FVector>& 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 LineId = NextFreeId;
    	//NextFreeId++; // TODO Fix Id system as this can possibly overflow.
    
    
    		
    	const int32 NumberSegments = Line.Num() - 1;
    	
    	const FIntPoint InitialTextureMarker = CurrentTextureMarker;
    	
    	for (int32 PointIndex = 0; PointIndex < NumberSegments; ++PointIndex)
    	{
    		// Add with a dummy transform
    		const int32 InstanceId = AddNewSegmentInstance(Color, Width, CurrentTextureIndex);		
    		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>(Line);
    	// "Mark" the first entry with w=0 as first segment of the line, and the last with w=-1
    	(*TextureData)[0].W = 0.0;
    	TextureData->Last().W = -1.0;
    
    	// Store the points in a linear array here
    	LinearLineData.Append(*TextureData);
    
    	UpdateTexture(InitialTextureMarker, Line.Num(), (uint8*)TextureData->GetData());
    
    	return LineId;	
    }
    
    int32 UGPUInstancedLineComponent::AddLine(TArray<FVector4>& Line, FLinearColor Color, float Width, bool bMarkRenderStateDirty)
    {
    	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
    		const int32 InstanceId = AddNewSegmentInstance(Color, Width, CurrentTextureIndex);
    		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);
    
    	UpdateTexture(InitialTextureMarker, TextureData->Num(), (uint8*)TextureData->GetData(), bMarkRenderStateDirty);
    	return LineId;
    
    	
    }
    
    int32 UGPUInstancedLineComponent::AddLineFromEditorData(FEditorLineData& LineData)
    {
    	const TArray<FEditorPoint>* EditorLineDataPtr = &LineData.Points;
    	const TArray<FVector>* FVectorLineDataPtr = reinterpret_cast<const TArray<FVector>*>(EditorLineDataPtr);
    
    	LineData.RespectiveLineId = AddLine(*FVectorLineDataPtr, LineData.Color, LineData.Width);
    	EditorLineIds.Add(LineData.RespectiveLineId);
    	
    	return LineData.RespectiveLineId;
    }
    
    
    // Add a point to the end of the specified line
    bool UGPUInstancedLineComponent::AddPoint(int32 LineId, const FVector& Point)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::AddPoint"))
    
    	// Adding a point to the last line is easy. Adding it to a line the middle is awkward and requires an almost complete recalculation
    
    	// Get color and width of the line
    	FGPULineArray& Line = LineMap[LineId];
    
    	const float Width = PerInstanceSMCustomData[Line.IndexArray[0].InstanceIndex * NumCustomDataFloats + 3];
    
    	const FIntPoint InitialTextureMarker = CurrentTextureMarker;
    	// Check if it's the last line:
    
    	FGPULineIndices& Indices = Line.IndexArray.Last();
    	const int32 InstanceId = AddNewSegmentInstance(GetLineColor(LineId), Width, Indices.TextureIndex);
    	Line.IndexArray.Last().InstanceIndex = InstanceId;
    
    	
    	
    	FVector4& PreviousLastPoint = LinearLineData[Indices.TextureIndex];
    	PreviousLastPoint.W = 1.0; // This isn't the line end anymore.
    
    	if(LineId == LineMap.Num() - 1)
    	{
    		
    		LinearLineData.Add(FVector4(Point, -1)); // This is the last point now.
    		
    		// Add a new dummy entry for the last point
    		Line.IndexArray.Add(FGPULineIndices{ -1, LinearLineData.Num() - 1 });		
    		
    		CurrentTextureIndex++;	
    		MoveTextureMarker();
    
    		// Update the new point
    		FUpdateTextureRegion2D* Region = new FUpdateTextureRegion2D(
    			InitialTextureMarker.X,
    			InitialTextureMarker.Y,
    			0,
    			0, 1, 1);
    
    		// Copy for now - no need to do that 
    		TArray<FVector4>* TextureData = new TArray<FVector4>{LinearLineData.Last()};
    
    		PositionTexture->UpdateTextureRegions(0, 1, Region, TextureWidth * sizeof(FVector4), sizeof(FVector4), (uint8*)TextureData->GetData(),
    			[](auto InTextureData, auto InRegions)
    		{
    			delete InTextureData;
    			delete InRegions;
    		});
    
    		// Update old point
    		
    		TArray<FVector4>* TextureData2 = new TArray<FVector4>{ PreviousLastPoint };
    		// Update the new point
    		int32 PrevX = InitialTextureMarker.X - 1;
    		int32 PrevY = InitialTextureMarker.Y;
    		if(PrevX < 0)
    		{
    			PrevX = TextureWidth - 1;
    			PrevY--;
    		}
    		
    		FUpdateTextureRegion2D* Region2 = new FUpdateTextureRegion2D(
    			PrevX,
    			PrevY,
    			0,
    			0, 1, 1);
    		PositionTexture->UpdateTextureRegions(0, 1, Region2, TextureWidth * sizeof(FVector4), sizeof(FVector4), (uint8*)TextureData2->GetData(),
    			[](auto InTextureData, auto InRegions)
    		{
    			delete InTextureData;
    			delete InRegions;
    		});
    		MarkRenderStateDirty();
    
    		return true;
    	}
    
    	// Add point to a line in the middle
    	// This is slightly more complicated as all the indices after the added point need to be adjusted.
    	// Additionally, the point needs to be inserted into the texture, shifting the whole thing. 
    
    	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
    	for (FGPULineArray& LineArray : LineMap)
    	{
    		if (LineArray.IndexArray[0].TextureIndex >= NewPointTextureIndex)
    		{
    			// Need to increase all indices by 1 and adjust the custom data
    			for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i)
    			{
    				LineArray.IndexArray[i].TextureIndex++;
    				SetCustomDataValue(LineArray.IndexArray[i].InstanceIndex, 4, LineArray.IndexArray[i].TextureIndex, false);
    			}
    		}
    	}
    	// Insert the point into the LinearData:
    	LinearLineData.Insert(FVector4(Point, -1), NewPointTextureIndex);
    	// Add it to the LineMap - this is the new dummy segment because we add it at the end of the line
    	Line.IndexArray.Add(FGPULineIndices{ -1, NewPointTextureIndex });
    	//SetCustomDataValue(InstanceId, 4, NewPointTextureIndex, true);
    	
    	CurrentTextureIndex++;
    	MoveTextureMarker();
    
    	// For simplicities sake, update the whole texture:
    
    	UpdateWholeTexture();
    	MarkRenderStateDirty();
    	return true;
    }
    
    bool UGPUInstancedLineComponent::InsertPoint(int32 LineId, int32 SegmentId, const FVector& Point, const FLinearColor& Color)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::InsertPoint"))
    
    	FGPULineArray& Line = LineMap[LineId];
    
    	if (SegmentId == Line.IndexArray.Num())
    	{
    		return AddPoint(LineId, Point);
    	}	
    	const float Width = PerInstanceSMCustomData[Line.IndexArray[0].InstanceIndex * NumCustomDataFloats + 3];
    
    	const int32 LinearDataIndex = Line.IndexArray[SegmentId].TextureIndex;
    	LinearLineData.Insert(FVector4(Point), LinearDataIndex);
    	if (SegmentId == 0)
    	{
    		// This is now the new start point
    		LinearLineData[LinearDataIndex].W = 0;
    		// This was the old start point
    		LinearLineData[LinearDataIndex + 1].W = 1;
    	}
    	// Add a new segment BEFORE the current one.
    	const int32 InstanceId = AddNewSegmentInstance(Color, Width, LinearDataIndex);
    
    	Line.IndexArray.Insert(FGPULineIndices{ InstanceId, LinearDataIndex }, SegmentId);
    
    	// Update the rest of the line
    	for(int32 i = SegmentId + 1; i < Line.IndexArray.Num(); ++i)
    	{
    		Line.IndexArray[i].TextureIndex++;
    		SetCustomDataValue(Line.IndexArray[i].InstanceIndex, 4, Line.IndexArray[i].TextureIndex);
    	}
    
    	// Update all following lines
    	for (FGPULineArray& LineArray : LineMap)
    	{
    		if (LineArray.IndexArray[0].TextureIndex > LinearDataIndex)
    		{
    			// Need to increase all indices by 1 and adjust the custom data
    			for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i)
    			{
    				LineArray.IndexArray[i].TextureIndex++;
    				SetCustomDataValue(LineArray.IndexArray[i].InstanceIndex, 4, LineArray.IndexArray[i].TextureIndex, false);
    			}
    		}
    	}
    
    	CurrentTextureIndex++;
    	MoveTextureMarker();
    
    	// For simplicities sake, update the whole texture:
    
    	UpdateWholeTexture();
    	MarkRenderStateDirty();
    	
    	return true;
    }
    
    bool UGPUInstancedLineComponent::InsertPointWithSameColor(int32 LineId, int32 SegmentId, const FVector& Point)
    {
    	return InsertPoint(LineId, SegmentId, Point, GetLineColor(LineId));
    }
    
    bool UGPUInstancedLineComponent::InsertPointWithColor(int32 LineId, int32 SegmentId, const FVector& Point,
                                                          const FLinearColor& Color)
    {
    	return InsertPoint(LineId, SegmentId, Point, Color);
    }
    
    bool UGPUInstancedLineComponent::AddPoints(int32 LineId, const TArray<FVector>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::AddPoint"))
    
    	// Adding a point to the last line is easy. Adding it to a line the middle is awkward and requires an almost complete recalculation
    
    	// Get color and width of the line
    	FGPULineArray& Line = LineMap[LineId];
    
    	const FLinearColor Color = GetLineColor(LineId);
    	
    	const float Width = PerInstanceSMCustomData[Line.IndexArray[0].InstanceIndex * NumCustomDataFloats + 3];
    
    	const FIntPoint InitialTextureMarker = CurrentTextureMarker;
    	// Check if it's the last line:
    
    	FGPULineIndices& Indices = Line.IndexArray.Last();	
    	const int32 PointTextureIndex = Indices.TextureIndex;
    	FVector4& PreviousLastPoint = LinearLineData[Indices.TextureIndex];
    	PreviousLastPoint.W = 1.0; // This isn't the line end anymore.
    
    	if (LineId == LineMap.Num() - 1)
    	{
    		for (int32 PointIndex = 0; PointIndex < Points.Num(); ++PointIndex)
    		{
    			const int32 InstanceId = AddNewSegmentInstance(Color, Width, PointTextureIndex + PointIndex);
    
    			// Update the latest dummy entry to point to the actual segment.
    			if (PointIndex == 0)
    				Line.IndexArray.Last().InstanceIndex = InstanceId;
    			else
    				Line.IndexArray.Add({InstanceId, LinearLineData.Num() - 1 + PointIndex});
    			CurrentTextureIndex++;
    			MoveTextureMarker();
    		}
    
    		LinearLineData.Append(Points);
    		LinearLineData.Last().W = -1;
    		
    		// Add a new dummy entry for the last point
    		Line.IndexArray.Add({ -1, LinearLineData.Num() - 1 });
    
    		CurrentTextureIndex++;
    		MoveTextureMarker();
    
    		// Recreate the data on the heap to allow asynchronous texture update.
    		TArray<FVector4>* TextureData = new TArray<FVector4>(Points);
    
    		UpdateTexture(InitialTextureMarker, Points.Num(), (uint8*)TextureData->GetData());		
    		
    		//MarkRenderStateDirty(); called in UpdateTexture already
    		return true;
    	}
    
    	// Add points to a line in the middle
    	// This is slightly more complicated as all the indices after the added points need to be adjusted.
    	// Additionally, the points needs to be inserted into the texture, shifting the whole thing. 
    
    	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 (FGPULineArray& LineArray : LineMap)
    	{
    		if (LineArray.IndexArray[0].TextureIndex >= NewPointTextureIndex)
    		{
    			// Need to increase all indices by 1 and adjust the custom data
    			for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i)
    			{
    				LineArray.IndexArray[i].TextureIndex += Points.Num();
    				SetCustomDataValue(LineArray.IndexArray[i].InstanceIndex, 4, LineArray.IndexArray[i].TextureIndex, false);
    			}
    		}
    	}
    
    	for (int32 PointIndex = 0; PointIndex < Points.Num(); ++PointIndex)
    	{
    		const int32 InstanceId = AddNewSegmentInstance(Color, Width, NewPointTextureIndex - 1 + PointIndex);
    		
    		// Update the latest dummy entry to point to the actual segment.
    		if (PointIndex == 0)
    			Line.IndexArray.Last().InstanceIndex = InstanceId;
    		else
    		{
    			Line.IndexArray.Add({InstanceId, NewPointTextureIndex + PointIndex - 1});
    			CurrentTextureIndex++;
    			MoveTextureMarker();			
    		}
    	}
    
    	
    	// Insert the points into the LinearData:
    	TArray<FVector4> TextureData = TArray<FVector4>(Points);
    	TextureData.Last().W = -1;	
    	LinearLineData.Insert(TextureData, NewPointTextureIndex);
    	
    	// Add it to the LineMap - this is the new dummy segment because we add it at the end of the line
    	Line.IndexArray.Add({ -1, NewPointTextureIndex + Points.Num() - 1 });
    	//SetCustomDataValue(InstanceId, 4, NewPointTextureIndex, true);
    
    	CurrentTextureIndex++;
    	MoveTextureMarker();
    
    	// For simplicities sake, update the whole texture:
    
    	UpdateWholeTexture();
    	MarkRenderStateDirty();
    	return true;
    }
    
    bool UGPUInstancedLineComponent::InsertPoints(int32 LineId, int32 SegmentId, const TArray<FVector>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::InsertPoint"))
    
    	FGPULineArray& Line = LineMap[LineId];
    
    	if (SegmentId == Line.IndexArray.Num())
    	{
    		return AddPoints(LineId, Points);
    	}
    
    	const float Width = PerInstanceSMCustomData[Line.IndexArray[0].InstanceIndex * NumCustomDataFloats + 3];
    
    	const int32 LinearDataIndex = Line.IndexArray[SegmentId].TextureIndex;
    	LinearLineData.Insert(TArray<FVector4>(Points), LinearDataIndex);
    	if (SegmentId == 0)
    	{
    		// This is now the new start point
    		LinearLineData[LinearDataIndex].W = 0;
    		// This was the old start point
    		LinearLineData[LinearDataIndex + Points.Num()].W = 1;
    	}
    	
    	for (int32 PointIndex = 0; PointIndex < Points.Num(); ++PointIndex)
    	{
    		const int32 InstanceId = AddNewSegmentInstance(GetLineColor(LineId), Width, LinearDataIndex + PointIndex);
    		
    		Line.IndexArray.Insert({ InstanceId, LinearDataIndex + PointIndex }, SegmentId + PointIndex);
    		CurrentTextureIndex++;
    		MoveTextureMarker();
    	}
    
    	// Update the rest of the line
    	for (int32 i = SegmentId + Points.Num(); i < Line.IndexArray.Num(); ++i)
    	{
    		Line.IndexArray[i].TextureIndex += Points.Num();
    		SetCustomDataValue(Line.IndexArray[i].InstanceIndex, 4, Line.IndexArray[i].TextureIndex);
    	}
    
    	// Update all following lines
    	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
    			for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i)
    			{
    				LineArray.IndexArray[i].TextureIndex += Points.Num();
    				SetCustomDataValue(LineArray.IndexArray[i].InstanceIndex, 4, LineArray.IndexArray[i].TextureIndex, false);
    			}
    		}
    	}
    
    	// For simplicities sake, update the whole texture:
    
    	UpdateWholeTexture();
    	MarkRenderStateDirty();
    
    	return true;
    }
    
    // todo test this not sure if it actually works 
    bool UGPUInstancedLineComponent::UpdateLine(int32 LineId, TArray<FVector>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdateLine"))
    
    	const FGPULineArray& Line = LineMap[LineId];
    
    	if(Line.IndexArray.Num() != Points.Num())
    	{
    		return false;
    	}
    	
    	TArray<FVector4>* TextureData = new TArray<FVector4>(MoveTemp(Points));
    	TextureData->Last().W = -1;
    	(*TextureData)[0].W = 0;
    	const int32 TextureIndex = Line.IndexArray[0].TextureIndex;
    	const FIntPoint StartIndex(TextureIndex % TextureWidth, TextureIndex / TextureWidth);
    	
    	FMemory::Memcpy(LinearLineData.GetData() + TextureIndex, TextureData->GetData(), TextureData->Num() * sizeof(FVector4));
    
    	UpdateTexture(StartIndex, TextureData->Num(), (uint8*)TextureData->GetData());
    	
    	return true;
    }
    
    // Use the Points array to update the lines, assuming the array contains already well defined data.
    bool UGPUInstancedLineComponent::UpdateLinesDataDirectly(int32 LineIdStart, TArray<FVector4>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdateLines - AlignedMem"))
    
    	if (LineIdStart + Points.Num() > LinearLineData.Num())
    		return false;
    	{
    		TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdateLines - Memcopy LinearLineData"))
    		FMemory::Memcpy(LinearLineData.GetData() + LineIdStart, Points.GetData(), Points.Num() * sizeof(FVector4));
    	}
    	const int32 X = LineIdStart % TextureWidth;
    	const int32 Y = LineIdStart / TextureWidth;
    	TArray<FVector4>* TextureData;
    	{
    		// Copy for now
    		TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdateLines - MoveTemp Texture Data"))
    		TextureData = new TArray<FVector4>(MoveTemp(Points));
    	}
    
    	UpdateTexture(FIntPoint(X, Y), TextureData->Num(), (uint8*)TextureData->GetData());	
    	return true;
    }
    
    bool UGPUInstancedLineComponent::UpdateLineFromEditorData(const FEditorLineData& LineData)
    {
    	TArray<FVector> Points;
    	Points.SetNumUninitialized(LineData.Points.Num());
    	FMemory::Memcpy(Points.GetData(), LineData.Points.GetData(), sizeof(FVector) * LineData.Points.Num());	
    	return UpdateLine(LineData.RespectiveLineId, Points);
    }
    
    bool UGPUInstancedLineComponent::DrawLinesDirectly(int32 LineIdStart, TArray<FVector4>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::DrawLinesDirectly - Move"))
    
    	const int32 X = LineIdStart % TextureWidth;
    	const int32 Y = LineIdStart / TextureWidth;
    	TArray<FVector4>* TextureData;
    	{
    		TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::DrawLinesDirectly - MoveTemp Texture Data"))
    		TextureData = new TArray<FVector4>(MoveTemp(Points));
    	}
    	UpdateTexture(FIntPoint(X, Y), TextureData->Num(), (uint8*)TextureData->GetData());
    
    
    	return true;
    }
    
    bool UGPUInstancedLineComponent::DrawLinesDirectly(int32 LineIdStart, const TArray<FVector4>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::DrawLinesDirectly - Memcpy"))
    
    	TArray<FVector4> Cpy = Points;
    	return DrawLinesDirectly(LineIdStart, Cpy);
    }
    
    bool UGPUInstancedLineComponent::DrawLinesDirectly(int32 LineIdStart, TArray<FVector4>* Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::DrawLinesDirectly - Ptr"))
    
    	const int32 X = LineIdStart % TextureWidth;
    	const int32 Y = LineIdStart / TextureWidth;
    
    	UpdateTexture(FIntPoint(X, Y), Points->Num(), (uint8*)Points->GetData());
    
    	return true;
    }
    
    bool UGPUInstancedLineComponent::DrawLinesDirectly(int32 LineIdStart, uint8* SrcData, int32 Num)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::DrawLinesDirectly - Raw Ptr"))
    
    	const int32 X = LineIdStart % TextureWidth;
    	const int32 Y = LineIdStart / TextureWidth;
    
    	UpdateTexture(FIntPoint(X, Y), Num, SrcData);
    
    	return true;
    }
    
    // todo test this not sure if it actually works 
    bool UGPUInstancedLineComponent::UpdatePoint(int32 LineId, int32 PointId, const FVector& Point)
    {
    	// Should check for validity
    	const FGPULineIndices& PointIndices = LineMap[LineId].IndexArray[PointId];
    
    	// Only need to update the linear data - don't overwrite W as the line start/end doesn't change
    	LinearLineData[PointIndices.TextureIndex].X = Point.X;
    	LinearLineData[PointIndices.TextureIndex].Y = Point.Y;
    	LinearLineData[PointIndices.TextureIndex].Z = Point.Z;
    
    	const int32 TextureIndex = PointIndices.TextureIndex;
    	const int32 X = TextureIndex % TextureWidth;
    	const int32 Y = TextureIndex / TextureWidth;
    
    
    	// Update the texture - need to get instance custom data
    
    	FUpdateTextureRegion2D* Region = new FUpdateTextureRegion2D(
    			X,
    			Y,
    			0,
    			0, 1, 1);
    
    	// Copy for now - no need to do that 
    	TArray<FVector4>* TextureData = new TArray<FVector4>{ LinearLineData[PointIndices.TextureIndex] };
    
    
    	PositionTexture->UpdateTextureRegions(0, 1, Region, TextureWidth * sizeof(FVector4), sizeof(FVector4), (uint8*)TextureData->GetData(),
    			[](auto InTextureData, auto InRegions)
    	{
    			delete InTextureData;
    			delete InRegions;
    	});
    
    	
    	return true;
    }
    
    // todo test this not sure if it actually works 
    bool UGPUInstancedLineComponent::UpdatePoints(int32 LineId, int32 StartingPointId, TArray<FVector>& Points)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::UpdatePoints"))
    
    	const FGPULineArray& Line = LineMap[LineId];
    
    	if (Line.IndexArray.Num() < Points.Num() || StartingPointId + Points.Num() > Line.IndexArray.Num())
    	{
    		return false;
    	}
    	if(Line.IndexArray.Num() == Points.Num() && StartingPointId == 0)
    	{
    		return UpdateLine(LineId, Points);
    	}
    
    	TArray<FVector4>* TextureData = new TArray<FVector4>(MoveTemp(Points));
    	if(StartingPointId == 0)
    	{
    		(*TextureData)[0].W = 0;
    	}
    	if (StartingPointId + TextureData->Num() == Line.IndexArray.Num())
    	{
    		TextureData->Last().W = -1;
    	}
    	
    	const int32 StartTextureIndex = Line.IndexArray[StartingPointId].TextureIndex;
    	const FIntPoint StartIndex(StartTextureIndex % TextureWidth, StartTextureIndex / TextureWidth);
    
    	FMemory::Memcpy(LinearLineData.GetData() + StartTextureIndex, TextureData->GetData(), TextureData->Num() * sizeof(FVector4));
    	
    	UpdateTexture(StartIndex, TextureData->Num(), (uint8*)TextureData->GetData());
    	
    	return true;
    }
    
    bool UGPUInstancedLineComponent::RemoveLine(int32 LineId)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::RemoveLine"))
    
    	const FGPULineArray& Line = LineMap[LineId];
    	const int32 LineLength = Line.IndexArray.Num();
    	const int32 LineTextureIndex = Line.IndexArray[0].TextureIndex;
    
    	// Because ISM are so great, they reshuffle id's on removal....
    	// This means we can't just remove consecutive ids, but need to actually re-calculate them EVERY SINGLE TIME
    	// As instances of a line aren't guaranteed to be in order, collect and sort the indices first, then start top down.
    	//
    	TArray<int32> InstancesToRemove;
    	InstancesToRemove.Reserve(Line.IndexArray.Num());
    	
    	for(int32 PointIndex = 0; PointIndex < Line.IndexArray.Num() - 1; PointIndex++)
    	{
    		InstancesToRemove.Add(Line.IndexArray[PointIndex].InstanceIndex);
    	}
    	InstancesToRemove.Sort();
    
    	for (int32 Index = 0; Index < InstancesToRemove.Num(); Index++)
    	{
    		RemoveInstance(InstancesToRemove[InstancesToRemove.Num() - 1 - Index]);		
    	}
    	
    	// Remove linear data:
    	LinearLineData.RemoveAt(LineTextureIndex, LineLength);
    	// Remove line from map, this invalidates above Line reference.
    	LineMap.RemoveAt(LineId);
    	
    	// Update all following lines
    	for (FGPULineArray& LineArray : LineMap)
    	{
    		//FGPULineArray& LineArray = Pair.Value;
    		for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i)
    		{
    			if (LineArray.IndexArray[i].InstanceIndex >= InstancesToRemove[0])
    			{
    				// Apparently unreal also shuffles the instance indices, this might bite us later on
    
    				// This is insanely slow and very ugly:
    
    				for(int32 Index = InstancesToRemove.Num(); Index > 0; --Index)
    				{
    					if (LineArray.IndexArray[i].InstanceIndex >= InstancesToRemove[Index - 1])
    					{
    						LineArray.IndexArray[i].InstanceIndex -= Index;
    						break;
    					}
    				}								
    			}
    			if (LineArray.IndexArray[0].TextureIndex >= LineTextureIndex)
    			{
    				LineArray.IndexArray[i].TextureIndex -= LineLength;
    				SetCustomDataValue(LineArray.IndexArray[i].InstanceIndex, 4, LineArray.IndexArray[i].TextureIndex, false);
    			}
    		}
    	}
    
    	CurrentTextureIndex -= LineLength;
    	CurrentTextureMarker.X = CurrentTextureIndex % TextureWidth;
    	CurrentTextureMarker.Y = CurrentTextureIndex / TextureWidth;
    
    	UpdateWholeTexture();
    	return true;
    }
    
    bool UGPUInstancedLineComponent::RemoveLineFromEditorData(int32 LineId)
    {
    	int32 Index = 0;
    	if (EditorLineIds.Find(LineId, Index))
    	{
    		RemoveLine(LineId);
    		EditorLineIds.RemoveAt(Index);
    	}
    	return false;	
    }
    
    bool UGPUInstancedLineComponent::RemoveAllEditorLines()
    {
    	for(const int32 Id : EditorLineIds)
    	{
    		RemoveLine(Id);
    	}
    	if(EditorLines.Num() != 0)
    	{
    		EditorLines.Empty();
    		UE_LOG(LogLineRendering, Display, TEXT("UGPUInstancedLineComponent::RemoveAllEditorLines - EditorLines wasn't empty already, clearing."));
    	}
    	EditorLineIds.Empty();
    	return true;
    }
    
    bool UGPUInstancedLineComponent::RemovePoint(int32 LineId, int32 PointId)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::RemovePoint"))
    
    	FGPULineArray& Line = LineMap[LineId];
    	const FGPULineIndices& Indices = Line.IndexArray[PointId];
    	const int32 LineTextureIndex = Indices.TextureIndex;
    	int32 RemovedInstanceId = Indices.InstanceIndex;
    
    	if (PointId == 0)
    	{
    		LinearLineData[LineTextureIndex + 1].W = 0;
    	}
    	
    	LinearLineData.RemoveAt(LineTextureIndex);
    
    	// Last point is special
    	if(PointId == Line.IndexArray.Num() - 1)
    	{
    		LinearLineData[Line.IndexArray[PointId - 1].TextureIndex].W = -1;
    		RemovedInstanceId = Line.IndexArray[PointId - 1].InstanceIndex;
    		RemoveInstance(Line.IndexArray[PointId - 1].InstanceIndex);
    		Line.IndexArray[PointId - 1].InstanceIndex = -1;
    	}
    	else
    	{
    		// Update the following segments in this line:
    		for (int32 i = PointId + 1; i < Line.IndexArray.Num(); ++i)
    		{
    			Line.IndexArray[i].TextureIndex -= 1;
    			SetCustomDataValue(Line.IndexArray[i].InstanceIndex, 4, Line.IndexArray[i].TextureIndex, false);
    		}
    		//MarkRenderStateDirty();
    
    		RemoveInstance(RemovedInstanceId);
    	}
    	Line.IndexArray.RemoveAt(PointId);
    
    	// Update all following lines
    	for (FGPULineArray& LineArray : LineMap)
    	{
    		for (int32 i = 0; i < LineArray.IndexArray.Num(); ++i)
    		{
    			if (LineArray.IndexArray[i].InstanceIndex >= RemovedInstanceId)
    			{
    				// Apparently unreal also shuffles the instance indices, this might bite us later on
    				LineArray.IndexArray[i].InstanceIndex -= 1;
    			}
    			if (LineArray.IndexArray[0].TextureIndex > LineTextureIndex)
    			{
    				LineArray.IndexArray[i].TextureIndex -= 1;
    				SetCustomDataValue(LineArray.IndexArray[i].InstanceIndex, 4, LineArray.IndexArray[i].TextureIndex, false);
    			}
    		}
    	}
    
    	CurrentTextureIndex--;
    	CurrentTextureMarker.X = CurrentTextureIndex % TextureWidth;
    	CurrentTextureMarker.Y = CurrentTextureIndex / TextureWidth;
    	MarkRenderStateDirty();
    
    	// For sake of simplicity for now:
    	UpdateWholeTexture();
    	return true;
    }
    
    // todo
    bool UGPUInstancedLineComponent::RemovePoints(int32 LineId, int32 StartingPointId, int32 NumberOfPoints)
    {
    	TRACE_CPUPROFILER_EVENT_SCOPE(TEXT("UGPUInstancedLineComponent::RemovePoints"))
    
    	
    	
    	return false;
    }
    
    bool UGPUInstancedLineComponent::SetLineColor(int32 LineId, const FLinearColor& Color)
    {
    	const FGPULineArray& Line = LineMap[LineId];
    
    	for (const FGPULineIndices& LineIndices: Line.IndexArray)
    	{
    		const int32 InstanceId = LineIndices.InstanceIndex;
    		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].IndexArray[SegmentId].InstanceIndex;
    
    	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 FGPULineArray& Line = LineMap[LineId];
    
    	for (const FGPULineIndices& LineIndices : Line.IndexArray)
    	{
    		const int32 InstanceId = LineIndices.InstanceIndex;
    		if (InstanceId >= 0)
    		{
    			SetCustomDataValue(InstanceId, 3, Width / 2.0f, false);
    		}
    	}
    	MarkRenderStateDirty();
    	return true;
    }
    
    bool UGPUInstancedLineComponent::SetSegmentWidth(int32 LineId, int32 SegmentId, float Width)
    {
    	const int32 InstanceId = LineMap[LineId].IndexArray[SegmentId].InstanceIndex;
    
    	SetCustomDataValue(InstanceId, 3, Width / 2.0f, false);
    	return true;
    }
    //#pragma optimize("", on)