UE5.2、CesiumForUnreal实现加载GeoJson绘制单面

文章目录

  • 前言
  • 一、实现目标
  • 二、实现过程
    • 1.实现原理
    • 2.数据读取
    • 3.三角剖分
    • 3.具体代码
  • 4.蓝图测试


前言

UE5、CesiumForUnreal实现加载GeoJson绘制单面(Polygon)功能(StaticMesh方式)


一、实现目标

通过读取本地的Geojson数据,在UE中以staticMeshComponent的形式绘制出面数据,支持Editor和Runtime环境,如下图

singlePolygon

二、实现过程

1.实现原理

首先读取Geojson数据,然后进行三角剖分,最后根据顶点和索引创建StaticMesh。

2.数据读取

本文使用的是polygon转linestring格式的Geojson线状数据文件,特别需要注意,转完的数据需要去掉coordinates字段里的一组中括号。在UE中直接读取文本对其进行解析,生成坐标数组。本文数据只考虑一个feature情况,数据坐标格式为wgs84经纬度坐标。

例:{
“type”: “FeatureCollection”,
“name”: “singlePolygon”,
“crs”: { “type”: “name”, “properties”: { “name”: “urn:ogc:def:crs:OGC:1.3:CRS84” } },
“features”: [
{ “type”: “Feature”, “properties”: { “id”: 1 }, “geometry”: { “type”: “MultiLineString”, “coordinates”: [ [ 107.5955545517036, 34.322768426465544 ], [ 108.086216375377106, 34.660927250889173 ], [ 109.133845674571887, 34.448749164976306 ], [ 109.518418455288952, 33.261877996901205 ], [ 109.067540022724117, 32.552407522130054 ], [ 107.734796420583919, 32.738063347303815 ], [ 106.726950512497794, 32.930349737662347 ], [ 106.786625599160786, 33.792323211683374 ], [ 107.025325945812767, 33.938195645748472 ], [ 107.608815682073157, 34.322768426465544 ], [ 107.608815682073157, 34.322768426465544 ],[ 107.5955545517036, 34.322768426465544 ] ] } }
]
}

3.三角剖分

ue不支持直接绘制面,因此需要将面进行三角剖分。为快速的对地理控件点位进行三角剖分,直接使用Mapbox的earcut.hpp耳切算法三角剖分库。
地址:传送门
在这里插入图片描述
将其放到工程中的Source下的某个目录中,本文是放到了Developer文件夹中
在这里插入图片描述

Build.cs配置


using UnrealBuildTool;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;public class cesiumGeoJson : ModuleRules
{public cesiumGeoJson(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;//PublicIncludePaths.AddRange(new string[] { Path.Combine(ModuleDirectory, "./Source/Engine/Developer") });PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" ,"CesiumRuntime","Json"});PrivateDependencyModuleNames.AddRange(new string[] {  });// Uncomment if you are using Slate UI// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });// Uncomment if you are using online features// PrivateDependencyModuleNames.Add("OnlineSubsystem");// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true}
}

3.具体代码

  1. AsinglePolygon_Geojson.h
// Copyright 2020-2021 CesiumGS, Inc. and Contributors#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CesiumGeoreference.h"
#include "singlePolygon_Geojson.generated.h"UCLASS()
class CESIUMGEOJSON_API AsinglePolygon_Geojson : public AActor
{GENERATED_BODY()public:	// Sets default values for this actor's propertiesAsinglePolygon_Geojson();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:	// Called every framevirtual void Tick(float DeltaTime) override;// Current world CesiumGeoreference.UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Entity | Polygon")ACesiumGeoreference* Georeference;// The selected feature index, current is only for '0', just for demo.UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Entity | Polygon");int SelectedFeatureIndex = 0;// The data path, that is the relative path of ue game content.UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Entity | Polygon");FString GeoJsonPath;/*** @breif Test generate polygon.*/UFUNCTION(CallInEditor, Category = "Entity | Polygon")void TestGeneratePolygon();/*** @brief Get feature vertices from linestring geojson.*/void GetCoordinatesFromLineStringGeoJson(const FString& FilePath, int FeatureIndex, TArray<FVector>& Positions);/*** @brief Build static polygon mesh component from current data.*/void BuildPolygonStaticMesh();private:// Verices that crs is unreal world.TArray<FVector> GeometryUE;// Vertices that crs is geographic wgs 84, epsg 4326.TArray<FVector> GeometryGeo;// Indices of vertices.TArray<uint32> Indices;};
  1. AsinglePolygon_Geojson.cpp
// Copyright 2020-2021 CesiumGS, Inc. and Contributors#include "singlePolygon_Geojson.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Engine/Developer/earcut.hpp"
#include "array"// Sets default values
AsinglePolygon_Geojson::AsinglePolygon_Geojson()
{// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = false;}// Called when the game starts or when spawned
void AsinglePolygon_Geojson::BeginPlay()
{Super::BeginPlay();}// Called every frame
void AsinglePolygon_Geojson::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}void AsinglePolygon_Geojson::TestGeneratePolygon()
{// Check file path and georeference is exist.if (!Georeference || GeoJsonPath.IsEmpty()){UE_LOG(LogTemp, Warning, TEXT("CesiumGeoreference or GeoJsonPath is valid, please check!"));return;}// Get the full path of file;FString FilePath = UKismetSystemLibrary::GetProjectDirectory() + GeoJsonPath;GetCoordinatesFromLineStringGeoJson(FilePath, 0, GeometryGeo);// First and last is the same point.GeometryGeo.Pop();// Triangulationstd::vector<std::vector<std::array<double, 2>>> Polygon;std::vector<std::array<double, 2>> Points;for (FVector& Item : GeometryGeo){std::array<double, 2> CurPoint = { Item.X, Item.Y };Points.push_back(CurPoint);// Convert coord from geo to ue.FVector PointUE = Georeference->TransformLongitudeLatitudeHeightPositionToUnreal(Item);GeometryUE.Push(PointUE);}// Current is just for simply polygon.Polygon.push_back(Points);std::vector<uint32_t> CalculateIndices = mapbox::earcut(Polygon);for (uint32_t Item : CalculateIndices){Indices.Push(Item);}// Build static mesh.BuildPolygonStaticMesh();
}void AsinglePolygon_Geojson::GetCoordinatesFromLineStringGeoJson(const FString& FilePath, int FeatureIndex, TArray<FVector>& Positions)
{// Check file exist.if (!FPaths::FileExists(FilePath)) {UE_LOG(LogTemp, Warning, TEXT("GeoJson file don't exist!"));return;}// ClearGeometryUE.Empty();GeometryGeo.Empty();Indices.Empty();FString FileString;FFileHelper::LoadFileToString(FileString, *FilePath);TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(FileString);TSharedPtr<FJsonObject> Root;// Check deserializeif (!FJsonSerializer::Deserialize(JsonReader, Root)) {return;}if (Root->HasField(TEXT("features"))) {TArray<TSharedPtr<FJsonValue>> Features = Root->GetArrayField(TEXT("features"));// Check feature existif (Features.Num() < 1 || Features.Num() < (FeatureIndex + 1)) {return;}TSharedPtr<FJsonObject> Feature = Features[FeatureIndex]->AsObject();if (Feature->HasField(TEXT("geometry"))) {TSharedPtr<FJsonObject> Geometry = Feature->GetObjectField(TEXT("geometry"));if (Geometry->HasField(TEXT("coordinates"))) {TArray<TSharedPtr<FJsonValue>> Coordinates = Geometry->GetArrayField(TEXT("coordinates"));for (auto Item : Coordinates) {auto Coordinate = Item->AsArray();FVector Position;// Check coord array is 2 or 3.if (Coordinate.Num() == 2) {// If don't have z value, add target value for z.Position = FVector(Coordinate[0]->AsNumber(), Coordinate[1]->AsNumber(), 5000);}else if (Coordinate.Num() == 3){Position = FVector(Coordinate[0]->AsNumber(), Coordinate[1]->AsNumber(), Coordinate[2]->AsNumber());}Positions.Emplace(Position);}}}}
}void AsinglePolygon_Geojson::BuildPolygonStaticMesh()
{UStaticMeshComponent* pStaticMeshComponent = NewObject<UStaticMeshComponent>(this);pStaticMeshComponent->SetFlags(RF_Transient);pStaticMeshComponent->SetWorldLocationAndRotation(FVector(0, 0, 0), FRotator(0, 0, 0));UStaticMesh* pStaticMesh = NewObject<UStaticMesh>(pStaticMeshComponent);pStaticMesh->NeverStream = true;pStaticMeshComponent->SetStaticMesh(pStaticMesh);FStaticMeshRenderData* pRenderData = new FStaticMeshRenderData();pRenderData->AllocateLODResources(1);FStaticMeshLODResources& LODResourece = pRenderData->LODResources[0];TArray<FStaticMeshBuildVertex> StaticMeshBuildVertices;StaticMeshBuildVertices.SetNum(GeometryUE.Num());// Calculate boundsglm::dvec3 MinPosition{ std::numeric_limits<double>::max() };glm::dvec3 MaxPosition{ std::numeric_limits<double>::lowest() };// Verticesfor (int i = 0; i < GeometryUE.Num(); i++){FStaticMeshBuildVertex& Vertex = StaticMeshBuildVertices[i];Vertex.Position = FVector3f(GeometryUE[i]);Vertex.UVs[0] = FVector2f(0, 0);Vertex.TangentX = FVector3f(0, 1, 0);Vertex.TangentY = FVector3f(1, 0, 0);Vertex.TangentZ = FVector3f(0, 0, 1);// Calculate max and min position;MinPosition.x = glm::min<double>(MinPosition.x, GeometryUE[i].X);MinPosition.y = glm::min<double>(MinPosition.y, GeometryUE[i].Y);MinPosition.z = glm::min<double>(MinPosition.z, GeometryUE[i].Z);MaxPosition.x = glm::max<double>(MaxPosition.x, GeometryUE[i].X);MaxPosition.y = glm::max<double>(MaxPosition.y, GeometryUE[i].Y);MaxPosition.z = glm::max<double>(MaxPosition.z, GeometryUE[i].Z);}// Bounding boxFBox BoundingBox(FVector3d(MinPosition.x, MinPosition.y, MinPosition.z), FVector3d(MaxPosition.x, MaxPosition.y, MaxPosition.z));BoundingBox.GetCenterAndExtents(pRenderData->Bounds.Origin, pRenderData->Bounds.BoxExtent);LODResourece.bHasColorVertexData = false;LODResourece.VertexBuffers.PositionVertexBuffer.Init(StaticMeshBuildVertices);LODResourece.VertexBuffers.StaticMeshVertexBuffer.Init(StaticMeshBuildVertices, 1);LODResourece.IndexBuffer.SetIndices(Indices, EIndexBufferStride::AutoDetect);LODResourece.bHasDepthOnlyIndices = false;LODResourece.bHasReversedIndices = false;LODResourece.bHasReversedDepthOnlyIndices = false;FStaticMeshSectionArray& Sections = LODResourece.Sections;FStaticMeshSection& Section = Sections.AddDefaulted_GetRef();Section.bEnableCollision = true;Section.bCastShadow = true;Section.NumTriangles = Indices.Num() / 3;Section.FirstIndex = 0;Section.MinVertexIndex = 0;Section.MaxVertexIndex = Indices.Num() - 1;// Add materialUMaterialInterface* CurMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("Material'/Game/Martials/M_Polygon.M_Polygon'"));  //此处的材质需要手动在编辑器中创建,而后在c++代码中引用UMaterialInstanceDynamic* CurMaterialIns = UMaterialInstanceDynamic::Create(CurMaterial, nullptr);CurMaterialIns->AddToRoot();CurMaterialIns->TwoSided = true;FName CurMaterialSlotName = pStaticMesh->AddMaterial(CurMaterialIns);int32 CurMaterialIndex = pStaticMesh->GetMaterialIndex(CurMaterialSlotName);Section.MaterialIndex = CurMaterialIndex;// Todo:Build CollisionpStaticMesh->SetRenderData(TUniquePtr<FStaticMeshRenderData>(pRenderData));pStaticMesh->InitResources();pStaticMesh->CalculateExtendedBounds();pRenderData->ScreenSize[0].Default = 1.0f;pStaticMesh->CreateBodySetup();pStaticMeshComponent->SetMobility(EComponentMobility::Movable);pStaticMeshComponent->SetupAttachment(this->RootComponent);pStaticMeshComponent->RegisterComponent();
}

代码中提到的材质,查看路径操作如下:
在这里插入图片描述

4.蓝图测试

  1. 基于c++类生成蓝图类,并放到世界场景中测试。
    在这里插入图片描述
  2. 在该细节面板中配置相关设置,主要是需要CesiumGeoference,用于WGS84和UE世界坐标的转换。已经Geojson数据的存放相对路径(相对于Game工程目录),不支持Geojson多feature。如下:
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/642368.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IP地址组成

一、简介 ​ IP地址由四段组成&#xff0c;每个字段是一个字节&#xff0c;即4个字节、 每个字节有8位&#xff0c;最大值是255(256&#xff1a;0~255)&#xff0c;是全世界范围是唯一的 32 位&#xff08;4个字节 * 8位&#xff09;的标识符。 ​ IP地址由两部分组成&#x…

电商模特危机!谷歌最新模型Tryon Diffusion一键试衣,线上购物被革命

目录 前言 摘要 方法 结果展示 多人试穿同一件衣服 同一个人试穿不同的衣服 交互式试穿演示 与最先进方法的比较 总结分析 前言 谷歌的新AI模型TryOnDiffusion&#xff0c;直接解决了AI换装的两大难题——既保留衣服细节&#xff0c;又能随意换姿势。…

通过curl访问k8s集群获取证书或token的方式

K8S安全控制框架主要由下面3个阶段进行控制&#xff0c;每一个阶段都支持插件方式&#xff0c;通过API Server配置来启用插件。 1. Authentication&#xff08;认证&#xff09; 2. Authorization&#xff08;授权&#xff09; 3. Admission Control&#xff08;准入控制&#…

大数据学习之Flink,Flink的安装部署

Flink部署 一、了解它的关键组件 客户端&#xff08;Client&#xff09; 作业管理器&#xff08;JobManager&#xff09; 任务管理器&#xff08;TaskManager&#xff09; 我们的代码&#xff0c;实际上是由客户端获取并做转换&#xff0c;之后提交给 JobManger 的。所以 …

【前端设计】card

欢迎来到前端设计专栏&#xff0c;本专栏收藏了一些好看且实用的前端作品&#xff0c;使用简单的html、css语法打造创意有趣的作品&#xff0c;为网站加入更多高级创意的元素。 html <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

一文让你了解UI自动化测试

测试都起什么作用 - 是项目的保险&#xff0c;但不是项目的救命草&#xff1b;测试无实际产出&#xff0c;但作用远大于实际产出&#xff1b;测试是从项目维度保证质量&#xff0c;而不是测试阶段。 UI自动化&#xff08;下面简称自动化&#xff09; - 基于UI进行自动功能测试…

C++ STL之stack的使用及模拟实现

文章目录 1. 介绍2. stack的使用3. 栈的模拟实现 1. 介绍 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作…

(M)unity2D敌人的创建、人物属性设置,遇敌掉血

敌人的创建 1.敌人添加与组件设置 1&#xff09;添加敌人后&#xff0c;刚体添加&#xff0c;碰撞体添加&#xff08;一个碰撞体使猪在地上走&#xff0c;不接触人&#xff0c;另一个碰撞体组件使人和猪碰在一起产生伤害&#xff09; ①刚体 ②碰撞体一 设置的只在脚下&a…

springboot整合MongoDB实战

目录 环境准备 引入依赖 配置yml 注入mongoTemplate 集合操作 文档操作 创建实体 添加文档 查询文档 更新文档 删除文档 环境准备 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-da…

优先级队列(堆)详解

优先级队列&#xff08;堆&#xff09;详解 目录 堆的概念堆的存储方式堆的基本操作优先级队列模拟实现PriorityQueue接口介绍堆排序Top-k问题 1、堆的概念 如果有一个关键码的集合K {k0&#xff0c;k1&#xff0c; k2&#xff0c;…&#xff0c;kn-1}&#xff0c;把它的所…

SAP 五个报废率设置简介(上)

通常在生产制造过程中都会面临报废率的问题,生产工艺路线的问题,原材料质量的问题,总会有一些产品在生产过程中被做成报废品,通常报废率的设置有时候会遵循行业的标准设置,亦或者根据工厂生产中统计的历史数据分析后根据不同的产品设置不同的报废率,从而在执行物料的采购…

VM下Unbunt虚拟机上网设置

系列文章目录 VM下虚拟机上网设置 VM下虚拟机上网设置 右击VM软件中你需要设置的虚拟机&#xff0c;选择设置 宿主机如果你用的是笔记本外加WIFI连接选择NAT网络模式 进入虚拟机看能否上网 不行的话&#xff0c;进入虚拟机点击&#xff0c;选择最后一栏&#xff0c;编辑连接 点…

华为认证的HCIP考实验考试么?

HCIP在考试的时候不考实验&#xff0c;只考笔试&#xff0c;只是不同方向的HCIP认证考试的考试科目不同&#xff0c;有的考一科&#xff0c;有的考二科&#xff0c;有的考三科&#xff0c;具体看方向来定。HCIA和HCIP只考笔试。HCIE考笔试和实验。 虽然HCIP不考实操&#xff0…

《WebKit 技术内幕》学习之七(1): 渲染基础

《WebKit 技术内幕》之七&#xff08;1&#xff09;&#xff1a; 渲染基础 WebKit的布局计算使用 RenderObject 树并保存计算结果到 RenderObject 树。 RenderObject 树同其他树&#xff08;如 RenderLayer 树等&#xff09;&#xff0c;构成了 WebKit 渲染的为要基础设施。 1…

【数据结构】链表(单链表与双链表实现+原理+源码)

博主介绍&#xff1a;✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦&#xff01; &#x1f345;附上相关C语言版源码讲解&#x1f345; &#x1f44…

python04-变量命名规则

python需要使用标识符来给变量命名。 标识符&#xff0c;我来解释下&#xff0c;就是给程序中变量、类、方法命名的符号&#xff0c;简单理解就是起一个名字&#xff0c;这个名字必须是合法的名字&#xff0c; 对于Python来说&#xff0c;标识符必须是以字母、下划线(_)开头&…

鸿蒙自定义刷新组件使用

前言 DevEco Studio版本&#xff1a;4.0.0.600 1、RefreshLibrary_HarmonyOS.har&#xff0c;用于HarmonyOS "minAPIVersion": 9, "targetAPIVersion": 9, "apiReleaseType": "Release", "compileSdkVersion": "3.…

用户画像系列——在线服务调优实践

前面文章讲到画像的应用的几个方面&#xff0c;其中画像的在线服务应用主要是在推荐场景、策略引擎场景&#xff0c;这两部分场景都是面向线上的c端服务。 推荐场景&#xff1a;根据不同的用户推荐不同的内容&#xff0c;做到个性化推荐&#xff0c;需要读取画像的一些偏好数据…

【方法】如何把Excel“只读方式”变成可直接编辑?

Excel在“只读方式”下&#xff0c;编辑后是无法直接保存原文件的&#xff0c;那如何可以直接编辑原文件呢&#xff1f;下面来一起看看看吧。 如果Excel设置的是无密码的“只读方式”&#xff0c;那在打开Excel后&#xff0c;会出现对话框&#xff0c;提示“是否以只读方式打开…

什么是甘特图?谁还不知道?做管理的来看看!

在现代商业社会&#xff0c;项目管理已成为不可或缺的技能。而甘特图作为一种强大的项目管理工具&#xff0c;正逐渐受到越来越多人的青睐。那么&#xff0c;什么是甘特图&#xff1f;又有什么工具可以绘制甘特图呢&#xff1f;本文将为你一一解答。 一、甘特图的定义 甘特图…