방향키를 눌렀을 때 물체를 보는 각도를 바꿔보자

2018, Jul 27    

방향키를 눌렀을 때 보는 점(시점)의 좌표를 특정 각도 만큼 회전시켜 보자.

구현하기 위해 먼저 키보드 입력 이벤트를 어떻게 처리하는지 알아보자.

클래스 위저드에서 메시지 선택을 WM_KEYDOWN을 선택하여 함수를 추가한다. 아래와 같은 함수가 추가된다.

void CVolumeRendererView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

이를 다음과 같이 방향키에서만 작동하도록 수정한다.

void CVolumeRendererView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

	switch(nChar)
    {
        case VK_LEFT: /// 왼쪽 화살표키
            printf("Left Key \n");

            printf("Left Key End \n");
            break;
        case VK_RIGHT: /// 오른쪽 화살표키
            printf("Left Key \n");

            printf("Left Key End \n");
            break;
    }
}

왼쪽키 오른쪽 키를 눌렀을 때 MIP ANY DIRECTION 함수를 호출 하게 만들어보고 잘 동작하는지 확인해보자.

void CVolumeRendererView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    CVolumeRendererDoc* pDoc = GetDocument();
    ASSERT_VALID(pDOc);
    if(!pDoc) return;

    int img_width = 256;
    int img_height = 256;

    printf("MIP AnyDirection \n");
    shared_ptr<unsigned char> image = 
        shared_ptr<unsigned char>(new unsigned char[img_width*img_height]);

    // Renderer 포인터를 가져온다.
    // Doc 클래스에 Renderer 포인터를 가져오는 함수를 만든다.
    // 이후 아래와 같이 함수를 이용해 Renderer 클래스를 가리키는 포인터를 가져온다.
    //shared_ptr<Renderer> m_pRenderer = pDoc->Getm_pRenderer();

	switch(nChar)
    {
        case VK_LEFT: /// 왼쪽 화살표키
            printf("Left Key \n");
            m_pRenderer->RenderMIPAnyDirection(image.get(), img_width, img_height);
            printf("Left Key End \n");
            break;
        case VK_RIGHT: /// 오른쪽 화살표키
            printf("Right Key \n");
            m_pRenderer->RenderMIPAnyDirection(image.get(), img_width, img_height);
            printf("Right Key End \n");
            break;
    }
}

Doc 에서 Renderer 클래스 포인터를 가져올 수 있는 함수를 만든다.

VolumeRendererDoc.cpp

shared_ptr<Renderer> VolumeRenderer::Getm_pRenderer()
{
    return m_pRenderer;
}

이제 키를 누르면 시점의 좌표를 변환시켜보자. 이를 구현하기 위해 변환행렬을 이용한다.

이는 원점을 중심으로 시점의 좌표를 반시계 방향으로 a 각도만큼 이동시키는 행렬이다.

Renderer.cpp

/// 시점을 얼마큼 회전시킬지 알수 있는 각도를 매개변수로 받는다.
bool Renderer::RenderMIPAnyDirection(unsigned char* image,
	const int img_width, const int img_height,
	float angle)
{
    ...

/// 눈좌표 rotate
	angle = angle * 3.141592f / 180.f;
	float cos_ = cosf(angle);
	float sin_ = sinf(angle);
	float x2 = center_coord.x * center_coord.x;
	float y2 = center_coord.y * center_coord.y;
	float z2 = center_coord.z * center_coord.z;
	float oneMinusCos = 1.f - cos_;

	float rotate_matrix[3][3] = {
		{ cos_, (-1)*sin_, 0.f },
		{ sin_,     cos_ , 0.f },
		{  0.f,       0.f, 1.f }
	};

    /// 눈 좌표에 위에서 선언한 회전행렬을 곱하여 시점을 회전시킨다.

	float rotate_eye[3] = { eye_coord.x, eye_coord.y, eye_coord.z };
	float res_arr[3] = { 0.f };
	for (int i = 0; i < 3; i++)
	{
		float res = 0.f;
		for (int j = 0; j < 3; j++)
		{
			res = res + rotate_eye[j] * rotate_matrix[i][j];
		}
		res_arr[i] = res;
	}

	eye_coord.x = res_arr[0]; eye_coord.y = res_arr[1]; eye_coord.z = res_arr[2];

    ...

}

이제 View 를 아래와 같이 다시 수정하고 실행하면 원점을 중심으로 시점이 회전하는 것을 볼 수 있다.

VolumeRendererView.cpp

void CVolumeRendererView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    ...
	

switch (nChar)
	{
	case VK_LEFT: // 왼쪽 화살표키 눌러짐.
		printf("Hello Left Key\n");
		total_angle -= 25.f;
		m_pRenderer->RenderMIPAnyDirection(image.get(), img_width, img_height, total_angle);

		printf("Hello Left Key end\n");
		break;
	case VK_RIGHT: // 오른쪽 화살표 키 눌러짐.
		printf("Hello Right Key\n");
		total_angle += 25.f;
		m_pRenderer->RenderMIPAnyDirection(image.get(), img_width, img_height, total_angle);

		printf("Hello Right Key end\n");
		break;
	}
    ...

}