/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code 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. Doom 3 Source Code 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 Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "tools/edit_gui_common.h" #include "qe3.h" #define ZERO_EPSILON 1.0E-6 class idVec3D { public: double x, y, z; double & operator[]( const int index ) { return (&x)[index]; } void Zero() { x = y = z = 0.0; } }; // // ======================================================================================================================= // compute a determinant using Sarrus rule ++timo "inline" this with a macro NOTE:: the three idVec3D are understood as // columns of the matrix // ======================================================================================================================= // double SarrusDet(idVec3D a, idVec3D b, idVec3D c) { return (double)a[0] * (double)b[1] * (double)c[2] + (double)b[0] * (double)c[1] * (double)a[2] + (double)c[0] * (double)a[1] * (double)b[2] - (double)c[0] * (double)b[1] * (double)a[2] - (double)a[1] * (double)b[0] * (double)c[2] - (double)a[0] * (double)b[2] * (double)c[1]; } // // ======================================================================================================================= // ++timo replace everywhere texX by texS etc. ( > and in q3map !) NOTE:: ComputeAxisBase here and in q3map code must // always BE THE SAME ! WARNING:: special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere // when x == 0 rotation by (0,RotY,RotZ) assigns X to normal // ======================================================================================================================= // void ComputeAxisBase(idVec3 &normal, idVec3D &texS, idVec3D &texT) { double RotY, RotZ; // do some cleaning if (idMath::Fabs(normal[0]) < 1e-6) { normal[0] = 0.0f; } if (idMath::Fabs(normal[1]) < 1e-6) { normal[1] = 0.0f; } if (idMath::Fabs(normal[2]) < 1e-6) { normal[2] = 0.0f; } RotY = -atan2(normal[2], idMath::Sqrt(normal[1] * normal[1] + normal[0] * normal[0])); RotZ = atan2(normal[1], normal[0]); // rotate (0,1,0) and (0,0,1) to compute texS and texT texS[0] = -sin(RotZ); texS[1] = cos(RotZ); texS[2] = 0; // the texT vector is along -Z ( T texture coorinates axis ) texT[0] = -sin(RotY) * cos(RotZ); texT[1] = -sin(RotY) * sin(RotZ); texT[2] = -cos(RotY); } /* ======================================================================================================================= ======================================================================================================================= */ void FaceToBrushPrimitFace(face_t *f) { idVec3D texX, texY; idVec3D proj; // ST of (0,0) (1,0) (0,1) idVec5 ST[3]; // [ point index ] [ xyz ST ] // // ++timo not used as long as brushprimit_texdef and texdef are static // f->brushprimit_texdef.contents=f->texdef.contents; // f->brushprimit_texdef.flags=f->texdef.flags; // f->brushprimit_texdef.value=f->texdef.value; // strcpy(f->brushprimit_texdef.name,f->texdef.name); // #ifdef _DEBUG if (f->plane[0] == 0.0f && f->plane[1] == 0.0f && f->plane[2] == 0.0f) { common->Printf("Warning : f->plane.normal is (0,0,0) in FaceToBrushPrimitFace\n"); } // check d_texture if (!f->d_texture) { common->Printf("Warning : f.d_texture is NULL in FaceToBrushPrimitFace\n"); return; } #endif // compute axis base ComputeAxisBase(f->plane.Normal(), texX, texY); // compute projection vector VectorCopy( f->plane, proj ); VectorScale(proj, -f->plane[3], proj); // // (0,0) in plane axis base is (0,0,0) in world coordinates + projection on the // affine plane (1,0) in plane axis base is texX in world coordinates + projection // on the affine plane (0,1) in plane axis base is texY in world coordinates + // projection on the affine plane use old texture code to compute the ST coords of // these points // VectorCopy(proj, ST[0]); EmitTextureCoordinates(ST[0], f->d_texture, f); VectorCopy(texX, ST[1]); VectorAdd(ST[1], proj, ST[1]); EmitTextureCoordinates(ST[1], f->d_texture, f); VectorCopy(texY, ST[2]); VectorAdd(ST[2], proj, ST[2]); EmitTextureCoordinates(ST[2], f->d_texture, f); // compute texture matrix f->brushprimit_texdef.coords[0][2] = ST[0][3]; f->brushprimit_texdef.coords[1][2] = ST[0][4]; f->brushprimit_texdef.coords[0][0] = ST[1][3] - f->brushprimit_texdef.coords[0][2]; f->brushprimit_texdef.coords[1][0] = ST[1][4] - f->brushprimit_texdef.coords[1][2]; f->brushprimit_texdef.coords[0][1] = ST[2][3] - f->brushprimit_texdef.coords[0][2]; f->brushprimit_texdef.coords[1][1] = ST[2][4] - f->brushprimit_texdef.coords[1][2]; } // // ======================================================================================================================= // compute texture coordinates for the winding points // ======================================================================================================================= // void EmitBrushPrimitTextureCoordinates(face_t *f, idWinding *w, patchMesh_t *patch) { idVec3D texX, texY; double x, y; if (f== NULL || (w == NULL && patch == NULL)) { return; } // compute axis base ComputeAxisBase(f->plane.Normal(), texX, texY); // // in case the texcoords matrix is empty, build a default one same behaviour as if // scale[0]==0 && scale[1]==0 in old code // if ( f->brushprimit_texdef.coords[0][0] == 0 && f->brushprimit_texdef.coords[1][0] == 0 && f->brushprimit_texdef.coords[0][1] == 0 && f->brushprimit_texdef.coords[1][1] == 0 ) { f->brushprimit_texdef.coords[0][0] = 1.0f; f->brushprimit_texdef.coords[1][1] = 1.0f; ConvertTexMatWithQTexture(&f->brushprimit_texdef, NULL, &f->brushprimit_texdef, f->d_texture); } int i; if (w) { for (i = 0; i < w->GetNumPoints(); i++) { x = DotProduct((*w)[i], texX); y = DotProduct((*w)[i], texY); (*w)[i][3] = f->brushprimit_texdef.coords[0][0] * x + f->brushprimit_texdef.coords[0][1] * y + f->brushprimit_texdef.coords[0][2]; (*w)[i][4] = f->brushprimit_texdef.coords[1][0] * x + f->brushprimit_texdef.coords[1][1] * y + f->brushprimit_texdef.coords[1][2]; } } if (patch) { int j; for ( i = 0; i < patch->width; i++ ) { for ( j = 0; j < patch->height; j++ ) { x = DotProduct(patch->ctrl(i, j).xyz, texX); y = DotProduct(patch->ctrl(i, j).xyz, texY); patch->ctrl(i, j).st.x = f->brushprimit_texdef.coords[0][0] * x + f->brushprimit_texdef.coords[0][1] * y + f->brushprimit_texdef.coords[0][2]; patch->ctrl(i, j).st.y = f->brushprimit_texdef.coords[1][0] * x + f->brushprimit_texdef.coords[1][1] * y + f->brushprimit_texdef.coords[1][2]; } } } } // // ======================================================================================================================= // parse a brush in brush primitive format // ======================================================================================================================= // void BrushPrimit_Parse(brush_t *b, bool newFormat, const idVec3 origin) { face_t *f; int i, j; GetToken(true); if (strcmp(token, "{")) { Warning("parsing brush primitive"); return; } do { if (!GetToken(true)) { break; } if (!strcmp(token, "}")) { break; } // reading of b->epairs if any if (strcmp(token, "(")) { ParseEpair(&b->epairs); } else { // it's a face f = Face_Alloc(); f->next = NULL; if (!b->brush_faces) { b->brush_faces = f; } else { face_t *scan; for (scan = b->brush_faces; scan->next; scan = scan->next) ; scan->next = f; } if (newFormat) { // read the three point plane definition idPlane plane; for (j = 0; j < 4; j++) { GetToken(false); plane[j] = atof(token); } f->plane = plane; f->originalPlane = plane; f->dirty = false; //idWinding *w = Brush_MakeFaceWinding(b, f, true); idWinding w; w.BaseForPlane( plane ); for (j = 0; j < 3; j++) { f->planepts[j].x = w[j].x + origin.x; f->planepts[j].y = w[j].y + origin.y; f->planepts[j].z = w[j].z + origin.z; } GetToken(false); } else { for (i = 0; i < 3; i++) { if (i != 0) { GetToken(true); } if (strcmp(token, "(")) { Warning("parsing brush"); return; } for (j = 0; j < 3; j++) { GetToken(false); f->planepts[i][j] = atof(token); } GetToken(false); if (strcmp(token, ")")) { Warning("parsing brush"); return; } } } // texture coordinates GetToken(false); if (strcmp(token, "(")) { Warning("parsing brush primitive"); return; } GetToken(false); if (strcmp(token, "(")) { Warning("parsing brush primitive"); return; } for (j = 0; j < 3; j++) { GetToken(false); f->brushprimit_texdef.coords[0][j] = atof(token); } GetToken(false); if (strcmp(token, ")")) { Warning("parsing brush primitive"); return; } GetToken(false); if (strcmp(token, "(")) { Warning("parsing brush primitive"); return; } for (j = 0; j < 3; j++) { GetToken(false); f->brushprimit_texdef.coords[1][j] = atof(token); } GetToken(false); if (strcmp(token, ")")) { Warning("parsing brush primitive"); return; } GetToken(false); if (strcmp(token, ")")) { Warning("parsing brush primitive"); return; } // read the texturedef GetToken(false); // strcpy(f->texdef.name, token); if (g_qeglobals.mapVersion < 2.0) { f->texdef.SetName(va("textures/%s", token)); } else { f->texdef.SetName(token); } if (TokenAvailable()) { GetToken(false); GetToken(false); GetToken(false); f->texdef.value = atoi(token); } } } while (1); } // // ======================================================================================================================= // compute a fake shift scale rot representation from the texture matrix these shift scale rot values are to be // understood in the local axis base // ======================================================================================================================= // void TexMatToFakeTexCoords(float texMat[2][3], float shift[2], float *rot, float scale[2]) { #ifdef _DEBUG // check this matrix is orthogonal if (idMath::Fabs(texMat[0][0] * texMat[0][1] + texMat[1][0] * texMat[1][1]) > ZERO_EPSILON) { common->Printf("Warning : non orthogonal texture matrix in TexMatToFakeTexCoords\n"); } #endif scale[0] = idMath::Sqrt(texMat[0][0] * texMat[0][0] + texMat[1][0] * texMat[1][0]); scale[1] = idMath::Sqrt(texMat[0][1] * texMat[0][1] + texMat[1][1] * texMat[1][1]); #ifdef _DEBUG if (scale[0] < ZERO_EPSILON || scale[1] < ZERO_EPSILON) { common->Printf("Warning : unexpected scale==0 in TexMatToFakeTexCoords\n"); } #endif // compute rotate value if (idMath::Fabs(texMat[0][0]) < ZERO_EPSILON) { #ifdef _DEBUG // check brushprimit_texdef[1][0] is not zero if (idMath::Fabs(texMat[1][0]) < ZERO_EPSILON) { common->Printf("Warning : unexpected texdef[1][0]==0 in TexMatToFakeTexCoords\n"); } #endif // rotate is +-90 if (texMat[1][0] > 0) { *rot = 90.0f; } else { *rot = -90.0f; } } else { *rot = RAD2DEG(atan2(texMat[1][0], texMat[0][0])); } shift[0] = -texMat[0][2]; shift[1] = texMat[1][2]; } // // ======================================================================================================================= // compute back the texture matrix from fake shift scale rot the matrix returned must be understood as a qtexture_t // with width=2 height=2 ( the default one ) // ======================================================================================================================= // void FakeTexCoordsToTexMat(float shift[2], float rot, float scale[2], float texMat[2][3]) { texMat[0][0] = scale[0] * cos(DEG2RAD(rot)); texMat[1][0] = scale[0] * sin(DEG2RAD(rot)); texMat[0][1] = -1.0f * scale[1] * sin(DEG2RAD(rot)); texMat[1][1] = scale[1] * cos(DEG2RAD(rot)); texMat[0][2] = -shift[0]; texMat[1][2] = shift[1]; } // // ======================================================================================================================= // convert a texture matrix between two qtexture_t if NULL for qtexture_t, basic 2x2 texture is assumed ( straight // mapping between s/t coordinates and geometric coordinates ) // ======================================================================================================================= // void ConvertTexMatWithQTexture(float texMat1[2][3], const idMaterial *qtex1, float texMat2[2][3], const idMaterial *qtex2, float sScale = 1.0, float tScale = 1.0) { float s1, s2; s1 = (qtex1 ? static_cast(qtex1->GetEditorImage()->uploadWidth) : 2.0f) / (qtex2 ? static_cast(qtex2->GetEditorImage()->uploadWidth) : 2.0f); s2 = (qtex1 ? static_cast(qtex1->GetEditorImage()->uploadHeight) : 2.0f) / (qtex2 ? static_cast(qtex2->GetEditorImage()->uploadHeight) : 2.0f); s1 *= sScale; s2 *= tScale; texMat2[0][0] = s1 * texMat1[0][0]; texMat2[0][1] = s1 * texMat1[0][1]; texMat2[0][2] = s1 * texMat1[0][2]; texMat2[1][0] = s2 * texMat1[1][0]; texMat2[1][1] = s2 * texMat1[1][1]; texMat2[1][2] = s2 * texMat1[1][2]; } /* ======================================================================================================================= ======================================================================================================================= */ void ConvertTexMatWithQTexture(brushprimit_texdef_t *texMat1, const idMaterial *qtex1, brushprimit_texdef_t *texMat2, const idMaterial *qtex2, float sScale, float tScale) { ConvertTexMatWithQTexture(texMat1->coords, qtex1, texMat2->coords, qtex2, sScale, tScale); } // // ======================================================================================================================= // texture locking // ======================================================================================================================= // void Face_MoveTexture_BrushPrimit(face_t *f, idVec3 delta) { idVec3D texS, texT; double tx, ty; idVec3D M[3]; // columns of the matrix .. easier that way double det; idVec3D D[2]; // compute plane axis base ( doesn't change with translation ) ComputeAxisBase(f->plane.Normal(), texS, texT); // compute translation vector in plane axis base tx = DotProduct(delta, texS); ty = DotProduct(delta, texT); // fill the data vectors M[0][0] = tx; M[0][1] = 1.0f + tx; M[0][2] = tx; M[1][0] = ty; M[1][1] = ty; M[1][2] = 1.0f + ty; M[2][0] = 1.0f; M[2][1] = 1.0f; M[2][2] = 1.0f; D[0][0] = f->brushprimit_texdef.coords[0][2]; D[0][1] = f->brushprimit_texdef.coords[0][0] + f->brushprimit_texdef.coords[0][2]; D[0][2] = f->brushprimit_texdef.coords[0][1] + f->brushprimit_texdef.coords[0][2]; D[1][0] = f->brushprimit_texdef.coords[1][2]; D[1][1] = f->brushprimit_texdef.coords[1][0] + f->brushprimit_texdef.coords[1][2]; D[1][2] = f->brushprimit_texdef.coords[1][1] + f->brushprimit_texdef.coords[1][2]; // solve det = SarrusDet(M[0], M[1], M[2]); f->brushprimit_texdef.coords[0][0] = SarrusDet(D[0], M[1], M[2]) / det; f->brushprimit_texdef.coords[0][1] = SarrusDet(M[0], D[0], M[2]) / det; f->brushprimit_texdef.coords[0][2] = SarrusDet(M[0], M[1], D[0]) / det; f->brushprimit_texdef.coords[1][0] = SarrusDet(D[1], M[1], M[2]) / det; f->brushprimit_texdef.coords[1][1] = SarrusDet(M[0], D[1], M[2]) / det; f->brushprimit_texdef.coords[1][2] = SarrusDet(M[0], M[1], D[1]) / det; } // // ======================================================================================================================= // call Face_MoveTexture_BrushPrimit after idVec3D computation // ======================================================================================================================= // void Select_ShiftTexture_BrushPrimit(face_t *f, float x, float y, bool autoAdjust) { #if 0 idVec3D texS, texT; idVec3D delta; ComputeAxisBase(f->plane.normal, texS, texT); VectorScale(texS, x, texS); VectorScale(texT, y, texT); VectorCopy(texS, delta); VectorAdd(delta, texT, delta); Face_MoveTexture_BrushPrimit(f, delta); #else if (autoAdjust) { x /= f->d_texture->GetEditorImage()->uploadWidth; y /= f->d_texture->GetEditorImage()->uploadHeight; } f->brushprimit_texdef.coords[0][2] += x; f->brushprimit_texdef.coords[1][2] += y; EmitBrushPrimitTextureCoordinates(f, f->face_winding); #endif } // // ======================================================================================================================= // best fitted 2D vector is x.X+y.Y // ======================================================================================================================= // void ComputeBest2DVector(idVec3 v, idVec3 X, idVec3 Y, int &x, int &y) { double sx, sy; sx = DotProduct(v, X); sy = DotProduct(v, Y); if (idMath::Fabs(sy) > idMath::Fabs(sx)) { x = 0; if (sy > 0.0) { y = 1; } else { y = -1; } } else { y = 0; if (sx > 0.0) { x = 1; } else { x = -1; } } } // // ======================================================================================================================= // in many case we know three points A,B,C in two axis base B1 and B2 and we want the matrix M so that A(B1) = T * // A(B2) NOTE: 2D homogeneous space stuff NOTE: we don't do any check to see if there's a solution or we have a // particular case .. need to make sure before calling NOTE: the third coord of the A,B,C point is ignored NOTE: see // the commented out section to fill M and D ++timo TODO: update the other members to use this when possible // ======================================================================================================================= // void MatrixForPoints(idVec3D M[3], idVec3D D[2], brushprimit_texdef_t *T) { // // idVec3D M[3]; // columns of the matrix .. easier that way (the indexing is not // standard! it's column-line .. later computations are easier that way) // double det; // idVec3D D[2]; M[2][0] = 1.0f; M[2][1] = 1.0f; M[2][2] = 1.0f; #if 0 // fill the data vectors M[0][0] = A2[0]; M[0][1] = B2[0]; M[0][2] = C2[0]; M[1][0] = A2[1]; M[1][1] = B2[1]; M[1][2] = C2[1]; M[2][0] = 1.0f; M[2][1] = 1.0f; M[2][2] = 1.0f; D[0][0] = A1[0]; D[0][1] = B1[0]; D[0][2] = C1[0]; D[1][0] = A1[1]; D[1][1] = B1[1]; D[1][2] = C1[1]; #endif // solve det = SarrusDet(M[0], M[1], M[2]); T->coords[0][0] = SarrusDet(D[0], M[1], M[2]) / det; T->coords[0][1] = SarrusDet(M[0], D[0], M[2]) / det; T->coords[0][2] = SarrusDet(M[0], M[1], D[0]) / det; T->coords[1][0] = SarrusDet(D[1], M[1], M[2]) / det; T->coords[1][1] = SarrusDet(M[0], D[1], M[2]) / det; T->coords[1][2] = SarrusDet(M[0], M[1], D[1]) / det; } // // ======================================================================================================================= // ++timo FIXME quick'n dirty hack, doesn't care about current texture settings (angle) can be improved .. bug #107311 // mins and maxs are the face bounding box ++timo fixme: we use the face info, mins and maxs are irrelevant // ======================================================================================================================= // void Face_FitTexture_BrushPrimit(face_t *f, idVec3 mins, idVec3 maxs, float height, float width) { idVec3D BBoxSTMin, BBoxSTMax; idWinding *w; int i, j; double val; idVec3D M[3], D[2]; // idVec3D N[2],Mf[2]; brushprimit_texdef_t N; idVec3D Mf[2]; //memset(f->brushprimit_texdef.coords, 0, sizeof(f->brushprimit_texdef.coords)); //f->brushprimit_texdef.coords[0][0] = 1.0f; //f->brushprimit_texdef.coords[1][1] = 1.0f; //ConvertTexMatWithQTexture(&f->brushprimit_texdef, NULL, &f->brushprimit_texdef, f->d_texture); // // we'll be working on a standardized texture size ConvertTexMatWithQTexture( // &f->brushprimit_texdef, f->d_texture, &f->brushprimit_texdef, NULL ); compute // the BBox in ST coords // EmitBrushPrimitTextureCoordinates(f, f->face_winding); BBoxSTMin[0] = BBoxSTMin[1] = BBoxSTMin[2] = 999999; BBoxSTMax[0] = BBoxSTMax[1] = BBoxSTMax[2] = -999999; w = f->face_winding; if (w) { for (i = 0; i < w->GetNumPoints(); i++) { // AddPointToBounds in 2D on (S,T) coordinates for (j = 0; j < 2; j++) { val = (*w)[i][j + 3]; if (val < BBoxSTMin[j]) { BBoxSTMin[j] = val; } if (val > BBoxSTMax[j]) { BBoxSTMax[j] = val; } } } } // // we have the three points of the BBox (BBoxSTMin[0].BBoxSTMin[1]) // (BBoxSTMax[0],BBoxSTMin[1]) (BBoxSTMin[0],BBoxSTMax[1]) in ST space the BP // matrix we are looking for gives (0,0) (nwidth,0) (0,nHeight) coordinates in // (Sfit,Tfit) space to these three points we have A(Sfit,Tfit) = (0,0) = Mf * // A(TexS,TexT) = N * M * A(TexS,TexT) = N * A(S,T) so we solve the system for N // and then Mf = N * M // M[0][0] = BBoxSTMin[0]; M[0][1] = BBoxSTMax[0]; M[0][2] = BBoxSTMin[0]; M[1][0] = BBoxSTMin[1]; M[1][1] = BBoxSTMin[1]; M[1][2] = BBoxSTMax[1]; D[0][0] = 0.0f; D[0][1] = width; D[0][2] = 0.0f; D[1][0] = 0.0f; D[1][1] = 0.0f; D[1][2] = height; MatrixForPoints(M, D, &N); #if 0 // // FIT operation gives coordinates of three points of the bounding box in (S',T'), // our target axis base A(S',T')=(0,0) B(S',T')=(nWidth,0) C(S',T')=(0,nHeight) // and we have them in (S,T) axis base: A(S,T)=(BBoxSTMin[0],BBoxSTMin[1]) // B(S,T)=(BBoxSTMax[0],BBoxSTMin[1]) C(S,T)=(BBoxSTMin[0],BBoxSTMax[1]) we // compute the N transformation so that: A(S',T') = N * A(S,T) // N[0][0] = (BBoxSTMax[0] - BBoxSTMin[0]) / width; N[0][1] = 0.0f; N[0][2] = BBoxSTMin[0]; N[1][0] = 0.0f; N[1][1] = (BBoxSTMax[1] - BBoxSTMin[1]) / height; N[1][2] = BBoxSTMin[1]; #endif // the final matrix is the product (Mf stands for Mfit) Mf[0][0] = N.coords[0][0] * f->brushprimit_texdef.coords[0][0] + N.coords[0][1] * f->brushprimit_texdef.coords[1][0]; Mf[0][1] = N.coords[0][0] * f->brushprimit_texdef.coords[0][1] + N.coords[0][1] * f->brushprimit_texdef.coords[1][1]; Mf[0][2] = N.coords[0][0] * f->brushprimit_texdef.coords[0][2] + N.coords[0][1] * f->brushprimit_texdef.coords[1][2] + N.coords[0][2]; Mf[1][0] = N.coords[1][0] * f->brushprimit_texdef.coords[0][0] + N.coords[1][1] * f->brushprimit_texdef.coords[1][0]; Mf[1][1] = N.coords[1][0] * f->brushprimit_texdef.coords[0][1] + N.coords[1][1] * f->brushprimit_texdef.coords[1][1]; Mf[1][2] = N.coords[1][0] * f->brushprimit_texdef.coords[0][2] + N.coords[1][1] * f->brushprimit_texdef.coords[1][2] + N.coords[1][2]; // copy back VectorCopy(Mf[0], f->brushprimit_texdef.coords[0]); VectorCopy(Mf[1], f->brushprimit_texdef.coords[1]); // // handle the texture size ConvertTexMatWithQTexture( &f->brushprimit_texdef, // NULL, &f->brushprimit_texdef, f->d_texture ); // } /* ======================================================================================================================= ======================================================================================================================= */ void Face_ScaleTexture_BrushPrimit(face_t *face, float sS, float sT) { if (!g_qeglobals.m_bBrushPrimitMode) { Sys_Status("BP mode required\n"); return; } brushprimit_texdef_t *pBP = &face->brushprimit_texdef; BPMatScale(pBP->coords, sS, sT); // now emit the coordinates on the winding EmitBrushPrimitTextureCoordinates(face, face->face_winding); } /* ======================================================================================================================= ======================================================================================================================= */ void Face_RotateTexture_BrushPrimit(face_t *face, float amount, idVec3 origin) { brushprimit_texdef_t *pBP = &face->brushprimit_texdef; if (amount) { float x = pBP->coords[0][0]; float y = pBP->coords[0][1]; float x1 = pBP->coords[1][0]; float y1 = pBP->coords[1][1]; float s = sin( DEG2RAD( amount ) ); float c = cos( DEG2RAD( amount ) ); pBP->coords[0][0] = (((x - origin[0]) * c) - ((y - origin[1]) * s)) + origin[0]; pBP->coords[0][1] = (((x - origin[0]) * s) + ((y - origin[1]) * c)) + origin[1]; pBP->coords[1][0] = (((x1 - origin[0]) * c) - ((y1 - origin[1]) * s)) + origin[0]; pBP->coords[1][1] = (((x1 - origin[0]) * s) + ((y1 - origin[1]) * c)) + origin[1]; EmitBrushPrimitTextureCoordinates(face, face->face_winding); } } // // TEXTURE LOCKING (Relevant to the editor only?) // internally used for texture locking on rotation and flipping the general // algorithm is the same for both lockings, it's only the geometric transformation // part that changes so I wanted to keep it in a single function if there are more // linear transformations that need the locking, going to a C++ or code pointer // solution would be best (but right now I want to keep brush_primit.cpp striclty // C) // bool txlock_bRotation; // rotation locking params int txl_nAxis; double txl_fDeg; idVec3D txl_vOrigin; // flip locking params idVec3D txl_matrix[3]; idVec3D txl_origin; /* ======================================================================================================================= ======================================================================================================================= */ void TextureLockTransformation_BrushPrimit(face_t *f) { idVec3D Orig, texS, texT; // axis base of initial plane // used by transformation algo idVec3D temp; int j; //idVec3D vRotate; // rotation vector idVec3D rOrig, rvecS, rvecT; // geometric transformation of (0,0) (1,0) (0,1) { initial plane axis base } idVec3 rNormal; idVec3D rtexS, rtexT; // axis base for the transformed plane idVec3D lOrig, lvecS, lvecT; // [2] are not used ( but usefull for debugging ) idVec3D M[3]; double det; idVec3D D[2]; // silence compiler warnings rOrig.Zero(); rvecS = rOrig; rvecT = rOrig; rNormal.x = rOrig.x; rNormal.y = rOrig.y; rNormal.z = rOrig.z; // compute plane axis base ComputeAxisBase(f->plane.Normal(), texS, texT); Orig.x = vec3_origin.x; Orig.y = vec3_origin.y; Orig.z = vec3_origin.z; // // compute coordinates of (0,0) (1,0) (0,1) ( expressed in initial plane axis base // ) after transformation (0,0) (1,0) (0,1) ( expressed in initial plane axis base // ) <-> (0,0,0) texS texT ( expressed world axis base ) input: Orig, texS, texT // (and the global locking params) ouput: rOrig, rvecS, rvecT, rNormal // if (txlock_bRotation) { /* // rotation vector vRotate.x = vec3_origin.x; vRotate.y = vec3_origin.y; vRotate.z = vec3_origin.z; vRotate[txl_nAxis] = txl_fDeg; VectorRotate3Origin(Orig, vRotate, txl_vOrigin, rOrig); VectorRotate3Origin(texS, vRotate, txl_vOrigin, rvecS); VectorRotate3Origin(texT, vRotate, txl_vOrigin, rvecT); // compute normal of plane after rotation VectorRotate3(f->plane.Normal(), vRotate, rNormal); */ } else { VectorSubtract(Orig, txl_origin, temp); for (j = 0; j < 3; j++) { rOrig[j] = DotProduct(temp, txl_matrix[j]) + txl_origin[j]; } VectorSubtract(texS, txl_origin, temp); for (j = 0; j < 3; j++) { rvecS[j] = DotProduct(temp, txl_matrix[j]) + txl_origin[j]; } VectorSubtract(texT, txl_origin, temp); for (j = 0; j < 3; j++) { rvecT[j] = DotProduct(temp, txl_matrix[j]) + txl_origin[j]; } // // we also need the axis base of the target plane, apply the transformation matrix // to the normal too.. // for (j = 0; j < 3; j++) { rNormal[j] = DotProduct(f->plane, txl_matrix[j]); } } // compute rotated plane axis base ComputeAxisBase(rNormal, rtexS, rtexT); // compute S/T coordinates of the three points in rotated axis base ( in M matrix ) lOrig[0] = DotProduct(rOrig, rtexS); lOrig[1] = DotProduct(rOrig, rtexT); lvecS[0] = DotProduct(rvecS, rtexS); lvecS[1] = DotProduct(rvecS, rtexT); lvecT[0] = DotProduct(rvecT, rtexS); lvecT[1] = DotProduct(rvecT, rtexT); M[0][0] = lOrig[0]; M[1][0] = lOrig[1]; M[2][0] = 1.0f; M[0][1] = lvecS[0]; M[1][1] = lvecS[1]; M[2][1] = 1.0f; M[0][2] = lvecT[0]; M[1][2] = lvecT[1]; M[2][2] = 1.0f; // fill data vector D[0][0] = f->brushprimit_texdef.coords[0][2]; D[0][1] = f->brushprimit_texdef.coords[0][0] + f->brushprimit_texdef.coords[0][2]; D[0][2] = f->brushprimit_texdef.coords[0][1] + f->brushprimit_texdef.coords[0][2]; D[1][0] = f->brushprimit_texdef.coords[1][2]; D[1][1] = f->brushprimit_texdef.coords[1][0] + f->brushprimit_texdef.coords[1][2]; D[1][2] = f->brushprimit_texdef.coords[1][1] + f->brushprimit_texdef.coords[1][2]; // solve det = SarrusDet(M[0], M[1], M[2]); f->brushprimit_texdef.coords[0][0] = SarrusDet(D[0], M[1], M[2]) / det; f->brushprimit_texdef.coords[0][1] = SarrusDet(M[0], D[0], M[2]) / det; f->brushprimit_texdef.coords[0][2] = SarrusDet(M[0], M[1], D[0]) / det; f->brushprimit_texdef.coords[1][0] = SarrusDet(D[1], M[1], M[2]) / det; f->brushprimit_texdef.coords[1][1] = SarrusDet(M[0], D[1], M[2]) / det; f->brushprimit_texdef.coords[1][2] = SarrusDet(M[0], M[1], D[1]) / det; } // // ======================================================================================================================= // texture locking called before the points on the face are actually rotated // ======================================================================================================================= // void RotateFaceTexture_BrushPrimit(face_t *f, int nAxis, float fDeg, idVec3 vOrigin) { // this is a placeholder to call the general texture locking algorithm txlock_bRotation = true; txl_nAxis = nAxis; txl_fDeg = fDeg; VectorCopy(vOrigin, txl_vOrigin); TextureLockTransformation_BrushPrimit(f); } // // ======================================================================================================================= // compute the new brush primit texture matrix for a transformation matrix and a flip order flag (change plane o // rientation) this matches the select_matrix algo used in select.cpp this needs to be called on the face BEFORE any // geometric transformation it will compute the texture matrix that will represent the same texture on the face after // the geometric transformation is done // ======================================================================================================================= // void ApplyMatrix_BrushPrimit(face_t *f, idMat3 matrix, idVec3 origin) { // this is a placeholder to call the general texture locking algorithm txlock_bRotation = false; VectorCopy(matrix[0], txl_matrix[0]); VectorCopy(matrix[1], txl_matrix[1]); VectorCopy(matrix[2], txl_matrix[2]); VectorCopy(origin, txl_origin); TextureLockTransformation_BrushPrimit(f); } // // ======================================================================================================================= // don't do C==A! // ======================================================================================================================= // void BPMatMul(float A[2][3], float B[2][3], float C[2][3]) { C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0]; C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0]; C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1]; C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1]; C[0][2] = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2]; C[1][2] = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2]; } /* ======================================================================================================================= ======================================================================================================================= */ void BPMatDump(float A[2][3]) { common->Printf("%g %g %g\n%g %g %g\n0 0 1\n", A[0][0], A[0][1], A[0][2], A[1][0], A[1][1], A[1][2]); } /* ======================================================================================================================= ======================================================================================================================= */ void BPMatRotate(float A[2][3], float theta) { float m[2][3]; float aux[2][3]; memset(&m, 0, sizeof (float) *6); m[0][0] = cos( DEG2RAD( theta ) ); m[0][1] = -sin( DEG2RAD( theta ) ); m[1][0] = -m[0][1]; m[1][1] = m[0][0]; BPMatMul(A, m, aux); BPMatCopy(aux, A); } void Face_GetScale_BrushPrimit(face_t *face, float *s, float *t, float *rot) { idVec3D texS, texT; ComputeAxisBase(face->plane.Normal(), texS, texT); if (face == NULL || face->face_winding == NULL) { return; } // find ST coordinates for the center of the face double Os = 0, Ot = 0; for (int i = 0; i < face->face_winding->GetNumPoints(); i++) { Os += DotProduct((*face->face_winding)[i], texS); Ot += DotProduct((*face->face_winding)[i], texT); } Os /= face->face_winding->GetNumPoints(); Ot /= face->face_winding->GetNumPoints(); brushprimit_texdef_t *pBP = &face->brushprimit_texdef; // here we have a special case, M is a translation and it's inverse is easy float BPO[2][3]; float aux[2][3]; float m[2][3]; memset(&m, 0, sizeof (float) *6); m[0][0] = 1; m[1][1] = 1; m[0][2] = -Os; m[1][2] = -Ot; BPMatMul(m, pBP->coords, aux); m[0][2] = Os; m[1][2] = Ot; // now M^-1 BPMatMul(aux, m, BPO); // apply a given scale (on S and T) ConvertTexMatWithQTexture(BPO, face->d_texture, aux, NULL); *s = idMath::Sqrt(aux[0][0] * aux[0][0] + aux[1][0] * aux[1][0]); *t = idMath::Sqrt(aux[0][1] * aux[0][1] + aux[1][1] * aux[1][1]); // compute rotate value if (idMath::Fabs(face->brushprimit_texdef.coords[0][0]) < ZERO_EPSILON) { // rotate is +-90 if (face->brushprimit_texdef.coords[1][0] > 0) { *rot = 90.0f; } else { *rot = -90.0f; } } else { *rot = RAD2DEG(atan2(face->brushprimit_texdef.coords[1][0] / (*s) ? (*s) : 1.0f, face->brushprimit_texdef.coords[0][0] / (*t) ? (*t) : 1.0f)); } } /* ======================================================================================================================= ======================================================================================================================= */ void Face_SetExplicitScale_BrushPrimit(face_t *face, float s, float t) { idVec3D texS, texT; ComputeAxisBase(face->plane.Normal(), texS, texT); // find ST coordinates for the center of the face double Os = 0, Ot = 0; for (int i = 0; i < face->face_winding->GetNumPoints(); i++) { Os += DotProduct((*face->face_winding)[i], texS); Ot += DotProduct((*face->face_winding)[i], texT); } Os /= face->face_winding->GetNumPoints(); Ot /= face->face_winding->GetNumPoints(); brushprimit_texdef_t *pBP = &face->brushprimit_texdef; // here we have a special case, M is a translation and it's inverse is easy float BPO[2][3]; float aux[2][3]; float m[2][3]; memset(&m, 0, sizeof (float) *6); m[0][0] = 1; m[1][1] = 1; m[0][2] = -Os; m[1][2] = -Ot; BPMatMul(m, pBP->coords, aux); m[0][2] = Os; m[1][2] = Ot; // now M^-1 BPMatMul(aux, m, BPO); // apply a given scale (on S and T) ConvertTexMatWithQTexture(BPO, face->d_texture, aux, NULL); // reset the scale (normalize the matrix) double v1, v2; v1 = idMath::Sqrt(aux[0][0] * aux[0][0] + aux[1][0] * aux[1][0]); v2 = idMath::Sqrt(aux[0][1] * aux[0][1] + aux[1][1] * aux[1][1]); if (s == 0.0) { s = v1; } if (t == 0.0) { t = v2; } double sS, sT; // put the values for scale on S and T here: sS = s / v1; sT = t / v2; aux[0][0] *= sS; aux[1][0] *= sS; aux[0][1] *= sT; aux[1][1] *= sT; ConvertTexMatWithQTexture(aux, NULL, BPO, face->d_texture); BPMatMul(m, BPO, aux); // m is M^-1 m[0][2] = -Os; m[1][2] = -Ot; BPMatMul(aux, m, pBP->coords); // now emit the coordinates on the winding EmitBrushPrimitTextureCoordinates(face, face->face_winding); } void Face_FlipTexture_BrushPrimit(face_t *f, bool y) { float s, t, rot; Face_GetScale_BrushPrimit(f, &s, &t, &rot); if (y) { Face_SetExplicitScale_BrushPrimit(f, 0.0, -t); } else { Face_SetExplicitScale_BrushPrimit(f, -s, 0.0); } #if 0 idVec3D texS, texT; ComputeAxisBase(f->plane.normal, texS, texT); double Os = 0, Ot = 0; for (int i = 0; i < f->face_winding->numpoints; i++) { Os += DotProduct(f->face_winding->p[i], texS); Ot += DotProduct(f->face_winding->p[i], texT); } Ot = abs(Ot); Ot *= t; Ot /= f->d_texture->GetEditorImage()->uploadHeight; Os = abs(Os); Os *= s; Os /= f->d_texture->GetEditorImage()->uploadWidth; if (y) { Face_FitTexture_BrushPrimit(f, texS, texT, -Ot, 1.0); } else { Face_FitTexture_BrushPrimit(f, texS, texT, 1.0, -Os); } EmitBrushPrimitTextureCoordinates(f, f->face_winding); #endif } void Brush_FlipTexture_BrushPrimit(brush_t *b, bool y) { for (face_t *f = b->brush_faces; f; f = f->next) { Face_FlipTexture_BrushPrimit(f, y); } } void Face_SetAxialScale_BrushPrimit(face_t *face, bool y) { if (!face) { return; } if (!face->face_winding) { return; } //float oldS, oldT, oldR; //Face_GetScale_BrushPrimit(face, &oldS, &oldT, &oldR); idVec3D min, max; min.x = min.y = min.z = 999999.0; max.x = max.y = max.z = -999999.0; for (int i = 0; i < face->face_winding->GetNumPoints(); i++) { for (int j = 0; j < 3; j++) { if ((*face->face_winding)[i][j] < min[j]) { min[j] = (*face->face_winding)[i][j]; } if ((*face->face_winding)[i][j] > max[j]) { max[j] = (*face->face_winding)[i][j]; } } } idVec3 len; if (g_bAxialMode) { if (g_axialAnchor >= 0 && g_axialAnchor < face->face_winding->GetNumPoints() && g_axialDest >= 0 && g_axialDest < face->face_winding->GetNumPoints() && g_axialAnchor != g_axialDest) { len = (*face->face_winding)[g_axialDest].ToVec3() - (*face->face_winding)[g_axialAnchor].ToVec3(); } else { return; } } else { if (y) { len = (*face->face_winding)[2].ToVec3() - (*face->face_winding)[1].ToVec3(); } else { len = (*face->face_winding)[1].ToVec3() - (*face->face_winding)[0].ToVec3(); } } double dist = len.Length(); double width = idMath::Fabs(max.x - min.x); double height = idMath::Fabs(max.z - min.z); //len = maxs[2] - mins[2]; //double yDist = len.Length(); if (dist != 0.0) { if (dist > face->d_texture->GetEditorImage()->uploadHeight) { height = 1.0 / (dist / face->d_texture->GetEditorImage()->uploadHeight); } else { height /= dist; } if (dist > face->d_texture->GetEditorImage()->uploadWidth) { width = 1.0 / (dist / face->d_texture->GetEditorImage()->uploadWidth); } else { width /= dist; } } if (y) { Face_SetExplicitScale_BrushPrimit(face, 0.0, height); //oldT = oldT / height * 10; //Select_ShiftTexture_BrushPrimit(face, 0, -oldT, true); } else { Face_SetExplicitScale_BrushPrimit(face, width, 0.0); } /* common->Printf("Face x: %f y: %f xr: %f yr: %f\n", x, y, xRatio, yRatio); common->Printf("Texture x: %i y: %i \n",face->d_texture->GetEditorImage()->uploadWidth, face->d_texture->GetEditorImage()->uploadHeight); idVec3D texS, texT; ComputeAxisBase(face->plane.normal, texS, texT); float Os = 0, Ot = 0; for (int i = 0; i < face->face_winding->numpoints; i++) { Os += DotProduct(face->face_winding->p[i], texS); Ot += DotProduct(face->face_winding->p[i], texT); } common->Printf("Face2 x: %f y: %f \n", Os, Ot); Os /= face->face_winding->numpoints; Ot /= face->face_winding->numpoints; //Os /= face->face_winding->numpoints; //Ot /= face->face_winding->numpoints; */ }