Centerpiece
Centerpiece
Centerpiece
Centerpiece
Centerpiece
Centerpiece

When I started working on coffee tables building columns, outdoor planters with benches… i noticed that they all had something in common. The placement of objects around a center object or point. So I decided to create a centerpiece class actor to make the layout of these types of objects easier. The main functions that I created for this are

// Gets the the bounding box of the center piece item

UCageUtilities::GetBoxCorners

These functions made it a breeze to place objects around a centerpiece. Now from here I can further define each blueprint/prefab based on the objects. A simple array of actors/meshes can be used, in conjunction with hierarchal instance static mesh components. For my use, I have specialized actors like light bulb lamp actors, chair actors and other specialized actors that I will be using accompanied by a data table to select the specific item type. I will not cover that here. But there may be a Part 2 in the near future.

It’s rather simple to roll your own placement of these objects, but Unreal’s PCG framework makes it pretty simple as well to lay out a ballroom for instance or dining hall.

TArray UCageUtilities::GetBoxCorners(UPrimitiveComponent* InComp, float RadiusOffset)
{
    if (InComp == nullptr)
    {
        return TArray();
    }
    else
    {
        TArray BoxCorners;
        FVector Origin, BoxExtent;
        float SphereRadius;
        UKismetSystemLibrary::GetComponentBounds(InComp, Origin, BoxExtent, SphereRadius); 
        FVector BoxVector = Origin - InComp->GetComponentLocation();
        float BoxZ = BoxVector.Z;
        FBoxSphereBounds ComponentBounds = InComp->CalcBounds(InComp->GetComponentTransform());

        // Apply inset using RadiusOffset to X and Y axes
        FVector Inset(BoxExtent.X - RadiusOffset, BoxExtent.Y - RadiusOffset, 0.0f);

        FVector Corner1 = FVector(-Inset.X, -Inset.Y, BoxZ);
        FVector Corner2 = FVector(Inset.X, -Inset.Y, BoxZ);
        FVector Corner3 = FVector(Inset.X, Inset.Y, BoxZ);
        FVector Corner4 = FVector(-Inset.X, Inset.Y, BoxZ);

        BoxCorners.Add(Corner1);
        BoxCorners.Add(Corner2);
        BoxCorners.Add(Corner3);
        BoxCorners.Add(Corner4);

        return BoxCorners;
    }
}

UCageUtilities::GetMeshDimensions

FVector UCageUtilities::GetMeshDimensions(class UStaticMesh* InMesh, bool UseLocalBounds)
{
    if (InMesh == nullptr)
    {
        return FVector::ZeroVector;
    }

    FVector Dimensions;

    if (UseLocalBounds)
    {

        // DEPR - requires the component input
/*        FBoxSphereBounds Bounds = InMesh->GetLocalBounds();
        FVector Scale = InMesh->GetComponentScale();
        Dimensions = Bounds.BoxExtent * 2 * Scale;*/
    }
    else
    {
        FVector BoxMin = InMesh->GetBoundingBox().Min;
        FVector BoxMax = InMesh->GetBoundingBox().Max;
        Dimensions = FVector(BoxMax.X - BoxMin.X, BoxMax.Y - BoxMin.Y, BoxMax.Z - BoxMin.Z);
    }

    return Dimensions;
}

UCageUtilities::CircleObjectsAroundComponent

/* Version that takes in a Scene Component */
void UCageUtilities::CircleObjectsAroundComponent(USceneComponent* Target, int32 Amount, float Radius, EAxis::Type Axis, TArray& ObjectTransforms)
{
    TArray LocalTransforms;

    if (Target != nullptr)
    {
        for (int32 Index = 0; Index < Amount; ++Index) 
        {
            float Angle = 2 * PI * Index / Amount; 
            FVector CircleCalculation;
            CircleCalculation = FVector(cos(Angle) * Radius, sin(Angle) * Radius, 0);

            FTransform NewTransform;
            NewTransform.SetLocation(Target->GetRelativeLocation() + CircleCalculation * -1.0f); 

            FRotator NewRotation;
            switch(Axis) 
            {
                case EAxis::Type::X:
                    NewRotation = FRotator(0, 0, FMath::RadiansToDegrees(Angle));
                    break;
                case EAxis::Type::Y:
                    NewRotation = FRotator(FMath::RadiansToDegrees(Angle), 0, 0);
                    break;
                case EAxis::Type::Z:
                    NewRotation = FRotator(0, FMath::RadiansToDegrees(Angle), 0);
                    break;
            }

            FQuat QuatRotation = FQuat(NewRotation);
            NewTransform.SetRotation(QuatRotation);

            NewTransform.SetScale3D(FVector(1, 1, 1)); 

            LocalTransforms.Add(NewTransform);
        }
    }

    ObjectTransforms = LocalTransforms;
}

UCageUtilities::CircleObjectsAroundVector

/* Version that takes in a vector */
void UCageUtilities::CircleObjectsAroundVector(FVector InVector, int32 Amount, float Radius, EAxis::Type Axis, TArray& ObjectTransforms)
{
    TArray LocalTransforms;

        for (int32 Index = 0; Index < Amount; ++Index) 
        {
            float Angle = 2 * PI * Index / Amount; 
            FVector CircleCalculation;

            switch(Axis) 
            {
                case EAxis::Type::X:
                    CircleCalculation = FVector(0, cos(Angle) * Radius, sin(Angle) * Radius);
                    break;
                case EAxis::Type::Y:
                    CircleCalculation = FVector(cos(Angle) * Radius, 0, sin(Angle) * Radius);
                    break;
                case EAxis::Type::Z:
                    CircleCalculation = FVector(cos(Angle) * Radius, sin(Angle) * Radius, 0);
                    break;
            }

            FTransform NewTransform;
            FVector NewPoint;
            NewPoint = InVector + CircleCalculation * -1.0f;
            NewTransform.SetLocation(NewPoint); 

            FRotator NewRotation;
            NewRotation = FRotator(0, FMath::RadiansToDegrees(Angle), 0);

            FQuat QuatRotation = FQuat(NewRotation);
            NewTransform.SetRotation(QuatRotation);
            NewTransform.SetScale3D(FVector(1, 1, 1));

            LocalTransforms.Add(NewTransform);
        }
    
    ObjectTransforms = LocalTransforms;

}