GLUT Tutorials

보조창(subwindow)에 대해서

GLUT 를 사용해서 보조창을 여러개 만들 수 있습니다. 즉, 메인창을 각기 다른 영역으로 나누어 각 창마다 OpenGL 문맥(Context)과 콜백함수들을 설정할 수 있습니다. 보조창을 만들려면 glutCreateSubWindow 함수를 사용해야 합니다. 다음은 이 함수의 설명입니다.

int glutCreateSubWindow(int parentWindow, int x, int y, int width, int height); 

인자 설명:
parentwindow - 부모창의 아이디(구분값)입니다.
x, y - 보조창의 왼쪽 위쪽 경계의 좌표입니다. 이 좌표는 부모창의 원점에 상대적인 좌표입니다.
width, height - 보조창의 크기입니다.

부모창의 아이디 값은 부모창을 만들 때 얻을 수 있습니다. 다음에 나오는 코드는 메인창과 보조창을 만드는 예제입니다.

mainWindow = glutCreateWindow("SnowMen from 3D-Tech");
...
subWindow1 = glutCreateSubWindow(mainWindow, 10,10,100,100);

주어진 보조창이 또 다른 보조창의 부모창이 될 수 있습니다. GLUT 의 명세에 따르면 보조창을 컨테이너로 사용할 수 있다고 되어 있습니다.

위에서도 말했듯이 보조창은 자신만의 OpenGL 문맥을 갖습니다. 그래서 디스플레이 리스트를 사용하려면 디스플레이 리스트를 사용하는 모든 창과 보조창에 대해서 디스플레이 리스트를 만들어 줘야 합니다. 그리고 대부분의 콜백함수들도 마찬가지입니다.

적어도 각 창마다 렌더링을 수행하는 함수를 등록해 줘야 합니다. 등록은 glutDisplayFunc 를 사용합니다. 그리고 또, 각 창마다 커서와 마우스 이벤트를 처리하는 콜백함수들을 따로 등록해야 합니다. 그리고 여러개의 보조창을 사용하면 특정 보조창에 팝업메뉴를 연결할 수 있습니다. 그리고 주의해야할 것은 휴면(idle)함수는 단 한개만 존재한다는 것입니다.

다음 코드는 더욱 완벽하게 초기화를 수행하는 코드입니다.

int mainWindow, subWindow1,subWindow2,subWindow3;
...
int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100,100);
    glutInitWindowSize(w,h);

    mainWindow = glutCreateWindow("SnowMen from 3D-Tech");

        // 키보드 관련 설정입니다.
        glutIgnoreKeyRepeat(1);
        glutKeyboardFunc(processNormalKeys);
        glutSpecialFunc(pressKey);
        glutSpecialUpFunc(releaseKey);

        // 크기변경처리 함수입니다.
        glutReshapeFunc(changeSize);

        // 렌더링과 휴지(idle)함수입니다.
        glutDisplayFunc(renderScene);
        glutIdleFunc(renderSceneAll);

        // 이 함수가 OpenGL 문맥을 만듭니다.
        initScene();

    subWindow1 = glutCreateSubWindow(mainWindow, border,border, w-2*border, h/2 - border*3/2);

        // 각 창마다 렌더링 함수를 등록해야합니다.
        glutDisplayFunc(renderScene1);

        // OpenGL 문맥을 다시 만들어야합니다.
        initScene();

    subWindow2 = glutCreateSubWindow(mainWindow, border,(h+border)/2, w/2-border*3/2, h/2 - border*3/2);

        // 각 창마다 렌더링 함수를 등록해야합니다.
        glutDisplayFunc(renderScene2);

        // OpenGL 문맥을 다시 만들어야합니다.
        initScene();

    subWindow1 = glutCreateSubWindow(mainWindow, (w+border)/2,(h+border)/2, w/2-border*3/2,h/2 - border*3/2);

        // 각 창마다 렌더링 함수를 등록해야합니다.
        glutDisplayFunc(renderScene3);

        // OpenGL 문맥을 다시 만들어야합니다.
        initScene();

    glutMainLoop();

    return(0);
}

위의 코드는 세 개의 보조창을 만듭니다. 각각의 보조창들은 같은 장면을 서로 다른 시점으로 표현하기 위해서 사용됩니다. 제일 위에 있는 보조창은 자유롭게 움직이는 카메라를 표현하고, 아래 왼쪽에 있는 보조창은 위에서 본 것을 그리고 아래 오른쪽 보조창은 오른쪽에서 본 것을 표현합니다. 이 것을 그림으로 보면 다음과 같습니다.

메인창이든 보조창이든 창이 하나 만들어지면 그 창이 현재 선택된 창이 됩니다. 현재 창에 관련된 모든 콜백함수들이 등록됩니다. 물론 휴면(idle)함수는 다시 등록되지 않습니다. 앞에서도 말했듯이 휴면(idle)함수는 전체 어플리케이션에서 오로지 하나만 있어야합니다.

그래서 위의 어플이케이션에서 키보드 콜백함수는 오로지 메인창에만 등록되어 있습니다. 제 경험에 의하면 마이크로소프트의 윈도우즈 운영체제에서는 키보드 콜백함수를 메인창에 등록하지 않으면 키보드 이벤트를 잡지 못 합니다. 다른 리눅스나 SGI 그리고 Mac 을 사용하는 분들도 그런 경험이 있는지 궁금합니다.

위의 코드를 보면 모든 보조창들의 OpenGL 문맥을 만들려고 initScene 함수를 호출했습니다. 그러나 크기변경을 처리하는 reshape 함수는 한번만 등록했습니다. 그 이유는 한 개의 함수를 사용해서 모든 창들의 크기변경을 처리하기 때문입니다. 하지만 각각의 보조창마다 reshape 함수를 따로 등록하는 것도 가능합니다.

그리고 또 하나, 렌더링을 처리하는 콜백함수를 각각의 창을 생성할 때 꼭 등록해야합니다. 절대 렌더링 함수를 등록하는 작업과 휴면(idle)함수에 관한 내용에 대해서 실수하지마세요. 이 두 함수에 대한 내용과 차이점이 궁금하다면 앞 장에서 살펴본 " 애니메이션 " 을 참고하세요.

보조창이 더 이상 필요가 없다면 제거할 수 있습니다. 보조창을 제거하려면 glutDestroyWindow 함수를 사용합니다.

void glutDestroyWindow(int windowIdentifier) 

인자 설명:
windowIdentifier - 창을 만들 때 반환된 아이디값입니다.

이 함수는 창을 제거합니다. 이 때 파괴되는 창이 포함하는 모든 보조창들도 제거됩니다. OpenGL 문맥은 창이 제거될 때 같이 제거됩니다.