| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 | /** @Author: namidame* @Description: A Heatmap Generate Plugin, Supports Heightmap, Texture Coordinate Points And Geographic Location Data.* @Date: 2023/03/24*/#include "HeatMapActor.h"#include <vector>#include <functional>#include "TextureResource.h"#include "GeoReferencingSystem.h"using namespace std;// Sets default valuesAHeatMapActor::AHeatMapActor()	: TextureSize(FVector2D(2048, 2048))	, MapSize(FVector2D(100000, 100000))	, MapSecment(FVector2D(500, 500))	, LerpScale(2.5)	, HeightScale(2500.0)	, Opacity(0.7)	, InfluenceSize(50){ 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.	// PrimaryActorTick.bCanEverTick = true;	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));		MapMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("MapMesh"));	MapMesh->SetupAttachment(RootComponent);}// Called when the game starts or when spawnedvoid AHeatMapActor::BeginPlay(){	Super::BeginPlay();}// Called every framevoid AHeatMapActor::Tick(float DeltaTime){	Super::Tick(DeltaTime);}void AHeatMapActor::CreateMapMesh(FVector2D mapSize){	UE_LOG(LogTemp, Log, TEXT("Create Map Mesh"));	MapSize = mapSize;	float secmentX = MapSecment.X;	float secmentY = MapSecment.Y;		MapMesh->ClearAllMeshSections();	TArray<FVector> Vertices;	TArray<int32> Triangles;	TArray<FVector> Normals;	TArray<FVector2D> UV0;	TArray<FColor> VertexColors;	TArray<FProcMeshTangent> Tangents;	int32 curX = 0;	int32 curY = 0;	FVector curLeftTop = FVector::ZeroVector;	float xStep = MapSize.X / secmentX;	float yStep = MapSize.Y / secmentY;	float uStep = 1 / secmentX;	float vStep = 1 / secmentY;	int32 times = 0;	while(curY < secmentY)	{		while(curX < secmentX)		{			FVector leftTop = curLeftTop;			FVector rightTop = leftTop + GetActorForwardVector() * xStep;			FVector leftBottom = curLeftTop + GetActorRightVector() * yStep;			FVector rightBottom = leftBottom + GetActorForwardVector() * xStep;;			int32 startIdx = Vertices.Num();			Vertices += {leftTop, rightTop, leftBottom, rightBottom};			Triangles += {startIdx+2, startIdx+1, startIdx, startIdx+3, startIdx+1, startIdx+2};			Normals += {FVector::ZAxisVector, FVector::ZAxisVector, FVector::ZAxisVector, FVector::ZAxisVector};			UV0 +=				{					FVector2D(curX*uStep, curY*vStep),					FVector2D((curX+1)*uStep, curY*vStep),					FVector2D(curX*uStep, (curY+1)*vStep),					FVector2D((curX+1)*uStep, (curY+1)*vStep)				};			curLeftTop = rightTop;			++curX;			++times;		}		curX = 0;		++curY;		curLeftTop = GetActorRightVector() * curY * yStep;	}	MapMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, VertexColors, Tangents, false);}void AHeatMapActor::CreateWithHeightMap(UTexture2D* texture, FVector2D mapSize){    m_type = E_HeatMapType::HEIGHT_MAP;    int32 sizeX = texture->GetSizeX();    int32 sizeY = texture->GetSizeY();    FVector2D textureSize(sizeX, sizeY);    TextureSize = textureSize;    // TArray<FColor> colorArr = AHeatMapActor::GetColorDataFromTexture(texture, textureSize);    // this->CreateWithData(colorArr, textureSize, mapSize);        // create map mesh    this->CreateMapMesh(mapSize);        // create material with texture    if(!HeatMapActorMaterial) return;    UMaterialInstanceDynamic* mat = UMaterialInstanceDynamic::Create(HeatMapActorMaterial, nullptr);    mat->SetTextureParameterValue(TEXT("Texture"), texture);    mat->SetScalarParameterValue(TEXT("ModelScale"), mapSize.X / 100000);        Material = mat;        this->setMaterialLerpScale(LerpScale);    this->setMaterialHeightScale(HeightScale);    this->setMaterialOpacity(Opacity);        // set material into map mesh    MapMesh->SetMaterial(0, mat);}void AHeatMapActor::CreateWithData(const TArray<FColor>& colorArr, FVector2D textureSize, FVector2D mapSize){	TextureSize = textureSize;// UE_LOG(LogTemp, Log, TEXT("check===%d,%d"), colorArr[0].B, colorArr[TextureSize.X-1].B);	UTexture2D* texture = UTexture2D::CreateTransient(textureSize.X, textureSize.Y);	FTexture2DMipMap* MipMap = &texture->GetPlatformData()->Mips[0];	FByteBulkData* ImageData = &MipMap->BulkData;	uint8* RawImageData = (uint8*)ImageData->Lock(LOCK_READ_WRITE);	FMemory::Memcpy(RawImageData, colorArr.GetData(), colorArr.Num()*4);	ImageData->Unlock();	texture->UpdateResource();		// create map mesh	this->CreateMapMesh(mapSize);		// create material with texture	if(!HeatMapActorMaterial) return;	UMaterialInstanceDynamic* mat = UMaterialInstanceDynamic::Create(HeatMapActorMaterial, nullptr);	mat->SetTextureParameterValue(TEXT("Texture"), texture);	mat->SetScalarParameterValue(TEXT("ModelScale"), mapSize.X / 100000);		Material = mat;		this->setMaterialLerpScale(LerpScale);	this->setMaterialHeightScale(HeightScale);	this->setMaterialOpacity(Opacity);		// set material into map mesh	MapMesh->SetMaterial(0, mat);}void AHeatMapActor::setMaterialLerpScale(float scale){	LerpScale = scale;	if(Material)	{		Material->SetScalarParameterValue(TEXT("LerpScale"), scale);	}}void AHeatMapActor::setMaterialHeightScale(float scale){	HeightScale = scale;	if(Material)	{		Material->SetScalarParameterValue(TEXT("HeightScale"), scale);	}}void AHeatMapActor::setMaterialOpacity(float opacity){	Opacity = opacity;	if(Material)	{		Material->SetScalarParameterValue(TEXT("Opacity"), opacity);	}}TArray<FColor> AHeatMapActor::GetColorDataFromTexture(UTexture2D* texture, FVector2D textureSize){	TArray<FColor> rlt;		TextureCompressionSettings oldSetting = texture->CompressionSettings;#if WITH_EDITORONLY_DATA	TextureMipGenSettings mipSetting = texture->MipGenSettings;#endif	bool isSRGB = texture->SRGB;		texture->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;#if WITH_EDITORONLY_DATA	texture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;#endif			texture->SRGB = false;	texture->UpdateResource();		const FColor* colorArr = (FColor*)(texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY));		for(int32 y = 0; y < textureSize.Y; ++y)	{		for(int32 x = 0; x < textureSize.X; ++x)		{			FColor color = colorArr[y * (int32)textureSize.X + x];			rlt.Push(color);		}	}	texture->GetPlatformData()->Mips[0].BulkData.Unlock();	texture->CompressionSettings = oldSetting;#if WITH_EDITORONLY_DATA	texture->MipGenSettings = mipSetting;#endif	texture->SRGB = isSRGB;	texture->UpdateResource();	return rlt;}TArray<FColor> AHeatMapActor::GetColorDataFromPointHeightMap(const TMap<FVector2D, float>& map, FVector2D textureSize, int32 influenceSize){	// 根据零散的点集生成高度图数据	int32 sizeX = textureSize.X;	int32 sizeY = textureSize.Y;	std::vector<float> heightVec(sizeX * sizeY, 0);	// 遍历所有数据点,对纹理数据增加权重	for(auto it : map)	{		int32 px = it.Key.X;		int32 py = it.Key.Y;		float height = it.Value;		int32 startX = FMath::Max(px - influenceSize, 0);		int32 endX = FMath::Min(px + influenceSize, sizeX - 1);		int32 startY = FMath::Max(py - influenceSize, 0);		int32 endY = FMath::Min(py + influenceSize, sizeY - 1);		for(int32 j = startY; j <= endY; ++j)		{			for(int32 i = startX; i <= endX; ++i)			{				int32 distance = (i - px)*(i - px) + (j - py)*(j - py);				if(distance <= influenceSize * influenceSize)				{					// 在影响圈内 增加权重					float percent = 1.0f * distance / (influenceSize * influenceSize);					float influence = 1 - percent;					if(InfluenceCurve)					{						influence = InfluenceCurve->GetFloatValue(percent);					}					float newHeight = influence * height;					heightVec[j * sizeX + i] = FMath::Max(heightVec[j * sizeX + i], newHeight);				}			}		}	}		TArray<FColor> rlt;	for (int32 i = 0; i < heightVec.size(); ++i)	{		float height = heightVec[i];		int32 bValue = FMath::CeilToInt(height * 255.0f);		bValue = FMath::Min(bValue, 255);		FColor color(0, 0, bValue);		rlt.Add(color);	}	return rlt;}void AHeatMapActor::setMapSecment(FVector2D secment){	MapSecment = FVector2D(round(secment.X), round(secment.Y));	this->ReGenerateMapAndMaterial();}void AHeatMapActor::setMapSize(FVector2D size){	MapSize = size;	this->ReGenerateMapAndMaterial();}void AHeatMapActor::CreateWithPointHeightValue(const TMap<FVector2D, float>& map, FVector2D textureSize, FVector2D mapSize, bool isGeoData){	if(isGeoData)	{		m_type = E_HeatMapType::GEO_DATA;	}	else	{		m_type = E_HeatMapType::POINT_HEIGHT_DATA;	}	PointHeightValueMap = map;	TArray<FColor> colorArr = this->GetColorDataFromPointHeightMap(map, textureSize, InfluenceSize);	this->CreateWithData(colorArr, textureSize, mapSize);}void AHeatMapActor::CreateWithGeoInfoMap(const TMap<FVector, float>& map){	UE_LOG(LogTemp, Log, TEXT("texturesize=%f,%f, mapsize=%f,%f, influence=%d, segment=%f,%f"),TextureSize.X,TextureSize	.Y,MapSize.X,MapSize.Y, InfluenceSize, MapSecment.X,MapSecment.Y)	int32 t1 = FDateTime::Now().GetMillisecond();	GeoInfoMap = map;	TMap<FVector2D, float> rlt = this->ConvertGeoValueMapToUnrealValueMap(map);	this->CreateWithPointHeightValue(rlt, TextureSize, MapSize, true);	int32 t2 = FDateTime::Now().GetMillisecond();	UE_LOG(LogTemp, Log, TEXT("use time=%d"), t2 - t1);}void AHeatMapActor::AddPointHeightValue(const FVector2D& pos, float value){	if(m_type == E_HeatMapType::POINT_HEIGHT_DATA)	{		if(PointHeightValueMap.Contains(pos))		{			PointHeightValueMap[pos] = value;		}		else		{			PointHeightValueMap.Add(pos, value);		}		this->ReGenerateMapAndMaterial();	}}void AHeatMapActor::DeletePointHeightValue(const FVector2D& pos){	if(m_type == E_HeatMapType::POINT_HEIGHT_DATA)	{		if(PointHeightValueMap.Contains(pos))		{			PointHeightValueMap.Remove(pos);		}		this->ReGenerateMapAndMaterial();	}}void AHeatMapActor::setPointInfluenceSize(int32 size){	InfluenceSize = size;	this->ReGenerateMapAndMaterial();}void AHeatMapActor::ReGenerateMapAndMaterial(){	if(m_type == E_HeatMapType::HEIGHT_MAP)	{		this->CreateWithHeightMap(HeightMapTexture, MapSize);		}	else if(m_type == E_HeatMapType::POINT_HEIGHT_DATA)	{		this->CreateWithPointHeightValue(PointHeightValueMap, TextureSize, MapSize, false);	}	else if(m_type == E_HeatMapType::GEO_DATA)	{		this->CreateWithGeoInfoMap(GeoInfoMap);	}}void AHeatMapActor::setMapGeoLocation(FVector location){	FVector newPos = this->ConvertGeoGraphicToEngine(location);		SetActorLocation(newPos);	this->ReGenerateMapAndMaterial();}TMap<FVector2D, float> AHeatMapActor::ConvertGeoValueMapToUnrealValueMap(const TMap<FVector, float>& map){	TMap<FVector2D, float> rlt1;	float minX = 0, maxX = 0, minY = 0, maxY = 0;		for(auto it : map)	{		FVector loc = it.Key;				FVector newLoc = this->ConvertGeoGraphicToEngine(loc);				FVector2D loc2(newLoc.X, newLoc.Y);		rlt1.Add(loc2, it.Value);		// 找出最大最小坐标 从而确定平面的位置		if(loc2.X < minX) minX = loc2.X;		if(loc2.X > maxX) maxX = loc2.X;		if(loc2.Y < minY) minY = loc2.Y;		if(loc2.Y > maxY) maxY = loc2.Y;	}	SetActorLocation(FVector(minX, minY, 0));		float xLen = maxX - minX;	float yLen = maxY - minY;	TMap<FVector2D, float> rlt2;		// 计算textureSize	int32 defaultSizeBase = 2048;	if(xLen > yLen)	{		TextureSize = FVector2D((int32)(defaultSizeBase * xLen / yLen), defaultSizeBase);	}	else	{		TextureSize = FVector2D(defaultSizeBase, (int32)(defaultSizeBase * yLen / xLen));	}		// 将点的坐标映射到tex.X, tex.Y范围内	for(auto it : rlt1)	{		FVector2D loc = it.Key;		float xPercent = (loc.X - minX) / xLen;		float x = xPercent * TextureSize.X;		float yPercent = (loc.Y - minY) / yLen;		float y = yPercent * TextureSize.Y;		rlt2.Add(FVector2D(x, y), it.Value);	}	// 计算mapSize	MapSize = FVector2D((int32)(xLen), (int32)(yLen));		return rlt2;}FVector AHeatMapActor::ConvertGeoGraphicToEngine(FVector location){	FGeographicCoordinates geo(location.X, location.Y, location.Z);	FVector newPos;	AGeoReferencingSystem::GetGeoReferencingSystem(this)->GeographicToEngine(geo, newPos);	return newPos;}
 |