#include "global.h" /* ----------------------------------------------------------------------------- Class: RageDisplay Desc: See header. Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved. Chris Danford Glenn Maynard ----------------------------------------------------------------------------- */ #include "RageDisplay.h" #include "D3D8.h" #include "RageUtil.h" #include "RageLog.h" #include "RageTimer.h" #include "RageException.h" #include "RageTexture.h" #include "RageTextureManager.h" #include "RageMath.h" #include "RageTypes.h" #include "GameConstantsAndTypes.h" #include "StepMania.h" #include "RageUtil.h" #include "D3dx8math.h" #include "SDL_video.h" // for SDL_Surface #include "SDL_utils.h" #include "arch/arch.h" #pragma comment(lib, "D3d8.lib") #pragma comment(lib, "D3dx8.lib") #include RageDisplay* DISPLAY = NULL; // // Globals // LPDIRECT3D8 g_pd3d = NULL; LPDIRECT3DDEVICE8 g_pd3dDevice = NULL; D3DCAPS8 g_DeviceCaps; D3DDISPLAYMODE g_DesktopMode; D3DPRESENT_PARAMETERS g_d3dpp; int g_ModelMatrixCnt=0; bool g_Windowed; int g_CurrentHeight, g_CurrentWidth, g_CurrentBPP; /* Direct3D doesn't associate a palette with textures. * Instead, we load a palette into a slot. We need to keep track * of which texture's palette is stored in what slot. */ map g_TexResourceToPaletteIndex; int GetUnusedPaletteIndex() { for( int i=0; i<256; i++ ) { bool bFreeToUse = true; for( map::const_iterator iter=g_TexResourceToPaletteIndex.begin(); iter!=g_TexResourceToPaletteIndex.end(); iter++ ) { if( iter->second == i ) { bFreeToUse = false; break; } } if( bFreeToUse ) return i; } ASSERT( 0 ); // couldn't find a free palette index. Resource leak? return 0; } #define D3DFVF_RAGEVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1) // D3D FVF flags which describe our vertex structure const PixelFormatDesc PIXEL_FORMAT_DESC[NUM_PIX_FORMATS] = { { /* A8B8G8R8 */ 32, { 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 } }, { /* A4R4G4B4 */ 16, { 0x0F00, 0x00F0, 0x000F, 0xF000 }, }, { /* A1B5G5R5 */ 16, { 0x7C00, 0x03E0, 0x001F, 0x8000 }, }, { /* X1R5G5R5 */ 16, { 0x7C00, 0x03E0, 0x001F, 0x0000 }, }, { /* B8G8R8 */ 24, { 0xFF0000, 0x00FF00, 0x0000FF, 0x000000 } }, { /* Paletted */ 8, { 0,0,0,0 } /* N/A */ } }; RageDisplay::RageDisplay( bool windowed, int width, int height, int bpp, int rate, bool vsync ) { LOG->Trace( "RageDisplay::RageDisplay()" ); if(!SDL_WasInit(SDL_INIT_VIDEO)) SDL_InitSubSystem(SDL_INIT_VIDEO); /* By default, ignore all SDL events. We'll enable them as we need them. * We must not enable any events we don't actually want, since we won't * query for them and they'll fill up the event queue. * * This needs to be done after we initialize video, since it's really part * of the SDL video system--it'll be reinitialized on us if we do this first. */ SDL_EventState(0xFF /*SDL_ALLEVENTS*/, SDL_IGNORE); SDL_EventState(SDL_VIDEORESIZE, SDL_ENABLE); g_Windowed = false; g_pd3d = Direct3DCreate8( D3D_SDK_VERSION ); if( FAILED( g_pd3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &g_DeviceCaps) ) ) throw RageException( "There was an error while initializing your video card.\n\n" "Your system is reporting that Direct3D8 hardware acceleration\n" "is not available. In most cases, you can download an updated\n" "driver from your card's manufacturer." ); D3DADAPTER_IDENTIFIER8 identifier; g_pd3d->GetAdapterIdentifier( D3DADAPTER_DEFAULT, 0, &identifier ); LOG->Trace( "Driver: %s\n" "Description: %s\n" "Max texture size: %d\n", identifier.Driver, identifier.Description, g_DeviceCaps.MaxTextureWidth ); LOG->Trace( "This display adaptor supports the following modes:" ); D3DDISPLAYMODE mode; for( UINT u=0; uGetAdapterModeCount(D3DADAPTER_DEFAULT); u++ ) if( SUCCEEDED( g_pd3d->EnumAdapterModes( D3DADAPTER_DEFAULT, u, &mode ) ) ) LOG->Trace( " %ux%u %uHz, format %d", mode.Width, mode.Height, mode.RefreshRate, mode.Format ); // Save the original desktop format. g_pd3d->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &g_DesktopMode ); SetVideoMode( windowed, width, height, bpp, rate, vsync ); } void RageDisplay::Update(float fDeltaTime) { SDL_Event event; while(SDL_GetEvent(event, SDL_VIDEORESIZEMASK)) { switch(event.type) { case SDL_VIDEORESIZE: g_CurrentWidth = event.resize.w; g_CurrentHeight = event.resize.h; /* Let DISPLAY know that our resolution has changed. */ ResolutionChanged(); break; } } } bool RageDisplay::IsSoftwareRenderer() { return false; } RageDisplay::~RageDisplay() { SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_EventState(SDL_VIDEORESIZE, SDL_IGNORE); g_pd3dDevice->Release(); g_pd3d->Release(); } D3DFORMAT FindBackBufferType(bool bWindowed, int iBPP) { HRESULT hr; // If windowed, then bpp is ignored. Use whatever works. vector vBackBufferFormats; // throw all possibilities in here /* When windowed, add all formats; otherwise add only formats that match dwBPP. */ if( iBPP == 16 || bWindowed ) { vBackBufferFormats.push_back( D3DFMT_R5G6B5 ); vBackBufferFormats.push_back( D3DFMT_X1R5G5B5 ); vBackBufferFormats.push_back( D3DFMT_A1R5G5B5 ); } if( iBPP == 32 || bWindowed ) { #ifndef _XBOX vBackBufferFormats.push_back( D3DFMT_R8G8B8 ); #endif vBackBufferFormats.push_back( D3DFMT_X8R8G8B8 ); vBackBufferFormats.push_back( D3DFMT_A8R8G8B8 ); } if( !bWindowed && iBPP != 16 && iBPP != 32 ) throw RageException( ssprintf("Invalid BPP '%u' specified", iBPP) ); // Test each back buffer format until we find something that works. for( unsigned i=0; i < vBackBufferFormats.size(); i++ ) { D3DFORMAT fmtBackBuffer = vBackBufferFormats[i]; D3DFORMAT fmtDisplay; if( bWindowed ) fmtDisplay = g_DesktopMode.Format; else // Fullscreen fmtDisplay = vBackBufferFormats[i]; LOG->Trace( "Testing format: display %d, back buffer %d, windowed %d...", fmtDisplay, fmtBackBuffer, bWindowed ); hr = g_pd3d->CheckDeviceType( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, fmtDisplay, fmtBackBuffer, bWindowed ); if( FAILED(hr) ) continue; // skip // done searching LOG->Trace( "This will work." ); return fmtBackBuffer; } RageException::Throw( "Couldn't find an appropriate back buffer format." ); } #ifndef _XBOX HWND GetHwnd() { SDL_SysWMinfo info; SDL_VERSION(&info.version); if( SDL_GetWMInfo(&info) < 0 ) RageException::Throw( "SDL_GetWMInfo failed" ); return info.window; } #endif /* Set the video mode. */ bool RageDisplay::SetVideoMode( bool windowed, int width, int height, int bpp, int rate, bool vsync ) { int flags = SDL_RESIZABLE; if( !windowed ) flags |= SDL_FULLSCREEN; g_Windowed = windowed; SDL_ShowCursor( g_Windowed ); SDL_Surface *screen = SDL_SetVideoMode(width, height, bpp, flags); if(!screen) RageException::Throw("SDL_SetVideoMode failed: %s", SDL_GetError()); g_CurrentWidth = screen->w; g_CurrentHeight = screen->h; g_CurrentBPP = bpp; ZeroMemory( &g_d3dpp, sizeof(g_d3dpp) ); g_d3dpp.BackBufferWidth = width; g_d3dpp.BackBufferHeight = height; g_d3dpp.BackBufferFormat = FindBackBufferType( windowed, bpp ); g_d3dpp.BackBufferCount = 1; g_d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; #ifndef _XBOX g_d3dpp.hDeviceWindow = GetHwnd(); #endif g_d3dpp.Windowed = windowed; g_d3dpp.EnableAutoDepthStencil = TRUE; g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16; g_d3dpp.Flags = 0; g_d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT; /* Windowed must always use D3DPRESENT_INTERVAL_DEFAULT. */ g_d3dpp.FullScreen_PresentationInterval = (windowed || vsync) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; if( g_pd3dDevice == NULL ) // device is not yet created. We need to create it { g_pd3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, #ifndef _XBOX GetHwnd(), D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, #else NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, #endif &g_d3dpp, &g_pd3dDevice ); ASSERT( g_pd3dDevice ); } else { g_pd3dDevice->Reset( &g_d3dpp ); } ResolutionChanged(); this->SetDefaultRenderStates(); g_pd3dDevice->BeginScene(); return false; // we can always reuse the D3D device } void RageDisplay::ResolutionChanged() { // no need to clear because D3D uses an overlay // SetViewport(0,0); // // /* Clear any junk that's in the framebuffer. */ // Clear(); // Flip(); } void RageDisplay::SetViewport(int shift_left, int shift_down) { /* left and down are on a 0..SCREEN_WIDTH, 0..SCREEN_HEIGHT scale. * Scale them to the actual viewport range. */ shift_left = int( shift_left * float(g_CurrentWidth) / SCREEN_WIDTH ); shift_down = int( shift_down * float(g_CurrentHeight) / SCREEN_HEIGHT ); D3DVIEWPORT8 viewData = { shift_left, -shift_down, g_CurrentWidth, g_CurrentHeight, 0.f, 1.f }; g_pd3dDevice->SetViewport( &viewData ); } int RageDisplay::GetMaxTextureSize() const { return g_DeviceCaps.MaxTextureWidth; } void RageDisplay::Clear() { g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0x00000000 ); } void RageDisplay::Flip() { g_pd3dDevice->EndScene(); g_pd3dDevice->Present( 0, 0, 0, 0 ); g_pd3dDevice->BeginScene(); ProcessStatsOnFlip(); } D3DFORMAT D3DFORMATS[NUM_PIX_FORMATS] = { D3DFMT_A8R8G8B8, D3DFMT_A4R4G4B4, D3DFMT_A1R5G5B5, D3DFMT_X1R5G5B5, #ifndef _XBOX D3DFMT_R8G8B8, #else D3DFMT_A8R8G8B8, #endif D3DFMT_P8 }; bool RageDisplay::SupportsTextureFormat( PixelFormat pixfmt ) { D3DFORMAT d3dfmt = D3DFORMATS[pixfmt]; HRESULT hr = g_pd3d->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_d3dpp.BackBufferFormat, 0, D3DRTYPE_TEXTURE, d3dfmt); return SUCCEEDED( hr ); } void RageDisplay::SaveScreenshot( CString sPath ) { #ifndef _XBOX IDirect3DSurface8* pSurface; g_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pSurface ); D3DXSaveSurfaceToFile( sPath, D3DXIFF_BMP, pSurface, 0, NULL ); pSurface->Release(); #endif } bool RageDisplay::IsWindowed() const { return g_Windowed; } int RageDisplay::GetWidth() const { return g_CurrentWidth; } int RageDisplay::GetHeight() const { return g_CurrentHeight; } int RageDisplay::GetBPP() const { return g_CurrentBPP; } #define SEND_CURRENT_MATRICES \ g_pd3dDevice->SetTransform( D3DTS_PROJECTION, (D3DMATRIX*)GetProjection() ); \ RageMatrix m; \ /* Convert to OpenGL-style "pixel-centered" coords */ \ RageMatrixTranslation( &m, -0.5f, -0.5f, 0 ); \ RageMatrixMultiply( &m, &m, GetModelViewTop() ); \ g_pd3dDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)&m ); void RageDisplay::DrawQuads( const RageVertex v[], int iNumVerts ) { ASSERT( (iNumVerts%4) == 0 ); if(iNumVerts == 0) return; // there isn't a quad primitive in D3D, so we have to fake it with indexed triangles int iNumQuads = iNumVerts/4; int iNumTriangles = iNumQuads*2; int iNumIndices = iNumTriangles*3; // make a temporary index buffer static vector vIndices; unsigned uOldSize = vIndices.size(); unsigned uNewSize = max(uOldSize,(unsigned)iNumIndices); vIndices.resize( uNewSize ); for( Uint16 i=(Uint16)uOldSize/6; i<(Uint16)iNumQuads; i++ ) { vIndices[i*6+0] = i*4+0; vIndices[i*6+1] = i*4+1; vIndices[i*6+2] = i*4+2; vIndices[i*6+3] = i*4+2; vIndices[i*6+4] = i*4+3; vIndices[i*6+5] = i*4+0; } g_pd3dDevice->SetVertexShader( D3DFVF_RAGEVERTEX ); SEND_CURRENT_MATRICES; g_pd3dDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, // PrimitiveType 0, // MinIndex iNumVerts, // NumVertices iNumTriangles, // PrimitiveCount, &vIndices[0], // pIndexData, D3DFMT_INDEX16, // IndexDataFormat, v, // pVertexStreamZeroData, sizeof(RageVertex) // VertexStreamZeroStride ); StatsAddVerts( iNumVerts ); } void RageDisplay::DrawFan( const RageVertex v[], int iNumVerts ) { ASSERT( iNumVerts >= 3 ); g_pd3dDevice->SetVertexShader( D3DFVF_RAGEVERTEX ); SEND_CURRENT_MATRICES; g_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, // PrimitiveType iNumVerts-2, // PrimitiveCount, v, // pVertexStreamZeroData, sizeof(RageVertex) ); StatsAddVerts( iNumVerts ); } void RageDisplay::DrawStrip( const RageVertex v[], int iNumVerts ) { ASSERT( iNumVerts >= 3 ); g_pd3dDevice->SetVertexShader( D3DFVF_RAGEVERTEX ); SEND_CURRENT_MATRICES; g_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, // PrimitiveType iNumVerts-2, // PrimitiveCount, v, // pVertexStreamZeroData, sizeof(RageVertex) ); StatsAddVerts( iNumVerts ); } void RageDisplay::DrawTriangles( const RageVertex v[], int iNumVerts ) { if( iNumVerts == 0 ) return; ASSERT( iNumVerts >= 3 ); g_pd3dDevice->SetVertexShader( D3DFVF_RAGEVERTEX ); SEND_CURRENT_MATRICES; g_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, // PrimitiveType iNumVerts/3, // PrimitiveCount, v, // pVertexStreamZeroData, sizeof(RageVertex) ); StatsAddVerts( iNumVerts ); } void RageDisplay::DrawIndexedTriangles( const RageVertex v[], const Uint16 pIndices[], int iNumIndices ) { if( iNumIndices == 0 ) return; g_pd3dDevice->SetVertexShader( D3DFVF_RAGEVERTEX ); SEND_CURRENT_MATRICES; g_pd3dDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, // PrimitiveType 0, // MinIndex iNumIndices, // NumVertices iNumIndices/3, // PrimitiveCount, pIndices, // pIndexData, D3DFMT_INDEX16, // IndexDataFormat, v, // pVertexStreamZeroData, sizeof(RageVertex) // VertexStreamZeroStride ); StatsAddVerts( iNumIndices ); } void RageDisplay::DrawLineStrip( const RageVertex v[], int iNumVerts, float LineWidth ) { ASSERT( iNumVerts >= 2 ); g_pd3dDevice->SetRenderState( D3DRS_POINTSIZE, *((DWORD*)&LineWidth) ); // funky cast. See D3DRENDERSTATETYPE doc g_pd3dDevice->SetVertexShader( D3DFVF_RAGEVERTEX ); SEND_CURRENT_MATRICES; g_pd3dDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, // PrimitiveType iNumVerts-1, // PrimitiveCount, v, // pVertexStreamZeroData, sizeof(RageVertex) ); StatsAddVerts( iNumVerts ); } void RageDisplay::SetTexture( RageTexture* pTexture ) { if( pTexture == NULL ) { g_pd3dDevice->SetTexture( 0, NULL ); return; } unsigned uTexHandle = pTexture->GetTexHandle(); IDirect3DTexture8* pTex = (IDirect3DTexture8*)uTexHandle; g_pd3dDevice->SetTexture( 0, pTex ); // Set palette (if any) if( g_TexResourceToPaletteIndex.find(uTexHandle) != g_TexResourceToPaletteIndex.end() ) { int iPaletteIndex = g_TexResourceToPaletteIndex.find(uTexHandle)->second; #ifndef _XBOX g_pd3dDevice->SetCurrentTexturePalette( iPaletteIndex ); #else ASSERT(0); #endif } } void RageDisplay::SetTextureModeModulate() { g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); } void RageDisplay::SetTextureModeGlow(GlowMode m) { g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); } void RageDisplay::SetTextureFiltering( bool b ) { g_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, b ? D3DTEXF_LINEAR : D3DTEXF_POINT ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, b ? D3DTEXF_LINEAR : D3DTEXF_POINT ); } void RageDisplay::SetBlendMode( BlendMode mode ) { g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); switch( mode ) { case BLEND_NORMAL: g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); break; case BLEND_ADD: g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE ); g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); break; case BLEND_NO_EFFECT: g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO ); g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); break; default: ASSERT(0); } } bool RageDisplay::IsZBufferEnabled() const { DWORD b; g_pd3dDevice->GetRenderState( D3DRS_ZENABLE, &b ); return b!=0; } void RageDisplay::SetZBuffer( bool b ) { g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, b ); g_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, b ); } void RageDisplay::ClearZBuffer() { g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0x00000000 ); } void RageDisplay::SetTextureWrapping( bool b ) { int mode = b ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP; g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU, mode ); g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV, mode ); } void RageDisplay::SetMaterial( float emissive[4], float ambient[4], float diffuse[4], float specular[4], float shininess ) { D3DMATERIAL8 mat; memcpy( &mat.Diffuse, diffuse, sizeof(diffuse) ); memcpy( &mat.Ambient, ambient, sizeof(ambient) ); memcpy( &mat.Specular, specular, sizeof(specular) ); memcpy( &mat.Emissive, emissive, sizeof(emissive) ); mat.Power = shininess; g_pd3dDevice->SetMaterial( &mat ); } void RageDisplay::SetLighting( bool b ) { g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, b ); } void RageDisplay::SetLightOff( int index ) { g_pd3dDevice->LightEnable( index, false ); } void RageDisplay::SetLightDirectional( int index, RageColor ambient, RageColor diffuse, RageColor specular, RageVector3 dir ) { g_pd3dDevice->LightEnable( index, true ); D3DLIGHT8 light; ZERO( light ); memcpy( &light.Direction, &dir, sizeof(dir) ); memcpy( &light.Direction, &dir, sizeof(dir) ); memcpy( &light.Diffuse, diffuse, sizeof(diffuse) ); memcpy( &light.Ambient, ambient, sizeof(ambient) ); memcpy( &light.Specular, specular, sizeof(specular) ); g_pd3dDevice->SetLight( index, &light ); } void RageDisplay::SetBackfaceCull( bool b ) { g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, b ? D3DCULL_CW : D3DCULL_NONE ); } void RageDisplay::DeleteTexture( unsigned uTexHandle ) { IDirect3DTexture8* pTex = (IDirect3DTexture8*) uTexHandle; pTex->Release(); // Delete palette (if any) if( g_TexResourceToPaletteIndex.find(uTexHandle) != g_TexResourceToPaletteIndex.end() ) g_TexResourceToPaletteIndex.erase( g_TexResourceToPaletteIndex.find(uTexHandle) ); } unsigned RageDisplay::CreateTexture( PixelFormat pixfmt, SDL_Surface*& img ) { // texture must be power of two ASSERT( img->w == power_of_two(img->w) ); ASSERT( img->h == power_of_two(img->h) ); HRESULT hr; IDirect3DTexture8* pTex; hr = g_pd3dDevice->CreateTexture( img->w, img->h, 1, 0, D3DFORMATS[pixfmt], D3DPOOL_MANAGED, &pTex ); unsigned uTexHandle = (unsigned)pTex; if( pixfmt == FMT_PAL ) { int iPalIndex = GetUnusedPaletteIndex(); // Save palette PALETTEENTRY pal[256]; memset( pal, 0, sizeof(pal) ); for( int i=0; iformat->palette->ncolors; i++ ) { SDL_Color c = img->format->palette->colors[i]; pal[i].peRed = c.r; pal[i].peGreen = c.g; pal[i].peBlue = c.b; bool bIsColorKey = img->flags & SDL_SRCCOLORKEY && (unsigned)i == img->format->colorkey; pal[i].peFlags = bIsColorKey ? 0x00 : 0xFF; } #ifndef _XBOX g_pd3dDevice->SetPaletteEntries( iPalIndex, pal ); #else ASSERT(0); #endif ASSERT( g_TexResourceToPaletteIndex.find(uTexHandle) == g_TexResourceToPaletteIndex.end() ); g_TexResourceToPaletteIndex[uTexHandle] = iPalIndex; } UpdateTexture( uTexHandle, pixfmt, img, 0, 0, img->w, img->h ); return uTexHandle; } void RageDisplay::UpdateTexture( unsigned uTexHandle, PixelFormat pixfmt, SDL_Surface*& img, int xoffset, int yoffset, int width, int height ) { IDirect3DTexture8* pTex = (IDirect3DTexture8*)uTexHandle; RECT rect; rect.left = xoffset; rect.top = yoffset; rect.right = width - xoffset; rect.bottom = height - yoffset; D3DLOCKED_RECT lr; pTex->LockRect( 0, &lr, &rect, 0 ); // copy each row int bytes_per_pixel = img->format->BytesPerPixel; for( int y=rect.top; ypixels + y*img->pitch + rect.left*bytes_per_pixel; char* dst = (char*)lr.pBits + y*lr.Pitch + rect.left*bytes_per_pixel; memcpy( dst, src, (rect.right-rect.left)*bytes_per_pixel ); } pTex->UnlockRect( 0 ); // SDL_SaveBMP( (SDL_Surface*)img, "testing-SDL.bmp" ); // D3DXSaveTextureToFile( // "testing-D3D.bmp", // D3DXIFF_BMP, // pTex, // NULL ); } void RageDisplay::SetAlphaTest( bool b ) { g_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, b ); g_pd3dDevice->SetRenderState( D3DRS_ALPHAREF, 0 ); g_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATER ); }