Select Git revision
LineInstancedStaticMeshComponent.cpp
LineInstancedStaticMeshComponent.cpp 7.40 KiB
// Fill out your copyright notice in the Description page of Project Settings.
#include "LineInstancedStaticMeshComponent.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Runtime/Engine/Private/InstancedStaticMesh.h"
#include "Runtime/Engine/Classes/Materials/MaterialInstanceDynamic.h"
#include "UObject/ConstructorHelpers.h"
#include "Runtime/Engine/Classes/Engine/Texture2D.h"
#include "OptiXModule.h"
#include "OptiXContextManager.h"
ULineInstancedStaticMeshComponent::ULineInstancedStaticMeshComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
UE_LOG(LogTemp, Display, TEXT("ULineInstancedStaticMeshComponent Constructor"));
SetGenerateOverlapEvents(false);
static ConstructorHelpers::FObjectFinder<UStaticMesh>LineMeshAsset(TEXT("StaticMesh'/OptiX/Laser/cylinder.cylinder'"));
UStaticMesh* LineAsset = LineMeshAsset.Object;
SetStaticMesh(LineAsset);
SetRenderCustomDepth(true);
SetCastShadow(false);
SetCollisionEnabled(ECollisionEnabled::NoCollision);
SetCullDistance(10000);
SetBoundsScale(1000); // Prevent culling due to usage of worldpositionoffset
SegmentsPerLine = 20;
LineNumber = 16;
}
void ULineInstancedStaticMeshComponent::BeginPlay()
{
Super::BeginPlay();
}
FPrimitiveSceneProxy* ULineInstancedStaticMeshComponent::CreateSceneProxy()
{
FPrimitiveSceneProxy* Proxy = Super::CreateSceneProxy();
if (!PerInstanceRenderData.IsValid() || PerInstanceSMData.Num() < 1 )
{
return Proxy;
}
//check(IsInGameThread());
//bool bSupportsVertexHalfFloat = GVertexElementTypeSupport.IsSupported(VET_Half2);
//int32 NumInstances = PerInstanceSMData.Num();
int32 NumInstancesRenderData = PerInstanceRenderData->InstanceBuffer_GameThread->GetNumInstances();
for (int32 Index = 0; Index < NumInstancesRenderData; ++Index)
{
int32 RenderIndex = InstanceReorderTable.IsValidIndex(Index) ? InstanceReorderTable[Index] : Index;
if (RenderIndex == INDEX_NONE)
{
// could be skipped by density settings
continue; }
FMatrix Transform;
PerInstanceRenderData->InstanceBuffer_GameThread->GetInstanceTransform(RenderIndex, Transform);
PerInstanceRenderData->InstanceBuffer_GameThread->SetInstance(RenderIndex, Transform, static_cast<float>(RenderIndex));
}
return Proxy;
}
// todo currently copies the array, save a reference/ptr instead
void ULineInstancedStaticMeshComponent::InitLineSegments(TArray<int32> Indices, int32 NumberOfSegmentsPerLine, float LineW)
{
ClearInstances();
LaserIndices = Indices;
LineNumber = LaserIndices.Num();
SegmentsPerLine = NumberOfSegmentsPerLine;
LineWidth = LineW;
for (int32 Line = 0; Line < LineNumber; Line++)
{
for (int32 Segment = 0; Segment < SegmentsPerLine; Segment++)
{
FTransform LineTransform;
LineTransform.SetScale3D({ 1, 1, 1 });
LineTransform.SetLocation(GetComponentLocation());
// Rotation - align z to point towards End:
LineTransform.SetRotation(FQuat::Identity);
if (LineTransform.ContainsNaN())
{
UE_LOG(LogTemp, Fatal, TEXT("Line Transform contains NaNs!"));
}
int32 Index = AddInstanceWorldSpace(LineTransform);
UE_LOG(LogTemp, Display, TEXT("Line Instance Index: %i"), Index);
}
}
UE_LOG(LogTemp, Display, TEXT("Created #%i Lines with #%i Segments."), LineNumber, SegmentsPerLine);
if (DynamicLaserMaterial != NULL)
{
DynamicLaserMaterial->SetScalarParameterValue("Segments", SegmentsPerLine);
DynamicLaserMaterial->SetScalarParameterValue("Lines", LineNumber);
TextureRegion->Height = LineNumber;
TextureRegion->Width = 1;
LaserIndicesFloat.Empty();
LaserIndicesFloat.SetNumZeroed(LineNumber);
for (int32 i = 0; i < LineNumber; i++)
{
LaserIndicesFloat[i] = static_cast<float>(LaserIndices[i]);
}
IndexMap = UTexture2D::CreateTransient(1, LineNumber, EPixelFormat::PF_R32_FLOAT);
IndexMap->UpdateResource();
UE_LOG(LogTemp, Display, TEXT("IndexMap | LineNumber (%i | %i "), IndexMap->GetSizeX(), LineNumber);
IndexMap->UpdateTextureRegions(0, 1, TextureRegion.Get(), sizeof(float), sizeof(float), (uint8*)LaserIndicesFloat.GetData());
DynamicLaserMaterial->SetTextureParameterValue("IndexMap", IndexMap);
}
}
void ULineInstancedStaticMeshComponent::UpdateLUT(TArray<FColor> ColorMap)
{
if (DynamicLaserMaterial != NULL)
{
ColorArray = ColorMap;
// Store the actual laser index in the alpha component of the LUT - bit hacky but should do the trick for now.
ColorLUT = UTexture2D::CreateTransient(1, LineNumber);
ColorLUT->UpdateResource();
UE_LOG(LogTemp, Display, TEXT("ColorLUT | LineNumber (%i | %i "), ColorLUT->GetSizeX(), LineNumber);
ColorLUT->UpdateTextureRegions(0, 1, TextureRegion.Get(), sizeof(FColor), sizeof(FColor), (uint8*)ColorArray.GetData());
DynamicLaserMaterial->SetTextureParameterValue("LUT", ColorLUT);
UE_LOG(LogTemp, Display, TEXT("#Colors: %i"), ColorArray.Num());
}
}
void ULineInstancedStaticMeshComponent::SetLaserMaterial(UMaterialInstanceDynamic * Mat)
{
// 1 for now, could be #Lines if we want multicolored lines
ColorLUT = UTexture2D::CreateTransient(1, LineNumber, EPixelFormat::PF_R32_FLOAT);
ColorLUT->UpdateResource();
IndexMap = UTexture2D::CreateTransient(1, LineNumber, EPixelFormat::PF_R32_FLOAT);
IndexMap->UpdateResource();
DynamicLaserMaterial = Mat;
SetMaterial(0, DynamicLaserMaterial);
DynamicLaserMaterial->SetTextureParameterValue("LUT", ColorLUT);
DynamicLaserMaterial->SetTextureParameterValue("IndexMap", IndexMap);
/*DynamicLaserMaterial->SetScalarParameterValue("Segments", SegmentsPerLine);
DynamicLaserMaterial->SetScalarParameterValue("Lines", LineNumber);
*/
TextureRegion = MakeUnique<FUpdateTextureRegion2D>();
TextureRegion->Height = LineNumber;
TextureRegion->Width = 1;
TextureRegion->SrcX = 0;
TextureRegion->SrcY = 0;
TextureRegion->DestX = 0;
TextureRegion->DestY = 0;
}
void ULineInstancedStaticMeshComponent::UpdateLines(float NewLineWidth)
{
LineWidth = NewLineWidth;
for (int32 i = 0; i < 50 * 50; ++i) // until queue is empty
{
if (FOptiXModule::Get().GetOptiXContextManager()->LaserIntersectionQueue.IsEmpty()) break;
TPair<uint32, TArray<FVector>> QueueItem;
FOptiXModule::Get().GetOptiXContextManager()->LaserIntersectionQueue.Dequeue(QueueItem);
int32 Index = QueueItem.Key;
FVector PrevIntersection(0, 0, 0);
bool bIsDirty = true;
for (int32 Intersection = 0; Intersection < SegmentsPerLine * 2; Intersection += 2)
{ int32 TransformIndex = Index * SegmentsPerLine + Intersection / 2; // should be safe
bIsDirty = true;
FTransform LineTransform;
FVector Start = QueueItem.Value[Intersection];
FVector End = QueueItem.Value[Intersection + 1];
float D = FVector::Distance(Start, End);
float DScaled = D / 100.0;
float Width = LineWidth;
if (D == 0)
{
Width = 0; // Set scale to 0 if it shouldn't be drawn
}
LineTransform.SetScale3D({ Width, Width, DScaled });
LineTransform.SetLocation(Start);
// Rotation - align z to point towards End:
FQuat Rot = FRotationMatrix::MakeFromZ(End - Start).ToQuat();
if (D == 0)
{
Rot = FRotationMatrix::Identity.ToQuat();
}
LineTransform.SetRotation(Rot);
FTransform OldTransform;
GetInstanceTransform(TransformIndex, OldTransform, true);
if (OldTransform.Equals(LineTransform))
{
bIsDirty = false;
}
if (LineTransform.ContainsNaN())
{
UE_LOG(LogTemp, Fatal, TEXT("Line Transform contains NaNs!"));
}
UpdateInstanceTransform(TransformIndex, LineTransform, true, bIsDirty, true);
TransformIndex++;
}
}
}