사용하는 컴파일러는 vc2005 express 버전입니다.
vc2005 express 는 dll 프로젝트를 만들 수 있는 위자드가 없으므로
첨부 파일에 포함된 Direct3DProjectMaker.py 를 사용합니다.
from ctypes import *
import os
VCPROJ_FORM = open("project_template/template.vcproj").read()
PROJECT_DEF_FORM = open("project_template/project.def").read()
PROJECT_CPP_FORM = open("project_template/project.cpp").read()
PROJECT_PY_FORM = open("project_template/project.py").read()
PROJECT_H_FORM = open("project_template/project.h").read()
STDAFX_CPP_FORM = open("project_template/stdafx.cpp").read()
STDAFX_H_FORM = open("project_template/stdafx.h").read()
class GUID(Structure):
_fields_ = [
("data1", c_ulong),
("data2", c_ushort),
("data3", c_ushort),
("data4", c_ubyte * 2),
("data5", c_ubyte * 6),
]
def __init__(self):
windll.ole32.CoCreateGuid(byref(self))
def __str__(self):
return "%X-%X-%X-%s-%s" % (self.data1, self.data2, self.data3,
"".join("%X" % (c) for c in self.data4),
"".join("%X" % (c) for c in self.data5))
def __repr__(self):
return "{%s}" % self.__str__()
def MakeProject(name):
PROJECT_GUID = str(GUID())
PROJECT_NAME = name
PROJECT_EXPORT = "%s_EXPORT" % (PROJECT_NAME.upper())
PROJECT_INC = "$(DXSDK_DIR)/include"
PROJECT_DEBUG_LIB = "$(DXSDK_DIR)/Lib/x86"
PROJECT_RELEASE_LIB = "$(DXSDK_DIR)/Lib/x86"
os.mkdir(name)
open("%s/%s.vcproj" % (name, name), "w").write(VCPROJ_FORM % locals())
open("%s/%s.def" % (name, name), "w").write(PROJECT_DEF_FORM % (locals()))
open("%s/%s.cpp" % (name, name), "w").write(PROJECT_CPP_FORM)
open("%s/%s.py" % (name, name), "w").write(PROJECT_PY_FORM % (locals()))
open("%s/%s.h" % (name, name), "w").write(PROJECT_H_FORM)
open("%s/stdafx.cpp" % (name), "w").write(STDAFX_CPP_FORM)
open("%s/stdafx.h" % (name), "w").write(STDAFX_H_FORM)
if __name__ == "__main__":
MakeProject("temp")
MakeProject 에 프로젝트 이름을 적고 실행하면 해당 이름을 가진 vc2005 dll 프로젝트가 만들어집니다.
D3D SDK 위치는 $(DXSDK_DIR) 를 사용하며, 환경변수로 DXSDK_DIR 를 만들어주거나, 해당 위치에 직접 경로를 적어주면 됩니다.
2. d3d 코드 작성
d3d 코드를 작성 합니다. 여기서는 d3d 예제인 vertices.cpp 파일을 복사한 후
WinMain 과 MsgProc 부분을 제거했습니다.
#include "stdafx.h"
//-----------------------------------------------------------------------------
// File: Vertices.cpp
//
// Desc: In this tutorial, we are rendering some vertices. This introduces the
// concept of the vertex buffer, a Direct3D object used to store
// vertices. Vertices can be defined any way we want by defining a
// custom structure and a custom FVF (flexible vertex format). In this
// tutorial, we are using vertices that are transformed (meaning they
// are already in 2D window coordinates) and lit (meaning we are not
// using Direct3D lighting, but are supplying our own colors).
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include <d3d9.h>
#pragma warning( disable : 4996 ) // disable deprecated warning
#include <strsafe.h>
#pragma warning( default : 4996 )
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9 g_pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Our rendering device
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices
// A structure for our custom vertex type
struct CUSTOMVERTEX
{
FLOAT x, y, z, rhw; // The transformed position for the vertex
DWORD color; // The vertex color
};
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
// Create the D3D object.
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
// Create the D3DDevice
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Device state would normally be set here
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: InitVB()
// Desc: Creates a vertex buffer and fills it with our vertices. The vertex
// buffer is basically just a chuck of memory that holds vertices. After
// creating it, we must Lock()/Unlock() it to fill it. For indices, D3D
// also uses index buffers. The special thing about vertex and index
// buffers is that they can be created in device memory, allowing some
// cards to process them in hardware, resulting in a dramatic
// performance gain.
//-----------------------------------------------------------------------------
HRESULT InitVB()
{
// Initialize three vertices for rendering a triangle
CUSTOMVERTEX vertices[] =
{
{ 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color
{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
};
// Create the vertex buffer. Here we are allocating enough memory
// (from the default pool) to hold all our 3 custom vertices. We also
// specify the FVF, so the vertex buffer knows what data it contains.
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
{
return E_FAIL;
}
// Now we fill the vertex buffer. To do this, we need to Lock() the VB to
// gain access to the vertices. This mechanism is required becuase vertex
// buffers may be in device memory.
VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
return E_FAIL;
memcpy( pVertices, vertices, sizeof(vertices) );
g_pVB->Unlock();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
if( g_pVB != NULL )
g_pVB->Release();
if( g_pd3dDevice != NULL )
g_pd3dDevice->Release();
if( g_pD3D != NULL )
g_pD3D->Release();
}
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()
{
// Clear the backbuffer to a blue color
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Begin the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Draw the triangles in the vertex buffer. This is broken into a few
// steps. We are passing the vertices down a "stream", so first we need
// to specify the source of that stream, which is our vertex buffer. Then
// we need to let D3D know what vertex shader to use. Full, custom vertex
// shaders are an advanced topic, but in most cases the vertex shader is
// just the FVF, so that D3D knows what type of vertices we are dealing
// with. Finally, we call DrawPrimitive() which does the actual rendering
// of our geometry (in this case, just one triangle).
g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
// End the scene
g_pd3dDevice->EndScene();
}
// Present the backbuffer contents to the display
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
3. def 파일 작성
def 파일에 파이썬에서 사용할 함수 이름을 적습니다.
LIBRARY tutorial
EXPORTS
InitD3D
InitVB
Render
Cleanup
4. 파이썬 코드 작성
ctypes 를 사용해 tutorial.def 의 함수 리스트 정보를 토대로 tutorial.dll 함수를 임포트합니다.
import ctypes
def Import(modName, funcNameList):
globalDict = globals()
mod = ctypes.cdll.__getattr__(modName)
for funcName in funcNameList:
func = mod.__getattr__(funcName)
func.restype = None
globalDict[funcName] = func
Import("tutorial", [funcName
for funcName in [line.strip() for line in open("tutorial.def").readlines()[2:]] if funcName])
from ctypes.wintypes import HRESULT
InitD3D.restype = HRESULT
InitVB.restype = HRESULT
S_OK = 0
if __name__ == "__main__":
import wx
class TestFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, -1, title, pos=(0, 0), size=(320, 240))
self.CentreOnScreen(wx.BOTH)
if self.__Startup():
self.Bind(wx.EVT_PAINT, self.__OnPaint)
def __del__(self):
self.__Cleanup()
def __Startup(self):
if InitD3D(self.GetHandle()) != S_OK:
print "InitD3D.Error"
return False
if InitVB() != S_OK:
print "InitVB.Error"
return False
return True
def __OnPaint(self, event):
dc = wx.PaintDC(self)
Render()
def __Cleanup(self):
Cleanup()
class TestApp(wx.App):
def OnInit(self):
"OnInit"
frame = TestFrame(None, "TestApp")
frame.Show()
self.SetTopWindow(frame)
return True
def OnExit(self):
"OnExit"
pass
TestApp(redirect=False).MainLoop()
wxPython 에서도 OnIdle 을 지원하긴 하지만, Threading 을 사용하는 것이 더 좋은 듯합니다. 이 방법에 대해서는 추후에 다루어 보도록 하겠습니다.


python 을 좋아하는 게임 프로그래머