HeatMapActor.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /*
  2. * @Author: namidame
  3. * @Description: A Heatmap Generate Plugin, Supports Heightmap, Texture Coordinate Points And Geographic Location Data.
  4. * @Date: 2023/03/24
  5. */
  6. #include "HeatMapActor.h"
  7. #include <vector>
  8. #include <functional>
  9. #include "TextureResource.h"
  10. #include "GeoReferencingSystem.h"
  11. using namespace std;
  12. // Sets default values
  13. AHeatMapActor::AHeatMapActor()
  14. : TextureSize(FVector2D(2048, 2048))
  15. , MapSize(FVector2D(100000, 100000))
  16. , MapSecment(FVector2D(500, 500))
  17. , LerpScale(2.5)
  18. , HeightScale(2500.0)
  19. , Opacity(0.7)
  20. , InfluenceSize(50)
  21. {
  22. // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
  23. // PrimaryActorTick.bCanEverTick = true;
  24. RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
  25. MapMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("MapMesh"));
  26. MapMesh->SetupAttachment(RootComponent);
  27. }
  28. // Called when the game starts or when spawned
  29. void AHeatMapActor::BeginPlay()
  30. {
  31. Super::BeginPlay();
  32. }
  33. // Called every frame
  34. void AHeatMapActor::Tick(float DeltaTime)
  35. {
  36. Super::Tick(DeltaTime);
  37. }
  38. void AHeatMapActor::CreateMapMesh(FVector2D mapSize)
  39. {
  40. UE_LOG(LogTemp, Log, TEXT("Create Map Mesh"));
  41. MapSize = mapSize;
  42. float secmentX = MapSecment.X;
  43. float secmentY = MapSecment.Y;
  44. MapMesh->ClearAllMeshSections();
  45. TArray<FVector> Vertices;
  46. TArray<int32> Triangles;
  47. TArray<FVector> Normals;
  48. TArray<FVector2D> UV0;
  49. TArray<FColor> VertexColors;
  50. TArray<FProcMeshTangent> Tangents;
  51. int32 curX = 0;
  52. int32 curY = 0;
  53. FVector curLeftTop = FVector::ZeroVector;
  54. float xStep = MapSize.X / secmentX;
  55. float yStep = MapSize.Y / secmentY;
  56. float uStep = 1 / secmentX;
  57. float vStep = 1 / secmentY;
  58. int32 times = 0;
  59. while(curY < secmentY)
  60. {
  61. while(curX < secmentX)
  62. {
  63. FVector leftTop = curLeftTop;
  64. FVector rightTop = leftTop + GetActorForwardVector() * xStep;
  65. FVector leftBottom = curLeftTop + GetActorRightVector() * yStep;
  66. FVector rightBottom = leftBottom + GetActorForwardVector() * xStep;;
  67. int32 startIdx = Vertices.Num();
  68. Vertices += {leftTop, rightTop, leftBottom, rightBottom};
  69. Triangles += {startIdx+2, startIdx+1, startIdx, startIdx+3, startIdx+1, startIdx+2};
  70. Normals += {FVector::ZAxisVector, FVector::ZAxisVector, FVector::ZAxisVector, FVector::ZAxisVector};
  71. UV0 +=
  72. {
  73. FVector2D(curX*uStep, curY*vStep),
  74. FVector2D((curX+1)*uStep, curY*vStep),
  75. FVector2D(curX*uStep, (curY+1)*vStep),
  76. FVector2D((curX+1)*uStep, (curY+1)*vStep)
  77. };
  78. curLeftTop = rightTop;
  79. ++curX;
  80. ++times;
  81. }
  82. curX = 0;
  83. ++curY;
  84. curLeftTop = GetActorRightVector() * curY * yStep;
  85. }
  86. MapMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, VertexColors, Tangents, false);
  87. }
  88. void AHeatMapActor::CreateWithHeightMap(UTexture2D* texture, FVector2D mapSize)
  89. {
  90. m_type = E_HeatMapType::HEIGHT_MAP;
  91. int32 sizeX = texture->GetSizeX();
  92. int32 sizeY = texture->GetSizeY();
  93. FVector2D textureSize(sizeX, sizeY);
  94. TextureSize = textureSize;
  95. // TArray<FColor> colorArr = AHeatMapActor::GetColorDataFromTexture(texture, textureSize);
  96. // this->CreateWithData(colorArr, textureSize, mapSize);
  97. // create map mesh
  98. this->CreateMapMesh(mapSize);
  99. // create material with texture
  100. if(!HeatMapActorMaterial) return;
  101. UMaterialInstanceDynamic* mat = UMaterialInstanceDynamic::Create(HeatMapActorMaterial, nullptr);
  102. mat->SetTextureParameterValue(TEXT("Texture"), texture);
  103. mat->SetScalarParameterValue(TEXT("ModelScale"), mapSize.X / 100000);
  104. Material = mat;
  105. this->setMaterialLerpScale(LerpScale);
  106. this->setMaterialHeightScale(HeightScale);
  107. this->setMaterialOpacity(Opacity);
  108. // set material into map mesh
  109. MapMesh->SetMaterial(0, mat);
  110. }
  111. void AHeatMapActor::CreateWithData(const TArray<FColor>& colorArr, FVector2D textureSize, FVector2D mapSize)
  112. {
  113. TextureSize = textureSize;
  114. // UE_LOG(LogTemp, Log, TEXT("check===%d,%d"), colorArr[0].B, colorArr[TextureSize.X-1].B);
  115. UTexture2D* texture = UTexture2D::CreateTransient(textureSize.X, textureSize.Y);
  116. FTexture2DMipMap* MipMap = &texture->GetPlatformData()->Mips[0];
  117. FByteBulkData* ImageData = &MipMap->BulkData;
  118. uint8* RawImageData = (uint8*)ImageData->Lock(LOCK_READ_WRITE);
  119. FMemory::Memcpy(RawImageData, colorArr.GetData(), colorArr.Num()*4);
  120. ImageData->Unlock();
  121. texture->UpdateResource();
  122. // create map mesh
  123. this->CreateMapMesh(mapSize);
  124. // create material with texture
  125. if(!HeatMapActorMaterial) return;
  126. UMaterialInstanceDynamic* mat = UMaterialInstanceDynamic::Create(HeatMapActorMaterial, nullptr);
  127. mat->SetTextureParameterValue(TEXT("Texture"), texture);
  128. mat->SetScalarParameterValue(TEXT("ModelScale"), mapSize.X / 100000);
  129. Material = mat;
  130. this->setMaterialLerpScale(LerpScale);
  131. this->setMaterialHeightScale(HeightScale);
  132. this->setMaterialOpacity(Opacity);
  133. // set material into map mesh
  134. MapMesh->SetMaterial(0, mat);
  135. }
  136. void AHeatMapActor::setMaterialLerpScale(float scale)
  137. {
  138. LerpScale = scale;
  139. if(Material)
  140. {
  141. Material->SetScalarParameterValue(TEXT("LerpScale"), scale);
  142. }
  143. }
  144. void AHeatMapActor::setMaterialHeightScale(float scale)
  145. {
  146. HeightScale = scale;
  147. if(Material)
  148. {
  149. Material->SetScalarParameterValue(TEXT("HeightScale"), scale);
  150. }
  151. }
  152. void AHeatMapActor::setMaterialOpacity(float opacity)
  153. {
  154. Opacity = opacity;
  155. if(Material)
  156. {
  157. Material->SetScalarParameterValue(TEXT("Opacity"), opacity);
  158. }
  159. }
  160. TArray<FColor> AHeatMapActor::GetColorDataFromTexture(UTexture2D* texture, FVector2D textureSize)
  161. {
  162. TArray<FColor> rlt;
  163. TextureCompressionSettings oldSetting = texture->CompressionSettings;
  164. #if WITH_EDITORONLY_DATA
  165. TextureMipGenSettings mipSetting = texture->MipGenSettings;
  166. #endif
  167. bool isSRGB = texture->SRGB;
  168. texture->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
  169. #if WITH_EDITORONLY_DATA
  170. texture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
  171. #endif
  172. texture->SRGB = false;
  173. texture->UpdateResource();
  174. const FColor* colorArr = (FColor*)(texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY));
  175. for(int32 y = 0; y < textureSize.Y; ++y)
  176. {
  177. for(int32 x = 0; x < textureSize.X; ++x)
  178. {
  179. FColor color = colorArr[y * (int32)textureSize.X + x];
  180. rlt.Push(color);
  181. }
  182. }
  183. texture->GetPlatformData()->Mips[0].BulkData.Unlock();
  184. texture->CompressionSettings = oldSetting;
  185. #if WITH_EDITORONLY_DATA
  186. texture->MipGenSettings = mipSetting;
  187. #endif
  188. texture->SRGB = isSRGB;
  189. texture->UpdateResource();
  190. return rlt;
  191. }
  192. TArray<FColor> AHeatMapActor::GetColorDataFromPointHeightMap(const TMap<FVector2D, float>& map, FVector2D textureSize, int32 influenceSize)
  193. {
  194. // 根据零散的点集生成高度图数据
  195. int32 sizeX = textureSize.X;
  196. int32 sizeY = textureSize.Y;
  197. std::vector<float> heightVec(sizeX * sizeY, 0);
  198. // 遍历所有数据点,对纹理数据增加权重
  199. for(auto it : map)
  200. {
  201. int32 px = it.Key.X;
  202. int32 py = it.Key.Y;
  203. float height = it.Value;
  204. int32 startX = FMath::Max(px - influenceSize, 0);
  205. int32 endX = FMath::Min(px + influenceSize, sizeX - 1);
  206. int32 startY = FMath::Max(py - influenceSize, 0);
  207. int32 endY = FMath::Min(py + influenceSize, sizeY - 1);
  208. for(int32 j = startY; j <= endY; ++j)
  209. {
  210. for(int32 i = startX; i <= endX; ++i)
  211. {
  212. int32 distance = (i - px)*(i - px) + (j - py)*(j - py);
  213. if(distance <= influenceSize * influenceSize)
  214. {
  215. // 在影响圈内 增加权重
  216. float percent = 1.0f * distance / (influenceSize * influenceSize);
  217. float influence = 1 - percent;
  218. if(InfluenceCurve)
  219. {
  220. influence = InfluenceCurve->GetFloatValue(percent);
  221. }
  222. float newHeight = influence * height;
  223. heightVec[j * sizeX + i] = FMath::Max(heightVec[j * sizeX + i], newHeight);
  224. }
  225. }
  226. }
  227. }
  228. TArray<FColor> rlt;
  229. for (int32 i = 0; i < heightVec.size(); ++i)
  230. {
  231. float height = heightVec[i];
  232. int32 bValue = FMath::CeilToInt(height * 255.0f);
  233. bValue = FMath::Min(bValue, 255);
  234. FColor color(0, 0, bValue);
  235. rlt.Add(color);
  236. }
  237. return rlt;
  238. }
  239. void AHeatMapActor::setMapSecment(FVector2D secment)
  240. {
  241. MapSecment = FVector2D(round(secment.X), round(secment.Y));
  242. this->ReGenerateMapAndMaterial();
  243. }
  244. void AHeatMapActor::setMapSize(FVector2D size)
  245. {
  246. MapSize = size;
  247. this->ReGenerateMapAndMaterial();
  248. }
  249. void AHeatMapActor::CreateWithPointHeightValue(const TMap<FVector2D, float>& map, FVector2D textureSize, FVector2D mapSize, bool isGeoData)
  250. {
  251. if(isGeoData)
  252. {
  253. m_type = E_HeatMapType::GEO_DATA;
  254. }
  255. else
  256. {
  257. m_type = E_HeatMapType::POINT_HEIGHT_DATA;
  258. }
  259. PointHeightValueMap = map;
  260. TArray<FColor> colorArr = this->GetColorDataFromPointHeightMap(map, textureSize, InfluenceSize);
  261. this->CreateWithData(colorArr, textureSize, mapSize);
  262. }
  263. void AHeatMapActor::CreateWithGeoInfoMap(const TMap<FVector, float>& map)
  264. {
  265. UE_LOG(LogTemp, Log, TEXT("texturesize=%f,%f, mapsize=%f,%f, influence=%d, segment=%f,%f"),TextureSize.X,TextureSize
  266. .Y,MapSize.X,MapSize.Y, InfluenceSize, MapSecment.X,MapSecment.Y)
  267. int32 t1 = FDateTime::Now().GetMillisecond();
  268. GeoInfoMap = map;
  269. TMap<FVector2D, float> rlt = this->ConvertGeoValueMapToUnrealValueMap(map);
  270. this->CreateWithPointHeightValue(rlt, TextureSize, MapSize, true);
  271. int32 t2 = FDateTime::Now().GetMillisecond();
  272. UE_LOG(LogTemp, Log, TEXT("use time=%d"), t2 - t1);
  273. }
  274. void AHeatMapActor::AddPointHeightValue(const FVector2D& pos, float value)
  275. {
  276. if(m_type == E_HeatMapType::POINT_HEIGHT_DATA)
  277. {
  278. if(PointHeightValueMap.Contains(pos))
  279. {
  280. PointHeightValueMap[pos] = value;
  281. }
  282. else
  283. {
  284. PointHeightValueMap.Add(pos, value);
  285. }
  286. this->ReGenerateMapAndMaterial();
  287. }
  288. }
  289. void AHeatMapActor::DeletePointHeightValue(const FVector2D& pos)
  290. {
  291. if(m_type == E_HeatMapType::POINT_HEIGHT_DATA)
  292. {
  293. if(PointHeightValueMap.Contains(pos))
  294. {
  295. PointHeightValueMap.Remove(pos);
  296. }
  297. this->ReGenerateMapAndMaterial();
  298. }
  299. }
  300. void AHeatMapActor::setPointInfluenceSize(int32 size)
  301. {
  302. InfluenceSize = size;
  303. this->ReGenerateMapAndMaterial();
  304. }
  305. void AHeatMapActor::ReGenerateMapAndMaterial()
  306. {
  307. if(m_type == E_HeatMapType::HEIGHT_MAP)
  308. {
  309. this->CreateWithHeightMap(HeightMapTexture, MapSize);
  310. }
  311. else if(m_type == E_HeatMapType::POINT_HEIGHT_DATA)
  312. {
  313. this->CreateWithPointHeightValue(PointHeightValueMap, TextureSize, MapSize, false);
  314. }
  315. else if(m_type == E_HeatMapType::GEO_DATA)
  316. {
  317. this->CreateWithGeoInfoMap(GeoInfoMap);
  318. }
  319. }
  320. void AHeatMapActor::setMapGeoLocation(FVector location)
  321. {
  322. FVector newPos = this->ConvertGeoGraphicToEngine(location);
  323. SetActorLocation(newPos);
  324. this->ReGenerateMapAndMaterial();
  325. }
  326. TMap<FVector2D, float> AHeatMapActor::ConvertGeoValueMapToUnrealValueMap(const TMap<FVector, float>& map)
  327. {
  328. TMap<FVector2D, float> rlt1;
  329. float minX = 0, maxX = 0, minY = 0, maxY = 0;
  330. for(auto it : map)
  331. {
  332. FVector loc = it.Key;
  333. FVector newLoc = this->ConvertGeoGraphicToEngine(loc);
  334. FVector2D loc2(newLoc.X, newLoc.Y);
  335. rlt1.Add(loc2, it.Value);
  336. // 找出最大最小坐标 从而确定平面的位置
  337. if(loc2.X < minX) minX = loc2.X;
  338. if(loc2.X > maxX) maxX = loc2.X;
  339. if(loc2.Y < minY) minY = loc2.Y;
  340. if(loc2.Y > maxY) maxY = loc2.Y;
  341. }
  342. SetActorLocation(FVector(minX, minY, 0));
  343. float xLen = maxX - minX;
  344. float yLen = maxY - minY;
  345. TMap<FVector2D, float> rlt2;
  346. // 计算textureSize
  347. int32 defaultSizeBase = 2048;
  348. if(xLen > yLen)
  349. {
  350. TextureSize = FVector2D((int32)(defaultSizeBase * xLen / yLen), defaultSizeBase);
  351. }
  352. else
  353. {
  354. TextureSize = FVector2D(defaultSizeBase, (int32)(defaultSizeBase * yLen / xLen));
  355. }
  356. // 将点的坐标映射到tex.X, tex.Y范围内
  357. for(auto it : rlt1)
  358. {
  359. FVector2D loc = it.Key;
  360. float xPercent = (loc.X - minX) / xLen;
  361. float x = xPercent * TextureSize.X;
  362. float yPercent = (loc.Y - minY) / yLen;
  363. float y = yPercent * TextureSize.Y;
  364. rlt2.Add(FVector2D(x, y), it.Value);
  365. }
  366. // 计算mapSize
  367. MapSize = FVector2D((int32)(xLen), (int32)(yLen));
  368. return rlt2;
  369. }
  370. FVector AHeatMapActor::ConvertGeoGraphicToEngine(FVector location)
  371. {
  372. FGeographicCoordinates geo(location.X, location.Y, location.Z);
  373. FVector newPos;
  374. AGeoReferencingSystem::GetGeoReferencingSystem(this)->GeographicToEngine(geo, newPos);
  375. return newPos;
  376. }