// Copyright 2016 Chris Conway (Koderz). All Rights Reserved. #pragma once #include "RuntimeMeshCore.h" #include "RuntimeMeshComponent.h" #define RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(TaskType, DataType, DataPtr, RuntimeMesh, Code) \ class FParallelRuntimeMeshComponentTask_##TaskType \ { \ TWeakObjectPtr RuntimeMeshComponent; \ DataType* RMCCallData; \ public: \ FParallelRuntimeMeshComponentTask_##TaskType(TWeakObjectPtr InRMC, DataType* InData) \ : RuntimeMeshComponent(InRMC), RMCCallData(InData) \ { \ } \ \ ~FParallelRuntimeMeshComponentTask_##TaskType() \ { \ if (RMCCallData != nullptr) \ { \ delete RMCCallData; \ } \ } \ \ FORCEINLINE TStatId GetStatId() const \ { \ RETURN_QUICK_DECLARE_CYCLE_STAT(FParallelRuntimeMeshComponentTask_##TaskType, STATGROUP_TaskGraphTasks); \ } \ \ static ENamedThreads::Type GetDesiredThread() \ { \ return ENamedThreads::GameThread; \ } \ \ static ESubsequentsMode::Type GetSubsequentsMode() \ { \ return ESubsequentsMode::FireAndForget; \ } \ \ void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) \ { \ DataType* Data = RMCCallData; \ if (URuntimeMeshComponent* Mesh = RuntimeMeshComponent.Get()) \ { \ Code \ } \ } \ }; \ TGraphTask::CreateTask().ConstructAndDispatchWhenReady(RuntimeMesh, DataPtr); class RUNTIMEMESHCOMPONENT_API FRuntimeMeshAsync { public: /** * Create/replace a section. * @param SectionIndex Index of the section to create or replace. * @param Vertices Vertex buffer all vertex data for this section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. * @param UpdateFlags Flags pertaining to this particular update. */ template static void CreateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& Vertices, TArray& Triangles, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; bool bCreateCollision; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; } CallData->bCreateCollision = bCreateCollision; CallData->UpdateFrequency = UpdateFrequency; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(CreateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->CreateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->bCreateCollision, Data->UpdateFrequency, Data->UpdateFlags); }); } /** * Create/replace a section. * @param SectionIndex Index of the section to create or replace. * @param Vertices Vertex buffer all vertex data for this section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. * @param UpdateFlags Flags pertaining to this particular update. */ template static void CreateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& Vertices, TArray& Triangles, const FBox& BoundingBox, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; FBox BoundingBox; bool bCreateCollision; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; } CallData->BoundingBox = BoundingBox; CallData->bCreateCollision = bCreateCollision; CallData->UpdateFrequency = UpdateFrequency; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(CreateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->CreateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->BoundingBox, Data->bCreateCollision, Data->UpdateFrequency, Data->UpdateFlags); }); } /** * Create/replace a section using 2 vertex buffers. One contains positions only, the other contains all other data. This allows for very efficient updates of the positions of a mesh. * @param SectionIndex Index of the section to create or replace. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param VertexData Vertex buffer containing everything except position for each vertex. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. * @param UpdateFlags Flags pertaining to this particular update. */ template static void CreateMeshSectionDualBuffer(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, TArray& Triangles, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; TArray Vertices; TArray Triangles; bool bCreateCollision; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); CallData->Vertices = MoveTemp(VertexData); CallData->Triangles = MoveTemp(Triangles); } else { CallData->VertexPositions = VertexPositions; CallData->Vertices = VertexData; CallData->Triangles = Triangles; } CallData->bCreateCollision = bCreateCollision; CallData->UpdateFrequency = UpdateFrequency; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(CreateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->CreateMeshSection(Data->SectionIndex, Data->VertexPositions, Data->Vertices, Data->Triangles, Data->bCreateCollision, Data->UpdateFrequency, Data->UpdateFlags); }); } /** * Create/replace a section using 2 vertex buffers. One contains positions only, the other contains all other data. This allows for very efficient updates of the positions of a mesh. * @param SectionIndex Index of the section to create or replace. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param VertexData Vertex buffer containing everything except position for each vertex. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. * @param UpdateFlags Flags pertaining to this particular update. */ template static void CreateMeshSectionDualBuffer(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, TArray& Triangles, const FBox& BoundingBox, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; TArray Vertices; TArray Triangles; FBox BoundingBox; bool bCreateCollision; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); CallData->Vertices = MoveTemp(VertexData); CallData->Triangles = MoveTemp(Triangles); } else { CallData->VertexPositions = VertexPositions; CallData->Vertices = VertexData; CallData->Triangles = Triangles; } CallData->BoundingBox = BoundingBox; CallData->bCreateCollision = bCreateCollision; CallData->UpdateFrequency = UpdateFrequency; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(CreateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->CreateMeshSection(Data->SectionIndex, Data->VertexPositions, Data->Vertices, Data->Triangles, Data->BoundingBox, Data->bCreateCollision, Data->UpdateFrequency, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. If this is a dual buffer section, you cannot change the length of the vertices. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer all vertex data for this section, or in the case of dual buffer section it contains everything but position. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& Vertices, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); } else { CallData->Vertices = Vertices; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. If this is a dual buffer section, you cannot change the length of the vertices. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer all vertex data for this section, or in the case of dual buffer section it contains everything but position. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& Vertices, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; FBox BoundingBox; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); } else { CallData->Vertices = Vertices; } CallData->BoundingBox = BoundingBox; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->BoundingBox, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. If this is a dual buffer section, you cannot change the length of the vertices. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer all vertex data for this section, or in the case of dual buffer section it contains everything but position. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& Vertices, TArray& Triangles, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. If this is a dual buffer section, you cannot change the length of the vertices. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer all vertex data for this section, or in the case of dual buffer section it contains everything but position. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& Vertices, TArray& Triangles, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; FBox BoundingBox; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; } CallData->BoundingBox = BoundingBox; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->BoundingBox, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. This is only for dual buffer sections. You cannot change the length of positions or vertex data unless you specify both together. * @param SectionIndex Index of the section to update. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param Vertices Vertex buffer containing everything except position for each vertex. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, TArray& Vertices, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; TArray Vertices; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); CallData->Vertices = MoveTemp(Vertices); } else { CallData->VertexPositions = VertexPositions; CallData->Vertices = Vertices; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->VertexPositions, Data->Vertices, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. This is only for dual buffer sections. You cannot change the length of positions or vertex data unless you specify both together. * @param SectionIndex Index of the section to update. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param Vertices Vertex buffer containing everything except position for each vertex. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, TArray& Vertices, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; TArray Vertices; FBox BoundingBox; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); CallData->Vertices = MoveTemp(Vertices); } else { CallData->VertexPositions = VertexPositions; CallData->Vertices = Vertices; } CallData->BoundingBox = BoundingBox; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->VertexPositions, Data->Vertices, Data->BoundingBox, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. This is only for dual buffer sections. You cannot change the length of positions or vertex data unless you specify both together. * @param SectionIndex Index of the section to update. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param Vertices Vertex buffer containing everything except position for each vertex. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, TArray& Vertices, TArray& Triangles, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; TArray Vertices; TArray Triangles; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); } else { CallData->VertexPositions = VertexPositions; CallData->Vertices = Vertices; CallData->Triangles = Triangles; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->VertexPositions, Data->Vertices, Data->Triangles, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. This is only for dual buffer sections. You cannot change the length of positions or vertex data unless you specify both together. * @param SectionIndex Index of the section to update. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param Vertices Vertex buffer containing everything except position for each vertex. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param UpdateFlags Flags pertaining to this particular update. */ template static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, TArray& Vertices, TArray& Triangles, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; TArray Vertices; TArray Triangles; FBox BoundingBox; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); } else { CallData->VertexPositions = VertexPositions; CallData->Vertices = Vertices; CallData->Triangles = Triangles; } CallData->BoundingBox = BoundingBox; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->VertexPositions, Data->Vertices, Data->Triangles, Data->BoundingBox, Data->UpdateFlags); }); } /** * Updates a sections position buffer only. This cannot be used on a non-dual buffer section. You cannot change the length of the vertex position buffer with this function. * @param SectionIndex Index of the section to update. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param UpdateFlags Flags pertaining to this particular update. */ static void UpdateMeshSectionPositionsImmediate(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); } else { CallData->VertexPositions = VertexPositions; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSectionPositionsImmediate, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSectionPositionsImmediate(Data->SectionIndex, Data->VertexPositions, Data->UpdateFlags); }); } /** * Updates a sections position buffer only. This cannot be used on a non-dual buffer section. You cannot change the length of the vertex position buffer with this function. * @param SectionIndex Index of the section to update. * @param VertexPositions Vertex buffer containing only the position information for each vertex. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. * @param UpdateFlags Flags pertaining to this particular update. */ static void UpdateMeshSectionPositionsImmediate(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, TArray& VertexPositions, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray VertexPositions; FBox BoundingBox; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->VertexPositions = MoveTemp(VertexPositions); } else { CallData->VertexPositions = VertexPositions; } CallData->BoundingBox = BoundingBox; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSectionPositionsImmediate, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSectionPositionsImmediate(Data->SectionIndex, Data->VertexPositions, Data->BoundingBox, Data->UpdateFlags); }); } /** * Create/replace a section. * @param SectionIndex Index of the section to create or replace. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV0 Optional array of texture co-ordinates for each vertex (UV Channel 0). If supplied, must be same length as Vertices array. * @param Colors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. */ static void CreateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& UV0, const TArray& Colors, const TArray& Tangents, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; TArray Normals; TArray UV0; TArray Colors; TArray Tangents; bool bCreateCollision = false; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); CallData->Normals = MoveTemp(Normals); CallData->UV0 = MoveTemp(UV0); CallData->Colors = MoveTemp(Colors); CallData->Tangents = MoveTemp(Tangents); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; CallData->Normals = Normals; CallData->UV0 = UV0; CallData->Colors = Colors; CallData->Tangents = Tangents; } CallData->bCreateCollision = bCreateCollision; CallData->UpdateFrequency = UpdateFrequency; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(CreateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->CreateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->Normals, Data->UV0, Data->Colors, Data->Tangents, Data->bCreateCollision, Data->UpdateFrequency, Data->UpdateFlags); }); } /** * Create/replace a section. * @param SectionIndex Index of the section to create or replace. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV0 Optional array of texture co-ordinates for each vertex (UV Channel 0). If supplied, must be same length as Vertices array. * @param UV1 Optional array of texture co-ordinates for each vertex (UV Channel 1). If supplied, must be same length as Vertices array. * @param Colors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. * @param bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. */ static void CreateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& UV0, const TArray& UV1, const TArray& Colors, const TArray& Tangents, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; TArray Normals; TArray UV0; TArray UV1; TArray Colors; TArray Tangents; bool bCreateCollision = false; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); CallData->Normals = MoveTemp(Normals); CallData->UV0 = MoveTemp(UV0); CallData->UV1 = MoveTemp(UV1); CallData->Colors = MoveTemp(Colors); CallData->Tangents = MoveTemp(Tangents); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; CallData->Normals = Normals; CallData->UV0 = UV0; CallData->UV1 = UV1; CallData->Colors = Colors; CallData->Tangents = Tangents; } CallData->bCreateCollision = bCreateCollision; CallData->UpdateFrequency = UpdateFrequency; CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(CreateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->CreateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->Normals, Data->UV0, Data->UV1, Data->Colors, Data->Tangents, Data->bCreateCollision, Data->UpdateFrequency, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV1 Optional array of texture co-ordinates for each vertex (UV Channel 1). If supplied, must be same length as Vertices array. * @param Colors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. */ static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& Vertices, const TArray& Normals, const TArray& UV0, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Normals; TArray UV0; TArray UV1; TArray Colors; TArray Tangents; bool bCreateCollision = false; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Normals = MoveTemp(Normals); CallData->UV0 = MoveTemp(UV0); CallData->Colors = MoveTemp(Colors); CallData->Tangents = MoveTemp(Tangents); } else { CallData->Vertices = Vertices; CallData->Normals = Normals; CallData->UV0 = UV0; CallData->Colors = Colors; CallData->Tangents = Tangents; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->Normals, Data->UV0, Data->Colors, Data->Tangents, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV0 Optional array of texture co-ordinates for each vertex (UV Channel 0). If supplied, must be same length as Vertices array. * @param UV1 Optional array of texture co-ordinates for each vertex (UV Channel 1). If supplied, must be same length as Vertices array. * @param Colors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. */ static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& Vertices, const TArray& Normals, const TArray& UV0, const TArray& UV1, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Normals; TArray UV0; TArray UV1; TArray Colors; TArray Tangents; bool bCreateCollision = false; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Normals = MoveTemp(Normals); CallData->UV0 = MoveTemp(UV0); CallData->UV1 = MoveTemp(UV1); CallData->Colors = MoveTemp(Colors); CallData->Tangents = MoveTemp(Tangents); } else { CallData->Vertices = Vertices; CallData->Normals = Normals; CallData->UV0 = UV0; CallData->UV1 = UV1; CallData->Colors = Colors; CallData->Tangents = Tangents; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->Normals, Data->UV0, Data->UV1, Data->Colors, Data->Tangents, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV0 Optional array of texture co-ordinates for each vertex (UV Channel 0). If supplied, must be same length as Vertices array. * @param Colors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. */ static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& UV0, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; TArray Normals; TArray UV0; TArray Colors; TArray Tangents; bool bCreateCollision = false; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); CallData->Normals = MoveTemp(Normals); CallData->UV0 = MoveTemp(UV0); CallData->Colors = MoveTemp(Colors); CallData->Tangents = MoveTemp(Tangents); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; CallData->Normals = Normals; CallData->UV0 = UV0; CallData->Colors = Colors; CallData->Tangents = Tangents; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->Normals, Data->UV0, Data->Colors, Data->Tangents, Data->UpdateFlags); }); } /** * Updates a section. This is faster than CreateMeshSection. * @param SectionIndex Index of the section to update. * @param Vertices Vertex buffer of all vertex positions to use for this mesh section. * @param Triangles Index buffer indicating which vertices make up each triangle. Length must be a multiple of 3. * @param Normals Optional array of normal vectors for each vertex. If supplied, must be same length as Vertices array. * @param UV0 Optional array of texture co-ordinates for each vertex (UV Channel 0). If supplied, must be same length as Vertices array. * @param UV1 Optional array of texture co-ordinates for each vertex (UV Channel 1). If supplied, must be same length as Vertices array. * @param Colors Optional array of colors for each vertex. If supplied, must be same length as Vertices array. * @param Tangents Optional array of tangent vector for each vertex. If supplied, must be same length as Vertices array. */ static void UpdateMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& UV0, const TArray& UV1, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { struct FRMCAsyncData { int32 SectionIndex; TArray Vertices; TArray Triangles; TArray Normals; TArray UV0; TArray UV1; TArray Colors; TArray Tangents; bool bCreateCollision = false; EUpdateFrequency UpdateFrequency; ESectionUpdateFlags UpdateFlags; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (!!(UpdateFlags & ESectionUpdateFlags::MoveArrays)) { CallData->Vertices = MoveTemp(Vertices); CallData->Triangles = MoveTemp(Triangles); CallData->Normals = MoveTemp(Normals); CallData->UV0 = MoveTemp(UV0); CallData->UV1 = MoveTemp(UV1); CallData->Colors = MoveTemp(Colors); CallData->Tangents = MoveTemp(Tangents); } else { CallData->Vertices = Vertices; CallData->Triangles = Triangles; CallData->Normals = Normals; CallData->UV0 = UV0; CallData->UV1 = UV1; CallData->Colors = Colors; CallData->Tangents = Tangents; } CallData->UpdateFlags = UpdateFlags | ESectionUpdateFlags::MoveArrays; // We can always use move arrays here since we either just copied it, or moved it from the original RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(UpdateMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->UpdateMeshSection(Data->SectionIndex, Data->Vertices, Data->Triangles, Data->Normals, Data->UV0, Data->UV1, Data->Colors, Data->Tangents, Data->UpdateFlags); }); } /** Clear a section of the procedural mesh. */ static void ClearMeshSection(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex) { struct FRMCAsyncData { int32 SectionIndex; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(ClearMeshSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->ClearMeshSection(Data->SectionIndex); }); } /** Clear all mesh sections and reset to empty state */ static void ClearAllMeshSections(TWeakObjectPtr InRuntimeMeshComponent) { struct FRMCAsyncData { int32 SectionIndex; }; FRMCAsyncData* CallData = new FRMCAsyncData; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(ClearAllMeshSections, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->ClearAllMeshSections(); }); } /** Sets the tessellation triangles needed to correctly support tessellation on a section. */ static void SetSectionTessellationTriangles(TWeakObjectPtr InRuntimeMeshComponent, int32 SectionIndex, const TArray& TessellationTriangles, bool bShouldMoveArray = false) { struct FRMCAsyncData { int32 SectionIndex; TArray TessellationTriangles; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->SectionIndex = SectionIndex; if (bShouldMoveArray) { CallData->TessellationTriangles = MoveTemp(TessellationTriangles); } else { CallData->TessellationTriangles = TessellationTriangles; } RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(SetSectionTessellationTriangles, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->SetSectionTessellationTriangles(Data->SectionIndex, Data->TessellationTriangles, true); }); } /** Sets the geometry for a collision only section */ static void SetMeshCollisionSection(TWeakObjectPtr InRuntimeMeshComponent, int32 CollisionSectionIndex, const TArray& Vertices, const TArray& Triangles) { struct FRMCAsyncData { int32 CollisionSectionIndex; TArray Vertices; TArray Triangles; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->CollisionSectionIndex = CollisionSectionIndex; CallData->Vertices = Vertices; CallData->Triangles = Triangles; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(SetMeshCollisionSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->SetMeshCollisionSection(Data->CollisionSectionIndex, Data->Vertices, Data->Triangles); }); } /** Clears the geometry for a collision only section */ static void ClearMeshCollisionSection(TWeakObjectPtr InRuntimeMeshComponent, int32 CollisionSectionIndex) { struct FRMCAsyncData { int32 CollisionSectionIndex; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->CollisionSectionIndex = CollisionSectionIndex; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(ClearMeshCollisionSection, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->ClearMeshCollisionSection(Data->CollisionSectionIndex); }); } /** Clears the geometry for ALL collision only sections */ static void ClearAllMeshCollisionSections(TWeakObjectPtr InRuntimeMeshComponent) { struct FRMCAsyncData { int32 CollisionSectionIndex; }; FRMCAsyncData* CallData = new FRMCAsyncData; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(ClearAllMeshCollisionSections, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->ClearAllMeshCollisionSections(); }); } /** Add simple collision convex to this component */ static void AddCollisionConvexMesh(TWeakObjectPtr InRuntimeMeshComponent, TArray ConvexVerts) { struct FRMCAsyncData { TArray ConvexVerts; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->ConvexVerts = ConvexVerts; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(AddCollisionConvexMesh, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->AddCollisionConvexMesh(Data->ConvexVerts); }); } /** Add simple collision convex to this component */ static void ClearCollisionConvexMeshes(TWeakObjectPtr InRuntimeMeshComponent) { struct FRMCAsyncData { int32 CollisionSectionIndex; }; FRMCAsyncData* CallData = new FRMCAsyncData; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(ClearCollisionConvexMeshes, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->ClearCollisionConvexMeshes(); }); } /** Function to replace _all_ simple collision in one go */ static void SetCollisionConvexMeshes(TWeakObjectPtr InRuntimeMeshComponent, const TArray< TArray >& ConvexMeshes) { struct FRMCAsyncData { TArray> ConvexMeshes; }; FRMCAsyncData* CallData = new FRMCAsyncData; CallData->ConvexMeshes = ConvexMeshes; RUNTIMEMESHCOMPONENTASYNC_ENQUEUETASK(SetCollisionConvexMeshes, FRMCAsyncData, CallData, InRuntimeMeshComponent, { Mesh->SetCollisionConvexMeshes(Data->ConvexMeshes); }); } };