DirectX C#

출처 : Developer의 블로그 < http://tramper2.blog.me/100060746510 >

XML 기본구조 파싱

1.1 What is 'XML' #

extensible markup language
HTML같은 마크업 언어지만, 사용자가 직접 규약을 정할수가 있다.

ex)
<MaterialSoup count="2">
 <Material index="0">
  <Texture>"test.bmp"</Texture>
  <Ambient>
   <A>255</A>
   <R>128</R>
   <G>32</G>
   <B>0</B>
  </Ambient>
 </Material>

 <Material index="1">
  <Texture>"bump.bmp"</Texture>
  <Ambient>
   <A>255</A>
   <R>0</R>
   <G>255</G>
   <B>255</B>
  </Ambient>
 </Material>
</MaterialSoup>

1.2 XML 문법 #

XML 문서의 첫줄에는 xml버젼과, encoding규약이 나온다.
ex)
<?xml version="1.0" encoding="euc-kr"?>

여타의 마크업 언어와 마찬가지로 XML도 트리 형태로 구성된다.

하나의 엘리먼트는 값, 혹은 자식 엘리먼트를 가질 수 있다.
ex) 값을 가지는 경우
<Value>10</Value>

ex) 자식 엘리먼트를 가지는 경우
<Parent>
  <Child/>
  <Child/>
  <Child/>
</Parent>

엘리먼트는 0개 이상의 속성을 가질 수 있다.
ex) 
<Element attribute1="test1" attribute2="test2"/>

1.3 Schema #

XML문법에 대한 규약을 정하는 방법은 두가지가 있다.
한가지는 DTD 이고, 다른한가지가 Schema이다.
DTD는 심플하지만 규약의 한계가 있고, 복잡한 표현식을 규약으로 정의하기 불편하다.

1.3.1 스키마 헤더 #

<?xml version="1.0" encoding="euc-kr"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">


'xmlns'는 가져올 네임스페이스 이름과, 경로.
즉 xsd라는 네임스페이스를 우측의 경로에서 가져온다.

1.3.2 엘리먼트 선언 #

<xsd:element name="element_name" type="xsd:string"/>


1.3.3 엘리먼트 갯수 제한 #

<xsd:element name="element_name" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>

1.3.4 빌트인(Built-in) 심플 타입 #

스키마 언어에 이미 정의되어 있는 데이터 타입.

<xsd:element name="name" type="xsd:string"/>
<xsd:element name="age" type="xsd:int"/>
<xsd:element name="stature" type="xsd:float"/>

1.3.5 사용자 정의 심플타입 #

사용자가 만들어서 사용하는 데이터 타입.
자식 엘리먼트나 속성을 가지지 않는다.

<심플타입 선언>
<xsd:simpleType name="carType">
 <xsd:restriction base="xsd:string">
  <xsd:enumeration value="suv"/>
  <xsd:enumeration value="sedan"/>
  <xsd:enumeration value="rv"/>
 </xsd:restriction>
</xsd:simpleType>

<심플타입 사용>
<xsd:element name="familyCar" type="carType"/>

<타입 선언없이 바로 사용>
<xsd:element name="familyCar">
 <xsd:simpleType>
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="suv"/>
   <xsd:enumeration value="sedan"/>
   <xsd:enumeration value="rv"/>
  </xsd:restriction>
 </xsd:simpleType>
</xsd:element>

restriction 외에도 list, union 등을 사용할 수 있다.

1.3.6 컴플렉스 타입 #

자식 엘리먼트나 속성을 가지는 엘리먼트는 컴플렉스 타입으로 정의해야 한다.

<컴플렉스타입 선언>
<xsd:complexType name="vector3">
 <xsd:sequence>
  <xsd:element name="x" type="xsd:float"/>
  <xsd:element name="y" type="xsd:float"/>
  <xsd:element name="z" type="xsd:float"/>
 </xsd:sequence>
</xsd:complexType>

1.3.7 속성 선언 #

일반적인 엘리먼트 정의해서 속성을 넣을때는 아래와 같이 하면 된다. 
<xsd:element name="person">
 <xsd:complexType>
  <xsd:sequence>
   <xsd:element name="name" type="xsd:string"/>
   <xsd:element name="age" type="xsd:int"/>
  </xsd:sequence>

  <xsd:attribute name="id" type="xsd:int"/>
 </xsd:complexType>
<xsd:element>

-이미 정의된- 심플 타입을 확장하여 속성등을 추가 한다 치자.
예를들어
<Person>종호</Person>
이런 xml데이터는

<xsd:element name="Person" type="xsd:string"/>
이런 스키마로 표현가능하다.

그런데
<Person gender="male">종호</Person>

이렇게 자식엘리먼트를 가지는 구조는 아니지만, 속성을 가지고 싶다면 타입을 확장해야 한다.
위의 예제같으면 string타입을 확장 해야 하는데, string이 심플타입 이므로 simpleContent를 써야한다.
<xsd:element name="Person">
 <xsd:complexType>
  <xsd:simpleContent>
   <xsd:extension base="xsd:string">
    <xsd:attribute name="gender" type="xsd:string"/>
   </xsd:extension>
  <xsd:simpleContent>
 </xsd:complexType>
</xsd:element>

이번에는 아래와 같이 속성이 없는 컴플렉스 타입이 있다고 하자
<xsd:complexType name="personType">
 <xsd:sequence>
  <xsd:element name="name" type="xsd:string"/>
  <xsd:element name="age" type="xsd:int"/>
 </xsd:sequence>
</xsd:complexType>

<xsd:element name="Person" type="personType"/>

이 타입에 속성을 넣고 싶다면 마찬가지로 타입을 확장 해야 한다
<xsd:element name="Person">
 <xsd:complexType>
  <xsd:complexContent>
   <xsd:extension base="personType">
    <xsd:attribute name="gender" type="xsd:string"/>
   </xsd:extension >
  </xsd:complexContent>
 </xsd:complexType>
</xsd:element>

1.4 XML Parsing 방법 #

XML parsing방법에는 크게 두가지가 있다.
하나는 DOM(Document Object Model)이고 다른 하나는 SAX(Simple API for Xml)
DOM은 XML문서를 트리형태로 메모리 상에 상주시키는 방식이고, 
SAX는 XML 엘리먼트 단위로 단순 파싱 해주는 방식이다.

SAX같은 경우 메모리에 상주시키는 방식이 아니라 가볍지만, 파서라기 보다는 단순히 토큰단위의 로더이기 때문에 직접 사용목적에 맞게 파싱해야 한다.

1.5 Tiny XML을 이용하여 C++에서 parsing #

c++의 경우 MSXML 이 지나치게 무거워서 tiny xml만을 설명 하려고 한다.
그중에서도 DOM형태만 설명 하겠다.

아래와 같은 xml데이터가 있다고 하자
<?xml version="1.0" encoding="euc-kr"?>

<Persons>

 <Person id="11111">
  <name>종호</name>
  <sex>male</sex>
  <age>26</age>
 </Persopn>

 <Person id="512">
  <name>길동</name>
  <sex>male</sex>
  <age>37</age>
 </Persopn>

 <Person id="1024">
  <name>순자</name>
  <sex>female</sex>
  <age>22</age>
 </Persopn>

</Persons>
이 데이터는 아래와 같은 방법으로 순회 하면서 읽어들이면 된다
TiXmlDocument doc("test.xml");
TiXmlNode* personsNode = doc.FirstChild("Persons");

TiXmlNode* personNode = personsNode .FirstChild("Person");
while(personNode !=NULL)
{
 char* id_str=personNode ->ToElement()->Attribute("id");

 TiXmlNode* nameNode = personNode->FirstChild("name");
 char* name_str = nameNode->ToElement->()GetText();

 TiXmlNode* sexNode = personNode->FirstChild("sex");
 char* sex_str = sexNode->ToElement->()GetText();
 
 TiXmlNode* ageNode = personNode->FirstChild("age");
 int age = (int)atoi( ageNode->ToElement()->GetText() );

 personNode = personNode ->NextSibling();
}


1.6 C++에서의 기타 Parsing 방법들 소개 #

XDS 사용
장점:XML에서 컴파일된 바이너리 파일이므로 읽는데 빠르다
단점:한번 변한을 해야한다. 트리구조로 만들수 없다
참조:http://www.suddenpresence.com/xds/
http://serious-code.net/moin.cgi/XDS 
expat 사용
참조:http://expat.sourceforge.net

1.7 C#에서 SAX parsing #

XmlTextReader xmlReader = new XmlTextReader("test.xml");
xmlReader.WhitespaceHandling = WhitespaceHandling.None;

XmlValidatingReader xmlValidate = new XmlValidatingReader(xmlReader);
xmlValidate.ValidationType = ValidationType.None;
            
//read가 false이면 eof이다.
while(xmlValidate.Read())
{
    Console.WriteLine("node type:{0}\n", xmlValidate.NodeType);
    Console.WriteLine("name:{0}\n", xmlValidate.Name);
    Console.WriteLine("value:{0}\n", xmlValidate.Value);
}

1.8 C#에서 DOM parsing #

TinyXml의 데이터를 예제로 하겠다.
Int32Converter converter = new Int32Converter();

XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
XmlNode personsNode =doc.SelectSingleNode("Persons");

XmlNodeList personNodeCollection = personsNode .SelectNodes("Person");
foreach (XmlNode personNode in personNodeCollection )
{
 string id_str=personNode.Attributes["id"];

 XmlNode nameNode = personNode.SelectSingleNode("name");
 string name_str = nameNode.FirstChild.Value();

 XmlNode sexNode = personNode.SelectSingleNode("sex");
 string sex_str = sexNode.FirstChild.Value();

 XmlNode ageNode = personNode.SelectSingleNode("age");
 int age = (int)converter(ageNode.FirstChild.Value());
}


1.9 C#에서 Serialize 하여 XML읽기 #

아래와 같은 값을 가지는 객체를 인스턴스화 하고, 여기에 값을 채우고 싶다
class testClass
{
  [XmlElement]
  public string type;
  [XmlElement]
  public int data;
}

serialize하여 값을 채운다

//스트림으로 데이터를 읽어오자
Stream reader= new FileStream("test.xml",FileMode.Open);
//직렬화 하기 위해서 타입을 알아야 한다.
XmlSerializer serializer = new XmlSerializer(typeof(testClass));

//시리얼라이저가 test.xml'에 정의된 대로 instance를 만들어 준다. 이로써 이미 파싱은 끝났다.
testClass instance = (testClass)serializer.Deserialize(reader);

단 이때 당연히! xml문서의 구조가 위의 testClass와 동일해야 한다.

<?xml version="1.0"?>
<testClass>
  <type>book</type>
  <data>12</data>
</testClass>

문서 구조와 클래스 구조가 틀리면 런타임시 틀린 줄 까지 표시해주니 디버깅도 아주 쉽다.
<DeSerialize>할 클래스 선언 자체가 xml 정의가 되므로 따로 스키마나 dtd를 정해줄 필요가 없다.

1.10 기타 #

XML에서의 경로규약 <XPath>
XML 쿼리 언어 <XQuery>
여러가지 마크업 언어간의 엘리먼트 이름 충돌을 방지하기 위한 <XML 네임스페이스>

출처 : 호야의 티스토리 < http://kin3d.tistory.com/165 >

< 초간단 TinyXml 사용 방법 > 파싱

 

-------------- 예제로 사용 될 xml 문서 --------------

exam.zip

-------------------------------------------------

 

 

1. TinyXml을 사용 할려면

- 먼저 TinyXml 라이브러리를 다운 받은 후 빌더를 해야된다

  빌더를 할 때는 이 라이브러리를 사용할 프로젝트가 싱글 쓰레드인지 멀티 쓰레드 인지에 

  따라서 tinyXml의 쓰레드 옵션을 설정 후 빌드를 해야된다.

빌드가 끝나면 사용할 프로젝트에서 lib파일을 링크 시킨다.

tinyxml.htinyxml.cpptinyxmlerror.cpptinyxmlparser.cpp를 프로젝트 폴더에 포함시킨다.

- 위 파일 중 tinyxml.h  파일만 소스에서 include 하면 된다.

- 사용할 프로젝트가 만약 mfc 프로젝트로 stdafx 파일을 사용한다면 

           tinyxml.cpptinyxmlerror.cpptinyxmlparser.cpp의 상단에 #include stdafx.h를 입력해야 된다.

TinyXml을 사용한 후 따로 delete로 지울 필요는 없다( 즉 TiXmlNode* node; 이렇게 사용된 것들..)

 

 

 

2. xml 파일 읽기

 

TiXmlDocument doc( "MaJak_Para.xml" );

doc.LoadFile();

 

TiXmlDocument m_XmlDoc;

   if( m_XmlDoc.LoadFile("MaJak_Para.xml" ) == false )

        return ERR_LOADXML;

 

 

 

3. 노드의 이동

 

루트에서 시작하여 바로 Option 노드로 이동할려면

TiXmlNode* node = doc.FirstChild("MAJAK")->FirstChild("LOBBY_CONTROL")->FirstChild( "Option" );

 

루트에서 LOBBY_CONTROL노드로 이동 할려면

    node = doc.FirstChild();

    node = node->NextSibling();

   

    int Pos = 1

    for( i = 0; i < Pos; ++i )

    {

        node = node->FirstChild();

    }

    여기에서 Pos 2로 하면 LOBBY_CONTROL노드의 첫번째 자식까지 이동 한다.

 

 - LOBBY_CONTROL 노드에 있는 상태에서 이 노드의 자식 중 LobbyExit 노드로 바로 갈려면

    node = node->FirstChild( "LobbyExit" );

 

-        노드의 값을 볼려면 node->Value()로 하면 문자가 나온다.

 

 - 도큐먼트의 top 레벨의 기준으로 해서 자식 노드로 이동 할려면

        count = 0;

       for( node = doc.FirstChild();

              node;

              node = node->NextSibling() )

       {

             count++;

       }

 

   

        // 아래서 위로 이동할려면

        count = 0;

       for( node = doc.LastChild();

              node;

              node = node->PreviousSibling() )

       {

             count++;

       }

 

    

        // 이터레이터를 사용

        count = 0;

       for( node = doc.IterateChildren( 0 );

              node;

              node = doc.IterateChildren( node ) )

       {

             count++;

       }

 

      

       // 자식 노드 중 원하는 노드만으로의 이동도 가능하다.

 

 

 

4. 노드의 애트리뷰트 값을 얻기

 

현재 노드에서 모든 자식노드의 애트리뷰트 값을 얻을려면 다음과 같이 한다.

    node = node->FirstChild();

    CONTROL_RECT ControlRect;

    TiXmlElementelem;

    
while( node != NULL )

    {

        elem = node->ToElement();

       

        elem->Attribute( "Index", &ControlRect.index )

        elem->Attribute( "X", &ControlRect.x );

        elem->Attribute( "Y", &ControlRect.y );

        elem->Attribute( "W", &ControlRect.w );

        elem->Attribute( "H", &ControlRect.h );

       

        node = node->NextSibling();

    }

 

     Attribute   리턴 값으로 애트리뷰트 값의 문자형을 얻을 수 있다.

 

애트리뷰트 이동 방법

 

        // 노드의 첫 애트리뷰트에서 마지막 애트리뷰트까지 이동

        count = 0;

       for( element = todoElement->FirstChildElement();

              element;

              element = element->NextSiblingElement() )

       {

             count++;

       }

      

            



5. 애트리뷰트를 얻을 수 있도록 노드 대신 element를 얻는 방법

 -  node = node->FirstChild();
TiXmlElementRootelem = node->NextSiblingElement( _strNodeName );

    TiXmlElementelem = Rootelem->FirstChildElement( _strSubNodeName );

    elem->Attribute( "Index", &iIndex );

 

 

6. 애트리뷰트의 첫 번째 속성과 다음 속성을 얻는 방법

 

 -  TiXmlAttributeAttribut;

    Attribut = elem->FirstAttribute()

 

    // 정수 및 스트링을 얻을 수 있다.

    Attribut->IntValue();    
    Attribut->Value();

 

    // 다음 애트리뷰트 값으로 이동
    Attribut = Attribut->Next();


Render Target Texture 3D

http://knob.egloos.com/1683876
Render Target(이하 렌더타겟)은 렌더링 디바이스(IDirect3DDevice9)가 렌더링을 수행할 Surface를 의미한다. 일반적으로는 클라이언트 전체에 대한 Surface가 Target이 되지만, Target으로 다른 Surface가 지정될 수도 있다.
 
만일 게임 내에 거울과 같은, 현재의 카메라 시점과는 조금 다른 카메라 시점을 가진 기능을 구현하고자 한다면, 클라이언트에 직접 그려주는 것이 아니라 렌더타겟에 렌더링 한 후에 그 결과를 거울의 해당 영역에 텍스쳐로 사용하는 방법도 사용될 수 있다.
 
이것뿐만 아니라, 게임의 User Interface(이하 UI)와 같은 객체들에도 렌더타겟을 속도를 높이는 수단으로 활용할 수 있다. UI는 여러 가지 구성 요소들이 복합적으로 결합되는 경우가 많고, 당연히 각각의 요소들을 모두 렌더링 해줘야 한다. 특히 폰트 렌더링에 소모되는 비용은 상당히 비싼 편이다. 거기에 외곽선이나 그림자를 추가하는 경우라면 조금 더 많은 비용이 들어간다. 속도를 높일 수 있다는 것은 UI 객체들은 화면에 정적으로 배치되는 경우가 많기 때문이다. UI객체들의 외형적인 변화의 횟수가 빈번하지 않다는 가정을 하면, 처음에 한 번 렌더타겟에 렌더링을 해주고, 외형적 변화가 발생하는 경우에만 다시 Target을 업데이트 해주는 방법으로 복합적으로 구성된 UI 객체를 단일 텍스쳐로 대체할 수 있기 때문이다.
 
아래와 같은 방법으로 렌더타겟으로 활용하기 위한 텍스쳐를 생성한다.
 
D3DXCreateTexture(
           pDevice,
           uWidth,                       // width
           uHeight,                      // height
           1,                              // Mipmap 레벨은 1
           D3DUSAGE_RENDERTARGET,
           eSurfaceFormat,            // surface format
           D3DPOOL_DEFAULT,
           &pTexture)
 
위의 함수가 성공적으로 수행되었다면 렌더타겟으로 사용될 새로운 텍스쳐가 마련된 것이다. 이 텍스쳐를 렌더타겟으로 활용하는 순서는 다음과 같다.
 
IDirect3DSurface9*        pRenderTarget_New;
IDirect3DSurface9*        pRenderTarget_Old;
HRESULT hr;
hr = pTexture->GetSurfaceLevel(0, &pRenderTarget_New);
if(FAILED(hr) || pRenderTarget_New == 0) {-- error --}
 
hr = pDevice->GetRenderTarget(0, &pRenderTarget_Old);
if(FAILED(hr) || m_ pRenderTarget _New == 0) {-- error --}
 
hr = pDevice->SetRenderTarget(0, pRenderTarget_New);
if(FAILED(hr)) {-- error --}
 
우선 텍스쳐로부터 GetSurfaceLevel()함수를 통하여 새로운 렌더타겟 Surface를 얻어온다. 이것이 성공했다면 IDirect3DDevice9::SetRenderTarget()을 통하여, 새로운 렌더타겟으로 설정할 수 있겠지만, 렌더타겟의 사용이 끝난 후 이전 렌더타겟으로 복구시키기 위하여 이전의 렌더타겟 Surface를 GetRenderTarget()을 통하여 저장해 놓는 것이다. 이전 렌더타겟의 복구는 다음과 같이 수행한다.
 
pDevice->SetRenderTarget(0, pRenderTarget);
 
구현으로 들어가면 일단 mResTextureRenderTarget이라고 이름을 짓고 mResTexture로부터 상속을 받을 것이다. 당연하지만 렌더타겟 역시 일반적인 텍스쳐와 동일하게 활용할 생각이기 때문이다.
 
class mResTextureRenderTarget : public mResTexture
{
protected:
           // render target properties
           UINT                          m_uWidth;
           UINT                          m_uHeight;
           D3DFORMAT                m_eSurfaceFormat;
           // configurations
           bool                           m_bRenderTargetClear;
           D3DCOLOR                  m_dwRenderTargetClearColor;
           // is target empty?
           bool                           m_bIsTargetEmpty;
           // restore target
           DWORD                      m_dwUseZ_Old;
           IDirect3DSurface9*        m_pRenderTarget_New;
           IDirect3DSurface9*        m_pRenderTarget_Old;
protected:
           // create render target texture
           virtual bool       createTargetTexture();
public:
           // 생성자/소멸자
           mResTextureRenderTarget();
           virtual ~mResTextureRenderTarget();
           // render target properties
           bool                setRenderTargetParam(UIN, UIN, D3DFORMAT);
           void                getRenderTargetParam(UINT&,UINT&,D3DFORMAT&);
 
           // configurations
           void                setRenderTargetClear(bool bFlag);
           bool                getRenderTargetClear();
           void                setRenderTargetClearColor(D3DCOLOR dwColor);
           D3DCOLOR        getRenderTargetClearColor();
           // is target empty?
           bool                getIsTargetEmpty();
           void                setIsTargetEmpty(bool bFlag = true);
           // using render target
           virtual bool       setupTarget();
           virtual bool       restoreTarget();
           // create/reset/lost/destroy
           virtual bool       onCreateDevice(IDirect3DDevice9* pDevice);
           virtual bool       onResetDevice(IDirect3DDevice9* pDevice);
           virtual void      onLostDevice();
           virtual void      onDestroyDevice();
           // Serialize
           virtual bool       save(mStreamBase& rStream);
           virtual bool       load(mStreamBase& rStream);
}; // class mResTextureRenderTarget
 
우선 렌더타겟의 속성은 폭(m_uWidth), 너비(m_uHeight), Surface포맷(m_eSurfaceFormat)이다. 일반적인 이미지와 마찬가지로 크기와 색상정보를 가지는 것이다. setRenderTargetParam() 함수는 이 속성들을 저장하는 동시에 onCreateDevice()를 호출한다.
 
bool mResTextureRenderTarget::setRenderTargetParam
           (UINT uWidth, UINT uHeight, D3DFORMAT eFmt)
{
           mASSERT(uWidth > 0);
           mASSERT(uHeight > 0);
           if((uWidth <= 0)||(uHeight <= 0)) return false;
 
           if((m_pTexture)&&
            (uWidth == m_uWidth)&&
            (uHeight == m_uHeight)&&
            (eFmt == m_eSurfaceFormat)) return true;
 
           m_uWidth = uWidth;
           m_uHeight = uHeight;
           m_eSurfaceFormat = eFmt;
 
           if(m_pDevice) return onCreateDevice(g_museKernel.getDevice());
 
           return true;
}
 
파라메터의 유효성을 검사하고, 이미 존재하는 렌더타겟과 동일한지 비교한 후에 저장하고 onCreateDevice()를 호출한다.
 
onCreateDevice()는 저장되었던 렌더타겟 속성을 가지고 처음에 등장했던 D3DXCreateTexture()를 수행하는 createTargetTexture ()를 호출한다.
 
bool mResTextureRenderTarget::onCreateDevice(IDirect3DDevice9* pDevice)
{
           onDestroyDevice();
           m_pDevice = pDevice;
           m_bIsTargetEmpty = true;
           if(m_pDevice)
           {
                     return createTargetTexture();
           }
           return true;
}
 
코드를 살펴보면 m_bIsTargetEmpty라는 속성이 추가된 것을 알 수 있다. 이 변수는 렌더타겟에 렌더링 결과물이 들어있는지 아닌지를 나타낸다. 이름이 의미하는 것처럼 true이면 내용물이 없다는 것이다. 물론 렌더타겟을 사용하는 측에서 결과물을 채우겠지만, 디바이스가 리셋되는 상황이 발생하면 사용하는 측의 의지와는 상관없이 결과물이 사라져 버리는 상황이 함께 발생된다. 사용하는 측에서는 변동이 발생하거나, m_bIsTargetEmpty를 확인하고 true인 경우에 결과물을 다시 채우게 되는 것이다. 아래의 함수들을 통해서 m_bIsTargetEmpty를 확인하고 변경할 수 있다.
 
bool     getIsTargetEmpty();
void     setIsTargetEmpty(bool bFlag = true);
 
초반에 언급되었던 렌더타겟의 설정 및 복구는 다음과 같이 구현된다.
 
bool mResTextureRenderTarget::setupTarget()
{
           if((m_pDevice == 0)||(m_pTexture == 0)) return false;
           if(m_pRenderTarget_Old) {return false;}
           if(m_pRenderTarget_New)          {return false;}
 
           HRESULT hr;
 
           // 렌더타겟 설정
           hr = m_pTexture->GetSurfaceLevel(0, &m_pRenderTarget_New);
           if(FAILED(hr) || m_pRenderTarget_New == 0)
           {
                     mASSERT(0);
                     return false;
           }
           hr = m_pDevice->GetRenderTarget(0, &m_pRenderTarget_Old);
           if(FAILED(hr) || m_pRenderTarget_Old == 0)
           {
                     mASSERT(0);
                     return false;
           }
           hr = m_pDevice->SetRenderTarget(0, m_pRenderTarget_New);
           if(FAILED(hr))
           {
                     mASSERT(0);
                     return false;
           }
 
           // Z설정저장
           if(FAILED(m_pDevice->GetRenderState(D3DRS_ZENABLE, &m_dwUseZ_Old)))
           {
                     mASSERT(0);
                     return false;
           }
           if(FAILED(m_pDevice->SetRenderState(D3DRS_ZENABLE, FALSE)))
           {
                     mASSERT(0);
                     return false;
           }
 
           // 렌더타겟 청소
           if(m_bRenderTargetClear == true)
           {
                     m_pDevice->Clear(0, NULL, D3DCLEAR_TARGET,
                                          m_dwRenderTargetClearColor, 1.0f, 0);
           }
 
           return true;
}
bool mResTextureRenderTarget::restoreTarget()
{
           if((m_pDevice == 0)||(m_pTexture == 0)) return false;
           if(m_pRenderTarget_Old == 0) return false;
           if(m_pRenderTarget_New == 0) return false;
 
           // Z설정복구
           if(FAILED(m_pDevice->SetRenderState(D3DRS_ZENABLE, m_dwUseZ_Old)))
           {
                     mASSERT(0);
                     return false;
           }
 
           // 렌더타겟 복구
           if(FAILED(m_pDevice->SetRenderTarget(0, m_pRenderTarget_Old)))
           {
                     mASSERT(0);
                     return false;
           }
 
           MRELEASE(m_pRenderTarget_Old);
           MRELEASE(m_pRenderTarget_New);
 
           return true;
}
 
초반의 설명과 마찬가지로, 이전 설정을 저장하고 다시 복구하는 과정을 둘로 나눠놓은 것인데, mResTextureRenderTarget는 Z버퍼를 사용하지 않는 클래스로 설정한 까닭에(Z버퍼를 사용하는 클래스는 여기로부터 한번 더 상속된다), GetRenderState()통해서 ZENABLE상태도 함께 저장한다. 또한, m_bRenderTargetClear, m_dwRenderTargetClearColor 와 아래의 함수들을 추가하여, 렌더타겟 셋업 상황에서 배경을 지울지 말지 선택하고, 배경색을 지정할 수 있다.
 
void                          setRenderTargetClear(bool bFlag);
bool                           getRenderTargetClear();
void                          setRenderTargetClearColor(D3DCOLOR dwColor);
D3DCOLOR                  getRenderTargetClearColor();
 
setRenderTargetClear(), setRenderTargetClearColor()의 내부에서는 setIsTargetEmpty(true)를 호출하여 다시 렌더타겟을 사용하는 측이 결과물을 다시 갱신하도록 상태를 설정해 놓는다.
 
이제 Depth-Stencil 버퍼(이하 Z버퍼라고……)를 추가한 mResTextureRenderTargetZ를 정의할 것이다. 우선 아래의 변수를 준비한다.
 
// setup/restore depth-stencil buffer
IDirect3DSurface9*       m_pDepthStencil;
IDirect3DSurface9*       m_pDepthStencil_Old;
 
// clear depth-stencil on setupTarget()
bool                           m_bRenderTargetClearZ;
 
createTargetTexture()에는 아래와 같이 Z버퍼를 생성하는 부분이 추가되었다.
 
// DepthStencil버퍼 생성
if(FAILED(m_pDevice->CreateDepthStencilSurface(
           m_uWidth,
           m_uHeight,
           g_museKernel.getDepthStencilFormat(),
           D3DMULTISAMPLE_NONE,
           D3DMULTISAMPLE_NONE,
           TRUE,
           &m_pDepthStencil,
           0)))
{
           onDestroyDevice();
           return false;
}
 
마찬가지로, setupTarget()에서는 아래와 같이 변경되었다.
 
// 이전 ***********************************************************
// Z설정저장
if(FAILED(m_pDevice->GetRenderState(D3DRS_ZENABLE, &m_dwUseZ_Old)))
{
           mASSERT(0);
           return false;
}
if(FAILED(m_pDevice->SetRenderState(D3DRS_ZENABLE, FALSE)))
{
           mASSERT(0);
           return false;
}
// 렌더타겟 청소
if(m_bRenderTargetClear == true)
{
           m_pDevice->Clear(0, NULL,
                      D3DCLEAR_TARGET, m_dwRenderTargetClearColor, 1.0f, 0);
}
 
// 이후 ***********************************************************
// DepthStencil 설정
hr = m_pDevice->GetDepthStencilSurface(&m_pDepthStencil_Old);
if(FAILED(hr)||(m_pDepthStencil_Old == 0))
{
           mASSERT(0);
           return false;
}
hr = m_pDevice->SetDepthStencilSurface(m_pDepthStencil);
if(FAILED(hr))
{
           mASSERT(0);
           return false;
}
if(m_bRenderTargetClearZ == true)
{
           if(m_bRenderTargetClear == true)
           {
                     m_pDevice->Clear(0, NULL,
                                D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
                                 m_dwRenderTargetClearColor, 1.0f, 0L);
           }
           else
           {
                     m_pDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER,
                                m_dwRenderTargetClearColor, 1.0f, 0L);
           }
}
else
{
           if(m_bRenderTargetClear == true)
           {
                     m_pDevice->Clear(0, NULL, D3DCLEAR_TARGET,
                                m_dwRenderTargetClearColor, 1.0f, 0L);
           }
}
 
restoreTarget()역시 변경되었다.
 
// 이전 ***********************************************************
// Z설정복구
if(FAILED(m_pDevice->SetRenderState(D3DRS_ZENABLE, m_dwUseZ_Old)))
{
           mASSERT(0);
           return false;
}
 
// 이후 ***********************************************************
// DepthStencil 복구
if(FAILED(m_pDevice->SetDepthStencilSurface(m_pDepthStencil_Old)))
{
           mASSERT(0);
           return false;
}

XML 사용법 파싱


1 2 3 4 5 6