commit 55fe2a8d29257b07636f230e95101a8b25181191 Author: FishOrBear Date: Wed Jun 30 17:11:29 2021 +0800 可用 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8c5502 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +Binaries +.vs +.idea +.history +node_modules +Intermediate +DerivedDataCache +Build +Saved +/Content/JavaScript +/Content/Typing +/Content/TypeScript +/Content/Developers +/Content/Development + +/Content/Data +/Content/project_ok +/Content/Blueprints + +ArtistRenderer.sln +*.pyc +package-lock.json +/dpaks diff --git a/B.bat b/B.bat new file mode 100644 index 0000000..6ee25cf --- /dev/null +++ b/B.bat @@ -0,0 +1,11 @@ +@echo off +rem 编译Shipping版本,打包pak +set BuildConfig="Shipping" +set UprojectPath="%~dp0%wjj%.uproject" +set DebugPath="%~dp0Build/%BuildConfig%" +D:\UnrealEngine-5.0.0-early-access-2\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project=%UprojectPath%^ + -nocompileeditor^ + -noP4 -platform=Win64^ + -clientconfig=%BuildConfig%^ + -cook -allmaps -build -stage^ + -pak -stage -archive -archivedirectory="%~dp0/Build/%BuildConfig%" diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Config/DefaultEditor.ini @@ -0,0 +1 @@ + diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini new file mode 100644 index 0000000..a8c2e13 --- /dev/null +++ b/Config/DefaultEngine.ini @@ -0,0 +1,43 @@ + + +[/Script/EngineSettings.GameMapsSettings] +GameDefaultMap=/Game/NewMap.NewMap +EditorStartupMap=/Game/NewMap.NewMap +GameInstanceClass=/Script/SMC_Build.SMCGameInstance + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/Engine.RendererSettings] +r.GenerateMeshDistanceFields=True +r.DynamicGlobalIlluminationMethod=1 +r.ReflectionMethod=1 + +r.SkinCache.CompileShaders=True + +r.RayTracing=True + +r.Shadow.Virtual.Enable=1 +r.Lumen.HardwareRayTracing=True +r.MinScreenRadiusForLights=500.000000 +r.RayTracing.Shadows=True +r.AllowStaticLighting=False +r.DefaultFeature.LightUnits=2 +r.DefaultFeature.AutoExposure=False + +[/Script/WindowsTargetPlatform.WindowsTargetSettings] +DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 + +[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] +bEnableWorldPartition=False +bEnableConversionPrompt=True +CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/SMC_Build") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/SMC_Build") ++ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="SMC_BuildGameModeBase") + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini new file mode 100644 index 0000000..23311f9 --- /dev/null +++ b/Config/DefaultGame.ini @@ -0,0 +1,7 @@ + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=DFD3A2844A4CA1EAF134B5968BDC572F + +[/Script/UnrealEd.ProjectPackagingSettings] +IncludeDebugFiles=True + diff --git a/Content/NewMap.umap b/Content/NewMap.umap new file mode 100644 index 0000000..0fe1e87 Binary files /dev/null and b/Content/NewMap.umap differ diff --git a/Content/NewMap1.umap b/Content/NewMap1.umap new file mode 100644 index 0000000..8d8b27e Binary files /dev/null and b/Content/NewMap1.umap differ diff --git a/Content/NewMap_BuiltData.uasset b/Content/NewMap_BuiltData.uasset new file mode 100644 index 0000000..bf4ab13 Binary files /dev/null and b/Content/NewMap_BuiltData.uasset differ diff --git a/Content/UV_Grid_Sm.uasset b/Content/UV_Grid_Sm.uasset new file mode 100644 index 0000000..4393c1c Binary files /dev/null and b/Content/UV_Grid_Sm.uasset differ diff --git a/Content/UV_Grid_Sm_Mat.uasset b/Content/UV_Grid_Sm_Mat.uasset new file mode 100644 index 0000000..1ec7460 Binary files /dev/null and b/Content/UV_Grid_Sm_Mat.uasset differ diff --git a/Content/aaa.uasset b/Content/aaa.uasset new file mode 100644 index 0000000..9d33abc Binary files /dev/null and b/Content/aaa.uasset differ diff --git a/Content/baise.uasset b/Content/baise.uasset new file mode 100644 index 0000000..d28fe4a Binary files /dev/null and b/Content/baise.uasset differ diff --git a/Content/file__1_.uasset b/Content/file__1_.uasset new file mode 100644 index 0000000..e9695ae Binary files /dev/null and b/Content/file__1_.uasset differ diff --git a/Content/webcad.uasset b/Content/webcad.uasset new file mode 100644 index 0000000..d2a4fb9 Binary files /dev/null and b/Content/webcad.uasset differ diff --git a/D.bat b/D.bat new file mode 100644 index 0000000..07fe219 --- /dev/null +++ b/D.bat @@ -0,0 +1,17 @@ +@echo off + +set "lj=%~p0" +set "lj=%lj:\= %" +for %%a in (%lj%) do set wjj=%%a + +rem 编译DebugGame的版本,跳过烘焙,打包pak +set BuildConfig=DebugGame +set UprojectPath="%~dp0%wjj%.uproject" +set DebugPath="%~dp0Build\%BuildConfig%" +D:\UnrealEngine-5.0.0-early-access-2\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project=%UprojectPath%^ + -nocompileeditor^ + -noP4 -platform=Win64^ + -clientconfig=%BuildConfig%^ + -build -utf8output -compile -ForceDebugInfo^ + -SkipCook^ + -pak -stage -archive -archivedirectory="%DebugPath%" diff --git a/DC.bat b/DC.bat new file mode 100644 index 0000000..c1442d4 --- /dev/null +++ b/DC.bat @@ -0,0 +1,16 @@ +@echo off + +set "lj=%~p0" +set "lj=%lj:\= %" +for %%a in (%lj%) do set wjj=%%a + +rem 编译DebugGame的版本,跳过烘焙,打包pak +set BuildConfig=DebugGame +set UprojectPath="%~dp0%wjj%.uproject" +set DebugPath="%~dp0Build\%BuildConfig%" +D:\UnrealEngine-5.0.0-early-access-2\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project=%UprojectPath%^ + -nocompileeditor^ + -noP4 -platform=Win64^ + -clientconfig=%BuildConfig%^ + -cook -allmaps -build -stage^ + -pak -stage -archive -archivedirectory=%DebugPath% diff --git a/Plugins/MeshUtilities2/MeshUtilities2.uplugin b/Plugins/MeshUtilities2/MeshUtilities2.uplugin new file mode 100644 index 0000000..d8d3170 --- /dev/null +++ b/Plugins/MeshUtilities2/MeshUtilities2.uplugin @@ -0,0 +1,24 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "MeshUtilities2", + "Description": "", + "Category": "Other", + "CreatedBy": "ChenX", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "MeshUtilities2", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/Plugins/MeshUtilities2/Resources/Icon128.png b/Plugins/MeshUtilities2/Resources/Icon128.png new file mode 100644 index 0000000..1231d4a Binary files /dev/null and b/Plugins/MeshUtilities2/Resources/Icon128.png differ diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/MeshUtilities2.Build.cs b/Plugins/MeshUtilities2/Source/MeshUtilities2/MeshUtilities2.Build.cs new file mode 100644 index 0000000..61a8f0c --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/MeshUtilities2.Build.cs @@ -0,0 +1,88 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class MeshUtilities2 : ModuleRules +{ + public MeshUtilities2(ReadOnlyTargetRules Target) : base(Target) + { + PublicDependencyModuleNames.AddRange( + new string[] + { + // "MaterialUtilities", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "RawMesh", + "RenderCore", // For FPackedNormal + "SlateCore", + "Slate", + // "MaterialUtilities", + // "MeshBoneReduction", + // "EditorFramework", + // "UnrealEd", + "RHI", + //"HierarchicalLODUtilities", + "Landscape", + // "LevelEditor", + // "PropertyEditor", + // "EditorStyle", + // "GraphColor", + // "MeshBuilderCommon", + "MeshUtilitiesCommon", + "MeshDescription", + "StaticMeshDescription", + // "ToolMenus", + } + ); + + PublicIncludePathModuleNames.AddRange( + new string[] + { + "MeshMergeUtilities" + } + ); + + PrivateIncludePathModuleNames.AddRange( + new string[] + { + // "AnimationBlueprintEditor", + // "AnimationEditor", + // "MeshMergeUtilities", + // "MaterialBaking", + // "Persona", + // "SkeletalMeshEditor", + } + ); + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // "AnimationBlueprintEditor", + // "AnimationEditor", + // "MeshMergeUtilities", + // "MaterialBaking", + // "SkeletalMeshEditor", + } + ); + + AddEngineThirdPartyPrivateStaticDependencies(Target, "nvTriStrip"); + AddEngineThirdPartyPrivateStaticDependencies(Target, "ForsythTriOptimizer"); + // AddEngineThirdPartyPrivateStaticDependencies(Target, "QuadricMeshReduction"); + AddEngineThirdPartyPrivateStaticDependencies(Target, "MikkTSpace"); + AddEngineThirdPartyPrivateStaticDependencies(Target, "nvTessLib"); + + if (Target.Platform == UnrealTargetPlatform.Win64) + { + AddEngineThirdPartyPrivateStaticDependencies(Target, "DX9"); + } + + AddEngineThirdPartyPrivateStaticDependencies(Target, "Embree3"); + } +} diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/DistanceFieldAtlas2.cpp b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/DistanceFieldAtlas2.cpp new file mode 100644 index 0000000..3df1204 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/DistanceFieldAtlas2.cpp @@ -0,0 +1,598 @@ +#include "DistanceFieldAtlas2.h" +// #include "DistanceFieldAtlas.h" +// #include "HAL/RunnableThread.h" +// #include "HAL/Runnable.h" +// #include "Misc/App.h" +// #include "Serialization/MemoryReader.h" +// #include "Serialization/MemoryWriter.h" +#include "Modules/ModuleManager.h" +#include "StaticMeshResources.h" +#include "ProfilingDebugging/CookStats.h" +#include "Templates/UniquePtr.h" +#include "Engine/StaticMesh.h" +#include "Misc/AutomationTest.h" +// #include "Async/ParallelFor.h" +// #include "DistanceFieldDownsampling.h" +// #include "GlobalShader.h" +#include "RenderGraph.h" +#include "MeshCardRepresentation.h" +#include "Misc/QueuedThreadPoolWrapper.h" +// #include "Async/Async.h" +#include "ObjectCacheContext.h" + +// #if WITH_EDITOR +// #include "DerivedDataCacheInterface.h" +#include "Engine/Public/DistanceFieldAtlas.h" + +// #include "AssetCompilingManager.h" +#include "MeshCardRepresentation2.h" +#include "StaticMeshCompiler.h" +#include "MeshUtilities2/Public/MeshUtilities2.h" +// #endif + +CSV_DEFINE_CATEGORY(DistanceField2, false); + +#if ENABLE_COOK_STATS +namespace DistanceFieldCookStats +{ + FCookStats::FDDCResourceUsageStats UsageStats; + static FCookStatsManager::FAutoRegisterCallback RegisterCookStats([](FCookStatsManager::AddStatFuncRef AddStat) + { + UsageStats.LogStats(AddStat, TEXT("DistanceField2.Usage"), TEXT("")); + }); +} +#endif + + +FDistanceFieldAsyncQueue2* GDistanceFieldAsyncQueue2 = nullptr; + +int32 GUseAsyncDistanceFieldBuildQueue2 = 1; +static FAutoConsoleVariableRef CVarAOAsyncBuildQueue( + TEXT("r.AOAsyncBuildQueue2"), + GUseAsyncDistanceFieldBuildQueue2, + TEXT("是否从网格异步构建距离场体数据。"), + ECVF_Default | ECVF_ReadOnly +); + +//构建距离场 +void BuildMeshDistanceField(UStaticMesh* StaticMesh) +{ + //ref:StaticMesh.cpp 2782 + auto RenderData = StaticMesh->GetRenderData(); + if (RenderData->LODResources.IsValidIndex(0)) + { + auto& LODResource = RenderData->LODResources[0]; + if (!LODResource.DistanceFieldData) + { + LODResource.DistanceFieldData = new FDistanceFieldVolumeData(); + LODResource.DistanceFieldData->AssetName = StaticMesh->GetFName(); + } + + // We don't actually build the resource until later, so only track the cycles used here. + // COOK_STAT(Timer.TrackCyclesOnly()); + FAsyncDistanceFieldTask2* NewTask = new FAsyncDistanceFieldTask2; + NewTask->DDCKey = ""; + // check(Mesh && GenerateSource); + // NewTask->TargetPlatform = RunningPlatform; + NewTask->StaticMesh = StaticMesh; + NewTask->GenerateSource = StaticMesh; //GenerateSource; + NewTask->DistanceFieldResolutionScale = 2; //DistanceFieldResolutionScale; + NewTask->bGenerateDistanceFieldAsIfTwoSided = false; // bGenerateDistanceFieldAsIfTwoSided; + NewTask->GeneratedVolumeData = new FDistanceFieldVolumeData(); //;给一个新的用于存放 + NewTask->GeneratedVolumeData->AssetName = StaticMesh->GetFName(); + NewTask->GeneratedVolumeData->bAsyncBuilding = true; + + for (int32 MaterialIndex = 0; MaterialIndex < StaticMesh->GetStaticMaterials().Num(); MaterialIndex++) + { + FSignedDistanceFieldBuildMaterialData2 MaterialData; + // Default material blend mode + MaterialData.BlendMode = BLEND_Opaque; + MaterialData.bTwoSided = false; + + if (StaticMesh->GetStaticMaterials()[MaterialIndex].MaterialInterface) + { + MaterialData.BlendMode = StaticMesh->GetStaticMaterials()[MaterialIndex].MaterialInterface->GetBlendMode(); + MaterialData.bTwoSided = StaticMesh->GetStaticMaterials()[MaterialIndex].MaterialInterface->IsTwoSided(); + } + + NewTask->MaterialBlendModes.Add(MaterialData); + } + + // // Nanite overrides source static mesh with a coarse representation. Need to load original data before we build the mesh SDF. + // if (StaticMesh->NaniteSettings.bEnabled) + // { + // IMeshBuilderModule& MeshBuilderModule = IMeshBuilderModule::GetForPlatform(TargetPlatform); + // if (!MeshBuilderModule.BuildMeshVertexPositions(Mesh, NewTask->SourceMeshData.TriangleIndices, NewTask->SourceMeshData.VertexPositions)) + // { + // UE_LOG(LogStaticMesh, Error, TEXT("Failed to build static mesh. See previous line(s) for details.")); + // } + // } + + GDistanceFieldAsyncQueue2->AddTask(NewTask); + } +} + +void BuildMeshCardRepresentation(UStaticMesh* StaticMeshAsset, FStaticMeshRenderData& RenderData, FSourceMeshDataForDerivedDataTask* OptionalSourceMeshData) +{ + if (RenderData.LODResources.IsValidIndex(0)) + { + if (!RenderData.LODResources[0].CardRepresentationData) + { + RenderData.LODResources[0].CardRepresentationData = new FCardRepresentationData(); + } + + // const FMeshBuildSettings& BuildSettings = StaticMeshAsset->GetSourceModel(0).BuildSettings; + // UStaticMesh* MeshToGenerateFrom = StaticMeshAsset; + + // We don't actually build the resource until later, so only track the cycles used here. + // COOK_STAT(Timer.TrackCyclesOnly()); + FAsyncCardRepresentationTask2* NewTask = new FAsyncCardRepresentationTask2; + // NewTask->DDCKey = InDDCKey; + check(StaticMeshAsset); + NewTask->StaticMesh = StaticMeshAsset; + NewTask->GenerateSource = StaticMeshAsset; + NewTask->GeneratedCardRepresentation = new FCardRepresentationData(); + NewTask->bGenerateDistanceFieldAsIfTwoSided = false; + + for (int32 MaterialIndex = 0; MaterialIndex < StaticMeshAsset->GetStaticMaterials().Num(); MaterialIndex++) + { + FSignedDistanceFieldBuildMaterialData2 MaterialData; + // Default material blend mode + MaterialData.BlendMode = BLEND_Opaque; + MaterialData.bTwoSided = false; + + if (StaticMeshAsset->GetStaticMaterials()[MaterialIndex].MaterialInterface) + { + MaterialData.BlendMode = StaticMeshAsset->GetStaticMaterials()[MaterialIndex].MaterialInterface->GetBlendMode(); + MaterialData.bTwoSided = StaticMeshAsset->GetStaticMaterials()[MaterialIndex].MaterialInterface->IsTwoSided(); + } + + NewTask->MaterialBlendModes.Add(MaterialData); + } + + // Nanite overrides source static mesh with a coarse representation. Need to load original data before we build the mesh SDF. + if (OptionalSourceMeshData) + { + NewTask->SourceMeshData = *OptionalSourceMeshData; + } + // else if (StaticMeshAsset->NaniteSettings.bEnabled) + // { + // IMeshBuilderModule& MeshBuilderModule = IMeshBuilderModule::GetForPlatform(TargetPlatform); + // if (!MeshBuilderModule.BuildMeshVertexPositions(Mesh, NewTask->SourceMeshData.TriangleIndices, NewTask->SourceMeshData.VertexPositions)) + // { + // UE_LOG(LogStaticMesh, Error, TEXT("Failed to build static mesh. See previous line(s) for details.")); + // } + // } + + GCardRepresentationAsyncQueue2->AddTask(NewTask); + } +} + + +FAsyncDistanceFieldTask2::FAsyncDistanceFieldTask2() + : StaticMesh(nullptr) + , GenerateSource(nullptr) + , DistanceFieldResolutionScale(0.0f) + , bGenerateDistanceFieldAsIfTwoSided(false) + , GeneratedVolumeData(nullptr) +{ +} + +FDistanceFieldAsyncQueue2::FDistanceFieldAsyncQueue2() +{ + MeshUtilities = NULL; + // #if WITH_EDITOR + // const int32 MaxConcurrency = -1; + // // In Editor, we allow faster compilation by letting the asset compiler's scheduler organize work. + // ThreadPool = MakeUnique(FAssetCompilingManager::Get().GetThreadPool(), MaxConcurrency, [](EQueuedWorkPriority) { return EQueuedWorkPriority::Lowest; }); + // #else + const int32 MaxConcurrency = -1; + ThreadPool = MakeUnique(GThreadPool, MaxConcurrency, [](EQueuedWorkPriority) { return EQueuedWorkPriority::Lowest; }); + // #endif +} + +FDistanceFieldAsyncQueue2::~FDistanceFieldAsyncQueue2() +{ +} + +void FAsyncDistanceFieldTaskWorker2::DoWork() +{ + // Put on background thread to avoid interfering with game-thread bound tasks + FQueuedThreadPoolTaskGraphWrapper TaskGraphWrapper(ENamedThreads::AnyBackgroundThreadNormalTask); + GDistanceFieldAsyncQueue2->Build(&Task, TaskGraphWrapper); +} + +void FDistanceFieldAsyncQueue2::CancelBackgroundTask(TArray Tasks) +{ + // Do all the cancellation first to make sure none of these tasks + // get scheduled as we're waiting for completion. + for (FAsyncDistanceFieldTask2* Task : Tasks) + { + if (Task->AsyncTask) + { + Task->AsyncTask->Cancel(); + } + } + + for (FAsyncDistanceFieldTask2* Task : Tasks) + { + if (Task->AsyncTask) + { + Task->AsyncTask->EnsureCompletion(); + Task->AsyncTask.Reset(); + } + } +} + +void FDistanceFieldAsyncQueue2::StartBackgroundTask(FAsyncDistanceFieldTask2* Task) +{ + check(Task->AsyncTask == nullptr); + Task->AsyncTask = MakeUnique>(*Task); + Task->AsyncTask->StartBackgroundTask(ThreadPool.Get(), EQueuedWorkPriority::Lowest); +} + +void FDistanceFieldAsyncQueue2::ProcessPendingTasks() +{ + FScopeLock Lock(&CriticalSection); + TArray Tasks = MoveTemp(PendingTasks); + for (FAsyncDistanceFieldTask2* Task : Tasks) + { + if (Task->GenerateSource && Task->GenerateSource->IsCompiling()) + { + PendingTasks.Add(Task); + } + else + { + StartBackgroundTask(Task); + } + } +} + +void FDistanceFieldAsyncQueue2::AddTask(FAsyncDistanceFieldTask2* Task) +{ + // #if WITH_EDITOR + if (!MeshUtilities) + { + MeshUtilities = &FModuleManager::Get().LoadModuleChecked(TEXT("MeshUtilities2")); + } + + { + // Array protection when called from multiple threads + FScopeLock Lock(&CriticalSection); + ReferencedTasks.Add(Task); + } + + // The Source Mesh's RenderData is not yet ready, postpone the build + if (Task->GenerateSource->IsCompiling()) + { + // Array protection when called from multiple threads + FScopeLock Lock(&CriticalSection); + PendingTasks.Add(Task); + } + else + { + // If we're already in worker threads, there is no need to launch an async task. + if (GUseAsyncDistanceFieldBuildQueue2 || !IsInGameThread()) + { + StartBackgroundTask(Task); + } + else + { + // To avoid deadlocks, we must queue the inner build tasks on another thread pool, so use the task graph. + // Put on background thread to avoid interfering with game-thread bound tasks + FQueuedThreadPoolTaskGraphWrapper TaskGraphWrapper(ENamedThreads::AnyBackgroundThreadNormalTask); + Build(Task, TaskGraphWrapper); + } + } + // #else + // UE_LOG(LogStaticMesh,Fatal,TEXT("Tried to build a distance field without editor support (this should have been done during cooking)")); + // #endif +} + +void FDistanceFieldAsyncQueue2::CancelBuild(UStaticMesh* StaticMesh) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FDistanceFieldAsyncQueue2::CancelBuild) + + TArray TasksToCancel; + { + FScopeLock Lock(&CriticalSection); + TArray Tasks = MoveTemp(PendingTasks); + PendingTasks.Reserve(Tasks.Num()); + for (FAsyncDistanceFieldTask2* Task : Tasks) + { + if (Task->GenerateSource != StaticMesh && Task->StaticMesh != StaticMesh) + { + PendingTasks.Add(Task); + } + } + + Tasks = MoveTemp(ReferencedTasks); + ReferencedTasks.Reserve(Tasks.Num()); + for (FAsyncDistanceFieldTask2* Task : Tasks) + { + if (Task->GenerateSource != StaticMesh && Task->StaticMesh != StaticMesh) + { + ReferencedTasks.Add(Task); + } + else + { + TasksToCancel.Add(Task); + } + } + } + + CancelBackgroundTask(TasksToCancel); + for (FAsyncDistanceFieldTask2* Task : TasksToCancel) + { + if (Task->GeneratedVolumeData != nullptr) + { + // Rendering thread may still be referencing the old one, use the deferred cleanup interface to delete it next frame when it is safe + BeginCleanup(Task->GeneratedVolumeData); + } + + delete Task; + } +} + +void FDistanceFieldAsyncQueue2::CancelAllOutstandingBuilds() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FDistanceFieldAsyncQueue2::CancelAllOutstandingBuilds) + + TArray OutstandingTasks; + { + FScopeLock Lock(&CriticalSection); + PendingTasks.Empty(); + OutstandingTasks = MoveTemp(ReferencedTasks); + } + + CancelBackgroundTask(OutstandingTasks); + for (FAsyncDistanceFieldTask2* Task : OutstandingTasks) + { + delete Task; + } +} + +void FDistanceFieldAsyncQueue2::RescheduleBackgroundTask(FAsyncDistanceFieldTask2* InTask, EQueuedWorkPriority InPriority) +{ + if (InTask->AsyncTask) + { + if (InTask->AsyncTask->GetPriority() != InPriority) + { + InTask->AsyncTask->Reschedule(GThreadPool, InPriority); + } + } +} + +void FDistanceFieldAsyncQueue2::BlockUntilBuildComplete(UStaticMesh* StaticMesh, bool bWarnIfBlocked) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FDistanceFieldAsyncQueue2::BlockUntilBuildComplete) + + // We will track the wait time here, but only the cycles used. + // This function is called whether or not an async task is pending, + // so we have to look elsewhere to properly count how many resources have actually finished building. + COOK_STAT(auto Timer = DistanceFieldCookStats::UsageStats.TimeAsyncWait()); + COOK_STAT(Timer.TrackCyclesOnly()); + bool bReferenced = false; + bool bHadToBlock = false; + double StartTime = 0; + + TSet RequiredFinishCompilation; + do + { + ProcessAsyncTasks(); + + bReferenced = false; + RequiredFinishCompilation.Reset(); + + // Reschedule the tasks we're waiting on as highest prio + { + FScopeLock Lock(&CriticalSection); + for (int TaskIndex = 0; TaskIndex < ReferencedTasks.Num(); TaskIndex++) + { + if (ReferencedTasks[TaskIndex]->StaticMesh == StaticMesh || + ReferencedTasks[TaskIndex]->GenerateSource == StaticMesh) + { + bReferenced = true; + + // If the task we are waiting on depends on other static meshes + // we need to force finish them too. + // #if WITH_EDITOR + + if (ReferencedTasks[TaskIndex]->GenerateSource != nullptr && + ReferencedTasks[TaskIndex]->GenerateSource->IsCompiling()) + { + RequiredFinishCompilation.Add(ReferencedTasks[TaskIndex]->GenerateSource); + } + + if (ReferencedTasks[TaskIndex]->StaticMesh != nullptr && + ReferencedTasks[TaskIndex]->StaticMesh->IsCompiling()) + { + RequiredFinishCompilation.Add(ReferencedTasks[TaskIndex]->StaticMesh); + } + // #endif + + RescheduleBackgroundTask(ReferencedTasks[TaskIndex], EQueuedWorkPriority::Highest); + } + } + } + +#if WITH_EDITOR + // Call the finish compilation outside of the critical section since those compilations + // might need to register new distance field tasks which also uses the critical section. + if (RequiredFinishCompilation.Num()) + { + FStaticMeshCompilingManager::Get().FinishCompilation(RequiredFinishCompilation.Array()); + } +#endif + + if (bReferenced) + { + if (!bHadToBlock) + { + StartTime = FPlatformTime::Seconds(); + } + + bHadToBlock = true; + FPlatformProcess::Sleep(.01f); + } + } + while (bReferenced); + + if (bHadToBlock && + bWarnIfBlocked +#if WITH_EDITOR + && !FAutomationTestFramework::Get().GetCurrentTest() // HACK - Don't output this warning during automation test +#endif + ) + { + UE_LOG(LogStaticMesh, Display, TEXT("Main thread blocked for %.3fs for async distance field build of %s to complete! This can happen if the mesh is rebuilt excessively."), + (float)(FPlatformTime::Seconds() - StartTime), + *StaticMesh->GetName()); + } +} + +void FDistanceFieldAsyncQueue2::BlockUntilAllBuildsComplete() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FDistanceFieldAsyncQueue2::BlockUntilAllBuildsComplete) + do + { +#if WITH_EDITOR + FStaticMeshCompilingManager::Get().FinishAllCompilation(); +#endif + // Reschedule as highest prio since we're explicitly waiting on them + { + FScopeLock Lock(&CriticalSection); + for (int TaskIndex = 0; TaskIndex < ReferencedTasks.Num(); TaskIndex++) + { + RescheduleBackgroundTask(ReferencedTasks[TaskIndex], EQueuedWorkPriority::Highest); + } + } + + ProcessAsyncTasks(); + FPlatformProcess::Sleep(.01f); + } + while (GetNumOutstandingTasks() > 0); +} + +void FDistanceFieldAsyncQueue2::Build(FAsyncDistanceFieldTask2* Task, FQueuedThreadPool& BuildThreadPool) +{ + //#if WITH_EDITOR + // Editor 'force delete' can null any UObject pointers which are seen by reference collecting (eg FProperty or serialized) + if (Task->StaticMesh && Task->GenerateSource) + { + TRACE_CPUPROFILER_EVENT_SCOPE(FDistanceFieldAsyncQueue2::Build); + + const FStaticMeshLODResources& LODModel = Task->GenerateSource->GetRenderData()->LODResources[0]; + MeshUtilities->GenerateSignedDistanceFieldVolumeData( + Task->StaticMesh->GetName(), + Task->SourceMeshData, + LODModel, + BuildThreadPool, + Task->MaterialBlendModes, + Task->GenerateSource->GetRenderData()->Bounds, + Task->DistanceFieldResolutionScale, + Task->bGenerateDistanceFieldAsIfTwoSided, + *Task->GeneratedVolumeData); + } + + CompletedTasks.Push(Task); + //#endif +} + +void FDistanceFieldAsyncQueue2::AddReferencedObjects(FReferenceCollector& Collector) +{ + FScopeLock Lock(&CriticalSection); + for (int TaskIndex = 0; TaskIndex < ReferencedTasks.Num(); TaskIndex++) + { + // Make sure none of the UObjects referenced by the async tasks are GC'ed during the task + Collector.AddReferencedObject(ReferencedTasks[TaskIndex]->StaticMesh); + Collector.AddReferencedObject(ReferencedTasks[TaskIndex]->GenerateSource); + } +} + +FString FDistanceFieldAsyncQueue2::GetReferencerName() const +{ + return TEXT("FDistanceFieldAsyncQueue2"); +} + +void FDistanceFieldAsyncQueue2::ProcessAsyncTasks(bool bLimitExecutionTime) +{ + // #if WITH_EDITOR + TRACE_CPUPROFILER_EVENT_SCOPE(FDistanceFieldAsyncQueue2::ProcessAsyncTasks); + + ProcessPendingTasks(); + + FObjectCacheContextScope ObjectCacheScope; + const double MaxProcessingTime = 0.016f; + double StartTime = FPlatformTime::Seconds(); + while (!bLimitExecutionTime || (FPlatformTime::Seconds() - StartTime) < MaxProcessingTime) + { + FAsyncDistanceFieldTask2* Task = CompletedTasks.Pop(); + if (Task == nullptr) + { + break; + } + + // We want to count each resource built from a DDC miss, so count each iteration of the loop separately. + COOK_STAT(auto Timer = DistanceFieldCookStats::UsageStats.TimeSyncWork()); + + bool bWasCancelled = false; + { + FScopeLock Lock(&CriticalSection); + bWasCancelled = ReferencedTasks.Remove(Task) == 0; + } + + if (bWasCancelled) + { + continue; + } + + if (Task->AsyncTask) + { + Task->AsyncTask->EnsureCompletion(); + Task->AsyncTask.Reset(); + } + + // Editor 'force delete' can null any UObject pointers which are seen by reference collecting (eg FProperty or serialized) + if (Task->StaticMesh) + { + Task->GeneratedVolumeData->bAsyncBuilding = false; + FDistanceFieldVolumeData* OldVolumeData = Task->StaticMesh->GetRenderData()->LODResources[0].DistanceFieldData; + + // Assign the new volume data, this is safe because the render thread makes a copy of the pointer at scene proxy creation time. + Task->StaticMesh->GetRenderData()->LODResources[0].DistanceFieldData = Task->GeneratedVolumeData; + + // Renderstates are not initialized between UStaticMesh::PreEditChange() and UStaticMesh::PostEditChange() + if (Task->StaticMesh->GetRenderData()->IsInitialized()) + { + for (UStaticMeshComponent* Component : ObjectCacheScope.GetContext().GetStaticMeshComponents(Task->StaticMesh)) + { + if (Component->IsRegistered() && Component->IsRenderStateCreated()) + { + Component->MarkRenderStateDirty(); + } + } + } + + if (OldVolumeData) + { + // Rendering thread may still be referencing the old one, use the deferred cleanup interface to delete it next frame when it is safe + BeginCleanup(OldVolumeData); + } + + //第二阶段 + BuildMeshCardRepresentation(Task->StaticMesh, *Task->StaticMesh->GetRenderData(), &Task->SourceMeshData); + } + + delete Task; + } + // #endif +} + +void FDistanceFieldAsyncQueue2::Shutdown() +{ + CancelAllOutstandingBuilds(); + + UE_LOG(LogStaticMesh, Log, TEXT("Abandoning remaining async distance field tasks for shutdown")); + ThreadPool->Destroy(); +} diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshCardRepresentation2.cpp b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshCardRepresentation2.cpp new file mode 100644 index 0000000..7949d0b --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshCardRepresentation2.cpp @@ -0,0 +1,456 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +/*============================================================================= + MeshCardRepresentation.cpp +=============================================================================*/ + +#include "MeshCardRepresentation2.h" + +#include "MeshCardRepresentation.h" +#include "MeshUtilities2.h" +#include "Modules/ModuleManager.h" +#include "StaticMeshResources.h" +#include "ProfilingDebugging/CookStats.h" +#include "Templates/UniquePtr.h" +#include "Engine/StaticMesh.h" +#include "Misc/AutomationTest.h" +#include "Misc/QueuedThreadPoolWrapper.h" +#include "ObjectCacheContext.h" + +#if WITH_EDITOR +#include "AssetCompilingManager.h" +// #include "DerivedDataCacheInterface.h" +// #include "MeshUtilities.h" +// #include "StaticMeshCompiler.h" +#endif + +#if WITH_EDITORONLY_DATA +#include "IMeshBuilderModule.h" +#endif + +#if ENABLE_COOK_STATS +namespace CardRepresentationCookStats +{ + FCookStats::FDDCResourceUsageStats UsageStats; + static FCookStatsManager::FAutoRegisterCallback RegisterCookStats([](FCookStatsManager::AddStatFuncRef AddStat) + { + UsageStats.LogStats(AddStat, TEXT("CardRepresentation.Usage"), TEXT("")); + }); +} +#endif + +// static TAutoConsoleVariable CVarCardRepresentation( +// TEXT("r.MeshCardRepresentation"), +// 1, +// TEXT(""), +// ECVF_ReadOnly); + +// static TAutoConsoleVariable CVarCardRepresentationMinSurface( +// TEXT("r.MeshCardRepresentation.MinSurface"), +// 0.2f, +// TEXT("Min percentage of surface treshold to spawn a new card, [0;1] range."), +// ECVF_ReadOnly); + +FCardRepresentationAsyncQueue2* GCardRepresentationAsyncQueue2 = NULL; + +int32 GUseAsyncCardRepresentationBuildQueue2 = 1; +static FAutoConsoleVariableRef CVarCardRepresentationAsyncBuildQueue2( + TEXT("r.MeshCardRepresentation.Async"), + GUseAsyncCardRepresentationBuildQueue2, + TEXT("."), + ECVF_Default | ECVF_ReadOnly +); + +FCardRepresentationAsyncQueue2::FCardRepresentationAsyncQueue2() +{ + MeshUtilities = NULL; +#if WITH_EDITOR + const int32 MaxConcurrency = -1; + // In Editor, we allow faster compilation by letting the asset compiler's scheduler organize work. + ThreadPool = MakeUnique(FAssetCompilingManager::Get().GetThreadPool(), MaxConcurrency, [](EQueuedWorkPriority) { return EQueuedWorkPriority::Lowest; }); +#else + const int32 MaxConcurrency = -1; + ThreadPool = MakeUnique(GThreadPool, MaxConcurrency, [](EQueuedWorkPriority) { return EQueuedWorkPriority::Lowest; }); +#endif +} + +FCardRepresentationAsyncQueue2::~FCardRepresentationAsyncQueue2() +{ +} + +void FAsyncCardRepresentationTaskWorker2::DoWork() +{ + // Put on background thread to avoid interfering with game-thread bound tasks + FQueuedThreadPoolTaskGraphWrapper TaskGraphWrapper(ENamedThreads::AnyBackgroundThreadNormalTask); + GCardRepresentationAsyncQueue2->Build(&Task, TaskGraphWrapper); +} + +void FCardRepresentationAsyncQueue2::CancelBackgroundTask(TArray Tasks) +{ + // Do all the cancellation first to make sure none of these tasks + // get scheduled as we're waiting for completion. + for (FAsyncCardRepresentationTask2* Task : Tasks) + { + if (Task->AsyncTask) + { + Task->AsyncTask->Cancel(); + } + } + + for (FAsyncCardRepresentationTask2* Task : Tasks) + { + if (Task->AsyncTask) + { + Task->AsyncTask->EnsureCompletion(); + Task->AsyncTask.Reset(); + } + } +} + +void FCardRepresentationAsyncQueue2::StartBackgroundTask(FAsyncCardRepresentationTask2* Task) +{ + check(Task->AsyncTask == nullptr); + Task->AsyncTask = MakeUnique>(*Task); + Task->AsyncTask->StartBackgroundTask(ThreadPool.Get(), EQueuedWorkPriority::Lowest); +} + +void FCardRepresentationAsyncQueue2::ProcessPendingTasks() +{ + FScopeLock Lock(&CriticalSection); + TArray Tasks = MoveTemp(PendingTasks); + for (FAsyncCardRepresentationTask2* Task : Tasks) + { + if (Task->GenerateSource && Task->GenerateSource->IsCompiling()) + { + PendingTasks.Add(Task); + } + else + { + StartBackgroundTask(Task); + } + } +} + +void FCardRepresentationAsyncQueue2::AddTask(FAsyncCardRepresentationTask2* Task) +{ + // #if WITH_EDITOR + if (!MeshUtilities) + { + MeshUtilities = &FModuleManager::Get().LoadModuleChecked(TEXT("MeshUtilities2")); + } + + { + // Array protection when called from multiple threads + FScopeLock Lock(&CriticalSection); + ReferencedTasks.Add(Task); + } + + // The Source Mesh's RenderData is not yet ready, postpone the build + if (Task->GenerateSource->IsCompiling()) + { + // Array protection when called from multiple threads + FScopeLock Lock(&CriticalSection); + PendingTasks.Add(Task); + } + else + { + // If we're already in worker threads there is no need to launch an async task. + if (GUseAsyncCardRepresentationBuildQueue2 || !IsInGameThread()) + { + StartBackgroundTask(Task); + } + else + { + // To avoid deadlocks, we must queue the inner build tasks on another thread pool, so use the task graph. + // Put on background thread to avoid interfering with game-thread bound tasks + FQueuedThreadPoolTaskGraphWrapper TaskGraphWrapper(ENamedThreads::AnyBackgroundThreadNormalTask); + Build(Task, TaskGraphWrapper); + } + } + // #else + // UE_LOG(LogStaticMesh,Fatal,TEXT("Tried to build a card representation without editor support (this should have been done during cooking)")); + // #endif +} + +void FCardRepresentationAsyncQueue2::CancelBuild(UStaticMesh* StaticMesh) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FCardRepresentationAsyncQueue2::CancelBuild); + + TArray TasksToCancel; + { + FScopeLock Lock(&CriticalSection); + TArray Tasks = MoveTemp(PendingTasks); + PendingTasks.Reserve(Tasks.Num()); + for (FAsyncCardRepresentationTask2* Task : Tasks) + { + if (Task->GenerateSource != StaticMesh && Task->StaticMesh != StaticMesh) + { + PendingTasks.Add(Task); + } + } + + Tasks = MoveTemp(ReferencedTasks); + ReferencedTasks.Reserve(Tasks.Num()); + for (FAsyncCardRepresentationTask2* Task : Tasks) + { + if (Task->GenerateSource != StaticMesh && Task->StaticMesh != StaticMesh) + { + ReferencedTasks.Add(Task); + } + else + { + TasksToCancel.Add(Task); + } + } + } + + CancelBackgroundTask(TasksToCancel); + for (FAsyncCardRepresentationTask2* Task : TasksToCancel) + { + if (Task->GeneratedCardRepresentation != nullptr) + { + // Rendering thread may still be referencing the old one, use the deferred cleanup interface to delete it next frame when it is safe + BeginCleanup(Task->GeneratedCardRepresentation); + } + + delete Task; + } +} + +void FCardRepresentationAsyncQueue2::CancelAllOutstandingBuilds() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FCardRepresentationAsyncQueue2::CancelAllOutstandingBuilds); + + TArray OutstandingTasks; + { + FScopeLock Lock(&CriticalSection); + PendingTasks.Empty(); + OutstandingTasks = MoveTemp(ReferencedTasks); + } + + CancelBackgroundTask(OutstandingTasks); + for (FAsyncCardRepresentationTask2* Task : OutstandingTasks) + { + delete Task; + } +} + +void FCardRepresentationAsyncQueue2::RescheduleBackgroundTask(FAsyncCardRepresentationTask2* InTask, EQueuedWorkPriority InPriority) +{ + if (InTask->AsyncTask) + { + if (InTask->AsyncTask->GetPriority() != InPriority) + { + InTask->AsyncTask->Reschedule(GThreadPool, InPriority); + } + } +} + +void FCardRepresentationAsyncQueue2::BlockUntilBuildComplete(UStaticMesh* StaticMesh, bool bWarnIfBlocked) +{ + // We will track the wait time here, but only the cycles used. + // This function is called whether or not an async task is pending, + // so we have to look elsewhere to properly count how many resources have actually finished building. + COOK_STAT(auto Timer = CardRepresentationCookStats::UsageStats.TimeAsyncWait()); + COOK_STAT(Timer.TrackCyclesOnly()); + bool bReferenced = false; + bool bHadToBlock = false; + double StartTime = 0; + + // #if WITH_EDITOR + // FStaticMeshCompilingManager::Get().FinishCompilation({ StaticMesh }); + // #endif + + do + { + ProcessAsyncTasks(); + + bReferenced = false; + + { + FScopeLock Lock(&CriticalSection); + for (int TaskIndex = 0; TaskIndex < ReferencedTasks.Num(); TaskIndex++) + { + if (ReferencedTasks[TaskIndex]->StaticMesh == StaticMesh || + ReferencedTasks[TaskIndex]->GenerateSource == StaticMesh) + { + bReferenced = true; + RescheduleBackgroundTask(ReferencedTasks[TaskIndex], EQueuedWorkPriority::Highest); + } + } + } + + if (bReferenced) + { + if (!bHadToBlock) + { + StartTime = FPlatformTime::Seconds(); + } + + bHadToBlock = true; + FPlatformProcess::Sleep(.01f); + } + } + while (bReferenced); + + if (bHadToBlock && + bWarnIfBlocked +#if WITH_EDITOR + && !FAutomationTestFramework::Get().GetCurrentTest() // HACK - Don't output this warning during automation test +#endif + ) + { + UE_LOG(LogStaticMesh, Display, TEXT("Main thread blocked for %.3fs for async card representation build of %s to complete! This can happen if the mesh is rebuilt excessively."), + (float)(FPlatformTime::Seconds() - StartTime), + *StaticMesh->GetName()); + } +} + +void FCardRepresentationAsyncQueue2::BlockUntilAllBuildsComplete() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FCardRepresentationAsyncQueue2::BlockUntilAllBuildsComplete) + do + { + // #if WITH_EDITOR + // FStaticMeshCompilingManager::Get().FinishAllCompilation(); + // #endif + // Reschedule as highest prio since we're explicitly waiting on them + { + FScopeLock Lock(&CriticalSection); + for (int TaskIndex = 0; TaskIndex < ReferencedTasks.Num(); TaskIndex++) + { + RescheduleBackgroundTask(ReferencedTasks[TaskIndex], EQueuedWorkPriority::Highest); + } + } + + ProcessAsyncTasks(); + FPlatformProcess::Sleep(.01f); + } + while (GetNumOutstandingTasks() > 0); +} + +void FCardRepresentationAsyncQueue2::Build(FAsyncCardRepresentationTask2* Task, FQueuedThreadPool& BuildThreadPool) +{ + // #if WITH_EDITOR + // Editor 'force delete' can null any UObject pointers which are seen by reference collecting (eg UProperty or serialized) + if (Task->StaticMesh && Task->GenerateSource) + { + TRACE_CPUPROFILER_EVENT_SCOPE(FCardRepresentationAsyncQueue2::Build); + + const FStaticMeshLODResources& LODModel = Task->GenerateSource->GetRenderData()->LODResources[0]; + + Task->bSuccess = MeshUtilities->GenerateCardRepresentationData( + Task->StaticMesh->GetName(), + Task->SourceMeshData, + LODModel, + BuildThreadPool, + Task->MaterialBlendModes, + Task->GenerateSource->GetRenderData()->Bounds, + Task->GenerateSource->GetRenderData()->LODResources[0].DistanceFieldData, + Task->bGenerateDistanceFieldAsIfTwoSided, + *Task->GeneratedCardRepresentation); + } + + CompletedTasks.Push(Task); + // #endif +} + +void FCardRepresentationAsyncQueue2::AddReferencedObjects(FReferenceCollector& Collector) +{ + FScopeLock Lock(&CriticalSection); + for (int TaskIndex = 0; TaskIndex < ReferencedTasks.Num(); TaskIndex++) + { + // Make sure none of the UObjects referenced by the async tasks are GC'ed during the task + Collector.AddReferencedObject(ReferencedTasks[TaskIndex]->StaticMesh); + Collector.AddReferencedObject(ReferencedTasks[TaskIndex]->GenerateSource); + } +} + +FString FCardRepresentationAsyncQueue2::GetReferencerName() const +{ + return TEXT("FCardRepresentationAsyncQueue2"); +} + +void FCardRepresentationAsyncQueue2::ProcessAsyncTasks(bool bLimitExecutionTime) +{ + // #if WITH_EDITOR + TRACE_CPUPROFILER_EVENT_SCOPE(FCardRepresentationAsyncQueue2::ProcessAsyncTasks); + + ProcessPendingTasks(); + + FObjectCacheContextScope ObjectCacheScope; + const double MaxProcessingTime = 0.016f; + double StartTime = FPlatformTime::Seconds(); + while (!bLimitExecutionTime || (FPlatformTime::Seconds() - StartTime) < MaxProcessingTime) + { + FAsyncCardRepresentationTask2* Task = CompletedTasks.Pop(); + if (Task == nullptr) + { + break; + } + + // We want to count each resource built from a DDC miss, so count each iteration of the loop separately. + COOK_STAT(auto Timer = CardRepresentationCookStats::UsageStats.TimeSyncWork()); + + bool bWasCancelled = false; + { + FScopeLock Lock(&CriticalSection); + bWasCancelled = ReferencedTasks.Remove(Task) == 0; + } + + if (bWasCancelled) + { + continue; + } + + if (Task->AsyncTask) + { + Task->AsyncTask->EnsureCompletion(); + Task->AsyncTask.Reset(); + } + + // Editor 'force delete' can null any UObject pointers which are seen by reference collecting (eg UProperty or serialized) + if (Task->StaticMesh && Task->bSuccess) + { + FCardRepresentationData* OldCardData = Task->StaticMesh->GetRenderData()->LODResources[0].CardRepresentationData; + + // Assign the new data, this is safe because the render threads makes a copy of the pointer at scene proxy creation time. + Task->StaticMesh->GetRenderData()->LODResources[0].CardRepresentationData = Task->GeneratedCardRepresentation; + + // Any already created render state needs to be dirtied + if (Task->StaticMesh->GetRenderData()->IsInitialized()) + { + for (UStaticMeshComponent* Component : ObjectCacheScope.GetContext().GetStaticMeshComponents(Task->StaticMesh)) + { + if (Component->IsRegistered() && Component->IsRenderStateCreated()) + { + Component->MarkRenderStateDirty(); + } + } + } + + // Rendering thread may still be referencing the old one, use the deferred cleanup interface to delete it next frame when it is safe + BeginCleanup(OldCardData); + + // { + // TArray DerivedData; + // // Save built data to DDC + // FMemoryWriter Ar(DerivedData, /*bIsPersistent=*/ true); + // Ar << *(Task->StaticMesh->GetRenderData()->LODResources[0].CardRepresentationData); + // GetDerivedDataCacheRef().Put(*Task->DDCKey, DerivedData, Task->StaticMesh->GetPathName()); + // COOK_STAT(Timer.AddMiss(DerivedData.Num())); + // } + } + + delete Task; + } + // #endif +} + +void FCardRepresentationAsyncQueue2::Shutdown() +{ + CancelAllOutstandingBuilds(); + + UE_LOG(LogStaticMesh, Log, TEXT("Abandoning remaining async card representation tasks for shutdown")); + ThreadPool->Destroy(); +} diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshCardRepresentationUtilities.cpp b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshCardRepresentationUtilities.cpp new file mode 100644 index 0000000..a9509ef --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshCardRepresentationUtilities.cpp @@ -0,0 +1,510 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "MeshUtilities2/Public/MeshUtilities2.h" +#include "MeshUtilitiesPrivate.h" +#include "StaticMeshResources.h" +#include "MeshCardRepresentation.h" +#include "DistanceFieldAtlas.h" +#include "MeshRepresentationCommon.h" + +class FGenerateCardMeshContext +{ +public: + const FString& MeshName; + RTCScene FullMeshEmbreeScene; + RTCDevice EmbreeDevice; + FCardRepresentationData& OutData; + + FGenerateCardMeshContext(const FString& InMeshName, RTCScene InEmbreeScene, RTCDevice InEmbreeDevice, FCardRepresentationData& InOutData) : + MeshName(InMeshName), + FullMeshEmbreeScene(InEmbreeScene), + EmbreeDevice(InEmbreeDevice), + OutData(InOutData) + {} +}; + +class FPlacedCard +{ +public: + int32 SliceMin; + int32 SliceMax; + + float NearPlane; + float FarPlane; + FBox Bounds; + int32 NumHits; +}; + +#if USE_EMBREE + +bool IsSurfacePointInsideMesh(const RTCScene& FullMeshEmbreeScene, FVector SurfacePoint, FVector SurfaceNormal, const TArray& RayDirectionsOverHemisphere) +{ + uint32 NumHits = 0; + uint32 NumBackFaceHits = 0; + + const FMatrix SurfaceBasis = MeshRepresentation::GetTangentBasisFrisvad(SurfaceNormal); + + for (int32 SampleIndex = 0; SampleIndex < RayDirectionsOverHemisphere.Num(); ++SampleIndex) + { + FVector RayDirection = SurfaceBasis.TransformVector(RayDirectionsOverHemisphere[SampleIndex]); + + FEmbreeRay EmbreeRay; + EmbreeRay.ray.org_x = SurfacePoint.X; + EmbreeRay.ray.org_y = SurfacePoint.Y; + EmbreeRay.ray.org_z = SurfacePoint.Z; + EmbreeRay.ray.dir_x = RayDirection.X; + EmbreeRay.ray.dir_y = RayDirection.Y; + EmbreeRay.ray.dir_z = RayDirection.Z; + EmbreeRay.ray.tnear = 0.1f; + EmbreeRay.ray.tfar = FLT_MAX; + + FEmbreeIntersectionContext EmbreeContext; + rtcInitIntersectContext(&EmbreeContext); + rtcIntersect1(FullMeshEmbreeScene, &EmbreeContext, &EmbreeRay); + + if (EmbreeRay.hit.geomID != RTC_INVALID_GEOMETRY_ID && EmbreeRay.hit.primID != RTC_INVALID_GEOMETRY_ID) + { + ++NumHits; + + if (FVector::DotProduct(RayDirection, EmbreeRay.GetHitNormal()) > 0.0f && !EmbreeContext.IsHitTwoSided()) + { + ++NumBackFaceHits; + } + } + } + + if (NumHits > 0 && NumBackFaceHits > RayDirectionsOverHemisphere.Num() * 0.4f) + { + return true; + } + + return false; +} + +struct FSurfacePoint +{ + float MinT; + float HitT; +}; + +int32 UpdatePlacedCards(TArray>& PlacedCards, + FVector RayOriginFrame, + FVector RayDirection, + FVector HeighfieldStepX, + FVector HeighfieldStepY, + FIntPoint HeighfieldSize, + int32 MeshSliceNum, + float MaxRayT, + int32 MinCardHits, + FVector VoxelExtent, + const TArray>>& HeightfieldLayers) +{ + for (int32 PlacedCardIndex = 0; PlacedCardIndex < PlacedCards.Num(); ++PlacedCardIndex) + { + FPlacedCard& PlacedCard = PlacedCards[PlacedCardIndex]; + PlacedCard.NearPlane = PlacedCard.SliceMin / float(MeshSliceNum) * MaxRayT; + PlacedCard.FarPlane = (PlacedCard.SliceMax / float(MeshSliceNum)) * MaxRayT; + PlacedCard.Bounds.Init(); + PlacedCard.NumHits = 0; + } + + for (int32 HeighfieldY = 0; HeighfieldY < HeighfieldSize.Y; ++HeighfieldY) + { + for (int32 HeighfieldX = 0; HeighfieldX < HeighfieldSize.X; ++HeighfieldX) + { + const int32 HeightfieldLinearIndex = HeighfieldX + HeighfieldY * HeighfieldSize.X; + + FVector RayOrigin = RayOriginFrame; + RayOrigin += (HeighfieldX + 0.5f) * HeighfieldStepX; + RayOrigin += (HeighfieldY + 0.5f) * HeighfieldStepY; + + int32 LayerIndex = 0; + int32 PlacedCardIndex = 0; + + while (LayerIndex < HeightfieldLayers[HeightfieldLinearIndex].Num() && PlacedCardIndex < PlacedCards.Num()) + { + const FSurfacePoint& SurfacePoint = HeightfieldLayers[HeightfieldLinearIndex][LayerIndex]; + FPlacedCard& PlacedCard = PlacedCards[PlacedCardIndex]; + + if (SurfacePoint.HitT >= PlacedCard.NearPlane && SurfacePoint.HitT <= PlacedCard.FarPlane + && SurfacePoint.MinT <= PlacedCard.NearPlane) + { + PlacedCard.NumHits += 1; + PlacedCard.Bounds += RayOrigin + SurfacePoint.HitT * RayDirection - VoxelExtent; + PlacedCard.Bounds += RayOrigin + SurfacePoint.HitT * RayDirection + VoxelExtent; + + ++PlacedCardIndex; + ++LayerIndex; + } + else + { + if (SurfacePoint.HitT >= PlacedCard.FarPlane) + { + ++PlacedCardIndex; + } + else + { + ++LayerIndex; + } + } + } + } + } + + int32 NumMeshHits = 0; + for (int32 PlacedCardIndex = 0; PlacedCardIndex < PlacedCards.Num(); ++PlacedCardIndex) + { + const FPlacedCard& PlacedCard = PlacedCards[PlacedCardIndex]; + if (PlacedCard.NumHits >= MinCardHits) + { + NumMeshHits += PlacedCard.NumHits; + } + } + return NumMeshHits; +} + +void SerializePlacedCards(TArray>& PlacedCards, + int32 LODLevel, + int32 Orientation, + int32 MinCardHits, + const FBox& MeshCardsBounds, + FCardRepresentationData& OutData) +{ + for (int32 PlacedCardIndex = 0; PlacedCardIndex < PlacedCards.Num(); ++PlacedCardIndex) + { + const FPlacedCard& PlacedCard = PlacedCards[PlacedCardIndex]; + if (PlacedCard.NumHits >= MinCardHits) + { + const FBox ClampedBox = PlacedCard.Bounds.Overlap(MeshCardsBounds); + + FLumenCardBuildData CardBuildData; + CardBuildData.Center = ClampedBox.GetCenter(); + CardBuildData.Extent = ClampedBox.GetExtent(); + CardBuildData.Extent = FLumenCardBuildData::TransformFaceExtent(CardBuildData.Extent, Orientation); + CardBuildData.Orientation = Orientation; + CardBuildData.LODLevel = LODLevel; + + OutData.MeshCardsBuildData.CardBuildData.Add(CardBuildData); + } + } +} + +void BuildMeshCards(const FBox& MeshBounds, const FGenerateCardMeshContext& Context, FCardRepresentationData& OutData) +{ + static const auto CVarMeshCardRepresentationMinSurface = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.MeshCardRepresentation.MinSurface")); + const float MinSurfaceThreshold = CVarMeshCardRepresentationMinSurface->GetValueOnAnyThread(); + + // Make sure BBox isn't empty and we can generate card representation for it. This handles e.g. infinitely thin planes. + const FVector MeshCardsBoundsCenter = MeshBounds.GetCenter(); + const FVector MeshCardsBoundsExtent = FVector::Max(MeshBounds.GetExtent() + 1.0f, FVector(5.0f)); + const FBox MeshCardsBounds(MeshCardsBoundsCenter - MeshCardsBoundsExtent, MeshCardsBoundsCenter + MeshCardsBoundsExtent); + + OutData.MeshCardsBuildData.Bounds = MeshCardsBounds; + OutData.MeshCardsBuildData.MaxLODLevel = 1; + OutData.MeshCardsBuildData.CardBuildData.Reset(); + + const float SamplesPerWorldUnit = 1.0f / 10.0f; + const int32 MinSamplesPerAxis = 4; + const int32 MaxSamplesPerAxis = 64; + FIntVector VolumeSizeInVoxels; + VolumeSizeInVoxels.X = FMath::Clamp(MeshCardsBounds.GetSize().X * SamplesPerWorldUnit, MinSamplesPerAxis, MaxSamplesPerAxis); + VolumeSizeInVoxels.Y = FMath::Clamp(MeshCardsBounds.GetSize().Y * SamplesPerWorldUnit, MinSamplesPerAxis, MaxSamplesPerAxis); + VolumeSizeInVoxels.Z = FMath::Clamp(MeshCardsBounds.GetSize().Z * SamplesPerWorldUnit, MinSamplesPerAxis, MaxSamplesPerAxis); + + const FVector VoxelExtent = MeshCardsBounds.GetSize() / FVector(VolumeSizeInVoxels); + + // Generate random ray directions over a hemisphere + TArray RayDirectionsOverHemisphere; + { + FRandomStream RandomStream(0); + MeshUtilities::GenerateStratifiedUniformHemisphereSamples(64, RandomStream, RayDirectionsOverHemisphere); + } + + for (int32 Orientation = 0; Orientation < 6; ++Orientation) + { + FIntPoint HeighfieldSize(0, 0); + FVector RayDirection(0.0f, 0.0f, 0.0f); + FVector RayOriginFrame = MeshCardsBounds.Min; + FVector HeighfieldStepX(0.0f, 0.0f, 0.0f); + FVector HeighfieldStepY(0.0f, 0.0f, 0.0f); + float MaxRayT = 0.0f; + int32 MeshSliceNum = 0; + + switch (Orientation / 2) + { + case 0: + MaxRayT = MeshCardsBounds.GetSize().X + 0.1f; + MeshSliceNum = VolumeSizeInVoxels.X; + HeighfieldSize.X = VolumeSizeInVoxels.Y; + HeighfieldSize.Y = VolumeSizeInVoxels.Z; + HeighfieldStepX = FVector(0.0f, MeshCardsBounds.GetSize().Y / HeighfieldSize.X, 0.0f); + HeighfieldStepY = FVector(0.0f, 0.0f, MeshCardsBounds.GetSize().Z / HeighfieldSize.Y); + break; + + case 1: + MaxRayT = MeshCardsBounds.GetSize().Y + 0.1f; + MeshSliceNum = VolumeSizeInVoxels.Y; + HeighfieldSize.X = VolumeSizeInVoxels.X; + HeighfieldSize.Y = VolumeSizeInVoxels.Z; + HeighfieldStepX = FVector(MeshCardsBounds.GetSize().X / HeighfieldSize.X, 0.0f, 0.0f); + HeighfieldStepY = FVector(0.0f, 0.0f, MeshCardsBounds.GetSize().Z / HeighfieldSize.Y); + break; + + case 2: + MaxRayT = MeshCardsBounds.GetSize().Z + 0.1f; + MeshSliceNum = VolumeSizeInVoxels.Z; + HeighfieldSize.X = VolumeSizeInVoxels.X; + HeighfieldSize.Y = VolumeSizeInVoxels.Y; + HeighfieldStepX = FVector(MeshCardsBounds.GetSize().X / HeighfieldSize.X, 0.0f, 0.0f); + HeighfieldStepY = FVector(0.0f, MeshCardsBounds.GetSize().Y / HeighfieldSize.Y, 0.0f); + break; + } + + switch (Orientation) + { + case 0: + RayDirection.X = +1.0f; + break; + + case 1: + RayDirection.X = -1.0f; + RayOriginFrame.X = MeshCardsBounds.Max.X; + break; + + case 2: + RayDirection.Y = +1.0f; + break; + + case 3: + RayDirection.Y = -1.0f; + RayOriginFrame.Y = MeshCardsBounds.Max.Y; + break; + + case 4: + RayDirection.Z = +1.0f; + break; + + case 5: + RayDirection.Z = -1.0f; + RayOriginFrame.Z = MeshCardsBounds.Max.Z; + break; + + default: + check(false); + }; + + TArray>> HeightfieldLayers; + HeightfieldLayers.SetNum(HeighfieldSize.X * HeighfieldSize.Y); + + // Fill surface points + { + TRACE_CPUPROFILER_EVENT_SCOPE(FillSurfacePoints); + + TArray Heightfield; + Heightfield.SetNum(HeighfieldSize.X * HeighfieldSize.Y); + for (int32 HeighfieldY = 0; HeighfieldY < HeighfieldSize.Y; ++HeighfieldY) + { + for (int32 HeighfieldX = 0; HeighfieldX < HeighfieldSize.X; ++HeighfieldX) + { + Heightfield[HeighfieldX + HeighfieldY * HeighfieldSize.X] = -1.0f; + } + } + + for (int32 HeighfieldY = 0; HeighfieldY < HeighfieldSize.Y; ++HeighfieldY) + { + for (int32 HeighfieldX = 0; HeighfieldX < HeighfieldSize.X; ++HeighfieldX) + { + FVector RayOrigin = RayOriginFrame; + RayOrigin += (HeighfieldX + 0.5f) * HeighfieldStepX; + RayOrigin += (HeighfieldY + 0.5f) * HeighfieldStepY; + + float StepTMin = 0.0f; + + for (int32 StepIndex = 0; StepIndex < 64; ++StepIndex) + { + FEmbreeRay EmbreeRay; + EmbreeRay.ray.org_x = RayOrigin.X; + EmbreeRay.ray.org_y = RayOrigin.Y; + EmbreeRay.ray.org_z = RayOrigin.Z; + EmbreeRay.ray.dir_x = RayDirection.X; + EmbreeRay.ray.dir_y = RayDirection.Y; + EmbreeRay.ray.dir_z = RayDirection.Z; + EmbreeRay.ray.tnear = StepTMin; + EmbreeRay.ray.tfar = FLT_MAX; + + FEmbreeIntersectionContext EmbreeContext; + rtcInitIntersectContext(&EmbreeContext); + rtcIntersect1(Context.FullMeshEmbreeScene, &EmbreeContext, &EmbreeRay); + + if (EmbreeRay.hit.geomID != RTC_INVALID_GEOMETRY_ID && EmbreeRay.hit.primID != RTC_INVALID_GEOMETRY_ID) + { + const FVector SurfacePoint = RayOrigin + RayDirection * EmbreeRay.ray.tfar; + const FVector SurfaceNormal = EmbreeRay.GetHitNormal(); + + const float NdotD = FVector::DotProduct(RayDirection, SurfaceNormal); + const bool bPassCullTest = EmbreeContext.IsHitTwoSided() || NdotD <= 0.0f; + const bool bPassProjectionAngleTest = FMath::Abs(NdotD) >= FMath::Cos(75.0f * (PI / 180.0f)); + + const float MinDistanceBetweenPoints = (MaxRayT / MeshSliceNum); + const bool bPassDistanceToAnotherSurfaceTest = EmbreeRay.ray.tnear <= 0.0f || (EmbreeRay.ray.tfar - EmbreeRay.ray.tnear > MinDistanceBetweenPoints); + + if (bPassCullTest && bPassProjectionAngleTest && bPassDistanceToAnotherSurfaceTest) + { + const bool bIsInsideMesh = IsSurfacePointInsideMesh(Context.FullMeshEmbreeScene, SurfacePoint, SurfaceNormal, RayDirectionsOverHemisphere); + if (!bIsInsideMesh) + { + HeightfieldLayers[HeighfieldX + HeighfieldY * HeighfieldSize.X].Add( + { EmbreeRay.ray.tnear, EmbreeRay.ray.tfar } + ); + } + } + + StepTMin = EmbreeRay.ray.tfar + 0.01f; + } + else + { + break; + } + } + } + } + } + + + const int32 MinCardHits = FMath::Floor(HeighfieldSize.X * HeighfieldSize.Y * MinSurfaceThreshold); + + + TArray> PlacedCards; + int32 PlacedCardsHits = 0; + + // Place a default card + { + FPlacedCard PlacedCard; + PlacedCard.SliceMin = 0; + PlacedCard.SliceMax = MeshSliceNum; + PlacedCards.Add(PlacedCard); + + PlacedCardsHits = UpdatePlacedCards(PlacedCards, + RayOriginFrame, + RayDirection, + HeighfieldStepX, + HeighfieldStepY, + HeighfieldSize, + MeshSliceNum, + MaxRayT, + MinCardHits, + VoxelExtent, + HeightfieldLayers); + + if (PlacedCardsHits < MinCardHits) + { + PlacedCards.Reset(); + } + } + + SerializePlacedCards(PlacedCards, /*LOD level*/ 0, Orientation, MinCardHits, MeshCardsBounds, OutData); + + // Try to place more cards by splitting existing ones + for (uint32 CardPlacementIteration = 0; CardPlacementIteration < 4; ++CardPlacementIteration) + { + TArray> BestPlacedCards; + int32 BestPlacedCardHits = PlacedCardsHits; + + for (int32 PlacedCardIndex = 0; PlacedCardIndex < PlacedCards.Num(); ++PlacedCardIndex) + { + const FPlacedCard& PlacedCard = PlacedCards[PlacedCardIndex]; + for (int32 SliceIndex = PlacedCard.SliceMin + 2; SliceIndex < PlacedCard.SliceMax; ++SliceIndex) + { + TArray> TempPlacedCards(PlacedCards); + + FPlacedCard NewPlacedCard; + NewPlacedCard.SliceMin = SliceIndex; + NewPlacedCard.SliceMax = PlacedCard.SliceMax; + + TempPlacedCards[PlacedCardIndex].SliceMax = SliceIndex - 1; + TempPlacedCards.Insert(NewPlacedCard, PlacedCardIndex + 1); + + const int32 NumHits = UpdatePlacedCards( + TempPlacedCards, + RayOriginFrame, + RayDirection, + HeighfieldStepX, + HeighfieldStepY, + HeighfieldSize, + MeshSliceNum, + MaxRayT, + MinCardHits, + VoxelExtent, + HeightfieldLayers); + + if (NumHits > BestPlacedCardHits) + { + BestPlacedCards = TempPlacedCards; + BestPlacedCardHits = NumHits; + } + } + } + + if (BestPlacedCardHits >= PlacedCardsHits + MinCardHits) + { + PlacedCards = BestPlacedCards; + PlacedCardsHits = BestPlacedCardHits; + } + } + + SerializePlacedCards(PlacedCards, /*LOD level*/ 1, Orientation, MinCardHits, MeshCardsBounds, OutData); + } +} + +#endif // #if USE_EMBREE + +bool FMeshUtilities2::GenerateCardRepresentationData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + const FDistanceFieldVolumeData* DistanceFieldVolumeData, + bool bGenerateAsIfTwoSided, + FCardRepresentationData& OutData) +{ +#if USE_EMBREE + TRACE_CPUPROFILER_EVENT_SCOPE(FMeshUtilities2::GenerateCardRepresentationData); + const double StartTime = FPlatformTime::Seconds(); + + FEmbreeScene EmbreeScene; + MeshRepresentation::SetupEmbreeScene(MeshName, + SourceMeshData, + LODModel, + MaterialBlendModes, + bGenerateAsIfTwoSided, + EmbreeScene); + + if (!EmbreeScene.EmbreeScene) + { + return false; + } + + FGenerateCardMeshContext Context(MeshName, EmbreeScene.EmbreeScene, EmbreeScene.EmbreeDevice, OutData); + + // Note: must operate on the SDF bounds because SDF generation can expand the mesh's bounds + BuildMeshCards(DistanceFieldVolumeData ? DistanceFieldVolumeData->LocalSpaceMeshBounds : Bounds.GetBox(), Context, OutData); + + MeshRepresentation::DeleteEmbreeScene(EmbreeScene); + + const float TimeElapsed = (float)(FPlatformTime::Seconds() - StartTime); + + if (TimeElapsed > 1.0f) + { + UE_LOG(LogMeshUtilities, Log, TEXT("Finished mesh card build in %.1fs %s"), + TimeElapsed, + *MeshName); + } + + return true; +#else + UE_LOG(LogMeshUtilities, Warning, TEXT("Platform did not set USE_EMBREE, GenerateCardRepresentationData failed.")); + return false; +#endif +} diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshDistanceFieldUtilities.cpp b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshDistanceFieldUtilities.cpp new file mode 100644 index 0000000..de481f5 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshDistanceFieldUtilities.cpp @@ -0,0 +1,554 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "MeshUtilities2/Public/MeshUtilities2.h" +#include "MeshUtilitiesPrivate.h" +#include "RawMesh.h" +#include "StaticMeshResources.h" +#include "DistanceFieldAtlas.h" +#include "MeshRepresentationCommon.h" +#include "Async/ParallelFor.h" + +#if USE_EMBREE + + +class FEmbreePointQueryContext : public RTCPointQueryContext +{ +public: + RTCGeometry MeshGeometry; + int32 NumTriangles; +}; + +bool EmbreePointQueryFunction(RTCPointQueryFunctionArguments* args) +{ + const FEmbreePointQueryContext* Context = (const FEmbreePointQueryContext*)args->context; + + check(args->userPtr); + float& ClosestDistanceSq = *(float*)(args->userPtr); + + const int32 TriangleIndex = args->primID; + check(TriangleIndex < Context->NumTriangles); + + const FVector* VertexBuffer = (const FVector*)rtcGetGeometryBufferData(Context->MeshGeometry, RTC_BUFFER_TYPE_VERTEX, 0); + const uint32* IndexBuffer = (const uint32*)rtcGetGeometryBufferData(Context->MeshGeometry, RTC_BUFFER_TYPE_INDEX, 0); + + const uint32 I0 = IndexBuffer[TriangleIndex * 3 + 0]; + const uint32 I1 = IndexBuffer[TriangleIndex * 3 + 1]; + const uint32 I2 = IndexBuffer[TriangleIndex * 3 + 2]; + + const FVector V0 = VertexBuffer[I0]; + const FVector V1 = VertexBuffer[I1]; + const FVector V2 = VertexBuffer[I2]; + + const FVector QueryPosition(args->query->x, args->query->y, args->query->z); + const FVector ClosestPoint = FMath::ClosestPointOnTriangleToPoint(QueryPosition, V0, V1, V2); + const float QueryDistanceSq = (ClosestPoint - QueryPosition).SizeSquared(); + + if (QueryDistanceSq < ClosestDistanceSq) + { + ClosestDistanceSq = QueryDistanceSq; + + bool bShrinkQuery = true; + + if (bShrinkQuery) + { + args->query->radius = FMath::Sqrt(ClosestDistanceSq); + // Return true to indicate that the query radius has shrunk + return true; + } + } + + // Return false to indicate that the query radius hasn't changed + return false; +} + +static int32 ComputeLinearVoxelIndex(FIntVector VoxelCoordinate, FIntVector VolumeDimensions) +{ + return (VoxelCoordinate.Z * VolumeDimensions.Y + VoxelCoordinate.Y) * VolumeDimensions.X + VoxelCoordinate.X; +} + +class FSparseMeshDistanceFieldAsyncTask +{ +public: + FSparseMeshDistanceFieldAsyncTask( + const FEmbreeScene& InEmbreeScene, + const TArray* InSampleDirections, + float InLocalSpaceTraceDistance, + FBox InVolumeBounds, + float InLocalToVolumeScale, + FVector2D InDistanceFieldToVolumeScaleBias, + FIntVector InBrickCoordinate, + FIntVector InIndirectionSize, + bool bInUsePointQuery) + : + EmbreeScene(InEmbreeScene), + SampleDirections(InSampleDirections), + LocalSpaceTraceDistance(InLocalSpaceTraceDistance), + VolumeBounds(InVolumeBounds), + LocalToVolumeScale(InLocalToVolumeScale), + DistanceFieldToVolumeScaleBias(InDistanceFieldToVolumeScaleBias), + BrickCoordinate(InBrickCoordinate), + IndirectionSize(InIndirectionSize), + bUsePointQuery(bInUsePointQuery), + BrickMaxDistance(MIN_uint8), + BrickMinDistance(MAX_uint8) + {} + + void DoWork(); + + // Readonly inputs + const FEmbreeScene& EmbreeScene; + const TArray* SampleDirections; + float LocalSpaceTraceDistance; + FBox VolumeBounds; + float LocalToVolumeScale; + FVector2D DistanceFieldToVolumeScaleBias; + FIntVector BrickCoordinate; + FIntVector IndirectionSize; + bool bUsePointQuery; + + // Output + uint8 BrickMaxDistance; + uint8 BrickMinDistance; + TArray DistanceFieldVolume; +}; + +int32 DebugX = 0; +int32 DebugY = 0; +int32 DebugZ = 0; + +void FSparseMeshDistanceFieldAsyncTask::DoWork() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FSparseMeshDistanceFieldAsyncTask::DoWork); + + const FVector IndirectionVoxelSize = VolumeBounds.GetSize() / FVector(IndirectionSize); + const FVector DistanceFieldVoxelSize = IndirectionVoxelSize / FVector(DistanceField::UniqueDataBrickSize); + const FVector BrickMinPosition = VolumeBounds.Min + FVector(BrickCoordinate) * IndirectionVoxelSize; + + DistanceFieldVolume.Empty(DistanceField::BrickSize * DistanceField::BrickSize * DistanceField::BrickSize); + DistanceFieldVolume.AddZeroed(DistanceField::BrickSize * DistanceField::BrickSize * DistanceField::BrickSize); + + for (int32 ZIndex = 0; ZIndex < DistanceField::BrickSize; ZIndex++) + { + for (int32 YIndex = 0; YIndex < DistanceField::BrickSize; YIndex++) + { + for (int32 XIndex = 0; XIndex < DistanceField::BrickSize; XIndex++) + { + if (XIndex == DebugX && YIndex == DebugY && ZIndex == DebugZ) + { + int32 DebugBreak = 0; + } + + const FVector VoxelPosition = FVector(XIndex, YIndex, ZIndex) * DistanceFieldVoxelSize + BrickMinPosition; + const int32 Index = (ZIndex * DistanceField::BrickSize * DistanceField::BrickSize + YIndex * DistanceField::BrickSize + XIndex); + + float MinLocalSpaceDistance = LocalSpaceTraceDistance; + + bool bTraceRays = true; + + if (bUsePointQuery) + { + RTCPointQuery PointQuery; + PointQuery.x = VoxelPosition.X; + PointQuery.y = VoxelPosition.Y; + PointQuery.z = VoxelPosition.Z; + PointQuery.time = 0; + PointQuery.radius = LocalSpaceTraceDistance; + + FEmbreePointQueryContext QueryContext; + rtcInitPointQueryContext(&QueryContext); + QueryContext.MeshGeometry = EmbreeScene.Geometry.InternalGeometry; + QueryContext.NumTriangles = EmbreeScene.Geometry.TriangleDescs.Num(); + float ClosestUnsignedDistanceSq = (LocalSpaceTraceDistance * 2.0f) * (LocalSpaceTraceDistance * 2.0f); + rtcPointQuery(EmbreeScene.EmbreeScene, &PointQuery, &QueryContext, EmbreePointQueryFunction, &ClosestUnsignedDistanceSq); + + const float ClosestDistance = FMath::Sqrt(ClosestUnsignedDistanceSq); + bTraceRays = ClosestDistance <= LocalSpaceTraceDistance; + MinLocalSpaceDistance = FMath::Min(MinLocalSpaceDistance, ClosestDistance); + } + + if (bTraceRays) + { + int32 Hit = 0; + int32 HitBack = 0; + + for (int32 SampleIndex = 0; SampleIndex < SampleDirections->Num(); SampleIndex++) + { + const FVector UnitRayDirection = (*SampleDirections)[SampleIndex]; + const float PullbackEpsilon = 1.e-4f; + // Pull back the starting position slightly to make sure we hit a triangle that VoxelPosition is exactly on. + // This happens a lot with boxes, since we trace from voxel corners. + const FVector StartPosition = VoxelPosition - PullbackEpsilon * LocalSpaceTraceDistance * UnitRayDirection; + const FVector EndPosition = VoxelPosition + UnitRayDirection * LocalSpaceTraceDistance; + + if (FMath::LineBoxIntersection(VolumeBounds, VoxelPosition, EndPosition, UnitRayDirection)) + { + FEmbreeRay EmbreeRay; + + FVector RayDirection = EndPosition - VoxelPosition; + EmbreeRay.ray.org_x = StartPosition.X; + EmbreeRay.ray.org_y = StartPosition.Y; + EmbreeRay.ray.org_z = StartPosition.Z; + EmbreeRay.ray.dir_x = RayDirection.X; + EmbreeRay.ray.dir_y = RayDirection.Y; + EmbreeRay.ray.dir_z = RayDirection.Z; + EmbreeRay.ray.tnear = 0; + EmbreeRay.ray.tfar = 1.0f; + + FEmbreeIntersectionContext EmbreeContext; + rtcInitIntersectContext(&EmbreeContext); + rtcIntersect1(EmbreeScene.EmbreeScene, &EmbreeContext, &EmbreeRay); + + if (EmbreeRay.hit.geomID != RTC_INVALID_GEOMETRY_ID && EmbreeRay.hit.primID != RTC_INVALID_GEOMETRY_ID) + { + check(EmbreeContext.ElementIndex != -1); + Hit++; + + const FVector HitNormal = EmbreeRay.GetHitNormal(); + + if (FVector::DotProduct(UnitRayDirection, HitNormal) > 0 && !EmbreeContext.IsHitTwoSided()) + { + HitBack++; + } + + if (!bUsePointQuery) + { + const float CurrentDistance = EmbreeRay.ray.tfar * LocalSpaceTraceDistance; + + if (CurrentDistance < MinLocalSpaceDistance) + { + MinLocalSpaceDistance = CurrentDistance; + } + } + } + } + } + + // Consider this voxel 'inside' an object if we hit a significant number of backfaces + if (Hit > 0 && HitBack > .25f * SampleDirections->Num()) + { + MinLocalSpaceDistance *= -1; + } + } + + // Transform to the tracing shader's Volume space + const float VolumeSpaceDistance = MinLocalSpaceDistance * LocalToVolumeScale; + // Transform to the Distance Field texture's space + const float RescaledDistance = (VolumeSpaceDistance - DistanceFieldToVolumeScaleBias.Y) / DistanceFieldToVolumeScaleBias.X; + check(DistanceField::DistanceFieldFormat == PF_G8); + const uint8 QuantizedDistance = FMath::Clamp(FMath::FloorToInt(RescaledDistance * 255.0f + .5f), 0, 255); + DistanceFieldVolume[Index] = QuantizedDistance; + BrickMaxDistance = FMath::Max(BrickMaxDistance, QuantizedDistance); + BrickMinDistance = FMath::Min(BrickMinDistance, QuantizedDistance); + } + } + } +} + +void FMeshUtilities2::GenerateSignedDistanceFieldVolumeData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + float DistanceFieldResolutionScale, + bool bGenerateAsIfTwoSided, + FDistanceFieldVolumeData& OutData) +{ + if (DistanceFieldResolutionScale > 0) + { + const double StartTime = FPlatformTime::Seconds(); + + FEmbreeScene EmbreeScene; + MeshRepresentation::SetupEmbreeScene(MeshName, + SourceMeshData, + LODModel, + MaterialBlendModes, + bGenerateAsIfTwoSided, + EmbreeScene); + + check(EmbreeScene.bUseEmbree); + + bool bMostlyTwoSided; + { + uint32 NumTrianglesTotal = 0; + uint32 NumTwoSidedTriangles = 0; + + for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) + { + const FStaticMeshSection& Section = LODModel.Sections[SectionIndex]; + + if (MaterialBlendModes.IsValidIndex(Section.MaterialIndex)) + { + NumTrianglesTotal += Section.NumTriangles; + + if (MaterialBlendModes[Section.MaterialIndex].bTwoSided) + { + NumTwoSidedTriangles += Section.NumTriangles; + } + } + } + + bMostlyTwoSided = NumTwoSidedTriangles * 4 >= NumTrianglesTotal || bGenerateAsIfTwoSided; + } + + // Whether to use an Embree Point Query to compute the closest unsigned distance. Rays will only be traced to determine backfaces visible for sign. + const bool bUsePointQuery = true; + + TArray SampleDirections; + { + const int32 NumVoxelDistanceSamples = bUsePointQuery ? 120 : 1200; + FRandomStream RandomStream(0); + MeshUtilities::GenerateStratifiedUniformHemisphereSamples(NumVoxelDistanceSamples, RandomStream, SampleDirections); + TArray OtherHemisphereSamples; + MeshUtilities::GenerateStratifiedUniformHemisphereSamples(NumVoxelDistanceSamples, RandomStream, OtherHemisphereSamples); + + for (int32 i = 0; i < OtherHemisphereSamples.Num(); i++) + { + FVector4 Sample = OtherHemisphereSamples[i]; + Sample.Z *= -1; + SampleDirections.Add(Sample); + } + } + + static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DistanceFields.MaxPerMeshResolution")); + const int32 PerMeshMax = CVar->GetValueOnAnyThread(); + + // Meshes with explicit artist-specified scale can go higher + const int32 MaxNumBlocksOneDim = FMath::Min(FMath::DivideAndRoundNearest(DistanceFieldResolutionScale <= 1 ? PerMeshMax / 2 : PerMeshMax, DistanceField::UniqueDataBrickSize), DistanceField::MaxIndirectionDimension - 1); + + static const auto CVarDensity = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.DistanceFields.DefaultVoxelDensity")); + const float VoxelDensity = CVarDensity->GetValueOnAnyThread(); + + const float NumVoxelsPerLocalSpaceUnit = VoxelDensity * DistanceFieldResolutionScale; + FBox LocalSpaceMeshBounds(Bounds.GetBox()); + + // Make sure the mesh bounding box has positive extents to handle planes + { + FVector MeshBoundsCenter = LocalSpaceMeshBounds.GetCenter(); + FVector MeshBoundsExtent = FVector::Max(LocalSpaceMeshBounds.GetExtent(), FVector(1.0f, 1.0f, 1.0f)); + LocalSpaceMeshBounds.Min = MeshBoundsCenter - MeshBoundsExtent; + LocalSpaceMeshBounds.Max = MeshBoundsCenter + MeshBoundsExtent; + } + + // We sample on voxel corners and use central differencing for gradients, so a box mesh using two-sided materials whose vertices lie on LocalSpaceMeshBounds produces a zero gradient on intersection + // Expand the mesh bounds by a fraction of a voxel to allow room for a pullback on the hit location for computing the gradient. + // Only expand for two sided meshes as this adds significant Mesh SDF tracing cost + if (bMostlyTwoSided) + { + const FVector DesiredDimensions = FVector(LocalSpaceMeshBounds.GetSize() * FVector(NumVoxelsPerLocalSpaceUnit / (float)DistanceField::UniqueDataBrickSize)); + const FIntVector Mip0IndirectionDimensions = FIntVector( + FMath::Clamp(FMath::RoundToInt(DesiredDimensions.X), 1, MaxNumBlocksOneDim), + FMath::Clamp(FMath::RoundToInt(DesiredDimensions.Y), 1, MaxNumBlocksOneDim), + FMath::Clamp(FMath::RoundToInt(DesiredDimensions.Z), 1, MaxNumBlocksOneDim)); + + const float CentralDifferencingExpandInVoxels = .25f; + const FVector TexelObjectSpaceSize = LocalSpaceMeshBounds.GetSize() / FVector(Mip0IndirectionDimensions * DistanceField::UniqueDataBrickSize - FIntVector(2 * CentralDifferencingExpandInVoxels)); + LocalSpaceMeshBounds = LocalSpaceMeshBounds.ExpandBy(TexelObjectSpaceSize); + } + + // The tracing shader uses a Volume space that is normalized by the maximum extent, to keep Volume space within [-1, 1], we must match that behavior when encoding + const float LocalToVolumeScale = 1.0f / LocalSpaceMeshBounds.GetExtent().GetMax(); + + const FVector DesiredDimensions = FVector(LocalSpaceMeshBounds.GetSize() * FVector(NumVoxelsPerLocalSpaceUnit / (float)DistanceField::UniqueDataBrickSize)); + const FIntVector Mip0IndirectionDimensions = FIntVector( + FMath::Clamp(FMath::RoundToInt(DesiredDimensions.X), 1, MaxNumBlocksOneDim), + FMath::Clamp(FMath::RoundToInt(DesiredDimensions.Y), 1, MaxNumBlocksOneDim), + FMath::Clamp(FMath::RoundToInt(DesiredDimensions.Z), 1, MaxNumBlocksOneDim)); + + TArray StreamableMipData; + + for (int32 MipIndex = 0; MipIndex < DistanceField::NumMips; MipIndex++) + { + const FIntVector IndirectionDimensions = FIntVector( + FMath::DivideAndRoundUp(Mip0IndirectionDimensions.X, 1 << MipIndex), + FMath::DivideAndRoundUp(Mip0IndirectionDimensions.Y, 1 << MipIndex), + FMath::DivideAndRoundUp(Mip0IndirectionDimensions.Z, 1 << MipIndex)); + + // Expand to guarantee one voxel border for gradient reconstruction using bilinear filtering + const FVector TexelObjectSpaceSize = LocalSpaceMeshBounds.GetSize() / FVector(IndirectionDimensions * DistanceField::UniqueDataBrickSize - FIntVector(2 * DistanceField::MeshDistanceFieldObjectBorder)); + const FBox DistanceFieldVolumeBounds = LocalSpaceMeshBounds.ExpandBy(TexelObjectSpaceSize); + + const FVector IndirectionVoxelSize = DistanceFieldVolumeBounds.GetSize() / FVector(IndirectionDimensions); + const float IndirectionVoxelRadius = IndirectionVoxelSize.Size(); + + const FVector VolumeSpaceDistanceFieldVoxelSize = IndirectionVoxelSize * LocalToVolumeScale / FVector(DistanceField::UniqueDataBrickSize); + const float MaxDistanceForEncoding = VolumeSpaceDistanceFieldVoxelSize.Size() * DistanceField::BandSizeInVoxels; + const float LocalSpaceTraceDistance = MaxDistanceForEncoding / LocalToVolumeScale; + const FVector2D DistanceFieldToVolumeScaleBias(2.0f * MaxDistanceForEncoding, -MaxDistanceForEncoding); + + TArray AsyncTasks; + AsyncTasks.Reserve(IndirectionDimensions.X * IndirectionDimensions.Y * IndirectionDimensions.Z / 8); + + for (int32 ZIndex = 0; ZIndex < IndirectionDimensions.Z; ZIndex++) + { + for (int32 YIndex = 0; YIndex < IndirectionDimensions.Y; YIndex++) + { + for (int32 XIndex = 0; XIndex < IndirectionDimensions.X; XIndex++) + { + AsyncTasks.Emplace( + EmbreeScene, + &SampleDirections, + LocalSpaceTraceDistance, + DistanceFieldVolumeBounds, + LocalToVolumeScale, + DistanceFieldToVolumeScaleBias, + FIntVector(XIndex, YIndex, ZIndex), + IndirectionDimensions, + bUsePointQuery); + } + } + } + + static bool bMultiThreaded = true; + + if (bMultiThreaded) + { + EParallelForFlags Flags = EParallelForFlags::BackgroundPriority | EParallelForFlags::Unbalanced; + + ParallelForTemplate(AsyncTasks.Num(), [&AsyncTasks](int32 TaskIndex) + { + AsyncTasks[TaskIndex].DoWork(); + }, Flags); + } + else + { + for (FSparseMeshDistanceFieldAsyncTask& AsyncTask : AsyncTasks) + { + AsyncTask.DoWork(); + } + } + + FSparseDistanceFieldMip& OutMip = OutData.Mips[MipIndex]; + TArray IndirectionTable; + IndirectionTable.Empty(IndirectionDimensions.X * IndirectionDimensions.Y * IndirectionDimensions.Z); + IndirectionTable.AddUninitialized(IndirectionDimensions.X * IndirectionDimensions.Y * IndirectionDimensions.Z); + + for (int32 i = 0; i < IndirectionTable.Num(); i++) + { + IndirectionTable[i] = DistanceField::InvalidBrickIndex; + } + + TArray ValidBricks; + ValidBricks.Empty(AsyncTasks.Num()); + + for (int32 TaskIndex = 0; TaskIndex < AsyncTasks.Num(); TaskIndex++) + { + if (AsyncTasks[TaskIndex].BrickMinDistance < MAX_uint8 && AsyncTasks[TaskIndex].BrickMaxDistance > MIN_uint8) + { + ValidBricks.Add(&AsyncTasks[TaskIndex]); + } + } + + const uint32 NumBricks = ValidBricks.Num(); + + const uint32 BrickSizeBytes = DistanceField::BrickSize * DistanceField::BrickSize * DistanceField::BrickSize * GPixelFormats[DistanceField::DistanceFieldFormat].BlockBytes; + + TArray DistanceFieldBrickData; + DistanceFieldBrickData.Empty(BrickSizeBytes * NumBricks); + DistanceFieldBrickData.AddUninitialized(BrickSizeBytes * NumBricks); + + for (int32 BrickIndex = 0; BrickIndex < ValidBricks.Num(); BrickIndex++) + { + const FSparseMeshDistanceFieldAsyncTask& Brick = *ValidBricks[BrickIndex]; + const int32 IndirectionIndex = ComputeLinearVoxelIndex(Brick.BrickCoordinate, IndirectionDimensions); + IndirectionTable[IndirectionIndex] = BrickIndex; + + check(BrickSizeBytes == Brick.DistanceFieldVolume.Num() * Brick.DistanceFieldVolume.GetTypeSize()); + FPlatformMemory::Memcpy(&DistanceFieldBrickData[BrickIndex * BrickSizeBytes], Brick.DistanceFieldVolume.GetData(), Brick.DistanceFieldVolume.Num() * Brick.DistanceFieldVolume.GetTypeSize()); + } + + const int32 IndirectionTableBytes = IndirectionTable.Num() * IndirectionTable.GetTypeSize(); + const int32 MipDataBytes = IndirectionTableBytes + DistanceFieldBrickData.Num(); + + if (MipIndex == DistanceField::NumMips - 1) + { + OutData.AlwaysLoadedMip.Empty(MipDataBytes); + OutData.AlwaysLoadedMip.AddUninitialized(MipDataBytes); + + FPlatformMemory::Memcpy(&OutData.AlwaysLoadedMip[0], IndirectionTable.GetData(), IndirectionTableBytes); + + if (DistanceFieldBrickData.Num() > 0) + { + FPlatformMemory::Memcpy(&OutData.AlwaysLoadedMip[IndirectionTableBytes], DistanceFieldBrickData.GetData(), DistanceFieldBrickData.Num()); + } + } + else + { + OutMip.BulkOffset = StreamableMipData.Num(); + StreamableMipData.AddUninitialized(MipDataBytes); + OutMip.BulkSize = StreamableMipData.Num() - OutMip.BulkOffset; + checkf(OutMip.BulkSize > 0, TEXT("BulkSize was 0 for %s with %ux%ux%u indirection"), *MeshName, IndirectionDimensions.X, IndirectionDimensions.Y, IndirectionDimensions.Z); + + FPlatformMemory::Memcpy(&StreamableMipData[OutMip.BulkOffset], IndirectionTable.GetData(), IndirectionTableBytes); + + if (DistanceFieldBrickData.Num() > 0) + { + FPlatformMemory::Memcpy(&StreamableMipData[OutMip.BulkOffset + IndirectionTableBytes], DistanceFieldBrickData.GetData(), DistanceFieldBrickData.Num()); + } + } + + OutMip.IndirectionDimensions = IndirectionDimensions; + OutMip.DistanceFieldToVolumeScaleBias = DistanceFieldToVolumeScaleBias; + OutMip.NumDistanceFieldBricks = NumBricks; + + // Account for the border voxels we added + const FVector VirtualUVMin = FVector(DistanceField::MeshDistanceFieldObjectBorder) / FVector(IndirectionDimensions * DistanceField::UniqueDataBrickSize); + const FVector VirtualUVSize = FVector(IndirectionDimensions * DistanceField::UniqueDataBrickSize - FIntVector(2 * DistanceField::MeshDistanceFieldObjectBorder)) / FVector(IndirectionDimensions * DistanceField::UniqueDataBrickSize); + + const FVector VolumePositionExtent = LocalSpaceMeshBounds.GetExtent() * LocalToVolumeScale; + + // [-VolumePositionExtent, VolumePositionExtent] -> [VirtualUVMin, VirtualUVMin + VirtualUVSize] + OutMip.VolumeToVirtualUVScale = VirtualUVSize / (2 * VolumePositionExtent); + OutMip.VolumeToVirtualUVAdd = VolumePositionExtent * OutMip.VolumeToVirtualUVScale + VirtualUVMin; + } + + MeshRepresentation::DeleteEmbreeScene(EmbreeScene); + + OutData.bMostlyTwoSided = bMostlyTwoSided; + OutData.LocalSpaceMeshBounds = LocalSpaceMeshBounds; + + OutData.StreamableMips.Lock(LOCK_READ_WRITE); + uint8* Ptr = (uint8*)OutData.StreamableMips.Realloc(StreamableMipData.Num()); + FMemory::Memcpy(Ptr, StreamableMipData.GetData(), StreamableMipData.Num()); + OutData.StreamableMips.Unlock(); + OutData.StreamableMips.SetBulkDataFlags(BULKDATA_Force_NOT_InlinePayload); + + const float BuildTime = (float)(FPlatformTime::Seconds() - StartTime); + + if (BuildTime > 1.0f) + { + UE_LOG(LogMeshUtilities, Log, TEXT("完成:距离场构建 %.1fs - %ux%ux%u 稀疏距离场, %.1fMb total, %.1fMb 总是加载, %u%% occupied, %u 三角形, %s"), + BuildTime, + Mip0IndirectionDimensions.X * DistanceField::UniqueDataBrickSize, + Mip0IndirectionDimensions.Y * DistanceField::UniqueDataBrickSize, + Mip0IndirectionDimensions.Z * DistanceField::UniqueDataBrickSize, + (OutData.GetResourceSizeBytes() + OutData.StreamableMips.GetBulkDataSize()) / 1024.0f / 1024.0f, + (OutData.AlwaysLoadedMip.GetAllocatedSize()) / 1024.0f / 1024.0f, + FMath::RoundToInt(100.0f * OutData.Mips[0].NumDistanceFieldBricks / (float)(Mip0IndirectionDimensions.X * Mip0IndirectionDimensions.Y * Mip0IndirectionDimensions.Z)), + EmbreeScene.NumIndices / 3, + *MeshName); + } + } +} + +#else + +void FMeshUtilities2::GenerateSignedDistanceFieldVolumeData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + float DistanceFieldResolutionScale, + bool bGenerateAsIfTwoSided, + FDistanceFieldVolumeData& OutData) +{ + if (DistanceFieldResolutionScale > 0) + { + UE_LOG(LogMeshUtilities, Warning, TEXT("Couldn't generate distance field for mesh, platform is missing Embree support.")); + } +} + +#endif // PLATFORM_ENABLE_VECTORINTRINSICS diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshRepresentationCommon.cpp b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshRepresentationCommon.cpp new file mode 100644 index 0000000..560fe7a --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshRepresentationCommon.cpp @@ -0,0 +1,318 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "MeshRepresentationCommon.h" +#include "MeshUtilities2/Public/MeshUtilities2.h" +#include "MeshUtilitiesPrivate.h" +#include "DerivedMeshDataTaskUtils.h" + +void MeshUtilities::GenerateStratifiedUniformHemisphereSamples(int32 NumSamples, FRandomStream& RandomStream, TArray& Samples) +{ + const int32 NumThetaSteps = FMath::TruncToInt(FMath::Sqrt(NumSamples / (2.0f * (float)PI))); + const int32 NumPhiSteps = FMath::TruncToInt(NumThetaSteps * (float)PI); + + Samples.Empty(NumThetaSteps * NumPhiSteps); + for (int32 ThetaIndex = 0; ThetaIndex < NumThetaSteps; ThetaIndex++) + { + for (int32 PhiIndex = 0; PhiIndex < NumPhiSteps; PhiIndex++) + { + const float U1 = RandomStream.GetFraction(); + const float U2 = RandomStream.GetFraction(); + + const float Fraction1 = (ThetaIndex + U1) / (float)NumThetaSteps; + const float Fraction2 = (PhiIndex + U2) / (float)NumPhiSteps; + + const float R = FMath::Sqrt(1.0f - Fraction1 * Fraction1); + + const float Phi = 2.0f * (float)PI * Fraction2; + // Convert to Cartesian + Samples.Add(FVector4(FMath::Cos(Phi) * R, FMath::Sin(Phi) * R, Fraction1)); + } + } +} + +// [Frisvad 2012, "Building an Orthonormal Basis from a 3D Unit Vector Without Normalization"] +FMatrix MeshRepresentation::GetTangentBasisFrisvad(FVector TangentZ) +{ + FVector TangentX; + FVector TangentY; + + if (TangentZ.Z < -0.9999999f) + { + TangentX = FVector(0, -1, 0); + TangentY = FVector(-1, 0, 0); + } + else + { + float A = 1.0f / (1.0f + TangentZ.Z); + float B = -TangentZ.X * TangentZ.Y * A; + TangentX = FVector(1.0f - TangentZ.X * TangentZ.X * A, B, -TangentZ.X); + TangentY = FVector(B, 1.0f - TangentZ.Y * TangentZ.Y * A, -TangentZ.Y); + } + + FMatrix LocalBasis; + LocalBasis.SetIdentity(); + LocalBasis.SetAxis(0, TangentX); + LocalBasis.SetAxis(1, TangentY); + LocalBasis.SetAxis(2, TangentZ); + return LocalBasis; +} + +#if USE_EMBREE +void EmbreeFilterFunc(const struct RTCFilterFunctionNArguments* args) +{ + FEmbreeGeometry* EmbreeGeometry = (FEmbreeGeometry*)args->geometryUserPtr; + FEmbreeTriangleDesc Desc = EmbreeGeometry->TriangleDescs[RTCHitN_primID(args->hit, 1, 0)]; + + FEmbreeIntersectionContext& IntersectionContext = *static_cast(args->context); + IntersectionContext.ElementIndex = Desc.ElementIndex; +} + +void EmbreeErrorFunc(void* userPtr, RTCError code, const char* str) +{ + FString ErrorString; + TArray& ErrorStringArray = ErrorString.GetCharArray(); + ErrorStringArray.Empty(); + + int32 StrLen = FCStringAnsi::Strlen(str); + int32 Length = FUTF8ToTCHAR_Convert::ConvertedLength(str, StrLen); + ErrorStringArray.AddUninitialized(Length + 1); // +1 for the null terminator + FUTF8ToTCHAR_Convert::Convert(ErrorStringArray.GetData(), ErrorStringArray.Num(), reinterpret_cast(str), StrLen); + ErrorStringArray[Length] = TEXT('\0'); + + UE_LOG(LogMeshUtilities, Error, TEXT("Embree error: %s Code=%u"), *ErrorString, (uint32)code); +} +#endif + +void MeshRepresentation::SetupEmbreeScene( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + const TArray& MaterialBlendModes, + bool bGenerateAsIfTwoSided, + FEmbreeScene& EmbreeScene) +{ + const uint32 NumIndices = SourceMeshData.IsValid() ? SourceMeshData.GetNumIndices() : LODModel.IndexBuffer.GetNumIndices(); + const int32 NumTriangles = NumIndices / 3; + const uint32 NumVertices = SourceMeshData.IsValid() ? SourceMeshData.GetNumVertices() : LODModel.VertexBuffers.PositionVertexBuffer.GetNumVertices(); + EmbreeScene.NumIndices = NumTriangles; + + TArray > BuildTriangles; + +#if USE_EMBREE + EmbreeScene.bUseEmbree = true; + + if (EmbreeScene.bUseEmbree) + { + EmbreeScene.EmbreeDevice = rtcNewDevice(nullptr); + rtcSetDeviceErrorFunction(EmbreeScene.EmbreeDevice, EmbreeErrorFunc, nullptr); + + RTCError ReturnErrorNewDevice = rtcGetDeviceError(EmbreeScene.EmbreeDevice); + if (ReturnErrorNewDevice != RTC_ERROR_NONE) + { + UE_LOG(LogMeshUtilities, Warning, TEXT("GenerateSignedDistanceFieldVolumeData failed for %s. Embree rtcNewDevice failed. Code: %d"), *MeshName, (int32)ReturnErrorNewDevice); + return; + } + + EmbreeScene.EmbreeScene = rtcNewScene(EmbreeScene.EmbreeDevice); + rtcSetSceneFlags(EmbreeScene.EmbreeScene, RTC_SCENE_FLAG_NONE); + + RTCError ReturnErrorNewScene = rtcGetDeviceError(EmbreeScene.EmbreeDevice); + if (ReturnErrorNewScene != RTC_ERROR_NONE) + { + UE_LOG(LogMeshUtilities, Warning, TEXT("GenerateSignedDistanceFieldVolumeData failed for %s. Embree rtcNewScene failed. Code: %d"), *MeshName, (int32)ReturnErrorNewScene); + rtcReleaseDevice(EmbreeScene.EmbreeDevice); + return; + } + } +#endif + + TArray FilteredTriangles; + FilteredTriangles.Empty(NumTriangles); + + if (SourceMeshData.IsValid()) + { + for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; ++TriangleIndex) + { + const uint32 I0 = SourceMeshData.TriangleIndices[TriangleIndex * 3 + 0]; + const uint32 I1 = SourceMeshData.TriangleIndices[TriangleIndex * 3 + 1]; + const uint32 I2 = SourceMeshData.TriangleIndices[TriangleIndex * 3 + 2]; + + const FVector V0 = SourceMeshData.VertexPositions[I0]; + const FVector V1 = SourceMeshData.VertexPositions[I1]; + const FVector V2 = SourceMeshData.VertexPositions[I2]; + + const FVector TriangleNormal = ((V1 - V2) ^ (V0 - V2)); + const bool bDegenerateTriangle = TriangleNormal.SizeSquared() < SMALL_NUMBER; + if (!bDegenerateTriangle) + { + FilteredTriangles.Add(TriangleIndex); + } + } + } + else + { + for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; ++TriangleIndex) + { + const FIndexArrayView Indices = LODModel.IndexBuffer.GetArrayView(); + const uint32 I0 = Indices[TriangleIndex * 3 + 0]; + const uint32 I1 = Indices[TriangleIndex * 3 + 1]; + const uint32 I2 = Indices[TriangleIndex * 3 + 2]; + + const FVector V0 = LODModel.VertexBuffers.PositionVertexBuffer.VertexPosition(I0); + const FVector V1 = LODModel.VertexBuffers.PositionVertexBuffer.VertexPosition(I1); + const FVector V2 = LODModel.VertexBuffers.PositionVertexBuffer.VertexPosition(I2); + + const FVector TriangleNormal = ((V1 - V2) ^ (V0 - V2)); + const bool bDegenerateTriangle = TriangleNormal.SizeSquared() < SMALL_NUMBER; + if (!bDegenerateTriangle) + { + bool bTriangleIsOpaqueOrMasked = false; + + for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) + { + const FStaticMeshSection& Section = LODModel.Sections[SectionIndex]; + + if ((uint32)(TriangleIndex * 3) >= Section.FirstIndex && (uint32)(TriangleIndex * 3) < Section.FirstIndex + Section.NumTriangles * 3) + { + if (MaterialBlendModes.IsValidIndex(Section.MaterialIndex)) + { + bTriangleIsOpaqueOrMasked = !IsTranslucentBlendMode(MaterialBlendModes[Section.MaterialIndex].BlendMode); + } + + break; + } + } + + if (bTriangleIsOpaqueOrMasked) + { + FilteredTriangles.Add(TriangleIndex); + } + } + } + } + + EmbreeScene.Geometry.VertexArray.Empty(NumVertices); + EmbreeScene.Geometry.VertexArray.AddUninitialized(NumVertices); + + const int32 NumFilteredIndices = FilteredTriangles.Num() * 3; + + EmbreeScene.Geometry.IndexArray.Empty(NumFilteredIndices); + EmbreeScene.Geometry.IndexArray.AddUninitialized(NumFilteredIndices); + + FVector* EmbreeVertices = EmbreeScene.Geometry.VertexArray.GetData(); + uint32* EmbreeIndices = EmbreeScene.Geometry.IndexArray.GetData(); + EmbreeScene.Geometry.TriangleDescs.Empty(FilteredTriangles.Num()); + + for (int32 FilteredTriangleIndex = 0; FilteredTriangleIndex < FilteredTriangles.Num(); FilteredTriangleIndex++) + { + uint32 I0, I1, I2; + FVector V0, V1, V2; + + const int32 TriangleIndex = FilteredTriangles[FilteredTriangleIndex]; + if (SourceMeshData.IsValid()) + { + I0 = SourceMeshData.TriangleIndices[TriangleIndex * 3 + 0]; + I1 = SourceMeshData.TriangleIndices[TriangleIndex * 3 + 1]; + I2 = SourceMeshData.TriangleIndices[TriangleIndex * 3 + 2]; + + V0 = SourceMeshData.VertexPositions[I0]; + V1 = SourceMeshData.VertexPositions[I1]; + V2 = SourceMeshData.VertexPositions[I2]; + } + else + { + const FIndexArrayView Indices = LODModel.IndexBuffer.GetArrayView(); + I0 = Indices[TriangleIndex * 3 + 0]; + I1 = Indices[TriangleIndex * 3 + 1]; + I2 = Indices[TriangleIndex * 3 + 2]; + + V0 = LODModel.VertexBuffers.PositionVertexBuffer.VertexPosition(I0); + V1 = LODModel.VertexBuffers.PositionVertexBuffer.VertexPosition(I1); + V2 = LODModel.VertexBuffers.PositionVertexBuffer.VertexPosition(I2); + } + + bool bTriangleIsTwoSided = false; + + for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) + { + const FStaticMeshSection& Section = LODModel.Sections[SectionIndex]; + + if ((uint32)(TriangleIndex * 3) >= Section.FirstIndex && (uint32)(TriangleIndex * 3) < Section.FirstIndex + Section.NumTriangles * 3) + { + if (MaterialBlendModes.IsValidIndex(Section.MaterialIndex)) + { + bTriangleIsTwoSided = MaterialBlendModes[Section.MaterialIndex].bTwoSided; + } + + break; + } + } + + if (EmbreeScene.bUseEmbree) + { + EmbreeIndices[FilteredTriangleIndex * 3 + 0] = I0; + EmbreeIndices[FilteredTriangleIndex * 3 + 1] = I1; + EmbreeIndices[FilteredTriangleIndex * 3 + 2] = I2; + + EmbreeVertices[I0] = V0; + EmbreeVertices[I1] = V1; + EmbreeVertices[I2] = V2; + + FEmbreeTriangleDesc Desc; + // Store bGenerateAsIfTwoSided in material index + Desc.ElementIndex = bGenerateAsIfTwoSided || bTriangleIsTwoSided ? 1 : 0; + EmbreeScene.Geometry.TriangleDescs.Add(Desc); + } + else + { + BuildTriangles.Add(FkDOPBuildCollisionTriangle( + // Store bGenerateAsIfTwoSided in material index + bGenerateAsIfTwoSided || bTriangleIsTwoSided ? 1 : 0, + V0, + V1, + V2)); + } + } + +#if USE_EMBREE + if (EmbreeScene.bUseEmbree) + { + RTCGeometry Geometry = rtcNewGeometry(EmbreeScene.EmbreeDevice, RTC_GEOMETRY_TYPE_TRIANGLE); + EmbreeScene.Geometry.InternalGeometry = Geometry; + + rtcSetSharedGeometryBuffer(Geometry, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, EmbreeVertices, 0, sizeof(FVector), NumVertices); + rtcSetSharedGeometryBuffer(Geometry, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, EmbreeIndices, 0, sizeof(uint32) * 3, FilteredTriangles.Num()); + + rtcSetGeometryUserData(Geometry, &EmbreeScene.Geometry); + rtcSetGeometryIntersectFilterFunction(Geometry, EmbreeFilterFunc); + + rtcCommitGeometry(Geometry); + rtcAttachGeometry(EmbreeScene.EmbreeScene, Geometry); + rtcReleaseGeometry(Geometry); + + rtcCommitScene(EmbreeScene.EmbreeScene); + + RTCError ReturnError = rtcGetDeviceError(EmbreeScene.EmbreeDevice); + if (ReturnError != RTC_ERROR_NONE) + { + UE_LOG(LogMeshUtilities, Warning, TEXT("GenerateSignedDistanceFieldVolumeData failed for %s. Embree rtcCommitScene failed. Code: %d"), *MeshName, (int32)ReturnError); + return; + } + } + else +#endif + { + EmbreeScene.kDopTree.Build(BuildTriangles); + } +} + +void MeshRepresentation::DeleteEmbreeScene(FEmbreeScene& EmbreeScene) +{ +#if USE_EMBREE + if (EmbreeScene.bUseEmbree) + { + rtcReleaseScene(EmbreeScene.EmbreeScene); + rtcReleaseDevice(EmbreeScene.EmbreeDevice); + } +#endif +} diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshRepresentationCommon.h b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshRepresentationCommon.h new file mode 100644 index 0000000..77a5c09 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshRepresentationCommon.h @@ -0,0 +1,158 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "MeshUtilities2/Public/MeshUtilities2.h" +#include "kDOP.h" +#include "MeshUtilities2/Public/DistanceFieldAtlas2.h" + +#if USE_EMBREE + #include + #include +#else + typedef void* RTCDevice; + typedef void* RTCScene; + typedef void* RTCGeometry; +#endif + +class FSourceMeshDataForDerivedDataTask; + +class FMeshBuildDataProvider +{ +public: + + /** Initialization constructor. */ + FMeshBuildDataProvider( + const TkDOPTree& InkDopTree) : + kDopTree(InkDopTree) + {} + + // kDOP data provider interface. + + FORCEINLINE const TkDOPTree& GetkDOPTree(void) const + { + return kDopTree; + } + + FORCEINLINE const FMatrix& GetLocalToWorld(void) const + { + return FMatrix::Identity; + } + + FORCEINLINE const FMatrix& GetWorldToLocal(void) const + { + return FMatrix::Identity; + } + + FORCEINLINE FMatrix GetLocalToWorldTransposeAdjoint(void) const + { + return FMatrix::Identity; + } + + FORCEINLINE float GetDeterminant(void) const + { + return 1.0f; + } + +private: + + const TkDOPTree& kDopTree; +}; + +struct FEmbreeTriangleDesc +{ + int16 ElementIndex; +}; + +// Mapping between Embree Geometry Id and engine Mesh/LOD Id +struct FEmbreeGeometry +{ + TArray IndexArray; + TArray VertexArray; + TArray TriangleDescs; // The material ID of each triangle. + RTCGeometry InternalGeometry; +}; + +class FEmbreeScene +{ +public: + bool bUseEmbree = false; + int32 NumIndices = 0; + + // Embree + RTCDevice EmbreeDevice = nullptr; + RTCScene EmbreeScene = nullptr; + FEmbreeGeometry Geometry; + + // DOP tree fallback + TkDOPTree kDopTree; +}; + +#if USE_EMBREE +struct FEmbreeRay : public RTCRayHit +{ + FEmbreeRay() : + ElementIndex(-1) + { + hit.u = hit.v = 0; + ray.time = 0; + ray.mask = 0xFFFFFFFF; + hit.geomID = RTC_INVALID_GEOMETRY_ID; + hit.instID[0] = RTC_INVALID_GEOMETRY_ID; + hit.primID = RTC_INVALID_GEOMETRY_ID; + } + + FVector GetHitNormal() const + { + return FVector(-hit.Ng_x, -hit.Ng_y, -hit.Ng_z).GetSafeNormal(); + } + + bool IsHitTwoSided() const + { + // MaterialIndex on the build triangles was set to 1 if two-sided, or 0 if one-sided + return ElementIndex == 1; + } + + // Additional Outputs. + int32 ElementIndex; // Material Index +}; + +struct FEmbreeIntersectionContext : public RTCIntersectContext +{ + FEmbreeIntersectionContext() : + ElementIndex(-1) + {} + + bool IsHitTwoSided() const + { + // MaterialIndex on the build triangles was set to 1 if two-sided, or 0 if one-sided + return ElementIndex == 1; + } + + // Additional Outputs. + int32 ElementIndex; // Material Index +}; + +#endif + +namespace MeshRepresentation +{ + /** + * Generates unit length, stratified and uniformly distributed direction samples in a hemisphere. + */ + void GenerateStratifiedUniformHemisphereSamples(int32 NumSamples, FRandomStream& RandomStream, TArray& Samples); + + /** + * [Frisvad 2012, "Building an Orthonormal Basis from a 3D Unit Vector Without Normalization"] + */ + FMatrix GetTangentBasisFrisvad(FVector TangentZ); + + void SetupEmbreeScene(FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + const TArray& MaterialBlendModes, + bool bGenerateAsIfTwoSided, + FEmbreeScene& EmbreeScene); + + void DeleteEmbreeScene(FEmbreeScene& EmbreeScene); +}; diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshUtilities2.cpp b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshUtilities2.cpp new file mode 100644 index 0000000..ca3f132 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshUtilities2.cpp @@ -0,0 +1,6217 @@ +// Copyright Epic Games, Inc. All Rights Reserved. +#include "MeshUtilities2/Public/MeshUtilities2.h" +#include "MeshUtilitiesPrivate.h" +#include "Misc/MessageDialog.h" +#include "Misc/ScopeLock.h" +#include "Containers/Ticker.h" +#include "Misc/FeedbackContext.h" +#include "Misc/ScopedSlowTask.h" +#include "Misc/ConfigCacheIni.h" +#include "Modules/ModuleManager.h" +#include "UObject/Package.h" +#include "Misc/PackageName.h" +#include "Textures/SlateIcon.h" +#include "Styling/SlateTypes.h" +#include "Framework/Commands/UIAction.h" +#include "Framework/Commands/UICommandList.h" +#include "Framework/MultiBox/MultiBoxExtender.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +// #include "ToolMenus.h" +// #include "SkeletalMeshToolMenuContext.h" +#include "Components/MeshComponent.h" +#include "RawIndexBuffer.h" +#include "Components/StaticMeshComponent.h" +#include "Components/ShapeComponent.h" +#include "Engine/StaticMesh.h" +#include "RawMesh.h" +#include "StaticMeshResources.h" +#include "MeshBuild.h" +// #include "ThirdPartyBuildOptimizationHelper.h" +#include "Engine/SkeletalMesh.h" +#include "Components/SkinnedMeshComponent.h" +#include "LayoutUV.h" +#include "mikktspace.h" +#include "Misc/FbxErrors.h" +#include "GameFramework/Character.h" +#include "Rendering/SkeletalMeshModel.h" +#include "Rendering/SkeletalMeshRenderData.h" + +// #include "LandscapeHeightfieldCollisionComponent.h" +// #include "Engine/MeshMergeCullingVolume.h" + +//#include "LevelEditor.h" +// #include "IAnimationBlueprintEditor.h" +// #include "IAnimationBlueprintEditorModule.h" +// #include "IAnimationEditor.h" +// #include "IAnimationEditorModule.h" +// #include "IContentBrowserSingleton.h" +// #include "ContentBrowserModule.h" +// #include "ISkeletalMeshEditor.h" +// #include "ISkeletalMeshEditorModule.h" +// #include "ISkeletonEditor.h" +// #include "ISkeletonEditorModule.h" +// #include "IPersonaToolkit.h" +// #include "Dialogs/DlgPickAssetPath.h" +#include "SkeletalRenderPublic.h" +// #include "AssetRegistryModule.h" +// #include "Framework/Notifications/NotificationManager.h" +// #include "Widgets/Notifications/SNotificationList.h" +// #include "Engine/MeshSimplificationSettings.h" +// #include "Engine/SkeletalMeshSimplificationSettings.h" +// #include "Engine/ProxyLODMeshSimplificationSettings.h" + +// #include "Editor/EditorPerProjectUserSettings.h" +// #include "IDetailCustomization.h" +// #include "EditorStyleSet.h" +// #include "PropertyEditorModule.h" +// #include "DetailLayoutBuilder.h" +// #include "DetailCategoryBuilder.h" +// #include "IDetailPropertyRow.h" +// #include "DetailWidgetRow.h" +#include "OverlappingCorners.h" +#include "MeshUtilitiesCommon.h" + +#include "StaticMeshAttributes.h" +// #include "StaticMeshOperations.h" + +// #if WITH_EDITOR +// #include "Editor.h" +// #include "UnrealEdMisc.h" +// #include "Subsystems/AssetEditorSubsystem.h" +// #endif +// +// #include "MaterialBakingStructures.h" +// #include "IMaterialBakingModule.h" +// #include "MaterialOptions.h" + + +// #include "PrimitiveSceneProxy.h" +#include "PrimitiveSceneInfo.h" +// #include "IMeshReductionManagerModule.h" +// #include "MeshMergeModule.h" + +#include "MeshCardRepresentation2.h" +#include "Animation/SkinWeightProfile.h" + +DEFINE_LOG_CATEGORY(LogMeshUtilities); +/*------------------------------------------------------------------------------ +MeshUtilities module. +------------------------------------------------------------------------------*/ + +// The version string is a GUID. If you make a change to mesh utilities that +// causes meshes to be rebuilt you MUST generate a new GUID and replace this +// string with it. + +#define MESH_UTILITIES_VER TEXT("228332BAE0224DD294E232B87D83948F") + +#define LOCTEXT_NAMESPACE "MeshUtils" + +IMPLEMENT_MODULE(FMeshUtilities2, MeshUtilities2); + +void FMeshUtilities2::CacheOptimizeIndexBuffer(TArray& Indices) +{ + // BuildOptimizationThirdParty::CacheOptimizeIndexBuffer(Indices); +} + +void FMeshUtilities2::CacheOptimizeIndexBuffer(TArray& Indices) +{ + // BuildOptimizationThirdParty::CacheOptimizeIndexBuffer(Indices); +} + +void FMeshUtilities2::BuildSkeletalAdjacencyIndexBuffer( + const TArray& VertexBuffer, + const uint32 TexCoordCount, + const TArray& Indices, + TArray& OutPnAenIndices +) +{ + // BuildOptimizationThirdParty::NvTriStripHelper::BuildSkeletalAdjacencyIndexBuffer(VertexBuffer, TexCoordCount, Indices, OutPnAenIndices); +} + +void CalculateTriangleTangentInternal(const FVector& VertexPosA, const FVector2D& VertexUVA + , const FVector& VertexPosB, const FVector2D& VertexUVB + , const FVector& VertexPosC, const FVector2D& VertexUVC + , TArray& OutTangents, float CompareThreshold) +{ + //We must always allocate the OutTangents to 3 FVector + OutTangents.Reset(3); + OutTangents.AddZeroed(3); + const FVector Positions[3] = {VertexPosA, VertexPosB, VertexPosC}; + const FVector Normal = ((Positions[1] - Positions[2]) ^ (Positions[0] - Positions[2])).GetSafeNormal(CompareThreshold); + //Avoid doing orthonormal vector from a degenerated triangle. + if (!Normal.IsNearlyZero(FLT_MIN)) + { + FMatrix ParameterToLocal( + FPlane(Positions[1].X - Positions[0].X, Positions[1].Y - Positions[0].Y, Positions[1].Z - Positions[0].Z, 0), + FPlane(Positions[2].X - Positions[0].X, Positions[2].Y - Positions[0].Y, Positions[2].Z - Positions[0].Z, 0), + FPlane(Positions[0].X, Positions[0].Y, Positions[0].Z, 0), + FPlane(0, 0, 0, 1) + ); + + const FVector2D T1 = VertexUVA; + const FVector2D T2 = VertexUVB; + const FVector2D T3 = VertexUVC; + + FMatrix ParameterToTexture( + FPlane(T2.X - T1.X, T2.Y - T1.Y, 0, 0), + FPlane(T3.X - T1.X, T3.Y - T1.Y, 0, 0), + FPlane(T1.X, T1.Y, 1, 0), + FPlane(0, 0, 0, 1) + ); + + // Use InverseSlow to catch singular matrices. Inverse can miss this sometimes. + const FMatrix TextureToLocal = ParameterToTexture.Inverse() * ParameterToLocal; + + OutTangents[0] = (TextureToLocal.TransformVector(FVector(1, 0, 0)).GetSafeNormal()); + OutTangents[1] = (TextureToLocal.TransformVector(FVector(0, 1, 0)).GetSafeNormal()); + OutTangents[2] = (Normal); + + FVector::CreateOrthonormalBasis( + OutTangents[0], + OutTangents[1], + OutTangents[2] + ); + + if (OutTangents[0].IsNearlyZero() || OutTangents[0].ContainsNaN() + || OutTangents[1].IsNearlyZero() || OutTangents[1].ContainsNaN()) + { + OutTangents[0] = FVector::ZeroVector; + OutTangents[1] = FVector::ZeroVector; + } + + if (OutTangents[2].IsNearlyZero() || OutTangents[2].ContainsNaN()) + { + OutTangents[2] = FVector::ZeroVector; + } + } + else + { + //Add zero tangents and normal for this triangle, this is like weighting it to zero when we compute the vertex normal + //But we need the triangle to correctly connect other neighbourg triangles + OutTangents[0] = (FVector::ZeroVector); + OutTangents[1] = (FVector::ZeroVector); + OutTangents[2] = (FVector::ZeroVector); + } +} + +void FMeshUtilities2::CalculateTriangleTangent(const FSoftSkinVertex& VertexA, const FSoftSkinVertex& VertexB, const FSoftSkinVertex& VertexC, TArray& OutTangents, float CompareThreshold) +{ + // CalculateTriangleTangentInternal(VertexA.Position, VertexA.UVs[0] + // , VertexB.Position, VertexB.UVs[0] + // , VertexC.Position, VertexC.UVs[0] + // , OutTangents, CompareThreshold); +} + +void FMeshUtilities2::CalcBoneVertInfos(USkeletalMesh* SkeletalMesh, TArray& Infos, bool bOnlyDominant) +{ + // SkeletalMeshTools::CalcBoneVertInfos(SkeletalMesh, Infos, bOnlyDominant); +} + +// Helper function for ConvertMeshesToStaticMesh +static void AddOrDuplicateMaterial(UMaterialInterface* InMaterialInterface, const FString& InPackageName, TArray& OutMaterials) +{ + if (InMaterialInterface && !InMaterialInterface->GetOuter()->IsA()) + { + // // Convert runtime material instances to new concrete material instances + // // Create new package + // FString OriginalMaterialName = InMaterialInterface->GetName(); + // FString MaterialPath = FPackageName::GetLongPackagePath(InPackageName) / OriginalMaterialName; + // FString MaterialName; + // FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + // AssetToolsModule.Get().CreateUniqueAssetName(MaterialPath, TEXT(""), MaterialPath, MaterialName); + // UPackage* MaterialPackage = CreatePackage( *MaterialPath); + // + // // Duplicate the object into the new package + // UMaterialInterface* NewMaterialInterface = DuplicateObject(InMaterialInterface, MaterialPackage, *MaterialName); + // NewMaterialInterface->SetFlags(RF_Public | RF_Standalone); + // + // if (UMaterialInstanceDynamic* MaterialInstanceDynamic = Cast(NewMaterialInterface)) + // { + // UMaterialInstanceDynamic* OldMaterialInstanceDynamic = CastChecked(InMaterialInterface); + // MaterialInstanceDynamic->K2_CopyMaterialInstanceParameters(OldMaterialInstanceDynamic); + // } + // + // NewMaterialInterface->MarkPackageDirty(); + // + // FAssetRegistryModule::AssetCreated(NewMaterialInterface); + // + // InMaterialInterface = NewMaterialInterface; + } + + OutMaterials.Add(InMaterialInterface); +} + +// Helper function for ConvertMeshesToStaticMesh +template +static void ProcessMaterials(ComponentType* InComponent, const FString& InPackageName, TArray& OutMaterials) +{ + const int32 NumMaterials = InComponent->GetNumMaterials(); + for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; MaterialIndex++) + { + UMaterialInterface* MaterialInterface = InComponent->GetMaterial(MaterialIndex); + AddOrDuplicateMaterial(MaterialInterface, InPackageName, OutMaterials); + } +} + +// Helper function for ConvertMeshesToStaticMesh +static bool IsValidSkinnedMeshComponent(USkinnedMeshComponent* InComponent) +{ + return InComponent && InComponent->MeshObject && InComponent->IsVisible(); +} + +/** Helper struct for tracking validity of optional buffers */ +struct FRawMeshTracker +{ + FRawMeshTracker() + : bValidColors(false) + { + FMemory::Memset(bValidTexCoords, 0); + } + + bool bValidTexCoords[MAX_MESH_TEXTURE_COORDS]; + bool bValidColors; +}; + +// Helper function for ConvertMeshesToStaticMesh +static void SkinnedMeshToRawMeshes(USkinnedMeshComponent* InSkinnedMeshComponent, int32 InOverallMaxLODs, const FMatrix& InComponentToWorld, const FString& InPackageName, TArray& OutRawMeshTrackers, TArray& OutRawMeshes, + TArray& OutMaterials) +{ + const int32 BaseMaterialIndex = OutMaterials.Num(); + + // Export all LODs to raw meshes + const int32 NumLODs = InSkinnedMeshComponent->GetNumLODs(); + + for (int32 OverallLODIndex = 0; OverallLODIndex < InOverallMaxLODs; OverallLODIndex++) + { + int32 LODIndexRead = FMath::Min(OverallLODIndex, NumLODs - 1); + + FRawMesh& RawMesh = OutRawMeshes[OverallLODIndex]; + FRawMeshTracker& RawMeshTracker = OutRawMeshTrackers[OverallLODIndex]; + const int32 BaseVertexIndex = RawMesh.VertexPositions.Num(); + + FSkeletalMeshLODInfo& SrcLODInfo = *(InSkinnedMeshComponent->SkeletalMesh->GetLODInfo(LODIndexRead)); + + // Get the CPU skinned verts for this LOD + TArray FinalVertices; + InSkinnedMeshComponent->GetCPUSkinnedVertices(FinalVertices, LODIndexRead); + + FSkeletalMeshRenderData& SkeletalMeshRenderData = InSkinnedMeshComponent->MeshObject->GetSkeletalMeshRenderData(); + FSkeletalMeshLODRenderData& LODData = SkeletalMeshRenderData.LODRenderData[LODIndexRead]; + + // Copy skinned vertex positions + for (int32 VertIndex = 0; VertIndex < FinalVertices.Num(); ++VertIndex) + { + RawMesh.VertexPositions.Add(InComponentToWorld.TransformPosition(FinalVertices[VertIndex].Position)); + } + + const uint32 NumTexCoords = FMath::Min(LODData.StaticVertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(), (uint32)MAX_MESH_TEXTURE_COORDS); + const int32 NumSections = LODData.RenderSections.Num(); + FRawStaticIndexBuffer16or32Interface& IndexBuffer = *LODData.MultiSizeIndexContainer.GetIndexBuffer(); + + for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++) + { + const FSkelMeshRenderSection& SkelMeshSection = LODData.RenderSections[SectionIndex]; + if (InSkinnedMeshComponent->IsMaterialSectionShown(SkelMeshSection.MaterialIndex, LODIndexRead)) + { + // Build 'wedge' info + const int32 NumWedges = SkelMeshSection.NumTriangles * 3; + for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; WedgeIndex++) + { + const int32 VertexIndexForWedge = IndexBuffer.Get(SkelMeshSection.BaseIndex + WedgeIndex); + + RawMesh.WedgeIndices.Add(BaseVertexIndex + VertexIndexForWedge); + + const FFinalSkinVertex& SkinnedVertex = FinalVertices[VertexIndexForWedge]; + const FVector TangentX = InComponentToWorld.TransformVector(SkinnedVertex.TangentX.ToFVector()); + const FVector TangentZ = InComponentToWorld.TransformVector(SkinnedVertex.TangentZ.ToFVector()); + const FVector4 UnpackedTangentZ = SkinnedVertex.TangentZ.ToFVector4(); + const FVector TangentY = (TangentZ ^ TangentX).GetSafeNormal() * UnpackedTangentZ.W; + + RawMesh.WedgeTangentX.Add(TangentX); + RawMesh.WedgeTangentY.Add(TangentY); + RawMesh.WedgeTangentZ.Add(TangentZ); + + for (uint32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; TexCoordIndex++) + { + if (TexCoordIndex >= NumTexCoords) + { + RawMesh.WedgeTexCoords[TexCoordIndex].AddDefaulted(); + } + else + { + RawMesh.WedgeTexCoords[TexCoordIndex].Add(LODData.StaticVertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndexForWedge, TexCoordIndex)); + RawMeshTracker.bValidTexCoords[TexCoordIndex] = true; + } + } + + if (LODData.StaticVertexBuffers.ColorVertexBuffer.IsInitialized()) + { + RawMesh.WedgeColors.Add(LODData.StaticVertexBuffers.ColorVertexBuffer.VertexColor(VertexIndexForWedge)); + RawMeshTracker.bValidColors = true; + } + else + { + RawMesh.WedgeColors.Add(FColor::White); + } + } + + int32 MaterialIndex = SkelMeshSection.MaterialIndex; + // use the remapping of material indices if there is a valid value + if (SrcLODInfo.LODMaterialMap.IsValidIndex(SectionIndex) && SrcLODInfo.LODMaterialMap[SectionIndex] != INDEX_NONE) + { + MaterialIndex = FMath::Clamp(SrcLODInfo.LODMaterialMap[SectionIndex], 0, InSkinnedMeshComponent->SkeletalMesh->GetMaterials().Num()); + } + + // copy face info + for (uint32 TriIndex = 0; TriIndex < SkelMeshSection.NumTriangles; TriIndex++) + { + RawMesh.FaceMaterialIndices.Add(BaseMaterialIndex + MaterialIndex); + RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false + } + } + } + } + + ProcessMaterials(InSkinnedMeshComponent, InPackageName, OutMaterials); +} + +// Helper function for ConvertMeshesToStaticMesh +static bool IsValidStaticMeshComponent(UStaticMeshComponent* InComponent) +{ + return InComponent && InComponent->GetStaticMesh() && InComponent->GetStaticMesh()->GetRenderData() && InComponent->IsVisible(); +} + +// Helper function for ConvertMeshesToStaticMesh +static void StaticMeshToRawMeshes(UStaticMeshComponent* InStaticMeshComponent, int32 InOverallMaxLODs, const FMatrix& InComponentToWorld, const FString& InPackageName, TArray& OutRawMeshTrackers, TArray& OutRawMeshes, + TArray& OutMaterials) +{ + const int32 BaseMaterialIndex = OutMaterials.Num(); + + const int32 NumLODs = InStaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources.Num(); + + for (int32 OverallLODIndex = 0; OverallLODIndex < InOverallMaxLODs; OverallLODIndex++) + { + int32 LODIndexRead = FMath::Min(OverallLODIndex, NumLODs - 1); + + FRawMesh& RawMesh = OutRawMeshes[OverallLODIndex]; + FRawMeshTracker& RawMeshTracker = OutRawMeshTrackers[OverallLODIndex]; + const FStaticMeshLODResources& LODResource = InStaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources[LODIndexRead]; + const int32 BaseVertexIndex = RawMesh.VertexPositions.Num(); + + for (int32 VertIndex = 0; VertIndex < LODResource.GetNumVertices(); ++VertIndex) + { + RawMesh.VertexPositions.Add(InComponentToWorld.TransformPosition(LODResource.VertexBuffers.PositionVertexBuffer.VertexPosition((uint32)VertIndex))); + } + + const FIndexArrayView IndexArrayView = LODResource.IndexBuffer.GetArrayView(); + const FStaticMeshVertexBuffer& StaticMeshVertexBuffer = LODResource.VertexBuffers.StaticMeshVertexBuffer; + const int32 NumTexCoords = FMath::Min(StaticMeshVertexBuffer.GetNumTexCoords(), (uint32)MAX_MESH_TEXTURE_COORDS); + const int32 NumSections = LODResource.Sections.Num(); + + for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++) + { + const FStaticMeshSection& StaticMeshSection = LODResource.Sections[SectionIndex]; + + const int32 NumIndices = StaticMeshSection.NumTriangles * 3; + for (int32 IndexIndex = 0; IndexIndex < NumIndices; IndexIndex++) + { + int32 Index = IndexArrayView[StaticMeshSection.FirstIndex + IndexIndex]; + RawMesh.WedgeIndices.Add(BaseVertexIndex + Index); + + RawMesh.WedgeTangentX.Add(InComponentToWorld.TransformVector(StaticMeshVertexBuffer.VertexTangentX(Index))); + RawMesh.WedgeTangentY.Add(InComponentToWorld.TransformVector(StaticMeshVertexBuffer.VertexTangentY(Index))); + RawMesh.WedgeTangentZ.Add(InComponentToWorld.TransformVector(StaticMeshVertexBuffer.VertexTangentZ(Index))); + + for (int32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; TexCoordIndex++) + { + if (TexCoordIndex >= NumTexCoords) + { + RawMesh.WedgeTexCoords[TexCoordIndex].AddDefaulted(); + } + else + { + RawMesh.WedgeTexCoords[TexCoordIndex].Add(StaticMeshVertexBuffer.GetVertexUV(Index, TexCoordIndex)); + RawMeshTracker.bValidTexCoords[TexCoordIndex] = true; + } + } + + if (LODResource.VertexBuffers.ColorVertexBuffer.IsInitialized()) + { + RawMesh.WedgeColors.Add(LODResource.VertexBuffers.ColorVertexBuffer.VertexColor(Index)); + RawMeshTracker.bValidColors = true; + } + else + { + RawMesh.WedgeColors.Add(FColor::White); + } + } + + // copy face info + for (uint32 TriIndex = 0; TriIndex < StaticMeshSection.NumTriangles; TriIndex++) + { + RawMesh.FaceMaterialIndices.Add(BaseMaterialIndex + StaticMeshSection.MaterialIndex); + RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false + } + } + } + + ProcessMaterials(InStaticMeshComponent, InPackageName, OutMaterials); +} + +UStaticMesh* FMeshUtilities2::ConvertMeshesToStaticMesh(const TArray& InMeshComponents, const FTransform& InRootTransform, const FString& InPackageName) +{ + UStaticMesh* StaticMesh = nullptr; + + // Build a package name to use + FString MeshName; + FString PackageName; + if (InPackageName.IsEmpty()) + { + FString NewNameSuggestion = FString(TEXT("StaticMesh")); + FString PackageNameSuggestion = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion; + FString Name; + // FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + // AssetToolsModule.Get().CreateUniqueAssetName(PackageNameSuggestion, TEXT(""), PackageNameSuggestion, Name); + + // TSharedPtr PickAssetPathWidget = + // SNew(SDlgPickAssetPath) + // .Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location")) + // .DefaultAssetPath(FText::FromString(PackageNameSuggestion)); + + // if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok) + // { + // // Get the full name of where we want to create the mesh asset. + // PackageName = PickAssetPathWidget->GetFullAssetPath().ToString(); + // MeshName = FPackageName::GetLongPackageAssetName(PackageName); + // + // // Check if the user inputed a valid asset name, if they did not, give it the generated default name + // if (MeshName.IsEmpty()) + // { + // // Use the defaults that were already generated. + // PackageName = PackageNameSuggestion; + // MeshName = *Name; + // } + // } + } + else + { + PackageName = InPackageName; + MeshName = *FPackageName::GetLongPackageAssetName(PackageName); + } + + if (!PackageName.IsEmpty() && !MeshName.IsEmpty()) + { + TArray RawMeshes; + TArray Materials; + + TArray RawMeshTrackers; + + FMatrix WorldToRoot = InRootTransform.ToMatrixWithScale().Inverse(); + + // first do a pass to determine the max LOD level we will be combining meshes into + int32 OverallMaxLODs = 0; + for (UMeshComponent* MeshComponent : InMeshComponents) + { + USkinnedMeshComponent* SkinnedMeshComponent = Cast(MeshComponent); + UStaticMeshComponent* StaticMeshComponent = Cast(MeshComponent); + + if (IsValidSkinnedMeshComponent(SkinnedMeshComponent)) + { + OverallMaxLODs = FMath::Max(SkinnedMeshComponent->MeshObject->GetSkeletalMeshRenderData().LODRenderData.Num(), OverallMaxLODs); + } + else if (IsValidStaticMeshComponent(StaticMeshComponent)) + { + OverallMaxLODs = FMath::Max(StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources.Num(), OverallMaxLODs); + } + } + + // Resize raw meshes to accommodate the number of LODs we will need + RawMeshes.SetNum(OverallMaxLODs); + RawMeshTrackers.SetNum(OverallMaxLODs); + + // Export all visible components + for (UMeshComponent* MeshComponent : InMeshComponents) + { + FMatrix ComponentToWorld = MeshComponent->GetComponentTransform().ToMatrixWithScale() * WorldToRoot; + + USkinnedMeshComponent* SkinnedMeshComponent = Cast(MeshComponent); + UStaticMeshComponent* StaticMeshComponent = Cast(MeshComponent); + + if (IsValidSkinnedMeshComponent(SkinnedMeshComponent)) + { + SkinnedMeshToRawMeshes(SkinnedMeshComponent, OverallMaxLODs, ComponentToWorld, PackageName, RawMeshTrackers, RawMeshes, Materials); + } + else if (IsValidStaticMeshComponent(StaticMeshComponent)) + { + StaticMeshToRawMeshes(StaticMeshComponent, OverallMaxLODs, ComponentToWorld, PackageName, RawMeshTrackers, RawMeshes, Materials); + } + } + + uint32 MaxInUseTextureCoordinate = 0; + + // scrub invalid vert color & tex coord data + check(RawMeshes.Num() == RawMeshTrackers.Num()); + for (int32 RawMeshIndex = 0; RawMeshIndex < RawMeshes.Num(); RawMeshIndex++) + { + if (!RawMeshTrackers[RawMeshIndex].bValidColors) + { + RawMeshes[RawMeshIndex].WedgeColors.Empty(); + } + + for (uint32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; TexCoordIndex++) + { + if (!RawMeshTrackers[RawMeshIndex].bValidTexCoords[TexCoordIndex]) + { + RawMeshes[RawMeshIndex].WedgeTexCoords[TexCoordIndex].Empty(); + } + else + { + // Store first texture coordinate index not in use + MaxInUseTextureCoordinate = FMath::Max(MaxInUseTextureCoordinate, TexCoordIndex); + } + } + } + + // Check if we got some valid data. + bool bValidData = false; + for (FRawMesh& RawMesh : RawMeshes) + { + if (RawMesh.IsValidOrFixable()) + { + bValidData = true; + break; + } + } + + if (bValidData) + { + // Then find/create it. + UPackage* Package = CreatePackage(*PackageName); + check(Package); + + // Create StaticMesh object + StaticMesh = NewObject(Package, *MeshName, RF_Public | RF_Standalone); + StaticMesh->InitResources(); + + StaticMesh->SetLightingGuid(); + + // Determine which texture coordinate map should be used for storing/generating the lightmap UVs + const uint32 LightMapIndex = FMath::Min(MaxInUseTextureCoordinate + 1, (uint32)MAX_MESH_TEXTURE_COORDS - 1); + + // Add source to new StaticMesh + for (FRawMesh& RawMesh : RawMeshes) + { + if (RawMesh.IsValidOrFixable()) + { + // FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel(); + // SrcModel.BuildSettings.bRecomputeNormals = false; + // SrcModel.BuildSettings.bRecomputeTangents = false; + // SrcModel.BuildSettings.bRemoveDegenerates = true; + // SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false; + // SrcModel.BuildSettings.bUseFullPrecisionUVs = false; + // SrcModel.BuildSettings.bGenerateLightmapUVs = true; + // SrcModel.BuildSettings.SrcLightmapIndex = 0; + // SrcModel.BuildSettings.DstLightmapIndex = LightMapIndex; + // SrcModel.SaveRawMesh(RawMesh); + } + } + + // Copy materials to new mesh + for (UMaterialInterface* Material : Materials) + { + StaticMesh->GetStaticMaterials().Add(FStaticMaterial(Material)); + } + + // //Set the Imported version before calling the build + // StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion; + // + // // Set light map coordinate index to match DstLightmapIndex + // StaticMesh->SetLightMapCoordinateIndex(LightMapIndex); + // + // // setup section info map + // for (int32 RawMeshLODIndex = 0; RawMeshLODIndex < RawMeshes.Num(); RawMeshLODIndex++) + // { + // const FRawMesh& RawMesh = RawMeshes[RawMeshLODIndex]; + // TArray UniqueMaterialIndices; + // for (int32 MaterialIndex : RawMesh.FaceMaterialIndices) + // { + // UniqueMaterialIndices.AddUnique(MaterialIndex); + // } + // + // int32 SectionIndex = 0; + // for (int32 UniqueMaterialIndex : UniqueMaterialIndices) + // { + // StaticMesh->GetSectionInfoMap().Set(RawMeshLODIndex, SectionIndex, FMeshSectionInfo(UniqueMaterialIndex)); + // SectionIndex++; + // } + // } + // StaticMesh->GetOriginalSectionInfoMap().CopyFrom(StaticMesh->GetSectionInfoMap()); + // + // // Build mesh from source + // StaticMesh->Build(false); + // StaticMesh->PostEditChange(); + // + // StaticMesh->MarkPackageDirty(); + // + // // Notify asset registry of new asset + // FAssetRegistryModule::AssetCreated(StaticMesh); + // + // // Display notification so users can quickly access the mesh + // if (GIsEditor) + // { + // FNotificationInfo Info(FText::Format(LOCTEXT("SkeletalMeshConverted", "Successfully Converted Mesh"), FText::FromString(StaticMesh->GetName()))); + // Info.ExpireDuration = 8.0f; + // Info.bUseLargeFont = false; + // Info.Hyperlink = FSimpleDelegate::CreateLambda([=]() { GEditor->GetEditorSubsystem()->OpenEditorForAssets(TArray({ StaticMesh })); }); + // Info.HyperlinkText = FText::Format(LOCTEXT("OpenNewAnimationHyperlink", "Open {0}"), FText::FromString(StaticMesh->GetName())); + // TSharedPtr Notification = FSlateNotificationManager::Get().AddNotification(Info); + // if ( Notification.IsValid() ) + // { + // Notification->SetCompletionState( SNotificationItem::CS_Success ); + // } + // } + } + } + + return StaticMesh; +} + +// /** +// * Builds a renderable skeletal mesh LOD model. Note that the array of chunks +// * will be destroyed during this process! +// * @param LODModel Upon return contains a renderable skeletal mesh LOD model. +// * @param RefSkeleton The reference skeleton associated with the model. +// * @param Chunks Skinned mesh chunks from which to build the renderable model. +// * @param PointToOriginalMap Maps a vertex's RawPointIdx to its index at import time. +// */ +// void FMeshUtilities2::BuildSkeletalModelFromChunks(FSkeletalMeshLODModel& LODModel, const FReferenceSkeleton& RefSkeleton, TArray& Chunks, const TArray& PointToOriginalMap) +// { +// #if WITH_EDITORONLY_DATA +// // Clear out any data currently held in the LOD model. +// LODModel.Sections.Empty(); +// LODModel.NumVertices = 0; +// LODModel.IndexBuffer.Empty(); + +// // Setup the section and chunk arrays on the model. +// for (int32 ChunkIndex = 0; ChunkIndex < Chunks.Num(); ++ChunkIndex) +// { +// FSkinnedMeshChunk* SrcChunk = Chunks[ChunkIndex]; + +// FSkelMeshSection& Section = *new(LODModel.Sections) FSkelMeshSection(); +// Section.MaterialIndex = SrcChunk->MaterialIndex; +// Exchange(Section.BoneMap, SrcChunk->BoneMap); + +// Section.OriginalDataSectionIndex = SrcChunk->OriginalSectionIndex; +// Section.ChunkedParentSectionIndex = SrcChunk->ParentChunkSectionIndex; + +// // Update the active bone indices on the LOD model. +// for (int32 BoneIndex = 0; BoneIndex < Section.BoneMap.Num(); ++BoneIndex) +// { +// LODModel.ActiveBoneIndices.AddUnique(Section.BoneMap[BoneIndex]); +// } +// } + +// // ensure parent exists with incoming active bone indices, and the result should be sorted +// RefSkeleton.EnsureParentsExistAndSort(LODModel.ActiveBoneIndices); + +// // Reset 'final vertex to import vertex' map info +// LODModel.MeshToImportVertexMap.Empty(); +// LODModel.MaxImportVertex = 0; + +// // Keep track of index mapping to chunk vertex offsets +// TArray< TArray > VertexIndexRemap; +// VertexIndexRemap.Empty(LODModel.Sections.Num()); +// // Pack the chunk vertices into a single vertex buffer. +// TArray RawPointIndices; +// LODModel.NumVertices = 0; + +// int32 PrevMaterialIndex = -1; +// int32 CurrentChunkBaseVertexIndex = -1; // base vertex index for all chunks of the same material +// int32 CurrentChunkVertexCount = -1; // total vertex count for all chunks of the same material +// int32 CurrentVertexIndex = 0; // current vertex index added to the index buffer for all chunks of the same material + +// // rearrange the vert order to minimize the data fetched by the GPU +// for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) +// { +// if (IsInGameThread()) +// { +// GWarn->StatusUpdate(SectionIndex, LODModel.Sections.Num(), NSLOCTEXT("UnrealEd", "ProcessingSections", "Processing Sections")); +// } + +// FSkinnedMeshChunk* SrcChunk = Chunks[SectionIndex]; +// FSkelMeshSection& Section = LODModel.Sections[SectionIndex]; +// TArray& ChunkVertices = SrcChunk->Vertices; +// TArray& ChunkIndices = SrcChunk->Indices; + +// // Reorder the section index buffer for better vertex cache efficiency. +// CacheOptimizeIndexBuffer(ChunkIndices); + +// // Calculate the number of triangles in the section. Note that CacheOptimize may change the number of triangles in the index buffer! +// Section.NumTriangles = ChunkIndices.Num() / 3; +// TArray OriginalVertices; +// Exchange(ChunkVertices, OriginalVertices); +// ChunkVertices.AddUninitialized(OriginalVertices.Num()); + +// TArray IndexCache; +// IndexCache.AddUninitialized(ChunkVertices.Num()); +// FMemory::Memset(IndexCache.GetData(), INDEX_NONE, IndexCache.Num() * IndexCache.GetTypeSize()); +// int32 NextAvailableIndex = 0; +// // Go through the indices and assign them new values that are coherent where possible +// for (int32 Index = 0; Index < ChunkIndices.Num(); Index++) +// { +// const int32 OriginalIndex = ChunkIndices[Index]; +// const int32 CachedIndex = IndexCache[OriginalIndex]; + +// if (CachedIndex == INDEX_NONE) +// { +// // No new index has been allocated for this existing index, assign a new one +// ChunkIndices[Index] = NextAvailableIndex; +// // Mark what this index has been assigned to +// IndexCache[OriginalIndex] = NextAvailableIndex; +// NextAvailableIndex++; +// } +// else +// { +// // Reuse an existing index assignment +// ChunkIndices[Index] = CachedIndex; +// } +// // Reorder the vertices based on the new index assignment +// ChunkVertices[ChunkIndices[Index]] = OriginalVertices[OriginalIndex]; +// } +// } + +// // Build the arrays of rigid and soft vertices on the model's chunks. +// for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) +// { +// FSkelMeshSection& Section = LODModel.Sections[SectionIndex]; +// TArray& ChunkVertices = Chunks[SectionIndex]->Vertices; + +// if (IsInGameThread()) +// { +// // Only update status if in the game thread. When importing morph targets, this function can run in another thread +// GWarn->StatusUpdate(SectionIndex, LODModel.Sections.Num(), NSLOCTEXT("UnrealEd", "ProcessingChunks", "Processing Chunks")); +// } + +// CurrentVertexIndex = 0; +// CurrentChunkVertexCount = 0; +// PrevMaterialIndex = Section.MaterialIndex; + +// // Calculate the offset to this chunk's vertices in the vertex buffer. +// Section.BaseVertexIndex = CurrentChunkBaseVertexIndex = LODModel.NumVertices; + +// // Update the size of the vertex buffer. +// LODModel.NumVertices += ChunkVertices.Num(); + +// // Separate the section's vertices into rigid and soft vertices. +// TArray& ChunkVertexIndexRemap = *new(VertexIndexRemap)TArray(); +// ChunkVertexIndexRemap.AddUninitialized(ChunkVertices.Num()); + +// for (int32 VertexIndex = 0; VertexIndex < ChunkVertices.Num(); VertexIndex++) +// { +// const FSoftSkinBuildVertex& SoftVertex = ChunkVertices[VertexIndex]; + +// FSoftSkinVertex NewVertex; +// NewVertex.Position = SoftVertex.Position; +// NewVertex.TangentX = SoftVertex.TangentX; +// NewVertex.TangentY = SoftVertex.TangentY; +// NewVertex.TangentZ = SoftVertex.TangentZ; +// FMemory::Memcpy(NewVertex.UVs, SoftVertex.UVs, sizeof(FVector2D)*MAX_TEXCOORDS); +// NewVertex.Color = SoftVertex.Color; +// for (int32 i = 0; i < MAX_TOTAL_INFLUENCES; ++i) +// { +// // it only adds to the bone map if it has weight on it +// // BoneMap contains only the bones that has influence with weight of >0.f +// // so here, just make sure it is included before setting the data +// if (Section.BoneMap.IsValidIndex(SoftVertex.InfluenceBones[i])) +// { +// NewVertex.InfluenceBones[i] = SoftVertex.InfluenceBones[i]; +// NewVertex.InfluenceWeights[i] = SoftVertex.InfluenceWeights[i]; +// } +// } +// Section.SoftVertices.Add(NewVertex); +// ChunkVertexIndexRemap[VertexIndex] = (uint32)(Section.BaseVertexIndex + CurrentVertexIndex); +// CurrentVertexIndex++; +// // add the index to the original wedge point source of this vertex +// RawPointIndices.Add(SoftVertex.PointWedgeIdx); +// // Also remember import index +// const int32 RawVertIndex = PointToOriginalMap[SoftVertex.PointWedgeIdx]; +// LODModel.MeshToImportVertexMap.Add(RawVertIndex); +// LODModel.MaxImportVertex = FMath::Max(LODModel.MaxImportVertex, RawVertIndex); +// } + +// // update NumVertices +// Section.NumVertices = Section.SoftVertices.Num(); + +// // update max bone influences +// Section.CalcMaxBoneInfluences(); +// Section.CalcUse16BitBoneIndex(); + +// // Log info about the chunk. +// UE_LOG(LogSkeletalMesh, Verbose, TEXT("Section %u: %u vertices, %u active bones"), +// SectionIndex, +// Section.GetNumVertices(), +// Section.BoneMap.Num() +// ); +// } + +// // Copy raw point indices to LOD model. +// LODModel.RawPointIndices.RemoveBulkData(); +// if (RawPointIndices.Num()) +// { +// LODModel.RawPointIndices.Lock(LOCK_READ_WRITE); +// void* Dest = LODModel.RawPointIndices.Realloc(RawPointIndices.Num()); +// FMemory::Memcpy(Dest, RawPointIndices.GetData(), LODModel.RawPointIndices.GetBulkDataSize()); +// LODModel.RawPointIndices.Unlock(); +// } + +// // Finish building the sections. +// for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) +// { +// FSkelMeshSection& Section = LODModel.Sections[SectionIndex]; + +// const TArray& SectionIndices = Chunks[SectionIndex]->Indices; + +// Section.BaseIndex = LODModel.IndexBuffer.Num(); +// const int32 NumIndices = SectionIndices.Num(); +// const TArray& SectionVertexIndexRemap = VertexIndexRemap[SectionIndex]; +// for (int32 Index = 0; Index < NumIndices; Index++) +// { +// uint32 VertexIndex = SectionVertexIndexRemap[SectionIndices[Index]]; +// LODModel.IndexBuffer.Add(VertexIndex); +// } +// } + +// // Free the skinned mesh chunks which are no longer needed. +// for (int32 i = 0; i < Chunks.Num(); ++i) +// { +// delete Chunks[i]; +// Chunks[i] = NULL; +// } +// Chunks.Empty(); + +// // Compute the required bones for this model. +// USkeletalMesh::CalculateRequiredBones(LODModel, RefSkeleton, NULL); +// #endif // #if WITH_EDITORONLY_DATA +// } + +/*------------------------------------------------------------------------------ +Common functionality. +------------------------------------------------------------------------------*/ + +static int32 ComputeNumTexCoords(FRawMesh const& RawMesh, int32 MaxSupportedTexCoords) +{ + int32 NumWedges = RawMesh.WedgeIndices.Num(); + int32 NumTexCoords = 0; + for (int32 TexCoordIndex = 0; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; ++TexCoordIndex) + { + if (RawMesh.WedgeTexCoords[TexCoordIndex].Num() != NumWedges) + { + break; + } + NumTexCoords++; + } + return FMath::Min(NumTexCoords, MaxSupportedTexCoords); +} + +static inline FVector GetPositionForWedge(FRawMesh const& Mesh, int32 WedgeIndex) +{ + int32 VertexIndex = Mesh.WedgeIndices[WedgeIndex]; + return Mesh.VertexPositions[VertexIndex]; +} + +struct FMeshEdgeDef +{ + int32 Vertices[2]; + int32 Faces[2]; +}; + +/** +* This helper class builds the edge list for a mesh. It uses a hash of vertex +* positions to edges sharing that vertex to remove the n^2 searching of all +* previously added edges. This class is templatized so it can be used with +* either static mesh or skeletal mesh vertices +*/ +template +class TEdgeBuilder +{ +protected: + /** + * The list of indices to build the edge data from + */ + const TArray& Indices; + /** + * The array of verts for vertex position comparison + */ + const TArray& Vertices; + /** + * The array of edges to create + */ + TArray& Edges; + /** + * List of edges that start with a given vertex + */ + TMultiMap VertexToEdgeList; + + /** + * This function determines whether a given edge matches or not. It must be + * provided by derived classes since they have the specific information that + * this class doesn't know about (vertex info, influences, etc) + * + * @param Index1 The first index of the edge being checked + * @param Index2 The second index of the edge + * @param OtherEdge The edge to compare. Was found via the map + * + * @return true if the edge is a match, false otherwise + */ + virtual bool DoesEdgeMatch(int32 Index1, int32 Index2, FMeshEdgeDef* OtherEdge) = 0; + + /** + * Searches the list of edges to see if this one matches an existing and + * returns a pointer to it if it does + * + * @param Index1 the first index to check for + * @param Index2 the second index to check for + * + * @return NULL if no edge was found, otherwise the edge that was found + */ + inline FMeshEdgeDef* FindOppositeEdge(int32 Index1, int32 Index2) + { + FMeshEdgeDef* Edge = NULL; + // Search the hash for a corresponding vertex + WorkingEdgeList.Reset(); + VertexToEdgeList.MultiFind(Vertices[Index2].Position, WorkingEdgeList); + // Now search through the array for a match or not + for (int32 EdgeIndex = 0; EdgeIndex < WorkingEdgeList.Num() && Edge == NULL; + EdgeIndex++) + { + FMeshEdgeDef* OtherEdge = WorkingEdgeList[EdgeIndex]; + // See if this edge matches the passed in edge + if (OtherEdge != NULL && DoesEdgeMatch(Index1, Index2, OtherEdge)) + { + // We have a match + Edge = OtherEdge; + } + } + return Edge; + } + + /** + * Updates an existing edge if found or adds the new edge to the list + * + * @param Index1 the first index in the edge + * @param Index2 the second index in the edge + * @param Triangle the triangle that this edge was found in + */ + inline void AddEdge(int32 Index1, int32 Index2, int32 Triangle) + { + // If this edge matches another then just fill the other triangle + // otherwise add it + FMeshEdgeDef* OtherEdge = FindOppositeEdge(Index1, Index2); + if (OtherEdge == NULL) + { + // Add a new edge to the array + int32 EdgeIndex = Edges.AddZeroed(); + Edges[EdgeIndex].Vertices[0] = Index1; + Edges[EdgeIndex].Vertices[1] = Index2; + Edges[EdgeIndex].Faces[0] = Triangle; + Edges[EdgeIndex].Faces[1] = -1; + // Also add this edge to the hash for faster searches + // NOTE: This relies on the array never being realloced! + VertexToEdgeList.Add(Vertices[Index1].Position, &Edges[EdgeIndex]); + } + else + { + OtherEdge->Faces[1] = Triangle; + } + } + +public: + /** + * Initializes the values for the code that will build the mesh edge list + */ + TEdgeBuilder(const TArray& InIndices, + const TArray& InVertices, + TArray& OutEdges) : + Indices(InIndices), Vertices(InVertices), Edges(OutEdges) + { + // Presize the array so that there are no extra copies being done + // when adding edges to it + Edges.Empty(Indices.Num()); + } + + /** + * Virtual dtor + */ + virtual ~TEdgeBuilder() + { + } + + + /** + * Uses a hash of indices to edge lists so that it can avoid the n^2 search + * through the full edge list + */ + void FindEdges(void) + { + // @todo Handle something other than trilists when building edges + int32 TriangleCount = Indices.Num() / 3; + int32 EdgeCount = 0; + // Work through all triangles building the edges + for (int32 Triangle = 0; Triangle < TriangleCount; Triangle++) + { + // Determine the starting index + int32 TriangleIndex = Triangle * 3; + // Get the indices for the triangle + int32 Index1 = Indices[TriangleIndex]; + int32 Index2 = Indices[TriangleIndex + 1]; + int32 Index3 = Indices[TriangleIndex + 2]; + // Add the first to second edge + AddEdge(Index1, Index2, Triangle); + // Now add the second to third + AddEdge(Index2, Index3, Triangle); + // Add the third to first edge + AddEdge(Index3, Index1, Triangle); + } + } + +private: + TArray WorkingEdgeList; +}; + +/** +* This is the static mesh specific version for finding edges +*/ +class FStaticMeshEdgeBuilder : public TEdgeBuilder +{ +public: + /** + * Constructor that passes all work to the parent class + */ + FStaticMeshEdgeBuilder(const TArray& InIndices, + const TArray& InVertices, + TArray& OutEdges) : + TEdgeBuilder(InIndices, InVertices, OutEdges) + { + } + + /** + * This function determines whether a given edge matches or not for a static mesh + * + * @param Index1 The first index of the edge being checked + * @param Index2 The second index of the edge + * @param OtherEdge The edge to compare. Was found via the map + * + * @return true if the edge is a match, false otherwise + */ + bool DoesEdgeMatch(int32 Index1, int32 Index2, FMeshEdgeDef* OtherEdge) + { + return Vertices[OtherEdge->Vertices[1]].Position == Vertices[Index1].Position && + OtherEdge->Faces[1] == -1; + } +}; + +static void ComputeTriangleTangents( + const TArray& InVertices, + const TArray& InIndices, + const TArray& InUVs, + TArray& OutTangentX, + TArray& OutTangentY, + TArray& OutTangentZ, + float ComparisonThreshold +) +{ + const int32 NumTriangles = InIndices.Num() / 3; + OutTangentX.Empty(NumTriangles); + OutTangentY.Empty(NumTriangles); + OutTangentZ.Empty(NumTriangles); + + //Currently GetSafeNormal do not support 0.0f threshold properly + float RealComparisonThreshold = FMath::Max(ComparisonThreshold, FLT_MIN); + + TArray TriangleTangents; + for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; TriangleIndex++) + { + FVector Positions[3]; + FVector2D UVs[3]; + for (int32 Index = 0; Index < 3; ++Index) + { + Positions[Index] = InVertices[InIndices[TriangleIndex * 3 + Index]]; + UVs[Index] = InUVs[TriangleIndex * 3 + Index]; + } + TriangleTangents.Reset(); + CalculateTriangleTangentInternal(Positions[0], UVs[0], Positions[1], UVs[1], Positions[2], UVs[2], TriangleTangents, RealComparisonThreshold); + check(TriangleTangents.Num() == 3); + OutTangentX.Add(TriangleTangents[0]); + OutTangentY.Add(TriangleTangents[1]); + OutTangentZ.Add(TriangleTangents[2]); + } + + check(OutTangentX.Num() == NumTriangles); + check(OutTangentY.Num() == NumTriangles); + check(OutTangentZ.Num() == NumTriangles); +} + +static void ComputeTriangleTangents( + TArray& OutTangentX, + TArray& OutTangentY, + TArray& OutTangentZ, + FRawMesh const& RawMesh, + float ComparisonThreshold +) +{ + ComputeTriangleTangents(RawMesh.VertexPositions, RawMesh.WedgeIndices, RawMesh.WedgeTexCoords[0], OutTangentX, OutTangentY, OutTangentZ, ComparisonThreshold); +} + +/** +* Create a table that maps the corner of each face to its overlapping corners. +* @param OutOverlappingCorners - Maps a corner index to the indices of all overlapping corners. +* @param InVertices - Triangle vertex positions for the mesh for which to compute overlapping corners. +* @param InIndices - Triangle indices for the mesh for which to compute overlapping corners. +* @param ComparisonThreshold - Positions are considered equal if all absolute differences between their X, Y and Z coordinates are less or equal to this value. +*/ +void FMeshUtilities2::FindOverlappingCorners( + FOverlappingCorners& OutOverlappingCorners, + const TArray& InVertices, + const TArray& InIndices, + float ComparisonThreshold) const +{ + OutOverlappingCorners = FOverlappingCorners(InVertices, InIndices, ComparisonThreshold); +} + +/** +* Create a table that maps the corner of each face to its overlapping corners. +* @param OutOverlappingCorners - Maps a corner index to the indices of all overlapping corners. +* @param RawMesh - The mesh for which to compute overlapping corners. +* @param ComparisonThreshold - Positions are considered equal if all absolute differences between their X, Y and Z coordinates are less or equal to this value. +*/ +void FMeshUtilities2::FindOverlappingCorners( + FOverlappingCorners& OutOverlappingCorners, + FRawMesh const& RawMesh, + float ComparisonThreshold +) const +{ + OutOverlappingCorners = FOverlappingCorners(RawMesh.VertexPositions, RawMesh.WedgeIndices, ComparisonThreshold); +} + +/** +* Smoothing group interpretation helper structure. +*/ +struct FFanFace +{ + int32 FaceIndex; + int32 LinkedVertexIndex; + bool bFilled; + bool bBlendTangents; + bool bBlendNormals; +}; + +static void ComputeTangents( + const TArray& InVertices, + const TArray& InIndices, + const TArray& InUVs, + const TArray& SmoothingGroupIndices, + const FOverlappingCorners& OverlappingCorners, + TArray& OutTangentX, + TArray& OutTangentY, + TArray& OutTangentZ, + const uint32 TangentOptions +) +{ + bool bBlendOverlappingNormals = (TangentOptions & ETangentOptions2::BlendOverlappingNormals) != 0; + bool bIgnoreDegenerateTriangles = (TangentOptions & ETangentOptions2::IgnoreDegenerateTriangles) != 0; + float ComparisonThreshold = bIgnoreDegenerateTriangles ? THRESH_POINTS_ARE_SAME : 0.0f; + + // Compute per-triangle tangents. + TArray TriangleTangentX; + TArray TriangleTangentY; + TArray TriangleTangentZ; + + ComputeTriangleTangents( + InVertices, + InIndices, + InUVs, + TriangleTangentX, + TriangleTangentY, + TriangleTangentZ, + bIgnoreDegenerateTriangles ? SMALL_NUMBER : FLT_MIN + ); + + // Declare these out here to avoid reallocations. + TArray RelevantFacesForCorner[3]; + TArray AdjacentFaces; + + int32 NumWedges = InIndices.Num(); + int32 NumFaces = NumWedges / 3; + + // Allocate storage for tangents if none were provided. + if (OutTangentX.Num() != NumWedges) + { + OutTangentX.Empty(NumWedges); + OutTangentX.AddZeroed(NumWedges); + } + if (OutTangentY.Num() != NumWedges) + { + OutTangentY.Empty(NumWedges); + OutTangentY.AddZeroed(NumWedges); + } + if (OutTangentZ.Num() != NumWedges) + { + OutTangentZ.Empty(NumWedges); + OutTangentZ.AddZeroed(NumWedges); + } + + for (int32 FaceIndex = 0; FaceIndex < NumFaces; FaceIndex++) + { + int32 WedgeOffset = FaceIndex * 3; + FVector CornerPositions[3]; + FVector CornerTangentX[3]; + FVector CornerTangentY[3]; + FVector CornerTangentZ[3]; + + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + CornerTangentX[CornerIndex] = FVector::ZeroVector; + CornerTangentY[CornerIndex] = FVector::ZeroVector; + CornerTangentZ[CornerIndex] = FVector::ZeroVector; + CornerPositions[CornerIndex] = InVertices[InIndices[WedgeOffset + CornerIndex]]; + RelevantFacesForCorner[CornerIndex].Reset(); + } + + // Don't process degenerate triangles. + if (PointsEqual(CornerPositions[0], CornerPositions[1], ComparisonThreshold) + || PointsEqual(CornerPositions[0], CornerPositions[2], ComparisonThreshold) + || PointsEqual(CornerPositions[1], CornerPositions[2], ComparisonThreshold)) + { + continue; + } + + // No need to process triangles if tangents already exist. + bool bCornerHasTangents[3] = {0}; + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + bCornerHasTangents[CornerIndex] = !OutTangentX[WedgeOffset + CornerIndex].IsZero() + && !OutTangentY[WedgeOffset + CornerIndex].IsZero() + && !OutTangentZ[WedgeOffset + CornerIndex].IsZero(); + } + if (bCornerHasTangents[0] && bCornerHasTangents[1] && bCornerHasTangents[2]) + { + continue; + } + + // Calculate smooth vertex normals. + float Determinant = FVector::Triple( + TriangleTangentX[FaceIndex], + TriangleTangentY[FaceIndex], + TriangleTangentZ[FaceIndex] + ); + + // Start building a list of faces adjacent to this face. + AdjacentFaces.Reset(); + for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) + { + int32 ThisCornerIndex = WedgeOffset + CornerIndex; + const TArray& DupVerts = OverlappingCorners.FindIfOverlapping(ThisCornerIndex); + for (int32 k = 0, nk = DupVerts.Num(); k < nk; k++) + { + AdjacentFaces.Add(DupVerts[k] / 3); + } + if (DupVerts.Num() == 0) + { + AdjacentFaces.Add(ThisCornerIndex / 3); // I am a "dup" of myself + } + } + + // We need to sort these here because the criteria for point equality is + // exact, so we must ensure the exact same order for all dups. + AdjacentFaces.Sort(); + + // Process adjacent faces + int32 LastIndex = -1; + for (int32 OtherFaceIndex : AdjacentFaces) + { + if (LastIndex == OtherFaceIndex) + { + continue; + } + + LastIndex = OtherFaceIndex; + + for (int32 OurCornerIndex = 0; OurCornerIndex < 3; ++OurCornerIndex) + { + if (bCornerHasTangents[OurCornerIndex]) + continue; + + FFanFace NewFanFace; + int32 CommonIndexCount = 0; + + // Check for vertices in common. + if (FaceIndex == OtherFaceIndex) + { + CommonIndexCount = 3; + NewFanFace.LinkedVertexIndex = OurCornerIndex; + } + else + { + // Check matching vertices against main vertex . + for (int32 OtherCornerIndex = 0; OtherCornerIndex < 3; ++OtherCornerIndex) + { + if (CornerPositions[OurCornerIndex].Equals(InVertices[InIndices[OtherFaceIndex * 3 + OtherCornerIndex]], ComparisonThreshold)) + { + CommonIndexCount++; + NewFanFace.LinkedVertexIndex = OtherCornerIndex; + } + } + } + + // Add if connected by at least one point. Smoothing matches are considered later. + if (CommonIndexCount > 0) + { + NewFanFace.FaceIndex = OtherFaceIndex; + NewFanFace.bFilled = (OtherFaceIndex == FaceIndex); // Starter face for smoothing floodfill. + NewFanFace.bBlendTangents = NewFanFace.bFilled; + NewFanFace.bBlendNormals = NewFanFace.bFilled; + RelevantFacesForCorner[OurCornerIndex].Add(NewFanFace); + } + } + } + + // Find true relevance of faces for a vertex normal by traversing + // smoothing-group-compatible connected triangle fans around common vertices. + for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) + { + if (bCornerHasTangents[CornerIndex]) + continue; + + int32 NewConnections; + do + { + NewConnections = 0; + for (int32 OtherFaceIdx = 0, ni = RelevantFacesForCorner[CornerIndex].Num(); OtherFaceIdx < ni; ++OtherFaceIdx) + { + FFanFace& OtherFace = RelevantFacesForCorner[CornerIndex][OtherFaceIdx]; + // The vertex' own face is initially the only face with bFilled == true. + if (OtherFace.bFilled) + { + for (int32 NextFaceIndex = 0, nk = RelevantFacesForCorner[CornerIndex].Num(); NextFaceIndex < nk; ++NextFaceIndex) + { + FFanFace& NextFace = RelevantFacesForCorner[CornerIndex][NextFaceIndex]; + if (!NextFace.bFilled) // && !NextFace.bBlendTangents) + { + if ((NextFaceIndex != OtherFaceIdx) + && (SmoothingGroupIndices[NextFace.FaceIndex] & SmoothingGroupIndices[OtherFace.FaceIndex])) + { + int32 CommonVertices = 0; + int32 CommonTangentVertices = 0; + int32 CommonNormalVertices = 0; + for (int32 OtherCornerIndex = 0; OtherCornerIndex < 3; ++OtherCornerIndex) + { + for (int32 NextCornerIndex = 0; NextCornerIndex < 3; ++NextCornerIndex) + { + int32 NextVertexIndex = InIndices[NextFace.FaceIndex * 3 + NextCornerIndex]; + int32 OtherVertexIndex = InIndices[OtherFace.FaceIndex * 3 + OtherCornerIndex]; + if (PointsEqual( + InVertices[NextVertexIndex], + InVertices[OtherVertexIndex], + ComparisonThreshold)) + { + CommonVertices++; + + + const FVector2D& UVOne = InUVs[NextFace.FaceIndex * 3 + NextCornerIndex]; + const FVector2D& UVTwo = InUVs[OtherFace.FaceIndex * 3 + OtherCornerIndex]; + + if (UVsEqual(UVOne, UVTwo)) + { + CommonTangentVertices++; + } + if (bBlendOverlappingNormals + || NextVertexIndex == OtherVertexIndex) + { + CommonNormalVertices++; + } + } + } + } + // Flood fill faces with more than one common vertices which must be touching edges. + if (CommonVertices > 1) + { + NextFace.bFilled = true; + NextFace.bBlendNormals = (CommonNormalVertices > 1); + NewConnections++; + + // Only blend tangents if there is no UV seam along the edge with this face. + if (OtherFace.bBlendTangents && CommonTangentVertices > 1) + { + float OtherDeterminant = FVector::Triple( + TriangleTangentX[NextFace.FaceIndex], + TriangleTangentY[NextFace.FaceIndex], + TriangleTangentZ[NextFace.FaceIndex] + ); + if ((Determinant * OtherDeterminant) > 0.0f) + { + NextFace.bBlendTangents = true; + } + } + } + } + } + } + } + } + } + while (NewConnections > 0); + } + + // Vertex normal construction. + for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) + { + if (bCornerHasTangents[CornerIndex]) + { + CornerTangentX[CornerIndex] = OutTangentX[WedgeOffset + CornerIndex]; + CornerTangentY[CornerIndex] = OutTangentY[WedgeOffset + CornerIndex]; + CornerTangentZ[CornerIndex] = OutTangentZ[WedgeOffset + CornerIndex]; + } + else + { + for (int32 RelevantFaceIdx = 0; RelevantFaceIdx < RelevantFacesForCorner[CornerIndex].Num(); ++RelevantFaceIdx) + { + FFanFace const& RelevantFace = RelevantFacesForCorner[CornerIndex][RelevantFaceIdx]; + if (RelevantFace.bFilled) + { + int32 OtherFaceIndex = RelevantFace.FaceIndex; + if (RelevantFace.bBlendTangents) + { + CornerTangentX[CornerIndex] += TriangleTangentX[OtherFaceIndex]; + CornerTangentY[CornerIndex] += TriangleTangentY[OtherFaceIndex]; + } + if (RelevantFace.bBlendNormals) + { + CornerTangentZ[CornerIndex] += TriangleTangentZ[OtherFaceIndex]; + } + } + } + if (!OutTangentX[WedgeOffset + CornerIndex].IsZero()) + { + CornerTangentX[CornerIndex] = OutTangentX[WedgeOffset + CornerIndex]; + } + if (!OutTangentY[WedgeOffset + CornerIndex].IsZero()) + { + CornerTangentY[CornerIndex] = OutTangentY[WedgeOffset + CornerIndex]; + } + if (!OutTangentZ[WedgeOffset + CornerIndex].IsZero()) + { + CornerTangentZ[CornerIndex] = OutTangentZ[WedgeOffset + CornerIndex]; + } + } + } + + // Normalization. + for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) + { + CornerTangentX[CornerIndex].Normalize(); + CornerTangentY[CornerIndex].Normalize(); + CornerTangentZ[CornerIndex].Normalize(); + + // Gram-Schmidt orthogonalization + CornerTangentY[CornerIndex] -= CornerTangentX[CornerIndex] * (CornerTangentX[CornerIndex] | CornerTangentY[CornerIndex]); + CornerTangentY[CornerIndex].Normalize(); + + CornerTangentX[CornerIndex] -= CornerTangentZ[CornerIndex] * (CornerTangentZ[CornerIndex] | CornerTangentX[CornerIndex]); + CornerTangentX[CornerIndex].Normalize(); + CornerTangentY[CornerIndex] -= CornerTangentZ[CornerIndex] * (CornerTangentZ[CornerIndex] | CornerTangentY[CornerIndex]); + CornerTangentY[CornerIndex].Normalize(); + } + + // Copy back to the mesh. + for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) + { + OutTangentX[WedgeOffset + CornerIndex] = CornerTangentX[CornerIndex]; + OutTangentY[WedgeOffset + CornerIndex] = CornerTangentY[CornerIndex]; + OutTangentZ[WedgeOffset + CornerIndex] = CornerTangentZ[CornerIndex]; + } + } + + check(OutTangentX.Num() == NumWedges); + check(OutTangentY.Num() == NumWedges); + check(OutTangentZ.Num() == NumWedges); +} + + +static void ComputeTangents( + FRawMesh& RawMesh, + const FOverlappingCorners& OverlappingCorners, + uint32 TangentOptions +) +{ + ComputeTangents(RawMesh.VertexPositions, RawMesh.WedgeIndices, RawMesh.WedgeTexCoords[0], RawMesh.FaceSmoothingMasks, OverlappingCorners, RawMesh.WedgeTangentX, RawMesh.WedgeTangentY, RawMesh.WedgeTangentZ, TangentOptions); +} + +/*------------------------------------------------------------------------------ +MikkTSpace for computing tangents. +------------------------------------------------------------------------------*/ +class MikkTSpace_Mesh +{ +public: + const TArray& Vertices; + const TArray& Indices; + const TArray& UVs; + + TArray& TangentsX; //Reference to newly created tangents list. + TArray& TangentsY; //Reference to newly created bitangents list. + const TArray& TangentsZ; //Reference to computed normals, will be empty otherwise. + + MikkTSpace_Mesh( + const TArray& InVertices, + const TArray& InIndices, + const TArray& InUVs, + TArray& InOutVertexTangentsX, + TArray& InOutVertexTangentsY, + const TArray& InVertexTangentsZ + ) + : + Vertices(InVertices), + Indices(InIndices), + UVs(InUVs), + TangentsX(InOutVertexTangentsX), + TangentsY(InOutVertexTangentsY), + TangentsZ(InVertexTangentsZ) + { + } +}; + +static int MikkGetNumFaces(const SMikkTSpaceContext* Context) +{ + MikkTSpace_Mesh* UserData = (MikkTSpace_Mesh*)(Context->m_pUserData); + return UserData->Indices.Num() / 3; +} + +static int MikkGetNumVertsOfFace(const SMikkTSpaceContext* Context, const int FaceIdx) +{ + // All of our meshes are triangles. + return 3; +} + +static void MikkGetPosition(const SMikkTSpaceContext* Context, float Position[3], const int FaceIdx, const int VertIdx) +{ + MikkTSpace_Mesh* UserData = (MikkTSpace_Mesh*)(Context->m_pUserData); + FVector VertexPosition = UserData->Vertices[UserData->Indices[FaceIdx * 3 + VertIdx]]; + Position[0] = VertexPosition.X; + Position[1] = VertexPosition.Y; + Position[2] = VertexPosition.Z; +} + +static void MikkGetNormal(const SMikkTSpaceContext* Context, float Normal[3], const int FaceIdx, const int VertIdx) +{ + MikkTSpace_Mesh* UserData = (MikkTSpace_Mesh*)(Context->m_pUserData); + const FVector& VertexNormal = UserData->TangentsZ[FaceIdx * 3 + VertIdx]; + for (int32 i = 0; i < 3; ++i) + { + Normal[i] = VertexNormal[i]; + } +} + +static void MikkSetTSpaceBasic(const SMikkTSpaceContext* Context, const float Tangent[3], const float BitangentSign, const int FaceIdx, const int VertIdx) +{ + MikkTSpace_Mesh* UserData = (MikkTSpace_Mesh*)(Context->m_pUserData); + FVector& VertexTangent = UserData->TangentsX[FaceIdx * 3 + VertIdx]; + for (int32 i = 0; i < 3; ++i) + { + VertexTangent[i] = Tangent[i]; + } + FVector Bitangent = BitangentSign * FVector::CrossProduct(UserData->TangentsZ[FaceIdx * 3 + VertIdx], VertexTangent); + FVector& VertexBitangent = UserData->TangentsY[FaceIdx * 3 + VertIdx]; + for (int32 i = 0; i < 3; ++i) + { + VertexBitangent[i] = -Bitangent[i]; + } +} + +static void MikkGetTexCoord(const SMikkTSpaceContext* Context, float UV[2], const int FaceIdx, const int VertIdx) +{ + MikkTSpace_Mesh* UserData = (MikkTSpace_Mesh*)(Context->m_pUserData); + const FVector2D& TexCoord = UserData->UVs[FaceIdx * 3 + VertIdx]; + UV[0] = TexCoord.X; + UV[1] = TexCoord.Y; +} + +// // MikkTSpace implementations for skeletal meshes, where tangents/bitangents are ultimately derived from lists of attributes. + +// // Holder for skeletal data to be passed to MikkTSpace. +// // Holds references to the wedge, face and points vectors that BuildSkeletalMesh is given. +// // Holds reference to the calculated normals array, which will be fleshed out if they've been calculated. +// // Holds reference to the newly created tangent and bitangent arrays, which MikkTSpace will fleshed out if required. +// class MikkTSpace_Skeletal_Mesh +// { +// public: +// const TArray &wedges; //Reference to wedge list. +// const TArray &faces; //Reference to face list. Also contains normal/tangent/bitanget/UV coords for each vertex of the face. +// const TArray &points; //Reference to position list. +// bool bComputeNormals; //Copy of bComputeNormals. +// TArray &TangentsX; //Reference to newly created tangents list. +// TArray &TangentsY; //Reference to newly created bitangents list. +// TArray &TangentsZ; //Reference to computed normals, will be empty otherwise. + +// MikkTSpace_Skeletal_Mesh( +// const TArray &Wedges, +// const TArray &Faces, +// const TArray &Points, +// bool bInComputeNormals, +// TArray &VertexTangentsX, +// TArray &VertexTangentsY, +// TArray &VertexTangentsZ +// ) +// : +// wedges(Wedges), +// faces(Faces), +// points(Points), +// bComputeNormals(bInComputeNormals), +// TangentsX(VertexTangentsX), +// TangentsY(VertexTangentsY), +// TangentsZ(VertexTangentsZ) +// { +// } +// }; + +// static int MikkGetNumFaces_Skeletal(const SMikkTSpaceContext* Context) +// { +// MikkTSpace_Skeletal_Mesh *UserData = (MikkTSpace_Skeletal_Mesh*)(Context->m_pUserData); +// return UserData->faces.Num(); +// } + +static int MikkGetNumVertsOfFace_Skeletal(const SMikkTSpaceContext* Context, const int FaceIdx) +{ + // Confirmed? + return 3; +} + +// +// static void MikkGetPosition_Skeletal(const SMikkTSpaceContext* Context, float Position[3], const int FaceIdx, const int VertIdx) +// { +// MikkTSpace_Skeletal_Mesh *UserData = (MikkTSpace_Skeletal_Mesh*)(Context->m_pUserData); +// const FVector &VertexPosition = UserData->points[UserData->wedges[UserData->faces[FaceIdx].iWedge[VertIdx]].iVertex]; +// Position[0] = VertexPosition.X; +// Position[1] = VertexPosition.Y; +// Position[2] = VertexPosition.Z; +// } +// +// static void MikkGetNormal_Skeletal(const SMikkTSpaceContext* Context, float Normal[3], const int FaceIdx, const int VertIdx) +// { +// MikkTSpace_Skeletal_Mesh *UserData = (MikkTSpace_Skeletal_Mesh*)(Context->m_pUserData); +// // Get different normals depending on whether they've been calculated or not. +// if (UserData->bComputeNormals) { +// FVector &VertexNormal = UserData->TangentsZ[FaceIdx * 3 + VertIdx]; +// Normal[0] = VertexNormal.X; +// Normal[1] = VertexNormal.Y; +// Normal[2] = VertexNormal.Z; +// } +// else +// { +// const FVector &VertexNormal = UserData->faces[FaceIdx].TangentZ[VertIdx]; +// Normal[0] = VertexNormal.X; +// Normal[1] = VertexNormal.Y; +// Normal[2] = VertexNormal.Z; +// } +// } +// +// static void MikkSetTSpaceBasic_Skeletal(const SMikkTSpaceContext* Context, const float Tangent[3], const float BitangentSign, const int FaceIdx, const int VertIdx) +// { +// MikkTSpace_Skeletal_Mesh *UserData = (MikkTSpace_Skeletal_Mesh*)(Context->m_pUserData); +// FVector &VertexTangent = UserData->TangentsX[FaceIdx * 3 + VertIdx]; +// VertexTangent.X = Tangent[0]; +// VertexTangent.Y = Tangent[1]; +// VertexTangent.Z = Tangent[2]; +// +// FVector Bitangent; +// // Get different normals depending on whether they've been calculated or not. +// if (UserData->bComputeNormals) { +// Bitangent = BitangentSign * FVector::CrossProduct(UserData->TangentsZ[FaceIdx * 3 + VertIdx], VertexTangent); +// } +// else +// { +// Bitangent = BitangentSign * FVector::CrossProduct(UserData->faces[FaceIdx].TangentZ[VertIdx], VertexTangent); +// } +// FVector &VertexBitangent = UserData->TangentsY[FaceIdx * 3 + VertIdx]; +// // Switch the tangent space swizzle to X+Y-Z+ for legacy reasons. +// VertexBitangent.X = -Bitangent[0]; +// VertexBitangent.Y = -Bitangent[1]; +// VertexBitangent.Z = -Bitangent[2]; +// } +// +// static void MikkGetTexCoord_Skeletal(const SMikkTSpaceContext* Context, float UV[2], const int FaceIdx, const int VertIdx) +// { +// MikkTSpace_Skeletal_Mesh *UserData = (MikkTSpace_Skeletal_Mesh*)(Context->m_pUserData); +// const FVector2D &TexCoord = UserData->wedges[UserData->faces[FaceIdx].iWedge[VertIdx]].UVs[0]; +// UV[0] = TexCoord.X; +// UV[1] = TexCoord.Y; +// } +// +// static void ComputeNormals( +// const TArray& InVertices, +// const TArray& InIndices, +// const TArray& InUVs, +// const TArray& SmoothingGroupIndices, +// const FOverlappingCorners& OverlappingCorners, +// TArray& OutTangentZ, +// const uint32 TangentOptions +// ) +// { +// bool bBlendOverlappingNormals = (TangentOptions & ETangentOptions::BlendOverlappingNormals) != 0; +// bool bIgnoreDegenerateTriangles = (TangentOptions & ETangentOptions::IgnoreDegenerateTriangles) != 0; +// float ComparisonThreshold = bIgnoreDegenerateTriangles ? THRESH_POINTS_ARE_SAME : 0.0f; +// +// // Compute per-triangle tangents. +// TArray TriangleTangentX; +// TArray TriangleTangentY; +// TArray TriangleTangentZ; +// +// ComputeTriangleTangents( +// InVertices, +// InIndices, +// InUVs, +// TriangleTangentX, +// TriangleTangentY, +// TriangleTangentZ, +// bIgnoreDegenerateTriangles ? SMALL_NUMBER : FLT_MIN +// ); +// +// // Declare these out here to avoid reallocations. +// TArray RelevantFacesForCorner[3]; +// // TArray AdjacentFaces; +// +// int32 NumWedges = InIndices.Num(); +// int32 NumFaces = NumWedges / 3; +// +// // Allocate storage for tangents if none were provided, and calculate normals for MikkTSpace. +// if (OutTangentZ.Num() != NumWedges) +// { +// // normals are not included, so we should calculate them +// OutTangentZ.Empty(NumWedges); +// OutTangentZ.AddZeroed(NumWedges); +// } +// +// // we need to calculate normals for MikkTSpace +// for (int32 FaceIndex = 0; FaceIndex < NumFaces; FaceIndex++) +// { +// int32 WedgeOffset = FaceIndex * 3; +// FVector CornerPositions[3]; +// FVector CornerNormal[3]; +// +// for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) +// { +// CornerNormal[CornerIndex] = FVector::ZeroVector; +// CornerPositions[CornerIndex] = InVertices[InIndices[WedgeOffset + CornerIndex]]; +// RelevantFacesForCorner[CornerIndex].Reset(); +// } +// +// // Don't process degenerate triangles. +// if (PointsEqual(CornerPositions[0], CornerPositions[1], ComparisonThreshold) +// || PointsEqual(CornerPositions[0], CornerPositions[2], ComparisonThreshold) +// || PointsEqual(CornerPositions[1], CornerPositions[2], ComparisonThreshold)) +// { +// continue; +// } +// +// // No need to process triangles if tangents already exist. +// bool bCornerHasNormal[3] = { 0 }; +// for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) +// { +// bCornerHasNormal[CornerIndex] = !OutTangentZ[WedgeOffset + CornerIndex].IsZero(); +// } +// if (bCornerHasNormal[0] && bCornerHasNormal[1] && bCornerHasNormal[2]) +// { +// continue; +// } +// +// // Start building a list of faces adjacent to this face. +// // AdjacentFaces.Reset(); +// TSet AdjacentFaces; +// for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) +// { +// int32 ThisCornerIndex = WedgeOffset + CornerIndex; +// const TArray& DupVerts = OverlappingCorners.FindIfOverlapping(ThisCornerIndex); +// if (DupVerts.Num() == 0) +// { +// // AdjacentFaces.AddUnique(ThisCornerIndex / 3); // I am a "dup" of myself +// AdjacentFaces.Add(ThisCornerIndex / 3); // I am a "dup" of myself +// } +// for (int32 k = 0; k < DupVerts.Num(); k++) +// { +// AdjacentFaces.Add(DupVerts[k] / 3); +// } +// } +// +// // We need to sort these here because the criteria for point equality is +// // exact, so we must ensure the exact same order for all dups. +// // AdjacentFaces.Sort(); +// +// // Process adjacent faces +// // for (int32 AdjacentFaceIndex = 0; AdjacentFaceIndex < AdjacentFaces.Num(); AdjacentFaceIndex++) +// for (int32 OtherFaceIndex : AdjacentFaces ) +// { +// // int32 OtherFaceIndex = AdjacentFaces[AdjacentFaceIndex]; +// for (int32 OurCornerIndex = 0; OurCornerIndex < 3; ++OurCornerIndex) +// { +// if (bCornerHasNormal[OurCornerIndex]) +// continue; +// +// FFanFace NewFanFace; +// int32 CommonIndexCount = 0; +// +// // Check for vertices in common. +// if (FaceIndex == OtherFaceIndex) +// { +// CommonIndexCount = 3; +// NewFanFace.LinkedVertexIndex = OurCornerIndex; +// } +// else +// { +// // Check matching vertices against main vertex . +// for (int32 OtherCornerIndex = 0; OtherCornerIndex < 3; OtherCornerIndex++) +// { +// if (PointsEqual( +// CornerPositions[OurCornerIndex], +// InVertices[InIndices[OtherFaceIndex * 3 + OtherCornerIndex]], +// ComparisonThreshold +// )) +// { +// CommonIndexCount++; +// NewFanFace.LinkedVertexIndex = OtherCornerIndex; +// } +// } +// } +// +// // Add if connected by at least one point. Smoothing matches are considered later. +// if (CommonIndexCount > 0) +// { +// NewFanFace.FaceIndex = OtherFaceIndex; +// NewFanFace.bFilled = (OtherFaceIndex == FaceIndex); // Starter face for smoothing floodfill. +// NewFanFace.bBlendTangents = NewFanFace.bFilled; +// NewFanFace.bBlendNormals = NewFanFace.bFilled; +// RelevantFacesForCorner[OurCornerIndex].Add(NewFanFace); +// } +// } +// } +// +// // Find true relevance of faces for a vertex normal by traversing +// // smoothing-group-compatible connected triangle fans around common vertices. +// for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) +// { +// if (bCornerHasNormal[CornerIndex]) +// continue; +// +// int32 NewConnections; +// do +// { +// NewConnections = 0; +// for (int32 OtherFaceIdx = 0; OtherFaceIdx < RelevantFacesForCorner[CornerIndex].Num(); OtherFaceIdx++) +// { +// FFanFace& OtherFace = RelevantFacesForCorner[CornerIndex][OtherFaceIdx]; +// // The vertex' own face is initially the only face with bFilled == true. +// if (OtherFace.bFilled) +// { +// for (int32 NextFaceIndex = 0; NextFaceIndex < RelevantFacesForCorner[CornerIndex].Num(); NextFaceIndex++) +// { +// FFanFace& NextFace = RelevantFacesForCorner[CornerIndex][NextFaceIndex]; +// if (!NextFace.bFilled) // && !NextFace.bBlendTangents) +// { +// if ((NextFaceIndex != OtherFaceIdx) +// && (SmoothingGroupIndices[NextFace.FaceIndex] & SmoothingGroupIndices[OtherFace.FaceIndex])) +// { +// int32 CommonVertices = 0; +// int32 CommonNormalVertices = 0; +// for (int32 OtherCornerIndex = 0; OtherCornerIndex < 3; ++OtherCornerIndex) +// { +// for (int32 NextCornerIndex = 0; NextCornerIndex < 3; ++NextCornerIndex) +// { +// int32 NextVertexIndex = InIndices[NextFace.FaceIndex * 3 + NextCornerIndex]; +// int32 OtherVertexIndex = InIndices[OtherFace.FaceIndex * 3 + OtherCornerIndex]; +// if (PointsEqual( +// InVertices[NextVertexIndex], +// InVertices[OtherVertexIndex], +// ComparisonThreshold)) +// { +// CommonVertices++; +// if (bBlendOverlappingNormals +// || NextVertexIndex == OtherVertexIndex) +// { +// CommonNormalVertices++; +// } +// } +// } +// } +// // Flood fill faces with more than one common vertices which must be touching edges. +// if (CommonVertices > 1) +// { +// NextFace.bFilled = true; +// NextFace.bBlendNormals = (CommonNormalVertices > 1); +// NewConnections++; +// } +// } +// } +// } +// } +// } +// } +// while (NewConnections > 0); +// } +// +// +// // Vertex normal construction. +// for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) +// { +// if (bCornerHasNormal[CornerIndex]) +// { +// CornerNormal[CornerIndex] = OutTangentZ[WedgeOffset + CornerIndex]; +// } +// else +// { +// for (int32 RelevantFaceIdx = 0; RelevantFaceIdx < RelevantFacesForCorner[CornerIndex].Num(); RelevantFaceIdx++) +// { +// FFanFace const& RelevantFace = RelevantFacesForCorner[CornerIndex][RelevantFaceIdx]; +// if (RelevantFace.bFilled) +// { +// int32 OtherFaceIndex = RelevantFace.FaceIndex; +// if (RelevantFace.bBlendNormals) +// { +// CornerNormal[CornerIndex] += TriangleTangentZ[OtherFaceIndex]; +// } +// } +// } +// if (!OutTangentZ[WedgeOffset + CornerIndex].IsZero()) +// { +// CornerNormal[CornerIndex] = OutTangentZ[WedgeOffset + CornerIndex]; +// } +// } +// } +// +// // Normalization. +// for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) +// { +// CornerNormal[CornerIndex].Normalize(); +// } +// +// // Copy back to the mesh. +// for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) +// { +// OutTangentZ[WedgeOffset + CornerIndex] = CornerNormal[CornerIndex]; +// } +// } +// +// check(OutTangentZ.Num() == NumWedges); +// } + +static void ComputeTangents_MikkTSpace( + const TArray& InVertices, + const TArray& InIndices, + const TArray& InUVs, + const TArray& InNormals, + bool bIgnoreDegenerateTriangles, + TArray& OutTangentX, + TArray& OutTangentY +) +{ + const int32 NumWedges = InIndices.Num(); + + bool bTangentsComputationNeeded = false; + + if (OutTangentX.Num() != NumWedges) + { + OutTangentX.Empty(NumWedges); + OutTangentX.AddZeroed(NumWedges); + bTangentsComputationNeeded = true; + } + if (OutTangentY.Num() != NumWedges) + { + OutTangentY.Empty(NumWedges); + OutTangentY.AddZeroed(NumWedges); + bTangentsComputationNeeded = true; + } + + if (!bTangentsComputationNeeded && NumWedges > 0) + { + for (int32 WedgeIdx = 0; WedgeIdx < NumWedges && !bTangentsComputationNeeded; ++WedgeIdx) + { + bTangentsComputationNeeded = OutTangentX[WedgeIdx].IsNearlyZero() || OutTangentY[WedgeIdx].IsNearlyZero(); + } + } + + if (!bTangentsComputationNeeded) + { + return; + } + + MikkTSpace_Mesh MikkTSpaceMesh(InVertices, InIndices, InUVs, OutTangentX, OutTangentY, InNormals); + + // use mikktspace to calculate the tangents + SMikkTSpaceInterface MikkTInterface; + MikkTInterface.m_getNormal = MikkGetNormal; + MikkTInterface.m_getNumFaces = MikkGetNumFaces; + MikkTInterface.m_getNumVerticesOfFace = MikkGetNumVertsOfFace; + MikkTInterface.m_getPosition = MikkGetPosition; + MikkTInterface.m_getTexCoord = MikkGetTexCoord; + MikkTInterface.m_setTSpaceBasic = MikkSetTSpaceBasic; + MikkTInterface.m_setTSpace = nullptr; + + SMikkTSpaceContext MikkTContext; + MikkTContext.m_pInterface = &MikkTInterface; + MikkTContext.m_pUserData = (void*)(&MikkTSpaceMesh); + MikkTContext.m_bIgnoreDegenerates = bIgnoreDegenerateTriangles; + genTangSpaceDefault(&MikkTContext); +} + +static void ComputeTangents_MikkTSpace( + const TArray& InVertices, + const TArray& InIndices, + const TArray& InUVs, + const TArray& SmoothingGroupIndices, + const FOverlappingCorners& OverlappingCorners, + TArray& OutTangentX, + TArray& OutTangentY, + TArray& OutTangentZ, + const uint32 TangentOptions +) +{ + // ComputeNormals( InVertices, InIndices, InUVs, SmoothingGroupIndices, OverlappingCorners, OutTangentZ, TangentOptions ); + + bool bIgnoreDegenerateTriangles = (TangentOptions & ETangentOptions2::IgnoreDegenerateTriangles) != 0; + + int32 NumWedges = InIndices.Num(); + + ComputeTangents_MikkTSpace(InVertices, InIndices, InUVs, OutTangentZ, bIgnoreDegenerateTriangles, OutTangentX, OutTangentY); + + check(OutTangentX.Num() == NumWedges); + check(OutTangentY.Num() == NumWedges); + check(OutTangentZ.Num() == NumWedges); +} + +static void ComputeTangents_MikkTSpace( + FRawMesh& RawMesh, + const FOverlappingCorners& OverlappingCorners, + uint32 TangentOptions +) +{ + ComputeTangents_MikkTSpace(RawMesh.VertexPositions, RawMesh.WedgeIndices, RawMesh.WedgeTexCoords[0], RawMesh.FaceSmoothingMasks, OverlappingCorners, RawMesh.WedgeTangentX, RawMesh.WedgeTangentY, RawMesh.WedgeTangentZ, TangentOptions); +} + +static void BuildDepthOnlyIndexBuffer( + TArray& OutDepthIndices, + const TArray& InVertices, + const TArray& InIndices, + const TArrayView& InSections +) +{ + int32 NumVertices = InVertices.Num(); + if (InIndices.Num() <= 0 || NumVertices <= 0) + { + OutDepthIndices.Empty(); + return; + } + + // Create a mapping of index -> first overlapping index to accelerate the construction of the shadow index buffer. + TArray VertIndexAndZ; + VertIndexAndZ.Empty(NumVertices); + for (int32 VertIndex = 0; VertIndex < NumVertices; VertIndex++) + { + new(VertIndexAndZ)FIndexAndZ(VertIndex, InVertices[VertIndex].Position); + } + VertIndexAndZ.Sort(FCompareIndexAndZ()); + + // Setup the index map. 0xFFFFFFFF == not set. + TArray IndexMap; + IndexMap.AddUninitialized(NumVertices); + FMemory::Memset(IndexMap.GetData(), 0xFF, NumVertices * sizeof(uint32)); + + // Search for duplicates, quickly! + for (int32 i = 0; i < VertIndexAndZ.Num(); i++) + { + uint32 SrcIndex = VertIndexAndZ[i].Index; + float Z = VertIndexAndZ[i].Z; + IndexMap[SrcIndex] = FMath::Min(IndexMap[SrcIndex], SrcIndex); + + // Search forward since we add pairs both ways. + for (int32 j = i + 1; j < VertIndexAndZ.Num(); j++) + { + if (FMath::Abs(VertIndexAndZ[j].Z - Z) > THRESH_POINTS_ARE_SAME * 4.01f) + break; // can't be any more dups + + uint32 OtherIndex = VertIndexAndZ[j].Index; + if (PointsEqual(InVertices[SrcIndex].Position, InVertices[OtherIndex].Position,/*bUseEpsilonCompare=*/ false)) + { + IndexMap[SrcIndex] = FMath::Min(IndexMap[SrcIndex], OtherIndex); + IndexMap[OtherIndex] = FMath::Min(IndexMap[OtherIndex], SrcIndex); + } + } + } + + // Build the depth-only index buffer by remapping all indices to the first overlapping + // vertex in the vertex buffer. + OutDepthIndices.Empty(); + for (int32 SectionIndex = 0; SectionIndex < InSections.Num(); ++SectionIndex) + { + const FStaticMeshSection& Section = InSections[SectionIndex]; + int32 FirstIndex = Section.FirstIndex; + int32 LastIndex = FirstIndex + Section.NumTriangles * 3; + for (int32 SrcIndex = FirstIndex; SrcIndex < LastIndex; ++SrcIndex) + { + uint32 VertIndex = InIndices[SrcIndex]; + OutDepthIndices.Add(IndexMap[VertIndex]); + } + } +} + +static float GetComparisonThreshold(FMeshBuildSettings const& BuildSettings) +{ + return BuildSettings.bRemoveDegenerates ? THRESH_POINTS_ARE_SAME : 0.0f; +} + +/*------------------------------------------------------------------------------ +Static mesh building. +------------------------------------------------------------------------------*/ + +static void BuildStaticMeshVertex(const FRawMesh& RawMesh, const FMatrix& ScaleMatrix, const FVector& Position, int32 WedgeIndex, FStaticMeshBuildVertex& Vertex) +{ + Vertex.Position = Position; + + Vertex.TangentX = ScaleMatrix.TransformVector(RawMesh.WedgeTangentX[WedgeIndex]).GetSafeNormal(); + Vertex.TangentY = ScaleMatrix.TransformVector(RawMesh.WedgeTangentY[WedgeIndex]).GetSafeNormal(); + Vertex.TangentZ = ScaleMatrix.TransformVector(RawMesh.WedgeTangentZ[WedgeIndex]).GetSafeNormal(); + + if (RawMesh.WedgeColors.IsValidIndex(WedgeIndex)) + { + Vertex.Color = RawMesh.WedgeColors[WedgeIndex]; + } + else + { + Vertex.Color = FColor::White; + } + + static const int32 NumTexCoords = FMath::Min(MAX_MESH_TEXTURE_COORDS, MAX_STATIC_TEXCOORDS); + for (int32 i = 0; i < NumTexCoords; ++i) + { + if (RawMesh.WedgeTexCoords[i].IsValidIndex(WedgeIndex)) + { + Vertex.UVs[i] = RawMesh.WedgeTexCoords[i][WedgeIndex]; + } + else + { + Vertex.UVs[i] = FVector2D(0.0f, 0.0f); + } + } +} + +static bool AreVerticesEqual( + FStaticMeshBuildVertex const& A, + FStaticMeshBuildVertex const& B, + float ComparisonThreshold +) +{ + if (!PointsEqual(A.Position, B.Position, ComparisonThreshold) + || !NormalsEqual(A.TangentX, B.TangentX) + || !NormalsEqual(A.TangentY, B.TangentY) + || !NormalsEqual(A.TangentZ, B.TangentZ) + || A.Color != B.Color) + { + return false; + } + + // UVs + for (int32 UVIndex = 0; UVIndex < MAX_STATIC_TEXCOORDS; UVIndex++) + { + if (!UVsEqual(A.UVs[UVIndex], B.UVs[UVIndex])) + { + return false; + } + } + + return true; +} + +void FMeshUtilities2::BuildStaticMeshVertexAndIndexBuffers( + TArray& OutVertices, + TArray>& OutPerSectionIndices, + TArray& OutWedgeMap, + const FRawMesh& RawMesh, + const FOverlappingCorners& OverlappingCorners, + const TMap& MaterialToSectionMapping, + float ComparisonThreshold, + FVector BuildScale, + int32 ImportVersion +) +{ + TMap FinalVerts; + int32 NumFaces = RawMesh.WedgeIndices.Num() / 3; + OutWedgeMap.Reset(RawMesh.WedgeIndices.Num()); + FMatrix ScaleMatrix(FScaleMatrix(BuildScale).Inverse().GetTransposed()); + + // Estimate how many vertices there will be to reduce number of re-allocations required + OutVertices.Reserve((int32)(NumFaces * 1.2) + 16); + + // Work with vertex in OutVertices array directly for improved performance + OutVertices.AddUninitialized(1); + FStaticMeshBuildVertex* ThisVertex = &OutVertices.Last(); + + // Process each face, build vertex buffer and per-section index buffers. + for (int32 FaceIndex = 0; FaceIndex < NumFaces; FaceIndex++) + { + int32 VertexIndices[3]; + FVector CornerPositions[3]; + + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + CornerPositions[CornerIndex] = GetPositionForWedge(RawMesh, FaceIndex * 3 + CornerIndex); + } + + // Don't process degenerate triangles. + if (PointsEqual(CornerPositions[0], CornerPositions[1], ComparisonThreshold) + || PointsEqual(CornerPositions[0], CornerPositions[2], ComparisonThreshold) + || PointsEqual(CornerPositions[1], CornerPositions[2], ComparisonThreshold)) + { + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + OutWedgeMap.Add(INDEX_NONE); + } + continue; + } + + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + int32 WedgeIndex = FaceIndex * 3 + CornerIndex; + BuildStaticMeshVertex(RawMesh, ScaleMatrix, CornerPositions[CornerIndex] * BuildScale, WedgeIndex, *ThisVertex); + + const TArray& DupVerts = OverlappingCorners.FindIfOverlapping(WedgeIndex); + + int32 Index = INDEX_NONE; + for (int32 k = 0; k < DupVerts.Num(); k++) + { + if (DupVerts[k] >= WedgeIndex) + { + // the verts beyond me haven't been placed yet, so these duplicates are not relevant + break; + } + + int32* Location = FinalVerts.Find(DupVerts[k]); + if (Location != NULL + && AreVerticesEqual(*ThisVertex, OutVertices[*Location], ComparisonThreshold)) + { + Index = *Location; + break; + } + } + if (Index == INDEX_NONE) + { + // Commit working vertex + Index = OutVertices.Num() - 1; + FinalVerts.Add(WedgeIndex, Index); + + // Setup next working vertex + OutVertices.AddUninitialized(1); + ThisVertex = &OutVertices.Last(); + } + VertexIndices[CornerIndex] = Index; + } + + // Reject degenerate triangles. + if (VertexIndices[0] == VertexIndices[1] + || VertexIndices[1] == VertexIndices[2] + || VertexIndices[0] == VertexIndices[2]) + { + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + OutWedgeMap.Add(INDEX_NONE); + } + continue; + } + + // Put the indices in the material index buffer. + uint32 SectionIndex = 0; + if (ImportVersion < RemoveStaticMeshSkinxxWorkflow) + { + SectionIndex = FMath::Clamp(RawMesh.FaceMaterialIndices[FaceIndex], 0, OutPerSectionIndices.Num() - 1); + } + else + { + SectionIndex = MaterialToSectionMapping.FindChecked(RawMesh.FaceMaterialIndices[FaceIndex]); + } + TArray& SectionIndices = OutPerSectionIndices[SectionIndex]; + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + SectionIndices.Add(VertexIndices[CornerIndex]); + OutWedgeMap.Add(VertexIndices[CornerIndex]); + } + } + + // Remove working vertex + OutVertices.Pop(false); +} + +void FMeshUtilities2::CacheOptimizeVertexAndIndexBuffer( + TArray& Vertices, + TArray>& PerSectionIndices, + TArray& WedgeMap +) +{ + // Copy the vertices since we will be reordering them + TArray OriginalVertices = Vertices; + + // Initialize a cache that stores which indices have been assigned + TArray IndexCache; + IndexCache.AddUninitialized(Vertices.Num()); + FMemory::Memset(IndexCache.GetData(), INDEX_NONE, IndexCache.Num() * IndexCache.GetTypeSize()); + int32 NextAvailableIndex = 0; + + // Iterate through the section index buffers, + // Optimizing index order for the post transform cache (minimizes the number of vertices transformed), + // And vertex order for the pre transform cache (minimizes the amount of vertex data fetched by the GPU). + for (int32 SectionIndex = 0; SectionIndex < PerSectionIndices.Num(); SectionIndex++) + { + TArray& Indices = PerSectionIndices[SectionIndex]; + + if (Indices.Num()) + { + // Optimize the index buffer for the post transform cache with. + CacheOptimizeIndexBuffer(Indices); + + // Copy the index buffer since we will be reordering it + TArray OriginalIndices = Indices; + + // Go through the indices and assign them new values that are coherent where possible + for (int32 Index = 0; Index < Indices.Num(); Index++) + { + const int32 CachedIndex = IndexCache[OriginalIndices[Index]]; + + if (CachedIndex == INDEX_NONE) + { + // No new index has been allocated for this existing index, assign a new one + Indices[Index] = NextAvailableIndex; + // Mark what this index has been assigned to + IndexCache[OriginalIndices[Index]] = NextAvailableIndex; + NextAvailableIndex++; + } + else + { + // Reuse an existing index assignment + Indices[Index] = CachedIndex; + } + // Reorder the vertices based on the new index assignment + Vertices[Indices[Index]] = OriginalVertices[OriginalIndices[Index]]; + } + } + } + + for (int32 i = 0; i < WedgeMap.Num(); i++) + { + int32 MappedIndex = WedgeMap[i]; + if (MappedIndex != INDEX_NONE) + { + WedgeMap[i] = IndexCache[MappedIndex]; + } + } +} + +struct FLayoutUVRawMeshView final : FLayoutUV::IMeshView +{ + FRawMesh& RawMesh; + const uint32 SrcChannel; + const uint32 DstChannel; + const bool bNormalsValid; + + FLayoutUVRawMeshView(FRawMesh& InRawMesh, uint32 InSrcChannel, uint32 InDstChannel) + : RawMesh(InRawMesh) + , SrcChannel(InSrcChannel) + , DstChannel(InDstChannel) + , bNormalsValid(InRawMesh.WedgeTangentZ.Num() == InRawMesh.WedgeTexCoords[InSrcChannel].Num()) + { + } + + uint32 GetNumIndices() const override { return RawMesh.WedgeIndices.Num(); } + FVector GetPosition(uint32 Index) const override { return RawMesh.GetWedgePosition(Index); } + FVector GetNormal(uint32 Index) const override { return bNormalsValid ? RawMesh.WedgeTangentZ[Index] : FVector::ZeroVector; } + FVector2D GetInputTexcoord(uint32 Index) const override { return RawMesh.WedgeTexCoords[SrcChannel][Index]; } + + void InitOutputTexcoords(uint32 Num) override { RawMesh.WedgeTexCoords[DstChannel].SetNumUninitialized(Num); } + void SetOutputTexcoord(uint32 Index, const FVector2D& Value) override { RawMesh.WedgeTexCoords[DstChannel][Index] = Value; } +}; + +// class FStaticMeshUtilityBuilder +// { +// public: +// FStaticMeshUtilityBuilder(UStaticMesh* InStaticMesh) : Stage(EStage::Uninit), NumValidLODs(0), StaticMesh(InStaticMesh) {} +// +// bool GatherSourceMeshesPerLOD(IMeshReduction* MeshReduction) +// { +// check(Stage == EStage::Uninit); +// check(StaticMesh != nullptr); +// ELightmapUVVersion LightmapUVVersion = (ELightmapUVVersion)StaticMesh->GetLightmapUVVersion(); +// +// FMeshUtilities2& MeshUtilities = FModuleManager::Get().LoadModuleChecked("MeshUtilities"); +// +// // Gather source meshes for each LOD. +// int32 NumSourceModels = StaticMesh->GetNumSourceModels(); +// for (int32 LODIndex = 0; LODIndex < NumSourceModels; ++LODIndex) +// { +// FStaticMeshSourceModel& SrcModel = StaticMesh->GetSourceModel(LODIndex); +// FRawMesh& RawMesh = *new FRawMesh; +// LODMeshes.Add(&RawMesh); +// FOverlappingCorners& OverlappingCorners = *new FOverlappingCorners; +// LODOverlappingCorners.Add(&OverlappingCorners); +// +// if (!SrcModel.IsRawMeshEmpty()) +// { +// SrcModel.LoadRawMesh(RawMesh); +// // Make sure the raw mesh is not irreparably malformed. +// if (!RawMesh.IsValidOrFixable()) +// { +// UE_LOG(LogMeshUtilities, Error, TEXT("Raw mesh is corrupt for LOD%d."), LODIndex); +// return false; +// } +// LODBuildSettings[LODIndex] = SrcModel.BuildSettings; +// +// float ComparisonThreshold = GetComparisonThreshold(LODBuildSettings[LODIndex]); +// int32 NumWedges = RawMesh.WedgeIndices.Num(); +// +// // Find overlapping corners to accelerate adjacency. +// MeshUtilities.FindOverlappingCorners(OverlappingCorners, RawMesh, ComparisonThreshold); +// +// // Figure out if we should recompute normals and tangents. +// bool bRecomputeNormals = SrcModel.BuildSettings.bRecomputeNormals || RawMesh.WedgeTangentZ.Num() != NumWedges; +// bool bRecomputeTangents = SrcModel.BuildSettings.bRecomputeTangents || RawMesh.WedgeTangentX.Num() != NumWedges || RawMesh.WedgeTangentY.Num() != NumWedges; +// +// // Dump normals and tangents if we are recomputing them. +// if (bRecomputeTangents) +// { +// RawMesh.WedgeTangentX.Empty(NumWedges); +// RawMesh.WedgeTangentX.AddZeroed(NumWedges); +// RawMesh.WedgeTangentY.Empty(NumWedges); +// RawMesh.WedgeTangentY.AddZeroed(NumWedges); +// } +// if (bRecomputeNormals) +// { +// RawMesh.WedgeTangentZ.Empty(NumWedges); +// RawMesh.WedgeTangentZ.AddZeroed(NumWedges); +// } +// +// // Compute any missing tangents. +// { +// // Static meshes always blend normals of overlapping corners. +// uint32 TangentOptions = ETangentOptions::BlendOverlappingNormals; +// if (SrcModel.BuildSettings.bRemoveDegenerates) +// { +// // If removing degenerate triangles, ignore them when computing tangents. +// TangentOptions |= ETangentOptions::IgnoreDegenerateTriangles; +// } +// +// //MikkTSpace should be use only when the user want to recompute the normals or tangents otherwise should always fallback on builtin +// if (SrcModel.BuildSettings.bUseMikkTSpace && (SrcModel.BuildSettings.bRecomputeNormals || SrcModel.BuildSettings.bRecomputeTangents)) +// { +// ComputeTangents_MikkTSpace(RawMesh, OverlappingCorners, TangentOptions); +// } +// else +// { +// ComputeTangents(RawMesh, OverlappingCorners, TangentOptions); +// } +// } +// +// // At this point the mesh will have valid tangents. +// check(RawMesh.WedgeTangentX.Num() == NumWedges); +// check(RawMesh.WedgeTangentY.Num() == NumWedges); +// check(RawMesh.WedgeTangentZ.Num() == NumWedges); +// +// // Generate lightmap UVs +// if (SrcModel.BuildSettings.bGenerateLightmapUVs) +// { +// if (RawMesh.WedgeTexCoords[SrcModel.BuildSettings.SrcLightmapIndex].Num() == 0) +// { +// SrcModel.BuildSettings.SrcLightmapIndex = 0; +// } +// +// FLayoutUVRawMeshView RawMeshView(RawMesh, SrcModel.BuildSettings.SrcLightmapIndex, SrcModel.BuildSettings.DstLightmapIndex); +// FLayoutUV Packer(RawMeshView); +// Packer.SetVersion(LightmapUVVersion); +// +// Packer.FindCharts(OverlappingCorners); +// +// int32 EffectiveMinLightmapResolution = SrcModel.BuildSettings.MinLightmapResolution; +// if (LightmapUVVersion >= ELightmapUVVersion::ConsiderLightmapPadding) +// { +// if (GLightmassDebugOptions.bPadMappings) +// { +// EffectiveMinLightmapResolution -= 2; +// } +// } +// +// bool bPackSuccess = Packer.FindBestPacking(EffectiveMinLightmapResolution); +// if (bPackSuccess) +// { +// Packer.CommitPackedUVs(); +// } +// } +// HasRawMesh[LODIndex] = true; +// } +// else if (LODIndex > 0 && MeshReduction) +// { +// // If a raw mesh is not explicitly provided, use the raw mesh of the +// // next highest LOD. +// int32 BaseRawMeshIndex = LODIndex - 1; +// RawMesh = LODMeshes[BaseRawMeshIndex]; +// OverlappingCorners = LODOverlappingCorners[BaseRawMeshIndex]; +// LODBuildSettings[LODIndex] = LODBuildSettings[BaseRawMeshIndex]; +// HasRawMesh[LODIndex] = false; +// //Make sure the SectionInfoMap is taken from the Base RawMesh +// int32 SectionNumber = StaticMesh->GetOriginalSectionInfoMap().GetSectionNumber(BaseRawMeshIndex); +// for (int32 SectionIndex = 0; SectionIndex < SectionNumber; ++SectionIndex) +// { +// FMeshSectionInfo Info = StaticMesh->GetOriginalSectionInfoMap().Get(BaseRawMeshIndex, SectionIndex); +// StaticMesh->GetSectionInfoMap().Set(LODIndex, SectionIndex, Info); +// StaticMesh->GetOriginalSectionInfoMap().Set(LODIndex, SectionIndex, Info); +// } +// } +// } +// check(LODMeshes.Num() == NumSourceModels); +// check(LODOverlappingCorners.Num() == NumSourceModels); +// +// // Bail if there is no raw mesh data from which to build a renderable mesh. +// if (LODMeshes.Num() == 0) +// { +// UE_LOG(LogMeshUtilities, Error, TEXT("Raw Mesh data contains no mesh data to build a mesh that can be rendered.")); +// return false; +// } +// else if (LODMeshes[0].WedgeIndices.Num() == 0) +// { +// UE_LOG(LogMeshUtilities, Error, TEXT("Raw Mesh data contains no wedge index data to build a mesh that can be rendered.")); +// return false; +// } +// +// Stage = EStage::Gathered; +// return true; +// } +// +// bool ReduceLODs(const FStaticMeshLODGroup& LODGroup, IMeshReduction* MeshReduction, TArray& OutWasReduced) +// { +// check(Stage == EStage::Gathered); +// check(StaticMesh != nullptr); +// int32 NumSourceModels = StaticMesh->GetNumSourceModels(); +// if (NumSourceModels == 0) +// { +// UE_LOG(LogMeshUtilities, Error, TEXT("Mesh contains zero source models.")); +// return false; +// } +// +// FMeshUtilities2& MeshUtilities = FModuleManager::Get().LoadModuleChecked("MeshUtilities"); +// +// // Reduce each LOD mesh according to its reduction settings. +// for (int32 LODIndex = 0; LODIndex < NumSourceModels; ++LODIndex) +// { +// const FStaticMeshSourceModel& SrcModel = StaticMesh->GetSourceModel(LODIndex); +// FMeshReductionSettings ReductionSettings = LODGroup.GetSettings(SrcModel.ReductionSettings, LODIndex); +// LODMaxDeviation[NumValidLODs] = 0.0f; +// if (LODIndex != NumValidLODs) +// { +// LODBuildSettings[NumValidLODs] = LODBuildSettings[LODIndex]; +// LODOverlappingCorners[NumValidLODs] = LODOverlappingCorners[LODIndex]; +// } +// +// if (MeshReduction && (ReductionSettings.PercentTriangles < 1.0f || ReductionSettings.MaxDeviation > 0.0f)) +// { +// FRawMesh& InMesh = LODMeshes[ReductionSettings.BaseLODModel]; +// FRawMesh& DestMesh = LODMeshes[NumValidLODs]; +// FOverlappingCorners& InOverlappingCorners = LODOverlappingCorners[ReductionSettings.BaseLODModel]; +// FOverlappingCorners& DestOverlappingCorners = LODOverlappingCorners[NumValidLODs]; +// +// FMeshDescription SrcMeshdescription; +// FStaticMeshAttributes(SrcMeshdescription).Register(); +// +// FMeshDescription DestMeshdescription; +// FStaticMeshAttributes(DestMeshdescription).Register(); +// +// TMap FromMaterialMap; +// FStaticMeshOperations::ConvertFromRawMesh(InMesh, SrcMeshdescription, FromMaterialMap); +// MeshReduction->ReduceMeshDescription(DestMeshdescription, LODMaxDeviation[NumValidLODs], SrcMeshdescription, InOverlappingCorners, ReductionSettings); +// TMap ToMaterialMap; +// FStaticMeshOperations::ConvertToRawMesh(DestMeshdescription, DestMesh, ToMaterialMap); +// +// if (DestMesh.WedgeIndices.Num() > 0 && !DestMesh.IsValid()) +// { +// UE_LOG(LogMeshUtilities, Error, TEXT("Mesh reduction produced a corrupt mesh for LOD%d"), LODIndex); +// return false; +// } +// OutWasReduced[LODIndex] = true; +// +// // Recompute adjacency information. +// float ComparisonThreshold = GetComparisonThreshold(LODBuildSettings[NumValidLODs]); +// MeshUtilities.FindOverlappingCorners(DestOverlappingCorners, DestMesh, ComparisonThreshold); +// +// //Make sure the static mesh SectionInfoMap is up to date with the new reduce LOD +// //We have to remap the material index with the ReductionSettings.BaseLODModel sectionInfoMap +// if (StaticMesh != nullptr) +// { +// if (DestMesh.IsValid()) +// { +// //Set the new SectionInfoMap for this reduced LOD base on the ReductionSettings.BaseLODModel SectionInfoMap +// const FMeshSectionInfoMap& BaseLODModelSectionInfoMap = StaticMesh->GetSectionInfoMap(); +// TArray UniqueMaterialIndex; +// //Find all unique Material in used order +// int32 NumFaces = DestMesh.FaceMaterialIndices.Num(); +// for (int32 FaceIndex = 0; FaceIndex < NumFaces; ++FaceIndex) +// { +// int32 MaterialIndex = DestMesh.FaceMaterialIndices[FaceIndex]; +// UniqueMaterialIndex.AddUnique(MaterialIndex); +// } +// //All used material represent a different section +// for (int32 SectionIndex = 0; SectionIndex < UniqueMaterialIndex.Num(); ++SectionIndex) +// { +// //Section material index have to be remap with the ReductionSettings.BaseLODModel SectionInfoMap to create +// //a valid new section info map for the reduced LOD. +// if (BaseLODModelSectionInfoMap.IsValidSection(ReductionSettings.BaseLODModel, UniqueMaterialIndex[SectionIndex])) +// { +// FMeshSectionInfo SectionInfo = BaseLODModelSectionInfoMap.Get(ReductionSettings.BaseLODModel, UniqueMaterialIndex[SectionIndex]); +// //Try to recuperate the valid data +// if (BaseLODModelSectionInfoMap.IsValidSection(LODIndex, SectionIndex)) +// { +// //If the old LOD section was using the same Material copy the data +// FMeshSectionInfo OriginalLODSectionInfo = BaseLODModelSectionInfoMap.Get(LODIndex, SectionIndex); +// if (OriginalLODSectionInfo.MaterialIndex == SectionInfo.MaterialIndex) +// { +// SectionInfo.bCastShadow = OriginalLODSectionInfo.bCastShadow; +// SectionInfo.bEnableCollision = OriginalLODSectionInfo.bEnableCollision; +// } +// } +// //Copy the BaseLODModel section info to the reduce LODIndex. +// StaticMesh->GetSectionInfoMap().Set(LODIndex, SectionIndex, SectionInfo); +// } +// } +// } +// } +// } +// +// if (LODMeshes[NumValidLODs].WedgeIndices.Num() > 0) +// { +// NumValidLODs++; +// } +// } +// +// if (NumValidLODs < 1) +// { +// UE_LOG(LogMeshUtilities, Error, TEXT("Mesh reduction produced zero LODs.")); +// return false; +// } +// Stage = EStage::Reduce; +// return true; +// } +// +// bool GenerateRenderingMeshes(FMeshUtilities2& MeshUtilities, FStaticMeshRenderData& OutRenderData) +// { +// check(Stage == EStage::Reduce); +// check(StaticMesh != nullptr); +// +// int32 ImportVersion = StaticMesh->ImportVersion; +// +// // Generate per-LOD rendering data. +// OutRenderData.AllocateLODResources(NumValidLODs); +// for (int32 LODIndex = 0; LODIndex < NumValidLODs; ++LODIndex) +// { +// FStaticMeshSourceModel& SourceModel = StaticMesh->GetSourceModel(LODIndex); +// FStaticMeshLODResources& LODModel = OutRenderData.LODResources[LODIndex]; +// FRawMesh& RawMesh = LODMeshes[LODIndex]; +// LODModel.MaxDeviation = LODMaxDeviation[LODIndex]; +// +// TArray Vertices; +// TArray > PerSectionIndices; +// +// TMap MaterialToSectionMapping; +// +// // Find out how many sections are in the mesh. +// TArray MaterialIndices; +// for ( const int32 MaterialIndex : RawMesh.FaceMaterialIndices ) +// { +// // Find all unique material indices +// MaterialIndices.AddUnique(MaterialIndex); +// } +// +// // Need X number of sections for X number of material indices +// //for (const int32 MaterialIndex : MaterialIndices) +// for ( int32 Index = 0; Index < MaterialIndices.Num(); ++Index) +// { +// const int32 MaterialIndex = MaterialIndices[Index]; +// FStaticMeshSection* Section = new(LODModel.Sections) FStaticMeshSection(); +// Section->MaterialIndex = MaterialIndex; +// if (ImportVersion < RemoveStaticMeshSkinxxWorkflow) +// { +// MaterialToSectionMapping.Add(MaterialIndex, MaterialIndex); +// } +// else +// { +// MaterialToSectionMapping.Add(MaterialIndex, Index); +// } +// new(PerSectionIndices)TArray; +// } +// +// // Build and cache optimize vertex and index buffers. +// { +// // TODO_STATICMESH: The wedge map is only valid for LODIndex 0 if no reduction has been performed. +// // TODO - write directly to TMemoryImageArray +// // We can compute an approximate one instead for other LODs. +// TArray TempWedgeMap; +// TArray& WedgeMap = SourceModel.ReductionSettings.PercentTriangles >= 1.0f ? LODModel.WedgeMap : TempWedgeMap; +// WedgeMap.Reset(); +// float ComparisonThreshold = GetComparisonThreshold(LODBuildSettings[LODIndex]); +// MeshUtilities.BuildStaticMeshVertexAndIndexBuffers(Vertices, PerSectionIndices, WedgeMap, RawMesh, LODOverlappingCorners[LODIndex], MaterialToSectionMapping, ComparisonThreshold, LODBuildSettings[LODIndex].BuildScale3D, ImportVersion); +// check(WedgeMap.Num() == RawMesh.WedgeIndices.Num()); +// +// if (RawMesh.WedgeIndices.Num() < 100000 * 3) +// { +// MeshUtilities.CacheOptimizeVertexAndIndexBuffer(Vertices, PerSectionIndices, WedgeMap); +// check(WedgeMap.Num() == RawMesh.WedgeIndices.Num()); +// } +// } +// +// verifyf(Vertices.Num() != 0, TEXT("No valid vertices found for the mesh.")); +// +// // Initialize the vertex buffer. +// int32 NumTexCoords = ComputeNumTexCoords(RawMesh, MAX_STATIC_TEXCOORDS); +// LODModel.VertexBuffers.StaticMeshVertexBuffer.SetUseHighPrecisionTangentBasis(LODBuildSettings[LODIndex].bUseHighPrecisionTangentBasis); +// LODModel.VertexBuffers.StaticMeshVertexBuffer.SetUseFullPrecisionUVs(LODBuildSettings[LODIndex].bUseFullPrecisionUVs); +// LODModel.VertexBuffers.StaticMeshVertexBuffer.Init(Vertices, NumTexCoords); +// LODModel.VertexBuffers.PositionVertexBuffer.Init(Vertices); +// LODModel.VertexBuffers.ColorVertexBuffer.Init(Vertices); +// +// // Concatenate the per-section index buffers. +// TArray CombinedIndices; +// bool bNeeds32BitIndices = false; +// for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) +// { +// FStaticMeshSection& Section = LODModel.Sections[SectionIndex]; +// TArray const& SectionIndices = PerSectionIndices[SectionIndex]; +// Section.FirstIndex = 0; +// Section.NumTriangles = 0; +// Section.MinVertexIndex = 0; +// Section.MaxVertexIndex = 0; +// +// if (SectionIndices.Num()) +// { +// Section.FirstIndex = CombinedIndices.Num(); +// Section.NumTriangles = SectionIndices.Num() / 3; +// +// CombinedIndices.AddUninitialized(SectionIndices.Num()); +// uint32* DestPtr = &CombinedIndices[Section.FirstIndex]; +// uint32 const* SrcPtr = SectionIndices.GetData(); +// +// Section.MinVertexIndex = *SrcPtr; +// Section.MaxVertexIndex = *SrcPtr; +// +// for (int32 Index = 0; Index < SectionIndices.Num(); Index++) +// { +// uint32 VertIndex = *SrcPtr++; +// +// bNeeds32BitIndices |= (VertIndex > MAX_uint16); +// Section.MinVertexIndex = FMath::Min(VertIndex, Section.MinVertexIndex); +// Section.MaxVertexIndex = FMath::Max(VertIndex, Section.MaxVertexIndex); +// *DestPtr++ = VertIndex; +// } +// } +// } +// LODModel.IndexBuffer.SetIndices(CombinedIndices, bNeeds32BitIndices ? EIndexBufferStride::Force32Bit : EIndexBufferStride::Force16Bit); +// +// // Build the reversed index buffer. +// if (LODModel.AdditionalIndexBuffers && StaticMesh->GetSourceModel(0).BuildSettings.bBuildReversedIndexBuffer) +// { +// TArray InversedIndices; +// const int32 IndexCount = CombinedIndices.Num(); +// InversedIndices.AddUninitialized(IndexCount); +// +// for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); ++SectionIndex) +// { +// const FStaticMeshSection& SectionInfo = LODModel.Sections[SectionIndex]; +// const int32 SectionIndexCount = SectionInfo.NumTriangles * 3; +// +// for (int32 i = 0; i < SectionIndexCount; ++i) +// { +// InversedIndices[SectionInfo.FirstIndex + i] = CombinedIndices[SectionInfo.FirstIndex + SectionIndexCount - 1 - i]; +// } +// } +// LODModel.AdditionalIndexBuffers->ReversedIndexBuffer.SetIndices(InversedIndices, bNeeds32BitIndices ? EIndexBufferStride::Force32Bit : EIndexBufferStride::Force16Bit); +// } +// +// // Build the depth-only index buffer. +// TArray DepthOnlyIndices; +// { +// BuildDepthOnlyIndexBuffer( +// DepthOnlyIndices, +// Vertices, +// CombinedIndices, +// LODModel.Sections +// ); +// +// if (DepthOnlyIndices.Num() < 50000 * 3) +// { +// MeshUtilities.CacheOptimizeIndexBuffer(DepthOnlyIndices); +// } +// +// LODModel.DepthOnlyIndexBuffer.SetIndices(DepthOnlyIndices, bNeeds32BitIndices ? EIndexBufferStride::Force32Bit : EIndexBufferStride::Force16Bit); +// } +// +// // Build the inversed depth only index buffer. +// if (LODModel.AdditionalIndexBuffers && StaticMesh->GetSourceModel(0).BuildSettings.bBuildReversedIndexBuffer) +// { +// TArray ReversedDepthOnlyIndices; +// const int32 IndexCount = DepthOnlyIndices.Num(); +// ReversedDepthOnlyIndices.AddUninitialized(IndexCount); +// for (int32 i = 0; i < IndexCount; ++i) +// { +// ReversedDepthOnlyIndices[i] = DepthOnlyIndices[IndexCount - 1 - i]; +// } +// LODModel.AdditionalIndexBuffers->ReversedDepthOnlyIndexBuffer.SetIndices(ReversedDepthOnlyIndices, bNeeds32BitIndices ? EIndexBufferStride::Force32Bit : EIndexBufferStride::Force16Bit); +// } +// +// // Build a list of wireframe edges in the static mesh. +// if (LODModel.AdditionalIndexBuffers) +// { +// TArray Edges; +// TArray WireframeIndices; +// +// FStaticMeshEdgeBuilder(CombinedIndices, Vertices, Edges).FindEdges(); +// WireframeIndices.Empty(2 * Edges.Num()); +// for (int32 EdgeIndex = 0; EdgeIndex < Edges.Num(); EdgeIndex++) +// { +// FMeshEdgeDef& Edge = Edges[EdgeIndex]; +// WireframeIndices.Add(Edge.Vertices[0]); +// WireframeIndices.Add(Edge.Vertices[1]); +// } +// LODModel.AdditionalIndexBuffers->WireframeIndexBuffer.SetIndices(WireframeIndices, bNeeds32BitIndices ? EIndexBufferStride::Force32Bit : EIndexBufferStride::Force16Bit); +// } +// } +// +// // Copy the original material indices to fixup meshes before compacting of materials was done. +// if (NumValidLODs > 0) +// { +// OutRenderData.MaterialIndexToImportIndex = LODMeshes[0].MaterialIndexToImportIndex; +// } +// +// // Calculate the bounding box. +// FBox BoundingBox(ForceInit); +// FPositionVertexBuffer& BasePositionVertexBuffer = OutRenderData.LODResources[0].VertexBuffers.PositionVertexBuffer; +// for (uint32 VertexIndex = 0; VertexIndex < BasePositionVertexBuffer.GetNumVertices(); VertexIndex++) +// { +// BoundingBox += BasePositionVertexBuffer.VertexPosition(VertexIndex); +// } +// BoundingBox.GetCenterAndExtents(OutRenderData.Bounds.Origin, OutRenderData.Bounds.BoxExtent); +// +// // Calculate the bounding sphere, using the center of the bounding box as the origin. +// OutRenderData.Bounds.SphereRadius = 0.0f; +// for (uint32 VertexIndex = 0; VertexIndex < BasePositionVertexBuffer.GetNumVertices(); VertexIndex++) +// { +// OutRenderData.Bounds.SphereRadius = FMath::Max( +// (BasePositionVertexBuffer.VertexPosition(VertexIndex) - OutRenderData.Bounds.Origin).Size(), +// OutRenderData.Bounds.SphereRadius +// ); +// } +// +// Stage = EStage::GenerateRendering; +// return true; +// } +// +// bool ReplaceRawMeshModels() +// { +// check(Stage == EStage::Reduce); +// check(StaticMesh != nullptr); +// +// check(HasRawMesh[0]); +// check(StaticMesh->GetNumSourceModels() >= NumValidLODs); +// bool bDirty = false; +// for (int32 Index = 1; Index < NumValidLODs; ++Index) +// { +// if (!HasRawMesh[Index]) +// { +// StaticMesh->GetSourceModel(Index).SaveRawMesh(LODMeshes[Index]); +// bDirty = true; +// } +// } +// +// Stage = EStage::ReplaceRaw; +// return true; +// } +// +// private: +// enum class EStage +// { +// Uninit, +// Gathered, +// Reduce, +// GenerateRendering, +// ReplaceRaw, +// }; +// +// EStage Stage; +// +// int32 NumValidLODs; +// +// TIndirectArray LODMeshes; +// TIndirectArray LODOverlappingCorners; +// float LODMaxDeviation[MAX_STATIC_MESH_LODS]; +// FMeshBuildSettings LODBuildSettings[MAX_STATIC_MESH_LODS]; +// bool HasRawMesh[MAX_STATIC_MESH_LODS]; +// UStaticMesh* StaticMesh; +// }; + +template +void FixupMaterialSlotNames_Implementation(TArray& MaterialSlots) +{ + // TArray UniqueMImportedaterialSlotName; + // + // //Make sure we have non empty imported material slot names + // for (MaterialSlotType& MaterialSlot : MaterialSlots) + // { + // if (MaterialSlot.ImportedMaterialSlotName == NAME_None) + // { + // if (MaterialSlot.MaterialSlotName != NAME_None) + // { + // MaterialSlot.ImportedMaterialSlotName = MaterialSlot.MaterialSlotName; + // } + // else if (MaterialSlot.MaterialInterface != nullptr) + // { + // MaterialSlot.ImportedMaterialSlotName = MaterialSlot.MaterialInterface->GetFName(); + // } + // else + // { + // MaterialSlot.ImportedMaterialSlotName = FName(TEXT("MaterialSlot")); + // } + // } + // + // FString BaseMaterialSlotName = MaterialSlot.ImportedMaterialSlotName.ToString(); + // FString UniqueName = BaseMaterialSlotName; + // int32 UniqueIndex = 1; + // while (UniqueMImportedaterialSlotName.Contains(FName(*UniqueName))) + // { + // UniqueName = FString::Printf(TEXT("%s_%d"), *BaseMaterialSlotName, UniqueIndex); + // UniqueIndex++; + // } + // MaterialSlot.ImportedMaterialSlotName = FName(*UniqueName); + // UniqueMImportedaterialSlotName.Add(MaterialSlot.ImportedMaterialSlotName); + // if (MaterialSlot.MaterialSlotName == NAME_None) + // { + // MaterialSlot.MaterialSlotName = MaterialSlot.ImportedMaterialSlotName; + // } + // } +} + +void FProcessAsyncTasksTickObject::Tick(float DeltaTime) +{ + GDistanceFieldAsyncQueue2->ProcessAsyncTasks(); + GCardRepresentationAsyncQueue2->ProcessAsyncTasks(); +} + +void FMeshUtilities2::FixupMaterialSlotNames(UStaticMesh* StaticMesh) const +{ + FixupMaterialSlotNames_Implementation(StaticMesh->GetStaticMaterials()); +} + +void FMeshUtilities2::FixupMaterialSlotNames(USkeletalMesh* SkeletalMesh) const +{ + FixupMaterialSlotNames_Implementation(SkeletalMesh->GetMaterials()); +} + +// +// bool FMeshUtilities2::BuildStaticMesh(FStaticMeshRenderData& OutRenderData, UStaticMesh* StaticMesh, const FStaticMeshLODGroup& LODGroup) +// { +// int32 LightmapUVVersion = StaticMesh->GetLightmapUVVersion(); +// int32 ImportVersion = StaticMesh->ImportVersion; +// +// IMeshReductionManagerModule& Module = FModuleManager::Get().LoadModuleChecked("MeshReductionInterface"); +// FStaticMeshUtilityBuilder Builder(StaticMesh); +// if (!Builder.GatherSourceMeshesPerLOD(Module.GetStaticMeshReductionInterface())) +// { +// return false; +// } +// +// TArray WasReduced; +// WasReduced.AddZeroed(StaticMesh->GetNumSourceModels()); +// if (!Builder.ReduceLODs(LODGroup, Module.GetStaticMeshReductionInterface(), WasReduced)) +// { +// return false; +// } +// +// return Builder.GenerateRenderingMeshes(*this, OutRenderData); +// } +// +// bool FMeshUtilities2::GenerateStaticMeshLODs(UStaticMesh* StaticMesh, const FStaticMeshLODGroup& LODGroup) +// { +// int32 LightmapUVVersion = StaticMesh->GetLightmapUVVersion(); +// +// FStaticMeshUtilityBuilder Builder(StaticMesh); +// IMeshReductionManagerModule& Module = FModuleManager::Get().LoadModuleChecked("MeshReductionInterface"); +// if (!Builder.GatherSourceMeshesPerLOD(Module.GetStaticMeshReductionInterface())) +// { +// return false; +// } +// +// TArray WasReduced; +// WasReduced.AddZeroed(StaticMesh->GetNumSourceModels()); +// if (!Builder.ReduceLODs(LODGroup, Module.GetStaticMeshReductionInterface(), WasReduced)) +// { +// return false; +// } +// +// if (WasReduced.Contains(true)) +// { +// return Builder.ReplaceRawMeshModels(); +// } +// +// return false; +// } + +class IMeshBuildData +{ +public: + virtual ~IMeshBuildData() + { + } + + virtual uint32 GetWedgeIndex(uint32 FaceIndex, uint32 TriIndex) = 0; + virtual uint32 GetVertexIndex(uint32 WedgeIndex) = 0; + virtual uint32 GetVertexIndex(uint32 FaceIndex, uint32 TriIndex) = 0; + virtual FVector GetVertexPosition(uint32 WedgeIndex) = 0; + virtual FVector GetVertexPosition(uint32 FaceIndex, uint32 TriIndex) = 0; + virtual FVector2D GetVertexUV(uint32 FaceIndex, uint32 TriIndex, uint32 UVIndex) = 0; + virtual uint32 GetFaceSmoothingGroups(uint32 FaceIndex) = 0; + + virtual uint32 GetNumFaces() = 0; + virtual uint32 GetNumWedges() = 0; + + virtual TArray& GetTangentArray(uint32 Axis) = 0; + virtual void ValidateTangentArraySize() = 0; + + virtual SMikkTSpaceInterface* GetMikkTInterface() = 0; + virtual void* GetMikkTUserData() = 0; + + const IMeshUtilities2::MeshBuildOptions& BuildOptions; + TArray* OutWarningMessages; + TArray* OutWarningNames; + bool bTooManyVerts; + +protected: + IMeshBuildData( + const IMeshUtilities2::MeshBuildOptions& InBuildOptions, + TArray* InWarningMessages, + TArray* InWarningNames) + : BuildOptions(InBuildOptions) + , OutWarningMessages(InWarningMessages) + , OutWarningNames(InWarningNames) + , bTooManyVerts(false) + { + } +}; + +// +// class SkeletalMeshBuildData final : public IMeshBuildData +// { +// public: +// SkeletalMeshBuildData( +// FSkeletalMeshLODModel& InLODModel, +// const FReferenceSkeleton& InRefSkeleton, +// const TArray& InInfluences, +// const TArray& InWedges, +// const TArray& InFaces, +// const TArray& InPoints, +// const TArray& InPointToOriginalMap, +// const IMeshUtilities::MeshBuildOptions& InBuildOptions, +// TArray* InWarningMessages, +// TArray* InWarningNames) +// : IMeshBuildData(InBuildOptions, InWarningMessages, InWarningNames) +// , MikkTUserData(InWedges, InFaces, InPoints, InBuildOptions.bComputeNormals, TangentX, TangentY, TangentZ) +// , LODModel(InLODModel) +// , RefSkeleton(InRefSkeleton) +// , Influences(InInfluences) +// , Wedges(InWedges) +// , Faces(InFaces) +// , Points(InPoints) +// , PointToOriginalMap(InPointToOriginalMap) +// { +// MikkTInterface.m_getNormal = MikkGetNormal_Skeletal; +// MikkTInterface.m_getNumFaces = MikkGetNumFaces_Skeletal; +// MikkTInterface.m_getNumVerticesOfFace = MikkGetNumVertsOfFace_Skeletal; +// MikkTInterface.m_getPosition = MikkGetPosition_Skeletal; +// MikkTInterface.m_getTexCoord = MikkGetTexCoord_Skeletal; +// MikkTInterface.m_setTSpaceBasic = MikkSetTSpaceBasic_Skeletal; +// MikkTInterface.m_setTSpace = nullptr; +// +// //Fill the NTBs information +// if (!InBuildOptions.bComputeNormals || !InBuildOptions.bComputeTangents) +// { +// if (!InBuildOptions.bComputeTangents) +// { +// TangentX.AddZeroed(Wedges.Num()); +// TangentY.AddZeroed(Wedges.Num()); +// } +// +// if (!InBuildOptions.bComputeNormals) +// { +// TangentZ.AddZeroed(Wedges.Num()); +// } +// +// for (const SkeletalMeshImportData::FMeshFace& MeshFace : Faces) +// { +// for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) +// { +// uint32 WedgeIndex = MeshFace.iWedge[CornerIndex]; +// if (!InBuildOptions.bComputeTangents) +// { +// TangentX[WedgeIndex] = MeshFace.TangentX[CornerIndex]; +// TangentY[WedgeIndex] = MeshFace.TangentY[CornerIndex]; +// } +// if (!InBuildOptions.bComputeNormals) +// { +// TangentZ[WedgeIndex] = MeshFace.TangentZ[CornerIndex]; +// } +// } +// } +// } +// } +// +// virtual uint32 GetWedgeIndex(uint32 FaceIndex, uint32 TriIndex) override +// { +// return Faces[FaceIndex].iWedge[TriIndex]; +// } +// +// virtual uint32 GetVertexIndex(uint32 WedgeIndex) override +// { +// return Wedges[WedgeIndex].iVertex; +// } +// +// virtual uint32 GetVertexIndex(uint32 FaceIndex, uint32 TriIndex) override +// { +// return Wedges[Faces[FaceIndex].iWedge[TriIndex]].iVertex; +// } +// +// virtual FVector GetVertexPosition(uint32 WedgeIndex) override +// { +// return Points[Wedges[WedgeIndex].iVertex]; +// } +// +// virtual FVector GetVertexPosition(uint32 FaceIndex, uint32 TriIndex) override +// { +// return Points[Wedges[Faces[FaceIndex].iWedge[TriIndex]].iVertex]; +// } +// +// virtual FVector2D GetVertexUV(uint32 FaceIndex, uint32 TriIndex, uint32 UVIndex) override +// { +// return Wedges[Faces[FaceIndex].iWedge[TriIndex]].UVs[UVIndex]; +// } +// +// virtual uint32 GetFaceSmoothingGroups(uint32 FaceIndex) +// { +// return Faces[FaceIndex].SmoothingGroups; +// } +// +// virtual uint32 GetNumFaces() override +// { +// return Faces.Num(); +// } +// +// virtual uint32 GetNumWedges() override +// { +// return Wedges.Num(); +// } +// +// virtual TArray& GetTangentArray(uint32 Axis) override +// { +// if (Axis == 0) +// { +// return TangentX; +// } +// else if (Axis == 1) +// { +// return TangentY; +// } +// +// return TangentZ; +// } +// +// virtual void ValidateTangentArraySize() override +// { +// check(TangentX.Num() == Wedges.Num()); +// check(TangentY.Num() == Wedges.Num()); +// check(TangentZ.Num() == Wedges.Num()); +// } +// +// virtual SMikkTSpaceInterface* GetMikkTInterface() override +// { +// return &MikkTInterface; +// } +// +// virtual void* GetMikkTUserData() override +// { +// return (void*)&MikkTUserData; +// } +// +// TArray TangentX; +// TArray TangentY; +// TArray TangentZ; +// TArray Chunks; +// +// SMikkTSpaceInterface MikkTInterface; +// MikkTSpace_Skeletal_Mesh MikkTUserData; +// +// FSkeletalMeshLODModel& LODModel; +// const FReferenceSkeleton& RefSkeleton; +// const TArray& Influences; +// const TArray& Wedges; +// const TArray& Faces; +// const TArray& Points; +// const TArray& PointToOriginalMap; +// }; + +class FSkeletalMeshUtilityBuilder +{ +public: + FSkeletalMeshUtilityBuilder() + : Stage(EStage::Uninit) + { + } + +public: + void Skeletal_FindOverlappingCorners( + FOverlappingCorners& OutOverlappingCorners, + IMeshBuildData* BuildData, + float ComparisonThreshold + ) + { + int32 NumFaces = BuildData->GetNumFaces(); + int32 NumWedges = BuildData->GetNumWedges(); + check(NumFaces * 3 <= NumWedges); + + // Create a list of vertex Z/index pairs + TArray VertIndexAndZ; + VertIndexAndZ.Empty(NumWedges); + for (int32 FaceIndex = 0; FaceIndex < NumFaces; FaceIndex++) + { + for (int32 TriIndex = 0; TriIndex < 3; ++TriIndex) + { + uint32 Index = BuildData->GetWedgeIndex(FaceIndex, TriIndex); + new(VertIndexAndZ)FIndexAndZ(Index, BuildData->GetVertexPosition(Index)); + } + } + + // Sort the vertices by z value + VertIndexAndZ.Sort(FCompareIndexAndZ()); + + OutOverlappingCorners.Init(NumWedges); + + // Search for duplicates, quickly! + for (int32 i = 0; i < VertIndexAndZ.Num(); i++) + { + // only need to search forward, since we add pairs both ways + for (int32 j = i + 1; j < VertIndexAndZ.Num(); j++) + { + if (FMath::Abs(VertIndexAndZ[j].Z - VertIndexAndZ[i].Z) > ComparisonThreshold) + break; // can't be any more dups + + FVector PositionA = BuildData->GetVertexPosition(VertIndexAndZ[i].Index); + FVector PositionB = BuildData->GetVertexPosition(VertIndexAndZ[j].Index); + + if (PointsEqual(PositionA, PositionB, ComparisonThreshold)) + { + OutOverlappingCorners.Add(VertIndexAndZ[i].Index, VertIndexAndZ[j].Index); + } + } + } + + OutOverlappingCorners.FinishAdding(); + } + + void Skeletal_ComputeTriangleTangents( + TArray& TriangleTangentX, + TArray& TriangleTangentY, + TArray& TriangleTangentZ, + IMeshBuildData* BuildData, + float ComparisonThreshold + ) + { + int32 NumTriangles = BuildData->GetNumFaces(); + TriangleTangentX.Empty(NumTriangles); + TriangleTangentY.Empty(NumTriangles); + TriangleTangentZ.Empty(NumTriangles); + + //Currently GetSafeNormal do not support 0.0f threshold properly + float RealComparisonThreshold = FMath::Max(ComparisonThreshold, FLT_MIN); + TArray TriangleTangents; + for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; TriangleIndex++) + { + const int32 UVIndex = 0; + FVector Positions[3]; + FVector2D UVs[3]; + for (int32 Index = 0; Index < 3; ++Index) + { + Positions[Index] = BuildData->GetVertexPosition(TriangleIndex, Index); + UVs[Index] = BuildData->GetVertexUV(TriangleIndex, Index, UVIndex); + } + + CalculateTriangleTangentInternal(Positions[0], UVs[0], Positions[1], UVs[1], Positions[2], UVs[2], TriangleTangents, RealComparisonThreshold); + check(TriangleTangents.Num() == 3); + TriangleTangentX.Add(TriangleTangents[0]); + TriangleTangentY.Add(TriangleTangents[1]); + TriangleTangentZ.Add(TriangleTangents[2]); + } + } + + //This function add every triangles connected to the triangle queue. + //A connected triangle pair must share at least 1 vertex between the two triangles. + //If bConnectByEdge is true, the connected triangle must share at least one edge (two vertex index) + void AddAdjacentFace(IMeshBuildData* BuildData, TBitArray<>& FaceAdded, TMap>& VertexIndexToAdjacentFaces, int32 FaceIndex, TArray& TriangleQueue, const bool bConnectByEdge) + { + int32 NumFaces = (int32)BuildData->GetNumFaces(); + check(FaceAdded.Num() == NumFaces); + + TMap AdjacentFaceCommonVertices; + for (int32 Corner = 0; Corner < 3; Corner++) + { + int32 VertexIndex = BuildData->GetVertexIndex(FaceIndex, Corner); + TArray& AdjacentFaces = VertexIndexToAdjacentFaces.FindChecked(VertexIndex); + for (int32 AdjacentFaceArrayIndex = 0; AdjacentFaceArrayIndex < AdjacentFaces.Num(); ++AdjacentFaceArrayIndex) + { + int32 AdjacentFaceIndex = AdjacentFaces[AdjacentFaceArrayIndex]; + if (!FaceAdded[AdjacentFaceIndex] && AdjacentFaceIndex != FaceIndex) + { + bool bAddConnected = !bConnectByEdge; + if (bConnectByEdge) + { + int32& AdjacentFaceCommonVerticeCount = AdjacentFaceCommonVertices.FindOrAdd(AdjacentFaceIndex); + AdjacentFaceCommonVerticeCount++; + //Is the connected triangles share 2 vertex index (one edge) not only one vertex + bAddConnected = AdjacentFaceCommonVerticeCount > 1; + } + + if (bAddConnected) + { + TriangleQueue.Add(AdjacentFaceIndex); + //Add the face only once by marking the face has computed + FaceAdded[AdjacentFaceIndex] = true; + } + } + } + } + } + + //Fill FaceIndexToPatchIndex so every triangle know is unique island patch index. + //We need to respect the island when we use the smooth group to compute the normals. + //Each island patch have its own smoothgroup data, there is no triangle connectivity possible between island patch. + //@Param bConnectByEdge: If true we need at least 2 vertex index (one edge) to connect 2 triangle. If false we just need one vertex index (bowtie) + void Skeletal_FillPolygonPatch(IMeshBuildData* BuildData, TArray& FaceIndexToPatchIndex, const bool bConnectByEdge) + { + int32 NumTriangles = BuildData->GetNumFaces(); + check(FaceIndexToPatchIndex.Num() == NumTriangles); + + int32 PatchIndex = 0; + + TMap> VertexIndexToAdjacentFaces; + VertexIndexToAdjacentFaces.Reserve(BuildData->GetNumFaces() * 2); + for (int32 FaceIndex = 0; FaceIndex < NumTriangles; ++FaceIndex) + { + int32 WedgeOffset = FaceIndex * 3; + for (int32 Corner = 0; Corner < 3; Corner++) + { + int32 VertexIndex = BuildData->GetVertexIndex(FaceIndex, Corner); + TArray& AdjacentFaces = VertexIndexToAdjacentFaces.FindOrAdd(VertexIndex); + AdjacentFaces.AddUnique(FaceIndex); + } + } + + //Mark added face so we do not add them more then once + TBitArray<> FaceAdded; + FaceAdded.Init(false, NumTriangles); + + TArray TriangleQueue; + TriangleQueue.Reserve(100); + for (int32 FaceIndex = 0; FaceIndex < NumTriangles; ++FaceIndex) + { + if (FaceAdded[FaceIndex]) + { + continue; + } + TriangleQueue.Reset(); + TriangleQueue.Add(FaceIndex); //Use a queue to avoid recursive function + FaceAdded[FaceIndex] = true; + while (TriangleQueue.Num() > 0) + { + int32 CurrentTriangleIndex = TriangleQueue.Pop(false); + FaceIndexToPatchIndex[CurrentTriangleIndex] = PatchIndex; + AddAdjacentFace(BuildData, FaceAdded, VertexIndexToAdjacentFaces, CurrentTriangleIndex, TriangleQueue, bConnectByEdge); + } + PatchIndex++; + } + } + + bool IsTriangleMirror(IMeshBuildData* BuildData, const TArray& TriangleTangentZ, const uint32 FaceIdxA, const uint32 FaceIdxB) + { + if (FaceIdxA == FaceIdxB) + { + return false; + } + for (int32 CornerA = 0; CornerA < 3; ++CornerA) + { + const FVector& CornerAPosition = BuildData->GetVertexPosition((FaceIdxA * 3) + CornerA); + bool bFoundMatch = false; + for (int32 CornerB = 0; CornerB < 3; ++CornerB) + { + const FVector& CornerBPosition = BuildData->GetVertexPosition((FaceIdxB * 3) + CornerB); + if (PointsEqual(CornerAPosition, CornerBPosition, BuildData->BuildOptions.OverlappingThresholds)) + { + bFoundMatch = true; + break; + } + } + + if (!bFoundMatch) + { + return false; + } + } + //Check if the triangles normals are opposite and parallel. Dot product equal -1.0f + if (FMath::IsNearlyEqual(FVector::DotProduct(TriangleTangentZ[FaceIdxA], TriangleTangentZ[FaceIdxB]), -1.0f, KINDA_SMALL_NUMBER)) + { + return true; + } + return false; + } + + void Skeletal_ComputeTangents( + const FString& SkeletalMeshName, + IMeshBuildData* BuildData, + const FOverlappingCorners& OverlappingCorners + ) + { + bool bBlendOverlappingNormals = true; + bool bIgnoreDegenerateTriangles = BuildData->BuildOptions.bRemoveDegenerateTriangles; + bool bComputeWeightedNormals = BuildData->BuildOptions.bComputeWeightedNormals; + bool bUseMikktSpace = BuildData->BuildOptions.bUseMikkTSpace && (BuildData->BuildOptions.bComputeNormals || BuildData->BuildOptions.bComputeTangents); + + int32 NumFaces = BuildData->GetNumFaces(); + int32 NumWedges = BuildData->GetNumWedges(); + check(NumFaces * 3 <= NumWedges); + + // Compute per-triangle tangents. + TArray TriangleTangentX; + TArray TriangleTangentY; + TArray TriangleTangentZ; + + Skeletal_ComputeTriangleTangents( + TriangleTangentX, + TriangleTangentY, + TriangleTangentZ, + BuildData, + bIgnoreDegenerateTriangles ? SMALL_NUMBER : FLT_MIN + ); + + TArray FaceIndexToPatchIndex; + FaceIndexToPatchIndex.AddZeroed(NumFaces); + //Since we use triangle normals to compute the vertex normal, we need a full edge connected (2 vertex component per triangle) + const bool bConnectByEdge = true; + Skeletal_FillPolygonPatch(BuildData, FaceIndexToPatchIndex, bConnectByEdge); + + TArray& WedgeTangentX = BuildData->GetTangentArray(0); + TArray& WedgeTangentY = BuildData->GetTangentArray(1); + TArray& WedgeTangentZ = BuildData->GetTangentArray(2); + + // Declare these out here to avoid reallocations. + TArray RelevantFacesForCorner[3]; + TArray AdjacentFaces; + + // Allocate storage for tangents and normal if none were provided. + if (WedgeTangentX.Num() != NumWedges) + { + WedgeTangentX.Empty(NumWedges); + WedgeTangentX.AddZeroed(NumWedges); + } + if (WedgeTangentY.Num() != NumWedges) + { + WedgeTangentY.Empty(NumWedges); + WedgeTangentY.AddZeroed(NumWedges); + } + if (WedgeTangentZ.Num() != NumWedges) + { + WedgeTangentZ.Empty(NumWedges); + WedgeTangentZ.AddZeroed(NumWedges); + } + + bool bIsZeroLengthNormalErrorMessageDisplayed = false; + // we need to calculate normals for MikkTSpace + for (int32 FaceIndex = 0; FaceIndex < NumFaces; FaceIndex++) + { + int32 PatchIndex = FaceIndexToPatchIndex[FaceIndex]; + int32 WedgeOffset = FaceIndex * 3; + FVector CornerPositions[3]; + FVector CornerTangentX[3]; + FVector CornerTangentY[3]; + FVector CornerNormal[3]; + + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + CornerTangentX[CornerIndex] = FVector::ZeroVector; + CornerTangentY[CornerIndex] = FVector::ZeroVector; + CornerNormal[CornerIndex] = FVector::ZeroVector; + CornerPositions[CornerIndex] = BuildData->GetVertexPosition(FaceIndex, CornerIndex); + RelevantFacesForCorner[CornerIndex].Reset(); + } + + // Don't process degenerate triangles. + if (PointsEqual(CornerPositions[0], CornerPositions[1], BuildData->BuildOptions.OverlappingThresholds) + || PointsEqual(CornerPositions[0], CornerPositions[2], BuildData->BuildOptions.OverlappingThresholds) + || PointsEqual(CornerPositions[1], CornerPositions[2], BuildData->BuildOptions.OverlappingThresholds)) + { + continue; + } + + // No need to process triangles if tangents already exist. + bool bCornerHasNormal[3] = {0}; + bool bCornerHasTangents[3] = {0}; + bool bSkipNTB = true; + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + bCornerHasNormal[CornerIndex] = !WedgeTangentZ[WedgeOffset + CornerIndex].IsNearlyZero(); + bCornerHasTangents[CornerIndex] = !WedgeTangentX[WedgeOffset + CornerIndex].IsNearlyZero() && !WedgeTangentY[WedgeOffset + CornerIndex].IsNearlyZero(); + + //If we want to compute mikkt we dont check tangents to skip this corner + if (!bCornerHasNormal[CornerIndex] || (!bUseMikktSpace && !bCornerHasTangents[CornerIndex])) + { + bSkipNTB = false; + } + } + if (bSkipNTB) + { + continue; + } + + // Calculate smooth vertex normals. + float Determinant = FVector::Triple( + TriangleTangentX[FaceIndex], + TriangleTangentY[FaceIndex], + TriangleTangentZ[FaceIndex] + ); + + // Start building a list of faces adjacent to this face. + AdjacentFaces.Reset(); + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + int32 ThisCornerIndex = WedgeOffset + CornerIndex; + const TArray& DupVerts = OverlappingCorners.FindIfOverlapping(ThisCornerIndex); + if (DupVerts.Num() == 0) + { + AdjacentFaces.AddUnique(ThisCornerIndex / 3); // I am a "dup" of myself + } + for (int32 k = 0; k < DupVerts.Num(); k++) + { + int32 PotentialTriangleIndex = DupVerts[k] / 3; + //Do not add a triangle that was remove + if (TriangleTangentZ.IsValidIndex(PotentialTriangleIndex)) + { + bool bDegeneratedTriangles = TriangleTangentZ[FaceIndex].IsNearlyZero() || TriangleTangentZ[PotentialTriangleIndex].IsNearlyZero(); + //Do not add mirror triangle to the adjacentFaces. Also make sure adjacent triangle is in the same connected triangle patch. Accept connected degenerate triangle + if ((bDegeneratedTriangles || !IsTriangleMirror(BuildData, TriangleTangentZ, FaceIndex, PotentialTriangleIndex)) && PatchIndex == FaceIndexToPatchIndex[PotentialTriangleIndex]) + { + AdjacentFaces.AddUnique(PotentialTriangleIndex); + } + } + } + } + + // We need to sort these here because the criteria for point equality is + // exact, so we must ensure the exact same order for all dups. + AdjacentFaces.Sort(); + + // Process adjacent faces + for (int32 AdjacentFaceIndex = 0; AdjacentFaceIndex < AdjacentFaces.Num(); AdjacentFaceIndex++) + { + int32 OtherFaceIndex = AdjacentFaces[AdjacentFaceIndex]; + for (int32 OurCornerIndex = 0; OurCornerIndex < 3; OurCornerIndex++) + { + if (bCornerHasNormal[OurCornerIndex] && (bUseMikktSpace || bCornerHasTangents[OurCornerIndex])) + continue; + + FFanFace NewFanFace; + int32 CommonIndexCount = 0; + + // Check for vertices in common. + if (FaceIndex == OtherFaceIndex) + { + CommonIndexCount = 3; + NewFanFace.LinkedVertexIndex = OurCornerIndex; + } + else + { + // Check matching vertices against main vertex . + for (int32 OtherCornerIndex = 0; OtherCornerIndex < 3; OtherCornerIndex++) + { + if (PointsEqual( + CornerPositions[OurCornerIndex], + BuildData->GetVertexPosition(OtherFaceIndex, OtherCornerIndex), + BuildData->BuildOptions.OverlappingThresholds + )) + { + CommonIndexCount++; + NewFanFace.LinkedVertexIndex = OtherCornerIndex; + } + } + } + + // Add if connected by at least one point. Smoothing matches are considered later. + if (CommonIndexCount > 0) + { + NewFanFace.FaceIndex = OtherFaceIndex; + NewFanFace.bFilled = (OtherFaceIndex == FaceIndex); // Starter face for smoothing floodfill. + NewFanFace.bBlendTangents = NewFanFace.bFilled; + NewFanFace.bBlendNormals = NewFanFace.bFilled; + RelevantFacesForCorner[OurCornerIndex].Add(NewFanFace); + } + } + } + + // Find true relevance of faces for a vertex normal by traversing + // smoothing-group-compatible connected triangle fans around common vertices. + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + if (bCornerHasNormal[CornerIndex] && (bUseMikktSpace || bCornerHasTangents[CornerIndex])) + continue; + + int32 NewConnections; + do + { + NewConnections = 0; + for (int32 OtherFaceIdx = 0; OtherFaceIdx < RelevantFacesForCorner[CornerIndex].Num(); OtherFaceIdx++) + { + FFanFace& OtherFace = RelevantFacesForCorner[CornerIndex][OtherFaceIdx]; + // The vertex' own face is initially the only face with bFilled == true. + if (OtherFace.bFilled) + { + for (int32 NextFaceIndex = 0; NextFaceIndex < RelevantFacesForCorner[CornerIndex].Num(); NextFaceIndex++) + { + FFanFace& NextFace = RelevantFacesForCorner[CornerIndex][NextFaceIndex]; + if (!NextFace.bFilled) // && !NextFace.bBlendTangents) + { + if ((NextFaceIndex != OtherFaceIdx) + && (BuildData->GetFaceSmoothingGroups(NextFace.FaceIndex) & BuildData->GetFaceSmoothingGroups(OtherFace.FaceIndex))) + { + int32 CommonVertices = 0; + int32 CommonTangentVertices = 0; + int32 CommonNormalVertices = 0; + for (int32 OtherCornerIndex = 0; OtherCornerIndex < 3; OtherCornerIndex++) + { + for (int32 NextCornerIndex = 0; NextCornerIndex < 3; NextCornerIndex++) + { + int32 NextVertexIndex = BuildData->GetVertexIndex(NextFace.FaceIndex, NextCornerIndex); + int32 OtherVertexIndex = BuildData->GetVertexIndex(OtherFace.FaceIndex, OtherCornerIndex); + if (PointsEqual( + BuildData->GetVertexPosition(NextFace.FaceIndex, NextCornerIndex), + BuildData->GetVertexPosition(OtherFace.FaceIndex, OtherCornerIndex), + BuildData->BuildOptions.OverlappingThresholds)) + { + CommonVertices++; + if (!bUseMikktSpace && UVsEqual( + BuildData->GetVertexUV(NextFace.FaceIndex, NextCornerIndex, 0), + BuildData->GetVertexUV(OtherFace.FaceIndex, OtherCornerIndex, 0), + BuildData->BuildOptions.OverlappingThresholds)) + { + CommonTangentVertices++; + } + if (bBlendOverlappingNormals + || NextVertexIndex == OtherVertexIndex) + { + CommonNormalVertices++; + } + } + } + } + // Flood fill faces with more than one common vertices which must be touching edges. + if (CommonVertices > 1) + { + NextFace.bFilled = true; + NextFace.bBlendNormals = (CommonNormalVertices > 1); + NewConnections++; + // Only blend tangents if there is no UV seam along the edge with this face. + if (!bUseMikktSpace && OtherFace.bBlendTangents && CommonTangentVertices > 1) + { + float OtherDeterminant = FVector::Triple( + TriangleTangentX[NextFace.FaceIndex], + TriangleTangentY[NextFace.FaceIndex], + TriangleTangentZ[NextFace.FaceIndex] + ); + if ((Determinant * OtherDeterminant) > 0.0f) + { + NextFace.bBlendTangents = true; + } + } + } + } + } + } + } + } + } + while (NewConnections > 0); + } + + // Vertex normal construction. + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + bool bUseProvidedNormal = bCornerHasNormal[CornerIndex]; + bool bUseProvidedTangents = !bUseMikktSpace && bCornerHasTangents[CornerIndex]; + if (bUseProvidedNormal) + { + CornerNormal[CornerIndex] = WedgeTangentZ[WedgeOffset + CornerIndex]; + } + if (bUseProvidedTangents) + { + CornerTangentX[CornerIndex] = WedgeTangentX[WedgeOffset + CornerIndex]; + CornerTangentY[CornerIndex] = WedgeTangentY[WedgeOffset + CornerIndex]; + } + + if (!bUseProvidedNormal || !bUseProvidedTangents) + { + for (int32 RelevantFaceIdx = 0; RelevantFaceIdx < RelevantFacesForCorner[CornerIndex].Num(); RelevantFaceIdx++) + { + FFanFace const& RelevantFace = RelevantFacesForCorner[CornerIndex][RelevantFaceIdx]; + if (RelevantFace.bFilled) + { + int32 OtherFaceIndex = RelevantFace.FaceIndex; + float CornerWeight = 1.0f; + if (bComputeWeightedNormals) + { + FVector OtherFacePoint[3] = {BuildData->GetVertexPosition(OtherFaceIndex, 0), BuildData->GetVertexPosition(OtherFaceIndex, 1), BuildData->GetVertexPosition(OtherFaceIndex, 2)}; + float OtherFaceArea = TriangleUtilities::ComputeTriangleArea(OtherFacePoint[0], OtherFacePoint[1], OtherFacePoint[2]); + int32 OtherFaceCornerIndex = RelevantFace.LinkedVertexIndex; + float OtherFaceAngle = TriangleUtilities::ComputeTriangleCornerAngle(OtherFacePoint[OtherFaceCornerIndex], OtherFacePoint[(OtherFaceCornerIndex + 1) % 3], OtherFacePoint[(OtherFaceCornerIndex + 2) % 3]); + //Get the CornerWeight + CornerWeight = OtherFaceArea * OtherFaceAngle; + } + if (!bUseProvidedTangents && RelevantFace.bBlendTangents) + { + CornerTangentX[CornerIndex] += CornerWeight * TriangleTangentX[OtherFaceIndex]; + CornerTangentY[CornerIndex] += CornerWeight * TriangleTangentY[OtherFaceIndex]; + } + if (!bUseProvidedNormal && RelevantFace.bBlendNormals) + { + CornerNormal[CornerIndex] += CornerWeight * TriangleTangentZ[OtherFaceIndex]; + } + } + } + if (!WedgeTangentX[WedgeOffset + CornerIndex].IsNearlyZero()) + { + CornerTangentX[CornerIndex] = WedgeTangentX[WedgeOffset + CornerIndex]; + } + if (!WedgeTangentY[WedgeOffset + CornerIndex].IsNearlyZero()) + { + CornerTangentY[CornerIndex] = WedgeTangentY[WedgeOffset + CornerIndex]; + } + if (!WedgeTangentZ[WedgeOffset + CornerIndex].IsNearlyZero()) + { + CornerNormal[CornerIndex] = WedgeTangentZ[WedgeOffset + CornerIndex]; + } + } + } + + // Normalization. + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + CornerNormal[CornerIndex].Normalize(); + if (!bUseMikktSpace) + { + CornerTangentX[CornerIndex].Normalize(); + CornerTangentY[CornerIndex].Normalize(); + + // Gram-Schmidt orthogonalization + CornerTangentY[CornerIndex] -= CornerTangentX[CornerIndex] * (CornerTangentX[CornerIndex] | CornerTangentY[CornerIndex]); + CornerTangentY[CornerIndex].Normalize(); + + CornerTangentX[CornerIndex] -= CornerNormal[CornerIndex] * (CornerNormal[CornerIndex] | CornerTangentX[CornerIndex]); + CornerTangentX[CornerIndex].Normalize(); + CornerTangentY[CornerIndex] -= CornerNormal[CornerIndex] * (CornerNormal[CornerIndex] | CornerTangentY[CornerIndex]); + CornerTangentY[CornerIndex].Normalize(); + } + } + + auto VerifyTangentSpace = [&bIsZeroLengthNormalErrorMessageDisplayed, &BuildData, &SkeletalMeshName](FVector& NormalizedVector) + { + if (NormalizedVector.IsNearlyZero() || NormalizedVector.ContainsNaN()) + { + NormalizedVector = FVector::ZeroVector; + //We also notify the log that we compute a zero length normals, so the user is aware of it + if (!bIsZeroLengthNormalErrorMessageDisplayed) + { + bIsZeroLengthNormalErrorMessageDisplayed = true; + + // add warning message if available, do a log if not + FFormatNamedArguments Args; + Args.Add(TEXT("SkeletalMeshName"), FText::FromString(SkeletalMeshName)); + FText TextMessage = FText::Format(LOCTEXT("Skeletal_ComputeTangents_MikkTSpace_Warning_ZeroLengthNormal", "{SkeletalMeshName} ComputeTangents MikkTSpace function: Compute a zero length normal vector."), Args); + if (BuildData->OutWarningMessages) + { + BuildData->OutWarningMessages->Add(TextMessage); + if (BuildData->OutWarningNames) + { + BuildData->OutWarningNames->Add(FFbxErrors::Generic_Mesh_TangentsComputeError); + } + } + else + { + UE_LOG(LogSkeletalMesh, Display, TEXT("%s"), *(TextMessage.ToString())); + } + } + } + }; + + // Copy back to the mesh. + for (int32 CornerIndex = 0; CornerIndex < 3; CornerIndex++) + { + VerifyTangentSpace(CornerNormal[CornerIndex]); + WedgeTangentZ[WedgeOffset + CornerIndex] = CornerNormal[CornerIndex]; + + if (!bUseMikktSpace) + { + VerifyTangentSpace(CornerTangentX[CornerIndex]); + WedgeTangentX[WedgeOffset + CornerIndex] = CornerTangentX[CornerIndex]; + + VerifyTangentSpace(CornerTangentY[CornerIndex]); + WedgeTangentY[WedgeOffset + CornerIndex] = CornerTangentY[CornerIndex]; + } + } + } + + if (bUseMikktSpace) + { + // we can use mikktspace to calculate the tangents + SMikkTSpaceContext MikkTContext; + MikkTContext.m_pInterface = BuildData->GetMikkTInterface(); + MikkTContext.m_pUserData = BuildData->GetMikkTUserData(); + //MikkTContext.m_bIgnoreDegenerates = bIgnoreDegenerateTriangles; + + genTangSpaceDefault(&MikkTContext); + } + + check(WedgeTangentX.Num() == NumWedges); + check(WedgeTangentY.Num() == NumWedges); + check(WedgeTangentZ.Num() == NumWedges); + } + + bool PrepareSourceMesh(const FString& SkeletalMeshName, IMeshBuildData* BuildData) + { + check(Stage == EStage::Uninit); + + BeginSlowTask(); + + FOverlappingCorners& OverlappingCorners = *new FOverlappingCorners; + LODOverlappingCorners.Add(&OverlappingCorners); + + float ComparisonThreshold = THRESH_POINTS_ARE_SAME; //GetComparisonThreshold(LODBuildSettings[LODIndex]); + int32 NumWedges = BuildData->GetNumWedges(); + + // Find overlapping corners to accelerate adjacency. + Skeletal_FindOverlappingCorners(OverlappingCorners, BuildData, ComparisonThreshold); + + // Figure out if we should recompute normals and tangents. + bool bRecomputeNormals = BuildData->BuildOptions.bComputeNormals; + bool bRecomputeTangents = BuildData->BuildOptions.bComputeTangents; + + // Dump normals and tangents if we are recomputing them. + if (bRecomputeTangents) + { + TArray& TangentX = BuildData->GetTangentArray(0); + TArray& TangentY = BuildData->GetTangentArray(1); + + TangentX.Empty(NumWedges); + TangentX.AddZeroed(NumWedges); + TangentY.Empty(NumWedges); + TangentY.AddZeroed(NumWedges); + } + if (bRecomputeNormals) + { + TArray& TangentZ = BuildData->GetTangentArray(2); + TangentZ.Empty(NumWedges); + TangentZ.AddZeroed(NumWedges); + } + + // Compute any missing tangents. MikkTSpace should be use only when the user want to recompute the normals or tangents otherwise should always fallback on builtin tangent + Skeletal_ComputeTangents(SkeletalMeshName, BuildData, OverlappingCorners); + + // At this point the mesh will have valid tangents. + BuildData->ValidateTangentArraySize(); + check(LODOverlappingCorners.Num() == 1); + + EndSlowTask(); + + Stage = EStage::Prepared; + return true; + } + + // + // bool GenerateSkeletalRenderMesh(IMeshBuildData* InBuildData) + // { + // check(Stage == EStage::Prepared); + // + // SkeletalMeshBuildData& BuildData = *(SkeletalMeshBuildData*)InBuildData; + // + // BeginSlowTask(); + // + // // Find wedge influences. + // TArray WedgeInfluenceIndices; + // TMap VertexIndexToInfluenceIndexMap; + // + // for (uint32 LookIdx = 0; LookIdx < (uint32)BuildData.Influences.Num(); LookIdx++) + // { + // // Order matters do not allow the map to overwrite an existing value. + // if (!VertexIndexToInfluenceIndexMap.Find(BuildData.Influences[LookIdx].VertIndex)) + // { + // VertexIndexToInfluenceIndexMap.Add(BuildData.Influences[LookIdx].VertIndex, LookIdx); + // } + // } + // + // for (int32 WedgeIndex = 0; WedgeIndex < BuildData.Wedges.Num(); WedgeIndex++) + // { + // uint32* InfluenceIndex = VertexIndexToInfluenceIndexMap.Find(BuildData.Wedges[WedgeIndex].iVertex); + // + // if (InfluenceIndex) + // { + // WedgeInfluenceIndices.Add(*InfluenceIndex); + // } + // else + // { + // // we have missing influence vert, we weight to root + // WedgeInfluenceIndices.Add(0); + // + // // add warning message + // if (BuildData.OutWarningMessages) + // { + // BuildData.OutWarningMessages->Add(FText::Format(FText::FromString("Missing influence on vert {0}. Weighting it to root."), FText::FromString(FString::FromInt(BuildData.Wedges[WedgeIndex].iVertex)))); + // if (BuildData.OutWarningNames) + // { + // BuildData.OutWarningNames->Add(FFbxErrors::SkeletalMesh_VertMissingInfluences); + // } + // } + // } + // } + // + // check(BuildData.Wedges.Num() == WedgeInfluenceIndices.Num()); + // + // TArray VertIndexAndZ; + // TArray RawVertices; + // + // VertIndexAndZ.Empty(BuildData.Points.Num()); + // RawVertices.Reserve(BuildData.Points.Num()); + // + // for (int32 FaceIndex = 0; FaceIndex < BuildData.Faces.Num(); FaceIndex++) + // { + // // Only update the status progress bar if we are in the game thread and every thousand faces. + // // Updating status is extremely slow + // if (FaceIndex % 5000 == 0) + // { + // UpdateSlowTask(FaceIndex, BuildData.Faces.Num()); + // } + // + // const SkeletalMeshImportData::FMeshFace& Face = BuildData.Faces[FaceIndex]; + // + // for (int32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) + // { + // FSoftSkinBuildVertex Vertex; + // const uint32 WedgeIndex = BuildData.GetWedgeIndex(FaceIndex, VertexIndex); + // const SkeletalMeshImportData::FMeshWedge& Wedge = BuildData.Wedges[WedgeIndex]; + // + // Vertex.Position = BuildData.GetVertexPosition(FaceIndex, VertexIndex); + // + // FVector TangentX, TangentY, TangentZ; + // TangentX = BuildData.TangentX[WedgeIndex].GetSafeNormal(); + // TangentY = BuildData.TangentY[WedgeIndex].GetSafeNormal(); + // TangentZ = BuildData.TangentZ[WedgeIndex].GetSafeNormal(); + // + // // Normalize overridden tangents. Its possible for them to import un-normalized. + // TangentX.Normalize(); + // TangentY.Normalize(); + // TangentZ.Normalize(); + // + // Vertex.TangentX = TangentX; + // Vertex.TangentY = TangentY; + // Vertex.TangentZ = TangentZ; + // + // FMemory::Memcpy(Vertex.UVs, Wedge.UVs, sizeof(FVector2D)*MAX_TEXCOORDS); + // Vertex.Color = Wedge.Color; + // + // { + // // Count the influences. + // int32 InfIdx = WedgeInfluenceIndices[Face.iWedge[VertexIndex]]; + // int32 LookIdx = InfIdx; + // + // uint32 InfluenceCount = 0; + // while (BuildData.Influences.IsValidIndex(LookIdx) && (BuildData.Influences[LookIdx].VertIndex == Wedge.iVertex)) + // { + // InfluenceCount++; + // LookIdx++; + // } + // + // InfluenceCount = FMath::Min(InfluenceCount, MAX_TOTAL_INFLUENCES); + // if (InfluenceCount > EXTRA_BONE_INFLUENCES && !FGPUBaseSkinVertexFactory::UseUnlimitedBoneInfluences(InfluenceCount)) + // { + // UE_LOG(LogSkeletalMesh, Display, TEXT("Skeletal mesh of %d bone influences requires unlimited bone influence mode on. Influence truncated to %d."), InfluenceCount, EXTRA_BONE_INFLUENCES); + // InfluenceCount = EXTRA_BONE_INFLUENCES; + // } + // + // // Setup the vertex influences. + // Vertex.InfluenceBones[0] = 0; + // Vertex.InfluenceWeights[0] = 255; + // for (uint32 i = 1; i < MAX_TOTAL_INFLUENCES; i++) + // { + // Vertex.InfluenceBones[i] = 0; + // Vertex.InfluenceWeights[i] = 0; + // } + // + // uint32 TotalInfluenceWeight = 0; + // for (uint32 i = 0; i < InfluenceCount; i++) + // { + // FBoneIndexType BoneIndex = (FBoneIndexType)BuildData.Influences[InfIdx + i].BoneIndex; + // if (BoneIndex >= BuildData.RefSkeleton.GetRawBoneNum()) + // continue; + // + // Vertex.InfluenceBones[i] = BoneIndex; + // Vertex.InfluenceWeights[i] = (uint8)(BuildData.Influences[InfIdx + i].Weight * 255.0f); + // TotalInfluenceWeight += Vertex.InfluenceWeights[i]; + // } + // Vertex.InfluenceWeights[0] += 255 - TotalInfluenceWeight; + // } + // + // // Add the vertex as well as its original index in the points array + // Vertex.PointWedgeIdx = Wedge.iVertex; + // + // int32 RawIndex = RawVertices.Add(Vertex); + // + // // Add an efficient way to find dupes of this vertex later for fast combining of vertices + // FSkeletalMeshVertIndexAndZ IAndZ; + // IAndZ.Index = RawIndex; + // IAndZ.Z = Vertex.Position.Z; + // + // VertIndexAndZ.Add(IAndZ); + // } + // } + // + // // Generate chunks and their vertices and indices + // SkeletalMeshTools::BuildSkeletalMeshChunks(BuildData.Faces, RawVertices, VertIndexAndZ, BuildData.BuildOptions.OverlappingThresholds, BuildData.Chunks, BuildData.bTooManyVerts); + // + // //Get alternate skinning weights map to retrieve easily the data + // TMap> AlternateBoneIDs; + // AlternateBoneIDs.Reserve(BuildData.Points.Num()); + // for (auto Kvp : BuildData.LODModel.SkinWeightProfiles) + // { + // FImportedSkinWeightProfileData& ImportedProfileData = Kvp.Value; + // if (ImportedProfileData.SourceModelInfluences.Num() > 0) + // { + // for (int32 InfluenceIndex = 0; InfluenceIndex < ImportedProfileData.SourceModelInfluences.Num(); ++InfluenceIndex) + // { + // const SkeletalMeshImportData::FVertInfluence& VertInfluence = ImportedProfileData.SourceModelInfluences[InfluenceIndex]; + // if (VertInfluence.Weight > 0.0f) + // { + // TArray& BoneMap = AlternateBoneIDs.FindOrAdd(VertInfluence.VertIndex); + // BoneMap.AddUnique(VertInfluence.BoneIndex); + // } + // } + // } + // } + // + // // Chunk vertices to satisfy the requested limit. + // const uint32 MaxGPUSkinBones = FGPUBaseSkinVertexFactory::GetMaxGPUSkinBones(); + // check(MaxGPUSkinBones <= FGPUBaseSkinVertexFactory::GHardwareMaxGPUSkinBones); + // SkeletalMeshTools::ChunkSkinnedVertices(BuildData.Chunks, AlternateBoneIDs, MaxGPUSkinBones); + // + // EndSlowTask(); + // + // Stage = EStage::GenerateRendering; + // return true; + // } + + void BeginSlowTask() + { + if (IsInGameThread()) + { + GWarn->BeginSlowTask(NSLOCTEXT("UnrealEd", "ProcessingSkeletalTriangles", "Processing Mesh Triangles"), true); + } + } + + void UpdateSlowTask(int32 Numerator, int32 Denominator) + { + if (IsInGameThread()) + { + GWarn->StatusUpdate(Numerator, Denominator, NSLOCTEXT("UnrealEd", "ProcessingSkeletalTriangles", "Processing Mesh Triangles")); + } + } + + void EndSlowTask() + { + if (IsInGameThread()) + { + GWarn->EndSlowTask(); + } + } + +private: + enum class EStage + { + Uninit, + Prepared, + GenerateRendering, + }; + + TIndirectArray LODOverlappingCorners; + EStage Stage; +}; + +// +// bool FMeshUtilities2::BuildSkeletalMesh(FSkeletalMeshLODModel& LODModel, const FString& SkeletalMeshName, const FReferenceSkeleton& RefSkeleton, const TArray& Influences, const TArray& Wedges, const TArray& Faces, const TArray& Points, const TArray& PointToOriginalMap, const MeshBuildOptions& BuildOptions, TArray * OutWarningMessages, TArray * OutWarningNames) +// { +// #if WITH_EDITORONLY_DATA +// +// auto UpdateOverlappingVertices = [](FSkeletalMeshLODModel& InLODModel) +// { +// // clear first +// for (int32 SectionIdx = 0; SectionIdx < InLODModel.Sections.Num(); SectionIdx++) +// { +// FSkelMeshSection& CurSection = InLODModel.Sections[SectionIdx]; +// CurSection.OverlappingVertices.Reset(); +// } +// +// for (int32 SectionIdx = 0; SectionIdx < InLODModel.Sections.Num(); SectionIdx++) +// { +// FSkelMeshSection& CurSection = InLODModel.Sections[SectionIdx]; +// const int32 NumSoftVertices = CurSection.SoftVertices.Num(); +// +// // Create a list of vertex Z/index pairs +// TArray VertIndexAndZ; +// VertIndexAndZ.Reserve(NumSoftVertices); +// for (int32 VertIndex = 0; VertIndex < NumSoftVertices; ++VertIndex) +// { +// FSoftSkinVertex& SrcVert = CurSection.SoftVertices[VertIndex]; +// new(VertIndexAndZ)FIndexAndZ(VertIndex, SrcVert.Position); +// } +// VertIndexAndZ.Sort(FCompareIndexAndZ()); +// +// // Search for duplicates, quickly! +// for (int32 i = 0; i < VertIndexAndZ.Num(); ++i) +// { +// const uint32 SrcVertIndex = VertIndexAndZ[i].Index; +// const float Z = VertIndexAndZ[i].Z; +// FSoftSkinVertex& SrcVert = CurSection.SoftVertices[SrcVertIndex]; +// +// // only need to search forward, since we add pairs both ways +// for (int32 j = i + 1; j < VertIndexAndZ.Num(); ++j) +// { +// if (FMath::Abs(VertIndexAndZ[j].Z - Z) > THRESH_POINTS_ARE_SAME) +// break; // can't be any more dups +// +// const uint32 IterVertIndex = VertIndexAndZ[j].Index; +// FSoftSkinVertex& IterVert = CurSection.SoftVertices[IterVertIndex]; +// if (PointsEqual(SrcVert.Position, IterVert.Position)) +// { +// // if so, we add to overlapping vert +// TArray& SrcValueArray = CurSection.OverlappingVertices.FindOrAdd(SrcVertIndex); +// SrcValueArray.Add(IterVertIndex); +// +// TArray& IterValueArray = CurSection.OverlappingVertices.FindOrAdd(IterVertIndex); +// IterValueArray.Add(SrcVertIndex); +// } +// } +// } +// } +// }; +// +// SkeletalMeshBuildData BuildData( +// LODModel, +// RefSkeleton, +// Influences, +// Wedges, +// Faces, +// Points, +// PointToOriginalMap, +// BuildOptions, +// OutWarningMessages, +// OutWarningNames); +// +// FSkeletalMeshUtilityBuilder Builder; +// if (!Builder.PrepareSourceMesh(SkeletalMeshName, &BuildData)) +// { +// return false; +// } +// +// if (!Builder.GenerateSkeletalRenderMesh(&BuildData)) +// { +// return false; +// } +// +// // Build the skeletal model from chunks. +// Builder.BeginSlowTask(); +// BuildSkeletalModelFromChunks(BuildData.LODModel, BuildData.RefSkeleton, BuildData.Chunks, BuildData.PointToOriginalMap); +// UpdateOverlappingVertices(BuildData.LODModel); +// Builder.EndSlowTask(); +// +// // Only show these warnings if in the game thread. When importing morph targets, this function can run in another thread and these warnings dont prevent the mesh from importing +// if (IsInGameThread()) +// { +// bool bHasBadSections = false; +// for (int32 SectionIndex = 0; SectionIndex < BuildData.LODModel.Sections.Num(); SectionIndex++) +// { +// FSkelMeshSection& Section = BuildData.LODModel.Sections[SectionIndex]; +// bHasBadSections |= (Section.NumTriangles == 0); +// +// // Log info about the section. +// UE_LOG(LogSkeletalMesh, Log, TEXT("Section %u: Material=%u, %u triangles"), +// SectionIndex, +// Section.MaterialIndex, +// Section.NumTriangles +// ); +// } +// if (bHasBadSections) +// { +// FText BadSectionMessage(NSLOCTEXT("UnrealEd", "Error_SkeletalMeshHasBadSections", "Input mesh has a section with no triangles. This mesh may not render properly.")); +// if (BuildData.OutWarningMessages) +// { +// BuildData.OutWarningMessages->Add(BadSectionMessage); +// if (BuildData.OutWarningNames) +// { +// BuildData.OutWarningNames->Add(FFbxErrors::SkeletalMesh_SectionWithNoTriangle); +// } +// } +// else +// { +// FMessageDialog::Open(EAppMsgType::Ok, BadSectionMessage); +// } +// } +// +// if (BuildData.bTooManyVerts) +// { +// FText TooManyVertsMessage(NSLOCTEXT("UnrealEd", "Error_SkeletalMeshTooManyVertices", "Input mesh has too many vertices. The generated mesh will be corrupt! Consider adding extra materials to split up the source mesh into smaller chunks.")); +// +// if (BuildData.OutWarningMessages) +// { +// BuildData.OutWarningMessages->Add(TooManyVertsMessage); +// if (BuildData.OutWarningNames) +// { +// BuildData.OutWarningNames->Add(FFbxErrors::SkeletalMesh_TooManyVertices); +// } +// } +// else +// { +// FMessageDialog::Open(EAppMsgType::Ok, TooManyVertsMessage); +// } +// } +// } +// +// return true; +// #else +// if (OutWarningMessages) +// { +// OutWarningMessages->Add(FText::FromString(TEXT("Cannot call FMeshUtilities2::BuildSkeletalMesh on a console!"))); +// } +// else +// { +// UE_LOG(LogSkeletalMesh, Fatal, TEXT("Cannot call FMeshUtilities2::BuildSkeletalMesh on a console!")); +// } +// return false; +// #endif +// } + +//The fail safe is there to avoid zeros in the tangents. Even if the fail safe prevent zero NTBs, a warning should be generate by the caller to let the artist know something went wrong +//Using a fail safe can lead to hard edge where its suppose to be smooth, it can also have some impact on the shading (lighting for tangentZ and normal map for tangentX and Y) +//Normally because we use the triangle data the tangent space is in a good direction and should give proper result. +void TangentFailSafe(const FVector& TriangleTangentX, const FVector& TriangleTangentY, const FVector& TriangleTangentZ + , FVector& TangentX, FVector& TangentY, FVector& TangentZ) +{ + bool bTangentXZero = TangentX.IsNearlyZero() || TangentX.ContainsNaN(); + bool bTangentYZero = TangentY.IsNearlyZero() || TangentY.ContainsNaN(); + bool bTangentZZero = TangentZ.IsNearlyZero() || TangentZ.ContainsNaN(); + + if (!bTangentXZero && !bTangentYZero && !bTangentZZero) + { + //No need to fail safe if everything is different from zero + return; + } + if (!bTangentZZero) + { + if (!bTangentXZero) + { + //Valid TangentZ and TangentX, we can recompute TangentY + TangentY = FVector::CrossProduct(TangentZ, TangentX).GetSafeNormal(); + } + else if (!bTangentYZero) + { + //Valid TangentZ and TangentY, we can recompute TangentX + TangentX = FVector::CrossProduct(TangentY, TangentZ).GetSafeNormal(); + } + else + { + //TangentX and Y are invalid, use the triangle data, can cause a hard edge + TangentX = TriangleTangentX.GetSafeNormal(); + TangentY = TriangleTangentY.GetSafeNormal(); + } + } + else if (!bTangentXZero) + { + if (!bTangentYZero) + { + //Valid TangentX and TangentY, we can recompute TangentZ + TangentZ = FVector::CrossProduct(TangentX, TangentY).GetSafeNormal(); + } + else + { + //TangentY and Z are invalid, use the triangle data, can cause a hard edge + TangentZ = TriangleTangentZ.GetSafeNormal(); + TangentY = TriangleTangentY.GetSafeNormal(); + } + } + else if (!bTangentYZero) + { + //TangentX and Z are invalid, use the triangle data, can cause a hard edge + TangentX = TriangleTangentX.GetSafeNormal(); + TangentZ = TriangleTangentZ.GetSafeNormal(); + } + else + { + //Everything is zero, use all triangle data, can cause a hard edge + TangentX = TriangleTangentX.GetSafeNormal(); + TangentY = TriangleTangentY.GetSafeNormal(); + TangentZ = TriangleTangentZ.GetSafeNormal(); + } + + bool bParaXY = FVector::Parallel(TangentX, TangentY); + bool bParaYZ = FVector::Parallel(TangentY, TangentZ); + bool bParaZX = FVector::Parallel(TangentZ, TangentX); + if (bParaXY || bParaYZ || bParaZX) + { + //In case XY are parallel, use the Z(normal) if valid and not parallel to both X and Y to find the missing component + if (bParaXY && !bParaZX) + { + TangentY = FVector::CrossProduct(TangentZ, TangentX).GetSafeNormal(); + } + else if (bParaXY && !bParaYZ) + { + TangentX = FVector::CrossProduct(TangentY, TangentZ).GetSafeNormal(); + } + else + { + //Degenerated value put something valid + TangentX = FVector(1.0f, 0.0f, 0.0f); + TangentY = FVector(0.0f, 1.0f, 0.0f); + TangentZ = FVector(0.0f, 0.0f, 1.0f); + } + } + else + { + //Ortho normalize the result + TangentY -= TangentX * (TangentX | TangentY); + TangentY.Normalize(); + + TangentX -= TangentZ * (TangentZ | TangentX); + TangentY -= TangentZ * (TangentZ | TangentY); + + TangentX.Normalize(); + TangentY.Normalize(); + + //If we still have some zero data (i.e. triangle data is degenerated) + if (TangentZ.IsNearlyZero() || TangentZ.ContainsNaN() + || TangentX.IsNearlyZero() || TangentX.ContainsNaN() + || TangentY.IsNearlyZero() || TangentY.ContainsNaN()) + { + //Since the triangle is degenerate this case can cause a hardedge, but will probably have no other impact since the triangle is degenerate (no visible surface) + TangentX = FVector(1.0f, 0.0f, 0.0f); + TangentY = FVector(0.0f, 1.0f, 0.0f); + TangentZ = FVector(0.0f, 0.0f, 1.0f); + } + } +} + +// +// //@TODO: The OutMessages has to be a struct that contains FText/FName, or make it Token and add that as error. Needs re-work. Temporary workaround for now. +// bool FMeshUtilities2::BuildSkeletalMesh_Legacy(FSkeletalMeshLODModel& LODModel +// , const FReferenceSkeleton& RefSkeleton +// , const TArray& Influences +// , const TArray& Wedges +// , const TArray& Faces +// , const TArray& Points +// , const TArray& PointToOriginalMap +// , const FOverlappingThresholds& OverlappingThresholds +// , bool bComputeNormals +// , bool bComputeTangents +// , bool bComputeWeightedNormals +// , TArray * OutWarningMessages +// , TArray * OutWarningNames) +// { +// bool bTooManyVerts = false; +// +// check(PointToOriginalMap.Num() == Points.Num()); +// +// // Calculate face tangent vectors. +// TArray FaceTangentX; +// TArray FaceTangentY; +// FaceTangentX.AddUninitialized(Faces.Num()); +// FaceTangentY.AddUninitialized(Faces.Num()); +// +// if (bComputeNormals || bComputeTangents) +// { +// for (int32 FaceIndex = 0; FaceIndex < Faces.Num(); FaceIndex++) +// { +// FVector P1 = Points[Wedges[Faces[FaceIndex].iWedge[0]].iVertex], +// P2 = Points[Wedges[Faces[FaceIndex].iWedge[1]].iVertex], +// P3 = Points[Wedges[Faces[FaceIndex].iWedge[2]].iVertex]; +// FVector TriangleNormal = FPlane(P3, P2, P1); +// if (!TriangleNormal.IsNearlyZero(FLT_MIN)) +// { +// FMatrix ParameterToLocal( +// FPlane(P2.X - P1.X, P2.Y - P1.Y, P2.Z - P1.Z, 0), +// FPlane(P3.X - P1.X, P3.Y - P1.Y, P3.Z - P1.Z, 0), +// FPlane(P1.X, P1.Y, P1.Z, 0), +// FPlane(0, 0, 0, 1) +// ); +// +// float U1 = Wedges[Faces[FaceIndex].iWedge[0]].UVs[0].X, +// U2 = Wedges[Faces[FaceIndex].iWedge[1]].UVs[0].X, +// U3 = Wedges[Faces[FaceIndex].iWedge[2]].UVs[0].X, +// V1 = Wedges[Faces[FaceIndex].iWedge[0]].UVs[0].Y, +// V2 = Wedges[Faces[FaceIndex].iWedge[1]].UVs[0].Y, +// V3 = Wedges[Faces[FaceIndex].iWedge[2]].UVs[0].Y; +// +// FMatrix ParameterToTexture( +// FPlane(U2 - U1, V2 - V1, 0, 0), +// FPlane(U3 - U1, V3 - V1, 0, 0), +// FPlane(U1, V1, 1, 0), +// FPlane(0, 0, 0, 1) +// ); +// +// FMatrix TextureToLocal = ParameterToTexture.Inverse() * ParameterToLocal; +// FVector TangentX = TextureToLocal.TransformVector(FVector(1, 0, 0)).GetSafeNormal(), +// TangentY = TextureToLocal.TransformVector(FVector(0, 1, 0)).GetSafeNormal(), +// TangentZ; +// +// TangentX = TangentX - TriangleNormal * (TangentX | TriangleNormal); +// TangentY = TangentY - TriangleNormal * (TangentY | TriangleNormal); +// +// FaceTangentX[FaceIndex] = TangentX.GetSafeNormal(); +// FaceTangentY[FaceIndex] = TangentY.GetSafeNormal(); +// } +// else +// { +// FaceTangentX[FaceIndex] = FVector::ZeroVector; +// FaceTangentY[FaceIndex] = FVector::ZeroVector; +// } +// } +// } +// +// TArray WedgeInfluenceIndices; +// +// // Find wedge influences. +// TMap VertexIndexToInfluenceIndexMap; +// +// for (uint32 LookIdx = 0; LookIdx < (uint32)Influences.Num(); LookIdx++) +// { +// // Order matters do not allow the map to overwrite an existing value. +// if (!VertexIndexToInfluenceIndexMap.Find(Influences[LookIdx].VertIndex)) +// { +// VertexIndexToInfluenceIndexMap.Add(Influences[LookIdx].VertIndex, LookIdx); +// } +// } +// +// for (int32 WedgeIndex = 0; WedgeIndex < Wedges.Num(); WedgeIndex++) +// { +// uint32* InfluenceIndex = VertexIndexToInfluenceIndexMap.Find(Wedges[WedgeIndex].iVertex); +// +// if (InfluenceIndex) +// { +// WedgeInfluenceIndices.Add(*InfluenceIndex); +// } +// else +// { +// // we have missing influence vert, we weight to root +// WedgeInfluenceIndices.Add(0); +// +// // add warning message +// if (OutWarningMessages) +// { +// OutWarningMessages->Add(FText::Format(FText::FromString("Missing influence on vert {0}. Weighting it to root."), FText::FromString(FString::FromInt(Wedges[WedgeIndex].iVertex)))); +// if (OutWarningNames) +// { +// OutWarningNames->Add(FFbxErrors::SkeletalMesh_VertMissingInfluences); +// } +// } +// } +// } +// +// check(Wedges.Num() == WedgeInfluenceIndices.Num()); +// +// // Calculate smooth wedge tangent vectors. +// +// if (IsInGameThread()) +// { +// // Only update status if in the game thread. When importing morph targets, this function can run in another thread +// GWarn->BeginSlowTask(NSLOCTEXT("UnrealEd", "ProcessingSkeletalTriangles", "Processing Mesh Triangles"), true); +// } +// +// +// // To accelerate generation of adjacency, we'll create a table that maps each vertex index +// // to its overlapping vertices, and a table that maps a vertex to the its influenced faces +// TMultiMap Vert2Duplicates; +// TMultiMap Vert2Faces; +// TArray VertIndexAndZ; +// { +// // Create a list of vertex Z/index pairs +// VertIndexAndZ.Empty(Points.Num()); +// for (int32 i = 0; i < Points.Num(); i++) +// { +// FSkeletalMeshVertIndexAndZ iandz; +// iandz.Index = i; +// iandz.Z = Points[i].Z; +// VertIndexAndZ.Add(iandz); +// } +// +// // Sorting function for vertex Z/index pairs +// struct FCompareFSkeletalMeshVertIndexAndZ +// { +// FORCEINLINE bool operator()(const FSkeletalMeshVertIndexAndZ& A, const FSkeletalMeshVertIndexAndZ& B) const +// { +// return A.Z < B.Z; +// } +// }; +// +// // Sort the vertices by z value +// VertIndexAndZ.Sort(FCompareFSkeletalMeshVertIndexAndZ()); +// +// // Search for duplicates, quickly! +// for (int32 i = 0; i < VertIndexAndZ.Num(); i++) +// { +// // only need to search forward, since we add pairs both ways +// for (int32 j = i + 1; j < VertIndexAndZ.Num(); j++) +// { +// if (FMath::Abs(VertIndexAndZ[j].Z - VertIndexAndZ[i].Z) > OverlappingThresholds.ThresholdPosition) +// { +// // our list is sorted, so there can't be any more dupes +// break; +// } +// +// // check to see if the points are really overlapping +// if (PointsEqual( +// Points[VertIndexAndZ[i].Index], +// Points[VertIndexAndZ[j].Index], OverlappingThresholds)) +// { +// Vert2Duplicates.Add(VertIndexAndZ[i].Index, VertIndexAndZ[j].Index); +// Vert2Duplicates.Add(VertIndexAndZ[j].Index, VertIndexAndZ[i].Index); +// } +// } +// } +// +// // we are done with this +// VertIndexAndZ.Reset(); +// +// // now create a map from vert indices to faces +// for (int32 FaceIndex = 0; FaceIndex < Faces.Num(); FaceIndex++) +// { +// const SkeletalMeshImportData::FMeshFace& Face = Faces[FaceIndex]; +// for (int32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) +// { +// Vert2Faces.AddUnique(Wedges[Face.iWedge[VertexIndex]].iVertex, FaceIndex); +// } +// } +// } +// +// TArray Chunks; +// TArray AdjacentFaces; +// TArray DupVerts; +// TArray DupFaces; +// +// // List of raw calculated vertices that will be merged later +// TArray RawVertices; +// RawVertices.Reserve(Points.Num()); +// +// int32 NTBErrorCount = 0; +// // Create a list of vertex Z/index pairs +// +// for (int32 FaceIndex = 0; FaceIndex < Faces.Num(); FaceIndex++) +// { +// // Only update the status progress bar if we are in the gamethread and every thousand faces. +// // Updating status is extremely slow +// if (FaceIndex % 5000 == 0 && IsInGameThread()) +// { +// // Only update status if in the game thread. When importing morph targets, this function can run in another thread +// GWarn->StatusUpdate(FaceIndex, Faces.Num(), NSLOCTEXT("UnrealEd", "ProcessingSkeletalTriangles", "Processing Mesh Triangles")); +// } +// +// const SkeletalMeshImportData::FMeshFace& Face = Faces[FaceIndex]; +// +// FVector VertexTangentX[3], +// VertexTangentY[3], +// VertexTangentZ[3]; +// +// if (bComputeNormals || bComputeTangents) +// { +// for (int32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) +// { +// VertexTangentX[VertexIndex] = FVector::ZeroVector; +// VertexTangentY[VertexIndex] = FVector::ZeroVector; +// VertexTangentZ[VertexIndex] = FVector::ZeroVector; +// } +// +// FVector TriangleNormal = FPlane( +// Points[Wedges[Face.iWedge[2]].iVertex], +// Points[Wedges[Face.iWedge[1]].iVertex], +// Points[Wedges[Face.iWedge[0]].iVertex] +// ); +// float Determinant = FVector::Triple(FaceTangentX[FaceIndex], FaceTangentY[FaceIndex], TriangleNormal); +// +// // Start building a list of faces adjacent to this triangle +// AdjacentFaces.Reset(); +// for (int32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) +// { +// int32 vert = Wedges[Face.iWedge[VertexIndex]].iVertex; +// DupVerts.Reset(); +// Vert2Duplicates.MultiFind(vert, DupVerts); +// DupVerts.Add(vert); // I am a "dupe" of myself +// for (int32 k = 0; k < DupVerts.Num(); k++) +// { +// DupFaces.Reset(); +// Vert2Faces.MultiFind(DupVerts[k], DupFaces); +// for (int32 l = 0; l < DupFaces.Num(); l++) +// { +// AdjacentFaces.AddUnique(DupFaces[l]); +// } +// } +// } +// +// FVector FacePoint[3] = { Points[Wedges[Face.iWedge[0]].iVertex], Points[Wedges[Face.iWedge[1]].iVertex], Points[Wedges[Face.iWedge[2]].iVertex] }; +// // Process adjacent faces +// for (int32 AdjacentFaceIndex = 0; AdjacentFaceIndex < AdjacentFaces.Num(); AdjacentFaceIndex++) +// { +// int32 OtherFaceIndex = AdjacentFaces[AdjacentFaceIndex]; +// const SkeletalMeshImportData::FMeshFace& OtherFace = Faces[OtherFaceIndex]; +// +// FVector OtherFacePoint[3] = { Points[Wedges[OtherFace.iWedge[0]].iVertex], Points[Wedges[OtherFace.iWedge[1]].iVertex], Points[Wedges[OtherFace.iWedge[2]].iVertex] }; +// float OtherFaceArea = !bComputeWeightedNormals ? 1.0f : TriangleUtilities::ComputeTriangleArea(OtherFacePoint[0], OtherFacePoint[1], OtherFacePoint[2]); +// FVector OtherTriangleNormal = FPlane( +// OtherFacePoint[2], +// OtherFacePoint[1], +// OtherFacePoint[0] +// ); +// float OtherFaceDeterminant = FVector::Triple(FaceTangentX[OtherFaceIndex], FaceTangentY[OtherFaceIndex], OtherTriangleNormal); +// +// for (int32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) +// { +// for (int32 OtherVertexIndex = 0; OtherVertexIndex < 3; OtherVertexIndex++) +// { +// if (PointsEqual( +// OtherFacePoint[OtherVertexIndex], +// FacePoint[VertexIndex], +// OverlappingThresholds +// )) +// { +// //Compute the angle +// float OtherFaceAngle = !bComputeWeightedNormals ? 1.0f : TriangleUtilities::ComputeTriangleCornerAngle(OtherFacePoint[OtherVertexIndex], OtherFacePoint[(OtherVertexIndex + 1) % 3], OtherFacePoint[(OtherVertexIndex + 2) % 3]); +// +// float CornerWeight = (OtherFaceArea * OtherFaceAngle); +// +// if (Determinant * OtherFaceDeterminant > 0.0f && SkeletalMeshTools::SkeletalMesh_UVsEqual(Wedges[OtherFace.iWedge[OtherVertexIndex]], Wedges[Face.iWedge[VertexIndex]], OverlappingThresholds)) +// { +// VertexTangentX[VertexIndex] += CornerWeight *FaceTangentX[OtherFaceIndex]; +// VertexTangentY[VertexIndex] += CornerWeight *FaceTangentY[OtherFaceIndex]; +// } +// +// // Only contribute 'normal' if the vertices are truly one and the same to obey hard "smoothing" edges baked into +// // the mesh by vertex duplication +// if (Wedges[OtherFace.iWedge[OtherVertexIndex]].iVertex == Wedges[Face.iWedge[VertexIndex]].iVertex) +// { +// VertexTangentZ[VertexIndex] += CornerWeight *OtherTriangleNormal; +// } +// } +// } +// } +// } +// } +// +// for (int32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) +// { +// FSoftSkinBuildVertex Vertex; +// +// Vertex.Position = Points[Wedges[Face.iWedge[VertexIndex]].iVertex]; +// +// FVector TangentX, TangentY, TangentZ; +// +// if (bComputeNormals || bComputeTangents) +// { +// TangentX = VertexTangentX[VertexIndex].GetSafeNormal(); +// TangentY = VertexTangentY[VertexIndex].GetSafeNormal(); +// +// if (bComputeNormals) +// { +// TangentZ = VertexTangentZ[VertexIndex].GetSafeNormal(); +// } +// else +// { +// TangentZ = Face.TangentZ[VertexIndex]; +// } +// +// TangentY -= TangentX * (TangentX | TangentY); +// TangentY.Normalize(); +// +// TangentX -= TangentZ * (TangentZ | TangentX); +// TangentY -= TangentZ * (TangentZ | TangentY); +// +// TangentX.Normalize(); +// TangentY.Normalize(); +// } +// else +// { +// TangentX = Face.TangentX[VertexIndex]; +// TangentY = Face.TangentY[VertexIndex]; +// TangentZ = Face.TangentZ[VertexIndex]; +// +// // Normalize overridden tangents. Its possible for them to import un-normalized. +// TangentX.Normalize(); +// TangentY.Normalize(); +// TangentZ.Normalize(); +// } +// +// //FAIL safe, avoid zero tangents +// bool bTangentXZero = TangentX.IsNearlyZero() || TangentX.ContainsNaN(); +// bool bTangentYZero = TangentY.IsNearlyZero() || TangentY.ContainsNaN(); +// bool bTangentZZero = TangentZ.IsNearlyZero() || TangentZ.ContainsNaN(); +// if (bTangentXZero || bTangentYZero || bTangentZZero) +// { +// NTBErrorCount++; +// FVector TriangleTangentZ = FPlane( +// Points[Wedges[Face.iWedge[2]].iVertex], +// Points[Wedges[Face.iWedge[1]].iVertex], +// Points[Wedges[Face.iWedge[0]].iVertex] +// ); +// FVector TriangleTangentX = FaceTangentX[FaceIndex]; +// FVector TriangleTangentY = FaceTangentY[FaceIndex]; +// TangentFailSafe(TriangleTangentX, TriangleTangentY, TriangleTangentZ, TangentX, TangentY, TangentZ); +// } +// +// Vertex.TangentX = TangentX; +// Vertex.TangentY = TangentY; +// Vertex.TangentZ = TangentZ; +// +// FMemory::Memcpy(Vertex.UVs, Wedges[Face.iWedge[VertexIndex]].UVs, sizeof(FVector2D)*MAX_TEXCOORDS); +// Vertex.Color = Wedges[Face.iWedge[VertexIndex]].Color; +// +// { +// // Count the influences. +// +// int32 InfIdx = WedgeInfluenceIndices[Face.iWedge[VertexIndex]]; +// int32 LookIdx = InfIdx; +// +// uint32 InfluenceCount = 0; +// while (Influences.IsValidIndex(LookIdx) && (Influences[LookIdx].VertIndex == Wedges[Face.iWedge[VertexIndex]].iVertex)) +// { +// InfluenceCount++; +// LookIdx++; +// } +// InfluenceCount = FMath::Min(InfluenceCount, MAX_TOTAL_INFLUENCES); +// +// // Setup the vertex influences. +// +// Vertex.InfluenceBones[0] = 0; +// Vertex.InfluenceWeights[0] = 255; +// for (uint32 i = 1; i < MAX_TOTAL_INFLUENCES; i++) +// { +// Vertex.InfluenceBones[i] = 0; +// Vertex.InfluenceWeights[i] = 0; +// } +// +// uint32 TotalInfluenceWeight = 0; +// for (uint32 i = 0; i < InfluenceCount; i++) +// { +// FBoneIndexType BoneIndex = (FBoneIndexType)Influences[InfIdx + i].BoneIndex; +// if (BoneIndex >= RefSkeleton.GetRawBoneNum()) +// continue; +// +// Vertex.InfluenceBones[i] = BoneIndex; +// Vertex.InfluenceWeights[i] = (uint8)(Influences[InfIdx + i].Weight * 255.0f); +// TotalInfluenceWeight += Vertex.InfluenceWeights[i]; +// } +// Vertex.InfluenceWeights[0] += 255 - TotalInfluenceWeight; +// } +// +// // Add the vertex as well as its original index in the points array +// Vertex.PointWedgeIdx = Wedges[Face.iWedge[VertexIndex]].iVertex; +// +// int32 RawIndex = RawVertices.Add(Vertex); +// +// // Add an efficient way to find dupes of this vertex later for fast combining of vertices +// FSkeletalMeshVertIndexAndZ IAndZ; +// IAndZ.Index = RawIndex; +// IAndZ.Z = Vertex.Position.Z; +// +// VertIndexAndZ.Add(IAndZ); +// } +// } +// +// if (NTBErrorCount > 0 && OutWarningMessages) +// { +// OutWarningMessages->Add(FText::FromString(TEXT("SkeletalMesh compute tangents [built in]: Build result data contain 0 or NAN tangent value. Bad tangent value will impact shading."))); +// if (OutWarningNames) +// { +// OutWarningNames->Add(FFbxErrors::Generic_Mesh_TangentsComputeError); +// } +// } +// +// // Generate chunks and their vertices and indices +// SkeletalMeshTools::BuildSkeletalMeshChunks(Faces, RawVertices, VertIndexAndZ, OverlappingThresholds, Chunks, bTooManyVerts); +// +// //Get alternate skinning weights map to retrieve easily the data +// TMap> AlternateBoneIDs; +// AlternateBoneIDs.Reserve(Points.Num()); +// for (auto Kvp : LODModel.SkinWeightProfiles) +// { +// FImportedSkinWeightProfileData& ImportedProfileData = Kvp.Value; +// if (ImportedProfileData.SourceModelInfluences.Num() > 0) +// { +// for (int32 InfluenceIndex = 0; InfluenceIndex < ImportedProfileData.SourceModelInfluences.Num(); ++InfluenceIndex) +// { +// const SkeletalMeshImportData::FVertInfluence& VertInfluence = ImportedProfileData.SourceModelInfluences[InfluenceIndex]; +// if (VertInfluence.Weight > 0.0f) +// { +// TArray& BoneMap = AlternateBoneIDs.FindOrAdd(VertInfluence.VertIndex); +// BoneMap.AddUnique(VertInfluence.BoneIndex); +// } +// } +// } +// } +// +// // Chunk vertices to satisfy the requested limit. +// const uint32 MaxGPUSkinBones = FGPUBaseSkinVertexFactory::GetMaxGPUSkinBones(); +// check(MaxGPUSkinBones <= FGPUBaseSkinVertexFactory::GHardwareMaxGPUSkinBones); +// SkeletalMeshTools::ChunkSkinnedVertices(Chunks, AlternateBoneIDs, MaxGPUSkinBones); +// +// // Build the skeletal model from chunks. +// BuildSkeletalModelFromChunks(LODModel, RefSkeleton, Chunks, PointToOriginalMap); +// +// if (IsInGameThread()) +// { +// // Only update status if in the game thread. When importing morph targets, this function can run in another thread +// GWarn->EndSlowTask(); +// } +// +// // Only show these warnings if in the game thread. When importing morph targets, this function can run in another thread and these warnings dont prevent the mesh from importing +// if (IsInGameThread()) +// { +// bool bHasBadSections = false; +// for (int32 SectionIndex = 0; SectionIndex < LODModel.Sections.Num(); SectionIndex++) +// { +// FSkelMeshSection& Section = LODModel.Sections[SectionIndex]; +// bHasBadSections |= (Section.NumTriangles == 0); +// +// // Log info about the section. +// UE_LOG(LogSkeletalMesh, Log, TEXT("Section %u: Material=%u, %u triangles"), +// SectionIndex, +// Section.MaterialIndex, +// Section.NumTriangles +// ); +// } +// if (bHasBadSections) +// { +// FText BadSectionMessage(NSLOCTEXT("UnrealEd", "Error_SkeletalMeshHasBadSections", "Input mesh has a section with no triangles. This mesh may not render properly.")); +// if (OutWarningMessages) +// { +// OutWarningMessages->Add(BadSectionMessage); +// if (OutWarningNames) +// { +// OutWarningNames->Add(FFbxErrors::SkeletalMesh_SectionWithNoTriangle); +// } +// } +// else +// { +// FMessageDialog::Open(EAppMsgType::Ok, BadSectionMessage); +// } +// } +// +// if (bTooManyVerts) +// { +// FText TooManyVertsMessage(NSLOCTEXT("UnrealEd", "Error_SkeletalMeshTooManyVertices", "Input mesh has too many vertices. The generated mesh will be corrupt! Consider adding extra materials to split up the source mesh into smaller chunks.")); +// +// if (OutWarningMessages) +// { +// OutWarningMessages->Add(TooManyVertsMessage); +// if (OutWarningNames) +// { +// OutWarningNames->Add(FFbxErrors::SkeletalMesh_TooManyVertices); +// } +// } +// else +// { +// FMessageDialog::Open(EAppMsgType::Ok, TooManyVertsMessage); +// } +// } +// } +// +// return true; +// } + +static bool NonOpaqueMaterialPredicate(UStaticMeshComponent* InMesh) +{ + TArray OutMaterials; + InMesh->GetUsedMaterials(OutMaterials); + for (auto Material : OutMaterials) + { + if (Material == nullptr || Material->GetBlendMode() != BLEND_Opaque) + { + return true; + } + } + + return false; +} + +void FMeshUtilities2::RecomputeTangentsAndNormalsForRawMesh(bool bRecomputeTangents, bool bRecomputeNormals, const FMeshBuildSettings& InBuildSettings, FRawMesh& OutRawMesh) const +{ + // Compute any missing tangents. + if (bRecomputeNormals || bRecomputeTangents) + { + float ComparisonThreshold = InBuildSettings.bRemoveDegenerates ? THRESH_POINTS_ARE_SAME : 0.0f; + FOverlappingCorners OverlappingCorners; + FindOverlappingCorners(OverlappingCorners, OutRawMesh, ComparisonThreshold); + + RecomputeTangentsAndNormalsForRawMesh(bRecomputeTangents, bRecomputeNormals, InBuildSettings, OverlappingCorners, OutRawMesh); + } +} + +void FMeshUtilities2::RecomputeTangentsAndNormalsForRawMesh(bool bRecomputeTangents, bool bRecomputeNormals, const FMeshBuildSettings& InBuildSettings, const FOverlappingCorners& InOverlappingCorners, FRawMesh& OutRawMesh) const +{ + const int32 NumWedges = OutRawMesh.WedgeIndices.Num(); + + // Dump normals and tangents if we are recomputing them. + if (bRecomputeTangents) + { + OutRawMesh.WedgeTangentX.Empty(NumWedges); + OutRawMesh.WedgeTangentX.AddZeroed(NumWedges); + OutRawMesh.WedgeTangentY.Empty(NumWedges); + OutRawMesh.WedgeTangentY.AddZeroed(NumWedges); + } + + if (bRecomputeNormals) + { + OutRawMesh.WedgeTangentZ.Empty(NumWedges); + OutRawMesh.WedgeTangentZ.AddZeroed(NumWedges); + } + + // Compute any missing tangents. + if (bRecomputeNormals || bRecomputeTangents) + { + // Static meshes always blend normals of overlapping corners. + uint32 TangentOptions = ETangentOptions2::BlendOverlappingNormals; + if (InBuildSettings.bRemoveDegenerates) + { + // If removing degenerate triangles, ignore them when computing tangents. + TangentOptions |= ETangentOptions2::IgnoreDegenerateTriangles; + } + + if (InBuildSettings.bUseMikkTSpace) + { + ComputeTangents_MikkTSpace(OutRawMesh, InOverlappingCorners, TangentOptions); + } + else + { + ComputeTangents(OutRawMesh, InOverlappingCorners, TangentOptions); + } + } + + // At this point the mesh will have valid tangents. + check(OutRawMesh.WedgeTangentX.Num() == NumWedges); + check(OutRawMesh.WedgeTangentY.Num() == NumWedges); + check(OutRawMesh.WedgeTangentZ.Num() == NumWedges); +} + +void FMeshUtilities2::ExtractMeshDataForGeometryCache(FRawMesh& RawMesh, const FMeshBuildSettings& BuildSettings, TArray& OutVertices, TArray>& OutPerSectionIndices, int32 ImportVersion) +{ + int32 NumWedges = RawMesh.WedgeIndices.Num(); + + // Figure out if we should recompute normals and tangents. By default generated LODs should not recompute normals + bool bRecomputeNormals = (BuildSettings.bRecomputeNormals) || RawMesh.WedgeTangentZ.Num() == 0; + bool bRecomputeTangents = (BuildSettings.bRecomputeTangents) || RawMesh.WedgeTangentX.Num() == 0 || RawMesh.WedgeTangentY.Num() == 0; + + // Dump normals and tangents if we are recomputing them. + if (bRecomputeTangents) + { + RawMesh.WedgeTangentX.Empty(NumWedges); + RawMesh.WedgeTangentX.AddZeroed(NumWedges); + RawMesh.WedgeTangentY.Empty(NumWedges); + RawMesh.WedgeTangentY.AddZeroed(NumWedges); + } + + if (bRecomputeNormals) + { + RawMesh.WedgeTangentZ.Empty(NumWedges); + RawMesh.WedgeTangentZ.AddZeroed(NumWedges); + } + + // Compute any missing tangents. + FOverlappingCorners OverlappingCorners; + if (bRecomputeNormals || bRecomputeTangents) + { + float ComparisonThreshold = GetComparisonThreshold(BuildSettings); + FindOverlappingCorners(OverlappingCorners, RawMesh, ComparisonThreshold); + + // Static meshes always blend normals of overlapping corners. + uint32 TangentOptions = ETangentOptions2::BlendOverlappingNormals; + if (BuildSettings.bRemoveDegenerates) + { + // If removing degenerate triangles, ignore them when computing tangents. + TangentOptions |= ETangentOptions2::IgnoreDegenerateTriangles; + } + if (BuildSettings.bUseMikkTSpace) + { + ComputeTangents_MikkTSpace(RawMesh, OverlappingCorners, TangentOptions); + } + else + { + ComputeTangents(RawMesh, OverlappingCorners, TangentOptions); + } + } + + // At this point the mesh will have valid tangents. + check(RawMesh.WedgeTangentX.Num() == NumWedges); + check(RawMesh.WedgeTangentY.Num() == NumWedges); + check(RawMesh.WedgeTangentZ.Num() == NumWedges); + + TArray OutWedgeMap; + + int32 MaxMaterialIndex = 1; + for (int32 FaceIndex = 0; FaceIndex < RawMesh.FaceMaterialIndices.Num(); FaceIndex++) + { + MaxMaterialIndex = FMath::Max(RawMesh.FaceMaterialIndices[FaceIndex], MaxMaterialIndex); + } + + TMap MaterialToSectionMapping; + for (int32 i = 0; i <= MaxMaterialIndex; ++i) + { + OutPerSectionIndices.Push(TArray()); + MaterialToSectionMapping.Add(i, i); + } + + BuildStaticMeshVertexAndIndexBuffers(OutVertices, OutPerSectionIndices, OutWedgeMap, RawMesh, OverlappingCorners, MaterialToSectionMapping, KINDA_SMALL_NUMBER, BuildSettings.BuildScale3D, ImportVersion); + + if (RawMesh.WedgeIndices.Num() < 100000 * 3) + { + CacheOptimizeVertexAndIndexBuffer(OutVertices, OutPerSectionIndices, OutWedgeMap); + check(OutWedgeMap.Num() == RawMesh.WedgeIndices.Num()); + } +} + +/*------------------------------------------------------------------------------ +Mesh merging +------------------------------------------------------------------------------*/ +// +// void FMeshUtilities2::CalculateTextureCoordinateBoundsForSkeletalMesh(const FSkeletalMeshLODModel& LODModel, TArray& OutBounds) const +// { +// TArray Vertices; +// LODModel.GetVertices(Vertices); +// +// const uint32 SectionCount = (uint32)LODModel.NumNonClothingSections(); +// +// check(OutBounds.Num() != 0); +// +// for (uint32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex) +// { +// const FSkelMeshSection& Section = LODModel.Sections[SectionIndex]; +// const uint32 FirstIndex = Section.BaseIndex; +// const uint32 LastIndex = FirstIndex + Section.NumTriangles * 3; +// const int32 MaterialIndex = Section.MaterialIndex; +// +// if (OutBounds.Num() <= MaterialIndex) +// { +// OutBounds.SetNumZeroed(MaterialIndex + 1); +// } +// +// for (uint32 Index = FirstIndex; Index < LastIndex; ++Index) +// { +// uint32 VertexIndex = LODModel.IndexBuffer[Index]; +// FSoftSkinVertex& Vertex = Vertices[VertexIndex]; +// +// FVector2D TexCoord = Vertex.UVs[0]; +// OutBounds[MaterialIndex] += TexCoord; +// } +// } +// } + +bool FMeshUtilities2::RemoveBonesFromMesh(USkeletalMesh* SkeletalMesh, int32 LODIndex, const TArray* BoneNamesToRemove) const +{ + return false; + // IMeshBoneReductionModule& MeshBoneReductionModule = FModuleManager::Get().LoadModuleChecked("MeshBoneReduction"); + // IMeshBoneReduction * MeshBoneReductionInterface = MeshBoneReductionModule.GetMeshBoneReductionInterface(); + // + // return MeshBoneReductionInterface->ReduceBoneCounts(SkeletalMesh, LODIndex, BoneNamesToRemove); +} + +// +// class FMeshSimplifcationSettingsCustomization : public IDetailCustomization +// { +// public: +// static TSharedRef MakeInstance() +// { +// return MakeShareable( new FMeshSimplifcationSettingsCustomization ); +// } +// +// virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) override +// { +// MeshReductionModuleProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMeshSimplificationSettings, MeshReductionModuleName)); +// +// IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(TEXT("General")); +// +// IDetailPropertyRow& PropertyRow = Category.AddProperty(MeshReductionModuleProperty); +// +// FDetailWidgetRow& WidgetRow = PropertyRow.CustomWidget(); +// WidgetRow.NameContent() +// [ +// MeshReductionModuleProperty->CreatePropertyNameWidget() +// ]; +// +// WidgetRow.ValueContent() +// .MaxDesiredWidth(0) +// [ +// SNew(SComboButton) +// .OnGetMenuContent(this, &FMeshSimplifcationSettingsCustomization::GenerateMeshSimplifierMenu) +// .ContentPadding(FMargin(2.0f, 2.0f)) +// .ButtonContent() +// [ +// SNew(STextBlock) +// .Font(IDetailLayoutBuilder::GetDetailFont()) +// .Text(this, &FMeshSimplifcationSettingsCustomization::GetCurrentMeshSimplifierName) +// ] +// ]; +// } +// +// private: +// FText GetCurrentMeshSimplifierName() const +// { +// if(MeshReductionModuleProperty->IsValidHandle()) +// { +// FText Name; +// MeshReductionModuleProperty->GetValueAsDisplayText(Name); +// +// return Name; +// } +// else +// { +// return LOCTEXT("AutomaticMeshReductionPlugin", "Automatic"); +// } +// } +// +// TSharedRef GenerateMeshSimplifierMenu() const +// { +// FMenuBuilder MenuBuilder(true, nullptr); +// +// TArray ModuleNames; +// FModuleManager::Get().FindModules(TEXT("*MeshReduction"), ModuleNames); +// +// if(ModuleNames.Num() > 0) +// { +// for(FName ModuleName : ModuleNames) +// { +// IMeshReductionModule& Module = FModuleManager::LoadModuleChecked(ModuleName); +// +// IMeshReduction* StaticMeshReductionInterface = Module.GetStaticMeshReductionInterface(); +// // Only include options that support static mesh reduction. +// if (StaticMeshReductionInterface) +// { +// FUIAction UIAction; +// UIAction.ExecuteAction.BindSP(const_cast(this), &FMeshSimplifcationSettingsCustomization::OnMeshSimplificationModuleChosen, ModuleName); +// UIAction.GetActionCheckState.BindSP(const_cast(this), &FMeshSimplifcationSettingsCustomization::IsMeshSimplificationModuleChosen, ModuleName); +// +// MenuBuilder.AddMenuEntry(FText::FromName(ModuleName), FText::GetEmpty(), FSlateIcon(), UIAction, NAME_None, EUserInterfaceActionType::RadioButton); +// } +// } +// +// MenuBuilder.AddMenuSeparator(); +// } +// +// +// FUIAction OpenMarketplaceAction; +// OpenMarketplaceAction.ExecuteAction.BindSP(const_cast(this), &FMeshSimplifcationSettingsCustomization::OnFindReductionPluginsClicked); +// FSlateIcon Icon = FSlateIcon(FEditorStyle::Get().GetStyleSetName(), "LevelEditor.OpenMarketplace.Menu"); +// MenuBuilder.AddMenuEntry( LOCTEXT("FindMoreReductionPluginsLink", "Search the Marketplace"), LOCTEXT("FindMoreReductionPluginsLink_Tooltip", "Opens the Marketplace to find more mesh reduction plugins"), Icon, OpenMarketplaceAction); +// return MenuBuilder.MakeWidget(); +// } +// +// void OnMeshSimplificationModuleChosen(FName ModuleName) +// { +// if(MeshReductionModuleProperty->IsValidHandle()) +// { +// MeshReductionModuleProperty->SetValue(ModuleName); +// } +// } +// +// ECheckBoxState IsMeshSimplificationModuleChosen(FName ModuleName) +// { +// if(MeshReductionModuleProperty->IsValidHandle()) +// { +// FName CurrentModuleName; +// MeshReductionModuleProperty->GetValue(CurrentModuleName); +// return CurrentModuleName == ModuleName ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; +// } +// +// return ECheckBoxState::Unchecked; +// } +// +// void OnFindReductionPluginsClicked() +// { +// FString URL; +// FUnrealEdMisc::Get().GetURL(TEXT("MeshSimplificationPluginsURL"), URL); +// +// FUnrealEdMisc::Get().OpenMarketplace(URL); +// } +// private: +// TSharedPtr MeshReductionModuleProperty; +// }; +// +// class FSkeletalMeshSimplificationSettingsCustomization : public IDetailCustomization +// { +// public: +// static TSharedRef MakeInstance() +// { +// return MakeShareable(new FSkeletalMeshSimplificationSettingsCustomization); +// } +// +// virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override +// { +// SkeletalMeshReductionModuleProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(USkeletalMeshSimplificationSettings, SkeletalMeshReductionModuleName)); +// +// IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(TEXT("General")); +// +// IDetailPropertyRow& PropertyRow = Category.AddProperty(SkeletalMeshReductionModuleProperty); +// +// FDetailWidgetRow& WidgetRow = PropertyRow.CustomWidget(); +// WidgetRow.NameContent() +// [ +// SkeletalMeshReductionModuleProperty->CreatePropertyNameWidget() +// ]; +// +// WidgetRow.ValueContent() +// .MaxDesiredWidth(0) +// [ +// SNew(SComboButton) +// .OnGetMenuContent(this, &FSkeletalMeshSimplificationSettingsCustomization::GenerateSkeletalMeshSimplifierMenu) +// .ContentPadding(FMargin(2.0f, 2.0f)) +// .ButtonContent() +// [ +// SNew(STextBlock) +// .Font(IDetailLayoutBuilder::GetDetailFont()) +// .Text(this, &FSkeletalMeshSimplificationSettingsCustomization::GetCurrentSkeletalMeshSimplifierName) +// ] +// ]; +// } +// +// private: +// FText GetCurrentSkeletalMeshSimplifierName() const +// { +// if (SkeletalMeshReductionModuleProperty->IsValidHandle()) +// { +// FText Name; +// SkeletalMeshReductionModuleProperty->GetValueAsDisplayText(Name); +// +// return Name; +// } +// else +// { +// return LOCTEXT("AutomaticSkeletalMeshReductionPlugin", "Automatic"); +// } +// } +// +// TSharedRef GenerateSkeletalMeshSimplifierMenu() const +// { +// FMenuBuilder MenuBuilder(true, nullptr); +// +// TArray ModuleNames; +// FModuleManager::Get().FindModules(TEXT("*MeshReduction"), ModuleNames); +// +// if (ModuleNames.Num() > 0) +// { +// for (FName ModuleName : ModuleNames) +// { +// IMeshReductionModule& Module = FModuleManager::LoadModuleChecked(ModuleName); +// +// IMeshReduction* SkeletalMeshReductionInterface = Module.GetSkeletalMeshReductionInterface(); +// // Only include options that support skeletal simplification. +// if (SkeletalMeshReductionInterface) +// { +// FUIAction UIAction; +// UIAction.ExecuteAction.BindSP(const_cast(this), &FSkeletalMeshSimplificationSettingsCustomization::OnSkeletalMeshSimplificationModuleChosen, ModuleName); +// UIAction.GetActionCheckState.BindSP(const_cast(this), &FSkeletalMeshSimplificationSettingsCustomization::IsSkeletalMeshSimplificationModuleChosen, ModuleName); +// +// MenuBuilder.AddMenuEntry(FText::FromName(ModuleName), FText::GetEmpty(), FSlateIcon(), UIAction, NAME_None, EUserInterfaceActionType::RadioButton); +// } +// } +// +// MenuBuilder.AddMenuSeparator(); +// } +// +// +// FUIAction OpenMarketplaceAction; +// OpenMarketplaceAction.ExecuteAction.BindSP(const_cast(this), &FSkeletalMeshSimplificationSettingsCustomization::OnFindReductionPluginsClicked); +// FSlateIcon Icon = FSlateIcon(FEditorStyle::Get().GetStyleSetName(), "LevelEditor.OpenMarketplace.Menu"); +// MenuBuilder.AddMenuEntry(LOCTEXT("FindMoreReductionPluginsLink", "Search the Marketplace"), LOCTEXT("FindMoreReductionPluginsLink_Tooltip", "Opens the Marketplace to find more mesh reduction plugins"), Icon, OpenMarketplaceAction); +// return MenuBuilder.MakeWidget(); +// } +// +// void OnSkeletalMeshSimplificationModuleChosen(FName ModuleName) +// { +// if (SkeletalMeshReductionModuleProperty->IsValidHandle()) +// { +// SkeletalMeshReductionModuleProperty->SetValue(ModuleName); +// } +// } +// +// ECheckBoxState IsSkeletalMeshSimplificationModuleChosen(FName ModuleName) +// { +// if (SkeletalMeshReductionModuleProperty->IsValidHandle()) +// { +// FName CurrentModuleName; +// SkeletalMeshReductionModuleProperty->GetValue(CurrentModuleName); +// return CurrentModuleName == ModuleName ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; +// } +// +// return ECheckBoxState::Unchecked; +// } +// +// void OnFindReductionPluginsClicked() +// { +// FString URL; +// FUnrealEdMisc::Get().GetURL(TEXT("MeshSimplificationPluginsURL"), URL); +// +// FUnrealEdMisc::Get().OpenMarketplace(URL); +// } +// private: +// TSharedPtr SkeletalMeshReductionModuleProperty; +// }; +// +// class FProxyLODMeshSimplificationSettingsCustomization : public IDetailCustomization +// { +// public: +// static TSharedRef MakeInstance() +// { +// return MakeShareable(new FProxyLODMeshSimplificationSettingsCustomization); +// } +// +// virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override +// { +// ProxyLODMeshReductionModuleProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UProxyLODMeshSimplificationSettings, ProxyLODMeshReductionModuleName)); +// +// IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(TEXT("General")); +// +// IDetailPropertyRow& PropertyRow = Category.AddProperty(ProxyLODMeshReductionModuleProperty); +// +// FDetailWidgetRow& WidgetRow = PropertyRow.CustomWidget(); +// WidgetRow.NameContent() +// [ +// ProxyLODMeshReductionModuleProperty->CreatePropertyNameWidget() +// ]; +// +// WidgetRow.ValueContent() +// .MaxDesiredWidth(0) +// [ +// SNew(SComboButton) +// .OnGetMenuContent(this, &FProxyLODMeshSimplificationSettingsCustomization::GenerateProxyLODMeshSimplifierMenu) +// .ContentPadding(FMargin(2.0f, 2.0f)) +// .ButtonContent() +// [ +// SNew(STextBlock) +// .Font(IDetailLayoutBuilder::GetDetailFont()) +// .Text(this, &FProxyLODMeshSimplificationSettingsCustomization::GetCurrentProxyLODMeshSimplifierName) +// ] +// ]; +// } +// +// private: +// FText GetCurrentProxyLODMeshSimplifierName() const +// { +// if (ProxyLODMeshReductionModuleProperty->IsValidHandle()) +// { +// FText Name; +// ProxyLODMeshReductionModuleProperty->GetValueAsDisplayText(Name); +// +// return Name; +// } +// else +// { +// return LOCTEXT("AutomaticProxyLODMeshReductionPlugin", "Automatic"); +// } +// } +// +// TSharedRef GenerateProxyLODMeshSimplifierMenu() const +// { +// FMenuBuilder MenuBuilder(true, nullptr); +// +// TArray ModuleNames; +// FModuleManager::Get().FindModules(TEXT("*MeshReduction"), ModuleNames); +// +// if (ModuleNames.Num() > 0) +// { +// for (FName ModuleName : ModuleNames) +// { +// IMeshReductionModule& Module = FModuleManager::LoadModuleChecked(ModuleName); +// +// IMeshMerging* MeshMergingInterface = Module.GetMeshMergingInterface(); +// // Only include options that support mesh mergine. +// if (MeshMergingInterface) +// { +// FUIAction UIAction; +// UIAction.ExecuteAction.BindSP(const_cast(this), &FProxyLODMeshSimplificationSettingsCustomization::OnProxyLODMeshSimplificationModuleChosen, ModuleName); +// UIAction.GetActionCheckState.BindSP(const_cast(this), &FProxyLODMeshSimplificationSettingsCustomization::IsProxyLODMeshSimplificationModuleChosen, ModuleName); +// +// MenuBuilder.AddMenuEntry(FText::FromName(ModuleName), FText::GetEmpty(), FSlateIcon(), UIAction, NAME_None, EUserInterfaceActionType::RadioButton); +// } +// } +// +// MenuBuilder.AddMenuSeparator(); +// } +// +// +// FUIAction OpenMarketplaceAction; +// OpenMarketplaceAction.ExecuteAction.BindSP(const_cast(this), &FProxyLODMeshSimplificationSettingsCustomization::OnFindReductionPluginsClicked); +// FSlateIcon Icon = FSlateIcon(FEditorStyle::Get().GetStyleSetName(), "LevelEditor.OpenMarketplace.Menu"); +// MenuBuilder.AddMenuEntry(LOCTEXT("FindMoreReductionPluginsLink", "Search the Marketplace"), LOCTEXT("FindMoreReductionPluginsLink_Tooltip", "Opens the Marketplace to find more mesh reduction plugins"), Icon, OpenMarketplaceAction); +// return MenuBuilder.MakeWidget(); +// } +// +// void OnProxyLODMeshSimplificationModuleChosen(FName ModuleName) +// { +// if (ProxyLODMeshReductionModuleProperty->IsValidHandle()) +// { +// ProxyLODMeshReductionModuleProperty->SetValue(ModuleName); +// } +// } +// +// ECheckBoxState IsProxyLODMeshSimplificationModuleChosen(FName ModuleName) +// { +// if (ProxyLODMeshReductionModuleProperty->IsValidHandle()) +// { +// FName CurrentModuleName; +// ProxyLODMeshReductionModuleProperty->GetValue(CurrentModuleName); +// return CurrentModuleName == ModuleName ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; +// } +// +// return ECheckBoxState::Unchecked; +// } +// +// void OnFindReductionPluginsClicked() +// { +// FString URL; +// FUnrealEdMisc::Get().GetURL(TEXT("MeshSimplificationPluginsURL"), URL); +// +// FUnrealEdMisc::Get().OpenMarketplace(URL); +// } +// private: +// TSharedPtr ProxyLODMeshReductionModuleProperty; +// }; +// +// /*------------------------------------------------------------------------------ +// Module initialization / teardown. +// ------------------------------------------------------------------------------*/ +// +void FMeshUtilities2::StartupModule() +{ + GDistanceFieldAsyncQueue2 = new FDistanceFieldAsyncQueue2; + GCardRepresentationAsyncQueue2 = new FCardRepresentationAsyncQueue2; + TickObject = new FProcessAsyncTasksTickObject; +} + +void FMeshUtilities2::ShutdownModule() +{ + if (GDistanceFieldAsyncQueue2) + { + GDistanceFieldAsyncQueue2->Shutdown(); + delete GDistanceFieldAsyncQueue2; + GDistanceFieldAsyncQueue2 = nullptr; + } + + if (GCardRepresentationAsyncQueue2) + { + GCardRepresentationAsyncQueue2->Shutdown(); + delete GCardRepresentationAsyncQueue2; + GCardRepresentationAsyncQueue2 = nullptr; + } + delete TickObject; +} + + +// bool FMeshUtilities2::GenerateUniqueUVsForSkeletalMesh(const FSkeletalMeshLODModel& LODModel, int32 TextureResolution, TArray& OutTexCoords) const +// { +// // Get easy to use SkeletalMesh data +// TArray Vertices; +// LODModel.GetVertices(Vertices); +// +// int32 NumCorners = LODModel.IndexBuffer.Num(); +// +// // Generate FRawMesh from FSkeletalMeshLODModel +// FRawMesh TempMesh; +// TempMesh.WedgeIndices.AddUninitialized(NumCorners); +// TempMesh.WedgeTexCoords[0].AddUninitialized(NumCorners); +// TempMesh.VertexPositions.AddUninitialized(NumCorners); +// +// // Prepare vertex to wedge map +// // PrevCorner[i] points to previous corner which shares the same wedge +// TArray LastWedgeCorner; +// LastWedgeCorner.AddUninitialized(Vertices.Num()); +// TArray PrevCorner; +// PrevCorner.AddUninitialized(NumCorners); +// for (int32 Index = 0; Index < Vertices.Num(); Index++) +// { +// LastWedgeCorner[Index] = -1; +// } +// +// for (int32 Index = 0; Index < NumCorners; Index++) +// { +// // Copy static vertex data +// int32 VertexIndex = LODModel.IndexBuffer[Index]; +// FSoftSkinVertex& Vertex = Vertices[VertexIndex]; +// TempMesh.WedgeIndices[Index] = Index; // rudimental data, not really used by FLayoutUV - but array size matters +// TempMesh.WedgeTexCoords[0][Index] = Vertex.UVs[0]; +// TempMesh.VertexPositions[Index] = Vertex.Position; +// // Link all corners belonging to a single wedge into list +// int32 PrevCornerIndex = LastWedgeCorner[VertexIndex]; +// LastWedgeCorner[VertexIndex] = Index; +// PrevCorner[Index] = PrevCornerIndex; +// } +// +// // Build overlapping corners map +// FOverlappingCorners OverlappingCorners; +// OverlappingCorners.Init(NumCorners); +// for (int32 Index = 0; Index < NumCorners; Index++) +// { +// int VertexIndex = LODModel.IndexBuffer[Index]; +// for (int32 CornerIndex = LastWedgeCorner[VertexIndex]; CornerIndex >= 0; CornerIndex = PrevCorner[CornerIndex]) +// { +// if (CornerIndex != Index) +// { +// OverlappingCorners.Add(Index, CornerIndex); +// } +// } +// } +// OverlappingCorners.FinishAdding(); +// +// // Generate new UVs +// FLayoutUVRawMeshView TempMeshView(TempMesh, 0, 1); +// FLayoutUV Packer(TempMeshView); +// Packer.FindCharts(OverlappingCorners); +// +// bool bPackSuccess = Packer.FindBestPacking(FMath::Clamp(TextureResolution / 4, 32, 512)); +// if (bPackSuccess) +// { +// Packer.CommitPackedUVs(); +// // Save generated UVs +// OutTexCoords = TempMesh.WedgeTexCoords[1]; +// } +// return bPackSuccess; +// } + +void FMeshUtilities2::CalculateTangents(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InSmoothingGroupIndices, const uint32 InTangentOptions, TArray& OutTangentX, + TArray& OutTangentY, TArray& OutNormals) const +{ + const float ComparisonThreshold = (InTangentOptions & ETangentOptions2::IgnoreDegenerateTriangles) ? THRESH_POINTS_ARE_SAME : 0.0f; + + FOverlappingCorners OverlappingCorners; + FindOverlappingCorners(OverlappingCorners, InVertices, InIndices, ComparisonThreshold); + + if (InTangentOptions & ETangentOptions2::UseMikkTSpace) + { + ComputeTangents_MikkTSpace(InVertices, InIndices, InUVs, InSmoothingGroupIndices, OverlappingCorners, OutTangentX, OutTangentY, OutNormals, InTangentOptions); + } + else + { + ComputeTangents(InVertices, InIndices, InUVs, InSmoothingGroupIndices, OverlappingCorners, OutTangentX, OutTangentY, OutNormals, InTangentOptions); + } +} + +void FMeshUtilities2::CalculateMikkTSpaceTangents(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InNormals, bool bIgnoreDegenerateTriangles, TArray& OutTangentX, + TArray& OutTangentY) const +{ + ComputeTangents_MikkTSpace(InVertices, InIndices, InUVs, InNormals, bIgnoreDegenerateTriangles, OutTangentX, OutTangentY); +} + +// +// void FMeshUtilities2::CalculateNormals(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InSmoothingGroupIndices, const uint32 InTangentOptions, TArray& OutNormals) const +// { +// const float ComparisonThreshold = (InTangentOptions & ETangentOptions::IgnoreDegenerateTriangles ) ? THRESH_POINTS_ARE_SAME : 0.0f; +// +// FOverlappingCorners OverlappingCorners; +// FindOverlappingCorners(OverlappingCorners, InVertices, InIndices, ComparisonThreshold); +// +// ComputeNormals(InVertices, InIndices, InUVs, InSmoothingGroupIndices, OverlappingCorners, OutNormals, InTangentOptions); +// } + +void FMeshUtilities2::CalculateOverlappingCorners(const TArray& InVertices, const TArray& InIndices, bool bIgnoreDegenerateTriangles, FOverlappingCorners& OutOverlappingCorners) const +{ + const float ComparisonThreshold = bIgnoreDegenerateTriangles ? THRESH_POINTS_ARE_SAME : 0.f; + FindOverlappingCorners(OutOverlappingCorners, InVertices, InIndices, ComparisonThreshold); +} + +// +// void FMeshUtilities2::GenerateRuntimeSkinWeightData(const FSkeletalMeshLODModel* ImportedModel, const TArray& InRawSkinWeights, FRuntimeSkinWeightProfileData& InOutSkinWeightOverrideData) const +// { +// const FSkeletalMeshLODModel& TargetLODModel = *ImportedModel; +// +// // Make sure the number of verts of the LOD matches the provided number of skin weights +// if (InRawSkinWeights.Num() == TargetLODModel.NumVertices) +// { +// // Retrieve all vertices for this LOD +// TArray TargetVertices; +// TargetLODModel.GetVertices(TargetVertices); +// +// // Determine how many influences each skinweight can contain +// const int32 NumInfluences = TargetLODModel.GetMaxBoneInfluences(); +// InOutSkinWeightOverrideData.NumWeightsPerVertex = NumInfluences; +// +// const bool b16BitBoneIndices = TargetLODModel.DoSectionsUse16BitBoneIndex(); +// InOutSkinWeightOverrideData.b16BitBoneIndices = b16BitBoneIndices; +// +// TArray UniqueWeights; +// for (int32 VertexIndex = 0; VertexIndex < TargetVertices.Num(); ++VertexIndex) +// { +// // Take each original skin weight from the LOD and compare it with supplied alternative weight data +// const FRawSkinWeight& SourceSkinWeight = InRawSkinWeights[VertexIndex]; +// const FSoftSkinVertex& TargetVertex = TargetVertices[VertexIndex]; +// +// bool bIsDifferent = false; +// for (int32 InfluenceIndex = 0; InfluenceIndex < NumInfluences; ++InfluenceIndex) +// { +// if (SourceSkinWeight.InfluenceBones[InfluenceIndex] != TargetVertex.InfluenceBones[InfluenceIndex] +// || SourceSkinWeight.InfluenceWeights[InfluenceIndex] != TargetVertex.InfluenceWeights[InfluenceIndex]) +// { +// bIsDifferent = true; +// break; +// } +// } +// +// if (bIsDifferent) +// { +// // Check whether or not there is already an override store which matches the new skin weight data +// int32 OverrideIndex = UniqueWeights.IndexOfByPredicate([SourceSkinWeight, NumInfluences](const FRawSkinWeight Override) +// { +// bool bSame = true; +// for (int32 InfluenceIndex = 0; InfluenceIndex < NumInfluences; ++InfluenceIndex) +// { +// bSame &= (Override.InfluenceBones[InfluenceIndex] == SourceSkinWeight.InfluenceBones[InfluenceIndex]); +// bSame &= (Override.InfluenceWeights[InfluenceIndex] == SourceSkinWeight.InfluenceWeights[InfluenceIndex]); +// } +// +// return bSame; +// }); +// +// // If one hasn't been added yet, create a new one +// if (OverrideIndex == INDEX_NONE) +// { +// OverrideIndex = UniqueWeights.Num(); +// +// // Write out non-zero weighted influences only +// for (int32 InfluenceIndex = 0; InfluenceIndex < NumInfluences; ++InfluenceIndex) +// { +// const FBoneIndexType Index = SourceSkinWeight.InfluenceBones[InfluenceIndex]; +// const uint8 Weight = SourceSkinWeight.InfluenceWeights[InfluenceIndex]; +// +// if (b16BitBoneIndices) +// { +// InOutSkinWeightOverrideData.BoneIDs.AddZeroed(2); +// FBoneIndexType* BoneIndex = (FBoneIndexType*)&InOutSkinWeightOverrideData.BoneIDs[InOutSkinWeightOverrideData.BoneIDs.Num() - 2]; +// *BoneIndex = Index; +// } +// else +// { +// InOutSkinWeightOverrideData.BoneIDs.Add((uint8)Index); +// } +// +// InOutSkinWeightOverrideData.BoneWeights.Add(Weight); +// } +// +// UniqueWeights.Add(SourceSkinWeight); +// } +// +// InOutSkinWeightOverrideData.VertexIndexToInfluenceOffset.Add(VertexIndex, OverrideIndex); +// } +// } +// } +// } + +void FMeshUtilities2::AddAnimationBlueprintEditorToolbarExtender() +{ + //删除 + // IAnimationBlueprintEditorModule& AnimationBlueprintEditorModule = FModuleManager::Get().LoadModuleChecked("AnimationBlueprintEditor"); + // auto& ToolbarExtenders = AnimationBlueprintEditorModule.GetAllAnimationBlueprintEditorToolbarExtenders(); + // + // ToolbarExtenders.Add(IAnimationBlueprintEditorModule::FAnimationBlueprintEditorToolbarExtender::CreateRaw(this, &FMeshUtilities2::GetAnimationBlueprintEditorToolbarExtender)); + // AnimationBlueprintEditorExtenderHandle = ToolbarExtenders.Last().GetHandle(); +} + +void FMeshUtilities2::RemoveAnimationBlueprintEditorToolbarExtender() +{ + //删除 + // IAnimationBlueprintEditorModule* AnimationBlueprintEditorModule = FModuleManager::Get().GetModulePtr("AnimationBlueprintEditor"); + // if (AnimationBlueprintEditorModule) + // { + // typedef IAnimationBlueprintEditorModule::FAnimationBlueprintEditorToolbarExtender DelegateType; + // AnimationBlueprintEditorModule->GetAllAnimationBlueprintEditorToolbarExtenders().RemoveAll([=](const DelegateType& In) { return In.GetHandle() == AnimationBlueprintEditorExtenderHandle; }); + // } +} + +// TSharedRef FMeshUtilities2::GetAnimationBlueprintEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InAnimationBlueprintEditor) +// { +// TSharedRef Extender = MakeShareable(new FExtender); +// +// if(InAnimationBlueprintEditor->GetBlueprintObj() && InAnimationBlueprintEditor->GetBlueprintObj()->BlueprintType != BPTYPE_Interface) +// { +// UMeshComponent* MeshComponent = InAnimationBlueprintEditor->GetPersonaToolkit()->GetPreviewMeshComponent(); +// +// Extender->AddToolBarExtension( +// "Asset", +// EExtensionHook::After, +// CommandList, +// FToolBarExtensionDelegate::CreateRaw(this, &FMeshUtilities2::HandleAddSkeletalMeshActionExtenderToToolbar, MeshComponent) +// ); +// } +// +// return Extender; +// } +// +// void FMeshUtilities2::AddAnimationEditorToolbarExtender() +// { +// IAnimationEditorModule& AnimationEditorModule = FModuleManager::Get().LoadModuleChecked("AnimationEditor"); +// auto& ToolbarExtenders = AnimationEditorModule.GetAllAnimationEditorToolbarExtenders(); +// +// ToolbarExtenders.Add(IAnimationEditorModule::FAnimationEditorToolbarExtender::CreateRaw(this, &FMeshUtilities2::GetAnimationEditorToolbarExtender)); +// AnimationEditorExtenderHandle = ToolbarExtenders.Last().GetHandle(); +// } +// +// void FMeshUtilities2::RemoveAnimationEditorToolbarExtender() +// { +// IAnimationEditorModule* AnimationEditorModule = FModuleManager::Get().GetModulePtr("AnimationEditor"); +// if (AnimationEditorModule) +// { +// typedef IAnimationEditorModule::FAnimationEditorToolbarExtender DelegateType; +// AnimationEditorModule->GetAllAnimationEditorToolbarExtenders().RemoveAll([=](const DelegateType& In) { return In.GetHandle() == AnimationEditorExtenderHandle; }); +// } +// } +// +// TSharedRef FMeshUtilities2::GetAnimationEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InAnimationEditor) +// { +// TSharedRef Extender = MakeShareable(new FExtender); +// +// UMeshComponent* MeshComponent = InAnimationEditor->GetPersonaToolkit()->GetPreviewMeshComponent(); +// +// Extender->AddToolBarExtension( +// "Asset", +// EExtensionHook::After, +// CommandList, +// FToolBarExtensionDelegate::CreateRaw(this, &FMeshUtilities2::HandleAddSkeletalMeshActionExtenderToToolbar, MeshComponent) +// ); +// +// return Extender; +// } +// +// TSharedRef FMeshUtilities2::GetSkeletalMeshEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InSkeletalMeshEditor) +// { +// TSharedRef Extender = MakeShareable(new FExtender); +// +// UMeshComponent* MeshComponent = InSkeletalMeshEditor->GetPersonaToolkit()->GetPreviewMeshComponent(); +// +// Extender->AddToolBarExtension( +// "Asset", +// EExtensionHook::After, +// CommandList, +// FToolBarExtensionDelegate::CreateRaw(this, &FMeshUtilities2::HandleAddSkeletalMeshActionExtenderToToolbar, MeshComponent) +// ); +// +// return Extender; +// } +// +// void FMeshUtilities2::AddSkeletonEditorToolbarExtender() +// { +// ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::Get().LoadModuleChecked("SkeletonEditor"); +// auto& ToolbarExtenders = SkeletonEditorModule.GetAllSkeletonEditorToolbarExtenders(); +// +// ToolbarExtenders.Add(ISkeletonEditorModule::FSkeletonEditorToolbarExtender::CreateRaw(this, &FMeshUtilities2::GetSkeletonEditorToolbarExtender)); +// SkeletonEditorExtenderHandle = ToolbarExtenders.Last().GetHandle(); +// } +// +// void FMeshUtilities2::RemoveSkeletonEditorToolbarExtender() +// { +// ISkeletonEditorModule* SkeletonEditorModule = FModuleManager::Get().GetModulePtr("SkeletonEditor"); +// if (SkeletonEditorModule) +// { +// typedef ISkeletonEditorModule::FSkeletonEditorToolbarExtender DelegateType; +// SkeletonEditorModule->GetAllSkeletonEditorToolbarExtenders().RemoveAll([=](const DelegateType& In) { return In.GetHandle() == SkeletonEditorExtenderHandle; }); +// } +// } +// +// TSharedRef FMeshUtilities2::GetSkeletonEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InSkeletonEditor) +// { +// TSharedRef Extender = MakeShareable(new FExtender); +// +// UMeshComponent* MeshComponent = InSkeletonEditor->GetPersonaToolkit()->GetPreviewMeshComponent(); +// +// Extender->AddToolBarExtension( +// "Asset", +// EExtensionHook::After, +// CommandList, +// FToolBarExtensionDelegate::CreateRaw(this, &FMeshUtilities2::HandleAddSkeletalMeshActionExtenderToToolbar, MeshComponent) +// ); +// +// return Extender; +// } +// +// +// void FMeshUtilities2::HandleAddSkeletalMeshActionExtenderToToolbar(FToolBarBuilder& ParentToolbarBuilder, UMeshComponent* InMeshComponent) +// { +// ParentToolbarBuilder.AddToolBarButton( +// FUIAction(FExecuteAction::CreateLambda([this, InMeshComponent]() +// { +// ConvertMeshesToStaticMesh(TArray({ InMeshComponent }), InMeshComponent->GetComponentToWorld()); +// })), +// NAME_None, +// LOCTEXT("MakeStaticMesh", "Make Static Mesh"), +// LOCTEXT("MakeStaticMeshTooltip", "Make a new static mesh out of the preview's current pose."), +// FSlateIcon("EditorStyle", "Persona.ConvertToStaticMesh") +// ); +// } +// +// void FMeshUtilities2::AddLevelViewportMenuExtender() +// { +// FLevelEditorModule& LevelEditorModule = FModuleManager::Get().LoadModuleChecked("LevelEditor"); +// auto& MenuExtenders = LevelEditorModule.GetAllLevelViewportContextMenuExtenders(); +// +// MenuExtenders.Add(FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors::CreateRaw(this, &FMeshUtilities2::GetLevelViewportContextMenuExtender)); +// LevelViewportExtenderHandle = MenuExtenders.Last().GetHandle(); +// } +// +// void FMeshUtilities2::RemoveLevelViewportMenuExtender() +// { +// if (LevelViewportExtenderHandle.IsValid()) +// { +// FLevelEditorModule* LevelEditorModule = FModuleManager::Get().GetModulePtr("LevelEditor"); +// if (LevelEditorModule) +// { +// typedef FLevelEditorModule::FLevelViewportMenuExtender_SelectedActors DelegateType; +// LevelEditorModule->GetAllLevelViewportContextMenuExtenders().RemoveAll([=](const DelegateType& In) { return In.GetHandle() == LevelViewportExtenderHandle; }); +// } +// } +// } +// +// /** Util for getting all MeshComponents from a supplied set of Actors */ +// void GetSkinnedAndStaticMeshComponentsFromActors(const TArray InActors, TArray& OutMeshComponents) +// { +// for (AActor* Actor : InActors) +// { +// // add all components from this actor +// TInlineComponentArray ActorComponents(Actor); +// for (UMeshComponent* ActorComponent : ActorComponents) +// { +// if (ActorComponent->IsA(USkinnedMeshComponent::StaticClass()) || ActorComponent->IsA(UStaticMeshComponent::StaticClass())) +// { +// OutMeshComponents.AddUnique(ActorComponent); +// } +// } +// +// // add all attached actors +// TArray AttachedActors; +// Actor->GetAttachedActors(AttachedActors); +// for (AActor* AttachedActor : AttachedActors) +// { +// TInlineComponentArray AttachedActorComponents(AttachedActor); +// for (UMeshComponent* AttachedActorComponent : AttachedActorComponents) +// { +// if (AttachedActorComponent->IsA(USkinnedMeshComponent::StaticClass()) || AttachedActorComponent->IsA(UStaticMeshComponent::StaticClass())) +// { +// OutMeshComponents.AddUnique(AttachedActorComponent); +// } +// } +// } +// } +// } +// +// TSharedRef FMeshUtilities2::GetLevelViewportContextMenuExtender(const TSharedRef CommandList, const TArray InActors) +// { +// TSharedRef Extender = MakeShareable(new FExtender); +// +// if (InActors.Num() > 0) +// { +// TArray Components; +// GetSkinnedAndStaticMeshComponentsFromActors(InActors, Components); +// if (Components.Num() > 0) +// { +// FText ActorName = InActors.Num() == 1 ? FText::Format(LOCTEXT("ActorNameSingular", "\"{0}\""), FText::FromString(InActors[0]->GetActorLabel())) : LOCTEXT("ActorNamePlural", "Actors"); +// +// FLevelEditorModule& LevelEditor = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); +// TSharedRef LevelEditorCommandBindings = LevelEditor.GetGlobalLevelEditorActions(); +// +// Extender->AddMenuExtension("ActorControl", EExtensionHook::After, LevelEditorCommandBindings, FMenuExtensionDelegate::CreateLambda( +// [this, ActorName, InActors](FMenuBuilder& MenuBuilder) { +// +// MenuBuilder.AddMenuEntry( +// FText::Format(LOCTEXT("ConvertSelectedActorsToStaticMeshText", "Convert {0} To Static Mesh"), ActorName), +// LOCTEXT("ConvertSelectedActorsToStaticMeshTooltip", "Convert the selected actor's meshes to a new Static Mesh asset. Supports static and skeletal meshes."), +// FSlateIcon(), +// FUIAction(FExecuteAction::CreateRaw(this, &FMeshUtilities2::ConvertActorMeshesToStaticMeshUIAction, InActors)) +// ); +// }) +// ); +// } +// } +// +// return Extender; +// } +// +// void FMeshUtilities2::ConvertActorMeshesToStaticMeshUIAction(const TArray InActors) +// { +// TArray MeshComponents; +// +// GetSkinnedAndStaticMeshComponentsFromActors(InActors, MeshComponents); +// +// auto GetActorRootTransform = [](AActor* InActor) +// { +// FTransform RootTransform(FTransform::Identity); +// if (ACharacter* Character = Cast(InActor)) +// { +// RootTransform = Character->GetTransform(); +// RootTransform.SetLocation(RootTransform.GetLocation() - FVector(0.0f, 0.0f, Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight())); +// } +// else +// { +// // otherwise just use the actor's origin +// RootTransform = InActor->GetTransform(); +// } +// +// return RootTransform; +// }; +// +// // now pick a root transform +// FTransform RootTransform(FTransform::Identity); +// if (InActors.Num() == 1) +// { +// RootTransform = GetActorRootTransform(InActors[0]); +// } +// else +// { +// // multiple actors use the average of their origins, with Z being the min of all origins. Rotation is identity for simplicity +// FVector Location(FVector::ZeroVector); +// float MinZ = FLT_MAX; +// for (AActor* Actor : InActors) +// { +// FTransform ActorTransform(GetActorRootTransform(Actor)); +// Location += ActorTransform.GetLocation(); +// MinZ = FMath::Min(ActorTransform.GetLocation().Z, MinZ); +// } +// Location /= (float)InActors.Num(); +// Location.Z = MinZ; +// +// RootTransform.SetLocation(Location); +// } +// +// UStaticMesh* StaticMesh = ConvertMeshesToStaticMesh(MeshComponents, RootTransform); +// +// // Also notify the content browser that the new assets exists +// if (StaticMesh != nullptr) +// { +// FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); +// ContentBrowserModule.Get().SyncBrowserToAssets(TArray({ StaticMesh }), true); +// } +// } +// +// /************************************************************************/ +// /* DEPRECATED FUNCTIONALITY */ +// /************************************************************************/ +// IMeshReduction* FMeshUtilities2::GetStaticMeshReductionInterface() +// { +// IMeshReductionManagerModule& Module = FModuleManager::Get().LoadModuleChecked("MeshReductionInterface"); +// return Module.GetStaticMeshReductionInterface(); +// } +// +// IMeshReduction* FMeshUtilities2::GetSkeletalMeshReductionInterface() +// { +// IMeshReductionManagerModule& Module = FModuleManager::Get().LoadModuleChecked("MeshReductionInterface"); +// return Module.GetSkeletalMeshReductionInterface(); +// } +// +// IMeshMerging* FMeshUtilities2::GetMeshMergingInterface() +// { +// IMeshReductionManagerModule& Module = FModuleManager::Get().LoadModuleChecked("MeshReductionInterface"); +// return Module.GetMeshMergingInterface(); +// } +// +// void FMeshUtilities2::MergeActors( +// const TArray& SourceActors, +// const FMeshMergingSettings& InSettings, +// UPackage* InOuter, +// const FString& InBasePackageName, +// TArray& OutAssetsToSync, +// FVector& OutMergedActorLocation, +// bool bSilent) const +// { +// checkf(SourceActors.Num(), TEXT("No actors supplied for merging")); +// +// // Collect all primitive components +// TInlineComponentArray PrimComps; +// for (AActor* Actor : SourceActors) +// { +// Actor->GetComponents(PrimComps); +// } +// +// // Filter only components we want (static mesh and shape) +// TArray ComponentsToMerge; +// for (UPrimitiveComponent* PrimComponent : PrimComps) +// { +// UStaticMeshComponent* MeshComponent = Cast(PrimComponent); +// if (MeshComponent && +// MeshComponent->GetStaticMesh() != nullptr && +// MeshComponent->GetStaticMesh()->GetNumSourceModels() > 0) +// { +// ComponentsToMerge.Add(MeshComponent); +// } +// +// UShapeComponent* ShapeComponent = Cast(PrimComponent); +// if (ShapeComponent) +// { +// ComponentsToMerge.Add(ShapeComponent); +// } +// } +// +// checkf(SourceActors.Num(), TEXT("No valid components found in actors supplied for merging")); +// +// UWorld* World = SourceActors[0]->GetWorld(); +// checkf(World != nullptr, TEXT("Invalid world retrieved from Actor")); +// const float ScreenSize = TNumericLimits::Max(); +// +// const IMeshMergeUtilities& Module = FModuleManager::Get().LoadModuleChecked("MeshMergeUtilities").GetUtilities(); +// Module.MergeComponentsToStaticMesh(ComponentsToMerge, World, InSettings, nullptr, InOuter, InBasePackageName, OutAssetsToSync, OutMergedActorLocation, ScreenSize, bSilent); +// } +// +// void FMeshUtilities2::MergeStaticMeshComponents( +// const TArray& ComponentsToMerge, +// UWorld* World, +// const FMeshMergingSettings& InSettings, +// UPackage* InOuter, +// const FString& InBasePackageName, +// TArray& OutAssetsToSync, +// FVector& OutMergedActorLocation, +// const float ScreenSize, +// bool bSilent /*= false*/) const +// { +// const IMeshMergeUtilities& Module = FModuleManager::Get().LoadModuleChecked("MeshMergeUtilities").GetUtilities(); +// +// // Convert array of StaticMeshComponents to PrimitiveComponents +// TArray PrimCompsToMerge; +// Algo::Transform(ComponentsToMerge, PrimCompsToMerge, [](UStaticMeshComponent* StaticMeshComp) { return StaticMeshComp; }); +// +// Module.MergeComponentsToStaticMesh(PrimCompsToMerge, World, InSettings, nullptr, InOuter, InBasePackageName, OutAssetsToSync, OutMergedActorLocation, ScreenSize, bSilent); +// } +// +// void FMeshUtilities2::CreateProxyMesh(const TArray& InActors, const struct FMeshProxySettings& InMeshProxySettings, UPackage* InOuter, const FString& InProxyBasePackageName, const FGuid InGuid, FCreateProxyDelegate InProxyCreatedDelegate, const bool bAllowAsync, +// const float ScreenAreaSize /*= 1.0f*/) +// { +// const IMeshMergeUtilities& Module = FModuleManager::Get().LoadModuleChecked("MeshMergeUtilities").GetUtilities(); +// Module.CreateProxyMesh(InActors, InMeshProxySettings, InOuter, InProxyBasePackageName, InGuid, InProxyCreatedDelegate, bAllowAsync, ScreenAreaSize); +// } +// +// bool FMeshUtilities2::GenerateUniqueUVsForStaticMesh(const FRawMesh& RawMesh, int32 TextureResolution, bool bMergeIdenticalMaterials, TArray& OutTexCoords) const +// { +// // Create a copy of original mesh (only copy necessary data) +// FRawMesh TempMesh; +// TempMesh.VertexPositions = RawMesh.VertexPositions; +// +// // Remove all duplicate faces if we are merging identical materials +// const int32 NumFaces = RawMesh.FaceMaterialIndices.Num(); +// TArray DuplicateFaceRecords; +// +// if(bMergeIdenticalMaterials) +// { +// TArray UniqueFaceIndices; +// UniqueFaceIndices.Reserve(NumFaces); +// DuplicateFaceRecords.SetNum(NumFaces); +// +// TempMesh.WedgeTexCoords[0].Reserve(RawMesh.WedgeTexCoords[0].Num()); +// TempMesh.WedgeIndices.Reserve(RawMesh.WedgeIndices.Num()); +// +// // insert only non-duplicate faces +// for(int32 FaceIndex = 0; FaceIndex < NumFaces; ++FaceIndex) +// { +// bool bFound = false; +// int32 UniqueFaceIndex = 0; +// for( ; UniqueFaceIndex < UniqueFaceIndices.Num(); ++UniqueFaceIndex) +// { +// int32 TestIndex = UniqueFaceIndices[UniqueFaceIndex]; +// +// if (TestIndex != FaceIndex && +// RawMesh.FaceMaterialIndices[FaceIndex] == RawMesh.FaceMaterialIndices[TestIndex] && +// RawMesh.WedgeTexCoords[0][(FaceIndex * 3) + 0] == RawMesh.WedgeTexCoords[0][(TestIndex * 3) + 0] && +// RawMesh.WedgeTexCoords[0][(FaceIndex * 3) + 1] == RawMesh.WedgeTexCoords[0][(TestIndex * 3) + 1] && +// RawMesh.WedgeTexCoords[0][(FaceIndex * 3) + 2] == RawMesh.WedgeTexCoords[0][(TestIndex * 3) + 2]) +// { +// bFound = true; +// break; +// } +// } +// +// if(!bFound) +// { +// UniqueFaceIndices.Add(FaceIndex); +// TempMesh.WedgeTexCoords[0].Add(RawMesh.WedgeTexCoords[0][(FaceIndex * 3) + 0]); +// TempMesh.WedgeTexCoords[0].Add(RawMesh.WedgeTexCoords[0][(FaceIndex * 3) + 1]); +// TempMesh.WedgeTexCoords[0].Add(RawMesh.WedgeTexCoords[0][(FaceIndex * 3) + 2]); +// TempMesh.WedgeIndices.Add(RawMesh.WedgeIndices[(FaceIndex * 3) + 0]); +// TempMesh.WedgeIndices.Add(RawMesh.WedgeIndices[(FaceIndex * 3) + 1]); +// TempMesh.WedgeIndices.Add(RawMesh.WedgeIndices[(FaceIndex * 3) + 2]); +// +// DuplicateFaceRecords[FaceIndex] = UniqueFaceIndices.Num() - 1; +// } +// else +// { +// DuplicateFaceRecords[FaceIndex] = UniqueFaceIndex; +// } +// } +// } +// else +// { +// TempMesh.WedgeTexCoords[0] = RawMesh.WedgeTexCoords[0]; +// TempMesh.WedgeIndices = RawMesh.WedgeIndices; +// } +// +// // Find overlapping corners for UV generator. Allow some threshold - this should not produce any error in a case if resulting +// // mesh will not merge these vertices. +// FOverlappingCorners OverlappingCorners; +// FModuleManager::Get().LoadModuleChecked("MeshUtilities").FindOverlappingCorners(OverlappingCorners, TempMesh.VertexPositions, TempMesh.WedgeIndices, THRESH_POINTS_ARE_SAME); +// +// // Generate new UVs +// FLayoutUVRawMeshView TempMeshView(TempMesh, 0, 1); +// FLayoutUV Packer(TempMeshView); +// Packer.FindCharts(OverlappingCorners); +// +// bool bPackSuccess = Packer.FindBestPacking(FMath::Clamp(TextureResolution / 4, 32, 512)); +// if (bPackSuccess) +// { +// Packer.CommitPackedUVs(); +// +// if(bMergeIdenticalMaterials) +// { +// // re-duplicate faces +// OutTexCoords.SetNum(RawMesh.WedgeTexCoords[0].Num()); +// +// for(int32 FaceIndex = 0; FaceIndex < DuplicateFaceRecords.Num(); ++FaceIndex) +// { +// int32 SourceFaceIndex = DuplicateFaceRecords[FaceIndex]; +// +// OutTexCoords[(FaceIndex * 3) + 0] = TempMesh.WedgeTexCoords[1][(SourceFaceIndex * 3) + 0]; +// OutTexCoords[(FaceIndex * 3) + 1] = TempMesh.WedgeTexCoords[1][(SourceFaceIndex * 3) + 1]; +// OutTexCoords[(FaceIndex * 3) + 2] = TempMesh.WedgeTexCoords[1][(SourceFaceIndex * 3) + 2]; +// } +// } +// else +// { +// // Save generated UVs +// OutTexCoords = TempMesh.WedgeTexCoords[1]; +// } +// } +// +// return bPackSuccess; +// } +// +// bool FMeshUtilities2::GenerateUniqueUVsForStaticMesh(const FRawMesh& RawMesh, int32 TextureResolution, TArray& OutTexCoords) const +// { +// return GenerateUniqueUVsForStaticMesh(RawMesh, TextureResolution, false, OutTexCoords); +// } +// +// void FMeshUtilities2::FlattenMaterialsWithMeshData(TArray& InMaterials, TArray& InSourceMeshes, TMap>& InMaterialIndexMap, TArray& InMeshShouldBakeVertexData, const FMaterialProxySettings &InMaterialProxySettings, TArray &OutFlattenedMaterials) const +// { +// checkf(false, TEXT("Function is removed, use functionality in new MeshMergeUtilities Module")); +// } + +#undef LOCTEXT_NAMESPACE diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshUtilitiesPrivate.h b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshUtilitiesPrivate.h new file mode 100644 index 0000000..894cfbb --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Private/MeshUtilitiesPrivate.h @@ -0,0 +1,259 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +//#include "MeshUtilities.h" +// #include "IAnimationBlueprintEditor.h" +// #include "IAnimationBlueprintEditorModule.h" +// #include "IAnimationEditor.h" +// #include "IAnimationEditorModule.h" +// #include "ISkeletalMeshEditor.h" +// #include "ISkeletalMeshEditorModule.h" +// #include "ISkeletonEditor.h" +// #include "ISkeletonEditorModule.h" +#include "Engine/StaticMesh.h" +#include "MeshUtilities2/Public/DistanceFieldAtlas2.h" +#include "MeshUtilities2/Public/MeshUtilities2.h" + +class FProcessAsyncTasksTickObject : FTickableGameObject +{ +public: + virtual bool IsTickableInEditor() const override { return true; } + virtual void Tick(float DeltaTime) override; + virtual TStatId GetStatId() const { return TStatId(); } +}; + + +class FMeshUtilities2 : public IMeshUtilities2 +{ +public: + // UE_DEPRECATED(4.17, "Use functionality in new MeshReduction Module") + // virtual IMeshReduction* GetStaticMeshReductionInterface() override; + // + // UE_DEPRECATED(4.17, "Use functionality in new MeshReduction Module") + // virtual IMeshReduction* GetSkeletalMeshReductionInterface() override; + // + // UE_DEPRECATED(4.17, "Use functionality in new MeshReduction Module") + // virtual IMeshMerging* GetMeshMergingInterface() override; + + //UE_DEPRECATED(4.17, "Use functionality in new MeshMergeUtilities Module") + //virtual void MergeActors( + // const TArray& SourceActors, + // const FMeshMergingSettings& InSettings, + // UPackage* InOuter, + // const FString& InBasePackageName, + // TArray& OutAssetsToSync, + // FVector& OutMergedActorLocation, + // bool bSilent = false) const override; + + //UE_DEPRECATED(4.17, "Use functionality in new MeshMergeUtilities Module") + //virtual void MergeStaticMeshComponents( + // const TArray& ComponentsToMerge, + // UWorld* World, + // const FMeshMergingSettings& InSettings, + // UPackage* InOuter, + // const FString& InBasePackageName, + // TArray& OutAssetsToSync, + // FVector& OutMergedActorLocation, + // const float ScreenSize, + // bool bSilent = false) const override; + + //UE_DEPRECATED(4.17, "Use functionality in new MeshMergeUtilities Module") + //virtual void CreateProxyMesh(const TArray& InActors, const struct FMeshProxySettings& InMeshProxySettings, UPackage* InOuter, const FString& InProxyBasePackageName, const FGuid InGuid, FCreateProxyDelegate InProxyCreatedDelegate, const bool bAllowAsync, + //const float ScreenAreaSize = 1.0f) override; + + //UE_DEPRECATED(4.17, "Function is removed, use functionality in new MeshMergeUtilities Module") + //virtual void FlattenMaterialsWithMeshData(TArray& InMaterials, TArray& InSourceMeshes, TMap>& InMaterialIndexMap, TArray& InMeshShouldBakeVertexData, const FMaterialProxySettings &InMaterialProxySettings, TArray &OutFlattenedMaterials) const override; + +private: + FProcessAsyncTasksTickObject* TickObject; + + /** Cached version string. */ + FString VersionString; + /** True if NvTriStrip is being used for tri order optimization. */ + bool bUsingNvTriStrip; + /** True if we disable triangle order optimization. For debugging purposes only */ + bool bDisableTriangleOrderOptimization; + + // IMeshUtilities interface. + virtual const FString& GetVersionString() const override + { + return VersionString; + } + + virtual void FixupMaterialSlotNames(UStaticMesh* StaticMesh) const override; + + virtual void FixupMaterialSlotNames(USkeletalMesh* SkeletalMesh) const override; + + //virtual bool BuildStaticMesh( + // FStaticMeshRenderData& OutRenderData, + // UStaticMesh* StaticMesh, + // const FStaticMeshLODGroup& LODGroup + // ) override; + + virtual void BuildStaticMeshVertexAndIndexBuffers( + TArray& OutVertices, + TArray>& OutPerSectionIndices, + TArray& OutWedgeMap, + const FRawMesh& RawMesh, + const FOverlappingCorners& OverlappingCorners, + const TMap& MaterialToSectionMapping, + float ComparisonThreshold, + FVector BuildScale, + int32 ImportVersion + ) override; + + //virtual bool GenerateStaticMeshLODs(UStaticMesh* StaticMesh, const FStaticMeshLODGroup& LODGroup) override; + + virtual void GenerateSignedDistanceFieldVolumeData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + float DistanceFieldResolutionScale, + bool bGenerateAsIfTwoSided, + FDistanceFieldVolumeData& OutData) override; + + virtual bool GenerateCardRepresentationData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + const FDistanceFieldVolumeData* DistanceFieldVolumeData, + bool bGenerateAsIfTwoSided, + class FCardRepresentationData& OutData) override; + + virtual void RecomputeTangentsAndNormalsForRawMesh(bool bRecomputeTangents, bool bRecomputeNormals, const FMeshBuildSettings& InBuildSettings, FRawMesh& OutRawMesh) const override; + virtual void RecomputeTangentsAndNormalsForRawMesh(bool bRecomputeTangents, bool bRecomputeNormals, const FMeshBuildSettings& InBuildSettings, const FOverlappingCorners& InOverlappingCorners, FRawMesh& OutRawMesh) const override; + + //virtual bool GenerateUniqueUVsForStaticMesh(const FRawMesh& RawMesh, int32 TextureResolution, TArray& OutTexCoords) const override; + //virtual bool GenerateUniqueUVsForStaticMesh(const FRawMesh& RawMesh, int32 TextureResolution, bool bMergeIdenticalMaterials, TArray& OutTexCoords) const override; + + //virtual bool BuildSkeletalMesh(FSkeletalMeshLODModel& LODModel, const FString& SkeletalMeshName, const FReferenceSkeleton& RefSkeleton, const TArray& Influences, const TArray& Wedges, const TArray& Faces, const TArray& Points, const TArray& PointToOriginalMap, const MeshBuildOptions& BuildOptions = MeshBuildOptions(), TArray * OutWarningMessages = NULL, TArray * OutWarningNames = NULL) override; + + //UE_DEPRECATED(4.24, "Use functionality in FSkeletalMeshUtilityBuilder instead.") + //bool BuildSkeletalMesh_Legacy(FSkeletalMeshLODModel& LODModel, const FReferenceSkeleton& RefSkeleton, const TArray& Influences, const TArray& Wedges, const TArray& Faces, const TArray& Points, const TArray& PointToOriginalMap, const FOverlappingThresholds& OverlappingThresholds, bool bComputeNormals = true, bool bComputeTangents = true, bool bComputeWeightedNormals = true, TArray * OutWarningMessages = NULL, TArray * OutWarningNames = NULL); + + virtual void CacheOptimizeIndexBuffer(TArray& Indices) override; + virtual void CacheOptimizeIndexBuffer(TArray& Indices) override; + void CacheOptimizeVertexAndIndexBuffer(TArray& Vertices, TArray>& PerSectionIndices, TArray& WedgeMap); + + virtual void BuildSkeletalAdjacencyIndexBuffer( + const TArray& VertexBuffer, + const uint32 TexCoordCount, + const TArray& Indices, + TArray& OutPnAenIndices + ) override; + + /** + * Calculate The tangent bi normal and normal for the triangle define by the tree SoftSkinVertex. + * + * @note The function will always fill properly the OutTangents array with 3 FVector. If the triangle is degenerated the OutTangent will contain zeroed vectors. + * + * @param VertexA - First triangle vertex. + * @param VertexB - Second triangle vertex. + * @param VertexC - Third triangle vertex. + * @param OutTangents - The function allocate the TArray with 3 FVector, to represent the triangle tangent, bi normal and normal. + * @param CompareThreshold - The threshold use to compare a tangent vector with zero. + */ + virtual void CalculateTriangleTangent(const FSoftSkinVertex& VertexA, const FSoftSkinVertex& VertexB, const FSoftSkinVertex& VertexC, TArray& OutTangents, float CompareThreshold) override; + + virtual void CalcBoneVertInfos(USkeletalMesh* SkeletalMesh, TArray& Infos, bool bOnlyDominant) override; + + /** + * Convert a set of mesh components in their current pose to a static mesh. + * @param InMeshComponents The mesh components we want to convert + * @param InRootTransform The transform of the root of the mesh we want to output + * @param InPackageName The package name to create the static mesh in. If this is empty then a dialog will be displayed to pick the mesh. + * @return a new static mesh (specified by the user) + */ + virtual UStaticMesh* ConvertMeshesToStaticMesh(const TArray& InMeshComponents, const FTransform& InRootTransform = FTransform::Identity, const FString& InPackageName = FString()) override; + + /** + * Builds a renderable skeletal mesh LOD model. Note that the array of chunks + * will be destroyed during this process! + * @param LODModel Upon return contains a renderable skeletal mesh LOD model. + * @param RefSkeleton The reference skeleton associated with the model. + * @param Chunks Skinned mesh chunks from which to build the renderable model. + * @param PointToOriginalMap Maps a vertex's RawPointIdx to its index at import time. + */ + //void BuildSkeletalModelFromChunks(FSkeletalMeshLODModel& LODModel, const FReferenceSkeleton& RefSkeleton, TArray& Chunks, const TArray& PointToOriginalMap); + + virtual void FindOverlappingCorners(FOverlappingCorners& OutOverlappingCorners, const TArray& InVertices, const TArray& InIndices, float ComparisonThreshold) const override; + + void FindOverlappingCorners(FOverlappingCorners& OutOverlappingCorners, FRawMesh const& RawMesh, float ComparisonThreshold) const; + // IModuleInterface interface. + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + virtual void ExtractMeshDataForGeometryCache(FRawMesh& RawMesh, const FMeshBuildSettings& BuildSettings, TArray& OutVertices, TArray>& OutPerSectionIndices, int32 ImportVersion); + + //virtual void CalculateTextureCoordinateBoundsForSkeletalMesh(const FSkeletalMeshLODModel& LODModel, TArray& OutBounds) const override; + + //virtual bool GenerateUniqueUVsForSkeletalMesh(const FSkeletalMeshLODModel& LODModel, int32 TextureResolution, TArray& OutTexCoords) const override; + + virtual bool RemoveBonesFromMesh(USkeletalMesh* SkeletalMesh, int32 LODIndex, const TArray* BoneNamesToRemove) const override; + + virtual void CalculateTangents(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InSmoothingGroupIndices, const uint32 InTangentOptions, TArray& OutTangentX, + TArray& OutTangentY, TArray& OutNormals) const override; + virtual void CalculateMikkTSpaceTangents(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InNormals, bool bIgnoreDegenerateTriangles, TArray& OutTangentX, + TArray& OutTangentY) const override; + //virtual void CalculateNormals(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InSmoothingGroupIndices, const uint32 InTangentOptions, TArray& OutNormals) const override; + virtual void CalculateOverlappingCorners(const TArray& InVertices, const TArray& InIndices, bool bIgnoreDegenerateTriangles, FOverlappingCorners& OutOverlappingCorners) const override; + + //virtual void GenerateRuntimeSkinWeightData(const FSkeletalMeshLODModel* ImportedModel, const TArray& InRawSkinWeights, FRuntimeSkinWeightProfileData& InOutSkinWeightOverrideData) const override; + + void RegisterMenus(); + + // Need to call some members from this class, (which is internal to this module) + friend class FStaticMeshUtilityBuilder; + +protected: + void AddAnimationBlueprintEditorToolbarExtender(); + + void RemoveAnimationBlueprintEditorToolbarExtender(); + + //TSharedRef GetAnimationBlueprintEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InAnimationBlueprintEditor); + + void AddAnimationEditorToolbarExtender(); + + void RemoveAnimationEditorToolbarExtender(); + + //TSharedRef GetAnimationEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InAnimationEditor); + + //TSharedRef GetSkeletalMeshEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InSkeletalMeshEditor); + + void AddSkeletonEditorToolbarExtender(); + + void RemoveSkeletonEditorToolbarExtender(); + + //TSharedRef GetSkeletonEditorToolbarExtender(const TSharedRef CommandList, TSharedRef InSkeletonEditor); + + void HandleAddSkeletalMeshActionExtenderToToolbar(FToolBarBuilder& ParentToolbarBuilder, UMeshComponent* MeshComponent); + + void AddLevelViewportMenuExtender(); + + void RemoveLevelViewportMenuExtender(); + + TSharedRef GetLevelViewportContextMenuExtender(const TSharedRef CommandList, const TArray InActors); + + void ConvertActorMeshesToStaticMeshUIAction(const TArray InActors); + + FDelegateHandle ModuleLoadedDelegateHandle; + FDelegateHandle LevelViewportExtenderHandle; + FDelegateHandle AnimationBlueprintEditorExtenderHandle; + FDelegateHandle AnimationEditorExtenderHandle; + FDelegateHandle SkeletonEditorExtenderHandle; +}; + +DECLARE_LOG_CATEGORY_EXTERN(LogMeshUtilities, Verbose, All); + +namespace MeshUtilities +{ + /** Generates unit length, stratified and uniformly distributed direction samples in a hemisphere. */ + void GenerateStratifiedUniformHemisphereSamples(int32 NumSamples, FRandomStream& RandomStream, TArray& Samples); +}; diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/DistanceFieldAtlas2.h b/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/DistanceFieldAtlas2.h new file mode 100644 index 0000000..298f716 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/DistanceFieldAtlas2.h @@ -0,0 +1,211 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +/*============================================================================= + DistanceFieldAtlas.h +=============================================================================*/ + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/LockFreeList.h" +#include "ProfilingDebugging/ResourceSize.h" +#include "Engine/EngineTypes.h" +#include "UObject/GCObject.h" +#include "Templates/UniquePtr.h" +#include "DerivedMeshDataTaskUtils.h" +#include "Async/AsyncWork.h" + +class MESHUTILITIES2_API FSignedDistanceFieldBuildMaterialData2 +{ +public: + EBlendMode BlendMode; + bool bTwoSided; +}; + +class UStaticMesh; +class UTexture2D; + +template +class TLockFreePointerListLIFO; + +// Change DDC key when modifying these (or any DF encoding logic) +namespace DistanceField2 +{ + // One voxel border around object for handling gradient + constexpr int32 MeshDistanceFieldObjectBorder = 1; + constexpr int32 UniqueDataBrickSize = 7; + // Half voxel border around brick for trilinear filtering + constexpr int32 BrickSize = 8; + // Trade off between SDF memory and number of steps required to find intersection + constexpr int32 BandSizeInVoxels = 4; + constexpr int32 NumMips = 3; + constexpr uint32 InvalidBrickIndex = 0xFFFFFFFF; + constexpr EPixelFormat DistanceFieldFormat = PF_G8; + + // Must match LoadDFAssetData + constexpr uint32 MaxIndirectionDimension = 1024; +}; + +class FSparseDistanceFieldMip2 +{ +public: + FSparseDistanceFieldMip2() : + IndirectionDimensions(FIntVector::ZeroValue), + NumDistanceFieldBricks(0), + VolumeToVirtualUVScale(FVector::ZeroVector), + VolumeToVirtualUVAdd(FVector::ZeroVector), + DistanceFieldToVolumeScaleBias(FVector2D::ZeroVector), + BulkOffset(0), + BulkSize(0) + { + } + + FIntVector IndirectionDimensions; + int32 NumDistanceFieldBricks; + FVector VolumeToVirtualUVScale; + FVector VolumeToVirtualUVAdd; + FVector2D DistanceFieldToVolumeScaleBias; + uint32 BulkOffset; + uint32 BulkSize; + + friend FArchive& operator<<(FArchive& Ar, FSparseDistanceFieldMip2& Mip) + { + Ar << Mip.IndirectionDimensions << Mip.NumDistanceFieldBricks << Mip.VolumeToVirtualUVScale << Mip.VolumeToVirtualUVAdd << Mip.DistanceFieldToVolumeScaleBias << Mip.BulkOffset << Mip.BulkSize; + return Ar; + } + + SIZE_T GetResourceSizeBytes() const + { + FResourceSizeEx ResSize; + GetResourceSizeEx(ResSize); + return ResSize.GetTotalMemoryBytes(); + } + + void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) const + { + CumulativeResourceSize.AddDedicatedSystemMemoryBytes(sizeof(*this)); + } +}; + +class FAsyncDistanceFieldTask2; + +class FAsyncDistanceFieldTaskWorker2 : public FNonAbandonableTask +{ +public: + FAsyncDistanceFieldTaskWorker2(FAsyncDistanceFieldTask2& InTask) + : Task(InTask) + { + } + + FORCEINLINE TStatId GetStatId() const + { + RETURN_QUICK_DECLARE_CYCLE_STAT(FAsyncDistanceFieldTaskWorker2, STATGROUP_ThreadPoolAsyncTasks); + } + + void DoWork(); + +private: + FAsyncDistanceFieldTask2& Task; +}; + +/** A task to build a distance field for a single mesh */ +class MESHUTILITIES2_API FAsyncDistanceFieldTask2 +{ +public: + FAsyncDistanceFieldTask2(); + + // #if WITH_EDITOR + TArray MaterialBlendModes; + // #endif + FSourceMeshDataForDerivedDataTask SourceMeshData; + UStaticMesh* StaticMesh; + UStaticMesh* GenerateSource; + float DistanceFieldResolutionScale; + bool bGenerateDistanceFieldAsIfTwoSided; + const ITargetPlatform* TargetPlatform; + FString DDCKey; + FDistanceFieldVolumeData* GeneratedVolumeData; + TUniquePtr> AsyncTask = nullptr; +}; + +/** Class that manages asynchronous building of mesh distance fields. */ +class MESHUTILITIES2_API FDistanceFieldAsyncQueue2 : public FGCObject +{ +public: + FDistanceFieldAsyncQueue2(); + + virtual ~FDistanceFieldAsyncQueue2(); + + /** Adds a new build task. (Thread-Safe) */ + void AddTask(FAsyncDistanceFieldTask2* Task); + + /** Cancel the build on this specific static mesh or block until it is completed if already started. */ + void CancelBuild(UStaticMesh* StaticMesh); + + /** Blocks the main thread until the async build are either cancelled or completed. */ + void CancelAllOutstandingBuilds(); + + /** Blocks the main thread until the async build of the specified mesh is complete. */ + void BlockUntilBuildComplete(UStaticMesh* StaticMesh, bool bWarnIfBlocked); + + /** Blocks the main thread until all async builds complete. */ + void BlockUntilAllBuildsComplete(); + + /** Called once per frame, fetches completed tasks and applies them to the scene. */ + void ProcessAsyncTasks(bool bLimitExecutionTime = false); + + /** Exposes UObject references used by the async build. */ + virtual void AddReferencedObjects(FReferenceCollector& Collector) override; + + /** Returns name of class for reference tracking */ + virtual FString GetReferencerName() const override; + + /** Blocks until it is safe to shut down (worker threads are idle). */ + void Shutdown(); + + int32 GetNumOutstandingTasks() const + { + FScopeLock Lock(&CriticalSection); + return ReferencedTasks.Num(); + } + +private: + friend FAsyncDistanceFieldTaskWorker2; + void ProcessPendingTasks(); + + TUniquePtr ThreadPool; + + /** Builds a single task with the given threadpool. Called from the worker thread. */ + void Build(FAsyncDistanceFieldTask2* Task, class FQueuedThreadPool& ThreadPool); + + /** Change the priority of the background task. */ + void RescheduleBackgroundTask(FAsyncDistanceFieldTask2* InTask, EQueuedWorkPriority InPriority); + + /** Task will be sent to a background worker. */ + void StartBackgroundTask(FAsyncDistanceFieldTask2* Task); + + /** Cancel or finish any background work for the given task. */ + void CancelBackgroundTask(TArray Tasks); + + /** Game-thread managed list of tasks in the async system. */ + TArray ReferencedTasks; + + /** Tasks that are waiting on static mesh compilation to proceed */ + TArray PendingTasks; + + /** Tasks that have completed processing. */ + // consider changing this from FIFO to Unordered, which may be faster + TLockFreePointerListLIFO CompletedTasks; + + class IMeshUtilities2* MeshUtilities; + + mutable FCriticalSection CriticalSection; +}; + +/** Global build queue. */ +extern MESHUTILITIES2_API FDistanceFieldAsyncQueue2* GDistanceFieldAsyncQueue2; + +extern MESHUTILITIES2_API FString BuildDistanceFieldDerivedDataKey2(const FString& InMeshKey); + +extern MESHUTILITIES2_API void BuildMeshDistanceField(UStaticMesh* StaticMesh); +extern MESHUTILITIES2_API void BuildMeshCardRepresentation(UStaticMesh* StaticMeshAsset, class FStaticMeshRenderData& RenderData, FSourceMeshDataForDerivedDataTask* OptionalSourceMeshData); diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/MeshCardRepresentation2.h b/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/MeshCardRepresentation2.h new file mode 100644 index 0000000..de64666 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/MeshCardRepresentation2.h @@ -0,0 +1,133 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +/*============================================================================= + MeshCardRepresentation.h +=============================================================================*/ + +#pragma once + +#include "CoreMinimal.h" +#include "Containers/LockFreeList.h" +#include "UObject/GCObject.h" +#include "Templates/UniquePtr.h" +#include "DerivedMeshDataTaskUtils.h" +#include "DistanceFieldAtlas2.h" +#include "Async/AsyncWork.h" + +template +class TLockFreePointerListLIFO; +class FSignedDistanceFieldBuildMaterialData; + +class FAsyncCardRepresentationTask2; + +class FAsyncCardRepresentationTaskWorker2 : public FNonAbandonableTask +{ +public: + FAsyncCardRepresentationTaskWorker2(FAsyncCardRepresentationTask2& InTask) + : Task(InTask) + { + } + + FORCEINLINE TStatId GetStatId() const + { + RETURN_QUICK_DECLARE_CYCLE_STAT(FAsyncCardRepresentationTaskWorker2, STATGROUP_ThreadPoolAsyncTasks); + } + + void DoWork(); + +private: + FAsyncCardRepresentationTask2& Task; +}; + +class FAsyncCardRepresentationTask2 +{ +public: + bool bSuccess = false; + + // #if WITH_EDITOR + TArray MaterialBlendModes; + // #endif + + FSourceMeshDataForDerivedDataTask SourceMeshData; + bool bGenerateDistanceFieldAsIfTwoSided = false; + UStaticMesh* StaticMesh = nullptr; + UStaticMesh* GenerateSource = nullptr; + FString DDCKey; + FCardRepresentationData* GeneratedCardRepresentation; + TUniquePtr> AsyncTask = nullptr; +}; + +/** Class that manages asynchronous building of mesh distance fields. */ +class MESHUTILITIES2_API FCardRepresentationAsyncQueue2 : public FGCObject +{ +public: + FCardRepresentationAsyncQueue2(); + + virtual ~FCardRepresentationAsyncQueue2() override; + + /** Adds a new build task. */ + void AddTask(FAsyncCardRepresentationTask2* Task); + + /** Cancel the build on this specific static mesh or block until it is completed if already started. */ + void CancelBuild(UStaticMesh* StaticMesh); + + /** Blocks the main thread until the async build are either cancelled or completed. */ + void CancelAllOutstandingBuilds(); + + /** Blocks the main thread until the async build of the specified mesh is complete. */ + void BlockUntilBuildComplete(UStaticMesh* StaticMesh, bool bWarnIfBlocked); + + /** Blocks the main thread until all async builds complete. */ + void BlockUntilAllBuildsComplete(); + + /** Called once per frame, fetches completed tasks and applies them to the scene. */ + void ProcessAsyncTasks(bool bLimitExecutionTime = false); + + /** Exposes UObject references used by the async build. */ + void AddReferencedObjects(FReferenceCollector& Collector); + + virtual FString GetReferencerName() const override; + + /** Blocks until it is safe to shut down (worker threads are idle). */ + void Shutdown(); + + int32 GetNumOutstandingTasks() const + { + FScopeLock Lock(&CriticalSection); + return ReferencedTasks.Num(); + } + +private: + friend FAsyncCardRepresentationTaskWorker2; + void ProcessPendingTasks(); + + TUniquePtr ThreadPool; + + /** Builds a single task with the given threadpool. Called from the worker thread. */ + void Build(FAsyncCardRepresentationTask2* Task, class FQueuedThreadPool& ThreadPool); + + /** Change the priority of the background task. */ + void RescheduleBackgroundTask(FAsyncCardRepresentationTask2* InTask, EQueuedWorkPriority InPriority); + + /** Task will be sent to a background worker. */ + void StartBackgroundTask(FAsyncCardRepresentationTask2* Task); + + /** Cancel or finish any background work for the given task. */ + void CancelBackgroundTask(TArray Tasks); + + /** Game-thread managed list of tasks in the async system. */ + TArray ReferencedTasks; + + /** Tasks that are waiting on static mesh compilation to proceed */ + TArray PendingTasks; + + /** Tasks that have completed processing. */ + TLockFreePointerListLIFO CompletedTasks; + + class IMeshUtilities2* MeshUtilities; + + mutable FCriticalSection CriticalSection; +}; + +/** Global build queue. */ +extern MESHUTILITIES2_API FCardRepresentationAsyncQueue2* GCardRepresentationAsyncQueue2; diff --git a/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/MeshUtilities2.h b/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/MeshUtilities2.h new file mode 100644 index 0000000..e964e32 --- /dev/null +++ b/Plugins/MeshUtilities2/Source/MeshUtilities2/Public/MeshUtilities2.h @@ -0,0 +1,410 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" +#include "Components.h" +#include "Engine/MeshMerging.h" +// #include "SkelImport.h" +#include "DistanceFieldAtlas2.h" +#include "MeshBuild.h" + +#include "IMeshMergeUtilities.h" + +class UMeshComponent; +class USkeletalMesh; +class UStaticMesh; +class UStaticMeshComponent; +struct FFlattenMaterial; +struct FRawMesh; +struct FStaticMeshLODResources; +class FSourceMeshDataForDerivedDataTask; + +typedef FIntPoint FMeshIdAndLOD; +struct FFlattenMaterial; +struct FReferenceSkeleton; +struct FStaticMeshLODResources; +class UMeshComponent; +class UStaticMesh; + +namespace ETangentOptions2 +{ + enum Type + { + None = 0, + BlendOverlappingNormals = 0x1, + IgnoreDegenerateTriangles = 0x2, + UseMikkTSpace = 0x4, + }; +}; + +/** +* Contains the vertices that are most dominated by that bone. Vertices are in Bone space. +* Not used at runtime, but useful for fitting physics assets etc. +*/ +struct FBoneVertInfo2 +{ + // Invariant: Arrays should be same length! + TArray Positions; + TArray Normals; +}; + +struct FOverlappingCorners; + +class IMeshUtilities2 : public IModuleInterface +{ +public: + /************************************************************************/ + /* DEPRECATED FUNCTIONALITY */ + /************************************************************************/ + + /** + * Harvest static mesh components from input actors + * and merge into signle mesh grouping them by unique materials + * + * @param SourceActors List of actors to merge + * @param InSettings Settings to use + * @param InOuter Outer if required + * @param InBasePackageName Destination package name for a generated assets. Used if Outer is null. + * @param UseLOD -1 if you'd like to build for all LODs. If you specify, that LOD mesh for source meshes will be used to merge the mesh + * This is used by hierarchical building LODs + * @param OutAssetsToSync Merged mesh assets + * @param OutMergedActorLocation World position of merged mesh + */ + + // virtual void MergeActors( + // const TArray& SourceActors, + // const FMeshMergingSettings& InSettings, + // UPackage* InOuter, + // const FString& InBasePackageName, + // TArray& OutAssetsToSync, + // FVector& OutMergedActorLocation, + // bool bSilent=false) const = 0; + /** + * MergeStaticMeshComponents + * + * @param ComponentsToMerge - Components to merge + * @param World - World in which the component reside + * @param InSettings - Settings to use + * @param InOuter - Outer if required + * @param InBasePackageName - Destination package name for a generated assets. Used if Outer is null. + * @param UseLOD -1 if you'd like to build for all LODs. If you specify, that LOD mesh for source meshes will be used to merge the mesh + * This is used by hierarchical building LODs + * @param OutAssetsToSync Merged mesh assets + * @param OutMergedActorLocation World position of merged mesh + * @param ViewDistance Distance for LOD determination + * @param bSilent Non-verbose flag + * @return void + */ + // virtual void MergeStaticMeshComponents( + // const TArray& ComponentsToMerge, + // UWorld* World, + // const FMeshMergingSettings& InSettings, + // UPackage* InOuter, + // const FString& InBasePackageName, + // TArray& OutAssetsToSync, + // FVector& OutMergedActorLocation, + // const float ScreenAreaSize, + // bool bSilent /*= false*/) const = 0; + + /** + * Creates a (proxy)-mesh combining the static mesh components from the given list of actors (at the moment this requires having Simplygon) + * + * @param InActors - List of Actors to merge + * @param InMeshProxySettings - Merge settings + * @param InOuter - Package for a generated assets, if NULL new packages will be created for each asset + * @param InProxyBasePackageName - Will be used for naming generated assets, in case InOuter is not specified ProxyBasePackageName will be used as long package name for creating new packages + * @param InGuid - Guid identifying the data used for this proxy job + * @param InProxyCreatedDelegate - Delegate callback for when the proxy is finished + * @param bAllowAsync - Flag whether or not this call could be run async (SimplygonSwarm) + */ + // virtual void CreateProxyMesh(const TArray& InActors, const struct FMeshProxySettings& InMeshProxySettings, UPackage* InOuter, const FString& InProxyBasePackageName, const FGuid InGuid, FCreateProxyDelegate InProxyCreatedDelegate, const bool bAllowAsync = false, const float ScreenAreaSize = 1.0f) = 0; + + /** + * FlattenMaterialsWithMeshData + * + * @param InMaterials - List of unique materials used by InSourceMeshes + * @param InSourceMeshes - List of raw meshes used to flatten the materials with (vertex data) + * @param InMaterialIndexMap - Map used for mapping the raw meshes to the correct materials + * @param InMeshShouldBakeVertexData - Array of flags to determine whether or not a mesh requires to have its vertex data baked down + * @param InMaterialProxySettings - Settings for creating the flattened material + * @param OutFlattenedMaterials - List of flattened materials (one for each mesh) + */ + //virtual void FlattenMaterialsWithMeshData(TArray& InMaterials, TArray& InSourceMeshes, TMap>& InMaterialIndexMap, TArray& InMeshShouldBakeVertexData, const FMaterialProxySettings &InMaterialProxySettings, TArray &OutFlattenedMaterials) const = 0; + + /** + * Calculates (new) non-overlapping UV coordinates for the given Raw Mesh + * + * @param RawMesh - Raw Mesh to generate UV coordinates for + * @param TextureResolution - Texture resolution to take into account while generating the UVs + * @param bMergeIdenticalMaterials - Whether faces with identical materials can be treated as one in the resulting set of unique UVs + * @param OutTexCoords - New set of UV coordinates + * @return bool - whether or not generating the UVs succeeded + */ + //virtual bool GenerateUniqueUVsForStaticMesh(const FRawMesh& RawMesh, int32 TextureResolution, TArray& OutTexCoords) const = 0; + //virtual bool GenerateUniqueUVsForStaticMesh(const FRawMesh& RawMesh, int32 TextureResolution, bool bMergeIdenticalMaterials, TArray& OutTexCoords) const = 0; + + // /** Returns the mesh reduction plugin if available. */ + // virtual IMeshReduction* GetStaticMeshReductionInterface() = 0; + // + // /** Returns the mesh reduction plugin if available. */ + // virtual IMeshReduction* GetSkeletalMeshReductionInterface() = 0; + // + // /** Returns the mesh merging plugin if available. */ + // virtual IMeshMerging* GetMeshMergingInterface() = 0; +public: + /** Returns a string uniquely identifying this version of mesh utilities. */ + virtual const FString& GetVersionString() const = 0; + + /** Used to make sure all imported material slot name are unique and non empty. + * + * @param StaticMesh + * @param bForceUniqueSlotName If true, make sure all slot names are unique as well. + */ + virtual void FixupMaterialSlotNames(UStaticMesh* StaticMesh) const = 0; + + /** Used to make sure all imported material slot name are unique and non empty. + * + * @param SkeletalMesh + * @param bForceUniqueSlotName If true, make sure all slot names are unique as well. + */ + virtual void FixupMaterialSlotNames(USkeletalMesh* SkeletalMesh) const = 0; + + /** + * Builds a renderable static mesh using the provided source models and the LOD groups settings. + * @returns true if the renderable mesh was built successfully. + */ + //virtual bool BuildStaticMesh( + //class FStaticMeshRenderData& OutRenderData, + //UStaticMesh* StaticMesh, + //const class FStaticMeshLODGroup& LODGroup + //) = 0; + + virtual void BuildStaticMeshVertexAndIndexBuffers( + TArray& OutVertices, + TArray >& OutPerSectionIndices, + TArray& OutWedgeMap, + const FRawMesh& RawMesh, + const FOverlappingCorners& OverlappingCorners, + const TMap& MaterialToSectionMapping, + float ComparisonThreshold, + FVector BuildScale, + int32 ImportVersion + ) = 0; + + /** + * Builds a static mesh using the provided source models and the LOD groups settings, and replaces + * the RawMeshes with the reduced meshes. Does not modify renderable data. + * @returns true if the meshes were built successfully. + */ + // virtual bool GenerateStaticMeshLODs( + // UStaticMesh* StaticMesh, + // const class FStaticMeshLODGroup& LODGroup + // ) = 0; + + /** Builds a signed distance field volume for the given LODModel. */ + virtual void GenerateSignedDistanceFieldVolumeData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + float DistanceFieldResolutionScale, + bool bGenerateAsIfTwoSided, + class FDistanceFieldVolumeData& OutData) = 0; + + virtual bool GenerateCardRepresentationData( + FString MeshName, + const FSourceMeshDataForDerivedDataTask& SourceMeshData, + const FStaticMeshLODResources& LODModel, + class FQueuedThreadPool& ThreadPool, + const TArray& MaterialBlendModes, + const FBoxSphereBounds& Bounds, + const class FDistanceFieldVolumeData* DistanceFieldVolumeData, + bool bGenerateAsIfTwoSided, + class FCardRepresentationData& OutData) = 0; + + /** Helper structure for skeletal mesh import options */ + struct MeshBuildOptions + { + MeshBuildOptions() + : bRemoveDegenerateTriangles(false) + , bComputeNormals(true) + , bComputeTangents(true) + , bUseMikkTSpace(false) + , bComputeWeightedNormals(false) + { + } + + bool bRemoveDegenerateTriangles; + bool bComputeNormals; + bool bComputeTangents; + bool bUseMikkTSpace; + bool bComputeWeightedNormals; + FOverlappingThresholds OverlappingThresholds; + + void FillOptions(const FSkeletalMeshBuildSettings& SkeletalMeshBuildSettings) + { + OverlappingThresholds.ThresholdPosition = SkeletalMeshBuildSettings.ThresholdPosition; + OverlappingThresholds.ThresholdTangentNormal = SkeletalMeshBuildSettings.ThresholdTangentNormal; + OverlappingThresholds.ThresholdUV = SkeletalMeshBuildSettings.ThresholdUV; + OverlappingThresholds.MorphThresholdPosition = SkeletalMeshBuildSettings.MorphThresholdPosition; + bComputeNormals = SkeletalMeshBuildSettings.bRecomputeNormals; + bComputeTangents = SkeletalMeshBuildSettings.bRecomputeTangents; + bUseMikkTSpace = SkeletalMeshBuildSettings.bUseMikkTSpace; + bComputeWeightedNormals = SkeletalMeshBuildSettings.bComputeWeightedNormals; + bRemoveDegenerateTriangles = SkeletalMeshBuildSettings.bRemoveDegenerates; + } + }; + + // /** + // * Create all render specific data for a skeletal mesh LOD model + // * @returns true if the mesh was built successfully. + // */ + // virtual bool BuildSkeletalMesh( + // FSkeletalMeshLODModel& LODModel, + // const FString& SkeletalMeshName, + // const FReferenceSkeleton& RefSkeleton, + // const TArray& Influences, + // const TArray& Wedges, + // const TArray& Faces, + // const TArray& Points, + // const TArray& PointToOriginalMap, + // const MeshBuildOptions& BuildOptions = MeshBuildOptions(), + // TArray * OutWarningMessages = NULL, + // TArray * OutWarningNames = NULL + // ) = 0; + + /** Cache optimize the index buffer. */ + virtual void CacheOptimizeIndexBuffer(TArray& Indices) = 0; + + /** Cache optimize the index buffer. */ + virtual void CacheOptimizeIndexBuffer(TArray& Indices) = 0; + + /** Build adjacency information for the skeletal mesh used for tessellation. */ + virtual void BuildSkeletalAdjacencyIndexBuffer( + const TArray& VertexBuffer, + const uint32 TexCoordCount, + const TArray& Indices, + TArray& OutPnAenIndices + ) = 0; + + /** + * Calculate The tangent, bi normal and normal for the triangle define by the tree SoftSkinVertex. + * + * @note The function will always fill properly the OutTangents array with 3 FVector. If the triangle is degenerated the OutTangent will contain zeroed vectors. + * + * @param VertexA - First triangle vertex. + * @param VertexB - Second triangle vertex. + * @param VertexC - Third triangle vertex. + * @param OutTangents - The function allocate the TArray with 3 FVector, to represent the triangle tangent, bi normal and normal. + * @param CompareThreshold - The threshold use to compare a tangent vector with zero. + */ + virtual void CalculateTriangleTangent(const FSoftSkinVertex& VertexA, const FSoftSkinVertex& VertexB, const FSoftSkinVertex& VertexC, TArray& OutTangents, float CompareThreshold) = 0; + + /** + * Calculate the verts associated weighted to each bone of the skeleton. + * The vertices returned are in the local space of the bone. + * + * @param SkeletalMesh The target skeletal mesh. + * @param Infos The output array of vertices associated with each bone. + * @param bOnlyDominant Controls whether a vertex is added to the info for a bone if it is most controlled by that bone, or if that bone has ANY influence on that vert. + */ + virtual void CalcBoneVertInfos( USkeletalMesh* SkeletalMesh, TArray& Infos, bool bOnlyDominant) = 0; + + /** + * Convert a set of mesh components in their current pose to a static mesh. + * @param InMeshComponents The mesh components we want to convert + * @param InRootTransform The transform of the root of the mesh we want to output + * @param InPackageName The package name to create the static mesh in. If this is empty then a dialog will be displayed to pick the mesh. + * @return a new static mesh (specified by the user) + */ + virtual UStaticMesh* ConvertMeshesToStaticMesh(const TArray& InMeshComponents, const FTransform& InRootTransform = FTransform::Identity, const FString& InPackageName = FString()) = 0; + + /** + * Calculates UV coordinates bounds for the given Skeletal Mesh + * + * @param InRawMesh - Skeletal Mesh to calculate the bounds for + * @param OutBounds - Out texture bounds (min-max) + */ + //virtual void CalculateTextureCoordinateBoundsForSkeletalMesh(const FSkeletalMeshLODModel& LODModel, TArray& OutBounds) const = 0; + + /** Calculates (new) non-overlapping UV coordinates for the given Skeletal Mesh + * + * @param LODModel - Skeletal Mesh to generate UV coordinates for + * @param TextureResolution - Texture resolution to take into account while generating the UVs + * @param OutTexCoords - New set of UV coordinates + * @return bool - whether or not generating the UVs succeeded + */ + //virtual bool GenerateUniqueUVsForSkeletalMesh(const FSkeletalMeshLODModel& LODModel, int32 TextureResolution, TArray& OutTexCoords) const = 0; + + /** + * Remove Bones based on LODInfo setting + * + * @param SkeletalMesh Mesh that needs bones to be removed + * @param LODIndex Desired LOD to remove bones [ 0 based ] + * @param BoneNamesToRemove List of bone names to remove + * + * @return true if success + */ + virtual bool RemoveBonesFromMesh(USkeletalMesh* SkeletalMesh, int32 LODIndex, const TArray* BoneNamesToRemove) const = 0; + + /** + * Calculates Tangents and Normals for a given set of vertex data + * + * @param InVertices Vertices that make up the mesh + * @param InIndices Indices for the Vertex array + * @param InUVs Texture coordinates (per-index based) + * @param InSmoothingGroupIndices Smoothing group index (per-face based) + * @param InTangentOptions Flags for Tangent calculation + * @param OutTangentX Array to hold calculated Tangents + * @param OutTangentY Array to hold calculated Bitangents + * @param OutNormals Array to hold calculated normals (if already contains normals will use those instead for the tangent calculation) + */ + virtual void CalculateTangents(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InSmoothingGroupIndices, const uint32 InTangentOptions, TArray& OutTangentX, TArray& OutTangentY, TArray& OutNormals) const = 0; + + /** + * Calculates MikkTSpace Tangents for a given set of vertex data with normals provided + * + * @param InVertices Vertices that make up the mesh + * @param InIndices Indices for the Vertex array + * @param InUVs Texture coordinates (per-index based) + * @param InNormals Normals used for the tangent calculation (must be normalized) + * @param bIgnoreDegenerateTriangles Flag for MikkTSpace to skip degenerate triangles fix-up path + * @param OutTangentX Array to hold calculated Tangents + * @param OutTangentY Array to hold calculated Bitangents + */ + virtual void CalculateMikkTSpaceTangents(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InNormals, bool bIgnoreDegenerateTriangles, TArray& OutTangentX, TArray& OutTangentY) const = 0; + + /** + * Calculates Normals for a given set of vertex data + * + * @param InVertices Vertices that make up the mesh + * @param InIndices Indices for the Vertex array + * @param InUVs Texture coordinates (per-index based) + * @param InSmoothingGroupIndices Smoothing group index (per-face based) + * @param InTangentOptions Flags for Tangent calculation + * @param OutNormals Array to hold calculated normals + */ + //virtual void CalculateNormals(const TArray& InVertices, const TArray& InIndices, const TArray& InUVs, const TArray& InSmoothingGroupIndices, const uint32 InTangentOptions, TArray& OutNormals) const = 0; + + /** + * Calculates the overlapping corners for a given set of vertex data + * + * @param InVertices Vertices that make up the mesh + * @param InIndices Indices for the Vertex array + * @param bIgnoreDegenerateTriangles Indicates if we should skip degenerate triangles + * @param OutOverlappingCorners Container to hold the overlapping corners. For a vertex, lists all the overlapping vertices. + */ + virtual void CalculateOverlappingCorners(const TArray& InVertices, const TArray& InIndices, bool bIgnoreDegenerateTriangles, FOverlappingCorners& OutOverlappingCorners) const = 0; + + virtual void RecomputeTangentsAndNormalsForRawMesh(bool bRecomputeTangents, bool bRecomputeNormals, const FMeshBuildSettings& InBuildSettings, FRawMesh &OutRawMesh) const = 0; + virtual void RecomputeTangentsAndNormalsForRawMesh(bool bRecomputeTangents, bool bRecomputeNormals, const FMeshBuildSettings& InBuildSettings, const FOverlappingCorners& InOverlappingCorners, FRawMesh &OutRawMesh) const = 0; + + virtual void FindOverlappingCorners(FOverlappingCorners& OutOverlappingCorners, const TArray& InVertices, const TArray& InIndices, float ComparisonThreshold) const = 0; + + /** Used to generate runtime skin weight data from Editor-only data */ + // virtual void GenerateRuntimeSkinWeightData(const FSkeletalMeshLODModel* ImportedModel, const TArray& InRawSkinWeights, struct FRuntimeSkinWeightProfileData& InOutSkinWeightOverrideData) const = 0; +}; diff --git a/Plugins/RuntimeGeometryUtils/Content/BlueMaterial.uasset b/Plugins/RuntimeGeometryUtils/Content/BlueMaterial.uasset new file mode 100644 index 0000000..f5e764b Binary files /dev/null and b/Plugins/RuntimeGeometryUtils/Content/BlueMaterial.uasset differ diff --git a/Plugins/RuntimeGeometryUtils/Content/RedMaterial.uasset b/Plugins/RuntimeGeometryUtils/Content/RedMaterial.uasset new file mode 100644 index 0000000..5430dde Binary files /dev/null and b/Plugins/RuntimeGeometryUtils/Content/RedMaterial.uasset differ diff --git a/Plugins/RuntimeGeometryUtils/Resources/Icon128.png b/Plugins/RuntimeGeometryUtils/Resources/Icon128.png new file mode 100644 index 0000000..1231d4a Binary files /dev/null and b/Plugins/RuntimeGeometryUtils/Resources/Icon128.png differ diff --git a/Plugins/RuntimeGeometryUtils/RuntimeGeometryUtils.uplugin b/Plugins/RuntimeGeometryUtils/RuntimeGeometryUtils.uplugin new file mode 100644 index 0000000..bf2f1d7 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/RuntimeGeometryUtils.uplugin @@ -0,0 +1,42 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "RuntimeGeometryUtils", + "Description": "", + "Category": "Other", + "CreatedBy": "Ryan Schmidt", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "RuntimeGeometryUtils", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "GeometryProcessing", + "Enabled": true + }, + { + "Name": "MeshModelingToolset", + "Enabled": true + }, + { + "Name": "ProceduralMeshComponent", + "Enabled": true + }, + { + "Name": "MeshUtilities2", + "Enabled": true + } + ] +} diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshBaseActor.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshBaseActor.cpp new file mode 100644 index 0000000..d8230b6 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshBaseActor.cpp @@ -0,0 +1,441 @@ +#include "DynamicMeshBaseActor.h" + +#include "Generators/SphereGenerator.h" +#include "Generators/GridBoxMeshGenerator.h" +#include "MeshQueries.h" +#include "DynamicMesh3.h" +#include "MeshNormals.h" +#include "MeshTransforms.h" +#include "MeshSimplification.h" +#include "Operations/MeshBoolean.h" +#include "Implicit/Solidify.h" + +#include "DynamicMeshOBJReader.h" +#include "MeshCardRepresentation2.h" + +// Sets default values +ADynamicMeshBaseActor::ADynamicMeshBaseActor() +{ + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; + + AccumulatedTime = 0; + MeshAABBTree.SetMesh(&SourceMesh); + + FastWinding = MakeUnique>(&MeshAABBTree, false); +} + +void ADynamicMeshBaseActor::PostLoad() +{ + Super::PostLoad(); + OnMeshGenerationSettingsModified(); +} + +void ADynamicMeshBaseActor::PostActorCreated() +{ + Super::PostActorCreated(); + OnMeshGenerationSettingsModified(); +} + + +// Called when the game starts or when spawned +void ADynamicMeshBaseActor::BeginPlay() +{ + Super::BeginPlay(); + AccumulatedTime = 0; + OnMeshGenerationSettingsModified(); +} + +// Called every frame +void ADynamicMeshBaseActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + AccumulatedTime += DeltaTime; + if (bRegenerateOnTick && SourceType == EDynamicMeshActorSourceType::Primitive) + { + OnMeshGenerationSettingsModified(); + } +} + + +#if WITH_EDITOR +void ADynamicMeshBaseActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + OnMeshGenerationSettingsModified(); +} +#endif + + +void ADynamicMeshBaseActor::EditMesh(TFunctionRef EditFunc) +{ + EditFunc(SourceMesh); + + // update spatial data structures + if (bEnableSpatialQueries || bEnableInsideQueries) + { + MeshAABBTree.Build(); + if (bEnableInsideQueries) + { + FastWinding->Build(); + } + } + + OnMeshEditedInternal(); +} + + +void ADynamicMeshBaseActor::GetMeshCopy(FDynamicMesh3& MeshOut) +{ + MeshOut = SourceMesh; +} + +const FDynamicMesh3& ADynamicMeshBaseActor::GetMeshRef() const +{ + return SourceMesh; +} + +void ADynamicMeshBaseActor::OnMeshEditedInternal() +{ + OnMeshModified.Broadcast(this); +} + + +void ADynamicMeshBaseActor::OnMeshGenerationSettingsModified() +{ + EditMesh([this](FDynamicMesh3& MeshToUpdate) + { + RegenerateSourceMesh(MeshToUpdate); + }); +} + + +void ADynamicMeshBaseActor::RegenerateSourceMesh(FDynamicMesh3& MeshOut) +{ + if (SourceType == EDynamicMeshActorSourceType::Primitive) + { + double UseRadius = (this->MinimumRadius + this->VariableRadius) + + (this->VariableRadius) * FMathd::Sin(PulseSpeed * AccumulatedTime); + + // generate new mesh + if (this->PrimitiveType == EDynamicMeshActorPrimitiveType::Sphere) + { + FSphereGenerator SphereGen; + SphereGen.NumPhi = SphereGen.NumTheta = FMath::Clamp(this->TessellationLevel, 3, 50); + SphereGen.Radius = UseRadius; + MeshOut.Copy(&SphereGen.Generate()); + } + else + { + FGridBoxMeshGenerator BoxGen; + int TessLevel = FMath::Clamp(this->TessellationLevel, 2, 50); + BoxGen.EdgeVertices = FIndex3i(TessLevel, TessLevel, TessLevel); + FVector3d BoxExtents = UseRadius * FVector3d::One(); + BoxExtents.Z *= BoxDepthRatio; + BoxGen.Box = FOrientedBox3d(FVector3d::Zero(), BoxExtents); + MeshOut.Copy(&BoxGen.Generate()); + } + } + else if (SourceType == EDynamicMeshActorSourceType::ImportedMesh) + { + FString UsePath = ImportPath; + if (FPaths::FileExists(UsePath) == false && FPaths::IsRelative(UsePath)) + { + UsePath = FPaths::ProjectContentDir() + ImportPath; + } + + MeshOut = FDynamicMesh3(); + if (! RTGUtils::ReadOBJMesh(UsePath, MeshOut, true, true, true, bReverseOrientation)) + { + UE_LOG(LogTemp, Warning, TEXT("Error reading mesh file %s"), *UsePath); + FSphereGenerator SphereGen; + SphereGen.NumPhi = SphereGen.NumTheta = 8; + SphereGen.Radius = this->MinimumRadius; + MeshOut.Copy(&SphereGen.Generate()); + } + + if (bCenterPivot) + { + MeshTransforms::Translate(MeshOut, -MeshOut.GetBounds().Center()); + } + + if (ImportScale != 1.0) + { + MeshTransforms::Scale(MeshOut, ImportScale * FVector3d::One(), FVector3d::Zero()); + } + } + + RecomputeNormals(MeshOut); +} + + +void ADynamicMeshBaseActor::RecomputeNormals(FDynamicMesh3& MeshOut) +{ + if (this->NormalsMode == EDynamicMeshActorNormalsMode::PerVertexNormals) + { + MeshOut.EnableAttributes(); + FMeshNormals::InitializeOverlayToPerVertexNormals(MeshOut.Attributes()->PrimaryNormals(), false); + } + else if (this->NormalsMode == EDynamicMeshActorNormalsMode::FaceNormals) + { + MeshOut.EnableAttributes(); + FMeshNormals::InitializeOverlayToPerTriangleNormals(MeshOut.Attributes()->PrimaryNormals()); + } +} + + +int ADynamicMeshBaseActor::GetTriangleCount() +{ + return SourceMesh.TriangleCount(); +} + + +float ADynamicMeshBaseActor::DistanceToPoint(FVector WorldPoint, FVector& NearestWorldPoint, int& NearestTriangle, FVector& TriBaryCoords) +{ + NearestWorldPoint = WorldPoint; + NearestTriangle = -1; + if (bEnableSpatialQueries == false) + { + return TNumericLimits::Max(); + } + + FTransform3d ActorToWorld(GetActorTransform()); + FVector3d LocalPoint = ActorToWorld.InverseTransformPosition((FVector3d)WorldPoint); + + double NearDistSqr; + NearestTriangle = MeshAABBTree.FindNearestTriangle(LocalPoint, NearDistSqr); + if (NearestTriangle < 0) + { + return TNumericLimits::Max(); + } + + FDistPoint3Triangle3d DistQuery = TMeshQueries::TriangleDistance(SourceMesh, NearestTriangle, LocalPoint); + NearestWorldPoint = (FVector)ActorToWorld.TransformPosition(DistQuery.ClosestTrianglePoint); + TriBaryCoords = (FVector)DistQuery.TriangleBaryCoords; + return (float)FMathd::Sqrt(NearDistSqr); +} + + +FVector ADynamicMeshBaseActor::NearestPoint(FVector WorldPoint) +{ + if (bEnableSpatialQueries) + { + FTransform3d ActorToWorld(GetActorTransform()); + FVector3d LocalPoint = ActorToWorld.InverseTransformPosition((FVector3d)WorldPoint); + return (FVector)ActorToWorld.TransformPosition(MeshAABBTree.FindNearestPoint(LocalPoint)); + } + return WorldPoint; +} + +bool ADynamicMeshBaseActor::ContainsPoint(FVector WorldPoint, float WindingThreshold) +{ + if (bEnableInsideQueries) + { + FTransform3d ActorToWorld(GetActorTransform()); + FVector3d LocalPoint = ActorToWorld.InverseTransformPosition((FVector3d)WorldPoint); + return FastWinding->IsInside(LocalPoint, WindingThreshold); + } + return false; +} + + +bool ADynamicMeshBaseActor::IntersectRay(FVector RayOrigin, FVector RayDirection, + FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, + float MaxDistance) +{ + if (bEnableSpatialQueries) + { + FTransform3d ActorToWorld(GetActorTransform()); + FVector3d WorldDirection(RayDirection); + WorldDirection.Normalize(); + FRay3d LocalRay(ActorToWorld.InverseTransformPosition((FVector3d)RayOrigin), + ActorToWorld.InverseTransformNormal(WorldDirection)); + IMeshSpatial::FQueryOptions QueryOptions; + if (MaxDistance > 0) + { + QueryOptions.MaxDistance = MaxDistance; + } + NearestTriangle = MeshAABBTree.FindNearestHitTriangle(LocalRay, QueryOptions); + if (SourceMesh.IsTriangle(NearestTriangle)) + { + FIntrRay3Triangle3d IntrQuery = TMeshQueries::TriangleIntersection(SourceMesh, NearestTriangle, LocalRay); + if (IntrQuery.IntersectionType == EIntersectionType::Point) + { + HitDistance = IntrQuery.RayParameter; + WorldHitPoint = (FVector)ActorToWorld.TransformPosition(LocalRay.PointAt(IntrQuery.RayParameter)); + TriBaryCoords = (FVector)IntrQuery.TriangleBaryCoords; + return true; + } + } + } + return false; +} + + +void ADynamicMeshBaseActor::SubtractMesh(ADynamicMeshBaseActor* OtherMeshActor) +{ + BooleanWithMesh(OtherMeshActor, EDynamicMeshActorBooleanOperation::Subtraction); +} + +void ADynamicMeshBaseActor::UnionWithMesh(ADynamicMeshBaseActor* OtherMeshActor) +{ + BooleanWithMesh(OtherMeshActor, EDynamicMeshActorBooleanOperation::Union); +} + +void ADynamicMeshBaseActor::IntersectWithMesh(ADynamicMeshBaseActor* OtherMeshActor) +{ + BooleanWithMesh(OtherMeshActor, EDynamicMeshActorBooleanOperation::Intersection); +} + + +void ADynamicMeshBaseActor::BooleanWithMesh(ADynamicMeshBaseActor* OtherMeshActor, EDynamicMeshActorBooleanOperation Operation) +{ + if (ensure(OtherMeshActor) == false) return; + + FTransform3d ActorToWorld(GetActorTransform()); + FTransform3d OtherToWorld(OtherMeshActor->GetActorTransform()); + + FDynamicMesh3 OtherMesh; + OtherMeshActor->GetMeshCopy(OtherMesh); + MeshTransforms::ApplyTransform(OtherMesh, OtherToWorld); + MeshTransforms::ApplyTransformInverse(OtherMesh, ActorToWorld); + + EditMesh([&](FDynamicMesh3& MeshToUpdate) + { + FDynamicMesh3 ResultMesh; + + FMeshBoolean::EBooleanOp ApplyOp = FMeshBoolean::EBooleanOp::Union; + switch (Operation) + { + default: + break; + case EDynamicMeshActorBooleanOperation::Subtraction: + ApplyOp = FMeshBoolean::EBooleanOp::Difference; + break; + case EDynamicMeshActorBooleanOperation::Intersection: + ApplyOp = FMeshBoolean::EBooleanOp::Intersect; + break; + } + + FMeshBoolean Boolean( + &MeshToUpdate, FTransform3d::Identity(), + &OtherMesh, FTransform3d::Identity(), + &ResultMesh, + ApplyOp); + Boolean.bPutResultInInputSpace = true; + bool bOK = Boolean.Compute(); + + if (!bOK) + { + // fill holes + } + + RecomputeNormals(ResultMesh); + + MeshToUpdate = MoveTemp(ResultMesh); + }); +} + + +bool ADynamicMeshBaseActor::ImportMesh(FString Path, bool bFlipOrientation, bool bRecomputeNormals) +{ + FDynamicMesh3 ImportedMesh; + if (!RTGUtils::ReadOBJMesh(Path, ImportedMesh, true, true, true, bFlipOrientation)) + { + UE_LOG(LogTemp, Warning, TEXT("Error reading mesh file %s"), *Path); + return false; + } + + if (bRecomputeNormals) + { + RecomputeNormals(ImportedMesh); + } + + EditMesh([&](FDynamicMesh3& MeshToUpdate) + { + MeshToUpdate = MoveTemp(ImportedMesh); + }); + + return true; +} + + +void ADynamicMeshBaseActor::CopyFromMesh(ADynamicMeshBaseActor* OtherMesh, bool bRecomputeNormals) +{ + if (! ensure(OtherMesh)) return; + + // the part where we generate a new mesh + FDynamicMesh3 TmpMesh; + OtherMesh->GetMeshCopy(TmpMesh); + + // apply our normals setting + if (bRecomputeNormals) + { + RecomputeNormals(TmpMesh); + } + + // update the mesh + EditMesh([&](FDynamicMesh3& MeshToUpdate) + { + MeshToUpdate = MoveTemp(TmpMesh); + }); +} + + +void ADynamicMeshBaseActor::SolidifyMesh(int VoxelResolution, float WindingThreshold) +{ + if (MeshAABBTree.IsValid() == false) + { + MeshAABBTree.Build(); + } + if (FastWinding->IsBuilt() == false) + { + FastWinding->Build(); + } + + // ugh workaround for bug + FDynamicMesh3 CompactMesh; + CompactMesh.CompactCopy(SourceMesh, false, false, false, false); + FDynamicMeshAABBTree3 AABBTree(&CompactMesh, true); + TFastWindingTree Winding(&AABBTree, true); + + double ExtendBounds = 2.0; + //TImplicitSolidify SolidifyCalc(&SourceMesh, &MeshAABBTree, FastWinding.Get()); + //SolidifyCalc.SetCellSizeAndExtendBounds(MeshAABBTree.GetBoundingBox(), ExtendBounds, VoxelResolution); + TImplicitSolidify SolidifyCalc(&CompactMesh, &AABBTree, &Winding); + SolidifyCalc.SetCellSizeAndExtendBounds(AABBTree.GetBoundingBox(), ExtendBounds, VoxelResolution); + SolidifyCalc.WindingThreshold = WindingThreshold; + SolidifyCalc.SurfaceSearchSteps = 5; + SolidifyCalc.bSolidAtBoundaries = true; + SolidifyCalc.ExtendBounds = ExtendBounds; + FDynamicMesh3 SolidMesh(&SolidifyCalc.Generate()); + + SolidMesh.EnableAttributes(); + RecomputeNormals(SolidMesh); + + EditMesh([&](FDynamicMesh3& MeshToUpdate) + { + MeshToUpdate = MoveTemp(SolidMesh); + }); +} + +void ADynamicMeshBaseActor::SimplifyMeshToTriCount(int32 TargetTriangleCount) +{ + TargetTriangleCount = FMath::Max(1, TargetTriangleCount); + if (TargetTriangleCount >= SourceMesh.TriangleCount()) return; + + // make compacted copy because it seems to change the results? + FDynamicMesh3 SimplifyMesh; + SimplifyMesh.CompactCopy(SourceMesh, false, false, false, false); + SimplifyMesh.EnableTriangleGroups(); // workaround for failing check() + FQEMSimplification Simplifier(&SimplifyMesh); + Simplifier.SimplifyToTriangleCount(TargetTriangleCount); + SimplifyMesh.EnableAttributes(); + RecomputeNormals(SimplifyMesh); + + EditMesh([&](FDynamicMesh3& MeshToUpdate) + { + MeshToUpdate.CompactCopy(SimplifyMesh); + }); +} diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJReader.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJReader.cpp new file mode 100644 index 0000000..c8a608b --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJReader.cpp @@ -0,0 +1,141 @@ +#include "DynamicMeshOBJReader.h" +#include "DynamicMeshAttributeSet.h" +#include "tinyobj/tiny_obj_loader.h" + + +bool RTGUtils::ReadOBJMesh( + const FString& Path, + FDynamicMesh3& MeshOut, + bool bNormals, + bool bTexCoords, + bool bVertexColors, + bool bReverseOrientation) +{ + std::string inputfile(TCHAR_TO_UTF8(*Path)); + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string warn; + std::string err; + + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str()); + + if (!warn.empty()) + { + UE_LOG(LogTemp, Display, TEXT("%s"), warn.c_str()); + } + + if (!err.empty()) + { + UE_LOG(LogTemp, Display, TEXT("%s"), err.c_str()); + } + + if (!ret) + { + return false; + } + + // append vertices + for (size_t vi = 0; vi < attrib.vertices.size() / 3; ++vi) + { + tinyobj::real_t vx = attrib.vertices[3 * vi + 0]; + tinyobj::real_t vy = attrib.vertices[3 * vi + 1]; + tinyobj::real_t vz = attrib.vertices[3 * vi + 2]; + + MeshOut.AppendVertex(FVector3d(vx, vy, vz)); + } + + + if (bVertexColors) + { + MeshOut.EnableVertexColors(FVector3f::Zero()); + for (size_t vi = 0; vi < attrib.vertices.size() / 3; ++vi) + { + tinyobj::real_t r = attrib.colors[3 * vi + 0]; + tinyobj::real_t g = attrib.colors[3 * vi + 1]; + tinyobj::real_t b = attrib.colors[3 * vi + 2]; + + MeshOut.SetVertexColor(vi, FVector3f((float)r, (float)g, (float)b)); + } + } + + if (bNormals || bTexCoords) + { + MeshOut.EnableAttributes(); + } + FDynamicMeshNormalOverlay* Normals = (bNormals) ? MeshOut.Attributes()->PrimaryNormals() : nullptr; + FDynamicMeshUVOverlay* UVs = (bTexCoords) ? MeshOut.Attributes()->PrimaryUV() : nullptr; + if (Normals) + { + for (size_t ni = 0; ni < attrib.normals.size() / 3; ++ni) + { + tinyobj::real_t nx = attrib.normals[3 * ni + 0]; + tinyobj::real_t ny = attrib.normals[3 * ni + 1]; + tinyobj::real_t nz = attrib.normals[3 * ni + 2]; + + Normals->AppendElement(FVector3f((float)nx, (float)ny, (float)nz)); + } + } + if (UVs) + { + for (size_t ti = 0; ti < attrib.texcoords.size() / 2; ++ti) + { + tinyobj::real_t tx = attrib.texcoords[2 * ti + 0]; + tinyobj::real_t ty = attrib.texcoords[2 * ti + 1]; + + UVs->AppendElement(FVector2f((float)tx, (float)ty)); + } + } + + // append faces as triangles + for (size_t s = 0; s < shapes.size(); s++) + { + // Loop over shapes + + size_t index_offset = 0; + for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) + { + // Loop over faces(polygon) + int fv = shapes[s].mesh.num_face_vertices[f]; + + TArray Triangles; + for (size_t v = 1; v < fv - 1; v++) + { + Triangles.Add(FIndex3i(0, v, v + 1)); + } + + int32 NumTris = Triangles.Num(); + for (int32 ti = 0; ti < NumTris; ++ti) + { + FIndex3i TriVerts = Triangles[ti]; + tinyobj::index_t idx0 = shapes[s].mesh.indices[index_offset + TriVerts.A]; + tinyobj::index_t idx1 = shapes[s].mesh.indices[index_offset + TriVerts.B]; + tinyobj::index_t idx2 = shapes[s].mesh.indices[index_offset + TriVerts.C]; + + int32 tid = MeshOut.AppendTriangle(idx0.vertex_index, idx1.vertex_index, idx2.vertex_index); + + if (Normals && Normals->IsElement(idx0.normal_index) && Normals->IsElement(idx1.normal_index) && Normals->IsElement(idx2.normal_index)) + { + Normals->SetTriangle(tid, FIndex3i(idx0.normal_index, idx1.normal_index, idx2.normal_index)); + } + if (UVs && UVs->IsElement(idx0.texcoord_index) && UVs->IsElement(idx1.texcoord_index) && UVs->IsElement(idx2.texcoord_index)) + { + UVs->SetTriangle(tid, FIndex3i(idx0.texcoord_index, idx1.texcoord_index, idx2.texcoord_index)); + } + } + + index_offset += fv; + + // per-face material + //shapes[s].mesh.material_ids[f]; + } + } + + if (bReverseOrientation) + { + MeshOut.ReverseOrientation(); + } + + return true; +} diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJWriter.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJWriter.cpp new file mode 100644 index 0000000..bc3a82a --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJWriter.cpp @@ -0,0 +1,191 @@ +#include "DynamicMeshOBJWriter.h" +#include "DynamicMeshAttributeSet.h" +#include "DynamicMeshEditor.h" + +#include + + + +class FDynamicMeshOBJWriter +{ +public: + + std::ofstream FileOut; + + TFunction OpenFile = [this](const FString& Path) { FileOut.open(*Path, std::ofstream::out | std::ofstream::trunc); return !!FileOut; }; + TFunction CloseFile = [this]() { FileOut.close(); }; + TFunction WriteLine = [this](const TCHAR* Line) { FileOut << TCHAR_TO_ANSI(Line) << std::endl; }; + + bool Write(const char* OutputPath, const FDynamicMesh3& Mesh) + { + if (!OpenFile(OutputPath)) + { + return false; + } + + int32 NumVertices = Mesh.VertexCount(); + for (int32 vi = 0; vi < NumVertices; ++vi) + { + FVector3d Pos = Mesh.GetVertex(vi); + WriteLine(*FString::Printf(TEXT("v %f %f %f"), Pos.X, Pos.Y, Pos.Z)); + } + + int32 NumUVs = 0; + const FDynamicMeshUVOverlay* UVs = nullptr; + if (Mesh.Attributes() && Mesh.Attributes()->PrimaryUV()) + { + UVs = Mesh.Attributes()->PrimaryUV(); + NumUVs = UVs->ElementCount(); + for (int32 ui = 0; ui < NumUVs; ++ui) + { + FVector2f UV = UVs->GetElement(ui); + WriteLine(*FString::Printf(TEXT("vt %f %f"), UV.X, UV.Y)); + } + } + + int32 NumNormals = 0; + const FDynamicMeshNormalOverlay* Normals = nullptr; + if (Mesh.Attributes() && Mesh.Attributes()->PrimaryNormals()) + { + Normals = Mesh.Attributes()->PrimaryNormals(); + NumNormals = Normals->ElementCount(); + for (int32 ni = 0; ni < NumNormals; ++ni) + { + FVector3f Normal = Normals->GetElement(ni); + WriteLine(*FString::Printf(TEXT("vn %f %f %f"), Normal.X, Normal.Y, Normal.Z)); + } + } + + struct FMeshTri + { + int32 Index; + int32 Group; + }; + TSet AllGroupIDs; + + TArray Triangles; + + int32 NumTriangles = Mesh.TriangleCount(); + for (int32 ti = 0; ti < NumTriangles; ++ti) + { + if (Mesh.IsTriangle(ti)) + { + int32 GroupID = Mesh.GetTriangleGroup(ti); + AllGroupIDs.Add(GroupID); + Triangles.Add({ ti, GroupID }); + } + } + bool bHaveGroups = AllGroupIDs.Num() > 1; + + Triangles.StableSort([](const FMeshTri& Tri0, const FMeshTri& Tri1) + { + return Tri0.Group < Tri1.Group; + }); + + + int32 CurGroupID = -99999; + for (FMeshTri MeshTri : Triangles) + { + if (bHaveGroups && MeshTri.Group != CurGroupID) + { + WriteLine(*FString::Printf(TEXT("g %d"), MeshTri.Group)); + CurGroupID = MeshTri.Group; + } + + int32 ti = MeshTri.Index; + + FIndex3i TriVertices = Mesh.GetTriangle(ti); + FIndex3i TriUVs = (NumUVs > 0) ? UVs->GetTriangle(ti) : FIndex3i::Invalid(); + FIndex3i TriNormals = (NumNormals > 0) ? Normals->GetTriangle(ti) : FIndex3i::Invalid(); + + bool bHaveUV = (NumUVs != 0) && UVs->IsSetTriangle(ti); + bool bHaveNormal = (NumNormals != 0) && Normals->IsSetTriangle(ti); + + if (bHaveUV && bHaveNormal) + { + WriteLine(*FString::Printf(TEXT("f %d/%d/%d %d/%d/%d %d/%d/%d"), + TriVertices.A + 1, TriUVs.A + 1, TriNormals.A + 1, + TriVertices.B + 1, TriUVs.B + 1, TriNormals.B + 1, + TriVertices.C + 1, TriUVs.C + 1, TriNormals.C + 1)); + } + else if (bHaveUV) + { + WriteLine(*FString::Printf(TEXT("f %d/%d %d/%d %d/%d"), + TriVertices.A + 1, TriUVs.A + 1, TriVertices.B + 1, TriUVs.B + 1, TriVertices.C + 1, TriUVs.C + 1)); + } + else if (bHaveNormal) + { + WriteLine(*FString::Printf(TEXT("f %d//%d %d//%d %d//%d"), + TriVertices.A + 1, TriNormals.A + 1, TriVertices.B + 1, TriNormals.B + 1, TriVertices.C + 1, TriNormals.C + 1)); + } + else + { + WriteLine(*FString::Printf(TEXT("f %d %d %d"), TriVertices.A + 1, TriVertices.B + 1, TriVertices.C + 1)); + } + } + + CloseFile(); + + return true; + } +}; + + + +bool RTGUtils::WriteOBJMesh( + const FString& OutputPath, + const FDynamicMesh3& Mesh, + bool bReverseOrientation) +{ + const FDynamicMesh3* WriteMesh = &Mesh; + + FDynamicMesh3 CopyMesh; + if (bReverseOrientation) + { + CopyMesh = Mesh; + CopyMesh.ReverseOrientation(); + WriteMesh = &CopyMesh; + } + + FDynamicMeshOBJWriter Writer; + std::string OutputFilePath(TCHAR_TO_UTF8(*OutputPath)); + return Writer.Write(OutputFilePath.c_str(), *WriteMesh); +} + + + +bool RTGUtils::WriteOBJMeshes( + const FString& OutputPath, + const TArray& Meshes, + bool bReverseOrientation) +{ + FDynamicMesh3 CombinedMesh; + FDynamicMeshEditor Editor(&CombinedMesh); + + for (const FDynamicMesh3& Mesh : Meshes) + { + if (Mesh.HasTriangleGroups()) + { + CombinedMesh.EnableTriangleGroups(); + } + if (Mesh.HasAttributes()) + { + CombinedMesh.EnableAttributes(); + } + + FMeshIndexMappings Mappings; + Editor.AppendMesh(&Mesh, Mappings); + } + + if (bReverseOrientation) + { + CombinedMesh.ReverseOrientation(); + } + + FDynamicMeshOBJWriter Writer; + + std::string OutputFilePath(TCHAR_TO_UTF8(*OutputPath)); + return Writer.Write(OutputFilePath.c_str(), CombinedMesh); +} + + diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicPMCActor.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicPMCActor.cpp new file mode 100644 index 0000000..46c1def --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicPMCActor.cpp @@ -0,0 +1,55 @@ +#include "DynamicPMCActor.h" +#include "MeshComponentRuntimeUtils.h" +#include "DynamicMesh3.h" + + +// Sets default values +ADynamicPMCActor::ADynamicPMCActor() +{ + MeshComponent = CreateDefaultSubobject(TEXT("Mesh"), false); + SetRootComponent(MeshComponent); +} + +// Called when the game starts or when spawned +void ADynamicPMCActor::BeginPlay() +{ + Super::BeginPlay(); +} + +// Called every frame +void ADynamicPMCActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); +} + + +void ADynamicPMCActor::OnMeshEditedInternal() +{ + UpdatePMCMesh(); + Super::OnMeshEditedInternal(); +} + +void ADynamicPMCActor::UpdatePMCMesh() +{ + if (MeshComponent) + { + bool bUseFaceNormals = (this->NormalsMode == EDynamicMeshActorNormalsMode::FaceNormals); + bool bUseUV0 = true; + bool bUseVertexColors = false; + + bool bGenerateSectionCollision = false; + if (this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimple + || this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync) + { + bGenerateSectionCollision = true; + MeshComponent->bUseAsyncCooking = (this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync); + MeshComponent->bUseComplexAsSimpleCollision = true; + } + + RTGUtils::UpdatePMCFromDynamicMesh_SplitTriangles(MeshComponent, &SourceMesh, bUseFaceNormals, bUseUV0, bUseVertexColors, bGenerateSectionCollision); + + // update material on new section + UMaterialInterface* UseMaterial = (this->Material != nullptr) ? this->Material : UMaterial::GetDefaultMaterial(MD_Surface); + MeshComponent->SetMaterial(0, UseMaterial); + } +} \ No newline at end of file diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSDMCActor.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSDMCActor.cpp new file mode 100644 index 0000000..9f0f2cc --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSDMCActor.cpp @@ -0,0 +1,45 @@ +#include "DynamicSDMCActor.h" +#include "DynamicMesh3.h" +#include "MeshUtilities2/Public/DistanceFieldAtlas2.h" + + +// Sets default values +ADynamicSDMCActor::ADynamicSDMCActor() +{ + MeshComponent = CreateDefaultSubobject(TEXT("MeshComponent"), false); + SetRootComponent(MeshComponent); +} + +// Called when the game starts or when spawned +void ADynamicSDMCActor::BeginPlay() +{ + Super::BeginPlay(); +} + + +// Called every frame +void ADynamicSDMCActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); +} + + + +void ADynamicSDMCActor::OnMeshEditedInternal() +{ + UpdateSDMCMesh(); + Super::OnMeshEditedInternal(); +} + +void ADynamicSDMCActor::UpdateSDMCMesh() +{ + if (MeshComponent) + { + *(MeshComponent->GetMesh()) = SourceMesh; + MeshComponent->NotifyMeshUpdated(); + + // update material on new section + UMaterialInterface* UseMaterial = (this->Material != nullptr) ? this->Material : UMaterial::GetDefaultMaterial(MD_Surface); + MeshComponent->SetMaterial(0, UseMaterial); + } +} diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSMCActor.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSMCActor.cpp new file mode 100644 index 0000000..675929d --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSMCActor.cpp @@ -0,0 +1,63 @@ +#include "DynamicSMCActor.h" +#include "MeshComponentRuntimeUtils.h" + +// Sets default values +ADynamicSMCActor::ADynamicSMCActor() +{ + MeshComponent = CreateDefaultSubobject(TEXT("Mesh"), false); + SetRootComponent(MeshComponent); + StaticMesh = nullptr; +} + +// Called when the game starts or when spawned +void ADynamicSMCActor::BeginPlay() +{ + StaticMesh = nullptr; + Super::BeginPlay(); +} + +void ADynamicSMCActor::PostLoad() +{ + StaticMesh = nullptr; + Super::PostLoad(); +} + +void ADynamicSMCActor::PostActorCreated() +{ + StaticMesh = nullptr; + Super::PostActorCreated(); +} + +// Called every frame +void ADynamicSMCActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); +} + + +void ADynamicSMCActor::OnMeshEditedInternal() +{ + UpdateSMCMesh(); + Super::OnMeshEditedInternal(); +} + +void ADynamicSMCActor::UpdateSMCMesh() +{ + if (StaticMesh == nullptr) + { + StaticMesh = NewObject(); + MeshComponent->SetStaticMesh(StaticMesh); + // add one material slot + StaticMesh->GetStaticMaterials().Add(FStaticMaterial()); + } + + if (MeshComponent) + { + auto MTL = LoadObject(nullptr,TEXT("Material'/Game/baise.baise'")); + // update material on new section + UMaterialInterface* UseMaterial = (this->Material != nullptr) ? this->Material : MTL; + MeshComponent->SetMaterial(0, UseMaterial); + + RTGUtils::UpdateStaticMeshFromDynamicMesh(StaticMesh, &SourceMesh); + } +} diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/MeshComponentRuntimeUtils.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/MeshComponentRuntimeUtils.cpp new file mode 100644 index 0000000..755e942 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/MeshComponentRuntimeUtils.cpp @@ -0,0 +1,147 @@ +#include "MeshComponentRuntimeUtils.h" + +#include "DynamicMeshAttributeSet.h" +#include "MeshNormals.h" + +#include "DynamicMeshToMeshDescription.h" +#include "StaticMeshAttributes.h" +#include "MeshUtilities2/Public/DistanceFieldAtlas2.h" + +void RTGUtils::UpdateStaticMeshFromDynamicMesh( + UStaticMesh* StaticMesh, + const FDynamicMesh3* Mesh) +{ + FMeshDescription MeshDescription; + FStaticMeshAttributes StaticMeshAttributes(MeshDescription); + StaticMeshAttributes.Register(); + + FDynamicMeshToMeshDescription Converter; + Converter.Convert(Mesh, MeshDescription); + + // todo: vertex color support + + //UStaticMesh* StaticMesh = NewObject(Component); + //FName MaterialSlotName = StaticMesh->AddMaterial(MyMaterial); + + // Build the static mesh render data, one FMeshDescription* per LOD. + TArray MeshDescriptionPtrs; + MeshDescriptionPtrs.Emplace(&MeshDescription); + + UStaticMesh::FBuildMeshDescriptionsParams Params; + Params.bFastBuild = true; + StaticMesh->BuildFromMeshDescriptions(MeshDescriptionPtrs,Params); + + //在编辑器模式下,也使用自定义的距离场构建和MeshCard构建 + BuildMeshDistanceField(StaticMesh); +} + + +void RTGUtils::UpdatePMCFromDynamicMesh_SplitTriangles( + UProceduralMeshComponent* Component, + const FDynamicMesh3* Mesh, + bool bUseFaceNormals, + bool bInitializeUV0, + bool bInitializePerVertexColors, + bool bCreateCollision) +{ + Component->ClearAllMeshSections(); + + int32 NumTriangles = Mesh->TriangleCount(); + int32 NumVertices = NumTriangles * 3; + + TArray Vertices, Normals; + Vertices.SetNumUninitialized(NumVertices); + Normals.SetNumUninitialized(NumVertices); + + FMeshNormals PerVertexNormals(Mesh); + bool bUsePerVertexNormals = false; + const FDynamicMeshNormalOverlay* NormalOverlay = nullptr; + if (Mesh->HasAttributes() == false && bUseFaceNormals == false) + { + PerVertexNormals.ComputeVertexNormals(); + bUsePerVertexNormals = true; + } + else if (Mesh->HasAttributes()) + { + NormalOverlay = Mesh->Attributes()->PrimaryNormals(); + } + + const FDynamicMeshUVOverlay* UVOverlay = (Mesh->HasAttributes()) ? Mesh->Attributes()->PrimaryUV() : nullptr; + TArray UV0; + if (UVOverlay && bInitializeUV0) + { + UV0.SetNum(NumVertices); + } + + TArray VtxColors; + bool bUsePerVertexColors = false; + if (bInitializePerVertexColors && Mesh->HasVertexColors()) + { + VtxColors.SetNum(NumVertices); + bUsePerVertexColors = true; + } + + TArray Tangents; // not supporting this for now + + TArray Triangles; + Triangles.SetNumUninitialized(NumTriangles * 3); + + FVector3d Position[3]; + FVector3f Normal[3]; + FVector2f UV[3]; + int32 BufferIndex = 0; + for (int32 tid : Mesh->TriangleIndicesItr()) + { + int32 k = 3 * (BufferIndex++); + + FIndex3i TriVerts = Mesh->GetTriangle(tid); + + Mesh->GetTriVertices(tid, Position[0], Position[1], Position[2]); + Vertices[k] = (FVector)Position[0]; + Vertices[k + 1] = (FVector)Position[1]; + Vertices[k + 2] = (FVector)Position[2]; + + + if (bUsePerVertexNormals) + { + Normals[k] = (FVector)PerVertexNormals[TriVerts.A]; + Normals[k + 1] = (FVector)PerVertexNormals[TriVerts.B]; + Normals[k + 2] = (FVector)PerVertexNormals[TriVerts.C]; + } + else if (NormalOverlay != nullptr && bUseFaceNormals == false) + { + NormalOverlay->GetTriElements(tid, Normal[0], Normal[1], Normal[2]); + Normals[k] = (FVector)Normal[0]; + Normals[k + 1] = (FVector)Normal[1]; + Normals[k + 2] = (FVector)Normal[2]; + } + else + { + FVector3d TriNormal = Mesh->GetTriNormal(tid); + Normals[k] = (FVector)TriNormal; + Normals[k + 1] = (FVector)TriNormal; + Normals[k + 2] = (FVector)TriNormal; + } + + if (UVOverlay != nullptr && UVOverlay->IsSetTriangle(tid)) + { + UVOverlay->GetTriElements(tid, UV[0], UV[1], UV[2]); + UV0[k] = (FVector2D)UV[0]; + UV0[k + 1] = (FVector2D)UV[1]; + UV0[k + 2] = (FVector2D)UV[2]; + } + + if (bUsePerVertexColors) + { + VtxColors[k] = (FLinearColor)Mesh->GetVertexColor(TriVerts.A); + VtxColors[k + 1] = (FLinearColor)Mesh->GetVertexColor(TriVerts.B); + VtxColors[k + 2] = (FLinearColor)Mesh->GetVertexColor(TriVerts.C); + } + + Triangles[k] = k; + Triangles[k + 1] = k + 1; + Triangles[k + 2] = k + 2; + } + + Component->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UV0, VtxColors, Tangents, bCreateCollision); +} diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/RuntimeGeometryUtilsModule.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/RuntimeGeometryUtilsModule.cpp new file mode 100644 index 0000000..af66fab --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/RuntimeGeometryUtilsModule.cpp @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "RuntimeGeometryUtilsModule.h" + +#define LOCTEXT_NAMESPACE "FRuntimeGeometryUtilsModule" + +void FRuntimeGeometryUtilsModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FRuntimeGeometryUtilsModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRuntimeGeometryUtilsModule, RuntimeGeometryUtils) \ No newline at end of file diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/LICENSE b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/LICENSE new file mode 100644 index 0000000..3af18ab --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012-2019 Syoyo Fujita and many contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/README.md b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/README.md new file mode 100644 index 0000000..3a6f336 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/README.md @@ -0,0 +1,334 @@ +# tinyobjloader + +[![Build Status](https://travis-ci.org/tinyobjloader/tinyobjloader.svg?branch=master)](https://travis-ci.org/tinyobjloader/tinyobjloader) + +[![AZ Build Status](https://dev.azure.com/tinyobjloader/tinyobjloader/_apis/build/status/tinyobjloader.tinyobjloader?branchName=master)](https://dev.azure.com/tinyobjloader/tinyobjloader/_build/latest?definitionId=1&branchName=master) + +[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/m6wfkvket7gth8wn/branch/master?svg=true)](https://ci.appveyor.com/project/syoyo/tinyobjloader-6e4qf/branch/master) + +[![Coverage Status](https://coveralls.io/repos/github/syoyo/tinyobjloader/badge.svg?branch=master)](https://coveralls.io/github/syoyo/tinyobjloader?branch=master) + +[![AUR version](https://img.shields.io/aur/version/tinyobjloader?logo=arch-linux)](https://aur.archlinux.org/packages/tinyobjloader) + +[![Download](https://api.bintray.com/packages/conan/conan-center/tinyobjloader%3A_/images/download.svg)](https://bintray.com/conan/conan-center/tinyobjloader%3A_/_latestVersion) (not recommended) + +Tiny but powerful single file wavefront obj loader written in C++03. No dependency except for C++ STL. It can parse over 10M polygons with moderate memory and time. + +`tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) + +If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c . + +Notice! +------- + +We have released new version v1.0.0 on 20 Aug, 2016. +Old version is available as `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x + +## What's new + +* 19 Feb, 2020 : The repository has been moved to https://github.com/tinyobjloader/tinyobjloader ! +* 18 May, 2019 : Python binding!(See `python` folder. Also see https://pypi.org/project/tinyobjloader/) +* 14 Apr, 2019 : Bump version v2.0.0 rc0. New C++ API and python bindings!(1.x API still exists for backward compatibility) +* 20 Aug, 2016 : Bump version v1.0.0. New data structure and API! + +## Requirements + +* C++03 compiler + +### Old version + +Previous old version is available in `v0.9.x` branch. + +## Example + +![Rungholt](images/rungholt.jpg) + +tinyobjloader can successfully load 6M triangles Rungholt scene. +http://casual-effects.com/data/index.html + +![](images/sanmugel.png) + +* [examples/viewer/](examples/viewer) OpenGL .obj viewer +* [examples/callback_api/](examples/callback_api/) Callback API example +* [examples/voxelize/](examples/voxelize/) Voxelizer example + +## Use case + +TinyObjLoader is successfully used in ... + +### New version(v1.0.x) + +* Double precision support through `TINYOBJLOADER_USE_DOUBLE` thanks to noma +* Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models +* .obj viewer with Metal https://github.com/middlefeng/NuoModelViewer/tree/master +* Vulkan Cookbook https://github.com/PacktPublishing/Vulkan-Cookbook +* cudabox: CUDA Solid Voxelizer Engine https://github.com/gaspardzoss/cudavox +* Drake: A planning, control, and analysis toolbox for nonlinear dynamical systems https://github.com/RobotLocomotion/drake +* VFPR - a Vulkan Forward Plus Renderer : https://github.com/WindyDarian/Vulkan-Forward-Plus-Renderer +* glslViewer: https://github.com/patriciogonzalezvivo/glslViewer +* Lighthouse2: https://github.com/jbikker/lighthouse2 +* rayrender(an open source R package for raytracing scenes in created in R): https://github.com/tylermorganwall/rayrender +* liblava - A modern C++ and easy-to-use framework for the Vulkan API. [MIT]: https://github.com/liblava/liblava +* rtxON - Simple Vulkan raytracing tutorials https://github.com/iOrange/rtxON +* metal-ray-tracer - Writing ray-tracer using Metal Performance Shaders https://github.com/sergeyreznik/metal-ray-tracer https://sergeyreznik.github.io/metal-ray-tracer/index.html +* Your project here! (Letting us know via github issue is welcome!) + +### Old version(v0.9.x) + +* bullet3 https://github.com/erwincoumans/bullet3 +* pbrt-v2 https://github.com/mmp/pbrt-v2 +* OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 +* mallie https://lighttransport.github.io/mallie +* IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ +* Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf +* Awesome Bump http://awesomebump.besaba.com/about/ +* sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront +* pbrt-v3 https://github.com/mmp/pbrt-v3 +* cocos2d-x https://github.com/cocos2d/cocos2d-x/ +* Android Vulkan demo https://github.com/SaschaWillems/Vulkan +* voxelizer https://github.com/karimnaaji/voxelizer +* Probulator https://github.com/kayru/Probulator +* OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking +* FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK +* parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg +* Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl +* Point Based Global Illumination on modern GPU https://pbgi.wordpress.com/code-source/ +* Fast OBJ file importing and parsing in CUDA http://researchonline.jcu.edu.au/42515/1/2015.CVM.OBJCUDA.pdf +* Sorted Shading for Uni-Directional Pathtracing by Joshua Bainbridge https://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc15/02Josh/joshua_bainbridge_thesis.pdf +* GeeXLab http://www.geeks3d.com/hacklab/20160531/geexlab-0-12-0-0-released-for-windows/ + + +## Features + +* Group(parse multiple group name) +* Vertex + * Vertex color(as an extension: https://blender.stackexchange.com/questions/31997/how-can-i-get-vertex-painted-obj-files-to-import-into-blender) +* Texcoord +* Normal +* Material + * Unknown material attributes are returned as key-value(value is string) map. +* Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) +* PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr +* Callback API for custom loading. +* Double precision support(for HPC application). +* Smoothing group +* Python binding : See `python` folder. + * Precompiled binary(manylinux1-x86_64 only) is hosted at pypi https://pypi.org/project/tinyobjloader/) + +### Primitives + +* [x] face(`f`) +* [x] lines(`l`) +* [ ] points(`p`) +* [ ] curve +* [ ] 2D curve +* [ ] surface. +* [ ] Free form curve/surfaces + + +## TODO + +* [ ] Fix obj_sticker example. +* [ ] More unit test codes. +* [x] Texture options + +## License + +TinyObjLoader is licensed under MIT license. + +### Third party licenses. + +* pybind11 : BSD-style license. + +## Usage + +### Installation + +One option is to simply copy the header file into your project and to make sure that `TINYOBJLOADER_IMPLEMENTATION` is defined exactly once. + +Tinyobjlaoder is also available as a [conan package](https://bintray.com/conan/conan-center/tinyobjloader%3A_/_latestVersion). Conan integrates with many build systems and lets you avoid manual dependency installation. Their [documentation](https://docs.conan.io/en/latest/getting_started.html) is a great starting point. + +### Building tinyobjloader - Using vcpkg + +You can download and install tinyobjloader using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install tinyobjloader + +The tinyobjloader port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +### Data format + +`attrib_t` contains single and linear array of vertex data(position, normal and texcoord). + +``` +attrib_t::vertices => 3 floats per vertex + + v[0] v[1] v[2] v[3] v[n-1] + +-----------+-----------+-----------+-----------+ +-----------+ + | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | + +-----------+-----------+-----------+-----------+ +-----------+ + +attrib_t::normals => 3 floats per vertex + + n[0] n[1] n[2] n[3] n[n-1] + +-----------+-----------+-----------+-----------+ +-----------+ + | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | + +-----------+-----------+-----------+-----------+ +-----------+ + +attrib_t::texcoords => 2 floats per vertex + + t[0] t[1] t[2] t[3] t[n-1] + +-----------+-----------+-----------+-----------+ +-----------+ + | u | v | u | v | u | v | u | v | .... | u | v | + +-----------+-----------+-----------+-----------+ +-----------+ + +attrib_t::colors => 3 floats per vertex(vertex color. optional) + + c[0] c[1] c[2] c[3] c[n-1] + +-----------+-----------+-----------+-----------+ +-----------+ + | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | + +-----------+-----------+-----------+-----------+ +-----------+ + +``` + +Each `shape_t::mesh_t` does not contain vertex data but contains array index to `attrib_t`. +See `loader_example.cc` for more details. + + +``` + +mesh_t::indices => array of vertex indices. + + +----+----+----+----+----+----+----+----+----+----+ +--------+ + | i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 | ... | i(n-1) | + +----+----+----+----+----+----+----+----+----+----+ +--------+ + +Each index has an array index to attrib_t::vertices, attrib_t::normals and attrib_t::texcoords. + +mesh_t::num_face_vertices => array of the number of vertices per face(e.g. 3 = triangle, 4 = quad , 5 or more = N-gons). + + + +---+---+---+ +---+ + | 3 | 4 | 3 | ...... | 3 | + +---+---+---+ +---+ + | | | | + | | | +-----------------------------------------+ + | | | | + | | +------------------------------+ | + | | | | + | +------------------+ | | + | | | | + |/ |/ |/ |/ + + mesh_t::indices + + | face[0] | face[1] | face[2] | | face[n-1] | + +----+----+----+----+----+----+----+----+----+----+ +--------+--------+--------+ + | i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 | ... | i(n-3) | i(n-2) | i(n-1) | + +----+----+----+----+----+----+----+----+----+----+ +--------+--------+--------+ + +``` + +Note that when `triangulate` flag is true in `tinyobj::LoadObj()` argument, `num_face_vertices` are all filled with 3(triangle). + +### float data type + +TinyObjLoader now use `real_t` for floating point data type. +Default is `float(32bit)`. +You can enable `double(64bit)` precision by using `TINYOBJLOADER_USE_DOUBLE` define. + +#### Example code + +```c++ +#define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc +#include "tiny_obj_loader.h" + +std::string inputfile = "cornell_box.obj"; +tinyobj::attrib_t attrib; +std::vector shapes; +std::vector materials; + +std::string warn; +std::string err; + +bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str()); + +if (!warn.empty()) { + std::cout << warn << std::endl; +} + +if (!err.empty()) { + std::cerr << err << std::endl; +} + +if (!ret) { + exit(1); +} + +// Loop over shapes +for (size_t s = 0; s < shapes.size(); s++) { + // Loop over faces(polygon) + size_t index_offset = 0; + for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { + int fv = shapes[s].mesh.num_face_vertices[f]; + + // Loop over vertices in the face. + for (size_t v = 0; v < fv; v++) { + // access to vertex + tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; + tinyobj::real_t vx = attrib.vertices[3*idx.vertex_index+0]; + tinyobj::real_t vy = attrib.vertices[3*idx.vertex_index+1]; + tinyobj::real_t vz = attrib.vertices[3*idx.vertex_index+2]; + tinyobj::real_t nx = attrib.normals[3*idx.normal_index+0]; + tinyobj::real_t ny = attrib.normals[3*idx.normal_index+1]; + tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2]; + tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0]; + tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1]; + // Optional: vertex colors + // tinyobj::real_t red = attrib.colors[3*idx.vertex_index+0]; + // tinyobj::real_t green = attrib.colors[3*idx.vertex_index+1]; + // tinyobj::real_t blue = attrib.colors[3*idx.vertex_index+2]; + } + index_offset += fv; + + // per-face material + shapes[s].mesh.material_ids[f]; + } +} + +``` + +## Optimized loader + +Optimized multi-threaded .obj loader is available at `experimental/` directory. +If you want absolute performance to load .obj data, this optimized loader will fit your purpose. +Note that the optimized loader uses C++11 thread and it does less error checks but may work most .obj data. + +Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core m5 1.2GHz). + +* Rungholt scene(6M triangles) + * old version(v0.9.x): 15500 msecs. + * baseline(v1.0.x): 6800 msecs(2.3x faster than old version) + * optimised: 1500 msecs(10x faster than old version, 4.5x faster than baseline) + +## Python binding + +### CI + PyPI upload + +cibuildwheels + twine upload for each git tagging event is handled in Azure Pipeline. + +#### How to bump version(For developer) + +* Bump version in CMakeLists.txt +* Update version in `python/setup.py` +* Commit with tag name starging with `v`(e.g. `v2.1.0`) +* `git push --tags` + * cibuildwheels + pypi upload(through twine) will be automatically triggered in Azure Pipeline. + +## Tests + +Unit tests are provided in `tests` directory. See `tests/README.md` for details. diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.cpp b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.cpp new file mode 100644 index 0000000..e57d044 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.cpp @@ -0,0 +1,2 @@ +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.h new file mode 100644 index 0000000..edd18f5 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.h @@ -0,0 +1,3082 @@ +/* +The MIT License (MIT) + +Copyright (c) 2012-2018 Syoyo Fujita and many contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// +// version 2.0.0 : Add new object oriented API. 1.x API is still provided. +// * Support line primitive. +// * Support points primitive. +// * Support multiple search path for .mtl(v1 API). +// * Support vertex weight `vw`(as an tinyobj extension) +// version 1.4.0 : Modifed ParseTextureNameAndOption API +// version 1.3.1 : Make ParseTextureNameAndOption API public +// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) +// version 1.2.3 : Added color space extension('-colorspace') to tex opts. +// version 1.2.2 : Parse multiple group names. +// version 1.2.1 : Added initial support for line('l') primitive(PR #178) +// version 1.2.0 : Hardened implementation(#175) +// version 1.1.1 : Support smoothing groups(#162) +// version 1.1.0 : Support parsing vertex color(#144) +// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138) +// version 1.0.7 : Support multiple tex options(#126) +// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) +// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) +// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) +// version 1.0.3 : Support parsing texture options(#85) +// version 1.0.2 : Improve parsing speed by about a factor of 2 for large +// files(#105) +// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) +// version 1.0.0 : Change data structure. Change license from BSD to MIT. +// + +// +// Use this in *one* .cc +// #define TINYOBJLOADER_IMPLEMENTATION +// #include "tiny_obj_loader.h" +// + +#ifndef TINY_OBJ_LOADER_H_ +#define TINY_OBJ_LOADER_H_ + +#include +#include +#include + +namespace tinyobj { + +// TODO(syoyo): Better C++11 detection for older compiler +#if __cplusplus > 199711L +#define TINYOBJ_OVERRIDE override +#else +#define TINYOBJ_OVERRIDE +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + +#pragma clang diagnostic ignored "-Wpadded" + +#endif + +// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... +// +// -blendu on | off # set horizontal texture blending +// (default on) +// -blendv on | off # set vertical texture blending +// (default on) +// -boost real_value # boost mip-map sharpness +// -mm base_value gain_value # modify texture map values (default +// 0 1) +// # base_value = brightness, +// gain_value = contrast +// -o u [v [w]] # Origin offset (default +// 0 0 0) +// -s u [v [w]] # Scale (default +// 1 1 1) +// -t u [v [w]] # Turbulence (default +// 0 0 0) +// -texres resolution # texture resolution to create +// -clamp on | off # only render texels in the clamped +// 0-1 range (default off) +// # When unclamped, textures are +// repeated across a surface, +// # when clamped, only texels which +// fall within the 0-1 +// # range are rendered. +// -bm mult_value # bump multiplier (for bump maps +// only) +// +// -imfchan r | g | b | m | l | z # specifies which channel of the file +// is used to +// # create a scalar or bump texture. +// r:red, g:green, +// # b:blue, m:matte, l:luminance, +// z:z-depth.. +// # (the default for bump is 'l' and +// for decal is 'm') +// bump -imfchan r bumpmap.tga # says to use the red channel of +// bumpmap.tga as the bumpmap +// +// For reflection maps... +// +// -type sphere # specifies a sphere for a "refl" +// reflection map +// -type cube_top | cube_bottom | # when using a cube map, the texture +// file for each +// cube_front | cube_back | # side of the cube is specified +// separately +// cube_left | cube_right +// +// TinyObjLoader extension. +// +// -colorspace SPACE # Color space of the texture. e.g. +// 'sRGB` or 'linear' +// + +#ifdef TINYOBJLOADER_USE_DOUBLE +//#pragma message "using double" +typedef double real_t; +#else +//#pragma message "using float" +typedef float real_t; +#endif + +typedef enum { + TEXTURE_TYPE_NONE, // default + TEXTURE_TYPE_SPHERE, + TEXTURE_TYPE_CUBE_TOP, + TEXTURE_TYPE_CUBE_BOTTOM, + TEXTURE_TYPE_CUBE_FRONT, + TEXTURE_TYPE_CUBE_BACK, + TEXTURE_TYPE_CUBE_LEFT, + TEXTURE_TYPE_CUBE_RIGHT +} texture_type_t; + +struct texture_option_t { + texture_type_t type; // -type (default TEXTURE_TYPE_NONE) + real_t sharpness; // -boost (default 1.0?) + real_t brightness; // base_value in -mm option (default 0) + real_t contrast; // gain_value in -mm option (default 1) + real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) + real_t scale[3]; // -s u [v [w]] (default 1 1 1) + real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) + int texture_resolution; // -texres resolution (No default value in the spec. + // We'll use -1) + bool clamp; // -clamp (default false) + char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') + bool blendu; // -blendu (default on) + bool blendv; // -blendv (default on) + real_t bump_multiplier; // -bm (for bump maps only, default 1.0) + + // extension + std::string colorspace; // Explicitly specify color space of stored texel + // value. Usually `sRGB` or `linear` (default empty). +}; + +struct material_t { + std::string name; + + real_t ambient[3]; + real_t diffuse[3]; + real_t specular[3]; + real_t transmittance[3]; + real_t emission[3]; + real_t shininess; + real_t ior; // index of refraction + real_t dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int dummy; // Suppress padding warning. + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, map_Bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + std::string reflection_texname; // refl + + texture_option_t ambient_texopt; + texture_option_t diffuse_texopt; + texture_option_t specular_texopt; + texture_option_t specular_highlight_texopt; + texture_option_t bump_texopt; + texture_option_t displacement_texopt; + texture_option_t alpha_texopt; + texture_option_t reflection_texopt; + + // PBR extension + // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr + real_t roughness; // [0, 1] default 0 + real_t metallic; // [0, 1] default 0 + real_t sheen; // [0, 1] default 0 + real_t clearcoat_thickness; // [0, 1] default 0 + real_t clearcoat_roughness; // [0, 1] default 0 + real_t anisotropy; // aniso. [0, 1] default 0 + real_t anisotropy_rotation; // anisor. [0, 1] default 0 + real_t pad0; + std::string roughness_texname; // map_Pr + std::string metallic_texname; // map_Pm + std::string sheen_texname; // map_Ps + std::string emissive_texname; // map_Ke + std::string normal_texname; // norm. For normal mapping. + + texture_option_t roughness_texopt; + texture_option_t metallic_texopt; + texture_option_t sheen_texopt; + texture_option_t emissive_texopt; + texture_option_t normal_texopt; + + int pad2; + + std::map unknown_parameter; + +#ifdef TINY_OBJ_LOADER_PYTHON_BINDING + // For pybind11 + std::array GetDiffuse() { + std::array values; + values[0] = double(diffuse[0]); + values[1] = double(diffuse[1]); + values[2] = double(diffuse[2]); + + return values; + } + + std::array GetSpecular() { + std::array values; + values[0] = double(specular[0]); + values[1] = double(specular[1]); + values[2] = double(specular[2]); + + return values; + } + + std::array GetTransmittance() { + std::array values; + values[0] = double(transmittance[0]); + values[1] = double(transmittance[1]); + values[2] = double(transmittance[2]); + + return values; + } + + std::array GetEmission() { + std::array values; + values[0] = double(emission[0]); + values[1] = double(emission[1]); + values[2] = double(emission[2]); + + return values; + } + + std::array GetAmbient() { + std::array values; + values[0] = double(ambient[0]); + values[1] = double(ambient[1]); + values[2] = double(ambient[2]); + + return values; + } + + void SetDiffuse(std::array &a) { + diffuse[0] = real_t(a[0]); + diffuse[1] = real_t(a[1]); + diffuse[2] = real_t(a[2]); + } + + void SetAmbient(std::array &a) { + ambient[0] = real_t(a[0]); + ambient[1] = real_t(a[1]); + ambient[2] = real_t(a[2]); + } + + void SetSpecular(std::array &a) { + specular[0] = real_t(a[0]); + specular[1] = real_t(a[1]); + specular[2] = real_t(a[2]); + } + + void SetTransmittance(std::array &a) { + transmittance[0] = real_t(a[0]); + transmittance[1] = real_t(a[1]); + transmittance[2] = real_t(a[2]); + } + + std::string GetCustomParameter(const std::string &key) { + std::map::const_iterator it = + unknown_parameter.find(key); + + if (it != unknown_parameter.end()) { + return it->second; + } + return std::string(); + } + +#endif +}; + +struct tag_t { + std::string name; + + std::vector intValues; + std::vector floatValues; + std::vector stringValues; +}; + +struct joint_and_weight_t { + int joint_id; + real_t weight; +}; + +struct skin_weight_t { + int vertex_id; // Corresponding vertex index in `attrib_t::vertices`. + // Compared to `index_t`, this index must be positive and + // start with 0(does not allow relative indexing) + std::vector weightValues; +}; + +// Index struct to support different indices for vtx/normal/texcoord. +// -1 means not used. +struct index_t { + int vertex_index; + int normal_index; + int texcoord_index; +}; + +struct mesh_t { + std::vector indices; + std::vector + num_face_vertices; // The number of vertices per + // face. 3 = triangle, 4 = quad, + // ... Up to 255 vertices per face. + std::vector material_ids; // per-face material ID + std::vector smoothing_group_ids; // per-face smoothing group + // ID(0 = off. positive value + // = group id) + std::vector tags; // SubD tag +}; + +// struct path_t { +// std::vector indices; // pairs of indices for lines +//}; + +struct lines_t { + // Linear flattened indices. + std::vector indices; // indices for vertices(poly lines) + std::vector num_line_vertices; // The number of vertices per line. +}; + +struct points_t { + std::vector indices; // indices for points +}; + +struct shape_t { + std::string name; + mesh_t mesh; + lines_t lines; + points_t points; +}; + +// Vertex attributes +struct attrib_t { + std::vector vertices; // 'v'(xyz) + + // For backward compatibility, we store vertex weight in separate array. + std::vector vertex_weights; // 'v'(w) + std::vector normals; // 'vn' + std::vector texcoords; // 'vt'(uv) + + // For backward compatibility, we store texture coordinate 'w' in separate + // array. + std::vector texcoord_ws; // 'vt'(w) + std::vector colors; // extension: vertex colors + + // + // TinyObj extension. + // + + // NOTE(syoyo): array index is based on the appearance order. + // To get a corresponding skin weight for a specific vertex id `vid`, + // Need to reconstruct a look up table: `skin_weight_t::vertex_id` == `vid` + // (e.g. using std::map, std::unordered_map) + std::vector skin_weights; + + attrib_t() {} + + // + // For pybind11 + // + const std::vector &GetVertices() const { return vertices; } + + const std::vector &GetVertexWeights() const { return vertex_weights; } +}; + +struct callback_t { + // W is optional and set to 1 if there is no `w` item in `v` line + void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); + void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); + + // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in + // `vt` line. + void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); + + // called per 'f' line. num_indices is the number of face indices(e.g. 3 for + // triangle, 4 for quad) + // 0 will be passed for undefined index in index_t members. + void (*index_cb)(void *user_data, index_t *indices, int num_indices); + // `name` material name, `material_id` = the array index of material_t[]. -1 + // if + // a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char *name, int material_id); + // `materials` = parsed material data. + void (*mtllib_cb)(void *user_data, const material_t *materials, + int num_materials); + // There may be multiple group names + void (*group_cb)(void *user_data, const char **names, int num_names); + void (*object_cb)(void *user_data, const char *name); + + callback_t() + : vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) {} +}; + +class MaterialReader { + public: + MaterialReader() {} + virtual ~MaterialReader(); + + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err) = 0; +}; + +/// +/// Read .mtl from a file. +/// +class MaterialFileReader : public MaterialReader { + public: + // Path could contain separator(';' in Windows, ':' in Posix) + explicit MaterialFileReader(const std::string &mtl_basedir) + : m_mtlBaseDir(mtl_basedir) {} + virtual ~MaterialFileReader() TINYOBJ_OVERRIDE {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err) TINYOBJ_OVERRIDE; + + private: + std::string m_mtlBaseDir; +}; + +/// +/// Read .mtl from a stream. +/// +class MaterialStreamReader : public MaterialReader { + public: + explicit MaterialStreamReader(std::istream &inStream) + : m_inStream(inStream) {} + virtual ~MaterialStreamReader() TINYOBJ_OVERRIDE {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *warn, + std::string *err) TINYOBJ_OVERRIDE; + + private: + std::istream &m_inStream; +}; + +// v2 API +struct ObjReaderConfig { + bool triangulate; // triangulate polygon? + + /// Parse vertex color. + /// If vertex color is not present, its filled with default value. + /// false = no vertex color + /// This will increase memory of parsed .obj + bool vertex_color; + + /// + /// Search path to .mtl file. + /// Default = "" = search from the same directory of .obj file. + /// Valid only when loading .obj from a file. + /// + std::string mtl_search_path; + + ObjReaderConfig() : triangulate(true), vertex_color(true) {} +}; + +/// +/// Wavefront .obj reader class(v2 API) +/// +class ObjReader { + public: + ObjReader() : valid_(false) {} + ~ObjReader() {} + + /// + /// Load .obj and .mtl from a file. + /// + /// @param[in] filename wavefront .obj filename + /// @param[in] config Reader configuration + /// + bool ParseFromFile(const std::string &filename, + const ObjReaderConfig &config = ObjReaderConfig()); + + /// + /// Parse .obj from a text string. + /// Need to supply .mtl text string by `mtl_text`. + /// This function ignores `mtllib` line in .obj text. + /// + /// @param[in] obj_text wavefront .obj filename + /// @param[in] mtl_text wavefront .mtl filename + /// @param[in] config Reader configuration + /// + bool ParseFromString(const std::string &obj_text, const std::string &mtl_text, + const ObjReaderConfig &config = ObjReaderConfig()); + + /// + /// .obj was loaded or parsed correctly. + /// + bool Valid() const { return valid_; } + + const attrib_t &GetAttrib() const { return attrib_; } + + const std::vector &GetShapes() const { return shapes_; } + + const std::vector &GetMaterials() const { return materials_; } + + /// + /// Warning message(may be filled after `Load` or `Parse`) + /// + const std::string &Warning() const { return warning_; } + + /// + /// Error message(filled when `Load` or `Parse` failed) + /// + const std::string &Error() const { return error_; } + + private: + bool valid_; + + attrib_t attrib_; + std::vector shapes_; + std::vector materials_; + + std::string warning_; + std::string error_; +}; + +/// ==>>========= Legacy v1 API ============================================= + +/// Loads .obj from a file. +/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data +/// 'shapes' will be filled with parsed shape data +/// Returns true when loading .obj become success. +/// Returns warning message into `warn`, and error message into `err` +/// 'mtl_basedir' is optional, and used for base directory for .mtl file. +/// In default(`NULL'), .mtl file is searched from an application's working +/// directory. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. +/// Option 'default_vcols_fallback' specifies whether vertex colors should +/// always be defined, even if no colors are given (fallback to white). +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, + const char *mtl_basedir = NULL, bool triangulate = true, + bool default_vcols_fallback = true); + +/// Loads .obj from a file with custom user callback. +/// .mtl is loaded as usual and parsed material_t data will be passed to +/// `callback.mtllib_cb`. +/// Returns true when loading .obj/.mtl become success. +/// Returns warning message into `warn`, and error message into `err` +/// See `examples/callback_api/` for how to use this function. +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data = NULL, + MaterialReader *readMatFn = NULL, + std::string *warn = NULL, std::string *err = NULL); + +/// Loads object from a std::istream, uses `readMatFn` to retrieve +/// std::istream for materials. +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn = NULL, bool triangulate = true, + bool default_vcols_fallback = true); + +/// Loads materials into std::map +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning, std::string *err); + +/// +/// Parse texture name and texture option for custom texture parameter through +/// material::unknown_parameter +/// +/// @param[out] texname Parsed texture name +/// @param[out] texopt Parsed texopt +/// @param[in] linebuf Input string +/// +bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, + const char *linebuf); + +/// =<<========== Legacy v1 API ============================================= + +} // namespace tinyobj + +#endif // TINY_OBJ_LOADER_H_ + +#ifdef TINYOBJLOADER_IMPLEMENTATION +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace tinyobj { + +MaterialReader::~MaterialReader() {} + +struct vertex_index_t { + int v_idx, vt_idx, vn_idx; + vertex_index_t() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index_t(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index_t(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} +}; + +// Internal data structure for face representation +// index + smoothing group. +struct face_t { + unsigned int + smoothing_group_id; // smoothing group id. 0 = smoothing groupd is off. + int pad_; + std::vector vertex_indices; // face vertex indices. + + face_t() : smoothing_group_id(0), pad_(0) {} +}; + +// Internal data structure for line representation +struct __line_t { + // l v1/vt1 v2/vt2 ... + // In the specification, line primitrive does not have normal index, but + // TinyObjLoader allow it + std::vector vertex_indices; +}; + +// Internal data structure for points representation +struct __points_t { + // p v1 v2 ... + // In the specification, point primitrive does not have normal index and + // texture coord index, but TinyObjLoader allow it. + std::vector vertex_indices; +}; + +struct tag_sizes { + tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} + int num_ints; + int num_reals; + int num_strings; +}; + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +// +// Manages group of primitives(face, line, points, ...) +struct PrimGroup { + std::vector faceGroup; + std::vector<__line_t> lineGroup; + std::vector<__points_t> pointsGroup; + + void clear() { + faceGroup.clear(); + lineGroup.clear(); + pointsGroup.clear(); + } + + bool IsEmpty() const { + return faceGroup.empty() && lineGroup.empty() && pointsGroup.empty(); + } + + // TODO(syoyo): bspline, surface, ... +}; + +// See +// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf +static std::istream &safeGetline(std::istream &is, std::string &t) { + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. + // Code that uses streambuf this way must be guarded by a sentry object. + // The sentry object performs various tasks, + // such as thread synchronization and updating the stream state. + + std::istream::sentry se(is, true); + std::streambuf *sb = is.rdbuf(); + + if (se) { + for (;;) { + int c = sb->sbumpc(); + switch (c) { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') sb->sbumpc(); + return is; + case EOF: + // Also handle the case when the last line has no line ending + if (t.empty()) is.setstate(std::ios::eofbit); + return is; + default: + t += static_cast(c); + } + } + } + + return is; +} + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) \ + (static_cast((x) - '0') < static_cast(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +// Make index zero-base, and also support relative index. +static inline bool fixIndex(int idx, int n, int *ret) { + if (!ret) { + return false; + } + + if (idx > 0) { + (*ret) = idx - 1; + return true; + } + + if (idx == 0) { + // zero is not allowed according to the spec. + return false; + } + + if (idx < 0) { + (*ret) = n + idx; // negative value = relative + return true; + } + + return false; // never reach here. +} + +static inline std::string parseString(const char **token) { + std::string s; + (*token) += strspn((*token), " \t"); + size_t e = strcspn((*token), " \t\r"); + s = std::string((*token), &(*token)[e]); + (*token) += e; + return s; +} + +static inline int parseInt(const char **token) { + (*token) += strspn((*token), " \t"); + int i = atoi((*token)); + (*token) += strcspn((*token), " \t\r"); + return i; +} + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + bool leading_decimal_dots = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + if ((curr != s_end) && (*curr == '.')) { + // accept. Somethig like `.7e+2`, `-.5234` + leading_decimal_dots = true; + } + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else if (*curr == '.') { + // accept. Somethig like `.7e+2`, `-.5234` + leading_decimal_dots = true; + } else { + goto fail; + } + + // Read the integer part. + end_not_reached = (curr != s_end); + if (!leading_decimal_dots) { + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + + // We must make sure we actually got something. + if (read == 0) goto fail; + } + + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + static const double pow_lut[] = { + 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, + }; + const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; + + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * + (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) goto fail; + } + +assemble: + *result = (sign == '+' ? 1 : -1) * + (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) + : mantissa); + return true; +fail: + return false; +} + +static inline real_t parseReal(const char **token, double default_value = 0.0) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val = default_value; + tryParseDouble((*token), end, &val); + real_t f = static_cast(val); + (*token) = end; + return f; +} + +static inline bool parseReal(const char **token, real_t *out) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val; + bool ret = tryParseDouble((*token), end, &val); + if (ret) { + real_t f = static_cast(val); + (*out) = f; + } + (*token) = end; + return ret; +} + +static inline void parseReal2(real_t *x, real_t *y, const char **token, + const double default_x = 0.0, + const double default_y = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); +} + +static inline void parseReal3(real_t *x, real_t *y, real_t *z, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); +} + +static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0, + const double default_w = 1.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + (*w) = parseReal(token, default_w); +} + +// Extension: parse vertex with colors(6 items) +static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, + real_t *r, real_t *g, real_t *b, + const char **token, + const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + + const bool found_color = + parseReal(token, r) && parseReal(token, g) && parseReal(token, b); + + if (!found_color) { + (*r) = (*g) = (*b) = 1.0; + } + + return found_color; +} + +static inline bool parseOnOff(const char **token, bool default_value = true) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + + bool ret = default_value; + if ((0 == strncmp((*token), "on", 2))) { + ret = true; + } else if ((0 == strncmp((*token), "off", 3))) { + ret = false; + } + + (*token) = end; + return ret; +} + +static inline texture_type_t parseTextureType( + const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + texture_type_t ty = default_value; + + if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { + ty = TEXTURE_TYPE_CUBE_TOP; + } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { + ty = TEXTURE_TYPE_CUBE_BOTTOM; + } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { + ty = TEXTURE_TYPE_CUBE_LEFT; + } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { + ty = TEXTURE_TYPE_CUBE_RIGHT; + } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { + ty = TEXTURE_TYPE_CUBE_FRONT; + } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { + ty = TEXTURE_TYPE_CUBE_BACK; + } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { + ty = TEXTURE_TYPE_SPHERE; + } + + (*token) = end; + return ty; +} + +static tag_sizes parseTagTriple(const char **token) { + tag_sizes ts; + + (*token) += strspn((*token), " \t"); + ts.num_ints = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + + (*token)++; // Skip '/' + + (*token) += strspn((*token), " \t"); + ts.num_reals = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; // Skip '/' + + ts.num_strings = parseInt(token); + + return ts; +} + +// Parse triples with index offsets: i, i/j/k, i//k, i/j +static bool parseTriple(const char **token, int vsize, int vnsize, int vtsize, + vertex_index_t *ret) { + if (!ret) { + return false; + } + + vertex_index_t vi(-1); + + if (!fixIndex(atoi((*token)), vsize, &(vi.v_idx))) { + return false; + } + + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + (*ret) = vi; + return true; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); + (*ret) = vi; + return true; + } + + // i/j/k or i/j + if (!fixIndex(atoi((*token)), vtsize, &(vi.vt_idx))) { + return false; + } + + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + (*ret) = vi; + return true; + } + + // i/j/k + (*token)++; // skip '/' + if (!fixIndex(atoi((*token)), vnsize, &(vi.vn_idx))) { + return false; + } + (*token) += strcspn((*token), "/ \t\r"); + + (*ret) = vi; + + return true; +} + +// Parse raw triples: i, i/j/k, i//k, i/j +static vertex_index_t parseRawTriple(const char **token) { + vertex_index_t vi(static_cast(0)); // 0 is an invalid index in OBJ + + vi.v_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +bool ParseTextureNameAndOption(std::string *texname, texture_option_t *texopt, + const char *linebuf) { + // @todo { write more robust lexer and parser. } + bool found_texname = false; + std::string texture_name; + + const char *token = linebuf; // Assume line ends with NULL + + while (!IS_NEW_LINE((*token))) { + token += strspn(token, " \t"); // skip space + if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendu = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendv = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->clamp = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->sharpness = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { + token += 4; + texopt->bump_multiplier = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), + &(texopt->origin_offset[2]), &token); + } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), + &token, 1.0, 1.0, 1.0); + } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), + &(texopt->turbulence[2]), &token); + } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { + token += 5; + texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); + } else if ((0 == strncmp(token, "-texres", 7)) && IS_SPACE((token[7]))) { + token += 7; + // TODO(syoyo): Check if arg is int type. + texopt->texture_resolution = parseInt(&token); + } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { + token += 9; + token += strspn(token, " \t"); + const char *end = token + strcspn(token, " \t\r"); + if ((end - token) == 1) { // Assume one char for -imfchan + texopt->imfchan = (*token); + } + token = end; + } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { + token += 4; + parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); + } else if ((0 == strncmp(token, "-colorspace", 11)) && + IS_SPACE((token[11]))) { + token += 12; + texopt->colorspace = parseString(&token); + } else { +// Assume texture filename +#if 0 + size_t len = strcspn(token, " \t\r"); // untile next space + texture_name = std::string(token, token + len); + token += len; + + token += strspn(token, " \t"); // skip space +#else + // Read filename until line end to parse filename containing whitespace + // TODO(syoyo): Support parsing texture option flag after the filename. + texture_name = std::string(token); + token += texture_name.length(); +#endif + + found_texname = true; + } + } + + if (found_texname) { + (*texname) = texture_name; + return true; + } else { + return false; + } +} + +static void InitTexOpt(texture_option_t *texopt, const bool is_bump) { + if (is_bump) { + texopt->imfchan = 'l'; + } else { + texopt->imfchan = 'm'; + } + texopt->bump_multiplier = static_cast(1.0); + texopt->clamp = false; + texopt->blendu = true; + texopt->blendv = true; + texopt->sharpness = static_cast(1.0); + texopt->brightness = static_cast(0.0); + texopt->contrast = static_cast(1.0); + texopt->origin_offset[0] = static_cast(0.0); + texopt->origin_offset[1] = static_cast(0.0); + texopt->origin_offset[2] = static_cast(0.0); + texopt->scale[0] = static_cast(1.0); + texopt->scale[1] = static_cast(1.0); + texopt->scale[2] = static_cast(1.0); + texopt->turbulence[0] = static_cast(0.0); + texopt->turbulence[1] = static_cast(0.0); + texopt->turbulence[2] = static_cast(0.0); + texopt->texture_resolution = -1; + texopt->type = TEXTURE_TYPE_NONE; +} + +static void InitMaterial(material_t *material) { + InitTexOpt(&material->ambient_texopt, /* is_bump */ false); + InitTexOpt(&material->diffuse_texopt, /* is_bump */ false); + InitTexOpt(&material->specular_texopt, /* is_bump */ false); + InitTexOpt(&material->specular_highlight_texopt, /* is_bump */ false); + InitTexOpt(&material->bump_texopt, /* is_bump */ true); + InitTexOpt(&material->displacement_texopt, /* is_bump */ false); + InitTexOpt(&material->alpha_texopt, /* is_bump */ false); + InitTexOpt(&material->reflection_texopt, /* is_bump */ false); + InitTexOpt(&material->roughness_texopt, /* is_bump */ false); + InitTexOpt(&material->metallic_texopt, /* is_bump */ false); + InitTexOpt(&material->sheen_texopt, /* is_bump */ false); + InitTexOpt(&material->emissive_texopt, /* is_bump */ false); + InitTexOpt(&material->normal_texopt, + /* is_bump */ false); // @fixme { is_bump will be true? } + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->reflection_texname = ""; + material->alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material->ambient[i] = static_cast(0.0); + material->diffuse[i] = static_cast(0.0); + material->specular[i] = static_cast(0.0); + material->transmittance[i] = static_cast(0.0); + material->emission[i] = static_cast(0.0); + } + material->illum = 0; + material->dissolve = static_cast(1.0); + material->shininess = static_cast(1.0); + material->ior = static_cast(1.0); + + material->roughness = static_cast(0.0); + material->metallic = static_cast(0.0); + material->sheen = static_cast(0.0); + material->clearcoat_thickness = static_cast(0.0); + material->clearcoat_roughness = static_cast(0.0); + material->anisotropy_rotation = static_cast(0.0); + material->anisotropy = static_cast(0.0); + material->roughness_texname = ""; + material->metallic_texname = ""; + material->sheen_texname = ""; + material->emissive_texname = ""; + material->normal_texname = ""; + + material->unknown_parameter.clear(); +} + +// code from https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html +template +static int pnpoly(int nvert, T *vertx, T *verty, T testx, T testy) { + int i, j, c = 0; + for (i = 0, j = nvert - 1; i < nvert; j = i++) { + if (((verty[i] > testy) != (verty[j] > testy)) && + (testx < + (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + + vertx[i])) + c = !c; + } + return c; +} + +// TODO(syoyo): refactor function. +static bool exportGroupsToShape(shape_t *shape, const PrimGroup &prim_group, + const std::vector &tags, + const int material_id, const std::string &name, + bool triangulate, + const std::vector &v) { + if (prim_group.IsEmpty()) { + return false; + } + + shape->name = name; + + // polygon + if (!prim_group.faceGroup.empty()) { + // Flatten vertices and indices + for (size_t i = 0; i < prim_group.faceGroup.size(); i++) { + const face_t &face = prim_group.faceGroup[i]; + + size_t npolys = face.vertex_indices.size(); + + if (npolys < 3) { + // Face must have 3+ vertices. + continue; + } + + vertex_index_t i0 = face.vertex_indices[0]; + vertex_index_t i1(-1); + vertex_index_t i2 = face.vertex_indices[1]; + + if (triangulate) { + // find the two axes to work in + size_t axes[2] = {1, 2}; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + i2 = face.vertex_indices[(k + 2) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + size_t vi2 = size_t(i2.v_idx); + + if (((3 * vi0 + 2) >= v.size()) || ((3 * vi1 + 2) >= v.size()) || + ((3 * vi2 + 2) >= v.size())) { + // Invalid triangle. + // FIXME(syoyo): Is it ok to simply skip this invalid triangle? + continue; + } + real_t v0x = v[vi0 * 3 + 0]; + real_t v0y = v[vi0 * 3 + 1]; + real_t v0z = v[vi0 * 3 + 2]; + real_t v1x = v[vi1 * 3 + 0]; + real_t v1y = v[vi1 * 3 + 1]; + real_t v1z = v[vi1 * 3 + 2]; + real_t v2x = v[vi2 * 3 + 0]; + real_t v2y = v[vi2 * 3 + 1]; + real_t v2z = v[vi2 * 3 + 2]; + real_t e0x = v1x - v0x; + real_t e0y = v1y - v0y; + real_t e0z = v1z - v0z; + real_t e1x = v2x - v1x; + real_t e1y = v2y - v1y; + real_t e1z = v2z - v1z; + real_t cx = std::fabs(e0y * e1z - e0z * e1y); + real_t cy = std::fabs(e0z * e1x - e0x * e1z); + real_t cz = std::fabs(e0x * e1y - e0y * e1x); + const real_t epsilon = std::numeric_limits::epsilon(); + if (cx > epsilon || cy > epsilon || cz > epsilon) { + // found a corner + if (cx > cy && cx > cz) { + } else { + axes[0] = 0; + if (cz > cx && cz > cy) axes[1] = 1; + } + break; + } + } + + real_t area = 0; + for (size_t k = 0; k < npolys; ++k) { + i0 = face.vertex_indices[(k + 0) % npolys]; + i1 = face.vertex_indices[(k + 1) % npolys]; + size_t vi0 = size_t(i0.v_idx); + size_t vi1 = size_t(i1.v_idx); + if (((vi0 * 3 + axes[0]) >= v.size()) || + ((vi0 * 3 + axes[1]) >= v.size()) || + ((vi1 * 3 + axes[0]) >= v.size()) || + ((vi1 * 3 + axes[1]) >= v.size())) { + // Invalid index. + continue; + } + real_t v0x = v[vi0 * 3 + axes[0]]; + real_t v0y = v[vi0 * 3 + axes[1]]; + real_t v1x = v[vi1 * 3 + axes[0]]; + real_t v1y = v[vi1 * 3 + axes[1]]; + area += (v0x * v1y - v0y * v1x) * static_cast(0.5); + } + + face_t remainingFace = face; // copy + size_t guess_vert = 0; + vertex_index_t ind[3]; + real_t vx[3]; + real_t vy[3]; + + // How many iterations can we do without decreasing the remaining + // vertices. + size_t remainingIterations = face.vertex_indices.size(); + size_t previousRemainingVertices = remainingFace.vertex_indices.size(); + + while (remainingFace.vertex_indices.size() > 3 && + remainingIterations > 0) { + npolys = remainingFace.vertex_indices.size(); + if (guess_vert >= npolys) { + guess_vert -= npolys; + } + + if (previousRemainingVertices != npolys) { + // The number of remaining vertices decreased. Reset counters. + previousRemainingVertices = npolys; + remainingIterations = npolys; + } else { + // We didn't consume a vertex on previous iteration, reduce the + // available iterations. + remainingIterations--; + } + + for (size_t k = 0; k < 3; k++) { + ind[k] = remainingFace.vertex_indices[(guess_vert + k) % npolys]; + size_t vi = size_t(ind[k].v_idx); + if (((vi * 3 + axes[0]) >= v.size()) || + ((vi * 3 + axes[1]) >= v.size())) { + // ??? + vx[k] = static_cast(0.0); + vy[k] = static_cast(0.0); + } else { + vx[k] = v[vi * 3 + axes[0]]; + vy[k] = v[vi * 3 + axes[1]]; + } + } + real_t e0x = vx[1] - vx[0]; + real_t e0y = vy[1] - vy[0]; + real_t e1x = vx[2] - vx[1]; + real_t e1y = vy[2] - vy[1]; + real_t cross = e0x * e1y - e0y * e1x; + // if an internal angle + if (cross * area < static_cast(0.0)) { + guess_vert += 1; + continue; + } + + // check all other verts in case they are inside this triangle + bool overlap = false; + for (size_t otherVert = 3; otherVert < npolys; ++otherVert) { + size_t idx = (guess_vert + otherVert) % npolys; + + if (idx >= remainingFace.vertex_indices.size()) { + // ??? + continue; + } + + size_t ovi = size_t(remainingFace.vertex_indices[idx].v_idx); + + if (((ovi * 3 + axes[0]) >= v.size()) || + ((ovi * 3 + axes[1]) >= v.size())) { + // ??? + continue; + } + real_t tx = v[ovi * 3 + axes[0]]; + real_t ty = v[ovi * 3 + axes[1]]; + if (pnpoly(3, vx, vy, tx, ty)) { + overlap = true; + break; + } + } + + if (overlap) { + guess_vert += 1; + continue; + } + + // this triangle is an ear + { + index_t idx0, idx1, idx2; + idx0.vertex_index = ind[0].v_idx; + idx0.normal_index = ind[0].vn_idx; + idx0.texcoord_index = ind[0].vt_idx; + idx1.vertex_index = ind[1].v_idx; + idx1.normal_index = ind[1].vn_idx; + idx1.texcoord_index = ind[1].vt_idx; + idx2.vertex_index = ind[2].v_idx; + idx2.normal_index = ind[2].vn_idx; + idx2.texcoord_index = ind[2].vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + + // remove v1 from the list + size_t removed_vert_index = (guess_vert + 1) % npolys; + while (removed_vert_index + 1 < npolys) { + remainingFace.vertex_indices[removed_vert_index] = + remainingFace.vertex_indices[removed_vert_index + 1]; + removed_vert_index += 1; + } + remainingFace.vertex_indices.pop_back(); + } + + if (remainingFace.vertex_indices.size() == 3) { + i0 = remainingFace.vertex_indices[0]; + i1 = remainingFace.vertex_indices[1]; + i2 = remainingFace.vertex_indices[2]; + { + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + shape->mesh.smoothing_group_ids.push_back(face.smoothing_group_id); + } + } + } else { + for (size_t k = 0; k < npolys; k++) { + index_t idx; + idx.vertex_index = face.vertex_indices[k].v_idx; + idx.normal_index = face.vertex_indices[k].vn_idx; + idx.texcoord_index = face.vertex_indices[k].vt_idx; + shape->mesh.indices.push_back(idx); + } + + shape->mesh.num_face_vertices.push_back( + static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face + shape->mesh.smoothing_group_ids.push_back( + face.smoothing_group_id); // per face + } + } + + shape->mesh.tags = tags; + } + + // line + if (!prim_group.lineGroup.empty()) { + // Flatten indices + for (size_t i = 0; i < prim_group.lineGroup.size(); i++) { + for (size_t j = 0; j < prim_group.lineGroup[i].vertex_indices.size(); + j++) { + const vertex_index_t &vi = prim_group.lineGroup[i].vertex_indices[j]; + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + shape->lines.indices.push_back(idx); + } + + shape->lines.num_line_vertices.push_back( + int(prim_group.lineGroup[i].vertex_indices.size())); + } + } + + // points + if (!prim_group.pointsGroup.empty()) { + // Flatten & convert indices + for (size_t i = 0; i < prim_group.pointsGroup.size(); i++) { + for (size_t j = 0; j < prim_group.pointsGroup[i].vertex_indices.size(); + j++) { + const vertex_index_t &vi = prim_group.pointsGroup[i].vertex_indices[j]; + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + shape->points.indices.push_back(idx); + } + } + } + + return true; +} + +// Split a string with specified delimiter character. +// http://stackoverflow.com/questions/236129/split-a-string-in-c +static void SplitString(const std::string &s, char delim, + std::vector &elems) { + std::stringstream ss; + ss.str(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} + +static std::string JoinPath(const std::string &dir, + const std::string &filename) { + if (dir.empty()) { + return filename; + } else { + // check '/' + char lastChar = *dir.rbegin(); + if (lastChar != '/') { + return dir + std::string("/") + filename; + } else { + return dir + filename; + } + } +} + +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning, std::string *err) { + (void)err; + + // Create a default material anyway. + material_t material; + InitMaterial(&material); + + // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. + bool has_d = false; + bool has_tr = false; + + // has_kd is used to set a default diffuse value when map_Kd is present + // and Kd is not. + bool has_kd = false; + + std::stringstream warn_ss; + + size_t line_no = 0; + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + line_no++; + + // Trim trailing whitespace. + if (linebuf.size() > 0) { + linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); + } + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + } + + // initial temporary material + InitMaterial(&material); + + has_d = false; + has_tr = false; + + // set new mtl name + token += 7; + { + std::stringstream sstr; + sstr << token; + material.name = sstr.str(); + } + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + has_kd = true; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || + (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseReal(&token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseReal(&token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + // dissolve + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseReal(&token); + + if (has_tr) { + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name + << "\". Use the value of `d` for dissolve (line " << line_no + << " in .mtl.)" << std::endl; + } + has_d = true; + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + if (has_d) { + // `d` wins. Ignore `Tr` value. + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name + << "\". Use the value of `d` for dissolve (line " << line_no + << " in .mtl.)" << std::endl; + } else { + // We invert value of Tr(assume Tr is in range [0, 1]) + // NOTE: Interpretation of Tr is application(exporter) dependent. For + // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) + material.dissolve = static_cast(1.0) - parseReal(&token); + } + has_tr = true; + continue; + } + + // PBR: roughness + if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + material.roughness = parseReal(&token); + continue; + } + + // PBR: metallic + if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { + token += 2; + material.metallic = parseReal(&token); + continue; + } + + // PBR: sheen + if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.sheen = parseReal(&token); + continue; + } + + // PBR: clearcoat thickness + if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { + token += 2; + material.clearcoat_thickness = parseReal(&token); + continue; + } + + // PBR: clearcoat roughness + if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { + token += 4; + material.clearcoat_roughness = parseReal(&token); + continue; + } + + // PBR: anisotropy + if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { + token += 6; + material.anisotropy = parseReal(&token); + continue; + } + + // PBR: anisotropy rotation + if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { + token += 7; + material.anisotropy_rotation = parseReal(&token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.ambient_texname), + &(material.ambient_texopt), token); + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.diffuse_texname), + &(material.diffuse_texopt), token); + + // Set a decent diffuse default value if a diffuse texture is specified + // without a matching Kd value. + if (!has_kd) { + material.diffuse[0] = static_cast(0.6); + material.diffuse[1] = static_cast(0.6); + material.diffuse[2] = static_cast(0.6); + } + + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_texname), + &(material.specular_texopt), token); + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_highlight_texname), + &(material.specular_highlight_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_Bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token); + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = token; + ParseTextureNameAndOption(&(material.alpha_texname), + &(material.alpha_texopt), token); + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.displacement_texname), + &(material.displacement_texopt), token); + continue; + } + + // reflection map + if ((0 == strncmp(token, "refl", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.reflection_texname), + &(material.reflection_texopt), token); + continue; + } + + // PBR: roughness texture + if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.roughness_texname), + &(material.roughness_texopt), token); + continue; + } + + // PBR: metallic texture + if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.metallic_texname), + &(material.metallic_texopt), token); + continue; + } + + // PBR: sheen texture + if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.sheen_texname), + &(material.sheen_texopt), token); + continue; + } + + // PBR: emissive texture + if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.emissive_texname), + &(material.emissive_texopt), token); + continue; + } + + // PBR: normal map texture + if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.normal_texname), + &(material.normal_texopt), token); + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, static_cast(len)); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + + if (warning) { + (*warning) = warn_ss.str(); + } +} + +bool MaterialFileReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *warn, std::string *err) { + if (!m_mtlBaseDir.empty()) { +#ifdef _WIN32 + char sep = ';'; +#else + char sep = ':'; +#endif + + // https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g + std::vector paths; + std::istringstream f(m_mtlBaseDir); + + std::string s; + while (getline(f, s, sep)) { + paths.push_back(s); + } + + for (size_t i = 0; i < paths.size(); i++) { + std::string filepath = JoinPath(paths[i], matId); + + std::ifstream matIStream(filepath.c_str()); + if (matIStream) { + LoadMtl(matMap, materials, &matIStream, warn, err); + + return true; + } + } + + std::stringstream ss; + ss << "Material file [ " << matId + << " ] not found in a path : " << m_mtlBaseDir << std::endl; + if (warn) { + (*warn) += ss.str(); + } + return false; + + } else { + std::string filepath = matId; + std::ifstream matIStream(filepath.c_str()); + if (matIStream) { + LoadMtl(matMap, materials, &matIStream, warn, err); + + return true; + } + + std::stringstream ss; + ss << "Material file [ " << filepath + << " ] not found in a path : " << m_mtlBaseDir << std::endl; + if (warn) { + (*warn) += ss.str(); + } + + return false; + } +} + +bool MaterialStreamReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *warn, std::string *err) { + (void)err; + (void)matId; + if (!m_inStream) { + std::stringstream ss; + ss << "Material stream in error state. " << std::endl; + if (warn) { + (*warn) += ss.str(); + } + return false; + } + + LoadMtl(matMap, materials, &m_inStream, warn, err); + + return true; +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, const char *mtl_basedir, + bool trianglulate, bool default_vcols_fallback) { + attrib->vertices.clear(); + attrib->normals.clear(); + attrib->texcoords.clear(); + attrib->colors.clear(); + shapes->clear(); + + std::stringstream errss; + + std::ifstream ifs(filename); + if (!ifs) { + errss << "Cannot open file [" << filename << "]" << std::endl; + if (err) { + (*err) = errss.str(); + } + return false; + } + + std::string baseDir = mtl_basedir ? mtl_basedir : ""; + if (!baseDir.empty()) { +#ifndef _WIN32 + const char dirsep = '/'; +#else + const char dirsep = '\\'; +#endif + if (baseDir[baseDir.length() - 1] != dirsep) baseDir += dirsep; + } + MaterialFileReader matFileReader(baseDir); + + return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader, + trianglulate, default_vcols_fallback); +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn /*= NULL*/, bool triangulate, + bool default_vcols_fallback) { + std::stringstream errss; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector vc; + std::vector vw; + std::vector tags; + PrimGroup prim_group; + std::string name; + + // material + std::map material_map; + int material = -1; + + // smoothing group id + unsigned int current_smoothing_id = + 0; // Initial value. 0 means no smoothing. + + int greatest_v_idx = -1; + int greatest_vn_idx = -1; + int greatest_vt_idx = -1; + + shape_t shape; + + bool found_all_colors = true; + + size_t line_num = 0; + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + line_num++; + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + real_t x, y, z; + real_t r, g, b; + + found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); + + v.push_back(x); + v.push_back(y); + v.push_back(z); + + if (found_all_colors || default_vcols_fallback) { + vc.push_back(r); + vc.push_back(g); + vc.push_back(b); + } + + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y; + parseReal2(&x, &y, &token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // skin weight. tinyobj extension + if (token[0] == 'v' && token[1] == 'w' && IS_SPACE((token[2]))) { + token += 3; + + // vw ... + // example: + // vw 0 0 0.25 1 0.25 2 0.5 + + // TODO(syoyo): Add syntax check + int vid = 0; + vid = parseInt(&token); + + skin_weight_t sw; + + sw.vertex_id = vid; + + while (!IS_NEW_LINE(token[0])) { + real_t j, w; + // joint_id should not be negative, weight may be negative + // TODO(syoyo): # of elements check + parseReal2(&j, &w, &token, -1.0); + + if (j < 0.0) { + if (err) { + std::stringstream ss; + ss << "Failed parse `vw' line. joint_id is negative. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + joint_and_weight_t jw; + + jw.joint_id = int(j); + jw.weight = w; + + sw.weightValues.push_back(jw); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + vw.push_back(sw); + } + + // line + if (token[0] == 'l' && IS_SPACE((token[1]))) { + token += 2; + + __line_t line; + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `l' line(e.g. zero value for vertex index. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + line.vertex_indices.push_back(vi); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + prim_group.lineGroup.push_back(line); + + continue; + } + + // points + if (token[0] == 'p' && IS_SPACE((token[1]))) { + token += 2; + + __points_t pts; + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `p' line(e.g. zero value for vertex index. " + "line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + pts.vertex_indices.push_back(vi); + + size_t n = strspn(token, " \t\r"); + token += n; + } + + prim_group.pointsGroup.push_back(pts); + + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + face_t face; + + face.smoothing_group_id = current_smoothing_id; + face.vertex_indices.reserve(3); + + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi; + if (!parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2), &vi)) { + if (err) { + std::stringstream ss; + ss << "Failed parse `f' line(e.g. zero value for face index. line " + << line_num << ".)\n"; + (*err) += ss.str(); + } + return false; + } + + greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; + greatest_vn_idx = + greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; + greatest_vt_idx = + greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; + + face.vertex_indices.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + // replace with emplace_back + std::move on C++11 + prim_group.faceGroup.push_back(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6))) { + token += 6; + std::string namebuf = parseString(&token); + + int newMaterialId = -1; + std::map::const_iterator it = material_map.find(namebuf); + if (it != material_map.end()) { + newMaterialId = it->second; + } else { + // { error!! material not found } + if (warn) { + (*warn) += "material [ '" + namebuf + "' ] not found in .mtl\n"; + } + } + + if (newMaterialId != material) { + // Create per-face material. Thus we don't add `shape` to `shapes` at + // this time. + // just clear `faceGroup` after `exportGroupsToShape()` call. + exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + prim_group.faceGroup.clear(); + material = newMaterialId; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (warn) { + std::stringstream ss; + ss << "Looks like empty filename for mtllib. Use default " + "material (line " + << line_num << ".)\n"; + + (*warn) += ss.str(); + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), materials, + &material_map, &warn_mtl, &err_mtl); + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; + } + + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " + "material.\n"; + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + (void)ret; // return value not used. + + if (shape.mesh.indices.size() > 0) { + shapes->push_back(shape); + } + + shape = shape_t(); + + // material = -1; + prim_group.clear(); + + std::vector names; + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + // names[0] must be 'g' + + if (names.size() < 2) { + // 'g' with empty names + if (warn) { + std::stringstream ss; + ss << "Empty group name. line: " << line_num << "\n"; + (*warn) += ss.str(); + name = ""; + } + } else { + std::stringstream ss; + ss << names[1]; + + // tinyobjloader does not support multiple groups for a primitive. + // Currently we concatinate multiple group names with a space to get + // single group name. + + for (size_t i = 2; i < names.size(); i++) { + ss << " " << names[i]; + } + + name = ss.str(); + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + (void)ret; // return value not used. + + if (shape.mesh.indices.size() > 0 || shape.lines.indices.size() > 0 || + shape.points.indices.size() > 0) { + shapes->push_back(shape); + } + + // material = -1; + prim_group.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + token += 2; + std::stringstream ss; + ss << token; + name = ss.str(); + + continue; + } + + if (token[0] == 't' && IS_SPACE(token[1])) { + const int max_tag_nums = 8192; // FIXME(syoyo): Parameterize. + tag_t tag; + + token += 2; + + tag.name = parseString(&token); + + tag_sizes ts = parseTagTriple(&token); + + if (ts.num_ints < 0) { + ts.num_ints = 0; + } + if (ts.num_ints > max_tag_nums) { + ts.num_ints = max_tag_nums; + } + + if (ts.num_reals < 0) { + ts.num_reals = 0; + } + if (ts.num_reals > max_tag_nums) { + ts.num_reals = max_tag_nums; + } + + if (ts.num_strings < 0) { + ts.num_strings = 0; + } + if (ts.num_strings > max_tag_nums) { + ts.num_strings = max_tag_nums; + } + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = parseInt(&token); + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + tag.stringValues[i] = parseString(&token); + } + + tags.push_back(tag); + + continue; + } + + if (token[0] == 's' && IS_SPACE(token[1])) { + // smoothing group id + token += 2; + + // skip space. + token += strspn(token, " \t"); // skip space + + if (token[0] == '\0') { + continue; + } + + if (token[0] == '\r' || token[1] == '\n') { + continue; + } + + if (strlen(token) >= 3 && token[0] == 'o' && token[1] == 'f' && + token[2] == 'f') { + current_smoothing_id = 0; + } else { + // assume number + int smGroupId = parseInt(&token); + if (smGroupId < 0) { + // parse error. force set to 0. + // FIXME(syoyo): Report warning. + current_smoothing_id = 0; + } else { + current_smoothing_id = static_cast(smGroupId); + } + } + + continue; + } // smoothing group id + + // Ignore unknown command. + } + + // not all vertices have colors, no default colors desired? -> clear colors + if (!found_all_colors && !default_vcols_fallback) { + vc.clear(); + } + + if (greatest_v_idx >= static_cast(v.size() / 3)) { + if (warn) { + std::stringstream ss; + ss << "Vertex indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + if (greatest_vn_idx >= static_cast(vn.size() / 3)) { + if (warn) { + std::stringstream ss; + ss << "Vertex normal indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + if (greatest_vt_idx >= static_cast(vt.size() / 2)) { + if (warn) { + std::stringstream ss; + ss << "Vertex texcoord indices out of bounds (line " << line_num << ".)\n" + << std::endl; + (*warn) += ss.str(); + } + } + + bool ret = exportGroupsToShape(&shape, prim_group, tags, material, name, + triangulate, v); + // exportGroupsToShape return false when `usemtl` is called in the last + // line. + // we also add `shape` to `shapes` when `shape.mesh` has already some + // faces(indices) + if (ret || shape.mesh.indices + .size()) { // FIXME(syoyo): Support other prims(e.g. lines) + shapes->push_back(shape); + } + prim_group.clear(); // for safety + + if (err) { + (*err) += errss.str(); + } + + attrib->vertices.swap(v); + attrib->vertex_weights.swap(v); + attrib->normals.swap(vn); + attrib->texcoords.swap(vt); + attrib->texcoord_ws.swap(vt); + attrib->colors.swap(vc); + attrib->skin_weights.swap(vw); + + return true; +} + +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data /*= NULL*/, + MaterialReader *readMatFn /*= NULL*/, + std::string *warn, /* = NULL*/ + std::string *err /*= NULL*/) { + std::stringstream errss; + + // material + std::map material_map; + int material_id = -1; // -1 = invalid + + std::vector indices; + std::vector materials; + std::vector names; + names.reserve(2); + std::vector names_out; + + std::string linebuf; + while (inStream.peek() != -1) { + safeGetline(inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + // TODO(syoyo): Support parsing vertex color extension. + real_t x, y, z, w; // w is optional. default = 1.0 + parseV(&x, &y, &z, &w, &token); + if (callback.vertex_cb) { + callback.vertex_cb(user_data, x, y, z, w); + } + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + if (callback.normal_cb) { + callback.normal_cb(user_data, x, y, z); + } + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; // y and z are optional. default = 0.0 + parseReal3(&x, &y, &z, &token); + if (callback.texcoord_cb) { + callback.texcoord_cb(user_data, x, y, z); + } + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + indices.clear(); + while (!IS_NEW_LINE(token[0])) { + vertex_index_t vi = parseRawTriple(&token); + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + indices.push_back(idx); + size_t n = strspn(token, " \t\r"); + token += n; + } + + if (callback.index_cb && indices.size() > 0) { + callback.index_cb(user_data, &indices.at(0), + static_cast(indices.size())); + } + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + token += 7; + std::stringstream ss; + ss << token; + std::string namebuf = ss.str(); + + int newMaterialId = -1; + std::map::const_iterator it = material_map.find(namebuf); + if (it != material_map.end()) { + newMaterialId = it->second; + } else { + // { warn!! material not found } + if (warn && (!callback.usemtl_cb)) { + (*warn) += "material [ " + namebuf + " ] not found in .mtl\n"; + } + } + + if (newMaterialId != material_id) { + material_id = newMaterialId; + } + + if (callback.usemtl_cb) { + callback.usemtl_cb(user_data, namebuf.c_str(), material_id); + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (warn) { + (*warn) += + "Looks like empty filename for mtllib. Use default " + "material. \n"; + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), &materials, + &material_map, &warn_mtl, &err_mtl); + + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; // This should be warn message. + } + + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " + "material.\n"; + } + } else { + if (callback.mtllib_cb) { + callback.mtllib_cb(user_data, &materials.at(0), + static_cast(materials.size())); + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + names.clear(); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + if (callback.group_cb) { + if (names.size() > 1) { + // create const char* array. + names_out.resize(names.size() - 1); + for (size_t j = 0; j < names_out.size(); j++) { + names_out[j] = names[j + 1].c_str(); + } + callback.group_cb(user_data, &names_out.at(0), + static_cast(names_out.size())); + + } else { + callback.group_cb(user_data, NULL, 0); + } + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // @todo { multiple object name? } + token += 2; + + std::stringstream ss; + ss << token; + std::string object_name = ss.str(); + + if (callback.object_cb) { + callback.object_cb(user_data, object_name.c_str()); + } + + continue; + } + +#if 0 // @todo + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + token += 2; + std::stringstream ss; + ss << token; + tag.name = ss.str(); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + std::stringstream ss; + ss << token; + tag.stringValues[i] = ss.str(); + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } +#endif + + // Ignore unknown command. + } + + if (err) { + (*err) += errss.str(); + } + + return true; +} + +bool ObjReader::ParseFromFile(const std::string &filename, + const ObjReaderConfig &config) { + std::string mtl_search_path; + + if (config.mtl_search_path.empty()) { + // + // split at last '/'(for unixish system) or '\\'(for windows) to get + // the base directory of .obj file + // + size_t pos = filename.find_last_of("/\\"); + if (pos != std::string::npos) { + mtl_search_path = filename.substr(0, pos); + } + } else { + mtl_search_path = config.mtl_search_path; + } + + valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, + filename.c_str(), mtl_search_path.c_str(), + config.triangulate, config.vertex_color); + + return valid_; +} + +bool ObjReader::ParseFromString(const std::string &obj_text, + const std::string &mtl_text, + const ObjReaderConfig &config) { + std::stringbuf obj_buf(obj_text); + std::stringbuf mtl_buf(mtl_text); + + std::istream obj_ifs(&obj_buf); + std::istream mtl_ifs(&mtl_buf); + + MaterialStreamReader mtl_ss(mtl_ifs); + + valid_ = LoadObj(&attrib_, &shapes_, &materials_, &warning_, &error_, + &obj_ifs, &mtl_ss, config.triangulate, config.vertex_color); + + return valid_; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +} // namespace tinyobj + +#endif diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshBaseActor.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshBaseActor.h new file mode 100644 index 0000000..9841cf9 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshBaseActor.h @@ -0,0 +1,373 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "DynamicMesh3.h" +#include "DynamicMeshAABBTree3.h" +#include "Spatial/FastWinding.h" +#include "DynamicMeshBaseActor.generated.h" + + +/** + * Type of Normals computation used by ADynamicMeshBaseActor + */ +UENUM() +enum class EDynamicMeshActorNormalsMode : uint8 +{ + SplitNormals = 0, + PerVertexNormals = 1, + FaceNormals = 2 +}; + + +/** + * Source of mesh used to initialize ADynamicMeshBaseActor + */ +UENUM() +enum class EDynamicMeshActorSourceType : uint8 +{ + Primitive, + ImportedMesh +}; + + +/** + * Geometric primitive types supported of by ADynamicMeshBaseActor + */ +UENUM() +enum class EDynamicMeshActorPrimitiveType : uint8 +{ + Sphere, + Box +}; + + +/** + * Boolean operation types supported of by ADynamicMeshBaseActor + */ +UENUM(BlueprintType) +enum class EDynamicMeshActorBooleanOperation : uint8 +{ + Union, + Subtraction, + Intersection +}; + + +UENUM(BlueprintType) +enum class EDynamicMeshActorCollisionMode : uint8 +{ + NoCollision, + ComplexAsSimple, + ComplexAsSimpleAsync +}; + + + +/** + * ADynamicMeshBaseActor is a base class for Actors that support being + * rebuilt in-game after mesh editing operations. The base Actor itself + * does not have any Components, it should be used via one of the + * DynamicPMCActor, DynamicSMCActor, or DynamicSDMCActor subclasses. + * + * ADynamicMeshBaseActor provides a FDynamicMesh3 "Source Mesh", which can + * be modified via lambdas passed to the EditMesh() function, which will + * then cause necessary updates to happen to the implementing Components. + * An AABBTree and FastWindingTree can optionally be enabled with the + * bEnableSpatialQueries and bEnableInsideQueries flags. + * + * When Spatial queries are enabled, a set of UFunctions DistanceToPoint(), + * NearestPoint(), ContainsPoint(), and IntersectRay() are available via Blueprints + * on the relevant Actor. These functions *do not* depend on the UE4 Physics + * system to work. + * + * A small set of mesh modification UFunctions are also available via Blueprints, + * including BooleanWithMesh(), SolidifyMesh(), SimplifyMeshToTriCount(), and + * CopyFromMesh(). + * + * Meshes can be read from OBJ files either using the ImportedMesh type for + * the SourceType property, or by calling the ImportMesh() UFunction from a Blueprint. + * Note that calling this in a Construction Script will be problematic in the Editor + * as the OBJ will be re-read any time the Actor is modified (including translated/rotated). + * + * Any Material set on the subclass Components will be overriden by the Material property. + * + * + */ +UCLASS(Abstract) +class RUNTIMEGEOMETRYUTILS_API ADynamicMeshBaseActor : public AActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + ADynamicMeshBaseActor(); + + +public: + /** Type of mesh used to initialize this Actor - either a generated mesh Primitive or an Imported OBJ file */ + UPROPERTY(EditAnywhere, Category = MeshOptions) + EDynamicMeshActorSourceType SourceType = EDynamicMeshActorSourceType::Primitive; + + /** Type of normals computed for the Mesh */ + UPROPERTY(EditAnywhere, Category = MeshOptions) + EDynamicMeshActorNormalsMode NormalsMode = EDynamicMeshActorNormalsMode::SplitNormals; + + /** Material assigned to child Components created by subclasses */ + UPROPERTY(EditAnywhere, Category = MaterialOptions) + UMaterialInterface* Material; + + /** If true, mesh will be regenerated or re-imported on tick. Can be useful for prototyping procedural animation, but not the most efficient way to do it */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive", EditConditionHides)) + bool bRegenerateOnTick = false; + + // + // Parameters for SourceType = Imported + // + + /** Path to OBJ file read to initialize mesh in SourceType=Imported mode */ + UPROPERTY(EditAnywhere, Category = ImportOptions, meta = (EditCondition = "SourceType == EDynamicMeshActorSourceType::ImportedMesh", EditConditionHides)) + FString ImportPath; + + /** Whether the imported mesh should have it's triangles reversed (commonly required for meshes authored in DCC tools) */ + UPROPERTY(EditAnywhere, Category = ImportOptions, meta = (EditCondition = "SourceType == EDynamicMeshActorSourceType::ImportedMesh", EditConditionHides)) + bool bReverseOrientation = true; + + /** If true the imported mesh will be recentered around the origin */ + UPROPERTY(EditAnywhere, Category = ImportOptions, meta = (EditCondition = "SourceType == EDynamicMeshActorSourceType::ImportedMesh", EditConditionHides)) + bool bCenterPivot = true; + + /** Uniform scaling applied to the imported mesh (baked into the mesh vertices, not the actor Transform) */ + UPROPERTY(EditAnywhere, Category = ImportOptions, meta = (EditCondition = "SourceType == EDynamicMeshActorSourceType::ImportedMesh", EditConditionHides)) + float ImportScale = 1.0; + + + // + // Parameters for SourceType = Primitive + // + + /** Type of generated mesh primitive */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive", EditConditionHides)) + EDynamicMeshActorPrimitiveType PrimitiveType = EDynamicMeshActorPrimitiveType::Box; + + /** Triangle density of generated primitive */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (UIMin = 0, UIMax = 50, EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive", EditConditionHides)) + int TessellationLevel = 8; + + /** Radius of generated sphere / Width of generated box */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (UIMin = 0, EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive", EditConditionHides)) + float MinimumRadius = 50; + + /** Multiplier on MinimumRadius used to define box depth (ie dimension on Z axis) */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (UIMin = 0.01, UIMax = 1.0, EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive && PrimitiveType == EDynamicMeshActorPrimitiveType::Box", EditConditionHides)) + float BoxDepthRatio = 1.0; + + /** A random value in range [-VariableRadius, VariableRadius] is added to MinimumRadius */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (UIMin = 0, EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive", EditConditionHides)) + float VariableRadius = 0; + + /** Speed of variation of VariableRadius, only has an effect when bRegenerateOnTick is true */ + UPROPERTY(EditAnywhere, Category = PrimitiveOptions, meta = (UIMin = 0, EditCondition = "SourceType == EDynamicMeshActorSourceType::Primitive", EditConditionHides)) + float PulseSpeed = 3.0; + + + + // + // ADynamicMeshBaseActor API + // +public: + + /** + * Call EditMesh() to safely modify the SourceMesh owned by this Actor. + * Your EditFunc will be called with the Current SourceMesh as argument, + * and you are expected to pass back the new/modified version. + * (If you are generating an entirely new mesh, MoveTemp can be used to do this without a copy) + */ + virtual void EditMesh(TFunctionRef EditFunc); + + /** + * Get a copy of the current SourceMesh stored in MeshOut + */ + virtual void GetMeshCopy(FDynamicMesh3& MeshOut); + + /** + * Get a reference to the current SourceMesh + */ + virtual const FDynamicMesh3& GetMeshRef() const; + + + /** + * This delegate is broadcast whenever the internal SourceMesh is updated + */ + DECLARE_MULTICAST_DELEGATE_OneParam(FOnMeshModified, ADynamicMeshBaseActor*); + FOnMeshModified OnMeshModified; + +protected: + + /** The SourceMesh used to initialize the mesh Components in the various subclasses */ + FDynamicMesh3 SourceMesh; + + /** Accumulated time since Actor was created, this is used for the animated primitives when bRegenerateOnTick = true*/ + double AccumulatedTime = 0; + + /** Called whenever the initial Source mesh needs to be regenerated / re-imported. Calls EditMesh() to do so. */ + virtual void OnMeshGenerationSettingsModified(); + + /** Called to generate or import a new source mesh. Override this to provide your own generated mesh. */ + virtual void RegenerateSourceMesh(FDynamicMesh3& MeshOut); + + /** Call this on a Mesh to compute normals according to the NormalsMode setting */ + virtual void RecomputeNormals(FDynamicMesh3& MeshOut); + + + + + // + // Support for AABBTree / Spatial Queries + // +public: + UPROPERTY(EditAnywhere, Category = SpatialQueryOptions) + bool bEnableSpatialQueries = false; + + UPROPERTY(EditAnywhere, Category = SpatialQueryOptions) + bool bEnableInsideQueries = false; + +protected: + // This AABBTree is updated each time SourceMesh is modified if bEnableSpatialQueries=true or bEnableInsideQueries=true + FDynamicMeshAABBTree3 MeshAABBTree; + // This FastWindingTree is updated each time SourceMesh is modified if bEnableInsideQueries=true + TUniquePtr> FastWinding; + + + // + // Support for Runtime-Generated Collision + // +public: + UPROPERTY(EditAnywhere, Category = RuntimeCollisionOptions) + EDynamicMeshActorCollisionMode CollisionMode = EDynamicMeshActorCollisionMode::NoCollision; + + + // + // ADynamicMeshBaseActor API that subclasses must implement. + // +protected: + /** + * Called when the SourceMesh has been modified. Subclasses override this function to + * update their respective Component with the new SourceMesh. + */ + virtual void OnMeshEditedInternal(); + + + + + // + // Standard UE4 Actor Callbacks. If you need to override these functions, + // make sure to call (eg) Super::Tick() or you will break the mesh updating functionality. + // +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + virtual void PostLoad() override; + virtual void PostActorCreated() override; + +#if WITH_EDITOR + // called when property is modified. This will call OnMeshGenerationSettingsModified() to update the mesh + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + +public: + // Called every frame + virtual void Tick(float DeltaTime) override; + + + + + // + // Blueprint API + // + + +public: + /** + * Update SourceMesh by reading external mesh file at Path. Optionally flip orientation and recompute normals. + * Note: Path may be relative to Content folder, otherwise it must be an absolute path. + * @return false if mesh read failed + */ + UFUNCTION(BlueprintCallable) + bool ImportMesh(FString Path, bool bFlipOrientation, bool bRecomputeNormals); + + /** Copy the SourceMesh of OtherMesh into our SourceMesh, and optionally recompute normals */ + UFUNCTION(BlueprintCallable) + void CopyFromMesh(ADynamicMeshBaseActor* OtherMesh, bool bRecomputeNormals); + + + + // + // Mesh Spatial Queries API + // +public: + /** + * Find NearestMeshWorldPoint on SourceMesh to WorldPoint, as well as NearestTriangle ID and barycentric coordinates of NearestMeshWorldPoint in triangle + * @return distance to point + */ + UFUNCTION(BlueprintCallable) + float DistanceToPoint(FVector WorldPoint, FVector& NearestMeshWorldPoint, int& NearestTriangle, FVector& TriBaryCoords); + + /** + * @return nearest world-space point on SourceMesh to WorldPoint + */ + UFUNCTION(BlueprintCallable) + FVector NearestPoint(FVector WorldPoint); + + /** + * @return true if mesh contains WorldPoint, which is defined as the mesh winding number being >= WindingThreshold + */ + UFUNCTION(BlueprintCallable) + bool ContainsPoint(FVector WorldPoint, float WindingThreshold = 0.5); + + /** + * Calculate intersection of given 3D World-Space ray defined by (RayOrigin,RayDirection) with the SourceMesh. + * If hit, returns WorldHitPoint position, distance along ray in HitDistance, NearestTriangle ID, and barycentric coordinates of hit point in triangle + * Pass MaxDistance > 0 to limit the allowable ray-hit distance + * @return true if hit is found + */ + UFUNCTION(BlueprintCallable) + bool IntersectRay(FVector RayOrigin, FVector RayDirection, FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, float MaxDistance = 0); + + + + // + // Mesh Modification API + // +public: + + /** Compute the specified a Boolean operation with OtherMesh (transformed to world space) and store in our SourceMesh */ + UFUNCTION(BlueprintCallable) + void BooleanWithMesh(ADynamicMeshBaseActor* OtherMesh, EDynamicMeshActorBooleanOperation Operation); + + /** Subtract OtherMesh from our SourceMesh */ + UFUNCTION(BlueprintCallable) + void SubtractMesh(ADynamicMeshBaseActor* OtherMesh); + + /** Union OtherMesh with our SourceMesh */ + UFUNCTION(BlueprintCallable) + void UnionWithMesh(ADynamicMeshBaseActor* OtherMesh); + + /** Intersect OtherMesh with our SourceMesh */ + UFUNCTION(BlueprintCallable) + void IntersectWithMesh(ADynamicMeshBaseActor* OtherMesh); + + /** Create a "solid" verison of SourceMesh by voxelizing with the fast winding number at the given grid resolution */ + UFUNCTION(BlueprintCallable) + void SolidifyMesh(int VoxelResolution = 64, float WindingThreshold = 0.5); + + /** Simplify current SourceMesh to the target triangle count */ + UFUNCTION(BlueprintCallable) + void SimplifyMeshToTriCount(int32 TargetTriangleCount); + +public: + /** @return number of triangles in current SourceMesh */ + UFUNCTION(BlueprintCallable) + int GetTriangleCount(); +}; diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJReader.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJReader.h new file mode 100644 index 0000000..0a43919 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJReader.h @@ -0,0 +1,24 @@ +#pragma once + +#include "CoreMinimal.h" +#include "DynamicMesh3.h" + +namespace RTGUtils +{ + /** + * Read mesh in OBJ format from the given path into a FDynamicMesh3. + * @param bNormals should normals be imported into primary normal attribute overlay + * @param bTexCoords should texture coordinates be imported into primary UV attribute overlay + * @param bVertexColors should normals be imported into per-vertex colors + * @param bReverseOrientation if true, mesh orientation/normals are flipped. You probably want this for importing to UE4 from other apps. + * @param return false if read failed + */ + RUNTIMEGEOMETRYUTILS_API bool ReadOBJMesh( + const FString& Path, + FDynamicMesh3& MeshOut, + bool bNormals, + bool bTexCoords, + bool bVertexColors, + bool bReverseOrientation); +} + diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJWriter.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJWriter.h new file mode 100644 index 0000000..ef5cae9 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJWriter.h @@ -0,0 +1,30 @@ +#pragma once + +#include "CoreMinimal.h" +#include "DynamicMesh3.h" + +namespace RTGUtils +{ + /** + * Write mesh to the given output path in OBJ format. + * @param bReverseOrientation if true, mesh orientation/normals are flipped. You probably want this for exporting from UE4 to other apps. + * @param return false if write failed + */ + RUNTIMEGEOMETRYUTILS_API bool WriteOBJMesh( + const FString& OutputPath, + const FDynamicMesh3& Mesh, + bool bReverseOrientation); + + /** + * Write set of meshes to the given output path in OBJ format. + * @param bReverseOrientation if true, mesh orientation/normals are flipped. You probably want this for exporting from UE4 to other apps. + * @param return false if write failed + */ + RUNTIMEGEOMETRYUTILS_API bool WriteOBJMeshes( + const FString& OutputPath, + const TArray& Meshes, + bool bReverseOrientation); +} + + + diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicPMCActor.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicPMCActor.h new file mode 100644 index 0000000..44af58b --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicPMCActor.h @@ -0,0 +1,44 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ProceduralMeshComponent.h" +#include "DynamicMeshBaseActor.h" +#include "DynamicPMCActor.generated.h" + + + +UCLASS() +class RUNTIMEGEOMETRYUTILS_API ADynamicPMCActor : public ADynamicMeshBaseActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + ADynamicPMCActor(); + +public: + UPROPERTY(VisibleAnywhere) + UProceduralMeshComponent* MeshComponent = nullptr; + + + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + +public: + // Called every frame + virtual void Tick(float DeltaTime) override; + + +protected: + /** + * ADynamicBaseActor API + */ + virtual void OnMeshEditedInternal() override; + +protected: + virtual void UpdatePMCMesh(); + +}; diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSDMCActor.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSDMCActor.h new file mode 100644 index 0000000..5fbfa1e --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSDMCActor.h @@ -0,0 +1,41 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "SimpleDynamicMeshComponent.h" +#include "DynamicMeshBaseActor.h" +#include "DynamicSDMCActor.generated.h" + +class FDynamicMesh3; + +UCLASS() +class RUNTIMEGEOMETRYUTILS_API ADynamicSDMCActor : public ADynamicMeshBaseActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + ADynamicSDMCActor(); + +public: + UPROPERTY(VisibleAnywhere) + USimpleDynamicMeshComponent* MeshComponent = nullptr; + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + +public: + // Called every frame + virtual void Tick(float DeltaTime) override; + +protected: + /** + * ADynamicBaseActor API + */ + virtual void OnMeshEditedInternal() override; + +protected: + virtual void UpdateSDMCMesh(); +}; diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSMCActor.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSMCActor.h new file mode 100644 index 0000000..a300058 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSMCActor.h @@ -0,0 +1,45 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "Engine/StaticMesh.h" +#include "Components/StaticMeshComponent.h" +#include "DynamicMeshBaseActor.h" +#include "DynamicSMCActor.generated.h" + +UCLASS() +class RUNTIMEGEOMETRYUTILS_API ADynamicSMCActor : public ADynamicMeshBaseActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + ADynamicSMCActor(); + +public: + UPROPERTY(VisibleAnywhere) + UStaticMeshComponent* MeshComponent = nullptr; + + UPROPERTY(Transient) + UStaticMesh* StaticMesh = nullptr; + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + virtual void PostLoad() override; + virtual void PostActorCreated() override; + +public: + // Called every frame + virtual void Tick(float DeltaTime) override; + +protected: + /** + * ADynamicBaseActor API + */ + virtual void OnMeshEditedInternal() override; + +protected: + virtual void UpdateSMCMesh(); +}; diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/MeshComponentRuntimeUtils.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/MeshComponentRuntimeUtils.h new file mode 100644 index 0000000..e6cdb8e --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/MeshComponentRuntimeUtils.h @@ -0,0 +1,37 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/StaticMesh.h" +#include "ProceduralMeshComponent.h" +#include "DynamicMesh3.h" + + +namespace RTGUtils +{ + + + /** + * Reinitialize the given StaticMesh with the input FDynamicMesh3. + * This calls StaticMesh->BuildFromMeshDescriptions(), which can be used at Runtime (vs StaticMesh->Build() which cannot) + */ + RUNTIMEGEOMETRYUTILS_API void UpdateStaticMeshFromDynamicMesh( + UStaticMesh* StaticMesh, + const FDynamicMesh3* Mesh); + + + + /** + * Initialize a ProceduralMeshComponent with a single section defined by the given FDynamicMesh3. + * @param bUseFaceNormals if true, each triangle is shaded with per-triangle normal instead of split-vertex normals from FDynamicMesh3 overlay + * @param bInitializeUV0 if true, UV0 is initialized, otherwise it is not (set to 0) + * @param bInitializePerVertexColors if true, per-vertex colors on the FDynamicMesh3 are used to initialize vertex colors of the PMC + */ + RUNTIMEGEOMETRYUTILS_API void UpdatePMCFromDynamicMesh_SplitTriangles( + UProceduralMeshComponent* Component, + const FDynamicMesh3* Mesh, + bool bUseFaceNormals, + bool bInitializeUV0, + bool bInitializePerVertexColors, + bool bCreateCollision); + +} \ No newline at end of file diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/RuntimeGeometryUtilsModule.h b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/RuntimeGeometryUtilsModule.h new file mode 100644 index 0000000..a8ca8bf --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/RuntimeGeometryUtilsModule.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FRuntimeGeometryUtilsModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/RuntimeGeometryUtils.Build.cs b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/RuntimeGeometryUtils.Build.cs new file mode 100644 index 0000000..957b480 --- /dev/null +++ b/Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/RuntimeGeometryUtils.Build.cs @@ -0,0 +1,61 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class RuntimeGeometryUtils : ModuleRules +{ + public RuntimeGeometryUtils(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "GeometricObjects", + "DynamicMesh", + "ProceduralMeshComponent", + "ModelingComponents", + "RenderCore" + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "MeshDescription", + "StaticMeshDescription", + "GeometryAlgorithms", + "MeshConversion", + "MeshUtilities2" + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/SMC_Build.sln b/SMC_Build.sln new file mode 100644 index 0000000..980dda6 --- /dev/null +++ b/SMC_Build.sln @@ -0,0 +1,4594 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28315.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Automation", "Automation", "{0BFBE63A-B98A-3411-8EEB-8918FEC737B3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Datasmith", "Datasmith", "{FD3DFD2B-113B-36C4-8511-C60D48287AA6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{233774A8-CC9D-3FA9-86D1-90573E92B704}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Games", "Games", "{DE1F8B53-6C02-3C13-9101-A7C8D96F3FF6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Programs", "Programs", "{A338B9E2-A559-34BE-A46D-F789DD488FAD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{57713676-9DBE-331C-AD10-26632AC9EE0C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TextureShare", "TextureShare", "{74618045-8FDF-37E1-A2C4-C2795A6DAF24}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BenchmarkTool", "Intermediate\ProjectFiles\BenchmarkTool.vcxproj", "{728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlankProgram", "Intermediate\ProjectFiles\BlankProgram.vcxproj", "{99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BuildPatchTool", "Intermediate\ProjectFiles\BuildPatchTool.vcxproj", "{C95BFC5E-4DFC-3531-8D97-1674BB663777}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChaosVisualDebugger", "Intermediate\ProjectFiles\ChaosVisualDebugger.vcxproj", "{154D22D7-3460-351E-92EE-1294BAD94EF6}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CrashReportClient", "Intermediate\ProjectFiles\CrashReportClient.vcxproj", "{74FC9350-8DAD-3511-A9F8-EE4212F5A703}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CrashReportClientEditor", "Intermediate\ProjectFiles\CrashReportClientEditor.vcxproj", "{D677E3A3-A3EB-373E-A872-C572DB91D8D5}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithCADWorker", "Intermediate\ProjectFiles\DatasmithCADWorker.vcxproj", "{99B55C07-00CC-3720-94F1-4308D3DA7996}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithFacadeCSharp", "Intermediate\ProjectFiles\DatasmithFacadeCSharp.vcxproj", "{8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithMax2016", "Intermediate\ProjectFiles\DatasmithMax2016.vcxproj", "{615CEF89-0B8F-36F2-923F-8CE076D1166C}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithMax2017", "Intermediate\ProjectFiles\DatasmithMax2017.vcxproj", "{6B544390-589A-3FD0-8505-80D626898D3C}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithMax2018", "Intermediate\ProjectFiles\DatasmithMax2018.vcxproj", "{0CA3555A-FCE8-3A67-A084-32250A7BF82E}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithMax2019", "Intermediate\ProjectFiles\DatasmithMax2019.vcxproj", "{FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithMax2020", "Intermediate\ProjectFiles\DatasmithMax2020.vcxproj", "{1751912E-33B1-37BD-B30E-24D05BDEC2B2}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithMax2021", "Intermediate\ProjectFiles\DatasmithMax2021.vcxproj", "{401D1B4F-84A5-3A92-A19C-279947EED6ED}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithNavisworks2019", "Intermediate\ProjectFiles\DatasmithNavisworks2019.vcxproj", "{56C20C58-BE0C-3F83-BF50-6DAED75FFB06}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithNavisworks2020", "Intermediate\ProjectFiles\DatasmithNavisworks2020.vcxproj", "{ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithRevit2018", "Intermediate\ProjectFiles\DatasmithRevit2018.vcxproj", "{ABAF7BAF-6484-3771-B35C-4957A2D729C8}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithRevit2019", "Intermediate\ProjectFiles\DatasmithRevit2019.vcxproj", "{75041236-9D97-3112-B228-15D19A8D8400}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithRevit2020", "Intermediate\ProjectFiles\DatasmithRevit2020.vcxproj", "{8C81677C-E927-3CBD-A61F-2579B76096E5}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithRevit2021", "Intermediate\ProjectFiles\DatasmithRevit2021.vcxproj", "{7663D84B-A880-3A97-9E6B-DF88CAE059FE}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithRhino6", "Intermediate\ProjectFiles\DatasmithRhino6.vcxproj", "{EEE7D7F0-A860-3966-8408-6E3484F1431E}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithRhino7", "Intermediate\ProjectFiles\DatasmithRhino7.vcxproj", "{EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithSDK", "Intermediate\ProjectFiles\DatasmithSDK.vcxproj", "{FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithSketchUp2017", "Intermediate\ProjectFiles\DatasmithSketchUp2017.vcxproj", "{0669598D-617C-3B37-AE25-2E7BC0CEF530}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithSketchUp2018", "Intermediate\ProjectFiles\DatasmithSketchUp2018.vcxproj", "{0F4E2BA4-8270-3AD4-831A-1CE849085505}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithSketchUp2019", "Intermediate\ProjectFiles\DatasmithSketchUp2019.vcxproj", "{725A117A-0FB4-3FF3-B69C-210BE9BE753D}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatasmithSketchUp2020", "Intermediate\ProjectFiles\DatasmithSketchUp2020.vcxproj", "{71A0B529-31B6-3A3E-A59D-6276170F18B3}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EpicWebHelper", "Intermediate\ProjectFiles\EpicWebHelper.vcxproj", "{0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HeadlessChaos", "Intermediate\ProjectFiles\HeadlessChaos.vcxproj", "{1401DF69-E2B8-31A8-AF33-3DC3904A39E7}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InterchangeWorker", "Intermediate\ProjectFiles\InterchangeWorker.vcxproj", "{362F809C-0D32-39C8-A9FB-B623889D2D4D}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LiveCodingConsole", "Intermediate\ProjectFiles\LiveCodingConsole.vcxproj", "{141AC720-D423-3A69-AB54-A18F99797871}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShaderCompileWorker", "Intermediate\ProjectFiles\ShaderCompileWorker.vcxproj", "{F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SlateViewer", "Intermediate\ProjectFiles\SlateViewer.vcxproj", "{AE589633-6E7B-38F8-86D6-127025F615D9}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SwitchboardListener", "Intermediate\ProjectFiles\SwitchboardListener.vcxproj", "{23A26DC2-5829-359E-AA28-11DE040D8584}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestPAL", "Intermediate\ProjectFiles\TestPAL.vcxproj", "{64ADF73D-8814-3B1F-9C32-570DE73879CC}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextureShareSDK", "Intermediate\ProjectFiles\TextureShareSDK.vcxproj", "{E25A3458-DE88-3E16-B848-8E2CF56A14E8}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealFileServer", "Intermediate\ProjectFiles\UnrealFileServer.vcxproj", "{FBC3D599-658A-3D61-A8AA-5927E12DACA8}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealFrontend", "Intermediate\ProjectFiles\UnrealFrontend.vcxproj", "{5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealHeaderTool", "Intermediate\ProjectFiles\UnrealHeaderTool.vcxproj", "{B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealInsights", "Intermediate\ProjectFiles\UnrealInsights.vcxproj", "{97B1CED6-8204-3641-B989-B0113201B608}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealLightmass", "Intermediate\ProjectFiles\UnrealLightmass.vcxproj", "{BC71F0FF-BB86-309D-9079-60E0DAE21FC6}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealMultiUserServer", "Intermediate\ProjectFiles\UnrealMultiUserServer.vcxproj", "{A162AD37-AB12-3725-BEB4-E94EE93F2756}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealObjectPtrTool", "Intermediate\ProjectFiles\UnrealObjectPtrTool.vcxproj", "{72395ABF-E458-3B53-90D7-D25940B9C45E}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealPak", "Intermediate\ProjectFiles\UnrealPak.vcxproj", "{EA694FFC-58C0-34D1-8539-645A5F1C1859}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealRecoverySvc", "Intermediate\ProjectFiles\UnrealRecoverySvc.vcxproj", "{353249D9-CCDD-3A43-8DEF-1C7954B0E69B}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnrealVersionSelector", "Intermediate\ProjectFiles\UnrealVersionSelector.vcxproj", "{51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextureShareSDK_D3D11", "Intermediate\ProjectFiles\TextureShareSDK_D3D11.vcxproj", "{A1B06536-06A2-3FE7-A9C7-A8359B888177}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextureShareSDK_D3D12", "Intermediate\ProjectFiles\TextureShareSDK_D3D12.vcxproj", "{5140C054-F83C-3C47-BEBE-0E6D55D90175}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BootstrapPackagedGame", "Intermediate\ProjectFiles\BootstrapPackagedGame.vcxproj", "{671C936A-EBEA-31C4-9DC1-883582196280}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UE5", "Intermediate\ProjectFiles\UE5.vcxproj", "{BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SMC_Build", "Intermediate\ProjectFiles\SMC_Build.vcxproj", "{9864B3C7-AB7D-3585-8C5C-A8649CCB0358}" + ProjectSection(ProjectDependencies) = postProject + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnrealBuildTool", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\UnrealBuildTool\UnrealBuildTool.csproj", "{35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomationTool", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\AutomationTool.csproj", "{FE372125-7A72-3A78-8984-732B784D3C65}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Android.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Android\Android.Automation.csproj", "{7237F39C-0653-372F-A495-3219761D457D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomationUtils.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\AutomationUtils\AutomationUtils.Automation.csproj", "{199732AC-C2EB-3676-B905-60397D421964}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildGraph.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\BuildGraph\BuildGraph.Automation.csproj", "{DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gauntlet.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Gauntlet\Gauntlet.Automation.csproj", "{9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HoloLens.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\HoloLens\HoloLens.Automation.csproj", "{328BDFF1-6D81-3942-A143-A3CE5E55C73E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IOS.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\IOS\IOS.Automation.csproj", "{EC075F99-DF6B-3A0E-BA98-BB578D4613D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Linux.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Linux\Linux.Automation.csproj", "{02E88A55-E7FA-3D70-AFE6-CBB1DD869087}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Localization.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Localization\Localization.Automation.csproj", "{45DE9D3D-637A-3B92-B1B7-248CE5FEC056}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lumin.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Lumin\Lumin.Automation.csproj", "{F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mac.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Mac\Mac.Automation.csproj", "{C7D60A52-C992-3269-9520-8280D5058ED7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneSkyLocalization.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\OneSkyLocalization\OneSkyLocalization.Automation.csproj", "{CB55C95C-91E5-32B6-BEC1-F855A72F7408}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomationScripts.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Scripts\AutomationScripts.Automation.csproj", "{F21DEF2A-BFC0-3368-BD0C-824A77704513}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Turnkey.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Turnkey\Turnkey.Automation.csproj", "{E53FDC1E-0E32-3F89-B571-76C13F991C52}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TVOS.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\TVOS\TVOS.Automation.csproj", "{B79003E3-8008-34C1-845A-4820F93D6F6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\Win\Win.Automation.csproj", "{11CB4E3A-D028-3291-8B6B-64A69A2E5301}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XLocLocalization.Automation", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationTool\XLocLocalization\XLocLocalization.Automation.csproj", "{7C617DDB-9845-358F-9D30-C2DA01E77EF7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EpicGames.Core", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\Shared\EpicGames.Core\EpicGames.Core.csproj", "{45F6849F-406D-3F90-83FC-2CC37A713A92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EpicGames.Jupiter", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\Shared\EpicGames.Jupiter\EpicGames.Jupiter.csproj", "{A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EpicGames.Perforce", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\Shared\EpicGames.Perforce\EpicGames.Perforce.csproj", "{131B3BA2-EC95-3762-A5DE-C59178136735}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutomationToolLauncher", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\AutomationToolLauncher\AutomationToolLauncher.csproj", "{C575DBCB-0998-3726-9A3A-8B12E9A89316}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildUtilities", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\DotNETCommon\BuildUtilities\BuildUtilities.csproj", "{49033409-2B45-344C-A58C-9A21741802A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNETUtilities", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\DotNETCommon\DotNETUtilities\DotNETUtilities.csproj", "{CF078A43-9249-3A36-9D52-9F1CCAF82EF0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitDependencies", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\GitDependencies\GitDependencies.csproj", "{9B740143-841C-4AF7-905E-D8883F1735DE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MemoryProfiler2", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\MemoryProfiler2\MemoryProfiler2.csproj", "{75240A67-CF47-4DDC-83B2-AD5D371F31BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "nDisplayLauncher", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\nDisplayLauncher\nDisplayLauncher.csproj", "{0CD16871-AE33-44AC-9E84-40DBFAEB51C4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "nDisplayListener", "D:\UnrealEngine-5.0.0-early-access-2\Engine\Source\Programs\nDisplayListener\nDisplayListener.csproj", "{89224712-325D-4500-A4A5-B102A3A8EE2F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Visualizers", "Visualizers", "{1CCEC849-CC72-4C59-8C36-2F7C38706D4C}" + ProjectSection(SolutionItems) = preProject + D:\UnrealEngine-5.0.0-early-access-2\Engine\Extras\VisualStudioDebugging\Unreal.natvis = D:\UnrealEngine-5.0.0-early-access-2\Engine\Extras\VisualStudioDebugging\Unreal.natvis + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Client|HoloLens = Debug Client|HoloLens + Debug Client|Win64 = Debug Client|Win64 + Debug Editor|HoloLens = Debug Editor|HoloLens + Debug Editor|Win64 = Debug Editor|Win64 + Debug Server|HoloLens = Debug Server|HoloLens + Debug Server|Win64 = Debug Server|Win64 + Debug|HoloLens = Debug|HoloLens + Debug|Win64 = Debug|Win64 + DebugGame Client|HoloLens = DebugGame Client|HoloLens + DebugGame Client|Win64 = DebugGame Client|Win64 + DebugGame Editor|HoloLens = DebugGame Editor|HoloLens + DebugGame Editor|Win64 = DebugGame Editor|Win64 + DebugGame Server|HoloLens = DebugGame Server|HoloLens + DebugGame Server|Win64 = DebugGame Server|Win64 + DebugGame|HoloLens = DebugGame|HoloLens + DebugGame|Win64 = DebugGame|Win64 + Development Client|HoloLens = Development Client|HoloLens + Development Client|Win64 = Development Client|Win64 + Development Editor|HoloLens = Development Editor|HoloLens + Development Editor|Win64 = Development Editor|Win64 + Development Server|HoloLens = Development Server|HoloLens + Development Server|Win64 = Development Server|Win64 + Development|HoloLens = Development|HoloLens + Development|Win64 = Development|Win64 + Shipping Client|HoloLens = Shipping Client|HoloLens + Shipping Client|Win64 = Shipping Client|Win64 + Shipping Server|HoloLens = Shipping Server|HoloLens + Shipping Server|Win64 = Shipping Server|Win64 + Shipping|HoloLens = Shipping|HoloLens + Shipping|Win64 = Shipping|Win64 + Test Client|HoloLens = Test Client|HoloLens + Test Client|Win64 = Test Client|Win64 + Test Server|HoloLens = Test Server|HoloLens + Test Server|Win64 = Test Server|Win64 + Test|HoloLens = Test|HoloLens + Test|Win64 = Test|Win64 + EndGlobalSection + # UnrealVS Section + GlobalSection(ddbf523f-7eb6-4887-bd51-85a714ff87eb) = preSolution + AvailablePlatforms=Win64;HoloLens + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Debug|Win64.Build.0 = Debug_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Client|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Editor|Win64.Build.0 = Development_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development Server|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development|Win64.ActiveCfg = Development_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Development|Win64.Build.0 = Development_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test Client|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test Server|Win64.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test|HoloLens.ActiveCfg = Invalid|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test|Win64.ActiveCfg = Test_Program|x64 + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1}.Test|Win64.Build.0 = Test_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Debug|Win64.Build.0 = Debug_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Client|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Editor|Win64.Build.0 = Development_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development Server|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development|Win64.ActiveCfg = Development_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Development|Win64.Build.0 = Development_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test Client|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test Server|Win64.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test|HoloLens.ActiveCfg = Invalid|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test|Win64.ActiveCfg = Test_Program|x64 + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15}.Test|Win64.Build.0 = Test_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Debug|Win64.Build.0 = Debug_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame Editor|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.DebugGame|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Client|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Editor|Win64.Build.0 = Development_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development Server|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development|Win64.ActiveCfg = Development_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Development|Win64.Build.0 = Development_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Test Client|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Test Server|Win64.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Test|HoloLens.ActiveCfg = Invalid|x64 + {C95BFC5E-4DFC-3531-8D97-1674BB663777}.Test|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Debug|Win64.Build.0 = Debug_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Client|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Editor|Win64.Build.0 = Development_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development Server|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development|Win64.ActiveCfg = Development_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Development|Win64.Build.0 = Development_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test Client|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test Server|Win64.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test|HoloLens.ActiveCfg = Invalid|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test|Win64.ActiveCfg = Test_Program|x64 + {154D22D7-3460-351E-92EE-1294BAD94EF6}.Test|Win64.Build.0 = Test_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Debug|Win64.Build.0 = Debug_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame Editor|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.DebugGame|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Client|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Editor|Win64.Build.0 = Development_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development Server|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development|Win64.ActiveCfg = Development_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Development|Win64.Build.0 = Development_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Test Client|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Test Server|Win64.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Test|HoloLens.ActiveCfg = Invalid|x64 + {74FC9350-8DAD-3511-A9F8-EE4212F5A703}.Test|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Debug|Win64.Build.0 = Debug_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame Editor|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.DebugGame|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Client|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Editor|Win64.Build.0 = Development_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development Server|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development|Win64.ActiveCfg = Development_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Development|Win64.Build.0 = Development_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Test Client|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Test Server|Win64.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Test|HoloLens.ActiveCfg = Invalid|x64 + {D677E3A3-A3EB-373E-A872-C572DB91D8D5}.Test|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Debug|Win64.Build.0 = Debug_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Client|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Editor|Win64.Build.0 = Development_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development Server|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development|Win64.ActiveCfg = Development_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Development|Win64.Build.0 = Development_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test Client|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test Server|Win64.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test|HoloLens.ActiveCfg = Invalid|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test|Win64.ActiveCfg = Test_Program|x64 + {99B55C07-00CC-3720-94F1-4308D3DA7996}.Test|Win64.Build.0 = Test_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development Client|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development Server|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Development|Win64.ActiveCfg = Development_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Test Client|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Test Server|Win64.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Test|HoloLens.ActiveCfg = Invalid|x64 + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100}.Test|Win64.ActiveCfg = Test_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development Client|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development Server|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Development|Win64.ActiveCfg = Development_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Test Client|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Test Server|Win64.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Test|HoloLens.ActiveCfg = Invalid|x64 + {615CEF89-0B8F-36F2-923F-8CE076D1166C}.Test|Win64.ActiveCfg = Test_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development Client|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development Server|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Development|Win64.ActiveCfg = Development_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Test Client|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Test Server|Win64.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Test|HoloLens.ActiveCfg = Invalid|x64 + {6B544390-589A-3FD0-8505-80D626898D3C}.Test|Win64.ActiveCfg = Test_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development Client|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development Server|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Development|Win64.ActiveCfg = Development_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Test Client|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Test Server|Win64.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Test|HoloLens.ActiveCfg = Invalid|x64 + {0CA3555A-FCE8-3A67-A084-32250A7BF82E}.Test|Win64.ActiveCfg = Test_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development Client|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development Server|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Development|Win64.ActiveCfg = Development_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Test Client|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Test Server|Win64.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Test|HoloLens.ActiveCfg = Invalid|x64 + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5}.Test|Win64.ActiveCfg = Test_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development Client|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development Server|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Development|Win64.ActiveCfg = Development_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Test Client|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Test Server|Win64.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Test|HoloLens.ActiveCfg = Invalid|x64 + {1751912E-33B1-37BD-B30E-24D05BDEC2B2}.Test|Win64.ActiveCfg = Test_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development Client|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development Server|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Development|Win64.ActiveCfg = Development_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Test Client|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Test Server|Win64.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Test|HoloLens.ActiveCfg = Invalid|x64 + {401D1B4F-84A5-3A92-A19C-279947EED6ED}.Test|Win64.ActiveCfg = Test_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development Client|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development Server|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Development|Win64.ActiveCfg = Development_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Test Client|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Test Server|Win64.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Test|HoloLens.ActiveCfg = Invalid|x64 + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06}.Test|Win64.ActiveCfg = Test_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development Client|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development Server|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Development|Win64.ActiveCfg = Development_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Test Client|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Test Server|Win64.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Test|HoloLens.ActiveCfg = Invalid|x64 + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C}.Test|Win64.ActiveCfg = Test_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development Client|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development Server|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Development|Win64.ActiveCfg = Development_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Test Client|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Test Server|Win64.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Test|HoloLens.ActiveCfg = Invalid|x64 + {ABAF7BAF-6484-3771-B35C-4957A2D729C8}.Test|Win64.ActiveCfg = Test_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development Client|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development Server|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Development|Win64.ActiveCfg = Development_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Test Client|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Test Server|Win64.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Test|HoloLens.ActiveCfg = Invalid|x64 + {75041236-9D97-3112-B228-15D19A8D8400}.Test|Win64.ActiveCfg = Test_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development Client|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development Server|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Development|Win64.ActiveCfg = Development_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Test Client|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Test Server|Win64.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Test|HoloLens.ActiveCfg = Invalid|x64 + {8C81677C-E927-3CBD-A61F-2579B76096E5}.Test|Win64.ActiveCfg = Test_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development Client|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development Server|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Development|Win64.ActiveCfg = Development_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Test Client|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Test Server|Win64.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Test|HoloLens.ActiveCfg = Invalid|x64 + {7663D84B-A880-3A97-9E6B-DF88CAE059FE}.Test|Win64.ActiveCfg = Test_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development Client|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development Server|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Development|Win64.ActiveCfg = Development_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Test Client|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Test Server|Win64.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Test|HoloLens.ActiveCfg = Invalid|x64 + {EEE7D7F0-A860-3966-8408-6E3484F1431E}.Test|Win64.ActiveCfg = Test_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development Client|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development Server|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Development|Win64.ActiveCfg = Development_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Test Client|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Test Server|Win64.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Test|HoloLens.ActiveCfg = Invalid|x64 + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8}.Test|Win64.ActiveCfg = Test_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Debug|Win64.Build.0 = Debug_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Client|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Editor|Win64.Build.0 = Development_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development Server|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development|Win64.ActiveCfg = Development_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Development|Win64.Build.0 = Development_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test Client|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test Server|Win64.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test|HoloLens.ActiveCfg = Invalid|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test|Win64.ActiveCfg = Test_Program|x64 + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F}.Test|Win64.Build.0 = Test_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development Client|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development Server|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Development|Win64.ActiveCfg = Development_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Test Client|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Test Server|Win64.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Test|HoloLens.ActiveCfg = Invalid|x64 + {0669598D-617C-3B37-AE25-2E7BC0CEF530}.Test|Win64.ActiveCfg = Test_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development Client|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development Server|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Development|Win64.ActiveCfg = Development_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Test Client|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Test Server|Win64.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Test|HoloLens.ActiveCfg = Invalid|x64 + {0F4E2BA4-8270-3AD4-831A-1CE849085505}.Test|Win64.ActiveCfg = Test_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development Client|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development Server|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Development|Win64.ActiveCfg = Development_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Test Client|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Test Server|Win64.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Test|HoloLens.ActiveCfg = Invalid|x64 + {725A117A-0FB4-3FF3-B69C-210BE9BE753D}.Test|Win64.ActiveCfg = Test_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development Client|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development Server|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Development|Win64.ActiveCfg = Development_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Test Client|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Test Server|Win64.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Test|HoloLens.ActiveCfg = Invalid|x64 + {71A0B529-31B6-3A3E-A59D-6276170F18B3}.Test|Win64.ActiveCfg = Test_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Debug|Win64.Build.0 = Debug_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame Editor|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.DebugGame|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Client|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Editor|Win64.Build.0 = Development_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development Server|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development|Win64.ActiveCfg = Development_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Development|Win64.Build.0 = Development_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Test Client|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Test Server|Win64.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Test|HoloLens.ActiveCfg = Invalid|x64 + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1}.Test|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Editor|HoloLens.ActiveCfg = Debug_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Editor|HoloLens.Build.0 = Debug_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Editor|HoloLens.Deploy.0 = Debug_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug|HoloLens.ActiveCfg = Debug_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug|HoloLens.Build.0 = Debug_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug|HoloLens.Deploy.0 = Debug_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Debug|Win64.Build.0 = Debug_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Editor|HoloLens.ActiveCfg = DebugGame_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Editor|HoloLens.Build.0 = DebugGame_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Editor|HoloLens.Deploy.0 = DebugGame_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame|HoloLens.ActiveCfg = DebugGame_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame|HoloLens.Build.0 = DebugGame_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame|HoloLens.Deploy.0 = DebugGame_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Client|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Editor|HoloLens.ActiveCfg = Development_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Editor|HoloLens.Build.0 = Development_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Editor|HoloLens.Deploy.0 = Development_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Editor|Win64.Build.0 = Development_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development Server|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development|HoloLens.ActiveCfg = Development_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development|HoloLens.Build.0 = Development_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development|HoloLens.Deploy.0 = Development_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development|Win64.ActiveCfg = Development_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Development|Win64.Build.0 = Development_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping|HoloLens.ActiveCfg = Shipping_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping|HoloLens.Build.0 = Shipping_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping|HoloLens.Deploy.0 = Shipping_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test Client|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test Server|Win64.ActiveCfg = Invalid|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test|HoloLens.ActiveCfg = Test_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test|HoloLens.Build.0 = Test_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test|HoloLens.Deploy.0 = Test_Program|arm64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test|Win64.ActiveCfg = Test_Program|x64 + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7}.Test|Win64.Build.0 = Test_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Debug|Win64.Build.0 = Debug_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Client|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Editor|Win64.Build.0 = Development_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development Server|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development|Win64.ActiveCfg = Development_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Development|Win64.Build.0 = Development_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test Client|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test Server|Win64.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test|HoloLens.ActiveCfg = Invalid|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test|Win64.ActiveCfg = Test_Program|x64 + {362F809C-0D32-39C8-A9FB-B623889D2D4D}.Test|Win64.Build.0 = Test_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Debug|Win64.Build.0 = Debug_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Client|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Editor|Win64.Build.0 = Development_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development Server|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development|Win64.ActiveCfg = Development_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Development|Win64.Build.0 = Development_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test Client|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test Server|Win64.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test|HoloLens.ActiveCfg = Invalid|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test|Win64.ActiveCfg = Test_Program|x64 + {141AC720-D423-3A69-AB54-A18F99797871}.Test|Win64.Build.0 = Test_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Debug|Win64.Build.0 = Debug_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Client|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Editor|Win64.Build.0 = Development_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development Server|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development|Win64.ActiveCfg = Development_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Development|Win64.Build.0 = Development_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test Client|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test Server|Win64.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test|HoloLens.ActiveCfg = Invalid|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test|Win64.ActiveCfg = Test_Program|x64 + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D}.Test|Win64.Build.0 = Test_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Debug|Win64.Build.0 = Debug_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Client|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Editor|Win64.Build.0 = Development_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development Server|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development|Win64.ActiveCfg = Development_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Development|Win64.Build.0 = Development_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test Client|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test Server|Win64.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test|HoloLens.ActiveCfg = Invalid|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test|Win64.ActiveCfg = Test_Program|x64 + {AE589633-6E7B-38F8-86D6-127025F615D9}.Test|Win64.Build.0 = Test_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Debug|Win64.Build.0 = Debug_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Client|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Editor|Win64.Build.0 = Development_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development Server|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development|Win64.ActiveCfg = Development_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Development|Win64.Build.0 = Development_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test Client|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test Server|Win64.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test|HoloLens.ActiveCfg = Invalid|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test|Win64.ActiveCfg = Test_Program|x64 + {23A26DC2-5829-359E-AA28-11DE040D8584}.Test|Win64.Build.0 = Test_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Debug|Win64.Build.0 = Debug_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Client|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Editor|Win64.Build.0 = Development_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development Server|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development|Win64.ActiveCfg = Development_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Development|Win64.Build.0 = Development_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test Client|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test Server|Win64.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test|HoloLens.ActiveCfg = Invalid|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test|Win64.ActiveCfg = Test_Program|x64 + {64ADF73D-8814-3B1F-9C32-570DE73879CC}.Test|Win64.Build.0 = Test_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development Client|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development Server|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Development|Win64.ActiveCfg = Development_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Test Client|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Test Server|Win64.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Test|HoloLens.ActiveCfg = Invalid|x64 + {E25A3458-DE88-3E16-B848-8E2CF56A14E8}.Test|Win64.ActiveCfg = Test_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Debug|Win64.Build.0 = Debug_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Client|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Editor|Win64.Build.0 = Development_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development Server|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development|Win64.ActiveCfg = Development_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Development|Win64.Build.0 = Development_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test Client|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test Server|Win64.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test|HoloLens.ActiveCfg = Invalid|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test|Win64.ActiveCfg = Test_Program|x64 + {FBC3D599-658A-3D61-A8AA-5927E12DACA8}.Test|Win64.Build.0 = Test_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Debug|Win64.Build.0 = Debug_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Client|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Editor|Win64.Build.0 = Development_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development Server|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development|Win64.ActiveCfg = Development_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Development|Win64.Build.0 = Development_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test Client|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test Server|Win64.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test|HoloLens.ActiveCfg = Invalid|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test|Win64.ActiveCfg = Test_Program|x64 + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6}.Test|Win64.Build.0 = Test_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Debug|Win64.Build.0 = Debug_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Client|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Editor|Win64.Build.0 = Development_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development Server|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development|Win64.ActiveCfg = Development_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Development|Win64.Build.0 = Development_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test Client|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test Server|Win64.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test|HoloLens.ActiveCfg = Invalid|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test|Win64.ActiveCfg = Test_Program|x64 + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8}.Test|Win64.Build.0 = Test_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Debug|Win64.Build.0 = Debug_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Client|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Editor|Win64.Build.0 = Development_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development Server|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development|Win64.ActiveCfg = Development_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Development|Win64.Build.0 = Development_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test Client|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test Server|Win64.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test|HoloLens.ActiveCfg = Invalid|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test|Win64.ActiveCfg = Test_Program|x64 + {97B1CED6-8204-3641-B989-B0113201B608}.Test|Win64.Build.0 = Test_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Debug|Win64.Build.0 = Debug_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Client|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Editor|Win64.Build.0 = Development_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development Server|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development|Win64.ActiveCfg = Development_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Development|Win64.Build.0 = Development_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test Client|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test Server|Win64.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test|HoloLens.ActiveCfg = Invalid|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test|Win64.ActiveCfg = Test_Program|x64 + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6}.Test|Win64.Build.0 = Test_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Debug|Win64.Build.0 = Debug_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Client|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Editor|Win64.Build.0 = Development_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development Server|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development|Win64.ActiveCfg = Development_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Development|Win64.Build.0 = Development_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test Client|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test Server|Win64.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test|HoloLens.ActiveCfg = Invalid|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test|Win64.ActiveCfg = Test_Program|x64 + {A162AD37-AB12-3725-BEB4-E94EE93F2756}.Test|Win64.Build.0 = Test_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Debug|Win64.Build.0 = Debug_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Client|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Editor|Win64.Build.0 = Development_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development Server|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development|Win64.ActiveCfg = Development_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Development|Win64.Build.0 = Development_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test Client|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test Server|Win64.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test|HoloLens.ActiveCfg = Invalid|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test|Win64.ActiveCfg = Test_Program|x64 + {72395ABF-E458-3B53-90D7-D25940B9C45E}.Test|Win64.Build.0 = Test_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Debug|Win64.Build.0 = Debug_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Client|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Editor|Win64.Build.0 = Development_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development Server|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development|Win64.ActiveCfg = Development_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Development|Win64.Build.0 = Development_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test Client|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test Server|Win64.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test|HoloLens.ActiveCfg = Invalid|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test|Win64.ActiveCfg = Test_Program|x64 + {EA694FFC-58C0-34D1-8539-645A5F1C1859}.Test|Win64.Build.0 = Test_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Debug|Win64.Build.0 = Debug_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Client|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Editor|Win64.Build.0 = Development_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development Server|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development|Win64.ActiveCfg = Development_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Development|Win64.Build.0 = Development_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test Client|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test Server|Win64.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test|HoloLens.ActiveCfg = Invalid|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test|Win64.ActiveCfg = Test_Program|x64 + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B}.Test|Win64.Build.0 = Test_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Debug|Win64.Build.0 = Debug_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Editor|Win64.Build.0 = DebugGame_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.DebugGame|Win64.Build.0 = DebugGame_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Client|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Editor|Win64.Build.0 = Development_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development Server|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development|Win64.ActiveCfg = Development_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Development|Win64.Build.0 = Development_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test Client|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test Server|Win64.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test|HoloLens.ActiveCfg = Invalid|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test|Win64.ActiveCfg = Test_Program|x64 + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207}.Test|Win64.Build.0 = Test_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development Client|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development Server|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Development|Win64.ActiveCfg = Development_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Test Client|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Test Server|Win64.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Test|HoloLens.ActiveCfg = Invalid|x64 + {A1B06536-06A2-3FE7-A9C7-A8359B888177}.Test|Win64.ActiveCfg = Test_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.DebugGame|Win64.ActiveCfg = DebugGame_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development Client|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development Server|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Development|Win64.ActiveCfg = Development_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Test Client|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Test Server|Win64.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Test|HoloLens.ActiveCfg = Invalid|x64 + {5140C054-F83C-3C47-BEBE-0E6D55D90175}.Test|Win64.ActiveCfg = Test_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Editor|Win64.ActiveCfg = Debug_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Editor|Win64.Build.0 = Debug_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug|Win64.ActiveCfg = Debug_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Debug|Win64.Build.0 = Debug_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame Editor|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.DebugGame|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Client|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Editor|Win64.ActiveCfg = Development_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Editor|Win64.Build.0 = Development_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development Server|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development|Win64.ActiveCfg = Development_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Development|Win64.Build.0 = Development_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping|Win64.ActiveCfg = Shipping_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Shipping|Win64.Build.0 = Shipping_Program|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Test Client|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Test Server|Win64.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Test|HoloLens.ActiveCfg = Invalid|x64 + {671C936A-EBEA-31C4-9DC1-883582196280}.Test|Win64.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Client|HoloLens.ActiveCfg = Debug_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Client|HoloLens.Build.0 = Debug_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Client|HoloLens.Deploy.0 = Debug_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Client|Win64.ActiveCfg = Debug_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Client|Win64.Build.0 = Debug_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Editor|Win64.ActiveCfg = Debug_Editor|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Editor|Win64.Build.0 = Debug_Editor|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Server|Win64.ActiveCfg = Debug_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug Server|Win64.Build.0 = Debug_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug|HoloLens.ActiveCfg = Debug|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug|HoloLens.Build.0 = Debug|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug|HoloLens.Deploy.0 = Debug|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug|Win64.ActiveCfg = Debug|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Debug|Win64.Build.0 = Debug|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Client|HoloLens.ActiveCfg = DebugGame_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Client|HoloLens.Build.0 = DebugGame_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Client|HoloLens.Deploy.0 = DebugGame_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Client|Win64.ActiveCfg = DebugGame_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Client|Win64.Build.0 = DebugGame_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Editor|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Editor|Win64.Build.0 = DebugGame_Editor|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Server|Win64.ActiveCfg = DebugGame_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame Server|Win64.Build.0 = DebugGame_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame|HoloLens.ActiveCfg = DebugGame|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame|HoloLens.Build.0 = DebugGame|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame|HoloLens.Deploy.0 = DebugGame|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame|Win64.ActiveCfg = DebugGame|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.DebugGame|Win64.Build.0 = DebugGame|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Client|HoloLens.ActiveCfg = Development_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Client|HoloLens.Build.0 = Development_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Client|HoloLens.Deploy.0 = Development_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Client|Win64.ActiveCfg = Development_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Client|Win64.Build.0 = Development_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Editor|Win64.ActiveCfg = Development_Editor|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Editor|Win64.Build.0 = Development_Editor|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Server|Win64.ActiveCfg = Development_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development Server|Win64.Build.0 = Development_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development|HoloLens.ActiveCfg = Development|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development|HoloLens.Build.0 = Development|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development|HoloLens.Deploy.0 = Development|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development|Win64.ActiveCfg = Development|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Development|Win64.Build.0 = Development|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Client|HoloLens.ActiveCfg = Shipping_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Client|HoloLens.Build.0 = Shipping_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Client|HoloLens.Deploy.0 = Shipping_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Client|Win64.ActiveCfg = Shipping_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Client|Win64.Build.0 = Shipping_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Server|Win64.ActiveCfg = Shipping_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping Server|Win64.Build.0 = Shipping_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping|HoloLens.ActiveCfg = Shipping|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping|HoloLens.Build.0 = Shipping|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping|HoloLens.Deploy.0 = Shipping|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping|Win64.ActiveCfg = Shipping|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Shipping|Win64.Build.0 = Shipping|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Client|HoloLens.ActiveCfg = Test_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Client|HoloLens.Build.0 = Test_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Client|HoloLens.Deploy.0 = Test_Client|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Client|Win64.ActiveCfg = Test_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Client|Win64.Build.0 = Test_Client|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Server|Win64.ActiveCfg = Test_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test Server|Win64.Build.0 = Test_Server|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test|HoloLens.ActiveCfg = Test|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test|HoloLens.Build.0 = Test|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test|HoloLens.Deploy.0 = Test|arm64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test|Win64.ActiveCfg = Test|x64 + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C}.Test|Win64.Build.0 = Test|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Client|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Client|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Editor|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Editor|Win64.ActiveCfg = Debug_Editor|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Editor|Win64.Build.0 = Debug_Editor|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Server|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug Server|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug|HoloLens.ActiveCfg = Debug|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug|HoloLens.Build.0 = Debug|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug|HoloLens.Deploy.0 = Debug|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug|Win64.ActiveCfg = Debug|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Debug|Win64.Build.0 = Debug|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Client|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Client|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Editor|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Editor|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Editor|Win64.Build.0 = DebugGame_Editor|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Server|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame Server|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame|HoloLens.ActiveCfg = DebugGame|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame|HoloLens.Build.0 = DebugGame|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame|HoloLens.Deploy.0 = DebugGame|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame|Win64.ActiveCfg = DebugGame|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.DebugGame|Win64.Build.0 = DebugGame|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Client|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Client|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Editor|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Editor|Win64.ActiveCfg = Development_Editor|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Editor|Win64.Build.0 = Development_Editor|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Server|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development Server|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development|HoloLens.ActiveCfg = Development|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development|HoloLens.Build.0 = Development|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development|HoloLens.Deploy.0 = Development|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development|Win64.ActiveCfg = Development|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Development|Win64.Build.0 = Development|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping Client|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping Client|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping Server|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping Server|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping|HoloLens.ActiveCfg = Shipping|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping|HoloLens.Build.0 = Shipping|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping|HoloLens.Deploy.0 = Shipping|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping|Win64.ActiveCfg = Shipping|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Shipping|Win64.Build.0 = Shipping|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test Client|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test Client|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test Server|HoloLens.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test Server|Win64.ActiveCfg = Invalid|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test|HoloLens.ActiveCfg = Test|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test|HoloLens.Build.0 = Test|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test|HoloLens.Deploy.0 = Test|arm64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test|Win64.ActiveCfg = Test|x64 + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358}.Test|Win64.Build.0 = Test|x64 + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Client|Win64.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug Server|Win64.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug|HoloLens.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug|Win64.ActiveCfg = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Debug|Win64.Build.0 = Debug|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.DebugGame|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Client|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Client|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Client|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Editor|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Server|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Server|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development Server|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Development|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Client|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping Server|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Shipping|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Client|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Client|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Client|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Server|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Server|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test Server|Win64.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test|HoloLens.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test|HoloLens.Build.0 = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test|Win64.ActiveCfg = Development|Any CPU + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F}.Test|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Client|Win64.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug Server|Win64.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug|HoloLens.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug|Win64.ActiveCfg = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Debug|Win64.Build.0 = Debug|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.DebugGame|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Client|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Client|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Client|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Editor|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Server|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Server|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development Server|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Development|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Client|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping Server|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Shipping|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Client|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Client|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Client|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Server|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Server|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test Server|Win64.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test|HoloLens.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test|HoloLens.Build.0 = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test|Win64.ActiveCfg = Development|Any CPU + {FE372125-7A72-3A78-8984-732B784D3C65}.Test|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Client|Win64.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug Server|Win64.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug|HoloLens.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug|Win64.ActiveCfg = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Debug|Win64.Build.0 = Debug|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.DebugGame|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Client|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Client|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Client|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Editor|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Server|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Server|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development Server|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Development|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Client|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping Server|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Shipping|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Client|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Client|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Client|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Server|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Server|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test Server|Win64.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test|HoloLens.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test|HoloLens.Build.0 = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test|Win64.ActiveCfg = Development|Any CPU + {7237F39C-0653-372F-A495-3219761D457D}.Test|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Client|Win64.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug Server|Win64.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug|HoloLens.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug|Win64.ActiveCfg = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Debug|Win64.Build.0 = Debug|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.DebugGame|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Client|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Client|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Client|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Editor|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Server|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Server|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development Server|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Development|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Client|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping Server|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Shipping|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Client|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Client|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Client|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Server|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Server|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test Server|Win64.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test|HoloLens.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test|HoloLens.Build.0 = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test|Win64.ActiveCfg = Development|Any CPU + {199732AC-C2EB-3676-B905-60397D421964}.Test|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Client|Win64.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug Server|Win64.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug|HoloLens.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug|Win64.ActiveCfg = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Debug|Win64.Build.0 = Debug|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.DebugGame|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Client|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Client|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Client|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Editor|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Server|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Server|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development Server|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Development|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Client|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping Server|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Shipping|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Client|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Client|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Client|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Server|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Server|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test Server|Win64.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test|HoloLens.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test|HoloLens.Build.0 = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test|Win64.ActiveCfg = Development|Any CPU + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C}.Test|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Client|Win64.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug Server|Win64.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug|HoloLens.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug|Win64.ActiveCfg = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Debug|Win64.Build.0 = Debug|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.DebugGame|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Client|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Client|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Client|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Editor|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Server|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Server|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development Server|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Development|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Client|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping Server|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Shipping|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Client|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Client|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Client|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Server|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Server|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test Server|Win64.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test|HoloLens.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test|HoloLens.Build.0 = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test|Win64.ActiveCfg = Development|Any CPU + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3}.Test|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Client|Win64.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug Server|Win64.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug|HoloLens.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug|Win64.ActiveCfg = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Debug|Win64.Build.0 = Debug|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.DebugGame|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Client|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Client|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Client|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Editor|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Server|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Server|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development Server|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Development|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Client|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping Server|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Shipping|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Client|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Client|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Client|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Server|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Server|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test Server|Win64.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test|HoloLens.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test|HoloLens.Build.0 = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test|Win64.ActiveCfg = Development|Any CPU + {328BDFF1-6D81-3942-A143-A3CE5E55C73E}.Test|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Client|Win64.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug Server|Win64.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug|HoloLens.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug|Win64.ActiveCfg = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Debug|Win64.Build.0 = Debug|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.DebugGame|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Client|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Client|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Client|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Editor|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Server|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Server|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development Server|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Development|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Client|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping Server|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Shipping|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Client|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Client|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Client|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Server|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Server|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test Server|Win64.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test|HoloLens.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test|HoloLens.Build.0 = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test|Win64.ActiveCfg = Development|Any CPU + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1}.Test|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Client|Win64.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug Server|Win64.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug|HoloLens.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug|Win64.ActiveCfg = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Debug|Win64.Build.0 = Debug|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.DebugGame|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Client|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Client|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Client|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Editor|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Server|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Server|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development Server|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Development|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Client|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping Server|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Shipping|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Client|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Client|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Client|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Server|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Server|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test Server|Win64.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test|HoloLens.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test|HoloLens.Build.0 = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test|Win64.ActiveCfg = Development|Any CPU + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087}.Test|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Client|Win64.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug Server|Win64.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug|HoloLens.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug|Win64.ActiveCfg = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Debug|Win64.Build.0 = Debug|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.DebugGame|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Client|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Client|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Client|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Editor|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Server|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Server|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development Server|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Development|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Client|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping Server|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Shipping|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Client|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Client|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Client|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Server|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Server|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test Server|Win64.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test|HoloLens.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test|HoloLens.Build.0 = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test|Win64.ActiveCfg = Development|Any CPU + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056}.Test|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Client|Win64.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug Server|Win64.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug|HoloLens.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug|Win64.ActiveCfg = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Debug|Win64.Build.0 = Debug|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.DebugGame|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Client|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Client|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Client|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Editor|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Server|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Server|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development Server|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Development|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Client|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping Server|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Shipping|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Client|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Client|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Client|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Server|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Server|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test Server|Win64.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test|HoloLens.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test|HoloLens.Build.0 = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test|Win64.ActiveCfg = Development|Any CPU + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2}.Test|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Client|Win64.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug Server|Win64.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug|HoloLens.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug|Win64.ActiveCfg = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Debug|Win64.Build.0 = Debug|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.DebugGame|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Client|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Client|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Client|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Editor|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Server|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Server|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development Server|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Development|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Client|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping Server|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Shipping|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Client|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Client|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Client|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Server|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Server|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test Server|Win64.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test|HoloLens.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test|HoloLens.Build.0 = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test|Win64.ActiveCfg = Development|Any CPU + {C7D60A52-C992-3269-9520-8280D5058ED7}.Test|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Client|Win64.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug Server|Win64.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug|HoloLens.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug|Win64.ActiveCfg = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Debug|Win64.Build.0 = Debug|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.DebugGame|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Client|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Client|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Client|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Editor|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Server|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Server|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development Server|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Development|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Client|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping Server|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Shipping|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Client|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Client|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Client|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Server|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Server|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test Server|Win64.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test|HoloLens.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test|HoloLens.Build.0 = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test|Win64.ActiveCfg = Development|Any CPU + {CB55C95C-91E5-32B6-BEC1-F855A72F7408}.Test|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Client|Win64.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug Server|Win64.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug|HoloLens.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug|Win64.ActiveCfg = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Debug|Win64.Build.0 = Debug|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.DebugGame|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Client|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Client|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Client|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Editor|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Server|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Server|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development Server|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Development|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Client|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping Server|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Shipping|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Client|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Client|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Client|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Server|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Server|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test Server|Win64.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test|HoloLens.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test|HoloLens.Build.0 = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test|Win64.ActiveCfg = Development|Any CPU + {F21DEF2A-BFC0-3368-BD0C-824A77704513}.Test|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Client|Win64.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug Server|Win64.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug|HoloLens.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug|Win64.ActiveCfg = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Debug|Win64.Build.0 = Debug|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.DebugGame|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Client|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Client|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Client|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Editor|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Server|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Server|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development Server|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Development|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Client|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping Server|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Shipping|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Client|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Client|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Client|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Server|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Server|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test Server|Win64.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test|HoloLens.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test|HoloLens.Build.0 = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test|Win64.ActiveCfg = Development|Any CPU + {E53FDC1E-0E32-3F89-B571-76C13F991C52}.Test|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Client|Win64.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug Server|Win64.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug|HoloLens.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug|Win64.ActiveCfg = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Debug|Win64.Build.0 = Debug|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.DebugGame|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Client|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Client|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Client|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Editor|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Server|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Server|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development Server|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Development|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Client|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping Server|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Shipping|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Client|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Client|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Client|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Server|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Server|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test Server|Win64.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test|HoloLens.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test|HoloLens.Build.0 = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test|Win64.ActiveCfg = Development|Any CPU + {B79003E3-8008-34C1-845A-4820F93D6F6D}.Test|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Client|Win64.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug Server|Win64.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug|HoloLens.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug|Win64.ActiveCfg = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Debug|Win64.Build.0 = Debug|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.DebugGame|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Client|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Client|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Client|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Editor|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Server|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Server|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development Server|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Development|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Client|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping Server|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Shipping|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Client|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Client|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Client|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Server|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Server|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test Server|Win64.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test|HoloLens.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test|HoloLens.Build.0 = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test|Win64.ActiveCfg = Development|Any CPU + {11CB4E3A-D028-3291-8B6B-64A69A2E5301}.Test|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Client|Win64.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug Server|Win64.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug|HoloLens.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug|Win64.ActiveCfg = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Debug|Win64.Build.0 = Debug|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.DebugGame|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Client|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Client|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Client|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Editor|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Server|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Server|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development Server|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Development|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Client|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping Server|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Shipping|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Client|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Client|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Client|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Server|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Server|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test Server|Win64.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test|HoloLens.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test|HoloLens.Build.0 = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test|Win64.ActiveCfg = Development|Any CPU + {7C617DDB-9845-358F-9D30-C2DA01E77EF7}.Test|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Client|Win64.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug Server|Win64.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug|HoloLens.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug|Win64.ActiveCfg = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Debug|Win64.Build.0 = Debug|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.DebugGame|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Client|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Client|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Client|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Editor|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Server|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Server|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development Server|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Development|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Client|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping Server|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Shipping|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Client|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Client|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Client|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Server|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Server|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test Server|Win64.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test|HoloLens.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test|HoloLens.Build.0 = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test|Win64.ActiveCfg = Development|Any CPU + {45F6849F-406D-3F90-83FC-2CC37A713A92}.Test|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Client|Win64.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug Server|Win64.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug|HoloLens.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug|Win64.ActiveCfg = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Debug|Win64.Build.0 = Debug|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.DebugGame|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Client|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Client|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Client|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Editor|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Server|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Server|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development Server|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Development|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Client|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping Server|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Shipping|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Client|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Client|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Client|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Server|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Server|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test Server|Win64.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test|HoloLens.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test|HoloLens.Build.0 = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test|Win64.ActiveCfg = Development|Any CPU + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957}.Test|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Client|Win64.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug Server|Win64.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug|HoloLens.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug|Win64.ActiveCfg = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Debug|Win64.Build.0 = Debug|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.DebugGame|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Client|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Client|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Client|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Editor|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Server|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Server|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development Server|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Development|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Client|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping Server|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Shipping|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Client|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Client|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Client|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Server|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Server|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test Server|Win64.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test|HoloLens.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test|HoloLens.Build.0 = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test|Win64.ActiveCfg = Development|Any CPU + {131B3BA2-EC95-3762-A5DE-C59178136735}.Test|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Client|Win64.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug Server|Win64.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug|HoloLens.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug|Win64.ActiveCfg = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Debug|Win64.Build.0 = Debug|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.DebugGame|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Client|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Client|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Client|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Editor|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Server|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Server|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development Server|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Development|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Client|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping Server|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Shipping|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Client|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Client|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Client|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Server|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Server|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test Server|Win64.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test|HoloLens.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test|HoloLens.Build.0 = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test|Win64.ActiveCfg = Development|Any CPU + {C575DBCB-0998-3726-9A3A-8B12E9A89316}.Test|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Client|Win64.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug Server|Win64.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug|HoloLens.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug|Win64.ActiveCfg = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Debug|Win64.Build.0 = Debug|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.DebugGame|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Client|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Client|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Client|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Editor|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Server|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Server|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development Server|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Development|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Client|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping Server|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Shipping|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Client|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Client|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Client|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Server|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Server|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test Server|Win64.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test|HoloLens.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test|HoloLens.Build.0 = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test|Win64.ActiveCfg = Development|Any CPU + {49033409-2B45-344C-A58C-9A21741802A9}.Test|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Client|Win64.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug Server|Win64.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug|HoloLens.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug|Win64.ActiveCfg = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Debug|Win64.Build.0 = Debug|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.DebugGame|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Client|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Client|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Client|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Editor|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Server|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Server|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development Server|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Development|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Client|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping Server|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Shipping|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Client|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Client|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Client|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Server|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Server|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test Server|Win64.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test|HoloLens.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test|HoloLens.Build.0 = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test|Win64.ActiveCfg = Development|Any CPU + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0}.Test|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Client|Win64.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug Server|Win64.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug|HoloLens.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug|Win64.ActiveCfg = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Debug|Win64.Build.0 = Debug|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.DebugGame|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Client|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Client|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Client|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Editor|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Server|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Server|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development Server|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Development|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Client|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping Server|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Shipping|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Client|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Client|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Client|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Server|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Server|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test Server|Win64.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test|HoloLens.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test|HoloLens.Build.0 = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test|Win64.ActiveCfg = Development|Any CPU + {9B740143-841C-4AF7-905E-D8883F1735DE}.Test|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Client|Win64.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug Server|Win64.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug|HoloLens.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug|Win64.ActiveCfg = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Debug|Win64.Build.0 = Debug|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.DebugGame|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Client|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Client|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Client|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Editor|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Server|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Server|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development Server|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Development|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Client|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping Server|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Shipping|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Client|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Client|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Client|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Server|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Server|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test Server|Win64.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test|HoloLens.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test|HoloLens.Build.0 = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test|Win64.ActiveCfg = Development|Any CPU + {75240A67-CF47-4DDC-83B2-AD5D371F31BD}.Test|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Client|Win64.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug Server|Win64.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug|HoloLens.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug|Win64.ActiveCfg = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Debug|Win64.Build.0 = Debug|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.DebugGame|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Client|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Client|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Client|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Editor|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Server|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Server|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development Server|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Development|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Client|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping Server|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Shipping|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Client|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Client|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Client|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Server|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Server|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test Server|Win64.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test|HoloLens.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test|HoloLens.Build.0 = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test|Win64.ActiveCfg = Development|Any CPU + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4}.Test|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Client|HoloLens.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Client|HoloLens.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Client|Win64.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Client|Win64.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Editor|HoloLens.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Editor|HoloLens.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Editor|Win64.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Editor|Win64.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Server|HoloLens.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Server|HoloLens.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Server|Win64.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug Server|Win64.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug|HoloLens.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug|HoloLens.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug|Win64.ActiveCfg = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Debug|Win64.Build.0 = Debug|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Client|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Client|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Client|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Client|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Editor|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Editor|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Editor|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Editor|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Server|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Server|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Server|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame Server|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.DebugGame|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Client|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Client|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Client|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Client|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Editor|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Editor|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Editor|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Editor|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Server|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Server|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Server|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development Server|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Development|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Client|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Client|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Client|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Client|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Server|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Server|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Server|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping Server|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Shipping|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Client|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Client|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Client|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Client|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Server|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Server|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Server|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test Server|Win64.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test|HoloLens.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test|HoloLens.Build.0 = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test|Win64.ActiveCfg = Development|Any CPU + {89224712-325D-4500-A4A5-B102A3A8EE2F}.Test|Win64.Build.0 = Development|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {BD26F4E2-B9EF-3B5F-8AB6-33BF8AB8C43C} = {233774A8-CC9D-3FA9-86D1-90573E92B704} + {9864B3C7-AB7D-3585-8C5C-A8649CCB0358} = {DE1F8B53-6C02-3C13-9101-A7C8D96F3FF6} + {728B3CC4-E3A5-311B-BC40-BB4DAF6DE2E1} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {99DADDCA-7B91-3571-A3A6-C0B9ACF86D15} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {C95BFC5E-4DFC-3531-8D97-1674BB663777} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {154D22D7-3460-351E-92EE-1294BAD94EF6} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {74FC9350-8DAD-3511-A9F8-EE4212F5A703} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {D677E3A3-A3EB-373E-A872-C572DB91D8D5} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {0B77C1D7-4115-3DBB-9978-3E19DCE1C9C1} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {1401DF69-E2B8-31A8-AF33-3DC3904A39E7} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {141AC720-D423-3A69-AB54-A18F99797871} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {F9B2B90C-6067-3860-A8E6-8EBE0985DF5D} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {AE589633-6E7B-38F8-86D6-127025F615D9} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {23A26DC2-5829-359E-AA28-11DE040D8584} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {64ADF73D-8814-3B1F-9C32-570DE73879CC} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {FBC3D599-658A-3D61-A8AA-5927E12DACA8} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {5A08A1DD-EAD4-3708-BEA5-F02EAA157EB6} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {B6C2C796-7A31-37DE-A6D9-0749C1EA70B8} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {97B1CED6-8204-3641-B989-B0113201B608} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {BC71F0FF-BB86-309D-9079-60E0DAE21FC6} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {A162AD37-AB12-3725-BEB4-E94EE93F2756} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {72395ABF-E458-3B53-90D7-D25940B9C45E} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {EA694FFC-58C0-34D1-8539-645A5F1C1859} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {353249D9-CCDD-3A43-8DEF-1C7954B0E69B} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {51F544B4-A2E6-3D72-92BE-3D9C5D9C8207} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {671C936A-EBEA-31C4-9DC1-883582196280} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {35ADF0C8-80B6-3BFA-A3F9-45E0BC905D3F} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {FE372125-7A72-3A78-8984-732B784D3C65} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {C575DBCB-0998-3726-9A3A-8B12E9A89316} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {49033409-2B45-344C-A58C-9A21741802A9} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {CF078A43-9249-3A36-9D52-9F1CCAF82EF0} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {9B740143-841C-4AF7-905E-D8883F1735DE} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {75240A67-CF47-4DDC-83B2-AD5D371F31BD} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {0CD16871-AE33-44AC-9E84-40DBFAEB51C4} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {89224712-325D-4500-A4A5-B102A3A8EE2F} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {362F809C-0D32-39C8-A9FB-B623889D2D4D} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {FD3DFD2B-113B-36C4-8511-C60D48287AA6} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {74618045-8FDF-37E1-A2C4-C2795A6DAF24} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {57713676-9DBE-331C-AD10-26632AC9EE0C} = {A338B9E2-A559-34BE-A46D-F789DD488FAD} + {99B55C07-00CC-3720-94F1-4308D3DA7996} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {8E5C46B5-A1AA-3B6A-B3F3-75D96CDD8100} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {615CEF89-0B8F-36F2-923F-8CE076D1166C} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {6B544390-589A-3FD0-8505-80D626898D3C} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {0CA3555A-FCE8-3A67-A084-32250A7BF82E} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {FBF0A356-D9BF-3D56-B344-475E9BFC5BE5} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {1751912E-33B1-37BD-B30E-24D05BDEC2B2} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {401D1B4F-84A5-3A92-A19C-279947EED6ED} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {56C20C58-BE0C-3F83-BF50-6DAED75FFB06} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {ACEE8A7F-D6EB-3B82-A3E6-18B2999CAF5C} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {ABAF7BAF-6484-3771-B35C-4957A2D729C8} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {75041236-9D97-3112-B228-15D19A8D8400} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {8C81677C-E927-3CBD-A61F-2579B76096E5} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {7663D84B-A880-3A97-9E6B-DF88CAE059FE} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {EEE7D7F0-A860-3966-8408-6E3484F1431E} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {EA2AE6E5-4BFC-38A4-B29C-51A4D364AED8} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {FE82D9F3-43E7-3CC2-96C4-CEC4B077D87F} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {0669598D-617C-3B37-AE25-2E7BC0CEF530} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {0F4E2BA4-8270-3AD4-831A-1CE849085505} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {725A117A-0FB4-3FF3-B69C-210BE9BE753D} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {71A0B529-31B6-3A3E-A59D-6276170F18B3} = {FD3DFD2B-113B-36C4-8511-C60D48287AA6} + {E25A3458-DE88-3E16-B848-8E2CF56A14E8} = {74618045-8FDF-37E1-A2C4-C2795A6DAF24} + {A1B06536-06A2-3FE7-A9C7-A8359B888177} = {74618045-8FDF-37E1-A2C4-C2795A6DAF24} + {5140C054-F83C-3C47-BEBE-0E6D55D90175} = {74618045-8FDF-37E1-A2C4-C2795A6DAF24} + {7237F39C-0653-372F-A495-3219761D457D} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {199732AC-C2EB-3676-B905-60397D421964} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {DE7C00C5-8717-3BB2-8634-6A7DD0228A0C} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {9B36FE7B-607E-3E33-A3FC-FA69E01B59C3} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {328BDFF1-6D81-3942-A143-A3CE5E55C73E} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {EC075F99-DF6B-3A0E-BA98-BB578D4613D1} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {02E88A55-E7FA-3D70-AFE6-CBB1DD869087} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {45DE9D3D-637A-3B92-B1B7-248CE5FEC056} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {F2A8EC7B-6FD3-310F-95E7-8D6CCEDAB7E2} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {C7D60A52-C992-3269-9520-8280D5058ED7} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {CB55C95C-91E5-32B6-BEC1-F855A72F7408} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {F21DEF2A-BFC0-3368-BD0C-824A77704513} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {E53FDC1E-0E32-3F89-B571-76C13F991C52} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {B79003E3-8008-34C1-845A-4820F93D6F6D} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {11CB4E3A-D028-3291-8B6B-64A69A2E5301} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {7C617DDB-9845-358F-9D30-C2DA01E77EF7} = {0BFBE63A-B98A-3411-8EEB-8918FEC737B3} + {45F6849F-406D-3F90-83FC-2CC37A713A92} = {57713676-9DBE-331C-AD10-26632AC9EE0C} + {A2DC5C7A-27C5-342A-8F9F-BA8DB4415957} = {57713676-9DBE-331C-AD10-26632AC9EE0C} + {131B3BA2-EC95-3762-A5DE-C59178136735} = {57713676-9DBE-331C-AD10-26632AC9EE0C} + EndGlobalSection +EndGlobal diff --git a/SMC_Build.uproject b/SMC_Build.uproject new file mode 100644 index 0000000..9f10696 --- /dev/null +++ b/SMC_Build.uproject @@ -0,0 +1,41 @@ +{ + "FileVersion": 3, + "EngineAssociation": "{24A6842D-4991-98A9-1A25-0DBE60CE4641}", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "SMC_Build", + "Type": "Runtime", + "LoadingPhase": "Default", + "AdditionalDependencies": [ + "Engine" + ] + } + ], + "Plugins": [ + { + "Name": "MeshUtilities2", + "Enabled": true + }, + { + "Name": "ModelingToolsEditorMode", + "Enabled": false + }, + { + "Name": "MeshModelingToolset", + "Enabled": false + }, + { + "Name": "RuntimeGeometryUtils", + "Enabled": true + }, + { + "Name": "Bridge", + "Enabled": false, + "SupportedTargetPlatforms": [ + "Win64" + ] + } + ] +} diff --git a/Source/SMC_Build.Target.cs b/Source/SMC_Build.Target.cs new file mode 100644 index 0000000..09cfe6e --- /dev/null +++ b/Source/SMC_Build.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class SMC_BuildTarget : TargetRules +{ + public SMC_BuildTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + DefaultBuildSettings = BuildSettingsVersion.Latest; + ExtraModuleNames.AddRange( new string[] { "SMC_Build" } ); + } +} diff --git a/Source/SMC_Build/MyBlueprintFunctionLibrary.cpp b/Source/SMC_Build/MyBlueprintFunctionLibrary.cpp new file mode 100644 index 0000000..af07fd7 --- /dev/null +++ b/Source/SMC_Build/MyBlueprintFunctionLibrary.cpp @@ -0,0 +1,15 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "MyBlueprintFunctionLibrary.h" + +void UMyBlueprintFunctionLibrary::MarkRenderStateDirty(UStaticMeshComponent* Comp) +{ + // Comp->MarkRenderStateDirty(); + Comp->GetScene()->UpdatePrimitiveDistanceFieldSceneData_GameThread(Comp); +} + +void UMyBlueprintFunctionLibrary::MarkRenderDynamicDataDirty(UStaticMeshComponent* Comp) +{ + Comp->MarkRenderDynamicDataDirty(); +} diff --git a/Source/SMC_Build/MyBlueprintFunctionLibrary.h b/Source/SMC_Build/MyBlueprintFunctionLibrary.h new file mode 100644 index 0000000..3930fe8 --- /dev/null +++ b/Source/SMC_Build/MyBlueprintFunctionLibrary.h @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "MyBlueprintFunctionLibrary.generated.h" + +/** + * + */ +UCLASS() +class SMC_BUILD_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable) + static void MarkRenderStateDirty(UStaticMeshComponent* Comp); + + UFUNCTION(BlueprintCallable) + static void MarkRenderDynamicDataDirty(UStaticMeshComponent* Comp); +}; diff --git a/Source/SMC_Build/SMCGameInstance.cpp b/Source/SMC_Build/SMCGameInstance.cpp new file mode 100644 index 0000000..bf6380f --- /dev/null +++ b/Source/SMC_Build/SMCGameInstance.cpp @@ -0,0 +1,15 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "SMCGameInstance.h" + +// #include "IMeshReductionManagerModule.h" +// #include "MeshUtilities.h" + +void USMCGameInstance::Shutdown() +{ +} + +void USMCGameInstance::OnStart() +{ +} diff --git a/Source/SMC_Build/SMCGameInstance.h b/Source/SMC_Build/SMCGameInstance.h new file mode 100644 index 0000000..03553b3 --- /dev/null +++ b/Source/SMC_Build/SMCGameInstance.h @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "SMCGameInstance.generated.h" + +/** + * + */ +UCLASS() +class SMC_BUILD_API USMCGameInstance : public UGameInstance +{ + GENERATED_BODY() + +public: + virtual void Shutdown() override; +protected: + virtual void OnStart() override; +}; diff --git a/Source/SMC_Build/SMC_Build.Build.cs b/Source/SMC_Build/SMC_Build.Build.cs new file mode 100644 index 0000000..50ddf4f --- /dev/null +++ b/Source/SMC_Build/SMC_Build.Build.cs @@ -0,0 +1,27 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class SMC_Build : ModuleRules +{ + public SMC_Build(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] + { + "Core", "CoreUObject", "Engine", "InputCore", + "DynamicMesh", + }); + + 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 + } +} diff --git a/Source/SMC_Build/SMC_Build.cpp b/Source/SMC_Build/SMC_Build.cpp new file mode 100644 index 0000000..16d2cec --- /dev/null +++ b/Source/SMC_Build/SMC_Build.cpp @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SMC_Build.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, SMC_Build, "SMC_Build" ); diff --git a/Source/SMC_Build/SMC_Build.h b/Source/SMC_Build/SMC_Build.h new file mode 100644 index 0000000..677c8e2 --- /dev/null +++ b/Source/SMC_Build/SMC_Build.h @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + diff --git a/Source/SMC_BuildEditor.Target.cs b/Source/SMC_BuildEditor.Target.cs new file mode 100644 index 0000000..8d40688 --- /dev/null +++ b/Source/SMC_BuildEditor.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class SMC_BuildEditorTarget : TargetRules +{ + public SMC_BuildEditorTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + DefaultBuildSettings = BuildSettingsVersion.Latest; + ExtraModuleNames.AddRange( new string[] { "SMC_Build" } ); + } +} diff --git a/修改引擎.md b/修改引擎.md new file mode 100644 index 0000000..92b6755 --- /dev/null +++ b/修改引擎.md @@ -0,0 +1,12 @@ +class ENGINE_API FStaticMeshRenderData +struct ENGINE_API FStaticMeshLODResources : public FRefCountBase + FStaticMeshLODResources(bool bAddRef = true); + ~FStaticMeshLODResources(); + +class ENGINE_API FObjectCacheContext + + +暴露这两个类后,对他们的方法都要取消暴露 + + +索引类改成cpu可访问