/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2002, 2004-2007 Sven Eberhardt * Copyright (c) 2004-2010 Günther Brammer * Copyright (c) 2007 Peter Wortmann * Copyright (c) 2008 Matthes Bender * Copyright (c) 2009 Nicolas Hake * Copyright (c) 2010 Armin Burgmeier * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de * * Portions might be copyrighted by other authors who have contributed * to OpenClonk. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * See isc_license.txt for full license and disclaimer. * * "Clonk" is a registered trademark of Matthes Bender. * See clonk_trademark_license.txt for full license. */ /* Direct3D implementation of NewGfx */ #include "C4Include.h" #include #include #include #include #include #ifdef USE_DIRECTX #include #include #include #include CStdD3D::CStdD3D(bool fSoftware) { this->fSoftware = fSoftware; Default(); // global ptr pD3D = this; } CStdD3D::~CStdD3D() { Clear(); pD3D=NULL; } void CStdD3D::Default() { CStdDDraw::Default(); SceneOpen=false; lpD3D=NULL; lpDevice=NULL; pVB=pVBClr=pVBClrTex=NULL; ZeroMemory(&sfcBmpInfo, sizeof(sfcBmpInfo)); bltState[0] = bltState[1] = bltState[2] = 0; bltBaseState[0] = bltBaseState[1] = bltBaseState[2] = bltBaseState[3] = 0; drawSolidState[0] = drawSolidState[1] = 0; pSavedState = 0; for (int i=0; iIntUnlock(); // cannot do this here or we can't preserve textures across GL reinitialization as required when changing multisampling if (lpDevice) { EndScene(); DeleteDeviceObjects(); } if (lpD3D) { if (lpDevice) { lpDevice->Release(); lpDevice=NULL; } lpD3D->Release(); lpD3D=NULL; } SceneOpen=false; CStdDDraw::Clear(); } /* Direct3D initialization */ bool CStdD3D::PageFlip(RECT *pSrcRt, RECT *pDstRt, CStdWindow * pWindow) { // call from gfx thread only! if (!pApp || !pApp->AssertMainThread()) return false; // safety if (!lpDevice) return false; // end the scene and present it EndScene(); if (lpDevice->Present(pSrcRt, pDstRt, pWindow ? pWindow->hWindow : 0, NULL) == D3DERR_DEVICELOST) { if (lpDevice->TestCooperativeLevel() == D3DERR_DEVICELOST) return false; if (!RestoreDeviceObjects()) return false; lpDevice->Present(NULL, NULL, NULL, NULL); } return true; } bool CStdD3D::BeginScene() { // already open? if (SceneOpen) return true; // not active? if (!Active) if (!RestoreDeviceObjects()) return false; // do open SceneOpen=(lpDevice->BeginScene() == D3D_OK); if (!SceneOpen) return false; // clear scene //lpDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(rand()%256,rand()%256,rand()%256), 1.0f, 0L ); // set some def values // success return true; } void CStdD3D::FillBG(DWORD dwClr) { if (lpDevice) lpDevice->Clear( 0, NULL, D3DCLEAR_TARGET, dwClr, 1.0f, 0L ); } void CStdD3D::EndScene() { // end scene, if open if (!SceneOpen) return; lpDevice->EndScene(); SceneOpen=false; } bool CStdD3D::UpdateClipper() { // no render target? do nothing if (!RenderTarget || !Active) return true; // negative/zero? int iWdt=Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1; int iHgt=Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1; int iX=iClipX1; if (iX<0) { iWdt+=iX; iX=0; } int iY=iClipY1; if (iY<0) { iHgt+=iY; iY=0; } if (iWdt<=0 || iHgt<=0) { ClipAll=true; return true; } ClipAll=false; // bound clipper to surface size D3DVIEWPORT9 Clipper; Clipper.X=iX; Clipper.Y=iY; Clipper.Width=iWdt; Clipper.Height=iHgt; Clipper.MinZ=0.0f; Clipper.MaxZ=1.0f; // set it lpDevice->SetViewport(&Clipper); return true; } bool CStdD3D::PrepareMaterial(StdMeshMaterial &mat) { // TODO return false; } bool CStdD3D::PrepareRendering(SURFACE sfcToSurface) { // call from gfx thread only! if (!pApp || !pApp->AssertMainThread()) return false; // do not render to inactive fullscreen if (!Active && !Editor) return false; // device? if (!lpDevice) return false; // target? if (!sfcToSurface) return false; // target locked? if (sfcToSurface->Locked) return false; // target is already set as render target? if (sfcToSurface != RenderTarget) { // target is a render-target? if (!sfcToSurface->IsRenderTarget()) return false; IDirect3DSurface9 *pDest=sfcToSurface->GetSurface(); if (!pDest) return false; // set target EndScene(); if (lpDevice->SetRenderTarget(0, pDest) != D3D_OK) { pDest->Release(); return false; } RenderTarget=sfcToSurface; pDest->Release(); // new target might need new clipping UpdateClipper(); } // start scene, if not already done BeginScene(); // done return true; } void CStdD3D::PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact) { if (!lpDevice || !pVB) return; // globally modulated blit int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE; assert(rBltData.byNumVertices < sizeof(bltClrVertices)/sizeof(*bltClrVertices)); // write to vertex buffer for (int i = 0; i < rBltData.byNumVertices; ++i) { bltClrVertices[i].x = rBltData.vtVtx[i].ftx; bltClrVertices[i].y = rBltData.vtVtx[i].fty; if (rBltData.pTransform) rBltData.pTransform->TransformPoint(bltClrVertices[i].x, bltClrVertices[i].y); // Compensate for D3D's half-a-pixel offset // FIXME: Put this into a global transformation matrix or something bltClrVertices[i].x -= 0.5; bltClrVertices[i].y -= 0.5; bltClrVertices[i].tu = rBltData.vtVtx[i].tx; bltClrVertices[i].tv = rBltData.vtVtx[i].ty; bltClrVertices[i].color = dwModClr; } bltBaseState[iAdditive + (fMod2 ? C4GFXBLIT_MOD2 : 0)]->Apply(); lpDevice->SetFVF(D3DFVF_C4CTVERTEX); // color mod map by pixel shader? CStdD3DShader *pShader = NULL; if (fUseClrModMap && HasShaders()) { bool fColoredFoW = pClrModMap->IsColored(); pShader = pShaders[fMod2 * SHIDX_Mod2 + fColoredFoW * SHIDX_ColoredFoW]; lpDevice->SetPixelShader(pShader->GetInterface()); lpDevice->SetTexture(pShader->iInTexIndex, pTex->pTex); CSurface * pModSurface = pClrModMap->GetSurface(); if (pModSurface->ppTex) lpDevice->SetTexture(pShader->iFoWTexIndex, (*(pModSurface->ppTex))->pTex); const float mod_proj[4] = { 1.0f/(pClrModMap->GetResolutionX()* (*(pModSurface->ppTex))->iSizeX), 1.0f/(pClrModMap->GetResolutionY()* (*(pModSurface->ppTex))->iSizeY), float(pClrModMap->OffX), float(pClrModMap->OffY) }; lpDevice->SetPixelShaderConstantF(pShader->iFoWTransformIndex, mod_proj, 1); lpDevice->SetSamplerState( pShader->iFoWTexIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); } else { // set texture lpDevice->SetTexture(0, pTex->pTex); } // override linear filter mode for exact blits if (fExact) { lpDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT); lpDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); } // draw! lpDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, rBltData.byNumVertices-2, bltClrVertices, sizeof(bltClrVertices[0]) ); // done, cleanup if (pShader) { lpDevice->SetPixelShader(NULL); lpDevice->SetTexture(pShader->iInTexIndex, NULL); lpDevice->SetTexture(pShader->iFoWTexIndex, NULL); } } void CStdD3D::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, CBltTransform *pTransform) { // TODO: Implement this for (int x = 0; x < twdt; x += 10) for (int y = 0; y < thgt; y += 10) this->DrawBoxDw(0, tx+x, ty+y, tx+Min(twdt, x+10.0f), ty+Min(thgt, y+10.0f), (((x+y)/10)&1)?0xff00ff:0x00ff00); } // get bit depth from surface format unsigned int Format2BitDepth(D3DFORMAT format) { switch (format) { case D3DFMT_X1R5G5B5: case D3DFMT_R5G6B5: return 16; case D3DFMT_X8R8G8B8: case D3DFMT_A8R8G8B8: return 32; default: // unknown return 0; } } bool CStdD3D::BlitTex2Window(CTexRef *pTexRef, HDC hdcTarget, RECT &rtFrom, RECT &rtTo) { // lock if (!pTexRef->Lock()) return false; // get bits BYTE *pBits = (BYTE *) pTexRef->texLock.pBits; if (!pBits) return false; // get size int fWdt = rtFrom.right-rtFrom.left; int fHgt = rtFrom.bottom-rtFrom.top; int tWdt = rtTo.right-rtTo.left; int tHgt = rtTo.bottom-rtTo.top; // adjust bitmap info sfcBmpInfo.bmiHeader.biHeight = pTexRef->iSizeY; sfcBmpInfo.bmiHeader.biWidth = pTexRef->iSizeX; // Same size if ((fWdt==tWdt) && (fHgt==tHgt)) return SetDIBitsToDevice(hdcTarget, rtTo.left, rtTo.top, fWdt, fHgt, rtFrom.left, rtFrom.top, 0, fWdt, pBits, &sfcBmpInfo, DIB_RGB_COLORS) > 0; // Stretch SetMapMode(hdcTarget,MM_TEXT); int i = StretchDIBits(hdcTarget, rtTo.left, rtTo.top, tWdt, tHgt, rtFrom.left, rtFrom.top, fWdt, fHgt, pBits, &sfcBmpInfo, DIB_RGB_COLORS, SRCCOPY); if (i == GDI_ERROR) return false; return true; } bool CStdD3D::BlitSurface2Window(SURFACE sfcSource, int fX, int fY, int fWdt, int fHgt, HWND hWnd, int tX, int tY, int tWdt, int tHgt) { bool fOkay = false; if (!sfcSource->Lock()) return false; HDC hdcTarget=GetDC(hWnd); if (hdcTarget) { // blit textures // blit with basesfc? bool fBaseSfc=false; if (sfcSource->pMainSfc) if (sfcSource->pMainSfc->ppTex) fBaseSfc=true; // calc stretch float scaleX=(float) tWdt/fWdt; float scaleY=(float) tHgt/fHgt; // get involved texture offsets int iTexSizeX=sfcSource->iTexSize; int iTexSizeY=sfcSource->iTexSize; int iTexX=fX/iTexSizeX; int iTexY=fY/iTexSizeY; int iTexX2=Min((fX+fWdt-1)/iTexSizeX +1, sfcSource->iTexX); int iTexY2=Min((fY+fHgt-1)/iTexSizeY +1, sfcSource->iTexY); CTexRef **ppTex=sfcSource->ppTex+iTexY*sfcSource->iTexX+iTexX; // blit from all these textures CTexRef **ppTexRow, *pBaseTex=NULL; for (int iY=iTexY; iYiTexSize*iX; int iBlitY=sfcSource->iTexSize*iY; // get new texture source bounds RECT fTexBlt; fTexBlt.left = Max(fX-iBlitX, 0); fTexBlt.top = Max(fY-iBlitY, 0); fTexBlt.right = Min(fX+fWdt-iBlitX, iTexSizeX); fTexBlt.bottom= Min(fY+fHgt-iBlitY, iTexSizeY); // get new dest bounds RECT tTexBlt; tTexBlt.left = (long) ((fTexBlt.left +iBlitX-fX)*scaleX+tX); tTexBlt.top = (long) ((fTexBlt.top +iBlitY-fY)*scaleY+tY); tTexBlt.right = (long) ((fTexBlt.right +iBlitX-fX)*scaleX+tX); tTexBlt.bottom= (long) ((fTexBlt.bottom+iBlitY-fY)*scaleY+tY); // is there a base-surface to be blitted first? if (fBaseSfc) { // then get this surface as same offset as from other surface // assuming this is only valid as long as there's no texture management, // organizing partially used textures together! pBaseTex=*(sfcSource->pMainSfc->ppTex+(ppTex-sfcSource->ppTex)); BlitTex2Window(pBaseTex, hdcTarget, fTexBlt, tTexBlt); } // blit this texture BlitTex2Window(*ppTex, hdcTarget, fTexBlt, tTexBlt); // next col ++ppTex; } // next row ppTex=ppTexRow+sfcSource->iTexX; } // done, release DC ReleaseDC(hWnd,hdcTarget); } sfcSource->Unlock(); return fOkay; } bool CStdD3D::FindDisplayMode(unsigned int iXRes, unsigned int iYRes, unsigned int iColorDepth, unsigned int iMonitor) { if (iColorDepth == 32) return FindDisplayMode(iXRes, iYRes, D3DFMT_A8R8G8B8, iMonitor) || FindDisplayMode(iXRes, iYRes, D3DFMT_X8R8G8B8, iMonitor); else if (iColorDepth == 16) return FindDisplayMode(iXRes, iYRes, D3DFMT_R5G6B5, iMonitor) || FindDisplayMode(iXRes, iYRes, D3DFMT_A1R5G5B5, iMonitor); else return false; } bool CStdD3D::FindDisplayMode(unsigned int iXRes, unsigned int iYRes, D3DFORMAT format, unsigned int iMonitor) { bool fFound=false; D3DDISPLAYMODE dmode; // enumerate modes int iNumModes=lpD3D->GetAdapterModeCount(iMonitor, format); for (int i=0; iEnumAdapterModes(iMonitor, format, i, &dmode) == D3D_OK) { // size is OK? if (dmode.Width==iXRes && dmode.Height==iYRes) { // compare with found one if (fFound) // try getting a mode that is close to 85Hz, rather than taking the one with highest refresh rate // (which may set absurd modes on some devices) if (Abs(85-dmode.RefreshRate)>Abs(85-dspMode.RefreshRate)) // the previous one was better continue; // choose this one fFound=true; dspMode=dmode; } } return fFound; } bool CStdD3D::SetOutputAdapter(unsigned int iMonitor) { // get associated monitor rect HMONITOR hMon = lpD3D->GetAdapterMonitor(iMonitor); if (!hMon) return Error("GetAdapterMonitor error"); MONITORINFO nfo; ZeroMemory(&nfo, sizeof(nfo)); nfo.cbSize = sizeof(MONITORINFO); if (!GetMonitorInfo(hMon, &nfo)) return Error("GetMonitorInfo error"); return true; } bool CStdD3D::CreatePrimarySurfaces(bool Editor, unsigned int iXRes, unsigned int iYRes, int iColorDepth, unsigned int iMonitor) { DebugLog("Init DX"); DebugLog(" Create Direct3D9..."); if ((lpD3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL) return Error(" Direct3DCreate9 failure."); // set monitor info (Monitor-var and target rect) DebugLog(" SetOutput adapter..."); // FIXME: Do not do resolution changes here, SetVideoMode will take care of that if (!SetOutputAdapter(iMonitor)) return Error(" Output adapter failure."); ZeroMemory( &d3dpp, sizeof(d3dpp) ); // set needed surface type switch (iColorDepth) { case 16: dwSurfaceType=D3DFMT_A4R4G4B4; break; case 32: dwSurfaceType=D3DFMT_A8R8G8B8; break; } HRESULT hr; HWND hWindow = pApp->pWindow->hWindow; DebugLog(" Create Device..."); if (!Editor) { // fullscreen mode // pick a display mode if (0) { // "Windowed fullscreen" if (lpD3D->GetAdapterDisplayMode(iMonitor, &dspMode) != D3D_OK) if (lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dspMode) != D3D_OK) return Error("Could not get current display mode"); SetWindowPos(hWindow, HWND_TOP, 0, 0, iXRes,iYRes, SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOMOVE); d3dpp.Windowed = true; } else { // true fullscreen if (!FindDisplayMode(iXRes, iYRes, iColorDepth, iMonitor)) return Error("Display mode not supported"); } // fill present structure d3dpp.BackBufferWidth = iXRes; d3dpp.BackBufferHeight= iYRes; d3dpp.BackBufferFormat= dspMode.Format; d3dpp.BackBufferCount = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP; d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; hr = lpD3D->CreateDevice(iMonitor, fSoftware ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL, hWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &lpDevice); } else { // windowed mode // get current display mode if (lpD3D->GetAdapterDisplayMode(iMonitor, &dspMode) != D3D_OK) if (lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dspMode) != D3D_OK) return Error("Could not get current display mode"); d3dpp.Windowed = true; d3dpp.BackBufferCount = 1; d3dpp.SwapEffect = D3DSWAPEFFECT_COPY; d3dpp.BackBufferWidth = dspMode.Width; d3dpp.BackBufferHeight= dspMode.Height; d3dpp.BackBufferFormat = dspMode.Format; d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; // create primary surface hr = lpD3D->CreateDevice(iMonitor, fSoftware ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL, hWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &lpDevice); // windowed mode: Try dfault adapter as well if (hr != D3D_OK && iMonitor != D3DADAPTER_DEFAULT) hr = lpD3D->CreateDevice(D3DADAPTER_DEFAULT, fSoftware ? D3DDEVTYPE_REF : D3DDEVTYPE_HAL, hWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3dpp, &lpDevice); } switch (hr) { case D3DERR_INVALIDCALL: return Error("CreateDevice: D3DERR_INVALIDCALL"); case D3DERR_NOTAVAILABLE: return Error("CreateDevice: D3DERR_NOTAVAILABLE"); case D3DERR_OUTOFVIDEOMEMORY: return Error("CreateDevice: D3DERR_OUTOFVIDEOMEMORY"); case D3DERR_DRIVERINTERNALERROR: return Error("CreateDevice: D3DERR_DRIVERINTERNALERROR"); case D3D_OK: break; default: return Error("CreateDevice: unknown error"); } // device successfully created? if (!lpDevice) return Error("CreateDevice: unreported error"); // store color depth byByteCnt=iColorDepth/8; PrimarySrfcFormat=d3dpp.BackBufferFormat; // create bmi-header ZeroMemory(&sfcBmpInfo, sizeof(sfcBmpInfo)); sfcBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); sfcBmpInfo.bmiHeader.biWidth = iXRes; sfcBmpInfo.bmiHeader.biHeight = -int(iYRes); sfcBmpInfo.bmiHeader.biPlanes = 1; sfcBmpInfo.bmiHeader.biBitCount = Format2BitDepth(dspMode.Format); sfcBmpInfo.bmiHeader.biCompression = BI_RGB; // initialize device-dependent objects if (!InitDeviceObjects()) { // cleanup DeleteDeviceObjects(); // failure return false; } // update monitor rect by new screen size if (!SetOutputAdapter(iMonitor)) return false; // success! return true; } bool CStdD3D::OnResolutionChanged(unsigned int iXRes, unsigned int iYRes) { assert(lpDevice); // NOTE: This should be replaced by using a desktop-size video buffer and // just clipping to the current resolution to improve performance d3dpp.BackBufferWidth = iXRes; d3dpp.BackBufferHeight = iYRes; DeleteDeviceObjects(); HRESULT hr = lpDevice->Reset(&d3dpp); InitDeviceObjects(); RestoreDeviceObjects(); return hr == D3D_OK; } bool CStdD3D::SetVideoMode(unsigned int iXRes, unsigned int iYRes, unsigned int iColorDepthFIXME, unsigned int iMonitor, bool fFullScreen) { // note that this should happen in fullscreen mode only! // do not mess with real presetn parameters until resolution change worked D3DPRESENT_PARAMETERS d3dpp; // reinit window for new resolution int iColorDepth = byByteCnt*8; ZeroMemory( &d3dpp, sizeof(d3dpp) ); HRESULT hr; HWND hWindow = pApp->pWindow->hWindow; // pick a display mode if (!fFullScreen) { // "Windowed fullscreen" if (lpD3D->GetAdapterDisplayMode(iMonitor, &dspMode) != D3D_OK) if (lpD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dspMode) != D3D_OK) return Error("Could not get current display mode"); SetWindowPos(hWindow, HWND_TOP, 0, 0, iXRes, iYRes, SWP_NOOWNERZORDER | SWP_SHOWWINDOW | SWP_NOMOVE); d3dpp.Windowed = true; } else { // true fullscreen if (!FindDisplayMode(iXRes, iYRes, iColorDepth, iMonitor)) return Error("Display mode not supported"); } // fill present structure d3dpp.BackBufferWidth = iXRes; d3dpp.BackBufferHeight= iYRes; d3dpp.BackBufferFormat= dspMode.Format; d3dpp.BackBufferCount = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP; d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; // reset device to these parameters DeleteDeviceObjects(); hr = lpDevice->Reset(&d3dpp); if (hr != D3D_OK) { // reset failed if (InitDeviceObjects()) RestoreDeviceObjects(); // try restore at old resolution switch (hr) { case D3DERR_INVALIDCALL: return Error(" CreateDevice: D3DERR_INVALIDCALL"); case D3DERR_NOTAVAILABLE: return Error(" CreateDevice: D3DERR_NOTAVAILABLE"); case D3DERR_OUTOFVIDEOMEMORY: return Error(" CreateDevice: D3DERR_OUTOFVIDEOMEMORY"); case D3DERR_DRIVERINTERNALERROR: return Error(" CreateDevice: D3DERR_DRIVERINTERNALERROR"); default: return Error(" CreateDevice: unknown error"); } } // device successfully reset! Reflect changes this->d3dpp = d3dpp; // store color depth byByteCnt=iColorDepth/8; PrimarySrfcFormat=d3dpp.BackBufferFormat; // update bmi-header sfcBmpInfo.bmiHeader.biWidth = iXRes; sfcBmpInfo.bmiHeader.biHeight = -int(iYRes); sfcBmpInfo.bmiHeader.biBitCount = Format2BitDepth(dspMode.Format); // reinit device-dependant objects InitDeviceObjects(); RestoreDeviceObjects(); // update monitor rect by new screen size if (!SetOutputAdapter(iMonitor)) return false; // success! return true; } void CStdD3D::PerformPix(SURFACE sfcDest, float tx, float ty, DWORD dwClr) { // is render target? if (!sfcDest->IsRenderTarget()) // on non-rendertargets, just set using locks { sfcDest->SetPixDw((int)tx, (int)ty, dwClr); return; } else if (sfcDest->IsLocked()) { DrawPixPrimary(sfcDest, int(tx), int(ty), dwClr); return; } // on a render target, blit as a box (ugh!) // set vertex buffer data // vertex order: // 0=upper left dwClr1 // 1=lower left dwClr3 // 2=lower right dwClr4 // 3=upper right dwClr2 float vtx[8]; vtx[0] = tx ; vtx[1] = ty; vtx[2] = tx ; vtx[3] = ty+1.0f; vtx[4] = tx+1.0f; vtx[5] = ty+1.0f; vtx[6] = tx+1.0f; vtx[7] = ty; DrawQuadDw(sfcDest, vtx, dwClr, dwClr, dwClr, dwClr); } void CStdD3D::DrawPixPrimary(SURFACE sfcDest, int iX, int iY, DWORD dwClr) { // Must be render target and locked if (!sfcDest->IsRenderTarget() || !sfcDest->IsLocked()) return; // set according to pixel format BYTE *pBits = sfcDest->PrimarySurfaceLockBits; int iPitch = sfcDest->PrimarySurfaceLockPitch; DWORD *pPix32; WORD *pPix16; switch (sfcDest->dwClrFormat) { case D3DFMT_X1R5G5B5: // 16 bit 5-5-5 pPix16=(WORD *) (pBits+iY*iPitch+iX*2); *pPix16=WORD((dwClr & 0x000000f8) >> 3) | WORD((dwClr & 0x0000f800) >> 6) | WORD((dwClr & 0x00f80000) >> 9); break; case D3DFMT_R5G6B5: // 16 bit 5-6-5 pPix16=(WORD *) (pBits+iY*iPitch+iX*2); *pPix16=WORD((dwClr & 0x000000f8) >> 3) | WORD((dwClr & 0x0000fc00) >> 5) | WORD((dwClr & 0x00f80000) >> 8); break; case D3DFMT_X8R8G8B8: // 32 bit pPix32=(DWORD *) (pBits+iY*iPitch+iX*4); BltAlpha(*pPix32, dwClr); break; default: assert(false); break; // should not happen } } void CStdD3D::DrawQuadDw(SURFACE sfcTarget, float *ipVtx, DWORD dwClr1, DWORD dwClr2, DWORD dwClr3, DWORD dwClr4) { // prepare rendering to target if (!PrepareRendering(sfcTarget)) return; // set blitting state int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE; drawSolidState[iAdditive]->Apply(); lpDevice->SetRenderState( D3DRS_DESTBLEND, iAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA ); lpDevice->SetStreamSource(0, pVBClr, 0, sizeof(C4CLRVERTEX)); lpDevice->SetFVF(D3DFVF_C4CLRVERTEX); // 2do: manual clipping? // set vertex buffer data float fX1 = (float) ipVtx[0] - 0.5f; float fY1 = (float) ipVtx[1] - 0.5f; float fX2 = (float) ipVtx[2] - 0.5f; float fY2 = (float) ipVtx[3] - 0.5f; float fX3 = (float) ipVtx[4] - 0.5f; float fY3 = (float) ipVtx[5] - 0.5f; float fX4 = (float) ipVtx[6] - 0.5f; float fY4 = (float) ipVtx[7] - 0.5f; // apply color modulation if (BlitModulated) { ModulateClr(dwClr1, BlitModulateClr); ModulateClr(dwClr2, BlitModulateClr); ModulateClr(dwClr3, BlitModulateClr); ModulateClr(dwClr4, BlitModulateClr); } // set vertices clrVertices[0].x = fX1; clrVertices[0].y = fY1; clrVertices[0].color=dwClr1; clrVertices[1].x = fX2; clrVertices[1].y = fY2; clrVertices[1].color=dwClr2; clrVertices[2].x = fX3; clrVertices[2].y = fY3; clrVertices[2].color=dwClr3; clrVertices[3].x = fX3; clrVertices[3].y = fY3; clrVertices[3].color=dwClr3; clrVertices[4].x = fX4; clrVertices[4].y = fY4; clrVertices[4].color=dwClr4; clrVertices[5].x = fX1; clrVertices[5].y = fY1; clrVertices[5].color=dwClr1; if (fUseClrModMap) for (int i = 0; i < 6; ++i) ModulateClr(clrVertices[i].color, pClrModMap->GetModAt((int)clrVertices[i].x, (int)clrVertices[i].y)); // copy into vertex buffer VOID* pVertices; if ( pVBClr->Lock(0, sizeof(clrVertices), &pVertices, 0) != D3D_OK ) return; memcpy(pVertices, clrVertices, sizeof(clrVertices)); pVBClr->Unlock(); // draw lpDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 ); } void CStdD3D::PerformLine(SURFACE sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr) { // FIXME: zoom width //dwClr |= 0xf0000000; // render target? if (sfcTarget->IsRenderTarget()) if (!sfcTarget->IsLocked()) { // draw as primitive if (!PrepareRendering(sfcTarget)) return; // set blitting state - use src alpha channel als opacity because some systems cannot antialias otherwise int iAdditive = dwBlitMode & C4GFXBLIT_ADDITIVE; drawSolidState[iAdditive]->Apply(); lpDevice->SetRenderState( D3DRS_DESTBLEND, iAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA ); lpDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); lpDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, true ); lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true ); lpDevice->SetStreamSource(0, pVBClr, 0, sizeof(C4CLRVERTEX)); lpDevice->SetFVF(D3DFVF_C4CLRVERTEX); // set vertex buffer data float fX1 = (float) x1;// - 0.5f; float fY1 = (float) y1;// - 0.5f; float fX2 = (float) x2;// + 0.5f; float fY2 = (float) y2;// + 0.5f; clrVertices[0].x = fX1; clrVertices[0].y = fY1; clrVertices[0].color=dwClr; clrVertices[1].x = fX2; clrVertices[1].y = fY2; clrVertices[1].color=dwClr; clrVertices[2].x = fX2; clrVertices[2].y = fY2; clrVertices[2].color=dwClr; clrVertices[3].x = fX1; clrVertices[3].y = fY1; clrVertices[3].color=dwClr; if (fUseClrModMap) for (int i = 0; i < 4; ++i) ModulateClr(clrVertices[i].color, pClrModMap->GetModAt((int)clrVertices[i].x, (int)clrVertices[i].y)); // copy into vertex buffer VOID* pVertices; if ( pVBClr->Lock(0, sizeof(clrVertices[0])*4, &pVertices, 0) != D3D_OK ) return; memcpy(pVertices, clrVertices, sizeof(clrVertices[0])*4); pVBClr->Unlock(); // draw - actually two lines, to ensure that the last pixels are drawn, too... lpDevice->DrawPrimitive(D3DPT_LINELIST, 0, 2 ); } else { if (fUseClrModMap) ModulateClr(dwClr, pClrModMap->GetModAt((int)((x1+x2)/2), (int)((y1+y2)/2))); if (Abs(x1 - x2) > Abs(y1 - y2)) { // flip line direction if (x2 < x1) { float tmp = x2; x2 = x1; x1 = tmp; tmp = y2; y2 = y1; y1 = tmp; } // round coordinates int32_t iX1 = BoundBy(int32_t(x1+.5f),0,sfcTarget->Wdt-1), iX2 = BoundBy(int32_t(x2+.5f),0,sfcTarget->Wdt-1), iY1 = BoundBy(int32_t(y1+.5f),0,sfcTarget->Hgt-1), iY2 = BoundBy(int32_t(y2+.5f),0,sfcTarget->Hgt-1), iDX = iX2 - iX1; // single pixel case? if (!iDX) { DrawPixPrimary(sfcTarget, iX1, iY1, dwClr); return; } // calculate gradient uint32_t g = ((uint32_t(Abs(iY2 - iY1)) << 16) / uint32_t(iX2 - iX1)) << 16; // alpha divisor uint32_t alpha = dwClr >> 24; if (alpha == 0) return; // invisible line uint32_t div = (uint32_t(1 << 24) / alpha) << 8; DWORD dwClrBase = dwClr & 0x00FFFFFF; // current position uint32_t sp = 0; if (y2 > y1) for (int32_t iX = 0, iY = 0; ; iX++) { // draw pixels DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24), dwClr2 = dwClrBase + ((255 - sp / div) << 24); DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY+1, dwClr2); if (iX * 2 >= iDX) break; DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY-1, dwClr2); if (iX * 2 + 1 >= iDX) break; // next pixel sp += g; if (sp < g) iY++; } else for (int32_t iX = 0, iY = 0; ; iX++) { // draw pixels DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24), dwClr2 = dwClrBase + ((255 - sp / div) << 24); DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY-1, dwClr2); if (iX * 2 >= iDX) break; DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY+1, dwClr2); if (iX * 2 + 1 >= iDX) break; // next pixel sp += g; if (sp < g) iY--; } } else { // flip line direction if (y2 < y1) { float tmp = y2; y2 = y1; y1 = tmp; tmp = x2; x2 = x1; x1 = tmp; } // calculate gradient uint32_t g = uint32_t( Abs(x2 - x1) / (y2 - y1) * 4294967296.0f ); // round coordinates int32_t iX1 = BoundBy(int32_t(x1+.5f),0,sfcTarget->Wdt-1), iX2 = BoundBy(int32_t(x2+.5f),0,sfcTarget->Wdt-1), iY1 = BoundBy(int32_t(y1+.5f),0,sfcTarget->Hgt-1), iY2 = BoundBy(int32_t(y2+.5f),0,sfcTarget->Hgt-1), iDY = iY2 - iY1; // single pixel case? if (!iDY) { DrawPixPrimary(sfcTarget, iX1, iY1, dwClr); return; } // alpha divisor uint32_t alpha = dwClr >> 24; if (alpha == 0) return; // invisible line uint32_t div = (uint32_t(1 << 24) / alpha) << 8; DWORD dwClrBase = dwClr & 0x00FFFFFF; // current position uint32_t sp = 0; if (x2 > x1) for (int32_t iY = 0, iX = 0; ; iY++) { // draw pixels DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24), dwClr2 = dwClrBase + ((255 - sp / div) << 24); DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX1+iX+1, iY1+iY, dwClr2); if (iY * 2 >= iDY) break; DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX2-iX-1, iY2-iY, dwClr2); if (iY * 2 + 1 >= iDY) break; // next pixel sp += g; if (sp < g) iX++; } else for (int32_t iY = 0, iX = 0; ; iY++) { // draw pixels DWORD dwClr1 = dwClrBase + ((alpha + sp / div) << 24), dwClr2 = dwClrBase + ((255 - sp / div) << 24); DrawPixPrimary(sfcTarget, iX1+iX, iY1+iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX1+iX-1, iY1+iY, dwClr2); if (iY * 2 >= iDY) break; DrawPixPrimary(sfcTarget, iX2-iX, iY2-iY, dwClr1); if (sp) DrawPixPrimary(sfcTarget, iX2-iX+1, iY2-iY, dwClr2); if (iY * 2 + 1 >= iDY) break; // next pixel sp += g; if (sp < g) iX--; } } } else /* if(!sfcTarget->IsRenderTarget()) */ { if (!LockSurfaceGlobal(sfcTarget)) return; ForLine((int)x1,(int)y1,(int)x2,(int)y2,&DLineSPixDw,dwClr); UnLockSurfaceGlobal(sfcTarget); } } bool CStdD3D::InitDeviceObjects() { D3DCAPS9 d3dCaps; pD3D->lpDevice->GetDeviceCaps(&d3dCaps); MaxTexSize = d3dCaps.MaxTextureWidth; bool fSuccess=true; // Create shaders for (int i=0; iInit(lpDevice, !!(i&SHIDX_Mod2), !!(i&SHIDX_ColoredFoW))) { fOK = false; break; } } if (!fOK) for (int i=0; iTestCooperativeLevel() == D3DERR_DEVICENOTRESET) lpDevice->Reset(&d3dpp); // couldn't reset? if (lpDevice->TestCooperativeLevel() != D3D_OK) return false; bool fSuccess=true; // restore primary/back /* IDirect3DSurface9 *pPrimarySfc; if (lpDevice->GetRenderTarget(0, &pPrimarySfc) != D3D_OK) { Error("Could not get primary surface"); fSuccess=false; } RenderTarget=lpPrimary; lpPrimary->AttachSfc(pPrimarySfc,0,0); lpPrimary->dwClrFormat=PrimarySrfcFormat; lpPrimary->byBytesPP=Format2BitDepth(PrimarySrfcFormat)/8;*/ // create vertex buffer if ( lpDevice->CreateVertexBuffer(sizeof(bltVertices), 0, D3DFVF_C4VERTEX, D3DPOOL_DEFAULT, &pVB, NULL) != D3D_OK ) return false; // fill initial data for vertex buffer int i; for (i=0; i<6; ++i) { bltVertices[i].z=0.9f; bltVertices[i].rhw=1.0f; } // create solid color vertex buffer if ( lpDevice->CreateVertexBuffer(sizeof(clrVertices), 0, D3DFVF_C4CLRVERTEX, D3DPOOL_DEFAULT, &pVBClr, NULL) != D3D_OK ) return false; // fill initial data for vertex buffer for (i=0; i<6; ++i) { clrVertices[i].z=0.9f; clrVertices[i].rhw=1.0f; } // create color-texblit vertices if ( lpDevice->CreateVertexBuffer(sizeof(bltClrVertices), 0, D3DFVF_C4CTVERTEX, D3DPOOL_DEFAULT, &pVBClrTex, NULL) != D3D_OK ) return false; // fill initial data for vertex buffer for (i=0; i<6; ++i) { bltClrVertices[i].z=0.9f; bltClrVertices[i].rhw=1.0f; } // create state blocks CreateStateBlock(&bltState[0], false, false, false, false, false); CreateStateBlock(&bltState[1], true, false, false, false, false); CreateStateBlock(&bltState[2], true, false, false, true, false); CreateStateBlock(&drawSolidState[0], false, false, false, false, false); CreateStateBlock(&drawSolidState[1], false, false, false, true, false); CreateStateBlock(&bltBaseState[0], true, false, true, false, false); CreateStateBlock(&bltBaseState[1], true, false, true, true, false); CreateStateBlock(&bltBaseState[2], true, false, true, false, true); CreateStateBlock(&bltBaseState[3], true, false, true, true, true); // activate if successful Active=fSuccess; // restore gamma if active if (Active) EnableGamma(); // reset blit states dwBlitMode = 0; // done return Active; } template static inline void SafeRelease(IF *&pInterface) { if (pInterface) pInterface->Release(); pInterface = 0; } bool CStdD3D::InvalidateDeviceObjects() { bool fSuccess=true; // clear gamma DisableGamma(); // deactivate Active=false; // release state blocks SafeRelease(bltState[0]); SafeRelease(bltState[1]); SafeRelease(bltState[2]); SafeRelease(drawSolidState[0]); SafeRelease(bltBaseState[0]); SafeRelease(bltBaseState[1]); SafeRelease(drawSolidState[1]); SafeRelease(bltBaseState[2]); SafeRelease(bltBaseState[3]); SafeRelease(pSavedState); // release vertex buffer SafeRelease(pVBClr); SafeRelease(pVB); SafeRelease(pVBClrTex); return fSuccess; } bool CStdD3D::DeleteDeviceObjects() { InvalidateDeviceObjects(); bool fSuccess=true; NoPrimaryClipper(); // del main surfaces RenderTarget=NULL; for (int i=0; iBeginStateBlock(); // set states lpDevice->SetRenderState( D3DRS_LIGHTING, false ); lpDevice->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, false ); // no antialiasing lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, true ); lpDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); lpDevice->SetRenderState( D3DRS_DESTBLEND, fAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA ); /* // This isn't necessary, as a>=0 is equal to no alpha testing anyway lpDevice->SetRenderState( D3DRS_ALPHATESTENABLE, true ); lpDevice->SetRenderState( D3DRS_ALPHAREF, 0x00 ); lpDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); */ lpDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); lpDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); lpDevice->SetRenderState( D3DRS_ZENABLE, false ); lpDevice->SetRenderState( D3DRS_STENCILENABLE, false ); lpDevice->SetRenderState( D3DRS_CLIPPING, true ); lpDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, false ); lpDevice->SetRenderState( D3DRS_CLIPPLANEENABLE, false ); lpDevice->SetRenderState( D3DRS_VERTEXBLEND, false ); lpDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, false ); lpDevice->SetRenderState( D3DRS_FOGENABLE, false ); if (!fBaseTex) { lpDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); lpDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, fSolid ? D3DTA_DIFFUSE : D3DTA_TEXTURE ); lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, fSolid ? D3DTA_DIFFUSE : D3DTA_TEXTURE ); } else { lpDevice->SetTextureStageState( 0, D3DTSS_COLOROP, fMod2 ? D3DTOP_ADDSIGNED2X : D3DTOP_MODULATE ); lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); lpDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); lpDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); lpDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); } lpDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE ); lpDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); lpDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); lpDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); lpDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); lpDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 ); lpDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE ); lpDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU , D3DTADDRESS_CLAMP ); lpDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV , D3DTADDRESS_CLAMP ); // capture return lpDevice->EndStateBlock(pBlock) == D3D_OK; } void CStdD3D::SetTexture() { } void CStdD3D::ResetTexture() { if (Active) lpDevice->SetTexture(0, NULL); } bool CStdD3D::ApplyGammaRamp(D3DGAMMARAMP &ramp, bool fForce) { if (!lpDevice || (!Active && !fForce)) return false; lpDevice->SetGammaRamp(0, D3DSGR_CALIBRATE, &ramp); return true; } bool CStdD3D::SaveDefaultGammaRamp(CStdWindow * pWindow) { if (!lpDevice) return false; lpDevice->GetGammaRamp(0, &DefRamp.ramp); return true; } void CStdD3D::TaskOut() { InvalidateDeviceObjects(); } void CStdD3D::TaskIn() { RestoreDeviceObjects(); } CStdD3D *pD3D=NULL; #endif // USE_DIRECTX