├── .gitignore ├── ExportNav.uplugin ├── LICENSE ├── README.md ├── Resources ├── ButtonIcon_40x.png └── Icon128.png └── Source ├── ExportNavEditor ├── ExportNavEditor.Build.cs ├── Private │ ├── ExportNavCommands.cpp │ ├── ExportNavEditor.cpp │ ├── ExportNavStyle.cpp │ ├── RecastDetourTestingActor.cpp │ └── RecastNavBin.cpp └── Public │ ├── ExportNavCommands.h │ ├── ExportNavEditor.h │ ├── ExportNavStyle.h │ ├── RecastDetourTestingActor.h │ └── RecastNavBin.h └── ExportNavRuntime ├── ExportNavRuntime.Build.cs ├── Private ├── ExportNavRuntime.cpp ├── ExternRecastNavMeshGenetator.cpp ├── FlibExportNavData.cpp ├── UE4RecastHelper.cpp └── dtNavMeshWrapper.cpp └── Public ├── ExportNavRuntime.h ├── ExternRecastNavMeshGenetator.h ├── FlibExportNavData.h ├── HACK_PRIVATE_MEMBER_UTILS.hpp ├── UE4RecastHelper.h └── dtNavMeshWrapper.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /ExportNav.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "ExportNav", 6 | "Description": "", 7 | "Category": "RecastNavigation", 8 | "CreatedBy": "imzlp", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "Installed": false, 16 | "Modules": [ 17 | { 18 | "Name": "ExportNavEditor", 19 | "Type": "Editor", 20 | "LoadingPhase": "Default" 21 | }, 22 | { 23 | "Name": "ExportNavRuntime", 24 | "Type": "Runtime", 25 | "LoadingPhase": "PreDefault" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 3125 luyuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ue5-export-nav-data 2 | reference : https://imzlp.com/posts/20203/ 3 | 4 | ## What is this? 5 | 6 | This is a Unreal Engine 4 Plugin that export ue4 navigation mesh data(recast mesh) to outside. 7 | 8 | With this plugin, you can export recast Navigation data directly from the UE without going through RecastDemo.Of course I also kept the export recast navmesh. 9 | 10 | ### How do use? 11 | 12 | 1. add the plugin to the project and enable it. 13 | 2. Launch the Project in Editor, Click the `ExportNav` button. It will create to a `.bin` and `.obj` file. 14 | 15 | - The `.bin` file is the export recast navigation data that is directly from the UE.You can use it in `detour`. 16 | - The `.obj` file is a navigation mesh exported from the UE(unit is centimeter). 17 | 18 | ### Use .obj 19 | 20 | ![](https://img.imzlp.com/imgs/zlp/blog/notes/ue/index/UE4/Plugins/export-nav-data/ue4-export-nav-data-usage-0.png) 21 | 22 | ![](https://img.imzlp.com/imgs/zlp/blog/notes/ue/index/UE4/Plugins/export-nav-data/ue4-export-nav-data-usage-1.png) 23 | 24 | 3. Open The Plugin `Source/ExportNav/ThirdParty/RecastDemoBin` 25 | 4. copy `.obj` to `RecastDemoBin/Meshes` 26 | 5. run `RecastDemo.exe` 27 | 28 | ![](https://img.imzlp.com/imgs/zlp/blog/notes/ue/index/UE4/Plugins/export-nav-data/Recast-Demo-bin.png) 29 | 30 | -------------------------------------------------------------------------------- /Resources/ButtonIcon_40x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luyuancpp/ue5-export-nav-data/9ce14e0718f39e3bde921b7317f08ab6f2b64ef3/Resources/ButtonIcon_40x.png -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luyuancpp/ue5-export-nav-data/9ce14e0718f39e3bde921b7317f08ab6f2b64ef3/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/ExportNavEditor/ExportNavEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ExportNavEditor : ModuleRules 6 | { 7 | public ExportNavEditor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | "ExportNavRuntime" 30 | // ... add other public dependencies that you statically link with here ... 31 | } 32 | ); 33 | 34 | 35 | PrivateDependencyModuleNames.AddRange( 36 | new string[] 37 | { 38 | "Core", 39 | "Projects", 40 | "DesktopPlatform", 41 | "InputCore", 42 | "UnrealEd", 43 | "LevelEditor", 44 | "CoreUObject", 45 | "Engine", 46 | "Slate", 47 | "SlateCore", 48 | "NavigationSystem", 49 | "NavMesh", 50 | // ... add private dependencies that you statically link with here ... 51 | } 52 | ); 53 | 54 | 55 | DynamicallyLoadedModuleNames.AddRange( 56 | new string[] 57 | { 58 | // ... add any modules that your module loads dynamically here ... 59 | } 60 | ); 61 | 62 | OptimizeCode = CodeOptimization.InShippingBuildsOnly; 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Private/ExportNavCommands.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "ExportNavCommands.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FExportNavModule" 6 | 7 | void FExportNavCommands::RegisterCommands() 8 | { 9 | UI_COMMAND(PluginAction, "Export Nav", "Export Navigation Data", EUserInterfaceActionType::Button, FInputGesture()); 10 | } 11 | 12 | #undef LOCTEXT_NAMESPACE 13 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Private/ExportNavEditor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "ExportNavEditor.h" 4 | #include "ExportNavStyle.h" 5 | #include "ExportNavCommands.h" 6 | 7 | #include "FlibExportNavData.h" 8 | 9 | #include "Misc/MessageDialog.h" 10 | #include "Framework/MultiBox/MultiBoxBuilder.h" 11 | #include "DesktopPlatformModule.h" 12 | #include "LevelEditor.h" 13 | #include "HAL/FileManager.h" 14 | #include "Interfaces/IPluginManager.h" 15 | #include "Framework/Notifications/NotificationManager.h" 16 | #include "Widgets/Notifications/SNotificationList.h" 17 | 18 | static const FName ExportNavTabName("ExportNav"); 19 | 20 | #define LOCTEXT_NAMESPACE "FExportNavEditorModule" 21 | 22 | void FExportNavEditorModule::StartupModule() 23 | { 24 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 25 | 26 | FExportNavStyle::Initialize(); 27 | FExportNavStyle::ReloadTextures(); 28 | 29 | FExportNavCommands::Register(); 30 | 31 | PluginCommands = MakeShareable(new FUICommandList); 32 | 33 | PluginCommands->MapAction( 34 | FExportNavCommands::Get().PluginAction, 35 | FExecuteAction::CreateRaw(this, &FExportNavEditorModule::PluginButtonClicked), 36 | FCanExecuteAction()); 37 | 38 | FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); 39 | 40 | { 41 | TSharedPtr MenuExtender = MakeShareable(new FExtender()); 42 | MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FExportNavEditorModule::AddMenuExtension)); 43 | 44 | LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); 45 | } 46 | 47 | { 48 | TSharedPtr ToolbarExtender = MakeShareable(new FExtender); 49 | ToolbarExtender->AddToolBarExtension("Settings", EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FExportNavEditorModule::AddToolbarExtension)); 50 | 51 | LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); 52 | } 53 | } 54 | 55 | void FExportNavEditorModule::ShutdownModule() 56 | { 57 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 58 | // we call this function before unloading the module. 59 | FExportNavStyle::Shutdown(); 60 | 61 | FExportNavCommands::Unregister(); 62 | } 63 | 64 | void FExportNavEditorModule::PluginButtonClicked() 65 | { 66 | UWorld* World = GEditor->GetEditorWorldContext().World(); 67 | 68 | if (!UFlibExportNavData::GetdtNavMeshInsByWorld(World)) 69 | { 70 | NotFountAnyValidNavDataMsg(); 71 | return; 72 | } 73 | 74 | FString MapName = World->GetMapName(); 75 | 76 | IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); 77 | FString PluginPath = FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin(TEXT("ExportNav"))->GetBaseDir()); 78 | 79 | FString OutPath; 80 | if (DesktopPlatform) 81 | { 82 | const bool bOpened = DesktopPlatform->OpenDirectoryDialog( 83 | nullptr, 84 | LOCTEXT("SaveNav", "Save Recast Navigation NavMesh & NavData").ToString(), 85 | PluginPath, 86 | OutPath 87 | ); 88 | if (!OutPath.IsEmpty() && FPaths::DirectoryExists(OutPath)) 89 | { 90 | FString CurrentTime = FDateTime::Now().ToString(); 91 | 92 | FText NavMeshMsg = LOCTEXT("SaveNavMeshMesh", "Successd to Export the NavMesh."); 93 | 94 | #if EXPORT_NAV_MESH_AS_CM 95 | FString NavMeshFileCM = FPaths::Combine(OutPath, MapName + TEXT("-NavMesh-CM-") + CurrentTime+TEXT(".obj")); 96 | DoExportNavMesh(NavMeshFileCM,EExportMode::Centimeter); 97 | CreateSaveFileNotify(NavMeshMsg, NavMeshFileCM); 98 | #endif 99 | #if EXPORT_NAV_MESH_AS_M 100 | FString NavMeshFileM = FPaths::Combine(OutPath, MapName + TEXT("-NavMesh-M-") + CurrentTime + TEXT(".obj")); 101 | DoExportNavMesh(NavMeshFileM, EExportMode::Metre); 102 | CreateSaveFileNotify(NavMeshMsg, NavMeshFileM); 103 | #endif 104 | FString NavDataFile = FPaths::Combine(OutPath, MapName + TEXT("-NavData-") + CurrentTime+TEXT(".bin")); 105 | DoExportNavData(NavDataFile); 106 | 107 | FText NavDataMsg = LOCTEXT("SaveNavMeshData", "Successd to Export the RecastNavigation data."); 108 | CreateSaveFileNotify(NavDataMsg, NavDataFile); 109 | } 110 | } 111 | } 112 | 113 | void FExportNavEditorModule::DoExportNavMesh(const FString& SaveToFile,EExportMode InExportMode) 114 | { 115 | UFlibExportNavData::ExportRecastNavMesh(SaveToFile,InExportMode); 116 | } 117 | 118 | void FExportNavEditorModule::NotFountAnyValidNavDataMsg() 119 | { 120 | UWorld* World = GEditor->GetEditorWorldContext().World(); 121 | FText DialogText = FText::Format( 122 | LOCTEXT("NotFoundValidNavDialogText", "Not found any valid Navigation data in {0} Map!"), 123 | FText::FromString(World->GetMapName()) 124 | ); 125 | FMessageDialog::Open(EAppMsgType::Ok, DialogText); 126 | } 127 | 128 | void FExportNavEditorModule::CreateSaveFileNotify(const FText& InMsg, const FString& InSavedFile) 129 | { 130 | auto Message = InMsg; 131 | FNotificationInfo Info(Message); 132 | Info.bFireAndForget = true; 133 | Info.ExpireDuration = 5.0f; 134 | Info.bUseSuccessFailIcons = false; 135 | Info.bUseLargeFont = false; 136 | 137 | const FString HyperLinkText = InSavedFile; 138 | Info.Hyperlink = FSimpleDelegate::CreateStatic( 139 | [](FString SourceFilePath) 140 | { 141 | FPlatformProcess::ExploreFolder(*SourceFilePath); 142 | }, 143 | HyperLinkText 144 | ); 145 | Info.HyperlinkText = FText::FromString(HyperLinkText); 146 | 147 | FSlateNotificationManager::Get().AddNotification(Info)->SetCompletionState(SNotificationItem::CS_Success); 148 | } 149 | 150 | void FExportNavEditorModule::DoExportNavData(const FString& SaveToFile) 151 | { 152 | UFlibExportNavData::ExportRecastNavData(SaveToFile); 153 | 154 | } 155 | 156 | void FExportNavEditorModule::AddMenuExtension(FMenuBuilder& Builder) 157 | { 158 | Builder.AddMenuEntry(FExportNavCommands::Get().PluginAction); 159 | } 160 | 161 | 162 | 163 | void FExportNavEditorModule::AddToolbarExtension(FToolBarBuilder& Builder) 164 | { 165 | Builder.AddToolBarButton(FExportNavCommands::Get().PluginAction); 166 | } 167 | 168 | #undef LOCTEXT_NAMESPACE 169 | 170 | IMPLEMENT_MODULE(FExportNavEditorModule, ExportNavEditor) -------------------------------------------------------------------------------- /Source/ExportNavEditor/Private/ExportNavStyle.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "ExportNavStyle.h" 4 | #include "ExportNavEditor.h" 5 | #include "Framework/Application/SlateApplication.h" 6 | #include "Styling/SlateStyleRegistry.h" 7 | #include "Slate/SlateGameResources.h" 8 | #include "Interfaces/IPluginManager.h" 9 | 10 | TSharedPtr< FSlateStyleSet > FExportNavStyle::StyleInstance = NULL; 11 | 12 | void FExportNavStyle::Initialize() 13 | { 14 | if (!StyleInstance.IsValid()) 15 | { 16 | StyleInstance = Create(); 17 | FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); 18 | } 19 | } 20 | 21 | void FExportNavStyle::Shutdown() 22 | { 23 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); 24 | ensure(StyleInstance.IsUnique()); 25 | StyleInstance.Reset(); 26 | } 27 | 28 | FName FExportNavStyle::GetStyleSetName() 29 | { 30 | static FName StyleSetName(TEXT("ExportNavStyle")); 31 | return StyleSetName; 32 | } 33 | 34 | #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 35 | #define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 36 | #define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 37 | #define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) 38 | #define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) 39 | 40 | const FVector2D Icon16x16(16.0f, 16.0f); 41 | const FVector2D Icon20x20(20.0f, 20.0f); 42 | const FVector2D Icon40x40(40.0f, 40.0f); 43 | 44 | TSharedRef< FSlateStyleSet > FExportNavStyle::Create() 45 | { 46 | TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("ExportNavStyle")); 47 | Style->SetContentRoot(IPluginManager::Get().FindPlugin("ExportNav")->GetBaseDir() / TEXT("Resources")); 48 | 49 | Style->Set("ExportNav.PluginAction", new IMAGE_BRUSH(TEXT("ButtonIcon_40x"), Icon40x40)); 50 | 51 | return Style; 52 | } 53 | 54 | #undef IMAGE_BRUSH 55 | #undef BOX_BRUSH 56 | #undef BORDER_BRUSH 57 | #undef TTF_FONT 58 | #undef OTF_FONT 59 | 60 | void FExportNavStyle::ReloadTextures() 61 | { 62 | if (FSlateApplication::IsInitialized()) 63 | { 64 | FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); 65 | } 66 | } 67 | 68 | const ISlateStyle& FExportNavStyle::Get() 69 | { 70 | return *StyleInstance; 71 | } 72 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Private/RecastDetourTestingActor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "RecastDetourTestingActor.h" 5 | #include "RecastNavBin.h" 6 | #include "FlibExportNavData.h" 7 | 8 | // engine 9 | #include "Kismet/GameplayStatics.h" 10 | #include "Kismet/KismetSystemLibrary.h" 11 | #include "Components/CapsuleComponent.h" 12 | 13 | // Sets default values 14 | ARecastDetourTestingActor::ARecastDetourTestingActor(const FObjectInitializer& Initializer):Super(Initializer) 15 | { 16 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 17 | PrimaryActorTick.bCanEverTick = true; 18 | 19 | CapsuleComponent = CreateDefaultSubobject(TEXT("CollisionCylinder")); 20 | CapsuleComponent->InitCapsuleSize(AgentRadius, AgentRadius / 2); 21 | CapsuleComponent->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName); 22 | CapsuleComponent->CanCharacterStepUpOn = ECB_No; 23 | CapsuleComponent->SetShouldUpdatePhysicsVolume(true); 24 | CapsuleComponent->SetCanEverAffectNavigation(false); 25 | RootComponent = CapsuleComponent; 26 | 27 | } 28 | 29 | // Called when the game starts or when spawned 30 | void ARecastDetourTestingActor::BeginPlay() 31 | { 32 | Super::BeginPlay(); 33 | 34 | } 35 | 36 | // Called every frame 37 | void ARecastDetourTestingActor::Tick(float DeltaTime) 38 | { 39 | Super::Tick(DeltaTime); 40 | if(Paths.Num()) 41 | { 42 | FVector LastPoint = Paths[0]; 43 | for(int32 index =1;index{},EDrawDebugTrace::ForOneFrame,result,true,FLinearColor::Red,FLinearColor::Green,1.0); 47 | LastPoint = Paths[index]; 48 | } 49 | } 50 | 51 | } 52 | 53 | bool ARecastDetourTestingActor::ShouldTickIfViewportsOnly() const 54 | { 55 | return true; 56 | } 57 | 58 | void ARecastDetourTestingActor::UpdatePathing() 59 | { 60 | if(!HasAnyFlags(RF_ClassDefaultObject)) 61 | { 62 | TArray AllRecastNavActors; 63 | UGameplayStatics::GetAllActorsOfClass(this,ARecastNavBin::StaticClass(),AllRecastNavActors); 64 | if(AllRecastNavActors.Num() == 0) 65 | { 66 | RecastNavBin = Cast(GetWorld()->SpawnActor(ARecastNavBin::StaticClass())); 67 | } 68 | else 69 | { 70 | RecastNavBin = Cast(AllRecastNavActors[0]); 71 | } 72 | } 73 | 74 | if(UKismetSystemLibrary::IsValid(RecastNavBin) && UKismetSystemLibrary::IsValid(OtherActor)) 75 | { 76 | UdtNavMeshWrapper* NavMeshWrapper = RecastNavBin->GetDtNavMesh(); 77 | if(NavMeshWrapper->GetNavData()) 78 | { 79 | Paths.Empty(); 80 | UFlibExportNavData::FindDetourPathByGameAxis(NavMeshWrapper->GetNavData(),GetActorLocation(),OtherActor->GetActorLocation(),RecastNavBin->GetFindPathExternSize(), Paths); 81 | } 82 | } 83 | } 84 | 85 | void ARecastDetourTestingActor::PostEditMove(bool bFinished) 86 | { 87 | UpdatePathing(); 88 | } 89 | 90 | void ARecastDetourTestingActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) 91 | { 92 | static FName OtherTestingActorName = GET_MEMBER_NAME_CHECKED(ARecastDetourTestingActor,OtherActor); 93 | if(PropertyChangedEvent.GetPropertyName() == OtherTestingActorName) 94 | { 95 | if(OtherActor) 96 | { 97 | OtherActor->OtherActor = this; 98 | UpdatePathing(); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /Source/ExportNavEditor/Private/RecastNavBin.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "RecastNavBin.h" 5 | #include "Kismet/KismetSystemLibrary.h" 6 | 7 | // Sets default values 8 | ARecastNavBin::ARecastNavBin(const FObjectInitializer& Initializer):Super(Initializer) 9 | { 10 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 11 | PrimaryActorTick.bCanEverTick = true; 12 | dtNavMesh = Initializer.CreateDefaultSubobject(this,FName{TEXT("dtNavMesh")}); 13 | } 14 | 15 | // Called when the game starts or when spawned 16 | void ARecastNavBin::BeginPlay() 17 | { 18 | Super::BeginPlay(); 19 | 20 | } 21 | 22 | // Called every frame 23 | void ARecastNavBin::Tick(float DeltaTime) 24 | { 25 | Super::Tick(DeltaTime); 26 | 27 | } 28 | 29 | void ARecastNavBin::ImportNavBin() 30 | { 31 | if(UKismetSystemLibrary::IsValid(dtNavMesh) && FPaths::FileExists(dtNavMeshBin.FilePath)) 32 | { 33 | dtNavMesh->LoadNavData(dtNavMeshBin.FilePath); 34 | } 35 | } 36 | 37 | UdtNavMeshWrapper* ARecastNavBin::GetDtNavMesh() const 38 | { 39 | return dtNavMesh; 40 | } 41 | 42 | FVector ARecastNavBin::GetFindPathExternSize() const 43 | { 44 | return ExternSize; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Public/ExportNavCommands.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Framework/Commands/Commands.h" 7 | #include "ExportNavStyle.h" 8 | 9 | class FExportNavCommands : public TCommands 10 | { 11 | public: 12 | 13 | FExportNavCommands() 14 | : TCommands(TEXT("ExportNav"), NSLOCTEXT("Contexts", "ExportNav", "ExportNav Plugin"), NAME_None, FExportNavStyle::GetStyleSetName()) 15 | { 16 | } 17 | 18 | // TCommands<> interface 19 | virtual void RegisterCommands() override; 20 | 21 | public: 22 | TSharedPtr< FUICommandInfo > PluginAction; 23 | }; 24 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Public/ExportNavEditor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "ExternRecastNavMeshGenetator.h" 5 | 6 | #include "CoreMinimal.h" 7 | #include "Modules/ModuleManager.h" 8 | 9 | class FToolBarBuilder; 10 | class FMenuBuilder; 11 | 12 | class FExportNavEditorModule : public IModuleInterface 13 | { 14 | public: 15 | 16 | /** IModuleInterface implementation */ 17 | virtual void StartupModule() override; 18 | virtual void ShutdownModule() override; 19 | 20 | /** This function will be bound to Command. */ 21 | void PluginButtonClicked(); 22 | 23 | private: 24 | 25 | void AddToolbarExtension(FToolBarBuilder& Builder); 26 | void AddMenuExtension(FMenuBuilder& Builder); 27 | 28 | private: 29 | // export bin file 30 | void DoExportNavData(const FString& SaveToFile); 31 | // export recast obj file,Editor only 32 | void DoExportNavMesh(const FString& SaveToFile,EExportMode InExportMode); 33 | 34 | // if not fount any valid navigation data,show the message 35 | void NotFountAnyValidNavDataMsg(); 36 | 37 | static void CreateSaveFileNotify(const FText& InMsg, const FString& InSavedFile); 38 | 39 | private: 40 | TSharedPtr PluginCommands; 41 | }; 42 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Public/ExportNavStyle.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Styling/SlateStyle.h" 7 | 8 | class FExportNavStyle 9 | { 10 | public: 11 | 12 | static void Initialize(); 13 | 14 | static void Shutdown(); 15 | 16 | /** reloads textures used by slate renderer */ 17 | static void ReloadTextures(); 18 | 19 | /** @return The Slate style set for the Shooter game */ 20 | static const ISlateStyle& Get(); 21 | 22 | static FName GetStyleSetName(); 23 | 24 | private: 25 | 26 | static TSharedRef< class FSlateStyleSet > Create(); 27 | 28 | private: 29 | 30 | static TSharedPtr< class FSlateStyleSet > StyleInstance; 31 | }; -------------------------------------------------------------------------------- /Source/ExportNavEditor/Public/RecastDetourTestingActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | #include "RecastNavBin.h" 8 | #include "GameFramework/Actor.h" 9 | #include "RecastDetourTestingActor.generated.h" 10 | 11 | UCLASS() 12 | class EXPORTNAVEDITOR_API ARecastDetourTestingActor : public AActor 13 | { 14 | GENERATED_UCLASS_BODY() 15 | 16 | public: 17 | // Sets default values for this actor's properties 18 | // ARecastDetourTestingActor(); 19 | 20 | protected: 21 | // Called when the game starts or when spawned 22 | virtual void BeginPlay() override; 23 | 24 | public: 25 | // Called every frame 26 | virtual void Tick(float DeltaTime) override; 27 | 28 | virtual bool ShouldTickIfViewportsOnly()const override; 29 | 30 | UFUNCTION(BlueprintCallable) 31 | void UpdatePathing(); 32 | 33 | #if WITH_EDITOR 34 | virtual void PostEditMove(bool bFinished)override; 35 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; 36 | #endif 37 | 38 | UPROPERTY(EditAnywhere,Category = "FindPathing") 39 | ARecastDetourTestingActor* OtherActor; 40 | UPROPERTY(EditAnywhere,Category = "FindPathing") 41 | float AgentRadius = 60; 42 | 43 | UPROPERTY() 44 | ARecastNavBin* RecastNavBin; 45 | UPROPERTY() 46 | class UCapsuleComponent* CapsuleComponent; 47 | TArray Paths; 48 | }; 49 | -------------------------------------------------------------------------------- /Source/ExportNavEditor/Public/RecastNavBin.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "Engine/EngineTypes.h" 6 | #include "CoreMinimal.h" 7 | 8 | #include "dtNavMeshWrapper.h" 9 | #include "GameFramework/Actor.h" 10 | #include "RecastNavBin.generated.h" 11 | 12 | UCLASS() 13 | class EXPORTNAVEDITOR_API ARecastNavBin : public AActor 14 | { 15 | GENERATED_UCLASS_BODY() 16 | 17 | public: 18 | // Sets default values for this actor's properties 19 | // ARecastNavBin(); 20 | 21 | protected: 22 | // Called when the game starts or when spawned 23 | virtual void BeginPlay() override; 24 | 25 | public: 26 | // Called every frame 27 | virtual void Tick(float DeltaTime) override; 28 | 29 | UPROPERTY(EditAnywhere,Category="ImportNavData") 30 | FFilePath dtNavMeshBin; 31 | UPROPERTY(EditAnywhere,Category="ImportNavData") 32 | FVector ExternSize{10.f,10.f,10.f}; 33 | UFUNCTION(BlueprintCallable,CallInEditor,Category="ImpoerNavData") 34 | void ImportNavBin(); 35 | 36 | UFUNCTION(BlueprintCallable) 37 | UdtNavMeshWrapper* GetDtNavMesh()const; 38 | UFUNCTION(BlueprintCallable) 39 | FVector GetFindPathExternSize()const; 40 | 41 | 42 | 43 | UdtNavMeshWrapper* dtNavMesh; 44 | }; 45 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/ExportNavRuntime.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.IO; 5 | 6 | public class ExportNavRuntime : ModuleRules 7 | { 8 | public ExportNavRuntime(ReadOnlyTargetRules Target) : base(Target) 9 | { 10 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 11 | 12 | PublicIncludePaths.AddRange( 13 | new string[] { 14 | Path.Combine(EngineDirectory,"Source/Runtime/Launch") 15 | // ... add public include paths required here ... 16 | } 17 | ); 18 | 19 | 20 | PrivateIncludePaths.AddRange( 21 | new string[] { 22 | // ... add other private include paths required here ... 23 | } 24 | ); 25 | 26 | 27 | PublicDependencyModuleNames.AddRange( 28 | new string[] 29 | { 30 | "Core", 31 | // ... add other public dependencies that you statically link with here ... 32 | } 33 | ); 34 | 35 | 36 | PrivateDependencyModuleNames.AddRange( 37 | new string[] 38 | { 39 | "CoreUObject", 40 | "Engine", 41 | "Slate", 42 | "SlateCore", 43 | "NavigationSystem", 44 | "NavMesh" 45 | // ... add private dependencies that you statically link with here ... 46 | } 47 | ); 48 | 49 | 50 | DynamicallyLoadedModuleNames.AddRange( 51 | new string[] 52 | { 53 | // ... add any modules that your module loads dynamically here ... 54 | } 55 | ); 56 | 57 | 58 | OptimizeCode = CodeOptimization.InShippingBuildsOnly; 59 | 60 | PublicDefinitions.AddRange( 61 | new string[] 62 | { 63 | "USE_DETOUR_BUILT_INTO_UE4", 64 | "EXPORT_NAV_MESH_AS_M", 65 | "EXPORT_NAV_MESH_AS_CM", 66 | } 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Private/ExportNavRuntime.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "ExportNavRuntime.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FExportNavRuntimeModule" 6 | 7 | void FExportNavRuntimeModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FExportNavRuntimeModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FExportNavRuntimeModule, ExportNavRuntime) -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Private/ExternRecastNavMeshGenetator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "ExternRecastNavMeshGenetator.h" 4 | #include "Navmesh/RecastHelpers.h" 5 | #include "GenericPlatform/GenericPlatform.h" 6 | #include "Detour/DetourNavMesh.h" 7 | #include "Kismet/KismetMathLibrary.h" 8 | #include "NavigationSystem.h" 9 | #include "HAL/FileManager.h" 10 | #include "HAL/FileManagerGeneric.h" 11 | #include "Resources/Version.h" 12 | 13 | FExternRecastGeometryCache::FExternRecastGeometryCache(const uint8* Memory) 14 | { 15 | Header = *((FHeader*)Memory); 16 | Verts = (dtReal*)(Memory + sizeof(FExternRecastGeometryCache)); 17 | Indices = (int32*)(Memory + sizeof(FExternRecastGeometryCache) + (sizeof(dtReal) * Header.NumVerts * 3)); 18 | } 19 | 20 | void FExternExportNavMeshGenerator::ExternExportNavigationData(const FString& FileName, EExportMode InExportMode) 21 | { 22 | const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent(GetWorld()); 23 | const FNavigationOctree* NavOctree = NavSys ? NavSys->GetNavOctree() : NULL; 24 | if (NavOctree == NULL) 25 | { 26 | UE_LOG(LogNavigation, Error, TEXT("Failed to export navigation data due to %s being NULL"), NavSys == NULL ? TEXT("NavigationSystem") : TEXT("NavOctree")); 27 | return; 28 | } 29 | 30 | const double StartExportTime = FPlatformTime::Seconds(); 31 | 32 | FString CurrentTimeStr = FDateTime::Now().ToString(); 33 | for (int32 Index = 0; Index < NavSys->NavDataSet.Num(); ++Index) 34 | { 35 | // feed data from octtree and mark for rebuild 36 | TNavStatArray CoordBuffer; 37 | TNavStatArray IndexBuffer; 38 | const ARecastNavMesh* NavData = Cast(NavSys->NavDataSet[Index]); 39 | if (NavData) 40 | { 41 | struct FAreaExportData 42 | { 43 | FConvexNavAreaData Convex; 44 | uint8 AreaId; 45 | }; 46 | TArray AreaExport; 47 | #if ENGINE_MAJOR_VERSION>=4 || ENGINE_MINOR_VERSION >=26 48 | NavOctree->FindElementsWithBoundsTest(TotalNavBounds, [&](const FNavigationOctreeElement& Element) 49 | #else 50 | for (FNavigationOctree::TConstElementBoxIterator It(*NavOctree, TotalNavBounds); 51 | It.HasPendingElements(); 52 | It.Advance()) 53 | #endif 54 | { 55 | #if ENGINE_MAJOR_VERSION<=4 && ENGINE_MINOR_VERSION < 26 56 | const FNavigationOctreeElement& Element = It.GetCurrentElement(); 57 | #endif 58 | const bool bExportGeometry = Element.Data->HasGeometry() && Element.ShouldUseGeometry(DestNavMesh->GetConfig()); 59 | 60 | if (bExportGeometry && Element.Data->CollisionData.Num()) 61 | { 62 | FExternRecastGeometryCache CachedGeometry(Element.Data->CollisionData.GetData()); 63 | IndexBuffer.Reserve(IndexBuffer.Num() + (CachedGeometry.Header.NumFaces * 3)); 64 | CoordBuffer.Reserve(CoordBuffer.Num() + (CachedGeometry.Header.NumVerts * 3)); 65 | for (int32 i = 0; i < CachedGeometry.Header.NumFaces * 3; i++) 66 | { 67 | IndexBuffer.Add(CachedGeometry.Indices[i] + CoordBuffer.Num() / 3); 68 | } 69 | 70 | switch (InExportMode) 71 | { 72 | case EExportMode::Metre: 73 | { 74 | //// Export unit metre 75 | for (int32 i = 0; i < CachedGeometry.Header.NumVerts * 3; i += 3) 76 | { 77 | FVector Corrd = FVector{ 78 | CachedGeometry.Verts[i] / 100.f, 79 | CachedGeometry.Verts[i + 2] / 100.f, 80 | CachedGeometry.Verts[i + 1] / 100.f, 81 | }; 82 | // CoordBuffer.Add(CachedGeometry.Verts[i]); 83 | CoordBuffer.Add(Corrd.X); 84 | CoordBuffer.Add(Corrd.Z); 85 | CoordBuffer.Add(Corrd.Y); 86 | } 87 | break; 88 | }; 89 | case EExportMode::Centimeter: 90 | { 91 | // Export unit centimeters 92 | for (int32 i = 0; i < CachedGeometry.Header.NumVerts * 3; i++) 93 | { 94 | CoordBuffer.Add(CachedGeometry.Verts[i]); 95 | } 96 | break; 97 | }; 98 | } 99 | 100 | } 101 | else 102 | 103 | 104 | { 105 | const TArray& AreaMods = Element.Data->Modifiers.GetAreas(); 106 | for (int32 i = 0; i < AreaMods.Num(); i++) 107 | { 108 | FAreaExportData ExportInfo; 109 | ExportInfo.AreaId = NavData->GetAreaID(AreaMods[i].GetAreaClass()); 110 | 111 | if (AreaMods[i].GetShapeType() == ENavigationShapeType::Convex) 112 | { 113 | AreaMods[i].GetConvex(ExportInfo.Convex); 114 | 115 | TArray ConvexVerts; 116 | GrowConvexHull(NavData->AgentRadius, ExportInfo.Convex.Points, ConvexVerts); 117 | ExportInfo.Convex.MinZ -= NavData->CellHeight; 118 | ExportInfo.Convex.MaxZ += NavData->CellHeight; 119 | ExportInfo.Convex.Points = ConvexVerts; 120 | 121 | AreaExport.Add(ExportInfo); 122 | } 123 | } 124 | } 125 | 126 | 127 | UWorld* NavigationWorld = GetWorld(); 128 | for (int32 LevelIndex = 0; LevelIndex < NavigationWorld->GetNumLevels(); ++LevelIndex) 129 | { 130 | const ULevel* const Level = NavigationWorld->GetLevel(LevelIndex); 131 | if (Level == NULL) 132 | { 133 | continue; 134 | } 135 | 136 | const TArray* LevelGeom = Level->GetStaticNavigableGeometry(); 137 | if (LevelGeom != NULL && LevelGeom->Num() > 0) 138 | { 139 | TNavStatArray Verts; 140 | TNavStatArray Faces; 141 | // For every ULevel in World take its pre-generated static geometry vertex soup 142 | TransformVertexSoupToRecast(*LevelGeom, Verts, Faces); 143 | 144 | IndexBuffer.Reserve(IndexBuffer.Num() + Faces.Num()); 145 | CoordBuffer.Reserve(CoordBuffer.Num() + Verts.Num() * 3); 146 | for (int32 i = 0; i < Faces.Num(); i++) 147 | { 148 | IndexBuffer.Add(Faces[i] + CoordBuffer.Num() / 3); 149 | } 150 | for (int32 i = 0; i < Verts.Num(); i++) 151 | { 152 | CoordBuffer.Add(Verts[i].X); 153 | CoordBuffer.Add(Verts[i].Y); 154 | CoordBuffer.Add(Verts[i].Z); 155 | } 156 | } 157 | } 158 | 159 | 160 | FString AreaExportStr; 161 | for (int32 i = 0; i < AreaExport.Num(); i++) 162 | { 163 | const FAreaExportData& ExportInfo = AreaExport[i]; 164 | AreaExportStr += FString::Printf(TEXT("\nAE %d %d %f %f\n"), 165 | ExportInfo.AreaId, ExportInfo.Convex.Points.Num(), ExportInfo.Convex.MinZ, ExportInfo.Convex.MaxZ); 166 | 167 | for (int32 iv = 0; iv < ExportInfo.Convex.Points.Num(); iv++) 168 | { 169 | FVector Pt = Unreal2RecastPoint(ExportInfo.Convex.Points[iv]); 170 | AreaExportStr += FString::Printf(TEXT("Av %f %f %f\n"), Pt.X, Pt.Y, Pt.Z); 171 | } 172 | } 173 | 174 | FString AdditionalData; 175 | 176 | if (AreaExport.Num()) 177 | { 178 | AdditionalData += "# Area export\n"; 179 | AdditionalData += AreaExportStr; 180 | AdditionalData += "\n"; 181 | } 182 | 183 | AdditionalData += "# RecastDemo specific data\n"; 184 | #if 0 185 | // use this bounds to have accurate navigation data bounds 186 | const FVector Center = Unreal2RecastPoint(NavData->GetBounds().GetCenter()); 187 | FVector Extent = FVector(NavData->GetBounds().GetExtent()); 188 | Extent = FVector(Extent.X, Extent.Z, Extent.Y); 189 | #else 190 | // this bounds match navigation bounds from level 191 | FBox RCNavBounds = Unreal2RecastBox(TotalNavBounds); 192 | const FVector Center = RCNavBounds.GetCenter(); 193 | const FVector Extent = RCNavBounds.GetExtent(); 194 | #endif 195 | const FBox Box = FBox::BuildAABB(Center, Extent); 196 | AdditionalData += FString::Printf( 197 | TEXT("rd_bbox %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f\n"), 198 | Box.Min.X, Box.Min.Y, Box.Min.Z, 199 | Box.Max.X, Box.Max.Y, Box.Max.Z 200 | ); 201 | 202 | const FRecastNavMeshGenerator* CurrentGen = static_cast(NavData->GetGenerator()); 203 | check(CurrentGen); 204 | 205 | AdditionalData += FString::Printf(TEXT("# Cell Size\n")); 206 | AdditionalData += FString::Printf(TEXT("rd_cs %5.5f\n"), CurrentGen->GetConfig().cs); 207 | AdditionalData += FString::Printf(TEXT("# Cell Height\n")); 208 | AdditionalData += FString::Printf(TEXT("rd_ch %5.5f\n"), CurrentGen->GetConfig().ch); 209 | AdditionalData += FString::Printf(TEXT("# Agent max slope\n")); 210 | AdditionalData += FString::Printf(TEXT("rd_ams %5.5f\n"), CurrentGen->GetConfig().walkableSlopeAngle); 211 | AdditionalData += FString::Printf(TEXT("# warkable height\n")); 212 | AdditionalData += FString::Printf(TEXT("rd_wh %d\n"), CurrentGen->GetConfig().walkableHeight); 213 | AdditionalData += FString::Printf(TEXT("# warkable Climb\n")); 214 | AdditionalData += FString::Printf(TEXT("rd_wc %d\n"), CurrentGen->GetConfig().walkableClimb); 215 | AdditionalData += FString::Printf(TEXT("# warkable radius\n")); 216 | AdditionalData += FString::Printf(TEXT("rd_wr %d\n"), CurrentGen->GetConfig().walkableRadius); 217 | 218 | AdditionalData += FString::Printf(TEXT("# AgentHeight\n")); 219 | AdditionalData += FString::Printf(TEXT("rd_agh %5.5f\n"), CurrentGen->GetConfig().AgentHeight); 220 | AdditionalData += FString::Printf(TEXT("# AgentRadius\n")); 221 | AdditionalData += FString::Printf(TEXT("rd_agr %5.5f\n"), CurrentGen->GetConfig().AgentRadius); 222 | AdditionalData += FString::Printf(TEXT("# Agent max climb\n")); 223 | AdditionalData += FString::Printf(TEXT("rd_amc %5.5f\n"), (int)CurrentGen->GetConfig().AgentMaxClimb); 224 | 225 | AdditionalData += FString::Printf(TEXT("# border size\n")); 226 | AdditionalData += FString::Printf(TEXT("rd_bs %d\n"), (int)CurrentGen->GetConfig().borderSize); 227 | AdditionalData += FString::Printf(TEXT("# Max edge len\n")); 228 | AdditionalData += FString::Printf(TEXT("rd_mel %d\n"), CurrentGen->GetConfig().maxEdgeLen); 229 | AdditionalData += FString::Printf(TEXT("# maxSimplificationError\n")); 230 | AdditionalData += FString::Printf(TEXT("rd_mse %5.5f\n"), CurrentGen->GetConfig().maxSimplificationError); 231 | 232 | 233 | AdditionalData += FString::Printf(TEXT("# Region min size\n")); 234 | AdditionalData += FString::Printf(TEXT("rd_rmis %d\n"), (uint32)FMath::Sqrt(CurrentGen->GetConfig().minRegionArea)); 235 | AdditionalData += FString::Printf(TEXT("# Region merge size\n")); 236 | AdditionalData += FString::Printf(TEXT("rd_rmas %d\n"), (uint32)FMath::Sqrt(CurrentGen->GetConfig().mergeRegionArea)); 237 | 238 | AdditionalData += FString::Printf(TEXT("# maxVertsPerPoly\n")); 239 | AdditionalData += FString::Printf(TEXT("rd_mvpp %d\n"), CurrentGen->GetConfig().maxVertsPerPoly); 240 | AdditionalData += FString::Printf(TEXT("# detailSampleDist\n")); 241 | AdditionalData += FString::Printf(TEXT("rd_dsd %5.5f\n"), CurrentGen->GetConfig().detailSampleDist); 242 | AdditionalData += FString::Printf(TEXT("# detailSampleMaxError\n")); 243 | AdditionalData += FString::Printf(TEXT("rd_dsm %5.5f\n"), CurrentGen->GetConfig().detailSampleMaxError); 244 | AdditionalData += FString::Printf(TEXT("# PolyMaxHeight\n")); 245 | #if ENGINE_MINOR_VERSION < 24 246 | AdditionalData += FString::Printf(TEXT("rd_pmh %d\n"), CurrentGen->GetConfig().PolyMaxHeight); 247 | #endif 248 | AdditionalData += FString::Printf(TEXT("# minRegionArea\n")); 249 | AdditionalData += FString::Printf(TEXT("rd_minra %d\n"), CurrentGen->GetConfig().minRegionArea); 250 | AdditionalData += FString::Printf(TEXT("# mergeRegionArea\n")); 251 | AdditionalData += FString::Printf(TEXT("rd_mergera %d\n"), CurrentGen->GetConfig().mergeRegionArea); 252 | 253 | 254 | AdditionalData += FString::Printf(TEXT("# Perform Voxel Filtering\n")); 255 | AdditionalData += FString::Printf(TEXT("rd_pvf %d\n"), CurrentGen->GetConfig().bPerformVoxelFiltering); 256 | AdditionalData += FString::Printf(TEXT("# bMarkLowHeightAreas\n")); 257 | AdditionalData += FString::Printf(TEXT("rd_mlha %d\n"), CurrentGen->GetConfig().bMarkLowHeightAreas); 258 | AdditionalData += FString::Printf(TEXT("# bFilterLowSpanSequences\n")); 259 | AdditionalData += FString::Printf(TEXT("rd_flss %d\n"), CurrentGen->GetConfig().bFilterLowSpanSequences); 260 | AdditionalData += FString::Printf(TEXT("# bFilterLowSpanFromTileCache\n")); 261 | AdditionalData += FString::Printf(TEXT("rd_flstc %d\n"), CurrentGen->GetConfig().bFilterLowSpanFromTileCache); 262 | 263 | AdditionalData += FString::Printf(TEXT("# AgentIndex\n")); 264 | AdditionalData += FString::Printf(TEXT("rd_ai %d\n"), CurrentGen->GetConfig().AgentIndex); 265 | AdditionalData += FString::Printf(TEXT("# Tile size\n")); 266 | AdditionalData += FString::Printf(TEXT("rd_ts %d\n"), CurrentGen->GetConfig().tileSize); 267 | 268 | AdditionalData += FString::Printf(TEXT("# regionChunkSize\n")); 269 | AdditionalData += FString::Printf(TEXT("rd_rcs %d\n"), CurrentGen->GetConfig().regionChunkSize); 270 | AdditionalData += FString::Printf(TEXT("# TileCacheChunkSize\n")); 271 | AdditionalData += FString::Printf(TEXT("rd_tccs %d\n"), CurrentGen->GetConfig().TileCacheChunkSize); 272 | AdditionalData += FString::Printf(TEXT("# regionPartitioning\n")); 273 | AdditionalData += FString::Printf(TEXT("rd_rp %d\n"), CurrentGen->GetConfig().regionPartitioning); 274 | AdditionalData += FString::Printf(TEXT("# TileCachePartitionType\n")); 275 | AdditionalData += FString::Printf(TEXT("rd_tcpt %d\n"), CurrentGen->GetConfig().TileCachePartitionType); 276 | 277 | AdditionalData += FString::Printf(TEXT("# Generate Detailed Mesh\n")); 278 | AdditionalData += FString::Printf(TEXT("rd_gdm %d\n"), CurrentGen->GetConfig().bGenerateDetailedMesh); 279 | AdditionalData += FString::Printf(TEXT("# MaxPolysPerTile\n")); 280 | AdditionalData += FString::Printf(TEXT("rd_mppt %d\n"), CurrentGen->GetConfig().MaxPolysPerTile); 281 | 282 | AdditionalData += FString::Printf(TEXT("\n")); 283 | 284 | const FString FilePathName = FileName;// FString::Printf(TEXT("_NavDataSet%d_%s.obj"), Index, *CurrentTimeStr); 285 | ExportGeomToOBJFile(FilePathName, CoordBuffer, IndexBuffer, AdditionalData); 286 | } 287 | #if ENGINE_MAJOR_VERSION > 4 ||ENGINE_MINOR_VERSION >=26 288 | ); 289 | #endif 290 | } 291 | // UE_LOG(LogNavigation, Log, TEXT("ExportNavigation time: %.3f sec ."), FPlatformTime::Seconds() - StartExportTime); 292 | } 293 | } 294 | 295 | void FExternExportNavMeshGenerator::GrowConvexHull(const dtReal ExpandBy, const TArray& Verts, TArray& OutResult) 296 | { 297 | if (Verts.Num() < 3) 298 | { 299 | return; 300 | } 301 | 302 | struct FSimpleLine 303 | { 304 | FVector P1, P2; 305 | 306 | FSimpleLine() {} 307 | 308 | FSimpleLine(FVector Point1, FVector Point2) 309 | : P1(Point1), P2(Point2) 310 | { 311 | 312 | } 313 | static FVector Intersection(const FSimpleLine& Line1, const FSimpleLine& Line2) 314 | { 315 | const dtReal A1 = Line1.P2.X - Line1.P1.X; 316 | const dtReal B1 = Line2.P1.X - Line2.P2.X; 317 | const dtReal C1 = Line2.P1.X - Line1.P1.X; 318 | 319 | const dtReal A2 = Line1.P2.Y - Line1.P1.Y; 320 | const dtReal B2 = Line2.P1.Y - Line2.P2.Y; 321 | const dtReal C2 = Line2.P1.Y - Line1.P1.Y; 322 | 323 | const dtReal Denominator = A2 * B1 - A1 * B2; 324 | if (Denominator != 0) 325 | { 326 | const dtReal t = (B1*C2 - B2 * C1) / Denominator; 327 | return Line1.P1 + t * (Line1.P2 - Line1.P1); 328 | } 329 | 330 | return FVector::ZeroVector; 331 | } 332 | }; 333 | 334 | TArray AllVerts(Verts); 335 | AllVerts.Add(Verts[0]); 336 | AllVerts.Add(Verts[1]); 337 | 338 | const int32 VertsCount = AllVerts.Num(); 339 | const FQuat Rotation90(FVector(0, 0, 1), FMath::DegreesToRadians(90)); 340 | 341 | dtReal RotationAngle = MAX_FLT; 342 | for (int32 Index = 0; Index < VertsCount - 2; ++Index) 343 | { 344 | const FVector& V1 = AllVerts[Index + 0]; 345 | const FVector& V2 = AllVerts[Index + 1]; 346 | const FVector& V3 = AllVerts[Index + 2]; 347 | 348 | const FVector V01 = (V1 - V2).GetSafeNormal(); 349 | const FVector V12 = (V2 - V3).GetSafeNormal(); 350 | const FVector NV1 = Rotation90.RotateVector(V01); 351 | const dtReal d = FVector::DotProduct(NV1, V12); 352 | 353 | if (d < 0) 354 | { 355 | // CW 356 | RotationAngle = -90; 357 | break; 358 | } 359 | else if (d > 0) 360 | { 361 | //CCW 362 | RotationAngle = 90; 363 | break; 364 | } 365 | } 366 | 367 | // check if we detected CW or CCW direction 368 | if (RotationAngle >= BIG_NUMBER) 369 | { 370 | return; 371 | } 372 | 373 | const dtReal ExpansionThreshold = 2 * ExpandBy; 374 | const dtReal ExpansionThresholdSQ = ExpansionThreshold * ExpansionThreshold; 375 | const FQuat Rotation(FVector(0, 0, 1), FMath::DegreesToRadians(RotationAngle)); 376 | FSimpleLine PreviousLine; 377 | OutResult.Reserve(Verts.Num()); 378 | for (int32 Index = 0; Index < VertsCount - 2; ++Index) 379 | { 380 | const FVector& V1 = AllVerts[Index + 0]; 381 | const FVector& V2 = AllVerts[Index + 1]; 382 | const FVector& V3 = AllVerts[Index + 2]; 383 | 384 | FSimpleLine Line1; 385 | if (Index > 0) 386 | { 387 | Line1 = PreviousLine; 388 | } 389 | else 390 | { 391 | const FVector V01 = (V1 - V2).GetSafeNormal(); 392 | const FVector N1 = Rotation.RotateVector(V01).GetSafeNormal(); 393 | const FVector MoveDir1 = N1 * ExpandBy; 394 | Line1 = FSimpleLine(V1 + MoveDir1, V2 + MoveDir1); 395 | } 396 | 397 | const FVector V12 = (V2 - V3).GetSafeNormal(); 398 | const FVector N2 = Rotation.RotateVector(V12).GetSafeNormal(); 399 | const FVector MoveDir2 = N2 * ExpandBy; 400 | const FSimpleLine Line2(V2 + MoveDir2, V3 + MoveDir2); 401 | 402 | const FVector NewPoint = FSimpleLine::Intersection(Line1, Line2); 403 | if (NewPoint == FVector::ZeroVector) 404 | { 405 | // both lines are parallel so just move our point by expansion distance 406 | OutResult.Add(V2 + MoveDir2); 407 | } 408 | else 409 | { 410 | const FVector VectorToNewPoint = NewPoint - V2; 411 | const dtReal DistToNewVector = VectorToNewPoint.SizeSquared2D(); 412 | if (DistToNewVector > ExpansionThresholdSQ) 413 | { 414 | //clamp our point to not move to far from original location 415 | const FVector HelpPos = V2 + VectorToNewPoint.GetSafeNormal2D() * ExpandBy * 1.4142; 416 | OutResult.Add(HelpPos); 417 | } 418 | else 419 | { 420 | OutResult.Add(NewPoint); 421 | } 422 | } 423 | 424 | PreviousLine = Line2; 425 | } 426 | } 427 | 428 | void FExternExportNavMeshGenerator::TransformVertexSoupToRecast(const TArray& VertexSoup, TNavStatArray& Verts, TNavStatArray& Faces) 429 | { 430 | if (VertexSoup.Num() == 0) 431 | { 432 | return; 433 | } 434 | 435 | check(VertexSoup.Num() % 3 == 0); 436 | 437 | const int32 StaticFacesCount = VertexSoup.Num() / 3; 438 | int32 VertsCount = Verts.Num(); 439 | const FVector* Vertex = VertexSoup.GetData(); 440 | 441 | for (int32 k = 0; k < StaticFacesCount; ++k, Vertex += 3) 442 | { 443 | Verts.Add(Unreal2RecastPoint(Vertex[0])); 444 | Verts.Add(Unreal2RecastPoint(Vertex[1])); 445 | Verts.Add(Unreal2RecastPoint(Vertex[2])); 446 | Faces.Add(VertsCount + 2); 447 | Faces.Add(VertsCount + 1); 448 | Faces.Add(VertsCount + 0); 449 | 450 | VertsCount += 3; 451 | } 452 | } 453 | 454 | void FExternExportNavMeshGenerator::ExportGeomToOBJFile(const FString& InFileName, const TNavStatArray& GeomCoords, const TNavStatArray& GeomFaces, const FString& AdditionalData) 455 | { 456 | #define USE_COMPRESSION 0 457 | 458 | #if ALLOW_DEBUG_FILES 459 | //SCOPE_CYCLE_COUNTER(STAT_Navigation_TileGeometryExportToObjAsync); 460 | 461 | FString FileName = InFileName; 462 | 463 | #if USE_COMPRESSION 464 | FileName += TEXT("z"); 465 | struct FDataChunk 466 | { 467 | TArray UncompressedBuffer; 468 | TArray CompressedBuffer; 469 | void CompressBuffer() 470 | { 471 | const int32 HeaderSize = sizeof(int32); 472 | const int32 UncompressedSize = UncompressedBuffer.Num(); 473 | CompressedBuffer.Init(0, HeaderSize + FMath::Trunc(1.1f * UncompressedSize)); 474 | 475 | int32 CompressedSize = CompressedBuffer.Num() - HeaderSize; 476 | uint8* DestBuffer = CompressedBuffer.GetData(); 477 | FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize); 478 | DestBuffer += HeaderSize; 479 | 480 | FCompression::CompressMemory(NAME_Zlib, (void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize, COMPRESS_BiasMemory); 481 | CompressedBuffer.SetNum(CompressedSize + HeaderSize, false); 482 | } 483 | }; 484 | FDataChunk AllDataChunks[3]; 485 | const int32 NumberOfChunks = sizeof(AllDataChunks) / sizeof(FDataChunk); 486 | { 487 | FMemoryWriter ArWriter(AllDataChunks[0].UncompressedBuffer); 488 | for (int32 i = 0; i < GeomCoords.Num(); i += 3) 489 | { 490 | FVector Vertex(GeomCoords[i + 0], GeomCoords[i + 1], GeomCoords[i + 2]); 491 | ArWriter << Vertex; 492 | } 493 | } 494 | 495 | { 496 | FMemoryWriter ArWriter(AllDataChunks[1].UncompressedBuffer); 497 | for (int32 i = 0; i < GeomFaces.Num(); i += 3) 498 | { 499 | FVector Face(GeomFaces[i + 0] + 1, GeomFaces[i + 1] + 1, GeomFaces[i + 2] + 1); 500 | ArWriter << Face; 501 | } 502 | } 503 | 504 | { 505 | auto AnsiAdditionalData = StringCast(*AdditionalData); 506 | FMemoryWriter ArWriter(AllDataChunks[2].UncompressedBuffer); 507 | ArWriter.Serialize((ANSICHAR*)AnsiAdditionalData.Get(), AnsiAdditionalData.Length()); 508 | } 509 | 510 | FArchive* FileAr = IFileManager::Get().CreateDebugFileWriter(*FileName); 511 | if (FileAr != NULL) 512 | { 513 | for (int32 Index = 0; Index < NumberOfChunks; ++Index) 514 | { 515 | AllDataChunks[Index].CompressBuffer(); 516 | int32 BufferSize = AllDataChunks[Index].CompressedBuffer.Num(); 517 | FileAr->Serialize(&BufferSize, sizeof(int32)); 518 | FileAr->Serialize((void*)AllDataChunks[Index].CompressedBuffer.GetData(), AllDataChunks[Index].CompressedBuffer.Num()); 519 | } 520 | UE_LOG(LogNavigation, Error, TEXT("UncompressedBuffer size:: %d "), AllDataChunks[0].UncompressedBuffer.Num() + AllDataChunks[1].UncompressedBuffer.Num() + AllDataChunks[2].UncompressedBuffer.Num()); 521 | FileAr->Close(); 522 | } 523 | 524 | #else 525 | FArchive* FileAr = IFileManager::Get().CreateDebugFileWriter(*FileName); 526 | if (FileAr != NULL) 527 | { 528 | for (int32 Index = 0; Index < GeomCoords.Num(); Index += 3) 529 | { 530 | FString LineToSave = FString::Printf(TEXT("v %f %f %f\n"), GeomCoords[Index + 0], GeomCoords[Index + 1], GeomCoords[Index + 2]); 531 | auto AnsiLineToSave = StringCast(*LineToSave); 532 | FileAr->Serialize((ANSICHAR*)AnsiLineToSave.Get(), AnsiLineToSave.Length()); 533 | } 534 | 535 | for (int32 Index = 0; Index < GeomFaces.Num(); Index += 3) 536 | { 537 | FString LineToSave = FString::Printf(TEXT("f %d %d %d\n"), GeomFaces[Index + 0] + 1, GeomFaces[Index + 1] + 1, GeomFaces[Index + 2] + 1); 538 | auto AnsiLineToSave = StringCast(*LineToSave); 539 | FileAr->Serialize((ANSICHAR*)AnsiLineToSave.Get(), AnsiLineToSave.Length()); 540 | } 541 | 542 | auto AnsiAdditionalData = StringCast(*AdditionalData); 543 | FileAr->Serialize((ANSICHAR*)AnsiAdditionalData.Get(), AnsiAdditionalData.Length()); 544 | FileAr->Close(); 545 | } 546 | #endif 547 | 548 | #undef USE_COMPRESSION 549 | #endif 550 | } 551 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Private/FlibExportNavData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "FlibExportNavData.h" 4 | #include "UE4RecastHelper.h" 5 | #include "dtNavMeshWrapper.h" 6 | 7 | // #include "Editor.h" 8 | #include "NavigationSystem.h" 9 | #include "NavigationData.h" 10 | #include "NavMesh/RecastQueryFilter.h" 11 | #include "AI/NavDataGenerator.h" 12 | #include "Kismet/KismetSystemLibrary.h" 13 | #include "Misc/Paths.h" 14 | #include "Engine/Engine.h" 15 | #include "Engine/World.h" 16 | #include "Resources/Version.h" 17 | 18 | DECLARE_STATS_GROUP(TEXT("ExportNav"), STATGROUP_ExportNav, STATCAT_Advanced); 19 | DECLARE_CYCLE_STAT(TEXT("ExportNav"), STAT_ExportNav, STATGROUP_ExportNav); 20 | 21 | bool UFlibExportNavData::ExportRecastNavMesh(const FString& SaveFile,EExportMode InExportMode) 22 | { 23 | #if WITH_EDITOR 24 | 25 | FString FinalSaveFile=SaveFile; 26 | 27 | // UWorld* World = GEditor->GetEditorWorldContext(false).World(); 28 | 29 | UWorld* World=NULL; 30 | 31 | auto WorldList = GEngine->GetWorldContexts(); 32 | for (int32 i=0;i < WorldList.Num();++i) 33 | { 34 | UWorld* local_World = WorldList[i].World(); 35 | 36 | if (UKismetSystemLibrary::IsValid(local_World) && local_World->WorldType == EWorldType::Editor) 37 | { 38 | World = local_World; 39 | break; 40 | } 41 | 42 | } 43 | 44 | if (World && World->GetNavigationSystem()) 45 | { 46 | if (ANavigationData* NavData = Cast(World->GetNavigationSystem()->GetMainNavData())) 47 | { 48 | if (FExternExportNavMeshGenerator* Generator = static_cast(NavData->GetGenerator())) 49 | { 50 | if (SaveFile.IsEmpty()) 51 | { 52 | const FString Name = NavData->GetName(); 53 | FinalSaveFile = FPaths::Combine(FPaths::ProjectSavedDir(), Name); 54 | } 55 | Generator->ExternExportNavigationData(FinalSaveFile,InExportMode); 56 | return true; 57 | } 58 | } 59 | } 60 | return false; 61 | #else 62 | return false; 63 | #endif 64 | } 65 | 66 | bool UFlibExportNavData::ExportRecastNavData(const FString& InFilePath) 67 | { 68 | // UWorld* World = GEditor->GetEditorWorldContext(false).World(); 69 | UWorld* World = NULL; 70 | 71 | auto WorldList = GEngine->GetWorldContexts(); 72 | for (int32 i = 0; i < WorldList.Num(); ++i) 73 | { 74 | UWorld* local_World = WorldList[i].World(); 75 | if (local_World && UKismetSystemLibrary::IsValid(local_World)) 76 | { 77 | World = local_World; 78 | break; 79 | } 80 | } 81 | if (!World) return false; 82 | 83 | dtNavMesh* RecastdtNavMesh = UFlibExportNavData::GetdtNavMeshInsByWorld(World); 84 | 85 | if (RecastdtNavMesh) 86 | { 87 | UE4RecastHelper::SerializedtNavMesh(TCHAR_TO_ANSI(*InFilePath), RecastdtNavMesh); 88 | return true; 89 | } 90 | else { 91 | return false; 92 | } 93 | } 94 | 95 | 96 | dtNavMesh* UFlibExportNavData::GetdtNavMeshInsByWorld(UWorld* InWorld) 97 | { 98 | UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent(InWorld); 99 | check(NavSys); 100 | ANavigationData* MainNavDataIns = NavSys->GetDefaultNavDataInstance(); 101 | ARecastNavMesh* RecastNavMeshIns = Cast(MainNavDataIns); 102 | if(RecastNavMeshIns && UKismetSystemLibrary::IsValid(RecastNavMeshIns)) 103 | { 104 | dtNavMesh* RecastdtNavMesh = RecastNavMeshIns->GetRecastMesh(); 105 | return RecastdtNavMesh; 106 | } 107 | return NULL; 108 | } 109 | 110 | bool UFlibExportNavData::IsValidNavigvationPointInWorld(UObject* WorldContextObject, const FVector& Point, const FVector InExtern /*= FVector::ZeroVector*/) 111 | { 112 | bool rSuccess = false; 113 | 114 | UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); 115 | dtNavMesh* NavMeshData = UFlibExportNavData::GetdtNavMeshInsByWorld(World); 116 | 117 | if (NavMeshData) 118 | { 119 | rSuccess = UE4RecastHelper::dtIsValidNavigationPoint(NavMeshData, Point, InExtern); 120 | dtFreeNavMesh(NavMeshData); 121 | } 122 | 123 | return rSuccess; 124 | } 125 | 126 | bool UFlibExportNavData::IsValidNavigationPointInNavObj(UdtNavMeshWrapper* InDtNavObject, const FVector& Point, const FVector InExtern) 127 | { 128 | bool rSuccess = false; 129 | 130 | if (InDtNavObject && InDtNavObject->IsAvailableNavData()) 131 | { 132 | dtNavMesh* NavMeshData = InDtNavObject->GetNavData(); 133 | if (NavMeshData) 134 | { 135 | rSuccess = UE4RecastHelper::dtIsValidNavigationPoint(NavMeshData, Point, InExtern); 136 | } 137 | } 138 | 139 | return rSuccess; 140 | } 141 | 142 | bool UFlibExportNavData::FindDetourPathFromGameAxisByNavObject(class UdtNavMeshWrapper* InDtNavObject, const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths) 143 | { 144 | using namespace UE4RecastHelper; 145 | 146 | bool bstatus = false; 147 | if (InDtNavObject && InDtNavObject->IsAvailableNavData()) 148 | { 149 | dtNavMesh* NavMeshData = InDtNavObject->GetNavData(); 150 | if (NavMeshData) 151 | { 152 | bstatus = UFlibExportNavData::FindDetourPathByGameAxis(NavMeshData, InStart, InEnd,InExternSize, OutPaths); 153 | } 154 | } 155 | return bstatus; 156 | } 157 | 158 | bool UFlibExportNavData::FindDetourPathFromRecastAxisByNavObject(class UdtNavMeshWrapper* InDtNavObject, const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths) 159 | { 160 | using namespace UE4RecastHelper; 161 | 162 | bool bstatus = false; 163 | if (InDtNavObject && InDtNavObject->IsAvailableNavData()) 164 | { 165 | dtNavMesh* NavMeshData = InDtNavObject->GetNavData(); 166 | if (NavMeshData) 167 | { 168 | bstatus = UFlibExportNavData::FindDetourPathByRecastAxis(NavMeshData, InStart, InEnd,InExternSize, OutPaths); 169 | } 170 | } 171 | return bstatus; 172 | } 173 | 174 | bool UFlibExportNavData::FindDetourPathByEngineNavMesh(const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths) 175 | { 176 | UWorld* World = NULL; 177 | 178 | auto WorldList = GEngine->GetWorldContexts(); 179 | for (int32 i = 0; i < WorldList.Num(); ++i) 180 | { 181 | UWorld* local_World = WorldList[i].World(); 182 | if (local_World && UKismetSystemLibrary::IsValid(local_World)) 183 | { 184 | World = local_World; 185 | break; 186 | } 187 | } 188 | if (!World) return false; 189 | 190 | dtNavMesh* RecastdtNavMesh = UFlibExportNavData::GetdtNavMeshInsByWorld(World); 191 | 192 | return UFlibExportNavData::FindDetourPathByGameAxis(RecastdtNavMesh, InStart, InEnd,InExternSize, OutPaths); 193 | 194 | } 195 | 196 | 197 | bool UFlibExportNavData::GetRandomPointByNavObject(class UdtNavMeshWrapper* InDtNavObject, const FVector& InOrigin, const FVector& InRedius, FVector& OutPoint) 198 | { 199 | dtNavMeshQuery NavQuery; 200 | dtQueryFilter QueryFilter; 201 | 202 | NavQuery.init(InDtNavObject->GetNavData(), 1024); 203 | bool Status = false; 204 | UE4RecastHelper::FVector3 Result; 205 | { 206 | SCOPE_CYCLE_COUNTER(STAT_ExportNav); 207 | Status = UE4RecastHelper::GetRandomPointInRadius(&NavQuery, &QueryFilter, InOrigin, InRedius, Result); 208 | } 209 | OutPoint = Result.UE4Vector(); 210 | return Status; 211 | } 212 | #include "Detour/DetourStatus.h" 213 | 214 | bool UFlibExportNavData::FindDetourPathByRecastAxis(dtNavMesh* InNavMesh, const FVector& InStart, const FVector& InEnd, const FVector& ExternSize, TArray& OutPaths) 215 | { 216 | FVector RcStart = InStart; 217 | FVector RcEnd = InEnd; 218 | 219 | dtNavMeshQuery NavQuery; 220 | dtQueryFilter QueryFilter; 221 | 222 | NavQuery.init(InNavMesh, 1024); 223 | 224 | dtReal Extern[3]{ ExternSize.X,ExternSize.Y,ExternSize.Z }; 225 | 226 | dtReal StartPoint[3]{ RcStart.X,RcStart.Y,RcStart.Z }; 227 | dtPolyRef StartPolyRef; 228 | dtReal StartNarestPt[3]{ 0.f }; 229 | dtStatus StartStatus = NavQuery.findNearestPoly(StartPoint, Extern, &QueryFilter, &StartPolyRef, StartNarestPt); 230 | 231 | dtReal EndPoint[3]{ RcEnd.X,RcEnd.Y,RcEnd.Z }; 232 | dtPolyRef EndPolyRef; 233 | dtReal EndNarestPt[3]{ 0.f }; 234 | dtStatus EndStatus = NavQuery.findNearestPoly(EndPoint, Extern, &QueryFilter, &EndPolyRef, EndNarestPt); 235 | 236 | UE_LOG(LogTemp, Log, TEXT("Start Point FindNearestPoly status is %u,PolyRef is %u."), StartStatus, StartPolyRef); 237 | UE_LOG(LogTemp, Log, TEXT("End Point FindNearestPoly status is %u.,PolyRef is %u."), EndStatus, EndPolyRef); 238 | UE_LOG(LogTemp, Log, TEXT("Start Point FindNearestPoly narestpt is %s."), *FVector(StartPoint[0], StartPoint[1], StartPoint[2]).ToString()); 239 | UE_LOG(LogTemp, Log, TEXT("End Point FindNearestPoly narestpt is %s."), *FVector(EndPoint[0], EndPoint[1], EndPoint[2]).ToString()); 240 | 241 | if (!dtStatusSucceed(StartStatus) || !dtStatusSucceed(EndStatus)) 242 | { 243 | UE_LOG(LogTemp,Log,TEXT("find Start or End nearest poly faild.")) 244 | return false; 245 | } 246 | 247 | dtQueryResult result; 248 | dtReal totalcost[1024 * 3]; 249 | 250 | #if ENGINE_MAJOR_VERSION <=4 && ENGINE_MINOR_VERSION < 24 251 | dtStatus FindPathStatus = NavQuery.findPath(StartPolyRef, EndPolyRef, StartNarestPt, EndNarestPt, &QueryFilter, result, totalcost); 252 | #else 253 | const float CostLimit = FLT_MAX; 254 | dtStatus FindPathStatus = NavQuery.findPath(StartPolyRef, EndPolyRef, StartNarestPt, EndNarestPt, CostLimit, &QueryFilter, result, totalcost); 255 | #endif 256 | 257 | UE_LOG(LogTemp, Log, TEXT("findPath status is %u.,result size is %u."), FindPathStatus, result.size()); 258 | if (!dtStatusSucceed(FindPathStatus) && result.size() <= 0) 259 | { 260 | UE_LOG(LogTemp, Log, TEXT("find path faild.")) 261 | return false; 262 | } 263 | 264 | std::vector path; 265 | 266 | for (int index = 0; index < result.size(); ++index) 267 | { 268 | UE_LOG(LogTemp, Log, TEXT("Find Path index is %d ref is %u."), index, result.getRef(index)); 269 | path.push_back(result.getRef(index)); 270 | dtReal currentpos[3]{ 0.f }; 271 | result.getPos(index, currentpos); 272 | UE_LOG(LogTemp, Log, TEXT("Find Path index is %d pos is %s."), index, *UFlibExportNavData::Recast2UnrealPoint(FVector(currentpos[0], currentpos[1], currentpos[2])).ToString()); 273 | // OutPaths.Add(UFlibExportNavData::Recast2UnrealPoint(FVector(currentpos[0], currentpos[1], currentpos[2]))); 274 | } 275 | 276 | dtQueryResult findStraightPathResult; 277 | NavQuery.findStraightPath(StartNarestPt, EndNarestPt, path.data(), path.size(), findStraightPathResult); 278 | 279 | UE_LOG(LogTemp, Log, TEXT("findStraightPath size is %u."), findStraightPathResult.size()); 280 | for (int index = 0; index < findStraightPathResult.size(); ++index) 281 | { 282 | dtReal currentpos[3]{ 0.f }; 283 | findStraightPathResult.getPos(index, currentpos); 284 | UE_LOG(LogTemp, Log, TEXT("findStraightPath index is %d ref is %u."), index, findStraightPathResult.getRef(index)); 285 | UE_LOG(LogTemp, Log, TEXT("findStraightPath index is %d pos is %s."), index, *UFlibExportNavData::Recast2UnrealPoint(FVector(currentpos[0], currentpos[1], currentpos[2])).ToString()); 286 | OutPaths.Add(UFlibExportNavData::Recast2UnrealPoint(FVector(currentpos[0], currentpos[1], currentpos[2]))); 287 | } 288 | return true; 289 | } 290 | 291 | bool UFlibExportNavData::FindDetourPathByGameAxis(dtNavMesh* InNavMesh, const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths) 292 | { 293 | FVector RcStart = UFlibExportNavData::Unreal2RecastPoint(InStart); 294 | FVector RcEnd = UFlibExportNavData::Unreal2RecastPoint(InEnd); 295 | return UFlibExportNavData::FindDetourPathByRecastAxis(InNavMesh, RcStart, RcEnd,InExternSize, OutPaths); 296 | } 297 | 298 | bool UFlibExportNavData::IsValidNavigationPointInNavbin(const FString& InNavBinPath,const FVector& Point, const FVector InExtern) 299 | { 300 | bool rSuccess = false; 301 | 302 | if (InNavBinPath.IsEmpty() || !FPaths::FileExists(InNavBinPath)) 303 | return false; 304 | 305 | dtNavMesh* NavMeshData = UE4RecastHelper::DeSerializedtNavMesh(TCHAR_TO_ANSI(*InNavBinPath)); 306 | 307 | if (NavMeshData) 308 | { 309 | rSuccess = UE4RecastHelper::dtIsValidNavigationPoint(NavMeshData, Point, InExtern); 310 | dtFreeNavMesh(NavMeshData); 311 | } 312 | 313 | return rSuccess; 314 | } 315 | 316 | FString UFlibExportNavData::ConvPath_Slash2BackSlash(const FString& InPath) 317 | { 318 | FString ResaultPath; 319 | TArray OutArray; 320 | InPath.ParseIntoArray(OutArray, TEXT("\\")); 321 | if (OutArray.Num() == 1 && OutArray[0] == InPath) 322 | { 323 | InPath.ParseIntoArray(OutArray, TEXT("/")); 324 | } 325 | for (const auto& item : OutArray) 326 | { 327 | if (FPaths::DirectoryExists(ResaultPath + item)) 328 | { 329 | ResaultPath.Append(item); 330 | ResaultPath.Append(TEXT("\\")); 331 | } 332 | } 333 | return ResaultPath; 334 | } 335 | 336 | FVector UFlibExportNavData::Recast2UnrealPoint(const FVector& Vector) 337 | { 338 | return FVector(-Vector.X, -Vector.Z, Vector.Y); 339 | } 340 | 341 | FVector UFlibExportNavData::Unreal2RecastPoint(const FVector& Vector) 342 | { 343 | return FVector(-Vector.X, Vector.Z, -Vector.Y); 344 | } 345 | 346 | float UFlibExportNavData::FindDistanceToWall(UObject* WorldContextObject,const FVector& StartLoc) 347 | { 348 | float result=0.f; 349 | UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent(WorldContextObject); 350 | check(NavSys); 351 | ANavigationData* MainNavDataIns = NavSys->GetDefaultNavDataInstance(); 352 | ARecastNavMesh* RecastNavMeshIns = Cast(MainNavDataIns); 353 | if (RecastNavMeshIns && UKismetSystemLibrary::IsValid(RecastNavMeshIns)) 354 | { 355 | result = RecastNavMeshIns->FindDistanceToWall(StartLoc); 356 | } 357 | return result; 358 | } -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Private/UE4RecastHelper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #include "UE4RecastHelper.h" 4 | #include "Detour/DetourStatus.h" 5 | #include "Detour/DetourNavMeshQuery.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef USE_DETOUR_BUILT_INTO_UE4 11 | #include "Resources/Version.h" 12 | #endif 13 | 14 | static const long RCN_NAVMESH_VERSION = 1; 15 | static const int INVALID_NAVMESH_POLYREF = 0; 16 | static const int MAX_POLYS = 256; 17 | static const int NAV_ERROR_NEARESTPOLY = -2; 18 | #pragma warning (disable:4996) 19 | 20 | bool UE4RecastHelper::dtIsValidNavigationPoint(dtNavMesh* InNavMeshData, const UE4RecastHelper::FVector3& InPoint, const UE4RecastHelper::FVector3& InExtent) 21 | { 22 | bool bSuccess = false; 23 | 24 | using namespace UE4RecastHelper; 25 | 26 | if (!InNavMeshData) return bSuccess; 27 | 28 | FVector3 RcPoint = UE4RecastHelper::Unreal2RecastPoint(InPoint); 29 | const FVector3 ModifiedExtent = InExtent; 30 | FVector3 RcExtent = UE4RecastHelper::Unreal2RecastPoint(ModifiedExtent).GetAbs(); 31 | FVector3 ClosestPoint; 32 | 33 | 34 | dtNavMeshQuery NavQuery; 35 | #ifdef USE_DETOUR_BUILT_INTO_UE4 36 | dtQuerySpecialLinkFilter LinkFilter; 37 | NavQuery.init(InNavMeshData, 0, &LinkFilter); 38 | // UE_LOG(LogTemp, Warning, TEXT("CALL NavQuery.init(InNavMeshData, 0, &LinkFilter);")); 39 | #else 40 | NavQuery.init(InNavMeshData, 0); 41 | #endif 42 | 43 | dtPolyRef PolyRef; 44 | dtQueryFilter QueryFilter; 45 | 46 | #ifdef USE_DETOUR_BUILT_INTO_UE4 47 | NavQuery.findNearestPoly2D(&RcPoint.X, &RcExtent.X, &QueryFilter, &PolyRef, (dtReal*)(&ClosestPoint)); 48 | UE_LOG(LogTemp, Log, TEXT("dtIsValidNavigationPoint PolyRef is %ud."), PolyRef); 49 | #else 50 | NavQuery.findNearestPoly(&RcPoint.X, &RcExtent.X, &QueryFilter, &PolyRef, (dtReal*)(&ClosestPoint)); 51 | #endif 52 | 53 | if (PolyRef > 0) 54 | { 55 | const FVector3& UnrealClosestPoint = UE4RecastHelper::Recast2UnrealPoint(ClosestPoint); 56 | const FVector3 ClosestPointDelta = UnrealClosestPoint - InPoint; 57 | if (-ModifiedExtent.X <= ClosestPointDelta.X && ClosestPointDelta.X <= ModifiedExtent.X 58 | && -ModifiedExtent.Y <= ClosestPointDelta.Y && ClosestPointDelta.Y <= ModifiedExtent.Y 59 | && -ModifiedExtent.Z <= ClosestPointDelta.Z && ClosestPointDelta.Z <= ModifiedExtent.Z) 60 | { 61 | bSuccess = true; 62 | } 63 | } 64 | 65 | return bSuccess; 66 | } 67 | 68 | int UE4RecastHelper::findStraightPath(dtNavMesh* InNavMeshData, dtNavMeshQuery* InNavmeshQuery, const FVector3& start, const FVector3& end, std::vector& paths) 69 | { 70 | bool bSuccess = false; 71 | 72 | using namespace UE4RecastHelper; 73 | 74 | if (!InNavMeshData) return bSuccess; 75 | 76 | FVector3 RcStart = UE4RecastHelper::Unreal2RecastPoint(start); 77 | FVector3 RcEnd = UE4RecastHelper::Unreal2RecastPoint(end); 78 | FVector3 RcExtent{ 10.f,10.f,10.f }; 79 | 80 | dtNavMeshQuery NavQuery; 81 | #ifdef USE_DETOUR_BUILT_INTO_UE4 82 | dtQuerySpecialLinkFilter LinkFilter; 83 | NavQuery.init(InNavMeshData, 0, &LinkFilter); 84 | // UE_LOG(LogTemp, Warning, TEXT("CALL NavQuery.init(InNavMeshData, 0, &LinkFilter);")); 85 | #else 86 | NavQuery.init(InNavMeshData, 0); 87 | #endif 88 | 89 | 90 | FVector3 StartClosestPoint; 91 | FVector3 EndClosestPoint; 92 | 93 | dtPolyRef StartPolyRef; 94 | dtPolyRef EndPolyRef; 95 | dtQueryFilter QueryFilter; 96 | 97 | #ifdef USE_DETOUR_BUILT_INTO_UE4 98 | NavQuery.findNearestPoly2D(&RcStart.X, &RcExtent.X, &QueryFilter, &StartPolyRef, (dtReal*)(&StartClosestPoint)); 99 | // UE_LOG(LogTemp, Warning, TEXT("CALL findNearestPoly2D")); 100 | #else 101 | NavQuery.findNearestPoly(&RcStart.X, &RcExtent.X, &QueryFilter, &StartPolyRef, (dtReal*)(&StartClosestPoint)); 102 | #endif 103 | 104 | #ifdef USE_DETOUR_BUILT_INTO_UE4 105 | NavQuery.findNearestPoly2D(&RcEnd.X, &RcExtent.X, &QueryFilter, &EndPolyRef, (dtReal*)(&EndClosestPoint)); 106 | // UE_LOG(LogTemp, Warning, TEXT("CALL findNearestPoly2D")); 107 | #else 108 | NavQuery.findNearestPoly(&RcEnd.X, &RcExtent.X, &QueryFilter, &EndPolyRef, (dtReal*)(&EndClosestPoint)); 109 | #endif 110 | 111 | #ifdef USE_DETOUR_BUILT_INTO_UE4 112 | UE_LOG(LogTemp, Log, TEXT("FindDetourPath StartPolyRef is %u."), StartPolyRef); 113 | UE_LOG(LogTemp, Log, TEXT("FindDetourPath EndPolyRef is %u."), EndPolyRef); 114 | 115 | UE_LOG(LogTemp, Log, TEXT("FindDetourPath StartClosestPoint is %s."), *FVector(StartClosestPoint.X,StartClosestPoint.Y,StartClosestPoint.Z).ToString()); 116 | UE_LOG(LogTemp, Log, TEXT("FindDetourPath EndClosestPoint is %s."), *FVector(EndClosestPoint.X, EndClosestPoint.Y, EndClosestPoint.Z).ToString()); 117 | #endif 118 | 119 | dtQueryResult Result; 120 | 121 | #if ENGINE_MAJOR_VERSION <=4 && ENGINE_MINOR_VERSION < 24 122 | dtStatus FindPathStatus = NavQuery.findPath(StartPolyRef, EndPolyRef, (dtReal*)(&StartClosestPoint), (dtReal*)(&EndClosestPoint), &QueryFilter, Result, NULL); 123 | #else 124 | const float CostLimit = FLT_MAX; 125 | dtStatus FindPathStatus = NavQuery.findPath(StartPolyRef, EndPolyRef, (dtReal*)(&StartClosestPoint), (dtReal*)(&EndClosestPoint), CostLimit, &QueryFilter, Result, NULL); 126 | #endif 127 | 128 | #ifdef USE_DETOUR_BUILT_INTO_UE4 129 | UE_LOG(LogTemp, Log, TEXT("FindDetourPath FindPath return status is %u."), FindPathStatus); 130 | 131 | UE_LOG(LogTemp, Log, TEXT("FindDetourPath dtQueryResult size is %u."), Result.size()); 132 | #endif 133 | // InNavmeshQuery->findStraightPath(&StartNearestPt.X, &EndNearestPt.X, ); 134 | return 0; 135 | } 136 | 137 | bool UE4RecastHelper::GetRandomPointInRadius(dtNavMeshQuery* InNavmeshQuery, dtQueryFilter* InQueryFilter, const FVector3& InOrigin, const FVector3& InRedius, FVector3& OutPoint) 138 | { 139 | bool bStatus = false; 140 | dtNavMeshQuery* NavQuery = InNavmeshQuery; 141 | if (!NavQuery) 142 | { 143 | return false; 144 | } 145 | dtPolyRef OriginPolyRef; 146 | FVector3 ClosestPoint; 147 | FVector3 RcPoint = UE4RecastHelper::Unreal2RecastPoint(InOrigin); 148 | 149 | #ifdef USE_DETOUR_BUILT_INTO_UE4 150 | InNavmeshQuery->findNearestPoly2D(&RcPoint.X, &InRedius.X, InQueryFilter, &OriginPolyRef, (dtReal*)(&ClosestPoint)); 151 | // UE_LOG(LogTemp, Warning, TEXT("CALL findNearestPoly2D")); 152 | #else 153 | NavQuery->findNearestPoly(&RcPoint.X, &InRedius.X, InQueryFilter, &OriginPolyRef, (dtReal*)(&ClosestPoint)); 154 | #endif 155 | 156 | dtPolyRef ResultPoly; 157 | FVector3 ResultPoint; 158 | auto NormalRand = []()->float 159 | { 160 | return std::rand() / (float)RAND_MAX; 161 | }; 162 | 163 | dtStatus Status = NavQuery->findRandomPointAroundCircle(OriginPolyRef, &RcPoint.X, InRedius.X, InQueryFilter, NormalRand, &ResultPoly, &ResultPoint.X); 164 | 165 | if (dtStatusSucceed(Status)) 166 | { 167 | OutPoint = UE4RecastHelper::Recast2UnrealPoint(ResultPoint); 168 | bStatus = true; 169 | } 170 | return bStatus; 171 | } 172 | 173 | void UE4RecastHelper::SerializedtNavMesh(const char* path, const dtNavMesh* mesh) 174 | { 175 | using namespace UE4RecastHelper; 176 | if (!mesh) return; 177 | 178 | std::FILE* fp = std::fopen(path, "wb"); 179 | if (!fp) 180 | return; 181 | 182 | // Store header. 183 | NavMeshSetHeader header; 184 | header.magic = NAVMESHSET_MAGIC; 185 | header.version = NAVMESHSET_VERSION; 186 | header.numTiles = 0; 187 | // auto dtNavMesh_getTile = GET_PRIVATE_MEMBER_FUNCTION(dtNavMesh, getTile); 188 | 189 | for (int i = 0; i < mesh->getMaxTiles(); ++i) 190 | { 191 | const dtMeshTile* tile = mesh->getTile(i); 192 | // const dtMeshTile* tile = CALL_MEMBER_FUNCTION(mesh, dtNavMesh_getTile, i); 193 | if (!tile || !tile->header || !tile->dataSize) continue; 194 | header.numTiles++; 195 | } 196 | std::memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams)); 197 | std::fwrite(&header, sizeof(NavMeshSetHeader), 1, fp); 198 | 199 | // Store tiles. 200 | for (int i = 0; i < mesh->getMaxTiles(); ++i) 201 | { 202 | const dtMeshTile* tile = mesh->getTile(i); 203 | // const dtMeshTile* tile = CALL_MEMBER_FUNCTION(mesh, dtNavMesh_getTile, i); 204 | if (!tile || !tile->header || !tile->dataSize) continue; 205 | 206 | NavMeshTileHeader tileHeader; 207 | tileHeader.tileRef = mesh->getTileRef(tile); 208 | tileHeader.dataSize = tile->dataSize; 209 | std::fwrite(&tileHeader, sizeof(tileHeader), 1, fp); 210 | 211 | std::fwrite(tile->data, tile->dataSize, 1, fp); 212 | } 213 | 214 | std::fclose(fp); 215 | } 216 | 217 | dtNavMesh* UE4RecastHelper::DeSerializedtNavMesh(const char* path) 218 | { 219 | 220 | std::FILE* fp = std::fopen(path, "rb"); 221 | if (!fp) return 0; 222 | 223 | using namespace UE4RecastHelper; 224 | // Read header. 225 | NavMeshSetHeader header; 226 | size_t sizenum = sizeof(NavMeshSetHeader); 227 | size_t readLen = std::fread(&header, sizenum, 1, fp); 228 | if (readLen != 1) 229 | { 230 | std::fclose(fp); 231 | return 0; 232 | } 233 | if (header.magic != NAVMESHSET_MAGIC) 234 | { 235 | std::fclose(fp); 236 | return 0; 237 | } 238 | if (header.version != NAVMESHSET_VERSION) 239 | { 240 | std::fclose(fp); 241 | return 0; 242 | } 243 | 244 | dtNavMesh* mesh = dtAllocNavMesh(); 245 | if (!mesh) 246 | { 247 | std::fclose(fp); 248 | return 0; 249 | } 250 | dtStatus status = mesh->init(&header.params); 251 | if (dtStatusFailed(status)) 252 | { 253 | std::fclose(fp); 254 | return 0; 255 | } 256 | 257 | // Read tiles. 258 | for (int i = 0; i < header.numTiles; ++i) 259 | { 260 | NavMeshTileHeader tileHeader; 261 | readLen = std::fread(&tileHeader, sizeof(tileHeader), 1, fp); 262 | if (readLen != 1) 263 | { 264 | std::fclose(fp); 265 | return 0; 266 | } 267 | 268 | if (!tileHeader.tileRef || !tileHeader.dataSize) 269 | break; 270 | 271 | unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_TEMP); 272 | if (!data) break; 273 | std::memset(data, 0, tileHeader.dataSize); 274 | readLen = fread(data, tileHeader.dataSize, 1, fp); 275 | if (readLen != 1) 276 | { 277 | dtFree(data, DT_ALLOC_TEMP); 278 | fclose(fp); 279 | return 0; 280 | } 281 | 282 | mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); 283 | } 284 | 285 | std::fclose(fp); 286 | 287 | return mesh; 288 | } 289 | 290 | namespace UE4RecastHelper 291 | { 292 | FVector3 Recast2UnrealPoint(const FVector3& Vector) 293 | { 294 | return FVector3(-Vector.X, -Vector.Z, Vector.Y); 295 | } 296 | 297 | FVector3 Unreal2RecastPoint(const FVector3& Vector) 298 | { 299 | return FVector3(-Vector.X, Vector.Z, -Vector.Y); 300 | } 301 | }; -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Private/dtNavMeshWrapper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | 4 | #include "dtNavMeshWrapper.h" 5 | #include "Detour/DetourNavMesh.h" 6 | #include "UE4RecastHelper.h" 7 | #include "Misc/Paths.h" 8 | 9 | UdtNavMeshWrapper::UdtNavMeshWrapper(const FObjectInitializer& ObjectInitializer) 10 | :Super(ObjectInitializer),NavmeshIns(NULL) 11 | { 12 | UE_LOG(LogTemp, Warning, TEXT("UdtNavMeshWrapper::UdtNavMeshWrapper(const FObjectInitializer& ObjectInitializer)")); 13 | } 14 | 15 | UdtNavMeshWrapper::~UdtNavMeshWrapper() 16 | { 17 | UE_LOG(LogTemp, Warning, TEXT("UdtNavMeshWrapper::~UdtNavMeshWrapper()")); 18 | 19 | if (NavmeshIns) 20 | { 21 | UE_LOG(LogTemp, Warning, TEXT("UdtNavMeshWrapper::~UdtNavMeshWrapper() free dtNavMesh")); 22 | dtFreeNavMesh(NavmeshIns); 23 | } 24 | } 25 | 26 | UdtNavMeshWrapper* UdtNavMeshWrapper::LoadNavData(const FString& NavDataBinPath) 27 | { 28 | if (!NavDataBinPath.IsEmpty()&& FPaths::FileExists(NavDataBinPath)) 29 | { 30 | if (NavmeshIns) 31 | { 32 | UE_LOG(LogTemp, Warning, TEXT("UdtNavMeshWrapper::LoadNavData Free old NavData")); 33 | dtFreeNavMesh(NavmeshIns); 34 | NavmeshIns = NULL; 35 | } 36 | 37 | UE_LOG(LogTemp, Warning, TEXT("UdtNavMeshWrapper::LoadNavData DeSerializedtNavMesh")); 38 | NavmeshIns = UE4RecastHelper::DeSerializedtNavMesh(TCHAR_TO_ANSI(*NavDataBinPath)); 39 | } 40 | 41 | return this; 42 | } 43 | 44 | bool UdtNavMeshWrapper::IsAvailableNavData() const 45 | { 46 | return !!NavmeshIns; 47 | } 48 | 49 | dtNavMesh* UdtNavMeshWrapper::GetNavData() const 50 | { 51 | return NavmeshIns; 52 | } 53 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Public/ExportNavRuntime.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FExportNavRuntimeModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Public/ExternRecastNavMeshGenetator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "Modules/ModuleManager.h" 6 | #include "Navmesh/RecastNavMeshGenerator.h" 7 | 8 | struct FExternRecastGeometryCache 9 | { 10 | 11 | struct FHeader 12 | { 13 | FNavigationRelevantData::FCollisionDataHeader Validation; 14 | 15 | int32 NumVerts; 16 | int32 NumFaces; 17 | struct FWalkableSlopeOverride SlopeOverride; 18 | 19 | static uint32 StaticMagicNumber; 20 | }; 21 | 22 | FHeader Header; 23 | 24 | /** recast coords of vertices (size: NumVerts * 3) */ 25 | dtReal* Verts; 26 | 27 | /** vert indices for triangles (size: NumFaces * 3) */ 28 | int32* Indices; 29 | 30 | FExternRecastGeometryCache() {} 31 | FExternRecastGeometryCache(const uint8* Memory); 32 | 33 | static bool IsValid(const uint8* Memory, int32 MemorySize); 34 | }; 35 | 36 | UENUM(BlueprintType) 37 | enum EExportMode 38 | { 39 | Metre, 40 | Centimeter 41 | }; 42 | 43 | class EXPORTNAVRUNTIME_API FExternExportNavMeshGenerator : public FRecastNavMeshGenerator 44 | { 45 | public: 46 | void ExternExportNavigationData(const FString& FileName,EExportMode InExportMode); 47 | 48 | static FVector ChangeDirectionOfPoint(FVector Coord); 49 | void GrowConvexHull(const dtReal ExpandBy, const TArray& Verts, TArray& OutResult); 50 | void TransformVertexSoupToRecast(const TArray& VertexSoup, TNavStatArray& Verts, TNavStatArray& Faces); 51 | void ExportGeomToOBJFile(const FString& InFileName, const TNavStatArray& GeomCoords, const TNavStatArray& GeomFaces, const FString& AdditionalData); 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Public/FlibExportNavData.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "ExternRecastNavMeshGenetator.h" 5 | #include "UE4RecastHelper.h" 6 | 7 | #include "Detour/DetourNavMesh.h" 8 | #include "Detour/DetourNavMeshQuery.h" 9 | #include "Misc/Paths.h" 10 | #include "CoreMinimal.h" 11 | #include "Kismet/BlueprintFunctionLibrary.h" 12 | #include "FLibExportNavData.generated.h" 13 | 14 | /** 15 | * 16 | */ 17 | UCLASS() 18 | class EXPORTNAVRUNTIME_API UFlibExportNavData : public UBlueprintFunctionLibrary 19 | { 20 | GENERATED_BODY() 21 | public: 22 | // Editor only 23 | // UFUNCTION(Exec,BlueprintCallable,Category="ExportNav") 24 | static bool ExportRecastNavMesh(const FString& SavePath,EExportMode InExportMode); 25 | // Editor and Runtime 26 | // UFUNCTION(Exec,BlueprintCallable,Category="ExportNav") 27 | static bool ExportRecastNavData(const FString& InFilePath); 28 | 29 | static dtNavMesh* GetdtNavMeshInsByWorld(UWorld* InWorld); 30 | 31 | UFUNCTION(BlueprintCallable, BlueprintPure,Category="ExportNav", meta = (WorldContext = "WorldContextObject")) 32 | static bool IsValidNavigvationPointInWorld(UObject* WorldContextObject, const FVector& Point, const FVector InExtern = FVector::ZeroVector); 33 | UFUNCTION(BlueprintCallable, BlueprintPure,Category="ExportNav") 34 | static bool IsValidNavigationPointInNavbin(const FString& InNavBinPath, const FVector& Point, const FVector InExtern = FVector::ZeroVector); 35 | UFUNCTION(BlueprintCallable, BlueprintPure,Category="ExportNav") 36 | static bool IsValidNavigationPointInNavObj(class UdtNavMeshWrapper* InDtNavObject ,const FVector& Point, const FVector InExtern = FVector::ZeroVector); 37 | 38 | UFUNCTION(BlueprintCallable,meta=(AutoCreateRefTerm="InStart,InEnd")) 39 | static bool FindDetourPathFromGameAxisByNavObject(class UdtNavMeshWrapper* InDtNavObject, const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths); 40 | UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "InStart,InEnd")) 41 | static bool FindDetourPathFromRecastAxisByNavObject(class UdtNavMeshWrapper* InDtNavObject, const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths); 42 | UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "InStart,InEnd")) 43 | static bool FindDetourPathByEngineNavMesh(const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths); 44 | UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "InOrigin,InRedius")) 45 | static bool GetRandomPointByNavObject(class UdtNavMeshWrapper* InDtNavObject, const FVector& InOrigin, const FVector& InRedius, FVector& OutPoint); 46 | 47 | static bool FindDetourPathByRecastAxis(dtNavMesh* InNavMesh, const FVector& InStart, const FVector& InEnd, const FVector& ExternSize, TArray& OutPaths); 48 | 49 | static bool FindDetourPathByGameAxis(dtNavMesh* InNavMesh, const FVector& InStart, const FVector& InEnd, const FVector& InExternSize, TArray& OutPaths); 50 | 51 | 52 | static FString ConvPath_Slash2BackSlash(const FString& InPath); 53 | 54 | UFUNCTION(BlueprintCallable) 55 | static FVector Recast2UnrealPoint(const FVector& Vector); 56 | UFUNCTION(BlueprintCallable) 57 | static FVector Unreal2RecastPoint(const FVector& Vector); 58 | 59 | 60 | UFUNCTION(BlueprintCallable,meta=(WorldContext="WorldContextObject",CallableWithoutWorldContext)) 61 | static float FindDistanceToWall(UObject* WorldContextObject, const FVector& StartLoc); 62 | }; 63 | -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Public/HACK_PRIVATE_MEMBER_UTILS.hpp: -------------------------------------------------------------------------------- 1 | // #include 2 | // using namespace std; 3 | namespace Hacker 4 | { 5 | template 6 | struct PrivateMemberStealer 7 | { 8 | // define friend funtion GetPrivate,return class member pointer 9 | friend typename Tag::MemType GetPrivate(Tag) { return M; } 10 | }; 11 | } 12 | #define DECL_HACK_PRIVATE_DATA(ClassName,DataType,DataName) namespace Hacker{\ 13 | struct ClassName##_##DataName\ 14 | {\ 15 | typedef DataType ClassName::*MemType;\ 16 | friend MemType GetPrivate(ClassName##_##DataName);\ 17 | };\ 18 | template struct PrivateMemberStealer;\ 19 | } 20 | #define GET_VAR_PRIVATE_DATA_MEMBER(ClassInstancePointer,ClassName,DataName) ClassInstancePointer->*GetPrivate(::Hacker::ClassName##_##DataName()) 21 | #define GET_REF_PRIVATE_DATA_MEMBER(RefName,ClassInstancePointer,ClassName,DataName) auto& RefName = ClassInstancePointer->*GetPrivate(::Hacker::ClassName##_##DataName()) 22 | 23 | 24 | #define DECL_HACK_PRIVATE_NOCONST_FUNCTION(ClassName,MemberName,ReturnType,...) \ 25 | namespace Hacker{\ 26 | struct ClassName##_##MemberName \ 27 | {\ 28 | typedef ReturnType (ClassName::*MemType)(__VA_ARGS__);\ 29 | friend MemType GetPrivate(ClassName##_##MemberName);\ 30 | };\ 31 | template struct PrivateMemberStealer;\ 32 | } 33 | #define DECL_HACK_PRIVATE_CONST_FUNCTION(ClassName,MemberName,ReturnType,...) \ 34 | namespace Hacker{\ 35 | struct ClassName##_##MemberName \ 36 | {\ 37 | typedef ReturnType (ClassName::*MemType)(__VA_ARGS__)const;\ 38 | friend MemType GetPrivate(ClassName##_##MemberName);\ 39 | };\ 40 | template struct PrivateMemberStealer;\ 41 | } 42 | 43 | // using ADL found to ::Hacker::Getprivate 44 | #define GET_PRIVATE_MEMBER_FUNCTION(ClassName,MemberName) GetPrivate(::Hacker::ClassName##_##MemberName()) 45 | #define CALL_MEMBER_FUNCTION(ClassPointer,MemberFuncPointer,...) (ClassPointer->*MemberFuncPointer)(__VA_ARGS__) 46 | 47 | // class A{ 48 | // public: 49 | // A(int ivalp=0):ival{ivalp}{} 50 | // private: 51 | // int func(int ival)const 52 | // { 53 | // printf("A::func(int)\t%d\n",ival); 54 | // printf("ival is %d\n",ival); 55 | // return 0; 56 | // } 57 | // int ival; 58 | // }; 59 | 60 | // DECL_HACK_PRIVATE_DATA(A,int,ival) 61 | // DECL_HACK_PRIVATE_CONST_FUNCTION(A, func, int, int) 62 | 63 | // int main() 64 | // { 65 | // A aobj(789); 66 | // // get private data member 67 | // GET_REF_PRIVATE_DATA_MEMBER(ref_ival, &aobj, A, ival); 68 | // ref_ival=456; 69 | // cout< 10 | #include 11 | #include 12 | 13 | namespace UE4RecastHelper 14 | { 15 | struct NavMeshSetHeader 16 | { 17 | int32_t magic; 18 | int32_t version; 19 | int32_t numTiles; 20 | dtNavMeshParams params; 21 | }; 22 | 23 | struct NavMeshTileHeader 24 | { 25 | dtTileRef tileRef; 26 | int32_t dataSize; 27 | }; 28 | 29 | static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET'; 30 | static const int NAVMESHSET_VERSION = 1; 31 | 32 | struct FVector3 33 | { 34 | dtReal X; 35 | dtReal Y; 36 | dtReal Z; 37 | public: 38 | inline FVector3() :X(0.f), Y(0.f), Z(0.f) {} 39 | inline FVector3(dtReal* InV) : X(InV[0]), Y(InV[1]), Z(InV[2]) {} 40 | inline FVector3(dtReal px, dtReal py, dtReal pz) : X(px), Y(py), Z(pz) {} 41 | FVector3(const FVector3&) = default; 42 | 43 | inline FVector3 operator-(const FVector3& V) const { 44 | return FVector3(X - V.X, Y - V.Y, Z - V.Z); 45 | } 46 | inline FVector3 operator+(const FVector3& V)const { 47 | return FVector3(X + V.X, Y + V.Y, Z + V.Z); 48 | } 49 | inline FVector3 operator-(const dtReal& V)const { 50 | return FVector3(X - V, Y - V, Z - V); 51 | } 52 | inline FVector3 operator+(const dtReal& V)const { 53 | return FVector3(X + V, Y + V, Z + V); 54 | } 55 | inline FVector3 GetAbs()const 56 | { 57 | return FVector3{ fabsf(X),fabsf(Y),fabsf(Z) }; 58 | } 59 | #ifdef USE_DETOUR_BUILT_INTO_UE4 60 | inline FVector3(FVector InUE4Vector) :X(InUE4Vector.X), Y(InUE4Vector.Y), Z(InUE4Vector.Z) {} 61 | 62 | inline FVector UE4Vector()const 63 | { 64 | return FVector{ X,Y,Z }; 65 | } 66 | #endif 67 | }; 68 | 69 | FVector3 Recast2UnrealPoint(const FVector3& Vector); 70 | FVector3 Unreal2RecastPoint(const FVector3& Vector); 71 | 72 | void SerializedtNavMesh(const char* path, const dtNavMesh* mesh); 73 | dtNavMesh* DeSerializedtNavMesh(const char* path); 74 | 75 | static int findStraightPath(dtNavMesh* InNavMeshData, dtNavMeshQuery* InNavmeshQuery, const FVector3& start, const FVector3& end, std::vector& paths); 76 | static bool dtIsValidNavigationPoint(dtNavMesh* InNavMeshData, const FVector3& InPoint, const FVector3& InExtent = FVector3{ 10.f,10.f,10.f }); 77 | static bool GetRandomPointInRadius(dtNavMeshQuery* InNavmeshQuery, dtQueryFilter* InQueryFilter,const FVector3& InOrigin,const FVector3& InRedius,FVector3& OutPoint); 78 | }; 79 | 80 | 81 | #endif -------------------------------------------------------------------------------- /Source/ExportNavRuntime/Public/dtNavMeshWrapper.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Lipeng Zha, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/NoExportTypes.h" 7 | #include "Detour/DetourNavMesh.h" 8 | #include "dtNavMeshWrapper.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS(BlueprintType,Blueprintable) 14 | class EXPORTNAVRUNTIME_API UdtNavMeshWrapper : public UObject 15 | { 16 | GENERATED_UCLASS_BODY() 17 | 18 | virtual ~UdtNavMeshWrapper()override; 19 | 20 | UFUNCTION(BlueprintCallable,Category="ExportNav") 21 | UdtNavMeshWrapper* LoadNavData(const FString& NavDataBinPath=TEXT("")); 22 | UFUNCTION(BlueprintCallable,Category="ExportNav") 23 | bool IsAvailableNavData()const; 24 | 25 | dtNavMesh* GetNavData()const; 26 | private: 27 | dtNavMesh* NavmeshIns; 28 | }; 29 | --------------------------------------------------------------------------------