void Serialize(FArchive& Ar, UObject* Owner, int32 Idx)
    {
		const uint8 AdjacencyDataStripFlag = 1;
		FStripDataFlags StripFlags( Ar, Ar.IsCooking() && !Ar.CookingTarget()->SupportsFeature(ETargetPlatformFeatures::Tessellation) ? AdjacencyDataStripFlag : 0 );

		UStaticMesh* StaticMesh = Cast<UStaticMesh>( Owner );
		bool bNeedsCPUAccess = true;

		if( !StripFlags.IsEditorDataStripped() )
        {
			RawTriangles.Serialize( Ar, Owner );
        }

		Ar << Elements;

		if( !StripFlags.IsDataStrippedForServer() )
        {
			PositionVertexBuffer.Serialize( Ar, bNeedsCPUAccess );
			VertexBuffer.Serialize( Ar, bNeedsCPUAccess );
			ColorVertexBuffer.Serialize( Ar, bNeedsCPUAccess );
			Ar << NumVertices;
			IndexBuffer.Serialize( Ar, bNeedsCPUAccess );
			if (Ar.UE4Ver() >= VER_UE4_SHADOW_ONLY_INDEX_BUFFERS)
            {		
				ShadowIndexBuffer.Serialize(Ar, bNeedsCPUAccess);
            }
			if( !StripFlags.IsEditorDataStripped() )
            {
				Ar << WireframeIndexBuffer;
            }
			if ( !StripFlags.IsClassDataStripped( AdjacencyDataStripFlag ) )
            { 					
				AdjacencyIndexBuffer.Serialize( Ar, bNeedsCPUAccess );
            }
        }

		if (Ar.IsLoading())
        {
			if (PositionVertexBuffer.GetNumVertices() != NumVertices)
            {
				PositionVertexBuffer.RemoveLegacyShadowVolumeVertices(NumVertices);
			}
			if (VertexBuffer.GetNumVertices() != NumVertices)
			{
				VertexBuffer.RemoveLegacyShadowVolumeVertices(NumVertices);
			}
			if (VertexBuffer.GetNumVertices() != NumVertices)
            {				
				ColorVertexBuffer.RemoveLegacyShadowVolumeVertices(NumVertices);
			}
        }
    }										
/**
 * Initializes this vertex buffer with the contents of the given vertex buffer.
 * @param InVertexBuffer - The vertex buffer to initialize from.
 */
void FPositionVertexBuffer::Init(const FPositionVertexBuffer& InVertexBuffer)
{
	NumVertices = InVertexBuffer.GetNumVertices();
	if ( NumVertices )
	{
		AllocateData();
		check( Stride == InVertexBuffer.GetStride() );
		VertexData->ResizeBuffer(NumVertices);
		Data = VertexData->GetDataPointer();
		const uint8* InData = InVertexBuffer.Data;
		FMemory::Memcpy( Data, InData, Stride * NumVertices );
	}
}
void RemapPaintedVertexColors(
	const TArray<FPaintedVertex>& InPaintedVertices,
	const FColorVertexBuffer& InOverrideColors,
	const FPositionVertexBuffer& NewPositions,
	const FStaticMeshVertexBuffer* OptionalVertexBuffer,
	TArray<FColor>& OutOverrideColors
	)
{

	// Local copy of painted vertices we can scratch on.
	TArray<FPaintedVertex> PaintedVertices(InPaintedVertices);

	// Find the extents formed by the cached vertex positions in order to optimize the octree used later
	FVector MinExtents( PaintedVertices[ 0 ].Position );
	FVector MaxExtents( PaintedVertices[ 0 ].Position );

	for (int32 VertIndex = 0; VertIndex < PaintedVertices.Num(); ++VertIndex)
	{
		FVector& CurVector = PaintedVertices[ VertIndex ].Position;

		MinExtents.X = FMath::Min<float>( MinExtents.X, CurVector.X );
		MinExtents.Y = FMath::Min<float>( MinExtents.Y, CurVector.Y );
		MinExtents.Z = FMath::Min<float>( MinExtents.Z, CurVector.Z );

		MaxExtents.X = FMath::Max<float>( MaxExtents.X, CurVector.X );
		MaxExtents.Y = FMath::Max<float>( MaxExtents.Y, CurVector.Y );
		MaxExtents.Z = FMath::Max<float>( MaxExtents.Z, CurVector.Z );
	}

	// Create an octree which spans the extreme extents of the old and new vertex positions in order to quickly query for the colors
	// of the new vertex positions
	for (int32 VertIndex = 0; VertIndex < (int32)NewPositions.GetNumVertices(); ++VertIndex)
	{
		FVector CurVector = NewPositions.VertexPosition(VertIndex);

		MinExtents.X = FMath::Min<float>( MinExtents.X, CurVector.X );
		MinExtents.Y = FMath::Min<float>( MinExtents.Y, CurVector.Y );
		MinExtents.Z = FMath::Min<float>( MinExtents.Z, CurVector.Z );

		MaxExtents.X = FMath::Max<float>( MaxExtents.X, CurVector.X );
		MaxExtents.Y = FMath::Max<float>( MaxExtents.Y, CurVector.Y );
		MaxExtents.Z = FMath::Max<float>( MaxExtents.Z, CurVector.Z );
	}

	FBox Bounds( MinExtents, MaxExtents );
	TSMCVertPosOctree VertPosOctree( Bounds.GetCenter(), Bounds.GetExtent().GetMax() );

	// Add each old vertex to the octree
	for ( int32 PaintedVertexIndex = 0; PaintedVertexIndex < PaintedVertices.Num(); ++PaintedVertexIndex )
	{
		VertPosOctree.AddElement( PaintedVertices[ PaintedVertexIndex ] );
	}


	// Iterate over each new vertex position, attempting to find the old vertex it is closest to, applying
	// the color of the old vertex to the new position if possible.
	OutOverrideColors.Empty(NewPositions.GetNumVertices());
	const float DistanceOverNormalThreshold = OptionalVertexBuffer ? KINDA_SMALL_NUMBER : 0.0f;
	for ( uint32 NewVertIndex = 0; NewVertIndex < NewPositions.GetNumVertices(); ++NewVertIndex )
	{
		TArray<FPaintedVertex> PointsToConsider;
		TSMCVertPosOctree::TConstIterator<> OctreeIter( VertPosOctree );
		const FVector& CurPosition = NewPositions.VertexPosition( NewVertIndex );
		FVector CurNormal = FVector::ZeroVector;
		if (OptionalVertexBuffer)
		{
			CurNormal = OptionalVertexBuffer->VertexTangentZ( NewVertIndex );
		}

		// Iterate through the octree attempting to find the vertices closest to the current new point
		while ( OctreeIter.HasPendingNodes() )
		{
			const TSMCVertPosOctree::FNode& CurNode = OctreeIter.GetCurrentNode();
			const FOctreeNodeContext& CurContext = OctreeIter.GetCurrentContext();

			// Find the child of the current node, if any, that contains the current new point
			FOctreeChildNodeRef ChildRef = CurContext.GetContainingChild( FBoxCenterAndExtent( CurPosition, FVector::ZeroVector ) );

			if ( !ChildRef.IsNULL() )
			{
				const TSMCVertPosOctree::FNode* ChildNode = CurNode.GetChild( ChildRef );

				// If the specified child node exists and contains any of the old vertices, push it to the iterator for future consideration
				if ( ChildNode && ChildNode->GetInclusiveElementCount() > 0 )
				{
					OctreeIter.PushChild( ChildRef );
				}
				// If the child node doesn't have any of the old vertices in it, it's not worth pursuing any further. In an attempt to find
				// anything to match vs. the new point, add all of the children of the current octree node that have old points in them to the
				// iterator for future consideration.
				else
				{
					FOREACH_OCTREE_CHILD_NODE( ChildRef )
					{
						if( CurNode.HasChild( ChildRef ) && CurNode.GetChild( ChildRef )->GetInclusiveElementCount() > 0 )
						{
							OctreeIter.PushChild( ChildRef );
						}
					}
				}
			}

			// Add all of the elements in the current node to the list of points to consider for closest point calculations
			PointsToConsider.Append( CurNode.GetElements() );
			OctreeIter.Advance();
		}

		// If any points to consider were found, iterate over each and find which one is the closest to the new point 
		if ( PointsToConsider.Num() > 0 )
		{
			FPaintedVertex BestVertex = PointsToConsider[0];
			float BestDistanceSquared = ( BestVertex.Position - CurPosition ).SizeSquared();
			float BestNormalDot = FVector( BestVertex.Normal ) | CurNormal;

			for ( int32 ConsiderationIndex = 1; ConsiderationIndex < PointsToConsider.Num(); ++ConsiderationIndex )
			{
				FPaintedVertex& Vertex = PointsToConsider[ ConsiderationIndex ];
				const float DistSqrd = ( Vertex.Position - CurPosition ).SizeSquared();
				const float NormalDot = FVector( Vertex.Normal ) | CurNormal;
				if ( DistSqrd < BestDistanceSquared - DistanceOverNormalThreshold )
				{
					BestVertex = Vertex;
					BestDistanceSquared = DistSqrd;
					BestNormalDot = NormalDot;
				}
				else if ( OptionalVertexBuffer && DistSqrd < BestDistanceSquared + DistanceOverNormalThreshold && NormalDot > BestNormalDot )
				{
					BestVertex = Vertex;
					BestDistanceSquared = DistSqrd;
					BestNormalDot = NormalDot;
				}
			}

			OutOverrideColors.Add(BestVertex.Color);
		}
	}
}