2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 // Preliminary patch stuff
30 #include "gtkr_list.h"
33 extern void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...);
34 extern face_t *Face_Alloc( void );
35 extern void DrawAlternatePoint(vec3_t v, float scale);
37 void _Write3DMatrix (FILE *f, int y, int x, int z, float *m);
38 void _Write3DMatrix (MemStream *f, int y, int x, int z, float *m);
40 void Patch_InitialiseLODPointers(patchMesh_t *p)
43 int rowcount = ((MAX_PATCH_WIDTH-1)/2) * MAX_PATCH_HEIGHT;
44 for (i=0; i<rowcount; i++)
46 int colcount = ((MAX_PATCH_HEIGHT-1)/2) * MAX_PATCH_WIDTH;
47 for (i=0; i<colcount; i++)
51 patchMesh_t* Patch_Alloc()
53 patchMesh_t *pPatch = (patchMesh_t *)malloc(sizeof(patchMesh_t));
54 pPatch->pShader = NULL;
55 pPatch->pSymbiot = NULL; // Hydra: added missing initialiser.
56 // spog - initialise patch LOD pointers
57 Patch_InitialiseLODPointers(pPatch);
58 pPatch->drawLists = NULL;
59 pPatch->bDirty = true;
61 pPatch->bSelected = false;
62 pPatch->bOverlay = false;
63 pPatch->bDirty = true;
64 pPatch->LODUpdated = false;
67 for (i=0; i<(((MAX_PATCH_WIDTH-1)-1)/2); i++)
68 pPatch->rowDirty[i] = false;
69 for (i=0; i<(((MAX_PATCH_HEIGHT-1)-1)/2); i++)
70 pPatch->colDirty[i] = false;
75 patchMesh_t* MakeNewPatch()
77 patchMesh_t *pm = reinterpret_cast<patchMesh_t*>(qmalloc(sizeof(patchMesh_t)));
79 // spog - initialise patch LOD pointers
80 Patch_InitialiseLODPointers(pm);
87 // FIXME: this needs to be dynamic
88 //#define MAX_PATCH_MESHES 4096
89 //patchMesh_t patchMeshes[MAX_PATCH_MESHES];
90 //int numPatchMeshes = 0;
92 // used for a save spot
93 patchMesh_t patchSave;
95 // Tracks the selected patch for point manipulation/update. FIXME: Need to revert back to a generalized
97 //--int g_nSelectedPatch = -1;
99 // HACK: for tracking which view generated the click
100 // as we dont want to deselect a point on a same point
101 // click if it is from a different view
102 int g_nPatchClickedView = -1;
103 bool g_bSameView = false;
105 //typedef enum XFormType { TRANSLATE, SCALE, ROTATE };
109 bool g_bPatchShowBounds = true;
110 bool g_bPatchWireFrame = false;
111 bool g_bPatchWeld = true;
112 bool g_bPatchDrillDown = true;
113 //bool g_bPatchInsertMode = false;
114 bool g_bPatchBendMode = false;
115 int g_nPatchBendState = -1;
116 int g_nPatchInsertState = -1;
117 int g_nBendOriginIndex = 0;
118 vec3_t g_vBendOrigin;
120 bool g_bPatchAxisOnRow = true;
121 int g_nPatchAxisIndex = 0;
122 bool g_bPatchLowerEdge = true;
124 vec3_t g_vCycleCapNormal;
125 // cycles when we use Patch_CycleCapSelected
126 VIEWTYPE g_nCycleCapIndex = XY;
131 BEND_SELECT_ROTATION = 0,
138 const char *g_pBendStateMsg[] =
140 "Use TAB to cycle through available bend axis. Press ENTER when the desired one is highlighted.",
141 "Use TAB to cycle through available rotation axis. This will LOCK around that point. You may also use Shift + Middle Click to select an arbitrary point. Press ENTER when the desired one is highlighted",
142 "Use TAB to choose which side to bend. Press ENTER when the desired one is highlighted.",
143 "Use the MOUSE to bend the patch. It uses the same ui rules as Free Rotation. Press ENTER to accept the bend, press ESC to abandon it and exit Bend mode",
150 INSERT_SELECT_EDGE = 0,
154 const char* g_pInsertStateMsg[] =
156 "Use TAB to cycle through available rows/columns for insertion/deletion. Press INS to insert at the highlight, DEL to remove the pair"
160 float *g_InversePoints[1024];
162 const float fFullBright = 1.0;
163 const float fLowerLimit = .50;
164 const float fDec = .05f;
165 void _SetColor(face_t* f, float fColor[3])
168 fColor[0] = f->d_color[0];
169 fColor[1] = f->d_color[1];
170 fColor[2] = f->d_color[2];
175 void _DecColor(float fColor[3])
181 for (int i = 0; i < 3; i++)
183 if (fColor[i] <= fLowerLimit)
185 fColor[0] = fFullBright;
186 fColor[1] = fFullBright;
187 fColor[2] = fFullBright;
194 vec_t __VectorNormalize (vec3_t in, vec3_t out)
196 vec_t length, ilength;
198 length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
205 ilength = 1.0/length;
206 out[0] = in[0]*ilength;
207 out[1] = in[1]*ilength;
208 out[2] = in[2]*ilength;
214 void Patch_SetType(patchMesh_t *p, int nType)
216 p->type = (p->type & PATCH_STYLEMASK) | nType;
219 void Patch_SetStyle(patchMesh_t *p, int nStyle)
221 p->type = (p->type & PATCH_TYPEMASK) | nStyle;
229 int Patch_MemorySize(patchMesh_t *p)
238 InterpolateInteriorPoints
241 void InterpolateInteriorPoints( patchMesh_t *p )
246 for ( i = 0 ; i < p->width ; i += 2 )
249 next = ( i == p->width - 1 ) ? 1 : ( i + 1 ) % p->width;
250 prev = ( i == 0 ) ? p->width - 2 : i - 1;
255 next = ( i + 1 ) % p->width;
256 prev = p->width - 2; // joined wrap case
258 else if ( i == p->width - 1 )
265 next = ( i + 1 ) % p->width;
270 for ( j = 0 ; j < p->height ; j++ )
272 for ( k = 0 ; k < 3 ; k++ )
274 p->ctrl[i][j].xyz[k] = ( p->ctrl[next][j].xyz[k] + p->ctrl[prev][j].xyz[k] ) * 0.5;
286 int neighbors[8][2] = {
287 {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
290 void Patch_MeshNormals(patchMesh_t *in )
300 vec3_t around[8], temp;
302 qboolean wrapWidth, wrapHeight;
306 for ( i = 0 ; i < in->height ; i++ )
309 VectorSubtract( in->ctrl[0][i].xyz,
310 in->ctrl[in->width-1][i].xyz, delta );
311 len = VectorLength( delta );
317 if ( i == in->height )
323 for ( i = 0 ; i < in->width ; i++ )
325 VectorSubtract( in->ctrl[i][0].xyz,
326 in->ctrl[i][in->height-1].xyz, delta );
327 len = VectorLength( delta );
339 for ( i = 0 ; i < in->width ; i++ )
341 for ( j = 0 ; j < in->height ; j++ )
344 //--dv = reinterpret_cast<drawVert_t*>(in.ctrl[j*in.width+i]);
345 dv = &in->ctrl[i][j];
346 VectorCopy( dv->xyz, base );
347 for ( k = 0 ; k < 8 ; k++ )
349 VectorClear( around[k] );
352 for ( dist = 1 ; dist <= 3 ; dist++ )
354 x = i + neighbors[k][0] * dist;
355 y = j + neighbors[k][1] * dist;
360 x = in->width - 1 + x;
362 else if ( x >= in->width )
364 x = 1 + x - in->width;
371 y = in->height - 1 + y;
373 else if ( y >= in->height )
375 y = 1 + y - in->height;
379 if ( x < 0 || x >= in->width || y < 0 || y >= in->height )
381 break; // edge of patch
383 //--VectorSubtract( in.ctrl[y*in.width+x]->xyz, base, temp );
384 VectorSubtract( in->ctrl[x][y].xyz, base, temp );
385 if ( __VectorNormalize( temp, temp ) == 0 )
387 continue; // degenerate edge, get more dist
392 VectorCopy( temp, around[k] );
399 for ( k = 0 ; k < 8 ; k++ )
401 if ( !good[k] || !good[(k+1)&7] )
403 continue; // didn't get two points
405 CrossProduct( around[(k+1)&7], around[k], normal );
406 if ( __VectorNormalize( normal, normal ) == 0 )
410 VectorAdd( normal, sum, sum );
415 //printf("bad normal\n");
419 __VectorNormalize( sum, dv->normal );
432 void Patch_CalcBounds(patchMesh_t *p, vec3_t& vMin, vec3_t& vMax)
434 vMin[0] = vMin[1] = vMin[2] = 99999;
435 vMax[0] = vMax[1] = vMax[2] = -99999;
438 for (int w = 0; w < p->width; w++)
440 for (int h = 0; h < p->height; h++)
442 for (int j = 0; j < 3; j++)
444 float f = p->ctrl[w][h].xyz[j];
459 void Brush_RebuildBrush(brush_t *b, vec3_t vMins, vec3_t vMaxs)
470 for (j = 0; j < 3; j++)
472 if ((int)vMins[j] == (int)vMaxs[j])
480 for (f=b->brush_faces ; f ; f=next)
488 b->brush_faces = NULL;
490 // left the last face so we can use its texdef
492 for (i=0 ; i<3 ; i++)
493 if (vMaxs[i] < vMins[i])
494 Error ("Brush_RebuildBrush: backwards");
496 pts[0][0][0] = vMins[0];
497 pts[0][0][1] = vMins[1];
499 pts[1][0][0] = vMins[0];
500 pts[1][0][1] = vMaxs[1];
502 pts[2][0][0] = vMaxs[0];
503 pts[2][0][1] = vMaxs[1];
505 pts[3][0][0] = vMaxs[0];
506 pts[3][0][1] = vMins[1];
508 for (i=0 ; i<4 ; i++)
510 pts[i][0][2] = vMins[2];
511 pts[i][1][0] = pts[i][0][0];
512 pts[i][1][1] = pts[i][0][1];
513 pts[i][1][2] = vMaxs[2];
516 for (i=0 ; i<4 ; i++)
520 f->texdef.flags &= ~SURF_KEEP;
521 f->texdef.contents &= ~CONTENTS_KEEP;
522 // f->texdef.flags |= SURF_PATCH;
523 f->next = b->brush_faces;
527 VectorCopy (pts[j][1], f->planepts[0]);
528 VectorCopy (pts[i][1], f->planepts[1]);
529 VectorCopy (pts[i][0], f->planepts[2]);
534 f->texdef.flags &= ~SURF_KEEP;
535 f->texdef.contents &= ~CONTENTS_KEEP;
536 // f->texdef.flags |= SURF_PATCH;
537 f->next = b->brush_faces;
540 VectorCopy (pts[0][1], f->planepts[0]);
541 VectorCopy (pts[1][1], f->planepts[1]);
542 VectorCopy (pts[2][1], f->planepts[2]);
546 f->texdef.flags &= ~SURF_KEEP;
547 f->texdef.contents &= ~CONTENTS_KEEP;
548 // f->texdef.flags |= SURF_PATCH;
549 f->next = b->brush_faces;
552 VectorCopy (pts[2][0], f->planepts[0]);
553 VectorCopy (pts[1][0], f->planepts[1]);
554 VectorCopy (pts[0][0], f->planepts[2]);
559 void WINAPI Patch_Rebuild(patchMesh_t *p)
562 Patch_CalcBounds(p, vMin, vMax);
563 Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
571 adds a patch brush and ties it to this patch id
573 brush_t* AddBrushForPatch(patchMesh_t *pm, bool bLinkToWorld )
575 // find the farthest points in x,y,z
577 Patch_CalcBounds(pm, vMin, vMax);
579 for (int j = 0; j < 3; j++)
581 if (vMin[j] == vMax[j])
588 brush_t *b = Brush_Create(vMin, vMax, &g_qeglobals.d_texturewin.texdef);
590 // FIXME: this entire type of linkage needs to be fixed
591 b->patchBrush = true;
594 pm->bSelected = false;
595 pm->bOverlay = false;
601 Brush_AddToList (b, &active_brushes);
602 Entity_LinkBrush (world_entity, b);
609 void Patch_SetPointIntensities(int n)
612 patchMesh_t *p = patchMeshes[n];
613 for (int i = 0; i < p->width; i++)
615 for (int j = 0; j < p->height; j++)
623 // very approximate widths and heights
630 float Patch_Width(patchMesh_t *p)
633 for (int i = 0; i < p->width-1; i++)
636 VectorSubtract(p->ctrl[i][0].xyz, p->ctrl[i+1][0].xyz, vTemp);
637 f += VectorLength(vTemp);
642 float Patch_WidthDistanceTo(patchMesh_t *p, int j)
645 for (int i = 0; i < j; i++)
648 VectorSubtract(p->ctrl[i][0].xyz, p->ctrl[i+1][0].xyz, vTemp);
649 f += VectorLength(vTemp);
661 float Patch_Height(patchMesh_t *p)
664 for (int i = 0; i < p->height-1; i++)
667 VectorSubtract(p->ctrl[0][i].xyz, p->ctrl[0][i+1].xyz, vTemp);
668 f += VectorLength(vTemp);
673 float Patch_HeightDistanceTo(patchMesh_t *p, int j)
676 for (int i = p->height-1; i > j; i--)
679 VectorSubtract(p->ctrl[0][i].xyz, p->ctrl[0][i-1].xyz, vTemp); // reverse order for T coords
680 f += VectorLength(vTemp);
691 texture = TotalTexture * LengthToThisControlPoint / TotalControlPointLength
693 dist( this control point to first control point ) / dist ( last control pt to first)
695 void WINAPI Patch_Naturalize(patchMesh_t *p)
697 int nWidth = (int)(p->d_texture->width * g_pGameDescription->mTextureDefaultScale);
698 int nHeight = (int)(p->d_texture->height * g_pGameDescription->mTextureDefaultScale);
699 float fPWidth = Patch_Width(p);
700 float fPHeight = Patch_Height(p);
703 for ( int i = 0; i < p->width ; i++ )
706 for ( int j = p->height-1; j >= 0 ; j-- )
708 p->ctrl[i][j].st[0] = (fPWidth / nWidth) * xAccum / fPWidth;
709 p->ctrl[i][j].st[1] = (fPHeight / nHeight) * yAccum / fPHeight;
710 yAccum = Patch_HeightDistanceTo(p,j-1);
711 //p->ctrl[i][j][3] = (fPWidth / nWidth) * (float)i / (p->width - 1);
712 //p->ctrl[i][j][4] = (fPHeight/ nHeight) * (float)j / (p->height - 1);
714 xAccum = Patch_WidthDistanceTo(p,i+1);
722 VectorCopy(p->ctrl[1][0], p->ctrl[1][1]);
727 VectorCopy(p->ctrl[3][0], p->ctrl[4][1]);
728 VectorCopy(p->ctrl[2][0], p->ctrl[3][1]);
729 VectorCopy(p->ctrl[2][0], p->ctrl[2][1]);
730 VectorCopy(p->ctrl[2][0], p->ctrl[1][1]);
731 VectorCopy(p->ctrl[1][0], p->ctrl[0][1]);
732 VectorCopy(p->ctrl[1][0], p->ctrl[0][2]);
733 VectorCopy(p->ctrl[1][0], p->ctrl[1][2]);
734 VectorCopy(p->ctrl[2][0], p->ctrl[2][2]);
735 VectorCopy(p->ctrl[3][0], p->ctrl[3][2]);
736 VectorCopy(p->ctrl[3][0], p->ctrl[4][2]);
781 int Interior3By[][2] =
786 int Interior5By[][2] =
799 int Interior3ByCount = sizeof(Interior3By) / sizeof(int[2]);
800 int Interior5ByCount = sizeof(Interior5By) / sizeof(int[2]);
802 extern int Plane_FromPoints(vec3_t p1, vec3_t p2, vec3_t p3, plane_t *plane);
803 // the bFaceCycle only means we are going through a patch cycling loop
804 // then we rely on g_vCycleCapNormal to compute the cap
806 void Patch_CapTexture(patchMesh_t *p, bool bFaceCycle = false)
808 vec3_t vProjection, vX, vY;
809 qtexture_t *texture = p->pShader->getTexture();
810 plane_t Plane1, Plane2, Plane3;
814 VectorCopy (g_vCycleCapNormal, vProjection);
818 VectorClear ( vProjection );
820 // find normal for plane from first 3 corner points
821 if (!Plane_FromPoints(p->ctrl[0][0].xyz,p->ctrl[0][p->height-1].xyz,p->ctrl[p->width-1][p->height-1].xyz,&Plane1))
823 VectorClear ( Plane3.normal );
827 // find normal for plane from next 3 corner points
828 if (!Plane_FromPoints(p->ctrl[p->width-1][p->height-1].xyz,p->ctrl[p->width-1][0].xyz,p->ctrl[0][0].xyz,&Plane2))
832 VectorCopy ( Plane1.normal, Plane3.normal );
833 Plane3.dist = Plane1.dist;
840 // find average plane for all 4 corner points
842 for (int n = 0; n <= 2; n++)
844 Plane3.normal[n] = (Plane1.normal[n] + Plane2.normal[n]) / 2;
846 Plane3.dist = (Plane1.dist + Plane2.dist) / 2;
850 VectorCopy ( Plane2.normal, Plane3.normal );
851 Plane3.dist = Plane2.dist;
855 // get best axis for projection from average plane
856 //Sys_Printf("surface normal1: (%f,%f,%f)\n",Plane1.normal[0],Plane1.normal[1],Plane1.normal[0]);
857 //Sys_Printf("surface normal2: (%f,%f,%f)\n",Plane2.normal[0],Plane2.normal[1],Plane2.normal[0]);
858 //Sys_Printf("surface normal3: (%f,%f,%f)\n",Plane3.normal[0],Plane3.normal[1],Plane3.normal[0]);
859 TextureAxisFromPlane(&Plane3, vX, vY);
862 for (int w = 0; w < p->width; w++)
864 for (int h = 0; h < p->height; h++)
866 if (vProjection[2] == 1.0f || (vX[0] == 1.0f && vY[1] == -1.0f))
868 p->ctrl[w][h].st[0] = p->ctrl[w][h].xyz[0] / (texture->width * g_pGameDescription->mTextureDefaultScale);
869 p->ctrl[w][h].st[1] = p->ctrl[w][h].xyz[1] / (texture->height * g_pGameDescription->mTextureDefaultScale) * -1;
871 else if (vProjection[0] == 1.0f || (vX[1] == 1.0f && vY[2] == -1.0f))
873 p->ctrl[w][h].st[0] = p->ctrl[w][h].xyz[1] / (texture->width * g_pGameDescription->mTextureDefaultScale);
874 p->ctrl[w][h].st[1] = p->ctrl[w][h].xyz[2] / (texture->height * g_pGameDescription->mTextureDefaultScale) * -1;
876 else if (vProjection[1] == 1.0f || (vX[0] == 1.0f && vY[2] == -1.0f))
878 p->ctrl[w][h].st[0] = p->ctrl[w][h].xyz[0] / (texture->width * g_pGameDescription->mTextureDefaultScale);
879 p->ctrl[w][h].st[1] = p->ctrl[w][h].xyz[2] / (texture->height * g_pGameDescription->mTextureDefaultScale) * -1;
881 //Sys_Printf("(%i,%i) (%f,%f,%f) (%f,%f) %f\n",w,h,
882 // p->ctrl[w][h].xyz[0],p->ctrl[w][h].xyz[1],p->ctrl[w][h].xyz[2],
883 // p->ctrl[w][h].st[0],p->ctrl[w][h].st[1],p->ctrl[w][h].normal);
886 // make sure it will rebuild
890 void FillPatch(patchMesh_t *p, vec3_t v)
892 for (int i = 0; i < p->width; i++)
894 for (int j = 0; j < p->height; j++)
896 VectorCopy(v, p->ctrl[i][j].xyz);
901 // temporarily moved function to allow use in Cap() and CapSpecial()
902 void patchInvert(patchMesh_t *p)
906 for ( int i = 0 ; i < p->width ; i++ )
908 for (int j = 0; j < p->height / 2; j++)
910 memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t));
911 memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t));
912 memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t));
917 brush_t* Cap(patchMesh_t *pParent, bool bByColumn, bool bFirst)
925 // make a generic patch
926 if (pParent->width <= 9)
928 b = Patch_GenericMesh(3, 3, 2, false);
932 b = Patch_GenericMesh(5, 5, 2, false);
938 Sys_Printf("Unable to cap. You may need to ungroup the patch.\n");
943 p->type |= PATCH_CAP;
945 vMin[0] = vMin[1] = vMin[2] = 9999;
946 vMax[0] = vMax[1] = vMax[2] = -9999;
948 // we seam the column edge, FIXME: this might need to be able to seem either edge
950 int nSize = (bByColumn) ? pParent->width : pParent->height;
951 int nIndex = (bFirst) ? 0 : (bByColumn) ? pParent->height-1 : pParent->width-1;
953 FillPatch(p, pParent->ctrl[0][nIndex].xyz);
955 for (i = 0; i < nSize; i++)
961 VectorCopy(pParent->ctrl[i][nIndex].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz);
965 VectorCopy(pParent->ctrl[i][nIndex].xyz, p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz);
972 VectorCopy(pParent->ctrl[nIndex][i].xyz, p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz);
976 VectorCopy(pParent->ctrl[nIndex][i].xyz, p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz);
980 for (j = 0; j < 3; j++)
982 float f = (bSmall) ? p->ctrl[Index3By[i][0]][Index3By[i][1]].xyz[j] : p->ctrl[Index5By[i][0]][Index5By[i][1]].xyz[j];
991 for (j = 0; j < 3; j++)
993 vTemp[j] = vMin[j] + fabs((vMax[j] - vMin[j]) * 0.5);
995 int nCount = (bSmall) ? Interior3ByCount : Interior5ByCount;
996 for (j = 0; j < nCount; j++)
1000 VectorCopy(vTemp, p->ctrl[Interior3By[j][0]][Interior3By[j][1]].xyz);
1004 VectorCopy(vTemp, p->ctrl[Interior5By[j][0]][Interior5By[j][1]].xyz);
1012 drawVert_t vertTemp;
1013 for (i = 0; i < p->width; i++)
1015 for (j = 0; j < p->height / 2; j++)
1017 memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t));
1018 memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t));
1019 memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t));
1026 Patch_CapTexture(p);
1030 brush_t* CapSpecial(patchMesh_t *pParent, int nType, bool bFirst)
1035 vec3_t vMin, vMax, vTemp;
1038 if (nType == IENDCAP)
1039 b = Patch_GenericMesh(5, 3, 2, false);
1041 b = Patch_GenericMesh(3, 3, 2, false);
1045 Sys_Printf("Unable to cap. Make sure you ungroup before re-capping.");
1050 p->type |= PATCH_CAP;
1052 vMin[0] = vMin[1] = vMin[2] = 9999;
1053 vMax[0] = vMax[1] = vMax[2] = -9999;
1055 // int nSize = pParent->width;
1056 int nIndex = (bFirst) ? 0 : pParent->height-1;
1058 // parent bounds are used for some things
1059 Patch_CalcBounds(pParent, vMin, vMax);
1061 for (j = 0; j < 3; j++)
1063 vTemp[j] = vMin[j] + fabs((vMax[j] - vMin[j]) * 0.5);
1066 if (nType == IBEVEL)
1068 VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz);
1069 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[0][2].xyz);
1070 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz);
1071 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][2].xyz);
1072 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][0].xyz);
1073 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][1].xyz);
1074 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[1][2].xyz);
1075 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][0].xyz);
1076 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[2][1].xyz);
1078 else if (nType == BEVEL)
1080 vec3_t p1, p2, p3, p4; //, temp, dir;
1082 VectorCopy(pParent->ctrl[0][nIndex].xyz, p3);
1083 VectorCopy(pParent->ctrl[1][nIndex].xyz, p1);
1084 VectorCopy(pParent->ctrl[2][nIndex].xyz, p2);
1086 //Sys_Printf("CapSpecial() p1: %f %f %f\n",p1[0],p1[1],p1[2]);
1087 //Sys_Printf("CapSpecial() p2: %f %f %f\n",p2[0],p2[1],p2[2]);
1088 //Sys_Printf("CapSpecial() p3: %f %f %f\n",p3[0],p3[1],p3[2]);
1090 VectorSubtract(p2, p1, p4);
1091 VectorAdd(p3, p4, p4);
1092 // spog - use opposite-point-on-parallelogram to find p4
1094 VectorSubtract(p3, p2, dir);
1095 VectorNormalize(dir);
1096 VectorSubtract(p1, p2, temp);
1097 vec_t dist = _DotProduct(temp, dir);
1098 VectorScale(dir, dist, temp);
1099 VectorAdd(p2, temp, temp);
1100 VectorSubtract(temp, p1, temp);
1101 VectorScale(temp, 2, temp);
1102 VectorAdd(p1, temp, p4);
1105 //Sys_Printf("CapSpecial() p1: %f %f %f\n",p1[0],p1[1],p1[2]);
1106 //Sys_Printf("CapSpecial() p2: %f %f %f\n",p2[0],p2[1],p2[2]);
1107 //Sys_Printf("CapSpecial() p3: %f %f %f\n",p3[0],p3[1],p3[2]);
1108 //Sys_Printf("CapSpecial() p4: %f %f %f\n",p4[0],p4[1],p4[2]);
1110 VectorCopy(p4, p->ctrl[0][0].xyz);
1111 VectorCopy(p4, p->ctrl[1][0].xyz);
1112 VectorCopy(p4, p->ctrl[0][1].xyz);
1113 VectorCopy(p4, p->ctrl[1][1].xyz);
1114 VectorCopy(p4, p->ctrl[0][2].xyz);
1115 VectorCopy(p4, p->ctrl[1][2].xyz);
1116 VectorCopy(p2, p->ctrl[2][0].xyz);
1117 VectorCopy(p1, p->ctrl[2][1].xyz);
1118 VectorCopy(p3, p->ctrl[2][2].xyz);
1121 else if (nType == ENDCAP)
1123 VectorAdd(pParent->ctrl[4][nIndex].xyz, pParent->ctrl[0][nIndex].xyz, vTemp);
1124 VectorScale(vTemp, 0.5, vTemp);
1125 VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[0][0].xyz);
1126 VectorCopy(vTemp, p->ctrl[1][0].xyz);
1127 VectorCopy(pParent->ctrl[4][nIndex].xyz, p->ctrl[2][0].xyz);
1129 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[0][2].xyz);
1130 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[1][2].xyz);
1131 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][2].xyz);
1132 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[1][1].xyz);
1134 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[0][1].xyz);
1135 VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[2][1].xyz);
1139 VectorCopy(pParent->ctrl[4][nIndex].xyz, p->ctrl[0][0].xyz);
1140 VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[1][0].xyz);
1141 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][0].xyz);
1142 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[3][0].xyz);
1143 VectorCopy(pParent->ctrl[0][nIndex].xyz, p->ctrl[4][0].xyz);
1145 VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[0][1].xyz);
1146 VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[1][1].xyz);
1147 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][1].xyz);
1148 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[3][1].xyz);
1149 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[4][1].xyz);
1151 VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[0][2].xyz);
1152 VectorCopy(pParent->ctrl[3][nIndex].xyz, p->ctrl[1][2].xyz);
1153 VectorCopy(pParent->ctrl[2][nIndex].xyz, p->ctrl[2][2].xyz);
1154 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[3][2].xyz);
1155 VectorCopy(pParent->ctrl[1][nIndex].xyz, p->ctrl[4][2].xyz);
1161 drawVert_t vertTemp;
1162 for (i = 0; i < p->width; i++)
1164 for (j = 0; j < p->height / 2; j++)
1166 memcpy(&vertTemp, &p->ctrl[i][p->height - 1- j], sizeof (drawVert_t));
1167 memcpy(&p->ctrl[i][p->height - 1 - j], &p->ctrl[i][j], sizeof(drawVert_t));
1168 memcpy(&p->ctrl[i][j], &vertTemp, sizeof(drawVert_t));
1173 //--Patch_CalcBounds(p, vMin, vMax);
1174 //--Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
1176 Patch_CapTexture(p);
1180 void Patch_CapCurrent()
1182 patchMesh_t *pParent = NULL;
1184 brush_t *pCap = NULL;
1185 b[0] = b[1] = b[2] = b[3] = NULL;
1187 bool b_GroupResult = TRUE;
1189 if (!QE_SingleBrush(true))
1191 Sys_Printf("Patch_CapCurrent: you must have a single patch selected\n");
1196 for (brush_t *pb = selected_brushes.next ; pb != NULL && pb != &selected_brushes ; pb = pb->next)
1200 pParent = pb->pPatch;
1201 // decide which if any ends we are going to cap
1202 // if any of these compares hit, it is a closed patch and as such
1203 // the generic capping will work.. if we do not find a closed edge
1204 // then we need to ask which kind of cap to add
1205 if (VectorCompare(pParent->ctrl[0][0].xyz, pParent->ctrl[pParent->width-1][0].xyz))
1207 pCap = Cap(pParent, true, false);
1213 if (VectorCompare(pParent->ctrl[0][pParent->height-1].xyz, pParent->ctrl[pParent->width-1][pParent->height-1].xyz))
1215 pCap = Cap(pParent, true, true);
1221 if (VectorCompare(pParent->ctrl[0][0].xyz, pParent->ctrl[0][pParent->height-1].xyz))
1223 pCap = Cap(pParent, false, false);
1229 if (VectorCompare(pParent->ctrl[pParent->width-1][0].xyz, pParent->ctrl[pParent->width-1][pParent->height-1].xyz))
1231 pCap = Cap(pParent, false, true);
1242 // if we did not cap anything with the above tests
1247 if (DoCapDlg (&type, &b_GroupResult) == IDOK)
1249 b[nIndex++] = CapSpecial(pParent, type, false);
1250 b[nIndex++] = CapSpecial(pParent, type, true);
1261 Select_Brush(b[nIndex]);
1264 // Gef: Added toggle for capped patch func_group
1266 entity_t *e = Entity_Alloc();
1267 SetKeyValue(e, "classname", "func_group");
1268 SetKeyValue(e, "type", "patchCapped");
1269 Select_GroupEntity(e);
1270 Entity_AddToList(e, &entities);
1281 void Patch_BrushToMesh(bool bCone, bool bBevel, bool bEndcap, bool bSquare, int nHeight)
1287 if (!QE_SingleBrush())
1290 b = selected_brushes.next;
1294 p->d_texture = b->brush_faces->d_texture;
1295 p->pShader = b->brush_faces->pShader;
1297 p->height = nHeight;
1299 p->type = PATCH_CYLINDER;
1300 if (bBevel & !bSquare)
1302 p->type = PATCH_BEVEL;
1304 int nStep = (int)((b->maxs[2] - b->mins[2]) / (p->height-1));
1305 int nStart = (int)(b->mins[2]);
1306 for (i = 0; i < p->height; i++)
1308 p->ctrl[0][i].xyz[0] = b->mins[0];
1309 p->ctrl[0][i].xyz[1] = b->mins[1];
1310 p->ctrl[0][i].xyz[2] = nStart;
1312 p->ctrl[1][i].xyz[0] = b->maxs[0];
1313 p->ctrl[1][i].xyz[1] = b->mins[1];
1314 p->ctrl[1][i].xyz[2] = nStart;
1316 p->ctrl[2][i].xyz[0] = b->maxs[0];
1317 p->ctrl[2][i].xyz[1] = b->maxs[1];
1318 p->ctrl[2][i].xyz[2] = nStart;
1322 else if (bEndcap & !bSquare)
1324 p->type = PATCH_ENDCAP;
1326 int nStep = (int)((b->maxs[2] - b->mins[2]) / (p->height-1));
1327 int nStart = (int)(b->mins[2]);
1328 for (i = 0; i < p->height; i++)
1330 p->ctrl[0][i].xyz[0] = b->mins[0];
1331 p->ctrl[0][i].xyz[1] = b->mins[1];
1332 p->ctrl[0][i].xyz[2] = nStart;
1334 p->ctrl[1][i].xyz[0] = b->mins[0];
1335 p->ctrl[1][i].xyz[1] = b->maxs[1];
1336 p->ctrl[1][i].xyz[2] = nStart;
1338 p->ctrl[2][i].xyz[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) * 0.5);
1339 p->ctrl[2][i].xyz[1] = b->maxs[1];
1340 p->ctrl[2][i].xyz[2] = nStart;
1342 p->ctrl[3][i].xyz[0] = b->maxs[0];
1343 p->ctrl[3][i].xyz[1] = b->maxs[1];
1344 p->ctrl[3][i].xyz[2] = nStart;
1346 p->ctrl[4][i].xyz[0] = b->maxs[0];
1347 p->ctrl[4][i].xyz[1] = b->mins[1];
1348 p->ctrl[4][i].xyz[2] = nStart;
1355 p->ctrl[1][0].xyz[0] = b->mins[0];
1356 p->ctrl[1][0].xyz[1] = b->mins[1];
1358 p->ctrl[3][0].xyz[0] = b->maxs[0];
1359 p->ctrl[3][0].xyz[1] = b->mins[1];
1361 p->ctrl[5][0].xyz[0] = b->maxs[0];
1362 p->ctrl[5][0].xyz[1] = b->maxs[1];
1364 p->ctrl[7][0].xyz[0] = b->mins[0];
1365 p->ctrl[7][0].xyz[1] = b->maxs[1];
1367 for ( i = 1 ; i < p->width - 1 ; i += 2 )
1370 p->ctrl[i][0].xyz[2] = b->mins[2];
1372 VectorCopy( p->ctrl[i][0].xyz, p->ctrl[i][2].xyz );
1374 p->ctrl[i][2].xyz[2] = b->maxs[2];
1376 p->ctrl[i][1].xyz[0] = ( p->ctrl[i][0].xyz[0] + p->ctrl[i][2].xyz[0] ) * 0.5;
1377 p->ctrl[i][1].xyz[1] = ( p->ctrl[i][0].xyz[1] + p->ctrl[i][2].xyz[1] ) * 0.5;
1378 p->ctrl[i][1].xyz[2] = ( p->ctrl[i][0].xyz[2] + p->ctrl[i][2].xyz[2] ) * 0.5;
1380 InterpolateInteriorPoints( p );
1384 if (bBevel || bEndcap)
1388 for (i = 0; i < p->height; i++)
1390 VectorCopy(p->ctrl[1][i].xyz, p->ctrl[2][i].xyz);
1391 VectorCopy(p->ctrl[7][i].xyz, p->ctrl[6][i].xyz);
1396 for (i = 0; i < p->height; i++)
1398 VectorCopy(p->ctrl[5][i].xyz, p->ctrl[4][i].xyz);
1399 VectorCopy(p->ctrl[1][i].xyz, p->ctrl[2][i].xyz);
1400 VectorCopy(p->ctrl[7][i].xyz, p->ctrl[6][i].xyz);
1401 VectorCopy(p->ctrl[8][i].xyz, p->ctrl[7][i].xyz);
1407 for (i = 0; i < p->width-1; i ++)
1409 for (j = 0; j < p->height; j++)
1411 VectorCopy(p->ctrl[i+1][j].xyz, p->ctrl[i][j].xyz);
1414 for (j = 0; j < p->height; j++)
1416 VectorCopy(p->ctrl[0][j].xyz, p->ctrl[8][j].xyz);
1423 Patch_Naturalize(p);
1427 p->type = PATCH_CONE;
1428 float xc = (b->maxs[0] + b->mins[0]) * 0.5;
1429 float yc = (b->maxs[1] + b->mins[1]) * 0.5;
1431 for ( i = 0 ; i < p->width ; i ++)
1433 p->ctrl[i][2].xyz[0] = xc;
1434 p->ctrl[i][2].xyz[1] = yc;
1438 b = AddBrushForPatch(p);
1450 brush_t* Patch_GenericMesh(int nWidth, int nHeight, int nOrientation, bool bDeleteSource, bool bOverride)
1454 if (nHeight < 3 || nHeight > 15 || nWidth < 3 || nWidth > 15)
1456 Sys_Printf("Invalid patch width or height.\n");
1460 if (! bOverride && !QE_SingleBrush())
1462 Sys_Printf("Error: you must have a single brush selected\n");
1466 patchMesh_t* p = MakeNewPatch();
1467 p->pShader = g_qeglobals.d_texturewin.pShader;
1468 p->d_texture = g_qeglobals.d_texturewin.pShader->getTexture();
1471 p->height = nHeight;
1472 p->type = PATCH_GENERIC;
1476 if (nOrientation == 0)
1481 else if (nOrientation == 1)
1486 brush_t *b = selected_brushes.next;
1487 // set the workzone to this brush, use it later to create the patch points
1488 UpdateWorkzone_ForBrush( b );
1490 int xStep = (int)(b->mins[nFirst]);
1491 float xAdj = fabs((b->maxs[nFirst] - b->mins[nFirst]) / (nWidth - 1));
1492 float yAdj = fabs((b->maxs[nSecond] - b->mins[nSecond]) / (nHeight - 1));
1494 for (i = 0; i < nWidth; i++)
1496 int yStep = (int)(b->mins[nSecond]);
1497 for (j = 0; j < nHeight; j++)
1499 p->ctrl[i][j].xyz[nFirst] = xStep;
1500 p->ctrl[i][j].xyz[nSecond] = yStep;
1501 // create patch based on workzone
1502 p->ctrl[i][j].xyz[nOrientation] = g_qeglobals.d_work_max[nOrientation];
1508 Patch_Naturalize(p);
1510 b = AddBrushForPatch(p);
1518 //g_qeglobals.d_select_mode = sel_curvepoint;
1526 int PointInMoveList(float *pf)
1528 for (int i = 0; i < g_qeglobals.d_num_move_points; i++)
1530 if (pf == &g_qeglobals.d_move_points[i][0])
1538 PointValueInMoveList
1541 int PointValueInMoveList(vec3_t v)
1543 for (int i = 0; i < g_qeglobals.d_num_move_points; i++)
1545 if (VectorCompare(v, g_qeglobals.d_move_points[i]))
1554 RemovePointFromMoveList
1557 void RemovePointFromMoveList(vec3_t v)
1560 while ( (n = PointValueInMoveList(v)) >= 0)
1562 for (int i = n; i < g_qeglobals.d_num_move_points-1; i++)
1564 g_qeglobals.d_move_points[i] = g_qeglobals.d_move_points[i+1];
1566 g_qeglobals.d_num_move_points--;
1575 bool ColumnSelected(patchMesh_t* p, int nCol)
1577 for (int i = 0; i < p->height; i++)
1579 if (PointInMoveList(p->ctrl[nCol][i].xyz) == -1)
1590 void AddPoint(patchMesh_t* p, vec3_t v, bool bWeldOrDrill = true)
1592 int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
1593 int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
1594 g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = v;
1595 if ((g_bPatchWeld || g_bPatchDrillDown) && bWeldOrDrill)
1597 for ( int i = 0 ; i < p->width ; i++ )
1599 for ( int j = 0 ; j < p->height ; j++ )
1603 if ( VectorCompare(v, p->ctrl[i][j].xyz)
1604 && PointInMoveList(p->ctrl[i][j].xyz) == -1)
1606 g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
1610 if (g_bPatchDrillDown && g_nPatchClickedView != W_CAMERA)
1612 if ( (fabs(v[nDim1] - p->ctrl[i][j].xyz[nDim1]) <= EQUAL_EPSILON)
1613 &&(fabs(v[nDim2] - p->ctrl[i][j].xyz[nDim2]) <= EQUAL_EPSILON))
1615 if (PointInMoveList(p->ctrl[i][j].xyz) == -1)
1617 g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
1632 void SelectRow(patchMesh_t* p, int nRow, bool bMulti)
1635 g_qeglobals.d_num_move_points = 0;
1636 for (int i = 0; i < p->width; i++)
1638 AddPoint(p, p->ctrl[i][nRow].xyz, false);
1640 //Sys_Printf("Selected Row %d\n", nRow);
1648 void SelectColumn(patchMesh_t* p, int nCol, bool bMulti)
1651 g_qeglobals.d_num_move_points = 0;
1652 for (int i = 0; i < p->height; i++)
1654 AddPoint(p, p->ctrl[nCol][i].xyz, false);
1656 //Sys_Printf("Selected Col %d\n", nCol);
1665 void AddPatchMovePoint(vec3_t v, bool bMulti, bool bFull)
1667 if (!g_bSameView && !bMulti && !bFull)
1670 //return; // was causing odd behaviour on patch vertex selection
1673 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
1677 patchMesh_t* p = pb->pPatch;
1678 for ( int i = 0 ; i < p->width ; i++ )
1680 for ( int j = 0 ; j < p->height ; j++ )
1682 if (VectorCompare(v, p->ctrl[i][j].xyz))
1684 if (PointInMoveList(p->ctrl[i][j].xyz) == -1)
1686 if (bFull) // if we want the full row/col this is on
1688 SelectColumn(p, i, bMulti);
1693 g_qeglobals.d_num_move_points = 0;
1694 AddPoint(p, p->ctrl[i][j].xyz);
1695 //Sys_Printf("Selected col:row %d:%d\n", i, j);
1704 if (ColumnSelected(p, i))
1706 SelectRow(p, j, bMulti);
1710 SelectColumn(p, i, bMulti);
1716 // g_qeglobals.d_num_move_points = 0;
1717 // AddPoint(p, p->ctrl[i][j].xyz);
1719 if (bMulti)// if (g_bSameView) // this is not having desired effect
1721 RemovePointFromMoveList(v);
1734 Patch_UpdateSelected
1737 void Patch_UpdateSelected(vec3_t vMove)
1740 for (i=0 ; i < g_qeglobals.d_num_move_points ; i++)
1742 VectorAdd (g_qeglobals.d_move_points[i], vMove, g_qeglobals.d_move_points[i]);
1743 if (g_qeglobals.d_num_move_points == 1)
1748 //--patchMesh_t* p = &patchMeshes[g_nSelectedPatch];
1749 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
1753 patchMesh_t* p = pb->pPatch;
1755 #if 0 //moving to SelectCurvePointByRay
1756 g_qeglobals.d_numpoints = 0;
1757 for (i = 0 ; i < p->width ; i++ )
1759 for ( j = 0 ; j < p->height ; j++ )
1761 VectorCopy (p->ctrl[i][j].xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
1762 if (g_qeglobals.d_numpoints < MAX_POINTS-1)
1764 g_qeglobals.d_numpoints++;
1770 Patch_CalcBounds(p, vMin, vMax);
1771 Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
1774 //Brush_Free(p->pSymbiot);
1775 //Select_Brush(AddBrushForPatch(g_nSelectedPatch));
1785 void SampleSinglePatch (float ctrl[3][3][5], float u, float v, float out[5]) {
1790 // find the control points for the v coordinate
1791 for (vPoint = 0 ; vPoint < 3 ; vPoint++)
1793 for (axis = 0 ; axis < 5 ; axis++)
1798 a = ctrl[0][vPoint][axis];
1799 b = ctrl[1][vPoint][axis];
1800 c = ctrl[2][vPoint][axis];
1805 vCtrl[vPoint][axis] = qA * u * u + qB * u + qC;
1809 // interpolate the v value
1810 for (axis = 0 ; axis < 5 ; axis++)
1822 out[axis] = qA * v * v + qB * v + qC;
1826 //spog - Curve LOD stuff starts
1828 float ShadeForNormal(vec3_t normal)
1838 // quick diffuse shading
1839 f = DotProduct(L, normal);
1843 //if (f < 0.0f) f = 0.0f;
1850 void ShadeVertex (drawVert_t &p)
1852 p.lightmap[0] = ShadeForNormal(p.normal);
1856 void Patch_DrawNormals(patchMesh_t *patch)
1861 qglBegin (GL_LINES);
1862 for (col=0; col<patch->width; col++)
1864 for (row=0; row<patch->height; row++)
1866 VectorAdd(patch->ctrl[col][row].xyz, patch->ctrl[col][row].normal, vNormal);
1867 qglVertex3fv (patch->ctrl[col][row].xyz);
1868 qglVertex3fv (vNormal);
1875 // take an array of three drawVerts, and the addresses of three more drawVerts
1876 // interpolate new XYZST values from the three drawVerts, these are:
1877 // the left sub-control-point, the right sub-control-point and the midpoint of the curve respectively
1878 // store these values in the drawVerts passed to the function
1879 void Patch_CurveSplit(drawVert_t *vCurve[3], drawVert_t &pLeft, drawVert_t &pRight, drawVert_t &pMid, float u)
1884 drawVert_t v1, v2, v3;
1890 v1.xyz[i] = vCurve[1]->xyz[i] - vCurve[0]->xyz[i];
1891 v2.xyz[i] = vCurve[2]->xyz[i] - vCurve[1]->xyz[i];
1894 pLeft.xyz[i] = vCurve[0]->xyz[i] + v1.xyz[i];
1895 pRight.xyz[i] = vCurve[1]->xyz[i] + v2.xyz[i];
1897 v3.xyz[i] = pRight.xyz[i] - pLeft.xyz[i];
1899 pMid.xyz[i] = pLeft.xyz[i] + v3.xyz[i];
1901 // normal (weighted average) // no, that's b0rked
1902 //a = 1 / u; // total
1903 //b = u * a; // component 2
1904 //a = u - b; // component 1
1905 //pMid.normal[i] = u * ((vCurve[0]->normal[i] * b) + (vCurve[2]->normal[i] * a));
1910 v1.st[i] = vCurve[1]->st[i] - vCurve[0]->st[i];
1911 v2.st[i] = vCurve[2]->st[i] - vCurve[1]->st[i];
1914 pLeft.st[i] = vCurve[0]->st[i] + v1.st[i];
1915 pRight.st[i] = vCurve[1]->st[i] + v2.st[i];
1917 v3.st[i] = pRight.st[i] - pLeft.st[i];
1919 pMid.st[i] = pLeft.st[i] + v3.st[i];
1923 // take an array of three points, return an index representing the curvature of those three points
1924 // return zero if the curve is a straight line, unless the midpoint is not between the endpoints
1925 float Patch_CurveIndex(vec3_t vCurve[])
1927 vec3_t vTemp, v1, v2, v3, vClear;
1932 VectorClear(vClear);
1934 VectorSubtract(vCurve[2], vCurve[0], vTemp);
1935 VectorSubtract(vCurve[1], vCurve[0], v1);
1936 VectorSubtract(vCurve[2], vCurve[1], v2);
1938 if (VectorCompare(v1, vClear) || VectorCompare(vTemp, v1)) // return 0 if 1->2 == 0 or 1->2 == 1->3
1941 VectorNormalize(v1, v1);
1942 VectorNormalize(v2, v2);
1943 if (VectorCompare(v1, v2))
1946 VectorCopy(vTemp, v3);
1947 width = VectorNormalize(v3, v3);
1949 if (VectorCompare(v1, v3) && VectorCompare(v2, v3))
1952 dot = DotProduct(v1, v2);
1954 angle = acos(dot) / Q_PI;
1956 index = width * angle;
1962 // create a new tree root, give it the coordinate values of the drawVert
1963 // return a pointer to the new tree root
1964 BTNode_t *BTree_Create(drawVert_t info)
1966 BTNode_t *BTree = new BTNode_t;
1967 BTree->left = BTree->right = NULL;
1968 VectorCopy(info.xyz, BTree->info.xyz);
1969 VectorCopy(info.xyz, BTree->vMid.xyz);
1970 for (int i=0; i<2; i++)
1972 BTree->info.st[i] = info.st[i];
1973 BTree->vMid.st[i] = info.st[i];
1978 // take ownership of the subtree
1979 // delete the entire subtree
1980 // return a NULL pointer
1981 BTNode_t *BTree_Delete(BTNode_t *pBT)
1985 BTree_Delete(pBT->left);
1986 BTree_Delete(pBT->right);
1992 // NOT currently used
1993 BTNode_t *BTree_Clear(BTNode_t *pBT, bool bFirst = true)
1997 BTree_Clear(pBT->left, false);
1998 BTree_Clear(pBT->right, false);
1999 if (!bFirst) delete pBT;
2004 // take a pointer to the last item added to the list (this can also be a NULL pointer)
2005 // take a pointer to the root of a subtree, and the patch points to the left and right of it
2006 // add a new item to the subtree list, and add the subtree and its adjacent points to the new item
2007 // return a pointer to the last item added to the subtree list
2008 BTreeList_t *BTree_AddToList(BTreeList_t *pBTList, BTNode_t *pBT, drawVert_t &pLeft, drawVert_t &pRight)
2010 BTreeList_t *newBTList = new BTreeList_t;
2011 newBTList->next = pBTList;
2012 newBTList->pBT = pBT;
2013 VectorCopy(pLeft.xyz, newBTList->vLeft.xyz);
2014 VectorCopy(pRight.xyz, newBTList->vRight.xyz);
2015 VectorCopy(pLeft.normal, newBTList->vLeft.normal);
2016 VectorCopy(pRight.normal, newBTList->vRight.normal);
2017 for (int i=0; i<2; i++)
2019 newBTList->vLeft.st[i] = pLeft.st[i];
2020 newBTList->vRight.st[i] = pRight.st[i];
2025 // NOT currently used, subtrees are now stored on the patch
2026 // take ownership of the subtree list
2027 // delete the entire list and the subtrees it points to
2028 // return a NULL pointer
2029 BTreeList_t *BTree_DeleteList(BTreeList_t *pBTList)
2031 if (pBTList != NULL)
2033 BTree_DeleteList(pBTList->next);
2034 pBTList->pBT = BTree_Delete(pBTList->pBT);
2040 // take ownership of the subtree list
2041 // delete the entire subtree list, but not the subtrees themselves
2042 // return a NULL pointer
2043 BTreeList_t *BTree_DeletePointerList(BTreeList_t *pBTList)
2045 if (pBTList != NULL)
2047 BTree_DeletePointerList(pBTList->next);
2053 // take a pointer to the last item added to the list of subtree lists
2054 // add a subtree list to the list
2055 // return a pointer to the last item added
2056 BTListList_t *BTree_AddListToList(BTListList_t *pBTListList, BTreeList_t *pBTList)
2058 BTListList_t *newBTListList = new BTListList_t;
2059 newBTListList->next = pBTListList;
2060 newBTListList->list = pBTList;
2061 return newBTListList;
2065 // take ownership of the list of subtree lists
2066 // delete the entire list of lists, but not the subtrees themselves
2067 // return a NULL pointer
2068 BTListList_t *BTree_DeleteListFromList(BTListList_t *pBTListList)
2070 if (pBTListList != NULL)
2072 BTree_DeleteListFromList(pBTListList->next);
2073 pBTListList->list = BTree_DeletePointerList(pBTListList->list);
2079 // take a pointer to the last item in the list
2080 // add a NULL linker subtree to the list, setting the "flipped" flag using the left curvepoint normal .. er.. hacky?
2081 BTreeList_t *BTree_AddLinkToList(BTreeList_t *pBTList, bool bFlipped = false)
2083 BTreeList_t *linkBTList = new BTreeList_t;
2084 linkBTList->pBT = NULL;
2085 linkBTList->next = pBTList;
2086 linkBTList->vLeft.normal[0] = (bFlipped) ? 1.0f : 0.0f;
2091 // take an array of three points and the address of a vector
2092 // store midpoint of the bezier curve formed by the three points, in the vector
2093 void Patch_BezierInterpolate(vec3_t vCurve[], vec3_t &pMid)
2097 VectorSubtract(vCurve[2], vCurve[0], vTemp); // Start->End
2100 VectorAdd(vCurve[0], vTemp, vTemp); // midpoint of Start->End
2102 VectorSubtract(vTemp, vCurve[1], vTemp); // Mid->(midpoint of Start->End)
2105 VectorAdd(vCurve[1], vTemp, pMid); // midpoint of Mid->(midpoint of Start->End)
2109 // take a pointer to the list of subtrees, and a threshold value
2110 // generate REAL surface curvature for the subtree curves, using bezier interpolation
2111 // if any of the real curves has an index greater than the threshold, return true
2112 bool Patch_MostCurvedRow(BTreeList_t *pBTList, int threshold)
2115 float index;//, bestindex = 0;
2120 for (p = pBTList; p != NULL; p = p->next->next)
2123 VectorCopy(p->vLeft.xyz, vCurve[0]);
2124 VectorCopy(p->pBT->info.xyz, vCurve[1]);
2125 VectorCopy(p->vRight.xyz, vCurve[2]);
2127 index = Patch_CurveIndex(vCurve);
2128 if (index > threshold)
2131 if (p->next == NULL)
2134 if (p->next->pBT == NULL) continue;
2136 VectorCopy(p->vLeft.xyz, vCurve[0]);
2137 VectorCopy(p->next->vLeft.xyz, vCurve[1]);
2138 VectorCopy(p->next->next->vLeft.xyz, vCurve[2]);
2139 Patch_BezierInterpolate(vCurve, vRow[0]);
2141 VectorCopy(p->pBT->info.xyz, vCurve[0]);
2142 VectorCopy(p->next->pBT->info.xyz, vCurve[1]);
2143 VectorCopy(p->next->next->pBT->info.xyz, vCurve[2]);
2144 Patch_BezierInterpolate(vCurve, vRow[1]);
2146 VectorCopy(p->vRight.xyz, vCurve[0]);
2147 VectorCopy(p->next->vRight.xyz, vCurve[1]);
2148 VectorCopy(p->next->next->vRight.xyz, vCurve[2]);
2149 Patch_BezierInterpolate(vCurve, vRow[2]);
2151 index = Patch_CurveIndex(vRow);
2152 if (index > threshold)
2159 // take a pointer to a list of subtrees.. each subtree in the list is a 3-point bezier curve formed by two endpoints owned by the list, and a midpoint subtree node owned by a patch.
2160 // if any of the subtrees are curved above a threshold, create a left and right subsubtree for each subtree in the list.
2161 // if a NULL linker subtree is found, check for an orientation flip - ie. an inverted LOD-match - and create a NULL subsubtree with the same orientation flip
2162 // this effectively generates trees for multiple patches at the same time.. the subtrees are always owned by their respective patches though
2163 void BTree_ListCurveRecurse(BTreeList_t *pBTList)
2166 BTreeList_t *leftBTList, *rightBTList;
2167 //drawVert_t pLeft, pRight, pMid;
2168 drawVert_t *vCurve[3];
2171 bool bFlipped = false;
2173 if (g_PrefsDlg.m_nSubdivisions >= 1)
2174 threshold = g_PrefsDlg.m_nSubdivisions;
2178 leftBTList = rightBTList = NULL;
2180 if (Patch_MostCurvedRow(pBTList, threshold)) // split all subtrees in list if any subtree is above threshold
2183 // traverse nodes in list
2184 for (p = pBTList; p != NULL; p=p->next)
2188 leftBTList = BTree_AddLinkToList(leftBTList, (p->vLeft.normal[0] == 1.0f));
2189 rightBTList = BTree_AddLinkToList(rightBTList, (p->vLeft.normal[0] == 1.0f));
2190 if (p->vLeft.normal[0] == 1.0f) bFlipped = (!bFlipped) ? true : false; // switch bFlipped if true
2194 // create left node for this subtree
2195 BTNode_t *newLeft = new BTNode_t;
2196 p->pBT->left = newLeft;
2197 newLeft->left = newLeft->right = NULL;
2199 // create right node for this subtree
2200 BTNode_t *newRight = new BTNode_t;
2201 p->pBT->right = newRight;
2202 newRight->left = newRight->right = NULL;
2205 vCurve[0] = &p->vLeft;
2206 vCurve[1] = &p->pBT->info;
2207 vCurve[2] = &p->vRight;
2208 Patch_CurveSplit(vCurve, newLeft->info, newRight->info, p->pBT->vMid, 0.5);
2210 memcpy(&newLeft->vMid, &newLeft->info, sizeof(drawVert_t));
2211 memcpy(&newRight->vMid, &newRight->info, sizeof(drawVert_t));
2216 // add new left subtree to left subtree list
2217 leftBTList = BTree_AddToList(leftBTList, newLeft, p->vLeft, p->pBT->vMid);
2219 // add new right subtree to right subtree list
2220 rightBTList = BTree_AddToList(rightBTList, newRight, p->pBT->vMid, p->vRight);
2224 // add new left subtree to right subtree list
2225 rightBTList = BTree_AddToList(rightBTList, newLeft, p->vLeft, p->pBT->vMid);
2227 // add new right subtree to left subtree list
2228 leftBTList = BTree_AddToList(leftBTList, newRight, p->pBT->vMid, p->vRight);
2232 // continue tree left
2233 BTree_ListCurveRecurse(leftBTList);
2234 leftBTList = BTree_DeletePointerList(leftBTList);
2236 // continue tree right
2237 BTree_ListCurveRecurse(rightBTList);
2238 rightBTList = BTree_DeletePointerList(rightBTList);
2242 // take mins and maxs values from two brushes
2243 // return true if they intersect on every axis
2244 bool TouchingAABBs(vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2)
2247 vec3_t v1, v2, p1, p2, T;
2248 for (int i=0; i<3; i++)
2250 v1[i] = maxs1[i] - mins1[i];
2251 v2[i] = maxs2[i] - mins2[i];
2254 p1[i] = mins1[i] + v1[i];
2255 p2[i] = mins2[i] + v2[i];
2256 // p1 == origin of aabb1
2257 // p2 == origin of aabb1
2258 // v1 == displacement of aabb1
2259 // v1 == displacement of aabb2
2260 T[i] = p2[i] - p1[i]; // T == vector from aabb1 to aabb2
2261 if ( fabs(T[i]) > (fabs(v1[i]) + fabs(v2[i])) )
2267 // take a pointer to the last item added to pBTList, a pointer to the patch, a row index (start) and a column index
2268 // generate a row of row-curve tree roots, owned by the patch and add the entire column of row-curves to the list, using the row index to decide the order to add
2269 // return a pointer to the last item added to the list
2270 BTreeList_t *Patch_CreateBTListForRows(BTreeList_t *pBTList, patchMesh_t *patch, int start, int col)
2273 patch->colDirty[(col-1)/2] = true;
2277 for (row=0; row<patch->height; row++)
2279 pos = (((col-1)/2)*patch->height)+row;
2280 patch->rowLOD[pos] = BTree_Delete(patch->rowLOD[pos]);
2281 patch->rowLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2282 pBTList = BTree_AddToList(pBTList, patch->rowLOD[pos], patch->ctrl[col-1][row], patch->ctrl[col+1][row]);
2287 for (row=patch->height-1; row>=0; row--)
2289 pos = (((col-1)/2)*patch->height)+row;
2290 patch->rowLOD[pos] = BTree_Delete(patch->rowLOD[pos]);
2291 patch->rowLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2292 pBTList = BTree_AddToList(pBTList, patch->rowLOD[pos], patch->ctrl[col-1][row], patch->ctrl[col+1][row]);
2298 // take a pointer to the last item added to pBTList, a pointer to the patch, a row index and a column index (start)
2299 // generate a row of column-curve tree roots, owned by the patch and add the entire row of column-curves to the list, using the column index to decide the order to add
2300 // return a pointer to the last item added to the list
2301 BTreeList_t *Patch_CreateBTListForCols(BTreeList_t *pBTList, patchMesh_t *patch, int row, int start)
2304 patch->rowDirty[(row-1)/2] = true;
2308 for (col=0; col<patch->width; col++)
2310 pos = (((row-1)/2)*patch->width)+col;
2311 patch->colLOD[pos] = BTree_Delete(patch->colLOD[pos]);
2312 patch->colLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2313 pBTList = BTree_AddToList(pBTList, patch->colLOD[pos], patch->ctrl[col][row-1], patch->ctrl[col][row+1]);
2318 for (col=patch->width-1; col>=0; col--)
2320 pos = (((row-1)/2)*patch->width)+col;
2321 patch->colLOD[pos] = BTree_Delete(patch->colLOD[pos]);
2322 patch->colLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2323 pBTList = BTree_AddToList(pBTList, patch->colLOD[pos], patch->ctrl[col][row-1], patch->ctrl[col][row+1]);
2330 bool BTree_IsInList(BTreeList_t *pBTList, BTNode_t *pBT)
2333 if (pBTList == NULL) return false;
2335 for (p=pBTList; p != NULL; p=p->next)
2346 int Patch_DegenCurve(vec3_t &start, vec3_t &mid, vec3_t &end)
2348 if (VectorCompare(start, mid) || VectorCompare(end, mid))
2350 if (VectorCompare(start, end)) return 2;
2356 // take a pointer to the last item added to the list, and a pointer to a patch (this patch is the owner of the three drawverts)
2357 // take the addresses of three drawVerts, and compare them with the edges of all patches that touch the patch
2358 // if they match an edge, add the tree roots for that section of the matched patch to the list, and recurse for the opposite edge of that patch section. Also, set the matched patch Dirty, so that its drawlists will be rebuilt
2359 // return a pointer to the last item added
2360 BTreeList_t *Patch_FindLODMatches(patchMesh_t *patch, BTreeList_t *pBTList, drawVert_t &pMid, drawVert_t &pLeft, drawVert_t &pRight)
2362 brush_t *pb, *brushlist;
2363 int row, col, i;//, pos;
2364 vec3_t vTemp, v1, v2;//, vClear;
2367 //Sys_Printf("Patch_FindLODMatches: called\n");
2369 if (VectorCompare(pMid.xyz, pLeft.xyz) && VectorCompare(pMid.xyz, pRight.xyz))
2372 //VectorClear(vClear);
2373 VectorSubtract(pRight.xyz, pLeft.xyz, vTemp);
2374 VectorSubtract(pMid.xyz, pLeft.xyz, v1);
2375 VectorSubtract(pRight.xyz, pMid.xyz, v2);
2377 //if (VectorCompare(v1, vClear) || VectorCompare(vTemp, v1)) // return null if 1->2 == 0 or 1->2 == 1->3
2380 VectorNormalize(v1, v1);
2381 VectorNormalize(v2, v2);
2382 if (VectorCompare(v1, v2))
2385 VectorNormalize(vTemp, vTemp);
2386 if (VectorCompare(v1, vTemp) && VectorCompare(v2, vTemp))
2389 brushlist = &active_brushes;
2392 for (pb = brushlist->next; pb != brushlist; pb=pb->next)
2394 if (!pb->patchBrush || pb->pPatch == patch)
2397 // ignore this patch if its AABB does not touch the subject patch
2398 if (!TouchingAABBs(patch->pSymbiot->maxs, patch->pSymbiot->mins, pb->maxs, pb->mins))
2401 // all columns of curves
2402 for (col=1; col<pb->pPatch->width; col+=2)
2404 if (pb->pPatch->colDirty[(col-1)/2]) continue;
2406 bAlreadyAdded = false;
2408 // top and bottom curves of this column
2409 for (row=0; row<pb->pPatch->height; row+=pb->pPatch->height-1)
2413 //if (!BTree_IsInList(pBTList, pb->pPatch->rowLOD[(((col-1)/2)*patch->height)+row]))
2415 // ignore this curve if it shares no mid ctrl point with the test curve
2416 if (!VectorCompare (pb->pPatch->ctrl[col][row].xyz, pMid.xyz))
2418 // ignore this curve if it is degenerate
2419 if (VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col-1][row].xyz) || VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col+1][row].xyz))
2421 // if curve matches the test curve directly
2422 if (VectorCompare (pb->pPatch->ctrl[col-1][row].xyz, pLeft.xyz) && VectorCompare (pb->pPatch->ctrl[col+1][row].xyz, pRight.xyz))
2424 // add a blank link as separator
2425 pBTList = BTree_AddLinkToList(pBTList);
2426 // add this entire column, if top, top-to-bottom, else bottom to top
2427 pBTList = Patch_CreateBTListForRows(pBTList, pb->pPatch, row, col);
2428 // continue checking from last curve added to list
2429 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2431 pb->pPatch->LODUpdated = true;
2432 bAlreadyAdded = true;
2434 // if curve matches test curve but flipped
2435 else if (VectorCompare (pb->pPatch->ctrl[col-1][row].xyz, pRight.xyz) && VectorCompare (pb->pPatch->ctrl[col+1][row].xyz, pLeft.xyz))
2437 pBTList = BTree_AddLinkToList(pBTList, true); // flip
2438 pBTList = Patch_CreateBTListForRows(pBTList, pb->pPatch, row, col);
2439 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2440 pb->pPatch->LODUpdated = true;
2441 bAlreadyAdded = true;
2446 // all rows of curves
2447 for (row=1; row<pb->pPatch->height; row+=2)
2449 if (pb->pPatch->rowDirty[(row-1)/2]) continue;
2451 bAlreadyAdded = false;
2453 for (col=0; col<pb->pPatch->width; col+=pb->pPatch->width-1)
2457 //if (BTree_IsInList(pBTList, pb->pPatch->colLOD[(((row-1)/2)*patch->width)+col]))
2459 if (!VectorCompare (pb->pPatch->ctrl[col][row].xyz, pMid.xyz))
2461 if (VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col][row-1].xyz) || VectorCompare (pb->pPatch->ctrl[col][row].xyz, pb->pPatch->ctrl[col][row+1].xyz))
2463 if (VectorCompare (pb->pPatch->ctrl[col][row-1].xyz, pLeft.xyz) && VectorCompare (pb->pPatch->ctrl[col][row+1].xyz, pRight.xyz))
2465 pBTList = BTree_AddLinkToList(pBTList);
2466 pBTList = Patch_CreateBTListForCols(pBTList, pb->pPatch, row, col);
2467 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2468 pb->pPatch->LODUpdated = true;
2469 bAlreadyAdded = true;
2471 else if (VectorCompare (pb->pPatch->ctrl[col][row-1].xyz, pRight.xyz) && VectorCompare (pb->pPatch->ctrl[col][row+1].xyz, pLeft.xyz))
2473 pBTList = BTree_AddLinkToList(pBTList, true); // flip
2474 pBTList = Patch_CreateBTListForCols(pBTList, pb->pPatch, row, col);
2475 pBTList = Patch_FindLODMatches(pb->pPatch, pBTList, pBTList->pBT->info, pBTList->vLeft, pBTList->vRight);
2476 pb->pPatch->LODUpdated = true;
2477 bAlreadyAdded = true;
2482 brushlist = &selected_brushes;
2487 // take a pointer to a patch
2488 // create tree roots for all the rows and columns of curves in the patch, the patch takes ownership of these new tree roots
2489 // generate lists of pointers to all the trees in all the patches in the map which need to match the LOD of trees owned by this patch
2490 // store all the lists in a list of lists
2491 // recursively generate the rest of every tree in each list in the list
2492 void Patch_CreateLODTrees(patchMesh_t *patch)
2494 BTreeList_t *pBTList;
2495 int col, row, pos;//, rowcount, colcount;
2496 BTListList_t *pLists;
2498 //Sys_Printf("Patch_CreateMatchedLODTrees: called\n");
2500 BTListList_t *LODLists;
2505 patch->bDirty = false;
2506 patch->LODUpdated = true;
2508 for(col=1; col<patch->width; col+=2)
2510 if (patch->colDirty[(col-1)/2]) continue;
2511 else patch->colDirty[(col-1)/2] = true;
2513 // create list for rows of current patch
2514 for(row=0; row<patch->height; row++)
2516 pos = (((col-1)/2)*patch->height)+row;
2517 patch->rowLOD[pos] = BTree_Delete(patch->rowLOD[pos]);
2518 patch->rowLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2519 pBTList = BTree_AddToList(pBTList, patch->rowLOD[pos], patch->ctrl[col-1][row], patch->ctrl[col+1][row]);
2522 //create connection list for first row
2523 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[col][0], patch->ctrl[col-1][0], patch->ctrl[col+1][0]);
2524 //create connection list for last row
2525 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[col][row-1], patch->ctrl[col-1][row-1], patch->ctrl[col+1][row-1]);
2527 LODLists = BTree_AddListToList(LODLists, pBTList);
2532 for(row=1; row<patch->height; row+=2)
2534 if (patch->rowDirty[(row-1)/2]) continue;
2535 else patch->rowDirty[(row-1)/2] = true;
2537 // create list for cols of current patch
2538 for(col=0; col<patch->width; col++)
2540 pos = (((row-1)/2)*patch->width)+col;
2541 patch->colLOD[pos] = BTree_Delete(patch->colLOD[pos]);
2542 patch->colLOD[pos] = BTree_Create(patch->ctrl[col][row]);
2543 pBTList = BTree_AddToList(pBTList, patch->colLOD[pos], patch->ctrl[col][row-1], patch->ctrl[col][row+1]);
2546 //create connection list for first col
2547 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[0][row], patch->ctrl[0][row-1], patch->ctrl[0][row+1]);
2548 //create connection list for last col
2549 pBTList = Patch_FindLODMatches(patch, pBTList, patch->ctrl[col-1][row], patch->ctrl[col-1][row-1], patch->ctrl[col-1][row+1]);
2551 LODLists = BTree_AddListToList(LODLists, pBTList);
2555 for (pLists = LODLists; pLists != NULL; pLists=pLists->next)
2556 BTree_ListCurveRecurse(pLists->list);
2557 LODLists = BTree_DeleteListFromList(LODLists);
2560 int Patch_GetCVTangent(vec3_t &v1, vec3_t &p1, vec3_t &p2, vec3_t &p3)
2562 if (VectorCompare(p1, p2))
2564 if (VectorCompare(p1, p3))
2568 else VectorSubtract(p3, p1, v1);
2571 else VectorSubtract(p2, p1, v1);
2575 void Patch_CVNormal(vec3_t ctrl[3][3], vec3_t &normal)
2577 vec3_t v1, v2, vTemp1, vTemp2;
2580 a = Patch_GetCVTangent(v1, ctrl[0][0], ctrl[1][0], ctrl[2][0]);
2581 b = Patch_GetCVTangent(v2, ctrl[0][0], ctrl[0][1], ctrl[0][2]);
2583 //Sys_Printf("p1: (%1.1f %1.1f %1.1f) p2: (%1.1f %1.1f %1.1f) p2: (%1.1f %1.1f %1.1f)\n",
2584 // ctrl[0][0][0], ctrl[0][0][1], ctrl[0][0][2], ctrl[0][2][0], ctrl[0][2][1], ctrl[0][2][2], ctrl[2][0][0], ctrl[2][0][1], ctrl[2][0][2]);
2586 v1[0] = v1[1] = v1[2] = v2[0] = v2[1] = v2[2] = 0;
2590 a = Patch_GetCVTangent(v1, ctrl[0][0], ctrl[1][1], ctrl[1][2]);
2594 b = Patch_GetCVTangent(v2, ctrl[0][0], ctrl[1][1], ctrl[2][1]);
2599 a = Patch_GetCVTangent(v1, ctrl[0][0], ctrl[2][1], ctrl[2][2]);
2603 b = Patch_GetCVTangent(v2, ctrl[0][0], ctrl[1][2], ctrl[2][2]);
2606 CrossProduct(v1, v2, normal);
2609 if (normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f)
2611 // more degenerate cases
2615 if (VectorCompare(ctrl[0][0], ctrl[2][0])) // endcap left
2617 if (VectorCompare(ctrl[0][2], ctrl[1][2]))
2619 VectorSubtract(ctrl[2][2], ctrl[0][0], v2);
2621 else if (VectorCompare(ctrl[1][2], ctrl[2][2]))
2623 VectorSubtract(ctrl[0][2], ctrl[0][0], v2);
2626 a = Patch_DegenCurve(ctrl[0][2], ctrl[1][2], ctrl[2][2]);
2629 VectorCopy(ctrl[0][2], vCurve[0]);
2630 VectorCopy(ctrl[1][2], vCurve[1]);
2631 VectorCopy(ctrl[2][2], vCurve[2]);
2632 Patch_BezierInterpolate(vCurve, pMid);
2633 VectorSubtract(pMid, ctrl[0][0], v1);
2638 else if (VectorCompare(ctrl[0][0], ctrl[0][2])) // endcap right
2641 if (VectorCompare(ctrl[2][0], ctrl[2][1]))
2643 VectorSubtract(ctrl[2][2], ctrl[0][0], v2);
2645 else if (VectorCompare(ctrl[2][1], ctrl[2][2]))
2647 VectorSubtract(ctrl[2][0], ctrl[0][0], v2);
2651 b = Patch_DegenCurve(ctrl[2][0], ctrl[2][1], ctrl[2][2]);
2654 VectorCopy(ctrl[2][0], vCurve[0]);
2655 VectorCopy(ctrl[2][1], vCurve[1]);
2656 VectorCopy(ctrl[2][2], vCurve[2]);
2657 Patch_BezierInterpolate(vCurve, pMid);
2658 VectorSubtract(pMid, ctrl[0][0], v2);
2663 if (VectorCompare(ctrl[0][0], ctrl[2][0])) // bottom degen
2665 Patch_GetCVTangent(v1, ctrl[0][0], ctrl[2][1], ctrl[2][2]);
2667 else if (VectorCompare(ctrl[0][0], ctrl[0][2])) // left degen
2669 Patch_GetCVTangent(v2, ctrl[0][0], ctrl[1][2], ctrl[2][2]);
2671 else if (VectorCompare(ctrl[0][2], ctrl[2][2])) // top degen
2673 VectorSubtract(ctrl[2][0], ctrl[0][0], v1);
2675 else if (VectorCompare(ctrl[2][0], ctrl[2][2])) // right degen
2677 VectorSubtract(ctrl[0][2], ctrl[0][0], v2);
2679 else // tangents parallel
2681 VectorCopy(v1, vTemp1);
2682 VectorCopy(v2, vTemp2);
2683 VectorNormalize(vTemp1, vTemp1);
2684 VectorNormalize(vTemp2, vTemp2);
2685 if (VectorCompare(vTemp1, vTemp2)) // parallel same way
2687 VectorSubtract(ctrl[2][0], ctrl[0][0], vTemp1);
2688 VectorNormalize(vTemp1, vTemp1);
2689 if (VectorCompare(vTemp1, vTemp2))
2691 VectorSubtract(ctrl[0][2], ctrl[0][0], v2);
2695 VectorCopy(vTemp1, v1);
2698 else // parallel opposite way
2700 VectorCopy(ctrl[2][0], vCurve[0]);
2701 VectorCopy(ctrl[1][1], vCurve[1]);
2702 VectorCopy(ctrl[0][2], vCurve[2]);
2703 Patch_BezierInterpolate(vCurve, pMid);
2704 VectorSubtract(pMid, ctrl[0][0], v2);
2708 CrossProduct(v1, v2, normal);
2712 void Patch_CalcCVNormals(patchMesh_t *patch)
2714 int row, col, i, j, n;
2718 for (col=0; col<patch->width; col+=2)
2720 for (row=0; row<patch->height; row+=2)
2723 if (col+1 != patch->width && row+1 != patch->height)
2727 VectorCopy (patch->ctrl[col+i][row+j].xyz, ctrl[i][j]);
2729 Patch_CVNormal(ctrl, normals[n]);
2730 VectorNormalize(normals[n], normals[n]);
2734 if (col-1 >= 0 && row-1 >= 0)
2738 VectorCopy (patch->ctrl[col-i][row-j].xyz, ctrl[i][j]);
2740 Patch_CVNormal(ctrl, normals[n]);
2741 VectorNormalize(normals[n], normals[n]);
2744 if (col-1 >= 0 && row+1 != patch->height)
2748 VectorCopy (patch->ctrl[col-i][row+j].xyz, ctrl[j][i]);
2750 Patch_CVNormal(ctrl, normals[n]);
2751 VectorNormalize(normals[n], normals[n]);
2754 if (col+1 != patch->width && row-1 >= 0)
2758 VectorCopy (patch->ctrl[col+i][row-j].xyz, ctrl[j][i]);
2760 Patch_CVNormal(ctrl, normals[n]);
2761 VectorNormalize(normals[n], normals[n]);
2767 if (n == 1) patch->ctrl[col][row].normal[i] = normals[0][i];
2768 if (n == 2) patch->ctrl[col][row].normal[i] = (normals[0][i] + normals[1][i]) / n;
2769 //if (n == 3) patch->ctrl[col][row].normal[i] = (normals[0][i] + normals[1][i] + normals[2][i]) / n;
2770 if (n == 4) patch->ctrl[col][row].normal[i] = (normals[0][i] + normals[1][i] + normals[2][i] + normals[3][i]) / n;
2772 VectorNormalize(patch->ctrl[col][row].normal, patch->ctrl[col][row].normal);
2773 //if (!g_PrefsDlg.m_bGLLighting)
2774 // ShadeVertex(patch->ctrl[col][row]);
2780 void BTree_SetNormals(BTNode_t *pBT, vec3_t &normal)
2784 if (pBT->left != NULL && pBT->right != NULL)
2786 VectorCopy(normal, pBT->vMid.normal);
2787 //if (!g_PrefsDlg.m_bGLLighting)
2788 // ShadeVertex(pBT->vMid);
2790 BTree_SetNormals(pBT->left, normal);
2791 BTree_SetNormals(pBT->right, normal);
2796 void NormalFromPoints(vec3_t p1, vec3_t p2, vec3_t p3, vec3_t &normal, bool flip = false)
2802 VectorSubtract(p2, p3, v1); //p3->p2
2803 VectorSubtract(p1, p2, v2); //p2->p1
2807 VectorSubtract(p2, p1, v1); //p1->p2
2808 VectorSubtract(p3, p2, v2); //p2->p3
2810 CrossProduct(v1, v2, normal);
2814 void BTree_GenerateNormals(BTNode_t *pBTMid, BTNode_t *pBTLeft, BTNode_t *pBTRight, bool avg, bool flat, bool nomid, bool noleft, bool noright, /*bool endcap, vec3_t &n1, vec3_t &n2,*/ bool flip)
2818 if (pBTMid->left != NULL && pBTMid->right != NULL)
2822 if (noleft) // left curve is degenerate
2824 if (nomid) // mid curve is degenerate
2826 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2827 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2831 // VectorCopy(n1, normal);
2832 // NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2836 NormalFromPoints(pBTMid->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2837 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2840 else if (noright) // right curve is degenerate
2842 if (nomid) // mid curve is degenerate
2844 NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2845 NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2849 // NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2850 // VectorCopy(n2, pBTRight->vMid.normal);
2854 NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2855 NormalFromPoints(pBTMid->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2860 if (flat) // all curves are semi-degenerate (flat) or degenerate
2862 NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTRight->vMid.xyz, normal, flip);
2863 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTLeft->vMid.xyz, pBTRight->vMid.normal, flip);
2867 NormalFromPoints(pBTLeft->left->info.xyz, pBTLeft->vMid.xyz, pBTMid->vMid.xyz, normal, flip);
2868 NormalFromPoints(pBTRight->right->info.xyz, pBTRight->vMid.xyz, pBTMid->vMid.xyz, pBTRight->vMid.normal, flip);
2872 VectorNormalize(normal, normal);
2874 for (int i=0; i<3; i++)
2875 pBTLeft->vMid.normal[i] = (normal[i] + pBTLeft->vMid.normal[i]) / 2.0f;
2876 else VectorCopy(normal, pBTLeft->vMid.normal);
2878 VectorNormalize(pBTLeft->vMid.normal, pBTLeft->vMid.normal);
2879 VectorNormalize(pBTRight->vMid.normal, pBTRight->vMid.normal);
2882 BTree_GenerateNormals(pBTMid->left, pBTLeft->left, pBTRight->left, avg, flat, nomid, noleft, noright, /*endcap, n1, n2,*/ flip);
2883 BTree_GenerateNormals(pBTMid->right, pBTLeft->right, pBTRight->right, avg, flat, nomid, noleft, noright, /*endcap, n1, n2,*/ flip);
2889 void Patch_GenerateLODNormals(patchMesh_t *patch)
2891 int col, row, rowpos, colpos, i;
2892 BTNode_t *tree[2][3];
2894 bool rowAvg, colAvg;
2896 for(col=0; col+2<patch->width; col+=2)
2898 for(row=0; row+2<patch->height; row+=2)
2900 if (!patch->colDirty[col/2] && !patch->rowDirty[row/2]) continue;
2902 rowpos = ((col/2)*patch->height)+row;
2903 colpos = ((row/2)*patch->width)+col;
2905 if (row==0) rowAvg = false;
2907 if (col==0) colAvg = false;
2912 tree[0][i] = patch->rowLOD[rowpos+i];
2913 tree[1][i] = patch->colLOD[colpos+i];
2915 degen[0][i] = Patch_DegenCurve(patch->ctrl[col][row+i].xyz, patch->ctrl[col+1][row+i].xyz, patch->ctrl[col+2][row+i].xyz);
2916 degen[1][i] = Patch_DegenCurve(patch->ctrl[col+i][row].xyz, patch->ctrl[col+i][row+1].xyz, patch->ctrl[col+i][row+2].xyz);
2919 BTree_GenerateNormals(tree[0][1], tree[0][0], tree[0][2], rowAvg, (degen[1][0] && degen[1][1] && degen[1][2]), degen[0][1] == 2, degen[0][0] == 2, degen[0][2] == 2, /*degen[1][1], patch->ctrl[col][row].normal, patch->ctrl[col][row+2].normal,*/ false);
2920 BTree_GenerateNormals(tree[1][1], tree[1][0], tree[1][2], colAvg, (degen[0][0] && degen[0][1] && degen[0][2]), degen[1][1] == 2, degen[1][0] == 2, degen[1][2] == 2, /*degen[0][1], patch->ctrl[col][row].normal, patch->ctrl[col+2][row].normal,*/ true);
2926 void Patch_ClearLODFlags(patchMesh_t *p)
2930 for (i=0;i<(p->width-1)/2; i++)
2931 p->colDirty[i] = false;
2933 for (i=0;i<(p->height-1)/2; i++)
2934 p->rowDirty[i] = false;
2937 // reset the lodDirty flags owned by all patches in the map
2938 // create new LOD trees for all dirty patches, matched with all other patches in the map
2939 void Patch_LODMatchAll()
2941 brush_t *pb, *brushlist;
2944 // create LOD tree roots and LOD tree lists for all patches that are dirty
2946 brushlist = &active_brushes;
2949 for (pb = brushlist->next; pb && (pb != brushlist); pb=pb->next)
2951 // create lod for selected patches when patches are filtered
2952 if (pb->bFiltered && (pb->patchBrush && !pb->pPatch->bSelected))
2954 if (!pb->patchBrush)
2956 if (!pb->pPatch->bDirty)
2959 Patch_CalcCVNormals(pb->pPatch);
2960 Patch_CreateLODTrees(pb->pPatch);
2962 brushlist = &selected_brushes;
2965 brushlist = &active_brushes;
2968 for (pb = brushlist->next; pb && (pb != brushlist); pb=pb->next)
2970 if (!pb->patchBrush)
2973 if (pb->pPatch->LODUpdated)
2974 Patch_GenerateLODNormals(pb->pPatch);
2976 Patch_ClearLODFlags(pb->pPatch);
2978 brushlist = &selected_brushes;
2983 void Vertex_TransformTexture(drawVert_t *pVert, float fx, float fy, transformtype xform)
2996 float x = pVert->st[0];
2997 float y = pVert->st[1];
2998 pVert->st[0] = x * fx - y * fy;
2999 pVert->st[1] = y * fx + x * fy;
3003 void BTree_TransformTexture(BTNode_t *pBT, float fx, float fy, transformtype xform)
3006 { // PreOrder traversal
3007 Vertex_TransformTexture(&pBT->info, fx, fy, xform);
3008 Vertex_TransformTexture(&pBT->vMid, fx, fy, xform);
3009 BTree_TransformTexture(pBT->left, fx, fy, xform);
3010 BTree_TransformTexture(pBT->right, fx, fy, xform);
3014 void Patch_TransformLODTexture(patchMesh_t *p, float fx, float fy, transformtype xform)
3018 for(col=1; col<p->width; col+=2)
3019 for(row=0; row<p->height; row++)
3020 BTree_TransformTexture(p->rowLOD[(((col-1)/2)*p->height)+row], fx, fy, xform);
3022 for(row=1; row<p->height; row+=2)
3023 for(col=0; col<p->width; col++)
3024 BTree_TransformTexture(p->colLOD[(((row-1)/2)*p->width)+col], fx, fy, xform);
3027 void Patch_AddBTreeToDrawListInOrder(list<drawVert_t> *drawList, BTNode_t *pBT)
3029 if (pBT != NULL) //traverse InOrder
3031 Patch_AddBTreeToDrawListInOrder(drawList, pBT->left);
3032 if (pBT->left != NULL && pBT->right != NULL)
3033 drawList->push_back(pBT->vMid);
3034 Patch_AddBTreeToDrawListInOrder(drawList, pBT->right);
3038 void Patch_InterpolateListFromRowBT(list<drawVert_t> *drawList, BTNode_t *rowBT, BTNode_t *rowBTLeft, drawVert_t *vCurve[], float u, float n, float v)
3042 Patch_InterpolateListFromRowBT(drawList, rowBT->left, rowBTLeft->left, vCurve, u-n, n*0.5f, v);
3043 if (rowBT->left != NULL && rowBT->right != NULL)
3046 drawVert_t newVert, vTemp1, vTemp2;
3047 Patch_CurveSplit(vCurve, vTemp1, vTemp2, newVert, u);
3048 for (int i=0; i<3; i++)
3050 v1[i] = rowBT->vMid.xyz[i] - rowBTLeft->vMid.xyz[i]; // left -> mid
3051 v1[i] = rowBTLeft->vMid.xyz[i] + (v1[i] * v);
3052 v1[i] = newVert.xyz[i] - v1[i];
3054 VectorSubtract(vTemp1.xyz, newVert.xyz, v2);
3055 CrossProduct(v1, v2, newVert.normal);
3056 VectorNormalize(newVert.normal, newVert.normal);
3057 //if (!g_PrefsDlg.m_bGLLighting)
3058 // ShadeVertex(newVert);
3059 drawList->push_back(newVert);
3061 Patch_InterpolateListFromRowBT(drawList, rowBT->right, rowBTLeft->right, vCurve, u+n, n*0.5f, v);
3065 void Patch_TraverseColBTInOrder(list<list<drawVert_t>*>::iterator& iter, BTNode_t *colBTLeft, BTNode_t *colBT, BTNode_t *colBTRight, BTNode_t *rowBT, BTNode_t *rowBTLeft, float v, float n)
3069 //traverse subtree In Order
3070 Patch_TraverseColBTInOrder(iter, colBTLeft->left, colBT->left, colBTRight->left, rowBT, rowBTLeft, v-n, n*0.5f);
3071 if (colBT->left != NULL && colBT->right != NULL)
3073 drawVert_t *vCurve[3];
3074 vCurve[0] = &colBTLeft->vMid;
3075 vCurve[1] = &colBT->vMid;
3076 vCurve[2] = &colBTRight->vMid;
3077 Patch_InterpolateListFromRowBT((*iter), rowBT, rowBTLeft, vCurve, 0.5f, 0.25f, v);
3079 (*iter)->push_back(colBTRight->vMid);
3082 Patch_TraverseColBTInOrder(iter, colBTLeft->right, colBT->right, colBTRight->right, rowBT, rowBTLeft, v+n, n*0.5f);
3087 void Patch_StartDrawLists(list<list<drawVert_t>*> *drawLists, BTNode_t *colBT)
3091 //traverse subtree In Order
3092 Patch_StartDrawLists(drawLists, colBT->left);
3093 if (colBT->left != NULL && colBT->right != NULL)
3095 list<drawVert_t> *newList = new list<drawVert_t>;
3096 drawLists->push_back(newList); // add empty list to back
3097 drawLists->back()->push_back(colBT->vMid);
3099 Patch_StartDrawLists(drawLists, colBT->right);
3103 typedef list<drawVert_t> drawList_t;
3104 typedef list<list<drawVert_t>*> drawLists_t;
3106 void Patch_CreateDrawLists(patchMesh_t *patch)
3108 int col, row, colpos, rowpos;
3110 drawLists_t *drawLists = new drawLists_t;
3112 drawLists_t::iterator iter1, iter2;
3114 for (row=0; row<patch->height; row+=2)
3116 colpos = (row/2)*patch->width;
3117 drawList_t *newList = new drawList_t;
3118 drawLists->push_back(newList); // add a new empty list to back
3119 drawLists->back()->push_back(patch->ctrl[0][row]); // fill list at back
3121 if (row+1 == patch->height)
3123 Patch_StartDrawLists(drawLists, patch->colLOD[colpos]);
3126 iter1 = drawLists->begin();
3127 for (row=0; row<patch->height; row+=2)
3130 for (col=0; col+1<patch->width; col+=2)
3133 colpos = ((row/2)*patch->width)+col;
3134 rowpos = ((col/2)*patch->height)+row;
3136 Patch_AddBTreeToDrawListInOrder((*iter1), patch->rowLOD[rowpos]);
3137 (*iter1)->push_back(patch->ctrl[col+2][row]);
3139 if (row+1 == patch->height)
3144 Patch_TraverseColBTInOrder(iter1, patch->colLOD[colpos], patch->colLOD[colpos+1], patch->colLOD[colpos+2], patch->rowLOD[rowpos+1], patch->rowLOD[rowpos], 0.5, 0.25);
3148 patch->drawLists = drawLists;
3152 void Patch_DeleteDrawLists(patchMesh_t *patch)
3154 drawLists_t *drawLists;
3155 drawLists_t::iterator iter;
3157 if (patch->drawLists == NULL)
3160 drawLists = (drawLists_t *)patch->drawLists;
3162 for (iter=drawLists->begin(); iter != drawLists->end(); iter++)
3168 patch->drawLists = NULL;
3172 void Patch_DrawLODPatchMesh(patchMesh_t *patch)
3174 drawLists_t *drawLists;
3176 drawLists_t::iterator iterLists, iterListsNext;
3177 drawList_t::iterator iterList, iterListNext;
3179 //int nGLState = g_pParentWnd->GetCamera()->Camera()->draw_glstate;
3181 if (patch->drawLists == NULL)
3184 drawLists = (drawLists_t *)patch->drawLists;
3186 iterListsNext=drawLists->begin();
3188 for (iterLists=drawLists->begin(); iterLists != drawLists->end() && iterListsNext != drawLists->end(); iterLists++, iterListsNext++)
3190 // traverse two drawlists at once to draw a strip
3191 //if (nGLState & DRAW_GL_LINE)
3192 qglBegin(GL_QUAD_STRIP);
3194 // qglBegin(GL_TRIANGLE_STRIP);
3195 for (iterList=(*iterLists)->begin(), iterListNext=(*iterListsNext)->begin(); iterList != (*iterLists)->end() && iterListNext != (*iterListsNext)->end(); iterList++, iterListNext++)
3197 //if (g_PrefsDlg.m_bGLLighting)
3198 qglNormal3fv((*iterList).normal);
3199 //else if (bShade && !g_PrefsDlg.m_bDisplayLists)
3200 // qglColor3f((*iterList).lightmap[0], (*iterList).lightmap[0], (*iterList).lightmap[0]);
3202 qglTexCoord2fv((*iterList).st);
3203 qglVertex3fv((*iterList).xyz);
3205 //if (g_PrefsDlg.m_bGLLighting)
3206 qglNormal3fv((*iterListNext).normal);
3207 //else if (bShade && !g_PrefsDlg.m_bDisplayLists)
3208 // qglColor3f((*iterListNext).lightmap[0], (*iterListNext).lightmap[0], (*iterListNext).lightmap[0]);
3210 qglTexCoord2fv((*iterListNext).st);
3211 qglVertex3fv((*iterListNext).xyz);
3218 for (iterLists=drawLists->begin(); iterLists != drawLists->end(); iterLists++)
3220 qglBegin (GL_LINES); // draw normals
3221 //qglColor3f(1,1,1);
3222 for (iterList=(*iterLists)->begin(); iterList != (*iterLists)->end(); iterList++)
3224 VectorAdd((*iterList).xyz, (*iterList).normal, vNormal);
3225 qglVertex3fv ((*iterList).xyz);
3226 qglVertex3fv (vNormal);
3231 Patch_DrawNormals(patch);
3238 // fast memory-efficient ray-triangle intersection - MollerTrumbore97
3240 #define EPSILON 0.000001
3241 #define CROSS(dest,v1,v2) {dest[0]=v1[1]*v2[2]-v1[2]*v2[1];dest[1]=v1[2]*v2[0]-v1[0]*v2[2];dest[2]=v1[0]*v2[1]-v1[1]*v2[0];}
3242 #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
3243 #define SUB(dest,v1,v2) {dest[0]=v1[0]-v2[0];dest[1]=v1[1]-v2[1];dest[2]=v1[2]-v2[2];}
3245 int intersect_triangle(float orig[3], float dir[3],
3246 float vert0[3], float vert1[3], float vert2[3],
3247 double *t, double *u, double *v)
3249 double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
3252 // find vectors for two edges sharing vert0
3253 SUB(edge1, vert1, vert0);
3254 SUB(edge2, vert2, vert0);
3256 // begin calculating determinant - also used to calculate U parameter
3257 CROSS(pvec, dir, edge2);
3259 // if determinant is near zero, ray lies in plane of triangle
3260 det = DOT(edge1, pvec);
3262 #ifdef TEST_CULL // define TEST_CULL if culling is desired
3266 // calculate distance from vert0 to ray origin
3267 SUB(tvec, orig, vert0);
3269 // calculate U parameter and test bounds
3270 *u = DOT(tvec, pvec);
3271 if (*u < 0.0 || *u > det)
3274 // prepare to test V parameter
3275 CROSS(qvec, tvec, edge1);
3277 // calculate V parameter and test bounds
3278 *v = DOT(dir, qvec);
3279 if (*v < 0.0 || *u + *v > det)
3282 // calculate t, scale parameters, ray intersects triangle
3283 *t = DOT(edge2, qvec);
3284 inv_det = 1.0 / det;
3288 #else // the non-culling branch
3289 if (det > -EPSILON && det < EPSILON)
3291 inv_det = 1.0 / det;
3293 // calculate distance from vert0 to ray origin
3294 SUB(tvec, orig, vert0);
3296 // calculate U parameter and test bounds
3297 *u = DOT(tvec, pvec) * inv_det;
3298 if (*u < 0.0 || *u > 1.0)
3301 // prepare to test V parameter
3302 CROSS(qvec, tvec, edge1);
3304 // calculate V parameter and test bounds
3305 *v = DOT(dir, qvec) * inv_det;
3306 if (*v < 0.0 || *u + *v > 1.0)
3309 // calculate t, ray intersects triangle
3310 *t = DOT(edge2, qvec) * inv_det;
3316 int Triangle_Ray(float orig[3], float dir[3], bool bCullBack,
3317 float vert0[3], float vert1[3], float vert2[3],
3318 double *t, double *u, double *v)
3320 float edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
3323 /* find vectors for two edges sharing vert0 */
3324 VectorSubtract(vert1, vert0, edge1);
3325 VectorSubtract(vert2, vert0, edge2);
3327 /* begin calculating determinant - also used to calculate U parameter */
3328 CrossProduct(dir, edge2, pvec);
3330 /* if determinant is near zero, ray lies in plane of triangle */
3331 det = DotProduct(edge1, pvec);
3338 // calculate distance from vert0 to ray origin
3339 VectorSubtract(orig, vert0, tvec);
3341 // calculate U parameter and test bounds
3342 *u = DotProduct(tvec, pvec);
3343 if (*u < 0.0 || *u > det)
3346 // prepare to test V parameter
3347 CrossProduct(tvec, edge1, qvec);
3349 // calculate V parameter and test bounds
3350 *v = DotProduct(dir, qvec);
3351 if (*v < 0.0 || *u + *v > det)
3354 // calculate t, scale parameters, ray intersects triangle
3355 *t = DotProduct(edge2, qvec);
3356 inv_det = 1.0 / det;
3363 /* the non-culling branch */
3364 if (det > -0.000001 && det < 0.000001)
3366 inv_det = 1.0 / det;
3368 /* calculate distance from vert0 to ray origin */
3369 VectorSubtract(orig, vert0, tvec);
3371 /* calculate U parameter and test bounds */
3372 *u = DotProduct(tvec, pvec) * inv_det;
3373 if (*u < 0.0 || *u > 1.0)
3376 /* prepare to test V parameter */
3377 CrossProduct(tvec, edge1, qvec);
3379 /* calculate V parameter and test bounds */
3380 *v = DotProduct(dir, qvec) * inv_det;
3381 if (*v < 0.0 || *u + *v > 1.0)
3384 /* calculate t, ray intersects triangle */
3385 *t = DotProduct(edge2, qvec) * inv_det;
3390 bool Patch_Ray(patchMesh_t *patch, vec3_t origin, vec3_t dir, double *t, double *u, double *v)
3392 drawLists_t *drawLists;
3394 drawLists_t::iterator iterLists, iterListsNext;
3395 drawList_t::iterator i1, i2, i3, i4;
3397 // vec3_t tris[2][3];
3398 bool bIntersect = false;
3399 float tBest = FLT_MAX;
3401 if (patch->drawLists == NULL)
3404 drawLists = (drawLists_t *)patch->drawLists;
3406 iterListsNext=drawLists->begin();
3408 for (iterLists=drawLists->begin(); iterLists != drawLists->end() && iterListsNext != drawLists->end(); iterLists++, iterListsNext++)
3410 // traverse two drawlists at once with two iterators each to triangulate
3411 i1 = i3 = (*iterLists)->begin();
3412 i2 = i4 = (*iterListsNext)->begin();
3415 while (i3 != (*iterLists)->end() && i4 != (*iterListsNext)->end())
3417 if (Triangle_Ray(origin, dir, false, (*i1).xyz, (*i2).xyz, (*i3).xyz, t, u, v))
3423 if (Triangle_Ray(origin, dir, false, (*i3).xyz, (*i4).xyz, (*i2).xyz, t, u, v))
3447 // spog - curve LOD stuff ends
3454 void DrawPatchMesh(patchMesh_t *pm)
3456 if (g_PrefsDlg.m_bDisplayLists)
3458 if (pm->bDirty || pm->nListID <= 0 || pm->LODUpdated)
3460 if (pm->nListID <= 0)
3461 pm->nListID = qglGenLists(1);
3462 if (pm->nListID > 0)
3464 qglNewList(pm->nListID, GL_COMPILE_AND_EXECUTE);
3467 Patch_DeleteDrawLists(pm);
3468 Patch_CreateDrawLists(pm);
3470 Patch_DrawLODPatchMesh(pm);
3472 if (pm->nListID > 0)
3478 pm->LODUpdated = false;
3482 qglCallList(pm->nListID);
3487 if (pm->bDirty || pm->LODUpdated)
3489 Patch_DeleteDrawLists(pm);
3490 Patch_CreateDrawLists(pm);
3492 pm->LODUpdated = false;
3494 Patch_DrawLODPatchMesh(pm);
3503 void DrawPatchControls(patchMesh_t *pm)
3506 bool bSelectedPoints[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT];
3508 bool bOverlay = pm->bOverlay;
3511 if (g_bPatchBendMode)
3514 if (g_bPatchAxisOnRow)
3516 qglColor3f(1, 0, 1);
3517 if(!g_PrefsDlg.m_bGlPtWorkaround)
3519 qglBegin(GL_POINTS);
3520 for (i = 0; i < pm->width; i++)
3522 qglVertex3fv(pm->ctrl[i][g_nPatchAxisIndex].xyz);
3530 for(i = 0; i < pm->width; i++)
3532 DrawAlternatePoint(pm->ctrl[i][g_nPatchAxisIndex].xyz, 0);
3538 if (g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN)
3540 if(!g_PrefsDlg.m_bGlPtWorkaround)
3542 qglColor3f(0, 0, 1);
3543 qglBegin(GL_POINTS);
3544 if (g_nPatchBendState == BEND_SELECT_ORIGIN)
3546 qglVertex3fv(g_vBendOrigin);
3550 for (i = 0; i < pm->width; i++)
3552 if (g_bPatchLowerEdge)
3554 for (j = 0; j < g_nPatchAxisIndex; j++)
3555 qglVertex3fv(pm->ctrl[i][j].xyz);
3559 for (j = pm->height-1; j > g_nPatchAxisIndex; j--)
3560 qglVertex3fv(pm->ctrl[i][j].xyz);
3567 qglColor3f(0, 0, 1);
3570 if(g_nPatchBendState == BEND_SELECT_ORIGIN)
3572 DrawAlternatePoint(g_vBendOrigin, 0);
3576 for(i = 0; i < pm->width; i++)
3578 if(g_bPatchLowerEdge)
3580 for(j = 0; j < g_nPatchAxisIndex; j++)
3582 DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3587 for (j = pm->height-1; j > g_nPatchAxisIndex; j--)
3589 DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3601 if(!g_PrefsDlg.m_bGlPtWorkaround)
3603 qglColor3f(1, 0, 1);
3604 qglBegin(GL_POINTS);
3605 for (i = 0; i < pm->height; i++)
3607 qglVertex3fv(pm->ctrl[g_nPatchAxisIndex][i].xyz);
3612 qglColor3f(1, 0, 1);
3615 for(i = 0; i < pm->height; i++)
3617 DrawAlternatePoint(pm->ctrl[g_nPatchAxisIndex][i].xyz, 0);
3623 if (g_nPatchBendState == BEND_SELECT_EDGE || g_nPatchBendState == BEND_BENDIT || g_nPatchBendState == BEND_SELECT_ORIGIN)
3625 if(!g_PrefsDlg.m_bGlPtWorkaround)
3627 qglColor3f(0, 0, 1);
3628 qglBegin(GL_POINTS);
3629 for (i = 0; i < pm->height; i++)
3631 if (g_nPatchBendState == BEND_SELECT_ORIGIN)
3633 qglVertex3fv(pm->ctrl[g_nBendOriginIndex][i].xyz);
3637 if (g_bPatchLowerEdge)
3639 for (j = 0; j < g_nPatchAxisIndex; j++)
3640 qglVertex3fv(pm->ctrl[j][i].xyz);
3644 for (j = pm->width-1; j > g_nPatchAxisIndex; j--)
3645 qglVertex3fv(pm->ctrl[j][i].xyz);
3652 qglColor3f(0, 0, 1);
3655 for(i = 0; i < pm->height; i++)
3657 if(g_nPatchBendState == BEND_SELECT_ORIGIN)
3659 DrawAlternatePoint(pm->ctrl[g_nBendOriginIndex][i].xyz, 0);
3663 if(g_bPatchLowerEdge)
3665 for(j = 0; j < g_nPatchAxisIndex; j++)
3667 DrawAlternatePoint(pm->ctrl[j][i].xyz, 0);
3672 for(j = pm->width-1; j > g_nPatchAxisIndex; j--)
3674 DrawAlternatePoint(pm->ctrl[j][i].xyz, 0);
3687 //qglDisable(GL_TEXTURE_2D); // stops point colours being multiplied by texture colour..
3688 //draw CV lattice - could be made optional
3689 //qglDisable( GL_CULL_FACE );
3690 // qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3691 qglEnable (GL_POLYGON_OFFSET_LINE);
3692 if (g_PrefsDlg.m_bNoStipple == FALSE)
3693 qglDisable (GL_LINE_STIPPLE);
3695 qglColor3f(1.0f, 0.75f, 0.0f);
3696 for ( i = 0 ; i+1 < pm->width ; i++ )
3698 qglBegin(GL_QUAD_STRIP);
3699 for ( j = 0 ; j < pm->height ; j++ )
3701 qglVertex3fv(pm->ctrl[i][j].xyz);
3702 qglVertex3fv(pm->ctrl[i+1][j].xyz);
3706 qglDisable (GL_POLYGON_OFFSET_LINE);
3707 //if (g_PrefsDlg.m_bNoStipple == FALSE)
3708 // qglEnable (GL_LINE_STIPPLE);
3710 // draw selection handles
3711 if(!g_PrefsDlg.m_bGlPtWorkaround)
3714 qglBegin(GL_POINTS);
3715 for ( i = 0 ; i < pm->width ; i++ )
3717 for ( j = 0 ; j < pm->height ; j++ )
3719 if (PointInMoveList(pm->ctrl[i][j].xyz) != -1)
3721 bSelectedPoints[i][j] = true;
3725 bSelectedPoints[i][j] = false;
3726 if (i & 0x01 || j & 0x01)
3727 qglColor3f(1, 0, 1);
3729 qglColor3f(0, 1, 0);
3731 qglVertex3fv(pm->ctrl[i][j].xyz);
3735 qglColor3f(0, 0, 1);
3736 for ( i = 0 ; i < pm->width ; i++ )
3738 for ( j = 0 ; j < pm->height ; j++ )
3740 if (bSelectedPoints[i][j])
3741 qglVertex3fv(pm->ctrl[i][j].xyz);
3750 for(i = 0; i < pm->width; i++)
3752 for(j = 0; j < pm->height; j++)
3754 if(PointInMoveList(pm->ctrl[i][j].xyz) != -1)
3756 bSelectedPoints[i][j] = true;
3760 bSelectedPoints[i][j] = false;
3761 if(i & 0x01 || j & 0x01)
3762 qglColor3f(1, 0, 1);
3764 qglColor3f(0, 1, 0);
3767 DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3771 qglColor3f(0, 0, 1);
3772 for(i = 0; i < pm->width; i++)
3774 for(j = 0; j < pm->height; j++)
3776 if(bSelectedPoints[i][j])
3779 DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3789 if(!g_PrefsDlg.m_bGlPtWorkaround)
3792 qglBegin(GL_POINTS);
3793 for ( i = 0 ; i < pm->width ; i++ )
3795 for ( j = 0 ; j < pm->height ; j++ )
3797 if (i & 0x01 || j & 0x01)
3798 qglColor3f(1, 0, 1);
3800 qglColor3f(0, 1, 0);
3801 qglVertex3fv(pm->ctrl[i][j].xyz);
3810 for ( i = 0 ; i < pm->width ; i++ )
3812 for ( j = 0 ; j < pm->height ; j++ )
3814 if (i & 0x01 || j & 0x01)
3815 qglColor3f(1, 0, 1);
3817 qglColor3f(0, 1, 0);
3819 DrawAlternatePoint(pm->ctrl[i][j].xyz, 0);
3834 void Patch_DrawXY(patchMesh_t *pm)
3836 qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3840 qglColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES]);
3841 if (g_PrefsDlg.m_bNoStipple == FALSE)
3842 qglEnable (GL_LINE_STIPPLE);
3848 if ( (pm->bSelected && (g_qeglobals.d_select_mode == sel_curvepoint
3849 || g_qeglobals.d_select_mode == sel_area
3850 || g_bPatchBendMode))
3852 DrawPatchControls(pm);
3860 void Patch_DrawCam(patchMesh_t *pm)
3862 qglPushAttrib(GL_ALL_ATTRIB_BITS); // save the current state
3864 if (g_bPatchWireFrame)
3866 qglDisable( GL_CULL_FACE );
3867 qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
3868 qglDisable(GL_TEXTURE_2D);
3869 if (g_PrefsDlg.m_bGLLighting)
3870 qglDisable(GL_LIGHTING);
3874 //if (g_PrefsDlg.m_bGLLighting)
3875 // qglEnable(GL_LIGHTING);
3876 //qglEnable( GL_CULL_FACE );
3880 qglDisable(GL_CULL_FACE);
3881 qglBindTexture (GL_TEXTURE_2D, pm->d_texture->texture_number);
3882 qglPolygonMode (GL_FRONT, GL_FILL);
3883 qglPolygonMode (GL_BACK, GL_LINE);
3885 if (pm->pShader->getTrans() < 1.0f)
3887 qglEnable(GL_BLEND);
3888 qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3889 qglColor4f(pm->d_texture->color[0], pm->d_texture->color[1], pm->d_texture->color[2], pm->pShader->getTrans());
3892 DrawPatchMesh(pm); // both sides
3895 qglPopAttrib(); // restore saved state
3898 void ConvexHullForSection( float section[2][4][7] ) {
3901 void BrushesForSection( float section[2][4][7] ) {
3909 void Patch_BuildPoints (brush_t *b)
3912 b->patchBrush = false;
3913 for (f=b->brush_faces ; f ; f=f->next)
3915 if (f->texdef.flags & SURF_PATCH)
3917 b->patchBrush = true;
3918 //vec3_t vMin, vMax;
3919 //Patch_CalcBounds(&patchMeshes[b->nPatchID], vMin, vMax);
3920 //VectorCopy(vMin, b->mins);
3921 //VectorCopy(vMax, b->maxs);
3932 void Patch_Move(patchMesh_t *pm, const vec3_t vMove, bool bRebuild)
3935 for (int w = 0; w < pm->width; w++)
3937 for (int h = 0; h < pm->height; h++)
3939 VectorAdd(pm->ctrl[w][h].xyz, vMove, pm->ctrl[w][h].xyz);
3942 // bRebuild is never true
3946 Patch_CalcBounds(pm, vMin, vMax);
3947 //Brush_RebuildBrush(patchMeshes[n].pSymbiot, vMin, vMax);
3949 UpdatePatchInspector();
3958 void Patch_ApplyMatrix(patchMesh_t *p, const vec3_t vOrigin, const vec3_t vMatrix[3], bool bSnap)
3962 for (int w = 0; w < p->width; w++)
3964 for (int h = 0; h < p->height; h++)
3966 if (((g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points != 0) || g_bPatchBendMode)
3967 && PointInMoveList(p->ctrl[w][h].xyz) == -1) // snap selected points only, if selected
3969 VectorSubtract (p->ctrl[w][h].xyz, vOrigin, vTemp);
3970 for (int j = 0; j < 3; j++)
3972 p->ctrl[w][h].xyz[j] = DotProduct(vTemp, vMatrix[j]) + vOrigin[j];
3975 p->ctrl[w][h].xyz[j] = floor(p->ctrl[w][h].xyz[j] + 0.5);
3981 Patch_CalcBounds(p, vMin, vMax);
3982 Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
3990 void Patch_EditPatch()
3992 //--patchMesh_t* p = &patchMeshes[n];
3993 g_qeglobals.d_numpoints = 0;
3994 g_qeglobals.d_num_move_points = 0;
3996 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4000 patchMesh_t* p = pb->pPatch;
4001 for ( int i = 0 ; i < p->width ; i++ )
4003 for ( int j = 0 ; j < p->height ; j++ )
4005 VectorCopy (p->ctrl[i][j].xyz, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
4006 if (g_qeglobals.d_numpoints < MAX_POINTS-1)
4008 g_qeglobals.d_numpoints++;
4014 g_qeglobals.d_select_mode = sel_curvepoint;
4015 //--g_nSelectedPatch = n;
4025 //FIXME: need all sorts of asserts throughout a lot of this crap
4026 void Patch_Deselect()
4028 //--g_nSelectedPatch = -1;
4029 g_qeglobals.d_select_mode = sel_brush;
4031 for (brush_t *b = selected_brushes.next ; b != &selected_brushes ; b=b->next)
4035 b->pPatch->bSelected = false;
4039 //for (int i = 0; i < numPatchMeshes; i++)
4040 // patchMeshes[i].bSelected = false;
4042 if (g_bPatchBendMode)
4044 // if (g_bPatchInsertMode)
4045 // Patch_InsDelToggle();
4054 void Patch_Select(patchMesh_t *p)
4056 // maintained for point manip.. which i need to fix as this
4057 // is pf error prone
4058 //--g_nSelectedPatch = n;
4059 p->bSelected = true;
4068 void Patch_Deselect(patchMesh_t *p)
4070 p->bSelected = false;
4079 extern BTNode_t *BTree_Delete(BTNode_t *pBT);
4080 extern BTListList_t *BTree_DeleteListFromList(BTListList_t *pBTListList);
4082 void Patch_Delete(patchMesh_t *p)
4084 if (p->pSymbiot) // Hydra - added a check to prevent access violations.
4086 p->pSymbiot->pPatch = NULL;
4087 p->pSymbiot->patchBrush = false;
4090 // spog - free dynamically allocated memory used by LODs
4091 int rowcount = ((MAX_PATCH_WIDTH-1)/2) * MAX_PATCH_HEIGHT;
4092 int colcount = ((MAX_PATCH_HEIGHT-1)/2) * MAX_PATCH_WIDTH;
4094 for (i=0; i<rowcount; i++)
4095 p->rowLOD[i] = BTree_Delete(p->rowLOD[i]);
4096 for (i=0; i<colcount; i++)
4097 p->colLOD[i] = BTree_Delete(p->colLOD[i]);
4099 // delete display list associated with patch
4100 if (p->nListID != -1)
4101 qglDeleteLists (p->nListID, 1); // list#, number of lists
4103 // delete LOD drawLists
4104 Patch_DeleteDrawLists(p);
4111 UpdatePatchInspector();
4120 void Patch_Scale(patchMesh_t *p, const vec3_t vOrigin, const vec3_t vAmt, bool bRebuild)
4123 for (int w = 0; w < p->width; w++)
4125 for (int h = 0; h < p->height; h++)
4127 if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
4129 for (int i=0 ; i<3 ; i++)
4131 p->ctrl[w][h].xyz[i] -= vOrigin[i];
4132 p->ctrl[w][h].xyz[i] *= vAmt[i];
4133 p->ctrl[w][h].xyz[i] += vOrigin[i];
4140 Patch_CalcBounds(p, vMin, vMax);
4141 Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
4143 UpdatePatchInspector();
4152 void Patch_SetView(int n)
4154 g_bSameView = (n == g_nPatchClickedView);
4155 g_nPatchClickedView = n;
4164 // FIXME: need array validation throughout
4165 void Patch_SetTexture(patchMesh_t *p, texdef_t *tex_def, IPluginTexdef* pPlugTexdef)
4167 // NOTE: I don't know for sure if this happens
4169 p->pShader->DecRef();
4170 p->pShader = QERApp_Shader_ForName(tex_def->GetName());
4171 p->pShader->IncRef();
4172 p->d_texture = p->pShader->getTexture();
4174 UpdatePatchInspector();
4183 bool Patch_DragScale(patchMesh_t *p, vec3_t vAmt, vec3_t vMove)
4185 vec3_t vMin, vMax, vScale, vTemp, vMid;
4188 Patch_CalcBounds(p, vMin, vMax);
4190 VectorSubtract(vMax, vMin, vTemp);
4192 // if we are scaling in the same dimension the patch has no depth
4193 for (i = 0; i < 3; i ++)
4195 if (vTemp[i] == 0 && vMove[i] != 0)
4197 //Patch_Move(n, vMove, true);
4202 for (i=0 ; i<3 ; i++)
4203 vMid[i] = (vMin[i] + ((vMax[i] - vMin[i]) / 2));
4205 for (i = 0; i < 3; i++)
4209 vScale[i] = 1.0 + vAmt[i] / vTemp[i];
4217 Patch_Scale(p, vMid, vScale, false);
4219 VectorSubtract(vMax, vMin, vTemp);
4221 Patch_CalcBounds(p, vMin, vMax);
4223 VectorSubtract(vMax, vMin, vMid);
4225 VectorSubtract(vMid, vTemp, vTemp);
4227 VectorScale(vTemp, 0.5, vTemp);
4229 // abs of both should always be equal
4230 if (!VectorCompare(vMove, vAmt))
4232 for (i = 0; i < 3; i++)
4234 if (vMove[i] != vAmt[i])
4235 vTemp[i] = -(vTemp[i]);
4239 Patch_Move(p, vTemp);
4248 void Patch_InsertColumn(patchMesh_t *p, bool bAdd)
4254 if (p->width + 2 >= MAX_PATCH_WIDTH)
4258 // check for selected column points
4259 for (h = 0; h < p->height; h++)
4261 for (w = 1; w < p->width; w+=2)
4262 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4266 for (w = 0; w < p->width; w+=2)
4267 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4275 if (bAdd) w=p->width-1;
4282 for (h = 0; h < p->height; h++)
4284 for (width = p->width-1; width > w; width--)
4285 memcpy(&p->ctrl[width+2][h],&p->ctrl[width][h], sizeof(drawVert_t));
4287 // set two new column points
4288 memcpy(&p->ctrl[w+2][h],&p->ctrl[w][h], sizeof(drawVert_t));
4289 memcpy(&p->ctrl[w+1][h],&p->ctrl[w-1][h], sizeof(drawVert_t));
4291 for (i=0; i<3; i++) // xyz
4293 vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4294 p->ctrl[w+1][h].xyz[i] = p->ctrl[w+1][h].xyz[i] + (vTemp[i] / 2);
4296 vTemp[i] = p->ctrl[w-2][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4297 p->ctrl[w-1][h].xyz[i] = p->ctrl[w-1][h].xyz[i] + (vTemp[i] / 2);
4299 vTemp[i] = p->ctrl[w+1][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4300 p->ctrl[w][h].xyz[i] = p->ctrl[w-1][h].xyz[i] + (vTemp[i] / 2);
4302 for (i=0; i<2; i++) // st
4304 stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w-1][h].st[i];
4305 p->ctrl[w+1][h].st[i] = p->ctrl[w+1][h].st[i] + (stTemp[i] / 2);
4307 stTemp[i] = p->ctrl[w-2][h].st[i] - p->ctrl[w-1][h].st[i];
4308 p->ctrl[w-1][h].st[i] = p->ctrl[w-1][h].st[i] + (stTemp[i] / 2);
4310 stTemp[i] = p->ctrl[w+1][h].st[i] - p->ctrl[w-1][h].st[i];
4311 p->ctrl[w][h].st[i] = p->ctrl[w-1][h].st[i] + (stTemp[i] / 2);
4316 // deselect all points to keep things neat
4317 if (g_qeglobals.d_select_mode == sel_curvepoint)
4320 UpdatePatchInspector();
4329 void Patch_InsertRow(patchMesh_t *p, bool bAdd)
4331 int h, w, i, height;
4335 if (p->height + 2 >= MAX_PATCH_HEIGHT)
4339 // check for selected row points
4340 for (w = 0; w < p->width; w++)
4342 for (h = 1; h < p->height; h+=2)
4343 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4347 for (h = 0; h < p->height; h+=2)
4348 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4355 if (bAdd) h=p->height-1;
4362 for (w = 0; w < p->width; w++)
4364 for (height = p->height-1; height > h; height--)
4365 memcpy(&p->ctrl[w][height+2],&p->ctrl[w][height], sizeof(drawVert_t));
4367 // set two new row points
4368 memcpy(&p->ctrl[w][h+2],&p->ctrl[w][h], sizeof(drawVert_t));
4369 memcpy(&p->ctrl[w][h+1],&p->ctrl[w][h-1], sizeof(drawVert_t));
4371 for (i=0; i<3; i++) // xyz
4373 vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w][h-1].xyz[i];
4374 p->ctrl[w][h+1].xyz[i] = p->ctrl[w][h+1].xyz[i] + (vTemp[i] / 2);
4376 vTemp[i] = p->ctrl[w][h-2].xyz[i] - p->ctrl[w][h-1].xyz[i];
4377 p->ctrl[w][h-1].xyz[i] = p->ctrl[w][h-1].xyz[i] + (vTemp[i] / 2);
4379 vTemp[i] = p->ctrl[w][h+1].xyz[i] - p->ctrl[w][h-1].xyz[i];
4380 p->ctrl[w][h].xyz[i] = p->ctrl[w][h-1].xyz[i] + (vTemp[i] / 2);
4382 for (i=0; i<2; i++) // st
4384 stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w][h-1].st[i];
4385 p->ctrl[w][h+1].st[i] = p->ctrl[w][h+1].st[i] + (stTemp[i] / 2);
4387 stTemp[i] = p->ctrl[w][h-2].st[i] - p->ctrl[w][h-1].st[i];
4388 p->ctrl[w][h-1].st[i] = p->ctrl[w][h-1].st[i] + (stTemp[i] / 2);
4390 stTemp[i] = p->ctrl[w][h+1].st[i] - p->ctrl[w][h-1].st[i];
4391 p->ctrl[w][h].st[i] = p->ctrl[w][h-1].st[i] + (stTemp[i] / 2);
4396 // deselect all points to keep things neat
4397 if (g_qeglobals.d_select_mode == sel_curvepoint)
4400 UpdatePatchInspector();
4408 void Patch_RemoveRow(patchMesh_t *p, bool bFirst)
4410 int w, h, i, height;
4413 bool bExtrapolate = true;
4415 if (p->height <= MIN_PATCH_HEIGHT)
4419 for (w = 0; w < p->width; w++)
4421 for (h = 0; h < p->height; h+=2)
4422 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4426 for (h = 1; h < p->height; h+=2)
4427 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4435 bExtrapolate = false;
4436 if (bFirst) h=p->height-3;
4439 else if (h <= 0) h=2;
4440 else if (h > p->height-3) h = p->height-3;
4445 for (w = 0; w < p->width; w++)
4449 for (i = 0; i < 3; i++) // xyz
4451 vTemp[i] = p->ctrl[w][h+2].xyz[i] - p->ctrl[w][h-2].xyz[i];
4452 p->ctrl[w][h-1].xyz[i] = p->ctrl[w][h-2].xyz[i] + (vTemp[i] / 2);
4454 vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w][h-1].xyz[i];
4455 p->ctrl[w][h-1].xyz[i] = p->ctrl[w][h-1].xyz[i] + (vTemp[i] * 2);
4458 for (i = 0; i < 2; i++) // st
4460 stTemp[i] = p->ctrl[w][h+2].st[i] - p->ctrl[w][h-2].st[i];
4461 p->ctrl[w][h-1].st[i] = p->ctrl[w][h-2].st[i] + (stTemp[i] / 2);
4463 stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w][h-1].st[i];
4464 p->ctrl[w][h-1].st[i] = p->ctrl[w][h-1].st[i] + (stTemp[i] * 2);
4473 for (height = h; height < p->height; height++)
4474 memcpy(&p->ctrl[w][height], &p->ctrl[w][height+2], sizeof(drawVert_t));
4476 // deselect all points to keep things neat
4477 if (g_qeglobals.d_select_mode == sel_curvepoint)
4480 UpdatePatchInspector();
4488 void Patch_RemoveColumn(patchMesh_t *p, bool bFirst)
4493 bool bExtrapolate = true;
4495 if (p->width <= MIN_PATCH_WIDTH)
4499 for (h = 0; h < p->height; h++)
4501 for (w = 0; w < p->width; w+=2)
4502 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4506 for (w = 1; w < p->width; w+=2)
4507 if (PointInMoveList(p->ctrl[w][h].xyz) != -1)
4515 bExtrapolate = false;
4516 if (bFirst) w=p->width-3;
4520 else if (w > p->width-3) w = p->width-3;
4525 for (h = 0; h < p->height; h++)
4529 for (i = 0; i < 3; i++) // xyz
4531 vTemp[i] = p->ctrl[w+2][h].xyz[i] - p->ctrl[w-2][h].xyz[i];
4532 p->ctrl[w-1][h].xyz[i] = p->ctrl[w-2][h].xyz[i] + (vTemp[i] / 2);
4534 vTemp[i] = p->ctrl[w][h].xyz[i] - p->ctrl[w-1][h].xyz[i];
4535 p->ctrl[w-1][h].xyz[i] = p->ctrl[w-1][h].xyz[i] + (vTemp[i] * 2);
4538 for (i = 0; i < 2; i++) // st
4540 stTemp[i] = p->ctrl[w+2][h].st[i] - p->ctrl[w-2][h].st[i];
4541 p->ctrl[w-1][h].st[i] = p->ctrl[w-2][h].st[i] + (stTemp[i] / 2);
4543 stTemp[i] = p->ctrl[w][h].st[i] - p->ctrl[w-1][h].st[i];
4544 p->ctrl[w-1][h].st[i] = p->ctrl[w-1][h].st[i] + (stTemp[i] * 2);
4554 for (width = w; width < p->width; width++)
4555 memcpy(&p->ctrl[width][h], &p->ctrl[width+2][h], sizeof(drawVert_t));
4557 // deselect all points to keep things neat
4558 if (g_qeglobals.d_select_mode == sel_curvepoint)
4561 UpdatePatchInspector();
4570 void Patch_AdjustColumns(patchMesh_t *p, int nCols)
4572 vec3_t vTemp, vTemp2;
4575 if (nCols & 0x01 || p->width + nCols < 3 || p->width + nCols > MAX_PATCH_WIDTH)
4578 // add in column adjustment
4581 for (h = 0; h < p->height; h++)
4583 // for each column, we need to evenly disperse p->width number
4584 // of points across the old bounds
4586 // calc total distance to interpolate
4587 VectorSubtract(p->ctrl[p->width - 1 - nCols][h].xyz, p->ctrl[0][h].xyz, vTemp);
4590 for (i = 0; i < 3; i ++)
4592 vTemp2[i] = vTemp[i] / (p->width - 1);
4596 for (w = 0; w < p->width-1; w++)
4598 VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w+1][h].xyz);
4602 for ( w = 0 ; w < p->width ; w++ )
4604 for ( h = 0 ; h < p->height ; h++ )
4606 p->ctrl[w][h].st[0] = 4 * (float)w / (p->width - 1);
4607 p->ctrl[w][h].st[1] = 4 * (float)h / (p->height - 1);
4610 UpdatePatchInspector();
4620 void Patch_AdjustRows(patchMesh_t *p, int nRows)
4622 vec3_t vTemp, vTemp2;
4625 if (nRows & 0x01 || p->height + nRows < 3 || p->height + nRows > MAX_PATCH_HEIGHT)
4628 // add in column adjustment
4631 for (w = 0; w < p->width; w++)
4633 // for each row, we need to evenly disperse p->height number
4634 // of points across the old bounds
4636 // calc total distance to interpolate
4637 VectorSubtract(p->ctrl[w][p->height - 1 - nRows].xyz, p->ctrl[w][0].xyz, vTemp);
4639 //vTemp[0] = vTemp[1] = vTemp[2] = 0;
4640 //for (h = 0; h < p->height - nRows; h ++)
4642 // VectorAdd(vTemp, p->ctrl[w][h], vTemp);
4646 for (i = 0; i < 3; i ++)
4648 vTemp2[i] = vTemp[i] / (p->height - 1);
4652 for (h = 0; h < p->height-1; h++)
4654 VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz);
4658 for ( w = 0 ; w < p->width ; w++ )
4660 for ( h = 0 ; h < p->height ; h++ )
4662 p->ctrl[w][h].st[0] = 4 * (float)w / (p->width - 1);
4663 p->ctrl[w][h].st[1] = 4 * (float)h / (p->height - 1);
4666 UpdatePatchInspector();
4676 void Patch_DisperseRows()
4678 vec3_t vTemp, vTemp2;
4682 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4686 patchMesh_t *p = pb->pPatch;
4688 for (w = 0; w < p->width; w++)
4690 // for each row, we need to evenly disperse p->height number
4691 // of points across the old bounds
4693 // calc total distance to interpolate
4694 VectorSubtract(p->ctrl[w][p->height - 1].xyz, p->ctrl[w][0].xyz, vTemp);
4696 //vTemp[0] = vTemp[1] = vTemp[2] = 0;
4697 //for (h = 0; h < p->height - nRows; h ++)
4699 // VectorAdd(vTemp, p->ctrl[w][h], vTemp);
4703 for (i = 0; i < 3; i ++)
4705 vTemp2[i] = vTemp[i] / (p->height - 1);
4709 for (h = 0; h < p->height-1; h++)
4711 VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz);
4713 Patch_Naturalize(p);
4718 UpdatePatchInspector();
4723 Patch_DisperseIntermediateRows
4727 void Patch_DisperseIntermediateRows()
4729 vec3_t vTemp, vTemp2;
4733 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4737 patchMesh_t *p = pb->pPatch;
4739 for (w = 0; w < p->width; w++)
4742 for (h = 0; h < p->height; h+=2)
4744 // calc distance to interpolate
4745 VectorSubtract(p->ctrl[w][h+2].xyz, p->ctrl[w][h].xyz, vTemp);
4748 for (i = 0; i < 3; i ++)
4750 vTemp2[i] = vTemp[i] / 2;
4753 // move control points
4754 VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w][h+1].xyz);
4759 UpdatePatchInspector();
4764 Patch_DisperseIntermediateColumns
4767 void Patch_DisperseIntermediateColumns()
4769 vec3_t vTemp, vTemp2;
4773 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4777 patchMesh_t *p = pb->pPatch;
4779 for (h = 0; h < p->height; h++)
4782 for (w = 0; w < p->width; w+=2)
4784 // calc distance to interpolate
4785 VectorSubtract(p->ctrl[w+2][h].xyz, p->ctrl[w][h].xyz, vTemp);
4788 for (i = 0; i < 3; i ++)
4790 vTemp2[i] = vTemp[i] / 2;
4793 // move control points
4794 VectorAdd(p->ctrl[w][h].xyz, vTemp2, p->ctrl[w+1][h].xyz);
4799 UpdatePatchInspector();
4806 Patch_AdjustSelected
4809 void Patch_AdjustSelected(bool bInsert, bool bColumn, bool bFlag)
4811 bool bUpdate = false;
4812 for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4820 Patch_InsertColumn(pb->pPatch, bFlag);
4824 Patch_InsertRow(pb->pPatch, bFlag);
4831 Patch_RemoveColumn(pb->pPatch, bFlag);
4835 Patch_RemoveRow(pb->pPatch, bFlag);
4840 patchMesh_t *p = pb->pPatch;
4841 Patch_CalcBounds(p, vMin, vMax);
4842 Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
4843 pb->pPatch->bDirty = true; // rebuild LOD trees and their normals
4848 Sys_UpdateWindows(W_ALL);
4855 Patch_AdjustSelectedRowCols
4859 void Patch_AdjustSelectedRowCols(int nRows, int nCols)
4861 for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
4865 Patch_InsertColumn(pb->pPatch, false);
4868 Patch_AdjustRows(pb->pPatch, nRows);
4873 Patch_AdjustColumns(pb->pPatch, nCols);
4877 UpdatePatchInspector();
4884 temporary stuff, detect potential problems when saving the texture name
4885 will correct the patch on the fly if problem detected
4889 \todo performance issue with CheckName calls
4890 don't call this too much, only when absolutely necessary
4891 strategies that call here too much are known to be slow
4892 patch 84 to bug 253 adds an additionnal check for textures/
4894 void CheckName( patchMesh_t *p, char *pname )
4896 if(strncmp(p->pShader->getName(), "textures/", 9) != 0)
4897 p->pShader = QERApp_Shader_ForName(SHADER_NOT_FOUND);
4899 // some manage to get long filename textures (with spaces) in their maps
4900 if (strchr( p->pShader->getName(), ' ' ))
4903 sprintf( Msg1, "Can't save texture with spaces in name. Rename %s\nNOTE: This message may popup several times .. once for each buggy face detected.", p->pShader->getName() );
4904 Sys_Printf("%s\n", Msg1 );
4905 gtk_MessageBox(g_pParentWnd->m_pWidget, Msg1, "Error saving map", MB_OK );
4906 strcpy( pname, SHADER_NOT_FOUND );
4907 p->pShader = QERApp_Shader_ForName(SHADER_NOT_FOUND);
4908 p->d_texture = p->pShader->getTexture();
4911 strcpy( pname, p->pShader->getName()+9 ); // remove "textures/"
4919 void Patch_Write (patchMesh_t *p, MemStream *file)
4923 MemFile_fprintf(file, " {\n patchDef2\n {\n");
4925 CheckName( p, pname );
4926 MemFile_fprintf(file, " %s\n", pname );
4927 MemFile_fprintf(file, " ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value);
4930 float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5];
4933 for (w = 0; w < p->width; w++)
4935 for (h = 0; h < p->height; h++)
4937 ctrl[w][h][0] = p->ctrl[w][h].xyz[0];
4938 ctrl[w][h][1] = p->ctrl[w][h].xyz[1];
4939 ctrl[w][h][2] = p->ctrl[w][h].xyz[2];
4940 ctrl[w][h][3] = p->ctrl[w][h].st[0];
4941 ctrl[w][h][4] = p->ctrl[w][h].st[1];
4945 _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast<float*>(&ctrl));
4947 if (g_qeglobals.m_bBrushPrimitMode)
4951 for (epair_t *ep = p->epairs ; ep ; ep=ep->next)
4953 MemFile_fprintf (file, "\"%s\" \"%s\"\n", ep->key, ep->value);
4958 MemFile_fprintf(file, " }\n }\n");
4961 void Patch_Write (patchMesh_t *p, FILE *file)
4965 fprintf(file, " {\n patchDef2\n {\n");
4967 CheckName( p, pname );
4968 fprintf(file, " %s\n", pname );
4969 fprintf(file, " ( %i %i %i %i %i ) \n", p->width, p->height, p->contents, p->flags, p->value);
4972 float ctrl[MAX_PATCH_WIDTH][MAX_PATCH_HEIGHT][5];
4975 for (w = 0; w < p->width; w++)
4977 for (h = 0; h < p->height; h++)
4979 ctrl[w][h][0] = p->ctrl[w][h].xyz[0];
4980 ctrl[w][h][1] = p->ctrl[w][h].xyz[1];
4981 ctrl[w][h][2] = p->ctrl[w][h].xyz[2];
4982 ctrl[w][h][3] = p->ctrl[w][h].st[0];
4983 ctrl[w][h][4] = p->ctrl[w][h].st[1];
4987 _Write3DMatrix(file, p->width, p->height, 5, reinterpret_cast<float*>(&ctrl));
4989 if (g_qeglobals.m_bBrushPrimitMode)
4993 for (epair_t *ep = p->epairs ; ep ; ep=ep->next)
4995 fprintf (file, "\"%s\" \"%s\"\n", ep->key, ep->value);
5000 fprintf(file, " }\n }\n");
5009 void Patch_RotateTexture(patchMesh_t *p, float fAngle)
5012 float c = cos(fAngle * Q_PI / 180);
5013 float s = sin(fAngle * Q_PI / 180);
5015 Patch_TransformLODTexture(p, c, s, ROTATE);
5017 for (int w = 0; w < p->width; w++)
5019 for (int h = 0; h < p->height; h++)
5021 //if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
5024 float x = p->ctrl[w][h].st[0];
5025 float y = p->ctrl[w][h].st[1];
5026 p->ctrl[w][h].st[0] = x * c - y * s;
5027 p->ctrl[w][h].st[1] = y * c + x * s;
5038 void Patch_ScaleTexture(patchMesh_t *p, float fx, float fy, bool bFixup)
5041 // this hack turns scales into 1.1 or 0.9
5044 fx = (fx == 0) ? 1.0 : (fx > 0) ? 0.9 : 1.10;
5045 fy = (fy == 0) ? 1.0 : (fy > 0) ? 0.9 : 1.10;
5055 for (int w = 0; w < p->width; w++)
5057 for (int h = 0; h < p->height; h++)
5059 if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
5062 p->ctrl[w][h].st[0] *= fx;
5063 p->ctrl[w][h].st[1] *= fy;
5066 if (g_qeglobals.d_select_mode == sel_curvepoint)
5069 Patch_LODMatchAll();
5073 Patch_TransformLODTexture(p, fx, fy, SCALE);
5074 p->LODUpdated = true;
5082 shift a texture given a pixel count
5085 void Patch_ShiftTexture(patchMesh_t *p, float fx, float fy)
5088 pTex = p->pShader->getTexture();
5089 fx = -1 * fx / pTex->width;
5090 fy = fy / pTex->height;
5091 Patch_ShiftTextureST(p, fx, fy);
5095 ====================
5096 Patch_ShiftTextureST
5097 shift a patch texture given an ST increment
5098 ====================
5100 void Patch_ShiftTextureST(patchMesh_t *p, float fx, float fy)
5103 // NOTE: when called by Patch_ShiftTexture this warning may be bogus
5104 if ((ABS(fx) >= 1) || (ABS(fy) >= 1))
5105 Sys_Printf("WARNING: increments exceed 1 in Patch_ShiftTextureST\n");
5107 for (int w = 0; w < p->width; w++)
5109 for (int h = 0; h < p->height; h++)
5111 if (g_qeglobals.d_select_mode == sel_curvepoint && PointInMoveList(p->ctrl[w][h].xyz) == -1)
5114 p->ctrl[w][h].st[0] += fx;
5115 p->ctrl[w][h].st[1] += fy;
5118 if (g_qeglobals.d_select_mode == sel_curvepoint)
5121 Patch_LODMatchAll();
5125 Patch_TransformLODTexture(p, fx, fy, TRANSLATE);
5126 p->LODUpdated = true;
5132 Patch_ToggleInverted
5135 void Patch_ToggleInverted()
5137 bool bUpdate = false;
5139 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5144 patchInvert(pb->pPatch);
5150 Sys_UpdateWindows(W_ALL);
5152 UpdatePatchInspector();
5157 Patch_ToggleInverted
5160 void Patch_InvertTexture(bool bY)
5162 bool bUpdate = false;
5165 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5170 patchMesh_t *p = pb->pPatch;
5174 for ( int i = 0 ; i < p->height ; i++ )
5176 for (int j = 0; j < p->width / 2; j++)
5178 memcpy(fTemp, &p->ctrl[p->width - 1- j][i].st[0], sizeof (float[2]));
5179 memcpy(&p->ctrl[p->width - 1- j][i].st[0], &p->ctrl[j][i].st[0], sizeof(float[2]));
5180 memcpy(&p->ctrl[j][i].st[0], fTemp, sizeof(float[2]));
5186 for ( int i = 0 ; i < p->width ; i++ )
5188 for (int j = 0; j < p->height / 2; j++)
5190 memcpy(fTemp, &p->ctrl[i][p->height - 1- j].st[0], sizeof (float[2]));
5191 memcpy(&p->ctrl[i][p->height - 1 - j].st[0], &p->ctrl[i][j].st[0], sizeof(float[2]));
5192 memcpy(&p->ctrl[i][j].st[0], fTemp, sizeof(float[2]));
5201 Sys_UpdateWindows(W_ALL);
5203 UpdatePatchInspector();
5213 Saves patch ctrl info (originally to deal with a
5214 cancel in the surface dialog
5216 void Patch_Save(patchMesh_t *p)
5218 patchSave.width = p->width;
5219 patchSave.height = p->height;
5220 memcpy(patchSave.ctrl, p->ctrl, sizeof(p->ctrl));
5229 void Patch_Restore(patchMesh_t *p)
5231 p->width = patchSave.width;
5232 p->height = patchSave.height;
5233 memcpy(p->ctrl, patchSave.ctrl, sizeof(p->ctrl));
5236 void Patch_ResetTexturing(float fx, float fy)
5238 for (brush_t* pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5242 patchMesh_t *p = pb->pPatch;
5244 for ( int i = 0 ; i < p->width ; i++ )
5246 for ( int j = 0 ; j < p->height ; j++ )
5248 p->ctrl[i][j].st[0] = fx * (float)i / (p->width - 1);
5249 p->ctrl[i][j].st[1] = 1 - fy * (float)j / (p->height - 1);
5257 void Patch_FitTexturing()
5259 Patch_ResetTexturing(1.0f, 1.0f);
5262 void Patch_SetTextureInfo(texdef_t *pt)
5264 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5269 Patch_RotateTexture(pb->pPatch, pt->rotate);
5271 if (pt->shift[0] || pt->shift[1])
5272 Patch_ShiftTexture(pb->pPatch, pt->shift[0], pt->shift[1]);
5274 if (pt->scale[0] || pt->scale[1])
5275 Patch_ScaleTexture(pb->pPatch, pt->scale[0], pt->scale[1], false);
5277 patchMesh_t *p = pb->pPatch;
5278 p->contents = pt->contents;
5279 p->flags = pt->flags;
5280 p->value = pt->value;
5285 bool OnlyPatchesSelected()
5287 if (g_ptrSelectedFaces.GetSize() > 0 || selected_brushes.next == &selected_brushes)
5289 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5291 if (!pb->patchBrush)
5299 bool AnyPatchesSelected()
5301 if (g_ptrSelectedFaces.GetSize() > 0 || selected_brushes.next == &selected_brushes)
5303 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5313 patchMesh_t* SinglePatchSelected()
5315 if (selected_brushes.next->patchBrush)
5317 return selected_brushes.next->pPatch;
5322 void Patch_BendToggle()
5324 if (g_bPatchBendMode)
5326 g_bPatchBendMode = false;
5328 g_pParentWnd->UpdatePatchToolbarButtons() ;
5332 brush_t* b = selected_brushes.next;
5334 if (!QE_SingleBrush(true) || !b->patchBrush)
5336 Sys_Printf("Patch_BendToggle: you must have a single patch selected\n");
5340 Patch_Save(b->pPatch);
5341 g_bPatchBendMode = true;
5342 g_nPatchBendState = BEND_SELECT_ROTATION;
5343 g_bPatchAxisOnRow = true;
5344 g_nPatchAxisIndex = 1;
5345 ShowInfoDialog(g_pBendStateMsg[BEND_SELECT_ROTATION]);
5348 void Patch_BendHandleTAB()
5350 if (!g_bPatchBendMode)
5355 brush_t* b = selected_brushes.next;
5356 if (!QE_SingleBrush() || !b->patchBrush)
5359 Sys_Printf("No patch to bend!");
5363 patchMesh_t *p = b->pPatch;
5365 bool bShift = Sys_ShiftDown ();
5367 if (g_nPatchBendState == BEND_SELECT_ROTATION)
5369 // only able to deal with odd numbered rows/cols
5370 g_nPatchAxisIndex += (bShift) ? -2 : 2;
5371 if (g_bPatchAxisOnRow)
5373 if ((bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->height)
5375 g_bPatchAxisOnRow = false;
5376 g_nPatchAxisIndex = (bShift) ? p->width-1 : 1;
5381 if ((bShift) ? g_nPatchAxisIndex <= 0 : g_nPatchAxisIndex >= p->width)
5383 g_bPatchAxisOnRow = true;
5384 g_nPatchAxisIndex = (bShift) ? p->height-1 : 1;
5389 if (g_nPatchBendState == BEND_SELECT_ORIGIN)
5391 g_nBendOriginIndex += (bShift) ? -1 : 1;
5392 if (g_bPatchAxisOnRow)
5396 if (g_nBendOriginIndex < 0)
5397 g_nBendOriginIndex = p->width-1;
5401 if (g_nBendOriginIndex > p->width-1)
5402 g_nBendOriginIndex = 0;
5404 VectorCopy(p->ctrl[g_nBendOriginIndex][g_nPatchAxisIndex].xyz, g_vBendOrigin);
5410 if (g_nBendOriginIndex < 0)
5411 g_nBendOriginIndex = p->height-1;
5415 if (g_nBendOriginIndex > p->height-1)
5416 g_nBendOriginIndex = 0;
5418 VectorCopy(p->ctrl[g_nPatchAxisIndex][g_nBendOriginIndex].xyz, g_vBendOrigin);
5422 if (g_nPatchBendState == BEND_SELECT_EDGE)
5424 g_bPatchLowerEdge ^= 1;
5426 Sys_UpdateWindows(W_ALL);
5429 void Patch_BendHandleENTER()
5431 if (!g_bPatchBendMode)
5436 if (g_nPatchBendState < BEND_BENDIT)
5438 g_nPatchBendState++;
5439 ShowInfoDialog(g_pBendStateMsg[g_nPatchBendState]);
5440 if (g_nPatchBendState == BEND_SELECT_ORIGIN)
5442 g_vBendOrigin[0] = g_vBendOrigin[1] = g_vBendOrigin[2] = 0;
5443 g_nBendOriginIndex = 0;
5444 Patch_BendHandleTAB();
5447 if (g_nPatchBendState == BEND_SELECT_EDGE)
5449 g_bPatchLowerEdge = true;
5452 if (g_nPatchBendState == BEND_BENDIT)
5454 // basically we go into rotation mode, set the axis to the center of the
5462 Sys_UpdateWindows(W_ALL);
5467 void Patch_BendHandleESC()
5469 if (!g_bPatchBendMode)
5474 brush_t* b = selected_brushes.next;
5475 if (QE_SingleBrush() && b->patchBrush)
5477 Patch_Restore(b->pPatch);
5479 Sys_UpdateWindows(W_ALL);
5482 void Patch_SetBendRotateOrigin(patchMesh_t *p)
5485 int nType = g_pParentWnd->ActiveXY()->GetViewType();
5486 int nDim3 = (nType == XY) ? 2 : (nType == YZ) ? 0 : 1;
5488 g_vBendOrigin[nDim3] = 0;
5489 VectorCopy(g_vBendOrigin, g_pParentWnd->ActiveXY()->RotateOrigin());
5492 int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
5493 int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
5495 float fxLo, fyLo, fxHi, fyHi;
5497 fxHi = fyHi = -9999;
5499 if (g_bPatchAxisOnRow)
5501 for (int i = 0; i < p->width; i++)
5503 if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1] < fxLo)
5504 fxLo = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1];
5506 if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1] > fxHi)
5507 fxHi = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim1];
5509 if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2] < fyLo)
5510 fyLo = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2];
5512 if (p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2] > fyHi)
5513 fyHi = p->ctrl[i][g_nPatchAxisIndex].xyz[nDim2];
5518 for (int i = 0; i < p->height; i++)
5520 if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1] < fxLo)
5521 fxLo = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1];
5523 if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1] > fxHi)
5524 fxHi = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim1];
5526 if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2] < fyLo)
5527 fyLo = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2];
5529 if (p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2] > fyHi)
5530 fyHi = p->ctrl[g_nPatchAxisIndex][i].xyz[nDim2];
5534 g_pParentWnd->ActiveXY()->RotateOrigin()[0] = g_pParentWnd->ActiveXY()->RotateOrigin()[1] = g_pParentWnd->ActiveXY()->RotateOrigin()[2] = 0.0;
5535 g_pParentWnd->ActiveXY()->RotateOrigin()[nDim1] = (fxLo + fxHi) * 0.5;
5536 g_pParentWnd->ActiveXY()->RotateOrigin()[nDim2] = (fyLo + fyHi) * 0.5;
5540 // also sets the rotational origin
5541 void Patch_SelectBendAxis()
5543 brush_t* b = selected_brushes.next;
5544 if (!QE_SingleBrush() || !b->patchBrush)
5546 // should not ever happen
5551 patchMesh_t *p = b->pPatch;
5552 if (g_bPatchAxisOnRow)
5554 SelectRow(p, g_nPatchAxisIndex, false);
5558 SelectColumn(p, g_nPatchAxisIndex, false);
5561 //FIXME: this only needs to be set once...
5562 Patch_SetBendRotateOrigin(p);
5566 void Patch_SelectBendNormal()
5568 brush_t* b = selected_brushes.next;
5569 if (!QE_SingleBrush() || !b->patchBrush)
5571 // should not ever happen
5576 patchMesh_t *p = b->pPatch;
5578 g_qeglobals.d_num_move_points = 0;
5579 if (g_bPatchAxisOnRow)
5581 if (g_bPatchLowerEdge)
5583 for (int j = 0; j < g_nPatchAxisIndex; j++)
5584 SelectRow(p, j, true);
5588 for (int j = p->height-1; j > g_nPatchAxisIndex; j--)
5589 SelectRow(p, j, true);
5594 if (g_bPatchLowerEdge)
5596 for (int j = 0; j < g_nPatchAxisIndex; j++)
5597 SelectColumn(p, j, true);
5601 for (int j = p->width-1; j > g_nPatchAxisIndex; j--)
5602 SelectColumn(p, j, true);
5605 Patch_SetBendRotateOrigin(p);
5611 void Patch_InsDelToggle()
5613 if (g_bPatchInsertMode)
5615 g_bPatchInsertMode = false;
5617 g_pParentWnd->UpdatePatchToolbarButtons() ;
5621 brush_t* b = selected_brushes.next;
5623 if (!QE_SingleBrush(true) || !b->patchBrush)
5625 Sys_Printf("Patch_InsDelToggle: you must have a single patch selected\n");
5629 Patch_Save(b->pPatch);
5630 g_bPatchInsertMode = true;
5631 g_nPatchInsertState = INSERT_SELECT_EDGE;
5632 g_bPatchAxisOnRow = true;
5633 g_nPatchAxisIndex = 0;
5634 ShowInfoDialog(g_pInsertStateMsg[INSERT_SELECT_EDGE]);
5638 void Patch_InsDelESC()
5640 if (!g_bPatchInsertMode)
5644 Patch_InsDelToggle();
5645 Sys_UpdateWindows(W_ALL);
5649 void Patch_InsDelHandleENTER()
5653 void Patch_InsDelHandleTAB()
5655 if (!g_bPatchInsertMode)
5657 Patch_InsDelToggle();
5661 brush_t* b = selected_brushes.next;
5662 if (!QE_SingleBrush() || !b->patchBrush)
5665 Sys_Printf("No patch to bend!");
5669 patchMesh_t *p = b->pPatch;
5671 // only able to deal with odd numbered rows/cols
5672 g_nPatchAxisIndex += 2;
5673 if (g_bPatchAxisOnRow)
5675 if (g_nPatchAxisIndex >= p->height-1)
5677 g_bPatchAxisOnRow = false;
5678 g_nPatchAxisIndex = 0;
5683 if (g_nPatchAxisIndex >= p->width-1)
5685 g_bPatchAxisOnRow = true;
5686 g_nPatchAxisIndex = 0;
5689 Sys_UpdateWindows(W_ALL);
5694 void _Write1DMatrix (FILE *f, int x, float *m) {
5698 for (i = 0 ; i < x ; i++) {
5699 if (m[i] == (int)m[i] ) {
5700 fprintf (f, "%i ", (int)m[i]);
5702 fprintf (f, "%f ", m[i]);
5708 void _Write2DMatrix (FILE *f, int y, int x, float *m) {
5712 for (i = 0 ; i < y ; i++) {
5713 _Write1DMatrix (f, x, m + i*x);
5720 void _Write3DMatrix (FILE *f, int z, int y, int x, float *m) {
5724 for (i = 0 ; i < z ; i++) {
5725 _Write2DMatrix (f, y, x, m + i*(x*MAX_PATCH_HEIGHT) );
5730 void _Write1DMatrix (MemStream *f, int x, float *m) {
5733 MemFile_fprintf (f, "( ");
5734 for (i = 0 ; i < x ; i++) {
5735 if (m[i] == (int)m[i] ) {
5736 MemFile_fprintf (f, "%i ", (int)m[i]);
5738 MemFile_fprintf (f, "%f ", m[i]);
5741 MemFile_fprintf (f, ")");
5744 void _Write2DMatrix (MemStream *f, int y, int x, float *m) {
5747 MemFile_fprintf (f, "( ");
5748 for (i = 0 ; i < y ; i++) {
5749 _Write1DMatrix (f, x, m + i*x);
5750 MemFile_fprintf (f, " ");
5752 MemFile_fprintf (f, ")\n");
5756 void _Write3DMatrix (MemStream *f, int z, int y, int x, float *m) {
5759 MemFile_fprintf (f, "(\n");
5760 for (i = 0 ; i < z ; i++) {
5761 _Write2DMatrix (f, y, x, m + i*(x*MAX_PATCH_HEIGHT) );
5763 MemFile_fprintf (f, ")\n");
5766 // NOTE: why the hell is this called Naturalize?
5767 // we dispatch either to Patch+Naturalize or Patch_CapTexture..
5768 void Patch_NaturalizeSelected(bool bCap)
5770 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5775 Patch_CapTexture(pb->pPatch);//, bCycleCap);
5777 Patch_Naturalize(pb->pPatch);
5782 // go through the selected patches and call Patch_CapTexture
5783 // deal with cycling
5784 void Patch_CycleCapSelected()
5786 // compute the g_vCycleCapNormal according to g_nCycleCapIndex
5787 VectorClear (g_vCycleCapNormal);
5788 g_vCycleCapNormal[g_nCycleCapIndex] = 1.0f; // cf VIEWTYPE defintion: enum VIEWTYPE {YZ, XZ, XY};
5789 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5793 Patch_CapTexture(pb->pPatch, true);
5796 switch (g_nCycleCapIndex)
5799 g_nCycleCapIndex = XZ;
5802 g_nCycleCapIndex = XY;
5805 g_nCycleCapIndex = YZ;
5810 bool within(vec3_t vTest, vec3_t vTL, vec3_t vBR)
5812 int nDim1 = (g_pParentWnd->ActiveXY()->GetViewType() == YZ) ? 1 : 0;
5813 int nDim2 = (g_pParentWnd->ActiveXY()->GetViewType() == XY) ? 1 : 2;
5814 if ((vTest[nDim1] > vTL[nDim1] && vTest[nDim1] < vBR[nDim1]) ||
5815 (vTest[nDim1] < vTL[nDim1] && vTest[nDim1] > vBR[nDim1]))
5817 if ((vTest[nDim2] > vTL[nDim2] && vTest[nDim2] < vBR[nDim2]) ||
5818 (vTest[nDim2] < vTL[nDim2] && vTest[nDim2] > vBR[nDim2]))
5825 void Patch_SelectAreaPoints(bool bMulti)
5828 g_qeglobals.d_num_move_points = 0;
5830 if( g_nPatchClickedView == W_CAMERA ) {
5831 // Clip against a pyramid
5832 // Create our 5 normals (that are pointing to the inside)
5833 camera_t *m_pCamera = g_pParentWnd->GetCamWnd()->Camera();
5835 float r[2], u[2], hh, hw;
5837 vec_t corners[2][2];
5841 VectorCopy( m_pCamera->vpn, norm[0] ); // only points in front of the camera
5843 // get our rectangle
5844 corners[0][0] = MIN( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaBR[0] );
5845 corners[0][1] = MAX( g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[1] );
5846 corners[1][0] = MAX( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaBR[0] );
5847 corners[1][1] = MIN( g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[1] );
5849 // calculate our four ray vectors
5850 hh = m_pCamera->height/2;
5851 hw = m_pCamera->width/2;
5852 u[0] = (float)(corners[0][1] - hh) / (hw);
5853 r[0] = (float)(corners[0][0] - hw) / (hw);
5854 u[1] = (float)(corners[1][1] - hh) / (hw);
5855 r[1] = (float)(corners[1][0] - hw) / (hw);
5857 for (idx=0 ; idx<3; idx++)
5858 ray[0][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[0] + m_pCamera->vup[idx] * u[0];
5859 for (idx=0 ; idx<3; idx++)
5860 ray[1][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[1] + m_pCamera->vup[idx] * u[0];
5861 for (idx=0 ; idx<3; idx++)
5862 ray[2][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[1] + m_pCamera->vup[idx] * u[1];
5863 for (idx=0 ; idx<3; idx++)
5864 ray[3][idx] = m_pCamera->vpn[idx] + m_pCamera->vright[idx] * r[0] + m_pCamera->vup[idx] * u[1];
5866 // Create our four other directions from these
5867 CrossProduct( ray[0], ray[1], norm[1] ); VectorNormalize( norm[1], norm[1] );
5868 CrossProduct( ray[1], ray[2], norm[2] ); VectorNormalize( norm[2], norm[2] );
5869 CrossProduct( ray[2], ray[3], norm[3] ); VectorNormalize( norm[3], norm[3] );
5870 CrossProduct( ray[3], ray[0], norm[4] ); VectorNormalize( norm[4], norm[4] );
5873 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5877 patchMesh_t *p = pb->pPatch;
5878 for (int i = 0; i < p->width; i++)
5880 for (int j = 0; j < p->height; j++)
5882 VectorSubtract( m_pCamera->origin, p->ctrl[i][j].xyz, check );
5883 VectorNormalize( check, check );
5884 for (idx=0 ; idx<5; idx++)
5886 if (DotProduct(check, norm[idx])>=0)
5889 if (idx == 5) // all test were good
5891 if (bMulti && PointInMoveList(p->ctrl[i][j].xyz) != -1)
5892 RemovePointFromMoveList(p->ctrl[i][j].xyz);
5894 g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
5902 // Simple 2D clipping
5903 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5907 patchMesh_t *p = pb->pPatch;
5908 for (int i = 0; i < p->width; i++)
5910 for (int j = 0; j < p->height; j++)
5912 if (within(p->ctrl[i][j].xyz, g_qeglobals.d_vAreaTL, g_qeglobals.d_vAreaBR))
5914 if (bMulti && PointInMoveList(p->ctrl[i][j].xyz) != -1)
5915 RemovePointFromMoveList(p->ctrl[i][j].xyz);
5917 g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = p->ctrl[i][j].xyz;
5925 g_nPatchClickedView = -1;
5928 // TTimo: return the shader name for a patch
5929 const char* Patch_GetTextureName()
5931 brush_t* b = selected_brushes.next;
5934 patchMesh_t *p = b->pPatch;
5935 return p->pShader->getName();
5940 patchMesh_t* Patch_Duplicate(patchMesh_t *pFrom)
5942 patchMesh_t* p = MakeNewPatch();
5943 memcpy(p, pFrom , sizeof(patchMesh_t));
5945 // spog - initialise patch LOD pointers (again)
5946 Patch_InitialiseLODPointers(p);
5947 p->drawLists = NULL;
5949 p->bSelected = false;
5951 p->bOverlay = false;
5953 AddBrushForPatch(p);
5959 void Patch_Thicken(int nAmount, bool bSeam, qboolean bGroupResult)
5970 if (!QE_SingleBrush())
5972 Sys_Printf("Cannot thicken multiple patches. Please select a single patch.\n");
5976 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
5980 patchMesh_t *p = pb->pPatch;
5981 Patch_MeshNormals(p);
5982 patchMesh_t *pNew = Patch_Duplicate(p);
5983 for (i = 0; i < p->width; i++)
5985 for (j = 0; j < p->height; j++)
5987 VectorMA (p->ctrl[i][j].xyz, nAmount, p->ctrl[i][j].normal, pNew->ctrl[i][j].xyz);
5991 Patch_Rebuild(pNew);
5992 pNew->type |= PATCH_THICK;
5993 brushes.Add(pNew->pSymbiot);
5998 // FIXME: this should detect if any edges of the patch are closed and act appropriately
6000 if (!(p->type & PATCH_CYLINDER))
6002 b = Patch_GenericMesh(3, p->height, 2, false, true);
6004 pSeam->type |= PATCH_SEAM;
6005 for (i = 0; i < p->height; i++)
6007 VectorCopy(p->ctrl[0][i].xyz, pSeam->ctrl[0][i].xyz);
6008 VectorCopy(pNew->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz);
6009 VectorAdd(pSeam->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz, pSeam->ctrl[1][i].xyz);
6010 VectorScale(pSeam->ctrl[1][i].xyz, 0.5, pSeam->ctrl[1][i].xyz);
6014 Patch_CalcBounds(pSeam, vMin, vMax);
6015 Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6016 //--Patch_CapTexture(pSeam);
6017 Patch_Naturalize(pSeam);
6022 b = Patch_GenericMesh(3, p->height, 2, false, true);
6024 pSeam->type |= PATCH_SEAM;
6025 for (i = 0; i < p->height; i++)
6027 VectorCopy(p->ctrl[w][i].xyz, pSeam->ctrl[0][i].xyz);
6028 VectorCopy(pNew->ctrl[w][i].xyz, pSeam->ctrl[2][i].xyz);
6029 VectorAdd(pSeam->ctrl[0][i].xyz, pSeam->ctrl[2][i].xyz, pSeam->ctrl[1][i].xyz);
6030 VectorScale(pSeam->ctrl[1][i].xyz, 0.5, pSeam->ctrl[1][i].xyz);
6032 Patch_CalcBounds(pSeam, vMin, vMax);
6033 Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6034 //--Patch_CapTexture(pSeam);
6035 Patch_Naturalize(pSeam);
6040 // otherwise we will add one per end
6041 b = Patch_GenericMesh(p->width, 3, 2, false, true);
6043 pSeam->type |= PATCH_SEAM;
6044 for (i = 0; i < p->width; i++)
6046 VectorCopy(p->ctrl[i][0].xyz, pSeam->ctrl[i][0].xyz);
6047 VectorCopy(pNew->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz);
6048 VectorAdd(pSeam->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz, pSeam->ctrl[i][1].xyz);
6049 VectorScale(pSeam->ctrl[i][1].xyz, 0.5, pSeam->ctrl[i][1].xyz);
6053 Patch_CalcBounds(pSeam, vMin, vMax);
6054 Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6055 //--Patch_CapTexture(pSeam);
6056 Patch_Naturalize(pSeam);
6061 b = Patch_GenericMesh(p->width, 3, 2, false, true);
6063 pSeam->type |= PATCH_SEAM;
6064 for (i = 0; i < p->width; i++)
6066 VectorCopy(p->ctrl[i][h].xyz, pSeam->ctrl[i][0].xyz);
6067 VectorCopy(pNew->ctrl[i][h].xyz, pSeam->ctrl[i][2].xyz);
6068 VectorAdd(pSeam->ctrl[i][0].xyz, pSeam->ctrl[i][2].xyz, pSeam->ctrl[i][1].xyz);
6069 VectorScale(pSeam->ctrl[i][1].xyz, 0.5, pSeam->ctrl[i][1].xyz);
6071 Patch_CalcBounds(pSeam, vMin, vMax);
6072 Brush_RebuildBrush(pSeam->pSymbiot, vMin, vMax);
6073 //--Patch_CapTexture(pSeam);
6074 Patch_Naturalize(pSeam);
6082 for (i = 0; i < brushes.GetSize(); i++)
6084 Select_Brush(reinterpret_cast<brush_t*>(brushes.GetAt(i)));
6089 entity_t *e = Entity_Alloc();
6090 SetKeyValue(e, "classname", "func_group");
6091 SetKeyValue(e, "type", "patchThick");
6092 Select_GroupEntity(e);
6093 Entity_AddToList(e, &entities);
6096 UpdatePatchInspector();
6101 lets get another list together as far as necessities..
6103 *snapping stuff to the grid (i will only snap movements by the mouse to the grid.. snapping the rotational bend stuff will fubar everything)
6105 capping bevels/endcaps
6109 texture fix for caps
6119 void Patch_SetOverlays()
6121 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6125 pb->pPatch->bOverlay = true;
6132 void Patch_ClearOverlays()
6135 for (pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6139 pb->pPatch->bOverlay = false;
6143 for (pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
6147 pb->pPatch->bOverlay = false;
6153 // FIXME: spog - er, someone forgot to finish their patch point freezing feature?
6154 // freezes selected vertices
6158 for (pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6162 pb->pPatch->bOverlay = false;
6166 for (pb = active_brushes.next ; pb != &active_brushes ; pb = pb->next)
6170 pb->pPatch->bOverlay = false;
6176 void Patch_UnFreeze(bool bAll)
6180 void Patch_Transpose()
6184 for (brush_t *pb = selected_brushes.next ; pb != &selected_brushes ; pb = pb->next)
6188 patchMesh_t *p = pb->pPatch;
6190 if ( p->width > p->height )
6192 for ( i = 0 ; i < p->height ; i++ )
6194 for ( j = i + 1 ; j < p->width ; j++ )
6196 if ( j < p->height )
6199 memcpy(&dv,&p->ctrl[j][i],sizeof(drawVert_t));
6200 memcpy(&p->ctrl[j][i],&p->ctrl[i][j], sizeof(drawVert_t));
6201 memcpy(&p->ctrl[i][j],&dv, sizeof(drawVert_t));
6206 memcpy(&p->ctrl[i][j],&p->ctrl[j][i], sizeof(drawVert_t));
6213 for ( i = 0 ; i < p->width ; i++ )
6215 for ( j = i + 1 ; j < p->height ; j++ )
6220 memcpy(&dv,&p->ctrl[i][j], sizeof(drawVert_t));
6221 memcpy(&p->ctrl[i][j],&p->ctrl[j][i], sizeof(drawVert_t));
6222 memcpy(&p->ctrl[j][i],&dv, sizeof(drawVert_t));
6227 memcpy(&p->ctrl[j][i],&p->ctrl[i][j], sizeof(drawVert_t));
6234 p->width = p->height;
6244 void Patch_SnapToGrid(patchMesh_t *p)
6248 // if patch points selected, snap only selected points
6249 if (g_qeglobals.d_select_mode == sel_curvepoint && g_qeglobals.d_num_move_points != 0)
6250 for (i=0; i<g_qeglobals.d_num_move_points; i++)
6251 for (j = 0; j < 3; j++)
6252 g_qeglobals.d_move_points[i][j] = floor(g_qeglobals.d_move_points[i][j] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
6254 // else snap all patch points
6256 for (i = 0; i < p->width; i++)
6257 for (j = 0; j < p->height; j++)
6258 for (k = 0; k < 3; k++)
6259 p->ctrl[i][j].xyz[k] = floor(p->ctrl[i][j].xyz[k] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize;
6262 Patch_CalcBounds(p, vMin, vMax);
6263 Brush_RebuildBrush(p->pSymbiot, vMin, vMax);
6267 void Patch_FindReplaceTexture(brush_t *pb, const char *pFind, const char *pReplace, bool bForce)
6271 patchMesh_t *p = pb->pPatch;
6272 if (bForce || strcmpi(p->pShader->getName(), pFind) == 0)
6274 p->pShader->DecRef();
6275 p->pShader = QERApp_Shader_ForName(pReplace);
6276 p->d_texture = p->pShader->getTexture();
6281 /* uncomment if necessary, currently not used
6282 void Patch_FromTriangle(vec5_t vx, vec5_t vy, vec5_t vz)
6284 patchMesh_t* p = MakeNewPatch();
6285 p->pShader = g_qeglobals.d_texturewin.pShader;
6286 p->d_texture = g_qeglobals.d_texturewin.pShader->getTexture();
6289 p->type = PATCH_TRIANGLE;
6295 // 1 0 goes to mid of x and z
6296 // 1 1 goes to mid of x y and z
6297 // 1 2 goes to mid of x and y
6300 // 2 1 goes to mid of y and z
6308 for (j = 0; j < 3; j++)
6310 _Vector5Add(vx, vz, vMidXZ);
6311 _Vector5Scale(vMidXZ, 0.5, vMidXZ);
6312 //vMidXZ[j] = vx[j] + abs((vx[j] - vz[j]) * 0.5);
6315 for (j = 0; j < 3; j++)
6317 _Vector5Add(vx, vy, vMidXY);
6318 _Vector5Scale(vMidXY, 0.5, vMidXY);
6319 //vMidXY[j] = vx[j] + abs((vx[j] - vy[j]) * 0.5);
6322 for (j = 0; j < 3; j++)
6324 _Vector5Add(vy, vz, vMidYZ);
6325 _Vector5Scale(vMidYZ, 0.5, vMidYZ);
6326 //vMidYZ[j] = vy[j] + abs((vy[j] - vz[j]) * 0.5);
6329 _Vector53Copy(vx, p->ctrl[0][0].xyz);
6330 _Vector53Copy(vx, p->ctrl[0][1].xyz);
6331 _Vector53Copy(vx, p->ctrl[0][2].xyz);
6332 p->ctrl[0][0].st[0] = vx[3];
6333 p->ctrl[0][0].st[1] = vx[4];
6334 p->ctrl[0][1].st[0] = vx[3];
6335 p->ctrl[0][1].st[1] = vx[4];
6336 p->ctrl[0][2].st[0] = vx[3];
6337 p->ctrl[0][2].st[1] = vx[4];
6339 _Vector53Copy(vMidXY, p->ctrl[1][0].xyz);
6340 _Vector53Copy(vx, p->ctrl[1][1].xyz);
6341 _Vector53Copy(vMidXZ, p->ctrl[1][2].xyz);
6342 p->ctrl[1][0].st[0] = vMidXY[3];
6343 p->ctrl[1][0].st[1] = vMidXY[4];
6344 p->ctrl[1][1].st[0] = vx[3];
6345 p->ctrl[1][1].st[1] = vx[4];
6346 p->ctrl[1][2].st[0] = vMidXZ[3];
6347 p->ctrl[1][2].st[1] = vMidXZ[4];
6349 _Vector53Copy(vy, p->ctrl[2][0].xyz);
6350 _Vector53Copy(vMidYZ, p->ctrl[2][1].xyz);
6351 _Vector53Copy(vz, p->ctrl[2][2].xyz);
6352 p->ctrl[2][0].st[0] = vy[3];
6353 p->ctrl[2][0].st[1] = vy[4];
6354 p->ctrl[2][1].st[0] = vMidYZ[3];
6355 p->ctrl[2][1].st[1] = vMidYZ[4];
6356 p->ctrl[2][2].st[0] = vz[3];
6357 p->ctrl[2][2].st[1] = vz[4];
6360 //Patch_Naturalize(p);
6363 AddBrushForPatch(p);
6368 #ifdef ENABLE_GROUPS
6372 sets an epair for the given patch
6375 void Patch_SetEpair(patchMesh_t *p, const char *pKey, const char *pValue)
6377 if (g_qeglobals.m_bBrushPrimitMode)
6379 SetKeyValue(p->epairs, pKey, pValue);
6388 const char* Patch_GetKeyValue(patchMesh_t *p, const char *pKey)
6390 if (g_qeglobals.m_bBrushPrimitMode)
6392 return ValueForKey(p->epairs, pKey);
6399 //Real nitpicky, but could you make CTRL-S save the current map with the current name? (ie: File/Save)
6402 When reading in textures, please check for the presence of a file called "textures.link" or something, which contains one line such as;
6404 g:\quake3\baseq3\textures\common
6406 So that, when I'm reading in, lets say, my \eerie directory, it goes through and adds my textures to the palette, along with everything in common.
6408 Don't forget to add "Finer texture alignment" to the list. I'd like to be able to move in 0.1 increments using the Shift-Arrow Keys.
6410 No. Sometimes textures are drawn the wrong way on patches. We'd like the ability to flip a texture. Like the way X/Y scale -1 used to worked.
6412 1) Easier way of deleting rows, columns
6413 2) Fine tuning of textures on patches (X/Y shifts other than with the surface dialog)
6414 2) Patch matrix transposition
6416 1) Actually, bump texture flipping on patches to the top of the list of things to do.
6417 2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S
6418 3) Brandon has a wierd anomoly. He fine-tunes a patch with caps. It looks fine when the patch is selected, but as soon as he escapes out, it reverts to it's pre-tuned state. When he selects the patch again, it looks tuned
6421 *1) Flipping textures on patches
6422 *2) When you select a patch, and hit S, it should read in the selected patch texture. Should not work if you multiselect patches and hit S
6423 3) Easier way of deleting rows columns
6425 5) Patch matrix transposition
6426 6) Inverted cylinder capping
6430 Have a new feature request. "Compute Bounding Box" for mapobjects (md3 files). This would be used for misc_mapobject (essentially, drop in 3DS Max models into our maps)
6432 Ok, Feature Request. Load and draw MD3's in the Camera view with proper bounding boxes. This should be off misc_model
6434 Feature Addition: View/Hide Hint Brushes -- This should be a specific case.