/** Loc is an anchor point in the world to guide which part of the infinite plane to draw. */
void DrawDebugSolidPlane(const UWorld* InWorld, FPlane const& P, FVector const& Loc, float Size, FColor const& Color, bool bPersistent, float LifeTime, uint8 DepthPriority)
{
	// no debug line drawing on dedicated server
	if (GEngine->GetNetMode(InWorld) != NM_DedicatedServer)
	{
		FVector const ClosestPtOnPlane = Loc - P.PlaneDot(Loc) * P;

		FVector U, V;
		P.FindBestAxisVectors(U, V);
		U *= Size;
		V *= Size;

		TArray<FVector> Verts;
		Verts.AddUninitialized(4);
		Verts[0] = ClosestPtOnPlane + U + V;
		Verts[1] = ClosestPtOnPlane - U + V;
		Verts[2] = ClosestPtOnPlane + U - V;
		Verts[3] = ClosestPtOnPlane - U - V;

		TArray<int32> Indices;
		Indices.AddUninitialized(6);
		Indices[0] = 0; Indices[1] = 2; Indices[2] = 1;
		Indices[3] = 1; Indices[4] = 2; Indices[5] = 3;

		// plane quad
		DrawDebugMesh(InWorld, Verts, Indices, Color, bPersistent, LifeTime, DepthPriority);

		// arrow indicating normal
		DrawDebugDirectionalArrow(InWorld, ClosestPtOnPlane, ClosestPtOnPlane + P * 16.f, 8.f, FColor::White, bPersistent, LifeTime, DepthPriority);
	}
}
void FGameplayDebuggerShape::Draw(UWorld* World, FGameplayDebuggerCanvasContext& Context)
{
	FVector DescLocation;
	switch (Type)
	{
	case EGameplayDebuggerShape::Point:
		if (ShapeData.Num() == 2 && ShapeData[1].X > 0)
		{
			DrawDebugSphere(World, ShapeData[0], ShapeData[1].X, 16, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Segment:
		if (ShapeData.Num() == 3 && ShapeData[2].X > 0)
		{
			DrawDebugLine(World, ShapeData[0], ShapeData[1], Color, false, -1.0f, 0, ShapeData[2].X);
			DescLocation = (ShapeData[0] + ShapeData[1]) * 0.5f;
		}
		break;

	case EGameplayDebuggerShape::Box:
		if (ShapeData.Num() == 2)
		{
			DrawDebugBox(World, ShapeData[0], ShapeData[1], Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Cone:
		if (ShapeData.Num() == 3 && ShapeData[2].X > 0)
		{
			DrawDebugCone(World, ShapeData[0], ShapeData[1], ShapeData[2].X, PI * 0.5f, PI * 0.5f, 16, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Cylinder:
		if (ShapeData.Num() == 2)
		{
			DrawDebugCylinder(World, ShapeData[0] - FVector(0, 0, ShapeData[1].Z), ShapeData[0] + FVector(0, 0, ShapeData[1].Z), ShapeData[1].X, 16, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Capsule:
		if (ShapeData.Num() == 2)
		{
			DrawDebugCapsule(World, ShapeData[0], ShapeData[1].Z, ShapeData[1].X, FQuat::Identity, Color);
			DescLocation = ShapeData[0];
		}
		break;

	case EGameplayDebuggerShape::Polygon:
		if (ShapeData.Num() > 0)
		{
			FVector MidPoint = FVector::ZeroVector;
			TArray<int32> Indices;
			for (int32 Idx = 0; Idx < ShapeData.Num(); Idx++)
			{
				Indices.Add(Idx);
				MidPoint += ShapeData[Idx];
			}

			DrawDebugMesh(World, ShapeData, Indices, Color);
			DescLocation = MidPoint / ShapeData.Num();
		}
		break;

	default:
		break;
	}

	if (Description.Len() && Context.IsLocationVisible(DescLocation))
	{
		const FVector2D ScreenLoc = Context.ProjectLocation(DescLocation);
		Context.PrintAt(ScreenLoc.X, ScreenLoc.Y, Color, Description);
	}
}