/* ** 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 . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WWPhys * * * * $Archive:: /Commando/Code/ww3d2/matrixmapper.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Kenny Mitchell * * * * $Modtime:: 06/26/02 4:04p $* * * * $Revision:: 11 $* * * * 06/26/02 KM Matrix name change to avoid MAX conflicts * *---------------------------------------------------------------------------------------------* * Functions: * * MatrixMapperClass::MatrixMapperClass -- Constructor * * MatrixMapperClass::Set_Texture_Transform -- Sets the viewspace-to-texturespace transform * * MatrixMapperClass::Update_View_To_Pixel_Transform -- recomputes ViewToPixel * * MatrixMapperClass::Compute_Texture_Coordinate -- compute a single texture coord * * MatrixMapperClass::Apply -- Apply texture mapper to render states and texture stage states* * MatrixMapperClass::Calculate_Texture_Matrix -- Calculate texture matrix * * CompositeMatrixMapperClass::CompositeMatrixMapperClass -- Constructor * * CompositeMatrixMapperClass::CompositeMatrixMapperClass -- Copy Constructor * * CompositeMatrixMapperClass::~CompositeMatrixMapperClass -- Destructor * * CompositeMatrixMapperClass::~CompositeMatrixMapperClass -- Destructor * * CompositeMatrixMapperClass::Apply -- Apply texture mapper to render states and ts states * * CompositeMatrixMapperClass::Calculate_Texture_Matrix -- Calculate texture matrix * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "matrixmapper.h" #include "dx8wrapper.h" /*********************************************************************************************** * MatrixMapperClass::MatrixMapperClass -- Constructor * * * * Initializes the member variables to defaults * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/13/99 gth : created * *=============================================================================================*/ MatrixMapperClass::MatrixMapperClass(int stage) : TextureMapperClass(stage), Flags(0), Type(ORTHO_PROJECTION), ViewToTexture(true), ViewToPixel(true), ViewSpaceProjectionNormal(0.0f, 0.0f, 0.0f), GradientUCoord(0.5f) { } /*********************************************************************************************** * MatrixMapperClass::Set_Texture_Transform -- Sets the viewspace-to-texturespace transform * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 6/20/2001 gth : created * *=============================================================================================*/ void MatrixMapperClass::Set_Texture_Transform(const Matrix3D & view_to_texture,float texsize) { ViewToTexture = Matrix4x4(view_to_texture); Update_View_To_Pixel_Transform(texsize); } /*********************************************************************************************** * MatrixMapperClass::Set_Texture_Transform -- Sets the viewspace-to-texturespace transform * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/13/99 gth : Created. * *=============================================================================================*/ void MatrixMapperClass::Set_Texture_Transform(const Matrix4x4 & view_to_texture,float texsize) { ViewToTexture=view_to_texture; Update_View_To_Pixel_Transform(texsize); } /*********************************************************************************************** * MatrixMapperClass::Update_View_To_Pixel_Transform -- recomputes ViewToPixel * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void MatrixMapperClass::Update_View_To_Pixel_Transform(float tex_size) { /* ** Create a ViewToPixel matrix which also does all of the offseting and flipping that has ** to take place to get the actual texture coordinates. ** ** Here is a description of the math: ** If you transform eye-space points with the ViewToTexture matrix, you then need to compute: ** s = ((x/w) + 1.0f) * 0.5f * ((texwid-2)/texwid) ** t = (1.0f - (y/w)) * 0.5f * ((texwid-2)/texwid) ** ** Let K = 0.5 * ((texwid-2)/texwid), Then: ** ((x/w) + 1.0f) * K = (K*x + K*w) / w ** (1.0f - (y/w)) * K = (-K*y + K*w) / w ** ** This leads to the following matrix (The KOOK matrix!) ** | K 0 0 K | ** | 0 -K 0 K | ** | 0 0 1 0 | ** | 0 0 0 1 | ** ** The code below manually "optimally" pre-multiplies this matrix with the ViewToTexture matrix ** ** In addition, the z-coordinate is modified so that it goes from 0 to 1 rather than -1 to +1. ** It can also be flipped if the INVERT_DEPTH_GRADIENT flag is on. */ float k = 0.5 * (tex_size - 2.0f) / tex_size; ViewToPixel[0][0] = k * ViewToTexture[0][0] + k * ViewToTexture[3][0]; ViewToPixel[0][1] = k * ViewToTexture[0][1] + k * ViewToTexture[3][1]; ViewToPixel[0][2] = k * ViewToTexture[0][2] + k * ViewToTexture[3][2]; ViewToPixel[0][3] = k * ViewToTexture[0][3] + k * ViewToTexture[3][3]; ViewToPixel[1][0] = -k * ViewToTexture[1][0] + k * ViewToTexture[3][0]; ViewToPixel[1][1] = -k * ViewToTexture[1][1] + k * ViewToTexture[3][1]; ViewToPixel[1][2] = -k * ViewToTexture[1][2] + k * ViewToTexture[3][2]; ViewToPixel[1][3] = -k * ViewToTexture[1][3] + k * ViewToTexture[3][3]; if (Get_Flag(INVERT_DEPTH_GRADIENT)) { ViewToPixel[2][0] = -0.5f * ViewToTexture[2][0] + 0.5f * ViewToTexture[3][0]; ViewToPixel[2][1] = -0.5f * ViewToTexture[2][1] + 0.5f * ViewToTexture[3][1]; ViewToPixel[2][2] = -0.5f * ViewToTexture[2][2] + 0.5f * ViewToTexture[3][2]; ViewToPixel[2][3] = -0.5f * ViewToTexture[2][3] + 0.5f * ViewToTexture[3][3]; } else { ViewToPixel[2][0] = 0.5f * ViewToTexture[2][0] + 0.5f * ViewToTexture[3][0]; ViewToPixel[2][1] = 0.5f * ViewToTexture[2][1] + 0.5f * ViewToTexture[3][1]; ViewToPixel[2][2] = 0.5f * ViewToTexture[2][2] + 0.5f * ViewToTexture[3][2]; ViewToPixel[2][3] = 0.5f * ViewToTexture[2][3] + 0.5f * ViewToTexture[3][3]; } ViewToPixel[3] = ViewToTexture[3]; /* ** Store the view space negative z vector for lighting effects */ ViewSpaceProjectionNormal.X = -ViewToTexture[2][0]; ViewSpaceProjectionNormal.Y = -ViewToTexture[2][1]; ViewSpaceProjectionNormal.Z = -ViewToTexture[2][2]; ViewSpaceProjectionNormal.Normalize(); } /*********************************************************************************************** * MatrixMapperClass::Compute_Texture_Coordinate -- compute a single texture coord * * * * INPUT: * * point - 3D point to calculate the texture coordinate for * * set_stq - pointer to texture coordinate, s,t will be stored in X,Y. q will be stored in Z * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/25/00 gth : Created. * *=============================================================================================*/ void MatrixMapperClass::Compute_Texture_Coordinate(const Vector3 & point,Vector3 * set_stq) { set_stq->X = ViewToPixel[0][0]*point.X + ViewToPixel[0][1]*point.Y + ViewToPixel[0][2]*point.Z + ViewToPixel[0][3]; set_stq->Y = ViewToPixel[1][0]*point.X + ViewToPixel[1][1]*point.Y + ViewToPixel[1][2]*point.Z + ViewToPixel[1][3]; set_stq->Z = ViewToPixel[3][0]*point.X + ViewToPixel[3][1]*point.Y + ViewToPixel[3][2]*point.Z + ViewToPixel[3][3]; } /*********************************************************************************************** * MatrixMapperClass::Apply -- Apply texture mapper to render states and texture stage states * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/05/01 NH : Created comment block. * *=============================================================================================*/ void MatrixMapperClass::Apply(int uv_array_index) { Matrix4x4 m; switch (Type) { case ORTHO_PROJECTION: /* ** Orthographic projection */ DX8Wrapper::Set_Transform((D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + Stage),ViewToPixel); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_CAMERASPACEPOSITION); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT2); break; case PERSPECTIVE_PROJECTION: /* ** Perspective projection */ m[0]=ViewToPixel[0]; m[1]=ViewToPixel[1]; m[2]=ViewToPixel[3]; DX8Wrapper::Set_Transform((D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + Stage),m); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_CAMERASPACEPOSITION); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_PROJECTED|D3DTTFF_COUNT3); break; case DEPTH_GRADIENT: /* ** Depth gradient, Set up second stage texture coordinates to ** apply a depth gradient to the projection. Note that the ** depth values have been set up to vary from 0 to 1 in the ** Update_View_To_Pixel_Transform function. */ m[0].Set(0,0,0,GradientUCoord); m[1]=ViewToPixel[2]; DX8Wrapper::Set_Transform((D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + Stage),m); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_CAMERASPACEPOSITION); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT2); break; case NORMAL_GRADIENT: /* ** Normal Gradient, Set up the second stage texture coordinates to ** apply a gradient based on the dot product of the vertex normal ** and the projection direction. (NOTE: this is basically texture- ** based diffuse lighting!) */ m[0].Set(0,0,0,GradientUCoord); m[1].Set(ViewSpaceProjectionNormal.X,ViewSpaceProjectionNormal.Y,ViewSpaceProjectionNormal.Z, 0); DX8Wrapper::Set_Transform((D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + Stage),m); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_CAMERASPACENORMAL); DX8Wrapper::Set_DX8_Texture_Stage_State(Stage,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT2); break; } } /*********************************************************************************************** * MatrixMapperClass::Calculate_Texture_Matrix -- Calculate texture matrix * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/06/01 NH : Created comment block. * *=============================================================================================*/ void MatrixMapperClass::Calculate_Texture_Matrix(Matrix4x4 &tex_matrix) { // We return ViewToPixel. This is not, strictly speaking, always correct, but it is close // and it is OK not to be 100% correct since this function is not called by the Apply() // function. tex_matrix = ViewToPixel; } /*********************************************************************************************** * CompositeMatrixMapperClass::CompositeMatrixMapperClass -- Constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/05/01 NH : Created. * *=============================================================================================*/ CompositeMatrixMapperClass::CompositeMatrixMapperClass(TextureMapperClass *internal_mapper, unsigned int stage) : MatrixMapperClass(stage), InternalMapper(internal_mapper) { if (InternalMapper) { InternalMapper->Add_Ref(); } } /*********************************************************************************************** * CompositeMatrixMapperClass::CompositeMatrixMapperClass -- Copy Constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/05/01 NH : Created. * *=============================================================================================*/ CompositeMatrixMapperClass::CompositeMatrixMapperClass(const CompositeMatrixMapperClass & src) : MatrixMapperClass(src), InternalMapper(src.InternalMapper ? src.InternalMapper->Clone() : NULL) { if (InternalMapper) { InternalMapper->Add_Ref(); } } /*********************************************************************************************** * CompositeMatrixMapperClass::~CompositeMatrixMapperClass -- Destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/05/01 NH : Created. * *=============================================================================================*/ CompositeMatrixMapperClass::~CompositeMatrixMapperClass(void) { if (InternalMapper) { InternalMapper->Release_Ref(); InternalMapper = NULL; } } /*********************************************************************************************** * CompositeMatrixMapperClass::Apply -- Apply texture mapper to render states and ts states * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/05/01 NH : Created. * *=============================================================================================*/ void CompositeMatrixMapperClass::Apply(int uv_array_index) { if (InternalMapper) { // Get the texture matrix from the internal mapper, composite it into ViewToPixel (save off // the previous value of ViewToPixel first), call the base class Apply() function (which will // use the modifiedViewToPixel) and then restore ViewToPixel to its previous state. Matrix4x4 int_mat; InternalMapper->Calculate_Texture_Matrix(int_mat); Matrix4x4 view_to_pixel_copy(ViewToPixel); // We need to modify the view-to-pixel matrix to produce q (third texture coordinate values) // equal to one. This is the input which the internal mappers' matrix was designed for (it // is what you get when you use 2D vertex coordinates from the vertex buffer). // For this we need to multiply the matrix by the following matrix: // [1 0 0 0] // [0 1 0 0] This is equivalent to overwriting the third row with the fourth one. // [0 0 0 1] // [0 0 0 1] Matrix4x4 tmp; tmp[0] = ViewToPixel[0]; tmp[1] = ViewToPixel[1]; tmp[2] = ViewToPixel[3]; tmp[3] = ViewToPixel[3]; // We multiply the matrices in this order so the camera position, transformed by ViewToPixel // is used as the 'input texture coordinates' to be affected by the internal mapper matrix. Matrix4x4::Multiply(int_mat, tmp, &ViewToPixel); MatrixMapperClass::Apply(uv_array_index); ViewToPixel = view_to_pixel_copy; } else { MatrixMapperClass::Apply(uv_array_index); } } /*********************************************************************************************** * MatrixMapperClass::Calculate_Texture_Matrix -- Calculate texture matrix * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 11/06/01 NH : Created comment block. * *=============================================================================================*/ void CompositeMatrixMapperClass::Calculate_Texture_Matrix(Matrix4x4 &tex_matrix) { if (InternalMapper) { // We return the internal mapper's matrix times ViewToPixel, adjusted to produce the correct // z-value of 1. This is only correct if the mapper is in ORTHO_PROJECTION or // PERSPECTIVE_PROJECTION mode, which should be the case for most uses of this mapper. It is // OK for this function not be be 100% correct in all cases, since it is not used in the // Apply() function. Matrix4x4 int_mat; InternalMapper->Calculate_Texture_Matrix(int_mat); // We need to modify the view-to-pixel matrix to produce q (third texture coordinate values) // equal to one. This is the input which the internal mappers' matrix was designed for (it // is what you get when you use 2D vertex coordinates from the vertex buffer). // For this we need to multiply the matrix by the following matrix: // [1 0 0 0] // [0 1 0 0] This is equivalent to overwriting the third row with the fourth one. // [0 0 0 1] // [0 0 0 1] Matrix4x4 tmp; tmp[0] = ViewToPixel[0]; tmp[1] = ViewToPixel[1]; tmp[2] = ViewToPixel[3]; tmp[3] = ViewToPixel[3]; // We multiply the matrices in this order so the camera position, transformed by ViewToPixel // is used as the 'input texture coordinates' to be affected by the internal mapper matrix. Matrix4x4::Multiply(int_mat, tmp, &tex_matrix); } else { MatrixMapperClass::Calculate_Texture_Matrix(tex_matrix); } }