초보 코린이의 성장 일지

UE4 Save StaticMesh 본문

언리얼

UE4 Save StaticMesh

코오린이 2023. 4. 13. 16:40

기본적인 이론을 알고 보는게 더 빠른 이해를 도울 수 있으므로 설명부터 시작해 보겠다.

1. 기본적인 에디터창이 보인다. 하지만 좌우 어디에도 카테고리 및 창이 존재하지 않는다.

2. 원래는 존재 했으나 하나씩 지울수가 있는데, 이 본 화면에 에디터창만은 에디터를 종료하지 않는다 끌 방법이 없다.

3. 메인 프레임이라는 화면상에 하나로 올라가있으며, 나머지 카테고리 및 창들이 주변에 붙어서 생성되어 화면에 보이는 것이다.

4. API나 DX에도 나오듯 View 영역이 메인 프레임을 나타낸다고 생각하면 이해하기 쉽다.

5. 메인 프레임을 반환하는 방식으로 Handle을 구해서 코드를 작성해볼 것이다.

 

1. | 기호에 보면 왼쪽, 오른쪽 구간으로 나눠서 사용하는데 의미가 다르다. 왼쪽이 나타나는 설명, 오른쪽이 실제 확장자를 나타낸다.

2. *를 사용하는것은 모든 바이너리 파일을 보여달라는 의미이다. (와일드카드 문자)

 

더보기
#include "StaticMesh_Detail.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "Misc/MessageDialog.h"
#include "Serialization/BufferArchive.h" // 버퍼를 통해 내보낼 것
#include "DesktopPlatformModule.h"
#include "Interfaces/IMainFrameModule.h"

#include "CStaticMesh.h"

FReply FStaticMesh_Detail::OnClicked_SaveMesh()
{

		// 핸들 구하기
		IMainFrameModule& mainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
		// GetNativeWindow() 부모의 윈도우창에 접근해서 창과 알맞은 윈도우 운영체제를 return
		// 각 운영체제마다 몇 byte일지 모르기 때문에 즉 크기를 모를때는 void*로 받는다.
		void* handle =  mainFrame.GetParentWindow()->GetNativeWindow()->GetOSWindowHandle();

		// 창을 열였을때 그 이름 
		FString title = meshActors[i]->GetName();
		// 파일을 열였을때 경로
		FString path;
		FPaths::GetPath(path); // 이전에 열였던 경로

		// 선택된 파일
		TArray<FString> fileNames;

		// 윈도우 프로그램에 대한 하나의 API 창이 있을것이다. 
		IDesktopPlatform* platform = FDesktopPlatformModule::Get(); // 윈도우, 리눅스 등으로 선택되는 구간
		// 선택된 파일을 읽어서 가져오고 저장해준다.
		platform->SaveFileDialog(handle, title, path, title + ".bin", "Mash Binary File(*.bin)|*.bin", EFileDialogFlags::None, fileNames);
		if (fileNames.Num() < 1) continue;
        
        for (const FString& name : fileNames)
			GLog->Log(name);
}

1. 운영체제를 찾아주고, 선택된 파일을 찾아준다.

2. 그 정보를 바탕으로 읽고 가져와 저장시켜준다.

3. 그밖에 경로 및 사용하기 편하도록 만들어 준다.

 

1. 이제 Static 객체를 눌러서 SaveMesh를 클릭해본다.

2. 그럼 파일을 저장하게 폴더창이 열리게되는데 그때 객체 유형과 설정해놓은 경로를 바탕으로 자동으로 입력해서 들어오게 되는걸 확인 할 수 있다.

3. 로그에서도 저장버튼을 누르게되면 저장경로가 나오는 것을 확인할 수 있다.

 

더보기
#include "StaticMesh_Detail.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "Misc/MessageDialog.h"
#include "Serialization/BufferArchive.h" // 버퍼를 통해 내보낼 것
#include "DesktopPlatformModule.h"
#include "Interfaces/IMainFrameModule.h"

#include "CStaticMesh.h"

FReply FStaticMesh_Detail::OnClicked_SaveMesh()
{
		// 만들어 놓은 Archive 활용
		FBufferArchive buffer;
		buffer << data;

		FFileHelper::SaveArrayToFile(buffer, *fileNames[0]); // 0번으로 첫번째 선택한 파일로 지정
		buffer.FlushCache(); // 나가라는 명령
		buffer.Empty(); // 버퍼 비워주기

		// 실제로 사용자에게 내보내거나, 실행할 데이터는 바이너리 형식으로 되어있지만
		// 개발팀이 내부적으로 확인하거나 혹은 수정하게 될떄 바로바로 변경이 가능하므로 Text를 사용해 보겠다.
		FString text;
		for (int32 index = 0; index < data.Positions.Num(); index++)
		{
			// 문자열 연결
			text.Append(data.Positions[index].ToString() + ", ");
			text.Append(data.Normals[index].ToString() + ", ");
			text.Append(data.Uvs[index].ToString() + ", ");
			text.Append(data.Colors[index].ToString() + "\r\n"); // \r\n 줄 끝이 아니라면 끝으로 보내줘라
		}

		// GetBaseFilename() 파일 이름만 return
		FString textFileName = FPaths::GetBaseFilename(fileNames[0], false);
		FString textSaveName = textFileName + ".csv";

		FFileHelper::SaveStringToFile(text, *textSaveName);

		// 이름만 보여주도록 경로 제거.
		FString str;
		str.Append(FPaths::GetBaseFilename(fileNames[0]));
		str.Append(" Saved.");

		GLog->Log(str);
}

1. 파일을 저장할때 저장된 경로를 제거한 이름만 보이도록 설정.

 

 I/O(SSD)에서 SaveArrayToFile을 타고 RAM로 들어가게 된다. 그렇게 되면 순차적으로 나간 파일이 메모리에 기록된다.

만일 4byte, 4byte로 나눠놓은 공간에 5byte 파일이 들어오면 3byte 공간이 남게된다. 하지만 파일은 기록을 하게되면서 나머지 3byte가 쓰레기 값으로 남게된다. 이러면 오류가 발생하거나 깨지는 현상이 발생할 수 있으므로, FlushCache를 사용하여 누적되어 있는 값을 풀어줘야 한다.

 

 

1. SaveMesh 버튼을 눌러서 저장을 해보면, 출력 로그에도 Save 했다고 문구가 나오면서 경로가 보인다.

2. 저장한 파일 경로되로 생성되는걸 볼 수 있다.

 

1. 디스크 할당 크기와 달리 크기를 보면 3KB가 덜 들어온 것을 볼 수 있다. 모든 메모리에서는 단편화가 발생할 수 있다는걸 알고 있어야한다. 이걸 디스크 단편화라고 부른다.

2. 디스크 단편화는 운영체제는 2의 배수로 올라가기 때문에 168이 나오게된다. 하지만 들어가는 메모리는 165로 3만큼이 빠지게된다. 그래서 3만큼의 디스크 단편화가 발생한 것이다.

3. 또 내부 단편화라고도 불린다. 또한 가상 메모리에서도 발생하고 RAM에서도 발생한다.

4. 그 중 RAM 메모리 단편화가 일어나는 이유는 만일 12byte를 할당한 공간을 삭제하고, 4byte, 4byte, 6byte를 넣게 되면 공간이 부족하므로 마지막 6byte는 들어오지 못한다. 그럼 단편화가 일어난다.

5. CPU화 된 메모리는 단편화가 일어나지 않는다 그 이유는 최소단위이기 때문

6. Cache Memory의 역할은 RAM은 느리고, SSD는 빠르기 때문에 중간 단계에서 그 속도차이를 극복 시켜주는 역할을 한다. Cache 단편화도 일어날 수 있다.

7. 우리가 더 큰 메모리를 사용할때 가상 메모리를 사용하게 된다. 여기서 데이터를 전달할때 RAM과 SSD와에 오고가는 과정속에서 가상메모리 구역에서 단편화가 일어난다.

8. CPU가 동작할때 Cache가 중요한 역할을 하게된다 이걸 "로컬리티(지역성)" 라고 부른다 이건 더 자세히 알아봐야겠다.

9. 지역성중에는 시간적 지역성, 공간적 지역성, 순차적 지역성이있다. 프로그래머가 여러번 참조될거같은 변수가 있다고 생각하여 임의적으로 공간을 만들어 준다고 생각하면 된다.

 

시간적 지역성 - 가장 최근에 참조되었던 변수가 다시 참조될거란 가능성.

공간적 지역성 - 밀접한 공간에 메모리는 다시 참조될 가능성이 높다.

순차적 지역성 - 배열을 나타낸다. 


더보기
#pragma once

#include "CoreMinimal.h"
#include "Framework/Commands/Commands.h"

/*
클래스 템플릿을 TCommands<FButtonCommand> 이렇게 상속을 받는 이유
만일 class A에 Static 변수가 존재할때, class B가 A를 상속받고 있다고 가정하면,
지역변수인 Static는 A의 공용 구역안에 들어가있게된다. 지역변수인 변수를 B에서는 사용할 수 가 없게된다.
각 A, B는 영역이 따로 존재하기 때문
class B : A<> 이렇게 문법을 사용하게 된다면 자식이 부모의 공용 영역을 접근하여 사용할 수 있게된다.
 */
class EXAMPLE_API FButtonCommand
	: public TCommands<FButtonCommand> // 부모의 공용 영역
{
public:
	FButtonCommand();
	~FButtonCommand();

public:
	void RegisterCommands() override;

public:
	// 커멘드 리스트
	TSharedPtr<FUICommandList> Command;

public:
	// Mesh를 부르는 커맨드
	TSharedPtr<FUICommandInfo> LoadMesh;
};
#include "ButtonCommand.h"

FButtonCommand::FButtonCommand()
	: TCommands("ToolBar_Buttons", FText(), NAME_None, FEditorStyle::GetStyleSetName())
{
	Command = MakeShareable(new FUICommandList());
}

FButtonCommand::~FButtonCommand()
{
	if (Command.IsValid())
		Command.Reset();
}

void FButtonCommand::RegisterCommands() // 추상 클래스이므로, 부모 호출 x
{
	// UI커멘드 매크로, 무조건 정의되어있는 방법으로 사용해야한다.
#define LOCTEXT_NAMESPACE ""
	// "LoadMesh"은 명령에 이름, ""은 설명부분, 이 명령이 어떤것에 연결될 것인가 버튼으로 설정.
	UI_COMMAND(LoadMesh, "LoadMesh", "", EUserInterfaceActionType::Button)
#undef LOCTEXT_NAMESPACE // 끝냈다는 의미
}

1. 에디터상 상단위에있는 툴바를 만들어 보도록 할 것이다.

2. UI_COMMAND를 사요하려면, 어디에 속해있는 기능인지 파악부터 해야한다.

3. 타고 들어가보면, LOC_DEFINE_REGION 매크로 안에 정의되어 있는걸 알 수 있다.

4. 이렇게 어떠한 구역안에 정해져있는 무언가를 사용하려면, 동일하게 작성을 해주고 그 안에서 작성해줘야한다.

 

 

https://www.youtube.com/watch?v=xemPTVWUVNY 

 

'언리얼' 카테고리의 다른 글

UE4 Button Command, Icon  (0) 2023.04.25
UE4 Button Command  (0) 2023.04.24
UE4 StaticMesh  (0) 2023.04.12
UE4 StaticMesh  (0) 2023.04.11
UE4 StaticMesh  (0) 2023.04.06
Comments