| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // W3DSmudge.cpp ////////////////////////////////////////////////////////////////////////////////
- // Smudge System implementation
- // Author: Mark Wilczynski, June 2003
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #include "Lib/Basetype.h"
- #include "always.h"
- #include "W3DDevice/GameClient/W3DSmudge.h"
- #include "W3DDevice/GameClient/W3DShaderManager.h"
- #include "Common/GameMemory.h"
- #include "GameClient/view.h"
- #include "GameClient/display.h"
- #include "WW3D2/texture.h"
- #include "WW3D2/dx8indexbuffer.h"
- #include "WW3D2/dx8wrapper.h"
- #include "WW3D2/rinfo.h"
- #include "WW3D2/camera.h"
- #include "WW3D2/sortingrenderer.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- SmudgeManager *TheSmudgeManager=NULL;
- W3DSmudgeManager::W3DSmudgeManager(void)
- {
- }
- W3DSmudgeManager::~W3DSmudgeManager()
- {
- ReleaseResources();
- }
- void W3DSmudgeManager::init(void)
- {
- SmudgeManager::init();
- ReAcquireResources();
- }
- void W3DSmudgeManager::reset (void)
- {
- SmudgeManager::reset(); //base
- }
- void W3DSmudgeManager::ReleaseResources(void)
- {
- #ifdef USE_COPY_RECTS
- REF_PTR_RELEASE(m_backgroundTexture);
- #endif
- REF_PTR_RELEASE(m_indexBuffer);
- }
- //Make sure (SMUDGE_DRAW_SIZE * 12) < 65535 because that's the max index buffer size.
- #define SMUDGE_DRAW_SIZE 500 //draw at most 50 smudges per call. Tweak value to improve CPU/GPU parallelism.
- void W3DSmudgeManager::ReAcquireResources(void)
- {
- ReleaseResources();
- SurfaceClass *surface=DX8Wrapper::_Get_DX8_Back_Buffer();
- SurfaceClass::SurfaceDescription surface_desc;
- surface->Get_Description(surface_desc);
- REF_PTR_RELEASE(surface);
- #ifdef USE_COPY_RECTS
- m_backgroundTexture = MSGNEW("TextureClass") TextureClass(TheTacticalView->getWidth(),TheTacticalView->getHeight(),surface_desc.Format,MIP_LEVELS_1,TextureClass::POOL_DEFAULT, true);
- #endif
- m_backBufferWidth = surface_desc.Width;
- m_backBufferHeight = surface_desc.Height;
- m_indexBuffer=NEW_REF(DX8IndexBufferClass,(SMUDGE_DRAW_SIZE*4*3)); //allocate 4 triangles per smudge, each with 3 indices.
- // Fill up the IB with static vertex indices that will be used for all smudges.
- {
- DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer);
- UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
- //quad of 4 triangles:
- // 0-----3
- // |\ /|
- // | 4 |
- // |/ \|
- // 1-----2
- Int vbCount=0;
- for (Int i=0; i<SMUDGE_DRAW_SIZE; i++)
- {
- //Top
- ib[0]=vbCount;
- ib[1]=vbCount+4;
- ib[2]=vbCount+3;
- //Right
- ib[3]=vbCount+3;
- ib[4]=vbCount+4;
- ib[5]=vbCount+2;
- //Bottom
- ib[6]=vbCount+2;
- ib[7]=vbCount+4;
- ib[8]=vbCount+1;
- //Left
- ib[9]=vbCount+1;
- ib[10]=vbCount+4;
- ib[11]=vbCount+0;
- vbCount += 5;
- ib+=12;
- }
- }
- }
- /*Copies a portion of the current render target into a specified buffer*/
- Int copyRect(unsigned char *buf, Int bufSize, int oX, int oY, int width, int height)
- {
- IDirect3DSurface8 *surface=NULL; ///<previous render target
- IDirect3DSurface8 *tempSurface=NULL;
- Int result = 0;
- HRESULT hr = S_OK;
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- if (!m_pDev)
- goto error;
- m_pDev->GetRenderTarget(&surface);
- if (!surface)
- goto error;
- D3DSURFACE_DESC desc;
-
- surface->GetDesc(&desc);
-
- RECT srcRect;
- srcRect.left=oX;
- srcRect.top=oY;
- srcRect.right=oX+width;
- srcRect.bottom=oY+height;
- POINT dstPoint;
- dstPoint.x=0;
- dstPoint.y=0;
- hr=m_pDev->CreateImageSurface( width, height, desc.Format, &tempSurface);
- if (hr != S_OK)
- goto error;
-
- hr=m_pDev->CopyRects(surface,&srcRect,1,tempSurface,&dstPoint);
- if (hr != S_OK)
- goto error;
- D3DLOCKED_RECT lrect;
-
- hr=tempSurface->LockRect(&lrect,NULL,D3DLOCK_READONLY);
- if (hr != S_OK)
- goto error;
- tempSurface->GetDesc(&desc);
- if (desc.Size < bufSize)
- bufSize = desc.Size;
-
- memcpy(buf,lrect.pBits,bufSize);
- result = bufSize;
- error:
- if (surface)
- surface->Release();
- if (tempSurface)
- tempSurface->Release();
- return result;
- }
- #define UNIQUE_COLOR (0x12345678)
- #define BLOCK_SIZE (8)
- Bool W3DSmudgeManager::testHardwareSupport(void)
- {
- if (m_hardwareSupportStatus == SMUDGE_SUPPORT_UNKNOWN)
- { //we have not done the test yet.
- IDirect3DTexture8 *backTexture=W3DShaderManager::getRenderTexture();
- if (!backTexture)
- { //do trivial test first to see if render target exists.
- m_hardwareSupportStatus = SMUDGE_SUPPORT_NO;
- return FALSE;
- }
- if (!W3DShaderManager::isRenderingToTexture())
- return FALSE; //can't do the test unless we're rendering to texture.
- VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
- DX8Wrapper::Set_Material(vmat);
- REF_PTR_RELEASE(vmat); //no need to keep a reference since it's a preset.
-
- ShaderClass shader=ShaderClass::_PresetOpaqueShader;
- shader.Set_Depth_Compare(ShaderClass::PASS_ALWAYS);
- shader.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_DISABLE);
- DX8Wrapper::Set_Shader(shader);
- DX8Wrapper::Set_Texture(0,NULL);
- DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
- struct _TRANS_LIT_TEX_VERTEX {
- Vector4 p;
- DWORD color; // diffuse color
- float u;
- float v;
- } v[4];
- //bottom right
- v[0].p = Vector4( BLOCK_SIZE-0.5f, BLOCK_SIZE-0.5f, 0.0f, 1.0f );
- v[0].u = BLOCK_SIZE/(Real)TheDisplay->getWidth(); v[0].v = BLOCK_SIZE/(Real)TheDisplay->getHeight();;
- //top right
- v[1].p = Vector4( BLOCK_SIZE-0.5f, 0-0.5f, 0.0f, 1.0f );
- v[1].u = BLOCK_SIZE/(Real)TheDisplay->getWidth(); v[1].v = 0;
- //bottom left
- v[2].p = Vector4( 0-0.5f, BLOCK_SIZE-0.5f, 0.0f, 1.0f );
- v[2].u = 0; v[2].v = BLOCK_SIZE/(Real)TheDisplay->getHeight();
- //top left
- v[3].p = Vector4( 0-0.5f, 0-0.5f, 0.0f, 1.0f );
- v[3].u = 0; v[3].v = 0;
- v[0].color = UNIQUE_COLOR;
- v[1].color = UNIQUE_COLOR;
- v[2].color = UNIQUE_COLOR;
- v[3].color = UNIQUE_COLOR;
- LPDIRECT3DDEVICE8 pDev=DX8Wrapper::_Get_D3D_Device8();
- //draw polygons like this is very inefficient but for only 2 triangles, it's
- //not worth bothering with index/vertex buffers.
- pDev->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1);
- pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(_TRANS_LIT_TEX_VERTEX));
- DWORD refData[BLOCK_SIZE*BLOCK_SIZE];
- memset(refData,0,sizeof(refData));
- Int bufSize=copyRect((unsigned char *)refData,sizeof(refData),0,0,BLOCK_SIZE,BLOCK_SIZE); //copy area we just rendered using solid color
- if (!bufSize)
- {
- m_hardwareSupportStatus = SMUDGE_SUPPORT_NO;
- return FALSE;
- }
- DX8Wrapper::Set_DX8_Texture(0,backTexture);
- DWORD testData[BLOCK_SIZE*BLOCK_SIZE];
- memset(testData,0xff,sizeof(testData));
- v[0].color = 0xffffffff;
- v[1].color = 0xffffffff;
- v[2].color = 0xffffffff;
- v[3].color = 0xffffffff;
- pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(_TRANS_LIT_TEX_VERTEX));
- bufSize=copyRect((unsigned char *)testData,sizeof(testData),0,0,BLOCK_SIZE,BLOCK_SIZE);
- if (!bufSize)
- {
- m_hardwareSupportStatus = SMUDGE_SUPPORT_NO;
- return FALSE;
- }
- //compare the 2 buffers to see if they match.
- if (memcmp(testData,refData,bufSize) == 0)
- {
- m_hardwareSupportStatus = SMUDGE_SUPPORT_YES;
- return TRUE;
- }
- m_hardwareSupportStatus = SMUDGE_SUPPORT_NO;
- }
- return (SMUDGE_SUPPORT_YES == m_hardwareSupportStatus);
- }
- void W3DSmudgeManager::render(RenderInfoClass &rinfo)
- {
- //Verify that the card supports the effect.
- if (!testHardwareSupport())
- return;
- CameraClass &camera=rinfo.Camera;
- Vector3 vsVert;
- Vector4 ssVert;
- Real uvSpanX,uvSpanY;
- Vector3 vertex_offsets[4] = {
- Vector3(-0.5f, 0.5f, 0.0f),
- Vector3(-0.5f, -0.5f, 0.0f),
- Vector3(0.5f, -0.5f, 0.0f),
- Vector3(0.5f, 0.5f, 0.0f)
- };
- #define THE_COLOR (0x00ffeedd)
- UnsignedInt vertexDiffuse[5]={THE_COLOR,THE_COLOR,THE_COLOR,THE_COLOR,THE_COLOR};
- Matrix4x4 proj;
- Matrix3D view;
- camera.Get_View_Matrix(&view);
- camera.Get_Projection_Matrix(&proj);
- SurfaceClass::SurfaceDescription surface_desc;
- #ifdef USE_COPY_RECTS
- SurfaceClass *background=m_backgroundTexture->Get_Surface_Level();
- background->Get_Description(surface_desc);
- #else
- D3DSURFACE_DESC D3DDesc;
- IDirect3DTexture8 *backTexture=W3DShaderManager::getRenderTexture();
- if (!backTexture || !W3DShaderManager::isRenderingToTexture())
- return; //this card doesn't support render targets.
- backTexture->GetLevelDesc(0,&D3DDesc);
- surface_desc.Width = D3DDesc.Width;
- surface_desc.Height = D3DDesc.Height;
- #endif
- Real texClampX = (Real)TheTacticalView->getWidth()/(Real)surface_desc.Width;
- Real texClampY = (Real)TheTacticalView->getHeight()/(Real)surface_desc.Height;
- Real texScaleX = texClampX*0.5f;
- Real texScaleY = texClampY*0.5f;
- //Do a first pass over the smudges to determine how many are visible
- //and to fill in their world-space positions and screen uv coordinates.
- //TODO: Optimize out this extra pass!
- //TODO: Find size of screen rectangle that actually needs copying.
- SmudgeSet *set=m_usedSmudgeSetList.Head(); //first set that didn't fit into render batch.
- Int count = 0;
- if (set)
- { //there are possibly some smudges to render, so make sure background particles have finished drawing.
- SortingRendererClass::Flush(); //draw sorted translucent polys like particles.
- }
- while (set)
- {
- Smudge *smudge=set->getUsedSmudgeList().Head();
- while (smudge)
- {
- //Get view-space center
- Matrix3D::Transform_Vector(view,smudge->m_pos,&vsVert);
- //Get 5 view-space vertices
- Smudge::smudgeVertex *verts=smudge->m_verts;
- //Do center vertex outside 'for' loop since it's different.
- verts[4].pos = vsVert;
- for (Int i=0; i<4; i++)
- {
- verts[i].pos = vsVert + vertex_offsets[i] * smudge->m_size;
- //Ge uv coordinates for each vertex
- ssVert = proj * verts[i].pos;
- Real oow = 1.0f/ssVert.W;
- ssVert *= oow; //returned in camera space which is -1,-1 (bottom-left) to 1,1 (top-right)
- //convert camera space to uv space: 0,0 (top-left), 1,1 (bottom-right)
- verts[i].uv.Set((ssVert.X+1.0f)*texScaleX,(1.0f-ssVert.Y)*texScaleY);
- Vector2 &thisUV=verts[i].uv;
- //Clamp coordinates so we're not referencing texels outside the view.
- if (thisUV.X > texClampX)
- smudge->m_offset.X = 0;
- else
- if (thisUV.X < 0)
- smudge->m_offset.X = 0;
- if (thisUV.Y > texClampY)
- smudge->m_offset.Y = 0;
- else
- if (thisUV.Y < 0)
- smudge->m_offset.Y = 0;
- }
- //Finish center vertex
- //Ge uv coordinates by interpolating corner uv coordinates and applying desired offset.
- uvSpanX=verts[3].uv.X - verts[0].uv.X;
- uvSpanY=verts[1].uv.Y - verts[0].uv.Y;
- verts[4].uv.X=verts[0].uv.X+uvSpanX*(0.5f+smudge->m_offset.X);
- verts[4].uv.Y=verts[0].uv.Y+uvSpanY*(0.5f+smudge->m_offset.X);
- count++; //increment visible smudge count.
- smudge=smudge->Succ();
- }
- set=set->Succ(); //advance to next node.
- }
- if (!count)
- {
- #ifdef USE_COPY_RECTS
- REF_PTR_RELEASE(background);
- #endif
- return; //nothing to render.
- }
- #ifdef USE_COPY_RECTS
- SurfaceClass *backBuffer=DX8Wrapper::_Get_DX8_Back_Buffer();
- backBuffer->Get_Description(surface_desc);
- //Copy the area of backbuffer occupied by smudges into an alternate buffer.
- background->Copy(0,0,0,0,surface_desc.Width,surface_desc.Height,backBuffer);
- REF_PTR_RELEASE(background);
- REF_PTR_RELEASE(backBuffer);
- #endif
- Matrix4x4 identity(true);
- DX8Wrapper::Set_Transform(D3DTS_WORLD,identity);
- DX8Wrapper::Set_Transform(D3DTS_VIEW,identity);
- DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
- //DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaqueSpriteShader);
- DX8Wrapper::Set_Shader(ShaderClass::_PresetAlphaShader);
- #ifdef USE_COPY_RECTS
- DX8Wrapper::Set_Texture(0,m_backgroundTexture);
- #else
- DX8Wrapper::Set_DX8_Texture(0,backTexture);
- //Need these states in case texture is non-power-of-2
- DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
- DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
- DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP);
- DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
- DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
- DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_MIPFILTER, D3DTEXF_NONE);
- #endif
- VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
- DX8Wrapper::Set_Material(vmat);
- REF_PTR_RELEASE(vmat);
- DX8Wrapper::Apply_Render_State_Changes();
- //Disable reading texture alpha since it's undefined.
- //DX8Wrapper::Set_DX8_Texture_Stage_State(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
- DX8Wrapper::Set_DX8_Texture_Stage_State(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG2);
- Int smudgesRemaining=count;
- set=m_usedSmudgeSetList.Head(); //first smudge set that needs rendering.
- Smudge *remainingSmudgeStart=set->getUsedSmudgeList().Head(); //first smudge that needs rendering.
- while (smudgesRemaining) //keep drawing smudges until we run out.
- {
- //Now that we know how many smudges need rendering, allocate vertex buffer space and copy verts.
- count=smudgesRemaining;
- if (count > SMUDGE_DRAW_SIZE)
- count = SMUDGE_DRAW_SIZE;
- Int smudgesInRenderBatch=0;
- DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,count*5); //allocate 5 verts per smudge.
- {
- DynamicVBAccessClass::WriteLockClass lock(&vb_access);
- VertexFormatXYZNDUV2* verts=lock.Get_Formatted_Vertex_Array();
- while (set)
- {
- Smudge *smudge=remainingSmudgeStart;
- while (smudge)
- {
- Smudge::smudgeVertex *smVerts = smudge->m_verts;
- //Check if we exceeded maximum number of smudges allowed per draw call.
- if (smudgesInRenderBatch >= count)
- { remainingSmudgeStart = smudge;
- goto flushSmudges;
- }
- //Set center vertex opacity.
- vertexDiffuse[4] = ((Int)(smudge->m_opacity * 255.0f) << 24) | THE_COLOR;
- for (Int i=0; i<5; i++)
- {
- verts->x=smVerts->pos.X;
- verts->y=smVerts->pos.Y;
- verts->z=smVerts->pos.Z;
- verts->nx=0; //keep AGP write-combining active
- verts->ny=0;
- verts->nz=0;
- verts->diffuse=vertexDiffuse[i]; //set to transparent
- verts->u1=smVerts->uv.X;
- verts->v1=smVerts->uv.Y;
- verts->u2=0; //keep AGP write-combining active
- verts->v2=0;
- verts++;
- smVerts++;
- }
- smudgesInRenderBatch++;
- smudge=smudge->Succ();
- }
- set=set->Succ(); //advance to next node.
- if (set) //start next batch at beginning of set.
- remainingSmudgeStart = set->getUsedSmudgeList().Head();
- }
- flushSmudges:
- DX8Wrapper::Set_Vertex_Buffer(vb_access);
- }
- DX8Wrapper::Draw_Triangles( 0,smudgesInRenderBatch*4, 0, smudgesInRenderBatch*5);
- //Debug Code which draws outline around smudge
- /* DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
- DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
- DX8Wrapper::Set_DX8_Texture_Stage_State(0,D3DTSS_COLOROP,D3DTOP_SELECTARG2);
- DX8Wrapper::Draw_Triangles( 0,smudgesInRenderBatch*4, 0, smudgesInRenderBatch*5);
- DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
- DX8Wrapper::_Get_D3D_Device8()->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
- DX8Wrapper::Set_DX8_Texture_Stage_State(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
- */
- smudgesRemaining -= smudgesInRenderBatch;
- }
- DX8Wrapper::Set_DX8_Texture_Stage_State(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
- DX8Wrapper::Set_DX8_Texture_Stage_State(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
- }
|