jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다. 3ds Max를 사용해서 게임용 3D 캐릭터를 셋업하는 방법
이를 위해 오랜 실무를 경험해 온 저자의 고급 노하우들이 공개
위 내용은 GameDevForever의 저자분들의 홍보를 위하여 운영진 자체적으로 올린 광고이며 일체의 수익이 없습니다.(밥좀사줘요~)
Posted by 친절한티스

요즘 툴을 만든다 하면 많은 분들이 C#를 선호하고 있습니다. MFC에 비해 생산성이 높기 때문이죠. 저도 마찬가지입니다. 개인 프로젝트에서 툴을 C#의 윈폼을 사용해 만들고 있습니다. 최근에는 WPF에도 관심을 가지게 되었습니다. 작년에 있었던 KGC11의 엔진과 툴의 그렇고 그런 사이 발표 자료를 보고 많은 감명을 받았죠.


이런 것도 있구나!!


윈폼이 MFC에 비해 편하기는 하지만, 컨트롤 구성에 제약이 있는 것은 윈폼도 마찬가지였습니다. 그에 반해 WPF는 윈폼에 비해 확장성과 유연성이 굉장히 높아 거의 원하는대로의 컨트롤 구성을 만들어낼 수 있습니다. 조금만 노력하면 언리얼 에디터 만큼의 퀄리티도 만들어낼 수 있을 정도죠.


이렇게 카와이하게 만들 수 있어요

출처 : http://wpfpropertygrid.codeplex.com/


이렇게 좋은 도구가 있는데, 문제는 역시 이놈도 C#이라는 것 입니다. 보통 게임엔진을 만든다 하면 대부분 C++로 만듭니다. 하지만 C#과 C++은 전혀 다른 언어죠. 그렇기에 C#으로 만들어진 툴이 C++로 만들어진 엔진과 연계가 되려면 둘 사이에 중계자가 필요합니다. 그 것이 바로 C++/CLI이죠. 이 C++/CLI란 놈은 C++과 C#의 영역을 자유자재(?)로 접근할수 있는 힘을 가지고 있습니다. C++에서 작성된 클래스 객체에 접근 할수 있고, C#의 객체에도 접근을 할 수 있습니다.


네... 뭐 그런놈이 있습니다.


이 중계 기능을 만들때 흔히 랩핑이라는 기법을 씁니다. C++에서 만들어진 클래스를 C++/CLI에서 가져와 그 클래스의 기능을 감싸는 클래스를 하나 더 만듭니다. 이 C++/CLI에서 만들어진 클래스는 [ 관리 되는 ] 클래스로 이 클래스는 C#에서 사용이 가능해집니다. 즉, C++/CLI를 통해 간접적으로 C#에서 C++ 클래스를 사용하는 것이지요.


C++

class DLL_API CSceneManager
{
public:
	void DoSomething( void )
	{
		// Do~ Do~ Do~
	}
};


C++/CLI

public ref class CWrappedSceneManager
{
public:
	void DoSomething( void )
	{
		m_pNativeSceneManager->DoSomething();
	}

private:
	CSceneManager* m_pNativeSceneManager;
};


C#

public partial class MainForm : Form
{
    private CWrappedSceneManager m_WrrapedSceneManager;
    public void DoSomething()
    {
        m_WrrapedSceneManager = new CWrappedSceneManager();
        m_WrrapedSceneManager.DoSomething();
    }
}


이 짓을 기능 추가때 마다 하라고??


대략 이와 같은 구조를 갖게 되는 겁니다. 벌써부터 머리가 지끈거려 오죠? 기능이 많으면 많을 수록 랩핑할 것도 많고, 손도 많이 갑니다. 정말 귀찮죠. 하지만 이렇게 일일히 클래스와 함수를 랩핑을 하는 것이 아니라 명령 단위로 처리한다면 어떨가요? 예로 게임 오브젝트를 하나 생성하는데, 게임 오브젝트 생성 부터 데이터를 채워넣는 것 까지 전부다 C# 툴 레벨에서 처리하는 것이 아니라 단지 C# 툴에서는 게임 오브젝트를 생성해라~ 명령어만 전달하는 것이지요.


그렇게 하면 C++/CLI 레벨에서는 단지 명령어를 전달 해줄 기능만 있으면 됩니다. 나머지는 엔진쪽에서 해당 명령에 대한 수행 로직만 만들면 되는 것이지요. 여기서 좀더 나아가 보겠습니다. 만약 이 C++/CLI 조차 필요없이 바로 C++쪽에 명령을 전달할수 있다면 어떨까요? 불필요한 파일도 없어지고, 좀더 수월해지겠죠?


하지만 C++과 C#은 같이 쓸수 없다고 했잖아요? 어떻게 하면 될까요? 바로 DLL Interop을 통해서 할 수 있습니다. C#에서도 DLL Interop을 통해 외부 DLL의 함수를 불러와 사용할 수 있습니다. 일단 엔진을 DLL로 빌드 하고, C#에서 사용될 명령어 처리 함수를 만듭니다. 그리고 C#에서 이 DLL을 가져와 함수를 사용하는 것이지요.


C++

extern "C"
{
	DLL_API bool SendCommand( const wchar_t* szCommand );
}


C#

public partial class MainWindow : Window
{
    [DllImport("Engine.dll", EntryPoint = "SendCommand", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.I1)]
    private static extern bool SendCommand(string strCommand);

    public void DoSomething()
    {
        SendCommand("DoSomething");
    }
}


이 얼마나 아름답고 간결한 코드인가~


이전 C++/CLI 사용때 보다 훨씬 간단해졌죠? 아주 좋습니다. 이제 C#과 C++ 연동은 알겠어요. 그럼 WPF로 게임 엔진 툴을 만들어 봅니다. 뚝딱~ 뚝딱~ 카와이 하게 언리얼 에디터 같은 것을 만들어보죠. 그런데 문제가 생겼습니다. WPF로 멋지게 툴을 만들었다고 생각했는데, 화면이 렌더링 될 부분을 어떻게 만들어야 하죠?


윈폼에서는 ImageBox 같은 더미 컨트롤을 하나 만들어서 그 컨트롤의 핸들을 엔진쪽에 넘겨주면 엔진쪽에서 그 핸들을 받아 그 곳에 화면을 렌더링할 수 있었습니다. 그런데 WPF는 컨트롤의 핸들이 없습니다. 오로지 윈도우의 핸들만 존재합니다. 열심히 컨트롤 붙이고 툴을 만들었는데, 정작 화면을 렌더링 할수 없으면 아무 소용이 없습니다.


난 그동안 뭐한건가...


다행히 방법은 있습니다. WPF Win32 Content Hosting 이라는 요상한 기능이 하나 있습니다. 즉, WPF 상에 Win32에서 만든 컨텐츠를 표시할 수 있게 해주는 기능이죠. 이를 이용해 우리는 엔진쪽에서 만든 Render Window를 WPF에 갖다 붙일 수 있습니다. 일단 이 기능을 쓰기 위해서는 HwndHost 라는 클래스를 이용해야 합니다.


C#

public class RenderViewHwndHost : HwndHost
{
    // Win32 창 생성 함수
    [DllImport("Engine.dll", EntryPoint = "CreateRenderWindow", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr CreateRenderWindow(IntPtr applicationInstance, IntPtr hWndParent, int screenWidth, int screenHeight);

    // 소멸시 할 일
    [DllImport("Engine.dll", EntryPoint = "DestroyWindow", CallingConvention = CallingConvention.Cdecl)]
    private static extern void DestroyWindow();

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        IntPtr instanceHandle = System.Runtime.InteropServices.Marshal.GetHINSTANCE(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]);
        IntPtr hwndHost = CreateRenderWindow(instanceHandle, hwndParent.Handle, 1024, 760);

        return new HandleRef(this, hwndHost);
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
        DestroyWindow();
    }
}


C++ 엔진쪽에서 WPF에 붙일 RenderWindow를 생성하는 함수 하나를 정의합니다. 그리고 C#에서 HwndHost 클래스를 재정의 하여, 이 함수를 호출해줍니다. HwndHost에서 재정의 해야될 멤버 함수가 BuildWindowCore와 DestroyWindowCore 두 가지가 있습니다. 전자는 창 생성, 후자는 창 소멸시 호출되는 HwndHost 멤버 함수입니다. 이 두 함수는 Hosting시에 자동으로 호출 되므로, 직접 호출해줄 일은 없습니다.


HwndHost를 만들었으면 이제 생성한 창을 붙일 일만 남았습니다. WPF에서 편집기를 통해 Border 컨트롤을 이곳에 렌더 화면을 띄어야지~~ 할 곳에 적당히 붙여둡니다. 이 Border 컨트롤은 더미 역할을 하게 됩니다. 그리고 재정의한 HwndHost를 생성해 이 Border의 Child에 찰싹~ 붙여주면, 이제 WPF에서도 렌더링 화면을 볼수 있게 됩니다.


C#

public partial class MainWindow : Window
{
    public void CreateRenderView()
    {
        RenderViewHwndHost renderViewHost = new RenderViewHwndHost();
        border1.Child = renderViewHost; // 찰싹~
    }
}



위 기능을 구현하기 위해 드플의 핵심 인재 끼로(@kgun86)님께서 많은 도움을 주셨습니다.

이 자리를 빌어 감사드립니다.


댓글을 달아 주세요

  1. Favicon of http://gamedevforever.com 월하 2012.05.08 15:02 신고  댓글주소  수정/삭제  댓글쓰기

    기획자를 위해 c#이랑 xaml 기초강의부터 해서 나갈줄 알았는데....
    아니군요. ㅡ.ㅜ

  2. Favicon of http://gamedevforever.com 알콜코더 2012.05.08 15:06 신고  댓글주소  수정/삭제  댓글쓰기

    이전에 NC에서 서버 프로그래머로 근무할때.. 서버 코어부분은 C++로 만들고, InterOP를 이용해서,
    ASP.NET과 연결해서 웹페이지들을 구현했었었는데..그것과 같은 방식이군요. ^^

    좋은 정보 땡큐!!

    이렇게 하면, C++.NET을 안써도 된다는 장점이 있어서 좋긴 합니다.. ^^

    다만 KGC 11의 자료를 보고 같은 의문을 있었었는데..
    DLL로 엔진을 구현하다고 했을때... 디버깅이 몇배나 어려워진다는 단점이 있었죠..
    DLL에 디버거를 붙여서 디버깅을 해야한다는 문제가..

    그리고 DLL과 WPF 툴 프로젝트는 물리적으로도도 별도의 프로젝트이기 때문에,
    빌드할때 깜박하고 두 프로젝트를 다 빌드하지 않으면, 개망하는 주의점도 있습니다. ^^;;

    그래도 MFC보다는 WPF가.. 100쯤 나음..

    • Favicon of http://gamedevforever.com 끼로 2012.05.08 15:51 신고  댓글주소  수정/삭제

      사실 엔진을 DLL로 안만들어도 됩니다.. 저희게임도 엔진쪽 프로젝트들이 항상 DLL로 뽑히는건 아니기도 하고요. C++쪽 전체 프로젝트가 하나여도 상관없습니다. 거기에 툴과 연동할 DLL프로젝트를 하나 만들고 그 DLL프로젝트가 C++쪽 코드를 참조를 하고 그 DLL 프로젝트에 SendCommand 함수를 만들어서 그걸 익스포트 시키는 방법을 사용하고 있습니다. 저희팀에서는 ToolInterface.dll 이라는 이름으로 만들고 있지요.. 그리고 ToolInterface.dll 이 clr과 비슷한 역활을 하게 될수도 있는데 그렇다 하더라도 저는 그게 clr을 쓰는것보다 더 스트레스를 덜 받는다고 생각합니다. C#의 저평가에 가장 큰 공을 세운 녀석이 바로 CLR이라고 생각합니다 ㅡ,.ㅡ;

    • Favicon of http://gamedevforever.com 알콜코더 2012.05.08 17:02 신고  댓글주소  수정/삭제

      그렇게 쓰는 방법 밖에 없으니.. 엔진 Static으로 만들어서 DLL에 포함하거나 하는수밖에..

      그런데 역시 DLL이기 때문에, C#에서 툴 작업하다가 디버깅을 하면, 결국 어태취해야 하는 방법은 똑같죠..

      그리고 C#의 저평가가 CLR 때문인건 아니고...
      다른데서는 다 C#으로 넘어가서 C#만으로 잘 쓰고 있는데.. 게임쪽에선 아직 C++에서 못넘어가기 때문..
      결국 이런걸로 고민하는건 게임 개발쪽 부분만이죠 ^^

      웹프로그래밍이나 스마트폰쪽에선 이젠 C++은 아무도 안쓰니..

    • Favicon of http://gamedevforever.com 친절한티스 2012.05.08 17:13 신고  댓글주소  수정/삭제

      엔진이랑 툴 작업을 한 솔루션에서 하면 되잖삼~

    • Favicon of http://gamedevforever.com 끼로 2012.05.08 17:49 신고  댓글주소  수정/삭제

      아 마지막 부분의 빌드 관련된부분도 저희팀은 ToolInterface.dll 프로젝트와 WPF로 작성된 프로젝트가 같은 솔루션에 있고 항상 솔루션빌드를 하다보니 문제된적은 없었습니다 확실히 하나만 빌드하면 개망ㅋ 주의해야 할 부분이겠네요

    • Favicon of http://gamedevforever.com 끼로 2012.05.08 17:51 신고  댓글주소  수정/삭제

      그리고 대충 쓰다보니 의미가 잘못 전달된 부분이 있는데 ㅠㅠ C# 자체의 저평가가 아니라 C#으로 툴을 만드는것에 대한 저평가에 대한 이야기였습니다.. 개인적으로 저도 C++/CLI를 이용해서 C# 툴을 만들었을 당시 스트레스가 어마어마해서 C#으로 툴을 만들지 말아야겠다는 생각을 좀 했었거든요 당연히 C# 자체에 대한 저평가는 C++/CLI때문은 아니라고 저도 생각합니다 ㅎㅎ

    • Favicon of http://gamedevforever.com 끼로 2012.05.08 17:53 신고  댓글주소  수정/삭제

      그리고 디버깅 관련해서는 어떤 부분 때문인지 설명좀 부탁드려도 될까요.. 어태치해야 한다고 하셧는데 그런적이 없어서... 그냥 디버깅 했는데....

  3. Favicon of http://lunapiece.net Lyn 2012.05.08 15:42 신고  댓글주소  수정/삭제  댓글쓰기

    MFC가 원체 상병신이라 (...)

    그런데 이러려면 애초에 엔진을 잘 랩핑 해놨어야 겠네요.
    아니 어차피 스크립트 붙이느라 잘 되있으려나?

  4. Favicon of http://gamedevforever.com 끼로 2012.05.08 15:48 신고  댓글주소  수정/삭제  댓글쓰기

    아.. 내용에 잘못된 부분이 있네요.. 저는 드플의 핵심인재가 아니라 허접잉여입니다... 그리고 전 도움을 준게 별로 없어요.. 초능력자 억대연봉 티스형이 뭔가 잘못알고 있네요...

    • Favicon of http://lunapiece.net Lyn 2012.05.08 15:50 신고  댓글주소  수정/삭제

      헛...

      드플에 다니시는분의 친구의 친구에게 듣기로는 감히 따라올수없는 인재라고 들었는데 ...

    • Favicon of http://gamedevforever.com 끼로 2012.05.08 15:52 신고  댓글주소  수정/삭제

      그 친구의 친구분이 누구신지 알려주실 수 있나요.. 잘못 알고 있는 것을 바로 잡아야 합니다

    • 마루 2012.05.10 14:50 신고  댓글주소  수정/삭제

      음.. 인재 맞음..
      내가 보증함.. ㅋㅋ
      같이 일해 본 사람중 상위 20% 안에 듦

    • Favicon of http://gamedevforever.com 끼로 2012.05.10 15:06 신고  댓글주소  수정/삭제

      헐 닉네임으로 보면 정영구팀장님.. 아니 이젠 뭐라고 불러야 하지 아무튼 삼촌이신것 같은데.. 여기서 이러시면 곤란합니다... 게다가 같이 일했을때는 제가 손가락 빨던 19살~20살때자나요 그때는 상속이 뭔지도 몰랐던때인데 이건 거짓말이라는 증거임!!!

  5. Favicon of http://struggle.tistory.com 정상택 2012.05.08 15:50 신고  댓글주소  수정/삭제  댓글쓰기

    Jni로 안드로이드에서 c++을 사용하는 것 같네요.

    • Favicon of http://lunapiece.net Lyn 2012.05.08 15:55 신고  댓글주소  수정/삭제

      그래도 좀 낫죠...

      C++/CLI로 Managed Code와 Native Code를 섞어서 랩핑할 수 있으니까요....

      단지 랩핑 다 해야되서 귀찮긴 합니다...
      C++/CLI가 만든 Mixed Method(Managed + Native)는 private라는 제약이 있어서 ... C#에서 참조불가 Orz

  6. Favicon of http://www.sysnet.pe.kr kevin 2012.05.08 15:58 신고  댓글주소  수정/삭제  댓글쓰기

    본문에서 CLR 이라는 단어가 요상하게 쓰인 것 같습니다. ^^ CLR 은 Common Language Runtime 으로 자바로 치면 JVM 에 해당하는 레벨입니다. 개인적인 느낌으로 전체적인 문맥으로 볼 때, 아마도 "CLR" 이 아니라 "C++/CLI"를 의미하시는 것이 아닌가 생각됩니다. ^^

  7. Favicon of http://www.sysnet.pe.kr kevin 2012.05.08 16:39 신고  댓글주소  수정/삭제  댓글쓰기

    자꾸... ^^ 딴지 거는 것 같아서 덧글 달기가 왠지 미안해지는데요. CLI 는 Common Language Infrastructure 로 CLR 위에서 동작하는 언어들에 대한 공통 기반을 의미합니다. 그래서 C#, VB.NET 및 C++/CLI 등의 언어들을 위한 기반을 의미하기 때문에 C++ 을 떼어낸 CLI도 문맥에 맞지 않게 됩니다. 조금 단어가 길어도 ^^ C++/CLI 라고 해야 되지 않을까... 하는.

  8. Favicon of http://bluekms21.blog.me 크로스 2012.05.08 16:40 신고  댓글주소  수정/삭제  댓글쓰기

    일이 있어서 중간정도밖에 못 읽었지만
    '짤방은 어떻게 써야 하는가'에 대해서도 강의해주세요...ㄷㄷㄷ
    (부재 : 효과적인 짤방 사용으로 발표의 가치와 주제의식 부각시키기)

  9. Favicon of http://kallru.com kallru 2012.05.08 18:12 신고  댓글주소  수정/삭제  댓글쓰기

    오오 하지만 WPF는 복잡하다능 ㅠ_ㅠ

  10. blueasa 2012.05.08 22:30 신고  댓글주소  수정/삭제  댓글쓰기

    우오~
    먼저 감사의 리플부터 달고 읽으러 갑니다. =_=

  11. Favicon of http://lunapiece.net Lyn 2012.05.08 23:22 신고  댓글주소  수정/삭제  댓글쓰기

    자 이제 다음글 올려달라고 압박을 (...)

  12. 이군 2012.05.10 14:12 신고  댓글주소  수정/삭제  댓글쓰기

    C# 윈폼의 경우 DllImport를 이용해서 직접 HWND을 얻어와 넘겨주는 방법도 있는데 WPF에선 불가능한가요?

    • Favicon of http://gamedevforever.com 친절한티스 2012.05.10 17:24 신고  댓글주소  수정/삭제

      말씀하시는 핸들이 어떤 핸들인가요??

    • Favicon of http://gamedevforever.com 끼로 2012.05.10 17:26 신고  댓글주소  수정/삭제

      윈폼의 컨트롤 핸들을 말씀하시는듯?!

    • 이군 2012.05.10 18:48 신고  댓글주소  수정/삭제

      이전에 윈폼에서 특정 패널의 핸들을 직접 구해서 렌더링 엔진에 넘겨주는 방법을 생각해 간단히나마 윈폼과 결합을 시켜본적이 있었습니다.
      제가 말하는 핸들은 win api에서 컨트롤이 갖는 그 HWND인데 쪼랩이라 정확한 용어가 맞는진 모르겠네요.

    • 이군 2012.05.10 19:02 신고  댓글주소  수정/삭제

      죄송합니다 다시 천천히 읽어보니 중간에 wpf 컨트롤은 핸들이 없어 안된다고 써두셨군요 ㅠㅠ

    • Favicon of http://gamedevforever.com 친절한티스 2012.05.10 21:54 신고  댓글주소  수정/삭제

      네. 저도 WPF로 툴 만들어 보면서 그 것 땜시 적잖이 당황했죠. 이 부분을 핵심 인재 끼로님께서 조언을 해주셔가지고, 위의 방법으로 해결을 봤습니다. ㅎㅎㅎ

  13. Favicon of http://blog.wimy.com zelon 2012.05.10 21:29 신고  댓글주소  수정/삭제  댓글쓰기

    C# 의 저평가 관련해서 이런저런 얘기가 나오는데, 게임 업계에서 C++ 에서 C# 으로 못 넘어가는 이유는 아직 클라이언트에서 C# 을 쓸 수 없기 때문이 아닌가요?

    서버 프로그램이나, 개발툴은 C# 으로 짤 수 있지만 클라이언트는 .NET Framework 가 없을 경우 깔아줘야하는 거 때문에 아직 C++ 에 머물러 있고, 서버/클라이언트의 언어가 같으면 (당연히) 편하기 때문에 서버도 대부분 C++ 이지만, 최근에는 C# 의 생산성 때문에 서버는 조금씩 움직이고 있다.... 라고 생각합니다. 어떤가요? ;;

    • Favicon of http://gamedevforever.com 끼로 2012.05.10 22:10 신고  댓글주소  수정/삭제

      여기에 C#의 저평가 얘기를 제가 처음 달았는데.. 위에서도 다시 댓글을 달았지만.. 제가 말실수를 했는데요 C#으로 개발툴을 만드는것에 대한 저평가 이야기였습니다...

  14. Favicon of http://blog.wimy.com zelon 2012.05.14 01:52 신고  댓글주소  수정/삭제  댓글쓰기

    @끼로 아 넵~ 그 댓글은 저도 봤었습니다. 저는 개발툴과는 별개로, 전체적인 게임 업계에서의 C# 이 어느 정도의 위치와 평가를 받고 있는지가 궁금했습니다 ^^;;

    제가 느끼는 게임 업계외(!)의 IT 에서는 Java(안드로이드), ObjectC(iOS), html/javascript(하이브리드 앱들과 웹) 처럼 사실 C++ 다음 세대의 언어들이 많이 쓰이고 있는데, 게임 업계는 아직 C++ 이 메인이고 lua 등의 스크립트 언어를 좀 곁들여쓴다는 느낌을 (개인적으로)가지고 있습니다.

    마영전 등에서 쓰면서 나름 서버에서도 쓰이기 시작하고, 다른 업체에서는 java 로 짠다는 말도 있고, 전체 게임 업계에서의 C++ 다음 메인 언어에 대한 동향?? 같은 게 궁금했습니다 ^^

  15. traeper 2012.10.04 07:17 신고  댓글주소  수정/삭제  댓글쓰기

    드디어 내용 찾았네요..
    소중한 내용 담아갑니다. ㅎㅎ



티스토리 툴바