www.pudn.com > tilesrc.zip > DXEngine.cpp
#include "DXEngine.h"
// initializes a direct draw struct, basically zeros it and sets the dwSize field
#define DDRAW_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }
DXEngine::DXEngine() {
};
void DXEngine::Init(HWND WinHandle, HINSTANCE AppHandle, int nx, int ny, int nbpp, int nWindowed) {
// Allocate Variables
MainWindow = WinHandle;
Application = AppHandle;
ScreenWidth = nx;
ScreenHeight = ny;
BitDepth = nbpp;
Windowed = nWindowed;
};
DXEngine::~DXEngine() {
// Shutdown Direct3D
if (Viewport) Viewport->Release();
if (Direct3D) Direct3D->Release();
// Shutdown DirectDraw
if (Clipper) {
Clipper->Release();
};
if (WindowClipper) {
WindowClipper->Release();
};
if (BBuffer) {
BBuffer->Release();
};
if (Primary) {
Primary->Release();
};
if (DirectDraw) {
DirectDraw->Release();
};
};
void DXEngine::InitDirectX() {
// Initialise DirectDraw
InitDirectDraw();
// Initialise Direct3D
InitDirect3D();
// Hide the mouse cursor if we aren't running in a window
if (!Windowed) {
ShowCursor(FALSE);
};
// Seed the random number generator
srand(GetTickCount());
};
void DXEngine::SetClientParams(int nx, int ny) {
WindowClientX0 = nx;
WindowClientY0 = ny;
}
void DXEngine::InitDirectDraw() {
// Set some variables to NULL for the sake of safety
Clipper = NULL;
WindowClipper = NULL;
DirectDraw = NULL;
Primary = NULL;
BBuffer = NULL;
// Temporary variable used to get DirectDraw 6
LPDIRECTDRAW lpddTemp = NULL;
// Get DirectDraw 1
if (FAILED(DirectDrawCreate(NULL,&lpddTemp,NULL))) {
// Unable to init basic DirectDraw
ReportError("Unable to initialise basic DirectDraw Object");
exit(10);
};
// Get latest DirectDraw (4 as of DirectX 6)
if (FAILED(lpddTemp->QueryInterface(IID_IDirectDraw4,(LPVOID *)&DirectDraw))) {
// Unable to upgrade DirectDraw
ReportError("Unable to upgrade to recent DirectDraw");
exit(10);
};
// Set Cooperation level, based on Window/Fullscreen
if (Windowed) {
// Windowed apps like DDSCL_NORMAL
if (FAILED(DirectDraw->SetCooperativeLevel(MainWindow,DDSCL_NORMAL))) {
// Cooperation failed
ReportError("Failed to cooperate (windowed mode) with Windows");
exit(10);
};
} else {
// Fullscreen apps (16bpp) should have more
if (FAILED(DirectDraw->SetCooperativeLevel(MainWindow,DDSCL_ALLOWMODEX | DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT | DDSCL_FPUSETUP))) {
// Cooperation failed
ReportError("Failed to cooperate (fullscreen mode) with Windows");
exit(10);
};
};
// Create the desired screen mode. 16 bpp fails to 24 when necessary.
if (!Windowed) { //changing screen mode when windowed is retarted!
if (FAILED(DirectDraw->SetDisplayMode(ScreenWidth,ScreenHeight,BitDepth,0,0))) {
// Screen mode failed
ReportError("Couldn't get 16bpp, trying 24");
if (BitDepth == 16) {
BitDepth = 24;
if (FAILED(DirectDraw->SetDisplayMode(ScreenWidth,ScreenHeight,BitDepth,0,0))) {
ReportError("No 24bpp either.");
exit(10);
};
} else {
// No screen. Sorry!
ReportError("Screen initialisation failed horribly. Don't use 8 bpp.");
exit(10);
};
};
};
// Blank the Surface Description
memset(&SurfaceDescription,0,sizeof(SurfaceDescription));
SurfaceDescription.dwSize = sizeof(SurfaceDescription);
// If its windowed, we don't want a complex Primary surface - otherwise,
// we do.
if (!Windowed) {
// Fullscreen mode
SurfaceDescription.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_3DDEVICE;
// set the backbuffer count to 0 for windowed mode
// 1 for fullscreen mode, 2 for triple buffering
SurfaceDescription.dwBackBufferCount = 1;
} else {
// Windowed mode
SurfaceDescription.dwFlags = DDSD_CAPS;
SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
// set the backbuffer count to 0 for windowed mode
// 1 for fullscreen mode, 2 for triple buffering
SurfaceDescription.dwBackBufferCount = 0;
};
// Create the Primary Surface
DirectDraw->CreateSurface(&SurfaceDescription,&Primary,NULL);
// Get the pixel format (555/565)
DDPIXELFORMAT ddpf;
DDRAW_INIT_STRUCT(ddpf);
Primary->GetPixelFormat(&ddpf);
PixelFormat = ddpf.dwRGBBitCount;
// Get a backbuffer if its a full screen mode
if (!Windowed) {
SurfaceDescription.dwFlags = DDSD_CAPS;
SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_3DDEVICE;
SurfaceDescription.dwBackBufferCount = 1;
if (FAILED(Primary->GetAttachedSurface(&SurfaceDescription.ddsCaps,&BBuffer))) {
// No back buffer is possible
ReportError("Double buffering isn't possible.");
exit(10);
};
} else {
BBuffer = CreateSurface(ScreenWidth,ScreenHeight,DDSCAPS_3DDEVICE,0);
};
// Clear primary and secondary surfaces
if (Windowed) {
FillSurface(BBuffer,0,NULL);
} else {
FillSurface(BBuffer,0,NULL);
FillSurface(Primary,0,NULL);
};
// Set software clipping region
ClipMinX = 0;
ClipMaxX = ScreenWidth -1;
ClipMinY = 0;
ClipMaxY = ScreenHeight -1;
// Setup a clipper for the backbuffer/window
RECT ScreenRect = {0,0,ScreenWidth,ScreenHeight};
Clipper = AttachClipper(BBuffer,1,&ScreenRect);
if (Windowed) {
if (FAILED(DirectDraw->CreateClipper(0,&WindowClipper,NULL))) {
// Clipper creation failed
ReportError("Failed to create a clipper");
exit(10);
};
if (FAILED(WindowClipper->SetHWnd(0, MainWindow))) {
// Clipper creation failed
ReportError("Failed to attach clipper to window");
exit(10);
};
if (FAILED(Primary->SetClipper(WindowClipper))) {
// Clipper creation failed
ReportError("Failed to set the clipper on the primary buffer");
exit(10);
};
};
}
LPDIRECTDRAWCLIPPER DXEngine::AttachClipper(LPDIRECTDRAWSURFACE4 surface, int RectCount, LPRECT ClipList)
{
int Count;
LPDIRECTDRAWCLIPPER Clipper;
LPRGNDATA RegionData;
// We need a clipper object to play with
if (FAILED(DirectDraw->CreateClipper(0,&Clipper,NULL))) {
// Clipper creation failed
return NULL;
};
// Create the clip list
RegionData = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)+RectCount*sizeof(RECT));
memcpy(RegionData->Buffer, ClipList, sizeof(RECT)*RectCount);
// Setup the header fields
RegionData->rdh.dwSize = sizeof(RGNDATAHEADER);
RegionData->rdh.iType = RDH_RECTANGLES;
RegionData->rdh.nCount = RectCount;
RegionData->rdh.nRgnSize = RectCount*sizeof(RECT);
RegionData->rdh.rcBound.left = 64000;
RegionData->rdh.rcBound.top = 64000;
RegionData->rdh.rcBound.right = -64000;
RegionData->rdh.rcBound.bottom = -64000;
// Find bounds of all clipping regions
for (Count = 0; Count < RectCount; Count++) {
// test if the next rectangle unioned with the current bound is larger
if (ClipList[Count].left < RegionData->rdh.rcBound.left)
RegionData->rdh.rcBound.left = ClipList[Count].left;
if (ClipList[Count].right > RegionData->rdh.rcBound.right)
RegionData->rdh.rcBound.right = ClipList[Count].right;
if (ClipList[Count].top < RegionData->rdh.rcBound.top)
RegionData->rdh.rcBound.top = ClipList[Count].top;
if (ClipList[Count].bottom > RegionData->rdh.rcBound.bottom)
RegionData->rdh.rcBound.bottom = ClipList[Count].bottom;
};
// Set the clip list
if (FAILED(Clipper->SetClipList(RegionData,0))) {
// Clip list set failed
free(RegionData);
return(NULL);
};
// Attach the clipper to the surface
if (FAILED(surface->SetClipper(Clipper))) {
// Attachment failed
free(RegionData);
return(NULL);
};
// It worked!
free(RegionData);
return Clipper;
};
// Create an offscreen surface
LPDIRECTDRAWSURFACE4 DXEngine::CreateSurface(int width, int height, int mem_flags, USHORT colour_key_value)
{
DDSURFACEDESC2 TempDescription;
LPDIRECTDRAWSURFACE4 Surface;
// Set appropriate values to the surface description
memset(&TempDescription,0,sizeof(TempDescription));
TempDescription.dwSize = sizeof(TempDescription);
TempDescription.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
// Set size
TempDescription.dwWidth = width;
TempDescription.dwHeight = height;
// Set the type
TempDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | mem_flags;
// Create the surface
if (FAILED(DirectDraw->CreateSurface(&TempDescription,&Surface,NULL))) {
// That failed.
ReportError("Surface creation failed!");
exit(10);
return NULL;
};
// Set the colour key to whatever was passed
DDCOLORKEY ColourKey;
ColourKey.dwColorSpaceLowValue = colour_key_value;
ColourKey.dwColorSpaceHighValue = colour_key_value;
// Set the colour key for source blitting
Surface->SetColorKey(DDCKEY_SRCBLT,&ColourKey);
// Return the surface
return Surface;
}
void DXEngine::FillSurface(LPDIRECTDRAWSURFACE4 lpdds, USHORT colour, RECT * client)
{
DDBLTFX BlitFX;
// Clear the structure
DDRAW_INIT_STRUCT(BlitFX);
BlitFX.dwFillColor = colour;
lpdds->Blt(client,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&BlitFX);
}
void DXEngine::InitDirect3D() {
// Initialise Direct3D
D3DFINDDEVICESEARCH search;
D3DFINDDEVICERESULT result;
if (FAILED(DirectDraw->QueryInterface(IID_IDirect3D3, (LPVOID *)&Direct3D))) {
// Unable to find Direct3D
ReportError("Direct3D Query Failed");
};
// Search for Direct3D devices
memset(&search,0,sizeof(search));
search.dwSize = sizeof(search);
//search.dwFlags = D3DFDS_HARDWARE || D3DFDS_SOFTWARE;
search.bHardware = 1;
memset(&result,0,sizeof(result));
result.dwSize = sizeof(result);
if (FAILED(Direct3D->FindDevice(&search,&result))) {
// Unable to find a 3D device
ReportError("3D Hardware Not Found!");
exit(10);
};
// Create a device
if (FAILED(Direct3D->CreateDevice(IID_IDirect3DHALDevice,BBuffer,&D3DDevice,NULL))) {
ReportError("Failed to create a HAL device, trying MMX.");
if (FAILED(Direct3D->CreateDevice(IID_IDirect3DMMXDevice,BBuffer,&D3DDevice,NULL))) {
ReportError("Failed to create an MMX device, trying RGB");
if (FAILED(Direct3D->CreateDevice(IID_IDirect3DRGBDevice,BBuffer,&D3DDevice,NULL))) {
ReportError("Failed to create an RGB device, trying RAMP. Icky.");
if (FAILED(Direct3D->CreateDevice(IID_IDirect3DRampDevice,BBuffer,&D3DDevice,NULL))) {
ReportError("No Direct3D Devices are available");
exit(10);
};
};
};
};
// Create a viewpoint
memset(&Viewdata,0,sizeof(Viewdata));
Viewdata.dwSize = sizeof(Viewdata);
Viewdata.dwWidth = ScreenWidth;
Viewdata.dwHeight = ScreenHeight;
Viewdata.dvClipX = -1.0f;
Viewdata.dvClipWidth = 2.0f;
Viewdata.dvClipY = 1.0f;
Viewdata.dvClipHeight = 2.0f;
Viewdata.dvMinZ = -10.0f;
Viewdata.dvMaxZ = 25.0f;
// Create the viewport
if (FAILED(Direct3D->CreateViewport(&Viewport,NULL))) {
ReportError("Failed to create a viewport");
};
if (FAILED(D3DDevice->AddViewport(Viewport))) {
ReportError("Failed to add a viewport");
};
if (FAILED(Viewport->SetViewport2(&Viewdata))) {
ReportError("Failed to set Viewport data");
};
// This isn't strictly necessary - but I've found it can crash if I
// don't put this here. Odd.
D3DDevice->SetCurrentViewport(Viewport);
};
void DXEngine::ReportError(char * text) {
MessageBox(MainWindow,text,"Engine Problems",MB_OK);
}
void DXEngine::Flip()
{
// Flip pages
if (!Windowed) {
while(FAILED(Primary->Flip(NULL,DDFLIP_WAIT)));
} else {
RECT dest;
// get the window's rectangle in screen coordinates
GetWindowRect(MainWindow, &dest);
dest.left += WindowClientX0;
dest.top += WindowClientY0;
dest.right = dest.left + ScreenWidth;
dest.bottom = dest.top + ScreenHeight;
if (FAILED(Primary->Blt(&dest, BBuffer, NULL, DDBLT_WAIT,NULL))) {
// Unable to blit, wonder why?
ReportError("Blit failed");
exit(10);
};
};
}