posted by REDFORCE 2017. 4. 18. 23:18

#1. Engine System

 + WinMain - EngineSystem

 + EngineCore

 + EngineError

 + MainNode


#2. FrameWork

 + Grahpics  (현재 글)

 + Image - SpriteData

 + Layer

 + Input

 + Game

 + Manager

 + Game Interface

 + Utility


#3. Testing Module

 + 2D Image Test

 + 2D Animation Test

 + Game UI Test


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


이번 글에서는 앞서 다룬 DrawSprite 함수에 이어서 적도록 하겠습니다.



1. changeDisplayMode (화면 전환 함수 [창모드/전체화면모드] )


이어서 다룰 첫 번째 함수는 changeDisplayMode 입니다.


이름만봐도 알 수 있듯이 위 함수는 전체화면이나 창모드 화면으로 변경할 시에 사용 되는 함수입니다.


사용빈도 자체는 높진 않지만 일반적인 게임이라면 필수적으로 지원되야하는


기능이라 할 수 있습니다.


우리는 일상에서 많은 게임들이 Alt+Enter 키를 누르면 전체화면이나 창모드로 바뀌는 그런 경우를 볼 수 있었을 것 입니다.


단축키 자체는 별도로 작성해서 이 함수를 호출하도록 하게끔만 만들면 되므로

키 입력에 대한 내용은 추후 Input Class 에서 다루도록 하고 여기서는


어떻게 화면이 변환되는지에 대해서만 언급하겠습니다.


먼저 코드를 살펴보겠습니다.


//=============================================================================
// Toggle window or fullscreen mode
// Pre: All user created D3DPOOL_DEFAULT surfaces are freed.
// Post: All user surfaces are recreated.
//=============================================================================
void Graphics::changeDisplayMode(graphicsNS::DISPLAY_MODE mode)
{
try{
switch(mode)
{
case graphicsNS::FULLSCREEN:
if(fullscreen) // if already in fullscreen mode
return;
fullscreen = true; break;
case graphicsNS::WINDOW:
if(fullscreen == false) // if already in window mode
return;
fullscreen = false; break;
default: // default to toggle window/fullscreen
fullscreen = !fullscreen;
}
reset();
if(fullscreen) // fullscreen
{
SetWindowLong(hwnd, GWL_STYLE, WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP);
}
else // windowed
{
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
} catch(...)
{
// An error occured, try windowed mode
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
}



함수 내용 전체는 try - catch 문으로 감싸져 있습니다.


이유는 미리 우리가 정의해놓은 어떤 디스플레이 모드 상에서 바뀌는 거라면

상관이 없지만 어떤 에러로 인해 제대로 동작이 안될 경우를 대비하여 


안전하게 화면을 바꾸기 위함입니다.



천천히 try 문 부터 살펴보도록 하겠습니다.



switch 문으로 mode 플래그 값을 통해 구분이 되어있는데


동작 원리는 밖에 선언해두었던 fullscreen (bool) 값을 이용하여 단순히 true / false 를 교체해주는 행위를 합니다.


그리고 switch 문에서 나오면 


reset() 함수를 호출하네요.


reset 함수의 내용은 아래와 같습니다.


//=============================================================================
// Reset the graphics device
//=============================================================================
HRESULT Graphics::reset()
{
safeRelease(line);
safeRelease(pOcclusionQuery); // release query
initD3Dpp(); // init D3D presentation parameters
sprite->OnLostDevice(); // release sprite
result = device3d->Reset(&d3dpp); // attempt to reset graphics device
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// recreate query
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
sprite->OnResetDevice(); // recreate sprite
createLine(&line); // recreate a DirectX line
return result;
}
view raw reset.cpp hosted with ❤ by GitHub


그리고 fullscreen 값을 체크하여 true 라면

현재 모니터의 전체 화면으로 메인 윈도우를 늘려주고,


아니라면 현재 게임의 width / height 값으로 창화면을 만들어줍니다.


catch 구문으로 갔을 경우의 하는 역할도 마찬가지 입니다.



2. pixelCollision (픽셀 충돌 함수)


두 번째로 다룰 함수는 픽셀 충돌 함수 입니다.


pixelCollision 함수는 저도 꽤 어려운 구역이라 어떻게 설명을 드려야 할지 저도 잘 모르겠습니다. 그래도 아에 스킵하고 넘어갈순 없으므로 어떤 식으로 픽셀 충돌을 검출하는지 확인해보겠습니다.


다음이 픽셀 충돌 함수의 내용입니다.


//=============================================================================
// Return the number of pixels colliding between the two sprites.
// Pre: The device supports a stencil buffer and pOcclusionQuery points to
// a valid occlusionQuery object.
// Post: Returns the number of pixels of overlap
// This function waits for the graphics card to render the last frame and return
// the collision query pixel count. To avoid slowing down your game, use a
// simple collison test first to eliminate entities that are not colliding.
// Using pixel perfect collision test with multiple entities would require
// creating multiple occlusionQuery objects and clearing the stencil buffer
// between each test.
//=============================================================================
DWORD Graphics::pixelCollision(const SpriteData &sprite1, const SpriteData &sprite2)
{
if(!stencilSupport) // if no stencil buffer support
return 0;
beginScene();
// Set up stencil buffer using current entity
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
device3d->SetRenderState(D3DRS_STENCILREF, 0x1);
device3d->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
device3d->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
device3d->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
// Write a 1 into the stencil buffer for each non-transparent pixel in ent
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite2); // write 1s to stencil buffer
spriteEnd();
// Change stencil buffer to only allow writes where the stencil value is 1
// (where the ent sprite is colliding with the current sprite)
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
// Begin occlusion query to count pixels that are drawn
pOcclusionQuery->Issue(D3DISSUE_BEGIN);
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite1); // draw current entity
spriteEnd();
// End occlusion query
pOcclusionQuery->Issue(D3DISSUE_END);
// Wait until the GPU is finished.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfPixelsColliding,
sizeof(DWORD), D3DGETDATA_FLUSH ))
{}
// Turn off stencil
device3d->SetRenderState(D3DRS_STENCILENABLE, false);
endScene();
return numberOfPixelsColliding;
}


픽셀 충돌은 먼저 두 개의 spriteData 구조체 파라미터를 받습니다.

그리고 다소 어려운 내용일 수도 있지만 깊이 버퍼 값을 이용하여 픽셀 충돌을 검사합니다.


코드를 보시면, 처음 stencilSupport 여부를 체크하는데요.

이것은 그래픽카드에서 깊이 버퍼를 이용한 기술적 지원을 해주는지 여부를 확인합니다.

(위 stencilSupport 값은 Initialize( ) 에서 true / false 여부가 판단되어 있습니다)


다음, 깊이 버퍼 기술을 이용할 수 있다면


다양한 RenderState를 설정하는 것을 볼 수 있습니다.

이 값들이 의미하는 바에 대해서는 여기서 일일이 설명드리기에 너무 많으므로


MSDN이나 구글링 하시는 것을 통해 알아가시기를 양해드립니다.



첫 번째, 


중요한 요점은 D3DRS_STENCILE 이라 하는 RenderState 값들을 이용한다는 점을 숙지하는 것 입니다.



두 번째,


설정한 RenderState 값들 중에 몇가지는 spriteBegin() 이 호출된 이후에 설정되어야 하는 값들이 있습니다.



세 번째,


pOcclusionQuery (IDirect3DQuery9*)를 이용하여 두 스프라이트의 픽셀 갯수만큼 while 문을 돌고 픽셀 충돌 여부를 검출합니다.


그리고 반환 된 충돌 된 픽셀 갯수가 numberOfPixelsColliding 에 담겨서 리턴되게 됩니다.



만약 이 함수를 이용하여 픽셀 충돌을 검사한다면 numberOfPixelsColliding 값이 0보다 큰지 아닌지만 검사한다면 픽셀 충돌이 이뤄졌는가 아닌가를 알 수 있습니다.



여기까지가 주요 Graphics Class에 대한 내용들이었습니다.


의외로 세 번에 걸쳐서 글을 쓰게 될줄은 몰랐습니다.



처음에는 한번에 다 적을 수 있겠지~~ 했는데, 

코드를 포함하여 적다보니 량이 적지않구나 하는걸 느낀 후로 조금 나눠서 글을 적게 되었습니다.


만약 여기까지 읽어주셨다면 정말 감사드리고, 궁금하신 점 있으시면 답글이나 이메일

redforce01@naver.com 으로 주시면 제가 아는 한에서 꼭 답변해드리겠습니다.




추신. getDeviceState() 함수가 여기서는 다뤄지지 않은 것 같습니다만,

추후에 reset() 함수를 다시 활용할 때 한번 더 정리 하여 올릴 예정입니다.



아래는 Graphics 클래스의 .h 파일과 .cpp 파일 전체 입니다.


헤더 코드입니다.

#ifndef _GRAPHICS_H // Prevent multiple definitions if this
#define _GRAPHICS_H // file is included in more than one place
#define WIN32_LEAN_AND_MEAN
#ifdef _DEBUG
#define D3D_DEBUG_INFO
#endif
class Graphics;
#include <d3d9.h>
#include <d3dx9.h>
#include <D3dx9core.h>
#include "constants.h"
#include "gameError.h"
// DirectX pointer types
#define LP_TEXTURE LPDIRECT3DTEXTURE9
#define LP_SPRITE LPD3DXSPRITE
#define LP_3DDEVICE LPDIRECT3DDEVICE9
#define LP_3D LPDIRECT3D9
#define VECTOR2 D3DXVECTOR2
#define LP_VERTEXBUFFER LPDIRECT3DVERTEXBUFFER9
#define LP_DXFONT LPD3DXFONT
#define LP_VERTEXBUFFER LPDIRECT3DVERTEXBUFFER9
#define LP_DXLINE LPD3DXLINE
// Color defines
// ARGB numbers range from 0 through 255
// a = Alpha channel (transparency where 255 is opaque)
// r = Red, g = Green, b = Blue
#define COLOR_ARGB DWORD
#define SETCOLOR_ARGB(a,r,g,b) \
((COLOR_ARGB)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
namespace graphicsNS
{
// Some common colors
// ARGB numbers range from 0 through 255
// A = Alpha channel (transparency where 255 is opaque)
// R = Red, G = Green, B = Blue
const COLOR_ARGB ORANGE = D3DCOLOR_ARGB(255,255,165, 0);
const COLOR_ARGB BROWN = D3DCOLOR_ARGB(255,139, 69, 19);
const COLOR_ARGB LTGRAY = D3DCOLOR_ARGB(255,192,192,192);
const COLOR_ARGB GRAY = D3DCOLOR_ARGB(255,128,128,128);
const COLOR_ARGB OLIVE = D3DCOLOR_ARGB(255,128,128, 0);
const COLOR_ARGB PURPLE = D3DCOLOR_ARGB(255,128, 0,128);
const COLOR_ARGB MAROON = D3DCOLOR_ARGB(255,128, 0, 0);
const COLOR_ARGB TEAL = D3DCOLOR_ARGB(255, 0,128,128);
const COLOR_ARGB GREEN = D3DCOLOR_ARGB(255, 0,128, 0);
const COLOR_ARGB NAVY = D3DCOLOR_ARGB(255, 0, 0,128);
const COLOR_ARGB WHITE = D3DCOLOR_ARGB(255,255,255,255);
const COLOR_ARGB YELLOW = D3DCOLOR_ARGB(255,255,255, 0);
const COLOR_ARGB MAGENTA = D3DCOLOR_ARGB(255,255, 0,255);
const COLOR_ARGB RED = D3DCOLOR_ARGB(255,255, 0, 0);
const COLOR_ARGB CYAN = D3DCOLOR_ARGB(255, 0,255,255);
const COLOR_ARGB LIME = D3DCOLOR_ARGB(255, 0,255, 0);
const COLOR_ARGB BLUE = D3DCOLOR_ARGB(255, 0, 0,255);
const COLOR_ARGB BLACK = D3DCOLOR_ARGB(255, 0, 0, 0);
const COLOR_ARGB FILTER = D3DCOLOR_ARGB(255,255,255,255); // use to specify drawing with colorFilter
const COLOR_ARGB ALPHA25 = D3DCOLOR_ARGB( 64,255,255,255); // AND with color to get 25% alpha
const COLOR_ARGB ALPHA50 = D3DCOLOR_ARGB(128,255,255,255); // AND with color to get 50% alpha
const COLOR_ARGB BACK_COLOR = NAVY; // background color of game
const COLOR_ARGB TRANSCOLOR = BLACK; // transparent color
enum DISPLAY_MODE{TOGGLE, FULLSCREEN, WINDOW};
}
struct VertexC // Vertex with Color
{
float x, y, z; // vertex location
float rhw; // reciprocal homogeneous W (set to 1)
unsigned long color; // vertex color
};
// Flexible Vertex Format Codes
// D3DFVF_XYZRHW = The verticies are transformed
// D3DFVF_DIFFUSE = The verticies contain diffuse color data
#define D3DFVF_VERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
// SpriteData: The properties required by Graphics::drawSprite to draw a sprite
struct SpriteData
{
int width; // width of sprite in pixels
int height; // height of sprite in pixels
float x; // screen location (top left corner of sprite)
float y;
float scale; // <1 smaller, >1 bigger
float angle; // rotation angle in radians
RECT rect; // used to select an image from a larger texture
LP_TEXTURE texture; // pointer to texture
bool flipHorizontal; // true to flip sprite horizontally (mirror)
bool flipVertical; // true to flip sprite vertically
};
class Graphics
{
private:
// DirectX pointers and stuff
LP_3D direct3d;
LP_3DDEVICE device3d;
LP_SPRITE sprite;
LP_DXLINE line; // line pointer
D3DPRESENT_PARAMETERS d3dpp;
D3DDISPLAYMODE pMode;
IDirect3DQuery9* pOcclusionQuery; // for pixel perfect collision detection
DWORD numberOfPixelsColliding; // for pixel perfect collision detection
// other variables
HRESULT result; // standard Windows return codes
HWND hwnd;
bool fullscreen;
bool stencilSupport; // true if device supports stencil buffer
int width;
int height;
COLOR_ARGB backColor; // background color
// (For internal engine use only. No user serviceable parts inside.)
// Initialize D3D presentation parameters
void initD3Dpp();
public:
// Constructor
Graphics();
// Destructor
virtual ~Graphics();
// Releases direct3d and device3d.
void releaseAll();
// Initialize DirectX graphics
// Throws GameError on error
// Pre: hw = handle to window
// width = width in pixels
// height = height in pixels
// fullscreen = true for full screen, false for window
void initialize(HWND hw, int width, int height, bool fullscreen);
// Create a vertex buffer.
// Pre: verts[] contains vertex data.
// size = size of verts[]
// Post: &vertexBuffer points to buffer if successful.
HRESULT createVertexBuffer(VertexC verts[], UINT size, LP_VERTEXBUFFER &vertexBuffer);
// Display a quad (rectangle) with alpha transparency.
// Pre: createVertexBuffer was used to create vertexBuffer containing four
// vertices defining the quad in clockwise order.
// g3ddev->BeginScene was called
bool drawQuad(LP_VERTEXBUFFER vertexBuffer);
// Load the texture into default D3D memory (normal texture use)
// For internal engine use only. Use the TextureManager class to load game textures.
// Pre: filename = name of texture file.
// transcolor = transparent color
// Post: width and height = size of texture
// texture points to texture
HRESULT loadTexture(const char * filename, COLOR_ARGB transcolor, UINT &width, UINT &height, LP_TEXTURE &texture);
// Load the texture into system memory (system memory is lockable)
// Provides direct access to pixel data. Use the TextureManager class to load textures for display.
// Pre: filename = name of texture file.
// transcolor = transparent color
// Post: width and height = size of texture
// texture points to texture
HRESULT loadTextureSystemMem(const char *filename, COLOR_ARGB transcolor, UINT &width, UINT &height, LP_TEXTURE &texture);
// Display the offscreen backbuffer to the screen.
HRESULT showBackbuffer();
// Checks the adapter to see if it is compatible with the BackBuffer height,
// width and refresh rate specified in d3dpp. Fills in the pMode structure with
// the format of the compatible mode, if found.
// Pre: d3dpp is initialized.
// Post: Returns true if compatible mode found and pMode structure is filled.
// Returns false if no compatible mode found.
bool isAdapterCompatible();
// Draw the sprite described in SpriteData structure.
// color is optional, it is applied as a filter, WHITE is default (no change).
// Creates a sprite Begin/End pair.
// Pre: spriteData.rect defines the portion of spriteData.texture to draw
// spriteData.rect.right must be right edge + 1
// spriteData.rect.bottom must be bottom edge + 1
void drawSprite(const SpriteData &spriteData, // sprite to draw
COLOR_ARGB color = graphicsNS::WHITE); // default to white color filter (no change)
// Reset the graphics device.
HRESULT reset();
// Toggle, fullscreen or window display mode
// Pre: All user created D3DPOOL_DEFAULT surfaces are freed.
// Post: All user surfaces are recreated.
void changeDisplayMode(graphicsNS::DISPLAY_MODE mode = graphicsNS::TOGGLE);
// Return length of vector v.
static float Vector2Length(const VECTOR2 *v) {return D3DXVec2Length(v);}
// Return Dot product of vectors v1 and v2.
static float Vector2Dot(const VECTOR2 *v1, const VECTOR2 *v2) {return D3DXVec2Dot(v1, v2);}
// Normalize vector v.
static void Vector2Normalize(VECTOR2 *v) {D3DXVec2Normalize(v, v);}
// Transform vector v with matrix m.
static VECTOR2* Vector2Transform(VECTOR2 *v, D3DXMATRIX *m) {return D3DXVec2TransformCoord(v,v,m);}
// Return the number of pixels colliding between the two sprites.
// Pre: The device supports a stencil buffer and pOcclusionQuery points to
// a valid occlusionQuery object.
// Post: Returns the number of pixels of overlap
// This function waits for the graphics card to render the last frame and return
// the collision query pixel count. To avoid slowing down your game, use a
// simple collison test first to eliminate entities that are not colliding.
DWORD pixelCollision(const SpriteData &sprite1, const SpriteData &sprite2);
// get functions
// Return direct3d.
LP_3D get3D() { return direct3d; }
// Return device3d.
LP_3DDEVICE get3Ddevice() { return device3d; }
// Return sprite
LP_SPRITE getSprite() { return sprite; }
// Return handle to device context (window).
HDC getDC() { return GetDC(hwnd); }
// Test for lost device
HRESULT getDeviceState();
// Return fullscreen
bool getFullscreen() { return fullscreen; }
// Return pOcclusionQuery
IDirect3DQuery9* getPOcclusionQuery() { return pOcclusionQuery; }
// Returns true if the graphics card supports a stencil buffer
bool getStencilSupport() { return stencilSupport; }
// Set color used to clear screen
void setBackColor(COLOR_ARGB c) {backColor = c;}
//=============================================================================
// Create DX line, description from D3dx9core.h
// This object intends to provide an easy way to draw lines using D3D.
//
// Begin -
// Prepares device for drawing lines
// Draw -
// Draws a line strip in screen-space.
// Input is in the form of a array defining points on the line strip. of D3DXVECTOR2
// DrawTransform -
// Draws a line in screen-space with a specified input transformation matrix.
// End -
// Restores device state to how it was when Begin was called.
// SetPattern -
// Applies a stipple pattern to the line. Input is one 32-bit
// DWORD which describes the stipple pattern. 1 is opaque, 0 is
// transparent.
// SetPatternScale -
// Stretches the stipple pattern in the u direction. Input is one
// floating-point value. 0.0f is no scaling, whereas 1.0f doubles
// the length of the stipple pattern.
// SetWidth -
// Specifies the thickness of the line in the v direction. Input is
// one floating-point value.
// SetAntialias -
// Toggles line antialiasing. Input is a BOOL.
// TRUE = Antialiasing on.
// FALSE = Antialiasing off.
// SetGLLines -
// Toggles non-antialiased OpenGL line emulation. Input is a BOOL.
// TRUE = OpenGL line emulation on.
// FALSE = OpenGL line emulation off.
// OpenGL line: Regular line:
// *\ *\
// | \ / \
// | \ *\ \
// *\ \ \ \
// \ \ \ \
// \ * \ *
// \ | \ /
// \| *
// *
// OnLostDevice, OnResetDevice -
// Call OnLostDevice() on this object before calling Reset() on the
// device, so that this object can release any stateblocks and video
// memory resources. After Reset(), the call OnResetDevice().
//=============================================================================
void createLine(LP_DXLINE *line)
{
D3DXCreateLine(device3d, line);
}
//=============================================================================
// Draw DirectX line from X1,Y1 to X2,Y2.
// width defauts to 1.
// color defaults to graphicsNS::WHITE.
//=============================================================================
void drawLine(float x1, float y1, float x2, float y2, float width = 1.0f, COLOR_ARGB color = graphicsNS::WHITE);
//=============================================================================
// Clear backbuffer and BeginScene()
//=============================================================================
HRESULT beginScene()
{
result = E_FAIL;
if(device3d == NULL)
return result;
// Clear back buffer, stencil buffer and depth buffer
device3d->Clear(0, 0,
D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
backColor, 1.0f, 0);
result = device3d->BeginScene(); // begin scene for drawing
return result;
}
//=============================================================================
// EndScene()
//=============================================================================
HRESULT endScene()
{
result = E_FAIL;
if(device3d)
result = device3d->EndScene();
return result;
}
//=============================================================================
// Sprite Begin
//=============================================================================
void spriteBegin()
{
sprite->Begin(D3DXSPRITE_ALPHABLEND);
}
//=============================================================================
// Sprite End
//=============================================================================
void spriteEnd()
{
sprite->End();
}
};
#endif
view raw Graphics.h hosted with ❤ by GitHub


cpp 코드입니다.


#include "stdafx.h"
#include "graphics.h"
//=============================================================================
// Constructor
//=============================================================================
Graphics::Graphics()
{
direct3d = NULL;
device3d = NULL;
sprite = NULL;
fullscreen = false;
stencilSupport = false;
width = GAME_WIDTH; // width & height are replaced in initialize()
height = GAME_HEIGHT;
backColor = graphicsNS::BACK_COLOR;
pOcclusionQuery = NULL;
numberOfPixelsColliding = 0;
line = NULL;
}
//=============================================================================
// Destructor
//=============================================================================
Graphics::~Graphics()
{
releaseAll();
}
//=============================================================================
// Initialize DirectX graphics
// throws GameError on error
//=============================================================================
void Graphics::initialize(HWND hw, int w, int h, bool full)
{
hwnd = hw;
width = w;
height = h;
fullscreen = full;
//initialize Direct3D
direct3d = Direct3DCreate9(D3D_SDK_VERSION);
if (direct3d == NULL)
throw(GameError(gameErrorNS::FATAL_ERROR, "Error initializing Direct3D"));
initD3Dpp(); // init D3D presentation parameters
if(fullscreen) // if full-screen mode
{
if(isAdapterCompatible()) // is the adapter compatible
// set the refresh rate with a compatible one
d3dpp.FullScreen_RefreshRateInHz = pMode.RefreshRate;
else
throw(GameError(gameErrorNS::FATAL_ERROR,
"The graphics device does not support the specified resolution and/or format."));
}
// determine if graphics card supports harware texturing and lighting and vertex shaders
D3DCAPS9 caps;
DWORD behavior;
result = direct3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
// If device doesn't support HW T&L or doesn't support 1.1 vertex
// shaders in hardware, then switch to software vertex processing.
if( (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0 ||
caps.VertexShaderVersion < D3DVS_VERSION(1,1) )
behavior = D3DCREATE_SOFTWARE_VERTEXPROCESSING; // use software only processing
else
behavior = D3DCREATE_HARDWARE_VERTEXPROCESSING; // use hardware only processing
//create Direct3D device
result = direct3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
behavior,
&d3dpp,
&device3d);
if (FAILED(result))
throw(GameError(gameErrorNS::FATAL_ERROR, "Error creating Direct3D device"));
result = D3DXCreateSprite(device3d, &sprite);
if (FAILED(result))
throw(GameError(gameErrorNS::FATAL_ERROR, "Error creating Direct3D sprite"));
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// Check if the device supports 8-bit stencil buffering.
if( FAILED( direct3d->CheckDeviceFormat(caps.AdapterOrdinal,
caps.DeviceType,
d3dpp.BackBufferFormat,
D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE,
D3DFMT_D24S8 ) ) )
stencilSupport = false;
else
stencilSupport = true;
// Create query object, used for pixel perfect collision detection
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
createLine(&line); // create a DirectX line
}
//=============================================================================
// Initialize D3D presentation parameters
//=============================================================================
void Graphics::initD3Dpp()
{
try{
ZeroMemory(&d3dpp, sizeof(d3dpp)); // fill the structure with 0
// fill in the parameters we need
d3dpp.BackBufferWidth = width;
d3dpp.BackBufferHeight = height;
if(fullscreen) // if fullscreen
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; // 24 bit color
else
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // use desktop setting
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hwnd;
d3dpp.Windowed = (!fullscreen);
if(VSYNC) // if vertical sync is enabled
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
else
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; // Depth 24, Stencil 8
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR,
"Error initializing D3D presentation parameters"));
}
}
//=============================================================================
// Load the texture into default D3D memory (normal texture use)
// For internal engine use only. Use the TextureManager class to load game textures.
// Pre: filename is name of texture file.
// transcolor is transparent color
// Post: width and height = size of texture
// texture points to texture
// Returns HRESULT
//=============================================================================
HRESULT Graphics::loadTexture(const char *filename, COLOR_ARGB transcolor,
UINT &width, UINT &height, LP_TEXTURE &texture)
{
// The struct for reading file info
D3DXIMAGE_INFO info;
result = E_FAIL;
try{
if(filename == NULL)
{
texture = NULL;
return D3DERR_INVALIDCALL;
}
// Get width and height from file
result = D3DXGetImageInfoFromFile(filename, &info);
if (result != D3D_OK)
return result;
width = info.Width;
height = info.Height;
// Create the new texture by loading from file
result = D3DXCreateTextureFromFileEx(
device3d, //3D device
filename, //image filename
info.Width, //texture width
info.Height, //texture height
1, //mip-map levels (1 for no chain)
0, //usage
D3DFMT_UNKNOWN, //surface format (default)
D3DPOOL_DEFAULT, //memory class for the texture
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info (from loaded file)
NULL, //color palette
&texture ); //destination texture
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
}
return result;
}
//=============================================================================
// Load the texture into system memory (system memory is lockable)
// Provides direct access to pixel data. Use the TextureManager class to load
// textures for display.
// Pre: filename is name of texture file.
// transcolor transparent color
// Post: width and height = size of texture
// texture points to texture
// Returns HRESULT and fills TextureData structure.
//=============================================================================
HRESULT Graphics::loadTextureSystemMem(const char *filename, COLOR_ARGB transcolor,
UINT &width, UINT &height, LP_TEXTURE &texture)
{
// The struct for reading bitmap file info
D3DXIMAGE_INFO info;
result = E_FAIL; // Standard Windows return value
try{
if(filename == NULL)
{
texture = NULL;
return D3DERR_INVALIDCALL;
}
// Get width and height from bitmap file
result = D3DXGetImageInfoFromFile(filename, &info);
if (result != D3D_OK)
return result;
width = info.Width;
height = info.Height;
// Create the new texture by loading a bitmap image file
result = D3DXCreateTextureFromFileEx(
device3d, //3D device
filename, //bitmap filename
info.Width, //bitmap image width
info.Height, //bitmap image height
1, //mip-map levels (1 for no chain)
0, //usage
D3DFMT_UNKNOWN, //surface format (default)
D3DPOOL_SYSTEMMEM, //systemmem is lockable
D3DX_DEFAULT, //image filter
D3DX_DEFAULT, //mip filter
transcolor, //color key for transparency
&info, //bitmap file info (from loaded file)
NULL, //color palette
&texture ); //destination texture
} catch(...)
{
throw(GameError(gameErrorNS::FATAL_ERROR, "Error in Graphics::loadTexture"));
}
return result;
}
//=============================================================================
// Create a vertex buffer.
// Pre: verts[] contains vertex data.
// size = size of verts[]
// Post: &vertexBuffer points to buffer if successful.
//=============================================================================
HRESULT Graphics::createVertexBuffer(VertexC verts[], UINT size, LP_VERTEXBUFFER &vertexBuffer)
{
// Standard Windows return value
HRESULT result = E_FAIL;
// Create a vertex buffer
result = device3d->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, D3DFVF_VERTEX,
D3DPOOL_DEFAULT, &vertexBuffer, NULL);
if(FAILED(result))
return result;
void *ptr;
// must lock buffer before data can be transferred in
result = vertexBuffer->Lock(0, size, (void**)&ptr, 0);
if(FAILED(result))
return result;
memcpy(ptr, verts, size); // copy vertex data into buffer
vertexBuffer->Unlock(); // unlock buffer
return result;
}
//=============================================================================
// Display a quad with alpha transparency using Triangle Fan
// Pre: createVertexBuffer was used to create vertexBuffer containing four
// vertices defining the quad in clockwise order.
// g3ddev->BeginScene was called
// Post: Quad is drawn
//=============================================================================
bool Graphics::drawQuad(LP_VERTEXBUFFER vertexBuffer)
{
HRESULT result = E_FAIL; // standard Windows return value
if(vertexBuffer == NULL)
return false;
device3d->SetRenderState(D3DRS_ALPHABLENDENABLE, true); // enable alpha blend
device3d->SetStreamSource(0, vertexBuffer, 0, sizeof(VertexC));
device3d->SetFVF(D3DFVF_VERTEX);
result = device3d->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
device3d->SetRenderState(D3DRS_ALPHABLENDENABLE, false); // alpha blend off
if(FAILED(result))
return false;
return true;
}
//=============================================================================
// Draw DirectX line from X1,Y1 to X2,Y2.
// width defauts to 1.
// color defaults to graphicsNS::WHITE.
//=============================================================================
void Graphics::drawLine(float x1, float y1, float x2, float y2, float width, COLOR_ARGB color)
{
VECTOR2 endpoints[] = { VECTOR2(x1, y1), VECTOR2(x2, y2) };
if (width < 0.1f)
width = 0.1f;
line->SetWidth(width); // SetWidth must be called before Begin
line->Begin(); // begin drawing lines
line->Draw(endpoints, 2, color); // draw line, 2 vertices
line->End(); // end drawing lines
}
//=============================================================================
// Display the backbuffer
//=============================================================================
HRESULT Graphics::showBackbuffer()
{
// Display backbuffer to screen
result = device3d->Present(NULL, NULL, NULL, NULL);
return result;
}
//=============================================================================
// Checks the adapter to see if it is compatible with the BackBuffer height,
// width and refresh rate specified in d3dpp. Fills in the pMode structure with
// the format of the compatible mode, if found.
// Pre: d3dpp is initialized.
// Post: Returns true if compatible mode found and pMode structure is filled.
// Returns false if no compatible mode found.
//=============================================================================
bool Graphics::isAdapterCompatible()
{
UINT modes = direct3d->GetAdapterModeCount(D3DADAPTER_DEFAULT,
d3dpp.BackBufferFormat);
for(UINT i=0; i<modes; i++)
{
result = direct3d->EnumAdapterModes( D3DADAPTER_DEFAULT,
d3dpp.BackBufferFormat,
i, &pMode);
if( pMode.Height == d3dpp.BackBufferHeight &&
pMode.Width == d3dpp.BackBufferWidth &&
pMode.RefreshRate >= d3dpp.FullScreen_RefreshRateInHz)
return true;
}
return false;
}
//=============================================================================
// Draw the sprite described in SpriteData structure
// Color is optional, it is applied like a filter, WHITE is default (no change)
// Pre : sprite->Begin() is called
// Post: sprite->End() is called
// spriteData.rect defines the portion of spriteData.texture to draw
// spriteData.rect.right must be right edge + 1
// spriteData.rect.bottom must be bottom edge + 1
//=============================================================================
void Graphics::drawSprite(const SpriteData &spriteData, COLOR_ARGB color)
{
if(spriteData.texture == NULL) // if no texture
return;
// Find center of sprite
D3DXVECTOR2 spriteCenter=D3DXVECTOR2((float)(spriteData.width/2*spriteData.scale),
(float)(spriteData.height/2*spriteData.scale));
// Screen position of the sprite
D3DXVECTOR2 translate=D3DXVECTOR2((float)spriteData.x,(float)spriteData.y);
// Scaling X,Y
D3DXVECTOR2 scaling(spriteData.scale,spriteData.scale);
if (spriteData.flipHorizontal) // if flip horizontal
{
scaling.x *= -1; // negative X scale to flip
// Get center of flipped image.
spriteCenter.x -= (float)(spriteData.width*spriteData.scale);
// Flip occurs around left edge, translate right to put
// Flipped image in same location as original.
translate.x += (float)(spriteData.width*spriteData.scale);
}
if (spriteData.flipVertical) // if flip vertical
{
scaling.y *= -1; // negative Y scale to flip
// Get center of flipped image
spriteCenter.y -= (float)(spriteData.height*spriteData.scale);
// Flip occurs around top edge, translate down to put
// Flipped image in same location as original.
translate.y += (float)(spriteData.height*spriteData.scale);
}
// Create a matrix to rotate, scale and position our sprite
D3DXMATRIX matrix;
D3DXMatrixTransformation2D(
&matrix, // the matrix
NULL, // keep origin at top left when scaling
0.0f, // no scaling rotation
&scaling, // scale amount
&spriteCenter, // rotation center
(float)(spriteData.angle), // rotation angle
&translate); // X,Y location
// Tell the sprite about the matrix "Hello Neo"
sprite->SetTransform(&matrix);
// Draw the sprite
sprite->Draw(spriteData.texture, &spriteData.rect,NULL,NULL,color);
}
//=============================================================================
// Toggle window or fullscreen mode
// Pre: All user created D3DPOOL_DEFAULT surfaces are freed.
// Post: All user surfaces are recreated.
//=============================================================================
void Graphics::changeDisplayMode(graphicsNS::DISPLAY_MODE mode)
{
try{
switch(mode)
{
case graphicsNS::FULLSCREEN:
if(fullscreen) // if already in fullscreen mode
return;
fullscreen = true; break;
case graphicsNS::WINDOW:
if(fullscreen == false) // if already in window mode
return;
fullscreen = false; break;
default: // default to toggle window/fullscreen
fullscreen = !fullscreen;
}
reset();
if(fullscreen) // fullscreen
{
SetWindowLong(hwnd, GWL_STYLE, WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP);
}
else // windowed
{
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
} catch(...)
{
// An error occured, try windowed mode
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0,0,GAME_WIDTH,GAME_HEIGHT,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
// Adjust window size so client area is GAME_WIDTH x GAME_HEIGHT
RECT clientRect;
GetClientRect(hwnd, &clientRect); // get size of client area of window
MoveWindow(hwnd,
0, // Left
0, // Top
GAME_WIDTH+(GAME_WIDTH-clientRect.right), // Right
GAME_HEIGHT+(GAME_HEIGHT-clientRect.bottom), // Bottom
TRUE); // Repaint the window
}
}
//=============================================================================
// Return the number of pixels colliding between the two sprites.
// Pre: The device supports a stencil buffer and pOcclusionQuery points to
// a valid occlusionQuery object.
// Post: Returns the number of pixels of overlap
// This function waits for the graphics card to render the last frame and return
// the collision query pixel count. To avoid slowing down your game, use a
// simple collison test first to eliminate entities that are not colliding.
// Using pixel perfect collision test with multiple entities would require
// creating multiple occlusionQuery objects and clearing the stencil buffer
// between each test.
//=============================================================================
DWORD Graphics::pixelCollision(const SpriteData &sprite1, const SpriteData &sprite2)
{
if(!stencilSupport) // if no stencil buffer support
return 0;
beginScene();
// Set up stencil buffer using current entity
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
device3d->SetRenderState(D3DRS_STENCILREF, 0x1);
device3d->SetRenderState(D3DRS_STENCILMASK, 0xffffffff);
device3d->SetRenderState(D3DRS_STENCILWRITEMASK,0xffffffff);
device3d->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE);
// Write a 1 into the stencil buffer for each non-transparent pixel in ent
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite2); // write 1s to stencil buffer
spriteEnd();
// Change stencil buffer to only allow writes where the stencil value is 1
// (where the ent sprite is colliding with the current sprite)
device3d->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
device3d->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
// Begin occlusion query to count pixels that are drawn
pOcclusionQuery->Issue(D3DISSUE_BEGIN);
spriteBegin();
// Enable stencil buffer (must be after spriteBegin)
device3d->SetRenderState(D3DRS_STENCILENABLE, true);
drawSprite(sprite1); // draw current entity
spriteEnd();
// End occlusion query
pOcclusionQuery->Issue(D3DISSUE_END);
// Wait until the GPU is finished.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfPixelsColliding,
sizeof(DWORD), D3DGETDATA_FLUSH ))
{}
// Turn off stencil
device3d->SetRenderState(D3DRS_STENCILENABLE, false);
endScene();
return numberOfPixelsColliding;
}
//=============================================================================
// Test for lost device
//=============================================================================
HRESULT Graphics::getDeviceState()
{
result = E_FAIL; // default to fail, replace on success
if (device3d == NULL)
return result;
result = device3d->TestCooperativeLevel();
return result;
}
//=============================================================================
// Release all
//=============================================================================
void Graphics::releaseAll()
{
safeRelease(pOcclusionQuery);
safeRelease(sprite);
safeRelease(device3d);
safeRelease(direct3d);
safeRelease(line);
}
//=============================================================================
// Reset the graphics device
//=============================================================================
HRESULT Graphics::reset()
{
safeRelease(line);
safeRelease(pOcclusionQuery); // release query
initD3Dpp(); // init D3D presentation parameters
sprite->OnLostDevice(); // release sprite
result = device3d->Reset(&d3dpp); // attempt to reset graphics device
// Configure for alpha blend of primitives
device3d->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device3d->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
device3d->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// recreate query
device3d->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
sprite->OnResetDevice(); // recreate sprite
createLine(&line); // recreate a DirectX line
return result;
}
view raw graphics.cpp hosted with ❤ by GitHub

'MyProject > SephyEngine' 카테고리의 다른 글

June_Engine #3_Image Class .cpp (1)  (0) 2017.04.19
June_Engine #3_Image (Class) & SpriteData (Struct)  (0) 2017.04.19
June_Engine #2_Graphics.cpp (2)  (0) 2017.04.17
June_Engine #2_Graphics.cpp (1)  (0) 2017.04.15
June_Engine #2_Graphics.h  (0) 2017.04.07