├── .github └── FUNDING.yml ├── .gitignore ├── CREDITS.md ├── Config ├── DefaultRealtimeMeshComponent.ini └── FilterPlugin.ini ├── Content └── Core │ ├── BP_RMC_PMCLike.uasset │ ├── BP_RMC_SMCConversion.uasset │ └── BP_RMC_SingleTriangle.uasset ├── LICENSE.txt ├── README.md ├── RealtimeMeshComponent.uplugin ├── Resources ├── Icon128.png └── MenuIcon.svg └── Source ├── RealtimeMeshComponent ├── Private │ ├── Data │ │ ├── RealtimeMeshData.cpp │ │ ├── RealtimeMeshLOD.cpp │ │ ├── RealtimeMeshSection.cpp │ │ ├── RealtimeMeshSectionGroup.cpp │ │ └── RealtimeMeshUpdateBuilder.cpp │ ├── InterfaceImpl │ │ ├── RealtimeMeshCollisionToolsInterfaceImpl.cpp │ │ ├── RealtimeMeshComponentInterfaceImpl.cpp │ │ ├── RealtimeMeshInterfaceImpl.cpp │ │ └── RealtimeMeshSimpleInterfaceImpl.cpp │ ├── Mesh │ │ ├── RealtimeMeshAlgo.cpp │ │ ├── RealtimeMeshBasicShapeTools.cpp │ │ ├── RealtimeMeshBlueprintMeshBuilder.cpp │ │ ├── RealtimeMeshCardRepresentation.cpp │ │ └── RealtimeMeshDistanceField.cpp │ ├── RDNInterface │ │ └── RealtimeDataNetworkInterface.h │ ├── RealtimeMesh.cpp │ ├── RealtimeMeshActor.cpp │ ├── RealtimeMeshCollisionLibrary.cpp │ ├── RealtimeMeshComponent.cpp │ ├── RealtimeMeshComponentModule.cpp │ ├── RealtimeMeshCore.cpp │ ├── RealtimeMeshGuard.cpp │ ├── RealtimeMeshLibrary.cpp │ ├── RealtimeMeshNoExportTypes.h │ ├── RealtimeMeshSceneViewExtension.cpp │ ├── RealtimeMeshSceneViewExtension.h │ ├── RealtimeMeshSerialization.cpp │ ├── RealtimeMeshShared.cpp │ ├── RealtimeMeshSimple.cpp │ ├── RealtimeMeshSubsystem.cpp │ ├── RealtimeMeshThreadingSubsystem.cpp │ └── RenderProxy │ │ ├── RealtimeMeshComponentProxy.cpp │ │ ├── RealtimeMeshGPUBuffer.cpp │ │ ├── RealtimeMeshLODProxy.cpp │ │ ├── RealtimeMeshProxy.cpp │ │ ├── RealtimeMeshProxyCommandBatch.cpp │ │ ├── RealtimeMeshSectionGroupProxy.cpp │ │ ├── RealtimeMeshSectionProxy.cpp │ │ └── RealtimeMeshVertexFactory.cpp ├── Public │ ├── Data │ │ ├── RealtimeMeshData.h │ │ ├── RealtimeMeshLOD.h │ │ ├── RealtimeMeshSection.h │ │ ├── RealtimeMeshSectionGroup.h │ │ ├── RealtimeMeshShared.h │ │ └── RealtimeMeshUpdateBuilder.h │ ├── Interface │ │ └── Core │ │ │ ├── RealtimeMeshBuilder.h │ │ │ ├── RealtimeMeshCollision.h │ │ │ ├── RealtimeMeshComponentInterface.h │ │ │ ├── RealtimeMeshConfig.h │ │ │ ├── RealtimeMeshDataConversion.cpp │ │ │ ├── RealtimeMeshDataConversion.h │ │ │ ├── RealtimeMeshDataStream.cpp │ │ │ ├── RealtimeMeshDataStream.h │ │ │ ├── RealtimeMeshDataTypes.cpp │ │ │ ├── RealtimeMeshDataTypes.h │ │ │ ├── RealtimeMeshFuture.h │ │ │ ├── RealtimeMeshInterface.h │ │ │ ├── RealtimeMeshInterfaceCore.cpp │ │ │ ├── RealtimeMeshInterfaceFwd.h │ │ │ ├── RealtimeMeshKeys.h │ │ │ ├── RealtimeMeshLODConfig.h │ │ │ ├── RealtimeMeshMaterial.h │ │ │ ├── RealtimeMeshModularFeatures.h │ │ │ ├── RealtimeMeshSectionConfig.h │ │ │ ├── RealtimeMeshSectionGroupConfig.h │ │ │ ├── RealtimeMeshSimpleInterface.h │ │ │ └── RealtimeMeshStreamRange.h │ ├── Mesh │ │ ├── RealtimeMeshAlgo.h │ │ ├── RealtimeMeshBasicShapeTools.h │ │ ├── RealtimeMeshBlueprintMeshBuilder.h │ │ ├── RealtimeMeshCardRepresentation.h │ │ ├── RealtimeMeshDistanceField.h │ │ └── RealtimeMeshNaniteResourcesInterface.h │ ├── RealtimeMesh.h │ ├── RealtimeMeshActor.h │ ├── RealtimeMeshCollisionLibrary.h │ ├── RealtimeMeshComponent.h │ ├── RealtimeMeshComponentModule.h │ ├── RealtimeMeshCore.h │ ├── RealtimeMeshGuard.h │ ├── RealtimeMeshLibrary.h │ ├── RealtimeMeshSimple.h │ ├── RealtimeMeshSubsystem.h │ ├── RealtimeMeshThreadingSubsystem.h │ └── RenderProxy │ │ ├── RealtimeMeshComponentProxy.h │ │ ├── RealtimeMeshGPUBuffer.h │ │ ├── RealtimeMeshLODProxy.h │ │ ├── RealtimeMeshNaniteProxyInterface.h │ │ ├── RealtimeMeshProxy.h │ │ ├── RealtimeMeshProxyCommandBatch.h │ │ ├── RealtimeMeshProxyShared.h │ │ ├── RealtimeMeshSectionGroupProxy.h │ │ ├── RealtimeMeshSectionProxy.h │ │ └── RealtimeMeshVertexFactory.h ├── RealtimeMeshComponent.Build.cs └── RealtimeMeshComponent.natvis ├── RealtimeMeshEditor ├── Private │ ├── RealtimeMeshEditor.cpp │ └── RealtimeMeshMenuExtension.cpp ├── Public │ ├── RealtimeMeshEditor.h │ └── RealtimeMeshMenuExtension.h └── RealtimeMeshEditor.Build.cs ├── RealtimeMeshExamples ├── Private │ ├── RealtimeMeshBasic.cpp │ ├── RealtimeMeshByStreamBuilder.cpp │ ├── RealtimeMeshDirect.cpp │ ├── RealtimeMeshExamples.cpp │ ├── RealtimeMeshLODExample.cpp │ └── RealtimeMeshMultipleUVs.cpp ├── Public │ ├── RealtimeMeshBasic.h │ ├── RealtimeMeshByStreamBuilder.h │ ├── RealtimeMeshDirect.h │ ├── RealtimeMeshExamples.h │ ├── RealtimeMeshLODExample.h │ └── RealtimeMeshMultipleUVs.h └── RealtimeMeshExamples.Build.cs └── RealtimeMeshTests ├── Private ├── FunctionalTests │ ├── RealtimeMeshBasicUsageActor.cpp │ ├── RealtimeMeshCubeGeneratorExample.h │ ├── RealtimeMeshLatentUpdateTestActor.cpp │ ├── RealtimeMeshStressTestActor.cpp │ └── RealtimeMeshUpdateTestActor.cpp ├── RealtimeMeshBufferLayoutTests.cpp ├── RealtimeMeshBuilderTests.cpp ├── RealtimeMeshElementTypeTests.cpp ├── RealtimeMeshMaterialIndicesTests.cpp ├── RealtimeMeshStreamAccessorTests.cpp ├── RealtimeMeshStreamConversionTests.cpp ├── RealtimeMeshStreamMemoryTest.cpp └── RealtimeMeshTests.cpp ├── Public ├── FunctionalTests │ ├── RealtimeMeshBasicUsageActor.h │ ├── RealtimeMeshLatentUpdateTestActor.h │ ├── RealtimeMeshStressTestActor.h │ └── RealtimeMeshUpdateTestActor.h └── RealtimeMeshTests.h └── RealtimeMeshTests.Build.cs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://www.paypal.me/Koderz'] 13 | -------------------------------------------------------------------------------- /.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 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Thank you to those of you who have contributed code to the project over the years! 2 | 3 | ### Special thanks to Moddingear who has submitted numerous code submissions, as well as assisted with the docs and helping the community in Discord 4 | [Moddingear](https://github.com/Moddingear) 5 | 6 | ### Contributors 7 | 8 | [alleysark](https://github.com/alleysark) 9 | [bhumphreys](https://github.com/bhumphreys) 10 | [caswal](https://github.com/caswal) 11 | [connorjak](https://github.com/connorjak) 12 | [davenye](https://github.com/davenye) 13 | [GlassBeaver](https://github.com/GlassBeaver) 14 | [gribuser](https://github.com/gribuser) 15 | [iblackcat](https://github.com/iblackcat) 16 | [JerryHyun](https://github.com/JerryHyun) 17 | [Kuldaen](https://github.com/Kuldaen) 18 | [manlok876](https://github.com/manlok876) 19 | [Maze-DK](https://github.com/Maze-DK) 20 | [michaeltchapman](https://github.com/michaeltchapman) 21 | [RaiaN](https://github.com/RaiaN) 22 | [RPG3D](https://github.com/RPG3D) 23 | [RumbleballTheReal](https://github.com/RumbleballTheReal) 24 | [SiggiG](https://github.com/SiggiG) 25 | [timdecode](https://github.com/timdecode) 26 | -------------------------------------------------------------------------------- /Config/DefaultRealtimeMeshComponent.ini: -------------------------------------------------------------------------------- 1 | [CoreRedirects] 2 | +FunctionRedirects=(OldName="/Script/RealtimeMeshComponent.RealtimeMeshBlueprintFunctionLibrary.MakeSectionGroupKey",NewName="/Script/RealtimeMeshComponent.RealtimeMeshBlueprintFunctionLibrary.MakeSectionGroupKeyIndexed") 3 | +FunctionRedirects=(OldName="/Script/RealtimeMeshComponent.RealtimeMeshBlueprintFunctionLibrary.MakeSectionKey",NewName="/Script/RealtimeMeshComponent.RealtimeMeshBlueprintFunctionLibrary.MakeSectionKeyIndexed") 4 | +ClassRedirects=(OldName="/Script/RealtimeMeshComponent.RealtimeMeshSimpleBasicShapeTools",NewName="/Script/RealtimeMeshComponent.RealtimeMeshBasicShapeTools") 5 | +PropertyRedirects=(OldName="/Script/RealtimeMeshComponent.RealtimeMeshComponent.RealtimeMeshReference",NewName="/Script/RealtimeMeshComponent.RealtimeMeshComponent.RealtimeMesh") -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | 10 | /CREDITS.md 11 | /README.md 12 | /Config/DefaultRealtimeMeshComponent.ini -------------------------------------------------------------------------------- /Content/Core/BP_RMC_PMCLike.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TriAxis-Games/RealtimeMeshComponent/826e65734744b9cda277bf895d57cd574440b53c/Content/Core/BP_RMC_PMCLike.uasset -------------------------------------------------------------------------------- /Content/Core/BP_RMC_SMCConversion.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TriAxis-Games/RealtimeMeshComponent/826e65734744b9cda277bf895d57cd574440b53c/Content/Core/BP_RMC_SMCConversion.uasset -------------------------------------------------------------------------------- /Content/Core/BP_RMC_SingleTriangle.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TriAxis-Games/RealtimeMeshComponent/826e65734744b9cda277bf895d57cd574440b53c/Content/Core/BP_RMC_SingleTriangle.uasset -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Runtime Mesh Component Community MIT License 2 | 3 | Copyright (c) 2016-2023 TriAxis Games L.L.C. 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 | 23 | 24 | Contributor License Agreement 25 | By contributing your code to Runtime Mesh Component you grant TriAxis Games L.L.C. 26 | a non-exclusive, irrevocable, worldwide, royalty-free, sublicenseable, 27 | transferable license under all of Your relevant intellectual property rights 28 | (including copyright, patent, and any other rights), to use, copy, prepare 29 | derivative works of, distribute and publicly perform and display the Contributions 30 | on any licensing terms, including without limitation: (a) open source licenses 31 | like the MIT license; and (b) binary, proprietary, or commercial licenses. Except 32 | for the licenses granted herein, You reserve all right, title, and interest in and 33 | to the Contribution. 34 | 35 | You confirm that you are able to grant us these rights. You represent that You are 36 | legally entitled to grant the above license. If Your employer has rights to 37 | intellectual property that You create, You represent that You have received 38 | permission to make the Contributions on behalf of that employer, or that Your 39 | employer has waived such rights for the Contributions. 40 | 41 | You represent that the Contributions are Your original works of authorship, and 42 | to Your knowledge, no other person claims, or has the right to claim, any right 43 | in any invention or patent related to the Contributions. You also represent that 44 | You are not legally obligated, whether by entering into an agreement or otherwise, 45 | in any way that conflicts with the terms of this license. 46 | 47 | TriAxis Games L.L.C. acknowledges that, except as explicitly described in this 48 | Agreement, any Contribution which you provide is on an "AS IS" BASIS, WITHOUT 49 | WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, 50 | WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 51 | MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RealtimeMeshComponent 5 for Unreal Engine 5 2 | **Join us on [Discord](https://discord.gg/KGvBBTv)** 3 | 4 | Documentation is coming soon, as well as Marketplace versions. 5 | --- 6 | **For information on installation, usage and everything else, please join Discord for now! More Docs and examples coming soon** 7 | 8 | The RuntimeMeshComponent or more commonly known as RMC, is a replacement to the ProceduralMeshComponent (aka PMC) found in UE4. The RMC is much more efficient, and carries many more features, while allowing for a much more fine-grained approach for advanced use cases, while being simple to use just like the PMC. It can handle any use case from simply loading models at runtime, to debug views, to modification of existing models all the way up to procedural generation of entire worlds! 9 | 10 | The RMC has been around for 6+ years and has an active community of users from individuals, to schools, to Fortune 500 companies, with many released projects. You can also find active support in our Discord server here: https://discord.gg/KGvBBTv 11 | 12 | 13 | --- 14 | This project is a labor of love. If you'd like to support the efforts, you may donate to me here: https://www.paypal.me/Koderz 15 | This is not a sale of the plugin, that will be available on the marketplace soon, this is simply another way to support the project if you are so generous. 16 | 17 | --- 18 | 19 | **Supported Engine Versions:** 20 | v5.0 supports engine versions 5.0+ 21 | 22 | ***All versions listed below here you can find through older versions of the component in the releases section*** 23 | v4.1 supports engine versions 4.23+ 24 | v4.0 supports engine versions 4.20+ 25 | v3.0 supports engine versions 4.17+ 26 | v2.0 supports engine versions 4.12+ 27 | v1.2 supports engine versions 4.10+ 28 | 29 | -------------------------------------------------------------------------------- /RealtimeMeshComponent.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 50, 4 | "VersionName": "5.2.3", 5 | "FriendlyName": "Realtime Mesh Component", 6 | "Description": "A component allowing for rendering and interacting with runtime generated geometry.", 7 | "Category": "Rendering", 8 | "CreatedBy": "Chris Conway (Koderz)", 9 | "CreatedByURL": "https://github.com/Koderz", 10 | "DocsURL": "https://realtimemesh.koderz.io/", 11 | "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/ad0b45a6a31e462aba7cf7c790a9c125", 12 | "SupportURL": "https://github.com/TriAxis-Games/RealtimeMeshComponent/issues", 13 | "EnabledByDefault": true, 14 | "CanContainContent": true, 15 | "IsBetaVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "RealtimeMeshComponent", 20 | "Type": "Runtime", 21 | "LoadingPhase": "PostConfigInit", 22 | "PlatformDenyList": [] 23 | }, 24 | { 25 | "Name": "RealtimeMeshTests", 26 | "Type": "Editor", 27 | "LoadingPhase": "Default", 28 | "PlatformDenyList": [] 29 | }, 30 | { 31 | "Name": "RealtimeMeshEditor", 32 | "Type": "Editor", 33 | "LoadingPhase": "Default", 34 | "PlatformDenyList": [] 35 | }, 36 | { 37 | "Name": "RealtimeMeshExamples", 38 | "Type": "Runtime", 39 | "LoadingPhase": "Default", 40 | "PlatformDenyList": [] 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TriAxis-Games/RealtimeMeshComponent/826e65734744b9cda277bf895d57cd574440b53c/Resources/Icon128.png -------------------------------------------------------------------------------- /Resources/MenuIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/InterfaceImpl/RealtimeMeshCollisionToolsInterfaceImpl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshComponent.h" 4 | #include "Core/RealtimeMeshCollision.h" 5 | #include "Core/RealtimeMeshModularFeatures.h" 6 | 7 | 8 | namespace RealtimeMesh 9 | { 10 | struct FRealtimeMeshCollisionToolsImpl_v0 : public IRealtimeMeshCollisionTools_v0 11 | { 12 | virtual bool FindCollisionUVRealtimeMesh(const struct FHitResult& Hit, int32 UVChannel, FVector2D& UV) const override 13 | { 14 | return URealtimeMeshCollisionTools::FindCollisionUVRealtimeMesh(Hit, UVChannel, UV); 15 | } 16 | virtual void CookConvexHull(FRealtimeMeshCollisionConvex& ConvexHull) const override 17 | { 18 | URealtimeMeshCollisionTools::CookConvexHull(ConvexHull); 19 | } 20 | virtual void CookComplexMesh(FRealtimeMeshCollisionMesh& CollisionMesh) const override 21 | { 22 | URealtimeMeshCollisionTools::CookComplexMesh(CollisionMesh); 23 | } 24 | }; 25 | 26 | // Register the interface 27 | TRealtimeMeshModularFeatureRegistration GRealtimeMeshCollisionToolsImpl_v0; 28 | } 29 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/InterfaceImpl/RealtimeMeshComponentInterfaceImpl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshComponent.h" 4 | #include "Core/RealtimeMeshComponentInterface.h" 5 | #include "Core/RealtimeMeshModularFeatures.h" 6 | 7 | 8 | namespace RealtimeMesh 9 | { 10 | struct FRealtimeMeshInterfaceImpl_v0 : public IRealtimeMeshInterface_v0 11 | { 12 | 13 | virtual bool IsRealtimeMesh(UMeshComponent* MeshComponent) const override 14 | { 15 | return MeshComponent->IsA(); 16 | } 17 | virtual UMeshComponent* CreateRealtimeMeshComponent(AActor* Owner, FName Name = NAME_None, EObjectFlags Flags = RF_NoFlags) const override 18 | { 19 | return NewObject(Owner, Name, Flags); 20 | } 21 | /*virtual TSharedRef GetRealtimeMesh(UMeshComponent* MeshComponent) const override 22 | { 23 | URealtimeMeshComponent* RealtimeMeshComp = CastChecked(MeshComponent); 24 | return nullptr; 25 | }*/ 26 | }; 27 | 28 | // Register the interface 29 | TRealtimeMeshModularFeatureRegistration GRealtimeMeshInterfaceImpl_v0; 30 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/InterfaceImpl/RealtimeMeshInterfaceImpl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "Core/RealtimeMeshInterface.h" 4 | 5 | namespace RealtimeMesh 6 | { 7 | struct FRealtimeMeshImpl_v0 : public IRealtimeMesh_v0 8 | { 9 | 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/InterfaceImpl/RealtimeMeshSimpleInterfaceImpl.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshComponent.h" 4 | #include "RealtimeMeshSimple.h" 5 | #include "Core/RealtimeMeshInterface.h" 6 | #include "Core/RealtimeMeshSimpleInterface.h" 7 | #include "RenderProxy/RealtimeMeshProxyCommandBatch.h" 8 | #include "Core/RealtimeMeshModularFeatures.h" 9 | #include "Data/RealtimeMeshUpdateBuilder.h" 10 | 11 | namespace RealtimeMesh 12 | { 13 | 14 | /*class FRealtimeMeshSimpleImpl_v0 : public IRealtimeMeshSimple_v0 15 | { 16 | TSharedRef Mesh; 17 | public: 18 | FRealtimeMeshSimpleImpl_v0(const TSharedRef& InMesh) 19 | : Mesh(InMesh) 20 | { } 21 | 22 | virtual TFuture CreateSectionGroup(const FRealtimeMeshSectionGroupKey& Key, const RealtimeMesh::FRealtimeMeshStreamSet& Streams, const FRealtimeMeshSectionGroupConfig& InConfig, bool bShouldAutoCreateSectionsForPolyGroups) override 23 | { 24 | return Mesh->CreateSectionGroup(Key, Streams, InConfig, bShouldAutoCreateSectionsForPolyGroups); 25 | } 26 | 27 | virtual TFuture CreateSectionGroup(const FRealtimeMeshSectionGroupKey& Key, RealtimeMesh::FRealtimeMeshStreamSet&& Streams, const FRealtimeMeshSectionGroupConfig& InConfig, bool bShouldAutoCreateSectionsForPolyGroups) override 28 | { 29 | return Mesh->CreateSectionGroup(Key, MoveTemp(Streams), InConfig, bShouldAutoCreateSectionsForPolyGroups); 30 | } 31 | 32 | virtual TFuture Reset(bool bShouldRecreate) override 33 | { 34 | FRealtimeMeshUpdateContext UpdateContext(Mesh); 35 | return Mesh->Reset(UpdateContext, bShouldRecreate); 36 | } 37 | }; 38 | 39 | 40 | 41 | struct FRealtimeMeshSimpleInterfaceImpl_v0 : public IRealtimeMeshSimpleInterface_v0 42 | { 43 | private: 44 | mutable TMap, TWeakPtr> MeshMap; 45 | 46 | void CleanupDanglingReferences() const 47 | { 48 | for (auto It = MeshMap.CreateIterator(); It; ++It) 49 | { 50 | if (!It.Value().IsValid()) 51 | { 52 | It.RemoveCurrent(); 53 | } 54 | } 55 | } 56 | public: 57 | virtual TSharedRef InitializeMesh(UMeshComponent* MeshComponent) const override 58 | { 59 | URealtimeMeshComponent* RealtimeMeshComp = CastChecked(MeshComponent); 60 | const URealtimeMeshSimple* MeshSimple = RealtimeMeshComp->InitializeRealtimeMesh(); 61 | const TSharedRef MeshData = MeshSimple->GetMeshAs(); 62 | TSharedRef MeshInterface = MakeShared(MeshData); 63 | MeshMap.Add(MeshData.ToSharedPtr(), MeshInterface); 64 | return MeshInterface; 65 | } 66 | 67 | virtual TSharedRef GetMesh(UMeshComponent* MeshComponent) const override 68 | { 69 | const URealtimeMeshComponent* RealtimeMeshComp = CastChecked(MeshComponent); 70 | const URealtimeMeshSimple* MeshSimple = RealtimeMeshComp->GetRealtimeMeshAs(); 71 | const TSharedRef MeshData = MeshSimple->GetMeshAs(); 72 | 73 | if (const auto* Ptr = MeshMap.Find(MeshData)) 74 | { 75 | if (const auto Pinned = Ptr->Pin()) 76 | { 77 | return Pinned.ToSharedRef(); 78 | } 79 | } 80 | 81 | TSharedRef MeshInterface = MakeShared(MeshData); 82 | MeshMap.Add(MeshData.ToSharedPtr(), MeshInterface); 83 | return MeshInterface; 84 | } 85 | }; 86 | 87 | // Register the interface 88 | TRealtimeMeshModularFeatureRegistration GRealtimeMeshSimpleInterfaceImpl_v0;*/ 89 | } 90 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/Mesh/RealtimeMeshCardRepresentation.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "Mesh/RealtimeMeshCardRepresentation.h" 5 | 6 | 7 | FRealtimeMeshCardRepresentation::FRealtimeMeshCardRepresentation() 8 | : Bounds(ForceInit) 9 | , bMostlyTwoSided(false) 10 | { 11 | } 12 | 13 | FRealtimeMeshCardRepresentation::FRealtimeMeshCardRepresentation(const FCardRepresentationData& Src) 14 | : Bounds(Src.MeshCardsBuildData.Bounds) 15 | #if RMC_ENGINE_ABOVE_5_2 16 | , bMostlyTwoSided(Src.MeshCardsBuildData.bMostlyTwoSided) 17 | #endif 18 | , CardBuildData(Src.MeshCardsBuildData.CardBuildData) 19 | { 20 | } 21 | 22 | FRealtimeMeshCardRepresentation::~FRealtimeMeshCardRepresentation() 23 | { 24 | } 25 | 26 | bool FRealtimeMeshCardRepresentation::IsValid() const 27 | { 28 | return Bounds.IsValid && CardBuildData.Num() > 0; 29 | } 30 | 31 | 32 | void FRealtimeMeshCardRepresentation::GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) const 33 | { 34 | CumulativeResourceSize.AddDedicatedSystemMemoryBytes(sizeof(*this)); 35 | CumulativeResourceSize.AddDedicatedSystemMemoryBytes(CardBuildData.GetAllocatedSize()); 36 | } 37 | 38 | SIZE_T FRealtimeMeshCardRepresentation::GetResourceSizeBytes() const 39 | { 40 | FResourceSizeEx ResSize; 41 | GetResourceSizeEx(ResSize); 42 | return ResSize.GetTotalMemoryBytes(); 43 | } 44 | 45 | void FRealtimeMeshCardRepresentation::Serialize(FArchive& Ar, UObject* Owner) 46 | { 47 | Ar << Bounds; 48 | Ar << bMostlyTwoSided; 49 | Ar << CardBuildData; 50 | } 51 | 52 | 53 | std::atomic FRealtimeMeshCardRepresentation::NextCardRepresentationId { TNumericLimits().Max() }; 54 | 55 | FCardRepresentationData FRealtimeMeshCardRepresentation::CreateRenderingData() const 56 | { 57 | FCardRepresentationData RenderingData; 58 | RenderingData.CardRepresentationDataId.Value = NextCardRepresentationId--; 59 | RenderingData.MeshCardsBuildData.Bounds = Bounds; 60 | #if RMC_ENGINE_ABOVE_5_2 61 | RenderingData.MeshCardsBuildData.bMostlyTwoSided = bMostlyTwoSided; 62 | #endif 63 | RenderingData.MeshCardsBuildData.CardBuildData = CardBuildData; 64 | return RenderingData; 65 | } 66 | 67 | FCardRepresentationData FRealtimeMeshCardRepresentation::MoveToRenderingData() 68 | { 69 | FCardRepresentationData RenderingData; 70 | RenderingData.CardRepresentationDataId.Value = NextCardRepresentationId--; 71 | RenderingData.MeshCardsBuildData.Bounds = MoveTemp(Bounds); 72 | #if RMC_ENGINE_ABOVE_5_2 73 | RenderingData.MeshCardsBuildData.bMostlyTwoSided = MoveTemp(bMostlyTwoSided); 74 | #endif 75 | RenderingData.MeshCardsBuildData.CardBuildData = MoveTemp(CardBuildData); 76 | return RenderingData; 77 | } 78 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/Mesh/RealtimeMeshDistanceField.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "Mesh/RealtimeMeshDistanceField.h" 5 | 6 | 7 | FRealtimeMeshDistanceField::FRealtimeMeshDistanceField() 8 | : Bounds(ForceInit) 9 | , bMostlyTwoSided(false) 10 | { 11 | } 12 | 13 | FRealtimeMeshDistanceField::FRealtimeMeshDistanceField(const FDistanceFieldVolumeData& Src) 14 | : Mips(Src.Mips) 15 | , AlwaysLoadedMip(Src.AlwaysLoadedMip) 16 | , StreamableMips(Src.StreamableMips) 17 | , Bounds(Src.LocalSpaceMeshBounds) 18 | , bMostlyTwoSided(Src.bMostlyTwoSided) 19 | { 20 | } 21 | 22 | FRealtimeMeshDistanceField::~FRealtimeMeshDistanceField() 23 | { 24 | } 25 | 26 | void FRealtimeMeshDistanceField::GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) const 27 | { 28 | CumulativeResourceSize.AddDedicatedSystemMemoryBytes(sizeof(*this)); 29 | 30 | for (const FSparseDistanceFieldMip& Mip : Mips) 31 | { 32 | Mip.GetResourceSizeEx(CumulativeResourceSize); 33 | } 34 | 35 | CumulativeResourceSize.AddDedicatedSystemMemoryBytes(AlwaysLoadedMip.GetAllocatedSize()); 36 | } 37 | 38 | SIZE_T FRealtimeMeshDistanceField::GetResourceSizeBytes() const 39 | { 40 | FResourceSizeEx ResSize; 41 | GetResourceSizeEx(ResSize); 42 | return ResSize.GetTotalMemoryBytes(); 43 | } 44 | 45 | 46 | struct FSparseDistanceFieldMip_OLD 47 | { 48 | FIntVector IndirectionDimensions; 49 | int32 NumDistanceFieldBricks; 50 | FVector VolumeToVirtualUVScale; 51 | FVector VolumeToVirtualUVAdd; 52 | FVector2D DistanceFieldToVolumeScaleBias; 53 | uint32 BulkOffset; 54 | uint32 BulkSize; 55 | 56 | friend FArchive& operator<<(FArchive& Ar, FSparseDistanceFieldMip_OLD& Mip) 57 | { 58 | Ar << Mip.IndirectionDimensions << Mip.NumDistanceFieldBricks << Mip.VolumeToVirtualUVScale << Mip.VolumeToVirtualUVAdd << Mip.DistanceFieldToVolumeScaleBias << Mip.BulkOffset << Mip.BulkSize; 59 | return Ar; 60 | } 61 | }; 62 | 63 | 64 | void FRealtimeMeshDistanceField::Serialize(FArchive& Ar, UObject* Owner) 65 | { 66 | Ar << Bounds; 67 | Ar << bMostlyTwoSided; 68 | 69 | #if RMC_ENGINE_ABOVE_5_4 70 | if (Ar.EngineVer().GetMajor() == 5 && Ar.EngineVer().GetMinor() < 4) 71 | { 72 | TStaticArray OldMips; 73 | Ar << OldMips; 74 | 75 | for (int32 Index = 0; Index < Mips.Num(); Index++) 76 | { 77 | Mips[Index].IndirectionDimensions = OldMips[Index].IndirectionDimensions; 78 | Mips[Index].NumDistanceFieldBricks = OldMips[Index].NumDistanceFieldBricks; 79 | Mips[Index].VolumeToVirtualUVScale = FVector3f(OldMips[Index].VolumeToVirtualUVScale); 80 | Mips[Index].VolumeToVirtualUVAdd = FVector3f(OldMips[Index].VolumeToVirtualUVAdd); 81 | Mips[Index].DistanceFieldToVolumeScaleBias = FVector2f(OldMips[Index].DistanceFieldToVolumeScaleBias); 82 | Mips[Index].BulkOffset = OldMips[Index].BulkOffset; 83 | Mips[Index].BulkSize = OldMips[Index].BulkSize; 84 | } 85 | } 86 | else 87 | #endif 88 | { 89 | Ar << Mips; 90 | } 91 | 92 | 93 | 94 | Ar << AlwaysLoadedMip; 95 | StreamableMips.Serialize(Ar, Owner, 0); 96 | } 97 | 98 | bool FRealtimeMeshDistanceField::IsValid() const 99 | { 100 | return Mips[0].IndirectionDimensions.GetMax() > 0; 101 | } 102 | 103 | FDistanceFieldVolumeData FRealtimeMeshDistanceField::CreateRenderingData() const 104 | { 105 | FDistanceFieldVolumeData RenderingData; 106 | #if RMC_ENGINE_ABOVE_5_4 107 | RenderingData.LocalSpaceMeshBounds = Bounds; 108 | #else 109 | RenderingData.LocalSpaceMeshBounds = FBox(Bounds); 110 | #endif 111 | RenderingData.bMostlyTwoSided = bMostlyTwoSided; 112 | RenderingData.Mips = Mips; 113 | RenderingData.AlwaysLoadedMip = AlwaysLoadedMip; 114 | RenderingData.StreamableMips = StreamableMips; 115 | return RenderingData; 116 | } 117 | 118 | FDistanceFieldVolumeData FRealtimeMeshDistanceField::MoveToRenderingData() 119 | { 120 | FDistanceFieldVolumeData RenderingData; 121 | #if RMC_ENGINE_ABOVE_5_4 122 | RenderingData.LocalSpaceMeshBounds = MoveTemp(Bounds); 123 | #else 124 | RenderingData.LocalSpaceMeshBounds = FBox(Bounds); 125 | Bounds.Init(); 126 | #endif 127 | RenderingData.bMostlyTwoSided = MoveTemp(bMostlyTwoSided); 128 | RenderingData.Mips = MoveTemp(Mips); 129 | RenderingData.AlwaysLoadedMip = MoveTemp(AlwaysLoadedMip); 130 | RenderingData.StreamableMips = MoveTemp(StreamableMips); 131 | return RenderingData; 132 | } 133 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RDNInterface/RealtimeDataNetworkInterface.h: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | class IRealtimeDataNetworkingChannel 5 | { 6 | virtual ~IRealtimeDataNetworkingChannel() {} 7 | }; 8 | 9 | 10 | 11 | 12 | 13 | 14 | class FRealtimeDataNetworkingFeature : public IModularFeature 15 | { 16 | private: 17 | static FName GetModularFeatureName() { return FName(TEXT("RealtimeDataNetworking_v0")); } 18 | 19 | public: 20 | static bool IsAvailable() { return IModularFeatures::Get().IsModularFeatureAvailable(GetModularFeatureName()); } 21 | static FRealtimeDataNetworkingFeature* Get() { return IsAvailable() ? &IModularFeatures::Get().GetModularFeature(GetModularFeatureName()) : nullptr; } 22 | static FRealtimeDataNetworkingFeature& GetChecked() { return IModularFeatures::Get().GetModularFeature(GetModularFeatureName()); } 23 | 24 | 25 | IRealtimeDataNetworkingChannel* CreateChannel(FName Name); 26 | 27 | DECLARE_MULTICAST_DELEGATE_OneParam(FOnSectionRemoved, const TSharedRef&) 28 | virtual FOnSectionRemoved& OnSectionRemoved() = 0; 29 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshComponentModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshComponentModule.h" 4 | #include "Serialization/CustomVersion.h" 5 | #include "Interfaces/IPluginManager.h" 6 | #include "ShaderCore.h" 7 | #include "RealtimeMeshCore.h" 8 | 9 | 10 | // Register the custom version with core 11 | FCustomVersionRegistration GRegisterRealtimeMeshCustomVersion(RealtimeMesh::FRealtimeMeshVersion::GUID, RealtimeMesh::FRealtimeMeshVersion::LatestVersion, TEXT("RealtimeMesh")); 12 | 13 | 14 | class FRealtimeMeshComponentPlugin : public IRealtimeMeshComponentPlugin 15 | { 16 | /** IModuleInterface implementation */ 17 | virtual void StartupModule() override; 18 | virtual void ShutdownModule() override; 19 | }; 20 | 21 | IMPLEMENT_MODULE(FRealtimeMeshComponentPlugin, RealtimeMeshComponent) 22 | 23 | 24 | void FRealtimeMeshComponentPlugin::StartupModule() 25 | { 26 | const FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("RealtimeMeshComponent"))->GetBaseDir(), TEXT("Shaders")); 27 | if (FPaths::DirectoryExists(PluginShaderDir)) 28 | { 29 | AddShaderSourceDirectoryMapping(TEXT("/Plugin/RealtimeMeshComponent"), PluginShaderDir); 30 | } 31 | } 32 | 33 | void FRealtimeMeshComponentPlugin::ShutdownModule() 34 | { 35 | } 36 | 37 | DEFINE_LOG_CATEGORY(LogRealtimeMesh); 38 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshCore.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshCore.h" 4 | 5 | #include "RealtimeMesh.h" 6 | #include "Data/RealtimeMeshSectionGroup.h" 7 | #include "Data/RealtimeMeshLOD.h" 8 | #include "Data/RealtimeMeshSection.h" 9 | #include "Data/RealtimeMeshData.h" 10 | #include "Core/RealtimeMeshKeys.h" 11 | #include "Data/RealtimeMeshUpdateBuilder.h" 12 | #include "RenderProxy/RealtimeMeshLODProxy.h" 13 | #include "RenderProxy/RealtimeMeshProxy.h" 14 | #include "RenderProxy/RealtimeMeshSectionGroupProxy.h" 15 | #include "RenderProxy/RealtimeMeshSectionProxy.h" 16 | #include "RenderProxy/RealtimeMeshVertexFactory.h" 17 | 18 | 19 | enum class ERealtimeMeshStreamType_OLD 20 | { 21 | Unknown, 22 | Vertex, 23 | Index, 24 | }; 25 | static_assert(sizeof(ERealtimeMeshStreamType_OLD) == 4); 26 | 27 | FArchive& operator<<(FArchive& Ar, FRealtimeMeshStreamKey& Key) 28 | { 29 | Ar << Key.StreamName; 30 | 31 | if (Ar.CustomVer(RealtimeMesh::FRealtimeMeshVersion::GUID) < RealtimeMesh::FRealtimeMeshVersion::StreamKeySizeChanged) 32 | { 33 | check(Ar.IsLoading()); 34 | ERealtimeMeshStreamType_OLD OldKey; 35 | Ar << OldKey; 36 | Key.StreamType = static_cast(OldKey); 37 | } 38 | else 39 | { 40 | Ar << Key.StreamType; 41 | } 42 | 43 | return Ar; 44 | } 45 | 46 | namespace RealtimeMesh 47 | { 48 | void FRealtimeMeshSharedResources::SetOwnerMesh(URealtimeMesh* InOwningMesh, const FRealtimeMeshRef& InOwner) 49 | { 50 | OwningMesh = InOwningMesh, Owner = InOwner; 51 | } 52 | 53 | ERHIFeatureLevel::Type FRealtimeMeshSharedResources::GetFeatureLevel() const 54 | { 55 | if (const auto ProxyPinned = Proxy.Pin()) { return ProxyPinned->GetRHIFeatureLevel(); } 56 | return GMaxRHIFeatureLevel; 57 | } 58 | 59 | FRealtimeMeshUpdateStateRef FRealtimeMeshSharedResources::CreateUpdateState() const 60 | { 61 | return MakeShared(); 62 | } 63 | 64 | FRealtimeMeshVertexFactoryRef FRealtimeMeshSharedResources::CreateVertexFactory() const 65 | { 66 | return MakeShareable(new FRealtimeMeshLocalVertexFactory(GetFeatureLevel()), FRealtimeMeshRenderThreadDeleter()); 67 | } 68 | 69 | FRealtimeMeshSectionProxyRef FRealtimeMeshSharedResources::CreateSectionProxy(const FRealtimeMeshSectionKey& InKey) const 70 | { 71 | return MakeShareable(new FRealtimeMeshSectionProxy(ConstCastSharedRef(this->AsShared()), InKey), 72 | FRealtimeMeshRenderThreadDeleter()); 73 | } 74 | 75 | TSharedRef FRealtimeMeshSharedResources::CreateSectionGroupProxy(const FRealtimeMeshSectionGroupKey& InKey) const 76 | { 77 | return MakeShareable(new FRealtimeMeshSectionGroupProxy(ConstCastSharedRef(this->AsShared()), InKey), 78 | FRealtimeMeshRenderThreadDeleter()); 79 | } 80 | 81 | TSharedRef FRealtimeMeshSharedResources::CreateLODProxy(const FRealtimeMeshLODKey& InKey) const 82 | { 83 | return MakeShareable(new FRealtimeMeshLODProxy(ConstCastSharedRef(this->AsShared()), InKey), 84 | FRealtimeMeshRenderThreadDeleter()); 85 | } 86 | 87 | TSharedRef FRealtimeMeshSharedResources::CreateRealtimeMeshProxy() const 88 | { 89 | return MakeShareable(new FRealtimeMeshProxy(ConstCastSharedRef(this->AsShared())), 90 | FRealtimeMeshRenderThreadDeleter()); 91 | } 92 | 93 | FRealtimeMeshSectionRef FRealtimeMeshSharedResources::CreateSection(const FRealtimeMeshSectionKey& InKey) const 94 | { 95 | return MakeShared(ConstCastSharedRef(this->AsShared()), InKey); 96 | } 97 | 98 | TSharedRef FRealtimeMeshSharedResources::CreateSectionGroup(const FRealtimeMeshSectionGroupKey& InKey) const 99 | { 100 | return MakeShared(ConstCastSharedRef(this->AsShared()), InKey); 101 | } 102 | 103 | FRealtimeMeshLODRef FRealtimeMeshSharedResources::CreateLOD(const FRealtimeMeshLODKey& InKey) const 104 | { 105 | return MakeShared(ConstCastSharedRef(this->AsShared()), InKey); 106 | } 107 | 108 | FRealtimeMeshRef FRealtimeMeshSharedResources::CreateRealtimeMesh() const 109 | { 110 | check(false && "Cannot create abstract FRealtimeMesh"); 111 | return MakeShareable(static_cast(nullptr)); 112 | } 113 | 114 | FRealtimeMeshSharedResourcesRef FRealtimeMeshSharedResources::CreateSharedResources() const 115 | { 116 | return MakeShared(); 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshGuard.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshGuard.h" 4 | 5 | #include "Data/RealtimeMeshData.h" 6 | #include "Data/RealtimeMeshShared.h" 7 | 8 | namespace RealtimeMesh 9 | { 10 | namespace Threading::Private 11 | { 12 | static thread_local TMap ActiveThreadLocks; 13 | } 14 | 15 | void FRealtimeMeshGuard::ReadLock() 16 | { 17 | Threading::Private::FRealtimeMeshGuardThreadState& State = Threading::Private::ActiveThreadLocks.FindOrAdd(this); 18 | State.ReadDepth++; 19 | 20 | const uint32 ThisThreadId = FPlatformTLS::GetCurrentThreadId(); 21 | 22 | // If we're already writing then don't attempt the lock, we already have exclusive access 23 | if (CurrentWriterThreadId.Load() != ThisThreadId && State.ReadDepth == 1) 24 | { 25 | InnerLock.ReadLock(); 26 | } 27 | } 28 | 29 | void FRealtimeMeshGuard::WriteLock() 30 | { 31 | Threading::Private::FRealtimeMeshGuardThreadState& State = Threading::Private::ActiveThreadLocks.FindOrAdd(this); 32 | State.WriteDepth++; 33 | 34 | const uint32 ThisThreadId = FPlatformTLS::GetCurrentThreadId(); 35 | 36 | if (CurrentWriterThreadId.Load() != ThisThreadId) 37 | { 38 | // Ensure we don't already own a read lock where we'd be trying to upgrade the lock 39 | check(State.ReadDepth == 0); 40 | 41 | InnerLock.WriteLock(); 42 | CurrentWriterThreadId.Store(ThisThreadId); 43 | } 44 | } 45 | 46 | void FRealtimeMeshGuard::ReadUnlock() 47 | { 48 | checkf(Threading::Private::ActiveThreadLocks.Contains(this), TEXT("ReadUnlock called when the thread doesn't hold the lock.")); 49 | Threading::Private::FRealtimeMeshGuardThreadState& State = Threading::Private::ActiveThreadLocks.FindChecked(this); 50 | checkf(State.ReadDepth > 0, TEXT("ReadUnlock called when the thread doesn't hold the lock.")); 51 | State.ReadDepth--; 52 | 53 | const uint32 ThisThreadId = FPlatformTLS::GetCurrentThreadId(); 54 | 55 | if (CurrentWriterThreadId.Load() != ThisThreadId && State.ReadDepth == 0) 56 | { 57 | InnerLock.ReadUnlock(); 58 | } 59 | 60 | if (State.ReadDepth == 0 && State.WriteDepth == 0) 61 | { 62 | Threading::Private::ActiveThreadLocks.Remove(this); 63 | } 64 | } 65 | 66 | void FRealtimeMeshGuard::WriteUnlock() 67 | { 68 | const uint32 ThisThreadId = FPlatformTLS::GetCurrentThreadId(); 69 | 70 | if (CurrentWriterThreadId.Load() == ThisThreadId) 71 | { 72 | checkf(Threading::Private::ActiveThreadLocks.Contains(this), TEXT("WriteUnlock called when the thread doesn't hold the lock.")); 73 | Threading::Private::FRealtimeMeshGuardThreadState& State = Threading::Private::ActiveThreadLocks.FindChecked(this); 74 | checkf(State.WriteDepth > 0, TEXT("WriteUnlock called when the thread doesn't hold the lock.")); 75 | State.WriteDepth--; 76 | 77 | if (State.WriteDepth == 0) 78 | { 79 | CurrentWriterThreadId.Store(0); 80 | InnerLock.WriteUnlock(); 81 | } 82 | 83 | if (State.ReadDepth == 0 && State.WriteDepth == 0) 84 | { 85 | Threading::Private::ActiveThreadLocks.Remove(this); 86 | } 87 | } 88 | else 89 | { 90 | checkf(false, TEXT("WriteUnlock called when the thread doesn't hold the lock.")); 91 | } 92 | } 93 | 94 | bool FRealtimeMeshGuard::IsWriteLocked() 95 | { 96 | Threading::Private::FRealtimeMeshGuardThreadState& State = Threading::Private::ActiveThreadLocks.FindOrAdd(this); 97 | const uint32 ThisThreadId = FPlatformTLS::GetCurrentThreadId(); 98 | return State.WriteDepth > 0 && CurrentWriterThreadId.Load() == ThisThreadId; 99 | } 100 | 101 | bool FRealtimeMeshGuard::IsReadLocked() 102 | { 103 | Threading::Private::FRealtimeMeshGuardThreadState& State = Threading::Private::ActiveThreadLocks.FindOrAdd(this); 104 | const uint32 ThisThreadId = FPlatformTLS::GetCurrentThreadId(); 105 | return State.ReadDepth > 0 || (State.WriteDepth > 0 && CurrentWriterThreadId.Load() == ThisThreadId); 106 | } 107 | 108 | 109 | FRealtimeMeshScopeGuardRead::FRealtimeMeshScopeGuardRead(FRealtimeMeshGuard& InGuard, bool bLockImmediately) 110 | : Guard(InGuard) 111 | , bIsLocked(false) 112 | { 113 | if (bLockImmediately) 114 | { 115 | Guard.ReadLock(); 116 | bIsLocked = true; 117 | } 118 | } 119 | 120 | FRealtimeMeshScopeGuardRead::FRealtimeMeshScopeGuardRead(const FRealtimeMeshSharedResourcesRef& InSharedResources, bool bLockImmediately) 121 | : FRealtimeMeshScopeGuardRead(InSharedResources->GetGuard(), bLockImmediately) 122 | { } 123 | 124 | FRealtimeMeshScopeGuardRead::FRealtimeMeshScopeGuardRead(const FRealtimeMeshPtr& InMesh, bool bLockImmediately) 125 | : FRealtimeMeshScopeGuardRead(InMesh->GetSharedResources(), bLockImmediately) 126 | { } 127 | 128 | 129 | FRealtimeMeshScopeGuardWrite::FRealtimeMeshScopeGuardWrite(FRealtimeMeshGuard& InGuard, bool bLockImmediately) 130 | : Guard(InGuard) 131 | , bIsLocked(false) 132 | { 133 | if (bLockImmediately) 134 | { 135 | Guard.WriteLock(); 136 | bIsLocked = true; 137 | } 138 | } 139 | 140 | FRealtimeMeshScopeGuardWrite::FRealtimeMeshScopeGuardWrite(const FRealtimeMeshSharedResourcesRef& InSharedResources, bool bLockImmediately) 141 | : FRealtimeMeshScopeGuardWrite(InSharedResources->GetGuard(), bLockImmediately) 142 | { } 143 | 144 | FRealtimeMeshScopeGuardWrite::FRealtimeMeshScopeGuardWrite(const FRealtimeMeshPtr& InMesh, bool bLockImmediately) 145 | : FRealtimeMeshScopeGuardWrite(InMesh->GetSharedResources(), bLockImmediately) 146 | { } 147 | 148 | FRealtimeMeshScopeGuardWriteCheck::FRealtimeMeshScopeGuardWriteCheck(const FRealtimeMeshSharedResourcesRef& SharedResources): FRealtimeMeshScopeGuardWriteCheck(SharedResources->GetGuard()) 149 | { 150 | } 151 | 152 | FRealtimeMeshScopeGuardReadCheck::FRealtimeMeshScopeGuardReadCheck(const FRealtimeMeshSharedResourcesRef& SharedResources): FRealtimeMeshScopeGuardReadCheck(SharedResources->GetGuard()) 153 | { 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshLibrary.h" 4 | #include "RealtimeMeshSimple.h" 5 | #include "Core/RealtimeMeshDataStream.h" 6 | 7 | using namespace RealtimeMesh; 8 | 9 | FRealtimeMeshLODKey URealtimeMeshBlueprintFunctionLibrary::Conv_IntToRealtimeMeshLODKey(int32 LODIndex) 10 | { 11 | return FRealtimeMeshLODKey(LODIndex); 12 | } 13 | 14 | FRealtimeMeshLODKey URealtimeMeshBlueprintFunctionLibrary::MakeLODKey(int32 LODIndex) 15 | { 16 | return FRealtimeMeshLODKey(LODIndex); 17 | } 18 | 19 | FRealtimeMeshSectionGroupKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionGroupKeyUnique(const FRealtimeMeshLODKey& LODKey) 20 | { 21 | return FRealtimeMeshSectionGroupKey::CreateUnique(LODKey); 22 | } 23 | 24 | FRealtimeMeshSectionGroupKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionGroupKeyIndexed(const FRealtimeMeshLODKey& LODKey, int32 SectionGroupIndex) 25 | { 26 | return FRealtimeMeshSectionGroupKey::Create(LODKey, SectionGroupIndex); 27 | } 28 | 29 | FRealtimeMeshSectionGroupKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionGroupKeyNamed(const FRealtimeMeshLODKey& LODKey, FName GroupName) 30 | { 31 | return FRealtimeMeshSectionGroupKey::Create(LODKey, GroupName); 32 | } 33 | 34 | FRealtimeMeshSectionKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionKeyUnique(const FRealtimeMeshSectionGroupKey& SectionGroupKey) 35 | { 36 | return FRealtimeMeshSectionKey::CreateUnique(SectionGroupKey); 37 | } 38 | 39 | FRealtimeMeshSectionKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionKeyIndexed(const FRealtimeMeshSectionGroupKey& SectionGroupKey, int32 SectionIndex) 40 | { 41 | return FRealtimeMeshSectionKey::Create(SectionGroupKey, SectionIndex); 42 | } 43 | 44 | FRealtimeMeshSectionKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionKeyNamed(const FRealtimeMeshSectionGroupKey& SectionGroupKey, FName SectionName) 45 | { 46 | return FRealtimeMeshSectionKey::Create(SectionGroupKey, SectionName); 47 | } 48 | 49 | FRealtimeMeshSectionKey URealtimeMeshBlueprintFunctionLibrary::MakeSectionKeyForPolygonGroup(const FRealtimeMeshSectionGroupKey& SectionGroupKey, int32 PolygonGroup) 50 | { 51 | return FRealtimeMeshSectionKey::CreateForPolyGroup(SectionGroupKey, PolygonGroup); 52 | } 53 | 54 | void URealtimeMeshBlueprintFunctionLibrary::BreakLODKey(const FRealtimeMeshLODKey& LODKey, int32& LODIndex) 55 | { 56 | LODIndex = LODKey.Index(); 57 | } 58 | 59 | FRealtimeMeshStreamRange URealtimeMeshBlueprintFunctionLibrary::MakeStreamRange(int32 VerticesLowerInclusive, 60 | int32 VerticesUpperExclusive, int32 IndicesLowerInclusive, int32 IndicesUpperExclusive) 61 | { 62 | return FRealtimeMeshStreamRange(VerticesLowerInclusive, VerticesUpperExclusive, IndicesLowerInclusive, IndicesUpperExclusive); 63 | } 64 | 65 | FRealtimeMeshStreamKey URealtimeMeshBlueprintFunctionLibrary::MakeStreamKey(ERealtimeMeshStreamType StreamType, FName StreamName) 66 | { 67 | return FRealtimeMeshStreamKey(StreamType, StreamName); 68 | } 69 | 70 | FRealtimeMeshStreamKey URealtimeMeshBlueprintFunctionLibrary::GetCommonStreamKey(ERealtimeMeshCommonStream StreamType) 71 | { 72 | switch(StreamType) 73 | { 74 | case ERealtimeMeshCommonStream::Position: 75 | return FRealtimeMeshStreams::Position; 76 | case ERealtimeMeshCommonStream::Tangents: 77 | return FRealtimeMeshStreams::Tangents; 78 | case ERealtimeMeshCommonStream::TexCoords: 79 | return FRealtimeMeshStreams::TexCoords; 80 | case ERealtimeMeshCommonStream::Colors: 81 | return FRealtimeMeshStreams::Color; 82 | case ERealtimeMeshCommonStream::Triangles: 83 | return FRealtimeMeshStreams::Triangles; 84 | case ERealtimeMeshCommonStream::DepthOnlyTriangles: 85 | return FRealtimeMeshStreams::DepthOnlyTriangles; 86 | case ERealtimeMeshCommonStream::PolyGroups: 87 | return FRealtimeMeshStreams::PolyGroups; 88 | case ERealtimeMeshCommonStream::DepthOnlyPolyGroups: 89 | return FRealtimeMeshStreams::DepthOnlyPolyGroups; 90 | default: 91 | return FRealtimeMeshStreamKey(); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshSceneViewExtension.cpp: -------------------------------------------------------------------------------- 1 | // // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "RealtimeMeshSceneViewExtension.h" 5 | 6 | #include "RealtimeMeshComponentModule.h" 7 | #include "RenderGraphBuilder.h" 8 | #include "RenderProxy/RealtimeMeshProxy.h" 9 | 10 | TSet FRealtimeMeshSceneViewExtension::ActiveProxies; 11 | 12 | void FRealtimeMeshSceneViewExtension::RegisterProxy(const RealtimeMesh::FRealtimeMeshProxyPtr& Proxy) 13 | { 14 | ActiveProxies.Add(Proxy); 15 | } 16 | 17 | void FRealtimeMeshSceneViewExtension::UnregisterProxy(const RealtimeMesh::FRealtimeMeshProxyPtr& Proxy) 18 | { 19 | ActiveProxies.Remove(Proxy); 20 | } 21 | 22 | void FRealtimeMeshSceneViewExtension::SetupViewFamily(FSceneViewFamily& InViewFamily) 23 | { 24 | } 25 | 26 | void FRealtimeMeshSceneViewExtension::SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) 27 | { 28 | } 29 | 30 | void FRealtimeMeshSceneViewExtension::BeginRenderViewFamily(FSceneViewFamily& InViewFamily) 31 | { 32 | } 33 | 34 | void FRealtimeMeshSceneViewExtension::PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) 35 | { 36 | for (auto It = ActiveProxies.CreateIterator(); It; ++It) 37 | { 38 | if (auto Pinned = It->Pin()) 39 | { 40 | // We only process commands if there are no components using it, this allows syncing to 41 | // the render thread, but without causing issues with existing components. 42 | if (!Pinned->HasAnyReferencingComponents()) 43 | { 44 | Pinned->ProcessCommands(GraphBuilder.RHICmdList); 45 | } 46 | } 47 | else 48 | { 49 | It.RemoveCurrent(); 50 | } 51 | } 52 | } 53 | 54 | void FRealtimeMeshSceneViewExtension::PostRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) 55 | { 56 | } 57 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshSceneViewExtension.h: -------------------------------------------------------------------------------- 1 | // // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "RealtimeMeshCore.h" 7 | #include "SceneViewExtension.h" 8 | 9 | class UWorld; 10 | 11 | /* 12 | * We use this extension to manage exactly when updates are processed to the RMC. 13 | */ 14 | class FRealtimeMeshSceneViewExtension final : public FWorldSceneViewExtension 15 | { 16 | private: 17 | static TSet ActiveProxies; 18 | public: 19 | static void RegisterProxy(const RealtimeMesh::FRealtimeMeshProxyPtr& Proxy); 20 | static void UnregisterProxy(const RealtimeMesh::FRealtimeMeshProxyPtr& Proxy); 21 | 22 | FRealtimeMeshSceneViewExtension(const FAutoRegister& AutoReg, UWorld* InWorld) 23 | : FWorldSceneViewExtension(AutoReg, InWorld) 24 | { 25 | } 26 | protected: 27 | 28 | virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override; 29 | virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override; 30 | virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override; 31 | 32 | virtual void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override; 33 | virtual void PostRenderView_RenderThread(FRDGBuilder& GraphBuilder, FSceneView& InView) override; 34 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshShared.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshSubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshSubsystem.h" 4 | #include "RealtimeMeshActor.h" 5 | #include "RealtimeMeshSceneViewExtension.h" 6 | #include "SceneViewExtension.h" 7 | #include "Engine/Engine.h" 8 | #include "Engine/Level.h" 9 | #include "Misc/LazySingleton.h" 10 | 11 | 12 | URealtimeMeshSubsystem::URealtimeMeshSubsystem() 13 | : bInitialized(false) 14 | { 15 | } 16 | 17 | bool URealtimeMeshSubsystem::ShouldCreateSubsystem(UObject* Outer) const 18 | { 19 | return true; 20 | } 21 | 22 | void URealtimeMeshSubsystem::Initialize(FSubsystemCollectionBase& Collection) 23 | { 24 | Super::Initialize(Collection); 25 | bInitialized = true; 26 | SceneViewExtension = FSceneViewExtensions::NewExtension(GetWorld()); 27 | } 28 | 29 | void URealtimeMeshSubsystem::Deinitialize() 30 | { 31 | SceneViewExtension.Reset(); 32 | bInitialized = false; 33 | Super::Deinitialize(); 34 | } 35 | 36 | bool URealtimeMeshSubsystem::IsTickable() const 37 | { 38 | return ActiveGeneratedActors.Num() > 0; 39 | } 40 | 41 | bool URealtimeMeshSubsystem::IsTickableInEditor() const 42 | { 43 | return true; 44 | } 45 | 46 | void URealtimeMeshSubsystem::Tick(float DeltaTime) 47 | { 48 | Super::Tick(DeltaTime); 49 | 50 | // Rebuild all valid generated actors, if necessary 51 | for (TWeakObjectPtr& Actor : ActiveGeneratedActors) 52 | { 53 | if (Actor.IsValid() && IsValid(Actor->GetLevel())) 54 | { 55 | Actor->ExecuteRebuildGeneratedMeshIfPending(); 56 | } 57 | } 58 | } 59 | 60 | TStatId URealtimeMeshSubsystem::GetStatId() const 61 | { 62 | RETURN_QUICK_DECLARE_CYCLE_STAT(URealtimeMeshSubsystem, STATGROUP_Tickables); 63 | } 64 | 65 | bool URealtimeMeshSubsystem::RegisterGeneratedMeshActor(ARealtimeMeshActor* Actor) 66 | { 67 | if (GetWorld() && bInitialized) 68 | { 69 | ActiveGeneratedActors.Add(Actor); 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | void URealtimeMeshSubsystem::UnregisterGeneratedMeshActor(ARealtimeMeshActor* Actor) 76 | { 77 | if (GetWorld() && bInitialized) 78 | { 79 | ActiveGeneratedActors.Remove(Actor); 80 | } 81 | } 82 | 83 | URealtimeMeshSubsystem* URealtimeMeshSubsystem::GetInstance(UWorld* World) 84 | { 85 | return World ? World->GetSubsystem() : nullptr; 86 | } 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | void RealtimeMesh::FRealtimeMeshEndOfFrameUpdateManager::OnPreSendAllEndOfFrameUpdates(UWorld* World) 104 | { 105 | SyncRoot.Lock(); 106 | auto MeshesCopy = MoveTemp(MeshesToUpdate); 107 | SyncRoot.Unlock(); 108 | 109 | for (const auto& MeshWeak : MeshesCopy) 110 | { 111 | if (auto Mesh = MeshWeak.Pin()) 112 | { 113 | Mesh->ProcessEndOfFrameUpdates(); 114 | } 115 | } 116 | } 117 | 118 | RealtimeMesh::FRealtimeMeshEndOfFrameUpdateManager::~FRealtimeMeshEndOfFrameUpdateManager() 119 | { 120 | if (EndOfFrameUpdateHandle.IsValid()) 121 | { 122 | FWorldDelegates::OnWorldPostActorTick.Remove(EndOfFrameUpdateHandle); 123 | EndOfFrameUpdateHandle.Reset(); 124 | } 125 | } 126 | 127 | void RealtimeMesh::FRealtimeMeshEndOfFrameUpdateManager::MarkComponentForUpdate(const RealtimeMesh::FRealtimeMeshWeakPtr& InMesh) 128 | { 129 | FScopeLock Lock(&SyncRoot); 130 | if (!EndOfFrameUpdateHandle.IsValid()) 131 | { 132 | 133 | // TODO: Moved this to post actor tick from OnWorldPreSendAlLEndOfFrameUpdates... Is this the best option? 134 | // Servers were not getting events but ever ~60 seconds 135 | EndOfFrameUpdateHandle = FWorldDelegates::OnWorldPostActorTick.AddLambda([this](UWorld* World, ELevelTick TickType, float DeltaSeconds) { OnPreSendAllEndOfFrameUpdates(World); }); 136 | } 137 | MeshesToUpdate.Add(InMesh); 138 | } 139 | 140 | void RealtimeMesh::FRealtimeMeshEndOfFrameUpdateManager::ClearComponentForUpdate(const RealtimeMesh::FRealtimeMeshWeakPtr& InMesh) 141 | { 142 | FScopeLock Lock(&SyncRoot); 143 | MeshesToUpdate.Remove(InMesh); 144 | } 145 | 146 | RealtimeMesh::FRealtimeMeshEndOfFrameUpdateManager& RealtimeMesh::FRealtimeMeshEndOfFrameUpdateManager::Get() 147 | { 148 | return TLazySingleton::Get(); 149 | } 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RealtimeMeshThreadingSubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "RealtimeMeshThreadingSubsystem.h" 5 | #include "Engine/Engine.h" 6 | 7 | void URealtimeMeshThreadingSubsystem::Initialize(FSubsystemCollectionBase& Collection) 8 | { 9 | Super::Initialize(Collection); 10 | } 11 | 12 | void URealtimeMeshThreadingSubsystem::Deinitialize() 13 | { 14 | Super::Deinitialize(); 15 | } 16 | 17 | URealtimeMeshThreadingSubsystem* URealtimeMeshThreadingSubsystem::Get() 18 | { 19 | return GEngine->GetEngineSubsystem(); 20 | } 21 | 22 | FQueuedThreadPool& URealtimeMeshThreadingSubsystem::GetThreadPool() 23 | { 24 | if (!ThreadPool.IsValid()) 25 | { 26 | ThreadPool = TUniquePtr(FQueuedThreadPool::Allocate()); 27 | ThreadPool->Create(4, 64 * 1024, TPri_Normal, TEXT("RealtimeMeshThreadPool")); 28 | } 29 | 30 | return *ThreadPool; 31 | } 32 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RenderProxy/RealtimeMeshGPUBuffer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RenderProxy/RealtimeMeshGPUBuffer.h" 4 | #include "Data/RealtimeMeshUpdateBuilder.h" 5 | 6 | namespace RealtimeMesh 7 | { 8 | void FRealtimeMeshSectionGroupStreamUpdateData::CreateBufferAsyncIfPossible(FRealtimeMeshUpdateContext& UpdateContext) 9 | { 10 | if (GRHISupportsAsyncTextureCreation) 11 | { 12 | auto& RHICmdList = UpdateContext.GetRHICmdList(); 13 | 14 | FRHIResourceCreateInfo CreateInfo(TEXT("RealtimeMeshBuffer-Temp"), &Stream); 15 | CreateInfo.bWithoutNativeResource = Stream.Num() == 0 || Stream.GetStride() == 0; 16 | 17 | if (GetStreamKey().IsVertexStream()) 18 | { 19 | #if RMC_ENGINE_ABOVE_5_5 20 | Buffer = RHICmdList.CreateBuffer(Stream.GetResourceDataSize(), UsageFlags | BUF_VertexBuffer | BUF_ShaderResource, 21 | Stream.GetStride(), ERHIAccess::SRVMask, CreateInfo); 22 | #else 23 | Buffer = RHICmdList->CreateBuffer(Stream.GetResourceDataSize(), UsageFlags | BUF_VertexBuffer | BUF_ShaderResource, 24 | Stream.GetStride(), ERHIAccess::SRVMask, CreateInfo); 25 | #endif 26 | } 27 | else 28 | { 29 | check(GetStreamKey().IsIndexStream()); 30 | #if RMC_ENGINE_ABOVE_5_5 31 | Buffer = RHICmdList.CreateBuffer(Stream.GetResourceDataSize(), UsageFlags | BUF_IndexBuffer | BUF_ShaderResource, 32 | Stream.GetElementStride(), ERHIAccess::SRVMask, CreateInfo); 33 | #else 34 | Buffer = RHICmdList->CreateBuffer(Stream.GetResourceDataSize(), UsageFlags | BUF_IndexBuffer | BUF_ShaderResource, 35 | Stream.GetElementStride(), ERHIAccess::SRVMask, CreateInfo); 36 | #endif 37 | } 38 | } 39 | } 40 | 41 | void FRealtimeMeshSectionGroupStreamUpdateData::FinalizeInitialization(FRHICommandListBase& RHICmdList) 42 | { 43 | if (!Buffer.IsValid()) 44 | { 45 | check(Stream.GetResourceDataSize()); 46 | 47 | FRHIResourceCreateInfo CreateInfo(TEXT("RealtimeMeshBuffer-Temp"), &Stream); 48 | CreateInfo.bWithoutNativeResource = Stream.Num() == 0 || Stream.GetStride() == 0; 49 | 50 | #if RMC_ENGINE_ABOVE_5_3 51 | if (GetStreamKey().IsVertexStream()) 52 | { 53 | Buffer = RHICmdList.CreateVertexBuffer(Stream.GetResourceDataSize(), UsageFlags | BUF_VertexBuffer | BUF_ShaderResource, CreateInfo); 54 | } 55 | else 56 | { 57 | check(GetStreamKey().IsIndexStream()); 58 | Buffer = RHICmdList.CreateIndexBuffer(Stream.GetElementStride(), Stream.GetResourceDataSize(), UsageFlags | BUF_IndexBuffer | BUF_ShaderResource, CreateInfo); 59 | } 60 | #else 61 | if (GetStreamKey().IsVertexStream()) 62 | { 63 | Buffer = RHICreateVertexBuffer(Stream.GetResourceDataSize(), UsageFlags | BUF_VertexBuffer | BUF_ShaderResource, CreateInfo); 64 | } 65 | else 66 | { 67 | check(GetStreamKey().IsIndexStream()); 68 | Buffer = RHICreateIndexBuffer(Stream.GetElementStride(), Stream.GetResourceDataSize(), UsageFlags | BUF_IndexBuffer | BUF_ShaderResource, CreateInfo); 69 | } 70 | #endif 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RenderProxy/RealtimeMeshLODProxy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RenderProxy/RealtimeMeshLODProxy.h" 4 | 5 | #include "Algo/IndexOf.h" 6 | #include "Data/RealtimeMeshShared.h" 7 | #include "Core/RealtimeMeshLODConfig.h" 8 | #include "RenderProxy/RealtimeMeshSectionGroupProxy.h" 9 | #include "RenderProxy/RealtimeMeshSectionProxy.h" 10 | 11 | namespace RealtimeMesh 12 | { 13 | FRealtimeMeshLODProxy::FRealtimeMeshLODProxy(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshLODKey& InKey) 14 | : SharedResources(InSharedResources) 15 | , Key(InKey) 16 | { 17 | } 18 | 19 | FRealtimeMeshLODProxy::~FRealtimeMeshLODProxy() 20 | { 21 | check(IsInRenderingThread()); 22 | Reset(); 23 | } 24 | 25 | FRealtimeMeshSectionGroupProxyPtr FRealtimeMeshLODProxy::GetSectionGroup(const FRealtimeMeshSectionGroupKey& SectionGroupKey) const 26 | { 27 | check(SectionGroupKey.IsPartOf(Key)); 28 | 29 | if (SectionGroupMap.Contains(SectionGroupKey)) 30 | { 31 | return SectionGroups[SectionGroupMap[SectionGroupKey]]; 32 | } 33 | return FRealtimeMeshSectionGroupProxyPtr(); 34 | } 35 | 36 | void FRealtimeMeshLODProxy::UpdateConfig(const FRealtimeMeshLODConfig& NewConfig) 37 | { 38 | Config = NewConfig; 39 | } 40 | 41 | void FRealtimeMeshLODProxy::CreateSectionGroupIfNotExists(const FRealtimeMeshSectionGroupKey& SectionGroupKey) 42 | { 43 | TRACE_CPUPROFILER_EVENT_SCOPE(FRealtimeMeshLODProxy::CreateSectionGroupIfNotExists); 44 | 45 | check(SectionGroupKey.IsPartOf(Key)); 46 | 47 | // Does this section already exist 48 | if (!SectionGroupMap.Contains(SectionGroupKey)) 49 | { 50 | const int32 SectionGroupIndex = SectionGroups.Add(SharedResources->CreateSectionGroupProxy(SectionGroupKey)); 51 | SectionGroupMap.Add(SectionGroupKey, SectionGroupIndex); 52 | } 53 | } 54 | 55 | void FRealtimeMeshLODProxy::RemoveSectionGroup(const FRealtimeMeshSectionGroupKey& SectionGroupKey) 56 | { 57 | TRACE_CPUPROFILER_EVENT_SCOPE(FRealtimeMeshLODProxy::RemoveSectionGroup); 58 | 59 | check(SectionGroupKey.IsPartOf(Key)); 60 | 61 | if (SectionGroupMap.Contains(SectionGroupKey)) 62 | { 63 | const int32 SectionGroupIndex = SectionGroupMap[SectionGroupKey]; 64 | SectionGroups.RemoveAt(SectionGroupIndex); 65 | RebuildSectionGroupMap(); 66 | } 67 | } 68 | 69 | #if RHI_RAYTRACING 70 | FRayTracingGeometry* FRealtimeMeshLODProxy::GetStaticRayTracingGeometry() const 71 | { 72 | return SectionGroups.IsValidIndex(StaticRayTraceSectionGroup)? SectionGroups[StaticRayTraceSectionGroup]->GetRayTracingGeometry() : nullptr; 73 | } 74 | #endif 75 | 76 | void FRealtimeMeshLODProxy::UpdateCachedState(FRHICommandListBase& RHICmdList) 77 | { 78 | TRACE_CPUPROFILER_EVENT_SCOPE(FRealtimeMeshLODProxy::UpdateCachedState); 79 | 80 | // Handle all SectionGroup updates 81 | for (const auto& SectionGroup : SectionGroups) 82 | { 83 | SectionGroup->UpdateCachedState(RHICmdList); 84 | } 85 | 86 | DrawMask = FRealtimeMeshDrawMask(); 87 | ActiveSectionGroupMask.SetNumUninitialized(SectionGroups.Num()); 88 | ActiveSectionGroupMask.SetRange(0, SectionGroups.Num(), false); 89 | if (Config.bIsVisible && Config.ScreenSize >= 0) 90 | { 91 | uint32 RayTracingRelevantSectionGroupCount = 0; 92 | 93 | for (auto It = SectionGroups.CreateConstIterator(); It; ++It) 94 | { 95 | const FRealtimeMeshSectionGroupProxyRef& SectionGroup = *It; 96 | auto SectionGroupDrawMask = SectionGroup->GetDrawMask(); 97 | DrawMask |= SectionGroupDrawMask; 98 | 99 | if (SectionGroupDrawMask.ShouldRenderInRayTracing()) 100 | { 101 | RayTracingRelevantSectionGroupCount++; 102 | } 103 | 104 | ActiveSectionGroupMask[It.GetIndex()] = SectionGroupDrawMask.ShouldRender(); 105 | } 106 | 107 | if (RayTracingRelevantSectionGroupCount > 1 || DrawMask.IsSet(ERealtimeMeshDrawMask::DrawDynamic)) 108 | { 109 | DrawMask.SetFlag(ERealtimeMeshDrawMask::DynamicRayTracing); 110 | } 111 | } 112 | 113 | #if RHI_RAYTRACING 114 | if (DrawMask.CanRenderInStaticRayTracing()) 115 | { 116 | StaticRayTraceSectionGroup = Algo::IndexOfByPredicate(SectionGroups, [](const FRealtimeMeshSectionGroupProxyRef& SectionGroup) 117 | { 118 | return SectionGroup->GetDrawMask().CanRenderInStaticRayTracing(); 119 | }); 120 | } 121 | else 122 | { 123 | StaticRayTraceSectionGroup = INDEX_NONE; 124 | } 125 | #endif 126 | } 127 | 128 | void FRealtimeMeshLODProxy::RebuildSectionGroupMap() 129 | { 130 | SectionGroupMap.Empty(); 131 | for (auto It = SectionGroups.CreateIterator(); It; ++It) 132 | { 133 | SectionGroupMap.Add((*It)->GetKey(), It.GetIndex()); 134 | } 135 | } 136 | 137 | void FRealtimeMeshLODProxy::Reset() 138 | { 139 | TRACE_CPUPROFILER_EVENT_SCOPE(FRealtimeMeshLODProxy::Reset); 140 | 141 | // Reset all the section groups 142 | SectionGroups.Empty(); 143 | SectionGroupMap.Empty(); 144 | 145 | Config = FRealtimeMeshLODConfig(); 146 | DrawMask = FRealtimeMeshDrawMask(); 147 | 148 | #if RHI_RAYTRACING 149 | StaticRayTraceSectionGroup = INDEX_NONE; 150 | #endif 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RenderProxy/RealtimeMeshProxyCommandBatch.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "RenderProxy/RealtimeMeshProxyCommandBatch.h" 5 | 6 | #include "Core/RealtimeMeshFuture.h" 7 | #include "Data/RealtimeMeshShared.h" 8 | #include "Data/RealtimeMeshData.h" 9 | #include "RenderProxy/RealtimeMeshLODProxy.h" 10 | #include "RenderProxy/RealtimeMeshProxy.h" 11 | #include "RenderProxy/RealtimeMeshSectionGroupProxy.h" 12 | 13 | namespace RealtimeMesh 14 | { 15 | FRealtimeMeshCommandBatchIntermediateFuture::FRealtimeMeshCommandBatchIntermediateFuture(): FinalPromise(MakeShared>()) 16 | , Result(ERealtimeMeshProxyUpdateStatus::NoUpdate) 17 | , bRenderThreadReady(false) 18 | , bGameThreadReady(false) 19 | , bFinalized(false) 20 | { 21 | } 22 | 23 | void FRealtimeMeshCommandBatchIntermediateFuture::FinalizeRenderThread(ERealtimeMeshProxyUpdateStatus Status) 24 | { 25 | AsyncTask(ENamedThreads::GameThread, [Status, ThisShared = this->AsShared()]() 26 | { 27 | ThisShared->Result = Status; 28 | ThisShared->bRenderThreadReady = true; 29 | 30 | if (!ThisShared->bFinalized) 31 | { 32 | ThisShared->FinalPromise->EmplaceValue(ThisShared->Result); 33 | ThisShared->bFinalized = true; 34 | } 35 | }); 36 | } 37 | 38 | void FRealtimeMeshCommandBatchIntermediateFuture::FinalizeGameThread() 39 | { 40 | bGameThreadReady = true; 41 | 42 | if (bRenderThreadReady && !bFinalized) 43 | { 44 | FinalPromise->EmplaceValue(Result); 45 | bFinalized = true; 46 | } 47 | } 48 | 49 | 50 | 51 | 52 | 53 | 54 | void FRealtimeMeshProxyUpdateBuilder::AddMeshTask(TUniqueFunction&& Function, bool bInRequiresProxyRecreate) 55 | { 56 | bRequiresProxyRecreate |= bInRequiresProxyRecreate; 57 | Tasks.Add(MoveTemp(Function)); 58 | } 59 | 60 | void FRealtimeMeshProxyUpdateBuilder::AddLODTask(const FRealtimeMeshLODKey& LODKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate) 61 | { 62 | AddMeshTask([LODKey, Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, const FRealtimeMeshProxy& MeshProxy) 63 | { 64 | const FRealtimeMeshLODProxyPtr LOD = MeshProxy.GetLOD(LODKey); 65 | 66 | if (ensure(LOD.IsValid())) 67 | { 68 | Func(RHICmdList, *LOD.Get()); 69 | } 70 | }, bInRequiresProxyRecreate); 71 | } 72 | 73 | void FRealtimeMeshProxyUpdateBuilder::AddSectionGroupTask(const FRealtimeMeshSectionGroupKey& SectionGroupKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate) 74 | { 75 | AddLODTask(SectionGroupKey.LOD(), [SectionGroupKey, Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, const FRealtimeMeshLODProxy& LOD) 76 | { 77 | const FRealtimeMeshSectionGroupProxyPtr SectionGroup = LOD.GetSectionGroup(SectionGroupKey); 78 | 79 | if (ensure(SectionGroup.IsValid())) 80 | { 81 | Func(RHICmdList, *SectionGroup.Get()); 82 | } 83 | }, bInRequiresProxyRecreate); 84 | } 85 | 86 | void FRealtimeMeshProxyUpdateBuilder::AddSectionTask(const FRealtimeMeshSectionKey& SectionKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate) 87 | { 88 | AddSectionGroupTask(SectionKey.SectionGroup(), [SectionKey, Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, const FRealtimeMeshSectionGroupProxy& SectionGroup) 89 | { 90 | const FRealtimeMeshSectionProxyPtr Section = SectionGroup.GetSection(SectionKey); 91 | 92 | if (ensure(Section.IsValid())) 93 | { 94 | Func(RHICmdList, *Section.Get()); 95 | } 96 | }, bInRequiresProxyRecreate); 97 | } 98 | 99 | 100 | 101 | TFuture FRealtimeMeshProxyUpdateBuilder::Commit(const TSharedRef& Mesh) 102 | { 103 | // Skip if no tasks 104 | if (Tasks.IsEmpty()) 105 | { 106 | return MakeFulfilledPromise(ERealtimeMeshProxyUpdateStatus::NoUpdate).GetFuture(); 107 | } 108 | 109 | // Grab the proxy we are going to update 110 | const FRealtimeMeshProxyPtr Proxy = Mesh->GetRenderProxy(false); 111 | 112 | // Skip if no proxy 113 | if (!Proxy.IsValid()) 114 | { 115 | return MakeFulfilledPromise(ERealtimeMeshProxyUpdateStatus::NoProxy).GetFuture(); 116 | } 117 | 118 | auto ThreadState = MakeShared(); 119 | 120 | Proxy->EnqueueCommandBatch(MoveTemp(Tasks), ThreadState); 121 | 122 | DoOnGameThread([ThreadState, MeshWeak = Mesh.ToWeakPtr(), bRecreateProxies = static_cast(bRequiresProxyRecreate)]() 123 | { 124 | if (bRecreateProxies) 125 | { 126 | if (const auto MeshToMarkDirty = MeshWeak.Pin()) 127 | { 128 | if (MeshToMarkDirty->GetSharedResources()->OnRenderProxyRequiresUpdate().IsBound()) 129 | { 130 | MeshToMarkDirty->GetSharedResources()->OnRenderProxyRequiresUpdate().Broadcast(); 131 | } 132 | } 133 | } 134 | 135 | ThreadState->FinalizeGameThread(); 136 | }); 137 | 138 | Tasks.Empty(); 139 | bRequiresProxyRecreate = false; 140 | 141 | return ThreadState->FinalPromise->GetFuture(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Private/RenderProxy/RealtimeMeshSectionProxy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RenderProxy/RealtimeMeshSectionProxy.h" 4 | #include "RenderProxy/RealtimeMeshSectionGroupProxy.h" 5 | #include "RenderProxy/RealtimeMeshVertexFactory.h" 6 | 7 | namespace RealtimeMesh 8 | { 9 | FRealtimeMeshSectionProxy::FRealtimeMeshSectionProxy(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshSectionKey InKey) 10 | : SharedResources(InSharedResources) 11 | , Key(InKey) 12 | , bRangeChanged(false) 13 | { 14 | } 15 | 16 | FRealtimeMeshSectionProxy::~FRealtimeMeshSectionProxy() 17 | { 18 | check(IsInRenderingThread()); 19 | } 20 | 21 | void FRealtimeMeshSectionProxy::UpdateConfig(const FRealtimeMeshSectionConfig& NewConfig) 22 | { 23 | if (Config != NewConfig) 24 | { 25 | Config = NewConfig; 26 | } 27 | } 28 | 29 | void FRealtimeMeshSectionProxy::UpdateStreamRange(const FRealtimeMeshStreamRange& NewStreamRange) 30 | { 31 | if (StreamRange != NewStreamRange) 32 | { 33 | StreamRange = NewStreamRange; 34 | bRangeChanged = true; 35 | } 36 | } 37 | 38 | bool FRealtimeMeshSectionProxy::InitializeMeshBatch(FMeshBatch& MeshBatch, FRHIUniformBuffer* PrimitiveUniformBuffer) const 39 | { 40 | FMeshBatchElement& BatchElement = MeshBatch.Elements[0]; 41 | 42 | BatchElement.PrimitiveUniformBuffer = PrimitiveUniformBuffer; 43 | //BatchElement.PrimitiveUniformBufferResource = nullptr; 44 | //BatchElement.LooseParametersUniformBuffer = nullptr; 45 | //BatchElement.IndexBuffer = nullptr; // &VertexFactory->GetIndexBuffer(bDepthOnly, bMatrixInverted, Params.ResourceSubmitter); 46 | //BatchElement.UserData = nullptr; 47 | //BatchElement.VertexFactoryUserData = nullptr; 48 | 49 | //BatchElement.IndirectArgsBuffer = nullptr; 50 | //BatchElement.IndirectArgsOffset = 0; 51 | 52 | BatchElement.FirstIndex = StreamRange.GetMinIndex(); 53 | BatchElement.NumPrimitives = StreamRange.NumPrimitives(REALTIME_MESH_NUM_INDICES_PER_PRIMITIVE); 54 | 55 | //BatchElement.NumInstances = 1; 56 | //BatchElement.BaseVertexIndex = 0; 57 | BatchElement.MinVertexIndex = StreamRange.GetMinVertex(); 58 | BatchElement.MaxVertexIndex = StreamRange.GetMaxVertex(); 59 | //BatchElement.UserIndex = -1; 60 | BatchElement.MinScreenSize = 0; 61 | BatchElement.MaxScreenSize = 1; 62 | 63 | BatchElement.InstancedLODIndex = 0; 64 | BatchElement.InstancedLODRange = 0; 65 | BatchElement.bUserDataIsColorVertexBuffer = false; 66 | BatchElement.bIsSplineProxy = false; 67 | BatchElement.bIsInstanceRuns = false; 68 | BatchElement.bForceInstanceCulling = false; 69 | BatchElement.bPreserveInstanceOrder = false; 70 | #if RMC_ENGINE_ABOVE_5_4 71 | BatchElement.bFetchInstanceCountFromScene = false; 72 | #endif 73 | 74 | #if UE_ENABLE_DEBUG_DRAWING 75 | BatchElement.VisualizeElementIndex = INDEX_NONE; 76 | #endif 77 | 78 | check(BatchElement.NumPrimitives <= (static_cast(BatchElement.IndexBuffer)->Num() - BatchElement.FirstIndex) / 3); 79 | check((int32)BatchElement.NumPrimitives <= StreamRange.NumPrimitives(REALTIME_MESH_NUM_INDICES_PER_PRIMITIVE)); 80 | check((int32)BatchElement.MaxVertexIndex <= StreamRange.GetMaxVertex()); 81 | 82 | return true; 83 | } 84 | 85 | void FRealtimeMeshSectionProxy::UpdateCachedState(FRealtimeMeshSectionGroupProxy& ParentGroup) 86 | { 87 | TRACE_CPUPROFILER_EVENT_SCOPE(FRealtimeMeshSectionProxy::UpdateCachedState); 88 | 89 | bRangeChanged = false; 90 | 91 | // First evaluate whether we have valid mesh data to render 92 | bool bHasValidMeshData = StreamRange.NumPrimitives(REALTIME_MESH_NUM_INDICES_PER_PRIMITIVE) > 0 && 93 | StreamRange.NumVertices() >= REALTIME_MESH_NUM_INDICES_PER_PRIMITIVE; 94 | 95 | if (bHasValidMeshData) 96 | { 97 | // Flip it here so if we don't get this series for whatever reason we're invalid after. 98 | bHasValidMeshData = ParentGroup.GetVertexFactory().IsValid() && ParentGroup.GetVertexFactory()->IsValidStreamRange(StreamRange); 99 | } 100 | 101 | DrawMask = FRealtimeMeshDrawMask(); 102 | 103 | // Then build the draw mask if it is valid 104 | if (bHasValidMeshData) 105 | { 106 | if (Config.bIsVisible) 107 | { 108 | if (Config.bIsMainPassRenderable) 109 | { 110 | DrawMask.SetFlag(ERealtimeMeshDrawMask::DrawMainPass); 111 | } 112 | 113 | if (Config.bCastsShadow) 114 | { 115 | DrawMask.SetFlag(ERealtimeMeshDrawMask::DrawShadowPass); 116 | } 117 | } 118 | } 119 | } 120 | 121 | void FRealtimeMeshSectionProxy::Reset() 122 | { 123 | Config = FRealtimeMeshSectionConfig(); 124 | StreamRange = FRealtimeMeshStreamRange(); 125 | DrawMask = FRealtimeMeshDrawMask(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Data/RealtimeMeshLOD.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | #include "RealtimeMeshSectionGroup.h" 7 | #include "Core/RealtimeMeshLODConfig.h" 8 | 9 | namespace RealtimeMesh 10 | { 11 | class REALTIMEMESHCOMPONENT_API FRealtimeMeshLOD : public TSharedFromThis 12 | { 13 | protected: 14 | const FRealtimeMeshSharedResourcesRef SharedResources; 15 | const FRealtimeMeshLODKey Key; 16 | TSet SectionGroups; 17 | FRealtimeMeshLODConfig Config; 18 | FRealtimeMeshBounds Bounds; 19 | 20 | public: 21 | FRealtimeMeshLOD(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshLODKey& InKey); 22 | virtual ~FRealtimeMeshLOD() = default; 23 | 24 | const FRealtimeMeshLODKey& GetKey(const FRealtimeMeshLockContext& LockContext) const { return Key; } 25 | bool HasSectionGroups(const FRealtimeMeshLockContext& LockContext) const; 26 | 27 | FRealtimeMeshLODConfig GetConfig(const FRealtimeMeshLockContext& LockContext) const { return Config; } 28 | 29 | template 30 | TSharedPtr GetSectionGroupAs(const FRealtimeMeshLockContext& LockContext, const FRealtimeMeshSectionGroupKey& SectionGroupKey) const 31 | { 32 | return StaticCastSharedPtr(GetSectionGroup(LockContext, SectionGroupKey)); 33 | } 34 | 35 | FRealtimeMeshSectionGroupPtr GetSectionGroup(const FRealtimeMeshLockContext& LockContext, const FRealtimeMeshSectionGroupKey& SectionGroupKey) const; 36 | 37 | 38 | template 39 | void ProcessSectionGroupsAs(const FRealtimeMeshLockContext& LockContext, FuncType ProcessFunc) const 40 | { 41 | FRealtimeMeshScopeGuardRead ScopeGuard(SharedResources->GetGuard()); 42 | for (TSharedPtr SectionGroup : SectionGroups) 43 | { 44 | ::Invoke(ProcessFunc, *StaticCastSharedPtr(SectionGroup)); 45 | } 46 | } 47 | 48 | template 49 | void ProcessSectionGroups(const FRealtimeMeshLockContext& LockContext, FuncType ProcessFunc) const 50 | { 51 | FRealtimeMeshScopeGuardRead ScopeGuard(SharedResources->GetGuard()); 52 | for (TSharedPtr SectionGroup : SectionGroups) 53 | { 54 | ::Invoke(ProcessFunc, *SectionGroup); 55 | } 56 | } 57 | 58 | 59 | 60 | TOptional GetLocalBounds(const FRealtimeMeshLockContext& LockContext) const; 61 | 62 | virtual void Initialize(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshLODConfig& InConfig); 63 | virtual void Reset(FRealtimeMeshUpdateContext& UpdateContext); 64 | 65 | virtual void UpdateConfig(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshLODConfig& InConfig); 66 | 67 | virtual void CreateOrUpdateSectionGroup(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshSectionGroupKey& SectionGroupKey, const FRealtimeMeshSectionGroupConfig& InConfig); 68 | virtual void RemoveSectionGroup(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshSectionGroupKey& SectionGroupKey); 69 | 70 | virtual bool Serialize(FArchive& Ar); 71 | 72 | virtual void InitializeProxy(FRealtimeMeshUpdateContext& UpdateContext); 73 | 74 | TSet GetSectionGroupKeys(const FRealtimeMeshLockContext& LockContext) const; 75 | 76 | virtual void FinalizeUpdate(FRealtimeMeshUpdateContext& UpdateContext); 77 | 78 | virtual bool ShouldRecreateProxyOnChange(const FRealtimeMeshLockContext& LockContext) { return true; } 79 | 80 | protected: 81 | 82 | void MarkBoundsDirtyIfNotOverridden(FRealtimeMeshUpdateContext& UpdateContext); 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Data/RealtimeMeshSectionGroup.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | #include "Core/RealtimeMeshDataStream.h" 7 | #include "RealtimeMeshSection.h" 8 | #include "Data/RealtimeMeshShared.h" 9 | #include "Core/RealtimeMeshKeys.h" 10 | #include "Core/RealtimeMeshSectionGroupConfig.h" 11 | 12 | namespace RealtimeMesh 13 | { 14 | class REALTIMEMESHCOMPONENT_API FRealtimeMeshSectionGroup : public TSharedFromThis 15 | { 16 | protected: 17 | const FRealtimeMeshSharedResourcesRef SharedResources; 18 | const FRealtimeMeshSectionGroupKey Key; 19 | 20 | TSet Streams; 21 | TSet Sections; 22 | FRealtimeMeshSectionGroupConfig Config; 23 | FRealtimeMeshBounds Bounds; 24 | 25 | public: 26 | FRealtimeMeshSectionGroup(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshSectionGroupKey& InKey); 27 | virtual ~FRealtimeMeshSectionGroup() = default; 28 | 29 | const FRealtimeMeshSectionGroupKey& GetKey(const FRealtimeMeshLockContext& LockContext) const { return Key; } 30 | FRealtimeMeshStreamRange GetInUseRange(const FRealtimeMeshLockContext& LockContext) const; 31 | TOptional GetLocalBounds(const FRealtimeMeshLockContext& LockContext) const; 32 | bool HasSections(const FRealtimeMeshLockContext& LockContext) const; 33 | int32 NumSections(const FRealtimeMeshLockContext& LockContext) const; 34 | bool HasStreams(const FRealtimeMeshLockContext& LockContext) const; 35 | 36 | TSet GetStreamKeys(const FRealtimeMeshLockContext& LockContext) const; 37 | TSet GetSectionKeys(const FRealtimeMeshLockContext& LockContext) const; 38 | 39 | template 40 | TSharedPtr GetSectionAs(const FRealtimeMeshLockContext& LockContext, const FRealtimeMeshSectionKey& SectionKey) const 41 | { 42 | return StaticCastSharedPtr(GetSection(LockContext, SectionKey)); 43 | } 44 | 45 | FRealtimeMeshSectionPtr GetSection(const FRealtimeMeshLockContext& LockContext, const FRealtimeMeshSectionKey& SectionKey) const; 46 | 47 | 48 | virtual void Initialize(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshSectionGroupConfig& InConfig); 49 | virtual void Reset(FRealtimeMeshUpdateContext& UpdateContext); 50 | 51 | virtual void SetOverrideBounds(FRealtimeMeshUpdateContext& UpdateContext, const FBoxSphereBounds3f& InBounds); 52 | virtual void ClearOverrideBounds(FRealtimeMeshUpdateContext& UpdateContext); 53 | 54 | 55 | /** 56 | * @brief Update the config for this section group 57 | * @param UpdateContext Update context used for this operation 58 | * @param InConfig New section group config 59 | */ 60 | virtual void UpdateConfig(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshSectionGroupConfig& InConfig); 61 | 62 | /** 63 | * @brief Edits the config for this section group using the specified function 64 | * @param UpdateContext Update context used for this operation 65 | * @param EditFunc Function to call to edit the config 66 | */ 67 | virtual void UpdateConfig(FRealtimeMeshUpdateContext& UpdateContext, TFunction EditFunc); 68 | 69 | virtual void CreateOrUpdateStream(FRealtimeMeshUpdateContext& UpdateContext, FRealtimeMeshStream&& Stream); 70 | virtual void RemoveStream(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshStreamKey& StreamKey); 71 | 72 | virtual void SetAllStreams(FRealtimeMeshUpdateContext& UpdateContext, FRealtimeMeshStreamSet&& InStreams); 73 | 74 | virtual void CreateOrUpdateSection(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshSectionKey& SectionKey, const FRealtimeMeshSectionConfig& InConfig, 75 | const FRealtimeMeshStreamRange& InStreamRange); 76 | virtual void RemoveSection(FRealtimeMeshUpdateContext& UpdateContext, const FRealtimeMeshSectionKey& SectionKey); 77 | 78 | virtual bool Serialize(FArchive& Ar); 79 | 80 | virtual void InitializeProxy(FRealtimeMeshUpdateContext& UpdateContext); 81 | 82 | virtual void FinalizeUpdate(FRealtimeMeshUpdateContext& UpdateContext); 83 | 84 | virtual bool ShouldRecreateProxyOnChange(const FRealtimeMeshLockContext& LockContext) const { return Config.DrawType == ERealtimeMeshSectionDrawType::Static; } 85 | protected: 86 | const FRealtimeMeshSectionGroupKey& GetKey_AssumesLocked() const { return Key; } 87 | friend struct FRealtimeMeshSectionGroupRefKeyFuncs; 88 | friend class FRealtimeMeshLOD; 89 | 90 | void MarkBoundsDirtyIfNotOverridden(FRealtimeMeshUpdateContext& UpdateContext); 91 | 92 | }; 93 | 94 | struct FRealtimeMeshSectionGroupRefKeyFuncs : BaseKeyFuncs, FRealtimeMeshSectionGroupKey, false> 95 | { 96 | /** 97 | * @return The key used to index the given element. 98 | */ 99 | static KeyInitType GetSetKey(const TSharedRef& Element) 100 | { 101 | return Element->GetKey_AssumesLocked(); 102 | } 103 | 104 | /** 105 | * @return True if the keys match. 106 | */ 107 | static bool Matches(KeyInitType A, KeyInitType B) 108 | { 109 | return A == B; 110 | } 111 | 112 | /** Calculates a hash index for a key. */ 113 | static uint32 GetKeyHash(KeyInitType Key) 114 | { 115 | return GetTypeHash(Key); 116 | } 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshComponentInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | 7 | namespace RealtimeMesh 8 | { 9 | class IRealtimeMesh_v0; 10 | 11 | class IRealtimeMeshInterface_v0 : public IModularFeature 12 | { 13 | public: 14 | static FName GetModularFeatureName() 15 | { 16 | static FName FeatureName = TEXT("IRealtimeMeshInterface_v0"); 17 | return FeatureName; 18 | } 19 | 20 | virtual ~IRealtimeMeshInterface_v0() = default; 21 | 22 | 23 | 24 | virtual bool IsRealtimeMesh(UMeshComponent* MeshComponent) const = 0; 25 | virtual UMeshComponent* CreateRealtimeMeshComponent(AActor* Owner, FName Name = NAME_None, EObjectFlags Flags = RF_NoFlags) const = 0; 26 | /*virtual TSharedRef GetRealtimeMesh(UMeshComponent* MeshComponent) const = 0;*/ 27 | 28 | }; 29 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | 7 | struct FRealtimeMeshConfig 8 | { 9 | int32 ForcedLOD; 10 | 11 | FRealtimeMeshConfig() : ForcedLOD(INDEX_NONE) { } 12 | 13 | bool operator==(const FRealtimeMeshConfig& Other) const 14 | { 15 | return ForcedLOD == Other.ForcedLOD; 16 | } 17 | 18 | bool operator!=(const FRealtimeMeshConfig& Other) const 19 | { 20 | return !(*this == Other); 21 | } 22 | 23 | friend FArchive& operator<<(FArchive& Ar, FRealtimeMeshConfig& Config); 24 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshInterfaceFwd.h" 6 | #include "RealtimeMeshKeys.h" 7 | 8 | namespace RealtimeMesh 9 | { 10 | struct FRealtimeMeshStreamSet; 11 | 12 | class IRealtimeMesh_v0 13 | { 14 | public: 15 | static FName GetModularFeatureName() 16 | { 17 | static FName FeatureName = TEXT("IRealtimeMesh_v0"); 18 | return FeatureName; 19 | } 20 | 21 | virtual ~IRealtimeMesh_v0() = default; 22 | }; 23 | 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshInterfaceCore.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshInterfaceFwd.h" 4 | 5 | DEFINE_LOG_CATEGORY(LogRealtimeMeshInterface); -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshInterfaceFwd.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "Runtime/Launch/Resources/Version.h" 7 | #include "StaticMeshResources.h" 8 | #include "Logging/LogMacros.h" 9 | 10 | 11 | #ifndef IS_REALTIME_MESH_LIBRARY 12 | #define IS_REALTIME_MESH_LIBRARY 0 13 | #endif 14 | 15 | #if IS_REALTIME_MESH_LIBRARY 16 | #define REALTIMEMESHCOMPONENT_INTERFACE_API REALTIMEMESHCOMPONENT_API 17 | #else 18 | #define REALTIMEMESHCOMPONENT_INTERFACE_API 19 | #endif 20 | 21 | #define RMC_ENGINE_ABOVE_5_0 (ENGINE_MAJOR_VERSION >= 5) 22 | #define RMC_ENGINE_BELOW_5_1 (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 1) 23 | #define RMC_ENGINE_ABOVE_5_1 (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1)) 24 | #define RMC_ENGINE_BELOW_5_2 (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 2) 25 | #define RMC_ENGINE_ABOVE_5_2 (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2)) 26 | #define RMC_ENGINE_BELOW_5_3 (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 3) 27 | #define RMC_ENGINE_ABOVE_5_3 (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 3)) 28 | #define RMC_ENGINE_BELOW_5_4 (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 4) 29 | #define RMC_ENGINE_ABOVE_5_4 (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4)) 30 | #define RMC_ENGINE_BELOW_5_5 (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION < 5) 31 | #define RMC_ENGINE_ABOVE_5_5 (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 5)) 32 | 33 | // This version of the RMC is only supported by engine version 5.0.0 and above 34 | static_assert(RMC_ENGINE_ABOVE_5_0); 35 | 36 | #define REALTIME_MESH_MAX_TEX_COORDS MAX_STATIC_TEXCOORDS 37 | #define REALTIME_MESH_MAX_LODS MAX_STATIC_MESH_LODS 38 | #define REALTIME_MESH_MAX_LOD_INDEX (REALTIME_MESH_MAX_LODS - 1) 39 | 40 | // Maximum number of elements in a vertex stream 41 | #define REALTIME_MESH_MAX_STREAM_ELEMENTS 8 42 | #define REALTIME_MESH_NUM_INDICES_PER_PRIMITIVE 3 43 | 44 | static_assert(REALTIME_MESH_MAX_STREAM_ELEMENTS >= REALTIME_MESH_MAX_TEX_COORDS, "REALTIME_MESH_MAX_STREAM_ELEMENTS must be large enough to contain REALTIME_MESH_MAX_TEX_COORDS"); 45 | 46 | #if RMC_ENGINE_ABOVE_5_1 47 | #define RMC_NODISCARD_CTOR UE_NODISCARD_CTOR 48 | #else 49 | #define RMC_NODISCARD_CTOR 50 | #endif 51 | 52 | 53 | #if RMC_ENGINE_BELOW_5_2 54 | template FORCEINLINE uint32 GetTypeHashHelper(const T& V) { return GetTypeHash(V); } 55 | #endif 56 | 57 | 58 | REALTIMEMESHCOMPONENT_INTERFACE_API DECLARE_LOG_CATEGORY_EXTERN(LogRealtimeMeshInterface, Warning, All); 59 | 60 | namespace RealtimeMesh 61 | { 62 | 63 | template 64 | using TFixedLODArray = TArray>; 65 | } 66 | 67 | 68 | 69 | enum class ERealtimeMeshProxyUpdateStatus : uint8 70 | { 71 | NoProxy, 72 | NoUpdate, 73 | Updated, 74 | }; 75 | 76 | enum class ERealtimeMeshBatchCreationFlags : uint8 77 | { 78 | None = 0, 79 | ForceAllDynamic = 0x1, 80 | SkipStaticRayTracedSections = 0x2, 81 | }; 82 | ENUM_CLASS_FLAGS(ERealtimeMeshBatchCreationFlags); 83 | 84 | enum class ERealtimeMeshOutcomePins : uint8 85 | { 86 | Failure, 87 | Success 88 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshLODConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | 7 | struct FRealtimeMeshLODConfig 8 | { 9 | bool bIsVisible; 10 | float ScreenSize; 11 | 12 | FRealtimeMeshLODConfig(float InScreenSize = 0.0f) 13 | : bIsVisible(true) 14 | , ScreenSize(InScreenSize) 15 | { 16 | } 17 | 18 | bool operator==(const FRealtimeMeshLODConfig& Other) const 19 | { 20 | return bIsVisible == Other.bIsVisible 21 | && ScreenSize == Other.ScreenSize; 22 | } 23 | 24 | bool operator!=(const FRealtimeMeshLODConfig& Other) const 25 | { 26 | return !(*this == Other); 27 | } 28 | 29 | friend FArchive& operator<<(FArchive& Ar, FRealtimeMeshLODConfig& Config); 30 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshMaterial.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | 7 | /* Material slot, including an optional name, and the material reference 8 | * that a section can then index to set the material on a mesh section 9 | */ 10 | struct FRealtimeMeshMaterialSlot 11 | { 12 | FName SlotName; 13 | TObjectPtr Material; 14 | 15 | FRealtimeMeshMaterialSlot() : SlotName(NAME_None), Material(nullptr) { } 16 | 17 | FRealtimeMeshMaterialSlot(const FName& InSlotName, UMaterialInterface* InMaterial) 18 | : SlotName(InSlotName), Material(InMaterial) { } 19 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshModularFeatures.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "Features/IModularFeature.h" 7 | #include "Features/IModularFeatures.h" 8 | 9 | namespace RealtimeMesh 10 | { 11 | /* 12 | * Helper to auto register/unregister a modular part of the interface 13 | */ 14 | template 15 | class TRealtimeMeshModularFeatureRegistration : FNoncopyable 16 | { 17 | private: 18 | Type Interface; 19 | public: 20 | TRealtimeMeshModularFeatureRegistration() 21 | { 22 | IModularFeatures::Get().RegisterModularFeature(Type::GetModularFeatureName(), &Interface); 23 | } 24 | 25 | ~TRealtimeMeshModularFeatureRegistration() 26 | { 27 | IModularFeatures::Get().UnregisterModularFeature(Type::GetModularFeatureName(), &Interface); 28 | } 29 | }; 30 | 31 | 32 | /* 33 | * Helper to auto capture/release a modular part of the interface 34 | */ 35 | template 36 | struct TRealtimeMeshModularInterface 37 | { 38 | Type* Interface; 39 | 40 | FDelegateHandle OnRegisteredHandle; 41 | FDelegateHandle OnUnregisteredHandle; 42 | public: 43 | TRealtimeMeshModularInterface(bool bAutoBind = true) 44 | : Interface(nullptr) 45 | { 46 | if (bAutoBind) 47 | { 48 | Bind(); 49 | } 50 | } 51 | 52 | ~TRealtimeMeshModularInterface() 53 | { 54 | Release(); 55 | } 56 | 57 | Type& operator*() 58 | { 59 | check(Interface); 60 | return *Interface; 61 | } 62 | 63 | Type* operator->() 64 | { 65 | check(Interface); 66 | return Interface; 67 | } 68 | 69 | bool IsAvailable() const 70 | { 71 | return Interface != nullptr || IModularFeatures::Get().IsModularFeatureAvailable(Type::GetModularFeatureName()); 72 | } 73 | 74 | bool IsConnected() const 75 | { 76 | return Interface != nullptr; 77 | } 78 | 79 | private: 80 | void Bind() 81 | { 82 | IModularFeatures& ModularFeatures = IModularFeatures::Get(); 83 | OnRegisteredHandle = ModularFeatures.OnModularFeatureRegistered().AddRaw(this, &TRealtimeMeshModularInterface::OnRegisteredFeature); 84 | OnUnregisteredHandle = ModularFeatures.OnModularFeatureUnregistered().AddRaw(this, &TRealtimeMeshModularInterface::OnUnregisteredFeature); 85 | 86 | if (ModularFeatures.IsModularFeatureAvailable(Type::GetModularFeatureName())) 87 | { 88 | Interface = &ModularFeatures.GetModularFeature(Type::GetModularFeatureName()); 89 | } 90 | } 91 | 92 | void Release() 93 | { 94 | Interface = nullptr; 95 | if (OnRegisteredHandle.IsValid()) 96 | { 97 | IModularFeatures& ModularFeatures = IModularFeatures::Get(); 98 | ModularFeatures.OnModularFeatureRegistered().Remove(OnRegisteredHandle); 99 | ModularFeatures.OnModularFeatureUnregistered().Remove(OnUnregisteredHandle); 100 | OnRegisteredHandle.Reset(); 101 | OnUnregisteredHandle.Reset(); 102 | } 103 | } 104 | 105 | 106 | void OnRegisteredFeature(const FName& Name, IModularFeature* Feature) 107 | { 108 | if (Name == Type::GetModularFeatureName()) 109 | { 110 | Interface = static_cast(Feature); 111 | } 112 | } 113 | void OnUnregisteredFeature(const FName& Name, IModularFeature* Feature) 114 | { 115 | if (Name == Type::GetModularFeatureName()) 116 | { 117 | Interface = nullptr; 118 | } 119 | } 120 | }; 121 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshSectionConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | 7 | struct FRealtimeMeshSectionConfig 8 | { 9 | int32 MaterialSlot; 10 | bool bIsVisible; 11 | bool bCastsShadow; 12 | bool bIsMainPassRenderable; 13 | bool bForceOpaque; 14 | 15 | FRealtimeMeshSectionConfig(int32 InMaterialSlot = 0) 16 | : MaterialSlot(InMaterialSlot) 17 | , bIsVisible(true) 18 | , bCastsShadow(true) 19 | , bIsMainPassRenderable(true) 20 | , bForceOpaque(false) 21 | { } 22 | 23 | bool operator==(const FRealtimeMeshSectionConfig& Other) const 24 | { 25 | return MaterialSlot == Other.MaterialSlot 26 | && bIsVisible == Other.bIsVisible 27 | && bCastsShadow == Other.bCastsShadow 28 | && bIsMainPassRenderable == Other.bIsMainPassRenderable 29 | && bForceOpaque == Other.bForceOpaque; 30 | } 31 | 32 | bool operator!=(const FRealtimeMeshSectionConfig& Other) const 33 | { 34 | return !(*this == Other); 35 | } 36 | 37 | friend FArchive& operator<<(FArchive& Ar, FRealtimeMeshSectionConfig& Config); 38 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshSectionGroupConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | 7 | /* The rendering path to use for this section. 8 | * Static has lower overhead but requires a proxy recreation on change for all components 9 | * Dynamic has slightly higher overhead but allows for more efficient section updates 10 | */ 11 | enum class ERealtimeMeshSectionDrawType : uint8 12 | { 13 | Static, 14 | Dynamic, 15 | }; 16 | 17 | struct FRealtimeMeshSectionGroupConfig 18 | { 19 | ERealtimeMeshSectionDrawType DrawType; 20 | 21 | FRealtimeMeshSectionGroupConfig(ERealtimeMeshSectionDrawType InDrawType = ERealtimeMeshSectionDrawType::Static) 22 | : DrawType(InDrawType) 23 | { } 24 | 25 | bool operator==(const FRealtimeMeshSectionGroupConfig& Other) const 26 | { 27 | return DrawType == Other.DrawType; 28 | } 29 | 30 | bool operator!=(const FRealtimeMeshSectionGroupConfig& Other) const 31 | { 32 | return !(*this == Other); 33 | } 34 | 35 | friend FArchive& operator<<(FArchive& Ar, FRealtimeMeshSectionGroupConfig& Config); 36 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshSimpleInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshInterface.h" 6 | 7 | namespace RealtimeMesh 8 | { 9 | class IRealtimeMeshSimple_v0 : public IRealtimeMesh_v0 10 | { 11 | public: 12 | static FName GetModularFeatureName() 13 | { 14 | static FName FeatureName = TEXT("IRealtimeMeshSimple_v0"); 15 | return FeatureName; 16 | } 17 | 18 | virtual TFuture CreateSectionGroup(const FRealtimeMeshSectionGroupKey& Key, const FRealtimeMeshStreamSet& Streams, const FRealtimeMeshSectionGroupConfig& InConfig = FRealtimeMeshSectionGroupConfig(), bool bShouldAutoCreateSectionsForPolyGroups = true) = 0; 19 | virtual TFuture CreateSectionGroup(const FRealtimeMeshSectionGroupKey& Key, FRealtimeMeshStreamSet&& Streams, const FRealtimeMeshSectionGroupConfig& InConfig = FRealtimeMeshSectionGroupConfig(), bool bShouldAutoCreateSectionsForPolyGroups = true) = 0; 20 | 21 | virtual TFuture Reset(bool bShouldRecreate) = 0; 22 | }; 23 | 24 | class IRealtimeMeshSimpleInterface_v0 : public IModularFeature 25 | { 26 | public: 27 | virtual ~IRealtimeMeshSimpleInterface_v0() = default; 28 | 29 | static FName GetModularFeatureName() 30 | { 31 | static FName FeatureName = TEXT("IRealtimeMeshSimpleInterface_v0"); 32 | return FeatureName; 33 | } 34 | 35 | 36 | virtual TSharedRef InitializeMesh(UMeshComponent* MeshComponent) const = 0; 37 | virtual TSharedRef GetMesh(UMeshComponent* MeshComponent) const = 0; 38 | }; 39 | 40 | 41 | #if !IS_REALTIME_MESH_LIBRARY 42 | using IRealtimeMeshSimple = IRealtimeMeshSimple_v0; 43 | using IRealtimeMeshSimpleInterface = IRealtimeMeshSimpleInterface_v0; 44 | #endif 45 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Interface/Core/RealtimeMeshStreamRange.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "UObject/NameTypes.h" 7 | #include "Math/Range.h" 8 | 9 | enum class ERealtimeMeshStreamType : uint8 10 | { 11 | Unknown, 12 | Vertex, 13 | Index, 14 | }; 15 | 16 | 17 | struct FRealtimeMeshStreamKey 18 | { 19 | private: 20 | ERealtimeMeshStreamType StreamType; 21 | FName StreamName; 22 | 23 | public: 24 | FRealtimeMeshStreamKey() 25 | : StreamType(ERealtimeMeshStreamType::Unknown) 26 | , StreamName(NAME_None) 27 | { } 28 | 29 | FRealtimeMeshStreamKey(ERealtimeMeshStreamType InStreamType, FName InStreamName) 30 | : StreamType(InStreamType) 31 | , StreamName(InStreamName) 32 | { } 33 | 34 | FName GetName() const { return StreamName; } 35 | 36 | ERealtimeMeshStreamType GetStreamType() const { return StreamType; } 37 | bool IsVertexStream() const { return StreamType == ERealtimeMeshStreamType::Vertex; } 38 | bool IsIndexStream() const { return StreamType == ERealtimeMeshStreamType::Index; } 39 | 40 | FORCEINLINE bool operator==(const FRealtimeMeshStreamKey& Other) const { return StreamType == Other.StreamType && StreamName == Other.StreamName; } 41 | FORCEINLINE bool operator!=(const FRealtimeMeshStreamKey& Other) const { return StreamType != Other.StreamType || StreamName != Other.StreamName; } 42 | 43 | friend inline uint32 GetTypeHash(const FRealtimeMeshStreamKey& StreamKey) 44 | { 45 | return GetTypeHashHelper(StreamKey.StreamType) + 23 * GetTypeHashHelper(StreamKey.StreamName); 46 | } 47 | 48 | FString ToString() const 49 | { 50 | FString TypeString; 51 | 52 | switch (StreamType) 53 | { 54 | case ERealtimeMeshStreamType::Unknown: 55 | TypeString += "Unknown"; 56 | break; 57 | case ERealtimeMeshStreamType::Vertex: 58 | TypeString += "Vertex"; 59 | break; 60 | case ERealtimeMeshStreamType::Index: 61 | TypeString += "Index"; 62 | break; 63 | } 64 | 65 | return TEXT("[") + StreamName.ToString() + TEXT(", Type:") + TypeString + TEXT("]"); 66 | } 67 | 68 | friend FArchive& operator<<(FArchive& Ar, FRealtimeMeshStreamKey& Key); 69 | }; 70 | 71 | struct FRealtimeMeshStreamRange 72 | { 73 | FInt32Range Vertices; 74 | FInt32Range Indices; 75 | 76 | FRealtimeMeshStreamRange() { } 77 | 78 | FRealtimeMeshStreamRange(const FInt32Range& VerticesRange, const FInt32Range& IndicesRange) 79 | : Vertices(VerticesRange), Indices(IndicesRange) { } 80 | 81 | FRealtimeMeshStreamRange(uint32 VerticesLower, uint32 VerticesUpper, uint32 IndicesLower, uint32 IndicesUpper) 82 | : Vertices(VerticesLower, VerticesUpper), Indices(IndicesLower, IndicesUpper) { } 83 | 84 | FRealtimeMeshStreamRange(const FInt32RangeBound& VerticesLower, const FInt32RangeBound& VerticesUpper, 85 | const FInt32RangeBound& IndicesLower, const FInt32RangeBound& IndicesUpper) 86 | : Vertices(VerticesLower, VerticesUpper), Indices(IndicesLower, IndicesUpper) { } 87 | 88 | static FRealtimeMeshStreamRange Empty() { return FRealtimeMeshStreamRange(FInt32Range::Empty(), FInt32Range::Empty()); } 89 | 90 | int32 NumPrimitives(int32 VertexCountPerPrimitive) const 91 | { 92 | if (!Indices.HasLowerBound() || !Indices.HasUpperBound() || Indices.IsDegenerate()) 93 | { 94 | return 0; 95 | } 96 | return (GetMaxIndex() - GetMinIndex() + 1) / VertexCountPerPrimitive; 97 | } 98 | 99 | int32 NumVertices() const 100 | { 101 | if (!Vertices.HasLowerBound() || !Vertices.HasUpperBound() || Vertices.IsDegenerate()) 102 | { 103 | return 0; 104 | } 105 | return GetMaxVertex() - GetMinVertex() + 1; 106 | } 107 | 108 | int32 GetMinVertex() const { return Vertices.GetLowerBound().IsExclusive() ? Vertices.GetLowerBoundValue() + 1 : Vertices.GetLowerBoundValue(); } 109 | int32 GetMaxVertex() const { return Vertices.GetUpperBound().IsExclusive() ? Vertices.GetUpperBoundValue() - 1 : Vertices.GetUpperBoundValue(); } 110 | 111 | int32 GetMinIndex() const { return Indices.GetLowerBound().IsExclusive() ? Indices.GetLowerBoundValue() + 1 : Indices.GetLowerBoundValue(); } 112 | int32 GetMaxIndex() const { return Indices.GetUpperBound().IsExclusive() ? Indices.GetUpperBoundValue() - 1 : Indices.GetUpperBoundValue(); } 113 | 114 | bool operator==(const FRealtimeMeshStreamRange& Other) const 115 | { 116 | return Vertices == Other.Vertices && Indices == Other.Indices; 117 | } 118 | 119 | bool operator!=(const FRealtimeMeshStreamRange& Other) const 120 | { 121 | return Vertices != Other.Vertices || Indices != Other.Indices; 122 | } 123 | 124 | bool Overlaps(const FRealtimeMeshStreamRange& Other) const 125 | { 126 | return Vertices.Overlaps(Other.Vertices) && Indices.Overlaps(Other.Indices); 127 | } 128 | 129 | bool Contains(const FRealtimeMeshStreamRange& Other) const 130 | { 131 | return Vertices.Contains(Other.Vertices) && Indices.Contains(Other.Indices); 132 | } 133 | 134 | FRealtimeMeshStreamRange Intersection(const FRealtimeMeshStreamRange& Other) const 135 | { 136 | return FRealtimeMeshStreamRange( 137 | FInt32Range::Intersection(Vertices, Other.Vertices), 138 | FInt32Range::Intersection(Indices, Other.Indices)); 139 | } 140 | 141 | FRealtimeMeshStreamRange Hull(const FRealtimeMeshStreamRange& Other) const 142 | { 143 | return FRealtimeMeshStreamRange(FInt32Range::Hull(Vertices, Other.Vertices), FInt32Range::Hull(Indices, Other.Indices)); 144 | } 145 | 146 | friend FArchive& operator<<(FArchive& Ar, FRealtimeMeshStreamRange& Range) 147 | { 148 | Ar << Range.Vertices << Range.Indices; 149 | return Ar; 150 | } 151 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Mesh/RealtimeMeshBasicShapeTools.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Kismet/BlueprintFunctionLibrary.h" 7 | #include "RealtimeMeshBasicShapeTools.generated.h" 8 | 9 | namespace RealtimeMesh 10 | { 11 | struct FRealtimeMeshStreamSet; 12 | } 13 | 14 | struct FRealtimeMeshSimpleMeshData; 15 | 16 | /** 17 | * 18 | */ 19 | UCLASS(meta=(ScriptName="RealtimeMeshBasicShapeTools")) 20 | class REALTIMEMESHCOMPONENT_API URealtimeMeshBasicShapeTools : public UBlueprintFunctionLibrary 21 | { 22 | GENERATED_BODY() 23 | 24 | public: 25 | 26 | 27 | static void AppendBoxMesh(RealtimeMesh::FRealtimeMeshStreamSet& StreamSet, FVector3f BoxRadius, FTransform3f BoxTransform = FTransform3f::Identity, int32 NewMaterialGroup = 0, FColor Color = FColor::White); 28 | 29 | static void AppendMesh(RealtimeMesh::FRealtimeMeshStreamSet& TargetMeshData, const RealtimeMesh::FRealtimeMeshStreamSet& MeshDataToAdd, const FTransform3f& Transform = FTransform3f::Identity, bool bSkipMissingStreams = false); 30 | 31 | }; 32 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Mesh/RealtimeMeshCardRepresentation.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshCore.h" 7 | 8 | #if RMC_ENGINE_ABOVE_5_2 9 | // This works around a compile issue in 5.2+ where there's invalid code at the bottom of MeshCardRepresentation.h 10 | // TODO: I think this is still a bug to this day, so could submit a bug fix for it. 11 | UE_PUSH_MACRO("UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2") 12 | #ifdef UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2 13 | #undef UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2 14 | #endif 15 | #define UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2 0 16 | #include "MeshCardBuild.h" 17 | UE_POP_MACRO("UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2") 18 | #else 19 | #include "MeshCardRepresentation.h" 20 | #endif 21 | 22 | #include "RealtimeMeshCardRepresentation.generated.h" 23 | 24 | /** 25 | * 26 | */ 27 | USTRUCT(BlueprintType) 28 | struct REALTIMEMESHCOMPONENT_API FRealtimeMeshCardRepresentation 29 | { 30 | GENERATED_BODY() 31 | private: 32 | FBox Bounds; 33 | bool bMostlyTwoSided; 34 | TArray CardBuildData; 35 | 36 | static std::atomic NextCardRepresentationId; 37 | public: 38 | FRealtimeMeshCardRepresentation(); 39 | FRealtimeMeshCardRepresentation(const FCardRepresentationData& Src); 40 | ~FRealtimeMeshCardRepresentation(); 41 | 42 | bool IsValid() const; 43 | 44 | const FBox& GetBounds() const { return Bounds; } 45 | 46 | void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) const; 47 | SIZE_T GetResourceSizeBytes() const; 48 | 49 | void Serialize(FArchive& Ar, UObject* Owner); 50 | 51 | FCardRepresentationData CreateRenderingData() const; 52 | FCardRepresentationData MoveToRenderingData(); 53 | }; 54 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Mesh/RealtimeMeshDistanceField.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "DistanceFieldAtlas.h" 7 | #include "RealtimeMeshCore.h" 8 | #include "RealtimeMeshDistanceField.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | USTRUCT(BlueprintType) 14 | struct REALTIMEMESHCOMPONENT_API FRealtimeMeshDistanceField 15 | { 16 | GENERATED_BODY() 17 | private: 18 | TStaticArray Mips; 19 | TArray AlwaysLoadedMip; 20 | FByteBulkData StreamableMips; 21 | 22 | FBox3f Bounds; 23 | bool bMostlyTwoSided; 24 | public: 25 | FRealtimeMeshDistanceField(); 26 | FRealtimeMeshDistanceField(const FDistanceFieldVolumeData& Src); 27 | ~FRealtimeMeshDistanceField(); 28 | 29 | bool IsValid() const; 30 | 31 | const FBox3f& GetBounds() const { return Bounds; } 32 | 33 | void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) const; 34 | SIZE_T GetResourceSizeBytes() const; 35 | 36 | void Serialize(FArchive& Ar, UObject* Owner); 37 | 38 | FDistanceFieldVolumeData CreateRenderingData() const; 39 | FDistanceFieldVolumeData MoveToRenderingData(); 40 | }; 41 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/Mesh/RealtimeMeshNaniteResourcesInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | 7 | namespace RealtimeMesh 8 | { 9 | struct IRealtimeMeshNaniteResources 10 | { 11 | virtual ~IRealtimeMeshNaniteResources() = default; 12 | 13 | virtual void InitResources(URealtimeMesh* Owner) = 0; 14 | }; 15 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "RealtimeMeshComponent.h" 8 | #include "Mesh/RealtimeMeshBlueprintMeshBuilder.h" 9 | #include "RealtimeMeshActor.generated.h" 10 | 11 | UCLASS(ConversionRoot, ComponentWrapperClass, ClassGroup=RealtimeMesh, meta = (ChildCanTick)) 12 | class REALTIMEMESHCOMPONENT_API ARealtimeMeshActor : public AActor 13 | { 14 | GENERATED_BODY() 15 | 16 | protected: 17 | UPROPERTY(Category = "RealtimeMeshActor", VisibleAnywhere, BlueprintReadOnly, 18 | meta = (ExposeFunctionCategories = "Mesh,Rendering,Physics,Components|StaticMesh", AllowPrivateAccess = "true")) 19 | TObjectPtr RealtimeMeshComponent; 20 | 21 | public: 22 | 23 | UFUNCTION(BlueprintCallable, Category="RealtimeMesh") 24 | URealtimeMeshStream* MakeStream(const FRealtimeMeshStreamKey& StreamKey, ERealtimeMeshSimpleStreamType StreamType, int32 NumElements); 25 | 26 | UFUNCTION(BlueprintCallable, Category="RealtimeMesh") 27 | URealtimeMeshStreamSet* MakeStreamSet(); 28 | 29 | UFUNCTION(BlueprintCallable, Category="RealtimeMesh") 30 | URealtimeMeshLocalBuilder* MakeMeshBuilder(ERealtimeMeshSimpleStreamConfig WantedTangents = ERealtimeMeshSimpleStreamConfig::Normal, 31 | ERealtimeMeshSimpleStreamConfig WantedTexCoords = ERealtimeMeshSimpleStreamConfig::Normal, 32 | bool bWants32BitIndices = false, 33 | ERealtimeMeshSimpleStreamConfig WantedPolyGroupType = ERealtimeMeshSimpleStreamConfig::None, 34 | bool bWantsColors = true, int32 WantedTexCoordChannels = 1, bool bKeepExistingData = true); 35 | 36 | 37 | /** 38 | * If true, the then OnGenerateMesh will get called, and you should move 39 | * your mesh generation logic there, instead of the construction script. 40 | * This helps improve editor performace by not running generation in 41 | * construction which fires every frame when dragging an actor in the editor. 42 | */ 43 | UPROPERTY(Category = "RealtimeMeshActor", EditAnywhere, BlueprintReadWrite) 44 | bool bDeferGeneration = false; 45 | 46 | /** 47 | * If true, the RealtimeMeshComponent will be "Frozen" in its current state, and automatic rebuilding 48 | * will be disabled. However the RealtimeMesh can still be modified by explicitly-called functions/etc. 49 | */ 50 | UPROPERTY(Category = "RealtimeMeshActor", EditAnywhere, BlueprintReadWrite) 51 | bool bFrozen = false; 52 | 53 | /** If true, the RealtimeMeshComponent will be cleared before the OnRebuildGeneratedMesh event is executed. */ 54 | UPROPERTY(Category = "RealtimeMeshActor|Advanced", EditAnywhere, BlueprintReadWrite) 55 | bool bResetOnRebuild = true; 56 | 57 | public: 58 | ARealtimeMeshActor(); 59 | virtual ~ARealtimeMeshActor() override; 60 | 61 | UFUNCTION(Category = RealtimeMeshActor, BlueprintCallable) 62 | URealtimeMeshComponent* GetRealtimeMeshComponent() const { return RealtimeMeshComponent; } 63 | 64 | /** 65 | * This event will be fired to notify the BP that the generated Mesh should 66 | * be rebuilt. GeneratedRealtimeMeshActor BP subclasses should rebuild their 67 | * meshes on this event, instead of doing so directly from the Construction Script. 68 | */ 69 | UFUNCTION(BlueprintNativeEvent, CallInEditor, Category = "Events") 70 | void OnGenerateMesh(); 71 | 72 | virtual void OnGenerateMesh_Implementation() 73 | { 74 | } 75 | 76 | 77 | /** 78 | * This function will fire the OnRebuildGeneratedMesh event if the actor has been 79 | * marked for a pending rebuild (eg via OnConstruction) 80 | */ 81 | virtual void ExecuteRebuildGeneratedMeshIfPending(); 82 | 83 | public: 84 | //~ Begin UObject/AActor Interface 85 | virtual void BeginPlay() override; 86 | virtual void PostLoad() override; 87 | virtual void PostActorCreated() override; 88 | virtual void OnConstruction(const FTransform& Transform) override; 89 | virtual void Destroyed() override; 90 | 91 | // these are called when Actor exists in a sublevel that is hidden/shown 92 | virtual void PreRegisterAllComponents() override; 93 | virtual void PostUnregisterAllComponents() override; 94 | 95 | #if WITH_EDITOR 96 | virtual void PostEditUndo() override; 97 | #endif 98 | 99 | protected: 100 | // this internal flag is set in OnConstruction, and will cause ExecuteRebuildGeneratedMesh to 101 | // fire the OnRebuildGeneratedMesh event, after which the flag will be cleared 102 | bool bGeneratedMeshRebuildPending = false; 103 | 104 | // indicates that this Actor is registered with the UEditorGeometryGenerationSubsystem, which 105 | // is where the mesh rebuilds are executed 106 | bool bIsRegisteredWithGenerationManager = false; 107 | 108 | void RegisterWithGenerationManager(); 109 | void UnregisterWithGenerationManager(); 110 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshComponent.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | #include "RealtimeMesh.h" 7 | #include "Components/MeshComponent.h" 8 | #include "RealtimeMeshComponent.generated.h" 9 | 10 | 11 | /** 12 | * Component that allows you to specify custom triangle mesh geometry for rendering and collision. 13 | */ 14 | UCLASS(ClassGroup=(Rendering, Common), HideCategories=(Object, Activation, "Components|Activation"), ShowCategories=(Mobility), Meta = (BlueprintSpawnableComponent)) 15 | class REALTIMEMESHCOMPONENT_API URealtimeMeshComponent : public UMeshComponent 16 | { 17 | GENERATED_BODY() 18 | 19 | private: 20 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Replicated, Category = RealtimeMesh, Meta = (AllowPrivateAccess = "true", DisplayName = "RealtimeMesh", ReplicatedUsing="OnRep_RealtimeMesh")) 21 | TObjectPtr RealtimeMesh; 22 | 23 | public: 24 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "RealtimeMesh") 25 | bool KeepMomentumOnCollisionUpdate = false; 26 | 27 | URealtimeMeshComponent(); 28 | 29 | UFUNCTION(BlueprintCallable, Category = "Components|RealtimeMeshComponent") 30 | void SetRealtimeMesh(URealtimeMesh* NewMesh); 31 | 32 | UFUNCTION(BlueprintCallable, Category = "Components|RealtimeMeshComponent", meta=(DeterminesOutputType="MeshClass")) 33 | URealtimeMesh* InitializeRealtimeMesh(UPARAM(meta = (AllowAbstract = "false")) TSubclassOf MeshClass); 34 | 35 | template 36 | MeshType* InitializeRealtimeMesh(TSubclassOf MeshClass = MeshType::StaticClass()) 37 | { 38 | return CastChecked(InitializeRealtimeMesh(MeshClass)); 39 | } 40 | 41 | /** Clears the geometry for ALL collision only sections */ 42 | UFUNCTION(BlueprintCallable, Category = "Components|RealtimeMeshComponent") 43 | FORCEINLINE URealtimeMesh* GetRealtimeMesh() const 44 | { 45 | if (RealtimeMesh && RealtimeMesh->IsValidLowLevel()) 46 | { 47 | return RealtimeMesh; 48 | } 49 | return nullptr; 50 | } 51 | 52 | template 53 | FORCEINLINE RealtimeMeshType* GetRealtimeMeshAs() const 54 | { 55 | if (RealtimeMesh && RealtimeMesh->IsValidLowLevel()) 56 | { 57 | return CastChecked(RealtimeMesh); 58 | } 59 | return nullptr; 60 | } 61 | 62 | UFUNCTION() 63 | void OnRep_RealtimeMesh(class URealtimeMesh *OldRealtimeMesh); 64 | 65 | public: 66 | void GetStreamingRenderAssetInfo(FStreamingTextureLevelContext& LevelContext, TArray& OutStreamingRenderAssets) const 67 | { 68 | //@TODO: Need to support this for proper texture streaming 69 | return Super::GetStreamingRenderAssetInfo(LevelContext, OutStreamingRenderAssets); 70 | } 71 | 72 | virtual void OnRegister() override; 73 | virtual void OnUnregister() override; 74 | 75 | 76 | //~ Begin USceneComponent Interface. 77 | virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; 78 | 79 | virtual bool IsSupportedForNetworking() const override 80 | { 81 | return true; 82 | } 83 | 84 | //~ Begin USceneComponent Interface. 85 | 86 | //~ Begin UPrimitiveComponent Interface. 87 | virtual FPrimitiveSceneProxy* CreateSceneProxy() override; 88 | virtual class UBodySetup* GetBodySetup() override; 89 | 90 | virtual bool UseNaniteOverrideMaterials() const override; 91 | //~ End UPrimitiveComponent Interface. 92 | public: 93 | //~ Begin UMeshComponent Interface 94 | virtual int32 GetMaterialIndex(FName MaterialSlotName) const override; 95 | virtual FName GetMaterialSlotName(uint32 Index) const; 96 | virtual TArray GetMaterialSlotNames() const override; 97 | virtual bool IsMaterialSlotNameValid(FName MaterialSlotName) const override; 98 | virtual void GetUsedMaterials(TArray& OutMaterials, bool bGetDebugMaterials = false) const override; 99 | //~ End UMeshComponent Interface 100 | 101 | //~ Being UPrimitiveComponent Interface 102 | virtual int32 GetNumMaterials() const override; 103 | virtual UMaterialInterface* GetMaterial(int32 ElementIndex) const override; 104 | #if RMC_ENGINE_ABOVE_5_4 105 | virtual void CollectPSOPrecacheData(const FPSOPrecacheParams& BasePrecachePSOParams, FMaterialInterfacePSOPrecacheParamsList& OutParams) override; 106 | #endif 107 | //~ End UPrimitiveComponent Interface 108 | 109 | virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; 110 | 111 | private: 112 | virtual void BindToEvents(URealtimeMesh* RealtimeMesh); 113 | virtual void UnbindFromEvents(URealtimeMesh* RealtimeMesh); 114 | 115 | virtual void HandleBoundsUpdated(URealtimeMesh* IncomingMesh); 116 | virtual void HandleMeshRenderingDataChanged(URealtimeMesh* IncomingMesh, bool bShouldProxyRecreate); 117 | virtual void HandleCollisionBodyUpdated(URealtimeMesh* RealtimeMesh, UBodySetup* BodySetup); 118 | 119 | virtual void UpdateCollision(); 120 | 121 | friend class FRealtimeMeshDetailsCustomization; 122 | }; 123 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshComponentModule.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | /** 9 | * The public interface to this module 10 | */ 11 | class IRealtimeMeshComponentPlugin : public IModuleInterface 12 | { 13 | public: 14 | /** 15 | * Singleton-like access to this module's interface. This is just for convenience! 16 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 17 | * 18 | * @return Returns singleton instance, loading the module on demand if needed 19 | */ 20 | static inline IRealtimeMeshComponentPlugin& Get() 21 | { 22 | return FModuleManager::LoadModuleChecked("RealtimeMeshComponent"); 23 | } 24 | 25 | /** 26 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 27 | * 28 | * @return True if the module is loaded and ready to use 29 | */ 30 | static inline bool IsAvailable() 31 | { 32 | return FModuleManager::Get().IsModuleLoaded("RealtimeMeshComponent"); 33 | } 34 | }; 35 | 36 | REALTIMEMESHCOMPONENT_API DECLARE_LOG_CATEGORY_EXTERN(LogRealtimeMesh, Warning, All); 37 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshCore.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Runtime/Launch/Resources/Version.h" 7 | #include "StaticMeshResources.h" 8 | #include "Core/RealtimeMeshInterfaceFwd.h" 9 | 10 | DECLARE_STATS_GROUP(TEXT("RealtimeMesh"), STATGROUP_RealtimeMesh, STATCAT_Advanced); 11 | 12 | namespace RealtimeMesh 13 | { 14 | // Custom version for runtime mesh serialization 15 | namespace FRealtimeMeshVersion 16 | { 17 | enum Type 18 | { 19 | Initial = 0, 20 | StreamsNowHoldEntireKey = 1, 21 | DataRestructure = 2, 22 | CollisionUpdateFlowRestructure = 3, 23 | StreamKeySizeChanged = 4, 24 | RemovedNamedStreamElements = 5, 25 | SimpleMeshStoresCollisionConfig = 6, 26 | ImprovingDataTypes = 7, 27 | SimpleMeshStoresCustomComplexCollision = 8, 28 | DistanceFieldAndCardRepresentationSupport = 9, 29 | SupportOptionalDataSerialization = 10, 30 | CollisionOverhaul = 11, 31 | DrawTypeMovedToSectionGroup = 12, 32 | ActorSupportsOptionalConstructionDefer = 13, 33 | 34 | // ------------------------------------------------------ 35 | VersionPlusOne, 36 | LatestVersion = VersionPlusOne - 1 37 | }; 38 | 39 | // The GUID for this custom version 40 | const static FGuid GUID = FGuid(0xAF3DD80C, 0x4C114B25, 0x9C7A9515, 0x5062D6E9); 41 | } 42 | 43 | 44 | 45 | /** Deleter function for TSharedPtrs that only allows the object to be destructed on the render thread. */ 46 | template 47 | struct FRealtimeMeshRenderThreadDeleter 48 | { 49 | void operator()(Type* Object) const 50 | { 51 | // This is a custom deleter to make sure the runtime mesh proxy is only ever deleted on the rendering thread. 52 | if (IsInRenderingThread()) 53 | { 54 | delete Object; 55 | } 56 | else 57 | { 58 | ENQUEUE_RENDER_COMMAND(FRealtimeMeshProxyDeleterCommand)( 59 | [Object](FRHICommandListImmediate& RHICmdList) 60 | { 61 | delete static_cast(Object); 62 | } 63 | ); 64 | } 65 | } 66 | }; 67 | 68 | #define CREATE_RMC_PTR_TYPES(TypeName) \ 69 | using TypeName##Ref = TSharedRef; \ 70 | using TypeName##Ptr = TSharedPtr; \ 71 | using TypeName##WeakPtr = TWeakPtr; \ 72 | using TypeName##ConstRef = TSharedRef; \ 73 | using TypeName##ConstPtr = TSharedPtr; \ 74 | using TypeName##ConstWeakPtr = TWeakPtr; 75 | 76 | struct FRealtimeMeshGPUUpdateBuilder; 77 | 78 | struct FRealtimeMeshStream; 79 | 80 | struct FRealtimeMeshUpdateState; 81 | CREATE_RMC_PTR_TYPES(FRealtimeMeshUpdateState); 82 | 83 | class FRealtimeMeshVertexFactory; 84 | CREATE_RMC_PTR_TYPES(FRealtimeMeshVertexFactory); 85 | 86 | class FRealtimeMeshSectionGroupProxy; 87 | CREATE_RMC_PTR_TYPES(FRealtimeMeshSectionGroupProxy); 88 | 89 | class FRealtimeMeshSectionProxy; 90 | CREATE_RMC_PTR_TYPES(FRealtimeMeshSectionProxy); 91 | 92 | class FRealtimeMeshLODProxy; 93 | CREATE_RMC_PTR_TYPES(FRealtimeMeshLODProxy); 94 | 95 | class FRealtimeMeshProxy; 96 | CREATE_RMC_PTR_TYPES(FRealtimeMeshProxy); 97 | 98 | class FRealtimeMeshSharedResources; 99 | CREATE_RMC_PTR_TYPES(FRealtimeMeshSharedResources); 100 | 101 | class FRealtimeMeshSectionGroup; 102 | CREATE_RMC_PTR_TYPES(FRealtimeMeshSectionGroup); 103 | 104 | class FRealtimeMeshSection; 105 | CREATE_RMC_PTR_TYPES(FRealtimeMeshSection); 106 | 107 | class FRealtimeMeshLOD; 108 | CREATE_RMC_PTR_TYPES(FRealtimeMeshLOD); 109 | 110 | class FRealtimeMesh; 111 | CREATE_RMC_PTR_TYPES(FRealtimeMesh); 112 | 113 | #undef CREATE_RMC_PTR_TYPES 114 | 115 | template 116 | using TFixedLODArray = TArray>; 117 | 118 | class FRealtimeMeshGPUBuffer; 119 | } 120 | 121 | 122 | class URealtimeMesh; 123 | class URealtimeMeshComponent; 124 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Kismet/BlueprintFunctionLibrary.h" 6 | #include "RealtimeMeshLibrary.generated.h" 7 | 8 | 9 | UENUM() 10 | enum class ERealtimeMeshCommonStream : uint8 11 | { 12 | Unknown, 13 | Position, 14 | Tangents, 15 | TexCoords, 16 | Colors, 17 | Triangles, 18 | DepthOnlyTriangles, 19 | PolyGroups, 20 | DepthOnlyPolyGroups 21 | }; 22 | 23 | UCLASS(meta=(ScriptName="RealtimeMeshLibrary")) 24 | class REALTIMEMESHCOMPONENT_API URealtimeMeshBlueprintFunctionLibrary : public UBlueprintFunctionLibrary 25 | { 26 | GENERATED_BODY() 27 | 28 | public: 29 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key", meta = (DisplayName = "LODIndex to LODKey", CompactNodeTitle = "->", BlueprintAutocast)) 30 | static FRealtimeMeshLODKey Conv_IntToRealtimeMeshLODKey(int32 LODIndex); 31 | 32 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 33 | static FRealtimeMeshLODKey MakeLODKey(int32 LODIndex); 34 | 35 | 36 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 37 | static FRealtimeMeshSectionGroupKey MakeSectionGroupKeyUnique(const FRealtimeMeshLODKey& LODKey); 38 | 39 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 40 | static FRealtimeMeshSectionGroupKey MakeSectionGroupKeyIndexed(const FRealtimeMeshLODKey& LODKey, int32 SectionGroupIndex); 41 | 42 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 43 | static FRealtimeMeshSectionGroupKey MakeSectionGroupKeyNamed(const FRealtimeMeshLODKey& LODKey, FName GroupName); 44 | 45 | 46 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 47 | static FRealtimeMeshSectionKey MakeSectionKeyUnique(const FRealtimeMeshSectionGroupKey& SectionGroupKey); 48 | 49 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 50 | static FRealtimeMeshSectionKey MakeSectionKeyIndexed(const FRealtimeMeshSectionGroupKey& SectionGroupKey, int32 SectionIndex); 51 | 52 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 53 | static FRealtimeMeshSectionKey MakeSectionKeyNamed(const FRealtimeMeshSectionGroupKey& SectionGroupKey, FName SectionName); 54 | 55 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Stream") 56 | static FRealtimeMeshSectionKey MakeSectionKeyForPolygonGroup(const FRealtimeMeshSectionGroupKey& SectionGroupKey, int32 PolygonGroup); 57 | 58 | 59 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 60 | static void BreakLODKey(const FRealtimeMeshLODKey& LODKey, int32& LODIndex); 61 | 62 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Stream") 63 | static FRealtimeMeshStreamRange MakeStreamRange(int32 VerticesLowerInclusive = 0, int32 VerticesUpperExclusive = 0, int32 IndicesLowerInclusive = 0, int32 IndicesUpperExclusive = 0); 64 | 65 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 66 | static FRealtimeMeshStreamKey MakeStreamKey(ERealtimeMeshStreamType StreamType, FName StreamName); 67 | 68 | UFUNCTION(BlueprintPure, Category = "RealtimeMesh|Key") 69 | static FRealtimeMeshStreamKey GetCommonStreamKey(ERealtimeMeshCommonStream StreamType); 70 | }; 71 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshSubsystem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshCore.h" 7 | #include "Subsystems/WorldSubsystem.h" 8 | #include "RealtimeMeshSubsystem.generated.h" 9 | 10 | class UWorld; 11 | class ARealtimeMeshActor; 12 | 13 | /** 14 | * URealtimeMeshEditorSubsystem manages recomputation of "generated" mesh actors, eg 15 | * to provide procedural mesh generation in-Editor. Generally such procedural mesh generation 16 | * is expensive, and if many objects need to be generated, the regeneration needs to be 17 | * managed at a higher level to ensure that the Editor remains responsive/interactive. 18 | * 19 | * ARealtimeMeshActors register themselves with this Subsystem, and 20 | * allow the Subsystem to tell them when they should regenerate themselves (if necessary). 21 | * The current behavior is to run all pending generations on a Tick, however in future 22 | * this regeneration will be more carefully managed via throttling / timeslicing / etc. 23 | * 24 | */ 25 | UCLASS() 26 | class REALTIMEMESHCOMPONENT_API URealtimeMeshSubsystem : public UTickableWorldSubsystem 27 | { 28 | GENERATED_BODY() 29 | 30 | public: 31 | URealtimeMeshSubsystem(); 32 | 33 | virtual bool ShouldCreateSubsystem(UObject* Outer) const override; 34 | 35 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 36 | virtual void Deinitialize() override; 37 | 38 | virtual bool IsTickable() const override; 39 | virtual bool IsTickableInEditor() const override; 40 | virtual void Tick(float DeltaTime) override; 41 | 42 | virtual TStatId GetStatId() const override; 43 | 44 | bool RegisterGeneratedMeshActor(ARealtimeMeshActor* Actor); 45 | void UnregisterGeneratedMeshActor(ARealtimeMeshActor* Actor); 46 | 47 | static URealtimeMeshSubsystem* GetInstance(UWorld* World); 48 | 49 | private: 50 | 51 | TSet> ActiveGeneratedActors; 52 | TSharedPtr SceneViewExtension; 53 | bool bInitialized; 54 | }; 55 | 56 | 57 | namespace RealtimeMesh 58 | { 59 | struct FRealtimeMeshEndOfFrameUpdateManager 60 | { 61 | private: 62 | FCriticalSection SyncRoot; 63 | TSet MeshesToUpdate; 64 | FDelegateHandle EndOfFrameUpdateHandle; 65 | 66 | void OnPreSendAllEndOfFrameUpdates(UWorld* World); 67 | 68 | public: 69 | ~FRealtimeMeshEndOfFrameUpdateManager(); 70 | 71 | void MarkComponentForUpdate(const FRealtimeMeshWeakPtr& InMesh); 72 | void ClearComponentForUpdate(const FRealtimeMeshWeakPtr& InMesh); 73 | 74 | static FRealtimeMeshEndOfFrameUpdateManager& Get(); 75 | }; 76 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RealtimeMeshThreadingSubsystem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Subsystems/EngineSubsystem.h" 7 | #include "Misc/QueuedThreadPool.h" 8 | #include "RealtimeMeshThreadingSubsystem.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class REALTIMEMESHCOMPONENT_API URealtimeMeshThreadingSubsystem : public UEngineSubsystem 15 | { 16 | GENERATED_BODY() 17 | private: 18 | TUniquePtr ThreadPool; 19 | public: 20 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 21 | virtual void Deinitialize() override; 22 | 23 | static URealtimeMeshThreadingSubsystem* Get(); 24 | 25 | FQueuedThreadPool& GetThreadPool(); 26 | }; 27 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RenderProxy/RealtimeMeshComponentProxy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshSectionProxy.h" 7 | #if RMC_ENGINE_ABOVE_5_2 8 | #include "PrimitiveSceneProxy.h" 9 | #endif 10 | 11 | class UBodySetup; 12 | class URealtimeMeshComponent; 13 | 14 | namespace RealtimeMesh 15 | { 16 | /** Runtime mesh scene proxy */ 17 | class REALTIMEMESHCOMPONENT_API FRealtimeMeshComponentSceneProxy : public FPrimitiveSceneProxy 18 | { 19 | private: 20 | // THis is the proxy we're rendering 21 | FRealtimeMeshProxyRef RealtimeMeshProxy; 22 | TSharedPtr MeshReferencingHandle; 23 | 24 | // All the in use materials 25 | FRealtimeMeshMaterialProxyMap MaterialMap; 26 | 27 | // Reference all the in-use buffers so that as long as this proxy is around these buffers will be too. 28 | // This is meant only for statically drawn sections. Dynamically drawn sections can update safely in place. 29 | // Static sections get new buffers on each update. 30 | FRealtimeMeshResourceReferenceList StaticResources; 31 | 32 | // Reference to the body setup for rendering. 33 | UBodySetup* BodySetup; 34 | 35 | // Store the combined material relevance. 36 | FMaterialRelevance MaterialRelevance; 37 | 38 | uint32 bAnyMaterialUsesDithering : 1; 39 | uint32 bSupportsRayTracing : 1; 40 | 41 | public: 42 | /*Constructor, copies the whole mesh data to feed to UE */ 43 | FRealtimeMeshComponentSceneProxy(URealtimeMeshComponent* Component, const FRealtimeMeshProxyRef& InRealtimeMeshProxy); 44 | 45 | virtual ~FRealtimeMeshComponentSceneProxy() override; 46 | 47 | #if RMC_ENGINE_ABOVE_5_4 48 | virtual void CreateRenderThreadResources(FRHICommandListBase& RHICmdList) override; 49 | #else 50 | virtual void CreateRenderThreadResources() override; 51 | #endif 52 | 53 | virtual bool CanBeOccluded() const override; 54 | 55 | virtual int32 GetLOD(const FSceneView* View) const override; 56 | 57 | virtual uint32 GetMemoryFootprint(void) const override; 58 | 59 | 60 | virtual SIZE_T GetTypeHash() const override; 61 | 62 | virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override; 63 | 64 | virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI) override; 65 | 66 | 67 | virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, 68 | FMeshElementCollector& Collector) const override; 69 | 70 | virtual void GetDistanceFieldAtlasData(const FDistanceFieldVolumeData*& OutDistanceFieldData, float& SelfShadowBias) const override; 71 | virtual void GetDistanceFieldInstanceData(TArray& InstanceLocalToPrimitiveTransforms) const override; 72 | virtual bool HasDistanceFieldRepresentation() const override; 73 | virtual bool HasDynamicIndirectShadowCasterRepresentation() const override; 74 | 75 | virtual const FCardRepresentationData* GetMeshCardRepresentation() const override; 76 | 77 | #if RHI_RAYTRACING 78 | virtual bool IsRayTracingRelevant() const override { return true; } 79 | virtual bool HasRayTracingRepresentation() const override { return bSupportsRayTracing; } 80 | virtual bool IsRayTracingStaticRelevant() const override; 81 | 82 | #if RMC_ENGINE_ABOVE_5_4 83 | virtual TArray GetStaticRayTracingGeometries() const override; 84 | #endif 85 | 86 | /** Gathers dynamic ray tracing instances from this proxy. */ 87 | #if RMC_ENGINE_ABOVE_5_5 88 | virtual void GetDynamicRayTracingInstances(class FRayTracingInstanceCollector& Collector) override; 89 | #else 90 | virtual void GetDynamicRayTracingInstances(struct FRayTracingMaterialGatheringContext& Context, TArray& OutRayTracingInstances) override; 91 | #endif 92 | #endif // RHI_RAYTRACING 93 | 94 | protected: 95 | SIZE_T GetAllocatedSize(void) const; 96 | 97 | int8 GetCurrentFirstLOD() const; 98 | 99 | int8 ComputeTemporalStaticMeshLOD(const FVector4& Origin, const float SphereRadius, const FSceneView& View, int32 MinLOD, float FactorScale, int32 SampleIndex) const; 100 | int8 ComputeStaticMeshLOD(const FVector4& Origin, const float SphereRadius, const FSceneView& View, int32 MinLOD, float FactorScale) const; 101 | FLODMask GetLODMask(const FSceneView* View) const; 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RenderProxy/RealtimeMeshLODProxy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | #include "RealtimeMeshProxyShared.h" 7 | #include "RealtimeMeshSectionGroupProxy.h" 8 | #include "Core/RealtimeMeshLODConfig.h" 9 | 10 | namespace RealtimeMesh 11 | { 12 | using FRealtimeMeshSectionGroupMask = TBitArray>; 13 | 14 | class FRealtimeMeshActiveSectionGroupIterator 15 | { 16 | private: 17 | const FRealtimeMeshLODProxy& Proxy; 18 | TConstSetBitIterator> Iterator; 19 | 20 | public: 21 | FRealtimeMeshActiveSectionGroupIterator(const FRealtimeMeshLODProxy& InProxy, const FRealtimeMeshSectionGroupMask& InMask) 22 | : Proxy(InProxy), Iterator(TConstSetBitIterator(InMask)) { } 23 | 24 | /** Forwards iteration operator. */ 25 | FORCEINLINE FRealtimeMeshActiveSectionGroupIterator& operator++() 26 | { 27 | ++Iterator; 28 | return *this; 29 | } 30 | 31 | FORCEINLINE bool operator==(const FRealtimeMeshActiveSectionGroupIterator& Other) const 32 | { 33 | return Iterator == Other.Iterator; 34 | } 35 | 36 | FORCEINLINE bool operator!=(const FRealtimeMeshActiveSectionGroupIterator& Other) const 37 | { 38 | return Iterator != Other.Iterator; 39 | } 40 | 41 | /** conversion to "bool" returning true if the iterator is valid. */ 42 | FORCEINLINE explicit operator bool() const 43 | { 44 | return (bool)Iterator; 45 | } 46 | /** inverse of the "bool" operator */ 47 | FORCEINLINE bool operator !() const 48 | { 49 | return !(bool)*this; 50 | } 51 | 52 | FRealtimeMeshSectionGroupProxy* operator*() const; 53 | FRealtimeMeshSectionGroupProxy& operator->() const; 54 | 55 | /** Index accessor. */ 56 | FORCEINLINE int32 GetIndex() const 57 | { 58 | return Iterator.GetIndex(); 59 | } 60 | }; 61 | 62 | 63 | class REALTIMEMESHCOMPONENT_API FRealtimeMeshLODProxy : public TSharedFromThis 64 | { 65 | private: 66 | const FRealtimeMeshSharedResourcesRef SharedResources; 67 | const FRealtimeMeshLODKey Key; 68 | TArray SectionGroups; 69 | TMap SectionGroupMap; 70 | FRealtimeMeshSectionGroupMask ActiveSectionGroupMask; 71 | 72 | FRealtimeMeshLODConfig Config; 73 | FRealtimeMeshDrawMask DrawMask; 74 | 75 | #if RHI_RAYTRACING 76 | int32 StaticRayTraceSectionGroup; 77 | #endif 78 | 79 | 80 | public: 81 | FRealtimeMeshLODProxy(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshLODKey& InKey); 82 | virtual ~FRealtimeMeshLODProxy(); 83 | 84 | const FRealtimeMeshLODKey& GetKey() const { return Key; } 85 | const FRealtimeMeshLODConfig& GetConfig() const { return Config; } 86 | FRealtimeMeshDrawMask GetDrawMask() const { return DrawMask; } 87 | FRealtimeMeshActiveSectionGroupIterator GetActiveSectionGroupMaskIter() const { return FRealtimeMeshActiveSectionGroupIterator(*this, ActiveSectionGroupMask); } 88 | float GetScreenSize() const { return Config.ScreenSize; } 89 | 90 | FRealtimeMeshSectionGroupProxyPtr GetSectionGroup(const FRealtimeMeshSectionGroupKey& SectionGroupKey) const; 91 | 92 | virtual void UpdateConfig(const FRealtimeMeshLODConfig& NewConfig); 93 | 94 | virtual void CreateSectionGroupIfNotExists(const FRealtimeMeshSectionGroupKey& SectionGroupKey); 95 | virtual void RemoveSectionGroup(const FRealtimeMeshSectionGroupKey& SectionGroupKey); 96 | 97 | #if RHI_RAYTRACING 98 | virtual FRayTracingGeometry* GetStaticRayTracingGeometry() const; 99 | #endif 100 | 101 | virtual void UpdateCachedState(FRHICommandListBase& RHICmdList); 102 | virtual void Reset(); 103 | 104 | protected: 105 | void RebuildSectionGroupMap(); 106 | 107 | friend class FRealtimeMeshActiveSectionGroupIterator; 108 | }; 109 | 110 | 111 | FORCEINLINE FRealtimeMeshSectionGroupProxy* FRealtimeMeshActiveSectionGroupIterator::operator*() const 112 | { 113 | check(Proxy.SectionGroups.IsValidIndex(Iterator.GetIndex())); 114 | return &Proxy.SectionGroups[Iterator.GetIndex()].Get(); 115 | } 116 | 117 | FORCEINLINE FRealtimeMeshSectionGroupProxy& FRealtimeMeshActiveSectionGroupIterator::operator->() const 118 | { 119 | check(Proxy.SectionGroups.IsValidIndex(Iterator.GetIndex())); 120 | return Proxy.SectionGroups[Iterator.GetIndex()].Get(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RenderProxy/RealtimeMeshNaniteProxyInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "NaniteSceneProxy.h" 7 | #include "RealtimeMeshCore.h" 8 | #include "Features/IModularFeatures.h" 9 | #include "Mesh/RealtimeMeshNaniteResourcesInterface.h" 10 | 11 | class URealtimeMeshComponent; 12 | 13 | namespace RealtimeMesh 14 | { 15 | struct FRealtimeMeshStreamSet; 16 | 17 | 18 | struct IRealtimeMeshNaniteSceneProxy : public Nanite::FSceneProxyBase 19 | { 20 | explicit IRealtimeMeshNaniteSceneProxy(const UPrimitiveComponent* Component) 21 | : Nanite::FSceneProxyBase((UPrimitiveComponent*)Component) 22 | { 23 | } 24 | }; 25 | 26 | class IRealtimeMeshNaniteSceneProxyManager : public IModuleInterface, public IModularFeature 27 | { 28 | public: 29 | static FName GetModularFeatureName() 30 | { 31 | return TEXT("RealtimeMeshNanite"); 32 | } 33 | 34 | static bool IsNaniteSupportAvailable() { return IModularFeatures::Get().IsModularFeatureAvailable(GetModularFeatureName()); } 35 | static IRealtimeMeshNaniteSceneProxyManager& GetNaniteModule() 36 | { 37 | return IModularFeatures::Get().GetModularFeature(GetModularFeatureName()); 38 | } 39 | 40 | virtual TSharedRef CreateNewResources(const Nanite::FResources& InResources) = 0; 41 | virtual bool ShouldUseNanite(URealtimeMeshComponent* RealtimeMeshComponent) = 0; 42 | virtual IRealtimeMeshNaniteSceneProxy* CreateNewSceneProxy(URealtimeMeshComponent* Component, const RealtimeMesh::FRealtimeMeshProxyRef& InRealtimeMeshProxy) = 0; 43 | 44 | virtual bool BuildRealtimeMeshNaniteData(Nanite::FResources& Resources, const FMeshNaniteSettings& Settings, const RealtimeMesh::FRealtimeMeshStreamSet& Streams) = 0; 45 | }; 46 | 47 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RenderProxy/RealtimeMeshProxyCommandBatch.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | #include "RealtimeMeshSectionProxy.h" 7 | 8 | 9 | namespace RealtimeMesh 10 | { 11 | struct FRealtimeMeshCommandBatchIntermediateFuture : public TSharedFromThis 12 | { 13 | TSharedRef> FinalPromise; 14 | ERealtimeMeshProxyUpdateStatus Result; 15 | uint8 bRenderThreadReady : 1; 16 | uint8 bGameThreadReady : 1; 17 | uint8 bFinalized : 1; 18 | 19 | FRealtimeMeshCommandBatchIntermediateFuture(); 20 | void FinalizeRenderThread(ERealtimeMeshProxyUpdateStatus Status); 21 | void FinalizeGameThread(); 22 | }; 23 | 24 | struct REALTIMEMESHCOMPONENT_API FRealtimeMeshProxyUpdateBuilder 25 | { 26 | public: 27 | using TaskFunctionType = TUniqueFunction; 28 | private: 29 | TArray Tasks; 30 | uint32 bRequiresProxyRecreate : 1; 31 | uint32 bIsIgnoringCommands : 1; 32 | public: 33 | FRealtimeMeshProxyUpdateBuilder(bool bShouldIgnoreCommands = false) 34 | : bRequiresProxyRecreate(false) 35 | , bIsIgnoringCommands(bShouldIgnoreCommands) 36 | { } 37 | UE_NONCOPYABLE(FRealtimeMeshProxyUpdateBuilder); 38 | 39 | FORCEINLINE bool IsValid() const { return !bIsIgnoringCommands; } 40 | FORCEINLINE operator bool() const { return IsValid(); } 41 | 42 | TFuture Commit(const TSharedRef& Mesh); 43 | 44 | void MarkForProxyRecreate() { bRequiresProxyRecreate = true; } 45 | void ClearProxyRecreate() { bRequiresProxyRecreate = false; } 46 | 47 | void AddMeshTask(TUniqueFunction&& Function, bool bInRequiresProxyRecreate = true); 48 | 49 | template 50 | void AddMeshTask(TUniqueFunction&& Function, bool bInRequiresProxyRecreate = true) 51 | { 52 | AddMeshTask([Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, FRealtimeMeshProxy& LOD) 53 | { 54 | Func(RHICmdList, static_cast(LOD)); 55 | }, bInRequiresProxyRecreate); 56 | } 57 | 58 | void AddLODTask(const FRealtimeMeshLODKey& LODKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate = true); 59 | 60 | template 61 | void AddLODTask(const FRealtimeMeshLODKey& LODKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate = true) 62 | { 63 | AddLODTask(LODKey, [Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, FRealtimeMeshLODProxy& LOD) 64 | { 65 | Func(RHICmdList, static_cast(LOD)); 66 | }, bInRequiresProxyRecreate); 67 | } 68 | 69 | void AddSectionGroupTask(const FRealtimeMeshSectionGroupKey& SectionGroupKey, TUniqueFunction&& Function, 70 | bool bInRequiresProxyRecreate = true); 71 | 72 | template 73 | void AddSectionGroupTask(const FRealtimeMeshSectionGroupKey& SectionGroupKey, TUniqueFunction&& Function, 74 | bool bInRequiresProxyRecreate = true) 75 | { 76 | AddSectionGroupTask(SectionGroupKey, [Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, FRealtimeMeshSectionGroupProxy& SectionGroup) 77 | { 78 | Func(RHICmdList, static_cast(SectionGroup)); 79 | }, bInRequiresProxyRecreate); 80 | } 81 | 82 | void AddSectionTask(const FRealtimeMeshSectionKey& SectionKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate = true); 83 | 84 | template 85 | void AddSectionTask(const FRealtimeMeshSectionKey& SectionKey, TUniqueFunction&& Function, bool bInRequiresProxyRecreate = true) 86 | { 87 | AddSectionTask(SectionKey, [Func = MoveTemp(Function)](FRHICommandListBase& RHICmdList, FRealtimeMeshSectionProxy& Section) 88 | { 89 | Func(RHICmdList, static_cast(Section)); 90 | }, bInRequiresProxyRecreate); 91 | } 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RenderProxy/RealtimeMeshSectionGroupProxy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshGPUBuffer.h" 6 | #include "RealtimeMeshProxyShared.h" 7 | #include "RealtimeMeshVertexFactory.h" 8 | #include "RealtimeMeshSectionProxy.h" 9 | #include "Core/RealtimeMeshSectionGroupConfig.h" 10 | 11 | namespace RealtimeMesh 12 | { 13 | using FRealtimeMeshSectionMask = TBitArray>; 14 | 15 | class FRealtimeMeshActiveSectionIterator 16 | { 17 | private: 18 | const FRealtimeMeshSectionGroupProxy& Proxy; 19 | TConstSetBitIterator> Iterator; 20 | 21 | public: 22 | FRealtimeMeshActiveSectionIterator(const FRealtimeMeshSectionGroupProxy& InProxy, const FRealtimeMeshSectionMask& InMask) 23 | : Proxy(InProxy), Iterator(TConstSetBitIterator(InMask)) { } 24 | 25 | /** Forwards iteration operator. */ 26 | FORCEINLINE FRealtimeMeshActiveSectionIterator& operator++() 27 | { 28 | ++Iterator; 29 | return *this; 30 | } 31 | 32 | FORCEINLINE bool operator==(const FRealtimeMeshActiveSectionIterator& Other) const 33 | { 34 | return Iterator == Other.Iterator; 35 | } 36 | 37 | FORCEINLINE bool operator!=(const FRealtimeMeshActiveSectionIterator& Other) const 38 | { 39 | return Iterator != Other.Iterator; 40 | } 41 | 42 | /** conversion to "bool" returning true if the iterator is valid. */ 43 | FORCEINLINE explicit operator bool() const 44 | { 45 | return (bool)Iterator; 46 | } 47 | /** inverse of the "bool" operator */ 48 | FORCEINLINE bool operator !() const 49 | { 50 | return !(bool)*this; 51 | } 52 | 53 | FRealtimeMeshSectionProxy* operator*() const; 54 | FRealtimeMeshSectionProxy& operator->() const; 55 | 56 | /** Index accessor. */ 57 | FORCEINLINE int32 GetIndex() const 58 | { 59 | return Iterator.GetIndex(); 60 | } 61 | }; 62 | 63 | class REALTIMEMESHCOMPONENT_API FRealtimeMeshSectionGroupProxy : public TSharedFromThis 64 | { 65 | private: 66 | const FRealtimeMeshSharedResourcesRef SharedResources; 67 | const FRealtimeMeshSectionGroupKey Key; 68 | FRealtimeMeshSectionGroupConfig Config; 69 | TSharedPtr VertexFactory; 70 | TArray Sections; 71 | TMap SectionMap; 72 | FRealtimeMeshSectionMask ActiveSectionMask; 73 | FRealtimeMeshStreamProxyMap Streams; 74 | #if RHI_RAYTRACING 75 | FRayTracingGeometry RayTracingGeometry; 76 | #endif 77 | 78 | FRealtimeMeshDrawMask DrawMask; 79 | bool bVertexFactoryDirty; 80 | 81 | public: 82 | FRealtimeMeshSectionGroupProxy(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshSectionGroupKey& InKey); 83 | virtual ~FRealtimeMeshSectionGroupProxy(); 84 | 85 | const FRealtimeMeshSectionGroupConfig& GetConfig() const { return Config; } 86 | ERealtimeMeshSectionDrawType GetDrawType() const { return Config.DrawType; } 87 | 88 | const FRealtimeMeshSectionGroupKey& GetKey() const { return Key; } 89 | TSharedPtr GetVertexFactory() const { return VertexFactory; } 90 | FRealtimeMeshDrawMask GetDrawMask() const { return DrawMask; } 91 | FRealtimeMeshActiveSectionIterator GetActiveSectionMaskIter() const { return FRealtimeMeshActiveSectionIterator(*this, ActiveSectionMask); } 92 | 93 | FRealtimeMeshSectionProxyPtr GetSection(const FRealtimeMeshSectionKey& SectionKey) const; 94 | TSharedPtr GetStream(const FRealtimeMeshStreamKey& StreamKey) const; 95 | 96 | #if RHI_RAYTRACING 97 | const FRayTracingGeometry* GetRayTracingGeometry() const { return &RayTracingGeometry; } 98 | #endif 99 | 100 | FRayTracingGeometry* GetRayTracingGeometry(); 101 | 102 | virtual void UpdateConfig(const FRealtimeMeshSectionGroupConfig& NewConfig); 103 | 104 | virtual void CreateSectionIfNotExists(const FRealtimeMeshSectionKey& SectionKey); 105 | virtual void RemoveSection(const FRealtimeMeshSectionKey& SectionKey); 106 | 107 | virtual void CreateOrUpdateStream(FRHICommandListBase& RHICmdList, const FRealtimeMeshSectionGroupStreamUpdateDataRef& InStream); 108 | virtual void RemoveStream(const FRealtimeMeshStreamKey& StreamKey); 109 | 110 | virtual bool InitializeMeshBatch(FMeshBatch& MeshBatch, FRealtimeMeshResourceReferenceList& Resources, bool bIsLocalToWorldDeterminantNegative, bool bWantsDepthOnly) const; 111 | 112 | virtual void UpdateCachedState(FRHICommandListBase& RHICmdList); 113 | virtual void Reset(); 114 | 115 | protected: 116 | virtual bool UpdateRayTracingInfo(FRHICommandListBase& RHICmdList); 117 | 118 | void RebuildSectionMap(); 119 | 120 | friend class FRealtimeMeshActiveSectionIterator; 121 | }; 122 | 123 | 124 | FORCEINLINE FRealtimeMeshSectionProxy* FRealtimeMeshActiveSectionIterator::operator*() const 125 | { 126 | check(Proxy.Sections.IsValidIndex(Iterator.GetIndex())); 127 | return &Proxy.Sections[Iterator.GetIndex()].Get(); 128 | } 129 | 130 | FORCEINLINE FRealtimeMeshSectionProxy& FRealtimeMeshActiveSectionIterator::operator->() const 131 | { 132 | check(Proxy.Sections.IsValidIndex(Iterator.GetIndex())); 133 | return Proxy.Sections[Iterator.GetIndex()].Get(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/Public/RenderProxy/RealtimeMeshSectionProxy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "RealtimeMeshCore.h" 6 | #include "RealtimeMeshProxyShared.h" 7 | #include "Core/RealtimeMeshKeys.h" 8 | #include "Core/RealtimeMeshSectionConfig.h" 9 | #include "Core/RealtimeMeshStreamRange.h" 10 | 11 | namespace RealtimeMesh 12 | { 13 | class REALTIMEMESHCOMPONENT_API FRealtimeMeshSectionProxy : public TSharedFromThis 14 | { 15 | private: 16 | const FRealtimeMeshSharedResourcesRef SharedResources; 17 | const FRealtimeMeshSectionKey Key; 18 | FRealtimeMeshSectionConfig Config; 19 | FRealtimeMeshStreamRange StreamRange; 20 | FRealtimeMeshDrawMask DrawMask; 21 | 22 | bool bRangeChanged; 23 | 24 | public: 25 | FRealtimeMeshSectionProxy(const FRealtimeMeshSharedResourcesRef& InSharedResources, const FRealtimeMeshSectionKey InKey); 26 | virtual ~FRealtimeMeshSectionProxy(); 27 | 28 | const FRealtimeMeshSectionKey& GetKey() const { return Key; } 29 | const FRealtimeMeshSectionConfig& GetConfig() const { return Config; } 30 | const FRealtimeMeshStreamRange& GetStreamRange() const { return StreamRange; } 31 | int32 GetMaterialSlot() const { return Config.MaterialSlot; } 32 | FRealtimeMeshDrawMask GetDrawMask() const { return DrawMask; } 33 | 34 | bool IsRangeDirty() const { return bRangeChanged; } 35 | 36 | virtual void UpdateConfig(const FRealtimeMeshSectionConfig& NewConfig); 37 | virtual void UpdateStreamRange(const FRealtimeMeshStreamRange& NewStreamRange); 38 | 39 | virtual bool InitializeMeshBatch(FMeshBatch& MeshBatch, FRHIUniformBuffer* PrimitiveUniformBuffer) const; 40 | 41 | 42 | virtual void UpdateCachedState(FRealtimeMeshSectionGroupProxy& ParentGroup); 43 | virtual void Reset(); 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/RealtimeMeshComponent.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | using System.IO; 4 | using UnrealBuildTool; 5 | 6 | public class RealtimeMeshComponent : ModuleRules 7 | { 8 | public RealtimeMeshComponent(ReadOnlyTargetRules rules) : base(rules) 9 | { 10 | //bEnforceIWYU = true; 11 | //IWYUSupport = 12 | //bLegacyPublicIncludePaths = false; 13 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 14 | bUseUnity = false; 15 | //IncludeOrderVersion = EngineIncludeOrderVersion.Latest; 16 | 17 | PublicDefinitions.Add("WITH_REALTIME_MESH=1"); 18 | PublicDefinitions.Add("IS_REALTIME_MESH_LIBRARY=1"); 19 | 20 | // This is to access RayTracing Definitions 21 | PrivateIncludePaths.Add(Path.Combine(EngineDirectory, "Shaders", "Shared")); 22 | PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public", "Interface")); 23 | 24 | //PublicDefinitions.Add("REALTIME_MESH_INTERFACE_ROOT="); 25 | 26 | PublicDependencyModuleNames.AddRange( 27 | new string[] 28 | { 29 | "Core", "CoreUObject", "GeometryCore", 30 | } 31 | ); 32 | 33 | PrivateDependencyModuleNames.AddRange( 34 | new string[] 35 | { 36 | "CoreUObject", 37 | "Engine", 38 | "RenderCore", 39 | "RHI", 40 | "NavigationSystem", 41 | "PhysicsCore", 42 | "DeveloperSettings", 43 | "Projects", 44 | "Chaos", 45 | "ChaosCore", 46 | } 47 | ); 48 | } 49 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshComponent/RealtimeMeshComponent.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Invalid 5 | RGB10A2 6 | {Type,en}{(int32)NumDatums} 7 | 8 | 9 | Invalid 10 | {ElementType}x{NumElements} 11 | 12 | 13 | {VertexType,en} | {IndexType,en} | {PixelFormat,en} 14 | Invalid 15 | 16 | 17 | {Layout,en} ({TypeDefinition,en}) 18 | Invalid 19 | 20 | 21 | {StreamName}({StreamType,en}) 22 | Invalid 23 | 24 | 25 | 26 | {{X:{X}, Y:{Y}}} 27 | 28 | 29 | {{X:{X}, Y:{Y}, Z:{Z}}} 30 | 31 | 32 | {{X:{X}, Y:{Y}, Z:{Z}, W:{W}}} 33 | 34 | 35 | 36 | {Elements},na 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Invalid 63 | Invalid 64 | Invalid 65 | Type:{LayoutDefinition.Layout} Empty 66 | {StreamKey} Type:{LayoutDefinition.Layout} Num:{ArrayNum} 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | CurrentRowPtr = Allocator.Data + (RowIndex * LayoutDefinition.Stride) 76 | RealtimeMesh::GetRowAsString(*this, RowIndex) 77 | RowIndex += 1 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Source/RealtimeMeshEditor/Private/RealtimeMeshMenuExtension.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "RealtimeMeshMenuExtension.h" 5 | #include "Framework/Application/SlateApplication.h" 6 | #include "Styling/SlateStyleRegistry.h" 7 | #include "Slate/SlateGameResources.h" 8 | #include "Interfaces/IPluginManager.h" 9 | #include "Styling/SlateStyleMacros.h" 10 | 11 | #define LOCTEXT_NAMESPACE "RealtimeMeshEditorModule" 12 | 13 | #define RootToContentDir Style->RootToContentDir 14 | 15 | TSharedPtr FRealtimeMeshEditorStyle::StyleInstance = nullptr; 16 | 17 | void FRealtimeMeshEditorStyle::Initialize() 18 | { 19 | if (!StyleInstance.IsValid()) 20 | { 21 | StyleInstance = Create(); 22 | FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); 23 | } 24 | } 25 | 26 | void FRealtimeMeshEditorStyle::Shutdown() 27 | { 28 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); 29 | ensure(StyleInstance.IsUnique()); 30 | StyleInstance.Reset(); 31 | } 32 | 33 | FName FRealtimeMeshEditorStyle::GetStyleSetName() 34 | { 35 | static FName StyleSetName(TEXT("RealtimeMeshStyle")); 36 | return StyleSetName; 37 | } 38 | 39 | 40 | const FVector2D Icon16x16(16.0f, 16.0f); 41 | const FVector2D Icon20x20(20.0f, 20.0f); 42 | 43 | TSharedRef< FSlateStyleSet > FRealtimeMeshEditorStyle::Create() 44 | { 45 | TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("RealtimeMeshStyle")); 46 | Style->SetContentRoot(IPluginManager::Get().FindPlugin("RealtimeMeshComponent")->GetBaseDir() / TEXT("Resources")); 47 | 48 | Style->Set("RealtimeMesh.MenuAction", new IMAGE_BRUSH_SVG(TEXT("MenuIcon"), Icon20x20)); 49 | return Style; 50 | } 51 | 52 | void FRealtimeMeshEditorStyle::ReloadTextures() 53 | { 54 | if (FSlateApplication::IsInitialized()) 55 | { 56 | FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); 57 | } 58 | } 59 | 60 | const ISlateStyle& FRealtimeMeshEditorStyle::Get() 61 | { 62 | return *StyleInstance; 63 | } 64 | 65 | void FRealtimeMeshEditorCommands::RegisterCommands() 66 | { 67 | UI_COMMAND(MarketplaceCoreAction, "RMC-Core Marketplace", "Get the core version of the RMC on the Unreal Engine Marketplace!", EUserInterfaceActionType::Button, FInputChord()); 68 | UI_COMMAND(MarketplaceProAction, "RMC-Pro Marketplace", "Get the pro version, with advanced features, of the RMC on the Unreal Engine Marketplace!", EUserInterfaceActionType::Button, FInputChord()); 69 | UI_COMMAND(DiscordAction, "Join RMC Discord", "Join the RMC Discord community to find inspiration or help from like minded users!", EUserInterfaceActionType::Button, FInputChord()); 70 | UI_COMMAND(DocumentationAction, "Open Documentation", "Open the RMC documentation in your web browser!", EUserInterfaceActionType::Button, FInputChord()); 71 | UI_COMMAND(IssuesAction, "Open RMC Issue Tracker", "Open the RMC issue tracker on GitHub if you have found a bug and want to report it!", EUserInterfaceActionType::Button, FInputChord()); 72 | } 73 | 74 | #undef LOCTEXT_NAMESPACE 75 | -------------------------------------------------------------------------------- /Source/RealtimeMeshEditor/Public/RealtimeMeshEditor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshCore.h" 7 | 8 | #if RMC_ENGINE_ABOVE_5_4 9 | struct FRealtimeMeshEditorSettings 10 | { 11 | bool bShouldIgnoreLumenNotification = false; 12 | bool bShouldIgnoreGeneralNotification = false; 13 | int64 LastLumenNotificationTime = 0; 14 | int64 LastGeneralNotificationTime = 0; 15 | }; 16 | #endif 17 | 18 | 19 | class FRealtimeMeshEditorModule : public IModuleInterface 20 | { 21 | private: 22 | TSharedPtr PluginCommands; 23 | 24 | #if RMC_ENGINE_ABOVE_5_4 25 | FRealtimeMeshEditorSettings Settings; 26 | 27 | TWeakPtr LumenNotification; 28 | FTimerHandle LumenUseCheckHandle; 29 | bool bUserOwnsPro = false; 30 | #endif 31 | 32 | public: 33 | virtual void StartupModule() override; 34 | virtual void ShutdownModule() override; 35 | 36 | private: 37 | void RegisterMenus(); 38 | 39 | static TSharedRef GenerateToolbarMenuContent(TSharedPtr Commands); 40 | 41 | void MarketplaceProButtonClicked(); 42 | void MarketplaceCoreButtonClicked(); 43 | void DiscordButtonClicked(); 44 | void DocumentationButtonClicked(); 45 | void IssuesButtonClicked(); 46 | 47 | #if RMC_ENGINE_ABOVE_5_4 48 | bool IsProVersion(); 49 | bool UserOwnsPro(); 50 | 51 | void SetupEditorTimer(); 52 | 53 | void ShowLumenNotification(); 54 | void HandleLumenNotificationBuyNowClicked(); 55 | void HandleLumenNotificationLaterClicked(); 56 | void HandleLumenNotificationIgnoreClicked(); 57 | 58 | void CheckUserOwnsPro(); 59 | void CheckLumenUseTimer(); 60 | 61 | void LoadSettings(); 62 | void SaveSettings(); 63 | #endif 64 | }; 65 | 66 | -------------------------------------------------------------------------------- /Source/RealtimeMeshEditor/Public/RealtimeMeshMenuExtension.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | class FRealtimeMeshEditorStyle 8 | { 9 | public: 10 | 11 | static void Initialize(); 12 | 13 | static void Shutdown(); 14 | 15 | /** reloads textures used by slate renderer */ 16 | static void ReloadTextures(); 17 | 18 | /** @return The Slate style set for the Shooter game */ 19 | static const ISlateStyle& Get(); 20 | 21 | static FName GetStyleSetName(); 22 | 23 | private: 24 | 25 | static TSharedRef< class FSlateStyleSet > Create(); 26 | 27 | private: 28 | 29 | static TSharedPtr< class FSlateStyleSet > StyleInstance; 30 | }; 31 | 32 | class FRealtimeMeshEditorCommands : public TCommands 33 | { 34 | public: 35 | 36 | FRealtimeMeshEditorCommands() : TCommands(TEXT("RealtimeMeshEditor"), NSLOCTEXT("Contexts", "RealtimeMeshEditor", "RealtimeMeshEditor Plugin"), NAME_None, FRealtimeMeshEditorStyle::GetStyleSetName()) 37 | { 38 | } 39 | 40 | // TCommands<> interface 41 | virtual void RegisterCommands() override; 42 | 43 | public: 44 | TSharedPtr MarketplaceCoreAction; 45 | TSharedPtr MarketplaceProAction; 46 | TSharedPtr DiscordAction; 47 | TSharedPtr DocumentationAction; 48 | TSharedPtr IssuesAction; 49 | }; 50 | 51 | 52 | class REALTIMEMESHEDITOR_API RealtimeMeshMenuExtension 53 | { 54 | public: 55 | RealtimeMeshMenuExtension(); 56 | ~RealtimeMeshMenuExtension(); 57 | }; 58 | -------------------------------------------------------------------------------- /Source/RealtimeMeshEditor/RealtimeMeshEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RealtimeMeshEditor : ModuleRules 6 | { 7 | public RealtimeMeshEditor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | //bUseUnity = false; 11 | #if UE_5_1_OR_LATER 12 | IncludeOrderVersion = EngineIncludeOrderVersion.Latest; 13 | #endif 14 | 15 | PublicDependencyModuleNames.AddRange( 16 | new string[] 17 | { 18 | "Core", "RealtimeMeshComponent", "AssetTools", "BlueprintGraph" 19 | } 20 | ); 21 | 22 | PrivateDependencyModuleNames.AddRange( 23 | new string[] 24 | { 25 | "CoreUObject", 26 | "Engine", 27 | "Slate", 28 | "SlateCore", 29 | "RealtimeMeshComponent", 30 | "UnrealEd", 31 | "ToolMenus", 32 | "Projects", 33 | "PluginWarden", 34 | "RenderCore", 35 | "RHI", 36 | } 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Private/RealtimeMeshBasic.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "RealtimeMeshBasic.h" 5 | #include "RealtimeMeshSimple.h" 6 | 7 | using namespace RealtimeMesh; 8 | 9 | // Sets default values 10 | ARealtimeMeshBasic::ARealtimeMeshBasic() 11 | { 12 | PrimaryActorTick.bCanEverTick = false; 13 | } 14 | 15 | void ARealtimeMeshBasic::OnConstruction(const FTransform& Transform) 16 | { 17 | Super::OnConstruction(Transform); 18 | 19 | // Initialize to a simple mesh, this behaves the most like a ProceduralMeshComponent 20 | // Where you can set the mesh data and forget about it. 21 | URealtimeMeshSimple* RealtimeMesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh(); 22 | 23 | // The most important part of the mesh data is the StreamSet, it contains the individual buffers, 24 | // like position, tangents, texcoords, triangles etc. 25 | FRealtimeMeshStreamSet StreamSet; 26 | 27 | // For this example we'll use a helper class to build the mesh data 28 | // You can make your own helpers or skip them and use individual TRealtimeMeshStreamBuilder, 29 | // or skip them entirely and copy data directly into the streams 30 | TRealtimeMeshBuilderLocal Builder(StreamSet); 31 | 32 | // here we go ahead and enable all the basic mesh data parts 33 | Builder.EnableTangents(); 34 | Builder.EnableTexCoords(); 35 | Builder.EnableColors(); 36 | 37 | // Poly groups allow us to easily create a single set of buffers with multiple sections by adding an index to the triangle data 38 | Builder.EnablePolyGroups(); 39 | 40 | // Add our first vertex 41 | int32 V0 = Builder.AddVertex(FVector3f(-50.0f, 0.0f, 0.0f)) 42 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 0.0f), FVector3f(1.0f, 0.0f, 0.0f)) 43 | .SetColor(FColor::Red) 44 | .SetTexCoord(FVector2f(0.0f, 0.0f)); 45 | 46 | // Add our second vertex 47 | int32 V1 = Builder.AddVertex(FVector3f(0.0f, 0.0f, 100.0f)) 48 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 0.0f), FVector3f(1.0f, 0.0f, 0.0f)) 49 | .SetColor(FColor::Green) 50 | .SetTexCoord(FVector2f(0.5f, 1.0f)); 51 | 52 | // Add our third vertex 53 | int32 V2 = Builder.AddVertex(FVector3f(50.0, 0.0, 0.0)) 54 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 0.0f), FVector3f(1.0f, 0.0f, 0.0f)) 55 | .SetColor(FColor::Blue) 56 | .SetTexCoord(FVector2f(1.0f, 0.0f)); 57 | 58 | // Add our triangle, placing the vertices in counter clockwise order 59 | Builder.AddTriangle(V0, V1, V2, 0/* This is the polygroup index */); 60 | 61 | // For this example we'll add the triangle again using reverse order so we can see the backface. 62 | // Usually you wouldn't want to do this, but in this case of a single triangle, 63 | // without it you'll only be able to see from a single side 64 | Builder.AddTriangle(V2, V1, V0, 1/* This is the polygroup index */); 65 | 66 | // Setup the two material slots 67 | RealtimeMesh->SetupMaterialSlot(0, "PrimaryMaterial"); 68 | RealtimeMesh->SetupMaterialSlot(1, "SecondaryMaterial"); 69 | 70 | // Now create the group key. This is a unique identifier for the section group 71 | // A section group contains one or more sections that all share the underlying buffers 72 | // these sections can overlap the used vertex/index ranges depending on use case. 73 | const FRealtimeMeshSectionGroupKey GroupKey = FRealtimeMeshSectionGroupKey::Create(0, FName("TestTriangle")); 74 | 75 | // Now create the section key, this is a unique identifier for a section within a group 76 | // The section contains the configuration for the section, like the material slot, 77 | // and the draw type, as well as the range of the index/vertex buffers to use to render. 78 | // Here we're using the version to create the key based on the PolyGroup index 79 | const FRealtimeMeshSectionKey PolyGroup0SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0); 80 | const FRealtimeMeshSectionKey PolyGroup1SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 1); 81 | 82 | // Now we create the section group, since the stream set has polygroups, this will create the sections as well 83 | RealtimeMesh->CreateSectionGroup(GroupKey, StreamSet); 84 | 85 | // Update the configuration of both the polygroup sections. 86 | RealtimeMesh->UpdateSectionConfig(PolyGroup0SectionKey, FRealtimeMeshSectionConfig(0)); 87 | RealtimeMesh->UpdateSectionConfig(PolyGroup1SectionKey, FRealtimeMeshSectionConfig(1)); 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Private/RealtimeMeshDirect.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "RealtimeMeshDirect.h" 5 | #include "RealtimeMeshSimple.h" 6 | #include "Engine/CollisionProfile.h" 7 | 8 | 9 | // Sets default values 10 | ARealtimeMeshDirect::ARealtimeMeshDirect() 11 | { 12 | // Here we register our new component 13 | RealtimeMeshComponent = CreateDefaultSubobject(TEXT("RealtimeMeshComponent")); 14 | RealtimeMeshComponent->SetMobility(EComponentMobility::Movable); 15 | RealtimeMeshComponent->SetGenerateOverlapEvents(false); 16 | RealtimeMeshComponent->SetCollisionProfileName(UCollisionProfile::BlockAll_ProfileName); 17 | SetRootComponent(RealtimeMeshComponent); 18 | } 19 | 20 | void ARealtimeMeshDirect::OnConstruction(const FTransform& Transform) 21 | { 22 | // Initialize to a simple mesh, this behaves the most like a ProceduralMeshComponent 23 | // Where you can set the mesh data and forget about it. 24 | URealtimeMeshSimple* RealtimeMesh = RealtimeMeshComponent->InitializeRealtimeMesh(); 25 | 26 | // The most important part of the mesh data is the StreamSet, it contains the individual buffers, 27 | // like position, tangents, texcoords, triangles etc. 28 | RealtimeMesh::FRealtimeMeshStreamSet StreamSet; 29 | 30 | // For this example we'll use a helper class to build the mesh data 31 | // You can make your own helpers or skip them and use individual TRealtimeMeshStreamBuilder, 32 | // or skip them entirely and copy data directly into the streams 33 | RealtimeMesh::TRealtimeMeshBuilderLocal Builder(StreamSet); 34 | 35 | // here we go ahead and enable all the basic mesh data parts 36 | Builder.EnableTangents(); 37 | Builder.EnableTexCoords(); 38 | Builder.EnableColors(); 39 | 40 | // Poly groups allow us to easily create a single set of buffers with multiple sections by adding an index to the triangle data 41 | Builder.EnablePolyGroups(); 42 | 43 | // Add our first vertex 44 | int32 V0 = Builder.AddVertex(FVector3f(-50.0f, 0.0f, 0.0f)) 45 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 1.0f), FVector3f(1.0f, 0.0f, 0.0f)) 46 | .SetColor(FColor::Red) 47 | .SetTexCoord(FVector2f(0.0f, 0.0f)); 48 | 49 | // Add our second vertex 50 | int32 V1 = Builder.AddVertex(FVector3f(0.0f, 0.0f, 100.0f)) 51 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 1.0f), FVector3f(1.0f, 0.0f, 0.0f)) 52 | .SetColor(FColor::Green) 53 | .SetTexCoord(FVector2f(0.5f, 1.0f)); 54 | 55 | // Add our third vertex 56 | int32 V2 = Builder.AddVertex(FVector3f(50.0, 0.0, 0.0)) 57 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 1.0f), FVector3f(1.0f, 0.0f, 0.0f)) 58 | .SetColor(FColor::Blue) 59 | .SetTexCoord(FVector2f(1.0f, 0.0f)); 60 | 61 | // Add our triangle, placing the vertices in counter clockwise order 62 | Builder.AddTriangle(V0, V1, V2, 0); 63 | 64 | // For this example we'll add the triangle again using reverse order so we can see the backface. 65 | // Usually you wouldn't want to do this, but in this case of a single triangle, 66 | // without it you'll only be able to see from a single side 67 | Builder.AddTriangle(V2, V1, V0, 1); 68 | 69 | // Setup the two material slots 70 | RealtimeMesh->SetupMaterialSlot(0, "PrimaryMaterial"); 71 | RealtimeMesh->SetupMaterialSlot(1, "SecondaryMaterial"); 72 | 73 | // Now create the group key. This is a unique identifier for the section group 74 | // A section group contains one or more sections that all share the underlying buffers 75 | // these sections can overlap the used vertex/index ranges depending on use case. 76 | const FRealtimeMeshSectionGroupKey GroupKey = FRealtimeMeshSectionGroupKey::Create(0, FName("TestTriangle")); 77 | 78 | // Now create the section key, this is a unique identifier for a section within a group 79 | // The section contains the configuration for the section, like the material slot, 80 | // and the draw type, as well as the range of the index/vertex buffers to use to render. 81 | // Here we're using the version to create the key based on the PolyGroup index 82 | const FRealtimeMeshSectionKey PolyGroup0SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0); 83 | const FRealtimeMeshSectionKey PolyGroup1SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 1); 84 | 85 | // Now we create the section group, since the stream set has polygroups, this will create the sections as well 86 | RealtimeMesh->CreateSectionGroup(GroupKey, StreamSet, FRealtimeMeshSectionGroupConfig(ERealtimeMeshSectionDrawType::Static)); 87 | 88 | // Update the configuration of both the polygroup sections. 89 | RealtimeMesh->UpdateSectionConfig(PolyGroup0SectionKey, FRealtimeMeshSectionConfig(0)); 90 | RealtimeMesh->UpdateSectionConfig(PolyGroup1SectionKey, FRealtimeMeshSectionConfig(1)); 91 | 92 | 93 | 94 | 95 | Super::OnConstruction(Transform); 96 | } 97 | 98 | 99 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Private/RealtimeMeshExamples.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshExamples.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FRealtimeMeshExamplesModule" 6 | 7 | void FRealtimeMeshExamplesModule::StartupModule() 8 | { 9 | 10 | } 11 | 12 | void FRealtimeMeshExamplesModule::ShutdownModule() 13 | { 14 | 15 | } 16 | 17 | #undef LOCTEXT_NAMESPACE 18 | 19 | IMPLEMENT_MODULE(FRealtimeMeshExamplesModule, RealtimeMeshExamples) -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Private/RealtimeMeshLODExample.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshLODExample.h" 4 | 5 | #include "RealtimeMeshSimple.h" 6 | #include "Mesh/RealtimeMeshBasicShapeTools.h" 7 | 8 | using namespace RealtimeMesh; 9 | 10 | /* 11 | * This example demonstrates how to create a mesh with multiple LODs. 12 | * Each LOD has a simple box at a different rotation so it's very easy to see the LODs change. 13 | * 14 | */ 15 | 16 | 17 | // Sets default values 18 | ARealtimeMeshLODExample::ARealtimeMeshLODExample() 19 | { 20 | PrimaryActorTick.bCanEverTick = false; 21 | } 22 | 23 | void ARealtimeMeshLODExample::OnConstruction(const FTransform& Transform) 24 | { 25 | Super::OnConstruction(Transform); 26 | 27 | // Initialize to a simple mesh, this behaves the most like a ProceduralMeshComponent 28 | // Where you can set the mesh data and forget about it. 29 | URealtimeMeshSimple* RealtimeMesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh(); 30 | 31 | // Setup the material slot 32 | RealtimeMesh->SetupMaterialSlot(0, "PrimaryMaterial"); 33 | RealtimeMesh->UpdateLODConfig(0, FRealtimeMeshLODConfig(0.75)); 34 | 35 | FColor Colors[4] = { FColor::White, FColor::Blue, FColor::Red, FColor::Green }; 36 | FTransform3f Transforms[4] = { 37 | FTransform3f::Identity, 38 | FTransform3f(FRotator3f(0.0f, 45.0f, 0.0f)), 39 | FTransform3f(FRotator3f(0.0f, 90.0f, 45.0f)), 40 | FTransform3f(FRotator3f(45.0f, 135.0f, 0.0f)) }; 41 | 42 | for (int32 LODIndex = 0; LODIndex < 4; LODIndex++) 43 | { 44 | // The first LOD is already created, so we only need to add the other LODs 45 | if (LODIndex > 0) 46 | { 47 | 48 | RealtimeMesh->AddLOD(FRealtimeMeshLODConfig(FMath::Pow(0.5f, LODIndex))); 49 | } 50 | 51 | // The most important part of the mesh data is the StreamSet, it contains the individual buffers, 52 | // like position, tangents, texcoords, triangles etc. 53 | FRealtimeMeshStreamSet StreamSet; 54 | 55 | // For this example we'll use a helper class to build the mesh data 56 | // You can make your own helpers or skip them and use individual TRealtimeMeshStreamBuilder, 57 | // or skip them entirely and copy data directly into the streams 58 | TRealtimeMeshBuilderLocal Builder(StreamSet); 59 | 60 | // here we go ahead and enable all the basic mesh data parts 61 | Builder.EnableTangents(); 62 | Builder.EnableTexCoords(); 63 | Builder.EnableColors(); 64 | 65 | // Poly groups allow us to easily create a single set of buffers with multiple sections by adding an index to the triangle data 66 | Builder.EnablePolyGroups(); 67 | 68 | URealtimeMeshBasicShapeTools::AppendBoxMesh(StreamSet, FVector3f(100.0f, 100.0f, 100.0f), Transforms[LODIndex], 0, Colors[LODIndex]); 69 | 70 | 71 | // Now create the group key. This is a unique identifier for the section group 72 | // A section group contains one or more sections that all share the underlying buffers 73 | // these sections can overlap the used vertex/index ranges depending on use case. 74 | const FRealtimeMeshSectionGroupKey GroupKey = FRealtimeMeshSectionGroupKey::Create(LODIndex, FName("TestBox")); 75 | 76 | // Now create the section key, this is a unique identifier for a section within a group 77 | // The section contains the configuration for the section, like the material slot, 78 | // and the draw type, as well as the range of the index/vertex buffers to use to render. 79 | // Here we're using the version to create the key based on the PolyGroup index 80 | const FRealtimeMeshSectionKey PolyGroup0SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0); 81 | 82 | // Now we create the section group, since the stream set has polygroups, this will create the sections as well 83 | RealtimeMesh->CreateSectionGroup(GroupKey, StreamSet); 84 | 85 | // Update the configuration of both the polygroup sections. 86 | RealtimeMesh->UpdateSectionConfig(PolyGroup0SectionKey, FRealtimeMeshSectionConfig(0)); 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Private/RealtimeMeshMultipleUVs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "RealtimeMeshMultipleUVs.h" 5 | 6 | #include "RealtimeMeshSimple.h" 7 | 8 | using namespace RealtimeMesh; 9 | 10 | // Sets default values 11 | ARealtimeMeshMultipleUVs::ARealtimeMeshMultipleUVs() 12 | { 13 | PrimaryActorTick.bCanEverTick = false; 14 | } 15 | 16 | void ARealtimeMeshMultipleUVs::OnConstruction(const FTransform& Transform) 17 | { 18 | Super::OnConstruction(Transform); 19 | 20 | // Initialize to a simple mesh, this behaves the most like a ProceduralMeshComponent 21 | // Where you can set the mesh data and forget about it. 22 | URealtimeMeshSimple* RealtimeMesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh(); 23 | 24 | // The most important part of the mesh data is the StreamSet, it contains the individual buffers, 25 | // like position, tangents, texcoords, triangles etc. 26 | FRealtimeMeshStreamSet StreamSet; 27 | 28 | // For this example we'll use a helper class to build the mesh data 29 | // You can make your own helpers or skip them and use individual TRealtimeMeshStreamBuilder, 30 | // or skip them entirely and copy data directly into the streams 31 | TRealtimeMeshBuilderLocal Builder(StreamSet); 32 | 33 | // here we go ahead and enable all the basic mesh data parts 34 | Builder.EnableTangents(); 35 | Builder.EnableTexCoords(); 36 | Builder.EnableColors(); 37 | 38 | // Poly groups allow us to easily create a single set of buffers with multiple sections by adding an index to the triangle data 39 | Builder.EnablePolyGroups(); 40 | 41 | // Add our first vertex 42 | int32 V0 = Builder.AddVertex(FVector3f(-50.0f, 0.0f, 0.0f)) 43 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 1.0f), FVector3f(1.0f, 0.0f, 0.0f)) 44 | .SetColor(FColor::Red) 45 | .SetTexCoord(FVector2f(0.0f, 0.0f)) 46 | .SetTexCoord(1, FVector2f(0.25f, 0.25f)); 47 | 48 | // Add our second vertex 49 | int32 V1 = Builder.AddVertex(FVector3f(0.0f, 0.0f, 100.0f)) 50 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 1.0f), FVector3f(1.0f, 0.0f, 0.0f)) 51 | .SetColor(FColor::Green) 52 | .SetTexCoord(FVector2f(0.5f, 1.0f)) 53 | .SetTexCoord(1, FVector2f(0.5f, 0.75f)); 54 | 55 | // Add our third vertex 56 | int32 V2 = Builder.AddVertex(FVector3f(50.0, 0.0, 0.0)) 57 | .SetNormalAndTangent(FVector3f(0.0f, -1.0f, 1.0f), FVector3f(1.0f, 0.0f, 0.0f)) 58 | .SetColor(FColor::Blue) 59 | .SetTexCoord(FVector2f(1.0f, 0.0f)) 60 | .SetTexCoord(1, FVector2f(0.75f, 0.25f)); 61 | 62 | // Add our triangle, placing the vertices in counter clockwise order 63 | Builder.AddTriangle(V0, V1, V2, 0); 64 | 65 | // For this example we'll add the triangle again using reverse order so we can see the backface. 66 | // Usually you wouldn't want to do this, but in this case of a single triangle, 67 | // without it you'll only be able to see from a single side 68 | Builder.AddTriangle(V2, V1, V0, 1); 69 | 70 | // Setup the two material slots 71 | RealtimeMesh->SetupMaterialSlot(0, "PrimaryMaterial"); 72 | RealtimeMesh->SetupMaterialSlot(1, "SecondaryMaterial"); 73 | 74 | // Now create the group key. This is a unique identifier for the section group 75 | // A section group contains one or more sections that all share the underlying buffers 76 | // these sections can overlap the used vertex/index ranges depending on use case. 77 | const FRealtimeMeshSectionGroupKey GroupKey = FRealtimeMeshSectionGroupKey::Create(0, FName("TestTriangle")); 78 | 79 | // Now create the section key, this is a unique identifier for a section within a group 80 | // The section contains the configuration for the section, like the material slot, 81 | // and the draw type, as well as the range of the index/vertex buffers to use to render. 82 | // Here we're using the version to create the key based on the PolyGroup index 83 | const FRealtimeMeshSectionKey PolyGroup0SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 0); 84 | const FRealtimeMeshSectionKey PolyGroup1SectionKey = FRealtimeMeshSectionKey::CreateForPolyGroup(GroupKey, 1); 85 | 86 | // Now we create the section group, since the stream set has polygroups, this will create the sections as well 87 | RealtimeMesh->CreateSectionGroup(GroupKey, StreamSet, ERealtimeMeshSectionDrawType::Static); 88 | 89 | // Update the configuration of both the polygroup sections. 90 | RealtimeMesh->UpdateSectionConfig(PolyGroup0SectionKey, FRealtimeMeshSectionConfig(0)); 91 | RealtimeMesh->UpdateSectionConfig(PolyGroup1SectionKey, FRealtimeMeshSectionConfig(1)); 92 | } 93 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Public/RealtimeMeshBasic.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "GameFramework/Actor.h" 8 | #include "RealtimeMeshBasic.generated.h" 9 | 10 | UCLASS() 11 | class REALTIMEMESHEXAMPLES_API ARealtimeMeshBasic : public ARealtimeMeshActor 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | // Sets default values for this actor's properties 17 | ARealtimeMeshBasic(); 18 | 19 | protected: 20 | 21 | virtual void OnConstruction(const FTransform& Transform) override; 22 | }; 23 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Public/RealtimeMeshByStreamBuilder.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "GameFramework/Actor.h" 8 | #include "RealtimeMeshByStreamBuilder.generated.h" 9 | 10 | UCLASS() 11 | class REALTIMEMESHEXAMPLES_API ARealtimeMeshByStreamBuilder : public ARealtimeMeshActor 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | // Sets default values for this actor's properties 17 | ARealtimeMeshByStreamBuilder(); 18 | 19 | protected: 20 | 21 | virtual void OnConstruction(const FTransform& Transform) override; 22 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Public/RealtimeMeshDirect.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshComponent.h" 7 | #include "GameFramework/Actor.h" 8 | #include "RealtimeMeshDirect.generated.h" 9 | 10 | 11 | /* 12 | * This example is meant to show how to use the RMC without using the RealtimeMeshActor, 13 | * and instead register a RealtimeMeshComponent yourself and work with it. 14 | */ 15 | UCLASS() 16 | class REALTIMEMESHEXAMPLES_API ARealtimeMeshDirect : public AActor 17 | { 18 | GENERATED_BODY() 19 | public: 20 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="RealtimeMesh") 21 | TObjectPtr RealtimeMeshComponent; 22 | 23 | // Sets default values for this actor's properties 24 | ARealtimeMeshDirect(); 25 | 26 | protected: 27 | virtual void OnConstruction(const FTransform& Transform) override; 28 | }; 29 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Public/RealtimeMeshExamples.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FRealtimeMeshExamplesModule : public IModuleInterface 9 | { 10 | public: 11 | virtual void StartupModule() override; 12 | virtual void ShutdownModule() override; 13 | }; 14 | -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Public/RealtimeMeshLODExample.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "GameFramework/Actor.h" 8 | #include "RealtimeMeshLODExample.generated.h" 9 | 10 | UCLASS() 11 | class REALTIMEMESHEXAMPLES_API ARealtimeMeshLODExample : public ARealtimeMeshActor 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | // Sets default values for this actor's properties 17 | ARealtimeMeshLODExample(); 18 | 19 | protected: 20 | 21 | virtual void OnConstruction(const FTransform& Transform) override; 22 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/Public/RealtimeMeshMultipleUVs.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "GameFramework/Actor.h" 8 | #include "RealtimeMeshMultipleUVs.generated.h" 9 | 10 | UCLASS() 11 | class REALTIMEMESHEXAMPLES_API ARealtimeMeshMultipleUVs : public ARealtimeMeshActor 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | // Sets default values for this actor's properties 17 | ARealtimeMeshMultipleUVs(); 18 | 19 | protected: 20 | 21 | virtual void OnConstruction(const FTransform& Transform) override; 22 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshExamples/RealtimeMeshExamples.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RealtimeMeshExamples : ModuleRules 6 | { 7 | public RealtimeMeshExamples(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | #if UE_5_1_OR_LATER 11 | IncludeOrderVersion = EngineIncludeOrderVersion.Latest; 12 | #endif 13 | 14 | PublicDependencyModuleNames.AddRange( 15 | new string[] 16 | { 17 | "Core", 18 | 19 | // Add RealtimeMeshComponent support to C++ 20 | "RealtimeMeshComponent" 21 | } 22 | ); 23 | 24 | PrivateDependencyModuleNames.AddRange( 25 | new string[] 26 | { 27 | "CoreUObject", 28 | "Engine", 29 | "Slate", 30 | "SlateCore", 31 | } 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/FunctionalTests/RealtimeMeshLatentUpdateTestActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "FunctionalTests/RealtimeMeshLatentUpdateTestActor.h" 5 | 6 | #include "RealtimeMeshLibrary.h" 7 | #include "RealtimeMeshSimple.h" 8 | #include "RealtimeMeshCubeGeneratorExample.h" 9 | 10 | using namespace RealtimeMesh; 11 | 12 | ARealtimeMeshLatentUpdateTestActor::ARealtimeMeshLatentUpdateTestActor() 13 | : RealtimeMesh(nullptr) 14 | { 15 | 16 | } 17 | 18 | void ARealtimeMeshLatentUpdateTestActor::OnConstruction(const FTransform& Transform) 19 | { 20 | Super::OnConstruction(Transform); 21 | } 22 | 23 | void ARealtimeMeshLatentUpdateTestActor::BeginPlay() 24 | { 25 | Super::BeginPlay(); 26 | 27 | // Initialize the simple mesh 28 | RealtimeMesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh(); 29 | 30 | GroupA = FRealtimeMeshSectionGroupKey::Create(0, "MainGroup"); 31 | GroupB = FRealtimeMeshSectionGroupKey::Create(0, "SecondaryGroup"); 32 | 33 | { // Create a basic single section 34 | 35 | // Create the new stream set and builder 36 | FRealtimeMeshStreamSet StreamSet; 37 | TRealtimeMeshBuilderLocal Builder(StreamSet); 38 | Builder.EnableTangents(); 39 | Builder.EnableTexCoords(); 40 | Builder.EnablePolyGroups(); 41 | Builder.EnableColors(); 42 | 43 | // This example create 3 rectangular prisms, one on each axis, with all 44 | // of them sharing a single set of buffers, but using separate sections for separate materials 45 | AppendBox(Builder, FVector3f(100, 100, 200), 0); 46 | 47 | RealtimeMesh->CreateSectionGroup(GroupA, MoveTemp(StreamSet)); 48 | } 49 | 50 | { // Create a basic group with 2 sections 51 | // Create the new stream set and builder 52 | FRealtimeMeshStreamSet StreamSet; 53 | TRealtimeMeshBuilderLocal Builder(StreamSet); 54 | Builder.EnableTangents(); 55 | Builder.EnableTexCoords(); 56 | Builder.EnablePolyGroups(); 57 | Builder.EnableColors(); 58 | 59 | // This example create 3 rectangular prisms, one on each axis, with all 60 | // of them sharing a single set of buffers, but using separate sections for separate materials 61 | AppendBox(Builder, FVector3f(200, 100, 100), 1); 62 | AppendBox(Builder, FVector3f(100, 200, 100), 2); 63 | 64 | RealtimeMesh->CreateSectionGroup(GroupB, MoveTemp(StreamSet)); 65 | 66 | RealtimeMesh->UpdateSectionConfig(FRealtimeMeshSectionKey::CreateForPolyGroup(GroupB, 1), FRealtimeMeshSectionConfig(0)); 67 | RealtimeMesh->UpdateSectionConfig(FRealtimeMeshSectionKey::CreateForPolyGroup(GroupB, 2), FRealtimeMeshSectionConfig(1)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/FunctionalTests/RealtimeMeshStressTestActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "FunctionalTests/RealtimeMeshStressTestActor.h" 5 | 6 | #include "MaterialDomain.h" 7 | #include "RealtimeMeshLibrary.h" 8 | #include "RealtimeMeshSimple.h" 9 | #include "Mesh/RealtimeMeshBasicShapeTools.h" 10 | #include "Core/RealtimeMeshBuilder.h" 11 | 12 | using namespace RealtimeMesh; 13 | 14 | ARealtimeMeshStressTestActor2::ARealtimeMeshStressTestActor2() 15 | { 16 | PrimaryActorTick.bCanEverTick = true; 17 | PrimaryActorTick.bStartWithTickEnabled = true; 18 | } 19 | 20 | static const auto SectionGroupKey = FRealtimeMeshSectionGroupKey::Create(0, FName("TestTripleBox")); 21 | 22 | static const auto Section0Key = FRealtimeMeshSectionKey::Create(SectionGroupKey, FName("Section0")); 23 | static const auto Section1Key = FRealtimeMeshSectionKey::Create(SectionGroupKey, FName("Section1")); 24 | static const auto Section2Key = FRealtimeMeshSectionKey::Create(SectionGroupKey, FName("Section2")); 25 | 26 | void ARealtimeMeshStressTestActor2::OnConstruction(const FTransform& Transform) 27 | { 28 | Super::OnConstruction(Transform); 29 | 30 | // Initialize the simple mesh 31 | URealtimeMeshSimple* RealtimeMesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh(); 32 | 33 | // Setup the two material slots 34 | RealtimeMesh->SetupMaterialSlot(0, "PrimaryMaterial", UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface)); 35 | RealtimeMesh->SetupMaterialSlot(1, "SecondaryMaterial", UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface)); 36 | 37 | // Setup empty structure 38 | RealtimeMesh->CreateSectionGroup(SectionGroupKey, FRealtimeMeshSectionGroupConfig(ERealtimeMeshSectionDrawType::Static), false); 39 | RealtimeMesh->CreateSection(Section0Key, FRealtimeMeshSectionConfig(0), FRealtimeMeshStreamRange(), false); 40 | RealtimeMesh->CreateSection(Section1Key, FRealtimeMeshSectionConfig(1), FRealtimeMeshStreamRange(), false); 41 | RealtimeMesh->CreateSection(Section2Key, FRealtimeMeshSectionConfig(2), FRealtimeMeshStreamRange(), false); 42 | 43 | } 44 | 45 | void ARealtimeMeshStressTestActor2::TickActor(float DeltaTime, ELevelTick TickType, FActorTickFunction& ThisTickFunction) 46 | { 47 | if (PendingGeneration.IsValid()) 48 | { 49 | PendingGeneration.Wait(); 50 | PendingGeneration.Reset(); 51 | } 52 | 53 | if (URealtimeMeshSimple* RealtimeMesh = GetRealtimeMeshComponent()->GetRealtimeMeshAs()) 54 | { 55 | TSharedRef> RealtimeMeshRef = MakeShared>(RealtimeMesh); 56 | 57 | TPromise Promise; 58 | PendingGeneration = Promise.GetFuture(); 59 | 60 | AsyncTask(ENamedThreads::AnyThread, [RealtimeMeshRef, Promise = MoveTemp(Promise)]() mutable 61 | { 62 | FRealtimeMeshStreamSet StreamSet; 63 | // Here we're using the builder just to initialize the stream set 64 | TRealtimeMeshBuilderLocal Builder(StreamSet); 65 | Builder.EnableTangents(); 66 | Builder.EnableTexCoords(); 67 | Builder.EnableColors(); 68 | 69 | const float Scale = (FMath::Cos(FPlatformTime::Seconds() * PI) * 0.25f) + 0.5f; 70 | const FTransform3f Transform = FTransform3f(FQuat4f::Identity, FVector3f(0, 0, 0), FVector3f(Scale, Scale, Scale)); 71 | 72 | 73 | int32 Section0StartVertex = Builder.NumVertices(); 74 | int32 Section0StartTriangle = Builder.NumTriangles(); 75 | URealtimeMeshBasicShapeTools::AppendBoxMesh(StreamSet, FVector3f(100.0f, 100.0f, 200.0f), Transform, 2, FColor::White); 76 | 77 | int32 Section1StartVertex = Builder.NumVertices(); 78 | int32 Section1StartTriangle = Builder.NumTriangles(); 79 | URealtimeMeshBasicShapeTools::AppendBoxMesh(StreamSet, FVector3f(200.0f, 100.0f, 100.0f), Transform, 1, FColor::White); 80 | 81 | int32 Section2StartVertex = Builder.NumVertices(); 82 | int32 Section2StartTriangle = Builder.NumTriangles(); 83 | URealtimeMeshBasicShapeTools::AppendBoxMesh(StreamSet, FVector3f(100.0f, 200.0f, 100.0f), Transform, 3, FColor::White); 84 | 85 | int32 FinalNumVertices = Builder.NumVertices(); 86 | int32 FinalNumTriangles = Builder.NumTriangles(); 87 | 88 | FRealtimeMeshStreamRange Section0Range(Section0StartVertex, Section1StartVertex, Section0StartTriangle * 3, Section1StartTriangle * 3); 89 | FRealtimeMeshStreamRange Section1Range(Section1StartVertex, Section2StartVertex, Section1StartTriangle * 3, Section2StartTriangle * 3); 90 | FRealtimeMeshStreamRange Section2Range(Section2StartVertex, FinalNumVertices, Section2StartTriangle * 3, FinalNumTriangles * 3); 91 | 92 | 93 | RealtimeMeshRef.Get()->UpdateSectionGroup(SectionGroupKey, MoveTemp(StreamSet)); 94 | 95 | // Update the ranges for all the sections. 96 | RealtimeMeshRef.Get()->UpdateSectionRange(Section0Key, Section0Range); 97 | RealtimeMeshRef.Get()->UpdateSectionRange(Section1Key, Section1Range); 98 | RealtimeMeshRef.Get()->UpdateSectionRange(Section2Key, Section2Range); 99 | 100 | Promise.EmplaceValue(); 101 | }); 102 | } 103 | 104 | Super::TickActor(DeltaTime, TickType, ThisTickFunction); 105 | } 106 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/FunctionalTests/RealtimeMeshUpdateTestActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | 4 | #include "FunctionalTests/RealtimeMeshUpdateTestActor.h" 5 | 6 | #include "RealtimeMeshLibrary.h" 7 | #include "RealtimeMeshSimple.h" 8 | #include "RealtimeMeshCubeGeneratorExample.h" 9 | 10 | using namespace RealtimeMesh; 11 | 12 | ARealtimeMeshUpdateTestActor::ARealtimeMeshUpdateTestActor() 13 | : RealtimeMesh(nullptr) 14 | { 15 | 16 | } 17 | 18 | void ARealtimeMeshUpdateTestActor::OnConstruction(const FTransform& Transform) 19 | { 20 | Super::OnConstruction(Transform); 21 | 22 | // Initialize the simple mesh 23 | RealtimeMesh = GetRealtimeMeshComponent()->InitializeRealtimeMesh(); 24 | 25 | // This example create 3 rectangular prisms, one on each axis, with 2 of them grouped in the same vertex buffers, but with different sections 26 | // This allows for setting up separate materials even if sections share a single set of buffers. 27 | // Here we do a latent mesh submission, so we create the mesh section group and sections first, and then apply the mesh data later 28 | 29 | GroupKey = FRealtimeMeshSectionGroupKey::CreateUnique(0); 30 | RealtimeMesh->CreateSectionGroup(GroupKey); 31 | 32 | { // Create a basic single sectiong 33 | // Create the new stream set and builder 34 | FRealtimeMeshStreamSet StreamSet; 35 | TRealtimeMeshBuilderLocal Builder(StreamSet); 36 | Builder.EnableTangents(); 37 | Builder.EnableTexCoords(); 38 | Builder.EnablePolyGroups(); 39 | Builder.EnableColors(); 40 | 41 | // This example create 3 rectangular prisms, one on each axis, with all 42 | // of them sharing a single set of buffers, but using separate sections for separate materials 43 | AppendBox(Builder, FVector3f(100, 100, 200), 0); 44 | AppendBox(Builder, FVector3f(200, 100, 100), 1); 45 | AppendBox(Builder, FVector3f(100, 200, 100), 2); 46 | 47 | RealtimeMesh->UpdateSectionGroup(GroupKey, MoveTemp(StreamSet)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/RealtimeMeshBufferLayoutTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "Misc/AutomationTest.h" 4 | #include "Core/RealtimeMeshDataTypes.h" 5 | 6 | IMPLEMENT_SIMPLE_AUTOMATION_TEST(RealtimeMeshBufferLayoutTests, "RealtimeMeshComponent.RealtimeMeshBufferLayout", 7 | EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) 8 | 9 | bool RealtimeMeshBufferLayoutTests::RunTest(const FString& Parameters) 10 | { 11 | //const auto VoidType = RealtimeMesh::GetRealtimeMeshDataElementType(); 12 | const auto FloatType = RealtimeMesh::GetRealtimeMeshDataElementType(); 13 | const auto VectorType = RealtimeMesh::GetRealtimeMeshDataElementType(); 14 | const auto IntType = RealtimeMesh::GetRealtimeMeshDataElementType(); 15 | 16 | //const RealtimeMesh::FRealtimeMeshBufferLayout VoidBuffer(VoidType, 1); 17 | const RealtimeMesh::FRealtimeMeshBufferLayout ZeroLengthBuffer(FloatType, 0); 18 | const RealtimeMesh::FRealtimeMeshBufferLayout FloatBuffer1(FloatType, 1); 19 | const RealtimeMesh::FRealtimeMeshBufferLayout FloatBuffer2(FloatType, 2); 20 | const RealtimeMesh::FRealtimeMeshBufferLayout VectorBuffer1(VectorType, 1); 21 | const RealtimeMesh::FRealtimeMeshBufferLayout VectorBuffer2(VectorType, 2); 22 | const RealtimeMesh::FRealtimeMeshBufferLayout UInt32Buffer(IntType, 1); 23 | 24 | //TestFalse(TEXT("VoidBuffer InValid"), VoidType.IsValid()); 25 | TestFalse(TEXT("ZeroLengthBuffer Invalid"), ZeroLengthBuffer.IsValid()); 26 | TestTrue(TEXT("FloatBuffer1 Valid"), FloatBuffer1.IsValid()); 27 | TestTrue(TEXT("FloatBuffer2 Valid"), FloatBuffer2.IsValid()); 28 | TestTrue(TEXT("VectorBuffer1 Valid"), VectorBuffer1.IsValid()); 29 | TestTrue(TEXT("VectorBuffer2 Valid"), VectorBuffer2.IsValid()); 30 | TestTrue(TEXT("UInt32Buffer Valid"), UInt32Buffer.IsValid()); 31 | 32 | const RealtimeMesh::FRealtimeMeshBufferLayout TestBuffer(VectorType, 2); 33 | 34 | TestTrue(TEXT("VectorBuffer2 == TestBuffer"), VectorBuffer2 == TestBuffer); 35 | TestFalse(TEXT("VectorBuffer2 != TestBuffer"), VectorBuffer2 != TestBuffer); 36 | 37 | TestTrue(TEXT("VectorBuffer2 DataType"), VectorBuffer2.GetElementType().GetDatumType() == RealtimeMesh::ERealtimeMeshDatumType::Float); 38 | TestTrue(TEXT("VectorBuffer2 NumDatums"), VectorBuffer2.GetElementType().GetNumDatums() == 2); 39 | TestTrue(TEXT("VectorBuffer2 NumElements"), VectorBuffer2.GetNumElements() == 2); 40 | 41 | 42 | //const RealtimeMesh::FRealtimeMeshBufferLayoutDefinition InvalidDef = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetBufferLayoutDefinition(VoidBuffer); 43 | const RealtimeMesh::FRealtimeMeshElementTypeDetails InvalidDefZero = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(ZeroLengthBuffer); 44 | const RealtimeMesh::FRealtimeMeshElementTypeDetails ValidDefVector2 = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(VectorBuffer2); 45 | const RealtimeMesh::FRealtimeMeshElementTypeDetails ValidDefUInt32 = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(UInt32Buffer); 46 | 47 | 48 | //TestFalse(TEXT("InvalidDef Valid"), InvalidDef.IsValid()); 49 | TestFalse(TEXT("InvalidDefZero Valid"), InvalidDefZero.IsValid()); 50 | TestTrue(TEXT("ValidDefVector2 Valid"), ValidDefVector2.IsValid()); 51 | TestTrue(TEXT("ValidDefUInt32 Valid"), ValidDefUInt32.IsValid()); 52 | 53 | struct TestVec 54 | { 55 | FVector2f A, B; 56 | }; 57 | 58 | TestTrue(TEXT("ValidDefVector2"), 59 | ValidDefVector2.IsSupportedVertexType() 60 | && ValidDefVector2.GetStride() == sizeof(TestVec) 61 | && ValidDefVector2.GetAlignment() == alignof(TestVec)); 62 | TestTrue(TEXT("ValidDefUInt32"), 63 | ValidDefUInt32.IsSupportedIndexType() 64 | && ValidDefUInt32.GetStride() == sizeof(uint32) 65 | && ValidDefUInt32.GetAlignment() == alignof(uint32)); 66 | 67 | return true; 68 | } 69 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/RealtimeMeshElementTypeTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "Misc/AutomationTest.h" 4 | #include "Core/RealtimeMeshDataTypes.h" 5 | 6 | IMPLEMENT_SIMPLE_AUTOMATION_TEST(FRealtimeMeshElementTypeTests, "RealtimeMeshComponent.RealtimeMeshElementType", 7 | EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) 8 | 9 | bool FRealtimeMeshElementTypeTests::RunTest(const FString& Parameters) 10 | { 11 | //const auto VoidType = RealtimeMesh::GetRealtimeMeshDataElementType(); 12 | const auto FloatType = RealtimeMesh::GetRealtimeMeshDataElementType(); 13 | const auto VectorType = RealtimeMesh::GetRealtimeMeshDataElementType(); 14 | const auto IntType = RealtimeMesh::GetRealtimeMeshDataElementType(); 15 | 16 | //TestFalse(TEXT("InvalidType Test"), VoidType.IsValid()); 17 | TestTrue(TEXT("FloatType Valid"), FloatType.IsValid()); 18 | TestTrue(TEXT("VectorType Valid"), FloatType.IsValid()); 19 | TestTrue(TEXT("IntType Valid"), IntType.IsValid()); 20 | 21 | const auto TestType = RealtimeMesh::GetRealtimeMeshDataElementType(); 22 | 23 | TestTrue(TEXT("FloatType == TestType"), FloatType == TestType); 24 | TestFalse(TEXT("FloatType != TestType"), FloatType != TestType); 25 | TestTrue(TEXT("FloatType != VectorType"), FloatType != VectorType); 26 | TestFalse(TEXT("FloatType == VectorType"), FloatType == VectorType); 27 | 28 | TestTrue(TEXT("VectorType DataType"), VectorType.GetDatumType() == RealtimeMesh::ERealtimeMeshDatumType::Float); 29 | TestTrue(TEXT("VectorType NumDatums"), VectorType.GetNumDatums() == 2); 30 | 31 | //const RealtimeMesh::FRealtimeMeshElementTypeDefinition InvalidDef = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetTypeDefinition(VoidType); 32 | const RealtimeMesh::FRealtimeMeshElementTypeDetails ValidVertex = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(VectorType); 33 | const RealtimeMesh::FRealtimeMeshElementTypeDetails ValidIndex = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(IntType); 34 | 35 | 36 | //TestFalse(TEXT("InvalidDef Valid"), InvalidDef.IsValid()); 37 | TestTrue(TEXT("ValidVertex Valid"), ValidVertex.IsValid()); 38 | TestTrue(TEXT("ValidIndex Valid"), ValidIndex.IsValid()); 39 | 40 | TestTrue(TEXT("ValidVertex"), ValidVertex.IsSupportedVertexType() && ValidVertex.GetVertexType() == VET_Float2 && ValidVertex.GetStride() == sizeof(FVector2f) && ValidVertex.GetAlignment() == alignof(FVector2f)); 41 | TestTrue(TEXT("ValidIndex"), ValidIndex.IsSupportedIndexType() && ValidIndex.GetIndexType() == RealtimeMesh::IET_UInt32 && ValidIndex.GetStride() == sizeof(uint32) && ValidIndex.GetAlignment() == alignof(uint32)); 42 | 43 | const RealtimeMesh::FRealtimeMeshElementTypeDetails PackedNormal = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(RealtimeMesh::GetRealtimeMeshDataElementType()); 44 | TestTrue(TEXT("PackedNormal"), PackedNormal.IsSupportedVertexType() && PackedNormal.GetVertexType() == VET_PackedNormal && PackedNormal.GetStride() == sizeof(FPackedNormal) && PackedNormal.GetAlignment() == alignof(FPackedNormal)); 45 | const RealtimeMesh::FRealtimeMeshElementTypeDetails PackedRGBA16N = RealtimeMesh::FRealtimeMeshBufferLayoutUtilities::GetElementTypeDetails(RealtimeMesh::GetRealtimeMeshDataElementType()); 46 | TestTrue(TEXT("PackedRGBA16N"), PackedRGBA16N.IsSupportedVertexType() && PackedRGBA16N.GetVertexType() == VET_Short4N && PackedRGBA16N.GetStride() == sizeof(FPackedRGBA16N) && PackedRGBA16N.GetAlignment() == alignof(FPackedRGBA16N)); 47 | 48 | return true; 49 | } 50 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/RealtimeMeshMaterialIndicesTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "Algo/RandomShuffle.h" 4 | #include "Mesh/RealtimeMeshAlgo.h" 5 | #include "Core/RealtimeMeshBuilder.h" 6 | #include "Core/RealtimeMeshDataStream.h" 7 | #include "Misc/AutomationTest.h" 8 | 9 | IMPLEMENT_SIMPLE_AUTOMATION_TEST(RealtimeMeshMaterialIndicesTests, "RealtimeMeshComponent.RealtimeMeshMaterialIndices", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) 10 | 11 | using namespace RealtimeMesh; 12 | 13 | bool RealtimeMeshMaterialIndicesTests::RunTest(const FString& Parameters) 14 | { 15 | const int32 StartingData[] = { 0, 0, 0, 1, 2, 3, 0, 5, 0, 9, 5, 5, 6, 6, 1, 1 }; 16 | const uint32 ExpectedRemapTable[] = { 0, 1, 2, 6, 8, 3, 14, 15, 4, 5, 7, 10, 11, 12, 13, 9 }; 17 | const uint32 ExpectedEndingData[] = { 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 5, 5, 5, 6, 6, 9 }; 18 | 19 | FRealtimeMeshStream MaterialIndices = FRealtimeMeshStream::Create(FRealtimeMeshStreams::PolyGroups); 20 | TRealtimeMeshStreamBuilder MaterialIndicesBuilder(MaterialIndices); 21 | 22 | for (int32 Index = 0; Index < 16; Index++) 23 | { 24 | MaterialIndicesBuilder.Add(StartingData[Index]); 25 | } 26 | 27 | TestFalse(TEXT("InitialDataIsntOptimal"), RealtimeMeshAlgo::ArePolygonGroupIndicesOptimal(MaterialIndices)); 28 | 29 | 30 | uint32 RemapTable[16]; 31 | TestTrue(TEXT("GenerateSortedRemapTable"), RealtimeMeshAlgo::GenerateSortedRemapTable(MaterialIndices, MakeArrayView(RemapTable))); 32 | 33 | TestTrue(TEXT("GotExpectedRemapTable"), FMemory::Memcmp(RemapTable, ExpectedRemapTable, sizeof(uint32) * 16) == 0); 34 | 35 | RealtimeMeshAlgo::ApplyRemapTableToStream(MakeArrayView(RemapTable), MaterialIndices); 36 | 37 | for (int32 Index = 0; Index < 16; Index++) 38 | { 39 | TestTrue(TEXT("FinalIndicesCorrect"), ExpectedEndingData[Index] == MaterialIndicesBuilder[Index]); 40 | } 41 | 42 | TestTrue(TEXT("InitialDataIsOptimalByIndices"), RealtimeMeshAlgo::ArePolygonGroupIndicesOptimal(MaterialIndices)); 43 | 44 | TArray Segments; 45 | 46 | RealtimeMeshAlgo::GatherSegmentsFromPolygonGroupIndices(MaterialIndices, [&Segments](const FRealtimeMeshPolygonGroupRange& NewSegment) 47 | { 48 | Segments.Add(NewSegment); 49 | }); 50 | 51 | const TArray KnownGoodSegments = { 52 | FRealtimeMeshPolygonGroupRange(0, 5, 0), 53 | FRealtimeMeshPolygonGroupRange(5, 3, 1), 54 | FRealtimeMeshPolygonGroupRange(8, 1, 2), 55 | FRealtimeMeshPolygonGroupRange(9, 1, 3), 56 | FRealtimeMeshPolygonGroupRange(10, 3, 5), 57 | FRealtimeMeshPolygonGroupRange(13, 2, 6), 58 | FRealtimeMeshPolygonGroupRange(15, 1, 9) 59 | }; 60 | TestTrue(TEXT("AreSegmentsValid"), Segments == KnownGoodSegments); 61 | TestTrue(TEXT("InitialDataIsOptimal"), RealtimeMeshAlgo::ArePolygonGroupIndicesOptimal(Segments)); 62 | 63 | 64 | TArray PolyGroupIndices = { 1, 1, 1, 1, 0, 0, 0, 0, 0, 2, 2, 2 }; 65 | TArray Triangles = { 66 | 0, 1, 2, 67 | 3, 0, 1, 68 | 2, 3, 4, 69 | 5, 6, 7, 70 | 71 | 0, 1, 2, 72 | 3, 4, 5, 73 | 6, 7, 8, 74 | 9, 10, 11, 75 | 5, 6, 7, 76 | 77 | 8, 9, 10, 78 | 11, 12, 10, 79 | 12, 11, 10 80 | }; 81 | 82 | /* 83 | TMap StreamSegments; 84 | RealtimeMeshAlgo::GatherStreamRangesFromPolyGroupIndices(PolyGroupIndices, Triangles, StreamSegments); 85 | 86 | 87 | TestTrue(TEXT("StreamSegmentsLength"), StreamSegments.Num() == 3); 88 | TestTrue(TEXT("StreamSegmentsElement0"), StreamSegments[0] == FRealtimeMeshStreamRange(0, 8, 0, 12)); 89 | TestTrue(TEXT("StreamSegmentsElement1"), StreamSegments[1] == FRealtimeMeshStreamRange(0, 12, 12, 27)); 90 | TestTrue(TEXT("StreamSegmentsElement2"), StreamSegments[2] == FRealtimeMeshStreamRange(8, 13, 27, 36)); 91 | */ 92 | 93 | 94 | // Make the test pass by returning true, or fail by returning false. 95 | return true; 96 | } 97 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/RealtimeMeshStreamConversionTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "Core/RealtimeMeshBuilder.h" 4 | #include "Misc/AutomationTest.h" 5 | 6 | IMPLEMENT_SIMPLE_AUTOMATION_TEST(RealtimeMeshStreamConversionTests, "RealtimeMeshComponent.RealtimeMeshStreamConversion", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) 7 | 8 | using namespace RealtimeMesh; 9 | 10 | bool RealtimeMeshStreamConversionTests::RunTest(const FString& Parameters) 11 | { 12 | FRealtimeMeshStream DataStream = FRealtimeMeshStream::Create>(RealtimeMesh::FRealtimeMeshStreams::Position); 13 | TRealtimeMeshStreamBuilder> InitialBuilder(DataStream); 14 | 15 | const FVector3f InitialData[] = 16 | { 17 | FVector3f(0, 0, 1), 18 | FVector3f(0, 1, 0), 19 | FVector3f(1, 0, 0), 20 | FVector3f(0, 0, 0), 21 | FVector3f(0, 0, 1), 22 | FVector3f(0, 1, 0), 23 | FVector3f(1, 0, 0), 24 | FVector3f(0, 0, 0), 25 | FVector3f(0, 0, 1), 26 | FVector3f(0, 1, 0), 27 | }; 28 | 29 | TArray> InitialCombined; 30 | for (int32 Index = 0; Index < 10; Index++) 31 | { 32 | auto Initial = TRealtimeMeshTangents(InitialData[Index], InitialData[9 - Index]); 33 | InitialCombined.Add(Initial); 34 | 35 | InitialBuilder.Add(Initial); 36 | TRealtimeMeshTangents A = InitialBuilder.GetValue(Index); 37 | TestTrue(FString::Printf(TEXT("TestRow: %d"), Index), A == Initial); 38 | } 39 | 40 | 41 | TRealtimeMeshTangents First = InitialCombined[0]; 42 | 43 | TRealtimeMeshStreamBuilder, TRealtimeMeshTangents> DirectBuilder(DataStream); 44 | TRealtimeMeshStreamBuilder, TRealtimeMeshTangents> ConvertedBuilder(DataStream); 45 | 46 | for (int32 Index = 0; Index < 10; Index++) 47 | { 48 | 49 | TRealtimeMeshTangents A(DirectBuilder[Index]); 50 | TRealtimeMeshTangents B = ConvertedBuilder[Index]; 51 | TestTrue(FString::Printf(TEXT("TestRow: %d Element 0"), Index), A == InitialCombined[Index]); 52 | TestTrue(FString::Printf(TEXT("TestRow: %d"), Index), ConvertedBuilder[Index].GetElement(0) == InitialData[9 - Index]); 53 | TestTrue(FString::Printf(TEXT("TestRow: %d Element 1"), Index), ConvertedBuilder[Index].GetElement(1) == InitialData[Index]); 54 | } 55 | 56 | 57 | const TArray TestDataA = 58 | { 59 | 3423, 60 | 6421, 61 | 6477, 62 | 24234, 63 | 7423, 64 | 117, 65 | 7754, 66 | 6787, 67 | 9106, 68 | 10022 69 | }; 70 | 71 | const TArray TestDataB = 72 | { 73 | 624, 74 | 523, 75 | 436, 76 | 4587, 77 | 924, 78 | 1955, 79 | 8583, 80 | 318, 81 | 85, 82 | 10349 83 | }; 84 | 85 | const TArray TestDataC = 86 | { 87 | // Just some random points for testing... 88 | FIntPoint(0, 0), 89 | FIntPoint(1, 0), 90 | FIntPoint(0, 1), 91 | FIntPoint(1, 1), 92 | FIntPoint(2, 0), 93 | FIntPoint(0, 2), 94 | FIntPoint(2, 2), 95 | FIntPoint(3, 0), 96 | FIntPoint(0, 3), 97 | FIntPoint(3, 3), 98 | }; 99 | 100 | const TArray TestDataD = 101 | { 102 | // Just some points with large arbitrary numbers for testing... 103 | FIntVector(3, 6, 7), 104 | FIntVector(-3, 5, 6), 105 | FIntVector(0, 1, 2), 106 | FIntVector(300, 500, 1000), 107 | FIntVector(20, 30, 40), 108 | FIntVector(50, 60, 70), 109 | FIntVector(80, 90, 100), 110 | FIntVector(110, 120, 130), 111 | FIntVector(140, 150, 160), 112 | FIntVector(170, 180, 190), 113 | FIntVector(200, 210, 220), 114 | FIntVector(230, 240, 250), 115 | FIntVector(260, 270, 280), 116 | FIntVector(290, 300, 310), 117 | FIntVector(320, 330, 340), 118 | FIntVector(350, 360, 370), 119 | FIntVector(380, 390, 400), 120 | FIntVector(410, 420, 430), 121 | FIntVector(440, 450, 460), 122 | FIntVector(470, 480, 490), 123 | FIntVector(500, 510, 520), 124 | FIntVector(530, 540, 550), 125 | FIntVector(560, 570, 580), 126 | FIntVector(590, 600, 610), 127 | FIntVector(620, 630, 640), 128 | FIntVector(650, 660, 670), 129 | FIntVector(680, 690, 700), 130 | FIntVector(710, 720, 730), 131 | FIntVector(740, 750, 760), 132 | FIntVector(770, 780, 790), 133 | FIntVector(800, 810, 820), 134 | FIntVector(830, 840, 850) 135 | }; 136 | 137 | 138 | // Test A Simple Full Copy 139 | // Test B Interleaved Straight Copy 140 | // Test C Simple Conversion Full Width 141 | // Test D Conversion Interleaved Single Element 142 | // Test E Conversion Interleaved Multi Element 143 | 144 | 145 | FRealtimeMeshStream TestTargetStream = FRealtimeMeshStream::Create(FRealtimeMeshStreams::Position); 146 | TestTargetStream.Append(TestDataD); 147 | 148 | 149 | TArray TestDirectCopyData; 150 | TestTargetStream.CopyRange(0, TestDataD.Num(), TestDirectCopyData); 151 | TestTrue(TEXT("Test Direct Copy"), TestDataD == TestDirectCopyData); 152 | 153 | 154 | 155 | 156 | FRealtimeMeshStream BulkConversionStream = FRealtimeMeshStream::Create>(RealtimeMesh::FRealtimeMeshStreams::Position); 157 | BulkConversionStream.Append(InitialCombined); 158 | TArray> BulkConverted; 159 | 160 | BulkConversionStream.CopyRange(0, InitialCombined.Num(), BulkConverted); 161 | TestTrue(TEXT("Test Bulk Conversion Append/CopyRange"), InitialCombined == BulkConverted); 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | // Make the test pass by returning true, or fail by returning false. 170 | return true; 171 | } -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/RealtimeMeshStreamMemoryTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "Mesh/RealtimeMeshAlgo.h" 4 | #include "Core/RealtimeMeshBuilder.h" 5 | #include "Misc/AutomationTest.h" 6 | 7 | IMPLEMENT_SIMPLE_AUTOMATION_TEST(RealtimeMeshStreamMemoryTest, "Private.RealtimeMeshStreamMemoryTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) 8 | 9 | using namespace RealtimeMesh; 10 | 11 | struct FChunkMesh { 12 | FRealtimeMeshStream Vertices = FRealtimeMeshStream::Create(FRealtimeMeshStreams::Position); 13 | FRealtimeMeshStream Tangents = FRealtimeMeshStream::Create>(FRealtimeMeshStreams::Tangents); 14 | FRealtimeMeshStream Colors = FRealtimeMeshStream::Create(FRealtimeMeshStreams::Color); 15 | FRealtimeMeshStream UV0 = FRealtimeMeshStream::Create(FRealtimeMeshStreams::TexCoords); 16 | }; 17 | 18 | bool RealtimeMeshStreamMemoryTest::RunTest(const FString& Parameters) 19 | { 20 | FChunkMesh TestMesh; 21 | TestMesh.Vertices.Add(FVector3f(1.f,1.f,1.f)); 22 | TestMesh.Tangents.Add(TRealtimeMeshTangents(FVector3f::UpVector, FVector3f::UpVector)); 23 | TestMesh.Colors.Add(FColor(255, 255, 255, 255)); 24 | TestMesh.UV0.Add(FVector2DHalf(1, 1)); 25 | 26 | FRealtimeMeshStreamSet TestSet; 27 | TestSet.AddStream(MoveTemp(TestMesh.Vertices)); 28 | TestSet.AddStream(MoveTemp(TestMesh.Tangents)); 29 | TestSet.AddStream(MoveTemp(TestMesh.Colors)); 30 | TestSet.AddStream(MoveTemp(TestMesh.UV0)); 31 | 32 | TestTrue(TEXT("Correct number of streams first pass"), TestSet.Num() == 4); 33 | 34 | TestMesh = FChunkMesh(); 35 | TestMesh.Vertices.Add(FVector3f(1.f,1.f,1.f)); 36 | TestMesh.Tangents.Add(TRealtimeMeshTangents(FVector3f::UpVector, FVector3f::UpVector)); 37 | TestMesh.Colors.Add(FColor(255, 255, 255, 255)); 38 | TestMesh.UV0.Add(FVector2DHalf(1, 1)); 39 | 40 | FRealtimeMeshStreamSet TestSet2; 41 | TestSet2.AddStream(MoveTemp(TestMesh.Vertices)); 42 | TestSet2.AddStream(MoveTemp(TestMesh.Tangents)); 43 | TestSet2.AddStream(MoveTemp(TestMesh.Colors)); 44 | TestSet2.AddStream(MoveTemp(TestMesh.UV0)); 45 | 46 | TestTrue(TEXT("Correct number of streams second pass"), TestSet.Num() == 4); 47 | 48 | 49 | const FRealtimeMeshStream PositionStream = FRealtimeMeshStream::Create(FRealtimeMeshStreams::Position); 50 | TArrayView StreamDataView = PositionStream.GetArrayView();//StreamDataPositions; 51 | 52 | TRealtimeMeshStreamBuilder PositionBuilder(PositionStream); 53 | for (int32 Index = 0; Index < PositionBuilder.Num(); ++Index) 54 | { 55 | UE_LOG(LogTemp, Warning, TEXT("X %f"), PositionBuilder.GetValue(Index).X); 56 | } 57 | 58 | // Make the test pass by returning true, or fail by returning false. 59 | return true; 60 | } 61 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Private/RealtimeMeshTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #include "RealtimeMeshTests.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FRealtimeMeshTestsModule" 6 | 7 | void FRealtimeMeshTestsModule::StartupModule() 8 | { 9 | } 10 | 11 | void FRealtimeMeshTestsModule::ShutdownModule() 12 | { 13 | 14 | } 15 | 16 | #undef LOCTEXT_NAMESPACE 17 | 18 | IMPLEMENT_MODULE(FRealtimeMeshTestsModule, RealtimeMeshTests) -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Public/FunctionalTests/RealtimeMeshBasicUsageActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "RealtimeMeshBasicUsageActor.generated.h" 8 | 9 | UCLASS() 10 | class REALTIMEMESHTESTS_API ARealtimeMeshBasicUsageActor : public ARealtimeMeshActor 11 | { 12 | GENERATED_BODY() 13 | private: 14 | FLinearColor LastColor; 15 | FLinearColor CurrentColor; 16 | float TimeRemaining; 17 | 18 | public: 19 | // Sets default values for this actor's properties 20 | ARealtimeMeshBasicUsageActor(); 21 | 22 | virtual void OnConstruction(const FTransform& Transform) override; 23 | 24 | virtual void TickActor(float DeltaTime, ELevelTick TickType, FActorTickFunction& ThisTickFunction) override; 25 | }; 26 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Public/FunctionalTests/RealtimeMeshLatentUpdateTestActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "RealtimeMeshLatentUpdateTestActor.generated.h" 8 | 9 | class URealtimeMeshSimple; 10 | 11 | 12 | /* 13 | * This is a test where the mesh structure is created on mesh generation, and the actual data is generated and applied later in BeginPlay 14 | */ 15 | UCLASS() 16 | class REALTIMEMESHTESTS_API ARealtimeMeshLatentUpdateTestActor : public ARealtimeMeshActor 17 | { 18 | GENERATED_BODY() 19 | 20 | public: 21 | 22 | UPROPERTY() 23 | TObjectPtr RealtimeMesh; 24 | 25 | UPROPERTY() 26 | FRealtimeMeshSectionGroupKey GroupA; 27 | 28 | UPROPERTY() 29 | FRealtimeMeshSectionGroupKey GroupB; 30 | 31 | 32 | // Sets default values for this actor's properties 33 | ARealtimeMeshLatentUpdateTestActor(); 34 | 35 | virtual void OnConstruction(const FTransform& Transform) override; 36 | 37 | virtual void BeginPlay() override; 38 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Public/FunctionalTests/RealtimeMeshStressTestActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "UObject/StrongObjectPtr.h" 8 | #include "RealtimeMeshSimple.h" 9 | #include "RealtimeMeshStressTestActor.generated.h" 10 | 11 | 12 | 13 | UCLASS() 14 | class REALTIMEMESHTESTS_API ARealtimeMeshStressTestActor2 : public ARealtimeMeshActor 15 | { 16 | GENERATED_BODY() 17 | private: 18 | TFuture PendingGeneration; 19 | 20 | public: 21 | // Sets default values for this actor's properties 22 | ARealtimeMeshStressTestActor2(); 23 | 24 | virtual void OnConstruction(const FTransform& Transform) override; 25 | 26 | virtual void TickActor(float DeltaTime, ELevelTick TickType, FActorTickFunction& ThisTickFunction) override; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Public/FunctionalTests/RealtimeMeshUpdateTestActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "RealtimeMeshActor.h" 7 | #include "RealtimeMeshUpdateTestActor.generated.h" 8 | 9 | class URealtimeMeshSimple; 10 | 11 | UCLASS() 12 | class REALTIMEMESHTESTS_API ARealtimeMeshUpdateTestActor : public ARealtimeMeshActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | 18 | UPROPERTY() 19 | TObjectPtr RealtimeMesh; 20 | 21 | // Create a section group passing it our mesh data 22 | UPROPERTY() 23 | FRealtimeMeshSectionGroupKey GroupKey; 24 | 25 | // Sets default values for this actor's properties 26 | ARealtimeMeshUpdateTestActor(); 27 | 28 | virtual void OnConstruction(const FTransform& Transform) override; 29 | 30 | }; -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/Public/RealtimeMeshTests.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FRealtimeMeshTestsModule : public IModuleInterface 9 | { 10 | public: 11 | virtual void StartupModule() override; 12 | virtual void ShutdownModule() override; 13 | }; 14 | -------------------------------------------------------------------------------- /Source/RealtimeMeshTests/RealtimeMeshTests.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025 TriAxis Games, L.L.C. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RealtimeMeshTests : ModuleRules 6 | { 7 | public RealtimeMeshTests(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | bUseUnity = false; 11 | #if UE_5_1_OR_LATER 12 | IncludeOrderVersion = EngineIncludeOrderVersion.Latest; 13 | #endif 14 | 15 | PublicDependencyModuleNames.AddRange( 16 | new string[] 17 | { 18 | "Core", 19 | "RealtimeMeshComponent", 20 | } 21 | ); 22 | 23 | PrivateDependencyModuleNames.AddRange( 24 | new string[] 25 | { 26 | "CoreUObject", 27 | "Engine", 28 | "Slate", 29 | "SlateCore", 30 | "RealtimeMeshComponent" 31 | } 32 | ); 33 | } 34 | } --------------------------------------------------------------------------------