모델 불러올 때 마다
파일경로를 목표에 넣고 F5 스위치..
파일경로를 목표에 넣고 F5 스위치..
파일경로를 목표에 넣고 F5 스위치..
실패하면 또 코드 보고 어디 잘못했는지 확인하고 수정하고...
화나죠?
그래서 이런 UI같은 화면을 구현해서 버튼으로
파일경로를 불러오는 기능을 구현하고자 함
우선 마우스 클릭을 통해 버튼이 눌렸는지 확인하기 위해서
SystemClass의 콜백함수에서 윈도우 메시지를 처리하여 WM_KEYDOWN이나
KEYUP 메시지를 받았을 경우 InputClass의 멤버 변수를 true또는 false로 바꾸며
마우스나 키보드를 눌렀는지 확인하고 lparam의 마우스 좌표를
저장하여 다른 클래스에서 사용할 수 있게 함수 리턴으로 전달하였음
LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_KEYDOWN:
{
m_inputClass->KeyPressed(wparam);
break;
}
case WM_KEYUP:
{
m_inputClass->KeyReleased(wparam);
break;
}
case WM_MOUSEMOVE:
{
m_inputClass->SetMousePosition(LOWORD(lparam), HIWORD(lparam));
break;
}
case WM_LBUTTONDOWN:
{
m_inputClass->SetMousePosition(LOWORD(lparam), HIWORD(lparam));
m_inputClass->KeyPressed(LBUTTON);
break;
}
case WM_LBUTTONUP:
{
m_inputClass->SetMousePosition(LOWORD(lparam), HIWORD(lparam));
m_inputClass->KeyReleased(LBUTTON);
break;
}
case WM_RBUTTONDOWN:
{
m_inputClass->SetMousePosition(LOWORD(lparam), HIWORD(lparam));
m_inputClass->KeyPressed(RBUTTON);
break;
}
case WM_RBUTTONUP:
{
m_inputClass->SetMousePosition(LOWORD(lparam), HIWORD(lparam));
m_inputClass->KeyReleased(RBUTTON);
break;
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
요렇게
InputClass의 포인터 변수를 매개변수로 전달하여 다른 클래스에서도
빠르고 쉽게 어떤키가 눌렸는지 확인 가능
그럼 마우스 위치를 알아냈으니 이를 버튼을 가르키는지 판별하기 위해서
레이캐스팅을 사용하였음
레이 캐스팅(Ray Casting)은 3D 그래픽스와 게임 개발에서 중요한 기법으로, 가상의 광선을 발사하여 그 광선이 장면의 어떤 객체와 교차하는지를 결정하는 방법입니다. 이를 통해 사용자는 마우스 클릭 등의 입력을 통해 3D 공간의 객체를 선택하거나, 시점 내의 물체를 확인할 수 있음...
ButtonClass의 레이캐스팅 하는 함수
//레이캐스팅을 이용하여 마우스가 버튼위에 올라가 있는지 확인하기 위한 함수
bool ButtonClass::CollisionCheck(D3DClass* pD3DClass, CameraClass* pCameraClass, InputClass* pInputClass)
{
bool result;
XMMATRIX viewMatrix, projectionMatrix, viewProjectionMatrix;
XMMATRIX inverseViewProjection;
XMFLOAT2 mousePoint, screenSize;
XMVECTOR rayOrigin, rayDirection;
pInputClass->GetMousePosition(mousePoint);//마우스 위치를 받아옴
pD3DClass->GetScreenSize(screenSize);//프로그램 사이즈를 받아옴
mousePoint = XMFLOAT2(
((2.0f * mousePoint.x) / screenSize.x) - 1.0f,
((-2.0f * mousePoint.y) / screenSize.y) + 1.0f
);//마우스 위치를 정규화
//UI의 경우 0,0,-10 위치의 기본 뷰 매트릭스에서 렌더링 되니 간단한 연산만 하면 됨
pCameraClass->GetBaseViewMatrix(viewMatrix);//기본 시점 뷰 매트릭스
pD3DClass->GetOrthoMatrix(projectionMatrix);//직교 투영 매트릭스
viewProjectionMatrix = XMMatrixMultiply(viewMatrix, projectionMatrix);//뷰, 투영 서로 행렬 곱
inverseViewProjection = XMMatrixInverse(NULL, viewProjectionMatrix);//역행렬을 구함
rayOrigin = XMVectorSet(mousePoint.x, mousePoint.y, 0.0f, 1.0f);//레이 시작
rayDirection = XMVectorSet(mousePoint.x, mousePoint.y, 1.0f, 1.0f);//레이 끝
rayOrigin = XMVector3Transform(rayOrigin, inverseViewProjection);//뷰.투영 역행렬로 벡터 변환
rayDirection = XMVector3Transform(rayDirection, inverseViewProjection);//뷰.투영 역행렬로 벡터 변환
rayDirection = XMVectorSubtract(rayDirection, rayOrigin);//레이 끝에서 시작을 빼서 방향을 구함
rayDirection = XMVector3Normalize(rayDirection);//레이 방향을 정규화
float dist = 0.0f;//충돌 거리를 받는 변수
XMVECTOR v1, v2, v3;
//XMFLOAT3을 XMVECTOR로 변환
v1 = XMLoadFloat3(&m_vertices[0].position);
v2 = XMLoadFloat3(&m_vertices[1].position);
v3 = XMLoadFloat3(&m_vertices[2].position);
//충돌 체크 함수
result = TriangleTests::Intersects(rayOrigin, rayDirection, v1, v2, v3, dist);
if (result)
{
return true;
}
//XMFLOAT3을 XMVECTOR로 변환
v1 = XMLoadFloat3(&m_vertices[3].position);
v2 = XMLoadFloat3(&m_vertices[4].position);
v3 = XMLoadFloat3(&m_vertices[5].position);
//충돌 체크 함수
result = TriangleTests::Intersects(rayOrigin, rayDirection, v1, v2, v3, dist);
if (result)
{
return true;
}
return false;
}
간단히 말해 프로그램 위 마우스 좌표를 3D 월드 공간으로 변환하여
어느 방향을 가르키는지 레이저를 쏴서 이게 네모에 적중하는지 확인하는 기능
버튼은 삼각형 두개로 이루어진 사각형으로,
위치와 크기를 매개변수로 받아 정점으로 만들어 저장함
대충 이렇게 배치 하고 UI 렌더링 할때만 알파 블랜딩을 켰다가 끔
안그러면 사각형 나머지 부분이 검정이나 하얗게 보이게 됨
돋보기는 모델을 불러오는 버튼이고
밑에는 모델을 좌우 회전시키는,
별은 + - 로 할려고 했는데 안보여서 그냥 대충 이미지 넣고
확대 축소하는 버튼임
참고로 그림은 아무 사이트에서 구해서 포샵으로 잘라냈음
원래 직접 그려보려고 시도하다가 답답해서 그냥 지우고 롤하러 감..
희생양들 ㅠㅠ
버튼 이미지 출처
https://kr.freepik.com/free-vector/web-buttons-circle-glass-icons-game_26568787.htm#query=ui%20button&position=40&from_view=keyword&track=ais_user&uuid=ee9c7236-e7ce-4a18-9323-1c3145a9bb19
마우스가 버튼 위에 존재하며, 동시에 LBUTTON이 true일 때
버튼이 눌렸다고 리턴하고 이전 상태와 비교해서
LBUTTON이 true -> false 로 바뀌는 순간에만 버튼이 눌렸다고 리턴함
물론 버튼을 누르는 도중에 버튼 영역을 이탈하면 전부 초기상태로 되돌리기
UI는 두 종류가 있는데
디버깅 정보를 표시하는 UI 와
이번에 만든 모델 불러오는 UI
근데 클래스 이름이 ModelInspector 인 이유는
원래는 모델의 자세한 정보도 표시되게 하고 싶었는데
UI 디자인 하는 센스도 구리고 스크롤 바랑 페이지를 구현하기도 오래 걸릴거 같아
그냥 파일 불러오고 회전, 확대하는 버튼만 넣었음
돋보기 누르면 파일 경로를 가져올 수 있게 대화상자를 띄워 디렉토리 탐색
대화상자를 띄우는 부분임
void FbxLoader::OpenFileDialog(HWND hwnd)
{
OPENFILENAMEA ofn; // 공통 대화 상자 구조체
if (m_filepath)
{
delete[] m_filepath;
m_filepath = nullptr;
}
m_filepath = new char[MAX_PATH];//파일명은 전체 경로를 포함하여 최대 259 글자
ZeroMemory(&ofn, sizeof(OPENFILENAMEA));//구조체 초기화
ofn.lStructSize = sizeof(OPENFILENAMEA);//구조체 크기 설정
ofn.hwndOwner = hwnd;
ofn.lpstrFile = m_filepath; //파일 경로를 저장할 버퍼
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = "All\0*.*\0Text\0*.TXT\0";//파일 필터 설정
ofn.nFilterIndex = 1; //필터 인덱스
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;//대화 상자 플래그
// 파일 열기 대화 상자 표시
GetOpenFileNameA(&ofn);//char형으로 파일 경로를 받기 위함 wchar는 GetOpenFileNameW로
return;
}
MFC 안쓰고 하는 방법 좀 알아보는데
많은 도움을 준 채찍피티에 박수
GetOpenFileName 함수는 프로젝트 속성이 멀티바이트인지 유니코드인지 여부에 따라
char, wchar_t로 파일경로를 저장하는데
GetOpenFileNameA는 그런거 무시하고 char로,
GetOpenFileNameW는 wchar_t로 저장
결과
편집해서 로딩이 빠른 것 처럼 보이는데
실제로는 엄청 느림
vector 써서 그럴지도
그래도 메모리 해제가 쉽고 안전해서 바꾸기엔 좀 그렇다
참고로 WASD로 이동가능
깃 링크 : https://github.com/h117562/Tutorial_UI