/* * Descent 3 * Copyright (C) 2024 Parallax Software * * 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 . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/editor/HTexture.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003-08-26 03:57:38 $ * $Author: kevinb $ * * HTexture implementation * * $Log: not supported by cvs2svn $ * * 37 12/27/99 8:26a Gwar * * 36 8/30/99 12:55p Gwar * added a parameter to HTexturePropagateToFace to not change the texture * of the target face * * 35 7/30/99 1:19p Gwar * don't set World_changed in NEWEDITOR * * 34 7/24/99 5:25a Gwar * NEWEDITOR changes to HTextureApplyToRoomFace * * 33 7/04/99 4:56p Gwar * changes for texture management in NEWEDITOR * * 32 5/19/99 12:19a Gwar * #define Curroomp, Curface, etc as members of prim struct in NEWEDITOR * -- this allows the main room frame and level window to share these * variables and makes them non-global (i.e. no more sync problem between * world and room views) * * 31 5/03/99 2:06a Gwar * added HTexture.cpp to project * * 30 1/20/99 10:50a Jason * added new terrain * * 29 10/21/98 1:27p Jason * fixed texture u2,v2 sliding problems * * 28 8/16/98 4:24p Matt * When copying UVs from one face to another, only copy u & v, and not * other fields in the uv structure. * * 27 8/15/98 5:04p Matt * Added code to copy a texture & UVs from one face to another * * 26 6/15/98 4:00p Jason * replaced monochromatic polymodel lighting with rgb lighting * * 25 4/16/98 10:42a Matt * Made texture propagation work across rooms. * * 24 4/02/98 12:23p Jason * trimmed some fat from our structures * * 23 9/23/97 12:20p Jason * fixed bug where rotating a texture would cause alphas and u2,v2s to * become corrupt * * 22 9/17/97 1:36p Samir * Fixed some new prototypes. * * 21 9/17/97 11:57a Matt * Ripped out segment code * * 19 8/21/97 6:01p Matt * Use groovy new FindSharedEdge() function * * 18 8/13/97 9:52a Matt * Combined slide left/right/up/down into one function, and the same with * rotate left/right * * 17 8/07/97 5:06p Jason * added new tiling system for terrain * * 16 8/07/97 3:19p Jason * replaced old terrain tmap system with new one * * 15 8/01/97 4:53p Jason * made a more general PropagateTextureToFace function call * * 14 7/22/97 7:08p Matt * Cleaned up D3EditState, moving some vars in and some out, and renaming * and changing a few others * * 13 7/18/97 7:38p Jason * finished room/terrain texture modifications * * 12 7/16/97 6:31p Jason * changed defaut texture uvs for terrain * * 15 6/24/97 12:41p Jason * checked in for safety * * 14 6/18/97 11:14a Mark * FROM JASON:Fixed dumb terrain rotational bug * * 13 6/17/97 4:16p Jason * added some terrain features * * 12 6/17/97 11:35a Jason * checked in for safety * * 11 6/06/97 4:57p Jason * more nifty megacell/tmap2 features * * 10 6/04/97 1:32p Matt * Changed Side_to_verts[] and Side_opposite[] to be arrays of ints, which * should be faster than bytes. * * 9 5/30/97 3:32p Samir * When rotating textures, lighting preserved * * 8 5/02/97 7:21p Matt * Added code to rotate tmap2's * * 7 4/17/97 4:03p Samir * Apply Tmap 1 and Tmap2 implemented, but no Tmap2 rendering is occurring * yet. * * 6 3/31/97 5:58p Matt * Revamped mine update flags * * 5 3/31/97 3:50p Samir * Set default texture uvs should work for side not whole segment. * * 4 2/26/97 6:40p Samir * changed g3s_uvl to g3UVL * * 3 2/26/97 3:59p Samir * Most scaling, rotating, sliding and flipping work. * * 2 2/20/97 11:59a Samir * Fixed bugs * * 1 2/20/97 11:50a Samir * * $NoKeywords: $ */ #include "HTexture.h" #ifndef NEWEDITOR #include "d3edit.h" #endif #include "roomuvs.h" #include "gametexture.h" #include "terrain.h" #include "room.h" #include "descent.h" #include "erooms.h" #include "pserror.h" #define HTEX_CALIB_VAL ((float)(1.0 / 128.0)) // --------------------------------------------------------------------------- // internal function prototypes // --------------------------------------------------------------------------- void HTextureRoomStretch(room *rp, int facenum, int edge, int direction); void HTextureRotUVPoint(g3UVL *uvrot, float *rotmat, roomUVL *uv, g3UVL *uvcenter); void HTextureCreate2DRotMat(float *rotmat, angle ang); void HTextureRotUVTerrainPoint(g3UVL *uvrot, float *rotmat, g3UVL *uv, g3UVL *uvcenter); void GetUVLForRoomPoint(int roomnum, int facenum, int vertnum, roomUVL *uvl); int HTextureGetTopVertexForFace(int roomnum, int facenum); int HTextureGetBottomVertexForFace(int roomnum, int facenum); int HTextureGetLeftVertexForFace(int roomnum, int facenum); int HTextureGetRightVertexForFace(int roomnum, int facenum); void HTextureRotUVPointsOnRoomFace(room *roomp, int facenum, float *rotmat, g3UVL *uvcenter); void HTextureComputeUVRoomFaceCenter(g3UVL *uvcenter, room *roomp, int facenum); void HTextureTerrainStretch(int edge, int direction); // --------------------------------------------------------------------------- // Functions to manipulate textures within the mine, through segments // --------------------------------------------------------------------------- // Apply the specified texture to the specified room:face void HTextureApplyToRoomFace(room *rp, int facenum, int tnum) { ASSERT(rp->used); #ifdef NEWEDITOR if (tnum == -1) return; if (rp->faces[facenum].tmap >= 0) SwitchTexture(rp, rp->faces[facenum].tmap, tnum); #endif rp->faces[facenum].tmap = tnum; } // Copy texture from current face to adjacent face, tiling the UV coordinates // Parameters: destrp,destface - room:face that the propagate is based on // srcrp,srcface - room:face that is changed // Return: 1 if success, 0 if faces not adjacent int HTexturePropagateToFace(room *destrp, int destface, room *srcrp, int srcface, bool tex) { face *dfp = &destrp->faces[destface]; face *sfp = &srcrp->faces[srcface]; int v0, v1; // Get shared edge if (destrp != srcrp) { // Faces in different rooms if (!FindSharedEdgeAcrossRooms(destrp, destface, srcrp, srcface, &v0, &v1)) return 0; } else { // Faces in same room if (!FindSharedEdge(dfp, sfp, &v0, &v1)) return 0; } if (tex) { #ifdef NEWEDITOR SwitchTexture(destrp, dfp->tmap, sfp->tmap); #endif // Copy texture from Curface dfp->tmap = sfp->tmap; } // Assign uvs from shared edge to rest of verts in face AssignUVsToFace(destrp, destface, &sfp->face_uvls[(v1 + 1) % sfp->num_verts], &sfp->face_uvls[v1], v0, (v0 + 1) % dfp->num_verts); // Success return 1; } // Copy texture UVs from one face to another // Parameters: destrp,destface - room:face that is changed // srcrp,srcface - room:face to copy from // offset - vert 0 on source is assigned to vert offset on dest // Return: 1 if success, 0 if faces don't have the same number of verts int HTextureCopyUVsToFace(room *destrp, int destface, room *srcrp, int srcface, int offset) { face *dfp = &destrp->faces[destface]; face *sfp = &srcrp->faces[srcface]; // Make sure faces have the same number of vertices if (dfp->num_verts != sfp->num_verts) return 0; // Copy UV values for (int i = 0; i < dfp->num_verts; i++) { dfp->face_uvls[(i + offset) % dfp->num_verts].u = sfp->face_uvls[i].u; dfp->face_uvls[(i + offset) % dfp->num_verts].v = sfp->face_uvls[i].v; } // Success! #ifndef NEWEDITOR World_changed = 1; #endif return 1; } // texture flipping void HTextureFlipX() { if (Editor_view_mode == VM_TERRAIN) { // terrain_segment oldseg; for (int i = 0; i < TERRAIN_DEPTH * TERRAIN_WIDTH; i++) { if (TerrainSelected[i]) { /*oldseg=Terrain_seg[i]; Terrain_seg[i].u[0]=oldseg.u[1]; Terrain_seg[i].u[1]=oldseg.u[0]; Terrain_seg[i].u[2]=oldseg.u[3]; Terrain_seg[i].u[3]=oldseg.u[2]; Terrain_seg[i].v[0]=oldseg.v[1]; Terrain_seg[i].v[1]=oldseg.v[0]; Terrain_seg[i].v[2]=oldseg.v[3]; Terrain_seg[i].v[3]=oldseg.v[2];*/ } } } else { ASSERT(Curroomp->used); for (int i = 0; i < Curroomp->faces[Curface].num_verts; i++) Curroomp->faces[Curface].face_uvls[i].u = 1 - Curroomp->faces[Curface].face_uvls[i].u; } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureFlipY() { if (Editor_view_mode == VM_TERRAIN) { terrain_segment oldseg; for (int i = 0; i < TERRAIN_DEPTH * TERRAIN_WIDTH; i++) { if (TerrainSelected[i]) { oldseg = Terrain_seg[i]; /*Terrain_seg[i].v[0]=oldseg.v[3]; Terrain_seg[i].v[1]=oldseg.v[2]; Terrain_seg[i].v[2]=oldseg.v[1]; Terrain_seg[i].v[3]=oldseg.v[0]; Terrain_seg[i].u[0]=oldseg.u[3]; Terrain_seg[i].u[1]=oldseg.u[2]; Terrain_seg[i].u[2]=oldseg.u[1]; Terrain_seg[i].u[3]=oldseg.u[0];*/ } } } else { ASSERT(Curroomp->used); for (int i = 0; i < Curroomp->faces[Curface].num_verts; i++) Curroomp->faces[Curface].face_uvls[i].v = 1 - Curroomp->faces[Curface].face_uvls[i].v; } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureSlide(room *rp, int facenum, float right, float up) { ASSERT(rp != NULL); ASSERT(rp->used); if (Editor_view_mode == VM_TERRAIN) { /*int i; for (i=0;ifaces[facenum].num_verts; i++) { rp->faces[facenum].face_uvls[i].u -= right / 128.0f; rp->faces[facenum].face_uvls[i].v += up / 128.0f; } } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureRotate(room *roomp, int facenum, angle ang) { g3UVL uvcenter; float rotmat[4]; HTextureCreate2DRotMat(rotmat, ang); if (Editor_view_mode == VM_TERRAIN && ang == 0x4000) { for (int i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) { if (TerrainSelected[i]) { /*int f=(Terrain_seg[i].rotation_factor&0x0F); f++; f%=4; Terrain_seg[i].rotation_factor&=0xF0; Terrain_seg[i].rotation_factor|=(f);*/ } } return; } else { HTextureComputeUVRoomFaceCenter(&uvcenter, roomp, facenum); HTextureRotUVPointsOnRoomFace(roomp, facenum, rotmat, &uvcenter); } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureStretchMore(room *rp, int face, int edge) { if (Editor_view_mode == VM_TERRAIN) { HTextureTerrainStretch(edge, 1 * D3EditState.texscale); } else { HTextureRoomStretch(rp, face, edge, 1 * D3EditState.texscale); } } void HTextureStretchLess(room *rp, int face, int edge) { if (Editor_view_mode == VM_TERRAIN) { HTextureTerrainStretch(edge, (-1 * D3EditState.texscale)); } else { HTextureRoomStretch(rp, face, edge, (-1 * D3EditState.texscale)); } } void HTextureSetDefault(room *rp, int face) { if (Editor_view_mode == VM_TERRAIN) { for (int i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) { if (TerrainSelected[i]) { int index = Terrain_seg[i].texseg_index; Terrain_tex_seg[index].rotation = 0; } } World_changed = 1; } else if (Editor_view_mode == VM_ROOM) { //@@Stretch_scale_x = (float)1.0; //@@Stretch_scale_y = (float)1.0; for (int i = 0; i < rp->faces[face].num_verts; i++) { float saveu2 = rp->faces[face].face_uvls[i].u2; float savev2 = rp->faces[face].face_uvls[i].v2; GetUVLForRoomPoint(ROOMNUM(rp), face, i, &rp->faces[face].face_uvls[i]); rp->faces[face].face_uvls[i].u2 = saveu2; rp->faces[face].face_uvls[i].v2 = savev2; } } else { //@@Stretch_scale_x = (float)1.0; //@@Stretch_scale_y = (float)1.0; for (int i = 0; i < rp->faces[face].num_verts; i++) { float saveu2 = rp->faces[face].face_uvls[i].u2; float savev2 = rp->faces[face].face_uvls[i].v2; GetUVLForRoomPoint(ROOMNUM(rp), face, i, &rp->faces[face].face_uvls[i]); rp->faces[face].face_uvls[i].u2 = saveu2; rp->faces[face].face_uvls[i].v2 = savev2; } } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureNextEdge() { Curedge++; if (Editor_view_mode == VM_TERRAIN) { if (Curedge >= 4) Curedge = 0; } else // mine { if (Curedge >= Curroomp->faces[Curface].num_verts) Curedge = 0; } State_changed = 1; } // --------------------------------------------------------------------------- // functions used by HTexture operations // --------------------------------------------------------------------------- // direction = -1 or 1 depending on direction // Stretches all vertices towards or away from a given edge // Depending on the geometry of the face, this can cause distortions void HTextureRoomStretch(room *rp, int facenum, int edge, int direction) { int next_edge = (edge + 1) % rp->faces[facenum].num_verts; // find deltas float du = rp->faces[facenum].face_uvls[next_edge].u - rp->faces[facenum].face_uvls[edge].u; float dv = rp->faces[facenum].face_uvls[next_edge].v - rp->faces[facenum].face_uvls[edge].v; // rotate delta's 90 degrees float nu = -dv; float nv = du; // Find a magnitude to shift by float mag = sqrt((nv * nv) + (nu * nu)); nv /= mag; nu /= mag; nv /= 64.0; nu /= 64.0; // Apply to all verts except for our edge for (int i = 0; i < rp->faces[facenum].num_verts; i++) { if (i == edge || i == next_edge) continue; rp->faces[facenum].face_uvls[i].v -= (nv * direction); rp->faces[facenum].face_uvls[i].u -= (nu * direction); } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureTerrainStretch(int edge, int direction) { for (int i = 0; i < TERRAIN_WIDTH * TERRAIN_DEPTH; i++) { if (TerrainSelected[i]) { } } #ifndef NEWEDITOR World_changed = 1; #endif } void HTextureComputeUVRoomFaceCenter(g3UVL *uvcenter, room *roomp, int facenum) { int i; face *facep = &roomp->faces[facenum]; uvcenter->u = 0; uvcenter->v = 0; for (i = 0; i < facep->num_verts; i++) { uvcenter->u += facep->face_uvls[i].u; uvcenter->v += facep->face_uvls[i].v; } uvcenter->u /= i; uvcenter->v /= i; } void HTextureRotUVPoint(g3UVL *uvrot, float *rotmat, roomUVL *uv, g3UVL *uvcenter) { uvrot->u = ((uv->u - uvcenter->u) * rotmat[0]) + ((uv->v - uvcenter->v) * rotmat[1]) + uvcenter->u; uvrot->v = ((uv->u - uvcenter->u) * rotmat[2]) + ((uv->v - uvcenter->v) * rotmat[3]) + uvcenter->v; } void HTextureRotUVPointsOnRoomFace(room *roomp, int facenum, float *rotmat, g3UVL *uvcenter) { int v; face *facep = &roomp->faces[facenum]; g3UVL tuv; for (v = 0; v < facep->num_verts; v++) { HTextureRotUVPoint(&tuv, rotmat, &facep->face_uvls[v], uvcenter); facep->face_uvls[v].u = tuv.u; facep->face_uvls[v].v = tuv.v; } } void HTextureCreate2DRotMat(float *rotmat, angle ang) { float sinang, cosang; sinang = FixSin(ang); cosang = FixCos(ang); rotmat[0] = cosang; rotmat[1] = sinang; rotmat[2] = -sinang; rotmat[3] = cosang; } void HTextureRotUVTerrainPoint(g3UVL *uvrot, float *rotmat, g3UVL *uv, g3UVL *uvcenter) { uvrot->u = ((uv->u - uvcenter->u) * rotmat[0]) + ((uv->v - uvcenter->v) * rotmat[1]) + uvcenter->u; uvrot->v = ((uv->u - uvcenter->u) * rotmat[2]) + ((uv->v - uvcenter->v) * rotmat[3]) + uvcenter->v; } // returns the index in the face_verts array for the leftmost point on this face int HTextureGetLeftVertexForFace(int roomnum, int facenum) { matrix face_matrix, trans_matrix; vector fvec; vector avg_vert; vector verts[MAX_VERTS_PER_FACE]; ASSERT(Rooms[roomnum].used); ASSERT(Rooms[roomnum].faces[facenum].num_verts >= 3); // find the center point of this face vm_MakeZero(&avg_vert); int i; for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) avg_vert += Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; avg_vert /= i; // Make the orientation matrix fvec = Rooms[roomnum].faces[facenum].normal; vm_VectorToMatrix(&face_matrix, &fvec, NULL, NULL); // Make the transformation matrix angvec avec; vm_ExtractAnglesFromMatrix(&avec, &face_matrix); vm_AnglesToMatrix(&trans_matrix, avec.p, avec.h, avec.b); // Rotate all the points for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { vector vert = Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; vector rot_vert; vert -= avg_vert; vm_MatrixMulVector(&rot_vert, &vert, &trans_matrix); verts[i] = rot_vert; } // Find left most point int leftmost_point = -1; float leftmost_x = 900000.00f; // a big number for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { if (verts[i].x < leftmost_x) { leftmost_point = i; leftmost_x = verts[i].x; } } ASSERT(leftmost_point != -1); return leftmost_point; } // returns the index in the face_verts array for the topmost point on this face int HTextureGetTopVertexForFace(int roomnum, int facenum) { matrix face_matrix, trans_matrix; vector fvec; vector avg_vert; vector verts[MAX_VERTS_PER_FACE]; ASSERT(Rooms[roomnum].used); ASSERT(Rooms[roomnum].faces[facenum].num_verts >= 3); // find the center point of this face vm_MakeZero(&avg_vert); int i; for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) avg_vert += Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; avg_vert /= i; // Make the orientation matrix fvec = Rooms[roomnum].faces[facenum].normal; vm_VectorToMatrix(&face_matrix, &fvec, NULL, NULL); // Make the transformation matrix angvec avec; vm_ExtractAnglesFromMatrix(&avec, &face_matrix); vm_AnglesToMatrix(&trans_matrix, avec.p, avec.h, avec.b); // Rotate all the points for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { vector vert = Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; vector rot_vert; vert -= avg_vert; vm_MatrixMulVector(&rot_vert, &vert, &trans_matrix); verts[i] = rot_vert; } // Find top most point int topmost_point = -1; float topmost_y = -900000.0f; // a big number for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { if (verts[i].y > topmost_y) { topmost_point = i; topmost_y = verts[i].y; } } ASSERT(topmost_point != -1); return topmost_point; } // returns the index in the face_verts array for the rightmost point on this face int HTextureGetRightVertexForFace(int roomnum, int facenum) { matrix face_matrix, trans_matrix; vector fvec; vector avg_vert; vector verts[MAX_VERTS_PER_FACE]; ASSERT(Rooms[roomnum].used); ASSERT(Rooms[roomnum].faces[facenum].num_verts >= 3); // find the center point of this face vm_MakeZero(&avg_vert); int i; for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) avg_vert += Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; avg_vert /= i; // Make the orientation matrix fvec = Rooms[roomnum].faces[facenum].normal; vm_VectorToMatrix(&face_matrix, &fvec, NULL, NULL); // Make the transformation matrix angvec avec; vm_ExtractAnglesFromMatrix(&avec, &face_matrix); vm_AnglesToMatrix(&trans_matrix, avec.p, avec.h, avec.b); // Rotate all the points for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { vector vert = Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; vector rot_vert; vert -= avg_vert; vm_MatrixMulVector(&rot_vert, &vert, &trans_matrix); verts[i] = rot_vert; } // Find right most point int rightmost_point = -1; float rightmost_x = -900000.00f; // a big number for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { if (verts[i].x > rightmost_x) { rightmost_point = i; rightmost_x = verts[i].x; } } ASSERT(rightmost_point != -1); return rightmost_point; } // returns the index in the face_verts array for the most point on this face int HTextureGetBottomVertexForFace(int roomnum, int facenum) { matrix face_matrix, trans_matrix; vector fvec; vector avg_vert; vector verts[MAX_VERTS_PER_FACE]; ASSERT(Rooms[roomnum].used); ASSERT(Rooms[roomnum].faces[facenum].num_verts >= 3); // find the center point of this face vm_MakeZero(&avg_vert); int i; for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) avg_vert += Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; avg_vert /= i; // Make the orientation matrix fvec = Rooms[roomnum].faces[facenum].normal; vm_VectorToMatrix(&face_matrix, &fvec, NULL, NULL); // Make the transformation matrix angvec avec; vm_ExtractAnglesFromMatrix(&avec, &face_matrix); vm_AnglesToMatrix(&trans_matrix, avec.p, avec.h, avec.b); // Rotate all the points for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { vector vert = Rooms[roomnum].verts[Rooms[roomnum].faces[facenum].face_verts[i]]; vector rot_vert; vert -= avg_vert; vm_MatrixMulVector(&rot_vert, &vert, &trans_matrix); verts[i] = rot_vert; } // Find bottom most point int bottommost_point = -1; float bottommost_y = 900000.0f; // a big number for (i = 0; i < Rooms[roomnum].faces[facenum].num_verts; i++) { if (verts[i].y < bottommost_y) { bottommost_point = i; bottommost_y = verts[i].y; } } ASSERT(bottommost_point != -1); return bottommost_point; } /*void HTextureGetBoxUVLForFace (room *rp,facenum) { int l,r,t,b; vector lpoint,rpoint,tpoint,bpoint; g3UVL luvl,ruvl,tuvl,buvl,xchange,ychange; b=HTextureGetBottomVertexForFace (roomnum,facenum); t=HTextureGetTopVertexForFace (roomnum,facenum); l=HTextureGetLeftVertexForFace (roomnum,facenum); r=HTextureGetRightVertexForFace (roomnum,facenum); lpoint=rp->verts[rp->faces[facenum].face_verts[l]]; rpoint=rp->verts[rp->faces[facenum].face_verts[r]]; tpoint=rp->verts[rp->faces[facenum].face_verts[t]]; bpoint=rp->verts[rp->faces[facenum].face_verts[b]]; luvl=rp->faces[facenum].face_uvls[l]; ruvl=rp->faces[facenum].face_uvls[r]; tuvl=rp->faces[facenum].face_uvls[t]; buvl=rp->faces[facenum].face_uvls[b]; xchange.u=ruvl.u-luvl.u; xchange.v=ruvl.v-luvl.v; ychange=buvl.u-tuvl.u; ychange=buvl.v-tuvl.v; }*/