// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.

#include "RuntimeMeshComponentPluginPrivatePCH.h"
#include "RuntimeMeshComponent.h"
#include "RuntimeMeshCore.h"
#include "RuntimeMeshGenericVertex.h"
#include "RuntimeMeshVersion.h"
#include "PhysicsEngine/PhysicsSettings.h"


/** Runtime mesh scene proxy */
class FRuntimeMeshSceneProxy : public FPrimitiveSceneProxy
{
private:
	// Temporarily holds all section creation data until this proxy is passsed to the RT.
	// After this data is applied this array is cleared.
	TArray<FRuntimeMeshSectionCreateDataInterface*> SectionCreationData;

public:

	FRuntimeMeshSceneProxy(URuntimeMeshComponent* Component)
		: FPrimitiveSceneProxy(Component)
		, BodySetup(Component->GetBodySetup())
	{
		bStaticElementsAlwaysUseProxyPrimitiveUniformBuffer = true;


		// Get the proxy for all mesh sections

		const int32 NumSections = Component->MeshSections.Num();
		Sections.AddDefaulted(NumSections);

		for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
		{
			RuntimeMeshSectionPtr& SourceSection = Component->MeshSections[SectionIdx];
			if (SourceSection.IsValid())
			{
				UMaterialInterface* Material = Component->GetMaterial(SectionIdx);
				if (Material == nullptr)
				{
					Material = UMaterial::GetDefaultMaterial(MD_Surface);
				}


				// Get the section creation data
				auto* SectionData = SourceSection->GetSectionCreationData(&GetScene(), Material);
				SectionData->SetTargetSection(SectionIdx);
				SectionCreationData.Add(SectionData);

			}
		}

		// Update material relevancy information needed to control the rendering.
		UpdateMaterialRelevance();
	}

	virtual ~FRuntimeMeshSceneProxy()
	{
		for (FRuntimeMeshSectionProxyInterface* Section : Sections)
		{
			if (Section)
			{
				delete Section;
			}
		}
	}

	void CreateRenderThreadResources() override
	{
		FPrimitiveSceneProxy::CreateRenderThreadResources();

		for (auto Section : SectionCreationData)
		{
			CreateSection_RenderThread(Section);
		}
		// The individual items are deleted by CreateSection_RenderThread so just clear the array.
		SectionCreationData.Empty();
	}

	/** Called on render thread to create a new dynamic section. (Static sections are handled differently) */
	void CreateSection_RenderThread(FRuntimeMeshSectionCreateDataInterface* SectionData)
	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateSection_RenderThread);

		check(IsInRenderingThread());
		check(SectionData);

		int32 SectionIndex = SectionData->GetTargetSection();

		// Make sure the array is big enough
		if (SectionIndex >= Sections.Num())
		{
			Sections.SetNum(SectionIndex + 1, false);
		}
		
		// If a section already exists... destroy it!
		if (FRuntimeMeshSectionProxyInterface* Section = Sections[SectionIndex])
		{			
			delete Section;
		}
		
		// Get the proxy and finish the creation here on the render thread.
		FRuntimeMeshSectionProxyInterface* Section = SectionData->NewProxy;
		Section->FinishCreate_RenderThread(SectionData);		

		// Save ref to new section
		Sections[SectionIndex] = Section;
		
		delete SectionData;
		
		// Update material relevancy information needed to control the rendering.
		UpdateMaterialRelevance();
	}

	/** Called on render thread to assign new dynamic data */
  	void UpdateSection_RenderThread(FRuntimeMeshRenderThreadCommandInterface* SectionData)
  	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateSection_RenderThread);

  		check(IsInRenderingThread());
		check(SectionData);

		if (SectionData->GetTargetSection() < Sections.Num() && Sections[SectionData->GetTargetSection()] != nullptr)
		{
			Sections[SectionData->GetTargetSection()]->FinishUpdate_RenderThread(SectionData);
		}

		delete SectionData;
 	}

	void UpdateSectionPositionOnly_RenderThread(FRuntimeMeshRenderThreadCommandInterface* SectionData)
	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateSectionPositionOnly_RenderThread);

		check(IsInRenderingThread());
		check(SectionData);

		if (SectionData->GetTargetSection() < Sections.Num() && Sections[SectionData->GetTargetSection()] != nullptr)
		{
			Sections[SectionData->GetTargetSection()]->FinishPositionUpdate_RenderThread(SectionData);
		}

		delete SectionData;
	}

	void UpdateSectionProperties_RenderThread(FRuntimeMeshRenderThreadCommandInterface* SectionData)
	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateSectionProperties_RenderThread);

		check(IsInRenderingThread());
		check(SectionData);

		int32 SectionIndex = SectionData->GetTargetSection();

		if (SectionIndex < Sections.Num() && Sections[SectionIndex] != nullptr)
		{
			Sections[SectionIndex]->FinishPropertyUpdate_RenderThread(SectionData);
		}
	}


	void DestroySection_RenderThread(int32 SectionIndex)
	{
		check(IsInRenderingThread());

		if (SectionIndex < Sections.Num() && Sections[SectionIndex] != nullptr)
		{
			delete Sections[SectionIndex];
			Sections[SectionIndex] = nullptr;
		}
		
		// Update material relevancy information needed to control the rendering.
		UpdateMaterialRelevance();
	}

	void ApplyBatchUpdate_RenderThread(FRuntimeMeshBatchUpdateData* BatchUpdateData)
	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ApplyBatchUpdate_RenderThread);

		check(IsInRenderingThread());
		check(BatchUpdateData);

		// Destroy flagged sections
		for (auto& SectionIndex : BatchUpdateData->DestroySections)
		{
			DestroySection_RenderThread(SectionIndex);
		}

		// Create new sections
		for (auto& SectionToCreate : BatchUpdateData->CreateSections)
		{
			CreateSection_RenderThread(static_cast<FRuntimeMeshSectionCreateDataInterface*>(SectionToCreate));
		}

		// Update sections
		for (auto& SectionToUpdate : BatchUpdateData->UpdateSections)
		{
			UpdateSection_RenderThread(SectionToUpdate);
		}		

		// Apply section property updates
		for (auto& SectionToUpdate : BatchUpdateData->PropertyUpdateSections)
		{
			UpdateSectionProperties_RenderThread(SectionToUpdate);
		}

		delete BatchUpdateData;

	}


	bool HasDynamicSections() const
	{
		for (FRuntimeMeshSectionProxyInterface* Section : Sections)
		{
			if (Section && !Section->WantsToRenderInStaticPath())
			{
				return true;
			}
		}
		return false;
	}

	bool HasStaticSections() const 
	{
		for (FRuntimeMeshSectionProxyInterface* Section : Sections)
		{
			if (Section && Section->WantsToRenderInStaticPath())
			{
				return true;
			}
		}
		return false;
	}

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 11
	virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
#else
	virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) override
#endif
	{
		FPrimitiveViewRelevance Result;
		Result.bDrawRelevance = IsShown(View);
		Result.bShadowRelevance = IsShadowCast(View);

		bool bForceDynamicPath = IsRichView(*View->Family) || View->Family->EngineShowFlags.Wireframe || IsSelected() || !IsStaticPathAvailable();
		Result.bStaticRelevance = !bForceDynamicPath && HasStaticSections();
		Result.bDynamicRelevance =  bForceDynamicPath || HasDynamicSections();
		
		Result.bRenderInMainPass = ShouldRenderInMainPass();
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 11
		Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
#endif
		Result.bRenderCustomDepth = ShouldRenderCustomDepth();
		MaterialRelevance.SetPrimitiveViewRelevance(Result);
		return Result;
	}

	void CreateMeshBatch(FMeshBatch& MeshBatch, FRuntimeMeshSectionProxyInterface* Section, FMaterialRenderProxy* WireframeMaterial) const
	{
		Section->CreateMeshBatch(MeshBatch, WireframeMaterial, IsSelected());

		MeshBatch.ReverseCulling = IsLocalToWorldDeterminantNegative();
		MeshBatch.bCanApplyViewModeOverrides = true;
		
		FMeshBatchElement& BatchElement = MeshBatch.Elements[0];
		BatchElement.PrimitiveUniformBufferResource = &GetUniformBuffer();
	}
	
	virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI) override
	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_DrawStaticElements);

		for (FRuntimeMeshSectionProxyInterface* Section : Sections)
		{
			if (Section && Section->ShouldRender() && Section->WantsToRenderInStaticPath())
			{
				FMeshBatch MeshBatch;
				CreateMeshBatch(MeshBatch, Section, nullptr);
				PDI->DrawMesh(MeshBatch, FLT_MAX);
			}
		}
	}

	virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
	{
		SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetDynamicMeshElements);

		// Set up wireframe material (if needed)
		const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe;

		FColoredMaterialRenderProxy* WireframeMaterialInstance = nullptr;
		if (bWireframe)
		{
			WireframeMaterialInstance = new FColoredMaterialRenderProxy(
				GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy(IsSelected()) : nullptr,
				FLinearColor(0, 0.5f, 1.f)
				);

			Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
		}

		// Iterate over sections
		for (FRuntimeMeshSectionProxyInterface* Section : Sections)
		{
			if (Section && Section->ShouldRender())
			{
				// Add the mesh batch to every view it's visible in
				for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
				{
					if (VisibilityMap & (1 << ViewIndex))
					{
						bool bForceDynamicPath = IsRichView(*Views[ViewIndex]->Family) || Views[ViewIndex]->Family->EngineShowFlags.Wireframe || IsSelected() || !IsStaticPathAvailable();

						if (bForceDynamicPath || !Section->WantsToRenderInStaticPath())
						{
							FMeshBatch& MeshBatch = Collector.AllocateMesh();
							CreateMeshBatch(MeshBatch, Section, WireframeMaterialInstance);

							Collector.AddMesh(ViewIndex, MeshBatch);
						}
					}
				}
			}			
		}

		// Draw bounds
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
		for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
		{
			if (VisibilityMap & (1 << ViewIndex))
			{				
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
				// Draw simple collision as wireframe if 'show collision', and collision is enabled, and we are not using the complex as the simple
				if (ViewFamily.EngineShowFlags.Collision && IsCollisionEnabled() && BodySetup->GetCollisionTraceFlag() != ECollisionTraceFlag::CTF_UseComplexAsSimple)
				{
					FTransform GeomTransform(GetLocalToWorld());
					BodySetup->AggGeom.GetAggGeom(GeomTransform, GetSelectionColor(FColor(157, 149, 223, 255), IsSelected(), IsHovered()).ToFColor(true), NULL, false, false, UseEditorDepthTest(), ViewIndex, Collector);
				}
#endif

				// Render bounds
				RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
			}
		}
#endif
	}


	virtual bool CanBeOccluded() const override
	{
		return !MaterialRelevance.bDisableDepthTest;
	}

	virtual uint32 GetMemoryFootprint(void) const
	{
		return(sizeof(*this) + GetAllocatedSize());
	}

	uint32 GetAllocatedSize(void) const
	{
		return(FPrimitiveSceneProxy::GetAllocatedSize());
	}

	void UpdateMaterialRelevance()
	{
		FMaterialRelevance NewMaterialRelevance;
		for (FRuntimeMeshSectionProxyInterface* Section : Sections)
		{
			if (Section)
			{
				NewMaterialRelevance |= Section->GetMaterialRelevance();
			}
		}
		MaterialRelevance = NewMaterialRelevance;
	}

private:
	/** Array of sections */
	TArray<FRuntimeMeshSectionProxyInterface*> Sections;
	UBodySetup* BodySetup;
	FMaterialRelevance MaterialRelevance;
};




void FRuntimeMeshComponentPrePhysicsTickFunction::ExecuteTick( float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
	/* Ensure target still exists */

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 11
	bool bIsValid = Target && !Target->IsPendingKillOrUnreachable();
#else
	bool bIsValid = Target && !Target->HasAnyFlags(RF_PendingKill | RF_Unreachable);
#endif

	if (bIsValid)
	{
		FScopeCycleCounterUObject ActorScope(Target);
		Target->BakeCollision();
	}
}

FString FRuntimeMeshComponentPrePhysicsTickFunction::DiagnosticMessage()
{
	return Target->GetFullName() + TEXT("[PrePhysicsTick]");
}



/* Helper for converting an array of FLinearColor to an array of FColors*/
void ConvertLinearColorToFColor(const TArray<FLinearColor>& LinearColors, TArray<FColor>& Colors)
{
	Colors.SetNumUninitialized(LinearColors.Num());
	for (int32 Index = 0; Index < LinearColors.Num(); Index++)
	{
		Colors[Index] = LinearColors[Index].ToFColor(false);
	}
}




URuntimeMeshComponent::URuntimeMeshComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bUseComplexAsSimpleCollision(true)
	, bShouldSerializeMeshData(true)
	, bCollisionDirty(true)
	, CollisionMode(ERuntimeMeshCollisionCookingMode::CookingPerformance)
{
	// Setup the collision update ticker
	PrePhysicsTick.TickGroup = TG_PrePhysics;
	PrePhysicsTick.bCanEverTick = true;
	PrePhysicsTick.bStartWithTickEnabled = true;

	// Reset the batch state
	BatchState.ResetBatch();

	SetNetAddressable();
}

TSharedPtr<FRuntimeMeshSectionInterface> URuntimeMeshComponent::CreateOrResetSectionLegacyType(int32 SectionIndex, int32 NumUVChannels)
{
	if (NumUVChannels == 1)
	{
		return CreateOrResetSection<FRuntimeMeshSection<FRuntimeMeshVertexSimple>>(SectionIndex, false, true);
	}
	else if (NumUVChannels == 2)
	{
		return CreateOrResetSection<FRuntimeMeshSection<FRuntimeMeshVertexDualUV>>(SectionIndex, false, true);
	}
	else
	{
		check(false && "Legacy sections only support standard vertex formats wit 1 or 2 uv channels");
		return nullptr;
	}
}


void URuntimeMeshComponent::CreateSectionInternal(int32 SectionIndex, ESectionUpdateFlags UpdateFlags)
{
	RuntimeMeshSectionPtr Section = MeshSections[SectionIndex];
	check(Section.IsValid());

	// Update normal/tangents if requested...
	if (!!(UpdateFlags & ESectionUpdateFlags::CalculateNormalTangent))
	{
		Section->GenerateNormalTangent();
	}

	// calculate tessellation if requested...
	if (!!(UpdateFlags & ESectionUpdateFlags::CalculateTessellationIndices))
	{
		Section->GenerateTessellationIndices();
	}

	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark section created
		BatchState.MarkSectionCreated(SectionIndex, Section->UpdateFrequency == EUpdateFrequency::Infrequent);

		// Flag collision if this section affects it
		if (Section->CollisionEnabled)
		{
			BatchState.MarkCollisionDirty();
		}
		
		// Flag bounds update
		BatchState.MarkBoundsDirty();

		// bail since we don't update directly in this case.
		return;
	}

	// Enqueue the RT command if we already have a SceneProxy
	if (SceneProxy && Section->UpdateFrequency != EUpdateFrequency::Infrequent)
	{
		// Gather all needed update info
		auto* SectionData = Section->GetSectionCreationData(GetScene(), GetSectionMaterial(SectionIndex));
		SectionData->SetTargetSection(SectionIndex);

		// Enqueue update on RT
		ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
			FRuntimeMeshSectionCreate,
			FRuntimeMeshSceneProxy*, RuntimeMeshSceneProxy, (FRuntimeMeshSceneProxy*)SceneProxy,
			FRuntimeMeshSectionCreateDataInterface*, SectionData, SectionData,
			{
				RuntimeMeshSceneProxy->CreateSection_RenderThread(SectionData);
			}
		);
	}
	else
	{
		// Mark the render state dirty so it's recreated when necessary.
		MarkRenderStateDirty();
	}

	// Mark collision dirty so it's re-baked at the end of this frame
	if (Section->CollisionEnabled)
	{
		MarkCollisionDirty();
	}

	// Update overall bounds
	UpdateLocalBounds();

}

void URuntimeMeshComponent::UpdateSectionInternal(int32 SectionIndex, bool bHadVertexPositionsUpdate, bool bHadVertexUpdates, bool bHadIndexUpdates, bool bNeedsBoundsUpdate, ESectionUpdateFlags UpdateFlags)
{
	// Ensure that something was updated
	check(bHadVertexPositionsUpdate || bHadVertexUpdates || bHadIndexUpdates || bNeedsBoundsUpdate);

	check(SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid());	
	RuntimeMeshSectionPtr Section = MeshSections[SectionIndex];
	
	// Update normal/tangents if requested...
	if (!!(UpdateFlags & ESectionUpdateFlags::CalculateNormalTangent))
	{
		Section->GenerateNormalTangent();
	}

	// calculate tessellation if requested...
	if (!!(UpdateFlags & ESectionUpdateFlags::CalculateTessellationIndices))
	{
		Section->GenerateTessellationIndices();
	}

	/* Make sure this is only flagged if the section is dual buffer */
	bHadVertexPositionsUpdate = Section->IsDualBufferSection() && bHadVertexPositionsUpdate;
	bool bNeedsCollisionUpdate = Section->CollisionEnabled && (bHadVertexPositionsUpdate || (!Section->IsDualBufferSection() && bHadVertexUpdates));
	
	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark update for section or promote to proxy recreate if static section
		if (Section->UpdateFrequency == EUpdateFrequency::Infrequent)
		{
			BatchState.MarkRenderStateDirty();
		}
		else
		{
			ERuntimeMeshSectionBatchUpdateType UpdateType = ERuntimeMeshSectionBatchUpdateType::None;
			UpdateType |= bHadVertexPositionsUpdate ? ERuntimeMeshSectionBatchUpdateType::PositionsUpdate : ERuntimeMeshSectionBatchUpdateType::None;
			UpdateType |= bHadVertexUpdates ? ERuntimeMeshSectionBatchUpdateType::VerticesUpdate : ERuntimeMeshSectionBatchUpdateType::None;
			UpdateType |= bHadIndexUpdates ? ERuntimeMeshSectionBatchUpdateType::IndicesUpdate : ERuntimeMeshSectionBatchUpdateType::None;

			BatchState.MarkUpdateForSection(SectionIndex, UpdateType);
		}

		// Flag collision if this section affects it
		if (bNeedsCollisionUpdate)
		{
			BatchState.MarkCollisionDirty();
		}

		// Flag bounds update if needed.
		if (bNeedsBoundsUpdate)
		{
			BatchState.MarkBoundsDirty();
		}

		// bail since we don't update directly in this case.
		return;
	}


	// Send the update to the render thread if the scene proxy exists
	if (SceneProxy && Section->UpdateFrequency != EUpdateFrequency::Infrequent)
	{
		auto* SectionData = Section->GetSectionUpdateData(bHadVertexPositionsUpdate, bHadVertexUpdates, bHadIndexUpdates);
		SectionData->SetTargetSection(SectionIndex);

		// Enqueue update on RT
		ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
			FRuntimeMeshSectionUpdate,
			FRuntimeMeshSceneProxy*, RuntimeMeshSceneProxy, (FRuntimeMeshSceneProxy*)SceneProxy,
			FRuntimeMeshRenderThreadCommandInterface*, SectionData, SectionData,
			{
				RuntimeMeshSceneProxy->UpdateSection_RenderThread(SectionData);
			}
		);
	}
	else
	{
		// Mark the renderstate dirty so it's recreated when necessary.
		MarkRenderStateDirty();
	}

	// Mark collision dirty so it's re-baked at the end of this frame
	if (bNeedsCollisionUpdate)
	{
		MarkCollisionDirty();
	}

	// Update overall bounds if needed
	if (bNeedsBoundsUpdate)
	{
		UpdateLocalBounds();
	}
}

void URuntimeMeshComponent::UpdateSectionVertexPositionsInternal(int32 SectionIndex, bool bNeedsBoundsUpdate)
{
	check(SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid());
	RuntimeMeshSectionPtr Section = MeshSections[SectionIndex];

	if (SceneProxy)
	{
		auto SectionData = Section->GetSectionPositionUpdateData();
		SectionData->SetTargetSection(SectionIndex);

		// Enqueue command to modify render thread info
		ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
			FRuntimeMeshSectionPositionUpdate,
			FRuntimeMeshSceneProxy*, RuntimeMeshSceneProxy, (FRuntimeMeshSceneProxy*)SceneProxy,
			FRuntimeMeshRenderThreadCommandInterface*, SectionData, SectionData,
			{
				RuntimeMeshSceneProxy->UpdateSectionPositionOnly_RenderThread(SectionData);
			}
		);
	}
	else
	{
		MarkRenderStateDirty();
	}

	if (bNeedsBoundsUpdate)
	{
		UpdateLocalBounds();
	}
}

void URuntimeMeshComponent::UpdateSectionPropertiesInternal(int32 SectionIndex, bool bUpdateRequiresProxyRecreateIfStatic)
{
	check(SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid());
	RuntimeMeshSectionPtr Section = MeshSections[SectionIndex];

	bool bRequiresRecreate = bUpdateRequiresProxyRecreateIfStatic && Section->UpdateFrequency == EUpdateFrequency::Infrequent;

	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		if (bRequiresRecreate)
		{
			BatchState.MarkRenderStateDirty();
		}
		else
		{
			BatchState.MarkUpdateForSection(SectionIndex, ERuntimeMeshSectionBatchUpdateType::PropertyUpdate);
		}

		// bail since we don't update directly in this case.
		return;
	}



	if (SceneProxy && !bRequiresRecreate)
	{
		auto SectionData = new FRuntimeMeshSectionPropertyUpdateData();
		SectionData->SetTargetSection(SectionIndex);
		SectionData->bIsVisible = Section->bIsVisible;
		SectionData->bCastsShadow = Section->bCastsShadow;


		// Enqueue command to modify render thread info
		ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
			FRuntimeMeshSectionPropertyUpdate,
			FRuntimeMeshSceneProxy*, RuntimeMeshSceneProxy, (FRuntimeMeshSceneProxy*)SceneProxy,
			FRuntimeMeshRenderThreadCommandInterface*, SectionData, SectionData,
			{
				RuntimeMeshSceneProxy->UpdateSectionProperties_RenderThread(SectionData);
			}
		);
	}
	else
	{
		MarkRenderStateDirty();
	}
}


void URuntimeMeshComponent::UpdateMeshSectionPositionsImmediate(int32 SectionIndex, TArray<FVector>& VertexPositions, ESectionUpdateFlags UpdateFlags)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSectionPositionsImmediate);

	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/);

	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];

	// Check dual buffer section status
	if (VertexPositions.Num() != Section->PositionVertexBuffer.Num())
	{
		Log(TEXT("UpdateMeshSection() - Positions cannot change length unless the vertexdata is 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."));
	}

	// Finalize section update if we have anything to apply
	if (bUpdatedVertexPositions)
	{
		UpdateSectionVertexPositionsInternal(SectionIndex, bNeedsBoundsUpdate);
	}
}

void URuntimeMeshComponent::UpdateMeshSectionPositionsImmediate(int32 SectionIndex, TArray<FVector>& VertexPositions, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSectionPositionsImmediate_WithBoundinBox);

	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/);

	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];

	// Check dual buffer section status
	if (VertexPositions.Num() != Section->PositionVertexBuffer.Num())
	{
		Log(TEXT("UpdateMeshSection() - Positions cannot change length unless the vertexdata is 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."));
	}

	// Finalize section update if we have anything to apply
	if (bUpdatedVertexPositions)
	{
		UpdateSectionVertexPositionsInternal(SectionIndex, bNeedsBoundsUpdate);
	}
}


TArray<FVector>* URuntimeMeshComponent::BeginMeshSectionPositionUpdate(int32 SectionIndex)
{
	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, nullptr);

	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];
	
	return &Section->PositionVertexBuffer;
}

void URuntimeMeshComponent::EndMeshSectionPositionUpdate(int32 SectionIndex)
{
	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/);
	
	// TODO: Validate that the position buffer is still the same length

	UpdateSectionVertexPositionsInternal(SectionIndex, true);
}

void URuntimeMeshComponent::EndMeshSectionPositionUpdate(int32 SectionIndex, const FBox& BoundingBox)
{
	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_DUALBUFFER(SectionIndex, /*VoidReturn*/);

	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];
	
	bool bNeedsBoundingBoxUpdate = !(Section->LocalBoundingBox == BoundingBox);
	if (bNeedsBoundingBoxUpdate)
	{
		Section->LocalBoundingBox = BoundingBox;
	}
	
	// TODO: Validate that the position buffer is still the same length

	UpdateSectionVertexPositionsInternal(SectionIndex, bNeedsBoundingBoxUpdate);
}




void URuntimeMeshComponent::EndMeshSectionUpdate(int32 SectionIndex, ERuntimeMeshBuffer UpdatedBuffers, ESectionUpdateFlags UpdateFlags)
{
	// Bail if we have no buffers to update.
	if (UpdatedBuffers == ERuntimeMeshBuffer::None)
	{
		return;
	}

	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/);

	// Get section and update bounding box
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];
	Section->RecalculateBoundingBox();

	// Finalize section update
	UpdateSectionInternal(SectionIndex, 
		!!(UpdatedBuffers & ERuntimeMeshBuffer::Positions), 
		!!(UpdatedBuffers & ERuntimeMeshBuffer::Vertices), 
		!!(UpdatedBuffers & ERuntimeMeshBuffer::Triangles), true, UpdateFlags);
}


void URuntimeMeshComponent::EndMeshSectionUpdate(int32 SectionIndex, ERuntimeMeshBuffer UpdatedBuffers, const FBox& BoundingBox, ESectionUpdateFlags UpdateFlags)
{
	// Bail if we have no buffers to update.
	if (UpdatedBuffers == ERuntimeMeshBuffer::None)
	{
		return;
	}

	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/);

	// Get section and update bounding box
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];
	Section->LocalBoundingBox = BoundingBox;

	// Finalize section update
	UpdateSectionInternal(SectionIndex,
		!!(UpdatedBuffers & ERuntimeMeshBuffer::Positions),
		!!(UpdatedBuffers & ERuntimeMeshBuffer::Vertices),
		!!(UpdatedBuffers & ERuntimeMeshBuffer::Triangles), true, UpdateFlags);
}



void URuntimeMeshComponent::CreateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector>& Normals,
	const TArray<FVector2D>& UV0, const TArray<FColor>& Colors, const TArray<FRuntimeMeshTangent>& Tangents, bool bCreateCollision,	EUpdateFrequency UpdateFrequency, 
	ESectionUpdateFlags UpdateFlags)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection);

	// Validate all creation parameters
	RMC_VALIDATE_CREATIONPARAMETERS(SectionIndex, Vertices, Triangles, /*VoidReturn*/);

	// Create the section
	auto NewSection = CreateOrResetSectionLegacyType(SectionIndex, 1);

	// Update the mesh data in the section
	NewSection->UpdateVertexBufferInternal(Vertices, Normals, Tangents, UV0, TArray<FVector2D>(), Colors);

	TArray<int32>& TrianglesRef = const_cast<TArray<int32>&>(Triangles);
	NewSection->UpdateIndexBuffer(TrianglesRef, false);

	// Track collision status and update collision information if necessary
	NewSection->CollisionEnabled = bCreateCollision;
	NewSection->UpdateFrequency = UpdateFrequency;

	// Finalize section.
	CreateSectionInternal(SectionIndex, UpdateFlags);
}

void URuntimeMeshComponent::CreateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector>& Normals,
	const TArray<FVector2D>& UV0, const TArray<FVector2D>& UV1, const TArray<FColor>& Colors, const TArray<FRuntimeMeshTangent>& Tangents,
	bool bCreateCollision, EUpdateFrequency UpdateFrequency, ESectionUpdateFlags UpdateFlags)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection_DualUV);

	// Validate all creation parameters
	RMC_VALIDATE_CREATIONPARAMETERS(SectionIndex, Vertices, Triangles, /*VoidReturn*/);

	// Create the section
	auto NewSection = CreateOrResetSectionLegacyType(SectionIndex, 2);

	// Update the mesh data in the section
	NewSection->UpdateVertexBufferInternal(Vertices, Normals, Tangents, UV0, UV1, Colors);

	TArray<int32>& TrianglesRef = const_cast<TArray<int32>&>(Triangles);
	NewSection->UpdateIndexBuffer(TrianglesRef, false);

	// Track collision status and update collision information if necessary
	NewSection->CollisionEnabled = bCreateCollision;
	NewSection->UpdateFrequency = UpdateFrequency;

	// Finalize section.
	CreateSectionInternal(SectionIndex, UpdateFlags);
}



void URuntimeMeshComponent::UpdateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<FVector>& Normals, const TArray<FVector2D>& UV0,
	const TArray<FColor>& Colors, const TArray<FRuntimeMeshTangent>& Tangents, ESectionUpdateFlags UpdateFlags)
{
	UpdateMeshSection(SectionIndex, Vertices, TArray<int32>(), Normals, UV0, Colors, Tangents, UpdateFlags);
}

void URuntimeMeshComponent::UpdateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<FVector>& Normals, const TArray<FVector2D>& UV0,
	const TArray<FVector2D>& UV1, const TArray<FColor>& Colors, const TArray<FRuntimeMeshTangent>& Tangents, ESectionUpdateFlags UpdateFlags)
{
	UpdateMeshSection(SectionIndex, Vertices, TArray<int32>(), Normals, UV0, UV1, Colors, Tangents, UpdateFlags);
}

void URuntimeMeshComponent::UpdateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector>& Normals,
	const TArray<FVector2D>& UV0, const TArray<FColor>& Colors, const TArray<FRuntimeMeshTangent>& Tangents, ESectionUpdateFlags UpdateFlags)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection);

	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_INTERNALSECTION(SectionIndex, /*VoidReturn*/);

	// Validate section type
	MeshSections[SectionIndex]->GetVertexType()->EnsureEquals<FRuntimeMeshVertexSimple>();

	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];
	
	// Tell the section to update the vertex buffer
	bool bHadVertexUpdates = Section->UpdateVertexBufferInternal(Vertices, Normals, Tangents, UV0, TArray<FVector2D>(), Colors);

	bool bHadTriangleUpdates = Triangles.Num() > 0;
	if (bHadTriangleUpdates)
	{
		TArray<int32>& TrianglesRef = const_cast<TArray<int32>&>(Triangles);

		Section->UpdateIndexBuffer(TrianglesRef, false);
	}

	UpdateSectionInternal(SectionIndex, false, bHadVertexUpdates, bHadTriangleUpdates, true, UpdateFlags);
}

void URuntimeMeshComponent::UpdateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector>& Normals,
	const TArray<FVector2D>& UV0, const TArray<FVector2D>& UV1, const TArray<FColor>& Colors, const TArray<FRuntimeMeshTangent>& Tangents, ESectionUpdateFlags UpdateFlags)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_DualUV);

	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_INTERNALSECTION(SectionIndex, /*VoidReturn*/);

	// Validate section type
	MeshSections[SectionIndex]->GetVertexType()->EnsureEquals<FRuntimeMeshVertexDualUV>();

	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];
	
	// Tell the section to update the vertex buffer
	bool bHadVertexUpdates = Section->UpdateVertexBufferInternal(Vertices, Normals, Tangents, UV0, UV1, Colors);

	bool bHadTriangleUpdates = Triangles.Num() > 0;
	if (bHadTriangleUpdates)
	{
		TArray<int32>& TrianglesRef = const_cast<TArray<int32>&>(Triangles);

		Section->UpdateIndexBuffer(TrianglesRef, false);
	}

	UpdateSectionInternal(SectionIndex, false, bHadVertexUpdates, bHadTriangleUpdates, true, UpdateFlags);
}


void URuntimeMeshComponent::CreateMeshSection_Blueprint(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector>& Normals, const TArray<FRuntimeMeshTangent>& Tangents,
	const TArray<FVector2D>& UV0, const TArray<FVector2D>& UV1, const TArray<FLinearColor>& VertexColors, bool bCreateCollision, bool bCalculateNormalTangent, bool bGenerateTessellationTriangles, EUpdateFrequency UpdateFrequency)
{	
	// Convert vertex colors to FColor
	TArray<FColor> Colors;
	ConvertLinearColorToFColor(VertexColors, Colors);

	ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None;
	UpdateFlags |= bCalculateNormalTangent ? ESectionUpdateFlags::CalculateNormalTangent : ESectionUpdateFlags::None;
	UpdateFlags |= bGenerateTessellationTriangles ? ESectionUpdateFlags::CalculateTessellationIndices : ESectionUpdateFlags::None;

	// Create section
	CreateMeshSection(SectionIndex, Vertices, Triangles, Normals, UV0, UV1, Colors, Tangents, bCreateCollision, UpdateFrequency, UpdateFlags);
}

void URuntimeMeshComponent::UpdateMeshSection_Blueprint(int32 SectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector>& Normals, const TArray<FRuntimeMeshTangent>& Tangents,
	const TArray<FVector2D>& UV0, const TArray<FVector2D>& UV1, const TArray<FLinearColor>& VertexColors, bool bCalculateNormalTangent, bool bGenerateTessellationTriangles)
{
	// Convert vertex colors to FColor
	TArray<FColor> Colors;
	ConvertLinearColorToFColor(VertexColors, Colors);

	ESectionUpdateFlags UpdateFlags = ESectionUpdateFlags::None;
	UpdateFlags |= bCalculateNormalTangent ? ESectionUpdateFlags::CalculateNormalTangent : ESectionUpdateFlags::None;
	UpdateFlags |= bGenerateTessellationTriangles ? ESectionUpdateFlags::CalculateTessellationIndices : ESectionUpdateFlags::None;

	// Update section
	UpdateMeshSection(SectionIndex, Vertices, Triangles, Normals, UV0, UV1, Colors, Tangents, UpdateFlags);
}



void URuntimeMeshComponent::CreateMeshSection(int32 SectionIndex, IRuntimeMeshVerticesBuilder& Vertices, FRuntimeMeshIndicesBuilder& Triangles, bool bCreateCollision,
	EUpdateFrequency UpdateFrequency, ESectionUpdateFlags UpdateFlags)
{
	RMC_CHECKINGAME_LOGINEDITOR((SectionIndex >= 0), "SectionIndex cannot be negative.", /**/);
	RMC_CHECKINGAME_LOGINEDITOR((Vertices.Length() > 0), "Vertices length must not be 0.", /**/);
	RMC_CHECKINGAME_LOGINEDITOR((Triangles.Length() > 0), "Triangles length must not be 0", /**/);

	// First we need to create the new section
	TSharedPtr<FRuntimeMeshSectionInterface> Section = MakeShareable(Vertices.GetVertexType()->CreateSection(Vertices.WantsSeparatePositionBuffer()));

	// Ensure sections array is long enough
	if (SectionIndex >= MeshSections.Num())
	{
		MeshSections.SetNum(SectionIndex + 1, false);
	}

	// Set the new section
	MeshSections[SectionIndex] = Section;

	// Set vertex/index buffers
	Section->UpdateVertexBuffer(Vertices, nullptr, !!(UpdateFlags & ESectionUpdateFlags::MoveArrays));
	Section->UpdateIndexBuffer(Triangles, !!(UpdateFlags & ESectionUpdateFlags::MoveArrays));
	

	// Track collision status and update collision information if necessary
	Section->CollisionEnabled = bCreateCollision;
	Section->UpdateFrequency = UpdateFrequency;

	// Finalize section.
	CreateSectionInternal(SectionIndex, UpdateFlags);
}

void URuntimeMeshComponent::UpdateMeshSection(int32 SectionIndex, IRuntimeMeshVerticesBuilder& Vertices, FRuntimeMeshIndicesBuilder& Triangles, ESectionUpdateFlags UpdateFlags)
{	
	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_INTERNALSECTION(SectionIndex, /*VoidReturn*/);

	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];

	// Set vertex/index buffers
	Section->UpdateVertexBuffer(Vertices, nullptr, !!(UpdateFlags & ESectionUpdateFlags::MoveArrays));
	Section->UpdateIndexBuffer(Triangles, !!(UpdateFlags & ESectionUpdateFlags::MoveArrays));

	UpdateSectionInternal(SectionIndex, Vertices.WantsSeparatePositionBuffer(), true, true, true, UpdateFlags);
}



void URuntimeMeshComponent::ClearMeshSection(int32 SectionIndex)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearMeshSection);

 	if (SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid())
 	{
		// Did this section have collision
		bool HadCollision = MeshSections[SectionIndex]->CollisionEnabled;
		bool bWasStaticSection = MeshSections[SectionIndex]->UpdateFrequency == EUpdateFrequency::Infrequent;

		// Clear the section
		MeshSections[SectionIndex].Reset();
		
		// Use the batch update if one is running
		if (BatchState.IsBatchPending())
		{
			// Mark section created
			BatchState.MarkSectionDestroyed(SectionIndex, bWasStaticSection);

			// Flag collision if this section affects it
			if (HadCollision)
			{
				BatchState.MarkCollisionDirty();
			}

			// Flag bounds update
			BatchState.MarkBoundsDirty();
			
			// bail since we don't update directly in this case.
			return;
		}


		if (SceneProxy && !bWasStaticSection)
		{			
			// Enqueue update on RT
			ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
				FRuntimeMeshSectionUpdate,
				FRuntimeMeshSceneProxy*, RuntimeMeshSceneProxy, (FRuntimeMeshSceneProxy*)SceneProxy,
				int32, SectionIndex, SectionIndex,
				{
					RuntimeMeshSceneProxy->DestroySection_RenderThread(SectionIndex);
				}
			);
		}
		else
		{
			MarkRenderStateDirty();
		}

		// Update our collision info only if this section had any influence on it
		if (HadCollision)
		{
			MarkCollisionDirty();
		}
		
		UpdateLocalBounds();

 	}
}

void URuntimeMeshComponent::ClearAllMeshSections()
{
 	MeshSections.Empty();

	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark render state dirty
		BatchState.MarkRenderStateDirty();

		// Flag collision
		BatchState.MarkCollisionDirty();

		// Flag bounds update
		BatchState.MarkBoundsDirty();

		// bail since we don't update directly in this case.
		return;
	}
	
 	MarkRenderStateDirty();
	MarkCollisionDirty();
	UpdateLocalBounds();
}

void URuntimeMeshComponent::SetSectionTessellationTriangles(int32 SectionIndex, const TArray<int32>& TessellationTriangles, bool bShouldMoveArray)
{
	// Validate all update parameters
	RMC_VALIDATE_UPDATEPARAMETERS_INTERNALSECTION(SectionIndex, /*VoidReturn*/);
	
	// Get section
	RuntimeMeshSectionPtr& Section = MeshSections[SectionIndex];

	// Tell the section to update the tessellation index buffer
	Section->UpdateTessellationIndexBuffer(const_cast<TArray<int32>&>(TessellationTriangles), bShouldMoveArray);

	UpdateSectionInternal(SectionIndex, false, false, true, false, ESectionUpdateFlags::None);
}




bool URuntimeMeshComponent::GetSectionBoundingBox(int32 SectionIndex, FBox& OutBoundingBox)
{
	if (SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid())
	{
		OutBoundingBox = MeshSections[SectionIndex]->LocalBoundingBox;
		return true;
	}
	return false;
}

void URuntimeMeshComponent::SetMeshSectionVisible(int32 SectionIndex, bool bNewVisibility)
{
 	if (SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid())
 	{
 		// Set game thread state
 		MeshSections[SectionIndex]->bIsVisible = bNewVisibility;

		// Finish the update
		UpdateSectionPropertiesInternal(SectionIndex, false);
 	}
}

bool URuntimeMeshComponent::IsMeshSectionVisible(int32 SectionIndex) const
{
	return SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid() && MeshSections[SectionIndex]->bIsVisible;
}

void URuntimeMeshComponent::SetMeshSectionCastsShadow(int32 SectionIndex, bool bNewCastsShadow)
{
	if (SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid())
	{
		// Set game thread state
		MeshSections[SectionIndex]->bCastsShadow = bNewCastsShadow;

		// Finish the update
		UpdateSectionPropertiesInternal(SectionIndex, true);
	}
}

bool URuntimeMeshComponent::IsMeshSectionCastingShadows(int32 SectionIndex) const
{
	return SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid() && MeshSections[SectionIndex]->bCastsShadow;
}

void URuntimeMeshComponent::SetMeshSectionCollisionEnabled(int32 SectionIndex, bool bNewCollisionEnabled)
{
	if (SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid())
	{
		auto& Section = MeshSections[SectionIndex];
		if (Section->CollisionEnabled != bNewCollisionEnabled)
		{
			Section->CollisionEnabled = bNewCollisionEnabled;
			
			// Use the batch update if one is running
			if (BatchState.IsBatchPending())
			{
				// Mark render state dirty
				BatchState.MarkCollisionDirty();
			}
			else
			{
				MarkCollisionDirty();
			}
		}
	}
}

bool URuntimeMeshComponent::IsMeshSectionCollisionEnabled(int32 SectionIndex)
{
	return SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid() && MeshSections[SectionIndex]->CollisionEnabled;
}



int32 URuntimeMeshComponent::GetNumSections() const
{
	int32 SectionCount = 0;
	for (int32 Index = 0; Index < MeshSections.Num(); Index++)
	{
		if (MeshSections[Index].IsValid())
		{
			SectionCount++;
		}
	}

	return SectionCount;
}

bool URuntimeMeshComponent::DoesSectionExist(int32 SectionIndex) const
{
	return SectionIndex < MeshSections.Num() && MeshSections[SectionIndex].IsValid();
}

int32 URuntimeMeshComponent::FirstAvailableMeshSectionIndex() const
{
	for (int32 Index = 0; Index < MeshSections.Num(); Index++)
	{
		if (!MeshSections[Index].IsValid())
		{
			return Index;
		}
	}
	return MeshSections.Num();
}

int32 URuntimeMeshComponent::GetLastSectionIndex() const
{
	for (int32 Index = MeshSections.Num() - 1; Index >= 0; Index--)
	{
		if (MeshSections[Index].IsValid())
		{
			return Index;
		}
	}

	return -1;
}


void URuntimeMeshComponent::SetMeshCollisionSection(int32 CollisionSectionIndex, const TArray<FVector>& Vertices, const TArray<int32>& Triangles)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetMeshCollisionSection);

	if (MeshCollisionSections.Num() <= CollisionSectionIndex)
	{
		MeshCollisionSections.SetNum(CollisionSectionIndex + 1, false);
	}

	auto& Section = MeshCollisionSections[CollisionSectionIndex];
	Section.VertexBuffer = Vertices;
	Section.IndexBuffer = Triangles;

	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark render state dirty
		BatchState.MarkCollisionDirty();
	}
	else
	{
		MarkCollisionDirty();
	}
}

void URuntimeMeshComponent::ClearMeshCollisionSection(int32 CollisionSectionIndex)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearMeshCollisionSection);

	if (MeshCollisionSections.Num() <= CollisionSectionIndex)
		return;

	MeshCollisionSections[CollisionSectionIndex].Reset();

	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark render state dirty
		BatchState.MarkCollisionDirty();
	}
	else
	{
		MarkCollisionDirty();
	}
}

void URuntimeMeshComponent::ClearAllMeshCollisionSections()
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearAllMeshCollisionSections);

	MeshCollisionSections.Empty();

	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark render state dirty
		BatchState.MarkCollisionDirty();
	}
	else
	{
		MarkCollisionDirty();
	}
}


void URuntimeMeshComponent::AddCollisionConvexMesh(TArray<FVector> ConvexVerts)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_AddCollisionConvexMesh);

	if (ConvexVerts.Num() >= 4)
	{
		FRuntimeConvexCollisionSection ConvexSection;
		ConvexSection.VertexBuffer = ConvexVerts;
		ConvexSection.BoundingBox = FBox(ConvexVerts);
		ConvexCollisionSections.Add(ConvexSection);
		

		// Use the batch update if one is running
		if (BatchState.IsBatchPending())
		{
			// Mark render state dirty
			BatchState.MarkCollisionDirty();
		}
		else
		{
			MarkCollisionDirty();
		}
	}
}

void URuntimeMeshComponent::ClearCollisionConvexMeshes()
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearCollisionConvexMeshes);

	// Empty simple collision info
	ConvexCollisionSections.Empty();


	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark render state dirty
		BatchState.MarkCollisionDirty();
	}
	else
	{
		MarkCollisionDirty();
	}
}

void URuntimeMeshComponent::SetCollisionConvexMeshes(const TArray< TArray<FVector> >& ConvexMeshes)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetCollisionConvexMeshes);

	ConvexCollisionSections.Empty(ConvexMeshes.Num());

	// Create element for each convex mesh
	for (int32 ConvexIndex = 0; ConvexIndex < ConvexMeshes.Num(); ConvexIndex++)
	{
		FRuntimeConvexCollisionSection ConvexSection;
		ConvexSection.VertexBuffer = ConvexMeshes[ConvexIndex];
		ConvexSection.BoundingBox = FBox(ConvexSection.VertexBuffer);
		ConvexCollisionSections.Add(ConvexSection);
	}


	// Use the batch update if one is running
	if (BatchState.IsBatchPending())
	{
		// Mark render state dirty
		BatchState.MarkCollisionDirty();
	}
	else
	{
		MarkCollisionDirty();
	}
}


void URuntimeMeshComponent::UpdateLocalBounds(bool bMarkRenderTransform)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateLocalBounds);
	
	FBox LocalBox(EForceInit::ForceInitToZero);

	for (const RuntimeMeshSectionPtr& Section : MeshSections)
	{
		if (Section.IsValid() && Section->bIsVisible)
		{
			LocalBox += Section->LocalBoundingBox;
		}
	}

	LocalBounds = LocalBox.IsValid ? FBoxSphereBounds(LocalBox) : FBoxSphereBounds(FVector(0, 0, 0), FVector(0, 0, 0), 0); // fallback to reset box sphere bounds

	// Update global bounds
	UpdateBounds();

	if (bMarkRenderTransform)
	{
		// Need to send to render thread
		MarkRenderTransformDirty();
	}
}

FPrimitiveSceneProxy* URuntimeMeshComponent::CreateSceneProxy()
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateSceneProxy);

	return new FRuntimeMeshSceneProxy(this);
}

int32 URuntimeMeshComponent::GetNumMaterials() const
{
	return MeshSections.Num();
}

FBoxSphereBounds URuntimeMeshComponent::CalcBounds(const FTransform& LocalToWorld) const
{
	return LocalBounds.TransformBy(LocalToWorld);
}





void URuntimeMeshComponent::EndBatchUpdates()
{
	// Bail if we have no pending updates
	if (!BatchState.IsBatchPending())
		return;

	// Handle all pending rendering updates..
	if (BatchState.RequiresSceneProxyRecreate())
	{
		MarkRenderStateDirty();
	}
	else
	{
		auto* BatchUpdateData = new FRuntimeMeshBatchUpdateData;

		for (int32 Index = 0; Index <= BatchState.GetMaxSection(); Index++)
		{
			// Skip this section if it has no updates.
			if (!BatchState.HasAnyFlagSet(Index))
			{
				continue;
			}

			// Check that we don't have both create and destroy flagged
			check(!(BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::Create) && BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::Destroy)));

			// Handle section created
			if (BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::Create))
			{
				// Validate section exists
				check(MeshSections.Num() >= Index && MeshSections[Index].IsValid());
				
				UMaterialInterface* Material = GetMaterial(Index);
				if (Material == nullptr)
				{
					Material = UMaterial::GetDefaultMaterial(MD_Surface);
				}

				// Get the section create data and add it to the list
				auto SectionCreateData = MeshSections[Index]->GetSectionCreationData(GetScene(), Material);
				SectionCreateData->SetTargetSection(Index);

				BatchUpdateData->CreateSections.Add(SectionCreateData);
			}
			// Handle destroy
			else if (BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::Destroy))
			{
				BatchUpdateData->DestroySections.Add(Index);
			}
			// Handle vertex/index updates
			else if (BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::VerticesUpdate) || BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::IndicesUpdate))
			{
				// Validate section exists
				check(MeshSections.Num() >= Index && MeshSections[Index].IsValid());

				// Get the section update data and add it to the list.
				bool bHadPositionUpdates = BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::PositionsUpdate);
				bool bHadVertexUpdates = BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::VerticesUpdate);
				bool bHadIndexUpdates = BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::IndicesUpdate);
				auto SectionUpdateData = MeshSections[Index]->GetSectionUpdateData(bHadPositionUpdates, bHadVertexUpdates, bHadIndexUpdates);
				SectionUpdateData->SetTargetSection(Index);

				BatchUpdateData->UpdateSections.Add(SectionUpdateData);
			}
			// Handle property updates
			else if (BatchState.HasFlagSet(Index, ERuntimeMeshSectionBatchUpdateType::PropertyUpdate))
			{
				// Validate section exists
				check(MeshSections.Num() >= Index && MeshSections[Index].IsValid());

				auto SectionProperties = new(BatchUpdateData->PropertyUpdateSections) FRuntimeMeshSectionPropertyUpdateData;

				auto& Section = MeshSections[Index];

				SectionProperties->SetTargetSection(Index);
				SectionProperties->bIsVisible = Section->bIsVisible;
				SectionProperties->bCastsShadow = Section->bCastsShadow;
			}
			else
			{
				// Unknown update type.
				checkNoEntry();
			}
		}



		// Enqueue update on RT
		ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
			FRuntimeMeshBatchUpdateCommand,
			FRuntimeMeshSceneProxy*, RuntimeMeshSceneProxy, (FRuntimeMeshSceneProxy*)SceneProxy,
			FRuntimeMeshBatchUpdateData*, BatchUpdateData, BatchUpdateData,
			{
				RuntimeMeshSceneProxy->ApplyBatchUpdate_RenderThread(BatchUpdateData);
			}
		);


	}

	// Update collision if necessary
	if (BatchState.RequiresCollisionUpdate())
	{
		MarkCollisionDirty();
	}

	// Update local bounds if necessary
	if (BatchState.RequiresBoundsUpdate())
	{
		UpdateLocalBounds(!BatchState.RequiresSceneProxyRecreate());
	}

	// Clear batch info
	BatchState.ResetBatch();
}




void URuntimeMeshComponent::GetSectionMesh(int32 SectionIndex, const IRuntimeMeshVerticesBuilder*& Vertices, const FRuntimeMeshIndicesBuilder*& Indices)
{
	RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/);

	IRuntimeMeshVerticesBuilder* TempVertices;
	FRuntimeMeshIndicesBuilder* TempIndices;

	MeshSections[SectionIndex]->GetSectionMesh(TempVertices, TempIndices);

	Vertices = TempVertices;
	Indices = TempIndices;
}

void URuntimeMeshComponent::BeginMeshSectionUpdate(int32 SectionIndex, IRuntimeMeshVerticesBuilder*& Vertices, FRuntimeMeshIndicesBuilder*& Indices)
{
	RMC_VALIDATE_UPDATEPARAMETERS(SectionIndex, /*VoidReturn*/);

	// Get mesh
	MeshSections[SectionIndex]->GetSectionMesh(Vertices, Indices);
}

bool URuntimeMeshComponent::GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetPhysicsTriMeshData);
 	int32 VertexBase = 0; // Base vertex index for current section
 
	bool HadCollision = false;

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
	// See if we should copy UVs
	bool bCopyUVs = UPhysicsSettings::Get()->bSupportUVFromHitResults;
	if (bCopyUVs)
	{
		CollisionData->UVs.AddZeroed(1); // only one UV channel
	}
#endif

	// For each section..
	for (int32 SectionIdx = 0; SectionIdx < MeshSections.Num(); SectionIdx++)
	{ 
		const RuntimeMeshSectionPtr& Section = MeshSections[SectionIdx];

		if (Section.IsValid() && Section->CollisionEnabled)
		{
			// Copy vertex data
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
			Section->GetCollisionInformation(CollisionData->Vertices, CollisionData->UVs, bCopyUVs);
#else
			Section->GetCollisionInformation(CollisionData->Vertices);
#endif

			// Copy indices
			const int32 NumTriangles = Section->IndexBuffer.Num() / 3;
			for (int32 TriIdx = 0; TriIdx < NumTriangles; TriIdx++)
			{
				// Add the triangle
				FTriIndices& Triangle = *new (CollisionData->Indices) FTriIndices;
				Triangle.v0 = Section->IndexBuffer[(TriIdx * 3) + 0] + VertexBase;
				Triangle.v1 = Section->IndexBuffer[(TriIdx * 3) + 1] + VertexBase;
				Triangle.v2 = Section->IndexBuffer[(TriIdx * 3) + 2] + VertexBase;

				// Add material info
				CollisionData->MaterialIndices.Add(SectionIdx);
			}

			// Update the vertex base index
			VertexBase = CollisionData->Vertices.Num();
			HadCollision = true;
		}
	}

	for (int32 SectionIdx = 0; SectionIdx < MeshCollisionSections.Num(); SectionIdx++)
	{
		auto& Section = MeshCollisionSections[SectionIdx];
		if (Section.VertexBuffer.Num() > 0 && Section.IndexBuffer.Num() > 0)
		{
			CollisionData->Vertices.Append(Section.VertexBuffer);

			const int32 NumTriangles = Section.IndexBuffer.Num() / 3;
			for (int32 TriIdx = 0; TriIdx < NumTriangles; TriIdx++)
			{
				// Add the triangle
				FTriIndices& Triangle = *new (CollisionData->Indices) FTriIndices;
				Triangle.v0 = Section.IndexBuffer[(TriIdx * 3) + 0] + VertexBase;
				Triangle.v1 = Section.IndexBuffer[(TriIdx * 3) + 1] + VertexBase;
				Triangle.v2 = Section.IndexBuffer[(TriIdx * 3) + 2] + VertexBase;

				// Add material info
				CollisionData->MaterialIndices.Add(SectionIdx);
			}


			VertexBase = CollisionData->Vertices.Num();
			HadCollision = true;
		}
	}
 
 	CollisionData->bFlipNormals = true;

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
	if (CollisionMode == ERuntimeMeshCollisionCookingMode::CookingPerformance)
	{
		CollisionData->bFlipNormals = true;
	}
#endif
 
 	return HadCollision;
 }

 bool URuntimeMeshComponent::ContainsPhysicsTriMeshData(bool InUseAllTriData) const
 {
 	for (const RuntimeMeshSectionPtr& Section : MeshSections)
 	{
 		if (Section.IsValid() && Section->IndexBuffer.Num() >= 3 && Section->CollisionEnabled)
 		{
 			return true;
 		}
 	}

	for (const auto& Section : MeshCollisionSections)
	{
		if (Section.VertexBuffer.Num() > 0 && Section.IndexBuffer.Num() > 0)
		{
			return true;
		}
	}
 
 	return false;
 }


void URuntimeMeshComponent::EnsureBodySetupCreated()
{
	if (BodySetup == nullptr)
	{
		BodySetup = NewObject<UBodySetup>(this, NAME_None, (IsTemplate() ? RF_Public : RF_NoFlags));
		BodySetup->BodySetupGuid = FGuid::NewGuid();

		BodySetup->bGenerateMirroredCollision = false;
		BodySetup->bDoubleSidedGeometry = true;
	}
}

void URuntimeMeshComponent::UpdateCollision()
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateCollision);

	bool NeedsNewPhysicsState = false;

	// Destroy physics state if it exists
	if (bPhysicsStateCreated)
	{
		DestroyPhysicsState();
		NeedsNewPhysicsState = true;
	}

	// Ensure we have a BodySetup
	EnsureBodySetupCreated();

	// Fill in simple collision convex elements
	BodySetup->AggGeom.ConvexElems.SetNum(ConvexCollisionSections.Num());
	for (int32 Index = 0; Index < ConvexCollisionSections.Num(); Index++)
	{
		FKConvexElem& NewConvexElem = BodySetup->AggGeom.ConvexElems[Index];

		NewConvexElem.VertexData = ConvexCollisionSections[Index].VertexBuffer;
		NewConvexElem.ElemBox = FBox(NewConvexElem.VertexData);
	} 

	// Set trace flag
	BodySetup->CollisionTraceFlag = bUseComplexAsSimpleCollision ? CTF_UseComplexAsSimple : CTF_UseDefault;

	// New GUID as collision has changed
	BodySetup->BodySetupGuid = FGuid::NewGuid();


#if WITH_RUNTIME_PHYSICS_COOKING || WITH_PHYSX || WITH_EDITOR
	// Clear current mesh data
	BodySetup->InvalidatePhysicsData();
	// Create new mesh data
	BodySetup->CreatePhysicsMeshes();
#endif // WITH_RUNTIME_PHYSICS_COOKING || WITH_EDITOR

	// Recreate physics state if necessary
	if (NeedsNewPhysicsState)
	{
		CreatePhysicsState();
	}

	UpdateNavigation();
}

UBodySetup* URuntimeMeshComponent::GetBodySetup()
{
	EnsureBodySetupCreated();
	return BodySetup;
}

void URuntimeMeshComponent::MarkCollisionDirty()
{
	if (!bCollisionDirty)
	{
		bCollisionDirty = true;
		PrePhysicsTick.SetTickFunctionEnable(true);
	}
}

void URuntimeMeshComponent::CookCollisionNow()
{
	if (bCollisionDirty)
	{
		BakeCollision();
	}
}


void URuntimeMeshComponent::BakeCollision()
{
	// Bake the collision
	UpdateCollision();

	bCollisionDirty = false;
	PrePhysicsTick.SetTickFunctionEnable(false);
}


void URuntimeMeshComponent::UpdateNavigation()
{
	if (UNavigationSystem::ShouldUpdateNavOctreeOnComponentChange() && IsRegistered())
	{
		UWorld* MyWorld = GetWorld();

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
		if (MyWorld != nullptr && MyWorld->GetNavigationSystem() != nullptr &&
			(MyWorld->GetNavigationSystem()->ShouldAllowClientSideNavigation() || !MyWorld->IsNetMode(ENetMode::NM_Client)))
#else

		if (MyWorld != nullptr && MyWorld->IsGameWorld() && MyWorld->GetNetMode() < ENetMode::NM_Client)
#endif
		{
			UNavigationSystem::UpdateComponentInNavOctree(*this);
		}
	}
}

void URuntimeMeshComponent::RegisterComponentTickFunctions(bool bRegister)
{
	Super::RegisterComponentTickFunctions(bRegister);

	if (bRegister)
	{
		if (SetupActorComponentTickFunction(&PrePhysicsTick))
		{
			PrePhysicsTick.Target = this;
			PrePhysicsTick.SetTickFunctionEnable(bCollisionDirty);
		}
	}
	else
	{
		if (PrePhysicsTick.IsTickFunctionRegistered())
		{
			PrePhysicsTick.UnRegisterTickFunction();
		}
	}
}

void URuntimeMeshComponent::SerializeLegacy(FArchive& Ar)
{
	if (Ar.CustomVer(FRuntimeMeshVersion::GUID) >= FRuntimeMeshVersion::Initial)
	{
		int32 SectionsCount = bShouldSerializeMeshData ? MeshSections.Num() : 0;
		Ar << SectionsCount;
		if (Ar.IsLoading() && MeshSections.Num() < SectionsCount)
		{
			MeshSections.SetNum(SectionsCount);
		}

		for (int32 Index = 0; Index < SectionsCount; Index++)
		{
			bool IsSectionValid = MeshSections[Index].IsValid();

			// WE can only load/save internal types (we don't know how to serialize arbitrary vertex types.
			if (Ar.IsSaving() && (IsSectionValid && !MeshSections[Index]->bIsLegacySectionType))
			{
				IsSectionValid = false;
			}

			Ar << IsSectionValid;

			if (IsSectionValid)
			{
				if (Ar.CustomVer(FRuntimeMeshVersion::GUID) >= FRuntimeMeshVersion::TemplatedVertexFix)
				{
					int32 NumUVChannels;
					bool WantsHalfPrecisionUVs;

					if (Ar.IsSaving())
					{
						MeshSections[Index]->GetInternalVertexComponents(NumUVChannels, WantsHalfPrecisionUVs);
					}

					Ar << NumUVChannels;
					Ar << WantsHalfPrecisionUVs;

					if (Ar.IsLoading())
					{
						CreateOrResetSectionLegacyType(Index, NumUVChannels);
					}

				}
				else
				{
					bool bWantsNormal;
					bool bWantsTangent;
					bool bWantsColor;
					int32 TextureChannels;

					Ar << bWantsNormal;
					Ar << bWantsTangent;
					Ar << bWantsColor;
					Ar << TextureChannels;

					if (Ar.IsLoading())
					{
						CreateOrResetSectionLegacyType(Index, TextureChannels);
					}
				}

				FRuntimeMeshSectionInterface& SectionPtr = *MeshSections[Index].Get();
				SectionPtr.Serialize(Ar);

			}
		}
	}

	if (Ar.CustomVer(FRuntimeMeshVersion::GUID) >= FRuntimeMeshVersion::SerializationOptional)
	{

		if (bShouldSerializeMeshData || Ar.IsLoading())
		{
			// Serialize the real data if we want it, also use this path for loading to get anything that was in the last save

			// Serialize the collision data
			Ar << MeshCollisionSections;
			Ar << ConvexCollisionSections;
		}
		else
		{
			// serialize empty arrays if we don't want serialization
			TArray<FRuntimeMeshCollisionSection> NullCollisionSections;
			Ar << NullCollisionSections;
			TArray<FRuntimeConvexCollisionSection> NullConvexBodies;
			Ar << NullConvexBodies;
		}
	}
}

void URuntimeMeshComponent::Serialize(FArchive& Ar)
{	
	Super::Serialize(Ar);

	SerializeInternal(Ar);
}	

void URuntimeMeshComponent::SerializeInternal(FArchive& Ar, bool bForceSaveAll)
{
	SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_Serialize);

	Ar.UsingCustomVersion(FRuntimeMeshVersion::GUID);

	// Handle old serialization
	if (Ar.CustomVer(FRuntimeMeshVersion::GUID) < FRuntimeMeshVersion::SerializationV2)
	{
		SerializeLegacy(Ar);
		return;
	}

	bool bSerializeMeshData = bShouldSerializeMeshData || bForceSaveAll;

	// Serialize basic settings
	Ar << bSerializeMeshData;
	Ar << bUseComplexAsSimpleCollision;

	// Serialize the number of sections...
	int32 NumSections = bSerializeMeshData ? MeshSections.Num() : 0;
	Ar << NumSections;

	// Resize the section array if we're loading.
	if (Ar.IsLoading())
	{
		MeshSections.Reset(NumSections);
		MeshSections.SetNum(NumSections);
	}

	// Next serialize all the sections...
	for (int32 Index = 0; Index < NumSections; Index++)
	{
		SerializeRMCSection(Ar, Index);
	}

	if (bSerializeMeshData || Ar.IsLoading())
	{
		// Serialize the real data if we want it, also use this path for loading to get anything that was in the last save

		// Serialize the collision data
		Ar << MeshCollisionSections;
		Ar << ConvexCollisionSections;
	}
	else
	{
		// serialize empty arrays if we don't want serialization
		TArray<FRuntimeMeshCollisionSection> NullCollisionSections;
		Ar << NullCollisionSections;
		TArray<FRuntimeConvexCollisionSection> NullConvexBodies;
		Ar << NullConvexBodies;
	}
}


void URuntimeMeshComponent::SerializeRMC(FArchive& Ar)
{
	SerializeInternal(Ar, true);
}

void URuntimeMeshComponent::SerializeRMCSection(FArchive& Ar, int32 SectionIndex)
{
	if (Ar.IsLoading() && MeshSections.Num() <= SectionIndex)
	{
		MeshSections.SetNum(SectionIndex + 1);
	}

	// Serialize the section validity (default it to section valid + type known for saving reasons)
	bool bSectionIsValid = MeshSections[SectionIndex].IsValid();
	bool bSectionTypeFound = bSectionIsValid ? FRuntimeMeshVertexTypeRegistrationContainer::GetInstance().GetVertexType(MeshSections[SectionIndex]->GetVertexType()->TypeGuid) != nullptr : true;
	bSectionIsValid = bSectionIsValid && bSectionTypeFound;
	Ar << bSectionIsValid;

	// If section is invalid, skip
	if (!bSectionIsValid)
	{
		if (!bSectionTypeFound)
		{
			UE_LOG(RuntimeMeshLog, Error, TEXT("Attempted to serialize a vertex of unknown type %s"), *MeshSections[SectionIndex]->GetVertexType()->TypeGuid.ToString());
		}
		return;
	}

	// Serialize section type info
	FGuid TypeGuid;
	bool bHasSeparatePositionBuffer;

	if (Ar.IsSaving())
	{
		TypeGuid = MeshSections[SectionIndex]->GetVertexType()->TypeGuid;
		bHasSeparatePositionBuffer = MeshSections[SectionIndex]->IsDualBufferSection();
	}

	Ar << TypeGuid;
	Ar << bHasSeparatePositionBuffer;

	if (Ar.IsLoading())
	{
		auto VertexTypeRegistration = FRuntimeMeshVertexTypeRegistrationContainer::GetInstance().GetVertexType(TypeGuid);

		if (VertexTypeRegistration == nullptr)
		{
			UE_LOG(RuntimeMeshLog, Error, TEXT("Attempted to serialize a vertex of unknown type %s"), *MeshSections[SectionIndex]->GetVertexType()->TypeGuid.ToString());
			bSectionIsValid = false;
		}
		else
		{
			auto NewSection = VertexTypeRegistration->CreateSection(bHasSeparatePositionBuffer);
			MeshSections[SectionIndex] = MakeShareable(NewSection);
		}
	}

	// Now we save the section data to a separate archive and then write in into the main. 
	// This way we can recover from unknown types or mismatch sizes


	TArray<uint8> SectionData;

	if (Ar.IsSaving())
	{
		FMemoryWriter SectionAr(SectionData, true);
		SectionAr.UsingCustomVersion(FRuntimeMeshVersion::GUID);

		MeshSections[SectionIndex]->Serialize(SectionAr);
	}

	Ar << SectionData;

	if (Ar.IsLoading() && bSectionIsValid)
	{
		FMemoryReader SectionAr(SectionData, true);
		SectionAr.Seek(0);
		SectionAr.UsingCustomVersion(FRuntimeMeshVersion::GUID);

		MeshSections[SectionIndex]->Serialize(SectionAr);

		// Was this section loaded correctly?
		if (SectionAr.IsError())
		{
			MeshSections[SectionIndex].Reset();
			UE_LOG(RuntimeMeshLog, Log, TEXT("Unable to load section %d of type %s. This is most likely caused by a reconfigured vertex type."),
				SectionIndex, *MeshSections[SectionIndex]->GetVertexType()->TypeName);
		}
	}
}








void URuntimeMeshComponent::PostLoad()
{
	Super::PostLoad();

	// Rebuild collision and local bounds.
	MarkCollisionDirty();
	UpdateLocalBounds();

	if (BodySetup && IsTemplate())
	{
		BodySetup->SetFlags(RF_Public);
	}
}