ExampleFrameListener는 ExampleApplication 클래스와 마찬가지로 Ogre에서 기본적으로 제공하는 Framework 클래스 중 하나이다. KeyListener와 FrameListener로부터 다중상속을 받는 구조이며 기본적으로 갖는 책임 중에 가장 중요한 것은 마우스나 키보드로부터 입력을 받아 처리하고 Frame을 렌더링하데는 필요한 상태정보를 갱신하고, 렌더링을 계속할 것인지의 여부의 결정이다. 아래는 ExampleFrameListener 클래스 관계도이다.
또한 Overlay 클래스를 이용하여 FPS 정보 등을 나타내준다. 사용자가 마우스나 키보드의 입력을 buffer 방식인지, unbuffered 방식인지의 결정에 따라 buffered 방식인 경우, 사용자 입력에 대한 처리를 Event 방식(Callback 방식)으로 하기 위해 EventProcessor 클래스의 인스턴스를 생성해준다.
unbuffered 방식인 경우에는 InputReader 클래스의 인스턴스를 생성해서 사용자가 필요한 시점에서 InputReader의 인스턴스를 이용해서 키보드나 마우스의 상태를 Query할 수 있다. buffer 방식인 경우에도 InputerReader 인스턴스를 사용할 수 있다는 점도 알아 두길 바란다.
기본적으로 ExampleFrameListener 클래스는 unbuffered 방식의 경우에 대해 마우스와 키보드 입력에 대한 처리를 해 놓았다. 예를 들어 커서키를 누르면 화면이 이동한다든지, 마우스를 이용해 확대나 이동을 한다든지 말이다. 하지만 사용자가 원할 경우 이러한 처리를 재정의해서 쉽게 변경할 수 있다.
buffered 방식인지, unbuffered 방식인지의 결정은 ExampleFrameListener 클래스 생성자의 인자를 통해 결정한다.
사용자 입력 이외에 ExampleFrameListener 클래스에서 중요한 것은 FrameListener 클래스의 상속에 있다. FrameListener 클래스는 단지 frameStarted와 frameEnd 가상함수만을 가지고 있다. frameStarted는 하나의 frame을 렌더링 하기 직전에 호출하고 frameEnd는 하나의 frame에 대한 렌더링이 끝났을때 호출된다. 두개의 함수 모두 bool을 반환하는데, false일 경우 다음 frame을 렌더링하지 않는다. 즉, 계속 렌더링하려면 true를 반환해야 한다.
위의 말대로라면 Ogre3D의 main loop인 Root::startRendering 함수의 내용은 다음과 같을 것이고, 실제로 같다.
- Root 객체는 자신에 등록된 모든 FrameListener 객체의 frameStarted 함수를 호출
- Root 객체는 하나의 frame을 렌더링 함
- Root 객체는 자신에 등록된 모든 FrameListener 객체의 frameEnd 함수를 호출
입력이 unbuffered 방식일때 frameStarted나 frameEnd 함수안에서 InputReader 인스턴스를 이용해 키보드나 마우스의 상태를 Query 해서 회전상태나, 이동상태값들을 변경해서 물체를 회전시키고 이동시킬 수 있다. 클래스 관계도에서 ExampleFrameListener 클래스가 Camera 클래스와 관계를 맺고 있는 이유가 여기에 있는데, 입력에 따라 상태정보를 변경하고 다시 이 상태정보를 통해 Camera를 제어하기 위해서이다. RenderWindow와 연관을 맺는 이유는 다음과 같다.
- FPS를 얻기
- 개발을 위한 Debug 기능을 이용
- 화면을 캡쳐화여 png 파일로 저장
마지막으로, 여러분들이 ExampleFrameListener 클래스의 소스를 열어놓고 기본적으로 입력에 대해서 구현해 놓은 것이 무엇인지를 살펴보길 바란다. 이 글을 읽은 후에 소스를 살펴보면 조금이라도 이해가 수월할 것이다.