HeatMapActor.cpp 11 KB

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