├── Config ├── DefaultEditor.ini ├── HoloLens │ └── HoloLensEngine.ini ├── DefaultEngine.ini ├── DefaultGame.ini └── DefaultInput.ini ├── Source ├── ToolsFrameworkDemo │ ├── ToolsFrameworkPlayerController.cpp │ ├── ToolsFrameworkDemo.h │ ├── ToolsFrameworkDemo.cpp │ ├── ToolsFrameworkPlayerController.h │ ├── ToolsFrameworkDemoGameModeBase.h │ ├── ToolsFrameworkDemo.Build.cs │ └── ToolsFrameworkDemoGameModeBase.cpp ├── RuntimeToolsSystem │ ├── Public │ │ ├── RuntimeToolsSystemModule.h │ │ ├── RuntimeToolsSystemModule.cpp │ │ ├── Tools │ │ │ ├── RuntimeRemeshMeshTool.h │ │ │ ├── RuntimeDrawPolygonTool.cpp │ │ │ ├── RuntimeMeshBooleanTool.h │ │ │ ├── RuntimeDynamicMeshSculptTool.h │ │ │ ├── RuntimeDrawPolygonTool.h │ │ │ ├── RuntimeRemeshMeshTool.cpp │ │ │ ├── RuntimePolyEditTool.cpp │ │ │ ├── RuntimePolyEditTool.h │ │ │ ├── RuntimeMeshBooleanTool.cpp │ │ │ └── RuntimeDynamicMeshSculptTool.cpp │ │ ├── RuntimeToolsFramework │ │ │ ├── RuntimeModelingObjectsCreationAPI.h │ │ │ ├── RuntimeModelingObjectsCreationAPI.cpp │ │ │ ├── ToolsContextActor.h │ │ │ ├── ToolsContextRenderComponent.h │ │ │ ├── RuntimeDynamicMeshComponentToolTarget.h │ │ │ ├── RuntimeToolsFrameworkSubsystem.h │ │ │ ├── ToolsContextRenderComponent.cpp │ │ │ ├── RuntimeDynamicMeshComponentToolTarget.cpp │ │ │ └── ToolsContextActor.cpp │ │ ├── Interaction │ │ │ ├── SceneObjectTransformInteraction.h │ │ │ ├── SceneObjectSelectionInteraction.h │ │ │ ├── SceneObjectSelectionInteraction.cpp │ │ │ └── SceneObjectTransformInteraction.cpp │ │ └── MeshScene │ │ │ ├── RuntimeMeshSceneObject.h │ │ │ ├── SceneHistoryManager.h │ │ │ ├── SceneHistoryManager.cpp │ │ │ ├── RuntimeMeshSceneSubsystem.h │ │ │ ├── RuntimeMeshSceneObject.cpp │ │ │ └── RuntimeMeshSceneSubsystem.cpp │ └── RuntimeToolsSystem.Build.cs ├── ToolsFrameworkDemo.Target.cs └── ToolsFrameworkDemoEditor.Target.cs ├── Content ├── DefaultMap.umap ├── M_Ground.uasset ├── DemoGameMode.uasset ├── ToolUI │ ├── ToolTestUI.uasset │ ├── DrawPolygonToolUI.uasset │ ├── EditPolygonsToolUI.uasset │ ├── MeshBooleanToolUI.uasset │ ├── RemeshMeshToolUI.uasset │ └── DynamicMeshSculptToolUI.uasset ├── BP_ToolsContextActor.uasset ├── DemoPlayerController.uasset └── RuntimeToolsFrameworkMaterials │ ├── SelectedMaterial.uasset │ ├── WireframeMaterial.uasset │ └── DefaultObjectMaterial.uasset ├── README.md ├── Plugins └── RuntimeGeometryUtils │ ├── Source │ └── RuntimeGeometryUtils │ │ ├── Private │ │ ├── tinyobj │ │ │ ├── tiny_obj_loader.cpp │ │ │ ├── LICENSE │ │ │ └── README.md │ │ ├── RuntimeGeometryUtilsModule.cpp │ │ ├── DynamicSMCActor.cpp │ │ ├── DynamicSDMCActor.cpp │ │ ├── DynamicPMCActor.cpp │ │ ├── GeneratedMeshDeformersLibrary.cpp │ │ ├── DynamicMeshOBJReader.cpp │ │ ├── MeshComponentRuntimeUtils.cpp │ │ ├── DynamicMeshOBJWriter.cpp │ │ └── DynamicMeshBaseActor.cpp │ │ ├── Public │ │ ├── RuntimeGeometryUtilsModule.h │ │ ├── DynamicMeshOBJReader.h │ │ ├── DynamicPMCActor.h │ │ ├── DynamicSDMCActor.h │ │ ├── DynamicMeshOBJWriter.h │ │ ├── DynamicSMCActor.h │ │ ├── MeshComponentRuntimeUtils.h │ │ └── GeneratedMeshDeformersLibrary.h │ │ └── RuntimeGeometryUtils.Build.cs │ ├── Resources │ └── Icon128.png │ ├── Content │ ├── RedMaterial.uasset │ └── BlueMaterial.uasset │ └── RuntimeGeometryUtils.uplugin ├── ToolsFrameworkDemo.uproject ├── LICENSE └── .gitignore /Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkPlayerController.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolsFrameworkPlayerController.h" 2 | 3 | -------------------------------------------------------------------------------- /Content/DefaultMap.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/DefaultMap.umap -------------------------------------------------------------------------------- /Content/M_Ground.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/M_Ground.uasset -------------------------------------------------------------------------------- /Content/DemoGameMode.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/DemoGameMode.uasset -------------------------------------------------------------------------------- /Content/ToolUI/ToolTestUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/ToolUI/ToolTestUI.uasset -------------------------------------------------------------------------------- /Content/BP_ToolsContextActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/BP_ToolsContextActor.uasset -------------------------------------------------------------------------------- /Content/DemoPlayerController.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/DemoPlayerController.uasset -------------------------------------------------------------------------------- /Content/ToolUI/DrawPolygonToolUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/ToolUI/DrawPolygonToolUI.uasset -------------------------------------------------------------------------------- /Content/ToolUI/EditPolygonsToolUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/ToolUI/EditPolygonsToolUI.uasset -------------------------------------------------------------------------------- /Content/ToolUI/MeshBooleanToolUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/ToolUI/MeshBooleanToolUI.uasset -------------------------------------------------------------------------------- /Content/ToolUI/RemeshMeshToolUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/ToolUI/RemeshMeshToolUI.uasset -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnrealRuntimeToolsFrameworkDemo 2 | Sample project/code that uses the UE5 InteractiveToolsFramework to provide a small modeling app at Runtime 3 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkDemo.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /Content/ToolUI/DynamicMeshSculptToolUI.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/ToolUI/DynamicMeshSculptToolUI.uasset -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/tiny_obj_loader.cpp: -------------------------------------------------------------------------------- 1 | #define TINYOBJLOADER_IMPLEMENTATION 2 | #include "tiny_obj_loader.h" 3 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Plugins/RuntimeGeometryUtils/Resources/Icon128.png -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Content/RedMaterial.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Plugins/RuntimeGeometryUtils/Content/RedMaterial.uasset -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Content/BlueMaterial.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Plugins/RuntimeGeometryUtils/Content/BlueMaterial.uasset -------------------------------------------------------------------------------- /Content/RuntimeToolsFrameworkMaterials/SelectedMaterial.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/RuntimeToolsFrameworkMaterials/SelectedMaterial.uasset -------------------------------------------------------------------------------- /Content/RuntimeToolsFrameworkMaterials/WireframeMaterial.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/RuntimeToolsFrameworkMaterials/WireframeMaterial.uasset -------------------------------------------------------------------------------- /Content/RuntimeToolsFrameworkMaterials/DefaultObjectMaterial.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradientspace/UE5RuntimeToolsFrameworkDemo/HEAD/Content/RuntimeToolsFrameworkMaterials/DefaultObjectMaterial.uasset -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkDemo.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "ToolsFrameworkDemo.h" 4 | #include "Modules/ModuleManager.h" 5 | 6 | IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ToolsFrameworkDemo, "ToolsFrameworkDemo" ); 7 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsSystemModule.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FRuntimeToolsSystemModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/RuntimeGeometryUtilsModule.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FRuntimeGeometryUtilsModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkPlayerController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GameFramework/PlayerController.h" 5 | #include "ToolsFrameworkPlayerController.generated.h" 6 | 7 | /** 8 | * This is not necessary, but you could add your own PlayerController code here, if you had any 9 | */ 10 | UCLASS() 11 | class TOOLSFRAMEWORKDEMO_API AToolsFrameworkPlayerController : public APlayerController 12 | { 13 | GENERATED_BODY() 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class ToolsFrameworkDemoTarget : TargetRules 7 | { 8 | public ToolsFrameworkDemoTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Game; 11 | DefaultBuildSettings = BuildSettingsVersion.Latest; 12 | IncludeOrderVersion = EngineIncludeOrderVersion.Latest; 13 | ExtraModuleNames.AddRange( new string[] { "ToolsFrameworkDemo" } ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemoEditor.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class ToolsFrameworkDemoEditorTarget : TargetRules 7 | { 8 | public ToolsFrameworkDemoEditorTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Editor; 11 | DefaultBuildSettings = BuildSettingsVersion.Latest; 12 | IncludeOrderVersion = EngineIncludeOrderVersion.Latest; 13 | ExtraModuleNames.AddRange( new string[] { "ToolsFrameworkDemo" } ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ToolsFrameworkDemo.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "5.5", 4 | "Category": "", 5 | "Description": "", 6 | "Modules": [ 7 | { 8 | "Name": "ToolsFrameworkDemo", 9 | "Type": "Runtime", 10 | "LoadingPhase": "Default", 11 | "AdditionalDependencies": [ 12 | "Engine" 13 | ] 14 | } 15 | ], 16 | "Plugins": [ 17 | { 18 | "Name": "GeometryProcessing", 19 | "Enabled": true 20 | }, 21 | { 22 | "Name": "MeshModelingToolset", 23 | "Enabled": true 24 | }, 25 | { 26 | "Name": "MeshModelingToolsetExp", 27 | "Enabled": true 28 | }, 29 | { 30 | "Name": "ModelingToolsEditorMode", 31 | "Enabled": true 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsSystemModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "RuntimeToolsSystemModule.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FRuntimeToolsSystemModule" 6 | 7 | void FRuntimeToolsSystemModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FRuntimeToolsSystemModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FRuntimeToolsSystemModule, RuntimeToolsSystem) -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/RuntimeGeometryUtilsModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "RuntimeGeometryUtilsModule.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FRuntimeGeometryUtilsModule" 6 | 7 | void FRuntimeGeometryUtilsModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FRuntimeGeometryUtilsModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FRuntimeGeometryUtilsModule, RuntimeGeometryUtils) -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/RuntimeGeometryUtils.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "RuntimeGeometryUtils", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "Ryan Schmidt", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "RuntimeGeometryUtils", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default" 22 | } 23 | ], 24 | "Plugins": [ 25 | { 26 | "Name": "GeometryProcessing", 27 | "Enabled": true 28 | }, 29 | { 30 | "Name": "MeshModelingToolset", 31 | "Enabled": true 32 | }, 33 | { 34 | "Name": "ProceduralMeshComponent", 35 | "Enabled": true 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJReader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "DynamicMesh/DynamicMesh3.h" 5 | 6 | namespace RTGUtils 7 | { 8 | /** 9 | * Read mesh in OBJ format from the given path into a FDynamicMesh3. 10 | * @param bNormals should normals be imported into primary normal attribute overlay 11 | * @param bTexCoords should texture coordinates be imported into primary UV attribute overlay 12 | * @param bVertexColors should normals be imported into per-vertex colors 13 | * @param bReverseOrientation if true, mesh orientation/normals are flipped. You probably want this for importing to UE4 from other apps. 14 | * @param return false if read failed 15 | */ 16 | RUNTIMEGEOMETRYUTILS_API bool ReadOBJMesh( 17 | const FString& Path, 18 | UE::Geometry::FDynamicMesh3& MeshOut, 19 | bool bNormals, 20 | bool bTexCoords, 21 | bool bVertexColors, 22 | bool bReverseOrientation); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicPMCActor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GameFramework/Actor.h" 5 | #include "ProceduralMeshComponent.h" 6 | #include "DynamicMeshBaseActor.h" 7 | #include "DynamicPMCActor.generated.h" 8 | 9 | 10 | 11 | UCLASS() 12 | class RUNTIMEGEOMETRYUTILS_API ADynamicPMCActor : public ADynamicMeshBaseActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | // Sets default values for this actor's properties 18 | ADynamicPMCActor(); 19 | 20 | public: 21 | UPROPERTY(VisibleAnywhere) 22 | UProceduralMeshComponent* MeshComponent = nullptr; 23 | 24 | 25 | 26 | protected: 27 | // Called when the game starts or when spawned 28 | virtual void BeginPlay() override; 29 | 30 | public: 31 | // Called every frame 32 | virtual void Tick(float DeltaTime) override; 33 | 34 | 35 | protected: 36 | /** 37 | * ADynamicBaseActor API 38 | */ 39 | virtual void OnMeshEditedInternal() override; 40 | 41 | protected: 42 | virtual void UpdatePMCMesh(); 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSDMCActor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GameFramework/Actor.h" 5 | #include "Components/DynamicMeshComponent.h" 6 | #include "DynamicMeshBaseActor.h" 7 | #include "DynamicSDMCActor.generated.h" 8 | 9 | class FDynamicMesh3; 10 | 11 | UCLASS() 12 | class RUNTIMEGEOMETRYUTILS_API ADynamicSDMCActor : public ADynamicMeshBaseActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | // Sets default values for this actor's properties 18 | ADynamicSDMCActor(); 19 | 20 | public: 21 | UPROPERTY(VisibleAnywhere) 22 | UDynamicMeshComponent* MeshComponent = nullptr; 23 | 24 | protected: 25 | // Called when the game starts or when spawned 26 | virtual void BeginPlay() override; 27 | 28 | 29 | public: 30 | // Called every frame 31 | virtual void Tick(float DeltaTime) override; 32 | 33 | protected: 34 | /** 35 | * ADynamicBaseActor API 36 | */ 37 | virtual void OnMeshEditedInternal() override; 38 | 39 | protected: 40 | virtual void UpdateSDMCMesh(); 41 | }; 42 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicMeshOBJWriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "DynamicMesh/DynamicMesh3.h" 5 | 6 | namespace RTGUtils 7 | { 8 | /** 9 | * Write mesh to the given output path in OBJ format. 10 | * @param bReverseOrientation if true, mesh orientation/normals are flipped. You probably want this for exporting from UE4 to other apps. 11 | * @param return false if write failed 12 | */ 13 | RUNTIMEGEOMETRYUTILS_API bool WriteOBJMesh( 14 | const FString& OutputPath, 15 | const UE::Geometry::FDynamicMesh3& Mesh, 16 | bool bReverseOrientation); 17 | 18 | /** 19 | * Write set of meshes to the given output path in OBJ format. 20 | * @param bReverseOrientation if true, mesh orientation/normals are flipped. You probably want this for exporting from UE4 to other apps. 21 | * @param return false if write failed 22 | */ 23 | RUNTIMEGEOMETRYUTILS_API bool WriteOBJMeshes( 24 | const FString& OutputPath, 25 | const TArray& Meshes, 26 | bool bReverseOrientation); 27 | } 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeRemeshMeshTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RemeshMeshTool.h" 4 | #include "RuntimeRemeshMeshTool.generated.h" 5 | 6 | /** ToolBuilder for URuntimeDrawPolygonTool instances */ 7 | UCLASS() 8 | class RUNTIMETOOLSSYSTEM_API URuntimeRemeshMeshToolBuilder : public URemeshMeshToolBuilder 9 | { 10 | GENERATED_BODY() 11 | public: 12 | virtual UMultiSelectionMeshEditingTool* CreateNewTool(const FToolBuilderState& SceneState) const override; 13 | }; 14 | 15 | 16 | 17 | UCLASS(BlueprintType) 18 | class RUNTIMETOOLSSYSTEM_API URuntimeRemeshMeshToolProperties : public UInteractiveToolPropertySet 19 | { 20 | GENERATED_BODY() 21 | public: 22 | UPROPERTY(BlueprintReadWrite) 23 | int TargetTriangleCount; 24 | 25 | UPROPERTY(BlueprintReadWrite) 26 | bool bDiscardAttributes; 27 | }; 28 | 29 | 30 | UCLASS(BlueprintType) 31 | class RUNTIMETOOLSSYSTEM_API URuntimeRemeshMeshTool : public URemeshMeshTool 32 | { 33 | GENERATED_BODY() 34 | 35 | public: 36 | virtual void Setup() override; 37 | 38 | UPROPERTY(BlueprintReadOnly) 39 | URuntimeRemeshMeshToolProperties* RuntimeProperties; 40 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 gradientspace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkDemoGameModeBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GameFramework/GameModeBase.h" 5 | #include "ToolsFrameworkDemoGameModeBase.generated.h" 6 | 7 | class URuntimeToolsFrameworkSubsystem; 8 | class URuntimeMeshSceneSubsystem; 9 | 10 | /** 11 | * AToolsFrameworkDemoGameModeBase is a GameMode that initializes the URuntimeMeshSceneSubsystem and 12 | * URuntimeToolsFrameworkSubsystem, and then registers various Tools (see InitializeToolsSystem). 13 | * 14 | * The GameMode Tick also ticks the Tools system 15 | */ 16 | UCLASS() 17 | class TOOLSFRAMEWORKDEMO_API AToolsFrameworkDemoGameModeBase : public AGameModeBase 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | AToolsFrameworkDemoGameModeBase(); 23 | 24 | virtual void Tick(float DeltaTime) override; 25 | virtual void StartPlay() override; 26 | 27 | virtual void InitializeToolsSystem(); 28 | virtual void ShutdownToolsSystem(); 29 | 30 | virtual void RegisterTools(); 31 | 32 | UPROPERTY() 33 | URuntimeToolsFrameworkSubsystem* ToolsSystem; 34 | 35 | UPROPERTY() 36 | URuntimeMeshSceneSubsystem* SceneSystem; 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /Config/HoloLens/HoloLensEngine.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | [/Script/HoloLensPlatformEditor.HoloLensTargetSettings] 4 | bBuildForEmulation=False 5 | bBuildForDevice=True 6 | bUseNameForLogo=True 7 | bBuildForRetailWindowsStore=False 8 | bAutoIncrementVersion=False 9 | bShouldCreateAppInstaller=False 10 | AppInstallerInstallationURL= 11 | HoursBetweenUpdateChecks=0 12 | bEnablePIXProfiling=False 13 | TileBackgroundColor=(B=64,G=0,R=0,A=255) 14 | SplashScreenBackgroundColor=(B=64,G=0,R=0,A=255) 15 | +PerCultureResources=(CultureId="",Strings=(PackageDisplayName="",PublisherDisplayName="",PackageDescription="",ApplicationDisplayName="",ApplicationDescription=""),Images=()) 16 | TargetDeviceFamily=Windows.Holographic 17 | MinimumPlatformVersion= 18 | MaximumPlatformVersionTested=10.0.18362.0 19 | MaxTrianglesPerCubicMeter=500.000000 20 | SpatialMeshingVolumeSize=20.000000 21 | CompilerVersion=Default 22 | Windows10SDKVersion=10.0.18362.0 23 | +CapabilityList=internetClientServer 24 | +CapabilityList=privateNetworkClientServer 25 | +Uap2CapabilityList=spatialPerception 26 | bSetDefaultCapabilities=False 27 | SpatializationPlugin= 28 | ReverbPlugin= 29 | OcclusionPlugin= 30 | SoundCueCookQualityIndex=-1 31 | 32 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/RuntimeToolsSystem.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RuntimeToolsSystem : ModuleRules 6 | { 7 | public RuntimeToolsSystem(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { 12 | "Core", 13 | "CoreUObject", 14 | "Engine", 15 | "InputCore", 16 | "RenderCore", 17 | "InteractiveToolsFramework", 18 | "MeshDescription", 19 | "StaticMeshDescription", 20 | "GeometryCore", 21 | "DynamicMesh", 22 | "GeometryFramework", 23 | "MeshConversion", 24 | "ModelingComponents", 25 | "MeshModelingTools", 26 | "MeshModelingToolsExp", 27 | "RuntimeGeometryUtils" 28 | }); 29 | 30 | PrivateDependencyModuleNames.AddRange(new string[] { 31 | "Slate", 32 | "SlateCore" 33 | }); 34 | 35 | // Uncomment if you are using online features 36 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 37 | 38 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkDemo.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ToolsFrameworkDemo : ModuleRules 6 | { 7 | public ToolsFrameworkDemo(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { 12 | "Core", 13 | "CoreUObject", 14 | "Engine", 15 | "InputCore", 16 | "RenderCore", 17 | "InteractiveToolsFramework", 18 | "MeshDescription", 19 | "StaticMeshDescription", 20 | "GeometryCore", 21 | "DynamicMesh", 22 | "MeshConversion", 23 | "ModelingComponents", 24 | "MeshModelingTools", 25 | "MeshModelingToolsExp", 26 | "RuntimeGeometryUtils", 27 | "RuntimeToolsSystem" 28 | }); 29 | 30 | PrivateDependencyModuleNames.AddRange(new string[] { 31 | "Slate", 32 | "SlateCore" 33 | }); 34 | 35 | // Uncomment if you are using online features 36 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 37 | 38 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/DynamicSMCActor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GameFramework/Actor.h" 5 | #include "Engine/StaticMesh.h" 6 | #include "Components/StaticMeshComponent.h" 7 | #include "DynamicMeshBaseActor.h" 8 | #include "DynamicSMCActor.generated.h" 9 | 10 | UCLASS() 11 | class RUNTIMEGEOMETRYUTILS_API ADynamicSMCActor : public ADynamicMeshBaseActor 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | // Sets default values for this actor's properties 17 | ADynamicSMCActor(); 18 | 19 | public: 20 | UPROPERTY(VisibleAnywhere) 21 | UStaticMeshComponent* MeshComponent = nullptr; 22 | 23 | UPROPERTY(Transient) 24 | UStaticMesh* StaticMesh = nullptr; 25 | 26 | protected: 27 | // Called when the game starts or when spawned 28 | virtual void BeginPlay() override; 29 | 30 | virtual void PostLoad() override; 31 | virtual void PostActorCreated() override; 32 | 33 | public: 34 | // Called every frame 35 | virtual void Tick(float DeltaTime) override; 36 | 37 | protected: 38 | /** 39 | * ADynamicBaseActor API 40 | */ 41 | virtual void OnMeshEditedInternal() override; 42 | 43 | protected: 44 | virtual void UpdateSMCMesh(); 45 | }; 46 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2019 Syoyo Fujita and many contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeDrawPolygonTool.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeDrawPolygonTool.h" 2 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 3 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 4 | 5 | #define LOCTEXT_NAMESPACE "URuntimeDrawPolygonTool" 6 | 7 | UInteractiveTool* URuntimeDrawPolygonToolBuilder::BuildTool(const FToolBuilderState& SceneState) const 8 | { 9 | URuntimeDrawPolygonTool* NewTool = NewObject(SceneState.ToolManager); 10 | NewTool->SetWorld(SceneState.World); 11 | return NewTool; 12 | } 13 | 14 | 15 | 16 | void URuntimeDrawPolygonTool::Setup() 17 | { 18 | UDrawPolygonTool::Setup(); 19 | 20 | // initialize to drawing material 21 | this->MaterialProperties->Material = URuntimeMeshSceneSubsystem::Get()->StandardMaterial; 22 | 23 | // mirror properties we want to expose at runtime 24 | RuntimeProperties = NewObject(this); 25 | 26 | RuntimeProperties->SelectedPolygonType = (int)PolygonProperties->PolygonDrawMode; 27 | RuntimeProperties->WatchProperty(RuntimeProperties->SelectedPolygonType, 28 | [this](int NewType) { PolygonProperties->PolygonDrawMode = (EDrawPolygonDrawMode)NewType; }); 29 | 30 | AddToolPropertySource(RuntimeProperties); 31 | } 32 | 33 | 34 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeMeshBooleanTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BaseTools/BaseCreateFromSelectedTool.h" 4 | #include "CSGMeshesTool.h" 5 | #include "RuntimeMeshBooleanTool.generated.h" 6 | 7 | 8 | UENUM(BlueprintType) 9 | enum class ERuntimeMeshBooleanOpType : uint8 10 | { 11 | DifferenceAB = 0, 12 | DifferenceBA = 1, 13 | Intersect = 2, 14 | Union = 3, 15 | TrimA = 4, 16 | TrimB = 5 17 | }; 18 | 19 | 20 | UCLASS(BlueprintType) 21 | class RUNTIMETOOLSSYSTEM_API URuntimeMeshBooleanToolProperties : public UInteractiveToolPropertySet 22 | { 23 | GENERATED_BODY() 24 | public: 25 | UPROPERTY(BlueprintReadWrite) 26 | int OperationType; 27 | }; 28 | 29 | 30 | UCLASS(BlueprintType) 31 | class RUNTIMETOOLSSYSTEM_API URuntimeMeshBooleanTool : public UCSGMeshesTool 32 | { 33 | GENERATED_BODY() 34 | 35 | public: 36 | virtual void Setup() override; 37 | 38 | virtual void Shutdown(EToolShutdownType ShutdownType) override; 39 | 40 | UPROPERTY(BlueprintReadOnly) 41 | URuntimeMeshBooleanToolProperties* RuntimeProperties; 42 | }; 43 | 44 | 45 | 46 | UCLASS() 47 | class RUNTIMETOOLSSYSTEM_API URuntimeMeshBooleanToolBuilder : public UCSGMeshesToolBuilder 48 | { 49 | GENERATED_BODY() 50 | public: 51 | virtual UMultiSelectionMeshEditingTool* CreateNewTool(const FToolBuilderState& SceneState) const override; 52 | }; 53 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeDynamicMeshSculptTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DynamicMeshSculptTool.h" 4 | #include "RuntimeDynamicMeshSculptTool.generated.h" 5 | 6 | UCLASS() 7 | class RUNTIMETOOLSSYSTEM_API URuntimeDynamicMeshSculptToolBuilder : public UDynamicMeshSculptToolBuilder 8 | { 9 | GENERATED_BODY() 10 | public: 11 | virtual UMeshSurfacePointTool* CreateNewTool(const FToolBuilderState& SceneState) const override; 12 | }; 13 | 14 | 15 | UENUM(BlueprintType) 16 | enum class ERuntimeDynamicMeshSculptBrushType : uint8 17 | { 18 | Move = 0, 19 | Sculpt = 1, 20 | Smooth = 2, 21 | Inflate = 3, 22 | Flatten = 4 23 | }; 24 | 25 | 26 | UCLASS(BlueprintType) 27 | class RUNTIMETOOLSSYSTEM_API URuntimeDynamicMeshSculptToolProperties : public UInteractiveToolPropertySet 28 | { 29 | GENERATED_BODY() 30 | public: 31 | UPROPERTY(BlueprintReadWrite) 32 | float BrushSize; 33 | 34 | UPROPERTY(BlueprintReadWrite) 35 | float BrushStrength; 36 | 37 | UPROPERTY(BlueprintReadWrite) 38 | float BrushFalloff; 39 | 40 | UPROPERTY(BlueprintReadWrite) 41 | int SelectedBrushType; 42 | }; 43 | 44 | 45 | UCLASS(BlueprintType) 46 | class RUNTIMETOOLSSYSTEM_API URuntimeDynamicMeshSculptTool : public UDynamicMeshSculptTool 47 | { 48 | GENERATED_BODY() 49 | 50 | public: 51 | virtual void Setup() override; 52 | 53 | UPROPERTY(BlueprintReadOnly) 54 | URuntimeDynamicMeshSculptToolProperties* RuntimeProperties; 55 | }; -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/RuntimeGeometryUtils.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RuntimeGeometryUtils : ModuleRules 6 | { 7 | public RuntimeGeometryUtils(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | "GeometryCore", 30 | "DynamicMesh", 31 | "GeometryFramework", 32 | "ProceduralMeshComponent" 33 | } 34 | ); 35 | 36 | if ((Target.Platform == UnrealTargetPlatform.Win64)) { 37 | PublicDependencyModuleNames.Add("ModelingComponents"); 38 | } 39 | 40 | 41 | PrivateDependencyModuleNames.AddRange( 42 | new string[] 43 | { 44 | "CoreUObject", 45 | "Engine", 46 | "MeshDescription", 47 | "StaticMeshDescription", 48 | "GeometryAlgorithms", 49 | "MeshConversion" 50 | } 51 | ); 52 | 53 | 54 | DynamicallyLoadedModuleNames.AddRange( 55 | new string[] 56 | { 57 | } 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/MeshComponentRuntimeUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Engine/StaticMesh.h" 5 | #include "ProceduralMeshComponent.h" 6 | #include "DynamicMesh/DynamicMesh3.h" 7 | 8 | 9 | namespace RTGUtils 10 | { 11 | 12 | 13 | /** 14 | * Reinitialize the given StaticMesh with the input FDynamicMesh3. 15 | * This calls StaticMesh->BuildFromMeshDescriptions(), which can be used at Runtime (vs StaticMesh->Build() which cannot) 16 | */ 17 | RUNTIMEGEOMETRYUTILS_API void UpdateStaticMeshFromDynamicMesh( 18 | UStaticMesh* StaticMesh, 19 | const UE::Geometry::FDynamicMesh3* Mesh); 20 | 21 | 22 | 23 | /** 24 | * Initialize a ProceduralMeshComponent with a single section defined by the given FDynamicMesh3. 25 | * @param bUseFaceNormals if true, each triangle is shaded with per-triangle normal instead of split-vertex normals from FDynamicMesh3 overlay 26 | * @param bInitializeUV0 if true, UV0 is initialized, otherwise it is not (set to 0) 27 | * @param bInitializePerVertexColors if true, per-vertex colors on the FDynamicMesh3 are used to initialize vertex colors of the PMC 28 | */ 29 | RUNTIMEGEOMETRYUTILS_API void UpdatePMCFromDynamicMesh_SplitTriangles( 30 | UProceduralMeshComponent* Component, 31 | const UE::Geometry::FDynamicMesh3* Mesh, 32 | bool bUseFaceNormals, 33 | bool bInitializeUV0, 34 | bool bInitializePerVertexColors, 35 | bool bCreateCollision); 36 | 37 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeDrawPolygonTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "DrawPolygonTool.h" 4 | #include "RuntimeDrawPolygonTool.generated.h" 5 | 6 | 7 | /** ToolBuilder for URuntimeDrawPolygonTool instances */ 8 | UCLASS() 9 | class RUNTIMETOOLSSYSTEM_API URuntimeDrawPolygonToolBuilder : public UDrawPolygonToolBuilder 10 | { 11 | GENERATED_BODY() 12 | 13 | public: 14 | virtual UInteractiveTool* BuildTool(const FToolBuilderState& SceneState) const override; 15 | }; 16 | 17 | 18 | UENUM(BlueprintType) 19 | enum class ERuntimeDrawPolygonType : uint8 20 | { 21 | Freehand = 0, 22 | Circle = 1, 23 | Square = 2, 24 | Rectangle = 3, 25 | RoundedRectangle = 4, 26 | HoleyCircle = 5 27 | }; 28 | 29 | 30 | UCLASS(BlueprintType) 31 | class RUNTIMETOOLSSYSTEM_API URuntimeDrawPolygonToolProperties : public UInteractiveToolPropertySet 32 | { 33 | GENERATED_BODY() 34 | public: 35 | UPROPERTY(BlueprintReadWrite) 36 | int SelectedPolygonType; 37 | }; 38 | 39 | 40 | 41 | 42 | /** 43 | * Extension of UDrawPolygonTool that overrides EmitCurrentPolygon() to work at Runtime, 44 | * because the base implementation calls checkNoEntry() in non-Editor builds. 45 | */ 46 | UCLASS(BlueprintType) 47 | class RUNTIMETOOLSSYSTEM_API URuntimeDrawPolygonTool : public UDrawPolygonTool 48 | { 49 | GENERATED_BODY() 50 | 51 | public: 52 | virtual void Setup() override; 53 | 54 | UPROPERTY(BlueprintReadOnly) 55 | URuntimeDrawPolygonToolProperties* RuntimeProperties; 56 | }; -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeRemeshMeshTool.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeRemeshMeshTool.h" 2 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 3 | 4 | #include "ToolBuilderUtil.h" 5 | 6 | #define LOCTEXT_NAMESPACE "URuntimeRemeshMeshTool" 7 | 8 | UMultiSelectionMeshEditingTool* URuntimeRemeshMeshToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const 9 | { 10 | return NewObject(SceneState.ToolManager); 11 | } 12 | 13 | 14 | void URuntimeRemeshMeshTool::Setup() 15 | { 16 | URemeshMeshTool::Setup(); 17 | 18 | // disable wireframe because it crashes at runtime 19 | //this->BasicProperties->bShowWireframe = false; 20 | 21 | // mirror properties we want to expose at runtime 22 | RuntimeProperties = NewObject(this); 23 | 24 | RuntimeProperties->bDiscardAttributes = BasicProperties->bDiscardAttributes; 25 | RuntimeProperties->WatchProperty(RuntimeProperties->bDiscardAttributes, 26 | [this](bool bNewValue) { BasicProperties->bDiscardAttributes = bNewValue; BasicProperties->bPreserveSharpEdges = !bNewValue; Preview->InvalidateResult(); }); 27 | 28 | RuntimeProperties->TargetTriangleCount = BasicProperties->TargetTriangleCount; 29 | RuntimeProperties->WatchProperty(RuntimeProperties->TargetTriangleCount, 30 | [this](int NewValue) { BasicProperties->TargetTriangleCount = NewValue; Preview->InvalidateResult(); }); 31 | 32 | AddToolPropertySource(RuntimeProperties); 33 | } 34 | 35 | 36 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | **/Binaries/* 50 | **/Binaries/* 51 | **/Plugins/*/Binaries/* 52 | 53 | # Builds 54 | **/Build/* 55 | 56 | # Whitelist PakBlacklist-.txt files 57 | !**/Build/*/ 58 | **/Build/*/** 59 | !**/Build/*/PakBlacklist*.txt 60 | 61 | # Don't ignore icon files in Build 62 | !Build/**/*.ico 63 | 64 | # Built data for maps 65 | *_BuiltData.uasset 66 | 67 | # Configuration files generated by the Editor 68 | Saved/* 69 | **/Saved/* 70 | 71 | # Compiled source files for the engine to use 72 | Intermediate/* 73 | **/Intermediate/* 74 | **/Plugins/*/Intermediate/* 75 | 76 | # Cache files for the editor to use 77 | **/DerivedDataCache/* 78 | 79 | # ignore starter content that will be copied into project if it is opened in the UE Editor 80 | **/Content/StarterContent/* 81 | 82 | # allow OBJ files in Content folders 83 | !Content/**/*.obj 84 | 85 | 86 | BUILD_TEST/ 87 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimePolyEditTool.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimePolyEditTool.h" 2 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 3 | 4 | #include "ToolBuilderUtil.h" 5 | 6 | #define LOCTEXT_NAMESPACE "URuntimeEditMeshPolygonsTool" 7 | 8 | 9 | USingleTargetWithSelectionTool* URuntimePolyEditToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const 10 | { 11 | URuntimePolyEditTool* PolyEditTool = NewObject(SceneState.ToolManager); 12 | PolyEditTool->LocalTargetWorld = SceneState.World; 13 | return PolyEditTool; 14 | } 15 | 16 | 17 | 18 | 19 | void URuntimePolyEditTool::Setup() 20 | { 21 | UEditMeshPolygonsTool::Setup(); 22 | 23 | // mirror properties we want to expose at runtime 24 | RuntimeProperties = NewObject(this); 25 | 26 | AddToolPropertySource(RuntimeProperties); 27 | 28 | check(GEngine->WireframeMaterial != nullptr); 29 | } 30 | 31 | 32 | void URuntimePolyEditTool::BeginExtrudeAction() 33 | { 34 | RequestAction(EEditMeshPolygonsToolActions::Extrude); 35 | } 36 | 37 | void URuntimePolyEditTool::BeginInsetAction() 38 | { 39 | RequestAction(EEditMeshPolygonsToolActions::Inset); 40 | } 41 | 42 | 43 | void URuntimePolyEditTool::BeginOutsetAction() 44 | { 45 | RequestAction(EEditMeshPolygonsToolActions::Outset); 46 | } 47 | 48 | void URuntimePolyEditTool::BeginCutFacesAction() 49 | { 50 | RequestAction(EEditMeshPolygonsToolActions::CutFaces); 51 | } 52 | 53 | UWorld* URuntimePolyEditTool::GetWorld() const 54 | { 55 | return this->LocalTargetWorld; 56 | } 57 | 58 | 59 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimePolyEditTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EditMeshPolygonsTool.h" 4 | #include "RuntimePolyEditTool.generated.h" 5 | 6 | UCLASS() 7 | class RUNTIMETOOLSSYSTEM_API URuntimePolyEditToolBuilder : public UEditMeshPolygonsToolBuilder 8 | { 9 | GENERATED_BODY() 10 | public: 11 | virtual USingleTargetWithSelectionTool* CreateNewTool(const FToolBuilderState& SceneState) const override; 12 | }; 13 | 14 | 15 | UCLASS(BlueprintType) 16 | class RUNTIMETOOLSSYSTEM_API URuntimePolyEditToolProperties : public UInteractiveToolPropertySet 17 | { 18 | GENERATED_BODY() 19 | public: 20 | 21 | }; 22 | 23 | 24 | UCLASS(BlueprintType) 25 | class RUNTIMETOOLSSYSTEM_API URuntimePolyEditTool : public UEditMeshPolygonsTool 26 | { 27 | GENERATED_BODY() 28 | 29 | public: 30 | virtual void Setup() override; 31 | 32 | UPROPERTY(BlueprintReadOnly) 33 | URuntimePolyEditToolProperties* RuntimeProperties; 34 | 35 | 36 | 37 | UFUNCTION(BlueprintCallable) 38 | void BeginExtrudeAction(); 39 | 40 | UFUNCTION(BlueprintCallable) 41 | void BeginInsetAction(); 42 | 43 | UFUNCTION(BlueprintCallable) 44 | void BeginOutsetAction(); 45 | 46 | UFUNCTION(BlueprintCallable) 47 | void BeginCutFacesAction(); 48 | 49 | 50 | // Multiple base classes have a UWorld* TargetWorld, causes scoping problems...so we add a third that we can rely on... 51 | UPROPERTY() 52 | TObjectPtr LocalTargetWorld = nullptr; 53 | 54 | // Override this to work around a bug in PolyEditActivityUtil::CreatePolyEditPreviewMesh() that calls GetWorld instead of GetTargetWorld() 55 | virtual UWorld* GetWorld() const; 56 | }; -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/RuntimeModelingObjectsCreationAPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "ModelingObjectsCreationAPI.h" 5 | #include "RuntimeModelingObjectsCreationAPI.generated.h" 6 | 7 | class UInteractiveToolsContext; 8 | 9 | /** 10 | * Implementation of UModelingObjectsCreationAPI, which UE Modeling Tools use to 11 | * emit new "Mesh Objects" (eg StaticMeshAsset+Actor, DynamicMeshActor, AVolume). 12 | * In this Runtime system we will only emit URuntimeMeshSceneObject. 13 | * 14 | * The UE Modeling Tools find an available UModelingObjectsCreationAPI by searching 15 | * in the ContextStore of the their ToolsContext. So, use the 16 | * URuntimeModelingObjectsCreationAPI::Register(ToolsContext) function to create 17 | * a new instance and set it in the ContextStore (and Deregister to remove it) 18 | * 19 | * This is similar to UEditorModelingObjectsCreationAPI, which is what Modeling Mode 20 | * in the UE Editor makes available to the Tools. 21 | * 22 | * CreateTextureObject currently not supported. 23 | */ 24 | UCLASS() 25 | class RUNTIMETOOLSSYSTEM_API URuntimeModelingObjectsCreationAPI : public UModelingObjectsCreationAPI 26 | { 27 | GENERATED_BODY() 28 | public: 29 | 30 | virtual FCreateMeshObjectResult CreateMeshObject(const FCreateMeshObjectParams& CreateMeshParams) override; 31 | virtual FCreateTextureObjectResult CreateTextureObject(const FCreateTextureObjectParams& CreateTexParams) override; 32 | 33 | // Call this to provide an instance of URuntimeModelingObjectsCreationAPI to UE Modeling Tools 34 | static URuntimeModelingObjectsCreationAPI* Register(UInteractiveToolsContext* ToolsContext); 35 | // Call this to clean up the Register'd instance 36 | static bool Deregister(UInteractiveToolsContext* ToolsContext); 37 | }; -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSMCActor.cpp: -------------------------------------------------------------------------------- 1 | #include "DynamicSMCActor.h" 2 | #include "MaterialDomain.h" 3 | #include "Materials/Material.h" 4 | #include "MeshComponentRuntimeUtils.h" 5 | 6 | using namespace UE::Geometry; 7 | 8 | // Sets default values 9 | ADynamicSMCActor::ADynamicSMCActor() 10 | { 11 | MeshComponent = CreateDefaultSubobject(TEXT("Mesh"), false); 12 | SetRootComponent(MeshComponent); 13 | StaticMesh = nullptr; 14 | } 15 | 16 | // Called when the game starts or when spawned 17 | void ADynamicSMCActor::BeginPlay() 18 | { 19 | StaticMesh = nullptr; 20 | Super::BeginPlay(); 21 | } 22 | 23 | void ADynamicSMCActor::PostLoad() 24 | { 25 | StaticMesh = nullptr; 26 | Super::PostLoad(); 27 | } 28 | 29 | void ADynamicSMCActor::PostActorCreated() 30 | { 31 | StaticMesh = nullptr; 32 | Super::PostActorCreated(); 33 | } 34 | 35 | // Called every frame 36 | void ADynamicSMCActor::Tick(float DeltaTime) 37 | { 38 | Super::Tick(DeltaTime); 39 | } 40 | 41 | 42 | void ADynamicSMCActor::OnMeshEditedInternal() 43 | { 44 | UpdateSMCMesh(); 45 | Super::OnMeshEditedInternal(); 46 | } 47 | 48 | void ADynamicSMCActor::UpdateSMCMesh() 49 | { 50 | if (StaticMesh == nullptr) 51 | { 52 | StaticMesh = NewObject(); 53 | MeshComponent->SetStaticMesh(StaticMesh); 54 | // add one material slot 55 | TArray Materials = StaticMesh->GetStaticMaterials(); 56 | Materials.Add(FStaticMaterial()); 57 | StaticMesh->SetStaticMaterials(Materials); 58 | } 59 | 60 | if (MeshComponent) 61 | { 62 | RTGUtils::UpdateStaticMeshFromDynamicMesh(StaticMesh, &SourceMesh); 63 | 64 | // update material on new section 65 | UMaterialInterface* UseMaterial = (this->Material != nullptr) ? this->Material : UMaterial::GetDefaultMaterial(MD_Surface); 66 | MeshComponent->SetMaterial(0, UseMaterial); 67 | } 68 | } -------------------------------------------------------------------------------- /Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | [/Script/HardwareTargeting.HardwareTargetingSettings] 4 | TargetedHardwareClass=Desktop 5 | AppliedTargetedHardwareClass=Desktop 6 | DefaultGraphicsPerformance=Maximum 7 | AppliedDefaultGraphicsPerformance=Maximum 8 | 9 | [/Script/Engine.Engine] 10 | +ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/ToolsFrameworkDemo") 11 | +ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/ToolsFrameworkDemo") 12 | +ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="ToolsFrameworkDemoGameModeBase") 13 | 14 | [/Script/EngineSettings.GameMapsSettings] 15 | GlobalDefaultGameMode=/Game/DemoGameMode.DemoGameMode_C 16 | EditorStartupMap=/Game/DefaultMap.DefaultMap 17 | GameDefaultMap=/Game/DefaultMap.DefaultMap 18 | 19 | [/Script/UnrealEd.CookerSettings] 20 | bCookOnTheFlyForLaunchOn=False 21 | 22 | [/Script/Engine.UserInterfaceSettings] 23 | ApplicationScale=0.900000 24 | 25 | [/Script/Engine.RendererSettings] 26 | r.DefaultFeature.AntiAliasing=1 27 | r.AntiAliasingMethod=1 28 | r.ReflectionMethod=0 29 | r.DefaultFeature.MotionBlur=False 30 | 31 | [CoreRedirects] 32 | +ClassRedirects=(OldName="RuntimeToolsFrameworkSubsystem",NewName="/Script/RuntimeToolsSystem.RuntimeToolsFrameworkSubsystem") 33 | +ClassRedirects=(OldName="RuntimeMeshSceneSubsystem",NewName="/Script/RuntimeToolsSystem.RuntimeMeshSceneSubsystem") 34 | +ClassRedirects=(OldName="SceneHistoryManager",NewName="/Script/RuntimeToolsSystem.SceneHistoryManager") 35 | +ClassRedirects=(OldName="ToolsContextActor",NewName="/Script/RuntimeToolsSystem.ToolsContextActor") 36 | 37 | [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] 38 | bEnablePlugin=True 39 | bAllowNetworkConnection=True 40 | SecurityToken=B842E46E4D974C2D22E887B667D6388E 41 | bIncludeInShipping=False 42 | bAllowExternalStartInShipping=False 43 | bCompileAFSProject=False 44 | bUseCompression=False 45 | bLogFiles=False 46 | bReportStats=False 47 | ConnectionType=USBOnly 48 | bUseManualIPAddress=False 49 | ManualIPAddress= 50 | 51 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Public/GeneratedMeshDeformersLibrary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Kismet/BlueprintFunctionLibrary.h" 3 | #include "GeneratedMesh.h" 4 | #include "GeneratedMeshDeformersLibrary.generated.h" 5 | 6 | 7 | /** 8 | * A BP Library of functions for applying deformations to the vertices of a UGeneratedMesh 9 | */ 10 | UCLASS(meta = (ScriptName = "GeneratedMeshDeformersLibrary")) 11 | class UGeneratedMeshDeformersLibrary : public UBlueprintFunctionLibrary 12 | { 13 | GENERATED_BODY() 14 | public: 15 | 16 | /** 17 | * Displace the mesh vertices using a 1D Sin wave. 18 | * The displacement is based on a parameter t measured along the projection Axis. 19 | * Once t is known, the new position is CurPos + Magnitude * Sin( (t*Frequency) + FrequencyShift ) * UpVector 20 | */ 21 | UFUNCTION(BlueprintCallable) static UPARAM(DisplayName = "Input Mesh") 22 | UGeneratedMesh* DeformMeshAxisSinWave1D(UGeneratedMesh* Mesh, float Magnitude = 1, float Frequency = 1, float FrequencyShift = 0, FVector Axis = FVector(1,0,0), FVector UpVector = FVector(0,0,1)); 23 | 24 | /** 25 | * Displace the mesh vertices using a 2D Sin wave. 26 | */ 27 | UFUNCTION(BlueprintCallable) static UPARAM(DisplayName = "Input Mesh") 28 | UGeneratedMesh* DeformMeshAxisSinWaveRadial(UGeneratedMesh* Mesh, float Magnitude = 1, float Frequency = 1, float FrequencyShift = 0, FVector Axis = FVector(1,0,0)); 29 | 30 | /** 31 | * Displace the mesh vertices along their vertex normal directions using 3D Perlin Noise 32 | */ 33 | UFUNCTION(BlueprintCallable) static UPARAM(DisplayName = "Input Mesh") 34 | UGeneratedMesh* DeformMeshPerlinNoiseNormal(UGeneratedMesh* Mesh, float Magnitude = 1, float Frequency = 1, FVector FrequencyShift = FVector(0,0,0), int RandomSeed = 31337); 35 | 36 | /** 37 | * Apply N iterations of explicit uniform Laplacian mesh smoothing to the vertex positions, with the given Alpha in range [0,1]. Clamps to max 100 iterations. 38 | */ 39 | UFUNCTION(BlueprintCallable) static UPARAM(DisplayName = "Input Mesh") 40 | UGeneratedMesh* SmoothMeshUniform(UGeneratedMesh* Mesh, float Alpha = 0.3, int32 Iterations = 1); 41 | 42 | 43 | }; -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Interaction/SceneObjectTransformInteraction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SceneObjectTransformInteraction.generated.h" 4 | 5 | class UTransformProxy; 6 | class UCombinedTransformGizmo; 7 | class URuntimeMeshSceneObject; 8 | 9 | /** 10 | * USceneObjectTransformInteraction manages a 3D Translate/Rotate/Scale (TRS) Gizmo for the 11 | * current URuntimeMeshSceneObject selection set (stored in URuntimeMeshSceneSubsystem). 12 | * 13 | * Gizmo local/global frame is not controlled here, the Gizmo looks this information up itself 14 | * based on the EToolContextCoordinateSystem provided by the IToolsContextQueriesAPI implementation 15 | * in URuntimeToolsFrameworkSubsystem. You can configure the Gizmo to ignore this, in UpdateGizmoTargets() 16 | * 17 | * Behavior of the TRS Gizmo (ie pivot position, etc) is controlled by a standard UTransformProxy. 18 | * See UTransformMeshesTool for sample code for doing things like modifying pivot dynamically/etc. 19 | */ 20 | UCLASS() 21 | class USceneObjectTransformInteraction : public UObject 22 | { 23 | GENERATED_BODY() 24 | public: 25 | 26 | /** 27 | * Set up the transform interaction. 28 | * @param GizmoEnabledCallbackIn callback that determines if Gizmo should be created and visible. For example during a Tool we generally want to hide the TRS Gizmo. 29 | */ 30 | void Initialize(TUniqueFunction GizmoEnabledCallbackIn); 31 | 32 | void Shutdown(); 33 | 34 | UFUNCTION(BlueprintCallable) 35 | void SetEnableScaling(bool bEnable); 36 | 37 | UFUNCTION(BlueprintCallable) 38 | void SetEnableNonUniformScaling(bool bEnable); 39 | 40 | // Recreate Gizmo. Call when external state changes, like set of selected objects 41 | UFUNCTION(BlueprintCallable) 42 | void ForceUpdateGizmoState(); 43 | 44 | protected: 45 | 46 | FDelegateHandle SelectionChangedEventHandle; 47 | 48 | UPROPERTY() 49 | UTransformProxy* TransformProxy; 50 | 51 | UPROPERTY() 52 | UCombinedTransformGizmo* TransformGizmo; 53 | 54 | void UpdateGizmoTargets(const TArray& Selection); 55 | 56 | bool bEnableScaling = true; 57 | bool bEnableNonUniformScaling = true; 58 | 59 | TUniqueFunction GizmoEnabledCallback = [&]() { return true; }; 60 | }; -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeMeshBooleanTool.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeMeshBooleanTool.h" 2 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 3 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 4 | 5 | #include "ToolBuilderUtil.h" 6 | #include "ModelingToolTargetUtil.h" 7 | #include "DynamicMesh/MeshTransforms.h" 8 | #include "DynamicMeshToMeshDescription.h" 9 | 10 | #define LOCTEXT_NAMESPACE "URuntimeMeshBooleanTool" 11 | 12 | using namespace UE::Geometry; 13 | 14 | UMultiSelectionMeshEditingTool* URuntimeMeshBooleanToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const 15 | { 16 | return NewObject(SceneState.ToolManager); 17 | } 18 | 19 | void URuntimeMeshBooleanTool::Setup() 20 | { 21 | UCSGMeshesTool::Setup(); 22 | 23 | this->CSGProperties->bTryFixHoles = true; 24 | // write to first input asset 25 | this->HandleSourcesProperties->OutputWriteTo = EBaseCreateFromSelectedTargetType::FirstInputObject; 26 | // set to keep sources because we will handle deleting SceneObjects ourselves. We cannot 27 | // allow the Tool to do it because it will just call Actor.Destroy(), but we actually want 28 | // to keep the Actor around for undo/redo 29 | this->HandleSourcesProperties->HandleInputs = EHandleSourcesMethod::KeepSources; 30 | 31 | // mirror properties we want to expose at runtime 32 | RuntimeProperties = NewObject(this); 33 | 34 | RuntimeProperties->OperationType = (int)CSGProperties->Operation; 35 | RuntimeProperties->WatchProperty(RuntimeProperties->OperationType, 36 | [this](int NewType) { CSGProperties->Operation = (ECSGOperation)NewType; Preview->InvalidateResult(); }); 37 | 38 | AddToolPropertySource(RuntimeProperties); 39 | } 40 | 41 | 42 | void URuntimeMeshBooleanTool::Shutdown(EToolShutdownType ShutdownType) 43 | { 44 | if (ShutdownType == EToolShutdownType::Accept) 45 | { 46 | GetToolManager()->BeginUndoTransaction(GetActionName()); 47 | 48 | // base UCSGMeshesTool will delete the Actor but we need to also delete the SO... 49 | AActor* KeepActor = UE::ToolTarget::GetTargetActor(Targets[0]); 50 | URuntimeMeshSceneObject* KeepSO = URuntimeMeshSceneSubsystem::Get()->FindSceneObjectByActor(KeepActor); 51 | URuntimeMeshSceneSubsystem::Get()->SetSelected(KeepSO, true, false); 52 | URuntimeMeshSceneSubsystem::Get()->DeleteSelectedSceneObjects(KeepActor); 53 | } 54 | 55 | UCSGMeshesTool::Shutdown(ShutdownType); 56 | 57 | if (ShutdownType == EToolShutdownType::Accept) 58 | { 59 | GetToolManager()->EndUndoTransaction(); 60 | } 61 | } 62 | 63 | 64 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicSDMCActor.cpp: -------------------------------------------------------------------------------- 1 | #include "DynamicSDMCActor.h" 2 | #include "MeshComponentRuntimeUtils.h" 3 | #include "DynamicMesh/DynamicMesh3.h" 4 | #include "Operations/MeshConvexHull.h" 5 | #include "ShapeApproximation/SimpleShapeSet3.h" 6 | #include "Physics/ComponentCollisionUtil.h" 7 | #include "MaterialDomain.h" 8 | #include "Materials/Material.h" 9 | 10 | using namespace UE::Geometry; 11 | 12 | // Sets default values 13 | ADynamicSDMCActor::ADynamicSDMCActor() 14 | { 15 | MeshComponent = CreateDefaultSubobject(TEXT("MeshComponent"), false); 16 | SetRootComponent(MeshComponent); 17 | } 18 | 19 | // Called when the game starts or when spawned 20 | void ADynamicSDMCActor::BeginPlay() 21 | { 22 | Super::BeginPlay(); 23 | } 24 | 25 | 26 | // Called every frame 27 | void ADynamicSDMCActor::Tick(float DeltaTime) 28 | { 29 | Super::Tick(DeltaTime); 30 | } 31 | 32 | 33 | 34 | void ADynamicSDMCActor::OnMeshEditedInternal() 35 | { 36 | UpdateSDMCMesh(); 37 | Super::OnMeshEditedInternal(); 38 | } 39 | 40 | void ADynamicSDMCActor::UpdateSDMCMesh() 41 | { 42 | if (MeshComponent) 43 | { 44 | *(MeshComponent->GetMesh()) = SourceMesh; 45 | 46 | if (this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimple 47 | || this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync) 48 | { 49 | //MeshComponent->bUseAsyncCooking = (this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync); 50 | MeshComponent->SetComplexAsSimpleCollisionEnabled(true); 51 | } 52 | else if (this->CollisionMode == EDynamicMeshActorCollisionMode::SimpleConvexHull) 53 | { 54 | // generate convex collision 55 | FMeshConvexHull HullCompute(&SourceMesh); 56 | int32 NumTris = FMath::Clamp(this->MaxHullTriangles, 0, 1000); 57 | if (NumTris != 0) 58 | { 59 | HullCompute.bPostSimplify = true; 60 | HullCompute.MaxTargetFaceCount = NumTris; 61 | } 62 | if (HullCompute.Compute()) 63 | { 64 | FSimpleShapeSet3d ShapeSet; 65 | FConvexShape3d& Convex = ShapeSet.Convexes.Emplace_GetRef(); 66 | Convex.Mesh = MoveTemp(HullCompute.ConvexHull); 67 | 68 | MeshComponent->SetComplexAsSimpleCollisionEnabled(false); 69 | UE::Geometry::SetSimpleCollision(MeshComponent, &ShapeSet); 70 | } 71 | } 72 | 73 | MeshComponent->NotifyMeshUpdated(); 74 | 75 | // update material 76 | UMaterialInterface* UseMaterial = (this->Material != nullptr) ? this->Material : UMaterial::GetDefaultMaterial(MD_Surface); 77 | MeshComponent->SetMaterial(0, UseMaterial); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/RuntimeModelingObjectsCreationAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeModelingObjectsCreationAPI.h" 2 | #include "InteractiveToolsContext.h" 3 | #include "ContextObjectStore.h" 4 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 5 | 6 | using namespace UE::Geometry; 7 | 8 | 9 | URuntimeModelingObjectsCreationAPI* URuntimeModelingObjectsCreationAPI::Register(UInteractiveToolsContext* ToolsContext) 10 | { 11 | check(ToolsContext); 12 | 13 | if ( URuntimeModelingObjectsCreationAPI* CreationAPI = ToolsContext->ContextObjectStore->FindContext() ) 14 | { 15 | return CreationAPI; 16 | } 17 | auto NewCreationAPI = NewObject(ToolsContext); 18 | ToolsContext->ContextObjectStore->AddContextObject(NewCreationAPI); 19 | return NewCreationAPI; 20 | } 21 | 22 | 23 | bool URuntimeModelingObjectsCreationAPI::Deregister(UInteractiveToolsContext* ToolsContext) 24 | { 25 | check(ToolsContext); 26 | 27 | if (URuntimeModelingObjectsCreationAPI* FoundAPI = ToolsContext->ContextObjectStore->FindContext() ) 28 | { 29 | ToolsContext->ContextObjectStore->RemoveContextObject(FoundAPI); 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | 36 | FCreateMeshObjectResult URuntimeModelingObjectsCreationAPI::CreateMeshObject(const FCreateMeshObjectParams& CreateMeshParams) 37 | { 38 | // create new SceneObject 39 | URuntimeMeshSceneObject* SceneObject = URuntimeMeshSceneSubsystem::Get()->CreateNewSceneObject(); 40 | 41 | // initialize the mesh, depending on whether we were passed a FMeshDescription or a FDynamicMesh3 42 | if (CreateMeshParams.MeshType == ECreateMeshObjectSourceMeshType::MeshDescription) 43 | { 44 | SceneObject->Initialize(CreateMeshParams.TargetWorld, & CreateMeshParams.MeshDescription.GetValue() ); 45 | } 46 | else 47 | { 48 | SceneObject->Initialize(CreateMeshParams.TargetWorld, & CreateMeshParams.DynamicMesh.GetValue() ); 49 | } 50 | 51 | SceneObject->SetTransform(CreateMeshParams.Transform); 52 | 53 | // return the created Actor/Component 54 | FCreateMeshObjectResult Result; 55 | Result.ResultCode = ECreateModelingObjectResult::Ok; 56 | Result.NewActor = SceneObject->GetActor(); 57 | Result.NewComponent = SceneObject->GetMeshComponent(); 58 | return Result; 59 | } 60 | 61 | 62 | FCreateTextureObjectResult URuntimeModelingObjectsCreationAPI::CreateTextureObject(const FCreateTextureObjectParams& CreateTexParams) 63 | { 64 | check(false); // not supported! 65 | return FCreateTextureObjectResult{ ECreateModelingObjectResult::Failed_InvalidTexture }; 66 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Interaction/SceneObjectSelectionInteraction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BaseBehaviors/BehaviorTargetInterfaces.h" 4 | #include "BaseBehaviors/SingleClickBehavior.h" 5 | #include "InputBehaviorSet.h" 6 | #include "SceneObjectSelectionInteraction.generated.h" 7 | 8 | 9 | /** 10 | * USceneObjectSelectionInteraction implements standard mouse-click selection interaction of 11 | * "Scene Objects", ie the URuntimeMeshSceneObject's in the current URuntimeMeshSceneSubsystem. 12 | * 13 | * - Left-Click on object changes active selection to that object 14 | * - Left-Click on "background" clears active selection 15 | * - Shift+Click modifier adds to selection 16 | * - Ctrl+Click modifier toggles selected/deselected 17 | * 18 | * Currently hover is not supported, but this would be relatively easy to add 19 | */ 20 | UCLASS() 21 | class USceneObjectSelectionInteraction : public UObject, public IInputBehaviorSource, public IClickBehaviorTarget 22 | { 23 | GENERATED_BODY() 24 | 25 | public: 26 | 27 | /** 28 | * Set up the Interaction, creates and registers Behaviors/etc. 29 | * @param CanChangeSelectionCallbackIn this function will be called to determine if the current Selection is allowed to be modified (for example, when a Tool is active, we may wish to lock selection) 30 | */ 31 | void Initialize(TUniqueFunction CanChangeSelectionCallbackIn); 32 | 33 | 34 | 35 | public: 36 | // click-to-select behavior 37 | UPROPERTY() 38 | USingleClickInputBehavior* ClickBehavior; 39 | 40 | // set of all behaviors, will be passed up to UInputRouter 41 | UPROPERTY() 42 | UInputBehaviorSet* BehaviorSet; 43 | 44 | 45 | 46 | public: 47 | // 48 | // IInputBehaviorSource API 49 | // 50 | virtual const UInputBehaviorSet* GetInputBehaviors() const 51 | { 52 | return BehaviorSet; 53 | } 54 | 55 | // 56 | // IClickBehaviorTarget implementation 57 | // 58 | virtual FInputRayHit IsHitByClick(const FInputDeviceRay& ClickPos) override; 59 | virtual void OnClicked(const FInputDeviceRay& ClickPos) override; 60 | 61 | 62 | // 63 | // IModifierToggleBehaviorTarget implementation 64 | // 65 | virtual void OnUpdateModifierState(int ModifierID, bool bIsOn) override; 66 | 67 | 68 | protected: 69 | 70 | // default change-selection callback always allows selection change 71 | TUniqueFunction CanChangeSelectionCallback = []() { return true; }; 72 | 73 | // flags used to identify behavior modifier keys/buttons 74 | static const int AddToSelectionModifier = 1; 75 | bool bAddToSelectionEnabled = false; 76 | 77 | static const int ToggleSelectionModifier = 2; 78 | bool bToggleSelectionEnabled = false; 79 | 80 | }; -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicPMCActor.cpp: -------------------------------------------------------------------------------- 1 | #include "DynamicPMCActor.h" 2 | #include "MaterialDomain.h" 3 | #include "Materials/Material.h" 4 | #include "MeshComponentRuntimeUtils.h" 5 | #include "DynamicMesh/DynamicMesh3.h" 6 | #include "Operations/MeshConvexHull.h" 7 | 8 | using namespace UE::Geometry; 9 | 10 | // Sets default values 11 | ADynamicPMCActor::ADynamicPMCActor() 12 | { 13 | MeshComponent = CreateDefaultSubobject(TEXT("Mesh"), false); 14 | SetRootComponent(MeshComponent); 15 | } 16 | 17 | // Called when the game starts or when spawned 18 | void ADynamicPMCActor::BeginPlay() 19 | { 20 | Super::BeginPlay(); 21 | } 22 | 23 | // Called every frame 24 | void ADynamicPMCActor::Tick(float DeltaTime) 25 | { 26 | Super::Tick(DeltaTime); 27 | } 28 | 29 | 30 | void ADynamicPMCActor::OnMeshEditedInternal() 31 | { 32 | UpdatePMCMesh(); 33 | Super::OnMeshEditedInternal(); 34 | } 35 | 36 | void ADynamicPMCActor::UpdatePMCMesh() 37 | { 38 | if (MeshComponent) 39 | { 40 | bool bUseFaceNormals = (this->NormalsMode == EDynamicMeshActorNormalsMode::FaceNormals); 41 | bool bUseUV0 = true; 42 | bool bUseVertexColors = false; 43 | 44 | bool bGenerateSectionCollision = false; 45 | if (this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimple 46 | || this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync) 47 | { 48 | bGenerateSectionCollision = true; 49 | MeshComponent->bUseAsyncCooking = (this->CollisionMode == EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync); 50 | MeshComponent->bUseComplexAsSimpleCollision = true; 51 | } 52 | 53 | RTGUtils::UpdatePMCFromDynamicMesh_SplitTriangles(MeshComponent, &SourceMesh, bUseFaceNormals, bUseUV0, bUseVertexColors, bGenerateSectionCollision); 54 | 55 | // update material on new section 56 | UMaterialInterface* UseMaterial = (this->Material != nullptr) ? this->Material : UMaterial::GetDefaultMaterial(MD_Surface); 57 | MeshComponent->SetMaterial(0, UseMaterial); 58 | 59 | // generate convex collision 60 | if (this->CollisionMode == EDynamicMeshActorCollisionMode::SimpleConvexHull) 61 | { 62 | FMeshConvexHull HullCompute(&SourceMesh); 63 | int32 NumTris = FMath::Clamp(this->MaxHullTriangles, 0, 1000); 64 | if (NumTris != 0) 65 | { 66 | HullCompute.bPostSimplify = true; 67 | HullCompute.MaxTargetFaceCount = NumTris; 68 | } 69 | if (HullCompute.Compute()) 70 | { 71 | TArray Points; 72 | for (FVector3d Pos : HullCompute.ConvexHull.VerticesItr()) 73 | { 74 | Points.Add((FVector)Pos); 75 | } 76 | MeshComponent->bUseComplexAsSimpleCollision = false; 77 | MeshComponent->ClearCollisionConvexMeshes(); 78 | MeshComponent->AddCollisionConvexMesh(Points); 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Interaction/SceneObjectSelectionInteraction.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "SceneObjectSelectionInteraction.h" 3 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 4 | 5 | 6 | void USceneObjectSelectionInteraction::Initialize(TUniqueFunction CanChangeSelectionCallbackIn) 7 | { 8 | CanChangeSelectionCallback = MoveTemp(CanChangeSelectionCallbackIn); 9 | 10 | // create click behavior and set ourselves as click target 11 | ClickBehavior = NewObject(); 12 | ClickBehavior->Modifiers.RegisterModifier(AddToSelectionModifier, FInputDeviceState::IsShiftKeyDown); 13 | ClickBehavior->Modifiers.RegisterModifier(ToggleSelectionModifier, FInputDeviceState::IsCtrlKeyDown); 14 | ClickBehavior->Initialize(this); 15 | 16 | BehaviorSet = NewObject(); 17 | BehaviorSet->Add(ClickBehavior, this); 18 | } 19 | 20 | 21 | void USceneObjectSelectionInteraction::OnUpdateModifierState(int ModifierID, bool bIsOn) 22 | { 23 | // update modifier state flags 24 | if (ModifierID == AddToSelectionModifier) 25 | { 26 | bAddToSelectionEnabled = bIsOn; 27 | } 28 | else if (ModifierID == ToggleSelectionModifier) 29 | { 30 | bToggleSelectionEnabled = bIsOn; 31 | } 32 | } 33 | 34 | 35 | FInputRayHit USceneObjectSelectionInteraction::IsHitByClick(const FInputDeviceRay& ClickPos) 36 | { 37 | FInputRayHit RayHit; 38 | 39 | if (CanChangeSelectionCallback() == false) 40 | { 41 | return RayHit; 42 | } 43 | 44 | FVector HitPoint, BaryCoords; 45 | float HitDist; 46 | int32 HitTri; 47 | URuntimeMeshSceneObject* HitObject = URuntimeMeshSceneSubsystem::Get()->FindNearestHitObject( 48 | ClickPos.WorldRay.Origin, ClickPos.WorldRay.Direction, HitPoint, HitDist, HitTri, BaryCoords); 49 | 50 | if (HitObject != nullptr) 51 | { 52 | RayHit.bHit = true; 53 | RayHit.HitDepth = HitDist; 54 | //RayHit.HitNormal = ; // todo - can compute from bary coords 55 | //RayHit.bHasHitNormal = ; // todo - can compute from bary coords 56 | RayHit.HitIdentifier = HitTri; 57 | RayHit.HitOwner = HitObject; 58 | } 59 | else 60 | { 61 | RayHit.bHit = true; 62 | RayHit.HitDepth = TNumericLimits::Max(); 63 | RayHit.HitIdentifier = 0; 64 | RayHit.HitOwner = this; 65 | } 66 | return RayHit; 67 | } 68 | 69 | void USceneObjectSelectionInteraction::OnClicked(const FInputDeviceRay& ClickPos) 70 | { 71 | FVector HitPoint, BaryCoords; 72 | float HitDist; 73 | int32 HitTri; 74 | URuntimeMeshSceneObject* HitObject = URuntimeMeshSceneSubsystem::Get()->FindNearestHitObject( 75 | ClickPos.WorldRay.Origin, ClickPos.WorldRay.Direction, HitPoint, HitDist, HitTri, BaryCoords); 76 | 77 | if (HitObject != nullptr) 78 | { 79 | if (bAddToSelectionEnabled) 80 | { 81 | URuntimeMeshSceneSubsystem::Get()->SetSelected(HitObject, false, false); 82 | } 83 | else if (bToggleSelectionEnabled) 84 | { 85 | URuntimeMeshSceneSubsystem::Get()->ToggleSelected(HitObject); 86 | } 87 | else 88 | { 89 | URuntimeMeshSceneSubsystem::Get()->SetSelected(HitObject, false, true); 90 | } 91 | } 92 | else 93 | { 94 | URuntimeMeshSceneSubsystem::Get()->ClearSelection(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Tools/RuntimeDynamicMeshSculptTool.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeDynamicMeshSculptTool.h" 2 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 3 | 4 | #include "ToolBuilderUtil.h" 5 | 6 | #define LOCTEXT_NAMESPACE "URuntimeDynamicMeshSculptTool" 7 | 8 | UMeshSurfacePointTool* URuntimeDynamicMeshSculptToolBuilder::CreateNewTool(const FToolBuilderState& SceneState) const 9 | { 10 | URuntimeDynamicMeshSculptTool* SculptTool = NewObject(SceneState.ToolManager); 11 | SculptTool->SetEnableRemeshing(this->bEnableRemeshing); 12 | SculptTool->SetWorld(SceneState.World); 13 | return SculptTool; 14 | } 15 | 16 | 17 | 18 | 19 | static ERuntimeDynamicMeshSculptBrushType Convert(EDynamicMeshSculptBrushType BrushType) 20 | { 21 | switch (BrushType) 22 | { 23 | case EDynamicMeshSculptBrushType::Move: return ERuntimeDynamicMeshSculptBrushType::Move; 24 | case EDynamicMeshSculptBrushType::Smooth: return ERuntimeDynamicMeshSculptBrushType::Smooth; 25 | case EDynamicMeshSculptBrushType::Inflate: return ERuntimeDynamicMeshSculptBrushType::Inflate; 26 | case EDynamicMeshSculptBrushType::PlaneViewAligned: return ERuntimeDynamicMeshSculptBrushType::Flatten; 27 | default: return ERuntimeDynamicMeshSculptBrushType::Sculpt; 28 | } 29 | } 30 | static EDynamicMeshSculptBrushType Convert(ERuntimeDynamicMeshSculptBrushType BrushType) 31 | { 32 | switch (BrushType) 33 | { 34 | case ERuntimeDynamicMeshSculptBrushType::Move: return EDynamicMeshSculptBrushType::Move; 35 | case ERuntimeDynamicMeshSculptBrushType::Smooth: return EDynamicMeshSculptBrushType::Smooth; 36 | case ERuntimeDynamicMeshSculptBrushType::Inflate: return EDynamicMeshSculptBrushType::Inflate; 37 | case ERuntimeDynamicMeshSculptBrushType::Flatten: return EDynamicMeshSculptBrushType::PlaneViewAligned; 38 | default: return EDynamicMeshSculptBrushType::Offset; 39 | } 40 | } 41 | 42 | 43 | void URuntimeDynamicMeshSculptTool::Setup() 44 | { 45 | UDynamicMeshSculptTool::Setup(); 46 | 47 | // mirror properties we want to expose at runtime 48 | RuntimeProperties = NewObject(this); 49 | 50 | RuntimeProperties->BrushSize = BrushProperties->BrushSize.AdaptiveSize; 51 | RuntimeProperties->WatchProperty(RuntimeProperties->BrushSize, 52 | [this](float NewValue) { 53 | BrushProperties->BrushSize.AdaptiveSize = NewValue; 54 | OnPropertyModified(nullptr,nullptr); // hack to get CalculateBrushRadius() to be called, because it is private (why?) 55 | }); 56 | 57 | RuntimeProperties->BrushStrength = SculptProperties->PrimaryBrushSpeed; 58 | RuntimeProperties->WatchProperty(RuntimeProperties->BrushStrength, 59 | [this](float NewValue) { SculptProperties->PrimaryBrushSpeed = NewValue; }); 60 | 61 | RuntimeProperties->BrushFalloff = BrushProperties->BrushFalloffAmount; 62 | RuntimeProperties->WatchProperty(RuntimeProperties->BrushFalloff, 63 | [this](float NewValue) { BrushProperties->BrushFalloffAmount = NewValue; }); 64 | 65 | RuntimeProperties->SelectedBrushType = (int)Convert(SculptProperties->PrimaryBrushType); 66 | RuntimeProperties->WatchProperty(RuntimeProperties->SelectedBrushType, 67 | [this](int NewType) { SculptProperties->PrimaryBrushType = Convert((ERuntimeDynamicMeshSculptBrushType)NewType); }); 68 | 69 | AddToolPropertySource(RuntimeProperties); 70 | } 71 | 72 | 73 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/MeshScene/RuntimeMeshSceneObject.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Engine/StaticMeshActor.h" 7 | #include "Components/PrimitiveComponent.h" 8 | #include "Templates/PimplPtr.h" 9 | #include "DynamicMesh/DynamicMesh3.h" 10 | #include "DynamicMesh/DynamicMeshAABBTree3.h" 11 | #include "DynamicPMCActor.h" 12 | #include "DynamicSDMCActor.h" 13 | #include "RuntimeMeshSceneObject.generated.h" 14 | 15 | struct FMeshDescription; 16 | 17 | /** 18 | * URuntimeMeshSceneObject is a "Scene Object" in the "Scene". Do not create these yourself. 19 | * Use the functions in URuntimeMeshSceneSubsystem to create and manage SceneObjects. 20 | * 21 | * Conceptually, URuntimeMeshSceneObject is a triangle mesh object that can be selected, 22 | * transformed, and edited using mesh editing tools. 23 | * 24 | * Under the hood, URuntimeMeshSceneObject will spawn a ADynamicSDMCActor to actually implement 25 | * most of that functionality. But, the premise is that the higher level Scene is not aware 26 | * of those details. 27 | */ 28 | UCLASS() 29 | class RUNTIMETOOLSSYSTEM_API URuntimeMeshSceneObject : public UObject 30 | { 31 | using FDynamicMesh3 = UE::Geometry::FDynamicMesh3; 32 | using FDynamicMeshAABBTree3 = UE::Geometry::FDynamicMeshAABBTree3; 33 | 34 | GENERATED_BODY() 35 | 36 | public: 37 | URuntimeMeshSceneObject(); 38 | 39 | void Initialize(UWorld* TargetWorld, const FMeshDescription* InitialMeshDescription); 40 | void Initialize(UWorld* TargetWorld, const FDynamicMesh3* InitialMesh); 41 | 42 | // set the 3D transform of this SceneObject 43 | void SetTransform(FTransform Transform); 44 | 45 | // get the Actor that represents this SceneObject 46 | ADynamicMeshBaseActor* GetActor(); 47 | 48 | // get the mesh component that represents this SceneObject 49 | UMeshComponent* GetMeshComponent(); 50 | 51 | 52 | // 53 | // Material functions 54 | // 55 | 56 | UFUNCTION(BlueprintCallable, Category = "RuntimeMeshSceneObject") 57 | void CopyMaterialsFromComponent(); 58 | 59 | UFUNCTION(BlueprintCallable, Category = "RuntimeMeshSceneObject") 60 | void SetAllMaterials(UMaterialInterface* SetToMaterial); 61 | 62 | UFUNCTION(BlueprintCallable, Category = "RuntimeMeshSceneObject") 63 | void SetToHighlightMaterial(UMaterialInterface* Material); 64 | 65 | UFUNCTION(BlueprintCallable, Category = "RuntimeMeshSceneObject") 66 | void ClearHighlightMaterial(); 67 | 68 | 69 | // 70 | // Spatial Query functions 71 | // 72 | 73 | UFUNCTION(BlueprintCallable, Category = "RuntimeMeshSceneObject") 74 | bool IntersectRay(FVector RayOrigin, FVector RayDirection, FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, float MaxDistance = 0); 75 | 76 | 77 | protected: 78 | // URuntimeMeshSceneObject's representation in UE Level is a ADynamicSDMCActor 79 | UPROPERTY() 80 | ADynamicSDMCActor* SimpleDynamicMeshActor = nullptr; 81 | 82 | protected: 83 | 84 | TUniquePtr SourceMesh; 85 | TUniquePtr MeshAABBTree; 86 | 87 | void UpdateSourceMesh(const FMeshDescription* MeshDescription); 88 | 89 | void OnExternalDynamicMeshComponentUpdate(); 90 | 91 | TArray Materials; 92 | void UpdateComponentMaterials(bool bForceRefresh); 93 | }; 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | [/Script/EngineSettings.GeneralProjectSettings] 4 | ProjectID=80E8A8B14AAC26C38AC764B746D912E6 5 | 6 | [/Script/UnrealEd.ProjectPackagingSettings] 7 | Build=IfProjectHasCode 8 | BuildConfiguration=PPBC_Development 9 | BuildTarget= 10 | LaunchOnTarget= 11 | StagingDirectory=(Path="C:/BUILD") 12 | FullRebuild=False 13 | ForDistribution=False 14 | IncludeDebugFiles=False 15 | BlueprintNativizationMethod=Disabled 16 | bIncludeNativizedAssetsInProjectGeneration=False 17 | bExcludeMonolithicEngineHeadersInNativizedCode=False 18 | UsePakFile=True 19 | bUseIoStore=False 20 | bUseZenStore=False 21 | bMakeBinaryConfig=False 22 | bGenerateChunks=False 23 | bGenerateNoChunks=False 24 | bChunkHardReferencesOnly=False 25 | bForceOneChunkPerFile=False 26 | MaxChunkSize=0 27 | bBuildHttpChunkInstallData=False 28 | HttpChunkInstallDataDirectory=(Path="") 29 | bCompressed=False 30 | PackageCompressionFormat=Oodle 31 | bForceUseProjectCompressionFormatIgnoreHardwareOverride=False 32 | PackageAdditionalCompressionOptions= 33 | PackageCompressionMethod=Kraken 34 | PackageCompressionLevel_DebugDevelopment=4 35 | PackageCompressionLevel_TestShipping=5 36 | PackageCompressionLevel_Distribution=7 37 | PackageCompressionMinBytesSaved=1024 38 | PackageCompressionMinPercentSaved=5 39 | bPackageCompressionEnableDDC=False 40 | PackageCompressionMinSizeToConsiderDDC=0 41 | HttpChunkInstallDataVersion= 42 | IncludePrerequisites=True 43 | IncludeAppLocalPrerequisites=False 44 | bShareMaterialShaderCode=True 45 | bDeterministicShaderCodeOrder=False 46 | bSharedMaterialNativeLibraries=True 47 | ApplocalPrerequisitesDirectory=(Path="") 48 | IncludeCrashReporter=False 49 | InternationalizationPreset=English 50 | -CulturesToStage=en 51 | +CulturesToStage=en 52 | LocalizationTargetCatchAllChunkId=0 53 | bCookAll=False 54 | bCookMapsOnly=False 55 | bSkipEditorContent=False 56 | bSkipMovies=False 57 | -IniKeyDenylist=KeyStorePassword 58 | -IniKeyDenylist=KeyPassword 59 | -IniKeyDenylist=rsa.privateexp 60 | -IniKeyDenylist=rsa.modulus 61 | -IniKeyDenylist=rsa.publicexp 62 | -IniKeyDenylist=aes.key 63 | -IniKeyDenylist=SigningPublicExponent 64 | -IniKeyDenylist=SigningModulus 65 | -IniKeyDenylist=SigningPrivateExponent 66 | -IniKeyDenylist=EncryptionKey 67 | -IniKeyDenylist=DevCenterUsername 68 | -IniKeyDenylist=DevCenterPassword 69 | -IniKeyDenylist=IOSTeamID 70 | -IniKeyDenylist=SigningCertificate 71 | -IniKeyDenylist=MobileProvision 72 | -IniKeyDenylist=IniKeyDenylist 73 | -IniKeyDenylist=IniSectionDenylist 74 | +IniKeyDenylist=DevCenterUsername 75 | +IniKeyDenylist=DevCenterPassword 76 | +IniKeyDenylist=IOSTeamID 77 | +IniKeyDenylist=SigningCertificate 78 | +IniKeyDenylist=MobileProvision 79 | +IniKeyDenylist=KeyStorePassword 80 | +IniKeyDenylist=KeyPassword 81 | +IniKeyDenylist=rsa.privateexp 82 | +IniKeyDenylist=rsa.modulus 83 | +IniKeyDenylist=rsa.publicexp 84 | +IniKeyDenylist=aes.key 85 | +IniKeyDenylist=SigningPublicExponent 86 | +IniKeyDenylist=SigningModulus 87 | +IniKeyDenylist=SigningPrivateExponent 88 | +IniKeyDenylist=EncryptionKey 89 | +IniKeyDenylist=IniKeyDenylist 90 | +IniKeyDenylist=IniSectionDenylist 91 | -IniSectionDenylist=HordeStorageServers 92 | +IniSectionDenylist=HordeStorageServers 93 | +DirectoriesToAlwaysCook=(Path="/MeshModelingToolset/Materials") 94 | +DirectoriesToAlwaysCook=(Path="/MeshModelingToolsetExp/Materials") 95 | +DirectoriesToAlwaysCook=(Path="/Game/RuntimeToolsFrameworkMaterials") 96 | +DirectoriesToAlwaysStageAsNonUFS=(Path="SampleOBJFiles") 97 | PerPlatformBuildConfig=() 98 | PerPlatformTargetFlavorName=() 99 | PerPlatformBuildTarget=() 100 | 101 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/Interaction/SceneObjectTransformInteraction.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "SceneObjectTransformInteraction.h" 3 | 4 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 5 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 6 | 7 | #include "BaseGizmos/CombinedTransformGizmo.h" 8 | #include "BaseGizmos/TransformProxy.h" 9 | #include "BaseGizmos/TransformGizmoUtil.h" 10 | 11 | void USceneObjectTransformInteraction::Initialize(TUniqueFunction GizmoEnabledCallbackIn) 12 | { 13 | GizmoEnabledCallback = MoveTemp(GizmoEnabledCallbackIn); 14 | 15 | SelectionChangedEventHandle = URuntimeMeshSceneSubsystem::Get()->OnSelectionModified.AddLambda([this](URuntimeMeshSceneSubsystem* SceneSubsystem) 16 | { 17 | UpdateGizmoTargets(SceneSubsystem->GetSelection()); 18 | }); 19 | 20 | } 21 | 22 | void USceneObjectTransformInteraction::Shutdown() 23 | { 24 | if (SelectionChangedEventHandle.IsValid()) 25 | { 26 | if (URuntimeMeshSceneSubsystem::Get()) 27 | { 28 | URuntimeMeshSceneSubsystem::Get()->OnSelectionModified.Remove(SelectionChangedEventHandle); 29 | } 30 | SelectionChangedEventHandle = FDelegateHandle(); 31 | } 32 | 33 | TArray EmptySelection; 34 | UpdateGizmoTargets(EmptySelection); 35 | } 36 | 37 | 38 | void USceneObjectTransformInteraction::SetEnableScaling(bool bEnable) 39 | { 40 | if (bEnable != bEnableScaling) 41 | { 42 | bEnableScaling = bEnable; 43 | ForceUpdateGizmoState(); 44 | } 45 | } 46 | 47 | void USceneObjectTransformInteraction::SetEnableNonUniformScaling(bool bEnable) 48 | { 49 | if (bEnable != bEnableNonUniformScaling) 50 | { 51 | bEnableNonUniformScaling = bEnable; 52 | ForceUpdateGizmoState(); 53 | } 54 | } 55 | 56 | void USceneObjectTransformInteraction::ForceUpdateGizmoState() 57 | { 58 | if (URuntimeMeshSceneSubsystem::Get()) 59 | { 60 | UpdateGizmoTargets(URuntimeMeshSceneSubsystem::Get()->GetSelection()); 61 | } 62 | } 63 | 64 | 65 | void USceneObjectTransformInteraction::UpdateGizmoTargets(const TArray& Selection) 66 | { 67 | UInteractiveGizmoManager* GizmoManager = URuntimeToolsFrameworkSubsystem::Get()->ToolsContext->GizmoManager; 68 | 69 | // destroy existing gizmos if we have any 70 | if (TransformGizmo != nullptr) 71 | { 72 | GizmoManager->DestroyAllGizmosByOwner(this); 73 | TransformGizmo = nullptr; 74 | TransformProxy = nullptr; 75 | } 76 | 77 | // if no selection, no gizmo 78 | if (Selection.Num() == 0 || GizmoEnabledCallback() == false ) 79 | { 80 | return; 81 | } 82 | 83 | TransformProxy = NewObject(this); 84 | for (URuntimeMeshSceneObject* SO : Selection) 85 | { 86 | // would be nice if this worked on Actors... 87 | TransformProxy->AddComponent(SO->GetMeshComponent()); 88 | } 89 | 90 | ETransformGizmoSubElements GizmoElements = ETransformGizmoSubElements::FullTranslateRotateScale; 91 | if (bEnableScaling == false) 92 | { 93 | GizmoElements = ETransformGizmoSubElements::StandardTranslateRotate; 94 | } 95 | else if (bEnableNonUniformScaling == false || Selection.Num() > 1) // cannot nonuniform-scale multiple objects 96 | { 97 | GizmoElements = ETransformGizmoSubElements::TranslateRotateUniformScale; 98 | } 99 | 100 | TransformGizmo = UE::TransformGizmoUtil::CreateCustomTransformGizmo(GizmoManager, GizmoElements, this); 101 | TransformGizmo->SetActiveTarget(TransformProxy); 102 | 103 | // optionally ignore coordinate system setting 104 | //TransformGizmo->bUseContextCoordinateSystem = false; 105 | //TransformGizmo->CurrentCoordinateSystem = EToolContextCoordinateSystem::Local; 106 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/ToolsContextActor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "GameFramework/DefaultPawn.h" 5 | #include "ToolsContextActor.generated.h" 6 | 7 | class URuntimeToolsFrameworkSubsystem; 8 | 9 | /** 10 | * Camera-control modes that AToolsContextActor has implemented 11 | */ 12 | UENUM() 13 | enum class EToolActorInteractionMode : uint8 14 | { 15 | NoInteraction, 16 | RightMouseCameraControl, 17 | AltCameraControl 18 | }; 19 | 20 | /** 21 | * AToolsContextActor is the Pawn used in the AToolsFrameworkDemoGameModeBase. 22 | * This Game Mode initializes the URuntimeMeshSceneSubsystem and URuntimeToolsFrameworkSubsystem. 23 | * Essentially, this Actor has two jobs: 24 | * 25 | * 1) to forward Input events (from the PlayerInputComponent) to these subsystems, 26 | * both mouse events and Actions configured in the project settings. 27 | * 28 | * 2) to implement "camera control", as the rendering is done from this actors position (ie as a first-person view). 29 | * The base ADefaultPawn implements standard right-mouse-fly controls, and so if right mouse is held down, 30 | * we just call those functions. In addition, Hold-alt camera controls are also done here, rather 31 | * than using the ITF. (*However*, the alt-controls are not fully implemented, ie it is not Maya-style alt+3-mouse-button controls) 32 | */ 33 | UCLASS() 34 | class RUNTIMETOOLSSYSTEM_API AToolsContextActor : public ADefaultPawn 35 | { 36 | GENERATED_BODY() 37 | 38 | public: 39 | // Sets default values for this pawn's properties 40 | AToolsContextActor(); 41 | 42 | // set in PossessedBy(), we keep track of this so that the ToolsFramework can access it to figure out mouse cursor rays 43 | UPROPERTY() 44 | APlayerController* PlayerController; 45 | 46 | // return current interaction mode, ie are we in a camera interaction or not 47 | UFUNCTION(BlueprintCallable) 48 | EToolActorInteractionMode GetCurrentInteractionMode() { return CurrentInteractionMode; } 49 | 50 | protected: 51 | // Called when the game starts or when spawned 52 | virtual void BeginPlay() override; 53 | // called when despawned 54 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 55 | 56 | public: 57 | // Called every frame 58 | virtual void Tick(float DeltaTime) override; 59 | 60 | // Called to bind functionality to input 61 | virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; 62 | 63 | // called on startup 64 | virtual void PossessedBy(AController* inController) override; 65 | 66 | 67 | protected: 68 | URuntimeToolsFrameworkSubsystem* ToolsSystem; 69 | 70 | EToolActorInteractionMode CurrentInteractionMode = EToolActorInteractionMode::NoInteraction; 71 | bool bIsRightMouseDown = false; 72 | bool bIsMiddleMouseDown = false; 73 | bool bIsLeftMouseDown = false; 74 | bool bIsAltKeyDown = false; 75 | 76 | virtual void OnLeftMouseDown(); 77 | virtual void OnLeftMouseUp(); 78 | 79 | virtual void OnMiddleMouseDown(); 80 | virtual void OnMiddleMouseUp(); 81 | 82 | virtual void OnRightMouseDown(); 83 | virtual void OnRightMouseUp(); 84 | 85 | virtual void OnAltKeyDown(); 86 | virtual void OnAltKeyUp(); 87 | 88 | virtual void OnToolAccept(); 89 | virtual void OnToolExit(); 90 | 91 | virtual void OnDelete(); 92 | virtual void OnUndo(); 93 | virtual void OnRedo(); 94 | 95 | virtual void OnMoveForwardKeyAxis(float MoveDelta); 96 | virtual void OnMoveRightKeyAxis(float MoveDelta); 97 | virtual void OnMoveUpKeyAxis(float MoveDelta); 98 | 99 | virtual void OnMouseMoveX(float MoveX); 100 | virtual void OnMouseMoveY(float MoveY); 101 | }; 102 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/ToolsContextRenderComponent.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Components/PrimitiveComponent.h" 7 | #include "ToolsContextRenderComponent.generated.h" 8 | 9 | class FPrimitiveDrawInterface; 10 | 11 | /** 12 | * UToolsContextRenderComponent is a helper component that can provide a FPrimitiveDrawInterface 13 | * API implementation. This PDI can be passed to UInterativeTool::Render() and 14 | * UInteractiveGizmo::Render() (via a IToolsContextRenderAPI implementation). 15 | * The UToolsContextRenderComponent will accumulate any DrawLine() and DrawPoint() requests 16 | * and then pass them to it's SceneProxy for rendering in the next frame. 17 | * 18 | * (in the UE Editor, those functions can be passed an Editor PDI that can draw immediately, 19 | * but this is not possible at Runtime, so we use this accumulate-and-draw workaround) 20 | * 21 | * TODO: lines and points could be drawn via Line/Point Components, which would allow them 22 | * to be batched, providing much better performance. 23 | */ 24 | UCLASS() 25 | class RUNTIMETOOLSSYSTEM_API UToolsContextRenderComponent : public UPrimitiveComponent 26 | { 27 | GENERATED_BODY() 28 | public: 29 | 30 | // parameters passed to a PDI DrawLine() call 31 | struct FPDILine 32 | { 33 | const FVector Start; 34 | const FVector End; 35 | const FLinearColor Color; 36 | uint8 DepthPriorityGroup; 37 | float Thickness; 38 | float DepthBias; 39 | bool bScreenSpace; 40 | }; 41 | 42 | // parameters passed to a PDI DrawPoint() call 43 | struct FPDIPoint 44 | { 45 | FVector Position; 46 | FLinearColor Color; 47 | float PointSize; 48 | uint8 DepthPriorityGroup; 49 | }; 50 | 51 | public: 52 | 53 | /** @return a new FPrimitiveDrawInterface implementation allocated for the given FSceneView. See .cpp for details. */ 54 | TSharedPtr GetPDIForView(const FSceneView* InView); 55 | 56 | // mirrors FPrimitiveDrawInterface::DrawLine() 57 | virtual void DrawLine( 58 | const FVector& Start, 59 | const FVector& End, 60 | const FLinearColor& Color, 61 | uint8 DepthPriorityGroup, 62 | float Thickness = 0.0f, 63 | float DepthBias = 0.0f, 64 | bool bScreenSpace = false 65 | ); 66 | 67 | // mirrors FPrimitiveDrawInterface::DrawPoint() 68 | virtual void DrawPoint( 69 | const FVector& Position, 70 | const FLinearColor& Color, 71 | float PointSize, 72 | uint8 DepthPriorityGroup 73 | ); 74 | 75 | 76 | protected: 77 | 78 | // set of lines populated by DrawLine calls 79 | TArray CurrentLines; 80 | 81 | // set of points created by DrawPoint calls 82 | TArray CurrentPoints; 83 | 84 | // protects CurrentLines and CurrentPoints 85 | FCriticalSection GeometryLock; 86 | 87 | // returns a lambda that will be passed to SceneProxy, which will then allow it to 88 | // steal values of CurrentLines and CurrentPoints 89 | TUniqueFunction&, TArray&)> MakeGetCurrentGeometryQueryFunc(); 90 | 91 | //~ Begin UPrimitiveComponent Interface. 92 | virtual FPrimitiveSceneProxy* CreateSceneProxy() override; 93 | virtual bool LineTraceComponent(FHitResult& OutHit, const FVector Start, const FVector End, const FCollisionQueryParams& Params) override; 94 | //~ End UPrimitiveComponent Interface. 95 | 96 | //~ Begin USceneComponent Interface. 97 | virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; 98 | //~ Begin USceneComponent Interface. 99 | 100 | // proxy is defined in cpp 101 | friend class FToolsContextRenderComponentSceneProxy; 102 | }; 103 | -------------------------------------------------------------------------------- /Source/ToolsFrameworkDemo/ToolsFrameworkDemoGameModeBase.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolsFrameworkDemoGameModeBase.h" 2 | 3 | #include "RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h" 4 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 5 | 6 | #include "AddPrimitiveTool.h" 7 | #include "DrawAndRevolveTool.h" 8 | #include "MeshVertexSculptTool.h" 9 | #include "PlaneCutTool.h" 10 | 11 | #include "Tools/RuntimeDrawPolygonTool.h" 12 | #include "Tools/RuntimeDynamicMeshSculptTool.h" 13 | #include "Tools/RuntimeRemeshMeshTool.h" 14 | #include "Tools/RuntimeMeshBooleanTool.h" 15 | #include "Tools/RuntimePolyEditTool.h" 16 | 17 | 18 | AToolsFrameworkDemoGameModeBase::AToolsFrameworkDemoGameModeBase() 19 | { 20 | PrimaryActorTick.bStartWithTickEnabled = true; 21 | PrimaryActorTick.bCanEverTick = true; 22 | } 23 | 24 | 25 | void AToolsFrameworkDemoGameModeBase::StartPlay() 26 | { 27 | Super::StartPlay(); 28 | InitializeToolsSystem(); 29 | } 30 | 31 | 32 | void AToolsFrameworkDemoGameModeBase::InitializeToolsSystem() 33 | { 34 | UWorld* World = GetWorld(); 35 | UGameInstance* GameInstance = GetGameInstance(); 36 | check(World && GameInstance); 37 | 38 | // create Scene subsystem 39 | SceneSystem = UGameInstance::GetSubsystem(GameInstance); 40 | URuntimeMeshSceneSubsystem::InitializeSingleton(SceneSystem); 41 | 42 | // create Tools subsystem 43 | ToolsSystem = UGameInstance::GetSubsystem(GameInstance); 44 | URuntimeToolsFrameworkSubsystem::InitializeSingleton(ToolsSystem); 45 | 46 | check(SceneSystem && ToolsSystem); 47 | 48 | // initialize Tools and Scene systems 49 | ToolsSystem->InitializeToolsContext(World); 50 | SceneSystem->SetCurrentTransactionsAPI(ToolsSystem->GetTransactionsAPI()); 51 | 52 | RegisterTools(); 53 | } 54 | 55 | 56 | void AToolsFrameworkDemoGameModeBase::RegisterTools() 57 | { 58 | UInteractiveToolManager* ToolManager = ToolsSystem->ToolsContext->ToolManager; 59 | 60 | auto AddPrimitiveToolBuilder = NewObject(); 61 | AddPrimitiveToolBuilder->ShapeType = UAddPrimitiveToolBuilder::EMakeMeshShapeType::Box; 62 | ToolManager->RegisterToolType("AddPrimitiveBox", AddPrimitiveToolBuilder); 63 | 64 | auto DrawPolygonToolBuilder = NewObject(); 65 | ToolManager->RegisterToolType("DrawPolygon", DrawPolygonToolBuilder); 66 | 67 | auto PolyRevolveToolBuilder = NewObject(); 68 | ToolManager->RegisterToolType("PolyRevolve", PolyRevolveToolBuilder); 69 | 70 | auto PolyEditToolBuilder = NewObject(); 71 | ToolManager->RegisterToolType("EditPolygons", PolyEditToolBuilder); 72 | 73 | auto MeshPlaneCutToolBuilder = NewObject(); 74 | ToolManager->RegisterToolType("PlaneCut", MeshPlaneCutToolBuilder); 75 | 76 | auto RemeshMeshToolBuilder = NewObject(); 77 | ToolManager->RegisterToolType("RemeshMesh", RemeshMeshToolBuilder); 78 | 79 | auto VertexSculptToolBuilder = NewObject(); 80 | ToolManager->RegisterToolType("VertexSculpt", VertexSculptToolBuilder); 81 | 82 | auto DynaSculptToolBuilder = NewObject(); 83 | DynaSculptToolBuilder->bEnableRemeshing = true; 84 | ToolManager->RegisterToolType("DynaSculpt", DynaSculptToolBuilder); 85 | 86 | auto MeshBooleanToolBuilder = NewObject(); 87 | ToolManager->RegisterToolType("MeshBoolean", MeshBooleanToolBuilder); 88 | } 89 | 90 | 91 | void AToolsFrameworkDemoGameModeBase::ShutdownToolsSystem() 92 | { 93 | // TODO: implement this 94 | check(false); 95 | } 96 | 97 | 98 | void AToolsFrameworkDemoGameModeBase::Tick(float DeltaTime) 99 | { 100 | Super::Tick(DeltaTime); 101 | 102 | if (ToolsSystem) 103 | { 104 | ToolsSystem->Tick(DeltaTime); 105 | } 106 | } 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/MeshScene/SceneHistoryManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Misc/Change.h" 5 | #include "SceneHistoryManager.generated.h" 6 | 7 | /** 8 | * FChangeHistoryRecord is a (UObject, FCommandChange, Description) tuple, as a UStruct so that the UObject can be kept alive for GC purposes 9 | */ 10 | USTRUCT() 11 | struct FChangeHistoryRecord 12 | { 13 | GENERATED_BODY() 14 | 15 | UPROPERTY() 16 | UObject* TargetObject = nullptr; 17 | 18 | UPROPERTY() 19 | FText Description; 20 | 21 | 22 | // UStruct and needs to be copyable so we need to wrap the TUniquePtr in a TSharedPtr. Gross. 23 | struct FChangeWrapper 24 | { 25 | TUniquePtr Change; 26 | }; 27 | 28 | TSharedPtr ChangeWrapper; 29 | }; 30 | 31 | 32 | /** 33 | * FChangeHistoryTransaction stores a list of FChangeHistoryRecord's 34 | */ 35 | USTRUCT() 36 | struct FChangeHistoryTransaction 37 | { 38 | GENERATED_BODY() 39 | 40 | UPROPERTY() 41 | TArray Records; 42 | 43 | UPROPERTY() 44 | FText Description; 45 | 46 | /** @return true if all contained FCommandChange's have Expired. In this case, the entire Transaction will have no effect, and should be skipped in Undo/Redo. */ 47 | bool HasExpired() const; 48 | }; 49 | 50 | 51 | /** 52 | * USceneHistoryManager provides a simple undo/redo history stack. In this context a "Transaction" is not a UObject property transaction, 53 | * but simply a list of FCommandChange objects. Transaction terminology is used here to be make the relationship clear 54 | * with various InteractiveToolsFramework APIs. 55 | * 56 | * Note that FCommandChange's are paired with UObjects, similar to the Editor Transaction system. This is necessary because 57 | * the Change implementations generally do not store the target UObject (because that's how the Editor works). 58 | * The FChangeHistoryTransaction and FChangeHistoryRecord structs are UStructs, stored in UProperties, so they will keep 59 | * these UObjects alive and prevent them from being GC'd (again similar to the UE Editor) 60 | */ 61 | UCLASS() 62 | class USceneHistoryManager : public UObject 63 | { 64 | GENERATED_BODY() 65 | 66 | public: 67 | 68 | /** Open a new Transaction, ie list of (UObject,FCommandChange) pairs */ 69 | void BeginTransaction(const FText& Description); 70 | 71 | /** Append a Change to the open Transaction */ 72 | void AppendChange(UObject* TargetObject, TUniquePtr Change, const FText& Description); 73 | 74 | /** Close the current Transaction and add it to the History sequence */ 75 | void EndTransaction(); 76 | 77 | 78 | /** @return true if we are inside an open Transaction */ 79 | UFUNCTION(BlueprintCallable) 80 | bool IsBuildingTransaction() const { return BeginTransactionDepth > 0; } 81 | 82 | /** Roll back in History to previous state by Revert()'ing intermediate Changes */ 83 | UFUNCTION(BlueprintCallable) 84 | void Undo(); 85 | 86 | /** Roll forward in History to next state by Apply()'ing intermediate Changes */ 87 | UFUNCTION(BlueprintCallable) 88 | void Redo(); 89 | 90 | /** This delegate is fired whenever we Undo() or Redo() */ 91 | DECLARE_MULTICAST_DELEGATE(FSceneHistoryStateChangeEvent); 92 | FSceneHistoryStateChangeEvent OnHistoryStateChange; 93 | 94 | protected: 95 | 96 | // undo history, stored as a set of transactions, which are themselves list of (UObject,FCommandChange) pairs 97 | UPROPERTY() 98 | TArray Transactions; 99 | 100 | // current index in Transactions list, will be Transactions.Num() unless user is Undo()/Redo()-ing 101 | int32 CurrentIndex = 0; 102 | 103 | // remove any elements of Transactions list beyond CurrentIndex (called if we are in Undo state and a new transaction is opened) 104 | void TruncateHistory(); 105 | 106 | // transaction currently being built 107 | UPROPERTY() 108 | FChangeHistoryTransaction ActiveTransaction; 109 | 110 | 111 | int BeginTransactionDepth = 0; 112 | }; 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/GeneratedMeshDeformersLibrary.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "GeneratedMeshDeformersLibrary.h" 3 | #include "DynamicMesh/DynamicMesh3.h" 4 | #include "DynamicMesh/MeshNormals.h" 5 | #include "FrameTypes.h" 6 | #include "Async/ParallelFor.h" 7 | 8 | using namespace UE::Geometry; 9 | 10 | UGeneratedMesh* UGeneratedMeshDeformersLibrary::DeformMeshAxisSinWave1D(UGeneratedMesh* MeshObj, float Magnitude, float Frequency, float FrequencyShift, FVector AxisIn, FVector UpIn) 11 | { 12 | FVector3d Axis(AxisIn), UpVector(UpIn); 13 | Axis.Normalize(); 14 | UpVector.Normalize(); 15 | 16 | if (MeshObj) 17 | { 18 | MeshObj->EditMeshInPlace([&](FDynamicMesh3& Mesh) 19 | { 20 | ParallelFor(Mesh.MaxVertexID(), [&](int32 vid) 21 | { 22 | if (Mesh.IsVertex(vid)) 23 | { 24 | FVector3d Pos = Mesh.GetVertex(vid); 25 | double Dot = Pos.Dot(Axis); 26 | Dot += FrequencyShift; 27 | FVector3d NewPos = Pos + Magnitude * FMathd::Sin(Frequency * Dot) * UpVector; 28 | Mesh.SetVertex(vid, NewPos); 29 | } 30 | }); 31 | }); 32 | } 33 | 34 | return MeshObj; 35 | } 36 | 37 | 38 | 39 | UGeneratedMesh* UGeneratedMeshDeformersLibrary::DeformMeshAxisSinWaveRadial(UGeneratedMesh* MeshObj, float Magnitude, float Frequency, float FrequencyShift, FVector AxisIn) 40 | { 41 | FVector3d Axis(AxisIn); 42 | Axis.Normalize(); 43 | FFrame3d AxisFrame(FVector3d::Zero(), Axis); 44 | 45 | if (MeshObj) 46 | { 47 | MeshObj->EditMeshInPlace([&](FDynamicMesh3& Mesh) 48 | { 49 | ParallelFor(Mesh.MaxVertexID(), [&](int32 vid) 50 | { 51 | if (Mesh.IsVertex(vid)) 52 | { 53 | FVector3d Pos = Mesh.GetVertex(vid); 54 | double Dot = Pos.Dot(Axis); 55 | Dot += FrequencyShift; 56 | double Displacement = Magnitude * FMathd::Sin(Frequency * (Dot + FrequencyShift)); 57 | FVector3d PlaneVec = AxisFrame.ToPlane(Pos); 58 | PlaneVec.Normalize(); 59 | FVector3d NewPos = Pos + Displacement * PlaneVec; 60 | Mesh.SetVertex(vid, NewPos); 61 | } 62 | }); 63 | }); 64 | } 65 | 66 | return MeshObj; 67 | } 68 | 69 | 70 | 71 | UGeneratedMesh* UGeneratedMeshDeformersLibrary::DeformMeshPerlinNoiseNormal(UGeneratedMesh* MeshObj, float Magnitude, float Frequency, FVector FrequencyShift, int RandomSeed) 72 | { 73 | if (MeshObj) 74 | { 75 | MeshObj->EditMeshInPlace([&](FDynamicMesh3& Mesh) 76 | { 77 | FMath::SRandInit(RandomSeed); 78 | const float RandomOffset = 10000.0f * FMath::SRand(); 79 | FVector3d Offset(RandomOffset, RandomOffset, RandomOffset); 80 | Offset += (FVector3d)FrequencyShift; 81 | 82 | FMeshNormals Normals(&Mesh); 83 | Normals.ComputeVertexNormals(); 84 | 85 | ParallelFor(Mesh.MaxVertexID(), [&](int32 vid) 86 | { 87 | if (Mesh.IsVertex(vid)) 88 | { 89 | FVector3d Pos = Mesh.GetVertex(vid); 90 | FVector NoisePos = (FVector)( (double)Frequency * (Pos + Offset) ); 91 | float Displacement = Magnitude * FMath::PerlinNoise3D(Frequency * NoisePos); 92 | FVector3d NewPos = Pos + Displacement * Normals[vid]; 93 | Mesh.SetVertex(vid, NewPos); 94 | } 95 | }); 96 | }); 97 | } 98 | 99 | return MeshObj; 100 | } 101 | 102 | 103 | 104 | 105 | UGeneratedMesh* UGeneratedMeshDeformersLibrary::SmoothMeshUniform(UGeneratedMesh* MeshObj, float Alpha, int32 Iterations) 106 | { 107 | Alpha = FMathf::Clamp(Alpha, 0.0f, 1.0f); 108 | Iterations = FMath::Clamp(Iterations, 0, 100); 109 | 110 | if (MeshObj) 111 | { 112 | MeshObj->EditMeshInPlace([&](FDynamicMesh3& Mesh) 113 | { 114 | int32 NumV = Mesh.MaxVertexID(); 115 | TArray SmoothPositions; 116 | SmoothPositions.SetNum(NumV); 117 | 118 | for (int32 k = 0; k < Iterations; ++k) 119 | { 120 | ParallelFor(NumV, [&](int32 vid) 121 | { 122 | if (Mesh.IsVertex(vid)) 123 | { 124 | FVector3d Centroid; 125 | Mesh.GetVtxOneRingCentroid(vid, Centroid); 126 | SmoothPositions[vid] = UE::Geometry::Lerp(Mesh.GetVertex(vid), Centroid, (double)Alpha); 127 | } 128 | }); 129 | for (int32 vid = 0; vid < NumV; ++vid) 130 | { 131 | if (Mesh.IsVertex(vid)) 132 | { 133 | Mesh.SetVertex(vid, SmoothPositions[vid]); 134 | } 135 | } 136 | } 137 | }); 138 | } 139 | 140 | return MeshObj; 141 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/MeshScene/SceneHistoryManager.cpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "SceneHistoryManager.h" 5 | 6 | 7 | 8 | bool FChangeHistoryTransaction::HasExpired() const 9 | { 10 | for (const FChangeHistoryRecord& Record : Records) 11 | { 12 | if (Record.ChangeWrapper->Change->HasExpired(Record.TargetObject) == false) 13 | { 14 | return false; 15 | } 16 | } 17 | return true; 18 | } 19 | 20 | 21 | void USceneHistoryManager::BeginTransaction(const FText& Description) 22 | { 23 | if (BeginTransactionDepth != 0) 24 | { 25 | BeginTransactionDepth++; 26 | return; 27 | } 28 | else 29 | { 30 | TruncateHistory(); 31 | 32 | ActiveTransaction = FChangeHistoryTransaction(); 33 | ActiveTransaction.Description = Description; 34 | 35 | BeginTransactionDepth++; 36 | } 37 | } 38 | 39 | 40 | void USceneHistoryManager::EndTransaction() 41 | { 42 | if (ensure(BeginTransactionDepth > 0) == false) return; 43 | 44 | BeginTransactionDepth--; 45 | 46 | if (BeginTransactionDepth == 0) 47 | { 48 | if (ActiveTransaction.Records.Num() > 0) 49 | { 50 | Transactions.Add(MoveTemp(ActiveTransaction)); 51 | } 52 | else 53 | { 54 | UE_LOG(LogTemp, Warning, TEXT("[EndTransaction] Empty Transaction Record!")); 55 | } 56 | 57 | ActiveTransaction = FChangeHistoryTransaction(); 58 | 59 | CurrentIndex = Transactions.Num(); 60 | } 61 | } 62 | 63 | 64 | void USceneHistoryManager::TruncateHistory() 65 | { 66 | // truncate history if we are in undo step 67 | if (CurrentIndex < Transactions.Num()) 68 | { 69 | Transactions.SetNum(CurrentIndex); 70 | } 71 | } 72 | 73 | 74 | void USceneHistoryManager::AppendChange(UObject* TargetObject, TUniquePtr Change, const FText& Description) 75 | { 76 | bool bAutoCloseTransaction = false; 77 | if (ensure(BeginTransactionDepth > 0) == false) 78 | { 79 | BeginTransaction(Description); 80 | bAutoCloseTransaction = true; 81 | } 82 | 83 | FChangeHistoryRecord Record; 84 | Record.TargetObject = TargetObject; 85 | Record.Description = Description; 86 | Record.ChangeWrapper = MakeShared(); 87 | Record.ChangeWrapper->Change = MoveTemp(Change); 88 | 89 | UE_LOG(LogTemp, Warning, TEXT("[HISTORY] %s"), *Record.Description.ToString()); 90 | 91 | ActiveTransaction.Records.Add(MoveTemp(Record)); 92 | 93 | if (bAutoCloseTransaction) 94 | { 95 | EndTransaction(); 96 | } 97 | } 98 | 99 | 100 | 101 | 102 | void USceneHistoryManager::Undo() 103 | { 104 | int32 NumReverted = 0; 105 | while (CurrentIndex > 0) 106 | { 107 | CurrentIndex = CurrentIndex - 1; 108 | UE_LOG(LogTemp, Warning, TEXT("[UNDO] %s"), *Transactions[CurrentIndex].Description.ToString()); 109 | 110 | // if transaction has expired, it is effectively a no-op and so we will continue to Undo() 111 | bool bContinue = Transactions[CurrentIndex].HasExpired(); 112 | 113 | const TArray& Records = Transactions[CurrentIndex].Records; 114 | for (int32 k = Records.Num() - 1; k >= 0; --k) 115 | { 116 | if (Records[k].TargetObject) 117 | { 118 | Records[k].ChangeWrapper->Change->Revert(Records[k].TargetObject); 119 | } 120 | NumReverted++; 121 | } 122 | 123 | if (!bContinue) 124 | { 125 | return; 126 | } 127 | } 128 | 129 | if (NumReverted > 0) 130 | { 131 | OnHistoryStateChange.Broadcast(); 132 | } 133 | } 134 | 135 | void USceneHistoryManager::Redo() 136 | { 137 | int32 NumApplied = 0; 138 | while (CurrentIndex < Transactions.Num()) 139 | { 140 | // if transaction has expired, it is effectively a no-op and so we will continue to Redo() 141 | bool bContinue = Transactions[CurrentIndex].HasExpired(); 142 | 143 | const TArray& Records = Transactions[CurrentIndex].Records; 144 | for (int32 k = 0; k < Records.Num(); ++k) 145 | { 146 | if (Records[k].TargetObject) 147 | { 148 | Records[k].ChangeWrapper->Change->Apply(Records[k].TargetObject); 149 | } 150 | ++NumApplied; 151 | } 152 | 153 | UE_LOG(LogTemp, Warning, TEXT("[UNDO] %s"), *Transactions[CurrentIndex].Description.ToString()); 154 | CurrentIndex = CurrentIndex + 1; 155 | 156 | if (!bContinue) 157 | { 158 | return; 159 | } 160 | } 161 | 162 | if (NumApplied > 0) 163 | { 164 | OnHistoryStateChange.Broadcast(); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/RuntimeDynamicMeshComponentToolTarget.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TargetInterfaces/DynamicMeshCommitter.h" 4 | #include "TargetInterfaces/DynamicMeshProvider.h" 5 | #include "TargetInterfaces/MaterialProvider.h" 6 | #include "TargetInterfaces/MeshDescriptionCommitter.h" 7 | #include "TargetInterfaces/MeshDescriptionProvider.h" 8 | #include "TargetInterfaces/DynamicMeshSource.h" 9 | #include "TargetInterfaces/PhysicsDataSource.h" 10 | #include "ToolTargets/PrimitiveComponentToolTarget.h" 11 | #include "RuntimeDynamicMeshComponentToolTarget.generated.h" 12 | 13 | class UDynamicMesh; 14 | 15 | /** 16 | * URuntimeDynamicMeshComponentToolTarget is a UToolTarget implementation suitable for 17 | * UDynamicMeshComponent, which is the Component Type that ultimately backs a URuntimeMeshSceneObject 18 | * (via an ADynamicSDMCActor). 19 | * 20 | * The Engine provides UPrimitiveComponentToolTarget, which we subclass to re-use various 21 | * common UPrimitiveComponent functionality. The Engine actually also provides UDynamicMeshComponentToolTarget, 22 | * but it is Editor-Only and so cannot be used at Runtime. This class mirrors that 23 | * implementation heavily, although some things are specific to the Runtime system 24 | * (ie no transactions, changes are emitted via RuntimeToolsFrameworkSubsystem) 25 | * 26 | */ 27 | UCLASS(Transient) 28 | class RUNTIMETOOLSSYSTEM_API URuntimeDynamicMeshComponentToolTarget : 29 | public UPrimitiveComponentToolTarget, 30 | public IMeshDescriptionCommitter, public IMeshDescriptionProvider, 31 | public IDynamicMeshProvider, public IDynamicMeshCommitter, 32 | public IMaterialProvider, 33 | public IPersistentDynamicMeshSource, 34 | public IPhysicsDataSource 35 | { 36 | GENERATED_BODY() 37 | public: 38 | 39 | // override UPrimitiveComponentToolTarget to also check if underlying UDynamicMesh is valid 40 | virtual bool IsValid() const override; 41 | 42 | // IMeshDescriptionProvider 43 | const FMeshDescription* GetMeshDescription(const FGetMeshParameters& GetMeshParams = FGetMeshParameters()) override; 44 | virtual FMeshDescription GetEmptyMeshDescription() override; 45 | 46 | // IMeshDescriptionCommitter 47 | virtual void CommitMeshDescription(const FCommitter& Committer, const FCommitMeshParameters& CommitMeshParams = FCommitMeshParameters()) override; 48 | using IMeshDescriptionCommitter::CommitMeshDescription; 49 | 50 | // IDynamicMeshProvider 51 | virtual UE::Geometry::FDynamicMesh3 GetDynamicMesh() override; 52 | 53 | // IDynamicMeshCommitter 54 | virtual void CommitDynamicMesh(const UE::Geometry::FDynamicMesh3& Mesh, const FDynamicMeshCommitInfo& CommitInfo) override; 55 | using IDynamicMeshCommitter::CommitDynamicMesh; 56 | 57 | // IMaterialProvider 58 | int32 GetNumMaterials() const override; 59 | UMaterialInterface* GetMaterial(int32 MaterialIndex) const override; 60 | void GetMaterialSet(FComponentMaterialSet& MaterialSetOut, bool bPreferAssetMaterials) const override; 61 | virtual bool CommitMaterialSetUpdate(const FComponentMaterialSet& MaterialSet, bool bApplyToAsset) override; 62 | 63 | // IPersistentDynamicMeshSource 64 | virtual UDynamicMesh* GetDynamicMeshContainer() override; 65 | virtual void CommitDynamicMeshChange(TUniquePtr Change, const FText& ChangeMessage) override; 66 | virtual bool HasDynamicMeshComponent() const override; 67 | virtual UDynamicMeshComponent* GetDynamicMeshComponent() override; 68 | 69 | // IPhysicsDataSource 70 | virtual UBodySetup* GetBodySetup() const override; 71 | virtual IInterface_CollisionDataProvider* GetComplexCollisionProvider() const override; 72 | 73 | protected: 74 | // In many cases it is necessary to convert the DynamicMeshComponent's UDynamicMesh/FDynamicMesh3 to a FMeshDescription. 75 | // We cache this conversion in case it can be re-used. 76 | TUniquePtr CachedMeshDescription; 77 | bool bHaveCachedMeshDescription = false; 78 | void InvalidateCachedMeshDescription(); 79 | 80 | protected: 81 | friend class URuntimeDynamicMeshComponentToolTargetFactory; 82 | }; 83 | 84 | 85 | UCLASS(Transient) 86 | class RUNTIMETOOLSSYSTEM_API URuntimeDynamicMeshComponentToolTargetFactory : public UToolTargetFactory 87 | { 88 | GENERATED_BODY() 89 | public: 90 | virtual bool CanBuildTarget(UObject* SourceObject, const FToolTargetTypeRequirements& TargetTypeInfo) const override; 91 | virtual UToolTarget* BuildTarget(UObject* SourceObject, const FToolTargetTypeRequirements& TargetTypeInfo) override; 92 | }; -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJReader.cpp: -------------------------------------------------------------------------------- 1 | #include "DynamicMeshOBJReader.h" 2 | #include "DynamicMesh/DynamicMeshAttributeSet.h" 3 | #include "tinyobj/tiny_obj_loader.h" 4 | 5 | using namespace UE::Geometry; 6 | 7 | bool RTGUtils::ReadOBJMesh( 8 | const FString& Path, 9 | FDynamicMesh3& MeshOut, 10 | bool bNormals, 11 | bool bTexCoords, 12 | bool bVertexColors, 13 | bool bReverseOrientation) 14 | { 15 | std::string inputfile(TCHAR_TO_UTF8(*Path)); 16 | tinyobj::attrib_t attrib; 17 | std::vector shapes; 18 | std::vector materials; 19 | 20 | std::string warn; 21 | std::string err; 22 | 23 | bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str()); 24 | 25 | if (!warn.empty()) { 26 | UE_LOG(LogTemp, Display, TEXT("%hs"), warn.c_str()); 27 | } 28 | 29 | if (!err.empty()) { 30 | UE_LOG(LogTemp, Display, TEXT("%hs"), err.c_str()); 31 | } 32 | 33 | if (!ret) { 34 | return false; 35 | } 36 | 37 | // append vertices 38 | for (size_t vi = 0; vi < attrib.vertices.size() / 3; ++vi) 39 | { 40 | tinyobj::real_t vx = attrib.vertices[3 * vi + 0]; 41 | tinyobj::real_t vy = attrib.vertices[3 * vi + 1]; 42 | tinyobj::real_t vz = attrib.vertices[3 * vi + 2]; 43 | 44 | MeshOut.AppendVertex(FVector3d(vx, vy, vz)); 45 | } 46 | 47 | 48 | if (bVertexColors) 49 | { 50 | MeshOut.EnableVertexColors(FVector3f::Zero()); 51 | for (size_t vi = 0; vi < attrib.vertices.size() / 3; ++vi) 52 | { 53 | tinyobj::real_t r = attrib.colors[3 * vi + 0]; 54 | tinyobj::real_t g = attrib.colors[3 * vi + 1]; 55 | tinyobj::real_t b = attrib.colors[3 * vi + 2]; 56 | 57 | MeshOut.SetVertexColor(vi, FVector3f((float)r, (float)g, (float)b)); 58 | } 59 | } 60 | 61 | if (bNormals || bTexCoords) 62 | { 63 | MeshOut.EnableAttributes(); 64 | } 65 | FDynamicMeshNormalOverlay* Normals = (bNormals) ? MeshOut.Attributes()->PrimaryNormals() : nullptr; 66 | FDynamicMeshUVOverlay* UVs = (bTexCoords) ? MeshOut.Attributes()->PrimaryUV() : nullptr; 67 | if (Normals) 68 | { 69 | for (size_t ni = 0; ni < attrib.normals.size() / 3; ++ni) 70 | { 71 | tinyobj::real_t nx = attrib.normals[3 * ni + 0]; 72 | tinyobj::real_t ny = attrib.normals[3 * ni + 1]; 73 | tinyobj::real_t nz = attrib.normals[3 * ni + 2]; 74 | 75 | Normals->AppendElement(FVector3f((float)nx, (float)ny, (float)nz)); 76 | } 77 | } 78 | if (UVs) 79 | { 80 | for (size_t ti = 0; ti < attrib.texcoords.size() / 2; ++ti) 81 | { 82 | tinyobj::real_t tx = attrib.texcoords[2 * ti + 0]; 83 | tinyobj::real_t ty = attrib.texcoords[2 * ti + 1]; 84 | 85 | UVs->AppendElement(FVector2f((float)tx, (float)ty)); 86 | } 87 | } 88 | 89 | // append faces as triangles 90 | for (size_t s = 0; s < shapes.size(); s++) { // Loop over shapes 91 | 92 | size_t index_offset = 0; 93 | for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { // Loop over faces(polygon) 94 | int fv = shapes[s].mesh.num_face_vertices[f]; 95 | 96 | TArray Triangles; 97 | for (size_t v = 1; v < fv - 1; v++) 98 | { 99 | Triangles.Add(FIndex3i(0, v, v + 1)); 100 | } 101 | 102 | int32 NumTris = Triangles.Num(); 103 | for (int32 ti = 0; ti < NumTris; ++ti) 104 | { 105 | FIndex3i TriVerts = Triangles[ti]; 106 | tinyobj::index_t idx0 = shapes[s].mesh.indices[index_offset + TriVerts.A]; 107 | tinyobj::index_t idx1 = shapes[s].mesh.indices[index_offset + TriVerts.B]; 108 | tinyobj::index_t idx2 = shapes[s].mesh.indices[index_offset + TriVerts.C]; 109 | 110 | int32 tid = MeshOut.AppendTriangle(idx0.vertex_index, idx1.vertex_index, idx2.vertex_index); 111 | 112 | if (Normals && Normals->IsElement(idx0.normal_index) && Normals->IsElement(idx1.normal_index) && Normals->IsElement(idx2.normal_index)) 113 | { 114 | Normals->SetTriangle(tid, FIndex3i(idx0.normal_index, idx1.normal_index, idx2.normal_index)); 115 | } 116 | if (UVs && UVs->IsElement(idx0.texcoord_index) && UVs->IsElement(idx1.texcoord_index) && UVs->IsElement(idx2.texcoord_index)) 117 | { 118 | UVs->SetTriangle(tid, FIndex3i(idx0.texcoord_index, idx1.texcoord_index, idx2.texcoord_index)); 119 | } 120 | } 121 | 122 | index_offset += fv; 123 | 124 | // per-face material 125 | //shapes[s].mesh.material_ids[f]; 126 | } 127 | } 128 | 129 | if (bReverseOrientation) 130 | { 131 | MeshOut.ReverseOrientation(); 132 | } 133 | 134 | return true; 135 | } -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/MeshComponentRuntimeUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "MeshComponentRuntimeUtils.h" 2 | 3 | #include "DynamicMesh/DynamicMeshAttributeSet.h" 4 | #include "DynamicMesh/MeshNormals.h" 5 | 6 | #include "DynamicMeshToMeshDescription.h" 7 | #include "StaticMeshAttributes.h" 8 | 9 | 10 | using namespace UE::Geometry; 11 | 12 | 13 | 14 | void RTGUtils::UpdateStaticMeshFromDynamicMesh( 15 | UStaticMesh* StaticMesh, 16 | const FDynamicMesh3* Mesh) 17 | { 18 | FMeshDescription MeshDescription; 19 | FStaticMeshAttributes StaticMeshAttributes(MeshDescription); 20 | StaticMeshAttributes.Register(); 21 | 22 | FDynamicMeshToMeshDescription Converter; 23 | Converter.Convert(Mesh, MeshDescription); 24 | 25 | // todo: vertex color support 26 | 27 | //UStaticMesh* StaticMesh = NewObject(Component); 28 | //FName MaterialSlotName = StaticMesh->AddMaterial(MyMaterial); 29 | 30 | // Build the static mesh render data, one FMeshDescription* per LOD. 31 | TArray MeshDescriptionPtrs; 32 | MeshDescriptionPtrs.Emplace(&MeshDescription); 33 | StaticMesh->BuildFromMeshDescriptions(MeshDescriptionPtrs); 34 | } 35 | 36 | 37 | 38 | 39 | 40 | void RTGUtils::UpdatePMCFromDynamicMesh_SplitTriangles( 41 | UProceduralMeshComponent* Component, 42 | const FDynamicMesh3* Mesh, 43 | bool bUseFaceNormals, 44 | bool bInitializeUV0, 45 | bool bInitializePerVertexColors, 46 | bool bCreateCollision) 47 | { 48 | Component->ClearAllMeshSections(); 49 | 50 | int32 NumTriangles = Mesh->TriangleCount(); 51 | int32 NumVertices = NumTriangles * 3; 52 | 53 | TArray Vertices, Normals; 54 | Vertices.SetNumUninitialized(NumVertices); 55 | Normals.SetNumUninitialized(NumVertices); 56 | 57 | FMeshNormals PerVertexNormals(Mesh); 58 | bool bUsePerVertexNormals = false; 59 | const FDynamicMeshNormalOverlay* NormalOverlay = nullptr; 60 | if (Mesh->HasAttributes() == false && bUseFaceNormals == false) 61 | { 62 | PerVertexNormals.ComputeVertexNormals(); 63 | bUsePerVertexNormals = true; 64 | } 65 | else if (Mesh->HasAttributes()) 66 | { 67 | NormalOverlay = Mesh->Attributes()->PrimaryNormals(); 68 | } 69 | 70 | const FDynamicMeshUVOverlay* UVOverlay = (Mesh->HasAttributes()) ? Mesh->Attributes()->PrimaryUV() : nullptr; 71 | TArray UV0; 72 | if (UVOverlay && bInitializeUV0) 73 | { 74 | UV0.SetNum(NumVertices); 75 | } 76 | 77 | TArray VtxColors; 78 | bool bUsePerVertexColors = false; 79 | if (bInitializePerVertexColors && Mesh->HasVertexColors()) 80 | { 81 | VtxColors.SetNum(NumVertices); 82 | bUsePerVertexColors = true; 83 | } 84 | 85 | TArray Tangents; // not supporting this for now 86 | 87 | TArray Triangles; 88 | Triangles.SetNumUninitialized(NumTriangles*3); 89 | 90 | FVector3d Position[3]; 91 | FVector3f Normal[3]; 92 | FVector2f UV[3]; 93 | int32 BufferIndex = 0; 94 | for (int32 tid : Mesh->TriangleIndicesItr()) 95 | { 96 | int32 k = 3 * (BufferIndex++); 97 | 98 | FIndex3i TriVerts = Mesh->GetTriangle(tid); 99 | 100 | Mesh->GetTriVertices(tid, Position[0], Position[1], Position[2]); 101 | Vertices[k] = (FVector)Position[0]; 102 | Vertices[k+1] = (FVector)Position[1]; 103 | Vertices[k+2] = (FVector)Position[2]; 104 | 105 | 106 | if (bUsePerVertexNormals) 107 | { 108 | Normals[k] = (FVector)PerVertexNormals[TriVerts.A]; 109 | Normals[k+1] = (FVector)PerVertexNormals[TriVerts.B]; 110 | Normals[k+2] = (FVector)PerVertexNormals[TriVerts.C]; 111 | } 112 | else if (NormalOverlay != nullptr && bUseFaceNormals == false) 113 | { 114 | NormalOverlay->GetTriElements(tid, Normal[0], Normal[1], Normal[2]); 115 | Normals[k] = (FVector)Normal[0]; 116 | Normals[k+1] = (FVector)Normal[1]; 117 | Normals[k+2] = (FVector)Normal[2]; 118 | } 119 | else 120 | { 121 | FVector3d TriNormal = Mesh->GetTriNormal(tid); 122 | Normals[k] = (FVector)TriNormal; 123 | Normals[k+1] = (FVector)TriNormal; 124 | Normals[k+2] = (FVector)TriNormal; 125 | } 126 | 127 | if (UVOverlay != nullptr && UVOverlay->IsSetTriangle(tid)) 128 | { 129 | UVOverlay->GetTriElements(tid, UV[0], UV[1], UV[2]); 130 | UV0[k] = (FVector2D)UV[0]; 131 | UV0[k+1] = (FVector2D)UV[1]; 132 | UV0[k+2] = (FVector2D)UV[2]; 133 | } 134 | 135 | if (bUsePerVertexColors) 136 | { 137 | VtxColors[k] = (FLinearColor)Mesh->GetVertexColor(TriVerts.A); 138 | VtxColors[k+1] = (FLinearColor)Mesh->GetVertexColor(TriVerts.B); 139 | VtxColors[k+2] = (FLinearColor)Mesh->GetVertexColor(TriVerts.C); 140 | } 141 | 142 | Triangles[k] = k; 143 | Triangles[k+1] = k+1; 144 | Triangles[k+2] = k+2; 145 | } 146 | 147 | Component->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UV0, VtxColors, Tangents, bCreateCollision); 148 | } 149 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/MeshScene/RuntimeMeshSceneSubsystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Subsystems/GameInstanceSubsystem.h" 5 | #include "InteractiveToolsContext.h" 6 | #include "RuntimeMeshSceneObject.h" 7 | #include "RuntimeMeshSceneSubsystem.generated.h" 8 | 9 | class FMeshSceneSelectionChange; 10 | class FAddRemoveSceneObjectChange; 11 | 12 | /** 13 | * URuntimeMeshSceneSubsystem manages a "Scene" of "SceneObjects", currently only URuntimeMeshSceneObject (SO). 14 | * 15 | * Use CreateNewSceneObject() to create a new SO, and the various Delete functions to remove them. 16 | * These changes will be undo-able, ie they will send Change events to the USceneHistoryManager instance. 17 | * 18 | * An active Selection Set is tracked, and there are API functions for modifying this Selection set, also undo-able. 19 | * 20 | * Cast rays into the scene using FindNearestHitObject() 21 | */ 22 | UCLASS() 23 | class RUNTIMETOOLSSYSTEM_API URuntimeMeshSceneSubsystem : public UGameInstanceSubsystem 24 | { 25 | GENERATED_BODY() 26 | 27 | public: 28 | static void InitializeSingleton(URuntimeMeshSceneSubsystem* Subsystem); 29 | static URuntimeMeshSceneSubsystem* Get(); 30 | protected: 31 | static URuntimeMeshSceneSubsystem* InstanceSingleton; 32 | 33 | public: 34 | virtual void Deinitialize() override; 35 | 36 | 37 | virtual void SetCurrentTransactionsAPI(IToolsContextTransactionsAPI* TransactionsAPI); 38 | 39 | 40 | public: 41 | UPROPERTY() 42 | UMaterialInterface* StandardMaterial; 43 | 44 | UPROPERTY() 45 | UMaterialInterface* SelectedMaterial; 46 | 47 | UPROPERTY() 48 | UMaterialInterface* WireframeMaterial; 49 | 50 | public: 51 | 52 | UFUNCTION(BlueprintCallable) 53 | URuntimeMeshSceneObject* CreateNewSceneObject(); 54 | 55 | UFUNCTION(BlueprintCallable) 56 | URuntimeMeshSceneObject* FindSceneObjectByActor(AActor* Actor); 57 | 58 | UFUNCTION(BlueprintCallable) 59 | bool DeleteSceneObject(URuntimeMeshSceneObject* Object); 60 | 61 | UFUNCTION(BlueprintCallable) 62 | bool DeleteSelectedSceneObjects(); 63 | 64 | bool DeleteSelectedSceneObjects(AActor* SkipActor); 65 | 66 | 67 | public: 68 | 69 | UFUNCTION(BlueprintCallable, Category = "URuntimeMeshSceneSubsystem") 70 | TArray GetSelection() const { return SelectedSceneObjects; } 71 | 72 | UFUNCTION(BlueprintCallable, Category = "URuntimeMeshSceneSubsystem") 73 | void ClearSelection(); 74 | 75 | UFUNCTION(BlueprintCallable, Category = "URuntimeMeshSceneSubsystem") 76 | void SetSelected(URuntimeMeshSceneObject* SceneObject, bool bDeselect = false, bool bDeselectOthers = true); 77 | 78 | UFUNCTION(BlueprintCallable, Category = "URuntimeMeshSceneSubsystem") 79 | void ToggleSelected(URuntimeMeshSceneObject* SceneObject); 80 | 81 | UFUNCTION(BlueprintCallable, Category = "URuntimeMeshSceneSubsystem") 82 | void SetSelection(const TArray& SceneObjects); 83 | 84 | 85 | DECLARE_MULTICAST_DELEGATE_OneParam(FMeshSceneSelectionChangedEvent, URuntimeMeshSceneSubsystem*); 86 | FMeshSceneSelectionChangedEvent OnSelectionModified; 87 | 88 | 89 | 90 | 91 | public: 92 | 93 | 94 | UFUNCTION(BlueprintCallable, Category = "URuntimeMeshSceneSubsystem") 95 | URuntimeMeshSceneObject* FindNearestHitObject(FVector RayOrigin, FVector RayDirection, FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, float MaxDistance = 0); 96 | 97 | 98 | protected: 99 | 100 | IToolsContextTransactionsAPI* TransactionsAPI = nullptr; 101 | 102 | UPROPERTY() 103 | TArray SceneObjects; 104 | 105 | void AddSceneObjectInternal(URuntimeMeshSceneObject* Object, bool bIsUndoRedo); 106 | void RemoveSceneObjectInternal(URuntimeMeshSceneObject* Object, bool bIsUndoRedo); 107 | 108 | UPROPERTY() 109 | TArray SelectedSceneObjects; 110 | 111 | void SetSelectionInternal(const TArray& SceneObjects); 112 | 113 | TUniquePtr ActiveSelectionChange; 114 | void BeginSelectionChange(); 115 | void EndSelectionChange(); 116 | 117 | friend class FMeshSceneSelectionChange; 118 | friend class FAddRemoveSceneObjectChange; 119 | }; 120 | 121 | 122 | 123 | 124 | 125 | /** 126 | * FMeshSelectionChange represents an reversible change to a UMeshSelectionSet 127 | */ 128 | class RUNTIMETOOLSSYSTEM_API FMeshSceneSelectionChange : public FToolCommandChange 129 | { 130 | public: 131 | TArray OldSelection; 132 | TArray NewSelection; 133 | 134 | virtual void Apply(UObject* Object) override; 135 | virtual void Revert(UObject* Object) override; 136 | virtual FString ToString() const override { return TEXT("FMeshSceneSelectionChange"); } 137 | }; 138 | 139 | 140 | 141 | class RUNTIMETOOLSSYSTEM_API FAddRemoveSceneObjectChange : public FToolCommandChange 142 | { 143 | public: 144 | URuntimeMeshSceneObject* SceneObject; 145 | bool bAdded = true; 146 | 147 | public: 148 | virtual void Apply(UObject* Object) override; 149 | virtual void Revert(UObject* Object) override; 150 | virtual FString ToString() const override { return TEXT("FAddRemoveSceneObjectChange"); } 151 | }; 152 | 153 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshOBJWriter.cpp: -------------------------------------------------------------------------------- 1 | #include "DynamicMeshOBJWriter.h" 2 | #include "DynamicMesh/DynamicMeshAttributeSet.h" 3 | #include "DynamicMeshEditor.h" 4 | 5 | #include 6 | 7 | using namespace UE::Geometry; 8 | 9 | class FDynamicMeshOBJWriter 10 | { 11 | public: 12 | 13 | std::ofstream FileOut; 14 | 15 | TFunction OpenFile = [this](const FString& Path) { FileOut.open(*Path, std::ofstream::out | std::ofstream::trunc); return !!FileOut; }; 16 | TFunction CloseFile = [this]() { FileOut.close(); }; 17 | TFunction WriteLine = [this](const TCHAR* Line) { FileOut << TCHAR_TO_ANSI(Line) << std::endl; }; 18 | 19 | bool Write(const char* OutputPath, const FDynamicMesh3& Mesh) 20 | { 21 | if (!OpenFile(OutputPath)) 22 | { 23 | return false; 24 | } 25 | 26 | int32 NumVertices = Mesh.VertexCount(); 27 | for (int32 vi = 0; vi < NumVertices; ++vi) 28 | { 29 | FVector3d Pos = Mesh.GetVertex(vi); 30 | WriteLine(*FString::Printf(TEXT("v %f %f %f"), Pos.X, Pos.Y, Pos.Z)); 31 | } 32 | 33 | int32 NumUVs = 0; 34 | const FDynamicMeshUVOverlay* UVs = nullptr; 35 | if (Mesh.Attributes() && Mesh.Attributes()->PrimaryUV()) 36 | { 37 | UVs = Mesh.Attributes()->PrimaryUV(); 38 | NumUVs = UVs->ElementCount(); 39 | for (int32 ui = 0; ui < NumUVs; ++ui) 40 | { 41 | FVector2f UV = UVs->GetElement(ui); 42 | WriteLine(*FString::Printf(TEXT("vt %f %f"), UV.X, UV.Y)); 43 | } 44 | } 45 | 46 | int32 NumNormals = 0; 47 | const FDynamicMeshNormalOverlay* Normals = nullptr; 48 | if (Mesh.Attributes() && Mesh.Attributes()->PrimaryNormals()) 49 | { 50 | Normals = Mesh.Attributes()->PrimaryNormals(); 51 | NumNormals = Normals->ElementCount(); 52 | for (int32 ni = 0; ni < NumNormals; ++ni) 53 | { 54 | FVector3f Normal = Normals->GetElement(ni); 55 | WriteLine(*FString::Printf(TEXT("vn %f %f %f"), Normal.X, Normal.Y, Normal.Z)); 56 | } 57 | } 58 | 59 | struct FMeshTri 60 | { 61 | int32 Index; 62 | int32 Group; 63 | }; 64 | TSet AllGroupIDs; 65 | 66 | TArray Triangles; 67 | 68 | int32 NumTriangles = Mesh.TriangleCount(); 69 | for (int32 ti = 0; ti < NumTriangles; ++ti) 70 | { 71 | if (Mesh.IsTriangle(ti)) 72 | { 73 | int32 GroupID = Mesh.GetTriangleGroup(ti); 74 | AllGroupIDs.Add(GroupID); 75 | Triangles.Add({ ti, GroupID }); 76 | } 77 | } 78 | bool bHaveGroups = AllGroupIDs.Num() > 1; 79 | 80 | Triangles.StableSort([](const FMeshTri& Tri0, const FMeshTri& Tri1) 81 | { 82 | return Tri0.Group < Tri1.Group; 83 | }); 84 | 85 | 86 | int32 CurGroupID = -99999; 87 | for (FMeshTri MeshTri : Triangles) 88 | { 89 | if (bHaveGroups && MeshTri.Group != CurGroupID) 90 | { 91 | WriteLine(*FString::Printf(TEXT("g %d"), MeshTri.Group)); 92 | CurGroupID = MeshTri.Group; 93 | } 94 | 95 | int32 ti = MeshTri.Index; 96 | 97 | FIndex3i TriVertices = Mesh.GetTriangle(ti); 98 | FIndex3i TriUVs = (NumUVs > 0) ? UVs->GetTriangle(ti) : FIndex3i::Invalid(); 99 | FIndex3i TriNormals = (NumNormals > 0) ? Normals->GetTriangle(ti) : FIndex3i::Invalid(); 100 | 101 | bool bHaveUV = (NumUVs != 0) && UVs->IsSetTriangle(ti); 102 | bool bHaveNormal = (NumNormals != 0) && Normals->IsSetTriangle(ti); 103 | 104 | if (bHaveUV && bHaveNormal) 105 | { 106 | WriteLine(*FString::Printf(TEXT("f %d/%d/%d %d/%d/%d %d/%d/%d"), 107 | TriVertices.A + 1, TriUVs.A + 1, TriNormals.A + 1, 108 | TriVertices.B + 1, TriUVs.B + 1, TriNormals.B + 1, 109 | TriVertices.C + 1, TriUVs.C + 1, TriNormals.C + 1)); 110 | } 111 | else if (bHaveUV) 112 | { 113 | WriteLine(*FString::Printf(TEXT("f %d/%d %d/%d %d/%d"), 114 | TriVertices.A + 1, TriUVs.A + 1, TriVertices.B + 1, TriUVs.B + 1, TriVertices.C + 1, TriUVs.C + 1)); 115 | } 116 | else if (bHaveNormal) 117 | { 118 | WriteLine(*FString::Printf(TEXT("f %d//%d %d//%d %d//%d"), 119 | TriVertices.A + 1, TriNormals.A + 1, TriVertices.B + 1, TriNormals.B + 1, TriVertices.C + 1, TriNormals.C + 1)); 120 | } 121 | else 122 | { 123 | WriteLine(*FString::Printf(TEXT("f %d %d %d"), TriVertices.A + 1, TriVertices.B + 1, TriVertices.C + 1)); 124 | } 125 | } 126 | 127 | CloseFile(); 128 | 129 | return true; 130 | } 131 | }; 132 | 133 | 134 | 135 | bool RTGUtils::WriteOBJMesh( 136 | const FString& OutputPath, 137 | const FDynamicMesh3& Mesh, 138 | bool bReverseOrientation) 139 | { 140 | const FDynamicMesh3* WriteMesh = &Mesh; 141 | 142 | FDynamicMesh3 CopyMesh; 143 | if (bReverseOrientation) 144 | { 145 | CopyMesh = Mesh; 146 | CopyMesh.ReverseOrientation(); 147 | WriteMesh = &CopyMesh; 148 | } 149 | 150 | FDynamicMeshOBJWriter Writer; 151 | std::string OutputFilePath(TCHAR_TO_UTF8(*OutputPath)); 152 | return Writer.Write(OutputFilePath.c_str(), *WriteMesh); 153 | } 154 | 155 | 156 | 157 | bool RTGUtils::WriteOBJMeshes( 158 | const FString& OutputPath, 159 | const TArray& Meshes, 160 | bool bReverseOrientation) 161 | { 162 | FDynamicMesh3 CombinedMesh; 163 | FDynamicMeshEditor Editor(&CombinedMesh); 164 | 165 | for (const FDynamicMesh3& Mesh : Meshes) 166 | { 167 | if (Mesh.HasTriangleGroups()) 168 | { 169 | CombinedMesh.EnableTriangleGroups(); 170 | } 171 | if (Mesh.HasAttributes()) 172 | { 173 | CombinedMesh.EnableAttributes(); 174 | } 175 | 176 | FMeshIndexMappings Mappings; 177 | Editor.AppendMesh(&Mesh, Mappings); 178 | } 179 | 180 | if (bReverseOrientation) 181 | { 182 | CombinedMesh.ReverseOrientation(); 183 | } 184 | 185 | FDynamicMeshOBJWriter Writer; 186 | 187 | std::string OutputFilePath(TCHAR_TO_UTF8(*OutputPath)); 188 | return Writer.Write(OutputFilePath.c_str(), CombinedMesh); 189 | } 190 | 191 | 192 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/RuntimeToolsFrameworkSubsystem.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Subsystems/GameInstanceSubsystem.h" 7 | #include "InteractiveToolsContext.h" 8 | 9 | #include "ToolsContextRenderComponent.h" 10 | #include "MeshScene/SceneHistoryManager.h" 11 | #include "Interaction/SceneObjectSelectionInteraction.h" 12 | #include "Interaction/SceneObjectTransformInteraction.h" 13 | 14 | #include "RuntimeToolsFrameworkSubsystem.generated.h" 15 | 16 | 17 | class FRuntimeToolsContextQueriesImpl; 18 | class FRuntimeToolsContextTransactionImpl; 19 | class FRuntimeToolsContextAssetImpl; 20 | class AToolsContextActor; 21 | 22 | 23 | /** 24 | * 25 | */ 26 | UCLASS() 27 | class RUNTIMETOOLSSYSTEM_API URuntimeToolsFrameworkSubsystem : public UGameInstanceSubsystem 28 | { 29 | GENERATED_BODY() 30 | 31 | // 32 | // Small hack to workaround the fact that you generally need the UGameInstance pointer to 33 | // look up a GameInstance subsystem. We store the pointer and then allow ::Get() to return it (ie actually a Singleton) 34 | // 35 | public: 36 | static void InitializeSingleton(URuntimeToolsFrameworkSubsystem* Subsystem); 37 | static URuntimeToolsFrameworkSubsystem* Get(); 38 | protected: 39 | static URuntimeToolsFrameworkSubsystem* InstanceSingleton; 40 | 41 | 42 | // 43 | // UGameInstanceSubsystem API implementation 44 | // 45 | public: 46 | virtual void Deinitialize() override; 47 | 48 | 49 | // 50 | // Functions to setup/shutdown/operate the RuntimeToolsFramework 51 | // 52 | public: 53 | void InitializeToolsContext(UWorld* TargetWorld); 54 | void ShutdownToolsContext(); 55 | void SetContextActor(AToolsContextActor* ActorIn); 56 | virtual void Tick(float DeltaTime); 57 | 58 | 59 | // 60 | // Access to various data structures created/tracked by the Subsystem 61 | // 62 | 63 | IToolsContextTransactionsAPI* GetTransactionsAPI(); 64 | 65 | UFUNCTION(BlueprintCallable) 66 | USceneHistoryManager* GetSceneHistory() { return SceneHistory; } 67 | 68 | TArray GetActiveToolPropertySets(); 69 | 70 | 71 | // 72 | // Tool creation/management BP API 73 | // 74 | 75 | UFUNCTION(BlueprintCallable) 76 | bool CanActivateToolByName(FString Name); 77 | 78 | UFUNCTION(BlueprintCallable) 79 | UInteractiveTool* BeginToolByName(FString Name); 80 | 81 | UFUNCTION(BlueprintCallable) 82 | bool HaveActiveTool(); 83 | 84 | UFUNCTION(BlueprintCallable) 85 | UInteractiveTool* GetActiveTool(); 86 | 87 | UFUNCTION(BlueprintCallable) 88 | bool IsActiveToolAcceptCancelType(); 89 | 90 | UFUNCTION(BlueprintCallable) 91 | bool CanAcceptActiveTool(); 92 | 93 | UFUNCTION(BlueprintCallable) 94 | bool AcceptActiveTool(); 95 | 96 | UFUNCTION(BlueprintCallable) 97 | bool CancelOrCompleteActiveTool(); 98 | 99 | 100 | 101 | 102 | // 103 | // Support for tracking World/Local coordinate system global state 104 | // 105 | 106 | UPROPERTY() 107 | EToolContextCoordinateSystem CurrentCoordinateSystem = EToolContextCoordinateSystem::World; 108 | 109 | //UFUNCTION(BlueprintCallable) // unsupported because EToolContextCoordinateSystem is not a uint8 110 | EToolContextCoordinateSystem GetCurrentCoordinateSystem() const { return CurrentCoordinateSystem; } 111 | 112 | //UFUNCTION(BlueprintCallable) // unsupported because EToolContextCoordinateSystem is not a uint8 113 | void SetCurrentCoordinateSystem(EToolContextCoordinateSystem CoordSystem); 114 | 115 | UFUNCTION(BlueprintCallable) 116 | void CycleCurrentCoordinateSystem(); 117 | 118 | UFUNCTION(BlueprintCallable) 119 | bool IsWorldCoordinateSystem() { return CurrentCoordinateSystem == EToolContextCoordinateSystem::World; } 120 | 121 | 122 | 123 | // 124 | // random utility BP functions 125 | // 126 | 127 | UFUNCTION(BlueprintCallable) 128 | URuntimeMeshSceneObject* ImportMeshSceneObject(const FString Path, bool bFlipOrientation); 129 | 130 | 131 | 132 | // 133 | // mouse state queries/functions 134 | // 135 | 136 | public: 137 | UFUNCTION(BlueprintCallable) 138 | bool IsCapturingMouse() const; 139 | protected: 140 | void OnLeftMouseDown(); 141 | void OnLeftMouseUp(); 142 | 143 | 144 | public: 145 | UPROPERTY() 146 | UWorld* TargetWorld; 147 | 148 | UPROPERTY() 149 | UInteractiveToolsContext* ToolsContext; 150 | 151 | UPROPERTY() 152 | AToolsContextActor* ContextActor; 153 | 154 | UPROPERTY() 155 | AActor* PDIRenderActor; 156 | 157 | UPROPERTY() 158 | UToolsContextRenderComponent* PDIRenderComponent; 159 | 160 | UPROPERTY() 161 | USceneObjectSelectionInteraction* SelectionInteraction; 162 | 163 | UPROPERTY() 164 | USceneObjectTransformInteraction* TransformInteraction; 165 | 166 | UPROPERTY() 167 | USceneHistoryManager* SceneHistory; 168 | 169 | 170 | protected: 171 | TSharedPtr ContextQueriesAPI; 172 | TSharedPtr ContextTransactionsAPI; 173 | 174 | void InternalConsistencyChecks(); 175 | bool bIsShuttingDown = false; 176 | 177 | void OnToolStarted(UInteractiveToolManager* Manager, UInteractiveTool* Tool); 178 | void OnToolEnded(UInteractiveToolManager* Manager, UInteractiveTool* Tool); 179 | 180 | void OnSceneHistoryStateChange(); 181 | 182 | 183 | // mouse things 184 | 185 | friend class AToolsContextActor; 186 | 187 | FVector2D PrevMousePosition = FVector2D::ZeroVector; 188 | 189 | FInputDeviceState CurrentMouseState; 190 | bool bPendingMouseStateChange = false; 191 | 192 | FViewCameraState CurrentViewCameraState; 193 | 194 | 195 | // property set keepalive hack 196 | 197 | void AddPropertySetKeepalive(UInteractiveToolPropertySet* PropertySet); 198 | void AddAllPropertySetKeepalives(UInteractiveTool* Tool); 199 | 200 | UPROPERTY() 201 | TArray PropertySetKeepAlives; 202 | 203 | }; 204 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/MeshScene/RuntimeMeshSceneObject.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "RuntimeMeshSceneObject.h" 3 | 4 | #include "DynamicMesh/DynamicMesh3.h" 5 | #include "DynamicMesh/DynamicMeshAABBTree3.h" 6 | #include "MeshDescriptionToDynamicMesh.h" 7 | #include "DynamicMeshToMeshDescription.h" 8 | 9 | #include "MaterialDomain.h" 10 | #include "Materials/Material.h" 11 | 12 | using namespace UE::Geometry; 13 | 14 | URuntimeMeshSceneObject::URuntimeMeshSceneObject() 15 | { 16 | if (!SourceMesh) 17 | { 18 | SourceMesh = MakeUnique(); 19 | } 20 | if (!MeshAABBTree) 21 | { 22 | MeshAABBTree = MakeUnique(); 23 | } 24 | 25 | UMaterialInterface* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface); 26 | Materials.Add(DefaultMaterial); 27 | } 28 | 29 | 30 | void URuntimeMeshSceneObject::Initialize(UWorld* TargetWorld, const FMeshDescription* InitialMeshDescription) 31 | { 32 | FActorSpawnParameters SpawnInfo; 33 | SimpleDynamicMeshActor = TargetWorld->SpawnActor(FVector::ZeroVector, FRotator(0, 0, 0), SpawnInfo); 34 | 35 | // listen for changes 36 | SimpleDynamicMeshActor->MeshComponent->OnMeshChanged.AddLambda([this]() { 37 | OnExternalDynamicMeshComponentUpdate(); 38 | }); 39 | 40 | GetActor()->SourceType = EDynamicMeshActorSourceType::ExternallyGenerated; 41 | GetActor()->CollisionMode = EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync; 42 | 43 | UpdateSourceMesh(InitialMeshDescription); 44 | 45 | GetActor()->EditMesh([&](FDynamicMesh3& MeshToEdit) 46 | { 47 | MeshToEdit = *SourceMesh; 48 | }); 49 | 50 | UpdateComponentMaterials(false); 51 | } 52 | 53 | void URuntimeMeshSceneObject::Initialize(UWorld* TargetWorld, const FDynamicMesh3* InitialMesh) 54 | { 55 | FActorSpawnParameters SpawnInfo; 56 | SimpleDynamicMeshActor = TargetWorld->SpawnActor(FVector::ZeroVector, FRotator(0, 0, 0), SpawnInfo); 57 | 58 | // listen for changes 59 | SimpleDynamicMeshActor->MeshComponent->OnMeshChanged.AddLambda([this]() { 60 | OnExternalDynamicMeshComponentUpdate(); 61 | }); 62 | 63 | GetActor()->SourceType = EDynamicMeshActorSourceType::ExternallyGenerated; 64 | GetActor()->CollisionMode = EDynamicMeshActorCollisionMode::ComplexAsSimpleAsync; 65 | 66 | *SourceMesh = *InitialMesh; 67 | MeshAABBTree->SetMesh(SourceMesh.Get(), true); 68 | 69 | GetActor()->EditMesh([&](FDynamicMesh3& MeshToEdit) 70 | { 71 | MeshToEdit = *SourceMesh; 72 | }); 73 | 74 | UpdateComponentMaterials(false); 75 | } 76 | 77 | 78 | void URuntimeMeshSceneObject::OnExternalDynamicMeshComponentUpdate() 79 | { 80 | const FDynamicMesh3* Mesh = SimpleDynamicMeshActor->MeshComponent->GetMesh(); 81 | *SourceMesh = *Mesh; 82 | MeshAABBTree->SetMesh(SourceMesh.Get(), true); 83 | } 84 | 85 | 86 | void URuntimeMeshSceneObject::SetTransform(FTransform Transform) 87 | { 88 | GetActor()->SetActorTransform(Transform); 89 | } 90 | 91 | 92 | ADynamicMeshBaseActor* URuntimeMeshSceneObject::GetActor() 93 | { 94 | return SimpleDynamicMeshActor; 95 | } 96 | 97 | UMeshComponent* URuntimeMeshSceneObject::GetMeshComponent() 98 | { 99 | return (SimpleDynamicMeshActor) ? SimpleDynamicMeshActor->MeshComponent : nullptr; 100 | } 101 | 102 | 103 | 104 | void URuntimeMeshSceneObject::CopyMaterialsFromComponent() 105 | { 106 | UMeshComponent* Component = GetMeshComponent(); 107 | int32 NumMaterials = Component->GetNumMaterials(); 108 | if (NumMaterials == 0) 109 | { 110 | Materials = { UMaterial::GetDefaultMaterial(MD_Surface) }; 111 | } 112 | else 113 | { 114 | Materials.SetNum(NumMaterials); 115 | for (int32 k = 0; k < NumMaterials; ++k) 116 | { 117 | Materials[k] = Component->GetMaterial(k); 118 | } 119 | } 120 | } 121 | 122 | 123 | void URuntimeMeshSceneObject::SetAllMaterials(UMaterialInterface* SetToMaterial) 124 | { 125 | int32 NumMaterials = Materials.Num(); 126 | for (int32 k = 0; k < NumMaterials; ++k) 127 | { 128 | Materials[k] = SetToMaterial; 129 | } 130 | UpdateComponentMaterials(true); 131 | } 132 | 133 | 134 | void URuntimeMeshSceneObject::SetToHighlightMaterial(UMaterialInterface* Material) 135 | { 136 | UMeshComponent* Component = GetMeshComponent(); 137 | int32 NumMaterials = FMath::Max(1, Component->GetNumMaterials()); 138 | for (int32 k = 0; k < NumMaterials; ++k) 139 | { 140 | Component->SetMaterial(k, Material); 141 | } 142 | 143 | // HACK TO FORCE MATERIAL UPDATE IN SDMC 144 | SimpleDynamicMeshActor->MeshComponent->NotifyMeshUpdated(); 145 | } 146 | 147 | void URuntimeMeshSceneObject::ClearHighlightMaterial() 148 | { 149 | UpdateComponentMaterials(true); 150 | } 151 | 152 | 153 | void URuntimeMeshSceneObject::UpdateComponentMaterials(bool bForceRefresh) 154 | { 155 | UMaterialInterface* DefaultMaterial = UMaterial::GetDefaultMaterial(MD_Surface); 156 | 157 | UMeshComponent* Component = GetMeshComponent(); 158 | if (!Component) return; 159 | 160 | int32 NumMaterials = FMath::Max(1, Component->GetNumMaterials()); 161 | for (int32 k = 0; k < NumMaterials; ++k) 162 | { 163 | UMaterialInterface* SetMaterial = (k < Materials.Num()) ? Materials[k] : DefaultMaterial; 164 | Component->SetMaterial(k, SetMaterial); 165 | } 166 | 167 | // HACK TO FORCE MATERIAL UPDATE IN SDMC 168 | if (bForceRefresh) 169 | { 170 | SimpleDynamicMeshActor->MeshComponent->NotifyMeshUpdated(); 171 | } 172 | } 173 | 174 | 175 | 176 | void URuntimeMeshSceneObject::UpdateSourceMesh(const FMeshDescription* MeshDescriptionIn) 177 | { 178 | FMeshDescriptionToDynamicMesh Converter; 179 | FDynamicMesh3 TmpMesh; 180 | Converter.Convert(MeshDescriptionIn, TmpMesh); 181 | *SourceMesh = MoveTemp(TmpMesh); 182 | 183 | MeshAABBTree->SetMesh(SourceMesh.Get(), true); 184 | } 185 | 186 | 187 | 188 | bool URuntimeMeshSceneObject::IntersectRay(FVector RayOrigin, FVector RayDirection, FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, float MaxDistance) 189 | { 190 | if (!ensure(SourceMesh)) return false; 191 | if (!GetActor()) return false; // this can happen if the Actor gets deleted but the SO does not. Bad situation but avoids crash... 192 | 193 | FTransformSRT3d ActorToWorld(GetActor()->GetActorTransform()); 194 | FVector3d WorldDirection(RayDirection); WorldDirection.Normalize(); 195 | FRay3d LocalRay(ActorToWorld.InverseTransformPosition((FVector3d)RayOrigin), 196 | ActorToWorld.InverseTransformNormal(WorldDirection)); 197 | IMeshSpatial::FQueryOptions QueryOptions; 198 | if (MaxDistance > 0) 199 | { 200 | QueryOptions.MaxDistance = MaxDistance; 201 | } 202 | NearestTriangle = MeshAABBTree->FindNearestHitTriangle(LocalRay, QueryOptions); 203 | if (SourceMesh->IsTriangle(NearestTriangle)) 204 | { 205 | FIntrRay3Triangle3d IntrQuery = TMeshQueries::TriangleIntersection(*SourceMesh, NearestTriangle, LocalRay); 206 | if (IntrQuery.IntersectionType == EIntersectionType::Point) 207 | { 208 | HitDistance = IntrQuery.RayParameter; 209 | WorldHitPoint = (FVector)ActorToWorld.TransformPosition(LocalRay.PointAt(IntrQuery.RayParameter)); 210 | TriBaryCoords = (FVector)IntrQuery.TriangleBaryCoords; 211 | return true; 212 | } 213 | } 214 | return false; 215 | } 216 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/ToolsContextRenderComponent.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "ToolsContextRenderComponent.h" 5 | #include "PrimitiveSceneProxy.h" 6 | #include "SceneManagement.h" 7 | 8 | 9 | // SceneProxy for UToolsContextRenderComponent. Just uses the PDI's available in GetDynamicMeshElements 10 | // to draw the lines/points accumulated by the Component. 11 | class FToolsContextRenderComponentSceneProxy final : public FPrimitiveSceneProxy 12 | { 13 | public: 14 | SIZE_T GetTypeHash() const override 15 | { 16 | static size_t UniquePointer; 17 | return reinterpret_cast(&UniquePointer); 18 | } 19 | 20 | FToolsContextRenderComponentSceneProxy( 21 | const UToolsContextRenderComponent* InComponent, 22 | TUniqueFunction&, TArray&)>&& GeometryQueryFunc) 23 | : FPrimitiveSceneProxy(InComponent) 24 | { 25 | GetGeometryQueryFunc = MoveTemp(GeometryQueryFunc); 26 | } 27 | 28 | virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override 29 | { 30 | TArray Lines; 31 | TArray Points; 32 | GetGeometryQueryFunc(Lines, Points); 33 | 34 | for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) 35 | { 36 | if (VisibilityMap & (1 << ViewIndex)) 37 | { 38 | const FSceneView* View = Views[ViewIndex]; 39 | FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex); 40 | 41 | int32 NumLines = Lines.Num(), NumPoints = Points.Num(); 42 | for (int32 k = 0; k < NumLines; ++k) 43 | { 44 | PDI->DrawLine(Lines[k].Start, Lines[k].End, Lines[k].Color, Lines[k].DepthPriorityGroup, Lines[k].Thickness, Lines[k].DepthBias, Lines[k].bScreenSpace); 45 | } 46 | for (int32 k = 0; k < NumPoints; ++k) 47 | { 48 | PDI->DrawPoint(Points[k].Position, Points[k].Color, Points[k].PointSize, Points[k].DepthPriorityGroup); 49 | } 50 | } 51 | } 52 | } 53 | 54 | virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override 55 | { 56 | FPrimitiveViewRelevance Result; 57 | Result.bDrawRelevance = IsShown(View); 58 | Result.bDynamicRelevance = true; 59 | Result.bShadowRelevance = false; 60 | Result.bEditorPrimitiveRelevance = UseEditorCompositing(View); 61 | Result.bRenderCustomDepth = ShouldRenderCustomDepth(); 62 | return Result; 63 | } 64 | 65 | //virtual bool CanBeOccluded() const override 66 | //{ 67 | // return false; 68 | //} 69 | 70 | virtual uint32 GetMemoryFootprint(void) const override { return sizeof * this + GetAllocatedSize(); } 71 | uint32 GetAllocatedSize(void) const { return FPrimitiveSceneProxy::GetAllocatedSize(); } 72 | 73 | 74 | // set to lambda that steals current lines/points from the Component 75 | TUniqueFunction&, TArray&)> GetGeometryQueryFunc; 76 | }; 77 | 78 | 79 | 80 | // implementation of FPrimitiveDrawInterface that forwards DrawLine/DrawPoint calls to 81 | // a UToolsContextRenderComponent instance. No other PDI functionality is implemented. 82 | // Instances of this class are created by GetPDIForView() below, which is called once per frame 83 | // by the URuntimeToolsFrameworkSubsystem, in the FRuntimeToolsFrameworkRenderAPI implementation. 84 | class FToolsContextRenderComponentPDI : public FPrimitiveDrawInterface 85 | { 86 | public: 87 | UToolsContextRenderComponent* RenderComponent; 88 | 89 | FToolsContextRenderComponentPDI(const FSceneView* InView, UToolsContextRenderComponent* RenderComponentIn) : FPrimitiveDrawInterface(InView) 90 | { 91 | RenderComponent = RenderComponentIn; 92 | } 93 | 94 | virtual bool IsHitTesting() { return false; } 95 | virtual void SetHitProxy(HHitProxy* HitProxy) { }; 96 | virtual void RegisterDynamicResource(FDynamicPrimitiveResource* DynamicResource) { ensure(false); } 97 | virtual void AddReserveLines(uint8 DepthPriorityGroup, int32 NumLines, bool bDepthBiased = false, bool bThickLines = false) { ensure(false); } 98 | virtual int32 DrawMesh(const FMeshBatch& Mesh) { ensure(false); return 0; } 99 | virtual void DrawSprite( 100 | const FVector& Position, float SizeX, float SizeY, 101 | const FTexture* Sprite, const FLinearColor& Color, uint8 DepthPriorityGroup, 102 | float U, float UL, float V, float VL, uint8 BlendMode = 1 /*SE_BLEND_Masked*/, float OpacityMaskRefVal = 0.5f) { ensure(false); } 103 | 104 | virtual void DrawLine( const FVector& Start, const FVector& End, const FLinearColor& Color, 105 | uint8 DepthPriorityGroup, float Thickness = 0.0f, float DepthBias = 0.0f, bool bScreenSpace = false ) 106 | { 107 | RenderComponent->DrawLine(Start, End, Color, DepthPriorityGroup, Thickness, DepthBias, bScreenSpace); 108 | } 109 | 110 | virtual void DrawTranslucentLine(const FVector& Start, const FVector& End, const FLinearColor& Color, 111 | uint8 DepthPriorityGroup, float Thickness = 0.0f, float DepthBias = 0.0f, bool bScreenSpace = false ) override 112 | { 113 | RenderComponent->DrawLine(Start, End, Color, DepthPriorityGroup, Thickness, DepthBias, bScreenSpace); 114 | } 115 | 116 | virtual void DrawPoint( const FVector& Position, const FLinearColor& Color, float PointSize, uint8 DepthPriorityGroup ) 117 | { 118 | RenderComponent->DrawPoint(Position, Color, PointSize, DepthPriorityGroup); 119 | } 120 | 121 | }; 122 | 123 | 124 | TSharedPtr UToolsContextRenderComponent::GetPDIForView(const FSceneView* InView) 125 | { 126 | return MakeShared(InView, this); 127 | } 128 | 129 | 130 | 131 | void UToolsContextRenderComponent::DrawLine( 132 | const FVector& Start, 133 | const FVector& End, 134 | const FLinearColor& Color, 135 | uint8 DepthPriorityGroupIn, 136 | float Thickness, 137 | float DepthBias, 138 | bool bScreenSpace 139 | ) 140 | { 141 | GeometryLock.Lock(); 142 | CurrentLines.Add( 143 | FPDILine{ Start, End, Color, DepthPriorityGroupIn, Thickness, DepthBias, bScreenSpace }); 144 | GeometryLock.Unlock(); 145 | } 146 | 147 | void UToolsContextRenderComponent::DrawPoint( 148 | const FVector& Position, 149 | const FLinearColor& Color, 150 | float PointSize, 151 | uint8 DepthPriorityGroupIn 152 | ) 153 | { 154 | GeometryLock.Lock(); 155 | CurrentPoints.Add( 156 | FPDIPoint{ Position, Color, PointSize, DepthPriorityGroupIn }); 157 | GeometryLock.Unlock(); 158 | } 159 | 160 | 161 | TUniqueFunction&, TArray&)> UToolsContextRenderComponent::MakeGetCurrentGeometryQueryFunc() 162 | { 163 | return [this](TArray& LineStorage, TArray& PointStorage) 164 | { 165 | GeometryLock.Lock(); 166 | LineStorage = MoveTemp(CurrentLines); 167 | PointStorage = MoveTemp(CurrentPoints); 168 | GeometryLock.Unlock(); 169 | }; 170 | } 171 | 172 | FPrimitiveSceneProxy* UToolsContextRenderComponent::CreateSceneProxy() 173 | { 174 | return new FToolsContextRenderComponentSceneProxy(this, MakeGetCurrentGeometryQueryFunc()); 175 | } 176 | 177 | bool UToolsContextRenderComponent::LineTraceComponent(FHitResult& OutHit, const FVector Start, const FVector End, const FCollisionQueryParams& Params) 178 | { 179 | // hit testing not supported 180 | return false; 181 | } 182 | 183 | 184 | FBoxSphereBounds UToolsContextRenderComponent::CalcBounds(const FTransform& LocalToWorld) const 185 | { 186 | // infinite bounds. TODO: accumulate actual bounds? will result in unstable depth sorting... 187 | float f = 9999999.0f; 188 | return FBoxSphereBounds(FBox(-FVector(f, f, f), FVector(f, f, f))); 189 | } -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/RuntimeDynamicMeshComponentToolTarget.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeToolsFramework/RuntimeDynamicMeshComponentToolTarget.h" 2 | 3 | #include "Components/DynamicMeshComponent.h" 4 | #include "DynamicMeshToMeshDescription.h" 5 | #include "MeshDescriptionToDynamicMesh.h" 6 | #include "StaticMeshAttributes.h" 7 | #include "Materials/Material.h" 8 | #include "ModelingToolTargetUtil.h" 9 | #include "RuntimeToolsFrameworkSubsystem.h" // to emit change transaction 10 | 11 | 12 | #define LOCTEXT_NAMESPACE "URuntimeDynamicMeshComponentToolTarget" 13 | 14 | bool URuntimeDynamicMeshComponentToolTarget::IsValid() const 15 | { 16 | if (!UPrimitiveComponentToolTarget::IsValid()) 17 | { 18 | return false; 19 | } 20 | if (UDynamicMeshComponent* DynamicMeshComponent = Cast(Component)) 21 | { 22 | if (UDynamicMesh* DynamicMesh = DynamicMeshComponent->GetDynamicMesh()) 23 | { 24 | return true; 25 | } 26 | } 27 | return false; 28 | } 29 | 30 | 31 | int32 URuntimeDynamicMeshComponentToolTarget::GetNumMaterials() const 32 | { 33 | check(IsValid()); 34 | return Component->GetNumMaterials(); 35 | } 36 | 37 | UMaterialInterface* URuntimeDynamicMeshComponentToolTarget::GetMaterial(int32 MaterialIndex) const 38 | { 39 | check(IsValid()); 40 | return Component->GetMaterial(MaterialIndex); 41 | } 42 | 43 | void URuntimeDynamicMeshComponentToolTarget::GetMaterialSet(FComponentMaterialSet& MaterialSetOut, bool bPreferAssetMaterials) const 44 | { 45 | check(IsValid()); 46 | int32 NumMaterials = Component->GetNumMaterials(); 47 | MaterialSetOut.Materials.SetNum(NumMaterials); 48 | for (int32 k = 0; k < NumMaterials; ++k) 49 | { 50 | MaterialSetOut.Materials[k] = Component->GetMaterial(k); 51 | } 52 | } 53 | 54 | bool URuntimeDynamicMeshComponentToolTarget::CommitMaterialSetUpdate(const FComponentMaterialSet& MaterialSet, bool bApplyToAsset) 55 | { 56 | check(IsValid()); 57 | 58 | UDynamicMeshComponent* DynamicMeshComponent = Cast(Component); 59 | 60 | // todo: is it necessary to filter the material set like UDynamicMeshComponentToolTarget does? 61 | 62 | int32 NumMaterialsNeeded = Component->GetNumMaterials(); 63 | int32 NumMaterialsGiven = MaterialSet.Materials.Num(); 64 | for (int32 k = 0; k < NumMaterialsGiven; ++k) 65 | { 66 | DynamicMeshComponent->SetMaterial(k, MaterialSet.Materials[k]); 67 | } 68 | 69 | return true; 70 | } 71 | 72 | 73 | const FMeshDescription* URuntimeDynamicMeshComponentToolTarget::GetMeshDescription(const FGetMeshParameters& GetMeshParams) 74 | { 75 | check(IsValid()); 76 | 77 | if (bHaveCachedMeshDescription) 78 | { 79 | return CachedMeshDescription.Get(); 80 | } 81 | 82 | UDynamicMeshComponent* DynamicMeshComponent = Cast(Component); 83 | 84 | CachedMeshDescription = MakeUnique(GetEmptyMeshDescription()); 85 | 86 | FDynamicMeshToMeshDescription Converter; 87 | Converter.Convert(DynamicMeshComponent->GetMesh(), *CachedMeshDescription, true); 88 | 89 | bHaveCachedMeshDescription = true; 90 | return CachedMeshDescription.Get(); 91 | } 92 | 93 | FMeshDescription URuntimeDynamicMeshComponentToolTarget::GetEmptyMeshDescription() 94 | { 95 | // FStaticMeshAttributes are the attribute set required by a UStaticMesh, and USkeletalMesh supports 96 | // all the same attributes. Lots of code assumes that these attributes are available, to the point 97 | // where a FMeshDescription is basically not usable without them 98 | FMeshDescription MeshDescription; 99 | FStaticMeshAttributes Attributes(MeshDescription); 100 | Attributes.Register(); 101 | return MeshDescription; 102 | } 103 | 104 | void URuntimeDynamicMeshComponentToolTarget::InvalidateCachedMeshDescription() 105 | { 106 | if (bHaveCachedMeshDescription) 107 | { 108 | CachedMeshDescription = nullptr; 109 | bHaveCachedMeshDescription = false; 110 | } 111 | } 112 | 113 | 114 | void URuntimeDynamicMeshComponentToolTarget::CommitMeshDescription(const FCommitter& Committer, const FCommitMeshParameters& CommitMeshParams) 115 | { 116 | if (ensure(IsValid()) == false) return; 117 | 118 | // we are going to replace FDynamicMesh3 inside the UDynamicMesh, we will pass to a FMeshChange so we can just steal it here 119 | UDynamicMesh* DynamicMesh = GetDynamicMeshContainer(); 120 | TSharedPtr CurrentMesh( DynamicMesh->ExtractMesh().Release() ); 121 | 122 | // run the Committer function to store to a temporary MeshDescription 123 | FMeshDescription TempMeshDescription(*GetMeshDescription()); 124 | FCommitterParams CommitterParams; 125 | CommitterParams.MeshDescriptionOut = &TempMeshDescription; 126 | Committer(CommitterParams); 127 | 128 | // convert to FDynamicMesh3 129 | FMeshDescriptionToDynamicMesh Converter; 130 | TSharedPtr NewMesh = MakeShared(); 131 | NewMesh->EnableAttributes(); 132 | Converter.Convert(CommitterParams.MeshDescriptionOut, *NewMesh, true); 133 | 134 | // update the UDynamicMesh 135 | DynamicMesh->EditMesh([&](FDynamicMesh3& EditMesh) { EditMesh = *NewMesh; }); 136 | 137 | // emit the change 138 | CommitDynamicMeshChange(MakeUnique(CurrentMesh, NewMesh), 139 | LOCTEXT("RuntimeDynamicMeshChange", "MeshChange") ); 140 | } 141 | 142 | 143 | UDynamicMesh* URuntimeDynamicMeshComponentToolTarget::GetDynamicMeshContainer() 144 | { 145 | return Cast(Component)->GetDynamicMesh(); 146 | } 147 | 148 | bool URuntimeDynamicMeshComponentToolTarget::HasDynamicMeshComponent() const 149 | { 150 | return true; 151 | } 152 | 153 | UDynamicMeshComponent* URuntimeDynamicMeshComponentToolTarget::GetDynamicMeshComponent() 154 | { 155 | return Cast(Component); 156 | } 157 | 158 | 159 | void URuntimeDynamicMeshComponentToolTarget::CommitDynamicMeshChange(TUniquePtr Change, const FText& ChangeMessage) 160 | { 161 | URuntimeToolsFrameworkSubsystem::Get()->GetTransactionsAPI()->AppendChange(GetDynamicMeshComponent(), MoveTemp(Change), 162 | LOCTEXT("UpdateMeshChange", "Update Mesh")); 163 | 164 | InvalidateCachedMeshDescription(); 165 | } 166 | 167 | FDynamicMesh3 URuntimeDynamicMeshComponentToolTarget::GetDynamicMesh() 168 | { 169 | UDynamicMesh* DynamicMesh = GetDynamicMeshContainer(); 170 | FDynamicMesh3 Mesh; 171 | DynamicMesh->ProcessMesh([&](const FDynamicMesh3& ReadMesh) { Mesh = ReadMesh; }); 172 | return Mesh; 173 | } 174 | 175 | void URuntimeDynamicMeshComponentToolTarget::CommitDynamicMesh(const FDynamicMesh3& UpdatedMesh, const FDynamicMeshCommitInfo& CommitInfo) 176 | { 177 | UE::ToolTarget::Internal::CommitDynamicMeshViaIPersistentDynamicMeshSource( 178 | *this, UpdatedMesh, CommitInfo.bTopologyChanged); 179 | } 180 | 181 | UBodySetup* URuntimeDynamicMeshComponentToolTarget::GetBodySetup() const 182 | { 183 | check(IsValid()); 184 | return Cast(Component)->GetBodySetup(); 185 | } 186 | 187 | 188 | IInterface_CollisionDataProvider* URuntimeDynamicMeshComponentToolTarget::GetComplexCollisionProvider() const 189 | { 190 | check(IsValid()); 191 | return Cast(Component); 192 | } 193 | 194 | 195 | bool URuntimeDynamicMeshComponentToolTargetFactory::CanBuildTarget(UObject* SourceObject, const FToolTargetTypeRequirements& Requirements) const 196 | { 197 | UDynamicMeshComponent* Component = Cast(SourceObject); 198 | return Component 199 | && IsValidChecked(Component) 200 | && (!Component->IsUnreachable()) 201 | && Component->IsValidLowLevel() 202 | && Component->GetDynamicMesh() 203 | && Requirements.AreSatisfiedBy(URuntimeDynamicMeshComponentToolTarget::StaticClass()); 204 | } 205 | 206 | UToolTarget* URuntimeDynamicMeshComponentToolTargetFactory::BuildTarget(UObject* SourceObject, const FToolTargetTypeRequirements& Requirements) 207 | { 208 | URuntimeDynamicMeshComponentToolTarget* Target = NewObject(this); 209 | Target->Component = Cast(SourceObject); 210 | return Target; 211 | } 212 | 213 | 214 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Config/DefaultInput.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | [/Script/Engine.InputSettings] 4 | -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 5 | -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 6 | -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 7 | -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 8 | -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 9 | -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 10 | -AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 11 | +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 12 | +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 13 | +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 14 | +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 15 | +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 16 | +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 17 | +AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 18 | +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 19 | +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 20 | +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 21 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 22 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 23 | +AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 24 | +AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 25 | +AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 26 | +AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 27 | +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 28 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 29 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 30 | +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 31 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 32 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 33 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 34 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 35 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 36 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 37 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 38 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 39 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 40 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 41 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 42 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 43 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 44 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 45 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 46 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 47 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 48 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 49 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 50 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 51 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 52 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 53 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 54 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 55 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 56 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 57 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 58 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 59 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 60 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 61 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 62 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 63 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 64 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 65 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 66 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 67 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 68 | bAltEnterTogglesFullscreen=True 69 | bF11TogglesFullscreen=True 70 | bUseMouseForTouch=False 71 | bEnableMouseSmoothing=True 72 | bEnableFOVScaling=True 73 | bCaptureMouseOnLaunch=False 74 | bEnableLegacyInputScales=True 75 | bEnableMotionControls=True 76 | bFilterInputByPlatformUser=False 77 | bEnableInputDeviceSubsystem=True 78 | bShouldFlushPressedKeysOnViewportFocusLost=True 79 | bEnableDynamicComponentInputBinding=True 80 | bAlwaysShowTouchInterface=False 81 | bShowConsoleOnFourFingerTap=True 82 | bEnableGestureRecognizer=False 83 | bUseAutocorrect=False 84 | DefaultViewportMouseCaptureMode=CaptureDuringMouseDown 85 | DefaultViewportMouseLockMode=DoNotLock 86 | FOVScale=0.011110 87 | DoubleClickTime=0.200000 88 | +ActionMappings=(ActionName="ActiveToolAccept",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Enter) 89 | +ActionMappings=(ActionName="ActiveToolExit",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=BackSpace) 90 | +ActionMappings=(ActionName="ActiveToolExit",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Escape) 91 | +ActionMappings=(ActionName="LeftMouseButtonAction",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton) 92 | +ActionMappings=(ActionName="DeleteAction",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Delete) 93 | +ActionMappings=(ActionName="UndoAction",bShift=False,bCtrl=True,bAlt=False,bCmd=False,Key=Z) 94 | +ActionMappings=(ActionName="RedoAction",bShift=False,bCtrl=True,bAlt=False,bCmd=False,Key=Y) 95 | +AxisMappings=(AxisName="MouseMovementX",Scale=1.000000,Key=MouseX) 96 | +AxisMappings=(AxisName="MouseMovementY",Scale=1.000000,Key=MouseY) 97 | DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput 98 | DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent 99 | DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks 100 | -ConsoleKeys=Tilde 101 | +ConsoleKeys=Tilde 102 | 103 | -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/MeshScene/RuntimeMeshSceneSubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "RuntimeMeshSceneSubsystem.h" 4 | #include "GameFramework/GameModeBase.h" 5 | #include "MaterialDomain.h" 6 | #include "Materials/Material.h" 7 | 8 | #define LOCTEXT_NAMESPACE "URuntimeMeshSceneSubsystem" 9 | 10 | URuntimeMeshSceneSubsystem* URuntimeMeshSceneSubsystem::InstanceSingleton = nullptr; 11 | 12 | void URuntimeMeshSceneSubsystem::InitializeSingleton(URuntimeMeshSceneSubsystem* Subsystem) 13 | { 14 | check(InstanceSingleton == nullptr); 15 | InstanceSingleton = Subsystem; 16 | 17 | // todo: expose these somehow? 18 | 19 | UMaterial* DefaultObjectMaterial = LoadObject(nullptr, TEXT("/Game/RuntimeToolsFrameworkMaterials/DefaultObjectMaterial")); 20 | InstanceSingleton->StandardMaterial = (DefaultObjectMaterial) ? DefaultObjectMaterial : UMaterial::GetDefaultMaterial(MD_Surface); 21 | 22 | UMaterial* SelectionMaterial = LoadObject(nullptr, TEXT("/Game/RuntimeToolsFrameworkMaterials/SelectedMaterial")); 23 | InstanceSingleton->SelectedMaterial = (SelectionMaterial) ? SelectionMaterial : UMaterial::GetDefaultMaterial(MD_Surface); 24 | 25 | UMaterial* WireframeMaterial = LoadObject(nullptr, TEXT("/Game/RuntimeToolsFrameworkMaterials/WireframeMaterial")); 26 | WireframeMaterial = (WireframeMaterial) ? WireframeMaterial : UMaterial::GetDefaultMaterial(MD_Surface); 27 | InstanceSingleton->WireframeMaterial = WireframeMaterial; 28 | GEngine->WireframeMaterial = WireframeMaterial; 29 | } 30 | 31 | 32 | URuntimeMeshSceneSubsystem* URuntimeMeshSceneSubsystem::Get() 33 | { 34 | return InstanceSingleton; 35 | } 36 | 37 | 38 | void URuntimeMeshSceneSubsystem::Deinitialize() 39 | { 40 | InstanceSingleton = nullptr; 41 | } 42 | 43 | 44 | void URuntimeMeshSceneSubsystem::SetCurrentTransactionsAPI(IToolsContextTransactionsAPI* TransactionsAPIIn) 45 | { 46 | TransactionsAPI = TransactionsAPIIn; 47 | } 48 | 49 | 50 | URuntimeMeshSceneObject* URuntimeMeshSceneSubsystem::CreateNewSceneObject() 51 | { 52 | URuntimeMeshSceneObject* SceneObject = NewObject(this); 53 | AddSceneObjectInternal(SceneObject, false); 54 | 55 | if (TransactionsAPI) 56 | { 57 | TUniquePtr AddChange = MakeUnique(); 58 | AddChange->SceneObject = SceneObject; 59 | AddChange->bAdded = true; 60 | // use SceneObject as target so that transaction will keep it from being GC'd 61 | TransactionsAPI->AppendChange(SceneObject, MoveTemp(AddChange), LOCTEXT("AddObjectChange", "Add SceneObject")); 62 | } 63 | 64 | SceneObject->SetAllMaterials(StandardMaterial); 65 | 66 | return SceneObject; 67 | } 68 | 69 | 70 | URuntimeMeshSceneObject* URuntimeMeshSceneSubsystem::FindSceneObjectByActor(AActor* Actor) 71 | { 72 | for (URuntimeMeshSceneObject* SceneObject : SceneObjects) 73 | { 74 | if (SceneObject->GetActor() == Actor) 75 | { 76 | return SceneObject; 77 | } 78 | } 79 | return nullptr; 80 | } 81 | 82 | 83 | bool URuntimeMeshSceneSubsystem::DeleteSceneObject(URuntimeMeshSceneObject* SceneObject) 84 | { 85 | if (SceneObjects.Contains(SceneObject)) 86 | { 87 | if (SelectedSceneObjects.Contains(SceneObject)) 88 | { 89 | BeginSelectionChange(); 90 | SelectedSceneObjects.Remove(SceneObject); 91 | EndSelectionChange(); 92 | OnSelectionModified.Broadcast(this); 93 | } 94 | 95 | RemoveSceneObjectInternal(SceneObject, true); 96 | 97 | if (TransactionsAPI) 98 | { 99 | TUniquePtr RemoveChange = MakeUnique(); 100 | RemoveChange->SceneObject = SceneObject; 101 | RemoveChange->bAdded = false; 102 | // use SceneObject as target so that transaction will keep it from being GC'd 103 | TransactionsAPI->AppendChange(SceneObject, MoveTemp(RemoveChange), LOCTEXT("RemoveObjectChange", "Delete SceneObject")); 104 | } 105 | 106 | return true; 107 | } 108 | else 109 | { 110 | UE_LOG(LogTemp, Warning, TEXT("[URuntimeMeshSceneSubsystem::DeleteSceneObject] Tried to delete non-existant SceneObject")); 111 | return false; 112 | } 113 | } 114 | 115 | 116 | bool URuntimeMeshSceneSubsystem::DeleteSelectedSceneObjects() 117 | { 118 | return DeleteSelectedSceneObjects(nullptr); 119 | } 120 | 121 | bool URuntimeMeshSceneSubsystem::DeleteSelectedSceneObjects(AActor* SkipActor) 122 | { 123 | if (SelectedSceneObjects.Num() == 0) return false; 124 | 125 | if (TransactionsAPI) 126 | { 127 | TransactionsAPI->BeginUndoTransaction(LOCTEXT("DeleteSelectedObjectsChange", "Delete Objects")); 128 | } 129 | 130 | TArray DeleteObjects = SelectedSceneObjects; 131 | 132 | BeginSelectionChange(); 133 | SelectedSceneObjects.Reset(); 134 | EndSelectionChange(); 135 | 136 | for (URuntimeMeshSceneObject* SceneObject : DeleteObjects) 137 | { 138 | if (SceneObject->GetActor() == SkipActor) 139 | { 140 | continue; 141 | } 142 | 143 | RemoveSceneObjectInternal(SceneObject, true); 144 | 145 | if (TransactionsAPI) 146 | { 147 | TUniquePtr RemoveChange = MakeUnique(); 148 | RemoveChange->SceneObject = SceneObject; 149 | RemoveChange->bAdded = false; 150 | // use SceneObject as target so that transaction will keep it from being GC'd 151 | TransactionsAPI->AppendChange(SceneObject, MoveTemp(RemoveChange), LOCTEXT("RemoveObjectChange", "Delete SceneObject")); 152 | } 153 | } 154 | 155 | if (TransactionsAPI) 156 | { 157 | TransactionsAPI->EndUndoTransaction(); 158 | } 159 | 160 | OnSelectionModified.Broadcast(this); 161 | return true; 162 | } 163 | 164 | 165 | 166 | 167 | 168 | 169 | void URuntimeMeshSceneSubsystem::ClearSelection() 170 | { 171 | if (SelectedSceneObjects.Num() > 0) 172 | { 173 | BeginSelectionChange(); 174 | 175 | for (URuntimeMeshSceneObject* SO : SelectedSceneObjects) 176 | { 177 | SO->ClearHighlightMaterial(); 178 | } 179 | SelectedSceneObjects.Reset(); 180 | 181 | EndSelectionChange(); 182 | OnSelectionModified.Broadcast(this); 183 | } 184 | } 185 | 186 | 187 | void URuntimeMeshSceneSubsystem::SetSelected(URuntimeMeshSceneObject* SceneObject, bool bDeselect, bool bDeselectOthers) 188 | { 189 | if (bDeselect) 190 | { 191 | if (SelectedSceneObjects.Contains(SceneObject)) 192 | { 193 | BeginSelectionChange(); 194 | SelectedSceneObjects.Remove(SceneObject); 195 | SceneObject->ClearHighlightMaterial(); 196 | EndSelectionChange(); 197 | OnSelectionModified.Broadcast(this); 198 | } 199 | } 200 | else 201 | { 202 | BeginSelectionChange(); 203 | 204 | bool bIsSelected = SelectedSceneObjects.Contains(SceneObject); 205 | if (bDeselectOthers) 206 | { 207 | for (URuntimeMeshSceneObject* SO : SelectedSceneObjects) 208 | { 209 | if (SO != SceneObject) 210 | { 211 | SO->ClearHighlightMaterial(); 212 | } 213 | } 214 | SelectedSceneObjects.Reset(); 215 | } 216 | if (bIsSelected == false) 217 | { 218 | SceneObject->SetToHighlightMaterial(this->SelectedMaterial); 219 | } 220 | SelectedSceneObjects.Add(SceneObject); 221 | 222 | EndSelectionChange(); 223 | OnSelectionModified.Broadcast(this); 224 | } 225 | } 226 | 227 | 228 | void URuntimeMeshSceneSubsystem::ToggleSelected(URuntimeMeshSceneObject* SceneObject) 229 | { 230 | BeginSelectionChange(); 231 | 232 | if (SelectedSceneObjects.Contains(SceneObject)) 233 | { 234 | SelectedSceneObjects.Remove(SceneObject); 235 | SceneObject->ClearHighlightMaterial(); 236 | } 237 | else 238 | { 239 | SelectedSceneObjects.Add(SceneObject); 240 | SceneObject->SetToHighlightMaterial(this->SelectedMaterial); 241 | } 242 | 243 | EndSelectionChange(); 244 | OnSelectionModified.Broadcast(this); 245 | } 246 | 247 | 248 | void URuntimeMeshSceneSubsystem::SetSelection(const TArray& NewSceneObjects) 249 | { 250 | BeginSelectionChange(); 251 | SetSelectionInternal(NewSceneObjects); 252 | EndSelectionChange(); 253 | } 254 | void URuntimeMeshSceneSubsystem::SetSelectionInternal(const TArray& NewSceneObjects) 255 | { 256 | if (SelectedSceneObjects.Num() > 0) 257 | { 258 | for (URuntimeMeshSceneObject* SO : SelectedSceneObjects) 259 | { 260 | SO->ClearHighlightMaterial(); 261 | } 262 | SelectedSceneObjects.Reset(); 263 | } 264 | 265 | for (URuntimeMeshSceneObject* SO : NewSceneObjects) 266 | { 267 | if (SceneObjects.Contains(SO)) 268 | { 269 | if (SelectedSceneObjects.Contains(SO) == false) 270 | { 271 | SelectedSceneObjects.Add(SO); 272 | SO->SetToHighlightMaterial(this->SelectedMaterial); 273 | } 274 | } 275 | else 276 | { 277 | UE_LOG(LogTemp, Warning, TEXT("[URuntimeMeshSceneSubsystem::SetSelectionInternal] Tried to select non-existant SceneObject")); 278 | } 279 | } 280 | 281 | OnSelectionModified.Broadcast(this); 282 | } 283 | 284 | 285 | 286 | URuntimeMeshSceneObject* URuntimeMeshSceneSubsystem::FindNearestHitObject(FVector RayOrigin, FVector RayDirection, FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, float MaxDistance) 287 | { 288 | URuntimeMeshSceneObject* FoundHit = nullptr; 289 | float MinHitDistance = TNumericLimits::Max(); 290 | 291 | for (URuntimeMeshSceneObject* SO : SceneObjects) 292 | { 293 | FVector HitPoint, BaryCoords; 294 | float HitDist; 295 | int32 NearestTri; 296 | if (SO->IntersectRay(RayOrigin, RayDirection, HitPoint, HitDist, NearestTri, BaryCoords, MaxDistance)) 297 | { 298 | if (HitDist < MinHitDistance) 299 | { 300 | MinHitDistance = HitDist; 301 | WorldHitPoint = HitPoint; 302 | HitDistance = HitDist; 303 | NearestTriangle = NearestTri; 304 | TriBaryCoords = BaryCoords; 305 | FoundHit = SO; 306 | } 307 | } 308 | } 309 | return FoundHit; 310 | } 311 | 312 | 313 | 314 | 315 | void URuntimeMeshSceneSubsystem::BeginSelectionChange() 316 | { 317 | check(!ActiveSelectionChange); 318 | 319 | ActiveSelectionChange = MakeUnique(); 320 | ActiveSelectionChange->OldSelection = SelectedSceneObjects; 321 | } 322 | 323 | void URuntimeMeshSceneSubsystem::EndSelectionChange() 324 | { 325 | check(ActiveSelectionChange); 326 | if (SelectedSceneObjects != ActiveSelectionChange->OldSelection) 327 | { 328 | ActiveSelectionChange->NewSelection = SelectedSceneObjects; 329 | 330 | if (TransactionsAPI) 331 | { 332 | TransactionsAPI->AppendChange(this, MoveTemp(ActiveSelectionChange), LOCTEXT("SelectionChange", "Selection Change")); 333 | } 334 | } 335 | 336 | ActiveSelectionChange = nullptr; 337 | } 338 | 339 | void FMeshSceneSelectionChange::Apply(UObject* Object) 340 | { 341 | if (URuntimeMeshSceneSubsystem* Subsystem = Cast(Object)) 342 | { 343 | Subsystem->SetSelectionInternal(NewSelection); 344 | } 345 | } 346 | void FMeshSceneSelectionChange::Revert(UObject* Object) 347 | { 348 | if (URuntimeMeshSceneSubsystem* Subsystem = Cast(Object)) 349 | { 350 | Subsystem->SetSelectionInternal(OldSelection); 351 | } 352 | } 353 | 354 | 355 | 356 | 357 | void URuntimeMeshSceneSubsystem::AddSceneObjectInternal(URuntimeMeshSceneObject* Object, bool bIsUndoRedo) 358 | { 359 | SceneObjects.Add(Object); 360 | 361 | if (bIsUndoRedo) 362 | { 363 | Object->GetActor()->RegisterAllComponents(); 364 | } 365 | } 366 | 367 | void URuntimeMeshSceneSubsystem::RemoveSceneObjectInternal(URuntimeMeshSceneObject* Object, bool bIsUndoRedo) 368 | { 369 | check(SceneObjects.Contains(Object)); 370 | SceneObjects.Remove(Object); 371 | 372 | Object->GetActor()->UnregisterAllComponents(true); 373 | } 374 | 375 | 376 | 377 | void FAddRemoveSceneObjectChange::Apply(UObject* Object) 378 | { 379 | if (bAdded) 380 | { 381 | URuntimeMeshSceneSubsystem::Get()->AddSceneObjectInternal(SceneObject, true); 382 | } 383 | else 384 | { 385 | URuntimeMeshSceneSubsystem::Get()->RemoveSceneObjectInternal(SceneObject, true); 386 | } 387 | } 388 | 389 | void FAddRemoveSceneObjectChange::Revert(UObject* Object) 390 | { 391 | if (bAdded) 392 | { 393 | URuntimeMeshSceneSubsystem::Get()->RemoveSceneObjectInternal(SceneObject, true); 394 | } 395 | else 396 | { 397 | URuntimeMeshSceneSubsystem::Get()->AddSceneObjectInternal(SceneObject, true); 398 | } 399 | } 400 | 401 | 402 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Source/RuntimeToolsSystem/Public/RuntimeToolsFramework/ToolsContextActor.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolsContextActor.h" 2 | #include "RuntimeToolsFrameworkSubsystem.h" 3 | #include "MeshScene/RuntimeMeshSceneSubsystem.h" 4 | #include "InputCoreTypes.h" 5 | #include "GameFramework/PlayerInput.h" 6 | #include "GameFramework/Controller.h" 7 | #include "GameFramework/PlayerController.h" 8 | #include "Components/InputComponent.h" 9 | 10 | // Sets default values 11 | AToolsContextActor::AToolsContextActor() 12 | { 13 | // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it. 14 | PrimaryActorTick.bCanEverTick = true; 15 | } 16 | 17 | // Called when the game starts or when spawned 18 | void AToolsContextActor::BeginPlay() 19 | { 20 | Super::BeginPlay(); 21 | 22 | UGameInstance* GameInstance = GetGameInstance(); 23 | ToolsSystem = UGameInstance::GetSubsystem(GameInstance); 24 | ToolsSystem->SetContextActor(this); 25 | } 26 | 27 | void AToolsContextActor::EndPlay(const EEndPlayReason::Type EndPlayReason) 28 | { 29 | Super::EndPlay(EndPlayReason); 30 | } 31 | 32 | 33 | // Called every frame 34 | void AToolsContextActor::Tick(float DeltaTime) 35 | { 36 | Super::Tick(DeltaTime); 37 | 38 | } 39 | 40 | 41 | void AToolsContextActor::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) 42 | { 43 | // do not want default player input behavior 44 | //Super::SetupPlayerInputComponent(PlayerInputComponent); 45 | 46 | // only allow these keys if RMB is down? 47 | UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("TFPawn_MoveForward", EKeys::W, 1.f)); 48 | UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("TFPawn_MoveForward", EKeys::S, -1.f)); 49 | UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("TFPawn_MoveRight", EKeys::A, -1.f)); 50 | UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("TFPawn_MoveRight", EKeys::D, 1.f)); 51 | UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("TFPawn_MoveUp", EKeys::E, 1.f)); 52 | UPlayerInput::AddEngineDefinedAxisMapping(FInputAxisKeyMapping("TFPawn_MoveUp", EKeys::Q, -1.f)); 53 | PlayerInputComponent->BindAxis("TFPawn_MoveForward", this, &AToolsContextActor::OnMoveForwardKeyAxis); 54 | PlayerInputComponent->BindAxis("TFPawn_MoveRight", this, &AToolsContextActor::OnMoveRightKeyAxis); 55 | PlayerInputComponent->BindAxis("TFPawn_MoveUp", this, &AToolsContextActor::OnMoveUpKeyAxis); 56 | 57 | 58 | UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("StandardAltButton", EKeys::LeftAlt)); 59 | UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("StandardAltButton", EKeys::RightAlt)); 60 | PlayerInputComponent->BindAction("StandardAltButton", IE_Pressed, this, &AToolsContextActor::OnAltKeyDown); 61 | PlayerInputComponent->BindAction("StandardAltButton", IE_Released, this, &AToolsContextActor::OnAltKeyUp); 62 | 63 | 64 | // [RMS] AddEngineDefinedActionMapping() does not work with EKeys::LeftMouseButton (can't figure out why...) 65 | // As a workaround, I have added a "LeftMouseButtonAction" mapping in the Input section of Project Settings 66 | //UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("StandardLeftMouseButton", EKeys::LeftMouseButton)); 67 | //PlayerInputComponent->BindAction("StandardMouseLeftButton", IE_Pressed, this, &AToolsContextActor::OnLeftMouseDown); 68 | //PlayerInputComponent->BindAction("StandardMouseLeftButton", IE_Released, this, &AToolsContextActor::OnLeftMouseUp); 69 | PlayerInputComponent->BindAction("LeftMouseButtonAction", IE_Pressed, this, &AToolsContextActor::OnLeftMouseDown); 70 | PlayerInputComponent->BindAction("LeftMouseButtonAction", IE_Released, this, &AToolsContextActor::OnLeftMouseUp); 71 | 72 | 73 | UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("StandardMiddleMouseButton", EKeys::MiddleMouseButton)); 74 | PlayerInputComponent->BindAction("StandardMiddleMouseButton", IE_Pressed, this, &AToolsContextActor::OnMiddleMouseDown); 75 | PlayerInputComponent->BindAction("StandardMiddleMouseButton", IE_Released, this, &AToolsContextActor::OnMiddleMouseUp); 76 | 77 | UPlayerInput::AddEngineDefinedActionMapping(FInputActionKeyMapping("StandardRightMouseButton", EKeys::RightMouseButton)); 78 | PlayerInputComponent->BindAction("StandardRightMouseButton", IE_Pressed, this, &AToolsContextActor::OnRightMouseDown); 79 | PlayerInputComponent->BindAction("StandardRightMouseButton", IE_Released, this, &AToolsContextActor::OnRightMouseUp); 80 | 81 | InputComponent->BindAxis("MouseMovementX", this, &AToolsContextActor::OnMouseMoveX); 82 | InputComponent->BindAxis("MouseMovementY", this, &AToolsContextActor::OnMouseMoveY); 83 | 84 | // generally bound to enter 85 | InputComponent->BindAction("ActiveToolAccept", IE_Released, this, &AToolsContextActor::OnToolAccept); 86 | // generally bound to escape 87 | InputComponent->BindAction("ActiveToolExit", IE_Released, this, &AToolsContextActor::OnToolExit); 88 | 89 | // generally bound to ctrl+z 90 | InputComponent->BindAction("UndoAction", IE_Released, this, &AToolsContextActor::OnUndo); 91 | // generally bound to ctrl+y 92 | InputComponent->BindAction("RedoAction", IE_Released, this, &AToolsContextActor::OnRedo); 93 | 94 | // generally bound to delete 95 | InputComponent->BindAction("DeleteAction", IE_Released, this, &AToolsContextActor::OnDelete); 96 | } 97 | 98 | 99 | 100 | 101 | 102 | void AToolsContextActor::OnLeftMouseDown() 103 | { 104 | bIsLeftMouseDown = true; 105 | 106 | // if we are in right-mouse cam mode and we press left, ignore it 107 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 108 | { 109 | ensure(ToolsSystem->IsCapturingMouse() == false); 110 | } 111 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 112 | { 113 | // do orbit 114 | ensure(ToolsSystem->IsCapturingMouse() == false); 115 | return; 116 | } 117 | else 118 | { 119 | ToolsSystem->OnLeftMouseDown(); 120 | } 121 | } 122 | 123 | 124 | void AToolsContextActor::OnLeftMouseUp() 125 | { 126 | bIsLeftMouseDown = false; 127 | 128 | // if we are in right-mouse cam mode and we press left, ignore it 129 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 130 | { 131 | ensure(ToolsSystem->IsCapturingMouse() == false); 132 | } 133 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 134 | { 135 | // end orbit 136 | ensure(ToolsSystem->IsCapturingMouse() == false); 137 | return; 138 | } 139 | else 140 | { 141 | ToolsSystem->OnLeftMouseUp(); 142 | } 143 | } 144 | 145 | 146 | 147 | void AToolsContextActor::OnMiddleMouseDown() 148 | { 149 | bIsMiddleMouseDown = true; 150 | 151 | if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 152 | { 153 | // want to start pan... 154 | } 155 | } 156 | 157 | void AToolsContextActor::OnMiddleMouseUp() 158 | { 159 | bIsMiddleMouseDown = false; 160 | 161 | if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 162 | { 163 | // want to end pan... 164 | } 165 | } 166 | 167 | 168 | void AToolsContextActor::OnRightMouseDown() 169 | { 170 | bIsRightMouseDown = true; 171 | 172 | // ignore if tool system is capturing 173 | if (ToolsSystem && ToolsSystem->IsCapturingMouse()) 174 | { 175 | ensure(CurrentInteractionMode == EToolActorInteractionMode::NoInteraction); 176 | return; 177 | } 178 | 179 | if (CurrentInteractionMode == EToolActorInteractionMode::NoInteraction) 180 | { 181 | CurrentInteractionMode = EToolActorInteractionMode::RightMouseCameraControl; 182 | } 183 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 184 | { 185 | // want to start dolly... 186 | } 187 | } 188 | 189 | void AToolsContextActor::OnRightMouseUp() 190 | { 191 | bIsRightMouseDown = false; 192 | 193 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 194 | { 195 | CurrentInteractionMode = EToolActorInteractionMode::NoInteraction; 196 | } 197 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 198 | { 199 | // end dolly 200 | } 201 | } 202 | 203 | 204 | 205 | 206 | void AToolsContextActor::OnAltKeyDown() 207 | { 208 | bIsAltKeyDown = true; 209 | 210 | // if we are in right-mouse cam mode and we press alt, ignore it 211 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 212 | { 213 | ensure(ToolsSystem->IsCapturingMouse() == false); 214 | } 215 | else if (ToolsSystem->IsCapturingMouse()) 216 | { 217 | // if tool system is capturing, ignore alt 218 | } 219 | else 220 | { 221 | // switch to alt-camera control 222 | CurrentInteractionMode = EToolActorInteractionMode::AltCameraControl; 223 | } 224 | } 225 | 226 | 227 | 228 | void AToolsContextActor::OnAltKeyUp() 229 | { 230 | bIsAltKeyDown = false; 231 | 232 | // if we are in right-mouse cam mode and we release alt, ignore it 233 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 234 | { 235 | ensure(ToolsSystem->IsCapturingMouse() == false); 236 | } 237 | else if (ToolsSystem->IsCapturingMouse()) 238 | { 239 | // if tool system is capturing, ignore alt 240 | } 241 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 242 | { 243 | // switch out of alt-camera control 244 | CurrentInteractionMode = EToolActorInteractionMode::NoInteraction; 245 | } 246 | } 247 | 248 | 249 | 250 | 251 | void AToolsContextActor::OnMouseMoveX(float MoveX) 252 | { 253 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 254 | { 255 | ADefaultPawn::AddControllerYawInput(MoveX); 256 | } 257 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 258 | { 259 | if (bIsLeftMouseDown) 260 | { 261 | ADefaultPawn::AddControllerYawInput(MoveX); 262 | } 263 | else if (bIsMiddleMouseDown) 264 | { 265 | ADefaultPawn::MoveRight(100.0 * MoveX); 266 | } 267 | else if (bIsRightMouseDown) 268 | { 269 | //ADefaultPawn::MoveForward(MoveX); 270 | } 271 | } 272 | else 273 | { 274 | } 275 | } 276 | 277 | 278 | void AToolsContextActor::OnMouseMoveY(float MoveY) 279 | { 280 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 281 | { 282 | ADefaultPawn::AddControllerPitchInput(-MoveY); 283 | } 284 | else if (CurrentInteractionMode == EToolActorInteractionMode::AltCameraControl) 285 | { 286 | if (bIsLeftMouseDown) 287 | { 288 | ADefaultPawn::AddControllerPitchInput(-MoveY); 289 | } 290 | else if (bIsMiddleMouseDown) 291 | { 292 | ADefaultPawn::MoveUp_World(100.0 * MoveY); 293 | } 294 | else if (bIsRightMouseDown) 295 | { 296 | ADefaultPawn::MoveForward(-100.0 * MoveY); 297 | } 298 | // else RMB 299 | } 300 | else 301 | { 302 | } 303 | } 304 | 305 | 306 | 307 | 308 | void AToolsContextActor::OnMoveForwardKeyAxis(float MoveDelta) 309 | { 310 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 311 | { 312 | ADefaultPawn::MoveForward(MoveDelta); 313 | } 314 | } 315 | void AToolsContextActor::OnMoveRightKeyAxis(float MoveDelta) 316 | { 317 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 318 | { 319 | ADefaultPawn::MoveRight(MoveDelta); 320 | } 321 | } 322 | void AToolsContextActor::OnMoveUpKeyAxis(float MoveDelta) 323 | { 324 | if (CurrentInteractionMode == EToolActorInteractionMode::RightMouseCameraControl) 325 | { 326 | ADefaultPawn::MoveUp_World(MoveDelta); 327 | } 328 | } 329 | 330 | 331 | 332 | 333 | void AToolsContextActor::OnToolAccept() 334 | { 335 | ToolsSystem->AcceptActiveTool(); 336 | } 337 | 338 | void AToolsContextActor::OnToolExit() 339 | { 340 | ToolsSystem->CancelOrCompleteActiveTool(); 341 | } 342 | 343 | void AToolsContextActor::OnUndo() 344 | { 345 | ToolsSystem->GetSceneHistory()->Undo(); 346 | } 347 | 348 | void AToolsContextActor::OnRedo() 349 | { 350 | ToolsSystem->GetSceneHistory()->Redo(); 351 | } 352 | 353 | void AToolsContextActor::OnDelete() 354 | { 355 | if (ToolsSystem->HaveActiveTool() == false) 356 | { 357 | URuntimeMeshSceneSubsystem::Get()->DeleteSelectedSceneObjects(); 358 | } 359 | } 360 | 361 | 362 | 363 | void AToolsContextActor::PossessedBy(AController* inController) 364 | { 365 | this->PlayerController = Cast(inController); 366 | //if (ensure(this->PlayerController)) 367 | //{ 368 | // UE_LOG(LogTemp, Warning, TEXT("Got Player Controller!")); 369 | //} 370 | } -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/tinyobj/README.md: -------------------------------------------------------------------------------- 1 | # tinyobjloader 2 | 3 | [![Build Status](https://travis-ci.org/tinyobjloader/tinyobjloader.svg?branch=master)](https://travis-ci.org/tinyobjloader/tinyobjloader) 4 | 5 | [![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) 6 | 7 | [![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) 8 | 9 | [![Coverage Status](https://coveralls.io/repos/github/syoyo/tinyobjloader/badge.svg?branch=master)](https://coveralls.io/github/syoyo/tinyobjloader?branch=master) 10 | 11 | [![AUR version](https://img.shields.io/aur/version/tinyobjloader?logo=arch-linux)](https://aur.archlinux.org/packages/tinyobjloader) 12 | 13 | [![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) 14 | 15 | 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. 16 | 17 | `tinyobjloader` is good for embedding .obj loader to your (global illumination) renderer ;-) 18 | 19 | If you are looking for C89 version, please see https://github.com/syoyo/tinyobjloader-c . 20 | 21 | Notice! 22 | ------- 23 | 24 | We have released new version v1.0.0 on 20 Aug, 2016. 25 | Old version is available as `v0.9.x` branch https://github.com/syoyo/tinyobjloader/tree/v0.9.x 26 | 27 | ## What's new 28 | 29 | * 19 Feb, 2020 : The repository has been moved to https://github.com/tinyobjloader/tinyobjloader ! 30 | * 18 May, 2019 : Python binding!(See `python` folder. Also see https://pypi.org/project/tinyobjloader/) 31 | * 14 Apr, 2019 : Bump version v2.0.0 rc0. New C++ API and python bindings!(1.x API still exists for backward compatibility) 32 | * 20 Aug, 2016 : Bump version v1.0.0. New data structure and API! 33 | 34 | ## Requirements 35 | 36 | * C++03 compiler 37 | 38 | ### Old version 39 | 40 | Previous old version is available in `v0.9.x` branch. 41 | 42 | ## Example 43 | 44 | ![Rungholt](images/rungholt.jpg) 45 | 46 | tinyobjloader can successfully load 6M triangles Rungholt scene. 47 | http://casual-effects.com/data/index.html 48 | 49 | ![](images/sanmugel.png) 50 | 51 | * [examples/viewer/](examples/viewer) OpenGL .obj viewer 52 | * [examples/callback_api/](examples/callback_api/) Callback API example 53 | * [examples/voxelize/](examples/voxelize/) Voxelizer example 54 | 55 | ## Use case 56 | 57 | TinyObjLoader is successfully used in ... 58 | 59 | ### New version(v1.0.x) 60 | 61 | * Double precision support through `TINYOBJLOADER_USE_DOUBLE` thanks to noma 62 | * Loading models in Vulkan Tutorial https://vulkan-tutorial.com/Loading_models 63 | * .obj viewer with Metal https://github.com/middlefeng/NuoModelViewer/tree/master 64 | * Vulkan Cookbook https://github.com/PacktPublishing/Vulkan-Cookbook 65 | * cudabox: CUDA Solid Voxelizer Engine https://github.com/gaspardzoss/cudavox 66 | * Drake: A planning, control, and analysis toolbox for nonlinear dynamical systems https://github.com/RobotLocomotion/drake 67 | * VFPR - a Vulkan Forward Plus Renderer : https://github.com/WindyDarian/Vulkan-Forward-Plus-Renderer 68 | * glslViewer: https://github.com/patriciogonzalezvivo/glslViewer 69 | * Lighthouse2: https://github.com/jbikker/lighthouse2 70 | * rayrender(an open source R package for raytracing scenes in created in R): https://github.com/tylermorganwall/rayrender 71 | * liblava - A modern C++ and easy-to-use framework for the Vulkan API. [MIT]: https://github.com/liblava/liblava 72 | * rtxON - Simple Vulkan raytracing tutorials https://github.com/iOrange/rtxON 73 | * 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 74 | * Your project here! (Letting us know via github issue is welcome!) 75 | 76 | ### Old version(v0.9.x) 77 | 78 | * bullet3 https://github.com/erwincoumans/bullet3 79 | * pbrt-v2 https://github.com/mmp/pbrt-v2 80 | * OpenGL game engine development http://swarminglogic.com/jotting/2013_10_gamedev01 81 | * mallie https://lighttransport.github.io/mallie 82 | * IBLBaker (Image Based Lighting Baker). http://www.derkreature.com/iblbaker/ 83 | * Stanford CS148 http://web.stanford.edu/class/cs148/assignments/assignment3.pdf 84 | * Awesome Bump http://awesomebump.besaba.com/about/ 85 | * sdlgl3-wavefront OpenGL .obj viewer https://github.com/chrisliebert/sdlgl3-wavefront 86 | * pbrt-v3 https://github.com/mmp/pbrt-v3 87 | * cocos2d-x https://github.com/cocos2d/cocos2d-x/ 88 | * Android Vulkan demo https://github.com/SaschaWillems/Vulkan 89 | * voxelizer https://github.com/karimnaaji/voxelizer 90 | * Probulator https://github.com/kayru/Probulator 91 | * OptiX Prime baking https://github.com/nvpro-samples/optix_prime_baking 92 | * FireRays SDK https://github.com/GPUOpen-LibrariesAndSDKs/FireRays_SDK 93 | * parg, tiny C library of various graphics utilities and GL demos https://github.com/prideout/parg 94 | * Opengl unit of ChronoEngine https://github.com/projectchrono/chrono-opengl 95 | * Point Based Global Illumination on modern GPU https://pbgi.wordpress.com/code-source/ 96 | * Fast OBJ file importing and parsing in CUDA http://researchonline.jcu.edu.au/42515/1/2015.CVM.OBJCUDA.pdf 97 | * Sorted Shading for Uni-Directional Pathtracing by Joshua Bainbridge https://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc15/02Josh/joshua_bainbridge_thesis.pdf 98 | * GeeXLab http://www.geeks3d.com/hacklab/20160531/geexlab-0-12-0-0-released-for-windows/ 99 | 100 | 101 | ## Features 102 | 103 | * Group(parse multiple group name) 104 | * Vertex 105 | * Vertex color(as an extension: https://blender.stackexchange.com/questions/31997/how-can-i-get-vertex-painted-obj-files-to-import-into-blender) 106 | * Texcoord 107 | * Normal 108 | * Material 109 | * Unknown material attributes are returned as key-value(value is string) map. 110 | * Crease tag('t'). This is OpenSubdiv specific(not in wavefront .obj specification) 111 | * PBR material extension for .MTL. Its proposed here: http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr 112 | * Callback API for custom loading. 113 | * Double precision support(for HPC application). 114 | * Smoothing group 115 | * Python binding : See `python` folder. 116 | * Precompiled binary(manylinux1-x86_64 only) is hosted at pypi https://pypi.org/project/tinyobjloader/) 117 | 118 | ### Primitives 119 | 120 | * [x] face(`f`) 121 | * [x] lines(`l`) 122 | * [ ] points(`p`) 123 | * [ ] curve 124 | * [ ] 2D curve 125 | * [ ] surface. 126 | * [ ] Free form curve/surfaces 127 | 128 | 129 | ## TODO 130 | 131 | * [ ] Fix obj_sticker example. 132 | * [ ] More unit test codes. 133 | * [x] Texture options 134 | 135 | ## License 136 | 137 | TinyObjLoader is licensed under MIT license. 138 | 139 | ### Third party licenses. 140 | 141 | * pybind11 : BSD-style license. 142 | 143 | ## Usage 144 | 145 | ### Installation 146 | 147 | One option is to simply copy the header file into your project and to make sure that `TINYOBJLOADER_IMPLEMENTATION` is defined exactly once. 148 | 149 | 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. 150 | 151 | ### Building tinyobjloader - Using vcpkg 152 | 153 | You can download and install tinyobjloader using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: 154 | 155 | git clone https://github.com/Microsoft/vcpkg.git 156 | cd vcpkg 157 | ./bootstrap-vcpkg.sh 158 | ./vcpkg integrate install 159 | ./vcpkg install tinyobjloader 160 | 161 | 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. 162 | 163 | ### Data format 164 | 165 | `attrib_t` contains single and linear array of vertex data(position, normal and texcoord). 166 | 167 | ``` 168 | attrib_t::vertices => 3 floats per vertex 169 | 170 | v[0] v[1] v[2] v[3] v[n-1] 171 | +-----------+-----------+-----------+-----------+ +-----------+ 172 | | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | 173 | +-----------+-----------+-----------+-----------+ +-----------+ 174 | 175 | attrib_t::normals => 3 floats per vertex 176 | 177 | n[0] n[1] n[2] n[3] n[n-1] 178 | +-----------+-----------+-----------+-----------+ +-----------+ 179 | | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | 180 | +-----------+-----------+-----------+-----------+ +-----------+ 181 | 182 | attrib_t::texcoords => 2 floats per vertex 183 | 184 | t[0] t[1] t[2] t[3] t[n-1] 185 | +-----------+-----------+-----------+-----------+ +-----------+ 186 | | u | v | u | v | u | v | u | v | .... | u | v | 187 | +-----------+-----------+-----------+-----------+ +-----------+ 188 | 189 | attrib_t::colors => 3 floats per vertex(vertex color. optional) 190 | 191 | c[0] c[1] c[2] c[3] c[n-1] 192 | +-----------+-----------+-----------+-----------+ +-----------+ 193 | | x | y | z | x | y | z | x | y | z | x | y | z | .... | x | y | z | 194 | +-----------+-----------+-----------+-----------+ +-----------+ 195 | 196 | ``` 197 | 198 | Each `shape_t::mesh_t` does not contain vertex data but contains array index to `attrib_t`. 199 | See `loader_example.cc` for more details. 200 | 201 | 202 | ``` 203 | 204 | mesh_t::indices => array of vertex indices. 205 | 206 | +----+----+----+----+----+----+----+----+----+----+ +--------+ 207 | | i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 | ... | i(n-1) | 208 | +----+----+----+----+----+----+----+----+----+----+ +--------+ 209 | 210 | Each index has an array index to attrib_t::vertices, attrib_t::normals and attrib_t::texcoords. 211 | 212 | mesh_t::num_face_vertices => array of the number of vertices per face(e.g. 3 = triangle, 4 = quad , 5 or more = N-gons). 213 | 214 | 215 | +---+---+---+ +---+ 216 | | 3 | 4 | 3 | ...... | 3 | 217 | +---+---+---+ +---+ 218 | | | | | 219 | | | | +-----------------------------------------+ 220 | | | | | 221 | | | +------------------------------+ | 222 | | | | | 223 | | +------------------+ | | 224 | | | | | 225 | |/ |/ |/ |/ 226 | 227 | mesh_t::indices 228 | 229 | | face[0] | face[1] | face[2] | | face[n-1] | 230 | +----+----+----+----+----+----+----+----+----+----+ +--------+--------+--------+ 231 | | i0 | i1 | i2 | i3 | i4 | i5 | i6 | i7 | i8 | i9 | ... | i(n-3) | i(n-2) | i(n-1) | 232 | +----+----+----+----+----+----+----+----+----+----+ +--------+--------+--------+ 233 | 234 | ``` 235 | 236 | Note that when `triangulate` flag is true in `tinyobj::LoadObj()` argument, `num_face_vertices` are all filled with 3(triangle). 237 | 238 | ### float data type 239 | 240 | TinyObjLoader now use `real_t` for floating point data type. 241 | Default is `float(32bit)`. 242 | You can enable `double(64bit)` precision by using `TINYOBJLOADER_USE_DOUBLE` define. 243 | 244 | #### Example code 245 | 246 | ```c++ 247 | #define TINYOBJLOADER_IMPLEMENTATION // define this in only *one* .cc 248 | #include "tiny_obj_loader.h" 249 | 250 | std::string inputfile = "cornell_box.obj"; 251 | tinyobj::attrib_t attrib; 252 | std::vector shapes; 253 | std::vector materials; 254 | 255 | std::string warn; 256 | std::string err; 257 | 258 | bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, inputfile.c_str()); 259 | 260 | if (!warn.empty()) { 261 | std::cout << warn << std::endl; 262 | } 263 | 264 | if (!err.empty()) { 265 | std::cerr << err << std::endl; 266 | } 267 | 268 | if (!ret) { 269 | exit(1); 270 | } 271 | 272 | // Loop over shapes 273 | for (size_t s = 0; s < shapes.size(); s++) { 274 | // Loop over faces(polygon) 275 | size_t index_offset = 0; 276 | for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { 277 | int fv = shapes[s].mesh.num_face_vertices[f]; 278 | 279 | // Loop over vertices in the face. 280 | for (size_t v = 0; v < fv; v++) { 281 | // access to vertex 282 | tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; 283 | tinyobj::real_t vx = attrib.vertices[3*idx.vertex_index+0]; 284 | tinyobj::real_t vy = attrib.vertices[3*idx.vertex_index+1]; 285 | tinyobj::real_t vz = attrib.vertices[3*idx.vertex_index+2]; 286 | tinyobj::real_t nx = attrib.normals[3*idx.normal_index+0]; 287 | tinyobj::real_t ny = attrib.normals[3*idx.normal_index+1]; 288 | tinyobj::real_t nz = attrib.normals[3*idx.normal_index+2]; 289 | tinyobj::real_t tx = attrib.texcoords[2*idx.texcoord_index+0]; 290 | tinyobj::real_t ty = attrib.texcoords[2*idx.texcoord_index+1]; 291 | // Optional: vertex colors 292 | // tinyobj::real_t red = attrib.colors[3*idx.vertex_index+0]; 293 | // tinyobj::real_t green = attrib.colors[3*idx.vertex_index+1]; 294 | // tinyobj::real_t blue = attrib.colors[3*idx.vertex_index+2]; 295 | } 296 | index_offset += fv; 297 | 298 | // per-face material 299 | shapes[s].mesh.material_ids[f]; 300 | } 301 | } 302 | 303 | ``` 304 | 305 | ## Optimized loader 306 | 307 | Optimized multi-threaded .obj loader is available at `experimental/` directory. 308 | If you want absolute performance to load .obj data, this optimized loader will fit your purpose. 309 | Note that the optimized loader uses C++11 thread and it does less error checks but may work most .obj data. 310 | 311 | Here is some benchmark result. Time are measured on MacBook 12(Early 2016, Core m5 1.2GHz). 312 | 313 | * Rungholt scene(6M triangles) 314 | * old version(v0.9.x): 15500 msecs. 315 | * baseline(v1.0.x): 6800 msecs(2.3x faster than old version) 316 | * optimised: 1500 msecs(10x faster than old version, 4.5x faster than baseline) 317 | 318 | ## Python binding 319 | 320 | ### CI + PyPI upload 321 | 322 | cibuildwheels + twine upload for each git tagging event is handled in Azure Pipeline. 323 | 324 | #### How to bump version(For developer) 325 | 326 | * Bump version in CMakeLists.txt 327 | * Update version in `python/setup.py` 328 | * Commit with tag name starging with `v`(e.g. `v2.1.0`) 329 | * `git push --tags` 330 | * cibuildwheels + pypi upload(through twine) will be automatically triggered in Azure Pipeline. 331 | 332 | ## Tests 333 | 334 | Unit tests are provided in `tests` directory. See `tests/README.md` for details. 335 | -------------------------------------------------------------------------------- /Plugins/RuntimeGeometryUtils/Source/RuntimeGeometryUtils/Private/DynamicMeshBaseActor.cpp: -------------------------------------------------------------------------------- 1 | #include "DynamicMeshBaseActor.h" 2 | 3 | #include "Generators/SphereGenerator.h" 4 | #include "Generators/GridBoxMeshGenerator.h" 5 | #include "MeshQueries.h" 6 | #include "DynamicMesh/DynamicMesh3.h" 7 | #include "DynamicMesh/MeshNormals.h" 8 | #include "DynamicMesh/MeshTransforms.h" 9 | #include "MeshSimplification.h" 10 | #include "Operations/MeshBoolean.h" 11 | #include "Implicit/Solidify.h" 12 | 13 | #include "Misc/Paths.h" 14 | #include "DynamicMeshOBJReader.h" 15 | 16 | using namespace UE::Geometry; 17 | 18 | // Sets default values 19 | ADynamicMeshBaseActor::ADynamicMeshBaseActor() 20 | { 21 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 22 | PrimaryActorTick.bCanEverTick = true; 23 | 24 | AccumulatedTime = 0; 25 | MeshAABBTree.SetMesh(&SourceMesh); 26 | 27 | FastWinding = MakeUnique>(&MeshAABBTree, false); 28 | 29 | MeshPool = CreateDefaultSubobject(TEXT("MeshPool")); 30 | } 31 | 32 | void ADynamicMeshBaseActor::PostLoad() 33 | { 34 | Super::PostLoad(); 35 | OnMeshGenerationSettingsModified(); 36 | } 37 | 38 | void ADynamicMeshBaseActor::PostActorCreated() 39 | { 40 | Super::PostActorCreated(); 41 | OnMeshGenerationSettingsModified(); 42 | } 43 | 44 | 45 | 46 | // Called when the game starts or when spawned 47 | void ADynamicMeshBaseActor::BeginPlay() 48 | { 49 | Super::BeginPlay(); 50 | 51 | AccumulatedTime = 0; 52 | OnMeshGenerationSettingsModified(); 53 | } 54 | 55 | // Called every frame 56 | void ADynamicMeshBaseActor::Tick(float DeltaTime) 57 | { 58 | Super::Tick(DeltaTime); 59 | 60 | AccumulatedTime += DeltaTime; 61 | if (bRegenerateOnTick && SourceType == EDynamicMeshActorSourceType::Primitive) 62 | { 63 | OnMeshGenerationSettingsModified(); 64 | } 65 | } 66 | 67 | 68 | #if WITH_EDITOR 69 | void ADynamicMeshBaseActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) 70 | { 71 | Super::PostEditChangeProperty(PropertyChangedEvent); 72 | OnMeshGenerationSettingsModified(); 73 | } 74 | #endif 75 | 76 | 77 | void ADynamicMeshBaseActor::EditMesh(TFunctionRef EditFunc) 78 | { 79 | EditFunc(SourceMesh); 80 | 81 | // update spatial data structures 82 | if (bEnableSpatialQueries || bEnableInsideQueries) 83 | { 84 | MeshAABBTree.Build(); 85 | if (bEnableInsideQueries) 86 | { 87 | FastWinding->Build(); 88 | } 89 | } 90 | 91 | OnMeshEditedInternal(); 92 | } 93 | 94 | 95 | void ADynamicMeshBaseActor::GetMeshCopy(FDynamicMesh3& MeshOut) 96 | { 97 | MeshOut = SourceMesh; 98 | } 99 | 100 | const FDynamicMesh3& ADynamicMeshBaseActor::GetMeshRef() const 101 | { 102 | return SourceMesh; 103 | } 104 | 105 | void ADynamicMeshBaseActor::OnMeshEditedInternal() 106 | { 107 | OnMeshModified.Broadcast(this); 108 | } 109 | 110 | 111 | void ADynamicMeshBaseActor::OnMeshGenerationSettingsModified() 112 | { 113 | // this lets PMC duplicate for PIE but we don't have our internal mesh then... 114 | if (SourceType == EDynamicMeshActorSourceType::ExternallyGenerated) return; 115 | 116 | EditMesh([this](FDynamicMesh3& MeshToUpdate) { 117 | RegenerateSourceMesh(MeshToUpdate); 118 | }); 119 | } 120 | 121 | 122 | void ADynamicMeshBaseActor::RegenerateSourceMesh(FDynamicMesh3& MeshOut) 123 | { 124 | if (SourceType == EDynamicMeshActorSourceType::Primitive) 125 | { 126 | double UseRadius = (this->MinimumRadius + this->VariableRadius) 127 | + (this->VariableRadius) * FMathd::Sin(PulseSpeed * AccumulatedTime); 128 | 129 | // generate new mesh 130 | if (this->PrimitiveType == EDynamicMeshActorPrimitiveType::Sphere) 131 | { 132 | FSphereGenerator SphereGen; 133 | SphereGen.NumPhi = SphereGen.NumTheta = FMath::Clamp(this->TessellationLevel, 3, 50); 134 | SphereGen.Radius = UseRadius; 135 | MeshOut.Copy(&SphereGen.Generate()); 136 | } 137 | else 138 | { 139 | FGridBoxMeshGenerator BoxGen; 140 | int TessLevel = FMath::Clamp(this->TessellationLevel, 2, 50); 141 | BoxGen.EdgeVertices = FIndex3i(TessLevel, TessLevel, TessLevel); 142 | FVector3d BoxExtents = UseRadius * FVector3d::One(); 143 | BoxExtents.Z *= BoxDepthRatio; 144 | BoxGen.Box = FOrientedBox3d(FVector3d::Zero(), BoxExtents); 145 | MeshOut.Copy(&BoxGen.Generate()); 146 | } 147 | 148 | } 149 | else if (SourceType == EDynamicMeshActorSourceType::ImportedMesh) 150 | { 151 | FString UsePath = ImportPath; 152 | if (FPaths::FileExists(UsePath) == false && FPaths::IsRelative(UsePath)) 153 | { 154 | UsePath = FPaths::ProjectContentDir() + ImportPath; 155 | } 156 | 157 | MeshOut = FDynamicMesh3(); 158 | if ( ! RTGUtils::ReadOBJMesh(UsePath, MeshOut, true, true, true, bReverseOrientation)) 159 | { 160 | UE_LOG(LogTemp, Warning, TEXT("Error reading mesh file %s"), *UsePath); 161 | FSphereGenerator SphereGen; 162 | SphereGen.NumPhi = SphereGen.NumTheta = 8; 163 | SphereGen.Radius = this->MinimumRadius; 164 | MeshOut.Copy(&SphereGen.Generate()); 165 | } 166 | 167 | if (bCenterPivot) 168 | { 169 | MeshTransforms::Translate(MeshOut, -MeshOut.GetBounds().Center()); 170 | } 171 | 172 | if (ImportScale != 1.0) 173 | { 174 | MeshTransforms::Scale(MeshOut, ImportScale*FVector3d::One(), FVector3d::Zero()); 175 | } 176 | } 177 | 178 | RecomputeNormals(MeshOut); 179 | } 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | void ADynamicMeshBaseActor::RecomputeNormals(FDynamicMesh3& MeshOut) 188 | { 189 | if (this->NormalsMode == EDynamicMeshActorNormalsMode::PerVertexNormals) 190 | { 191 | MeshOut.EnableAttributes(); 192 | FMeshNormals::InitializeOverlayToPerVertexNormals(MeshOut.Attributes()->PrimaryNormals(), false); 193 | } 194 | else if (this->NormalsMode == EDynamicMeshActorNormalsMode::FaceNormals) 195 | { 196 | MeshOut.EnableAttributes(); 197 | FMeshNormals::InitializeOverlayToPerTriangleNormals(MeshOut.Attributes()->PrimaryNormals()); 198 | } 199 | } 200 | 201 | 202 | 203 | int ADynamicMeshBaseActor::GetTriangleCount() const 204 | { 205 | return SourceMesh.TriangleCount(); 206 | } 207 | 208 | FVector ADynamicMeshBaseActor::GetTriNormal(int TriangleID, bool bWorldSpace) const 209 | { 210 | if (SourceMesh.IsTriangle(TriangleID)) 211 | { 212 | FVector3d Normal = SourceMesh.GetTriNormal(TriangleID); 213 | if (bWorldSpace) 214 | { 215 | FTransformSRT3d ActorToWorld(GetActorTransform()); 216 | return (FVector)ActorToWorld.TransformNormal(Normal); 217 | } 218 | return (FVector)Normal; 219 | } 220 | return FVector(0, 0, 1); 221 | } 222 | 223 | float ADynamicMeshBaseActor::DistanceToPoint(FVector WorldPoint, FVector& NearestWorldPoint, int& NearestTriangle, FVector& TriBaryCoords) 224 | { 225 | NearestWorldPoint = WorldPoint; 226 | NearestTriangle = -1; 227 | if (bEnableSpatialQueries == false) 228 | { 229 | return TNumericLimits::Max(); 230 | } 231 | 232 | FTransform3d ActorToWorld(GetActorTransform()); 233 | FVector3d LocalPoint = ActorToWorld.InverseTransformPosition((FVector3d)WorldPoint); 234 | 235 | double NearDistSqr; 236 | NearestTriangle = MeshAABBTree.FindNearestTriangle(LocalPoint, NearDistSqr); 237 | if (NearestTriangle < 0) 238 | { 239 | return TNumericLimits::Max(); 240 | } 241 | 242 | FDistPoint3Triangle3d DistQuery = TMeshQueries::TriangleDistance(SourceMesh, NearestTriangle, LocalPoint); 243 | NearestWorldPoint = (FVector)ActorToWorld.TransformPosition(DistQuery.ClosestTrianglePoint); 244 | TriBaryCoords = (FVector)DistQuery.TriangleBaryCoords; 245 | return (float)FMathd::Sqrt(NearDistSqr); 246 | } 247 | 248 | 249 | FVector ADynamicMeshBaseActor::NearestPoint(FVector WorldPoint) 250 | { 251 | if (bEnableSpatialQueries) 252 | { 253 | FTransform3d ActorToWorld(GetActorTransform()); 254 | FVector3d LocalPoint = ActorToWorld.InverseTransformPosition((FVector3d)WorldPoint); 255 | return (FVector)ActorToWorld.TransformPosition(MeshAABBTree.FindNearestPoint(LocalPoint)); 256 | } 257 | return WorldPoint; 258 | } 259 | 260 | bool ADynamicMeshBaseActor::ContainsPoint(FVector WorldPoint, float WindingThreshold) 261 | { 262 | if (bEnableInsideQueries) 263 | { 264 | FTransform3d ActorToWorld(GetActorTransform()); 265 | FVector3d LocalPoint = ActorToWorld.InverseTransformPosition((FVector3d)WorldPoint); 266 | return FastWinding->IsInside(LocalPoint, WindingThreshold); 267 | } 268 | return false; 269 | } 270 | 271 | 272 | bool ADynamicMeshBaseActor::IntersectRay(FVector RayOrigin, FVector RayDirection, 273 | FVector& WorldHitPoint, float& HitDistance, int& NearestTriangle, FVector& TriBaryCoords, 274 | float MaxDistance) 275 | { 276 | if (bEnableSpatialQueries) 277 | { 278 | FTransformSRT3d ActorToWorld(GetActorTransform()); 279 | FVector3d WorldDirection(RayDirection); WorldDirection.Normalize(); 280 | FRay3d LocalRay(ActorToWorld.InverseTransformPosition((FVector3d)RayOrigin), 281 | ActorToWorld.InverseTransformNormal(WorldDirection)); 282 | IMeshSpatial::FQueryOptions QueryOptions; 283 | if (MaxDistance > 0) 284 | { 285 | QueryOptions.MaxDistance = MaxDistance; 286 | } 287 | NearestTriangle = MeshAABBTree.FindNearestHitTriangle(LocalRay, QueryOptions); 288 | if (SourceMesh.IsTriangle(NearestTriangle)) 289 | { 290 | FIntrRay3Triangle3d IntrQuery = TMeshQueries::TriangleIntersection(SourceMesh, NearestTriangle, LocalRay); 291 | if (IntrQuery.IntersectionType == EIntersectionType::Point) 292 | { 293 | HitDistance = IntrQuery.RayParameter; 294 | WorldHitPoint = (FVector)ActorToWorld.TransformPosition(LocalRay.PointAt(IntrQuery.RayParameter)); 295 | TriBaryCoords = (FVector)IntrQuery.TriangleBaryCoords; 296 | return true; 297 | } 298 | } 299 | } 300 | return false; 301 | } 302 | 303 | 304 | 305 | 306 | void ADynamicMeshBaseActor::SubtractMesh(ADynamicMeshBaseActor* OtherMeshActor) 307 | { 308 | BooleanWithMesh(OtherMeshActor, EDynamicMeshActorBooleanOperation::Subtraction); 309 | } 310 | void ADynamicMeshBaseActor::UnionWithMesh(ADynamicMeshBaseActor* OtherMeshActor) 311 | { 312 | BooleanWithMesh(OtherMeshActor, EDynamicMeshActorBooleanOperation::Union); 313 | } 314 | void ADynamicMeshBaseActor::IntersectWithMesh(ADynamicMeshBaseActor* OtherMeshActor) 315 | { 316 | BooleanWithMesh(OtherMeshActor, EDynamicMeshActorBooleanOperation::Intersection); 317 | } 318 | 319 | 320 | void ADynamicMeshBaseActor::BooleanWithMesh(ADynamicMeshBaseActor* OtherMeshActor, EDynamicMeshActorBooleanOperation Operation) 321 | { 322 | if (ensure(OtherMeshActor) == false) return; 323 | 324 | FTransform3d ActorToWorld(GetActorTransform()); 325 | FTransform3d OtherToWorld(OtherMeshActor->GetActorTransform()); 326 | 327 | FDynamicMesh3 OtherMesh; 328 | OtherMeshActor->GetMeshCopy(OtherMesh); 329 | MeshTransforms::ApplyTransform(OtherMesh, OtherToWorld); 330 | MeshTransforms::ApplyTransformInverse(OtherMesh, ActorToWorld); 331 | 332 | EditMesh([&](FDynamicMesh3& MeshToUpdate) { 333 | 334 | FDynamicMesh3 ResultMesh; 335 | 336 | FMeshBoolean::EBooleanOp ApplyOp = FMeshBoolean::EBooleanOp::Union; 337 | switch (Operation) 338 | { 339 | default: 340 | break; 341 | case EDynamicMeshActorBooleanOperation::Subtraction: 342 | ApplyOp = FMeshBoolean::EBooleanOp::Difference; 343 | break; 344 | case EDynamicMeshActorBooleanOperation::Intersection: 345 | ApplyOp = FMeshBoolean::EBooleanOp::Intersect; 346 | break; 347 | } 348 | 349 | FMeshBoolean Boolean( 350 | &MeshToUpdate, FTransform3d::Identity, 351 | &OtherMesh, FTransform3d::Identity, 352 | &ResultMesh, 353 | ApplyOp); 354 | Boolean.bPutResultInInputSpace = true; 355 | bool bOK = Boolean.Compute(); 356 | 357 | if (!bOK) 358 | { 359 | // fill holes 360 | } 361 | 362 | RecomputeNormals(ResultMesh); 363 | 364 | MeshToUpdate = MoveTemp(ResultMesh); 365 | }); 366 | } 367 | 368 | 369 | 370 | bool ADynamicMeshBaseActor::ImportMesh(FString Path, bool bFlipOrientation, bool bRecomputeNormals) 371 | { 372 | FDynamicMesh3 ImportedMesh; 373 | if (!RTGUtils::ReadOBJMesh(Path, ImportedMesh, true, true, true, bFlipOrientation)) 374 | { 375 | UE_LOG(LogTemp, Warning, TEXT("Error reading mesh file %s"), *Path); 376 | return false; 377 | } 378 | 379 | if (bRecomputeNormals) 380 | { 381 | RecomputeNormals(ImportedMesh); 382 | } 383 | 384 | EditMesh([&](FDynamicMesh3& MeshToUpdate) 385 | { 386 | MeshToUpdate = MoveTemp(ImportedMesh); 387 | }); 388 | 389 | return true; 390 | } 391 | 392 | 393 | void ADynamicMeshBaseActor::CopyFromMeshActor(ADynamicMeshBaseActor* OtherMesh, bool bRecomputeNormals) 394 | { 395 | if (! ensure(OtherMesh) ) return; 396 | 397 | // the part where we generate a new mesh 398 | FDynamicMesh3 TmpMesh; 399 | OtherMesh->GetMeshCopy(TmpMesh); 400 | 401 | // apply our normals setting 402 | if (bRecomputeNormals) 403 | { 404 | RecomputeNormals(TmpMesh); 405 | } 406 | 407 | // update the mesh 408 | EditMesh([&](FDynamicMesh3& MeshToUpdate) 409 | { 410 | MeshToUpdate = MoveTemp(TmpMesh); 411 | }); 412 | } 413 | 414 | 415 | 416 | void ADynamicMeshBaseActor::CopyFromMesh(UGeneratedMesh* GeneratedMesh, bool bRecomputeNormals, bool bDeferComponentUpdate) 417 | { 418 | if (!GeneratedMesh) return; 419 | 420 | const TUniquePtr& OtherMesh = GeneratedMesh->GetMesh(); 421 | 422 | if ( bDeferComponentUpdate ) 423 | { 424 | SourceMesh.CompactCopy(*OtherMesh); 425 | } 426 | else 427 | { 428 | // update the mesh 429 | EditMesh([&](FDynamicMesh3& MeshToUpdate) 430 | { 431 | //MeshToUpdate.Copy(*OtherMesh); 432 | MeshToUpdate.CompactCopy(*OtherMesh); 433 | if (bRecomputeNormals) 434 | { 435 | RecomputeNormals(MeshToUpdate); 436 | } 437 | }); 438 | } 439 | } 440 | 441 | 442 | 443 | 444 | void ADynamicMeshBaseActor::SolidifyMesh(int VoxelResolution, float WindingThreshold) 445 | { 446 | if (MeshAABBTree.IsValid(false) == false) 447 | { 448 | MeshAABBTree.Build(); 449 | } 450 | if (FastWinding->IsBuilt() == false) 451 | { 452 | FastWinding->Build(); 453 | } 454 | 455 | // ugh workaround for bug 456 | FDynamicMesh3 CompactMesh; 457 | CompactMesh.CompactCopy(SourceMesh, false, false, false, false); 458 | FDynamicMeshAABBTree3 AABBTree(&CompactMesh, true); 459 | TFastWindingTree Winding(&AABBTree, true); 460 | 461 | double ExtendBounds = 2.0; 462 | //TImplicitSolidify SolidifyCalc(&SourceMesh, &MeshAABBTree, FastWinding.Get()); 463 | //SolidifyCalc.SetCellSizeAndExtendBounds(MeshAABBTree.GetBoundingBox(), ExtendBounds, VoxelResolution); 464 | TImplicitSolidify SolidifyCalc(&CompactMesh, &AABBTree, &Winding); 465 | SolidifyCalc.SetCellSizeAndExtendBounds(AABBTree.GetBoundingBox(), ExtendBounds, VoxelResolution); 466 | SolidifyCalc.WindingThreshold = WindingThreshold; 467 | SolidifyCalc.SurfaceSearchSteps = 5; 468 | SolidifyCalc.bSolidAtBoundaries = true; 469 | SolidifyCalc.ExtendBounds = ExtendBounds; 470 | FDynamicMesh3 SolidMesh(&SolidifyCalc.Generate()); 471 | 472 | SolidMesh.EnableAttributes(); 473 | RecomputeNormals(SolidMesh); 474 | 475 | EditMesh([&](FDynamicMesh3& MeshToUpdate) 476 | { 477 | MeshToUpdate = MoveTemp(SolidMesh); 478 | }); 479 | } 480 | 481 | void ADynamicMeshBaseActor::SimplifyMeshToTriCount(int32 TargetTriangleCount) 482 | { 483 | TargetTriangleCount = FMath::Max(1, TargetTriangleCount); 484 | if (TargetTriangleCount >= SourceMesh.TriangleCount()) return; 485 | 486 | // make compacted copy because it seems to change the results? 487 | FDynamicMesh3 SimplifyMesh; 488 | SimplifyMesh.CompactCopy(SourceMesh, false, false, false, false); 489 | SimplifyMesh.EnableTriangleGroups(); // workaround for failing check() 490 | FQEMSimplification Simplifier(&SimplifyMesh); 491 | Simplifier.SimplifyToTriangleCount(TargetTriangleCount); 492 | SimplifyMesh.EnableAttributes(); 493 | RecomputeNormals(SimplifyMesh); 494 | 495 | EditMesh([&](FDynamicMesh3& MeshToUpdate) 496 | { 497 | MeshToUpdate.CompactCopy(SimplifyMesh); 498 | }); 499 | } 500 | 501 | 502 | 503 | 504 | UGeneratedMesh* ADynamicMeshBaseActor::AllocateComputeMesh() 505 | { 506 | if (bEnableComputeMeshPool) 507 | { 508 | if (MeshPool == nullptr) 509 | { 510 | MeshPool = NewObject(this); 511 | } 512 | return MeshPool->RequestMesh(); 513 | } 514 | else 515 | { 516 | return NewObject(this); 517 | } 518 | } 519 | 520 | 521 | void ADynamicMeshBaseActor::ReleaseComputeMesh(UGeneratedMesh* Mesh) 522 | { 523 | if (bEnableComputeMeshPool && Mesh) 524 | { 525 | if (MeshPool == nullptr) 526 | { 527 | MeshPool = NewObject(this); 528 | } 529 | MeshPool->ReturnMesh(Mesh); 530 | } 531 | } 532 | 533 | void ADynamicMeshBaseActor::ReleaseComputeMeshes(TArray Meshes) 534 | { 535 | if (bEnableComputeMeshPool) 536 | { 537 | if (MeshPool == nullptr) 538 | { 539 | MeshPool = NewObject(this); 540 | } 541 | for (UGeneratedMesh* Mesh : Meshes) 542 | { 543 | if (Mesh) 544 | { 545 | MeshPool->ReturnMesh(Mesh); 546 | } 547 | } 548 | } 549 | } 550 | 551 | 552 | void ADynamicMeshBaseActor::ReleaseAllComputeMeshes() 553 | { 554 | if (MeshPool) 555 | { 556 | MeshPool->ReturnAllMeshes(); 557 | } 558 | } 559 | 560 | void ADynamicMeshBaseActor::FreeAllComputeMeshes() 561 | { 562 | if (MeshPool) 563 | { 564 | MeshPool->FreeAllMeshes(); 565 | } 566 | } --------------------------------------------------------------------------------