// Copyright 2016 Chris Conway (Koderz). All Rights Reserved. #pragma once #include "Components/MeshComponent.h" #include "RuntimeMeshCore.h" #include "RuntimeMeshSection.h" #include "RuntimeMeshGenericVertex.h" #include "RuntimeMeshBuilder.h" #include "PhysicsEngine/ConvexElem.h" #include "RuntimeMeshComponent.generated.h" // This set of macros is only meant for argument validation as it will return out of whatever scope. #if WITH_EDITOR #define RMC_CHECKINGAME_LOGINEDITOR(Condition, Message, RetVal) \ { if (!(Condition)) \ { \ Log(TEXT(Message), true); \ return RetVal; \ } } #else #define RMC_CHECKINGAME_LOGINEDITOR(Condition, Message, RetVal) \ check(Condition && Message); #endif #define RMC_VALIDATE_CREATIONPARAMETERS(SectionIndex, Vertices, Triangles, RetVal) \ RMC_CHECKINGAME_LOGINEDITOR((SectionIndex >= 0), "SectionIndex cannot be negative.", RetVal); \ RMC_CHECKINGAME_LOGINEDITOR((Vertices.Num() > 0), "Vertices length must not be 0.", RetVal); \ RMC_CHECKINGAME_LOGINEDITOR((Triangles.Num() > 0), "Triangles length must not be 0", RetVal); #define RMC_VALIDATE_CREATIONPARAMETERS_DUALBUFFER(SectionIndex, Vertices, Triangles, Positions, RetVal) \ RMC_VALIDATE_CREATIONPARAMETERS(SectionIndex, Vertices, Triangles, RetVal) \ RMC_CHECKINGAME_LOGINEDITOR((Positions.Num() == Vertices.Num()), "Positions must be the same length as Vertices", RetVal); #define RMC_VALIDATE_BOUNDINGBOX(BoundingBox, RetVal) \ RMC_CHECKINGAME_LOGINEDITOR(BoundingBox.IsValid, "BoundingBox must be valid.", RetVal); #define RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, RetVal) \ RMC_CHECKINGAME_LOGINEDITOR((SectionIndex >= 0), "SectionIndex cannot be negative.", RetVal); \ RMC_CHECKINGAME_LOGINEDITOR((SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid()), "Invalid SectionIndex.", RetVal); #define RMC_VALIDATE_UPDATEPARAMETERS_INTERNALSECTION(SectionIndex, RetVal) \ RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, RetVal) \ RMC_CHECKINGAME_LOGINEDITOR((MeshSections[SectionIndex]->bIsLegacySectionType), "Section is not of legacy type.", RetVal); #define RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, RetVal) \ RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, RetVal) \ RMC_CHECKINGAME_LOGINEDITOR((MeshSections[SectionIndex]->IsDualBufferSection()), "Section is not dual buffer.", RetVal); /* * This tick function is used to drive the collision cooker. It is enabled for one frame when we need to update collision. * This keeps from cooking on each individual create/update section as the original PMC did */ USTRUCT() struct RUNTIMEMESHCOMPONENT_API FRuntimeMeshComponentPrePhysicsTickFunction : public FTickFunction { GENERATED_USTRUCT_BODY() /* Target RMC to tick */ class URuntimeMeshComponent* Target; virtual void ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) override; virtual FString DiagnosticMessage() override; }; template<> #if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION <= 15 struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase #else struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 #endif { enum { WithCopy = false }; }; /** * Component that allows you to specify custom triangle mesh geometry for rendering and collision. */ UCLASS(HideCategories = (Object, LOD), Meta = (BlueprintSpawnableComponent)) class RUNTIMEMESHCOMPONENT_API URuntimeMeshComponent : public UMeshComponent, public IInterface_CollisionDataProvider { GENERATED_BODY() private: /* Creates an mesh section of a specified type at the specified index. */ template TSharedPtr CreateOrResetSection(int32 SectionIndex, bool bWantsSeparatePositionBuffer, bool bInIsLegacySectionType = false) { // Ensure sections array is long enough if (SectionIndex >= MeshSections.Num()) { MeshSections.SetNum(SectionIndex + 1, false); } // Create new section TSharedPtr NewSection = MakeShareable(new SectionType(bWantsSeparatePositionBuffer)); NewSection->bIsLegacySectionType = bInIsLegacySectionType; // Store section at index MeshSections[SectionIndex] = NewSection; return NewSection; } /* Creates a mesh section of an internal type meant for the generic vertex and the old PMC style API */ TSharedPtr CreateOrResetSectionLegacyType(int32 SectionIndex, int32 NumUVChannels); /* Gets the material for a section or the default material if one's not provided. */ UMaterialInterface* GetSectionMaterial(int32 Index) { auto Material = GetMaterial(Index); return Material ? Material : UMaterial::GetDefaultMaterial(MD_Surface); } /* Finishes creating a section, including entering it for batch updating, or updating the RT directly */ void CreateSectionInternal(int32 SectionIndex, ESectionUpdateFlags UpdateFlags); /* Finishes updating a section, including entering it for batch updating, or updating the RT directly */ void UpdateSectionInternal(int32 SectionIndex, bool bHadVertexPositionsUpdate, bool bHadVertexUpdates, bool bHadIndexUpdates, bool bNeedsBoundsUpdate, ESectionUpdateFlags UpdateFlags); /* Finishes updating a sections positions (Only used if section is dual vertex buffer), including entering it for batch updating, or updating the RT directly */ void UpdateSectionVertexPositionsInternal(int32 SectionIndex, bool bNeedsBoundsUpdate); /* Finishes updating a sections properties, like visible/casts shadow, a*/ void UpdateSectionPropertiesInternal(int32 SectionIndex, bool bUpdateRequiresProxyRecreateIfStatic); /* Internal log helper for the templates to be able to use the internal logger */ static void Log(FString Text, bool bIsError = false) { if (bIsError) { UE_LOG(RuntimeMeshLog, Error, TEXT("%s"), *Text); } else { UE_LOG(RuntimeMeshLog, Warning, TEXT("%s"), *Text); } } public: URuntimeMeshComponent(const FObjectInitializer& ObjectInitializer); /** * 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 void CreateMeshSection(int32 SectionIndex, TArray& Vertices, TArray& Triangles, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { // It is only safe to call these functions from the game thread. check(IsInGameThread()); SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection_VertexType); // Validate all creation parameters RMC_VALIDATE_CREATIONPARAMETERS(SectionIndex, Vertices, Triangles, /*VoidReturn*/); // Create the section TSharedPtr> Section = CreateOrResetSection>(SectionIndex, false); // Set the vertex and index buffers bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; Section->UpdateVertexBuffer(Vertices, nullptr, bShouldUseMove); Section->UpdateIndexBuffer(Triangles, bShouldUseMove); // Track collision status and update collision information if necessary Section->CollisionEnabled = bCreateCollision; Section->UpdateFrequency = UpdateFrequency; // Finalize section. CreateSectionInternal(SectionIndex, 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 void CreateMeshSection(int32 SectionIndex, TArray& Vertices, TArray& Triangles, const FBox& BoundingBox, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { // It is only safe to call these functions from the game thread. check(IsInGameThread()); SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection_VertexType_WithBoundingBox); // Validate all creation parameters RMC_VALIDATE_CREATIONPARAMETERS(SectionIndex, Vertices, Triangles, /*VoidReturn*/); RMC_VALIDATE_BOUNDINGBOX(BoundingBox, /*VoidReturn*/); // Create the section TSharedPtr> Section = CreateOrResetSection>(SectionIndex, false); // Set the vertex and index buffers bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; Section->UpdateVertexBuffer(Vertices, &BoundingBox, bShouldUseMove); Section->UpdateIndexBuffer(Triangles, bShouldUseMove); // Track collision status and update collision information if necessary Section->CollisionEnabled = bCreateCollision; Section->UpdateFrequency = UpdateFrequency; // Finalize section. CreateSectionInternal(SectionIndex, 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 void CreateMeshSectionDualBuffer(int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, TArray& Triangles, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSectionDualBuffer_VertexType); // Validate all creation parameters RMC_VALIDATE_CREATIONPARAMETERS_DUALBUFFER(SectionIndex, VertexData, Triangles, VertexPositions, /*VoidReturn*/); TSharedPtr> Section = CreateOrResetSection>(SectionIndex, true); bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; Section->UpdateVertexPositionBuffer(VertexPositions, nullptr, bShouldUseMove); Section->UpdateVertexBuffer(VertexData, nullptr, bShouldUseMove); Section->UpdateIndexBuffer(Triangles, bShouldUseMove); // Track collision status and update collision information if necessary Section->CollisionEnabled = bCreateCollision; Section->UpdateFrequency = UpdateFrequency; // Finalize section. CreateSectionInternal(SectionIndex, 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 void CreateMeshSectionDualBuffer(int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, TArray& Triangles, const FBox& BoundingBox, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSectionDualBuffer_VertexType_WithBoundingBox); // Validate all creation parameters RMC_VALIDATE_CREATIONPARAMETERS_DUALBUFFER(SectionIndex, VertexData, Triangles, VertexPositions, /*VoidReturn*/); RMC_VALIDATE_BOUNDINGBOX(BoundingBox, /*VoidReturn*/); TSharedPtr> Section = CreateOrResetSection>(SectionIndex, true); bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; Section->UpdateVertexPositionBuffer(VertexPositions, &BoundingBox, bShouldUseMove); Section->UpdateVertexBuffer(VertexData, nullptr, bShouldUseMove); Section->UpdateIndexBuffer(Triangles, bShouldUseMove); // Track collision status and update collision information if necessary Section->CollisionEnabled = bCreateCollision; Section->UpdateFrequency = UpdateFrequency; // Finalize section. CreateSectionInternal(SectionIndex, 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 void UpdateMeshSection(int32 SectionIndex, TArray& Vertices, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_VertexType); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && Vertices.Num() != Section->VertexBuffer.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertices if supplied bool bUpdatedVertices = false; if (Vertices.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexBuffer(Vertices, nullptr, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertices) { UpdateSectionInternal(SectionIndex, false, bUpdatedVertices, false, bNeedsBoundsUpdate, 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 void UpdateMeshSection(int32 SectionIndex, TArray& Vertices, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_VertexType_WithBoundingBox); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); RMC_VALIDATE_BOUNDINGBOX(BoundingBox, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && Vertices.Num() != Section->VertexBuffer.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertices if supplied bool bUpdatedVertices = false; if (Vertices.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexBuffer(Vertices, &BoundingBox, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertices) { UpdateSectionInternal(SectionIndex, false, bUpdatedVertices, false, bNeedsBoundsUpdate, 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 void UpdateMeshSection(int32 SectionIndex, TArray& Vertices, TArray& Triangles, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_VertexType_WithTriangles); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && Vertices.Num() != Section->VertexBuffer.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertices if supplied bool bUpdatedVertices = false; if (Vertices.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexBuffer(Vertices, nullptr, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Update triangles if supplied bool bUpdatedIndices = false; if (Triangles.Num() > 0) { Section->UpdateIndexBuffer(Triangles, bShouldUseMove); bUpdatedIndices = true; } else { Log(TEXT("UpdateMeshSection() - Triangles empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertices || bUpdatedIndices) { UpdateSectionInternal(SectionIndex, false, bUpdatedVertices, bUpdatedIndices, bNeedsBoundsUpdate, 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 void UpdateMeshSection(int32 SectionIndex, TArray& Vertices, TArray& Triangles, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_VertexType_WithTrianglesAndBoundinBox); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); RMC_VALIDATE_BOUNDINGBOX(BoundingBox, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && Vertices.Num() != Section->VertexBuffer.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertices if supplied bool bUpdatedVertices = false; if (Vertices.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexBuffer(Vertices, &BoundingBox, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Update indices if supplied bool bUpdatedIndices = false; if (Triangles.Num() > 0) { Section->UpdateIndexBuffer(Triangles, bShouldUseMove); bUpdatedIndices = true; } else { Log(TEXT("UpdateMeshSection() - Triangles empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertices || bUpdatedIndices) { UpdateSectionInternal(SectionIndex, false, bUpdatedVertices, bUpdatedIndices, bNeedsBoundsUpdate, 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 VertexData Vertex buffer containing everything except position for each vertex. * @param UpdateFlags Flags pertaining to this particular update. */ template void UpdateMeshSection(int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && VertexData.Num() != Section->VertexBuffer.Num() && VertexPositions.Num() != VertexData.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertex positions if supplied bool bUpdatedVertexPositions = false; if (VertexPositions.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexPositionBuffer(VertexPositions, nullptr, bShouldUseMove); bUpdatedVertexPositions = true; } else { Log(TEXT("UpdatemeshSection() - Vertex positions empty. They will not be updated.")); } // Update vertices if supplied bool bUpdatedVertices = false; if (VertexData.Num() > 0) { Section->UpdateVertexBuffer(VertexData, nullptr, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertexPositions || bUpdatedVertices) { UpdateSectionInternal(SectionIndex, bUpdatedVertexPositions, bUpdatedVertices, false, bNeedsBoundsUpdate, 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 VertexData 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 void UpdateMeshSection(int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType_WithBoundingBox); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/); RMC_VALIDATE_BOUNDINGBOX(BoundingBox, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && VertexData.Num() != Section->VertexBuffer.Num() && VertexPositions.Num() != VertexData.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertex positions if supplied bool bUpdatedVertexPositions = false; if (VertexPositions.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexPositionBuffer(VertexPositions, &BoundingBox, bShouldUseMove); bUpdatedVertexPositions = true; } else { Log(TEXT("UpdatemeshSection() - Vertex positions empty. They will not be updated.")); } // Update vertices if supplied bool bUpdatedVertices = false; if (VertexData.Num() > 0) { Section->UpdateVertexBuffer(VertexData, nullptr, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertexPositions || bUpdatedVertices) { UpdateSectionInternal(SectionIndex, bUpdatedVertexPositions, bUpdatedVertices, false, bNeedsBoundsUpdate, 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 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 UpdateFlags Flags pertaining to this particular update. */ template void UpdateMeshSection(int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, TArray& Triangles, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType_WithTriangles); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && VertexData.Num() != Section->VertexBuffer.Num() && VertexPositions.Num() != VertexData.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertex positions if supplied bool bUpdatedVertexPositions = false; if (VertexPositions.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexPositionBuffer(VertexPositions, nullptr, bShouldUseMove); bUpdatedVertexPositions = true; } else { Log(TEXT("UpdatemeshSection() - Vertex positions empty. They will not be updated.")); } // Update vertices if supplied bool bUpdatedVertices = false; if (VertexData.Num() > 0) { Section->UpdateVertexBuffer(VertexData, nullptr, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Update triangles if supplied bool bUpdatedIndices = false; if (Triangles.Num() > 0) { Section->UpdateIndexBuffer(Triangles, bShouldUseMove); bUpdatedIndices = true; } else { Log(TEXT("UpdateMeshSection() - Triangles empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertexPositions || bUpdatedVertices || bUpdatedIndices) { UpdateSectionInternal(SectionIndex, bUpdatedVertexPositions, bUpdatedVertices, bUpdatedIndices, bNeedsBoundsUpdate, 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 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 UpdateFlags Flags pertaining to this particular update. */ template void UpdateMeshSection(int32 SectionIndex, TArray& VertexPositions, TArray& VertexData, TArray& Triangles, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None) { SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType_WithTrianglesAndBoundinBox); // Validate all update parameters RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/); RMC_VALIDATE_BOUNDINGBOX(BoundingBox, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); // Check dual buffer section status if (Section->IsDualBufferSection() && VertexData.Num() != Section->VertexBuffer.Num() && VertexPositions.Num() != VertexData.Num()) { Log(TEXT("UpdateMeshSection() - Vertices cannot change length unless the positions are updated as well."), true); return; } bool bShouldUseMove = (UpdateFlags & ESectionUpdateFlags::MoveArrays) != ESectionUpdateFlags::None; bool bNeedsBoundsUpdate = false; // Update vertex positions if supplied bool bUpdatedVertexPositions = false; if (VertexPositions.Num() > 0) { bNeedsBoundsUpdate = Section->UpdateVertexPositionBuffer(VertexPositions, &BoundingBox, bShouldUseMove); bUpdatedVertexPositions = true; } else { Log(TEXT("UpdatemeshSection() - Vertex positions empty. They will not be updated.")); } // Update vertices if supplied bool bUpdatedVertices = false; if (VertexData.Num() > 0) { Section->UpdateVertexBuffer(VertexData, nullptr, bShouldUseMove); bUpdatedVertices = true; } else { Log(TEXT("UpdateMeshSection() - Vertices empty. They will not be updated.")); } // Update indices if supplied bool bUpdatedIndices = false; if (Triangles.Num() > 0) { Section->UpdateIndexBuffer(Triangles, bShouldUseMove); bUpdatedIndices = true; } else { Log(TEXT("UpdateMeshSection() - Triangles empty. They will not be updated.")); } // Finalize section update if we have anything to apply if (bUpdatedVertexPositions || bUpdatedVertices || bUpdatedIndices) { UpdateSectionInternal(SectionIndex, bUpdatedVertexPositions, bUpdatedVertices, bUpdatedIndices, bNeedsBoundsUpdate, 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. */ void UpdateMeshSectionPositionsImmediate(int32 SectionIndex, TArray& VertexPositions, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** * 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. */ void UpdateMeshSectionPositionsImmediate(int32 SectionIndex, TArray& VertexPositions, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** * Starts an in place update of vertex positions. * @param SectionIndex Index of the section to update. */ TArray* BeginMeshSectionPositionUpdate(int32 SectionIndex); /** * Finishes an in place update of vertex positions. * This will push the update to the GPU and calculate the new Bounding Box * @param SectionIndex Index of the section to update. */ void EndMeshSectionPositionUpdate(int32 SectionIndex); /** * Finishes an in place update of vertex positions. * This will push the update to the GPU * @param SectionIndex Index of the section to update. * @param BoundingBox The bounds of this section. Faster than the RMC automatically calculating it. */ void EndMeshSectionPositionUpdate(int32 SectionIndex, const FBox& BoundingBox); template void BeginMeshSectionUpdate(int32 SectionIndex, TArray*& Vertices) { RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); Vertices = &Section->VertexBuffer; } template void BeginMeshSectionUpdate(int32 SectionIndex, TArray*& Vertices, TArray*& Triangles) { RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); Vertices = &Section->VertexBuffer; Triangles = &Section->IndexBuffer; } template void BeginMeshSectionUpdate(int32 SectionIndex, TArray*& Positions, TArray*& Vertices) { RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); Positions = &Section->PositionVertexBuffer; Vertices = &Section->VertexBuffer; } template void BeginMeshSectionUpdate(int32 SectionIndex, TArray*& Positions, TArray*& Vertices, TArray*& Triangles) { RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/); // Validate section type MeshSections[SectionIndex]->GetVertexType()->EnsureEquals(); // Cast section to correct type TSharedPtr> Section = StaticCastSharedPtr>(MeshSections[SectionIndex]); Positions = &Section->PositionVertexBuffer; Vertices = &Section->VertexBuffer; Triangles = &Section->IndexBuffer; } void BeginMeshSectionUpdate(int32 SectionIndex, IRuntimeMeshVerticesBuilder*& Vertices, FRuntimeMeshIndicesBuilder*& Indices); void EndMeshSectionUpdate(int32 SectionIndex, ERuntimeMeshBuffer UpdatedBuffers, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); void EndMeshSectionUpdate(int32 SectionIndex, ERuntimeMeshBuffer UpdatedBuffers, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /* * Gets a readonly pointer to the sections mesh data. * To be able to edit the section data use BegineMeshSectionUpdate() */ void GetSectionMesh(int32 SectionIndex, const IRuntimeMeshVerticesBuilder*& Vertices, const FRuntimeMeshIndicesBuilder*& Indices); /** * 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. */ void CreateMeshSection(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); /** * 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. */ void CreateMeshSection(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); /** * 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. */ void UpdateMeshSection(int32 SectionIndex, const TArray& Vertices, const TArray& Normals, const TArray& UV0, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** * 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. */ void UpdateMeshSection(int32 SectionIndex, const TArray& Vertices, const TArray& Normals, const TArray& UV0, const TArray& UV1, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** * 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. */ void UpdateMeshSection(int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& UV0, const TArray& Colors, const TArray& Tangents, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** * 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. */ void UpdateMeshSection(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); /** * 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 Tangents Optional array of tangent vector 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 bCreateCollision Indicates whether collision should be created for this section. This adds significant cost. * @param bCalculateNormalTangent Indicates whether normal/tangent information should be calculated automatically. This can add significant cost. * @param bGenerateTessellationTriangles Indicates whether tessellation supporting triangles should be calculated. This can add significant cost. * @param UpdateFrequency Indicates how frequently the section will be updated. Allows the RMC to optimize itself to a particular use. */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh", meta = (DisplayName = "Create Mesh Section", AutoCreateRefTerm = "Normals,Tangents,UV0,UV1,Colors")) void CreateMeshSection_Blueprint(int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& Tangents, const TArray& UV0, const TArray& UV1, const TArray& Colors, bool bCreateCollision, bool bCalculateNormalTangent, bool bGenerateTessellationTriangles, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average); /** * Updates a section. This is faster than CreateMeshSection. If you change the vertices count, you must update the other components. * @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 Tangents Optional array of tangent vector 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 bCalculateNormalTangent Indicates whether normal/tangent information should be calculated automatically. This can add significant cost. * @param bGenerateTessellationTriangles Indicates whether tessellation supporting triangles should be calculated. This can add significant cost. */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh", meta = (DisplayName = "Update Mesh Section", AutoCreateRefTerm = "Triangles,Normals,Tangents,UV0,UV1,Colors")) void UpdateMeshSection_Blueprint(int32 SectionIndex, const TArray& Vertices, const TArray& Triangles, const TArray& Normals, const TArray& Tangents, const TArray& UV0, const TArray& UV1, const TArray& Colors, bool bCalculateNormalTangent, bool bGenerateTessellationTriangles); /** * 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. */ void CreateMeshSection(int32 SectionIndex, IRuntimeMeshVerticesBuilder& Vertices, FRuntimeMeshIndicesBuilder& Indices, bool bCreateCollision = false, EUpdateFrequency UpdateFrequency = EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** * 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. */ void UpdateMeshSection(int32 SectionIndex, IRuntimeMeshVerticesBuilder& Vertices, FRuntimeMeshIndicesBuilder& Indices, ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None); /** Clear a section of the procedural mesh. */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void ClearMeshSection(int32 SectionIndex); /** Clear all mesh sections and reset to empty state */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void ClearAllMeshSections(); /** Sets the tessellation triangles needed to correctly support tessellation on a section. */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void SetSectionTessellationTriangles(int32 SectionIndex, const TArray& TessellationTriangles, bool bShouldMoveArray = false); /** Gets the bounding box of a specific section */ bool GetSectionBoundingBox(int32 SectionIndex, FBox& OutBoundingBox); /** Control visibility of a particular section */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void SetMeshSectionVisible(int32 SectionIndex, bool bNewVisibility); /** Returns whether a particular section is currently visible */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") bool IsMeshSectionVisible(int32 SectionIndex) const; /** Control whether a particular section casts a shadow */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void SetMeshSectionCastsShadow(int32 SectionIndex, bool bNewCastsShadow); /** Returns whether a particular section is currently casting shadows */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") bool IsMeshSectionCastingShadows(int32 SectionIndex) const; /** Control whether a particular section has collision */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void SetMeshSectionCollisionEnabled(int32 SectionIndex, bool bNewCollisionEnabled); /** Returns whether a particular section has collision */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") bool IsMeshSectionCollisionEnabled(int32 SectionIndex); /** Returns number of sections currently created for this component */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") int32 GetNumSections() const; /** Returns whether a particular section currently exists */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") bool DoesSectionExist(int32 SectionIndex) const; /** Returns first available section index */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") int32 FirstAvailableMeshSectionIndex() const; /** Returns the last in use section index */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") int32 GetLastSectionIndex() const; /** Sets the geometry for a collision only section */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void SetMeshCollisionSection(int32 CollisionSectionIndex, const TArray& Vertices, const TArray& Triangles); /** Clears the geometry for a collision only section */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void ClearMeshCollisionSection(int32 CollisionSectionIndex); /** Clears the geometry for ALL collision only sections */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void ClearAllMeshCollisionSections(); /** Add simple collision convex to this component */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void AddCollisionConvexMesh(TArray ConvexVerts); /** Add simple collision convex to this component */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void ClearCollisionConvexMeshes(); /** Function to replace _all_ simple collision in one go */ void SetCollisionConvexMeshes(const TArray< TArray >& ConvexMeshes); /** Begins a batch of updates, delays updates until you call EndBatchUpdates() */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void BeginBatchUpdates() { BatchState.StartBatch(); } /** Ends a batch of updates started with BeginBatchUpdates() */ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void EndBatchUpdates(); /** Runs any pending collision cook (Not required to call this. This is only if you need to make sure all changes are cooked before doing something)*/ UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh") void CookCollisionNow(); /** * Controls whether the complex (Per poly) geometry should be treated as 'simple' collision. * Should be set to false if this component is going to be given simple collision and simulated. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RuntimeMesh") bool bUseComplexAsSimpleCollision; /** * Controls whether the mesh data should be serialized with the component. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "RuntimeMesh") bool bShouldSerializeMeshData; /* * The current mode of the collision cooker * WARNING: This feature will only work in engine version 4.14 or above! */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "RuntimeMesh") ERuntimeMeshCollisionCookingMode CollisionMode; /** Collision data */ UPROPERTY(Instanced) class UBodySetup* BodySetup; /* Serialize the entire RMC to the supplied archive. */ void SerializeRMC(FArchive& Ar); /* Serialize the designated section into the supplied archive. */ void SerializeRMCSection(FArchive& Ar, int32 SectionIndex); private: //~ Begin Interface_CollisionDataProvider Interface virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) override; virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const override; virtual bool WantsNegXTriMesh() override { return false; } //~ End Interface_CollisionDataProvider Interface //~ Begin USceneComponent Interface. virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; virtual bool IsSupportedForNetworking() const override { return true; } //~ Begin USceneComponent Interface. //~ Begin UPrimitiveComponent Interface. virtual FPrimitiveSceneProxy* CreateSceneProxy() override; virtual class UBodySetup* GetBodySetup() override; //~ End UPrimitiveComponent Interface. //~ Begin UMeshComponent Interface. virtual int32 GetNumMaterials() const override; //~ End UMeshComponent Interface. /** Update LocalBounds member from the local box of each section */ void UpdateLocalBounds(bool bMarkRenderTransform = true); /** Ensure ProcMeshBodySetup is allocated and configured */ void EnsureBodySetupCreated(); /** Mark collision data as dirty, and re-create on instance if necessary */ void UpdateCollision(); /* Marks the collision for an end of frame update */ void MarkCollisionDirty(); /* Cooks the new collision mesh updating the body */ void BakeCollision(); void UpdateNavigation(); /* Serializes this component */ virtual void Serialize(FArchive& Ar) override; void SerializeInternal(FArchive& Ar, bool bForceSaveAll = false); void SerializeLegacy(FArchive& Ar); /* Does post load fixups */ virtual void PostLoad() override; /* Registers the pre-physics tick function used to cook new meshes when necessary */ virtual void RegisterComponentTickFunctions(bool bRegister) override; /* Current state of a batch update. */ FRuntimeMeshBatchUpdateState BatchState; /* Is the collision in need of a recook? */ bool bCollisionDirty; /** Array of sections of mesh */ TArray MeshSections; /* Array of collision only mesh sections*/ UPROPERTY(Transient) TArray MeshCollisionSections; /** Convex shapes used for simple collision */ UPROPERTY(Transient) TArray ConvexCollisionSections; /** Local space bounds of mesh */ UPROPERTY(Transient) FBoxSphereBounds LocalBounds; /* Tick function used to cook collision when needed*/ UPROPERTY(Transient) FRuntimeMeshComponentPrePhysicsTickFunction PrePhysicsTick; friend class FRuntimeMeshSceneProxy; friend struct FRuntimeMeshComponentPrePhysicsTickFunction; };