[UE5] Project_StackBlock #3 - DynamicMeshComponent의 겹친 영역을 완벽히제거하는 방법!

ChangJin·2024년 7월 27일
0

Unreal Engine5

목록 보기
89/114
post-thumbnail

2024.07.26

이번 글에서는 블루프린트로 구현한 DynamicMeshComponent의 겹쳐진 영역 자르기 기능을 C++ 코드로 변환하여 최적화하는 방법을 설명합니다. 또한 AABB의 개념과 어떻게 사용할 수 있는지를 설명합니다.


블루프린트 로직리팩토링 후 블루프린트 로직

1. DynamicMeshComponent의 겹치는 부분 자르기

BP_Wall과 BP_Boolean의 BoxCollision 컴포넌트가 겹치는 부분을 찾아 BP_Boolean의 Dynamic Mesh에서 해당 부분을 제거하려고 했습니다. 잘 동작하는지 블루프린트로 로직을 검증 해봤습니다. 잘 작동하는걸 확인 후에 C++로 구현했습니다.

블루프린트에서 InverseTransformLocation의 결과와 일치하도록 C++ 코드에서 보정을 해주어야했습니다. Transform 위치를 올바르게 보정하여 Dynamic Mesh에서 겹치는 부분을 제거했습니다.

우선 이 기능을 구현하기 위해서는 AABB에 대해서 이해해야합니다.

AABB(Axis-Aligned Bounding Box)란 무엇인가?

AABB는 Axis-Aligned Bounding Box의 약자로, 3D 공간에서 객체를 둘러싸는 축에 맞춰진 직육면체를 의미합니다. 이는 객체의 위치와 크기를 기준으로 계산됩니다. AABB는 객체의 최소값(Min)과 최대값(Max)으로 정의되며, 이는 각각 객체의 좌표 중 가장 작은 값과 가장 큰 값을 의미합니다. AABB는 주로 충돌 감지, 렌더링 최적화, 물리 엔진 등에서 사용됩니다.

AABB 계산 방법

  1. 위치와 크기 가져오기: 각 BoxCollision 컴포넌트의 위치와 크기를 가져옵니다.
  2. 최대값(Max)과 최소값(Min) 계산: 최대값은 위치 + 크기, 최소값은 위치 - 크기입니다.
  3. 겹치는 영역 계산: 두 박스의 최소값 중 큰 값이 겹치는 영역의 시작점이고, 두 박스의 최대값 중 작은 값이 겹치는 영역의 끝점입니다.

AABB의 활용

  • 충돌 감지: 객체 간의 충돌을 감지하는 데 사용됩니다. AABB를 사용하면 객체의 경계 상자를 비교하여 빠르게 충돌 여부를 판단할 수 있습니다.
  • 렌더링 최적화: 객체의 경계 상자를 사용하여 보이는 객체와 보이지 않는 객체를 구분하여 렌더링 성능을 최적화할 수 있습니다.
  • 물리 엔진: 객체의 경계 상자를 사용하여 물리적 상호작용을 계산할 수 있습니다.

AABB의 프로젝트에서의 적용

  • BP_Wall과 BP_Boolean의 BoxCollision 컴포넌트를 사용하여 AABB를 계산하였습니다.
  • 겹치는 영역을 찾아내어 Dynamic Mesh에서 해당 부분을 제거하는 데 AABB를 활용하였습니다.

블루프린트 로직

블루프린트 로직
  1. 두 BoxCollision의 AABB를 계산하여 겹치는 영역을 찾습니다.
  2. 겹치는 영역의 위치와 크기를 구한 후, Compute Mesh를 만들어냅니다.그 다음 Apply Boolean으로 Dynamic Mesh에서 해당 부분을 제거합니다.
  3. Release All Compute Mesh를 해주는 것을 잊으면 안됩니다.

C++ 코드 구현

이제 이 블루프린트 로직을 C++로 구현한 코드는 다음과 같습니다.

DynamicBlockActor.h

#pragma once

#include "CoreMinimal.h"
#include "DynamicMeshActor.h"
#include "Components/BoxComponent.h"
#include "DynamicBlockActor.generated.h"

UCLASS()
class STUDY002_API ADynamicBlockActor : public ADynamicMeshActor
{
	GENERATED_BODY()

public:
	ADynamicBlockActor();
	
private:
	virtual void OnConstruction(const FTransform& Transform) override;

public:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	UBoxComponent* BoxCollision;

	UPROPERTY(BlueprintReadWrite)
	FVector MaxAxis1;

	UPROPERTY(BlueprintReadWrite)
	FVector MinAxis1;

	UPROPERTY(BlueprintReadWrite)
	FVector MaxAxis2;

	UPROPERTY(BlueprintReadWrite)
	FVector MinAxis2;

	UFUNCTION(BlueprintCallable)
	UDynamicMesh* GetOverlappedArea(UBoxComponent* TargetBoxComponent);

	void SetOverlapExtentAndLocation(UBoxComponent* Box1, UBoxComponent* Box2);

private:
	FVector OverlapLocationVector;
	FVector OverlapExtentVector;
};

DynamicBlockActor.cpp

#include "DynamicBlockActor.h"
#include "AI/NavigationModifier.h"
#include "GeometryScript/MeshPrimitiveFunctions.h"

ADynamicBlockActor::ADynamicBlockActor()
{
	BoxCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollision"));
	BoxCollision->SetupAttachment(DynamicMeshComponent);
}

void ADynamicBlockActor::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);

	UDynamicMesh* DynamicMesh =  DynamicMeshComponent->GetDynamicMesh();
	if(DynamicMesh)
	{
		DynamicMesh->Reset();

		FTransform T;
		FGeometryScriptPrimitiveOptions Options;
		FVector Dimension = BoxCollision->GetScaledBoxExtent();
		float DimensionX = Dimension.X * 2;
		float DimensionY = Dimension.Y * 2;
		float DimensionZ = Dimension.Z * 2;
		float TransformLocationZ = Dimension.Z * -1;
		T.SetLocation(FVector(0,0,TransformLocationZ));
		UGeometryScriptLibrary_MeshPrimitiveFunctions::AppendBox(DynamicMesh, Options, T, DimensionX, DimensionY, DimensionZ);
		DynamicMeshComponent->EnableComplexAsSimpleCollision();
	}
}

void ADynamicBlockActor::SetOverlapExtentAndLocation(UBoxComponent* Box1, UBoxComponent* Box2)
{
    FVector Box1Location = Box1->GetComponentLocation();
    FVector Box1Extent = Box1->GetScaledBoxExtent();

    FVector Box2Location = Box2->GetComponentLocation();
    FVector Box2Extent = Box2->GetScaledBoxExtent();

    FVector Max1 = Box1Location + Box1Extent;
    FVector Min1 = Box1Location - Box1Extent;

    FVector Max2 = Box2Location + Box2Extent;
    FVector Min2 = Box2Location - Box2Extent;
	
	FVector OverlapMax = FVector(FMath::Min(Max1.X, Max2.X), FMath::Min(Max1.Y, Max2.Y), FMath::Min(Max1.Z, Max2.Z));
    FVector OverlapMin = FVector(FMath::Max(Min1.X, Min2.X), FMath::Max(Min1.Y, Min2.Y), FMath::Max(Min1.Z, Min2.Z));

	OverlapLocationVector = (OverlapMin + OverlapMax) / 2.0f;
	OverlapExtentVector = OverlapMax - OverlapMin;
}

UDynamicMesh* ADynamicBlockActor::GetOverlappedArea(UBoxComponent* TargetBoxComponent)
{
	UDynamicMesh* TargetMesh = nullptr;
	SetOverlapExtentAndLocation(BoxCollision, TargetBoxComponent);

	UDynamicMesh* DynamicMesh = DynamicMeshComponent->GetDynamicMesh();
	FTransform Transform = DynamicMeshComponent->GetComponentTransform();
	FVector BoxTransform = Transform.InverseTransformPosition(OverlapLocationVector);

	if(DynamicMesh)
	{
		UDynamicMesh* ComputeMesh = AllocateComputeMesh();
		if(ComputeMesh)
		{
			FTransform T;
			FGeometryScriptPrimitiveOptions Options;
			FVector LocalOverlapLocation = FVector(BoxTransform.X, BoxTransform.Y, BoxTransform.Z - OverlapExtentVector.Z /2 );
			T.SetLocation(LocalOverlapLocation);
			
			TargetMesh = UGeometryScriptLibrary_MeshPrimitiveFunctions::AppendBox(
				ComputeMesh,
				Options,
				T,
				OverlapExtentVector.X,
				OverlapExtentVector.Y,
				OverlapExtentVector.Z
				);
		}
	}

	return TargetMesh;
}

리팩토링한 블루프린트 로직

블루프린트 로직

문제 해결 과정

  1. 블루프린트 로직 검증

    • 블루프린트를 사용하여 겹치는 영역을 계산하고, Dynamic Mesh에서 해당 부분을 제거하는 로직을 검증했습니다.
  2. C++ 코드로 변환

    • 검증된 블루프린트 로직을 C++로 변환했습니다.
    • Transform 위치 설정과 Z축 보정을 정확히 수행하여, Dynamic Mesh에서 겹치는 부분을 제거할 수 있도록 했습니다.
  3. 문제 해결

    • InverseTransformLocation의 결과를 정확히 반영하여 Transform 위치를 설정했습니다.
    • Z축 보정을 통해 Dynamic Mesh에서 겹치는 부분을 제거할 수 있었습니다.

결론

이번 글에서는 DynamicMeshComponent의 겹치는 부분을 찾아 제거하는 방법을 다루었습니다. 블루프린트로 로직을 검증한 후, 이를 C++로 변환했습니다. 이 과정을 통해 Dynamic Mesh와 관련된 기능을 효율적으로 구현할 수 있었습니다. 추가적인 질문이나 수정이 필요하다면 언제든지 알려주세요!

참고 자료

profile
게임 프로그래머

0개의 댓글