// Fill out your copyright notice in the Description page of Project Settings. #include "PlotCanvas.h" #include "PawnBase.h" #include "Plot3D.h" #include "Runtime/Engine/Classes/Kismet/GameplayStatics.h" void UPlotCanvas::InitializeCanvas(const int32 pixelsH, const int32 pixelsV) { //dynamic texture initialization canvasWidth = pixelsH; canvasHeight = pixelsV; dynamicCanvas = UTexture2D::CreateTransient(canvasWidth, canvasHeight); #if WITH_EDITORONLY_DATA dynamicCanvas->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps; #endif dynamicCanvas->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap; dynamicCanvas->SRGB = 1; dynamicCanvas->AddToRoot(); dynamicCanvas->Filter = TextureFilter::TF_Nearest; dynamicCanvas->UpdateResource(); echoUpdateTextureRegion = std::unique_ptr(new FUpdateTextureRegion2D(0, 0, 0, 0, canvasWidth, canvasHeight)); // buffers initialization bytesPerPixel = 4; // r g b a bufferPitch = canvasWidth * bytesPerPixel; bufferSize = canvasWidth * canvasHeight * bytesPerPixel; canvasPixelData = std::unique_ptr(new uint8[bufferSize]); ClearCanvas(); } void UPlotCanvas::InitializeDrawingTools(const int32 brushRadius, uint8 r, uint8 g, uint8 b) { radius = brushRadius; brushBufferSize = radius * radius * 4 * bytesPerPixel; //2r*2r * bpp canvasBrushMask = std::unique_ptr(new uint8[brushBufferSize]); uint8* canvasBrushPixelPtr = canvasBrushMask.get(); for (int px = -radius; px < radius; ++px) { for (int py = -radius; py < radius; ++py) { int32 tx = px + radius; int32 ty = py + radius; canvasBrushPixelPtr = canvasBrushMask.get() + (tx + +ty * 2 * radius) * bytesPerPixel; if (px*px + py * py < radius*radius) { setPixelColor(canvasBrushPixelPtr, r,g,b, 255); //black alpha 255 - bgra } else { setPixelColor(canvasBrushPixelPtr, 0, 0, 0, 0); // alpha 0 } } } } void UPlotCanvas::DrawFromData() { APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0); APawnBase* PawnBase = Cast(Pawn); for (int i = 0; i < PawnBase->Plot3D->Points.Num(); ++i) { InitializeDrawingTools(1, PawnBase->Plot3D->Points[i].Color.R, PawnBase->Plot3D->Points[i].Color.G, PawnBase->Plot3D->Points[i].Color.B); DrawDot((PawnBase->Plot3D->Points[i].Location.X - PawnBase->Plot3D->MinX) / (PawnBase->Plot3D->MaxX - PawnBase->Plot3D->MinX) * canvasWidth, (PawnBase->Plot3D->U[i] - PawnBase->Plot3D->MinU) / (PawnBase->Plot3D->MaxU - PawnBase->Plot3D->MinU) * canvasHeight); } UpdateCanvas(); } void UPlotCanvas::DrawParallelCoordinatesPlot() { APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0); APawnBase* PawnBase = Cast(Pawn); for (int i = 0; i < PawnBase->Plot3D->Points.Num(); ++i) { int32 x0 = 0; int32 x1 = canvasWidth * 1 / 5; int32 x2 = canvasWidth * 2 / 5; int32 x3 = canvasWidth * 3 / 5; int32 x4 = canvasWidth * 4 / 5; int32 x5 = canvasWidth; int32 y0 = (PawnBase->Plot3D->Points[i].Location.X - PawnBase->Plot3D->MinX) / (PawnBase->Plot3D->MaxX - PawnBase->Plot3D->MinX) * canvasHeight / 4; int32 y1 = (PawnBase->Plot3D->Points[i].Location.Y - PawnBase->Plot3D->MinY) / (PawnBase->Plot3D->MaxY - PawnBase->Plot3D->MinY) * canvasHeight / 4; int32 y2 = (PawnBase->Plot3D->Points[i].Location.Z - PawnBase->Plot3D->MinZ) / (PawnBase->Plot3D->MaxZ - PawnBase->Plot3D->MinZ) * canvasHeight / 4; int32 y3 = (PawnBase->Plot3D->U[i] - PawnBase->Plot3D->MinU) / (PawnBase->Plot3D->MaxU - PawnBase->Plot3D->MinU) * canvasHeight / 4; int32 y4 = (PawnBase->Plot3D->V[i] - PawnBase->Plot3D->MinV) / (PawnBase->Plot3D->MaxV - PawnBase->Plot3D->MinV) * canvasHeight / 4; int32 y5 = (PawnBase->Plot3D->W[i] - PawnBase->Plot3D->MinW) / (PawnBase->Plot3D->MaxW - PawnBase->Plot3D->MinW) * canvasHeight / 4; uint8 r = PawnBase->Plot3D->Points[i].Color.R; uint8 g = PawnBase->Plot3D->Points[i].Color.G; uint8 b = PawnBase->Plot3D->Points[i].Color.B; DrawLine(x0, y0, x1, y1, r, g, b, 255); DrawLine(x1, y1, x2, y2, r, g, b, 255); DrawLine(x2, y2, x3, y3, r, g, b, 255); DrawLine(x3, y3, x4, y4, r, g, b, 255); DrawLine(x4, y4, x5, y5, r, g, b, 255); } UpdateCanvas(); } void UPlotCanvas::DrawDot(const int32 pixelCoordX, const int32 pixelCoordY) { uint8* canvasPixelPtr = canvasPixelData.get(); const uint8* canvasBrushPixelPtr = canvasBrushMask.get(); for (int px = -radius; px < radius; ++px) { for (int py = -radius; py < radius; ++py) { int32 tbx = px + radius; int32 tby = py + radius; canvasBrushPixelPtr = canvasBrushMask.get() + (tbx + tby * 2 * radius) * bytesPerPixel; if (*(canvasBrushPixelPtr + 3) == 255) // check the alpha value of the pixel of the brush mask { int32 tx = pixelCoordX + px; int32 ty = pixelCoordY + py; DrawPixel(tx, ty, *(canvasBrushPixelPtr + 2), *(canvasBrushPixelPtr + 1), *(canvasBrushPixelPtr), *(canvasBrushPixelPtr + 3)); } } } } void UPlotCanvas::DrawPixel(const int32 x, const int32 y, const uint8 r, const uint8 g, const uint8 b, const uint8 a) { if (x >= 0 && x < canvasWidth && y >= 0 && y < canvasHeight) { uint8* canvasPixelPtr = canvasPixelData.get() + (x + y * canvasWidth) * bytesPerPixel; setPixelColor(canvasPixelPtr, r, g, b, a); } } void UPlotCanvas::ClearCanvas() { uint8* canvasPixelPtr = canvasPixelData.get(); for (int i = 0; i < canvasWidth * canvasHeight; ++i) { setPixelColor(canvasPixelPtr, 128, 128, 128, 0); canvasPixelPtr += bytesPerPixel; } UpdateCanvas(); } void UPlotCanvas::UpdateCanvas() { if (echoUpdateTextureRegion) { dynamicCanvas->UpdateTextureRegions((int32)0, (uint32)1, echoUpdateTextureRegion.get(), (uint32)bufferPitch, (uint32)bytesPerPixel, canvasPixelData.get()); } } void UPlotCanvas::setPixelColor(uint8*& pointer, uint8 red, uint8 green, uint8 blue, uint8 alpha) { *pointer = blue; //b *(pointer + 1) = green; //g *(pointer + 2) = red; //r *(pointer + 3) = alpha; //a } void UPlotCanvas::DrawLine(int32 x1, int32 y1, int32 x2, int32 y2, const uint8 r, const uint8 g, const uint8 b, const uint8 a) { // Bresenham's line algorithm const bool steep = (fabs(y2 - y1) > fabs(x2 - x1)); if (steep) { std::swap(x1, y1); std::swap(x2, y2); } if (x1 > x2) { std::swap(x1, x2); std::swap(y1, y2); } const float dx = x2 - x1; const float dy = fabs(y2 - y1); float error = dx / 2.0f; const int ystep = (y1 < y2) ? 1 : -1; int y = (int)y1; const int maxX = (int)x2; for (int x = (int)x1; x < maxX; x++) { if (steep) { DrawPixel(y, x, r, g, b, a); } else { DrawPixel(x, y, r, g, b, a); } error -= dy; if (error < 0) { y += ystep; error += dx; } } }