/* ** Command & Conquer Generals(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 . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: W3DCustomEdging.cpp //////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: W3DCustomEdging.cpp // // Created: John Ahlquist, May 2001 // // Desc: Draw buffer to handle all the custom tile edges in a scene. // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- #include "W3DDevice/GameClient/W3DCustomEdging.h" #include #include #include #include #include "common/GlobalData.h" #include "common/RandomValue.h" #include "W3DDevice/GameClient/TerrainTex.h" #include "W3DDevice/GameClient/HeightMap.h" #include "W3DDevice/GameClient/W3DDynamicLight.h" #include "WW3D2/Camera.h" #include "WW3D2/DX8Wrapper.h" #include "WW3D2/DX8Renderer.h" #include "WW3D2/Mesh.h" #include "WW3D2/MeshMdl.h" //----------------------------------------------------------------------------- // Private Data //----------------------------------------------------------------------------- // A W3D shader that does alpha, texturing, tests zbuffer, doesn't update zbuffer. #define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \ ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \ ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_DISABLE, \ ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) ) static ShaderClass detailAlphaTestShader(SC_ALPHA_DETAIL); #define SC_NO_ALPHA ( SHADE_CNST(ShaderClass::PASS_ALWAYS, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \ ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \ ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, \ ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) ) static ShaderClass detailShader(SC_NO_ALPHA); #define SC_DETAIL_BLEND ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \ ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \ ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE) ) static ShaderClass detailOpaqueShader(SC_DETAIL_BLEND); /* #define SC_ALPHA_MIRROR ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \ ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \ ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, \ ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) ) static ShaderClass mirrorAlphaShader(SC_ALPHA_DETAIL); // ShaderClass::PASS_ALWAYS, #define SC_ALPHA_2D ( SHADE_CNST(PASS_ALWAYS, DEPTH_WRITE_DISABLE, COLOR_WRITE_ENABLE, \ SRCBLEND_SRC_ALPHA, DSTBLEND_ONE_MINUS_SRC_ALPHA, FOG_DISABLE, GRADIENT_DISABLE, \ SECONDARY_GRADIENT_DISABLE, TEXTURING_ENABLE, DETAILCOLOR_DISABLE, DETAILALPHA_DISABLE, \ ALPHATEST_DISABLE, CULL_MODE_ENABLE, DETAILCOLOR_DISABLE, DETAILALPHA_DISABLE) ) ShaderClass ShaderClass::_PresetAlpha2DShader(SC_ALPHA_2D); */ //----------------------------------------------------------------------------- // Private Functions //----------------------------------------------------------------------------- //============================================================================= // W3DCustomEdging::loadEdgingsInVertexAndIndexBuffers //============================================================================= /** Loads the trees into the vertex buffer for drawing. */ //============================================================================= void W3DCustomEdging::loadEdgingsInVertexAndIndexBuffers(WorldHeightMap *pMap, Int minX, Int maxX, Int minY, Int maxY) { if (!m_indexEdging || !m_vertexEdging || !m_initialized) { return; } if (!m_anythingChanged) { return; } m_anythingChanged = false; m_curNumEdgingVertices = 0; m_curNumEdgingIndices = 0; VertexFormatXYZDUV2 *vb; UnsignedShort *ib; // Lock the buffers. DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexEdging); DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexEdging); vb=(VertexFormatXYZDUV2*)lockVtxBuffer.Get_Vertex_Array(); ib = lockIdxBuffer.Get_Index_Array(); UnsignedShort *curIb = ib; VertexFormatXYZDUV2 *curVb = vb; if (minX<0) minX = 0; if (minY<0) minY = 0; if (maxX >= pMap->getXExtent()) maxX = pMap->getXExtent()-1; if (maxY >= pMap->getYExtent()) maxY = pMap->getYExtent()-1; Int row; Int column; for (row=minY; rowgetXExtent(); Int blend = pMap->m_blendTileNdxes[cellNdx]; if (blend == 0) continue; // no blend. if (pMap->m_blendedTiles[blend].customBlendEdgeClass<0) continue; // alpha blend. Int i, j; Real uOffset; Real vOffset; if (pMap->m_blendedTiles[blend].horiz) { uOffset = 0; vOffset = 0.25f * (1 + (row&1)); if (pMap->m_blendedTiles[blend].inverted) { uOffset = 0.75f; } } else if (pMap->m_blendedTiles[blend].vert) { vOffset = 0.75; uOffset = 0.25f * (1 + (column&1)); if (pMap->m_blendedTiles[blend].inverted) { vOffset = 0.0f; } } else if (pMap->m_blendedTiles[blend].rightDiagonal) { if (pMap->m_blendedTiles[blend].longDiagonal) { vOffset = 0.25; uOffset = 0.5; if (pMap->m_blendedTiles[blend].inverted) { uOffset = 0.5f; vOffset = 0.5f; } } else { vOffset = .75; uOffset = 0; if (pMap->m_blendedTiles[blend].inverted) { uOffset = 0.0f; vOffset = 0.0f; } } } else if (pMap->m_blendedTiles[blend].leftDiagonal) { if (pMap->m_blendedTiles[blend].longDiagonal) { uOffset = 0.25f; vOffset = 0.25f; if (pMap->m_blendedTiles[blend].inverted) { uOffset = 0.25f; vOffset = 0.5f; } } else { vOffset = 0.75; uOffset = 0.75f; if (pMap->m_blendedTiles[blend].inverted) { uOffset = 0.75f; vOffset = 0.0f; } } } else { continue; } Region2D range; pMap->getUVForBlend(pMap->m_blendedTiles[blend].customBlendEdgeClass, &range); uOffset = range.lo.x + range.width()*uOffset; vOffset = range.lo.y + range.height()*vOffset; UnsignedByte alpha[4]; float UA[4], VA[4]; Bool flipForBlend; pMap->getAlphaUVData(column-pMap->getDrawOrgX(), row-pMap->getDrawOrgY(), UA, VA, alpha, &flipForBlend, false); Int startVertex = m_curNumEdgingVertices; for (j=0; j<2; j++) { for (i=0; i<2; i++) { if (m_curNumEdgingVertices >= MAX_EDGE_VERTEX) return; cellNdx = column+i+(row+j)*pMap->getXExtent(); Int diffuse = TheTerrainRenderObject->getStaticDiffuse(column+i, row+j); curVb->diffuse = 0x80000000 + (diffuse&0x00FFFFFF); // set alpha to 5b. Real theZ; theZ = ((float)pMap->getDataPtr()[cellNdx])*MAP_HEIGHT_SCALE; Real X = (column+i)*MAP_XY_FACTOR; Real Y = (row+j)*MAP_XY_FACTOR; curVb->u2 = uOffset + i*0.25f*range.width(); curVb->v2 = vOffset + (1-j)*0.25f*range.height(); Int ndx; if (j==0) ndx=i; if (j==1) ndx = 3-i; curVb->u1 = UA[ndx]; curVb->v1 = VA[ndx]; curVb->x = X; curVb->y = Y; curVb->z = theZ; curVb++; m_curNumEdgingVertices++; } } Int yOffset = 2; if (m_curNumEdgingIndices+6 > MAX_EDGE_INDEX) return; #ifdef FLIP_TRIANGLES // jba - reduces "diamonding" in some cases, not others. Better cliffs, though. if (flipForBlend) { *curIb++ = startVertex + 1; *curIb++ = startVertex + yOffset; *curIb++ = startVertex ; *curIb++ = startVertex + 1; *curIb++ = startVertex + 1+yOffset; *curIb++ = startVertex + yOffset; } else #endif { *curIb++ = startVertex; *curIb++ = startVertex + 1+yOffset; *curIb++ = startVertex + yOffset; *curIb++ = startVertex ; *curIb++ = startVertex + 1; *curIb++ = startVertex + 1+yOffset; } m_curNumEdgingIndices+=6; } } m_anythingChanged = false; } //----------------------------------------------------------------------------- // Public Functions //----------------------------------------------------------------------------- //============================================================================= // W3DCustomEdging::~W3DCustomEdging //============================================================================= /** Destructor. Releases w3d assets. */ //============================================================================= W3DCustomEdging::~W3DCustomEdging(void) { freeEdgingBuffers(); } //============================================================================= // W3DCustomEdging::W3DCustomEdging //============================================================================= /** Constructor. Sets m_initialized to true if it finds the w3d models it needs for the trees. */ //============================================================================= W3DCustomEdging::W3DCustomEdging(void) { m_initialized = false; m_vertexEdging = NULL; m_indexEdging = NULL; clearAllEdging(); allocateEdgingBuffers(); m_initialized = true; } //============================================================================= // W3DCustomEdging::freeEdgingBuffers //============================================================================= /** Frees the index and vertex buffers. */ //============================================================================= void W3DCustomEdging::freeEdgingBuffers(void) { REF_PTR_RELEASE(m_vertexEdging); REF_PTR_RELEASE(m_indexEdging); } //============================================================================= // W3DCustomEdging::allocateEdgingBuffers //============================================================================= /** Allocates the index and vertex buffers. */ //============================================================================= void W3DCustomEdging::allocateEdgingBuffers(void) { m_vertexEdging=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV2,MAX_EDGE_VERTEX+4,DX8VertexBufferClass::USAGE_DYNAMIC)); m_indexEdging=NEW_REF(DX8IndexBufferClass,(2*MAX_EDGE_INDEX+4, DX8IndexBufferClass::USAGE_DYNAMIC)); m_curNumEdgingVertices=0; m_curNumEdgingIndices=0; //m_edgeTexture = MSGNEW("TextureClass") TextureClass("EdgingTemplate.tga","EdgingTemplate.tga", TextureClass::MIP_LEVELS_3); } //============================================================================= // W3DCustomEdging::clearAllEdging //============================================================================= /** Removes all trees. */ //============================================================================= void W3DCustomEdging::clearAllEdging(void) { m_curNumEdgingVertices=0; m_curNumEdgingIndices=0; m_anythingChanged = true; } //============================================================================= // W3DCustomEdging::drawEdging //============================================================================= /** Draws the trees. Uses camera to cull. */ //============================================================================= void W3DCustomEdging::drawEdging(WorldHeightMap *pMap, Int minX, Int maxX, Int minY, Int maxY, TextureClass * terrainTexture, TextureClass * cloudTexture, TextureClass * noiseTexture) { static Bool foo = false; if (foo) { return; } //m_anythingChanged = true; loadEdgingsInVertexAndIndexBuffers(pMap, minX, maxX, minY, maxY); if (m_curNumEdgingIndices == 0) { return; } TextureClass *edgeTex = pMap->getEdgeTerrainTexture(); // Setup the vertex buffer, shader & texture. DX8Wrapper::Set_Index_Buffer(m_indexEdging,0); DX8Wrapper::Set_Vertex_Buffer(m_vertexEdging); DX8Wrapper::Set_Shader(detailAlphaTestShader); #ifdef _DEBUG //DX8Wrapper::Set_Shader(detailShader); // shows clipping. #endif DX8Wrapper::Set_Texture(0,terrainTexture); DX8Wrapper::Set_Texture(1,edgeTex); DX8Wrapper::Apply_Render_State_Changes(); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x7B); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_LESSEQUAL); //pass pixels who's alpha is not zero DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering. DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices); DX8Wrapper::Set_Texture(0,edgeTex); DX8Wrapper::Set_Texture(1, NULL); // Draw the custom edge. DX8Wrapper::Apply_Render_State_Changes(); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x84); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL); //pass pixels who's alpha is not zero DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering. DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices); #if 0 // Dumps out unmasked data. DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHABLENDENABLE,false); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, false); //test pixels if transparent(clipped) before rendering. DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices); #endif DX8Wrapper::Set_Texture(1, NULL); if (cloudTexture) { DX8Wrapper::Set_Shader(detailOpaqueShader); DX8Wrapper::Apply_Render_State_Changes(); DX8Wrapper::Set_Texture(1,edgeTex); DX8Wrapper::Apply_Render_State_Changes(); DX8Wrapper::Set_Texture(0,cloudTexture); DX8Wrapper::Apply_Render_State_Changes(); #if 1 DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ALPHAARG1, D3DTA_CURRENT ); DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG1, D3DTA_CURRENT ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAARG1, D3DTA_CURRENT ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAARG2, D3DTA_TEXTURE ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_TEXCOORDINDEX, 1 ); #endif DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x80); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_NOTEQUAL); //pass pixels who's alpha is not zero DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering. DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHABLENDENABLE,true); DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR); DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND,D3DBLEND_ZERO); DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices); } if (noiseTexture) { DX8Wrapper::Set_Texture(1, NULL); DX8Wrapper::Set_Texture(0,noiseTexture); DX8Wrapper::Apply_Render_State_Changes(); DX8Wrapper::Set_Texture(1,edgeTex); DX8Wrapper::Apply_Render_State_Changes(); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x80); DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_NOTEQUAL); //pass pixels who's alpha is not zero DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering. DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHABLENDENABLE,true); DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR); DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND,D3DBLEND_ZERO); DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices); } }