- 음악과 나 -/『 짬 통 』

framework( Minpress.com )

noon2dy 2006. 10. 31. 15:13

 

DirectX SDK framework를 이용한 프로그래밍


1. 프레임 워크
■ 프레임워크란 ?
프레임 워크란 일종의 프로그램의 흐름과 골격을 형성하는 클래스를 말한다.
즉, 프레임 워크를 제공하는 클래스는 프로그램의 전체적인 구조와 흐름을 확정 지으며, 프로그래머가 정의해야할 함수의 이름까지도 확정짓는다.
이와같은 프로그래밍 방식은 C++ 기능을 충분히 살리면서, 많은 효율성과 편 의성을 제공하고 있다.
프로그래머가 프레임워크를 이용하면, 이미 안정화와 통일된 구조를 사용하 게되며, 전체적인 흐름에 신경쓰지 않고, 프레임워크에서 제공을 해주는 인 터페이스(공통 함수)를 이용하여서 부분적인 프로그래밍을 하게 된다.
이로써 프로그래머는 설계에 대한 부담을 최소화 할수 있어서, 기능적인 부 분에 더욱 신경을 써서 프로그래밍을 하게 된다.
DirectX SDK에서는 CD3DApplication이라는 클래스가 이에 해당 되며, 3D의 기본적인 Direct3D의 셋팅과 종료 부분 및 갱신 부분을 이미 클래스의 인터 페이스로써 제공하고 있다. 이 부분은 C++의 가상함수라는 개념이 도입이되 어 있으며, C++의 이와 같은 기능을 이용하여 재정의(overriding)하여 사용 하게 된다. 물론 이와 같은 클래스를 사용하기 위해서는 C++의 기본 개념들 을 충분히 이해하고 있어야 하며, 그래야 제대로된 프레임워크를 제작 및 사 용하게 된다.

■ 프레임워크를 이해하기 위한 C++ 개념
① 추상 클래스와 순수 가상함수의 개념
② 상속 개념과 Overriding, Overloading 개념

프레임워크를 이해하기 위에서 제시한 C++ 개념정도는 이해하고 있어야 제 대로된 프레임워크를 만들 수 있으며, 또한 활용을 할수 있다.

■ 프레임워크의 함수명 정의시에 고려해야할 부분
프레임워크는 공통 인터페이스를 제공하는 것이 목적이며, 이와같은 함수를 오버라이딩을 하여서 즉, 상속을 통한 재정의를 통해서, 세부적인 기능을 정의하도록 하는 것이 목적이다. 그러기에 프레임워크를 제작함에 있어서 함 수의 기능을 직관적으로 이해하도록 만드는 것은 프로그래밍을 팀 단위로 작업을 할때 상당한 효율성을 가져온다.
이에 프레임워크에서 자주사용하는 함수명을 보면서 이와 같은 함수명 정의 를 다시한번 고려해 보도록 하자.

① Render()
이 함수명은 주로 화면에 이미지를 그리는 부분에 이와 같은 함수명을 사용 한다. 즉 이함수안에서는 이미지를 화면에 그리는 기능이 들어가게 된다.

② xxx Init xxxx()
Init라는 함수명은 거의가 초기화에 쓰이는 함수명이다.

③ Invalidatexxxx(), Deletexxx()
Invalidate나 Delete라는 함수명은 현재에 쓰고 있는 자원을 해제하는 부분에 많이 쓰이는 함수명중 하나이다.
뒤의 xxx 부분에는 해제가 되는 부분 객체의 간단한 이름을 정의하도록 하고 있다.

④ Update() 또는 FrameMove()
이 함수는 매프레임 마다 갱신되어야하는 데이타에 대한 사항들이 정의 되도록 하고 있다.

보다 더욱 많은 함수명이 있을수 있지만 가장 많이 쓰이는 함수명을 소개하 였고, 이런 부분은 모든 프로그래머가 직관적으로 함수의 기능을 파악하고 어떤 코드를 넣어야하는지를 알수 있게 함으로써 효율성이 증대되도록 하고 있음을 예상할수 있다.

■ DirectX SDK에서 제공하는 framework
DirectX SDK에서는 2D 프로그래밍을 하고자하는 프로그래머에게 특히 과 거의 DirectDraw를 이용하여서 2D 프로그래밍을 원하는 프로그래머에게는 ddutil.cpp 파일로 CDisplay class 와 CSurface class를 제공하고 있으며, 그 외의 3D의 가속기능을 이용하여서 2D, 3D 프로그램을 만들고자하는 프 로그래머를 위해서를 d3dapp.cpp 파일에 들어 있는 CD3DApplication class 를 들수 있다.
참고적으로 CD3DApplication Class는 프로그래머가 코딩을 해야하는 가상함 수가 정해져 있는 반면, CDisplay 클래스등은 그런 부분이 들어 있지 않기 에 이 클래스를 한번더 상속해서 나만의 프레임워크를 제작하여야 한다.
그러기에 C++의 개념과 기능을 충분히 이해하고 있어야 하며, CD3DApplication를 이해하기 위해서도 반듯이 필요 하다.

※ 이와같은 프레임워크를 설명하는 이유는 위의 프레임워크 지식을 기반으로 우리의 최종 목표인 DirectX를 Visual C++ 6.0의 MFC에 붙여서 게임 및 툴을 제작하기 위함이다. 그래서 게임에 필요한 각종 데이터가 MFC의 컨트 롤을 통해서 입력되며, 우리는 DirectX에서 제공하는 화면을 통해서 실시간 렌더링 화면을 보게 될것이다.
이와 같은 작업을 보다 쉽게 빠르게 하기 위해서 이와 같은 프레임워크를 사 용하는 것이다.

2. DirectX를 이용한 화면 분할 윈도우 프로그래밍
■ 최종 출력 윈도우 화면



우리가 얻고자하는 형태는 위와 같은 형태가 된다.
한쪽은 DirectX화면이 보여지며, 한쪽은 Form View로써 여기에 많은 컨트롤을
올려 놓고서 각 데이터가 어떤 식으로 적용되는지를 DirectX 화면을보면서
확인을 할수 있다. 이와 같은 형태는 우리가 게임 제작 툴을 만들때가장 많이
쓰는 형태이다.

■ MFC에서 View를 Call하는 순서
App --> MainFrame --> View
MainFrame에서 모든 뷰를 생성한다.
고로 위와 같은 분할 윈도우는 MainFrame의 onCreateClient()라는 함수에서
정의하여서 MainFrame의 자식인 View를 등록해주어 뷰가 생성되게 된다.

즉 아래와 같은 개념이 만들어진다.



■ MainFrame에 두개의 뷰를 설정하는 방법
제일 먼저해야할 것은 프로젝트 파일을 생성하는 옵션에서 이와 같은 분할
윈도우 화면을 위한 옵션을 설정해주는 것이 중요하다. 물론 꼭 여기서가
아니라도 상관은 없으나 보다 손쉬운 방법이라고 할수 있다.

① 프로젝트 생성시에 설정



위와 같이 하면 CSplitterWnd m_wndSplitter; 라는 코드가 자동으로 생성되며,
OnCreateClient(LPCREATESTRUCT /*lpcs*/,CCreateContext* pContext) 라는 함수가
자동으로 생성이된다. 우리가 화면을 나누고자 할경우에는 CSplitterWnd을이용하여서
화면을 나누게된다.


② FormView 생성과 App Class의 설정
일단 기존의 뷰는 렌더링을 걸기 위한 뷰로 설정을 하기에 각 데이터
입력을 위한 컨트롤을 올려 놓기 위한 뷰를 생성하면 되며, 이때의 뷰가
Form View가 된다.
ClassWizard를 이용하여서 Form View를 생성하게 되면 App class에
자신의 뷰를 등록하는 코드가 자동으로 생성된다. 이 코드로 인하여서
실행시에 어느 뷰를 선택할것인가하는 아래와 같은 메시지가 뜨게 된다.
왜냐하면 우린 Single View를 쓴다하고 프로젝트를 생성하였기에 두개의
뷰중에서 선택을 하도록 자동으로 화면에 뜨게 된다.




이와 같은 창이 뜨는 것을 막아주기 위해선 App Class에 보면 Form View의
윈도우를 등록하는 부분이 있을 것이다. 이 부분을 과감히 삭제하거나,
주석처리를 하면 된다.


코드는 아래와 같다.

BOOL CD3DMFCFrameWorkApp::InitInstance()
{

//Note: 메뉴를 위해서 폼뷰를 삽입하면 이 부분이 자동으로 생긴다.
// 메인 프레이에 두개의 뷰를 붙이기 위해선 이와 같이 템풀리트에
// 등록되는 것을 제거해야 한다.
/*{ // BLOCK: doc template registration
// Register the document template. Document templates serve
// as the connection between documents, frame windows and views.
// Attach this form to another document or frame window by changing
// the document or frame class in the constructor below.
CSingleDocTemplate* pNewDocTemplate = new CSingleDocTemplate(
      IDR_MAINMENUFORMVIEW_TMPL,
      RUNTIME_CLASS(CD3DMFCFrameWorkDoc), // document class
      RUNTIME_CLASS(CMainFrame), // frame class
RUNTIME_CLASS(CMainMenuFormView)); // view class
AddDocTemplate(pNewDocTemplate);
}
*/
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
      IDR_MAINFRAME,
      RUNTIME_CLASS(CDDMFCFrameWorkDoc),
      RUNTIME_CLASS(CMainFrame), // main SDI frame window
      RUNTIME_CLASS(CDDMFCFrameWorkView));
AddDocTemplate(pDocTemplate);

// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
      return FALSE;

// The one and only window has been initialized, so show and update it.
// srand( GetTickCount() );

m_pMainWnd->SetWindowText("2D툴 기본틀");
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
m_pMainWnd->UpdateWindow();
return TRUE;
}


이와 같이 될것이다. 그러면 위와 같은 윈도우 창이 뜨는 문제는 해결이 될것이다.

③ Main Frame의 onCreateClient의 설정

      BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
CRect rect;
GetClientRect( &rect );
if( !m_wndSplitter.CreateStatic(this, 1, 2 ) )
return FALSE;
int size_x = rect.right - rect.left;
int menu_size = size_x/8;
if( !m_wndSplitter.CreateView( 0, 0,
      RUNTIME_CLASS(CD3DMFCFrameWorkView),
      CSize(size_x-menu_size,0), pContext ))
return FALSE;

if( !m_wndSplitter.CreateView( 0,1, RUNTIME_CLASS(CMainMenuFormView),
      CSize(menu_size, 0), pContext))
      return FALSE;
ShowWindow(SW_SHOWMAXIMIZED);
g_pView = (CD3DMFCFrameWorkView *)m_wndSplitter.GetPane(0,0);
SetActiveView( (CView *)m_wndSplitter.GetPane(0,0) );
return TRUE;
}

④ 각 View의 생성과 체크
각 뷰의 생성은 MainFrame에 의해서 생성이 된다.
생성이되고 나면 모든 제어권은 이제 View에게로 넘아가게 된다.
렌더링 부분은 App의 onIdle()에서 이루어지며, App Class에서도
렌더링 뷰에 대한 포인터를 이용하여서 생성이 되어 DirectX가 셋되었을
경우에만 렌더링이 되도록 코딩을 해주어야 한다.
그러기에 뷰에 대한 포인터를 이와같이 얻을수 있다.
App Class에선 전역 변수로 포인터 선언을 해준다.

CD3DMFCFrameWorkView g_pView = NULL;

이와같이 하고 MainFrame에서는

extern CD3DMFCFrameWorkView g_pView ;

이와 같이 선언하고 생성자에서 자신의 포인터를 셋하도록 하면 된다.

CDDMFCFrameWorkView::CDDMFCFrameWorkView()
{
// TODO: add construction code here
g_pView = this;

}

즉 Main Frame에서 View 생성을 않하는 이상 g_pView는 계속 NULL 일것이다.
이제 App Class의 onIdle()에서는

      BOOL CD3DMFCFrameWorkApp::OnIdle(LONG lCount)
{
// TODO: Add your specialized code here and/or call the base class
      if( g_pView == NULL || m_pMainWnd->IsIconic() )
      return FALSE;
      if( g_pView ->IsReady() )
      {
      g_pView->Render2DEnvironment();
      }
      return TRUE;
}

이와같이 하면 깔끔하게 모든 처리가 끝나게 된다.

3. 2D 게임 프로그래밍을 위한 framework 제작
■ DirectDraw framewark class 파일
ddutil.cpp ddutil.h dxutil.cpp dxutil.h

■ DirectDraw framework class인 CDDApplication 클래스 제작시의 고려사항
프레임워크를 만드는 이유는 앞에서도 설명 했듯이 반복되는 초기화와 예외
적인 하드웨어에 관한 처리 사항에 신경을 쓰지 않고, 프로그래밍을 하기 위함이다.
이로인해 우리는 게임의 로직에만 집중 할수 있다.
지금 우리가 제작하는 클래스가 프레인워크가 되기 위해서는 이러한 반복되는
공통적인 처리를 먼저 생각해야하고, 이 프레임 워크를 사용할때에는 프레
임워크에서 지정한 함수에서만 프로그래밍을 할수 있도록 하는 것이다.
그래서 이러한 구분을 먼저 해주는 것이 중요하다.

- 셋팅 함수 역할
① DirectDraw, BackBuffer, FrontBuffer, Clipper 초기화
② DirectDraw, BackBuffer, FrontBuffer 해제
③ 윈도우 크기가 바뀌었을 경우의 BackBufer 재 생성
④ 렌더링 준비와 페이지 플리핑 및 메모리 Lost시의 재로딩

- 게임 렌더링 관련 함수 역할
① 사용자 Surface 생성 및 Restore
② 사용자 Surface 해제
③ 렌더링
④ 프레임마다 데이터 업데이트

이와 같이 구분을 하면 최하위클래스에서 정의 되어야 할 가상함수와 상위
클래스인 CDDApplication에서 정의되어야할 부분을 구분할수 있다.
특히 기본적인 셋팅에 관한 사항은 전부 CDDApplication에서 이루어짐을 알 수
있다. 즉, 고정적인, 반복적인 부분은 전부 CDDApplication에서 정의되어지는
것이고, 그 외 사용자 측에서 프로그래밍을 개별적으로 해주어야하는 가변적인
부분인 렌더링과 데이터 업데이트, Surface의 생성부분은 최하위 클래스에서
가상함수로써 실행이 되도록 해야 할것이다.
사용자는 게임의 흐름에는 상관없이 지정한 가상함수에 해당하는 기능을
프로그래밍 해주면 되는 것이다. 이와같은 구조는 다른 프로그램을 짜더라도
항상 동일하게 적용이 되기에 잘만든 CDDApplication만 가지고 있으면 모든
게임 프로그래밍을 매번 초기화 프로그래밍을 해줄 필요없이 바로 코드의 작성이
가능해지는 것이다.

■ CDDApplication framework와 MFC와의 연결 구조도




■ DirectDraw framewark classes인 CDisplay 기초 사항
CDisplay라는 함수는 DirectDraw의 7.0 버전으로 셋하여 준다.
DirectX SDK 8.0 버전부터는 DirectDraw의 버전에 대한 버전업을 지원 하지
않고, Direct3D를 이용하여서 2D를 구현을 하도록 하고 있다. 그래서 3D의
가속기능과 3D의 기능을 이용하여서 보다 화려한 2D를 표현하도록 유도하고
있다. 실제로 DirectX SDK 9.0버젼에서도 DirectDraw에 대한 버전업은
이루어지고 있지 않다.
이와같은 Direct3D를 이용한 2D 표현은 3D에 대한 기초가 바탕이 되어야만
가능하기에 많은 시간이 걸리게 된다.
비록 DirectDraw 7.0으로 구현을 하지만 성능면에서는 결코 떨어지지 않으며,
보다 빠르게 DirectX에 접할수 있게 해주어, DirectX를 이용한 프로그래밍
감각을 가지도록 하는데는 좋은 도구라고 생각 하며, CDisplay의 각 부분들을
살펴 보도록 하자.

① CDisplay의 기능
이 클래스는 DirectDraw에 대한 셋과 BackBuffer(후위면)과 FrontBuffer
(전위면)의 설정과 해상도 설정등을 셋팅하여 준다.
우리가 여기에서 중점적으로 이용을 해야하는 부분은 위와 같은 부분의
셋팅을 CDisplay를 이용하자는 것이다.

② CDisplay의 문제점
CDispaly는 framework라기 보다는 일종의 셋팅에 대한 사항을 감싸고 있는
일반적인 클래스에 불과하다. 왜냐하면 우리가 일반적으로 FrameWork
라고 하면 이와같은 설정은 물론이거니와 프로그램의 흐름을 유도해주는
부분이 있어야 한다.
하지만 이 CDispaly라는 클래스는 이와 같은 부분이 전혀 없다.
그래서 지금 우리가 해야하는 것은 이와 같은 부분을 만들어 주는 것이다.

③ CDisplay의 멤버 함수 분석
CDisplay클래스의 인터페이스는 22개의 함수로 이루어져 있다.
멤버함수는 기능에 따라서 ddutil.cpp 파일에 주석과 함께 설명이 되어져있다.
일단 22개의 함수를 기능에 따라 분류하면 아래와 같인 나뉘어진다.


- Access functions
: CDisplay에 설정되어 있는 정보를 알고자할 때 사용이되며, 주로
Get??() 함수가 대부분이다.

HWND GetHWnd()
LPDIRECTDRAW7 GetDirectDraw()
LPDIRECTDRAWSURFACE7 GetFrontBuffer()
LPDIRECTDRAWSURFACE7 GetBackBuffer()
LPDIRECTDRAWSURFACE7 GetBackBufferLeft()

- Status functions
: CDisplay에 설정되어 있는 상태 정보를 얻기 위함이다. 현재의 모드가
윈도우 모드인지 풀모드인지를 알기 위해서 주로 사용하다.

BOOL IsWindowed()
BOOL IsStereo()

- Creation/destruction methods
: DirectDarw의 설정, Back Buffer와 Front Buffer의 설정

HRESULT CreateFullScreenDisplay( HWND hWnd, DWORD dwWidth,
           DWORD dwHeight, DWORD dwBPP );
HRESULT CreateWindowedDisplay( HWND hWnd, DWORD dwWidth,
           DWORD dwHeight );
HRESULT InitClipper();
HRESULT UpdateBounds();
virtual HRESULT DestroyObjects();

- Methods to create child objects
: Surface의 생성과 이미지 로딩

HRESULT CreateSurface( CSurface** ppSurface, DWORD dwWidth,
           DWORD dwHeight );
HRESULT CreateSurfaceFromBitmap( CSurface** ppSurface, TCHAR* strBMP,
           DWORD dwDesiredWidth, DWORD dwDesiredHeight );
HRESULT CreateSurfaceFromText( CSurface** ppSurface, HFONT hFont,
           TCHAR* strText, COLORREF crBackground, COLORREF crForeground );
HRESULT CreatePaletteFromBitmap( LPDIRECTDRAWPALETTE* ppPalette,
const TCHAR* strBMP );

- Display methods
: 화면에 보여주기 위한 처리 부분 으로써 페이지 플리핑, 백버퍼 지기,
Blt부분등이 지원 된다.

HRESULT Clear( DWORD dwColor = 0L );
HRESULT ColorKeyBlt( DWORD x, DWORD y, LPDIRECTDRAWSURFACE7
           pdds, RECT* prc = NULL );
HRESULT Blt( DWORD x, DWORD y, LPDIRECTDRAWSURFACE7 pdds,
           RECT* prc=NULL, DWORD dwFlags=0 );
HRESULT Blt( DWORD x, DWORD y, CSurface* pSurface, RECT* prc=NULL );
HRESULT ShowBitmap( HBITMAP hbm, LPDIRECTDRAWPALETTE
           pPalette=NULL );
HRESULT SetPalette( LPDIRECTDRAWPALETTE pPalette );
HRESULT Present();

 

■ DirectDraw framework class인 CDisplay를 이용한 framework 제작
일반적인 클래스가 프레임워크가 되기 위해서는 프로그램의 흐름을 진행할수
있는 클래스여야만 한다. 즉 이 클래스를 상속해서 재정의 부분만 하위클래스
에서 정의해주면 전체 프로그램이 동작할수 있도록 하여야 한다.
일단 이와 같은 일을 해주는 프레임워크의 이름을 CDDApplication class 라고
정하고 시작 하도록 하자.

① CDDApplication 헤더 분석

class CDDApplication : public CDisplay
{
protected:
     BOOL m_bReady; // Note: DirectDraw가 생성이 되었는지 체크하는 부분
     char m_strFrameStats[90]; // Note: FPS를 문자열로 보관하는 부분
float m_fFPS; // Note: FPS를 위한 부분

public:
     BOOL IsReady();
     HRESULT Render2DEnvironment(); // Note: 최종 상속에서 호출하는 함수
     void InitDeviceObject(); // Note: 최종 상속에서 호출하는 함수
     HRESULT Resize2DEnvironment(); // Note: 윈도우의 크기가 바뀌었을 때의 호출
                함수
     // Note: 스프라이트에 관한 함수 부분
     virtual HRESULT FrameMove() { return S_OK; } // Note: 매 프레임마다 데이터
                업데이트
     virtual HRESULT Render() { return S_OK; } // Note: 렌더링 부분
     virtual void InitAllSurface() {}; // Note: 스프라이트에 대한 초기화 부분
     virtual HRESULT RestoreAllSurfaces() { return S_OK; }
      // Note: 스프라이트 Surface Restore.
     virtual void DeleteAllSurface() {}; // Note: 스프라이트 Surface Release.
public:
     CDDApplication();
     virtual ~CDDApplication();
};

위의 헤더는 우리가 제작을 하게되는 CDDApplication class이다.
헤더를 보면 그 클래스의 전체적인 멤버의 구성을 볼수 있기에 이 헤더를
보면서 원리를 분석해보도록 하자.
일단 CDisplay를 상속 하였다. CDisplay 클래스는 DirectDraw를 셋팅하는
클래스이며 전체에 영향을 미치고, 프로그램 종결과 동시에 마지막에 해제되는
부분이기에 has-a의 관계가 아닌 is-a의 관계로써 관계를 설정지어야 한다.
또한 CDisplay 클래스는 객체가 초기에 한번만 생성되어지고 계속 재사용이
가능한 구조로 짜여져 있기에 더욱 활용도가 높으며, 이와 같은 부분은 아래의
코드를 보면 알수 있다.
이를 증명해주는 코드를 보도록 하자.

HRESULT CDisplay::DestroyObjects()
{
     SAFE_RELEASE( m_pddsBackBufferLeft );
     SAFE_RELEASE( m_pddsBackBuffer );
     SAFE_RELEASE( m_pddsFrontBuffer );

     if( m_pDD )
           m_pDD->SetCooperativeLevel( m_hWnd, DDSCL_NORMAL );

      SAFE_RELEASE( m_pDD );

      return S_OK;
}

위의 코드는 언제든지 객체를 해제시키고 초기화를 하는 코드이다.
이와 같은 코드가 소멸자 안에 넣어져 버리려져 있었으면 어떻게 될까?
프로그래밍을 하는 방법이 틀려 지게 될것이다. 즉 관계의 설정이 is-a에서
has-a의 관계로 되게 되어 new와 delete를 통해서 생성과 소멸을 반복해주
어야만 할것이다.
그럼 이와 같은 코드가 어디에서 호출될까? 당연히 설정이 바뀌어서 재생성
할 필요가 있을때가 될것이다.
그와 같은 부분을 CDisplay에서 찾아보면 CreateFullScreenDisplay()
와 CreateWindowedDisplay()에 선언되어 있음을 알수 있다.

HRESULT CDisplay::CreateFullScreenDisplay( HWND hWnd, DWORD dwWidth,
                DWORD dwHeight, DWORD dwBPP )
{
      HRESULT hr;

      // Cleanup anything from a previous call
      DestroyObjects();

HRESULT CDisplay::CreateWindowedDisplay( HWND hWnd, DWORD dwWidth,
                DWORD dwHeight )
{
      HRESULT hr;

// Cleanup anything from a previous call
DestroyObjects();

이제 멤버 변수와 함수에 대한 사항을 보도록 하자.

BOOL m_bReady;
BOOL IsReady();

m_bReady 변수는 DirectDraw에 대한 모든 셋팅이 끝나면 이 플래그를
셋하여준다. 만약 DirectDraw가 셋팅되어 지지도 않았는데 화면 렌더링이
나 Surface를 만드는 함수가 호출된다면 않되기에, 사전에 막기위한 플래
그 이다.

virtual HRESULT FrameMove() { return S_OK; } // Note: 매 프레임마다 데이터업데이트
virtual HRESULT Render() { return S_OK; } // Note: 렌더링 부분
virtual void InitAllSurface() {}; // Note: 스프라이트에 대한 초기화 부분
virtual HRESULT RestoreAllSurfaces() { return S_OK; }
           // Note: 스프라이트 Surface Restore.
virtual void DeleteAllSurface() {}; // Note: 스프라이트 Surface Release.

위의 함수는 실제로 우리가 재정의를 해주어야 하는 부분이다.
또한 위의 함수의 이름은 CD3DApplication 클래스와 비슷한 이름으로 명
명하였다. 즉 2D에서 3D로의 프레임워크를 바꾸더라도 쉽게 적응될수 있
도록 하기 위하여서 동일한 이름으로 명명하였다.
이제 함수를 언제 사용하는 지에 대해서 알아 보자

- FrameMove()
매 프레임 마다 데이터의 업데이트 부분 설정

- Render()
화면에 나타낼 Suface의 렌더링 부분

- InitAllSurface()
스프라이트 Surface의 로딩 및 초기화 부분

- RestoreAllSurfaces()
스프라이트 Surface의 Restore 부분으로써 Device가 초점을 잃을때에
다시 복구해주는 부분이다.

- DeleteAllSurface()
스프라이트의 모든 Surface를 해제하는 부분

위의 함수의 핵심은 DirectDraw의 셋팅과 백버퍼와 FrontBuffer등의 기본
적인 셋팅은 CDisplay class에서 셋팅할수 있도록하고, 단지 사용자가 생성을
해주어야하는 Surface 부분만을 재정의 하도록 하기 위함이다.

② MFC에서의 CDDApplication class 함수 호출
게임의 전체적인 구조는 아래와 같다고 생각할수 있다.




CDDApplication은 이와 같은 구조에서 호출되는 함수를 정의해주면 된다.
각 게임마다 셋팅되어 지는 스프라이트는 동일할 수가 없다. 그러기에 당
연히 하위 클래스에서 정의를 해주어야한다.
나머지 반복되는 셋팅에 관한 정의는 CDDAppliaction안에서 처리되도록
하면 된다.
그럼 위의 재정의 함수와 CDDApplication에서 정의 된 함수가 언제 불려
지는 지를 파악해보도록 하자.

MFC의 MainView class
+--- onInitialUpdate()
           +--- InitDeviceObjects()
+--- onSize()
           +--- ReSize2DEnvironment()
+--- onMove()
           +--- UpdateBound()
+--- 생성자
+--- 소멸자
           +--- DeleteAllSurfaces()


MFC의 App class
+--- onIdle()
      +--- Render2DEnvironment()
           +--- FrameMove()
                +--- Render()

이정도로 도표를 만들 수 있다.
여기서 주의 깊게 봐야 할부분은 App의 onIdle()부분이다.
즉, 이 부분이 무한 루프가 되는 부분이다.
그리고 onIdle()에서 Render2DEnvironment()를 호출하여 FrameMove(),
Render()를 호출하여 계속적으로 렌더링 되도록 하고 있다.
실제 코드를 본다면 더욱 이해가 빠르리라 생각을 한다.



//-----------------------------------------------------------------
// Note: 2D 렌더링 함수
//
//-----------------------------------------------------------------
HRESULT CDDApplication::Render2DEnvironment()
{
      HRESULT hr;
      // Note: 데이타 갱신
      FrameMove();

      // Note: 화면의 Device를 잃었을때의 처리 부분
      if( FAILED( hr = GetDirectDraw()->TestCooperativeLevel() ) )
      {
           switch( hr )
           {
           case DDERR_EXCLUSIVEMODEALREADYSET:
           // Do nothing because some other app has exclusive mode
           Sleep(10);
           return S_OK;

           case DDERR_WRONGMODE:
           // Note: 모든 서페이스를 해제후에 다시 DirectDraw 생성
           DeleteAllSurface();
           if( IsWindowed() )
           InitDeviceObject( m_hWnd );
           else
           InitDeviceObject( m_hWnd, m_dwWidth, m_dwHeight, m_dwBpp );
           InitAllSurface();
           return S_OK;
      }
      return hr;
}

//------------------- Note: 렌더링하는 부분 -----------------------//
Clear( 0 ); // Note: 화면을 모두 지우는 부분

Render(); // Note: 화면 렌더링 재정의 함수

// Note: FPS 계산하는 부분
static FLOAT fLastTime = 0.0f;
static DWORD dwFrames = 0L;
FLOAT fTime = DXUtil_Timer( TIMER_GETABSOLUTETIME );
++dwFrames;

// Update the scene stats once per second
if( fTime - fLastTime > 1.0f )
{
      m_fFPS = dwFrames / (fTime - fLastTime);
      fLastTime = fTime;
      dwFrames = 0L;
}

sprintf( m_strFrameStats, "%.02f fps", m_fFPS );

// Note: 페이지 플리핑 부분
if( FAILED( hr = Present() ) )
{
if( hr != DDERR_SURFACELOST )
           return hr;
      RestoreAllSurfaces(); // Note: Sprite Surface에 대한 부분
return S_OK;
}
return S_OK;
}

이와 같이 하나의 함수에서 FrameMove()와 Render()를 적당한 때에 호출
하고 있다. 이것은 우리가 프로그램의 흐름을 모르더라도 그 부분에 대한
프로그래밍을 해주기만 하여도 제대로 동작을 한다는 것을 알수 있다.
그리고 FrameMove()와 Render()함수외에 많은 부분이 있는데 이는
Device를 잃어 버렸을때의 부분들이다.
결론적으로 말하자면 이와같이 코드의 흐름은 위와 같은 형태로 결정되어
적장한 때에 호출되도록 하고 있기에 이런 구조가 바로 framework가 되는
것이다.

 

■ DirectDraw framewark classes인 CDDApplication 코드
#include
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

class CDDApplication : public CDisplay
{
protected:
      BOOL m_bReady; // Note: DirectDraw가 생성이 되었는지 체크하는 부분
      char m_strFrameStats[90]; // Note: FPS를 문자열로 보관하는 부분
      float m_fFPS; // Note: FPS를 위한 부분
      DWORD m_dwWidth;
      DWORD m_dwHeight;
      DWORD m_dwBpp;

public:
      BOOL IsReady();
      HRESULT Render2DEnvironment(); // Note: 최종 상속에서 호출하는 함수
      BOOL InitDeviceObject( HWND hWnd, DWORD dwWidth, DWORD dwHeight,
DWORD dwBpp ); // Note: 풀모드
      BOOL InitDeviceObject( HWND hWnd ); // Note: 윈도우 모드
      HRESULT Resize2DEnvironment(); // Note: 윈도우의 크기가 바뀌었을 때의 호출 함수

      // Note: 스프라이트에 관한 함수 부분
      virtual HRESULT FrameMove() { return S_OK; } // Note: 매 프레임마다 데이타 업데이트
      virtual HRESULT Render() { return S_OK; } // Note: 렌더링 부분
      virtual void InitAllSurface() {}; // Note: 스프라이트에 대한 초기화 부분
      virtual HRESULT RestoreAllSurfaces() { return S_OK; }
           // Note: 스프라이트 Surface Restore.
      virtual void DeleteAllSurface() {}; // Note: 스프라이트 Surface Release.
public:
      CDDApplication();
      virtual ~CDDApplication();
};


/////////////////////////////////////////////////////////////////////
// DDApplication.cpp: implementation of the CDDApplication class.
// 작성자 : 이태성
// 작성일 : 2003. 2. 27
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DDApplication.h"

#include
#include
#include "ddutil.h"
#include "dxutil.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDDApplication::CDDApplication(): m_bReady(FALSE)
{

}

CDDApplication::~CDDApplication()
{
m_bReady = FALSE;
DeleteAllSurface();
}

//----------------------------------------------------
// Note: DirectDraw 윈도우 모드 초기화 부분
// 스프라이트 초기화는 InitAllSurface()에서
//----------------------------------------------------

// Note: 윈도우 모드 초기화
// 화면의 초기 생성 크기에 따라서 클라이언트 영역 결정되도록 하기위해서
// UpdateBounds() 호출

BOOL CDDApplication::InitDeviceObject( HWND hWnd )
{
// Note: DirectDraw 초기화 부분
      m_hWnd = hWnd;

      m_bWindowed = TRUE;
      UpdateBounds();

      m_dwWidth = m_rcWindow.right - m_rcWindow.left+1;
      m_dwHeight = m_rcWindow.bottom - m_rcWindow.top+1;

if( FAILED( CreateWindowedDisplay( hWnd, m_dwWidth, m_dwHeight) ) )
           return FALSE;

// Note: Surface의 초기화 부분 ( 비트맵 로딩과 Surface의 생성 )
InitAllSurface();

DXUtil_Timer( TIMER_START ); // Note: 타이머 스타트
m_bReady = TRUE;

return TRUE;
}

// Note: 풀 모드 초기화
BOOL CDDApplication::InitDeviceObject( HWND hWnd, DWORD dwWidth, DWORD dwHeight,
DWORD dwBPP )
{
      m_hWnd = hWnd;
      m_dwWidth = dwWidth;
      m_dwHeight = dwHeight;
      m_dwBPP = dwBPP;

if( FAILED( CreateFullScreenDisplay( hWnd, dwWidth, dwHeight, dwBPP ) ) )
           return FALSE;

if( FAILED( InitClipper() ) )
           return FALSE;

InitAllSurface();

DXUtil_Timer( TIMER_START ); // Note: 타이머 스타트
m_bReady = TRUE;

return TRUE;
}

//-----------------------------------------------------------------
// Note: 2D 렌더링 함수
//
//-----------------------------------------------------------------
HRESULT CDDApplication::Render2DEnvironment()
{
HRESULT hr;
// Note: 데이타 갱신
FrameMove();

// Note: 화면의 Device를 잃었을때의 처리 부분
if( FAILED( hr = GetDirectDraw()->TestCooperativeLevel() ) )
{
      switch( hr )
      {
           case DDERR_EXCLUSIVEMODEALREADYSET:
           // Do nothing because some other app has exclusive mode
           Sleep(10);
           return S_OK;

      case DDERR_WRONGMODE:
            // Note: 모든 서페이스를 해제후에 다시 DirectDraw 생성
            DeleteAllSurface();
           if( IsWindowed() )
           InitDeviceObject( m_hWnd );
      else
           InitDeviceObject( m_hWnd, m_dwWidth, m_dwHeight, m_dwBPP );

           InitAllSurface();
      return S_OK;
      }
return hr;
}

//------------------- Note: 렌더링하는 부분 -----------------------//
Clear( 0 ); // Note: 화면을 모두 지우는 부분

Render(); // Note: 화면 렌더링 재정의 함수

// Note: FPS 계산하는 부분
static FLOAT fLastTime = 0.0f;
static DWORD dwFrames = 0L;
FLOAT fTime = DXUtil_Timer( TIMER_GETABSOLUTETIME );
++dwFrames;

// Update the scene stats once per second
if( fTime - fLastTime > 1.0f )
{
      m_fFPS = dwFrames / (fTime - fLastTime);
      fLastTime = fTime;
      dwFrames = 0L;
}

sprintf( m_strFrameStats, "%.02f fps", m_fFPS );

// Note: 페이지 플리핑 부분
if( FAILED( hr = Present() ) )
{
      if( hr != DDERR_SURFACELOST )
      return hr;

      m_pDD->RestoreAllSurfaces();
      RestoreAllSurfaces(); // Note: Sprite Surface에 대한 부분
      return S_OK;
}

return S_OK;
}

//----------------------------------------------------------
// Note: DirectDraw가 생성 되어는지를 체크하는 부분
//----------------------------------------------------------
BOOL CDDApplication::IsReady()
{
      return m_bReady;
}

//----------------------------------------------------------
// Note: 윈도우 핸들 셋 함수
//----------------------------------------------------------
void CDDApplication::SetHWnd(HWND hWnd)
{
      m_hWnd = hWnd;
}

//--------------------------------------------------------------
// Note: 윈도우 싸이즈 바뀔경우에 백버퍼를 다시 생성해주는 부분
//--------------------------------------------------------------
HRESULT CDDApplication::Resize2DEnvironment()
{
     HRESULT hr;
     UpdateBounds();

     // Create the primary surface
      DDSURFACEDESC2 ddsd;
     ZeroMemory( &ddsd, sizeof( ddsd ) );
      ddsd.dwSize = sizeof( ddsd );

      // Create the backbuffer surface
      ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
      ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
      ddsd.dwWidth = m_rcWindow.right - m_rcWindow.left ;
     ddsd.dwHeight = m_rcWindow.bottom - m_rcWindow.top ;

if( FAILED( hr = m_pDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL ) ) )
      return E_FAIL;

     return S_OK;
}

 

■ DirectDraw framewark classes인 CDDApplication class와 MFC 코드결합
① App Class
// DDMFCFrameWork.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "DDMFCFrameWork.h"

#include "MainFrm.h"
#include "DDMFCFrameWorkDoc.h"
#include "DDMFCFrameWorkView.h"
#include

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkApp

BEGIN_MESSAGE_MAP(CDDMFCFrameWorkApp, CWinApp)
      //{{AFX_MSG_MAP(CDDMFCFrameWorkApp)
      on_COMMAND(ID_APP_ABOUT, onAppAbout)
           // NOTE - the ClassWizard will add and remove mapping macros here.
           // DO NOT EDIT what you see in these blocks of generated code!
      //}}AFX_MSG_MAP
      // Standard file based document commands
      on_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
      on_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkApp construction

CDDMFCFrameWorkApp::CDDMFCFrameWorkApp()
{
      // TODO: add construction code here,
      // Place all significant initialization in InitInstance
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CDDMFCFrameWorkApp object

CDDMFCFrameWorkApp       theApp;
CDDMFCFrameWorkView*       g_pView = NULL;

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkApp initialization

BOOL CDDMFCFrameWorkApp::InitInstance()
{
      AfxEnableControlContainer();

     // Standard initialization
     // If you are not using these features and wish to reduce the size
     // of your final executable, you should remove from the following
     // the specific initialization routines you do not need.

#ifdef _AFXDLL
     Enable3dControls(); // Call this when using MFC in a shared DLL
#else
     Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

     // Change the registry key under which our settings are stored.
     // TODO: You should modify this string to be something appropriate
     // such as the name of your company or organization.
     SetRegistryKey(_T("Local AppWizard-Generated Applications"));

     LoadStdProfileSettings(); // Load standard INI file options (including MRU)

     // Register the application's document templates. Document templates
     // serve as the connection between documents, frame windows and views.

     CSingleDocTemplate* pDocTemplate;
     pDocTemplate = new CSingleDocTemplate(
           IDR_MAINFRAME,
           RUNTIME_CLASS(CDDMFCFrameWorkDoc),
           RUNTIME_CLASS(CMainFrame), // main SDI frame window
           RUNTIME_CLASS(CDDMFCFrameWorkView));
     AddDocTemplate(pDocTemplate);

     // Parse command line for standard shell commands, DDE, file open
     CCommandLineInfo cmdInfo;
     ParseCommandLine(cmdInfo);

     // Dispatch commands specified on the command line
     if (!ProcessShellCommand(cmdInfo))
           return FALSE;

     // The one and only window has been initialized, so show and update it.
// srand( GetTickCount() );

     m_pMainWnd->SetWindowText("2D툴 기본틀");
     m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
     m_pMainWnd->UpdateWindow();

     return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
     CAboutDlg();

// Dialog Data
     //{{AFX_DATA(CAboutDlg)
     enum { IDD = IDD_ABOUTBOX };
     //}}AFX_DATA

     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CAboutDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
     //}}AFX_VIRTUAL

// Implementation
protected:
     //{{AFX_MSG(CAboutDlg)
           // No message handlers
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
     //{{AFX_DATA_INIT(CAboutDlg)
     //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CAboutDlg)
     //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
     //{{AFX_MSG_MAP(CAboutDlg)
           // No message handlers
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CDDMFCFrameWorkApp::OnAppAbout()
{
CAboutDlg aboutDlg;
aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkApp message handlers


BOOL CDDMFCFrameWorkApp::OnIdle(LONG lCount)
{
     // TODO: Add your specialized code here and/or call the base class
     if( g_pView == NULL || m_pMainWnd->IsIconic() )
           return FALSE;

     if( g_pView->IsReady() )
     {
           // Note: FrameMove()와 Render()를 돌림
           g_pView->Render2DEnvironment();
     }
     return TRUE;
}

② MainFrame
class CMainFrame : public CFrameWnd
{

protected: // create from serialization only
     CMainFrame();
     DECLARE_DYNCREATE(CMainFrame)

// Attributes
protected:
     CSplitterWnd m_wndSplitter;
public:

// Operations
public:

// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CMainFrame)
     public:
     virtual BOOL onCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
     //}}AFX_VIRTUAL

// Implementation
public:
     virtual ~CMainFrame();
#ifdef _DEBUG
     virtual void AssertValid() const;
     virtual void Dump(CDumpContext& dc) const;
#endif

protected: // control bar embedded members
     CStatusBar m_wndStatusBar;
     CToolBar m_wndToolBar;

// Generated message map functions
protected:
     //{{AFX_MSG(CMainFrame)
     afx_msg int onCreate(LPCREATESTRUCT lpCreateStruct);
     afx_msg void onMove(int x, int y);
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

#include "stdafx.h"
#include "DDMFCFrameWork.h"
#include "DDMFCFrameWorkView.h"
#include "MenuFormView.h"
#include "MenuFormView.h"

#include

#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
     //{{AFX_MSG_MAP(CMainFrame)
     ON_WM_CREATE()
     ON_WM_MOVE()
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

static UINT indicators[] =
{
     ID_SEPARATOR, // status line indicator
     ID_INDICATOR_CAPS,
     ID_INDICATOR_NUM,
     ID_INDICATOR_SCRL,
};

// Note: 전역 변수 선언 부분
extern CDDMFCFrameWorkView* g_pView;

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
     // TODO: add member initialization code here

}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
     if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
      return -1;

     if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |
           CBRS_SIZE_DYNAMIC)|| !m_wndToolBar.LoadToolBar(IDR_MAINFRAME) )
     {
           TRACE0("Failed to create toolbar\n");
           return -1; // fail to create
     }

     if (!m_wndStatusBar.Create(this) ||
           !m_wndStatusBar.SetIndicators(indicators,
           sizeof(indicators)/sizeof(UINT)))
     {
           TRACE0("Failed to create status bar\n");
           return -1; // fail to create
     }

     // TODO: Delete these three lines if you don't want the toolbar to
     // be dockable
     m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
     EnableDocking(CBRS_ALIGN_ANY);
     DockControlBar(&m_wndToolBar);

     return 0;
}

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
     CCreateContext* pContext)
{
     CRect rect;
     GetClientRect( &rect );

      if( !m_wndSplitter.CreateStatic(this, 1, 2 ) )
           return FALSE;

     int size_x = rect.right - rect.left;
     const int MenuSizeX = 108;

      if( !m_wndSplitter.CreateView( 0, 0, RUNTIME_CLASS(CDDMFCFrameWorkView),

           CSize(size_x-MenuSizeX,0), pContext ))
           return FALSE;

     if( !m_wndSplitter.CreateView( 0,1, RUNTIME_CLASS(CMenuFormView),
           CSize(MenuSizeX, 0), pContext))
           return FALSE;

     ShowWindow(SW_SHOWMAXIMIZED);
     g_pView = (CDDMFCFrameWorkView *)m_wndSplitter.GetPane(0,0);
     SetActiveView( (CView *)m_wndSplitter.GetPane(0,0) );

     return TRUE;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
     if( !CFrameWnd::PreCreateWindow(cs) )
           return FALSE;
     // TODO: Modify the Window class or styles here by modifying
     // the CREATESTRUCT cs

     cs.style &= ~FWS_ADDTOTITLE ;

     cs.cx = 1024;
     cs.cy = 768;

     return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
     CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
     CFrameWnd::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
void CMainFrame::OnMove(int x, int y)
{
     CFrameWnd::OnMove(x, y);

     // TODO: Add your message handler code here
     if( g_pView != NULL && g_pView->IsReady() )
           g_pView->UpdateBounds();

}

③ DirectDraw View

#include "DDApplication.h"
#include "DDMFCFrameWorkDoc.h"

class CDDMFCFrameWorkView : public CView, public CDDApplication
{

public :
     virtual HRESULT FrameMove();
     virtual HRESULT Render();

protected: // create from serialization only
     CDDMFCFrameWorkView();
     DECLARE_DYNCREATE(CDDMFCFrameWorkView)

// Attributes
public:
     CDDMFCFrameWorkDoc* GetDocument();

// Operations
public:

// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CDDMFCFrameWorkView)
     public:
     virtual void onDraw(CDC* pDC); // overridden to draw this view
     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
     virtual void onInitialUpdate();
     protected:
     //}}AFX_VIRTUAL

// Implementation
public:
     virtual HRESULT RestoreAllSurfaces();
     virtual ~CDDMFCFrameWorkView();
#ifdef _DEBUG
     virtual void AssertValid() const;
     virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
     //{{AFX_MSG(CDDMFCFrameWorkView)
     afx_msg void onSize(UINT nType, int cx, int cy);
     afx_msg void onMove(int x, int y);
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

#include "stdafx.h"
#include "DDMFCFrameWork.h"

#include "DDMFCFrameWorkDoc.h"
#include "DDMFCFrameWorkView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkView

IMPLEMENT_DYNCREATE(CDDMFCFrameWorkView, CView)

BEGIN_MESSAGE_MAP(CDDMFCFrameWorkView, CView)
     //{{AFX_MSG_MAP(CDDMFCFrameWorkView)
     ON_WM_SIZE()
     ON_WM_MOVE()
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()

// Note: 전역 변수 선언 부분
extern CDDMFCFrameWorkView* g_pView;

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkView construction/destruction

CDDMFCFrameWorkView::CDDMFCFrameWorkView()
{
     // TODO: add construction code here
     g_pView = this;

}

CDDMFCFrameWorkView::~CDDMFCFrameWorkView()
{
}

BOOL CDDMFCFrameWorkView::PreCreateWindow(CREATESTRUCT& cs)
{
     // TODO: Modify the Window class or styles here by modifying
     // the CREATESTRUCT cs

     return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkView drawing

void CDDMFCFrameWorkView::OnDraw(CDC* pDC)
{
     CDDMFCFrameWorkDoc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
     // TODO: add draw code for native data here
     if( IsReady() )
     {
           if( Render2DEnvironment() == DDERR_SURFACELOST )
           {
                RestoreAllSurfaces();
                Render2DEnvironment();
           }
     }
}

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkView diagnostics

#ifdef _DEBUG
void CDDMFCFrameWorkView::AssertValid() const
{
     CView::AssertValid();
}

void CDDMFCFrameWorkView::Dump(CDumpContext& dc) const
{
     CView::Dump(dc);
}

CDDMFCFrameWorkDoc* CDDMFCFrameWorkView::GetDocument() // non-debug version is inline
{
     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDDMFCFrameWorkDoc)));
     return (CDDMFCFrameWorkDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CDDMFCFrameWorkView message handlers
//-------------------------------------------------------------
// Note: 매 프레임마다 업데이트 되어야 할 데이타 처리 부분
//-------------------------------------------------------------
HRESULT CDDMFCFrameWorkView::FrameMove()
{

     return S_OK;
}

//--------------------------------------------------------------
// Note: 렌더링 부분
//--------------------------------------------------------------
HRESULT CDDMFCFrameWorkView::Render()
{
     // Note: 이안에 각 서페이스의 Draw를 해주면 된다.
     // Note: FPS를 화면에 나타내기 위한 부분
     HDC hdc;
      GetBackBuffer()->GetDC( &hdc );
      TextOut( hdc, 0, 0, m_strFrameStats, strlen(m_strFrameStats) );
      GetBackBuffer()->ReleaseDC( hdc);

      return S_OK;
}

//---------------------------------------------------------------
// Note: DirectDraw의 초기화 부분
//---------------------------------------------------------------
void CDDMFCFrameWorkView::OnInitialUpdate()
{
     CView::OnInitialUpdate();

      // TODO: Add your specialized code here and/or call the base class
     InitDeviceObject( GetSafeHwnd() );
     m_bReady = TRUE;
}

//-----------------------------------------------------------------
// Note: 윈도우의 크기가 변하면 크기에 따른 백버퍼 다시 생성
//-----------------------------------------------------------------
void CDDMFCFrameWorkView::OnSize(UINT nType, int cx, int cy)
{
     CView::OnSize(nType, cx, cy);

     if( IsReady() )
     {
      Resize2DEnvironment();
     }

}

//-----------------------------------------------------------------
// Note: 모든 Sprite Surface의 복구
//-----------------------------------------------------------------
HRESULT CDDMFCFrameWorkView::RestoreAllSurfaces()
{
     // Note: Sprite Surface에 대한 Restore 부분이 오면 된다.

      return S_OK;
}

void CDDMFCFrameWorkView::OnMove(int x, int y)
{
     CView::OnMove(x, y);

     // TODO: Add your message handler code here
     if( IsReady() )
           UpdateBounds();
}

■ 최종 출력 화면

 

 

 

 

'- 음악과 나 - > 『 짬 통 』' 카테고리의 다른 글

framework( minpress.com ) - 2  (0) 2006.10.31
IndexBuffer  (0) 2006.10.08
Mesh  (0) 2006.10.08
Texture  (0) 2006.10.07
Light  (0) 2006.10.07