블로그 이미지
대갈장군

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Notice

2008. 10. 16. 02:47 프로그래밍/MSDN
앞서 본 DLL 이야기 처럼 DLL은 많은 장점을 가지고 있으나 관리하기 쉽지 않다는 문제가 있다.

사람들이 흔히 만드는 DLL의 오류나 에러는 3가지 타입으로 분류된다고 하는데,

Type 1은 사용자나 일부 잘못된 프로그램이 OS가 사용하는 핵심 DLL의 버전을 지 맘대로 바꿔버릴는 경우를 말하는데 최신 버전의 DLL이 구버전으로 바뀌어 버림으로써 오는 문제다.

이 Type 1 DLL 에러는 과거 Windows 98 시절에는 셀수없이 많았다고 한다. 하지만 지금은 그 자취를 거의 감추었다는데 그 이유는 차차 알아보자.

Type 2Side-Effect 라 불리는 오류인데 이것은 DLL이 업데이트 되면서 (최신 버전으로 바뀌면서) 사용자가 의도와는 다르게 변경된 DLL의 함수들이 예상치 못한 작동을 하는 것이라는데 이것은 뭐 DLL을 만든사람의 실수가 아닌가 싶다만... 구버전의 DLL로 잘 작동하던 프로그램은 반드시 새 버전의 DLL과 잘 작동해야 하지만 100% 그렇지는 않다는게 문제란다.

Type 3는 가장 드물지만 DLL 자체에 버그가 있는 경우다. 물론 이건 아주아주 드물기는 해도 발생할 수 있음을 명심해야 한다. 베타 버젼을 즐겨 사용하는 사람이라면 더더욱 그렇겠지...

이런 3가지 타입의 DLL 오류의 제거를 위해서 Windows 2000에서 아주 기발하고 유용한 방법이 제시되는데 그것이 바로 WFP (Windows File Protection) 이라는 것이다.

우선 이것을 이해하기 위해서는 System DLLPrivate DLL을 알아야 한다. 하나의 컴퓨터에는 무수히 많은 DLL이 존재하는데 이것들은 딱 크게 나눠서 두가지의 DLL로 분류가 된다.

System DLL은 Windows/System32 폴더안에 주로 존재하는 '핵심적인' DLL들이다. 즉, OS가 빈번히 사용하며 여러가지의 프로그램에 대해서 많이 사용하는 DLL을 말한다.

고로 이런 System DLL들은 '잘못' 건드리게 되면 윈도우 전체가 멈춰버리거나 죽음의 '퍼런 화면'을 경험하게 될것이다.

그래서 Windows 2000부터는 이런 System DLL들은 사용자가 변경을 하려해도 DLL에 내장되어 있는 Key를 체크하여 잘못된 변경을 하더라도 윈도우가 알아서 재복사를 해버린다.

즉, Service Pack 과 같은 '시스템이 제공하는 업데이트'가 아니고서는 이 System DLL들을 변경 할수 없다. 고로 사용자의 실수로 인한 시스템 전체의 오류는 완전히 차단하는 것이다.

두번째로 Private DLL이다. 이 Private DLL은 다른말로 하면 Side-by-side DLL이다. 흐흐흐... 나는 이 말을 듣는 순간, Side-by-side Assembly가 빠악! 하고 머리에 떠올랐는데 과연 다른 사람도 그럴까? :)

이 Private DLL은 어떻게 보면 DLL의 기본 원리중에 최대 장점이었던 '통일성'을 완전히 역행하는 반역자이지만 '독립성'을 확보하게 해주는, 즉 소소한 프로그램을 많이 사용하는 나같은 프로그래머에게는 꼬옥 필요한 것이다.

원래 DLL은 통일된 하나의 DLL을 사용함으로써 모든 프로그램이 공유하는 DLL 하나를 관리함으로써 유지 보수 및 관리를 쉽게 한다는 장점이 디스크나 메모리 공간 절약보다 더 크다고 말했다.

하지만 Visual Studio를 써본 사람은 알겠지만 Visual Studio는 하나의 프로젝트를 컴파일 할때 .NET Framework가 제공하는 Assembly 들 (일종의 DLL)을 사용하는데 이놈들의 버전이 컴퓨터마다 달라서 기껏 만들어논 프로그램이 다른 컴에서 돌리면 실행조차 되지 않는 이상한 경험을 해보았을 것이다.

이때는 물론 다른 컴퓨터에 Visual Studio를 설치하거나 .NET Framework를 설치해서 업데이트 하면 되겠지만 만약 그런게 없는 컴퓨터라면 어쩔건가?

바로 이런 점이 '통일성'이 가지는 문제점이다. 다른 환경을 가지는 다른 컴퓨터에 대한 '이식성'이 떨어지는데 이걸 해결하기 위한 것이 'Static한 링크' 즉, 통일성과 반대되는 의미로 '유동성 혹은 프로그램의 독립성'이다.

이 Private DLL은 내가 실행하고자 하는 .exe파일과 같은 폴더에 존재하며 .exe 파일에 필요한 DLL 파일을 말한다. 이것은 당연히 System DLL이 아니기 때문에 사용자가 원하는, 필요로 하는 특정한 버전의 DLL을 .exe 파일과 함께 놔두면 되는 것이다.

내가 참고한 MSDN의 문서에서는 Private DLL을 사용한다는 것을 OS인 윈도우에게 알려주기 위해서는 .exe가 있는 폴더에 (Private DLL에 있는 폴더) 간단하게 실행하고자 하는 실행 파일과 같은 이름 + .local 파일을 만들어주면 된다고 되어 있는데 아직까지 이 룰이 적용되는지는 잘 모르겠다.

내가 알기로는 .exe 파일은 기본적으로 현재 실행 파일이 있는 폴더에 필요로 하는 DLL이 있는지 먼저 검색하는 것으로 알고 있다. 물론 테스트 해본적은 없다... -_-

이 Private DLL을 .exe 파일과 함께 두면 .exe 파일은 실행시 Private DLL을 읽게되고 프로그램 제작자는 이 프로그램이 잘 작동하는 Private DLL을 .exe 파일과 같이 제공하면 그만인 것이다. 이렇게 함으로써 '통일성'은 잃게 되지만 Private DLL을 사용하는 .exe 프로그램은 대신에 System DLL에 영향을 전혀 받지 않는 '독립성'을 가지게 된다.

자, 이 두가지의 DLL 분류와 WFP를 이용하면 위에서 나열한 거의 모든 Type 에러를 해결할 수 있다.

System DLL은 윈도우가 자동으로 관리함으로써 사용자의 미숙으로 인한 잘못을 예방하고, Private DLL을 이용함으로써 환경이 다른 컴퓨터로 이식성 및 독립성을 증가 시켰다.


이 문서에서는 DLL Universal Problem Solver라는 프로그램(DUPS: 둡스!?)을 이용해서 네트워크 상에 있는 모든 연결된 컴퓨터에 대해 DLL 버전을 체크하고 문제가 있는지 검사하는 프로그램에 대해 간단히 설명하고 있는데 사실 나에게는 아직 필요가 없는 듯해서 한번 읽고 넘어 갔지만, 게임방 사장님들에게는 필요한 유틸리티가 아닌가 싶다...

워낙 우리나라 게이머들께서 겜방 PC로 이것 저것 많이 테스트 하시니...  ^^
posted by 대갈장군
2008. 10. 15. 07:00 프로그래밍/MSDN

DLL HELL이라는 단어를 처음 접하고 '풋' 하고 웃었다면 당신은 이미 DLL로 인해 머리 한움큼 정도는 뽑아본 사람이 분명하다..

MSDN에서 머리에 쏙쏙 들어올만한 정보를 찾아 수없는 클릭질을 해댄지 어언 10분. 재미있는 재목의 글을 찾았다. 이름하여 THE END OF DLL HELL.

이름만 봐도 재미있을 것 같아서 차근 차근 읽어보았다.

DLL에 대해서 그 개념 정도는 충분히 이해하고 있었으나 정작 DLL이 일으켜온 문제와 어떤 방식으로 DLL의 문제를 종식시켜 나갔는지는 전혀 몰랐다. 이번을 계기로 확실히 알고 넘어가게 되었다.

DLL은 풀어서 쓰면 Dynamic Library Link인데 한국어로 하면 '동적 라이브러리 링크'이다.

C 프로그램을 작성하게 되면 우리는 생각없이 헤더 파일을 포함하는데 대표적인 헤더 파일이 바로 stdio.h파일이라던가 iostream 같은 파일들이다.

이 헤더 파일들은 실행 코드를 담고 있는, 즉 다시말해서, 실행에 필요한 모든 코드를 가지고 있는 것이 아니라 prototype만 담고 있는 글에 불과하다. 책의 '목차'에 불과한 것이다.

실제 '내용'은 바로 라이브러리가 담고 있는데 라이브러리는 참 많다... -_- 짜증나게 많고 다양하다.

대표적으로 Visual Studio 가 사용하는 라이브러리는 MSVCRT.DLL이다. 걍 이놈이란 놈이 자주 쓰인다고 알고 있자.

이 라이브러리는 수많은 윈도우 프로그램들에서 사용이된다. 프로그래머가 짜는 거의 대다수의 프로그램도 이 라이브러리를 사용하게 되는데 자 그렇다면 여기서 생각을 해보자.

만약에 저 라이브러리를 사용하는 모든 프로그램들이 저 MSVCRT.DLL을 하나씩 소유한다면 얼마나 비효율적일까?

우선 하드드라이브에 저 파일이 수십 수백개 만들어질꺼고, 게다가 내 피같은 RAM (메모리)에 저 파일이 수십 수백개가 생길것이다.

즉, 메모리 낭비요 게다가 하드디스크의 낭비다. 물론 요즘 메모리나 하드디스크가 똥값이긴 해도 그래도 낭비는 낭비다!

DLL을 개발해낸 사람의 최후의 카드는 바로 'Consistency' 즉 '일관성' 혹은 '통일성'에 있다. 바로 이것이 DLL의 가치를 드높여 주는 것인데, 이것이 무엇인가 하면...

많은 프로그램이 공유하게 되는 저 MSVCRT.DLL 파일 하나를 수정함으로써 혹은 업뎃함으로써 모든 프로그램의 동작을 수정할 수 있게된다. 즉, 사용되는 파일을 단 하나로 통일 시킴으로 얻게되는 장점이 바로 그것이다.

허나 반대로 말하면 저 파일 하나가 잘못됨으로써 저것을 사용해서 프로그램을 돌리는 모든 것이 한방에 동작을 안할 수도 있다는 말이다. 허걱. 이 얼마나 위험한 일인가...

바로 이런 문제들이 Windows 98이전에는 수도없이 빈번했으며 인증되지 않은 프로그램에 의해서 혹은 사용자의 실수에 의해서 중요한 DLL 파일이 바뀌어지는 바람에 수많은 프로그램이 갑자기 동작을 멈추게 되었고 이로인해 수많은 머리털이 뽑히거나 하드디스크의 쓸데없는 포멧으로 이어진것이다.... 헐헐헐..

이것을 어떻게 고쳤는가는... 다음 글에서 써야 겠다. 집에 가야 할 시간이 왔군요.. ^^

참조 http://msdn.microsoft.com/en-us/library/ms811694.aspx 

posted by 대갈장군
2008. 10. 14. 06:28 프로그래밍/MSDN

사실 Visual Studio 2005 (VS 2005)를 사용해 오면서 신경쓰지 않았던 부분이 바로 이 manifest file의 존재다.

아마 이글을 본사람들 중에 상당수도 '저게 뭐임?' 이라고 생각하는 사람이 많을 것이라 생각된다.

이 manifest의 중요성은 내가 만든 프로그램을 다른 컴퓨터에서 돌려볼려고 할때 나타난다. 골 깨지는 소리와 함께... 두둥.

전반적인 설명을 위해 자세한 내용은 무시하고 개념만 적어보자. 내가 잘 까먹으므로 이렇게 적어놓는게 훨씬 도움이 됨...

일단 .NET Framework를 알아야 한다. 다 알거는 없고, 이 녀석은 간단히 말해서 컴퓨터의 spec에 관계 없이 VS 2005로 작성한 프로그램이 잘 돌아가도록 해주는 놈이라고 생각하자.

그니까 int가 4바이트인 컴퓨터건 8바이트인 컴퓨터건 (32 비트 컴퓨터냐 64 비트 컴퓨터냐) 관계없이 내가 짠 프로그램이 잘 돌아가도록 해준다.

상식적으로 .NET Framework가 컴퓨터에 관계없이 내 프로그램을 잘 돌아가게 만들어 준다는 것은 .NET Framework가 어떤 공통적인 무언가를 가지고 있거나 아니면 통일된 규정에 따라서 해당 컴퓨터에 맞게 Binary code (실행 코드)를 만들어 낸다는 것이다.

사실 많은 중요하지만 이해하기 쉽지 않은 단어들이 있다. CLR, CRL, CIL, MSIL, 등등등... 이런것들은 VS 2005 책이나 VS 2008 졸라 두꺼운 책 (빨강색) 에 보면 잘 나와있다. 아니면 MSDN 혹은 Wiki를 머리 뽑으면서 읽으면 이해할 수 있다.

이런거 모두 제끼고, .NET Framework Assemblies을 보자. 이놈들은 일부만 컴파일된 dll 파일들이다. 이놈들은 프로그램이 실행되는 시점 (CLR이 Just-in-time complier를 이용해 machine code를 생성해 내는 시점) 에 호출되어 사용된다.

한마디로 필요한 정보가 저장된 도서관(library)이다. 중요한 것은 이제부터인데, VS 2005를 이용해서 프로그램을 작성하게 되면 기본적으로 VS 2005는 manifest 파일이라는 것을 만들어낸다.

이 manifest 파일은 만들어진 프로그램이 어떤 .NET Framework Assemblies를 사용하는지 그리고 그 버젼은 무엇인지, 또 의존성은 어떻게 되는지 등등등을 설명하고 있다.

한번이라도 VS2005를 이용해서 프로그램을 만들어 본 사람이라면 Debug 혹은 Release 폴더에 manifest 라는 단어가 들어가 있는 파일이 생성된 것을 보았을 것이다.

그 파일을 열어서 보게 되면 안에 내용중에 이런 부분이 있다,
<assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>

이것이 바로 .NET Assemblies 중에 어떤 버전을 사용하고 있는지 그리고 Debug인지 Release인지, 또 x86 인지 아닌지, 게다가 publickey 까지 자세히 보여주고 있다.

여기서 주목할 것은 name과 version이다. 윈도우 폴더 아래에 있는 WinSxS 라는 폴더를 열어보자. 여기를 열어보면 저 name과 비스무리한 폴더들이 보인다. 그리고 폴더 이름뒤에 version 번호도 보인다.

바로 WinSxS 폴더가 현재 컴퓨터에 설치되어 있는 Shared Side-by-Side Assemblies를 모두 보여준다. 한마디로 이 컴퓨터에서 제공되는 모든 .NET Framework Assemblies를 담고 있는 폴더인 셈이다.



만약 어떤 프로그램을 실행했는데 터무니 없게도 This system cannot execute from this program. 이라든가 위 그림처럼 어이없는 에러가 터진다면 가장 먼저 실행 파일과 함께 있는 manifest 파일을 열어보자. 그리고 그 컴퓨터의 WinSxS 폴더를 열어서 해당 어셈블리가 존재하는지 확인하자.
사용자 삽입 이미지

만약 manifest 파일에 나와있는 Version과 같은 폴더가 존재하지 않는다면 이 컴퓨터는 프로그램을 돌리기 위한 자료가 없기 때문에 실행할 수 없다는 '어이가 없는' 에러 메세지를 내 보낸다.

이것을 해결하기 위한 방법으로는, (확인해 본적은 없지만)
1) 만약 컴퓨터에 VS 2005나 VS 2008이 설치되어 있다면 업데이트를 해라. (업데이트를 하면 새로운 버젼의 어셈블리들이 자동으로 설치된다.)
2) Manifest를 열어서 해당되는 버젼의 DLL 파일을 찾아서 실행파일이 있는 폴더에 복사해 넣어라. 이것은 100% 된다고 장담할 수 없다. .NET Framework가 설치조차 안되어 있다면 아마도 작동 안될 것이다.
3) Microsoft Visual C++ Redistribution (재배포) Package 를 다운받아 설치하라. 

3번이 가장 합리적인 방법인데 구글에서 Visual C++ Redistribution package를 검색하면 Visual Studio 2005 와 2008 버전에 대한 각각의 다운로드들이 뜬다. 이 파일을 다운로드 하여 설치하면 WinSxS 폴더에 해당 버전의 어셈블리들이 자동으로 설치되며 Visual Studio가 없는 사람들도 프로그램을 돌릴수 있게 된다.

재배포 파일의 설치가 가장 중요하다... 이 프로그램은 이 컴퓨터에서 돌아갈수 없다는 말도 안되는 문제를 거의 100% 해결해 준다.


posted by 대갈장군
2008. 10. 14. 05:14 나의 이야기

1등을 해야한다는 강박관념...
뒤쳐지면 안된다는 두려움...
떠나간 사람에 대한 슬픔...
제한된 시간에 대한 미련...
죽음이 가져올 영원한 암흑에 대한 공포...

매 순간 어깨를 누르는 것들을 털어버리자..
등뒤에서 나를 떠밀던 것들을 털어버리자..

주어진 시간이 끝날땐, 저것들에 의해 이끌려온 내가 아닌,
후회없이 살아온 '나'이고 싶다.


'나의 이야기' 카테고리의 다른 글

친구 이야기  (2) 2008.12.12
죽기전에 "한번" 가볼만한 곳 - 알래스카  (0) 2008.10.16
방관자 효과  (0) 2008.01.15
그렇고 그런 이야기  (0) 2007.11.15
오늘의 단편 뉴스  (1) 2007.11.14
posted by 대갈장군
2008. 7. 21. 23:45 프로그래밍/MSDN
내가 만든 프로그램이 어떤 Visual C++ Library들을 사용하는지 확인하고 싶다면 Property Pages에 있는 General Tab에 들어가보면 쉽게 알수 있다.

예를 들어서 내 프로젝트가 MFC나 ATL을 쓰는지도 쉽게 알수 있다. 만약 내가 MFC를 Dynamic Library에서 사용하고 있다면 내 프로그램은 MFC DLLs에 의존하게 된다. (mfc90.dll) 만약 내 프로그램이 MFC나 ATL을 사용하지 않는다해도 Multi-threaded debug DLL (/MDd)를 Runtime Library로 사용할 경우 프로그램은 여전히 CRT library에 의존하게 된다.

가장 좋은 방법은 depends.exe (Dependency Walker)를 실행해보는 방법이라는데 이것은 /Microsoft Visual Studio 2005/Common7/Tools/bin 에 설치 되어 있단다. 이건 Win32 SDK Tool을 설치한 경우에만 설치되어 있단다.

이 depends.exe를 실행하면 모든 로딩되는 DLLs를 다 알수 있다. (static이건 dynamic 이건간에)

이 프로그램을 실행할때 주의해야 할것은 하나의 DLL은 또다른 DLL 파일 혹은 특정 버젼의 DLL에 의존할수도 있다는 점이다.

모든 DLLs 리스트를 받고나서는 이중에서 어떤 DLLs들이 다른 컴퓨터에서 실행될때 redistricution되어야 하는지를 결정해야 한다는데 대부분의 경우는 system DLLs들은 redistribution 할 필요가 없다. 하지만 Visual C++ library는 redistribution 할 필요가 있다는 군. 아마도 이게 문제 아닐까?

'프로그래밍 > MSDN' 카테고리의 다른 글

[MSDN] /MT, /MTd, /MD, /MDd (C Runtime Library Option)  (12) 2008.10.22
[MSDN] COM (Component Object Model)  (2) 2008.10.17
[MSDN] End of DLL War!  (0) 2008.10.16
[MSDN] DLL HELL!!!! (1)  (0) 2008.10.15
[MSDN] Manifest 파일?!?!  (0) 2008.10.14
posted by 대갈장군
2008. 2. 23. 04:28 프로그래밍/DirectX
이 장을 끝으로 이 부분은 끝나네. DX에 대해서 더 알고 싶다면 어떤 것을 더 읽어보아야 할까?

이 장에서 만들어 볼것은 우선 레이더 스크린과 체력바, 그리고 총알 숫자 표시다.

1. A Too-Simple Wrapper for Sprites
처음으로 Wrapper라는 단어가 나온듯 하다. 원래 부터 복잡한 DX의 속성상, 모든것을 단순화 시켜주는 함수가 있다면 좋을 것이다.

이 튜토리얼에서는 두개의 함수에 대해서 랩 함수를 만드는데 하나는 텍스쳐 파일 로딩 ㅎ마수 다른 하나는 파일의 일부만 렌더링 하는 함수다.

고로 의역하자면 wrapper 함수란 복잡한 인자를 가지는 함수를 간단하게 표현하는 둘러싸기 함수다.

1-1. LoadTexture()

void LoadTexture(LPDIRECT3DTEXTURE9* texture, LPCTSTR filename)
{
    D3DXCreateTextureFromFileEx(d3ddev, filename, D3DX_DEFAULT, D3DX_DEFAULT,
        D3DX_DEFAULT, NULL, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT,
        D3DX_DEFAULT, D3DCOLOR_XRGB(255, 0, 255), NULL, NULL, texture);

    return;
}

복잡했던 텍스쳐 로딩 함수를 단 두인자로 압축해 버렸다. 특이점은 color key가 핫 핑크로 설정된점, 그리고 alpha 채널이 포함된점이다.

1-2. DrawTexture()

이 놈은 쉽지 않다는 구나. 여기는 더 많은 인자가 필요로 하다. 각각의 그래픽이 그려지기 위해서는 텍스쳐의 위치 사이즈 스크린에서의 위치등을 정해 줘야 한다. 게다가 어떤 텍스쳐를 사용할지도 지정해야 한다. 벌써 7개의 인자가 필요하다.

일단 RECT 구조체를 사용해서 텍스쳐의 사이즈와 위치를 나타내자.

void DrawTexture(LPDIRECT3DTEXTURE9 texture, RECT texcoords, int x, int y, int a)
{
    D3DXVECTOR3 center(0.0f, 0.0f, 0.0f), position(x, y, 0.0f);
    d3dspt->Draw(texture, texcoords, &center, &position, D3DCOLOR_ARGB(a, 255, 255, 255));

    return;
}

2. Constructing the Radar

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 5.1 - The Radar Sprites


위의 그림이 레이더를 구성하는 요소들이다. 중요한건 각각의 스프라이트는 핑크로 배경색이 채워져 있어 컬러키를 이용해서 필요한 부분만 렌더링이 가능하다.

2-1. The Code
레이더를 그리기 위해서는 그리는 순서를 정확하게 할 필요가 있다. 왜냐구? 만약 순서가 틀리면 (빨간점이 가장 나중에 그려지지 않으면) 잘못된 결과가 나온다. 그래서 그리는 순서는 1. 투명한 그리드 2. 빨간점. 3. 테두리 이다.

// globals
LPDIRECT3DTEXTURE9 DisplayTexture;
float enemyX = 60.0f, enemyY = 60.0f;

void LoadDisplay()
{
    LoadTexture(&DisplayTexture, L"DisplaySprites.png");

    return;
}

void DrawDisplay()
{
    RECT Part;

    // DRAW THE RADAR
    // display the backdrop
    SetRect(&Part, 2, 14, 169, 181);
    DrawTexture(DisplayTexture, Part, 10, 10, 127);
     
    // display the enemy
    SetRect(&Part, 341, 14, 344, 17);
    DrawTexture(DisplayTexture, Part, enemyX, enemyY, 255);

    // display the border
    SetRect(&Part, 171, 13, 340, 182);
    DrawTexture(DisplayTexture, Part, 9, 9, 255);

    return;
}

여기서 보면 두개의 함수로 하는데 LoadDisplay() 함수와 DrawDisplay() 함수다.

우선 LoadDisplay() 함수로 텍스쳐를 로딩해 놓는다. 이 텍스쳐에는 필요한 모든 이미지가 들어가 있고 배경은 핑크색이다. 그런다음 SetRect 함수를 이용해서 원하고자 하는 영역만 설정한다. (그리고자 하는 영역만 설정, 텍스쳐 안에서)  그런다음에 적당한 좌표에다가 그려준다.

2-2. Fixing the Radar

이것의 문제점이 하나 있으니 바로 적군을 표시하는 빨간점이 레이다 영역 밖에서도 표시된다는 점이다. 고로 이걸 해결하기 위해서는 빨간점을 특정한 영역 내에 있을때만 표시하도록 해야 한다.

점 1과 점 2사이의 거리를 측정하는 방법은 다음과 같지요.

Distance = √(x1 - x2)² + (y1 - y2)²

이걸 이용해서 빨간점과 중심과의 거리를 잰다. 만약 넘어가면 그리지 않는다. 고로 다음과 같이 코드가 바뀐다.

// globals
LPDIRECT3DTEXTURE9 DisplayTexture;
float enemyX = 60.0f, enemyY = 60.0f;

void LoadDisplay()
{
    LoadTexture(&DisplayTexture, L"DisplaySprites.png");

    return;
}

void DrawDisplay()
{
    RECT Part;

    // DRAW THE RADAR
    // display the backdrop
    SetRect(&Part, 2, 14, 169, 181);
    DrawTexture(DisplayTexture, Part, 10, 10, 127);
     
    // if the enemy is within 84 units of the player, display the enemy
    if(sqrt((92 - enemyX) * (92 - enemyX) + (92 - enemyY) * (92 - enemyY)) < 84)
    {
        SetRect(&Part, 341, 14, 344, 17);
        DrawTexture(DisplayTexture, Part, enemyX, enemyY, 255);
    }

    // display the border
    SetRect(&Part, 171, 13, 340, 182);
    DrawTexture(DisplayTexture, Part, 9, 9, 255);

    return;
}

3. Constructing the Health Bar

이제 피가 남아있는 걸 보여주는 바를 만들 차례인데, 이 아이템은 딱 두개의 그래픽을 가진다.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 5.2 - The Health Bar Sprites

그림이 조금 잘 안보이지만 윗 그림의 제일 오른쪽 편에 보면 조그마한 막대로 하나 있다.

방법은 간단하다. 우선 바를 그리고 %만큼 필터를 채워나가면 된다. 여기서 필터란 윗 그림에서 잘 안보이는 조그만 막대를 말한다.
Image 1.1 - DirectGrid.png Displayed in DirectX

Image 5.3 - The Health Bar Pieced Together at Varying Percentages

// globals
LPDIRECT3DTEXTURE9 DisplayTexture;
float enemyX = 60.0f, enemyY = 60.0f;
int health = 300;
int maxhealth = 1000;


void LoadDisplay()
{
    LoadTexture(&DisplayTexture, L"DisplaySprites.png");

    return;
}

void DrawDisplay()
{
    RECT Part;

    // DRAW THE RADAR
    // display the backdrop
    SetRect(&Part, 2, 14, 169, 181);
    DrawTexture(DisplayTexture, Part, 10, 10, 127);

    // if the enemy is within 84 units of the player, display the enemy
    if(sqrt((92 - enemyX) * (92 - enemyX) + (92 - enemyY) * (92 - enemyY)) < 84)
    {
        SetRect(&Part, 341, 14, 344, 17);
        DrawTexture(DisplayTexture, Part, enemyX, enemyY, 255);
    }

    // display the border
    SetRect(&Part, 171, 13, 340, 182);
    DrawTexture(DisplayTexture, Part, 9, 9, 255);

    // DRAW THE HEALTHBAR
    // display the bar
    SetRect(&Part, 1, 1, 505, 12);
    DrawTexture(DisplayTexture, Part, 11, 456, 255);

    // display the health "juice"
    SetRect(&Part, 506, 1, 507, 12);
    for(int index = 0; index < (health * 490 / maxhealth); index++)
        DrawTexture(DisplayTexture, Part, index + 18, 456, 255);


    return;
}

흠. 복잡해 보이지만 (상수가 많아서) 그닥 어려운 코드는 아니다. 다만 정확한 텍스쳐 내부의 좌표를 꿰고 있어야 한다는 단점이 있네... 왜 텍스쳐를 따로 쓰지 않을까? :)

4. Constructing the Ammo Indicator

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 5.4 - The Ammo Indicator Sprites

위의 두 그림을 이용할 텐데 오른쪽 그림 하나만 있어도 될거 같은디?

// globals
LPDIRECT3DTEXTURE9 DisplayTexture;
float enemyX = 60.0f, enemyY = 60.0f;
int health = 300;
int maxhealth = 1000;
int ammo = 10394;
LPD3DXFONT dxfont;


void LoadDisplay()
{
    LoadTexture(&DisplayTexture, L"DisplaySprites.png");

    D3DXCreateFont(d3ddev, 12, 0, FW_NORMAL, 1, false, DEFAULT_CHARSET,
                   OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
                   L"Arial", &dxfont);


    return;
}

void DrawDisplay()
{
    RECT Part;

    // DRAW THE RADAR
    // display the backdrop
    SetRect(&Part, 2, 14, 169, 181);
    DrawTexture(DisplayTexture, Part, 10, 10, 127);

    // if the enemy is within 84 units of the player, display the enemy
    if(sqrt((92 - enemyX) * (92 - enemyX) + (92 - enemyY) * (92 - enemyY)) < 84)
    {
        SetRect(&Part, 341, 14, 344, 17);
        DrawTexture(DisplayTexture, Part, enemyX, enemyY, 255);
    }

    // display the border
    SetRect(&Part, 171, 13, 340, 182);
    DrawTexture(DisplayTexture, Part, 9, 9, 255);

    // DRAW THE HEALTHBAR
    // display the bar
    SetRect(&Part, 1, 1, 505, 12);
    DrawTexture(DisplayTexture, Part, 11, 456, 255);

    // display the health "juice"
    SetRect(&Part, 506, 14, 507, 12);
    for(int index = 0; index < (health * 490 / maxhealth); index++)
        DrawTexture(DisplayTexture, Part, index + 18, 456, 255);

    // DRAW THE AMMO INDICATOR
    // display the backdrop
    SetRect(&Part, 351, 14, 456, 40);
    DrawTexture(DisplayTexture, Part, 530, 449, 127);

    // display the border
    SetRect(&Part, 351, 45, 457, 72);
    DrawTexture(DisplayTexture, Part, 530, 449, 255);

    // display the font
    SetRect(&Part, 535, 453, 630, 470);
    static char strAmmoText[10];
    _itoa_s(ammo, strAmmoText, 10);
    dxfont->DrawTextA(NULL,
                      (LPCSTR)&strAmmoText,
                      strlen((LPCSTR) &strAmmoText),
                      &textbox,
                      DT_RIGHT,
                      D3DCOLOR_ARGB(255, 120, 120, 255));

      return;
}

위에서 보면 _itoa_s 함수로 정수를 문자열로 바꾼다. 근데 저기 계속 해서 등장하는 LPCSTR등은 참으로 짜증난다.

5. The Finished Program
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface
LPD3DXFONT dxfont;    // the pointer to the font object
float enemyX = 60.0f, enemyY = 60.0f;    // the enemy position
int health = 300;    // the player's current hitpoints
int maxhealth = 1000;    // the player's max hitpoints

int ammo = 10394;    // the player's current ammo

// sprite declarations
LPDIRECT3DTEXTURE9 DisplayTexture;    // the pointer to the texture

// function prototypes
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);    // renders a single frame
void cleanD3D(void);    // closes Direct3D and releases memory
void load_display();
void draw_display();
void LoadTexture(LPDIRECT3DTEXTURE9* texture, LPCTSTR filename);
void DrawTexture(LPDIRECT3DTEXTURE9 texture, RECT texcoords, float x, float y, int a);


// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;


    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

    load_display();

    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a deep blue
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // begins the 3D scene

    d3dspt->Begin(D3DXSPRITE_ALPHABLEND);    // begin sprite drawing with transparency

    draw_display();

    d3dspt->End();    // end sprite drawing

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    DisplayTexture->Release();
    d3ddev->Release();
    d3d->Release();

    return;
}


// this loads the display graphics and font
void load_display()
{
    LoadTexture(&DisplayTexture, L"DisplaySprites.png");

    D3DXCreateFont(d3ddev, 20, 0, FW_BOLD, 1, false, DEFAULT_CHARSET,
                   OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
                   L"Arial", &dxfont);

    return;
}


// this draws the display
void draw_display()
{
    RECT Part;

    // DRAW THE RADAR
    // display the backdrop
    SetRect(&Part, 2, 14, 169, 181);
    DrawTexture(DisplayTexture, Part, 10, 10, 127);

    // if the enemy is within 84 units of the player, display the enemy
    if(sqrt((92 - enemyX) * (92 - enemyX) + (92 - enemyY) * (92 - enemyY)) < 84)

    {
        SetRect(&Part, 341, 14, 344, 17);
        DrawTexture(DisplayTexture, Part, enemyX, enemyY, 255);
    }

    // display the border
    SetRect(&Part, 171, 13, 340, 182);
    DrawTexture(DisplayTexture, Part, 9, 9, 255);

    // DRAW THE HEALTHBAR
    // display the bar
    SetRect(&Part, 1, 1, 505, 12);
    DrawTexture(DisplayTexture, Part, 11, 456, 255);

    // display the health "juice"
    SetRect(&Part, 506, 1, 507, 12);
    for(int index = 0; index < (health * 490 / maxhealth); index++)
        DrawTexture(DisplayTexture, Part, index + 18, 456, 255);

    // DRAW THE AMMO INDICATOR
    // display the backdrop
    SetRect(&Part, 351, 14, 456, 40);
    DrawTexture(DisplayTexture, Part, 530, 449, 127);

    // display the border
    SetRect(&Part, 351, 45, 457, 72);
    DrawTexture(DisplayTexture, Part, 530, 449, 255);

    // display the font
    SetRect(&Part, 535, 453, 630, 470);
    static char strAmmoText[10];
    _itoa_s(ammo, strAmmoText, 10);
    dxfont->DrawTextA(NULL,
                      (LPCSTR)&strAmmoText,
                      strlen((LPCSTR) &strAmmoText),
                      &Part,
                      DT_RIGHT,
                      D3DCOLOR_ARGB(255, 120, 120, 255));

    return;
}


// this loads a texture from a file
void LoadTexture(LPDIRECT3DTEXTURE9* texture, LPCTSTR filename)
{
    D3DXCreateTextureFromFileEx(d3ddev, filename, D3DX_DEFAULT, D3DX_DEFAULT,
        D3DX_DEFAULT, NULL, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT,
        D3DX_DEFAULT, D3DCOLOR_XRGB(255, 0, 255), NULL, NULL, texture);

    return;
}


// this draws a portion of the specified texture
void DrawTexture(LPDIRECT3DTEXTURE9 texture, RECT texcoords, float x, float y, int a)
{
    D3DXVECTOR3 center(0.0f, 0.0f, 0.0f), position(x, y, 0.0f);
    d3dspt->Draw(texture, &texcoords, &center, &position, D3DCOLOR_ARGB(a,255, 255, 255));

    return;
}

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 5.5 - The enemy is coming, and you're low on health.  Good thing there's LOTS of ammo!
posted by 대갈장군
2008. 2. 23. 02:28 프로그래밍/DirectX

이제 글자를 넣어보자.. 이거네

1. All About Fonts
font를 결정하는 세가지 요소가 있으니 바로 typeface, style 그리고 size다.

1-1. Typeface
타입페이스는 폰트의 모양, 길이 그리고 스트로크등을 결정하는 요소다. serif도 typeface를 결정하는 요소인데 이 serif라는 놈을 찾아보니 글자 모양 중에 끝부분에 각이진것을 serif라 하고 없는 것을 san serif라 한다. 다음 그림을 보면 그 예가 있는데

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 4.1 - Sans Serif and Serif

Times New Roman의 글자 중 M자를 보면 아래쪽에 꺽이는 짧은 라인이 들어간다. 위에도 들어간다. 하지만 San serif에는 그런게 없다. 그것이 차이점이란다... 흠... 그랬군..

1-2. Style
Style은 사실 typeface의 변형이다. 이것은 글자의 무게나 기울기등을 담당한다. 무게라함은 사실 blod냐 아니냐를 말하고 기울기라 함은 italics냐 아니냐를 말한다.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 4.2 - Italic and Bold Styles

1-3. Size
이제는 크기다. 일반적으로 크기라 함은 글자의 제일 윗부분에서 제일 아랫부분의 크기인데 아래 그림에서 보다시피 의외로 낭비되는 공간이 있네.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 4.3 - Arial Font Size


이것들을 외우고 있을 필요는 없지만 나중에 사용할때는 반드시 이해하고 있는것이 좋다. 당삼하지요.

2. Creating Fonts and Displaying Text
글자를 그려내는것은 과정상으로 보면 되게 간단한데 다음과 같이 2단계만 거치면 된다.

1. Create the font
2. Draw the text using the created font

2-1. Create the font
폰트를 만들어내야 하는데 만들기 위해선 D3DXCreateFont() 함수를 호출해야 한다.

HRESULT D3DXCreateFont(LPDIRECT3DDEVICE9 pDevice,
                       INT Height,
                       UINT Width,
                       UINT Weight,
                       UINT MipLevels,
                       BOOL Italic,
                       DWORD CharSet,
                       DWORD OutputPrecision,
                       DWORD Quality,
                       DWORD PitchAndFamily,
                       LPCTSTR pFacename,
                       LPD3DXFONT* ppFont);

LPDIRECT3DDEVICE9 pDevice : 이건뭐 d3ddev다

INT Height : g의 아래쪽 부터 M의 위쪽까지의 크기다. 일반적인 사이즈는 12이다.

UINT Width : 폰트의 폭인데 이것은 각각의 글자마다 다르므로 딱히 하나로 지정할 수는 없고 0으로 설정하면 알아서 적당한 크기의 글자로 찍어낸다. 이 인자는 초기값을 주는 것으로써 아마도 크게 주면 크게 줄수록 글자들 자체가 옆으로 늘어날것으로 예상. (테스트 해보자)

UINT Weight : 글자의 두께. 0에서 1000까지 된다는 군. 0으로 놓으면 기본값으로 표현하기. 이 인자에 대해서는 특별히 두께 플래깅이 존재하는데 다음 테이블과 같다.

Flag Value
FW_DONTCARE 0
FW_THIN 100
FW_ULTRALIGHT 200
FW_LIGHT 300
FW_REGULAR 400
FW_MEDIUM 500
FW_DEMIBOLD 600
FW_BOLD 700
FW_ULTRABOLD 800
FW_BLACK 900
[Close Table]

UINT MipLevels : 밉맵 사용 레벨 설정인데 여기서는 1로 해놔도 됨

BOOL Italic : 이것은 이탤릭 체를 사용할 건가 말건가에 대한 설정. TRUE혹은 FALSE설정

DWORD CharSet : 이것은 어떤 글자셋을 사용할 건지 설정하는 건데 일반적으로는 English set으로 설정한다. 왜? 여기는 미국이니까... -_- 각각의 셋에 대해서 알고 싶다면 (한글, 중국어등) 문서를 찾아보면 된단다. 일단 영어를 기본으로 하니까 DEFAULT_CHARSET으로 쓰면 OK

DWORD OutputPrecision : 흠, 이것은 매치되는 글자체가 없는 경우 윈도우가 선택해야 할 글자체를 말하나 본데 일반적으로는 OUT_DEFAULT_PRECIS를 선택해서 사용한단다. 이 인자는 조금 이해가 안된다만 일단 넘어가자.

DWORD Quality : 폰트의 질을 말하는데 다음 테이블과 같은 값이 들어갈 수 있다.

Value Description
DEFAULT_QUALITY This indicates that the quality of the font does not matter.
NONANTIALIASED_QUALITY This indicates that the edges of the font will not be smooth.  It is faster and more efficient than quality types where smooth edges are used.
ANTIALIASED_QUALITY This indicates that the edges of the font are smooth.

Note that if you don't use either this flag or the prior one, DirectX will use antialiased fonts only if the user has selected "Smooth Screen Fonts" in the Control Panel.
PROOF_QUALITY This indicates that the quality of the font is very high, and exactly matches the font image.  However, because font images are not given in every size, this setting limits which sizes you can use, and using different fonts will cause DirectX to choose the closest font size supported.
DRAFT_QUALITY This indicates that the quality of the font is less important than in PROOF_QUALITY, but it allows you to select different sizes of fonts.  Be warned, however, using various styles, such as bold and italic, can slow this one down if you have a lot of fonts.
[Close Table]

DWORD PitchAndFamily : pitch와 font family는 폰트에 추가되는 새로운 2가지 속성이라는데 pitch는 1 인치에 들어가는 글자의 갯수를 말하고 Font Family는 비슷한 형태와 스타일을 가지는 그룹을 말한다.

일반적으로 내가 굉장히 폰트에 민감해 하지 않는다면 DEFAULT_PITCH || FF_DONTCARE로 놓으면 된다만 정말로 정확하게 하고 싶다면 문서를 뒤져봐라.

LPCTSTR pFacename : 폰트 이름을 지정하는 문자열에 대한 포인터. 일반적으로 Arial, Times New Roman 같은게 되겠죠.

LPD3DXFONT* ppFont : 폰트 오브젝트에 대한 포인터. 이 오브젝트가 폰트에 대한 모든 정보를 담게 된다.

LPD3DXFONT font_1;    // create a font object

D3DXCreateFont(d3ddev,    // the Direct3D Device
               20, 0,    // font size twenty with the default width
               FW_NORMAL,    // normal font weight
               1,    // no mipmap levels
               FALSE,    // not italic
               DEFAULT_CHARSET,    // default character set
               OUT_DEFAULT_PRECIS,    // default precision
               DEFAULT_QUALITY,    // default quality
               DEFAULT_PITCH || FF_DONTCARE,    // more defaults...
               L"Arial",    // typeface "Arial"
               &font_1);    // address of the font object created above

이제 다 만들었다.

2-2. Draw the text using the created font
이제 만들어진 폰트를 이용해서 그려보자. 음 DrawText라는 함수가 따로 존재하는구나...

INT DrawText(LPD3DXSPRITE pSprite,
             LPCTSTR pString,
             INT Count,
             LPRECT pRect,
             DWORD Format,
             D3DCOLOR Color);

LPD3DXSPRITE pSprite : 쪽화면 인자네... 이것을 사용하여 글자를 그려내는데 만약 안 만들었다면 NULL로 놓아도 무방하나 만약 빈번하게 재사용할 글자라면 만들어서 사용하는것이 효율적인 면에서 낫다.

LPCSTR pString : 디스플레이 하고 싶은 글자다.

INT Count : 디스플레이 할 글자수.

LPRECT pRect : 어디에 글자가 디스플레이 되어야 할지 설정해주는 사각형 좌표.

DWORD Format : 레이아웃을 결정하는 인자. 다음 테이블 참조.

[Table 4.3 - Font Format Flags]
Value Description
DT_SINGLELINE Displays the text on a single line, regardless of line breaks or carraige returns.
DT_CALCRECT Calculates the height and width of the rectangle for you, regardless of the values stored specified in the pRect parameter.
DT_CENTER Centers the text in the rectangle.
DT_LEFT Aligns the text to the left side of the rectangle.
DT_RIGHT Aligns the text to the right side of the rectangle.
DT_TOP Justifies the text to the top of the rectangle you select.
DT_BOTTOM Justifies the text to the bottom of the rectangle you select.  It only works when combined with DT_SINGLELINE
DT_VCENTER Justifies the text in the center of the rectangle.  It only works on single lines.
DT_WORDBREAK Lines are automatically broken between words when text reaches the end of a rectangle, thus adding an additional line.
[Close Table]

D3DCOLOR Color : 글자의 색상.

dxfont->DrawTextA(NULL,
                      "Hello World...FINALLY!",
                      22,
                      &textbox,
                      DT_CENTER | DT_VCENTER,
                      D3DCOLOR_ARGB(255, 255, 255, 255));

여기서 textbox는 전체 화면 사이즈 인듯.

3. The Finished Program

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPD3DXFONT dxfont;    // the pointer to the font object

// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass1";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",
                          L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP,
                          0, 0,
                          SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;


    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    D3DXCreateFont(d3ddev,    // the D3D Device
                   30,    // font height of 30
                   0,    // default font width
                   FW_NORMAL,    // font weight
                   1,    // not using MipLevels
                   true,    // italic font
                   DEFAULT_CHARSET,    // default character set
                   OUT_DEFAULT_PRECIS,    // default OutputPrecision,
                   DEFAULT_QUALITY,    // default Quality
                   DEFAULT_PITCH | FF_DONTCARE,    // default pitch and family
                   L"Arial",    // use Facename Arial
                   &dxfont);    // the font object


    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a deep blue
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // begins the 3D scene

    // create a RECT to contain the text
    static RECT textbox; SetRect(&textbox, 0, 0, 640, 480);

    // draw the Hello World text
    dxfont->DrawTextA(NULL,
                      "Hello World...FINALLY!",
                      22,
                      &textbox,
                      DT_CENTER | DT_VCENTER,
                      D3DCOLOR_ARGB(255, 255, 255, 255));

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    d3ddev->Release();
    d3d->Release();

    return;
}



Image 1.1 - DirectGrid.png Displayed in DirectX

Image 2.5 - Hello World, DirectX Style
posted by 대갈장군
2008. 2. 23. 01:18 프로그래밍/DirectX
이제까지는 멈춰있는 스프라이트만 봤지요. 2차원 영상이라면 정지해 있는게 정상 아닌가? 어쨌든 이번 장에서는 움직이게 만든단다. 2차원 영상이 이리저리 움직이는데 어떻게 로딩하고 어떻게 프로그래밍하는지 알아보자.

1. Rectangles
왜 갑자기 사각형 이야기가 나오는 거지? 사각형은 매우 쉽지만 중요한 부분이란다. C++에서는 사각형을 표현할때 RECT 구조체를 이용하는데 이것은 4개의 LONG값을 가진다. top, left, bottom, right 4개의 값을 사용하여 4각형을 표현한다. 각각의 값은 어디서 부터 얼마나 떨어져 있는지를 나타낸다.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 3.1 - The Four Values of RECT

이 구조체의 정의를 살펴보면,
typedef struct _RECT
{
    LONG left;    // the left value
    LONG top;    // the top value
    LONG right;    // the right value
    LONG bottom;    // the bottom value
} RECT, *PRECT;

이고 이것을 사용하는 예를 보자면,

RECT rectangle = {100, 100, 300, 250};    // create the rectangle in Image 3.1

이 될것이다.

초기값을 설정하거나 중간에 값을 변경하기 위해서는 SetRect 함수를 사용하면 된다는 구나.

RECT rectangle;    // create the rectangle
SetRect(&rectangle, 100, 100, 300, 250);    // initialize the rectangle

2. Animation with Sprites
근데 어떻게 이 rect 구조체가 animation을 만든단 말인가? 일단 Draw 함수를 다시 한번 살펴보자.

HRESULT Draw(LPDIRECT3DTEXTURE9 pTexture,
             CONST RECT* pSrcRect,
             CONST D3DXVECTOR3* pCenter,
             CONST D3DXVECTOR3* pPosition,
             D3DCOLOR Color);

두번째 인자를 보면 CONST RECT* pSrcRect라고 되어 있는데 RECT 구조체에 대한 포인터다.이 RECT 구조체는 그려져야할 스프라이트의 일부를 담고 있다. 예를 들면 다음 그림처럼 앞장에서 그렸던 그리드 중에 일부만 선택적으로 보여 줄 수도 있다는 말. (원래는 NULL이었음)

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 3.2 - Only Some of Panel2.png

물론 이 부분이 들어감으로써 아주 놀라운 변화를 줄수는 있다만 이게 어떻게 애니매이션이 되지? 흠. 그러니까 아주 긴 (위로든 아래로든) 그림이 있는데 그 그림은 사실 연속된 화면의 컷들이다. 각각의 컷은 핫 핑크색을 배경색으로 해서 중간쯤에 움직이는 물체 (우리가 보여주길 원하는 것) 가 존재한다.

내 생각에는 배경색이 핫 핑크이므로 color key를 이용하면 간단히 제거 가능할 테고 중간 주변에 물체를 그려내고 있으므로 한 프래임씩 RECT 구조체의 값을 바꿔가면서 보여주면 애니매이션이 될것이다.

아래가 간단한 연속 컷 화면의 예인데 너무 커서 겨우 3컷 정도만 보인다.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 3.3 - Part of the Animation Strip

자, 1번 프래임은 다음과 같은 화면일 것이고,

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 3.4 - The First Frame

2번 프래임은 다음과 같을 건데 (좌측으로부터 181 픽셀이 증가한 위치부터 시작)


Image 1.1 - DirectGrid.png Displayed in DirectX

Image 3.5 - The Second Frame

이런식으로 착착 다음 프레임을 보여주게 될것이다.

3. The Finished Program

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

// sprite declarations
LPDIRECT3DTEXTURE9 sprite;    // the pointer to the sprite

// function prototypes
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);    // renders a single frame
void cleanD3D(void);    // closes Direct3D and releases memory

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;


    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

    D3DXCreateTextureFromFileEx(d3ddev,    // the device pointer
                                L"Panel3.png",    // the file name
                                D3DX_DEFAULT,    // default width
                                D3DX_DEFAULT,    // default height
                                D3DX_DEFAULT,    // no mip mapping
                                NULL,    // regular usage
                                D3DFMT_A8R8G8B8,    // 32-bit pixels with alpha
                                D3DPOOL_MANAGED,    // typical memory handling
                                D3DX_DEFAULT,    // no filtering
                                D3DX_DEFAULT,    // no mip filtering
                                D3DCOLOR_XRGB(255, 0, 255),    // the hot-pink color key
                                NULL,    // no image info struct
                                NULL,    // not using 256 colors
                                &sprite);    // load to sprite

    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a deep blue
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // begins the 3D scene

    d3dspt->Begin(D3DXSPRITE_ALPHABLEND);    // // begin sprite drawing with transparency

    // DRAW THE SPRITE FRAME

    // count from 0 to 22 to determine the current frame
    static int frame = 21;    // start the program on the final frame
    if(KEY_DOWN(VK_SPACE)) frame=0;     // when the space key is pressed, start at frame 0
    if(frame < 21) frame++;     // if we aren't on the last frame, go to the next frame

    // calculate the x-position
    int xpos = frame * 182 + 1;

    // draw the selected frame using the coordinates
    RECT part;
    SetRect(&part, xpos, 0, xpos + 181, 128);

    D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner
    D3DXVECTOR3 position(50.0f, 50.0f, 0.0f);    // position at 50, 50 with no depth
    d3dspt->Draw(sprite, &part, &center, &position, D3DCOLOR_ARGB(127, 255, 255, 255));

    d3dspt->End();    // end sprite drawing

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    sprite->Release();
    d3ddev->Release();
    d3d->Release();

    return;
}





posted by 대갈장군
2008. 2. 20. 05:34 프로그래밍/DirectX

스프라이트에 대해서 좀더 알아보자. 앞에서 실험해본 결과 특정 이미지를 특정 위치에 그리는건 좋은데 문제는 투명도 설정이나 그런게 안되서 배경색과 매치가 안되는 경우 심각한 문제를 발생시키더라. 허, 그러고 보니 생각나는게 color key 방법이네 :)

1. Color-Keys
알다시피 지난번에 컬러 키를 이용한 색 제거를 했었다. 이것은 텍스쳐를 사용하는 3차원 물체에 적용했던 것이었는데 스프라이트도 텍스쳐를 사용하므로 컬러 키를 그대로 사용가능하단다.

잘 기억이 나지 않으면 lesson 12에 D3DXCreateTextureFromFileEx() 함수를 참조하라.

Alpha Blending with Sprites
이전에 사용했던 스프라이트를 그리기 위한 함수가 생각나나?

d3dspt->Begin(NULL);    // begin sprite drawing

BeginScene()과 EndScene() 사이에 썼던 바로 그 함수인데 컬러 키를 사용하기 위해서는 이 함수에 추가적인 플래그가 필요하다는데...

d3dspt->Begin(D3DXSPRITE_ALPHABLEND);    // begin sprite drawing with transparency

바로 알파 블랜딩 플래그이구나. 바로 이 플래그를 추가함으로써 우리는 스프라이트를 그릴때 컬러 키를 사용할수도 있고 반투명도를 사용할수도 있단다.

아마도 투패스 코드에도 이런 기본 셋팅 (DX용)이 들어가 있을것이다. 물론 스프라이트로 그리지는 않겠지만 말이다.

Using Color-Keyed Textures
내가 테스트 해본 바로 그 결과를 그림이 보여주는데, 만약 백그라운드 색상이 뻘건 색이면 이렇게 된다.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 2.1 - Panel1.png Displayed Against a Colored Background

그리고자 하는 이미지는 사실 검은색 테두리 안에 있는 사각형일 뿐인데 배경색과 달라서 문제가 있다.

그래서 이런 걸 해결하기 위해서 드디어 컬러 키를 사용하는데,

D3DXCreateTextureFromFileEx(d3ddev,    // the device pointer
                            L"Panel2.png",    // the new file name
                            D3DX_DEFAULT,    // default width
                            D3DX_DEFAULT,    // default height
                            D3DX_DEFAULT,    // no mip mapping
                            NULL,    // regular usage
                            D3DFMT_A8R8G8B8,    // 32-bit pixels with alpha
                            D3DPOOL_MANAGED,    // typical memory handling
                            D3DX_DEFAULT,    // no filtering
                            D3DX_DEFAULT,    // no mip filtering
                            D3DCOLOR_XRGB(255, 0, 255),    // the hot-pink color key
                            NULL,    // no image info struct
                            NULL,    // not using 256 colors
                            &sprite);    // load to sprite

헐, 여기서 잠깐 혼란에 빠졌는데 이 쓰뎅구리가 제대로 설명안해놨다. 바로 요 전에 사용했던 텍스쳐 파일은 배경이 검정색이지만 이 레슨에서 사용하는 새로운 텍스쳐 파일은 배경인 hot pink 색이다.

고것때문에 헷갈려서 한참을 뒤졌네. 젠장할쓴. 결론적으로 위에서 마지막에서 4번째 인자로 지정하는 색이 투명해 지는 것이고 주로 핫 핑크를 쓴단다.

고로 저렇게 하면 다음과 같은 결과가...

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 2.2 - Panel2.png Displayed with Alpha

2. Semi-Transparency
이제는 좀더 발전된 단계로 진행해보자. 여기서 추가로 반투명도 까지 올리는 것인데, 흠.. 재미있겠는데?

 

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 2.3 - DirectX vs DirectXTutorial

바로 저런걸 해보자는 건데... 반투명한 패널뒤로 물체가 사알짝 비취는 거다.

이렇게 하는것은 매우 매우 간단하다는데 다음 명령만 바꿔주면 된단다.

d3dspt->Draw(sprite, NULL, &center, &position, D3DCOLOR_ARGB(127, 255, 255, 255));

이것은 그려지는 스프라이트를 50%의 alpha값을 가지도록 하여 반투명하게 만드는 역확을 한다... 너무 간단하다.

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 2.4 - The Panel Drawn with Semi-Transparency


결과는 위와 같은 것이다. (물론 화면 왼쪽 상단 일부만 캡춰한거임)

3. The Finished Program

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

// sprite declarations
LPDIRECT3DTEXTURE9 sprite;    // the pointer to the sprite

// function prototypes
void initD3D(HWND hWnd);    // sets up and initializes Direct3D
void render_frame(void);    // renders a single frame
void cleanD3D(void);    // closes Direct3D and releases memory

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;


    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

    D3DXCreateTextureFromFileEx(d3ddev,    // the device pointer
                                L"Panel2.png",    // the new file name
                                D3DX_DEFAULT,    // default width
                                D3DX_DEFAULT,    // default height
                                D3DX_DEFAULT,    // no mip mapping
                                NULL,    // regular usage
                                D3DFMT_A8R8G8B8,    // 32-bit pixels with alpha
                                D3DPOOL_MANAGED,    // typical memory handling
                                D3DX_DEFAULT,    // no filtering
                                D3DX_DEFAULT,    // no mip filtering
                                D3DCOLOR_XRGB(255, 0, 255),    // the hot-pink color key
                                NULL,    // no image info struct
                                NULL,    // not using 256 colors
                                &sprite);    // load to sprite

    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a bright red
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // begins the 3D scene

    d3dspt->Begin(D3DXSPRITE_ALPHABLEND);    // // begin sprite drawing with transparency

    // draw the sprite
    D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner
    D3DXVECTOR3 position(50.0f, 50.0f, 0.0f);    // position at 50, 50 with no depth
    d3dspt->Draw(sprite, NULL, &center, &position, D3DCOLOR_ARGB(127, 255, 255, 255));

    d3dspt->End();    // end sprite drawing

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    sprite->Release();
    d3ddev->Release();
    d3d->Release();

    return;
}




 

posted by 대갈장군
2008. 2. 20. 04:37 프로그래밍/DirectX
이 sprites라는 놈은 뭐길래 이렇게 내 눈에 자주 띄는건가? 2D 그래픽이 필요 없을 것 같아도 3D 영상에도 항상 2D는 사용되고 있으며 2D 가 많이 이곳 저곳에 사용되고 있단다.

이번 강의에서는 2D 그래픽을 그리는데 가장 많이 사용되는 sprites라는 기술에 대해서 알아 볼 것이다. 

1. Loading and Dislpaying Sprites
sprites를 사용하는 방법은 매우 간단하고 쉽단다. 구차하게 설명하기 보다 바로 어떻게 하는지 살펴보자.

1. D3DXSprite interface 초기화
2. texture 로딩
3. texture 그리기

헐, 너무 간단하게 말하는걸? :)

1-1. Initializing the D3DXSprite Interface
D3DXSprite는 COM 객체로써 2D 그래픽을 쉽게 만들도록 해준다. 시스템에서 관리를 해주는 아주 적절하고 좋은 방법이라는데... 문제는 COM 객체니까 초기화가 되어야 하는데 이것을 초기화 하는 방법은 다른 DirectX interface를 초기화 하는 방법과 유사하다. 우선 인터페이스에 대한 포인터를 만들고 함수를 호출하여 인터페이스를 생성(객체화) 한단다.

예전에 Direct3D interface를 초기화 했던 방법을 살펴보면,

LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface

d3d = Direct3DCreate9(D3D_SDK_VERSION);    // create the Direct3D object

인데 interface 포인터를 만들고 함수를 호출하여 생성하였다. 이것 외에도 Direct3D Device를 생성할때 이런 비슷한것을 했다는데 그건 좀 더 복잡했단다.
 
D3DXSprite도 위와 거의 유사한 방식인데 조금더 쉽다.

LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

첫번째 인자는 늘 보던거고 두번째 인자가 생성된 인터페이스 객체를 받을 주소인갑다.

1-2. Loading a Texture
Sprites는 이미지를 그리기 위해서 텍스쳐를 사용한단다. 고로 sprite를 디스플레이 하기 위해서는 반드시 텍스쳐가 로딩되어야 한다는데... sprite 찾아보니..  (헐헐 찾는데 2시간 넘게 걸렸다. 졸고 졸고 놀고 놀고... 미친놈) 스프라이트는 음료수가 아니라 쪽화면이라는 뜻이다. 화면상의 일부를 말한다.

LPDIRECT3DTEXTURE9 sprite;    // the pointer to the sprite

D3DXCreateTextureFromFile(d3ddev, L"graphic.bmp", &sprite);

음, 이전에도 했던 것과 똑같은데... 그냥 텍스쳐 포인터 선언하고 파일로 부터 읽어 들인다. 아무런 변화는 없다. 다만 조금 느낌이 이상하다면... 흠... 아니다, 이상할게 없다. 스프라이트건 아니건 간에 텍스쳐를 읽어오고 메모리상에 저장하는 과정은 다를 이유가 없지 않나.

여기서 하나 집고 갈것은 2의 승수가 아닌 텍스쳐는 컴퓨터가 알아서 스트레칭을 하는데 스프라이트의 경우 스트레칭이 일어나면 상당히 어글리하다는 점... 고로 사이즈를 잘 잡아야 한다는 점.

1-3. Drawing the Texture
아시다시피 3차원 물체를 그리는 것은 두가지 함수로 시작하고 멈춘다. BeginScene() 함수와 EndScene() 함수다. 헌데 이 스프라이트를 그리는 것은 2차원 영상을 그리는 것으로써 3차원 영상을 그리는 함수와 구별된다. 그래서 일반적으로 위의 두 함수 사이에 스프라이트를 그리고 멈추는 함수가 따로 한벌로 존재한다. 다음 예를보자.

d3ddev->BeginScene();

// perform 3D rendering here

d3dspt->Begin(NULL);    // begin sprite drawing

// perform sprite drawing here

d3dspt->End();    // end sprite drawing

d3ddev->EndScene();

여기 보면 스프라이트 그리기 함수에 인자값으로 널이 들어가 있는데 이 것은 다양한 그리기 옵션이란다. 현재로써는 그냥 널을 사용하는 것이 좋고 다음에 한번 자세히 알아보자.

위의 구조를 잠깐 보아하니 우선 3차원 영상을 먼저 그려내고 그다음에 2차원 영상을 그려내는 구나.. 흠.. 이유가 있나? :)

이제 본격적으로 스프라이트를 그리는 명령인 Draw를 알아볼 차례이다. 제법 긴 함수다.

HRESULT Draw(LPDIRECT3DTEXTURE9 pTexture,
             CONST RECT* pSrcRect,
             CONST D3DXVECTOR3* pCenter,
             CONST D3DXVECTOR3* pPosition,
             D3DCOLOR Color);

LPDIRECT3DTEXTURE9 pTexture : 우리가 사용하고자 하는 텍스쳐로의 포인터.

CONST RECT* pSrcRect : 텍스쳐의 어떤 부분을 사용할지 알려주는 좌표 구조체 포인터. 애니매이션을 사용하지 않는 이상은 주로 전체 텍스쳐를 다 사용하게 되는데 이때는 NULL을 넣어주면 전체를 가져온다.

CONST D3DXVECTOR* pCenter : 이 벡터는 이미지의 중심을 표시한다. 헌데 이 중심이라 함은 말그대로 진짜 이미지의 중심이라기 보다는 이미지의 포터스 포인트이다.

설명을 조금 이상하게 해놨는데 마우스 커서를 예로 들었는데 일반적으로 마우스 커서의 왼쪽 끝이 '중심점'이 된다. 내 생각에 글쓴이가 의도하는 설명은, 그림에 따라 중심을 둬야 하는 위치가 제 각각일 수 있다는 것 같다.

근데 한가지 더 남아있는 의문은 왜 z 축 좌표도 필요하냐는 건데 나중에 설명한단다.

CONST D3DXVECTOR3* pPosition :
이것은 스크린에서 이미지 중심의 좌표를 지시하는 벡터 이다. 일반적으로 이미지가 스크린의 50, 50 에 있다고 말하면 그것은 이미지의 중심이 그 좌표에 있다는 것을 의미한다. 흠, 그래서 이미지의 중심이 어디인가가 중요하겠군.

이제 z 축의 사용법에 대해서 알아보면 3차원 영상과는 달리 2차원 영상에서는 z 축의 값이 0.0에서 1.0 까지 밖에 없다. 그리고 그 사이에서의 거리에 따른 크기 감쇄도 전혀 없다. (왜? 2차원 영상 이니까~)

다만 2차원 이미지는 더 가까운 이미지가 뒤에것에 겹쳐 보인다. 아직 이 글쓴이도 어떻게 이것을 사용해야 할지 모르겠다는데... 그럴만도 한게... 2차원 이미지가 겹쳐보일 일이 잘 없지 않은가?

D3DCOLOR Color : 이미지를 칠하고 싶은 색상을 정하는 인자.

어떻게 사용하는지 실제로 살펴보면,

d3dspt->Begin(NULL);    // begin sprite drawing

D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner
D3DXVECTOR3 position(50.0f, 50.0f, 0.0f);    // position at 50, 50 with no depth
d3dspt->Draw(sprite, NULL, &center, &position, D3DCOLOR_XRGB(255, 255, 255));  // draw it!

d3dspt->End();    // end sprite drawing

2. The Finished Program
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution and keyboard macros
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev;    // the pointer to the device class
LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

// sprite declarations
LPDIRECT3DTEXTURE9 sprite;    // the pointer to the sprite


// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = L"WindowClass1";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",
                          L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP,
                          0, 0,
                          SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);

    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();

        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = FALSE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;


    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

    D3DXCreateTextureFromFile(d3ddev, L"Panel1.png", &sprite);


    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a deep blue
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();    // begins the 3D scene

    d3dspt->Begin(NULL);    // begin sprite drawing

    // draw the sprite
    D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner
    D3DXVECTOR3 position(50.0f, 50.0f, 0.0f);    // position at 50, 50 with no depth
    d3dspt->Draw(sprite, NULL, &center, &position, D3DCOLOR_XRGB(255, 255, 255));

    d3dspt->End();    // end sprite drawing

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
      sprite->Release();
    d3ddev->Release();
    d3d->Release();

    return;
}

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 1.1 - Panel1.png Displayed in DirectX



posted by 대갈장군