Unreal 게임 개발

Unreal Skeletal Mesh to Procedural Mesh - Slice

daisy0461 2025. 2. 26. 00:51

이제 잘라보자.

자르는 코드는 어렵지 않다.

void AEnemy::SliceMeshAtBone(FVector SliceNormal, bool bCreateOtherHalf) 
{
    if (!GetMesh()|| !ProcMeshComponent) {
        UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: SkeletalMeshComponent or ProcMeshComponent is null."));
        return;
    }

    FVector BoneLocation = GetMesh()->GetBoneLocation(TargetBoneName);
    if (BoneLocation == FVector::ZeroVector) {
        UE_LOG(LogTemp, Error, TEXT("SliceMeshAtBone: Failed to get Bone '%s' location. Check if the bone exists in the skeleton."), *TargetBoneName.ToString());
        return;
    }

    UMaterialInterface* ProcMeshMaterial = ProcMeshComponent->GetMaterial(0);
    if (!ProcMeshMaterial) {
        UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: Procedural mesh has no material assigned."));
    }

	UProceduralMeshComponent* OtherHalfMesh = nullptr;		//잘린 Procedural Mesh가 OtherHalfMesh가 된다.
    UKismetProceduralMeshLibrary::SliceProceduralMesh(
        ProcMeshComponent,                         
        BoneLocation,                         
        SliceNormal,                          
        bCreateOtherHalf,                     
        OtherHalfMesh,                        
        EProcMeshSliceCapOption::CreateNewSectionForCap,       
        CapMaterial                           //절단면 Material
    );

	if(!OtherHalfMesh){
		UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: Failed to slice mesh at bone '%s'."), *TargetBoneName.ToString());
		return;
	}
    //Attach할 Socket 이름을 확인하기 - Socket이 있는지 꼭 확인!!
    if (ProceduralMeshAttachSocketName.IsNone() || OtherHalfMeshAttachSocketName.IsNone()) {
        UE_LOG(LogTemp, Warning, TEXT("SliceMeshAtBone: One or both Socket Names are invalid!"));
        return;
    }
	ProcMeshComponent->SetSimulatePhysics(false);
	OtherHalfMesh->SetSimulatePhysics(false);
	UE_LOG(LogTemp, Display, TEXT("Physic Disable"));

    //Procedural Mesh를 특정 Socket에 Attach
	FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget, true);
	ProcMeshComponent->AttachToComponent(GetMesh(), TransformRules, ProceduralMeshAttachSocketName);
	OtherHalfMesh->AttachToComponent(GetMesh(), TransformRules, OtherHalfMeshAttachSocketName);

    //Ragdoll 적용 & Bone 자름.
    GetMesh()->SetCollisionProfileName(TEXT("Ragdoll"));
    GetMesh()->BreakConstraint(FVector(1000.f, 1000.f, 1000.f), FVector::ZeroVector, TargetBoneName);
    GetMesh()->SetSimulatePhysics(true);

    //Procedural Mesh에 물리 적용
    //ProcMeshComponent->SetSimulatePhysics(true); -> true 시 따로 움직인다.
    ProcMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

이 부분에선 크게 설명할 부분이 없는 것 같다.

BreakConstraint는 Bone의 연결을 끊는 함수이다. 자세한건 도큐먼트를 참고하면 될 것 같다.

 

여기까지하면 거의 다 왔다.

여기까지 따라오셨다면 Slice했을 때 기이한 현상이 일어날 것이다.

 

Skeletal Mesh가 BreakConstraint에 있는 FVector Impulse 파라미터로 인해 Mesh의 vertex들이 예상치 못한 위치로 이동하면서 텍스쳐가 왜곡된 현상이다.

 

이 현상을 방지하기 위해 다음과 같은 코드를 실행시켜주면 된다.

vertex를 수집할 때 NumVertices를 찾는 코드를 추가해준다.

	//GetResourceForRendering - Skeletal Mesh의 렌더링 데이터를 가져오는 함수
    const FSkeletalMeshRenderData* RenderData = SkeletalMesh->GetResourceForRendering();
    if (!RenderData || !RenderData->LODRenderData.IsValidIndex(LODIndex)){
        UE_LOG(LogTemp, Warning, TEXT("CopySkeletalMeshToProcedural: LODRenderData[%d] is not valid."), LODIndex);
        return;
    }
    
	//vertex의 총 개수를 들고온다.
	NumVertices = RenderData->LODRenderData[LODIndex].GetNumVertices();

 

그리고 해당 함수를 추가해주면 된다.

void AEnemy::ApplyVertexAlphaToSkeletalMesh()
{
    if (!GetMesh() || !GetMesh()->GetSkeletalMeshAsset()) return;

    TArray<FLinearColor> LinearVertexColors;
    LinearVertexColors.Init(FLinearColor(1, 1, 1, 1), NumVertices); // 흰색(보임)

    // VertexIndexMap을 활용해 잘린 부분만 색상을 변경
    for (const TPair<int32, int32>& Pair : VertexIndexMap) {
        int32 ColorChangeIndex = Pair.Key;  // 원본 Skeletal Mesh의 버텍스 인덱스
		if (ColorChangeIndex >= 0) {		//잘못된 Index 방지.
            LinearVertexColors[ColorChangeIndex] = FLinearColor(0, 0, 0, 0);  // 검은색 = 마스킹 처리
        }
    }

    // Skeletal Mesh에 버텍스 컬러 적용
    GetMesh()->SetVertexColorOverride_LinearColor(0, LinearVertexColors);
    GetMesh()->MarkRenderStateDirty(); // 렌더 상태 갱신
}

이 함수도 그렇게 어려운건 없어서 그냥 넘어간다.

 

결과는 위 사진과 같이 깔끔하게 잘린다.

 

물론 이렇게 하기 위해서 Matrial을 수정할 필요가 있다.

이 마네킹의 경우 아래와 같이 MakeMaterialAttributes노드와 VertexColor Node를 추가해주었다.

Blend Mode는 Masked이다.

전체 노드는 다음과 같다.