-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
-*/\r
-\r
-#include "stdafx.h"\r
-#include <assert.h>\r
-#include "winding.h"\r
-#include <limits.h>\r
-#include "filters.h"\r
-\r
-extern MainFrame* g_pParentWnd;\r
-extern void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...);\r
-\r
-// globals\r
-\r
-int g_nBrushId = 0;\r
-\r
-#ifdef ENABLE_GROUPS\r
-const char* Brush_Name(brush_t *b)\r
-{\r
- static char cBuff[1024];\r
- b->numberId = g_nBrushId++;\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- sprintf(cBuff, "Brush %i", b->numberId);\r
- Brush_SetEpair(b, "Name", cBuff);\r
- }\r
- return cBuff;\r
-}\r
-#endif\r
-\r
-brush_t *Brush_Alloc()\r
-{\r
- brush_t *b = (brush_t*)qmalloc(sizeof(brush_t));\r
- return b;\r
-}\r
-/*\r
-void Brush_Free(brush_t *b)\r
-{\r
- free(b);\r
-}\r
-*/\r
-void PrintWinding (winding_t *w)\r
-{\r
- int i;\r
-\r
- Sys_Printf ("-------------\n");\r
- for (i=0 ; i<w->numpoints ; i++)\r
- Sys_Printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0]\r
- , w->points[i][1], w->points[i][2]);\r
-}\r
-\r
-void PrintPlane (plane_t *p)\r
-{\r
- Sys_Printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n", p->normal[0], p->normal[1], \r
- p->normal[2], p->dist);\r
-}\r
-\r
-void PrintVector (vec3_t v)\r
-{\r
- Sys_Printf ("(%5.2f, %5.2f, %5.2f)\n", v[0], v[1], v[2]);\r
-}\r
-\r
-\r
-/*\r
-=============================================================================\r
-\r
- TEXTURE COORDINATES\r
-\r
-=============================================================================\r
-*/\r
-\r
-\r
-/*\r
-==================\r
-textureAxisFromPlane\r
-==================\r
-*/\r
-vec3_t baseaxis[18] =\r
-{\r
-{0,0,1}, {1,0,0}, {0,-1,0}, // floor\r
-{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling\r
-{1,0,0}, {0,1,0}, {0,0,-1}, // west wall\r
-{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall\r
-{0,1,0}, {1,0,0}, {0,0,-1}, // south wall\r
-{0,-1,0}, {1,0,0}, {0,0,-1} // north wall\r
-};\r
-\r
-void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)\r
-{\r
- int bestaxis;\r
- float dot,best;\r
- int i;\r
- \r
- best = 0;\r
- bestaxis = 0;\r
- \r
- for (i=0 ; i<6 ; i++)\r
- {\r
- dot = DotProduct (pln->normal, baseaxis[i*3]);\r
- if (g_PrefsDlg.m_bQ3Map2Texturing && dot > best + 0.0001f || dot > best)\r
- {\r
- best = dot;\r
- bestaxis = i;\r
- }\r
- }\r
- \r
- VectorCopy (baseaxis[bestaxis*3+1], xv);\r
- VectorCopy (baseaxis[bestaxis*3+2], yv);\r
-}\r
-\r
-\r
-\r
-float lightaxis[3] = {0.6f, 0.8f, 1.0f};\r
-/*\r
-================\r
-SetShadeForPlane\r
-\r
-Light different planes differently to\r
-improve recognition\r
-================\r
-*/\r
-extern float ShadeForNormal(vec3_t normal);\r
-\r
-float SetShadeForPlane (plane_t *p)\r
-{\r
- //return ShadeForNormal(p->normal);\r
-\r
- \r
- int i;\r
- float f;\r
-\r
- // axial plane\r
- for (i=0 ; i<3 ; i++)\r
- if (fabs(p->normal[i]) > 0.9)\r
- {\r
- f = lightaxis[i];\r
- return f;\r
- }\r
-\r
- // between two axial planes\r
- for (i=0 ; i<3 ; i++)\r
- if (fabs(p->normal[i]) < 0.1)\r
- {\r
- f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2;\r
- return f;\r
- }\r
-\r
- // other\r
- f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3;\r
- return f;\r
- \r
-}\r
-\r
-vec3_t vecs[2];\r
-float shift[2];\r
-\r
-/*\r
-================\r
-Face_Alloc\r
-================\r
-*/\r
-face_t *Face_Alloc( void )\r
-{\r
- face_t *f = (face_t*)qmalloc( sizeof( *f ) );\r
- return f;\r
-}\r
-\r
-/*\r
-================\r
-Face_Free\r
-================\r
-*/\r
-void Face_Free( face_t *f )\r
-{\r
- assert( f != 0 );\r
-\r
- if ( f->face_winding )\r
- {\r
- free( f->face_winding );\r
- f->face_winding = 0;\r
- }\r
- f->texdef.~texdef_t();;\r
-\r
- free( f );\r
-}\r
-\r
-/*\r
-================\r
-Face_Clone\r
-================\r
-*/\r
-face_t *Face_Clone (face_t *f)\r
-{\r
- face_t *n;\r
-\r
- n = Face_Alloc();\r
- n->texdef = f->texdef;\r
- n->brushprimit_texdef = f->brushprimit_texdef;\r
-\r
- memcpy (n->planepts, f->planepts, sizeof(n->planepts));\r
-\r
- // all other fields are derived, and will be set by Brush_Build\r
- // FIXME: maybe not, for example n->pData!\r
- return n;\r
-}\r
-\r
-/*\r
-================\r
-Face_FullClone\r
-\r
-makes an exact copy of the face\r
-================\r
-*/\r
-face_t *Face_FullClone (face_t *f)\r
-{\r
- face_t *n;\r
-\r
- n = Face_Alloc();\r
- n->texdef = f->texdef;\r
- n->brushprimit_texdef = f->brushprimit_texdef;\r
- memcpy(n->planepts, f->planepts, sizeof(n->planepts));\r
- memcpy(&n->plane, &f->plane, sizeof(plane_t));\r
- if (f->face_winding)\r
- n->face_winding = Winding_Clone(f->face_winding);\r
- else\r
- n->face_winding = NULL;\r
- n->pShader = f->pShader;\r
- n->pShader->IncRef();\r
- n->d_texture = n->pShader->getTexture();\r
- return n;\r
-}\r
-\r
-void Face_SetShader(face_t *face, const char *name)\r
-{\r
- if(face->pShader != NULL)\r
- face->pShader->DecRef();\r
- face->texdef.SetName(name);\r
- face->pShader = QERApp_Shader_ForName(name);\r
- face->pShader->IncRef();\r
- face->d_texture = face->pShader->getTexture();\r
- face->texdef.flags = face->pShader->getFlags();\r
-}\r
-\r
-void Face_SetShader(face_t *face, IShader *shader)\r
-{\r
- if(face->pShader != NULL)\r
- face->pShader->DecRef();\r
- face->texdef.SetName(shader->getName());\r
- face->d_texture = shader->getTexture();\r
- face->texdef.flags = shader->getFlags();\r
- face->pShader = shader;\r
- face->pShader->IncRef();\r
-}\r
-\r
-/*\r
-================\r
-Clamp\r
-================\r
-*/\r
-void Clamp(float& f, int nClamp)\r
-{\r
- float fFrac = f - static_cast<int>(f);\r
- f = static_cast<int>(f) % nClamp;\r
- f += fFrac;\r
-}\r
-\r
-/*\r
-================\r
-Face_MoveTexture\r
-================\r
-*/\r
-void Face_MoveTexture(face_t *f, vec3_t delta)\r
-{\r
- vec3_t vX, vY;\r
-\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- ShiftTextureGeometric_BrushPrimit( f, delta );\r
- else\r
- {\r
- TextureAxisFromPlane(&f->plane, vX, vY);\r
-\r
- vec3_t vDP, vShift;\r
- vDP[0] = DotProduct(delta, vX);\r
- vDP[1] = DotProduct(delta, vY);\r
-\r
- double fAngle = f->texdef.rotate / 180 * Q_PI;\r
- double c = cos(fAngle);\r
- double s = sin(fAngle);\r
-\r
- vShift[0] = vDP[0] * c - vDP[1] * s;\r
- vShift[1] = vDP[0] * s + vDP[1] * c;\r
-\r
- if (!f->texdef.scale[0])\r
- f->texdef.scale[0] = g_pGameDescription->mTextureDefaultScale;\r
- if (!f->texdef.scale[1])\r
- f->texdef.scale[1] = g_pGameDescription->mTextureDefaultScale;\r
-\r
- f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0];\r
- f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1];\r
- \r
- // clamp the shifts\r
- Clamp(f->texdef.shift[0], f->d_texture->width);\r
- Clamp(f->texdef.shift[1], f->d_texture->height);\r
- }\r
-}\r
-\r
-/*\r
-================\r
-Face_SetColor\r
-================\r
-*/\r
-/*!\todo Replace all face_t::d_texture access with face_t::pShader::GetTexture.*/\r
-void Face_SetColor (brush_t *b, face_t *f, float fCurveColor) \r
-{\r
- // set shading for face\r
- f->d_shade = SetShadeForPlane (&f->plane);\r
- f->d_color[0] = f->pShader->getTexture()->color[0] * f->d_shade;\r
- f->d_color[1] = f->pShader->getTexture()->color[1] * f->d_shade;\r
- f->d_color[2] = f->pShader->getTexture()->color[2] * f->d_shade;\r
-}\r
-\r
-/*\r
-================\r
-Face_TextureVectors\r
-================\r
-*/\r
-void Face_TextureVectors (face_t *f, float STfromXYZ[2][4])\r
-{\r
- vec3_t pvecs[2];\r
- int sv, tv;\r
- float ang, sinv, cosv;\r
- float ns, nt;\r
- int i,j;\r
- qtexture_t *q;\r
- texdef_t *td;\r
-\r
-#ifdef _DEBUG\r
- // this code is not supposed to be used while in BP mode, warning here can help spot the problem\r
- if (g_qeglobals.m_bBrushPrimitMode && !g_qeglobals.bNeedConvert)\r
- Sys_Printf("Warning : illegal call of Face_TextureVectors in brush primitive mode\n");\r
-#endif\r
-\r
- td = &f->texdef;\r
- q = f->d_texture;\r
-\r
- memset (STfromXYZ, 0, 8*sizeof(float));\r
-\r
- if (!td->scale[0])\r
- td->scale[0] = g_pGameDescription->mTextureDefaultScale;\r
- if (!td->scale[1])\r
- td->scale[1] = g_pGameDescription->mTextureDefaultScale;\r
-\r
- // get natural texture axis\r
- TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);\r
-\r
- // rotate axis\r
- if (td->rotate == 0)\r
- { sinv = 0 ; cosv = 1; }\r
- else if (td->rotate == 90)\r
- { sinv = 1 ; cosv = 0; }\r
- else if (td->rotate == 180)\r
- { sinv = 0 ; cosv = -1; }\r
- else if (td->rotate == 270)\r
- { sinv = -1 ; cosv = 0; }\r
- else\r
- { \r
- ang = td->rotate / 180 * Q_PI;\r
- sinv = sin(ang);\r
- cosv = cos(ang);\r
- }\r
-\r
- if (pvecs[0][0])\r
- sv = 0;\r
- else if (pvecs[0][1])\r
- sv = 1;\r
- else\r
- sv = 2;\r
- \r
- if (pvecs[1][0])\r
- tv = 0;\r
- else if (pvecs[1][1])\r
- tv = 1;\r
- else\r
- tv = 2;\r
- \r
- for (i=0 ; i<2 ; i++) {\r
- ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];\r
- nt = sinv * pvecs[i][sv] + cosv * pvecs[i][tv];\r
- STfromXYZ[i][sv] = ns;\r
- STfromXYZ[i][tv] = nt;\r
- }\r
-\r
- // scale\r
- for (i=0 ; i<2 ; i++)\r
- for (j=0 ; j<3 ; j++)\r
- STfromXYZ[i][j] = STfromXYZ[i][j] / td->scale[i];\r
-\r
- // shift\r
- STfromXYZ[0][3] = td->shift[0];\r
- STfromXYZ[1][3] = td->shift[1];\r
-\r
- for (j=0 ; j<4 ; j++) {\r
- STfromXYZ[0][j] /= q->width;\r
- STfromXYZ[1][j] /= q->height;\r
- }\r
-}\r
-\r
-/*\r
-================\r
-Face_MakePlane\r
-================\r
-*/\r
-void Face_MakePlane (face_t *f)\r
-{\r
- int j;\r
- vec3_t t1, t2, t3;\r
-\r
- // convert to a vector / dist plane\r
- for (j=0 ; j<3 ; j++)\r
- {\r
- t1[j] = f->planepts[0][j] - f->planepts[1][j];\r
- t2[j] = f->planepts[2][j] - f->planepts[1][j];\r
- t3[j] = f->planepts[1][j];\r
- }\r
- \r
- CrossProduct(t1,t2, f->plane.normal);\r
- if (VectorCompare (f->plane.normal, vec3_origin))\r
- Sys_FPrintf (SYS_WRN, "WARNING: brush plane with no normal\n");\r
- VectorNormalize (f->plane.normal, f->plane.normal);\r
- f->plane.dist = DotProduct (t3, f->plane.normal);\r
-}\r
-\r
-/*\r
-================\r
-EmitTextureCoordinates\r
-================\r
-*/\r
-void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)\r
-{\r
- float STfromXYZ[2][4];\r
-\r
- Face_TextureVectors (f, STfromXYZ);\r
- xyzst[3] = DotProduct (xyzst, STfromXYZ[0]) + STfromXYZ[0][3];\r
- xyzst[4] = DotProduct (xyzst, STfromXYZ[1]) + STfromXYZ[1][3];\r
-}\r
-\r
-//==========================================================================\r
-\r
-/*\r
-================\r
-Brush_MakeFacePlanes\r
-================\r
-*/\r
-void Brush_MakeFacePlanes (brush_t *b)\r
-{\r
- face_t *f;\r
-\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- {\r
- Face_MakePlane (f);\r
- }\r
-}\r
-\r
-/*\r
-================\r
-DrawBrushEntityName\r
-================\r
-*/\r
-void DrawBrushEntityName (brush_t *b)\r
-{\r
- const char *name;\r
- float a, s, c;\r
- vec3_t mid;\r
- int i;\r
-\r
- if (!b->owner)\r
- return; // during contruction\r
-\r
- if (b->owner == world_entity)\r
- return;\r
-\r
- if (b != b->owner->brushes.onext)\r
- return; // not key brush\r
-\r
- // TTimo: Brush_DrawFacingAngle is for camera view rendering, this function is called for 2D views\r
- // FIXME - spog - not sure who put this here.. Brush_DrawFacingAngle() does this job?\r
- // Brush_DrawFacingAngle() works when called, but is not being called.\r
- if (g_qeglobals.d_savedinfo.show_angles && (b->owner->eclass->nShowFlags & ECLASS_ANGLE))\r
- {\r
- // draw the angle pointer\r
- a = FloatForKey (b->owner, "angle");\r
- s = sin (a/180*Q_PI);\r
- c = cos (a/180*Q_PI);\r
- for (i=0 ; i<3 ; i++)\r
- mid[i] = (b->mins[i] + b->maxs[i])*0.5; \r
-\r
- qglBegin (GL_LINE_STRIP);\r
- qglVertex3fv (mid);\r
- mid[0] += c*8;\r
- mid[1] += s*8;\r
- mid[2] += s*8;\r
- qglVertex3fv (mid);\r
- mid[0] -= c*4;\r
- mid[1] -= s*4;\r
- mid[2] -= s*4;\r
- mid[0] -= s*4;\r
- mid[1] += c*4;\r
- mid[2] += c*4;\r
- qglVertex3fv (mid);\r
- mid[0] += c*4;\r
- mid[1] += s*4;\r
- mid[2] += s*4;\r
- mid[0] += s*4;\r
- mid[1] -= c*4;\r
- mid[2] -= c*4;\r
- qglVertex3fv (mid);\r
- mid[0] -= c*4;\r
- mid[1] -= s*4;\r
- mid[2] -= s*4;\r
- mid[0] += s*4;\r
- mid[1] -= c*4;\r
- mid[2] -= c*4;\r
- qglVertex3fv (mid);\r
- qglEnd ();\r
- }\r
-\r
- if (g_qeglobals.d_savedinfo.show_names)\r
- {\r
- name = ValueForKey (b->owner, "classname");\r
- qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4);\r
- gtk_glwidget_print_string(name);\r
- }\r
-}\r
-\r
-/*\r
-=================\r
-Brush_MakeFaceWinding\r
-\r
-returns the visible polygon on a face\r
-=================\r
-*/\r
-winding_t *Brush_MakeFaceWinding (brush_t *b, face_t *face)\r
-{\r
- winding_t *w;\r
- face_t *clip;\r
- plane_t plane;\r
- qboolean past;\r
-\r
- // get a poly that covers an effectively infinite area\r
- w = Winding_BaseForPlane (&face->plane);\r
-\r
- // chop the poly by all of the other faces\r
- past = false;\r
- for (clip = b->brush_faces ; clip && w ; clip=clip->next)\r
- {\r
- if (clip == face)\r
- {\r
- past = true;\r
- continue;\r
- }\r
- if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999\r
- && fabs(face->plane.dist - clip->plane.dist) < 0.01 )\r
- { // identical plane, use the later one\r
- if (past)\r
- {\r
- free (w);\r
- return NULL;\r
- }\r
- continue;\r
- }\r
-\r
- // flip the plane, because we want to keep the back side\r
- VectorSubtract (vec3_origin,clip->plane.normal, plane.normal);\r
- plane.dist = -clip->plane.dist;\r
- \r
- w = Winding_Clip (w, &plane, false);\r
- if (!w)\r
- return w;\r
- }\r
- \r
- if (w->numpoints < 3)\r
- {\r
- free(w);\r
- w = NULL;\r
- }\r
-\r
- if (!w)\r
- Sys_FPrintf (SYS_WRN, "unused plane\n");\r
-\r
- return w;\r
-}\r
-\r
-/*\r
-=================\r
-Brush_SnapPlanepts\r
-=================\r
-*/\r
-void Brush_SnapPlanepts (brush_t *b)\r
-{\r
- int i, j;\r
- face_t *f;\r
-\r
- if (g_PrefsDlg.m_bNoClamp)\r
- return;\r
-\r
- if (g_qeglobals.d_bSmallGrid)\r
- {\r
- for (f=b->brush_faces ; f; f=f->next)\r
- for (i=0 ; i<3 ; i++)\r
- for (j=0 ; j<3 ; j++)\r
- f->planepts[i][j] = floor (f->planepts[i][j]/g_qeglobals.d_gridsize + 0.5)*g_qeglobals.d_gridsize;\r
- }\r
- else\r
- {\r
- for (f=b->brush_faces ; f; f=f->next)\r
- for (i=0 ; i<3 ; i++)\r
- for (j=0 ; j<3 ; j++)\r
- f->planepts[i][j] = floor (f->planepts[i][j] + 0.5);\r
- }\r
-}\r
- \r
-/*\r
-** Brush_Build\r
-**\r
-** Builds a brush rendering data and also sets the min/max bounds\r
-*/\r
-// TTimo\r
-// added a bConvert flag to convert between old and new brush texture formats\r
-// TTimo\r
-// brush grouping: update the group treeview if necessary\r
-void Brush_Build( brush_t *b, bool bSnap, bool bMarkMap, bool bConvert, bool bFilterTest)\r
-{\r
- bool bLocalConvert;\r
-\r
- \r
-#ifdef _DEBUG\r
- if (!g_qeglobals.m_bBrushPrimitMode && bConvert)\r
- Sys_Printf("Warning : conversion from brush primitive to old brush format not implemented\n");\r
-#endif\r
-\r
- // if bConvert is set and g_qeglobals.bNeedConvert is not, that just means we need convert for this brush only\r
- if (bConvert && !g_qeglobals.bNeedConvert)\r
- {\r
-#ifdef _DEBUG\r
- //++timo FIXME: it's not very clear when this can happen, I guess while dealing with plugins that send brushes\r
- // back and forth in one format or the other .. more when mixing BP / noBP in the same maps.\r
-#endif\r
- bLocalConvert = true;\r
- g_qeglobals.bNeedConvert = true;\r
- }\r
- else\r
- bLocalConvert = false;\r
-\r
- /*\r
- ** build the windings and generate the bounding box\r
- */\r
- Brush_BuildWindings(b, bSnap);\r
-\r
- if(b->owner->model.pRender)\r
- {\r
- const aabb_t *aabb = b->owner->model.pRender->GetAABB();\r
- VectorAdd(aabb->origin, aabb->extents, b->maxs);\r
- VectorSubtract(aabb->origin, aabb->extents, b->mins);\r
- } \r
-\r
- //Patch_BuildPoints (b); // does nothing but set b->patchBrush true if the texdef contains SURF_PATCH !\r
-\r
- /*\r
- ** move the points and edges if in select mode\r
- */\r
- if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)\r
- SetupVertexSelection ();\r
-\r
- if (b->itemOwner == 0) //NULL)\r
- Group_AddToProperGroup(b);\r
-\r
- if (bMarkMap)\r
- {\r
- Sys_MarkMapModified();\r
- }\r
-\r
- if (bLocalConvert)\r
- g_qeglobals.bNeedConvert = false;\r
-\r
- // spog - applying filters to brush during brush_build instead of during redraw\r
- if (bFilterTest)\r
- b->bFiltered = FilterBrush( b );\r
-}\r
-\r
-/*\r
-==============\r
-Brush_SplitBrushByFace\r
-\r
-The incoming brush is NOT freed.\r
-The incoming face is NOT left referenced.\r
-==============\r
-*/\r
-void Brush_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back, boolean bCaulk)\r
-{\r
- brush_t *b;\r
- face_t *nf;\r
- vec3_t temp;\r
-\r
- b = Brush_Clone (in);\r
- nf = Face_Clone (f);\r
-\r
- nf->texdef = b->brush_faces->texdef;\r
- if (bCaulk)\r
- {\r
- nf->texdef.SetName(g_pGameDescription->mCaulkShader.GetBuffer());\r
- }\r
- nf->next = b->brush_faces;\r
- b->brush_faces = nf;\r
-\r
- Brush_Build( b );\r
- Brush_RemoveEmptyFaces ( b );\r
- if ( !b->brush_faces )\r
- { // completely clipped away\r
- Brush_Free (b);\r
- *back = NULL;\r
- }\r
- else\r
- {\r
- Entity_LinkBrush (in->owner, b);\r
- *back = b;\r
- }\r
-\r
- b = Brush_Clone (in);\r
- nf = Face_Clone (f);\r
- // swap the plane winding\r
- VectorCopy (nf->planepts[0], temp);\r
- VectorCopy (nf->planepts[1], nf->planepts[0]);\r
- VectorCopy (temp, nf->planepts[1]);\r
-\r
- nf->texdef = b->brush_faces->texdef;\r
- if (bCaulk)\r
- {\r
- nf->texdef.SetName(g_pGameDescription->mCaulkShader.GetBuffer());\r
- }\r
- nf->next = b->brush_faces;\r
- b->brush_faces = nf;\r
-\r
- Brush_Build( b );\r
- Brush_RemoveEmptyFaces ( b );\r
- if ( !b->brush_faces )\r
- { // completely clipped away\r
- Brush_Free (b);\r
- *front = NULL;\r
- }\r
- else\r
- {\r
- Entity_LinkBrush (in->owner, b);\r
- *front = b;\r
- }\r
-}\r
-\r
-/*\r
-=================\r
-Brush_BestSplitFace\r
-\r
-returns the best face to split the brush with.\r
-return NULL if the brush is convex\r
-=================\r
-*/\r
-face_t *Brush_BestSplitFace(brush_t *b)\r
-{\r
- face_t *face, *f, *bestface;\r
- winding_t *front, *back;\r
- int splits, tinywindings, value, bestvalue;\r
-\r
- bestvalue = 999999;\r
- bestface = NULL;\r
- for (face = b->brush_faces; face; face = face->next)\r
- {\r
- splits = 0;\r
- tinywindings = 0;\r
- for (f = b->brush_faces; f; f = f->next)\r
- {\r
- if (f == face) continue;\r
- //\r
- Winding_SplitEpsilon(f->face_winding, face->plane.normal, face->plane.dist, 0.1f, &front, &back);\r
-\r
- if (!front)\r
- {\r
- Winding_Free(back);\r
- }\r
- else if (!back)\r
- {\r
- Winding_Free(front);\r
- }\r
- else\r
- {\r
- splits++;\r
- if (Winding_IsTiny(front)) tinywindings++;\r
- if (Winding_IsTiny(back)) tinywindings++;\r
- }\r
- }\r
- if (splits)\r
- {\r
- value = splits + 50 * tinywindings;\r
- if (value < bestvalue)\r
- {\r
- bestvalue = value;\r
- bestface = face;\r
- }\r
- }\r
- }\r
- return bestface;\r
-}\r
-\r
-/*\r
-=================\r
-Brush_MakeConvexBrushes\r
-\r
-MrE FIXME: this doesn't work because the old\r
- Brush_SplitBrushByFace is used\r
-Turns the brush into a minimal number of convex brushes.\r
-If the input brush is convex then it will be returned.\r
-Otherwise the input brush will be freed.\r
-NOTE: the input brush should have windings for the faces.\r
-=================\r
-*/\r
-brush_t *Brush_MakeConvexBrushes(brush_t *b)\r
-{\r
- brush_t *front, *back, *end;\r
- face_t *face;\r
-\r
- b->next = NULL;\r
- face = Brush_BestSplitFace(b);\r
- if (!face) return b;\r
- Brush_SplitBrushByFace(b, face, &front, &back);\r
- //this should never happen\r
- if (!front && !back) return b;\r
- Brush_Free(b);\r
- if (!front)\r
- return Brush_MakeConvexBrushes(back);\r
- b = Brush_MakeConvexBrushes(front);\r
- if (back)\r
- {\r
- for (end = b; end->next; end = end->next);\r
- end->next = Brush_MakeConvexBrushes(back);\r
- }\r
- return b;\r
-}\r
-\r
-/*\r
-=================\r
-Brush_Convex\r
-=================\r
-*/\r
-int Brush_Convex(brush_t *b)\r
-{\r
- face_t *face1, *face2;\r
-\r
- for (face1 = b->brush_faces; face1; face1 = face1->next)\r
- {\r
- if (!face1->face_winding) continue;\r
- for (face2 = b->brush_faces; face2; face2 = face2->next)\r
- {\r
- if (face1 == face2) continue;\r
- if (!face2->face_winding) continue;\r
- if (Winding_PlanesConcave(face1->face_winding, face2->face_winding,\r
- face1->plane.normal, face2->plane.normal,\r
- face1->plane.dist, face2->plane.dist))\r
- {\r
- return false;\r
- }\r
- }\r
- }\r
- return true;\r
-}\r
-\r
-/*\r
-=================\r
-Brush_MoveVertexes\r
-\r
-- The input brush must be convex\r
-- The input brush must have face windings.\r
-- The output brush will be convex.\r
-- Returns true if the WHOLE vertex movement is performed.\r
-=================\r
-*/\r
-\r
-// define this to debug the vertex editing mode\r
-#ifdef _DEBUG\r
-//#define DBG_VERT\r
-#endif\r
-\r
-#define MAX_MOVE_FACES 64\r
-\r
-int Brush_MoveVertex(brush_t *b, vec3_t vertex, vec3_t delta, vec3_t end, bool bSnap)\r
-{\r
- face_t *f, *face, *newface, *lastface, *nextface;\r
- face_t *movefaces[MAX_MOVE_FACES];\r
- int movefacepoints[MAX_MOVE_FACES];\r
- winding_t *w, tmpw;\r
- vec3_t start, mid;\r
- plane_t plane;\r
- int i, j, k, nummovefaces, result, done;\r
- float dot, front, back, frac, smallestfrac;\r
-\r
-#ifdef DBG_VERT\r
- Sys_Printf("Bursh_MoveVertex: %p vertex: %g %g %g delta: %g %g %g end: %g %g %g snap: %s\n", b, vertex[0], vertex[1], vertex[2], delta[0], delta[1], delta[2], end[0], end[1], end[2], bSnap ? "true" : "false" );\r
-#endif\r
-\r
- result = true;\r
- //\r
- tmpw.numpoints = 3;\r
- tmpw.maxpoints = 3;\r
- VectorCopy(vertex, start);\r
- VectorAdd(vertex, delta, end);\r
- //snap or not?\r
- if (bSnap)\r
- for (i = 0; i < 3; i++)\r
- end[i] = floor(end[i] / g_qeglobals.d_gridsize + 0.1) * g_qeglobals.d_gridsize;\r
- //\r
- VectorCopy(end, mid);\r
- //if the start and end are the same\r
- if (Point_Equal(start, end, 0.3f)) return false;\r
- //the end point may not be the same as another vertex\r
- for (face = b->brush_faces; face; face = face->next)\r
- {\r
- w = face->face_winding;\r
- if (!w) continue;\r
- for (i = 0; i < w->numpoints; i++)\r
- {\r
- if (Point_Equal(w->points[i], end, 0.3f))\r
- {\r
- VectorCopy(vertex, end);\r
- return false;\r
- }\r
- }\r
- }\r
- //\r
- done = false;\r
- while(!done)\r
- {\r
- //chop off triangles from all brush faces that use the to be moved vertex\r
- //store pointers to these chopped off triangles in movefaces[]\r
- nummovefaces = 0;\r
- for (face = b->brush_faces; face; face = face->next)\r
- {\r
- w = face->face_winding;\r
- if (!w) continue;\r
- for (i = 0; i < w->numpoints; i++)\r
- {\r
- if (Point_Equal(w->points[i], start, 0.2f))\r
- {\r
- if (face->face_winding->numpoints <= 3)\r
- {\r
- movefacepoints[nummovefaces] = i;\r
- movefaces[nummovefaces++] = face;\r
- break;\r
- }\r
- dot = DotProduct(end, face->plane.normal) - face->plane.dist;\r
- //if the end point is in front of the face plane\r
- if (dot > 0.1)\r
- {\r
- //fanout triangle subdivision\r
- for (k = i; k < i + w->numpoints-3; k++)\r
- {\r
- VectorCopy(w->points[i], tmpw.points[0]);\r
- VectorCopy(w->points[(k+1) % w->numpoints], tmpw.points[1]);\r
- VectorCopy(w->points[(k+2) % w->numpoints], tmpw.points[2]);\r
- //\r
- newface = Face_Clone(face);\r
- //get the original\r
- for (f = face; f->original; f = f->original) ;\r
- newface->original = f;\r
- //store the new winding\r
- if (newface->face_winding) Winding_Free(newface->face_winding);\r
- newface->face_winding = Winding_Clone(&tmpw);\r
- //get the texture information\r
- newface->pShader = face->pShader;\r
- newface->d_texture = face->d_texture;\r
-\r
- //add the face to the brush\r
- newface->next = b->brush_faces;\r
- b->brush_faces = newface;\r
- //add this new triangle to the move faces\r
- movefacepoints[nummovefaces] = 0;\r
- movefaces[nummovefaces++] = newface;\r
- }\r
- //give the original face a new winding\r
- VectorCopy(w->points[(i-2+w->numpoints) % w->numpoints], tmpw.points[0]);\r
- VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[1]);\r
- VectorCopy(w->points[i], tmpw.points[2]);\r
- Winding_Free(face->face_winding);\r
- face->face_winding = Winding_Clone(&tmpw);\r
- //add the original face to the move faces\r
- movefacepoints[nummovefaces] = 2;\r
- movefaces[nummovefaces++] = face;\r
- }\r
- else\r
- {\r
- //chop a triangle off the face\r
- VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[0]);\r
- VectorCopy(w->points[i], tmpw.points[1]);\r
- VectorCopy(w->points[(i+1) % w->numpoints], tmpw.points[2]);\r
- //remove the point from the face winding\r
- Winding_RemovePoint(w, i);\r
- //get texture crap right\r
- Face_SetColor(b, face, 1.0);\r
- for (j = 0; j < w->numpoints; j++)\r
- EmitTextureCoordinates(w->points[j], face->d_texture, face);\r
- //make a triangle face\r
- newface = Face_Clone(face);\r
- //get the original\r
- for (f = face; f->original; f = f->original) ;\r
- newface->original = f;\r
- //store the new winding\r
- if (newface->face_winding) Winding_Free(newface->face_winding);\r
- newface->face_winding = Winding_Clone(&tmpw);\r
- //get the texture\r
- newface->pShader = face->pShader;\r
- newface->d_texture = newface->pShader->getTexture();\r
-// newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );\r
- //add the face to the brush\r
- newface->next = b->brush_faces;\r
- b->brush_faces = newface;\r
- //\r
- movefacepoints[nummovefaces] = 1;\r
- movefaces[nummovefaces++] = newface;\r
- }\r
- break;\r
- }\r
- }\r
- }\r
- //now movefaces contains pointers to triangle faces that\r
- //contain the to be moved vertex\r
- //\r
- done = true;\r
- VectorCopy(end, mid);\r
- smallestfrac = 1;\r
- for (face = b->brush_faces; face; face = face->next)\r
- {\r
- //check if there is a move face that has this face as the original\r
- for (i = 0; i < nummovefaces; i++)\r
- {\r
- if (movefaces[i]->original == face) break;\r
- }\r
- if (i >= nummovefaces) continue;\r
- //check if the original is not a move face itself\r
- for (j = 0; j < nummovefaces; j++)\r
- {\r
- if (face == movefaces[j]) break;\r
- }\r
- //if the original is not a move face itself\r
- if (j >= nummovefaces)\r
- {\r
- memcpy(&plane, &movefaces[i]->original->plane, sizeof(plane_t));\r
- }\r
- else\r
- {\r
- k = movefacepoints[j];\r
- w = movefaces[j]->face_winding;\r
- VectorCopy(w->points[(k+1)%w->numpoints], tmpw.points[0]);\r
- VectorCopy(w->points[(k+2)%w->numpoints], tmpw.points[1]);\r
- //\r
- k = movefacepoints[i];\r
- w = movefaces[i]->face_winding;\r
- VectorCopy(w->points[(k+1)%w->numpoints], tmpw.points[2]);\r
- if (!Plane_FromPoints(tmpw.points[0], tmpw.points[1], tmpw.points[2], &plane))\r
- {\r
- VectorCopy(w->points[(k+2)%w->numpoints], tmpw.points[2]);\r
- if (!Plane_FromPoints(tmpw.points[0], tmpw.points[1], tmpw.points[2], &plane))\r
- //this should never happen otherwise the face merge did a crappy job a previous pass\r
- continue;\r
- }\r
- }\r
- //now we've got the plane to check agains\r
- front = DotProduct(start, plane.normal) - plane.dist;\r
- back = DotProduct(end, plane.normal) - plane.dist;\r
- //if the whole move is at one side of the plane\r
- if (front < 0.01 && back < 0.01) continue;\r
- if (front > -0.01 && back > -0.01) continue;\r
- //if there's no movement orthogonal to this plane at all\r
- if (fabs(front-back) < 0.001) continue;\r
- //ok first only move till the plane is hit\r
- frac = front/(front-back);\r
- if (frac < smallestfrac)\r
- {\r
- mid[0] = start[0] + (end[0] - start[0]) * frac;\r
- mid[1] = start[1] + (end[1] - start[1]) * frac;\r
- mid[2] = start[2] + (end[2] - start[2]) * frac;\r
- smallestfrac = frac;\r
- }\r
- //\r
- done = false;\r
- }\r
-\r
- //move the vertex\r
- for (i = 0; i < nummovefaces; i++)\r
- {\r
- //move vertex to end position\r
- VectorCopy(mid, movefaces[i]->face_winding->points[movefacepoints[i]]);\r
- //create new face plane\r
- for (j = 0; j < 3; j++)\r
- {\r
- VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);\r
- }\r
- Face_MakePlane(movefaces[i]);\r
- if (VectorLength(movefaces[i]->plane.normal) < 0.1)\r
- result = false;\r
- }\r
- //if the brush is no longer convex\r
- if (!result || !Brush_Convex(b))\r
- {\r
- for (i = 0; i < nummovefaces; i++)\r
- {\r
- //move the vertex back to the initial position\r
- VectorCopy(start, movefaces[i]->face_winding->points[movefacepoints[i]]);\r
- //create new face plane\r
- for (j = 0; j < 3; j++)\r
- {\r
- VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);\r
- }\r
- Face_MakePlane(movefaces[i]);\r
- }\r
- result = false;\r
- VectorCopy(start, end);\r
- done = true;\r
- }\r
- else\r
- {\r
- VectorCopy(mid, start);\r
- }\r
- //get texture crap right\r
- for (i = 0; i < nummovefaces; i++)\r
- {\r
- Face_SetColor(b, movefaces[i], 1.0);\r
- for (j = 0; j < movefaces[i]->face_winding->numpoints; j++)\r
- EmitTextureCoordinates(movefaces[i]->face_winding->points[j], movefaces[i]->d_texture, movefaces[i]);\r
- }\r
-\r
- //now try to merge faces with their original faces\r
- lastface = NULL;\r
- for (face = b->brush_faces; face; face = nextface)\r
- {\r
- nextface = face->next;\r
- if (!face->original)\r
- {\r
- lastface = face;\r
- continue;\r
- }\r
- if (!Plane_Equal(&face->plane, &face->original->plane, false))\r
- {\r
- lastface = face;\r
- continue;\r
- }\r
- w = Winding_TryMerge(face->face_winding, face->original->face_winding, face->plane.normal, true);\r
- if (!w)\r
- {\r
- lastface = face;\r
- continue;\r
- }\r
- Winding_Free(face->original->face_winding);\r
- face->original->face_winding = w;\r
- //get texture crap right\r
- Face_SetColor(b, face->original, 1.0);\r
- for (j = 0; j < face->original->face_winding->numpoints; j++)\r
- EmitTextureCoordinates(face->original->face_winding->points[j], face->original->d_texture, face->original);\r
- //remove the face that was merged with the original\r
- if (lastface) lastface->next = face->next;\r
- else b->brush_faces = face->next;\r
- Face_Free(face);\r
- }\r
- }\r
- return result;\r
-}\r
-\r
-/*\r
-=================\r
-Brush_InsertVertexBetween\r
-=================\r
-*/\r
-int Brush_InsertVertexBetween(brush_t *b, vec3_t p1, vec3_t p2)\r
-{\r
- face_t *face;\r
- winding_t *w, *neww;\r
- vec3_t point;\r
- int i, insert;\r
-\r
- if (Point_Equal(p1, p2, 0.4f))\r
- return false;\r
- VectorAdd(p1, p2, point);\r
- VectorScale(point, 0.5f, point);\r
- insert = false;\r
- //the end point may not be the same as another vertex\r
- for (face = b->brush_faces; face; face = face->next)\r
- {\r
- w = face->face_winding;\r
- if (!w) continue;\r
- neww = NULL;\r
- for (i = 0; i < w->numpoints; i++)\r
- {\r
- if (!Point_Equal(w->points[i], p1, 0.1f))\r
- continue;\r
- if (Point_Equal(w->points[(i+1) % w->numpoints], p2, 0.1f))\r
- {\r
- neww = Winding_InsertPoint(w, point, (i+1) % w->numpoints);\r
- break;\r
- }\r
- else if (Point_Equal(w->points[(i-1+w->numpoints) % w->numpoints], p2, 0.3f))\r
- {\r
- neww = Winding_InsertPoint(w, point, i);\r
- break;\r
- }\r
- }\r
- if (neww)\r
- {\r
- Winding_Free(face->face_winding);\r
- face->face_winding = neww;\r
- insert = true;\r
- }\r
- }\r
- return insert;\r
-}\r
-\r
-\r
-/*\r
-=================\r
-Brush_ResetFaceOriginals\r
-=================\r
-*/\r
-void Brush_ResetFaceOriginals(brush_t *b)\r
-{\r
- face_t *face;\r
-\r
- for (face = b->brush_faces; face; face = face->next)\r
- {\r
- face->original = NULL;\r
- }\r
-}\r
-\r
-#ifdef ENABLE_GROUPS\r
-/*\r
-==============\r
-Brush_SetEpair\r
-sets an epair for the given brush\r
-==============\r
-*/\r
-void Brush_SetEpair(brush_t *b, const char *pKey, const char *pValue)\r
-{\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- if (b->patchBrush)\r
- {\r
- Patch_SetEpair(b->pPatch, pKey, pValue);\r
- }\r
- else\r
- {\r
- SetKeyValue(b->epairs, pKey, pValue);\r
- }\r
- }\r
- else\r
- {\r
- Sys_Printf("Can only set key/values in Brush primitive mode\n");\r
- }\r
-}\r
-\r
-/* \r
-=================\r
-Brush_GetKeyValue\r
-=================\r
-*/\r
-const char* Brush_GetKeyValue(brush_t *b, const char *pKey)\r
-{\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- if (b->patchBrush)\r
- {\r
- return Patch_GetKeyValue(b->pPatch, pKey);\r
- }\r
- else\r
- {\r
- return ValueForKey(b->epairs, pKey);\r
- }\r
- }\r
- else\r
- {\r
- Sys_Printf("Can only set brush/patch key/values in Brush primitive mode\n");\r
- }\r
- return "";\r
-}\r
-#endif\r
-/*\r
-=================\r
-CheckName\r
-temporary stuff, detect potential problems when saving the texture name\r
-=================\r
-*/\r
-void CheckName( face_t *fa, char *pname )\r
-{\r
- if (!strlen(fa->texdef.GetName()))\r
- {\r
-#ifdef _DEBUG\r
- Sys_Printf("WARNING: unexpected texdef.name is empty in Brush.cpp CheckName\n");\r
-#endif\r
- fa->texdef.SetName(SHADER_NOT_FOUND);\r
- strcpy(pname, SHADER_NOT_FOUND);\r
- return;\r
- }\r
-\r
- // some people manage to get long filename textures (with spaces) in their maps\r
- if (strchr( fa->texdef.GetName(), ' ' ))\r
- {\r
- char Msg1[1024];\r
- \r
- 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.", fa->texdef.GetName() );\r
- \r
- Sys_Printf("%s\n", Msg1 );\r
- gtk_MessageBox(g_pParentWnd->m_pWidget, Msg1, "Error saving map", MB_OK );\r
- strcpy( pname, SHADER_NOT_FOUND );\r
- return;\r
- }\r
-\r
- //++timo FIXME: bug #103494 detection attempt\r
- // TODO: clean this detection part when bug will have disappeared\r
- if (fa->texdef.GetName()[0] == '(')\r
- {\r
- char *text = "Bug #103494 detected, dropping texture. Please report to timo@qeradiant.com if you have a way to reproduce!\nNOTE: this message may popup several times .. once for each buggy face detected.";\r
- Sys_Printf("%s\n", text);\r
- gtk_MessageBox(g_pParentWnd->m_pWidget, text, "Error saving map", MB_OK );\r
- // need to cleanup this dead face name or we may loop endlessly\r
- fa->texdef.SetName(SHADER_NOT_FOUND);\r
- strcpy( pname, SHADER_NOT_FOUND );\r
- return;\r
- }\r
- strcpy( pname, fa->texdef.GetName()+9 ); // remove "textures/"\r
-}\r
-\r
-/*\r
-=============\r
-Brush_Create\r
-\r
-Create non-textured blocks for entities\r
-The brush is NOT linked to any list\r
-=============\r
-*/\r
-brush_t *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef)\r
-{\r
- int i, j;\r
- vec3_t pts[4][2];\r
- face_t *f;\r
- brush_t *b;\r
-\r
-#if DBG_BP\r
- // brush primitive mode : convert texdef to brushprimit_texdef ?\r
- // most of the time texdef is empty\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- // check texdef is empty .. if there are cases it's not we need to write some conversion code\r
- if (texdef->shift[0]!=0 || texdef->shift[1]!=0 || texdef->scale[0]!=0 || texdef->scale[1]!=0 || texdef->rotate!=0)\r
- Sys_Printf("Warning : non-zero texdef detected in Brush_Create .. need brush primitive conversion\n");\r
- }\r
-#endif\r
-\r
- for (i=0 ; i<3 ; i++)\r
- {\r
- if (maxs[i] < mins[i])\r
- Error ("Brush_InitSolid: backwards");\r
- }\r
-\r
- b = Brush_Alloc();\r
- \r
- pts[0][0][0] = mins[0];\r
- pts[0][0][1] = mins[1];\r
- \r
- pts[1][0][0] = mins[0];\r
- pts[1][0][1] = maxs[1];\r
- \r
- pts[2][0][0] = maxs[0];\r
- pts[2][0][1] = maxs[1];\r
- \r
- pts[3][0][0] = maxs[0];\r
- pts[3][0][1] = mins[1];\r
- \r
- for (i=0 ; i<4 ; i++)\r
- {\r
- pts[i][0][2] = mins[2];\r
- pts[i][1][0] = pts[i][0][0];\r
- pts[i][1][1] = pts[i][0][1];\r
- pts[i][1][2] = maxs[2];\r
- }\r
-\r
- for (i=0 ; i<4 ; i++)\r
- {\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->texdef.flags &= ~SURF_KEEP;\r
- f->texdef.contents &= ~CONTENTS_KEEP;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
- j = (i+1)%4;\r
-\r
- VectorCopy (pts[j][1], f->planepts[0]);\r
- VectorCopy (pts[i][1], f->planepts[1]);\r
- VectorCopy (pts[i][0], f->planepts[2]);\r
- }\r
- \r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->texdef.flags &= ~SURF_KEEP;\r
- f->texdef.contents &= ~CONTENTS_KEEP;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- VectorCopy (pts[0][1], f->planepts[0]);\r
- VectorCopy (pts[1][1], f->planepts[1]);\r
- VectorCopy (pts[2][1], f->planepts[2]);\r
-\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->texdef.flags &= ~SURF_KEEP;\r
- f->texdef.contents &= ~CONTENTS_KEEP;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- VectorCopy (pts[2][0], f->planepts[0]);\r
- VectorCopy (pts[1][0], f->planepts[1]);\r
- VectorCopy (pts[0][0], f->planepts[2]);\r
-\r
- return b;\r
-}\r
-\r
-/*\r
-=============\r
-Brush_CreatePyramid\r
-\r
-Create non-textured pyramid for light entities\r
-The brush is NOT linked to any list\r
-=============\r
-*/\r
-brush_t *Brush_CreatePyramid (vec3_t mins, vec3_t maxs, texdef_t *texdef)\r
-{\r
- int i;\r
-\r
- //++timo handle new brush primitive ? return here ??\r
- return Brush_Create(mins, maxs, texdef);\r
-\r
- for (i=0 ; i<3 ; i++)\r
- if (maxs[i] < mins[i])\r
- Error ("Brush_InitSolid: backwards");\r
-\r
- brush_t* b = Brush_Alloc();\r
-\r
- vec3_t corners[4];\r
-\r
- float fMid = Rad_rint(mins[2] + (Rad_rint((maxs[2] - mins[2]) / 2)));\r
-\r
- corners[0][0] = mins[0];\r
- corners[0][1] = mins[1];\r
- corners[0][2] = fMid;\r
-\r
- corners[1][0] = mins[0];\r
- corners[1][1] = maxs[1];\r
- corners[1][2] = fMid;\r
-\r
- corners[2][0] = maxs[0];\r
- corners[2][1] = maxs[1];\r
- corners[2][2] = fMid;\r
-\r
- corners[3][0] = maxs[0];\r
- corners[3][1] = mins[1];\r
- corners[3][2] = fMid;\r
-\r
- vec3_t top, bottom;\r
-\r
- top[0] = Rad_rint(mins[0] + ((maxs[0] - mins[0]) / 2));\r
- top[1] = Rad_rint(mins[1] + ((maxs[1] - mins[1]) / 2));\r
- top[2] = Rad_rint(maxs[2]);\r
-\r
- VectorCopy(top, bottom);\r
- bottom[2] = mins[2];\r
-\r
- // sides\r
- for (i = 0; i < 4; i++)\r
- {\r
- face_t* f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->texdef.flags &= ~SURF_KEEP;\r
- f->texdef.contents &= ~CONTENTS_KEEP;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
- int j = (i+1)%4;\r
-\r
- VectorCopy (top, f->planepts[0]);\r
- VectorCopy (corners[i], f->planepts[1]);\r
- VectorCopy(corners[j], f->planepts[2]);\r
-\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->texdef.flags &= ~SURF_KEEP;\r
- f->texdef.contents &= ~CONTENTS_KEEP;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- VectorCopy (bottom, f->planepts[2]);\r
- VectorCopy (corners[i], f->planepts[1]);\r
- VectorCopy(corners[j], f->planepts[0]);\r
- }\r
-\r
- return b;\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-=============\r
-Brush_MakeSided\r
-\r
-Makes the current brush have the given number of 2d sides\r
-=============\r
-*/\r
-void Brush_MakeSided (int sides)\r
-{\r
- int i, axis;\r
- vec3_t mins, maxs;\r
- brush_t *b;\r
- texdef_t *texdef;\r
- face_t *f;\r
- vec3_t mid;\r
- float width;\r
- float sv, cv;\r
-\r
- if (sides < 3)\r
- {\r
- Sys_Status ("Bad sides number", 0);\r
- return;\r
- }\r
-\r
- if (sides >= MAX_POINTS_ON_WINDING-4)\r
- {\r
- Sys_Printf("too many sides.\n");\r
- return;\r
- }\r
-\r
- if (!QE_SingleBrush ())\r
- {\r
- Sys_Status ("Must have a single brush selected", 0 );\r
- return;\r
- }\r
-\r
- b = selected_brushes.next;\r
- VectorCopy (b->mins, mins);\r
- VectorCopy (b->maxs, maxs);\r
- texdef = &g_qeglobals.d_texturewin.texdef;\r
-\r
- Brush_Free (b);\r
-\r
- if (g_pParentWnd->ActiveXY())\r
- {\r
- switch(g_pParentWnd->ActiveXY()->GetViewType())\r
- {\r
- case XY: axis = 2; break;\r
- case XZ: axis = 1; break;\r
- case YZ: axis = 0; break;\r
- }\r
- }\r
- else\r
- {\r
- axis = 2;\r
- }\r
-\r
- // find center of brush\r
- width = 8;\r
- for (i = 0; i < 3; i++)\r
- {\r
- mid[i] = (maxs[i] + mins[i]) * 0.5;\r
- if (i == axis) continue;\r
- if ((maxs[i] - mins[i]) * 0.5 > width)\r
- width = (maxs[i] - mins[i]) * 0.5;\r
- }\r
-\r
- b = Brush_Alloc();\r
- \r
- // create top face\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- f->planepts[2][(axis+1)%3] = mins[(axis+1)%3]; f->planepts[2][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[2][axis] = maxs[axis];\r
- f->planepts[1][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[1][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[1][axis] = maxs[axis];\r
- f->planepts[0][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[0][(axis+2)%3] = maxs[(axis+2)%3]; f->planepts[0][axis] = maxs[axis];\r
-\r
- // create bottom face\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- f->planepts[0][(axis+1)%3] = mins[(axis+1)%3]; f->planepts[0][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[0][axis] = mins[axis];\r
- f->planepts[1][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[1][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[1][axis] = mins[axis];\r
- f->planepts[2][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[2][(axis+2)%3] = maxs[(axis+2)%3]; f->planepts[2][axis] = mins[axis];\r
-\r
- for (i=0 ; i<sides ; i++)\r
- {\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- sv = sin (i*3.14159265*2/sides);\r
- cv = cos (i*3.14159265*2/sides);\r
-\r
- f->planepts[0][(axis+1)%3] = floor(mid[(axis+1)%3]+width*cv+0.5);\r
- f->planepts[0][(axis+2)%3] = floor(mid[(axis+2)%3]+width*sv+0.5);\r
- f->planepts[0][axis] = mins[axis];\r
-\r
- f->planepts[1][(axis+1)%3] = f->planepts[0][(axis+1)%3];\r
- f->planepts[1][(axis+2)%3] = f->planepts[0][(axis+2)%3];\r
- f->planepts[1][axis] = maxs[axis];\r
-\r
- f->planepts[2][(axis+1)%3] = floor(f->planepts[0][(axis+1)%3] - width*sv + 0.5);\r
- f->planepts[2][(axis+2)%3] = floor(f->planepts[0][(axis+2)%3] + width*cv + 0.5);\r
- f->planepts[2][axis] = maxs[axis];\r
- }\r
-\r
- Brush_AddToList (b, &selected_brushes);\r
-\r
- Entity_LinkBrush (world_entity, b);\r
-\r
- Brush_Build( b );\r
-\r
- Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-\r
-\r
-/*\r
-=============\r
-Brush_Free\r
-\r
-Frees the brush with all of its faces and display list.\r
-Unlinks the brush from whichever chain it is in.\r
-Decrements the owner entity's brushcount.\r
-Removes owner entity if this was the last brush\r
-unless owner is the world.\r
-Removes from groups\r
-=============\r
-*/\r
-void Brush_Free (brush_t *b, bool bRemoveNode)\r
-{\r
- face_t *f, *next;\r
- epair_t *ep, *enext;\r
-\r
- // remove from group\r
- if (bRemoveNode)\r
- Group_RemoveBrush(b);\r
-\r
- // free the patch if it's there\r
- if (b->patchBrush)\r
- {\r
- Patch_Delete(b->pPatch);\r
- }\r
-\r
- // free faces\r
- for (f=b->brush_faces ; f ; f=next)\r
- {\r
- next = f->next;\r
- Face_Free( f );\r
- }\r
-\r
- // TTimo : free brush epairs\r
- for (ep = b->epairs ; ep ; ep=enext )\r
- {\r
- enext = ep->next;\r
- free (ep->key);\r
- free (ep->value);\r
- free (ep);\r
- }\r
-\r
- // unlink from active/selected list\r
- if (b->next)\r
- Brush_RemoveFromList (b);\r
-\r
- // unlink from entity list\r
- if (b->onext)\r
- Entity_UnlinkBrush (b);\r
-\r
- free (b);\r
-}\r
-\r
-/*\r
-=============\r
-Face_MemorySize\r
-=============\r
-*/\r
-int Face_MemorySize(face_t *f )\r
-{\r
- int size = 0;\r
-\r
- if (f->face_winding)\r
- {\r
-// size += _msize(f->face_winding);\r
- size += sizeof(vec3_t)*f->face_winding->numpoints+2*sizeof(int);\r
- }\r
-// size += _msize(f);\r
- size += sizeof(face_t);\r
- return size;\r
-}\r
-\r
-/*\r
-=============\r
-Brush_MemorySize\r
-=============\r
-*/\r
-int Brush_MemorySize(brush_t *b)\r
-{\r
- face_t *f;\r
- epair_t *ep;\r
- int size = 0;\r
-\r
- //\r
- if (b->patchBrush)\r
- {\r
- size += Patch_MemorySize(b->pPatch);\r
- }\r
- //\r
- for (f = b->brush_faces; f; f = f->next)\r
- {\r
- size += Face_MemorySize(f);\r
- }\r
- //\r
- for (ep = b->epairs; ep; ep = ep->next )\r
- {\r
-// size += _msize(ep->key);\r
- size += strlen(ep->key);\r
-// size += _msize(ep->value);\r
- size += strlen(ep->value);\r
-// size += _msize(ep);\r
- size += sizeof(epair_t);\r
- }\r
-// size += _msize(b);\r
- size += sizeof(brush_t);\r
- return size;\r
-}\r
-\r
-\r
-/*\r
-============\r
-Brush_Clone\r
-\r
-Does NOT add the new brush to any lists\r
-============\r
-*/\r
-brush_t *Brush_Clone (brush_t *b)\r
-{\r
- brush_t *n = NULL;\r
- face_t *f, *nf;\r
-\r
- if (b->patchBrush)\r
- {\r
- patchMesh_t *p = Patch_Duplicate(b->pPatch);\r
- Brush_RemoveFromList(p->pSymbiot);\r
- Entity_UnlinkBrush(p->pSymbiot);\r
- n = p->pSymbiot;\r
- }\r
- else\r
- {\r
- n = Brush_Alloc();\r
- n->numberId = g_nBrushId++;\r
- n->owner = b->owner;\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- {\r
- nf = Face_Clone( f );\r
- nf->next = n->brush_faces;\r
- n->brush_faces = nf;\r
- }\r
- }\r
-\r
- return n;\r
-}\r
-\r
-/*\r
-============\r
-Brush_Clone\r
-\r
-Does NOT add the new brush to any lists\r
-============\r
-*/\r
-brush_t *Brush_FullClone(brush_t *b)\r
-{\r
- brush_t *n = NULL;\r
- face_t *f, *nf, *f2, *nf2;\r
- int j;\r
-\r
- if (b->patchBrush)\r
- {\r
- patchMesh_t *p = Patch_Duplicate(b->pPatch);\r
- Brush_RemoveFromList(p->pSymbiot);\r
- Entity_UnlinkBrush(p->pSymbiot);\r
- n = p->pSymbiot;\r
- n->owner = b->owner;\r
- Brush_Build(n);\r
- }\r
- else\r
- {\r
- n = Brush_Alloc();\r
- n->numberId = g_nBrushId++;\r
- n->owner = b->owner;\r
- VectorCopy(b->mins, n->mins);\r
- VectorCopy(b->maxs, n->maxs);\r
- //\r
- for (f = b->brush_faces; f; f = f->next)\r
- {\r
- if (f->original) continue;\r
- nf = Face_FullClone(f);\r
- nf->next = n->brush_faces;\r
- n->brush_faces = nf;\r
- //copy all faces that have the original set to this face\r
- for (f2 = b->brush_faces; f2; f2 = f2->next)\r
- {\r
- if (f2->original == f)\r
- {\r
- nf2 = Face_FullClone(f2);\r
- nf2->next = n->brush_faces;\r
- n->brush_faces = nf2;\r
- //set original\r
- nf2->original = nf;\r
- }\r
- }\r
- }\r
- for (nf = n->brush_faces; nf; nf = nf->next)\r
- {\r
- Face_SetColor(n, nf, 1.0);\r
- if (nf->face_winding)\r
- {\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- EmitBrushPrimitTextureCoordinates(nf,nf->face_winding);\r
- else\r
- {\r
- for (j = 0; j < nf->face_winding->numpoints; j++)\r
- EmitTextureCoordinates(nf->face_winding->points[j], nf->d_texture, nf);\r
- }\r
- }\r
- }\r
- }\r
- return n;\r
-}\r
-\r
- // FIXME - spog - finish this later..\r
- /*\r
-bool Triangle_Ray(vec3_t origin, vec3_t dir, vec3_t p1, vec3_t p2, vec3_t p3)\r
-{\r
- int i;\r
- vec3_t v1, v2, normal[3];\r
- float d;\r
-\r
- //Sys_Printf("p1: %f %f %f\n",p1[0],p1[1],p1[2]);\r
- //Sys_Printf("p2: %f %f %f\n",p2[0],p2[1],p2[2]);\r
- //Sys_Printf("p3: %f %f %f\n",p3[0],p3[1],p3[2]);\r
- //Sys_Printf("origin: %f %f %f\n",origin[0],origin[1],origin[2]);\r
- \r
- // test ray against triangle\r
- // get triangle plane normal\r
- //VectorSubtract(p1, p2, v1);\r
- //VectorSubtract(p1, p3, v2);\r
- //CrossProduct(v1, v2, v1);\r
- // check normal against direction\r
- //if (DotProduct(dir, v1) >= 0)\r
- //{\r
- // generate cone normals\r
- VectorSubtract(origin, p1, v1);\r
- VectorSubtract(origin, p2, v2);\r
- CrossProduct(v1, v2, normal[0]);\r
- VectorSubtract(origin, p2, v1);\r
- VectorSubtract(origin, p3, v2);\r
- CrossProduct(v1, v2, normal[1]);\r
- VectorSubtract(origin, p3, v1);\r
- VectorSubtract(origin, p1, v2);\r
- CrossProduct(v1, v2, normal[2]);\r
- //}\r
- //else\r
- //{\r
- // flip normals if triangle faces away\r
- // Sys_Printf("flipped\n");\r
- // VectorSubtract(origin, p1, v1);\r
- // VectorSubtract(origin, p3, v2);\r
- // CrossProduct(v1, v2, normal[0]);\r
- // VectorSubtract(origin, p3, v1);\r
- // VectorSubtract(origin, p2, v2);\r
- // CrossProduct(v1, v2, normal[1]);\r
- // VectorSubtract(origin, p2, v1);\r
- // VectorSubtract(origin, p1, v2);\r
- // CrossProduct(v1, v2, normal[2]);\r
- //}\r
- \r
- for (i=0; i<3; i++)\r
- {\r
- VectorNormalize(normal[i]);\r
- //Sys_Printf("direction: %f %f %f\n",dir[0],dir[1],dir[2]);\r
- //Sys_Printf("normal: %f %f %f\n",normal[i][0],normal[i][1],normal[i][2]);\r
- d = DotProduct(dir, normal[i]);\r
- //Sys_Printf("dotproduct: %f\n",d);\r
- if (d < 0)\r
- return false;\r
- }\r
- return true;\r
-}\r
-*/\r
-\r
-/*\r
-extern int Triangle_Ray(float orig[3], float dir[3], bool bCullBack,\r
- float vert0[3], float vert1[3], float vert2[3],\r
- double *t, double *u, double *v);\r
-\r
-bool Model_Ray(brush_t *b, vec3_t origin, vec3_t dir, double *t, double *u, double *v)\r
-{\r
- bool bIntersect = false;\r
- float tBest = FLT_MAX;\r
- int i, j;\r
- vec3_t xyz[3];\r
- vec3_t vRay[2];\r
-\r
- float angle = FloatForKey (b->owner, "angle"); // FIXME: should be set when this entity key is set\r
- \r
- VectorSubtract (origin, b->owner->origin, vRay[0]);\r
- VectorCopy (dir, vRay[1]);\r
-\r
- if (angle > 0)\r
- {\r
- int i;\r
- float s, c;\r
- float x, y;\r
-\r
- s = sin (-angle/180*Q_PI);\r
- c = cos (-angle/180*Q_PI);\r
-\r
- for (i=0; i<2; i++)\r
- {\r
- x = vRay[i][0];\r
- y = vRay[i][1];\r
- vRay[i][0] = (x * c) - (y * s);\r
- vRay[i][1] = (x * s) + (y * c);\r
- }\r
- }\r
-\r
- entitymodel *model = b->owner->md3Class->model;\r
-\r
- while (model != NULL)\r
- {\r
- for (i = 0; i < model->nTriCount; i++)\r
- {\r
- for (j = 0; j < 3; j++)\r
- VectorCopy(model->pVertList[model->pTriList[i].indexes[j]].v, xyz[j]);\r
- \r
- if (Triangle_Ray(vRay[0], vRay[1], true, xyz[0], xyz[2], xyz[1], t, u, v))\r
- {\r
- bIntersect = true;\r
- if (*t < tBest)\r
- tBest = *t;\r
- }\r
- }\r
- model = model->pNext;\r
- }\r
- if (bIntersect)\r
- {\r
- *t = tBest;\r
- return true;\r
- }\r
- else\r
- {\r
- *t = 0;\r
- return false;\r
- }\r
-}\r
-*/\r
-\r
-/*\r
-==============\r
-Brush_Ray\r
-\r
-Itersects a ray with a brush\r
-Returns the face hit and the distance along the ray the intersection occured at\r
-Returns NULL and 0 if not hit at all\r
-\r
-http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=556\r
-==============\r
-*/\r
-extern bool Patch_Ray(patchMesh_t *patch, vec3_t origin, vec3_t dir, double *t, double *u, double *v);\r
-face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist, int nFlags)\r
-{\r
- face_t *f, *firstface = NULL;\r
- vec3_t p1, p2;\r
- float frac, d1, d2;\r
- int i;\r
-\r
- if (b->owner->eclass->fixedsize\r
- && b->owner->model.pSelect\r
- && !(!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY))\r
- && g_PrefsDlg.m_nEntityShowState != ENTITY_BOX)\r
- {\r
- ray_t ray_local;\r
- vec_t dist_local = FLT_MAX;\r
- ray_construct_for_vec3(&ray_local, origin, dir);\r
- if (b->owner->model.pSelect->TestRay(&ray_local, &dist_local))\r
- {\r
- *dist = dist_local;\r
- return b->brush_faces;\r
- }\r
- else\r
- {\r
- *dist = 0.0f;\r
- return NULL;\r
- }\r
- }\r
-\r
- VectorCopy (origin, p1);\r
- for (i=0 ; i<3 ; i++)\r
- p2[i] = p1[i] + dir[i]*2*g_MaxWorldCoord;\r
-\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- {\r
- d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;\r
- d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;\r
- if (d1 >= 0 && d2 >= 0)\r
- {\r
- *dist = 0;\r
- return NULL; // ray is on front side of face\r
- }\r
- if (d1 <=0 && d2 <= 0)\r
- continue;\r
- // clip the ray to the plane\r
- frac = d1 / (d1 - d2);\r
- if (d1 > 0)\r
- {\r
- firstface = f;\r
- for (i=0 ; i<3 ; i++)\r
- p1[i] = p1[i] + frac *(p2[i] - p1[i]);\r
- }\r
- else\r
- {\r
- for (i=0 ; i<3 ; i++)\r
- p2[i] = p1[i] + frac *(p2[i] - p1[i]);\r
- }\r
- }\r
-\r
- // find distance p1 is along dir\r
- VectorSubtract (p1, origin, p1);\r
- d1 = DotProduct (p1, dir);\r
-\r
- *dist = d1;\r
- \r
- // new test stuff for patches\r
- if (!g_PrefsDlg.m_bPatchBBoxSelect && b->patchBrush)\r
- {\r
- double t, u, v; // t is the distance from origin to point-of-intersection.. er.. i don't know what u and v are\r
- if (!Patch_Ray(b->pPatch, origin, dir, &t, &u, &v))\r
- {\r
- *dist = 0;\r
- return NULL;\r
- }\r
- else\r
- {\r
- *dist = (float)t;\r
- //Sys_Printf("t: %f, u: %f, v: %f\n", t, u, v);\r
- }\r
- }\r
-\r
- // IMPORTANT NOTE:\r
- // modifications to the discarding code here should be matched in the selection code\r
- // see Brush_Draw \r
- \r
- // do some last minute filtering\r
- if (firstface && nFlags & SF_CAMERA)\r
- {\r
- if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK)\r
- {\r
- if (strstr(firstface->texdef.GetName(), "caulk"))\r
- {\r
- *dist = 0;\r
- return NULL;\r
- }\r
- }\r
- if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_BOTCLIP)\r
- {\r
- if (strstr(firstface->texdef.GetName(), "botclip") || strstr(firstface->texdef.GetName(), "clipmonster"))\r
- {\r
- *dist = 0;\r
- return NULL;\r
- }\r
- } \r
- if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)\r
- {\r
- if (strstr(firstface->texdef.GetName(), "clip"))\r
- {\r
- *dist = 0;\r
- return NULL;\r
- }\r
- }\r
- }\r
-\r
- return firstface;\r
-}\r
-\r
-//PGM\r
-face_t *Brush_Point (vec3_t origin, brush_t *b)\r
-{\r
- face_t *f;\r
- float d1;\r
-\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- {\r
- d1 = DotProduct (origin, f->plane.normal) - f->plane.dist;\r
- if (d1 > 0)\r
- {\r
- return NULL; // point is on front side of face\r
- }\r
- }\r
-\r
- return b->brush_faces;\r
-}\r
-//PGM\r
-\r
-\r
-void Brush_AddToList (brush_t *b, brush_t *blist)\r
-{\r
- if (b->next || b->prev)\r
- Error ("Brush_AddToList: already linked");\r
- \r
- if (blist == &selected_brushes || blist == &active_brushes)\r
- {\r
- if (b->patchBrush && blist == &selected_brushes)\r
- {\r
- Patch_Select(b->pPatch);\r
- }\r
- }\r
- b->next = blist->next;\r
- blist->next->prev = b;\r
- blist->next = b;\r
- b->prev = blist;\r
- \r
- // TTimo messaging\r
- DispatchRadiantMsg( RADIANT_SELECTION ); \r
-}\r
-\r
-void Brush_RemoveFromList (brush_t *b)\r
-{\r
- if (!b->next || !b->prev)\r
- Error ("Brush_RemoveFromList: not linked");\r
- \r
- if (b->patchBrush)\r
- {\r
- Patch_Deselect(b->pPatch);\r
- }\r
- b->next->prev = b->prev;\r
- b->prev->next = b->next;\r
- b->next = b->prev = NULL;\r
-}\r
-\r
-/*\r
-===============\r
-SetFaceTexdef\r
-\r
-Doesn't set the curve flags\r
-\r
-NOTE : ( TTimo )\r
- never trust f->d_texture here, f->texdef and f->d_texture are out of sync when called by Brush_SetTexture\r
- use Texture_ForName() to find the right shader\r
- FIXME : send the right shader ( qtexture_t * ) in the parameters ?\r
-\r
-TTimo: surface plugin, added an IPluginTexdef* parameter\r
- if not NULL, get ->Copy() of it into the face ( and remember to hook )\r
- if NULL, ask for a default\r
-\r
- TTimo - shader code cleanup\r
- added IShader* parameter\r
- ===============\r
-*/\r
-void SetFaceTexdef2 (brush_t *b, face_t *f, IShader *pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pPlugTexdef) {\r
- int oldFlags;\r
- int oldContents;\r
- face_t *tf;\r
-\r
- oldFlags = f->texdef.flags;\r
- oldContents = f->texdef.contents;\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- f->texdef = *texdef;\r
- ConvertTexMatWithQTexture( brushprimit_texdef, NULL, &f->brushprimit_texdef, QERApp_Shader_ForName( f->texdef.GetName() )->getTexture() );\r
- }\r
- else\r
- if (bFitScale)\r
- {\r
- f->texdef = *texdef;\r
- // fit the scaling of the texture on the actual plane\r
- vec3_t p1,p2,p3; // absolute coordinates\r
- // compute absolute coordinates\r
- ComputeAbsolute(f,p1,p2,p3);\r
- // compute the scale\r
- vec3_t vx,vy;\r
- VectorSubtract(p2,p1,vx);\r
- VectorNormalize(vx, vx);\r
- VectorSubtract(p3,p1,vy);\r
- VectorNormalize(vy, vy);\r
- // assign scale\r
- VectorScale(vx,texdef->scale[0],vx);\r
- VectorScale(vy,texdef->scale[1],vy);\r
- VectorAdd(p1,vx,p2);\r
- VectorAdd(p1,vy,p3);\r
- // compute back shift scale rot\r
- AbsoluteToLocal(f->plane,f,p1,p2,p3);\r
- }\r
- else\r
- f->texdef = *texdef;\r
- f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);\r
- f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);\r
-\r
- // if this is a curve face, set all other curve faces to the same texdef\r
- if (f->texdef.flags & SURF_CURVE)\r
- {\r
- for (tf = b->brush_faces ; tf ; tf = tf->next)\r
- {\r
- if (tf->texdef.flags & SURF_CURVE)\r
- tf->texdef = f->texdef;\r
- }\r
- }\r
-}\r
-\r
-/*\r
-===============\r
-SetFaceTexdef\r
-\r
-Doesn't set the curve flags\r
- \r
-NOTE : ( TTimo )\r
- never trust f->d_texture here, f->texdef and f->d_texture are out of sync when called by Brush_SetTexture\r
- use Texture_ForName() to find the right shader\r
- FIXME : send the right shader ( qtexture_t * ) in the parameters ?\r
- \r
- TTimo: surface plugin, added an IPluginTexdef* parameter\r
- if not NULL, get ->Copy() of it into the face ( and remember to hook )\r
- if NULL, ask for a default\r
-===============\r
-*/\r
-void SetFaceTexdef (face_t *f, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pPlugTexdef) {\r
- int oldFlags;\r
- int oldContents;\r
- \r
- oldFlags = f->texdef.flags;\r
- oldContents = f->texdef.contents;\r
-\r
- if(strcmp(f->texdef.GetName(), texdef->GetName()) != 0) // set shader here instead of Brush_Build\r
- Face_SetShader(f, texdef->GetName());\r
-\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- f->texdef = *texdef;\r
- ConvertTexMatWithQTexture( brushprimit_texdef, NULL, &f->brushprimit_texdef, QERApp_Shader_ForName( f->texdef.GetName() )->getTexture() );\r
- }\r
- else\r
- { \r
- if (bFitScale)\r
- {\r
- f->texdef = *texdef;\r
- // fit the scaling of the texture on the actual plane\r
- vec3_t p1,p2,p3; // absolute coordinates\r
- // compute absolute coordinates\r
- ComputeAbsolute(f,p1,p2,p3);\r
- // compute the scale\r
- vec3_t vx,vy;\r
- VectorSubtract(p2,p1,vx);\r
- VectorNormalize(vx, vx);\r
- VectorSubtract(p3,p1,vy);\r
- VectorNormalize(vy, vy);\r
- // assign scale\r
- VectorScale(vx,texdef->scale[0],vx);\r
- VectorScale(vy,texdef->scale[1],vy);\r
- VectorAdd(p1,vx,p2);\r
- VectorAdd(p1,vy,p3);\r
- // compute back shift scale rot\r
- AbsoluteToLocal(f->plane,f,p1,p2,p3);\r
- }\r
- else\r
- {\r
- f->texdef = *texdef;\r
- }\r
- }\r
- f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);\r
- f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);\r
-}\r
- \r
-#ifdef _DEBUG\r
-void Brush_SetTexture2 (brush_t *b, IShader *pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pTexdef)\r
-{\r
- for (face_t* f = b->brush_faces ; f ; f = f->next) \r
- SetFaceTexdef2 (b, f, pShader, texdef, brushprimit_texdef, bFitScale, pTexdef);\r
- Brush_Build( b );\r
- if (b->patchBrush)\r
- {\r
- Patch_SetTexture(b->pPatch, texdef, pTexdef );\r
- b->bFiltered = FilterBrush( b );\r
- }\r
-}\r
-#endif\r
-\r
-void Brush_SetTexture (brush_t *b, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pTexdef)\r
-{\r
- for (face_t* f = b->brush_faces ; f ; f = f->next) \r
- SetFaceTexdef (f, texdef, brushprimit_texdef, bFitScale, pTexdef);\r
- Brush_Build( b );\r
- if (b->patchBrush)\r
- {\r
- Patch_SetTexture(b->pPatch, texdef, pTexdef );\r
- b->bFiltered = FilterBrush( b );\r
- }\r
-}\r
-\r
-\r
-qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f)\r
-{\r
- float d1, d2, fr;\r
- int i;\r
- float *v;\r
-\r
- d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;\r
- d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;\r
-\r
- if (d1 >= 0 && d2 >= 0)\r
- return false; // totally outside\r
- if (d1 <= 0 && d2 <= 0)\r
- return true; // totally inside\r
-\r
- fr = d1 / (d1 - d2);\r
-\r
- if (d1 > 0)\r
- v = p1;\r
- else\r
- v = p2;\r
-\r
- for (i=0 ; i<3 ; i++)\r
- v[i] = p1[i] + fr*(p2[i] - p1[i]);\r
-\r
- return true;\r
-}\r
-\r
-\r
-int AddPlanept (float *f)\r
-{\r
- int i;\r
-\r
- for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)\r
- if (g_qeglobals.d_move_points[i] == f)\r
- return 0;\r
- g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f;\r
- return 1;\r
-}\r
-\r
-/*\r
-==============\r
-Brush_SelectFaceForDragging\r
-\r
-Adds the faces planepts to move_points, and\r
-rotates and adds the planepts of adjacent face if shear is set\r
-==============\r
-*/\r
-void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear)\r
-{\r
- int i;\r
- face_t *f2;\r
- winding_t *w;\r
- float d;\r
- brush_t *b2;\r
- int c;\r
-\r
- if (b->owner->eclass->fixedsize)\r
- return;\r
-\r
- c = 0;\r
- for (i=0 ; i<3 ; i++)\r
- c += AddPlanept (f->planepts[i]);\r
- if (c == 0)\r
- return; // already completely added\r
-\r
- // select all points on this plane in all brushes the selection\r
- for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next)\r
- {\r
- if (b2 == b)\r
- continue;\r
- for (f2=b2->brush_faces ; f2 ; f2=f2->next)\r
- {\r
- for (i=0 ; i<3 ; i++)\r
- if (fabs(DotProduct(f2->planepts[i], f->plane.normal)\r
- -f->plane.dist) > ON_EPSILON)\r
- break;\r
- if (i==3)\r
- { // move this face as well\r
- Brush_SelectFaceForDragging (b2, f2, shear);\r
- break;\r
- }\r
- }\r
- }\r
-\r
-\r
- // if shearing, take all the planes adjacent to \r
- // selected faces and rotate their points so the\r
- // edge clipped by a selcted face has two of the points\r
- if (!shear)\r
- return;\r
-\r
- for (f2=b->brush_faces ; f2 ; f2=f2->next)\r
- {\r
- if (f2 == f)\r
- continue;\r
- w = Brush_MakeFaceWinding (b, f2);\r
- if (!w)\r
- continue;\r
-\r
- // any points on f will become new control points\r
- for (i=0 ; i<w->numpoints ; i++)\r
- {\r
- d = DotProduct (w->points[i], f->plane.normal) \r
- - f->plane.dist;\r
- if (d > -ON_EPSILON && d < ON_EPSILON)\r
- break;\r
- }\r
-\r
- //\r
- // if none of the points were on the plane,\r
- // leave it alone\r
- //\r
- if (i != w->numpoints)\r
- {\r
- if (i == 0)\r
- { // see if the first clockwise point was the\r
- // last point on the winding\r
- d = DotProduct (w->points[w->numpoints-1]\r
- , f->plane.normal) - f->plane.dist;\r
- if (d > -ON_EPSILON && d < ON_EPSILON)\r
- i = w->numpoints - 1;\r
- }\r
-\r
- AddPlanept (f2->planepts[0]);\r
-\r
- VectorCopy (w->points[i], f2->planepts[0]);\r
- if (++i == w->numpoints)\r
- i = 0;\r
- \r
- // see if the next point is also on the plane\r
- d = DotProduct (w->points[i]\r
- , f->plane.normal) - f->plane.dist;\r
- if (d > -ON_EPSILON && d < ON_EPSILON)\r
- AddPlanept (f2->planepts[1]);\r
-\r
- VectorCopy (w->points[i], f2->planepts[1]);\r
- if (++i == w->numpoints)\r
- i = 0;\r
-\r
- // the third point is never on the plane\r
-\r
- VectorCopy (w->points[i], f2->planepts[2]);\r
- }\r
-\r
- free(w);\r
- }\r
-}\r
-\r
-/*\r
-==============\r
-Brush_SideSelect\r
-\r
-The mouse click did not hit the brush, so grab one or more side\r
-planes for dragging\r
-==============\r
-*/\r
-void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir\r
- , qboolean shear)\r
-{\r
- face_t *f, *f2;\r
- vec3_t p1, p2;\r
-\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- {\r
- VectorCopy (origin, p1);\r
- VectorMA (origin, 2*g_MaxWorldCoord, dir, p2);\r
-\r
- for (f2=b->brush_faces ; f2 ; f2=f2->next)\r
- {\r
- if (f2 == f)\r
- continue;\r
- ClipLineToFace (p1, p2, f2);\r
- }\r
-\r
- if (f2)\r
- continue;\r
-\r
- if (VectorCompare (p1, origin))\r
- continue;\r
- if (ClipLineToFace (p1, p2, f))\r
- continue;\r
-\r
- Brush_SelectFaceForDragging (b, f, shear);\r
- } \r
-}\r
-\r
-bool g_bBuildWindingsNoTexBuild = false;\r
-\r
-void Brush_SetBuildWindingsNoTexBuild(bool bBuild)\r
-{\r
- g_bBuildWindingsNoTexBuild = bBuild;\r
-}\r
-\r
-// TTimo: don't rebuild pShader and d_texture if it doesn't seem necessary\r
-// saves quite a lot of time, but on the other hand we've gotta make sure we clean the d_texture in some cases\r
-// ie when we want to update a shader\r
-// default will make Radiant rebuild the texture, but it can be turned off by setting the flag g_bBuildWindingsNoTexBuild\r
-void Brush_BuildWindings( brush_t *b, bool bSnap )\r
-{\r
- winding_t *w;\r
- face_t *face;\r
- vec_t v;\r
-\r
- if (bSnap)\r
- Brush_SnapPlanepts( b );\r
-\r
- // clear the mins/maxs bounds\r
- b->mins[0] = b->mins[1] = b->mins[2] = 99999;\r
- b->maxs[0] = b->maxs[1] = b->maxs[2] = -99999;\r
-\r
- Brush_MakeFacePlanes (b);\r
-\r
- face = b->brush_faces;\r
-\r
- float fCurveColor = 1.0;\r
-\r
- for ( ; face ; face=face->next)\r
- {\r
- int i, j;\r
- free(face->face_winding);\r
- w = face->face_winding = Brush_MakeFaceWinding (b, face);\r
-\r
- if (!g_bBuildWindingsNoTexBuild || !face->d_texture)\r
- {\r
-#ifdef _DEBUG\r
- // if there's no d_texture, then we expect pShader to be empty\r
- if (!face->d_texture && face->pShader)\r
- Sys_FPrintf(SYS_ERR, "ERROR: unexpected face->pShader != NULL with face->d_texture == NULL in Brush_BuildWindings\n");\r
-#endif\r
- if ((!face->d_texture && !face->pShader) || !face->pShader)\r
- {\r
- // NOTE TTimo\r
- // patch 84 for bug 253 doesn't dec ref the potential face->pShader\r
- // add a debug check to make sure this is actually not necessary\r
-#ifdef _DEBUG\r
- if (face->pShader)\r
- {\r
- Sys_FPrintf(SYS_ERR, "ERROR: face->pShader != NULL in Brush_BuildWindings\n");\r
- }\r
-#endif\r
- face->pShader = QERApp_Shader_ForName( face->texdef.GetName() );\r
- face->pShader->IncRef();\r
- face->d_texture = face->pShader->getTexture();\r
- }\r
- }\r
-\r
- if (!w)\r
- continue;\r
- \r
- for (i=0 ; i<w->numpoints ; i++)\r
- {\r
- // add to bounding box\r
- for (j=0 ; j<3 ; j++)\r
- {\r
- v = w->points[i][j];\r
- if (v > b->maxs[j])\r
- b->maxs[j] = v;\r
- if (v < b->mins[j])\r
- b->mins[j] = v;\r
- }\r
- }\r
- Face_SetColor (b, face, fCurveColor);\r
-\r
- fCurveColor -= .10f;\r
- if (fCurveColor <= 0)\r
- fCurveColor = 1.0f;\r
-\r
- // computing ST coordinates for the windings\r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- {\r
- if (g_qeglobals.bNeedConvert)\r
- {\r
- // we have parsed old brushes format and need conversion\r
- // convert old brush texture representation to new format\r
- FaceToBrushPrimitFace(face);\r
-#ifdef _DEBUG\r
- // use old texture coordinates code to check against\r
- for (i=0 ; i<w->numpoints ; i++)\r
- EmitTextureCoordinates( w->points[i], face->d_texture, face);\r
-#endif\r
- }\r
- // use new texture representation to compute texture coordinates\r
- // in debug mode we will check against old code and warn if there are differences\r
- EmitBrushPrimitTextureCoordinates(face,w);\r
- }\r
- else\r
- {\r
- if (g_qeglobals.bNeedConvert)\r
- {\r
- BrushPrimitFaceToFace(face);\r
-/*\r
- // we have parsed brush primitives and need conversion back to standard format\r
- // NOTE: converting back is a quick hack, there's some information lost and we can't do anything about it\r
- // FIXME: if we normalize the texture matrix to a standard 2x2 size, we end up with wrong scaling\r
- // I tried various tweaks, no luck .. seems shifting is lost\r
- brushprimit_texdef_t aux;\r
- ConvertTexMatWithQTexture( &face->brushprimit_texdef, face->d_texture, &aux, NULL );\r
- TexMatToFakeTexCoords( aux.coords, face->texdef.shift, &face->texdef.rotate, face->texdef.scale );\r
- face->texdef.scale[0]/=2.0;\r
- face->texdef.scale[1]/=2.0;\r
-*/\r
- }\r
- for (i=0 ; i<w->numpoints ; i++)\r
- EmitTextureCoordinates( w->points[i], face->d_texture, face);\r
- }\r
- }\r
-}\r
-\r
-/*\r
-==================\r
-Brush_RemoveEmptyFaces\r
-\r
-Frees any overconstraining faces\r
-==================\r
-*/\r
-void Brush_RemoveEmptyFaces ( brush_t *b )\r
-{\r
- face_t *f, *next;\r
-\r
- f = b->brush_faces;\r
- b->brush_faces = NULL;\r
-\r
- for ( ; f ; f=next)\r
- {\r
- next = f->next;\r
- if (!f->face_winding)\r
- Face_Free (f);\r
- else\r
- {\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
- }\r
-\r
- }\r
-}\r
-\r
-void Brush_SnapToGrid(brush_t *pb)\r
-{\r
- face_t *f;\r
- vec3_t temp;\r
- vec3_t diff[2];\r
- int mult[3];\r
- int i, j, n;\r
- // TTimo: some brushes are "special" and should not be snapped\r
- // specially fixed-size entity ones\r
- if (pb->owner->eclass->fixedsize)\r
- {\r
- // save current origin\r
- VectorCopy (pb->owner->origin, temp);\r
- // snap the origin\r
- VectorFSnap(pb->owner->origin, g_qeglobals.d_gridsize);\r
- // return if amount is zero\r
- if (VectorCompare (pb->owner->origin, temp))\r
- return;\r
- // transform brush faces same amount\r
- VectorSubtract (pb->owner->origin, temp, temp);\r
- for (f = pb->brush_faces; f; f = f->next)\r
- {\r
- for (i=0 ; i<3 ; i++)\r
- VectorAdd (f->planepts[i], temp, f->planepts[i]);\r
- }\r
- }\r
- else\r
- {\r
- for (f = pb->brush_faces ; f; f = f->next)\r
- {\r
- for (j=0; j<2; j++)\r
- {\r
- // spog - move planepts apart just far enough to avoid snapping two together\r
- VectorSubtract (f->planepts[j+1], f->planepts[j], diff[j]);\r
- for (i=0; i<3; i++)\r
- {\r
- if (diff[j][i] == 0.0f)\r
- mult[i] = 2; // next value up from 1\r
- else // multiplier = gridsize / component difference, rounded up\r
- mult[i] = (int)ceil(fabs(g_qeglobals.d_gridsize / diff[j][i]));\r
- }\r
- \r
- if (mult[0] > 1 && mult[1] > 1 && mult[2] > 1) // if all multipliers are greater than 1\r
- {\r
- n = (mult[0] >= mult[1] && mult[0] >= mult[2]) ? 0 : (mult[1] >= mult[0] && mult[1] >= mult[2]) ? 1 : 2;\r
- for (i=0; i<3; i++)\r
- diff[j][i] *= mult[n]; // multiply difference by multiplier of smallest component\r
- }\r
- VectorAdd (f->planepts[j], diff[j], f->planepts[j+1]);\r
- }\r
-\r
- for (i=0; i<3; i++)\r
- VectorFSnap(f->planepts[i], g_qeglobals.d_gridsize);\r
-\r
- }\r
- }\r
- Brush_Build(pb,true,true,false,false); // don't filter\r
-}\r
-\r
-void Brush_Rotate(brush_t *b, vec3_t vAngle, vec3_t vOrigin, bool bBuild)\r
-{\r
- for (face_t* f=b->brush_faces ; f ; f=f->next)\r
- {\r
- for (int i=0 ; i<3 ; i++)\r
- {\r
- VectorRotateOrigin (f->planepts[i], vAngle, vOrigin, f->planepts[i]);\r
- }\r
- }\r
- if (bBuild)\r
- {\r
- Brush_Build(b,false,false,false,false); // don't filter\r
- }\r
-}\r
-\r
-void Brush_Center(brush_t *b, vec3_t vNewCenter)\r
-{\r
- vec3_t vMid;\r
- // get center of the brush\r
- for (int j = 0; j < 3; j++)\r
- {\r
- vMid[j] = b->mins[j] + fabs((b->maxs[j] - b->mins[j]) * 0.5);\r
- }\r
- // calc distance between centers\r
- VectorSubtract(vNewCenter, vMid, vMid);\r
- Brush_Move(b, vMid, true);\r
-\r
-}\r
-\r
-void Brush_Resize(brush_t *b, vec3_t vMin, vec3_t vMax)\r
-{\r
- face_t *f;\r
- texdef_t texdef;\r
- int i;\r
- short box[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } };\r
-\r
- for (i=0 ; i<3 ; i++)\r
- if (vMax[i] < vMin[i])\r
- Error ("Brush_Resize: invalid input");\r
-\r
- if(b->brush_faces != NULL)\r
- texdef = b->brush_faces->texdef;\r
- else\r
- texdef = g_qeglobals.d_texturewin.texdef;\r
-\r
- while (b->brush_faces != NULL)\r
- {\r
- f = b->brush_faces->next;\r
- Face_Free(b->brush_faces);\r
- b->brush_faces = f;\r
- }\r
-\r
- for(i=0; i<3; i++)\r
- {\r
- f = b->brush_faces;\r
- b->brush_faces = Face_Alloc();\r
- b->brush_faces->next = f;\r
- f = b->brush_faces;\r
- f->texdef = texdef;\r
- VectorCopy(vMax, f->planepts[0]);\r
- VectorCopy(vMax, f->planepts[1]);\r
- VectorCopy(vMax, f->planepts[2]);\r
- f->planepts[2][box[i][0]] = vMin[box[i][0]];\r
- f->planepts[1][box[i][1]] = vMin[box[i][1]];\r
- }\r
- for(i=0; i<3; i++)\r
- {\r
- f = b->brush_faces;\r
- b->brush_faces = Face_Alloc();\r
- b->brush_faces->next = f;\r
- f = b->brush_faces;\r
- f->texdef = texdef;\r
- VectorCopy(vMin, f->planepts[0]);\r
- VectorCopy(vMin, f->planepts[1]);\r
- VectorCopy(vMin, f->planepts[2]);\r
- f->planepts[1][box[i][0]] = vMax[box[i][0]];\r
- f->planepts[2][box[i][1]] = vMax[box[i][1]];\r
- }\r
-}\r
-\r
-void FacingVectors (entity_t *e, vec3_t forward, vec3_t right, vec3_t up)\r
-{\r
- int angleVal;\r
- vec3_t angles;\r
-\r
- angleVal = IntForKey(e, "angle");\r
- if (angleVal == -1) // up\r
- {\r
- VectorSet(angles, 270, 0, 0);\r
- }\r
- else if(angleVal == -2) // down\r
- {\r
- VectorSet(angles, 90, 0, 0);\r
- }\r
- else\r
- {\r
- VectorSet(angles, 0, angleVal, 0);\r
- }\r
-\r
- AngleVectors(angles, forward, right, up);\r
-}\r
-\r
-void Brush_DrawFacingAngle (brush_t *b, entity_t *e)\r
-{\r
- vec3_t forward, right, up;\r
- vec3_t endpoint, tip1, tip2;\r
- vec3_t start;\r
- float dist;\r
-\r
- VectorAdd(e->brushes.onext->mins, e->brushes.onext->maxs, start);\r
- VectorScale(start, 0.5, start);\r
- dist = (b->maxs[0] - start[0]) * 2.5;\r
-\r
- FacingVectors (e, forward, right, up);\r
- VectorMA (start, dist, forward, endpoint);\r
-\r
- dist = (b->maxs[0] - start[0]) * 0.5;\r
- VectorMA (endpoint, -dist, forward, tip1);\r
- VectorMA (tip1, -dist, up, tip1);\r
- VectorMA (tip1, 2*dist, up, tip2);\r
-\r
- qglColor4f (1, 1, 1, 1);\r
- qglLineWidth (4);\r
- qglBegin (GL_LINES);\r
- qglVertex3fv (start);\r
- qglVertex3fv (endpoint);\r
- qglVertex3fv (endpoint);\r
- qglVertex3fv (tip1);\r
- qglVertex3fv (endpoint);\r
- qglVertex3fv (tip2);\r
- qglEnd ();\r
- qglLineWidth (1);\r
-}\r
-\r
-void Brush_FaceDraw(face_t *face, int nGLState)\r
-{\r
- const winding_t *w = face->face_winding;\r
- if (w == NULL) return;\r
- if (nGLState & DRAW_GL_LIGHTING && g_PrefsDlg.m_bGLLighting)\r
- qglNormal3fv(face->plane.normal);\r
- /*\r
- if (mode & DRAW_GL_TEXTURE_2D)\r
- qglTexCoordPointer(2, GL_FLOAT, 5, &w->points[3]);\r
- qglVertexPointer(3, GL_FLOAT, 5, w->points);\r
-\r
- if (mode & DRAW_GL_FILL)\r
- qglDrawArrays(GL_TRIANGLE_FAN, 0, w->numpoints);\r
- else\r
- qglDrawArrays(GL_POLYGON, 0, w->numpoints);\r
- */\r
-\r
- if (nGLState & DRAW_GL_FILL)\r
- qglBegin(GL_TRIANGLE_FAN);\r
- else\r
- qglBegin(GL_POLYGON);\r
- \r
- for (int i=0 ; i<w->numpoints ; i++)\r
- {\r
- if (nGLState & DRAW_GL_TEXTURE_2D)\r
- qglTexCoord2fv( &w->points[i][3] );\r
- qglVertex3fv(w->points[i]);\r
- }\r
- qglEnd();\r
-}\r
-\r
-void Brush_Draw(brush_t *b)\r
-{\r
- face_t *face;\r
- int order;\r
- qtexture_t *prev = 0;\r
- winding_t *w;\r
-\r
- int nDrawMode = g_pParentWnd->GetCamWnd()->Camera()->draw_mode;\r
- int nGLState = g_pParentWnd->GetCamWnd()->Camera()->draw_glstate;\r
-\r
- GLfloat material[4], identity[4];\r
- VectorSet(identity, 0.8f, 0.8f, 0.8f);\r
- IShader *pShader;\r
- qglPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);\r
- qglDisableClientState(GL_NORMAL_ARRAY);\r
-\r
- // guarantee the texture will be set first\r
- bool bTrans;\r
- prev = NULL;\r
- for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)\r
- {\r
- w = face->face_winding;\r
- if (!w)\r
- {\r
- continue; // freed face\r
- }\r
-\r
- bTrans = (face->pShader->getFlags() & QER_TRANS);\r
-\r
- if (bTrans && !(nGLState & DRAW_GL_BLEND))\r
- continue;\r
- if (!bTrans && nGLState & DRAW_GL_BLEND)\r
- continue;\r
- \r
- // IMPORTANT NOTE:\r
- // modifications to the discarding code here should be matched in the selection code\r
- // see Brush_Ray\r
-\r
- if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK)\r
- {\r
- if (strstr(face->texdef.GetName(), "caulk"))\r
- continue;\r
- }\r
-\r
- if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_BOTCLIP)\r
- {\r
- if (strstr(face->texdef.GetName(), "botclip") || strstr(face->texdef.GetName(), "clipmonster"))\r
- continue;\r
- }\r
-\r
- if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)\r
- {\r
- if (strstr(face->texdef.GetName(), "clip"))\r
- continue;\r
- }\r
-\r
- if (nGLState & DRAW_GL_TEXTURE_2D && face->d_texture->name[0] == '(')\r
- {\r
- prev = NULL;\r
- qglDisable(GL_TEXTURE_2D);\r
- }\r
- else if (nGLState & DRAW_GL_TEXTURE_2D && (nDrawMode == cd_texture || nDrawMode == cd_light) && face->d_texture != prev)\r
- {\r
- // set the texture for this face\r
- prev = face->d_texture;\r
- qglBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );\r
- }\r
- \r
- if (nGLState & DRAW_GL_LIGHTING && !g_PrefsDlg.m_bGLLighting)\r
- {\r
- if (!b->owner->eclass->fixedsize)\r
- material[3] = face->pShader->getTrans();\r
- else\r
- material[3] = 1;\r
- VectorCopy(face->d_color, material);\r
-\r
- if (nGLState & DRAW_GL_TEXTURE_2D)\r
- qglColor4f(face->d_shade, face->d_shade, face->d_shade, material[3]);\r
- else\r
- qglColor4fv(material);\r
- }\r
- else if (!b->owner->eclass->fixedsize)\r
- {\r
- pShader = face->pShader;\r
- VectorCopy(pShader->getTexture()->color, material);\r
- material[3] = identity[3] = pShader->getTrans();\r
-\r
- if (nGLState & DRAW_GL_TEXTURE_2D)\r
- qglColor4fv(identity);\r
- else\r
- qglColor4fv(material);\r
- }\r
- \r
- // draw the polygon\r
-\r
- Brush_FaceDraw(face, nGLState);\r
- }\r
- qglPopClientAttrib();\r
-}\r
-\r
-void Face_Draw( face_t *f )\r
-{\r
- int i;\r
-\r
- if ( f->face_winding == 0 )\r
- return;\r
- qglBegin(GL_POLYGON);\r
- for ( i = 0 ; i < f->face_winding->numpoints; i++)\r
- qglVertex3fv( f->face_winding->points[i] );\r
- qglEnd();\r
-}\r
-\r
-entity_t *FindEntity(const char *pszKey, const char *pszValue)\r
-{\r
- entity_t *pe;\r
- \r
- pe = entities.next;\r
- \r
- for (; pe != NULL && pe != &entities ; pe = pe->next)\r
- {\r
- if (!strcmp(ValueForKey(pe, pszKey), pszValue))\r
- return pe;\r
- }\r
-\r
- return NULL;\r
-}\r
-\r
-void Brush_DrawXY(brush_t *b, int nViewType)\r
-{\r
- face_t *face;\r
- int order;\r
- winding_t *w;\r
- int i;\r
-\r
- if (b->patchBrush)\r
- {\r
- Patch_DrawXY(b->pPatch);\r
- if (!g_bPatchShowBounds)\r
- return;\r
- } \r
-\r
- if (b->owner->eclass->fixedsize)\r
- {\r
- if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT))\r
- {\r
-#if 1 // requires vertex arrays enabled\r
- DrawLight(b->owner, DRAW_GL_WIRE, (IsBrushSelected(b)) ? g_PrefsDlg.m_nLightRadiuses : 0, nViewType);\r
-#else\r
- vec3_t vCorners[4];\r
- float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;\r
-\r
- vCorners[0][0] = b->mins[0];\r
- vCorners[0][1] = b->mins[1];\r
- vCorners[0][2] = fMid;\r
-\r
- vCorners[1][0] = b->mins[0];\r
- vCorners[1][1] = b->maxs[1];\r
- vCorners[1][2] = fMid;\r
-\r
- vCorners[2][0] = b->maxs[0];\r
- vCorners[2][1] = b->maxs[1];\r
- vCorners[2][2] = fMid;\r
-\r
- vCorners[3][0] = b->maxs[0];\r
- vCorners[3][1] = b->mins[1];\r
- vCorners[3][2] = fMid;\r
-\r
- vec3_t vTop, vBottom;\r
-\r
- vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2);\r
- vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2);\r
- vTop[2] = b->maxs[2];\r
-\r
- VectorCopy(vTop, vBottom);\r
- vBottom[2] = b->mins[2];\r
-\r
- qglBegin(GL_LINES);\r
- qglVertex3fv(vTop);\r
- qglVertex3fv(vCorners[0]);\r
- qglVertex3fv(vTop);\r
- qglVertex3fv(vCorners[1]);\r
- qglVertex3fv(vTop);\r
- qglVertex3fv(vCorners[2]);\r
- qglVertex3fv(vTop);\r
- qglVertex3fv(vCorners[3]);\r
- qglEnd();\r
-\r
- qglBegin(GL_LINES);\r
- qglVertex3fv(vBottom);\r
- qglVertex3fv(vCorners[0]);\r
- qglVertex3fv(vBottom);\r
- qglVertex3fv(vCorners[1]);\r
- qglVertex3fv(vBottom);\r
- qglVertex3fv(vCorners[2]);\r
- qglVertex3fv(vBottom);\r
- qglVertex3fv(vCorners[3]);\r
- qglEnd();\r
-\r
- qglBegin(GL_LINE_LOOP);\r
- qglVertex3fv(vCorners[0]);\r
- qglVertex3fv(vCorners[1]);\r
- qglVertex3fv(vCorners[2]);\r
- qglVertex3fv(vCorners[3]);\r
- qglEnd();\r
-#endif\r
- DrawBrushEntityName (b);\r
- return;\r
- }\r
- else if (b->owner->model.pRender && !(!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY)))\r
- {\r
- qglPushAttrib(GL_CURRENT_BIT); // save brush colour\r
- qglColor3fv(b->owner->eclass->color);\r
- if( g_PrefsDlg.m_nEntityShowState != ENTITY_BOX )\r
- b->owner->model.pRender->Draw(DRAW_GL_WIRE, DRAW_RF_XY);\r
- aabb_draw(b->owner->model.pRender->GetAABB(), DRAW_GL_WIRE);\r
- qglPopAttrib();\r
- return;\r
- }\r
- //}\r
- }\r
-\r
- for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)\r
- {\r
- // moved so check occurs earlier\r
- w = face->face_winding;\r
- if (!w)\r
- continue;\r
- // only draw polygons facing in a direction we care about\r
- if (nViewType == XY)\r
- {\r
- if (face->plane.normal[2] <= 0)\r
- continue;\r
- }\r
- else\r
- {\r
- if (nViewType == XZ)\r
- {\r
- if (face->plane.normal[1] >= 0) // stop axes being mirrored\r
- continue;\r
- }\r
- else \r
- {\r
- if (face->plane.normal[0] <= 0)\r
- continue;\r
- }\r
- }\r
-\r
- // draw the polygon\r
- qglBegin(GL_LINE_LOOP);\r
- for (i=0 ; i<w->numpoints ; i++)\r
- qglVertex3fv(w->points[i]);\r
- qglEnd();\r
- }\r
-\r
- DrawBrushEntityName (b);\r
-\r
-}\r
-\r
-/*\r
-============\r
-Brush_Move\r
-============\r
-*/\r
-void Brush_Move (brush_t *b, const vec3_t move, bool bSnap)\r
-{\r
- int i;\r
- face_t *f;\r
-\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- for (i=0 ; i<3 ; i++)\r
- VectorAdd (f->planepts[i], move, f->planepts[i]);\r
-\r
- if (g_PrefsDlg.m_bTextureLock && !b->owner->eclass->fixedsize)\r
- {\r
- for (f=b->brush_faces ; f ; f=f->next)\r
- {\r
- vec3_t vTemp;\r
- VectorCopy(move, vTemp);\r
- Face_MoveTexture(f, vTemp);\r
- }\r
- }\r
-\r
- Brush_Build( b, bSnap,true,false,false); // don't filter\r
-\r
-\r
- if (b->patchBrush)\r
- {\r
- //Patch_Move(b->nPatchID, move);\r
- Patch_Move(b->pPatch, move);\r
- }\r
-\r
-\r
- // PGM - keep the origin vector up to date on fixed size entities.\r
- if(b->owner->eclass->fixedsize)\r
- {\r
- char text[64];\r
- VectorAdd(b->owner->origin, move, b->owner->origin);\r
- sprintf (text, "%i %i %i",\r
- (int)b->owner->origin[0], (int)b->owner->origin[1], (int)b->owner->origin[2]);\r
- SetKeyValue(b->owner, "origin", text);\r
- //VectorAdd(b->maxs, b->mins, b->owner->origin);\r
- //VectorScale(b->owner->origin, 0.5, b->owner->origin);\r
- }\r
-}\r
-\r
-\r
-\r
-void Brush_Print(brush_t* b)\r
-{\r
- int nFace = 0;\r
- for (face_t* f = b->brush_faces ; f ; f=f->next)\r
- {\r
- Sys_Printf("Face %i\n", nFace++);\r
- Sys_Printf("%f %f %f\n", f->planepts[0][0], f->planepts[0][1], f->planepts[0][2]);\r
- Sys_Printf("%f %f %f\n", f->planepts[1][0], f->planepts[1][1], f->planepts[1][2]);\r
- Sys_Printf("%f %f %f\n", f->planepts[2][0], f->planepts[2][1], f->planepts[2][2]);\r
- }\r
- }\r
-\r
-\r
-\r
-/*\r
-=============\r
-Brush_MakeSided\r
-\r
-Makes the current brushhave the given number of 2d sides and turns it into a cone\r
-=============\r
-*/\r
-void Brush_MakeSidedCone(int sides)\r
-{\r
- int i;\r
- vec3_t mins, maxs;\r
- brush_t *b;\r
- texdef_t *texdef;\r
- face_t *f;\r
- vec3_t mid;\r
- float width;\r
- float sv, cv;\r
-\r
- if (sides < 3 || sides > 32)\r
- {\r
- Sys_Status ("Bad sides number", 0);\r
- return;\r
- }\r
-\r
- if (!QE_SingleBrush ())\r
- {\r
- Sys_Status ("Must have a single brush selected", 0 );\r
- return;\r
- }\r
-\r
- b = selected_brushes.next;\r
- VectorCopy (b->mins, mins);\r
- VectorCopy (b->maxs, maxs);\r
- texdef = &g_qeglobals.d_texturewin.texdef;\r
-\r
- Brush_Free (b);\r
-\r
- // find center of brush\r
- width = 8;\r
- for (i=0 ; i<2 ; i++)\r
- {\r
- mid[i] = (maxs[i] + mins[i])*0.5;\r
- if (maxs[i] - mins[i] > width)\r
- width = maxs[i] - mins[i];\r
- }\r
- width /= 2;\r
-\r
- b = Brush_Alloc();\r
-\r
- // create bottom face\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2];\r
- f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2];\r
- f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2];\r
-\r
- for (i=0 ; i<sides ; i++)\r
- {\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- sv = sin (i*3.14159265*2/sides);\r
- cv = cos (i*3.14159265*2/sides);\r
-\r
-\r
- f->planepts[0][0] = floor(mid[0]+width*cv+0.5);\r
- f->planepts[0][1] = floor(mid[1]+width*sv+0.5);\r
- f->planepts[0][2] = mins[2];\r
-\r
- f->planepts[1][0] = mid[0];\r
- f->planepts[1][1] = mid[1];\r
- f->planepts[1][2] = maxs[2];\r
-\r
- f->planepts[2][0] = floor(f->planepts[0][0] - width * sv + 0.5);\r
- f->planepts[2][1] = floor(f->planepts[0][1] + width * cv + 0.5);\r
- f->planepts[2][2] = maxs[2];\r
-\r
- }\r
-\r
- Brush_AddToList (b, &selected_brushes);\r
-\r
- Entity_LinkBrush (world_entity, b);\r
-\r
- Brush_Build( b );\r
-\r
- Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-/*\r
-=============\r
-Brush_MakeSided\r
-\r
-Makes the current brushhave the given number of 2d sides and turns it into a sphere\r
-=============\r
-\r
-*/\r
-void Brush_MakeSidedSphere(int sides)\r
-{\r
- int i,j;\r
- vec3_t mins, maxs;\r
- brush_t *b;\r
- texdef_t *texdef;\r
- face_t *f;\r
- vec3_t mid;\r
-\r
- if (sides < 4 || sides > 32)\r
- {\r
- Sys_Status ("Bad sides number", 0);\r
- return;\r
- }\r
-\r
- if (!QE_SingleBrush ())\r
- {\r
- Sys_Status ("Must have a single brush selected", 0 );\r
- return;\r
- }\r
-\r
- b = selected_brushes.next;\r
- VectorCopy (b->mins, mins);\r
- VectorCopy (b->maxs, maxs);\r
- texdef = &g_qeglobals.d_texturewin.texdef;\r
-\r
- Brush_Free (b);\r
-\r
- // find center of brush\r
- float radius = 8;\r
- for (i=0 ; i<2 ; i++)\r
- {\r
- mid[i] = (maxs[i] + mins[i])*0.5;\r
- if (maxs[i] - mins[i] > radius)\r
- radius = maxs[i] - mins[i];\r
- }\r
- radius /= 2;\r
-\r
- b = Brush_Alloc();\r
-\r
- float dt = float(2 * Q_PI / sides);\r
- float dp = float(Q_PI / sides);\r
- float t,p;\r
- for(i=0; i <= sides-1; i++)\r
- {\r
- for(j=0;j <= sides-2; j++)\r
- {\r
- t = i * dt;\r
- p = float(j * dp - Q_PI / 2);\r
-\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- VectorPolar(f->planepts[0], radius, t, p);\r
- VectorPolar(f->planepts[1], radius, t, p + dp);\r
- VectorPolar(f->planepts[2], radius, t + dt, p + dp);\r
-\r
- for (int k = 0; k < 3; k++)\r
- VectorAdd(f->planepts[k], mid, f->planepts[k]);\r
- }\r
- }\r
-\r
- p = float((sides - 1) * dp - Q_PI / 2);\r
- for(i = 0; i <= sides-1; i++)\r
- {\r
- t = i * dt;\r
-\r
- f = Face_Alloc();\r
- f->texdef = *texdef;\r
- f->next = b->brush_faces;\r
- b->brush_faces = f;\r
-\r
- VectorPolar(f->planepts[0], radius, t, p);\r
- VectorPolar(f->planepts[1], radius, t + dt, p + dp);\r
- VectorPolar(f->planepts[2], radius, t + dt, p);\r
-\r
- for (int k = 0; k < 3; k++)\r
- VectorAdd(f->planepts[k], mid, f->planepts[k]);\r
- }\r
-\r
- Brush_AddToList (b, &selected_brushes);\r
-\r
- Entity_LinkBrush (world_entity, b);\r
-\r
- Brush_Build( b );\r
-\r
- Sys_UpdateWindows (W_ALL);\r
-}\r
-\r
-void Face_FitTexture( face_t * face, int nHeight, int nWidth )\r
-{\r
- winding_t *w;\r
- vec3_t mins,maxs;\r
- int i;\r
- float width, height, temp;\r
- float rot_width, rot_height;\r
- float cosv,sinv,ang;\r
- float min_t, min_s, max_t, max_s;\r
- float s,t;\r
- vec3_t vecs[2];\r
- vec3_t coords[4];\r
- texdef_t *td;\r
- \r
- if (nHeight < 1)\r
- nHeight = 1;\r
- if (nWidth < 1)\r
- nWidth = 1;\r
- \r
- ClearBounds (mins, maxs);\r
- \r
- w = face->face_winding;\r
- if (!w)\r
- {\r
- return;\r
- }\r
- for (i=0 ; i<w->numpoints ; i++)\r
- {\r
- AddPointToBounds( w->points[i], mins, maxs );\r
- }\r
- \r
- if (g_qeglobals.m_bBrushPrimitMode)\r
- Face_FitTexture_BrushPrimit( face, mins, maxs, nHeight, nWidth );\r
- else\r
- {\r
- \r
- td = &face->texdef;\r
- //\r
- // get the current angle\r
- //\r
- ang = td->rotate / 180 * Q_PI;\r
- sinv = sin(ang);\r
- cosv = cos(ang);\r
- \r
- // get natural texture axis\r
- TextureAxisFromPlane(&face->plane, vecs[0], vecs[1]);\r
- \r
- min_s = DotProduct( mins, vecs[0] );\r
- min_t = DotProduct( mins, vecs[1] );\r
- max_s = DotProduct( maxs, vecs[0] );\r
- max_t = DotProduct( maxs, vecs[1] );\r
- width = max_s - min_s;\r
- height = max_t - min_t;\r
- coords[0][0] = min_s;\r
- coords[0][1] = min_t;\r
- coords[1][0] = max_s;\r
- coords[1][1] = min_t;\r
- coords[2][0] = min_s;\r
- coords[2][1] = max_t;\r
- coords[3][0] = max_s;\r
- coords[3][1] = max_t;\r
- min_s = min_t = 99999;\r
- max_s = max_t = -99999;\r
- for (i=0; i<4; i++)\r
- {\r
- s = cosv * coords[i][0] - sinv * coords[i][1];\r
- t = sinv * coords[i][0] + cosv * coords[i][1];\r
- if (i&1)\r
- {\r
- if (s > max_s)\r
- {\r
- max_s = s;\r
- }\r
- }\r
- else\r
- {\r
- if (s < min_s)\r
- {\r
- min_s = s;\r
- }\r
- if (i<2)\r
- {\r
- if (t < min_t)\r
- {\r
- min_t = t;\r
- }\r
- }\r
- else\r
- {\r
- if (t > max_t)\r
- {\r
- max_t = t;\r
- }\r
- }\r
- }\r
- }\r
- rot_width = (max_s - min_s);\r
- rot_height = (max_t - min_t);\r
- td->scale[0] = -(rot_width/((float)(face->d_texture->width*nWidth)));\r
- td->scale[1] = -(rot_height/((float)(face->d_texture->height*nHeight)));\r
- \r
- td->shift[0] = min_s/td->scale[0];\r
- temp = (int)(td->shift[0] / (face->d_texture->width*nWidth));\r
- temp = (temp+1)*face->d_texture->width*nWidth;\r
- td->shift[0] = (int)(temp - td->shift[0])%(face->d_texture->width*nWidth);\r
- \r
- td->shift[1] = min_t/td->scale[1];\r
- temp = (int)(td->shift[1] / (face->d_texture->height*nHeight));\r
- temp = (temp+1)*(face->d_texture->height*nHeight);\r
- td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight);\r
- \r
- td->shift[1] = min_t/td->scale[1];\r
- temp = (int)(td->shift[1] / (face->d_texture->height*nHeight));\r
- temp = (temp+1)*(face->d_texture->height*nHeight);\r
- td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight);\r
- \r
- }\r
-}\r
-\r
-void Brush_FitTexture( brush_t *b, int nHeight, int nWidth )\r
-{\r
- face_t *face;\r
-\r
- for (face = b->brush_faces ; face ; face=face->next)\r
- {\r
- Face_FitTexture( face, nHeight, nWidth );\r
- }\r
-}\r
-\r
-void aabb_draw(const aabb_t *aabb, int mode)\r
-{\r
- vec3_t normals[6] = { { 1, 0, 0}, { 0, 1, 0 }, { 0, 0, 1 }, {-1, 0, 0}, { 0,-1, 0 }, { 0, 0,-1 } };\r
- vec3_t points[8];\r
- vec3_t vMin, vMax;\r
- VectorSubtract(aabb->origin, aabb->extents, vMin);\r
- VectorAdd(aabb->origin, aabb->extents, vMax);\r
- VectorSet(points[0], vMin[0], vMax[1], vMax[2]);\r
- VectorSet(points[1], vMax[0], vMax[1], vMax[2]);\r
- VectorSet(points[2], vMax[0], vMin[1], vMax[2]);\r
- VectorSet(points[3], vMin[0], vMin[1], vMax[2]);\r
- VectorSet(points[4], vMin[0], vMax[1], vMin[2]);\r
- VectorSet(points[5], vMax[0], vMax[1], vMin[2]);\r
- VectorSet(points[6], vMax[0], vMin[1], vMin[2]);\r
- VectorSet(points[7], vMin[0], vMin[1], vMin[2]);\r
-\r
- qglBegin(GL_QUADS);\r
-\r
- qglNormal3fv(normals[0]);\r
- qglVertex3fv(points[2]);\r
- qglVertex3fv(points[1]);\r
- qglVertex3fv(points[5]);\r
- qglVertex3fv(points[6]);\r
-\r
- qglNormal3fv(normals[1]);\r
- qglVertex3fv(points[1]);\r
- qglVertex3fv(points[0]);\r
- qglVertex3fv(points[4]);\r
- qglVertex3fv(points[5]);\r
-\r
- qglNormal3fv(normals[2]);\r
- qglVertex3fv(points[0]);\r
- qglVertex3fv(points[1]);\r
- qglVertex3fv(points[2]);\r
- qglVertex3fv(points[3]);\r
-\r
- qglNormal3fv(normals[3]);\r
- qglVertex3fv(points[3]);\r
- qglVertex3fv(points[7]);\r
- qglVertex3fv(points[4]);\r
- qglVertex3fv(points[0]);\r
-\r
- qglNormal3fv(normals[4]);\r
- qglVertex3fv(points[3]);\r
- qglVertex3fv(points[2]);\r
- qglVertex3fv(points[6]);\r
- qglVertex3fv(points[7]);\r
-\r
- qglNormal3fv(normals[5]);\r
- qglVertex3fv(points[7]);\r
- qglVertex3fv(points[6]);\r
- qglVertex3fv(points[5]);\r
- qglVertex3fv(points[4]);\r
-\r
- qglEnd();\r
-\r
-/*\r
-\r
-\r
- vec3_t Coords[8];\r
-\r
- vec3_t vMin, vMax;\r
- VectorSubtract(aabb->origin, aabb->extents, vMin);\r
- VectorAdd(aabb->origin, aabb->extents, vMax);\r
- VectorSet(Coords[0], vMin[0], vMax[1], vMax[2]);\r
- VectorSet(Coords[1], vMax[0], vMax[1], vMax[2]);\r
- VectorSet(Coords[2], vMax[0], vMin[1], vMax[2]);\r
- VectorSet(Coords[3], vMin[0], vMin[1], vMax[2]);\r
- VectorSet(Coords[4], vMin[0], vMax[1], vMin[2]);\r
- VectorSet(Coords[5], vMax[0], vMax[1], vMin[2]);\r
- VectorSet(Coords[6], vMax[0], vMin[1], vMin[2]);\r
- VectorSet(Coords[7], vMin[0], vMin[1], vMin[2]);\r
-\r
- vec3_t Normals[8] = { {-1, 0, 0 },\r
- { 0, 0, 0 },\r
- { 0, 0, 0 },\r
- { 0, 0, 1 },\r
- { 0, 0,-1 },\r
- { 0, 1, 0 },\r
- { 1, 0, 0 },\r
- { 0,-1, 0 } };\r
- \r
- unsigned short Indices[24] = { 2, 1, 5, 6,\r
- 1, 0, 4, 5,\r
- 0, 1, 2, 3,\r
- 3, 7, 4, 0,\r
- 3, 2, 6, 7,\r
- 7, 6, 5, 4 };\r
-\r
- qglVertexPointer(3, GL_FLOAT, 0, Coords); // filling the arrays\r
- qglNormalPointer(GL_FLOAT, 0, Normals);\r
- \r
- //glLockArraysEXT(0, count); // extension GL_EXT_compiled_vertex_array\r
- \r
- qglDrawElements(GL_QUADS, 24, GL_UNSIGNED_SHORT, Indices);\r
- \r
- //glUnlockArraysEXT; // extension GL_EXT_compiled_vertex_array\r
-*/\r
-}\r
-\r
-qboolean IsBrushSelected(brush_t* bSel)\r
-{\r
- for (brush_t* b = selected_brushes.next ;b != NULL && b != &selected_brushes; b = b->next)\r
- {\r
- if (b == bSel)\r
- return true;\r
- }\r
- return false;\r
-}\r
-\r
-\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "stdafx.h"
+#include <assert.h>
+#include "winding.h"
+#include <limits.h>
+#include "filters.h"
+
+extern MainFrame* g_pParentWnd;
+extern void MemFile_fprintf(MemStream* pMemFile, const char* pText, ...);
+
+// globals
+
+int g_nBrushId = 0;
+
+#ifdef ENABLE_GROUPS
+const char* Brush_Name(brush_t *b)
+{
+ static char cBuff[1024];
+ b->numberId = g_nBrushId++;
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ sprintf(cBuff, "Brush %i", b->numberId);
+ Brush_SetEpair(b, "Name", cBuff);
+ }
+ return cBuff;
+}
+#endif
+
+brush_t *Brush_Alloc()
+{
+ brush_t *b = (brush_t*)qmalloc(sizeof(brush_t));
+ return b;
+}
+/*
+void Brush_Free(brush_t *b)
+{
+ free(b);
+}
+*/
+void PrintWinding (winding_t *w)
+{
+ int i;
+
+ Sys_Printf ("-------------\n");
+ for (i=0 ; i<w->numpoints ; i++)
+ Sys_Printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0]
+ , w->points[i][1], w->points[i][2]);
+}
+
+void PrintPlane (plane_t *p)
+{
+ Sys_Printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n", p->normal[0], p->normal[1],
+ p->normal[2], p->dist);
+}
+
+void PrintVector (vec3_t v)
+{
+ Sys_Printf ("(%5.2f, %5.2f, %5.2f)\n", v[0], v[1], v[2]);
+}
+
+
+/*
+=============================================================================
+
+ TEXTURE COORDINATES
+
+=============================================================================
+*/
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0}, // floor
+{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
+{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+ int bestaxis;
+ float dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (g_PrefsDlg.m_bQ3Map2Texturing && dot > best + 0.0001f || dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+float lightaxis[3] = {0.6f, 0.8f, 1.0f};
+/*
+================
+SetShadeForPlane
+
+Light different planes differently to
+improve recognition
+================
+*/
+extern float ShadeForNormal(vec3_t normal);
+
+float SetShadeForPlane (plane_t *p)
+{
+ //return ShadeForNormal(p->normal);
+
+
+ int i;
+ float f;
+
+ // axial plane
+ for (i=0 ; i<3 ; i++)
+ if (fabs(p->normal[i]) > 0.9)
+ {
+ f = lightaxis[i];
+ return f;
+ }
+
+ // between two axial planes
+ for (i=0 ; i<3 ; i++)
+ if (fabs(p->normal[i]) < 0.1)
+ {
+ f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2;
+ return f;
+ }
+
+ // other
+ f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3;
+ return f;
+
+}
+
+vec3_t vecs[2];
+float shift[2];
+
+/*
+================
+Face_Alloc
+================
+*/
+face_t *Face_Alloc( void )
+{
+ face_t *f = (face_t*)qmalloc( sizeof( *f ) );
+ return f;
+}
+
+/*
+================
+Face_Free
+================
+*/
+void Face_Free( face_t *f )
+{
+ assert( f != 0 );
+
+ if ( f->face_winding )
+ {
+ free( f->face_winding );
+ f->face_winding = 0;
+ }
+ f->texdef.~texdef_t();;
+
+ free( f );
+}
+
+/*
+================
+Face_Clone
+================
+*/
+face_t *Face_Clone (face_t *f)
+{
+ face_t *n;
+
+ n = Face_Alloc();
+ n->texdef = f->texdef;
+ n->brushprimit_texdef = f->brushprimit_texdef;
+
+ memcpy (n->planepts, f->planepts, sizeof(n->planepts));
+
+ // all other fields are derived, and will be set by Brush_Build
+ // FIXME: maybe not, for example n->pData!
+ return n;
+}
+
+/*
+================
+Face_FullClone
+
+makes an exact copy of the face
+================
+*/
+face_t *Face_FullClone (face_t *f)
+{
+ face_t *n;
+
+ n = Face_Alloc();
+ n->texdef = f->texdef;
+ n->brushprimit_texdef = f->brushprimit_texdef;
+ memcpy(n->planepts, f->planepts, sizeof(n->planepts));
+ memcpy(&n->plane, &f->plane, sizeof(plane_t));
+ if (f->face_winding)
+ n->face_winding = Winding_Clone(f->face_winding);
+ else
+ n->face_winding = NULL;
+ n->pShader = f->pShader;
+ n->pShader->IncRef();
+ n->d_texture = n->pShader->getTexture();
+ return n;
+}
+
+void Face_SetShader(face_t *face, const char *name)
+{
+ if(face->pShader != NULL)
+ face->pShader->DecRef();
+ face->texdef.SetName(name);
+ face->pShader = QERApp_Shader_ForName(name);
+ face->pShader->IncRef();
+ face->d_texture = face->pShader->getTexture();
+ face->texdef.flags = face->pShader->getFlags();
+}
+
+void Face_SetShader(face_t *face, IShader *shader)
+{
+ if(face->pShader != NULL)
+ face->pShader->DecRef();
+ face->texdef.SetName(shader->getName());
+ face->d_texture = shader->getTexture();
+ face->texdef.flags = shader->getFlags();
+ face->pShader = shader;
+ face->pShader->IncRef();
+}
+
+/*
+================
+Clamp
+================
+*/
+void Clamp(float& f, int nClamp)
+{
+ float fFrac = f - static_cast<int>(f);
+ f = static_cast<int>(f) % nClamp;
+ f += fFrac;
+}
+
+/*
+================
+Face_MoveTexture
+================
+*/
+void Face_MoveTexture(face_t *f, vec3_t delta)
+{
+ vec3_t vX, vY;
+
+ if (g_qeglobals.m_bBrushPrimitMode)
+ ShiftTextureGeometric_BrushPrimit( f, delta );
+ else
+ {
+ TextureAxisFromPlane(&f->plane, vX, vY);
+
+ vec3_t vDP, vShift;
+ vDP[0] = DotProduct(delta, vX);
+ vDP[1] = DotProduct(delta, vY);
+
+ double fAngle = f->texdef.rotate / 180 * Q_PI;
+ double c = cos(fAngle);
+ double s = sin(fAngle);
+
+ vShift[0] = vDP[0] * c - vDP[1] * s;
+ vShift[1] = vDP[0] * s + vDP[1] * c;
+
+ if (!f->texdef.scale[0])
+ f->texdef.scale[0] = g_pGameDescription->mTextureDefaultScale;
+ if (!f->texdef.scale[1])
+ f->texdef.scale[1] = g_pGameDescription->mTextureDefaultScale;
+
+ f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0];
+ f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1];
+
+ // clamp the shifts
+ Clamp(f->texdef.shift[0], f->d_texture->width);
+ Clamp(f->texdef.shift[1], f->d_texture->height);
+ }
+}
+
+/*
+================
+Face_SetColor
+================
+*/
+/*!\todo Replace all face_t::d_texture access with face_t::pShader::GetTexture.*/
+void Face_SetColor (brush_t *b, face_t *f, float fCurveColor)
+{
+ // set shading for face
+ f->d_shade = SetShadeForPlane (&f->plane);
+ f->d_color[0] = f->pShader->getTexture()->color[0] * f->d_shade;
+ f->d_color[1] = f->pShader->getTexture()->color[1] * f->d_shade;
+ f->d_color[2] = f->pShader->getTexture()->color[2] * f->d_shade;
+}
+
+/*
+================
+Face_TextureVectors
+================
+*/
+void Face_TextureVectors (face_t *f, float STfromXYZ[2][4])
+{
+ vec3_t pvecs[2];
+ int sv, tv;
+ float ang, sinv, cosv;
+ float ns, nt;
+ int i,j;
+ qtexture_t *q;
+ texdef_t *td;
+
+#ifdef _DEBUG
+ // this code is not supposed to be used while in BP mode, warning here can help spot the problem
+ if (g_qeglobals.m_bBrushPrimitMode && !g_qeglobals.bNeedConvert)
+ Sys_Printf("Warning : illegal call of Face_TextureVectors in brush primitive mode\n");
+#endif
+
+ td = &f->texdef;
+ q = f->d_texture;
+
+ memset (STfromXYZ, 0, 8*sizeof(float));
+
+ if (!td->scale[0])
+ td->scale[0] = g_pGameDescription->mTextureDefaultScale;
+ if (!td->scale[1])
+ td->scale[1] = g_pGameDescription->mTextureDefaultScale;
+
+ // get natural texture axis
+ TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);
+
+ // rotate axis
+ if (td->rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (td->rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (td->rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (td->rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = td->rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (pvecs[0][0])
+ sv = 0;
+ else if (pvecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (pvecs[1][0])
+ tv = 0;
+ else if (pvecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++) {
+ ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];
+ nt = sinv * pvecs[i][sv] + cosv * pvecs[i][tv];
+ STfromXYZ[i][sv] = ns;
+ STfromXYZ[i][tv] = nt;
+ }
+
+ // scale
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ STfromXYZ[i][j] = STfromXYZ[i][j] / td->scale[i];
+
+ // shift
+ STfromXYZ[0][3] = td->shift[0];
+ STfromXYZ[1][3] = td->shift[1];
+
+ for (j=0 ; j<4 ; j++) {
+ STfromXYZ[0][j] /= q->width;
+ STfromXYZ[1][j] /= q->height;
+ }
+}
+
+/*
+================
+Face_MakePlane
+================
+*/
+void Face_MakePlane (face_t *f)
+{
+ int j;
+ vec3_t t1, t2, t3;
+
+ // convert to a vector / dist plane
+ for (j=0 ; j<3 ; j++)
+ {
+ t1[j] = f->planepts[0][j] - f->planepts[1][j];
+ t2[j] = f->planepts[2][j] - f->planepts[1][j];
+ t3[j] = f->planepts[1][j];
+ }
+
+ CrossProduct(t1,t2, f->plane.normal);
+ if (VectorCompare (f->plane.normal, vec3_origin))
+ Sys_FPrintf (SYS_WRN, "WARNING: brush plane with no normal\n");
+ VectorNormalize (f->plane.normal, f->plane.normal);
+ f->plane.dist = DotProduct (t3, f->plane.normal);
+}
+
+/*
+================
+EmitTextureCoordinates
+================
+*/
+void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)
+{
+ float STfromXYZ[2][4];
+
+ Face_TextureVectors (f, STfromXYZ);
+ xyzst[3] = DotProduct (xyzst, STfromXYZ[0]) + STfromXYZ[0][3];
+ xyzst[4] = DotProduct (xyzst, STfromXYZ[1]) + STfromXYZ[1][3];
+}
+
+//==========================================================================
+
+/*
+================
+Brush_MakeFacePlanes
+================
+*/
+void Brush_MakeFacePlanes (brush_t *b)
+{
+ face_t *f;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ Face_MakePlane (f);
+ }
+}
+
+/*
+================
+DrawBrushEntityName
+================
+*/
+void DrawBrushEntityName (brush_t *b)
+{
+ const char *name;
+ float a, s, c;
+ vec3_t mid;
+ int i;
+
+ if (!b->owner)
+ return; // during contruction
+
+ if (b->owner == world_entity)
+ return;
+
+ if (b != b->owner->brushes.onext)
+ return; // not key brush
+
+ // TTimo: Brush_DrawFacingAngle is for camera view rendering, this function is called for 2D views
+ // FIXME - spog - not sure who put this here.. Brush_DrawFacingAngle() does this job?
+ // Brush_DrawFacingAngle() works when called, but is not being called.
+ if (g_qeglobals.d_savedinfo.show_angles && (b->owner->eclass->nShowFlags & ECLASS_ANGLE))
+ {
+ // draw the angle pointer
+ a = FloatForKey (b->owner, "angle");
+ s = sin (a/180*Q_PI);
+ c = cos (a/180*Q_PI);
+ for (i=0 ; i<3 ; i++)
+ mid[i] = (b->mins[i] + b->maxs[i])*0.5;
+
+ qglBegin (GL_LINE_STRIP);
+ qglVertex3fv (mid);
+ mid[0] += c*8;
+ mid[1] += s*8;
+ mid[2] += s*8;
+ qglVertex3fv (mid);
+ mid[0] -= c*4;
+ mid[1] -= s*4;
+ mid[2] -= s*4;
+ mid[0] -= s*4;
+ mid[1] += c*4;
+ mid[2] += c*4;
+ qglVertex3fv (mid);
+ mid[0] += c*4;
+ mid[1] += s*4;
+ mid[2] += s*4;
+ mid[0] += s*4;
+ mid[1] -= c*4;
+ mid[2] -= c*4;
+ qglVertex3fv (mid);
+ mid[0] -= c*4;
+ mid[1] -= s*4;
+ mid[2] -= s*4;
+ mid[0] += s*4;
+ mid[1] -= c*4;
+ mid[2] -= c*4;
+ qglVertex3fv (mid);
+ qglEnd ();
+ }
+
+ if (g_qeglobals.d_savedinfo.show_names)
+ {
+ name = ValueForKey (b->owner, "classname");
+ qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4);
+ gtk_glwidget_print_string(name);
+ }
+}
+
+/*
+=================
+Brush_MakeFaceWinding
+
+returns the visible polygon on a face
+=================
+*/
+winding_t *Brush_MakeFaceWinding (brush_t *b, face_t *face)
+{
+ winding_t *w;
+ face_t *clip;
+ plane_t plane;
+ qboolean past;
+
+ // get a poly that covers an effectively infinite area
+ w = Winding_BaseForPlane (&face->plane);
+
+ // chop the poly by all of the other faces
+ past = false;
+ for (clip = b->brush_faces ; clip && w ; clip=clip->next)
+ {
+ if (clip == face)
+ {
+ past = true;
+ continue;
+ }
+ if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999
+ && fabs(face->plane.dist - clip->plane.dist) < 0.01 )
+ { // identical plane, use the later one
+ if (past)
+ {
+ free (w);
+ return NULL;
+ }
+ continue;
+ }
+
+ // flip the plane, because we want to keep the back side
+ VectorSubtract (vec3_origin,clip->plane.normal, plane.normal);
+ plane.dist = -clip->plane.dist;
+
+ w = Winding_Clip (w, &plane, false);
+ if (!w)
+ return w;
+ }
+
+ if (w->numpoints < 3)
+ {
+ free(w);
+ w = NULL;
+ }
+
+ if (!w)
+ Sys_FPrintf (SYS_WRN, "unused plane\n");
+
+ return w;
+}
+
+/*
+=================
+Brush_SnapPlanepts
+=================
+*/
+void Brush_SnapPlanepts (brush_t *b)
+{
+ int i, j;
+ face_t *f;
+
+ if (g_PrefsDlg.m_bNoClamp)
+ return;
+
+ if (g_qeglobals.d_bSmallGrid)
+ {
+ for (f=b->brush_faces ; f; f=f->next)
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<3 ; j++)
+ f->planepts[i][j] = floor (f->planepts[i][j]/g_qeglobals.d_gridsize + 0.5)*g_qeglobals.d_gridsize;
+ }
+ else
+ {
+ for (f=b->brush_faces ; f; f=f->next)
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<3 ; j++)
+ f->planepts[i][j] = floor (f->planepts[i][j] + 0.5);
+ }
+}
+
+/*
+** Brush_Build
+**
+** Builds a brush rendering data and also sets the min/max bounds
+*/
+// TTimo
+// added a bConvert flag to convert between old and new brush texture formats
+// TTimo
+// brush grouping: update the group treeview if necessary
+void Brush_Build( brush_t *b, bool bSnap, bool bMarkMap, bool bConvert, bool bFilterTest)
+{
+ bool bLocalConvert;
+
+
+#ifdef _DEBUG
+ if (!g_qeglobals.m_bBrushPrimitMode && bConvert)
+ Sys_Printf("Warning : conversion from brush primitive to old brush format not implemented\n");
+#endif
+
+ // if bConvert is set and g_qeglobals.bNeedConvert is not, that just means we need convert for this brush only
+ if (bConvert && !g_qeglobals.bNeedConvert)
+ {
+#ifdef _DEBUG
+ //++timo FIXME: it's not very clear when this can happen, I guess while dealing with plugins that send brushes
+ // back and forth in one format or the other .. more when mixing BP / noBP in the same maps.
+#endif
+ bLocalConvert = true;
+ g_qeglobals.bNeedConvert = true;
+ }
+ else
+ bLocalConvert = false;
+
+ /*
+ ** build the windings and generate the bounding box
+ */
+ Brush_BuildWindings(b, bSnap);
+
+ if(b->owner->model.pRender)
+ {
+ const aabb_t *aabb = b->owner->model.pRender->GetAABB();
+ VectorAdd(aabb->origin, aabb->extents, b->maxs);
+ VectorSubtract(aabb->origin, aabb->extents, b->mins);
+ }
+
+ //Patch_BuildPoints (b); // does nothing but set b->patchBrush true if the texdef contains SURF_PATCH !
+
+ /*
+ ** move the points and edges if in select mode
+ */
+ if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
+ SetupVertexSelection ();
+
+ if (b->itemOwner == 0) //NULL)
+ Group_AddToProperGroup(b);
+
+ if (bMarkMap)
+ {
+ Sys_MarkMapModified();
+ }
+
+ if (bLocalConvert)
+ g_qeglobals.bNeedConvert = false;
+
+ // spog - applying filters to brush during brush_build instead of during redraw
+ if (bFilterTest)
+ b->bFiltered = FilterBrush( b );
+}
+
+/*
+==============
+Brush_SplitBrushByFace
+
+The incoming brush is NOT freed.
+The incoming face is NOT left referenced.
+==============
+*/
+void Brush_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back, boolean bCaulk)
+{
+ brush_t *b;
+ face_t *nf;
+ vec3_t temp;
+
+ b = Brush_Clone (in);
+ nf = Face_Clone (f);
+
+ nf->texdef = b->brush_faces->texdef;
+ if (bCaulk)
+ {
+ nf->texdef.SetName(g_pGameDescription->mCaulkShader.GetBuffer());
+ }
+ nf->next = b->brush_faces;
+ b->brush_faces = nf;
+
+ Brush_Build( b );
+ Brush_RemoveEmptyFaces ( b );
+ if ( !b->brush_faces )
+ { // completely clipped away
+ Brush_Free (b);
+ *back = NULL;
+ }
+ else
+ {
+ Entity_LinkBrush (in->owner, b);
+ *back = b;
+ }
+
+ b = Brush_Clone (in);
+ nf = Face_Clone (f);
+ // swap the plane winding
+ VectorCopy (nf->planepts[0], temp);
+ VectorCopy (nf->planepts[1], nf->planepts[0]);
+ VectorCopy (temp, nf->planepts[1]);
+
+ nf->texdef = b->brush_faces->texdef;
+ if (bCaulk)
+ {
+ nf->texdef.SetName(g_pGameDescription->mCaulkShader.GetBuffer());
+ }
+ nf->next = b->brush_faces;
+ b->brush_faces = nf;
+
+ Brush_Build( b );
+ Brush_RemoveEmptyFaces ( b );
+ if ( !b->brush_faces )
+ { // completely clipped away
+ Brush_Free (b);
+ *front = NULL;
+ }
+ else
+ {
+ Entity_LinkBrush (in->owner, b);
+ *front = b;
+ }
+}
+
+/*
+=================
+Brush_BestSplitFace
+
+returns the best face to split the brush with.
+return NULL if the brush is convex
+=================
+*/
+face_t *Brush_BestSplitFace(brush_t *b)
+{
+ face_t *face, *f, *bestface;
+ winding_t *front, *back;
+ int splits, tinywindings, value, bestvalue;
+
+ bestvalue = 999999;
+ bestface = NULL;
+ for (face = b->brush_faces; face; face = face->next)
+ {
+ splits = 0;
+ tinywindings = 0;
+ for (f = b->brush_faces; f; f = f->next)
+ {
+ if (f == face) continue;
+ //
+ Winding_SplitEpsilon(f->face_winding, face->plane.normal, face->plane.dist, 0.1f, &front, &back);
+
+ if (!front)
+ {
+ Winding_Free(back);
+ }
+ else if (!back)
+ {
+ Winding_Free(front);
+ }
+ else
+ {
+ splits++;
+ if (Winding_IsTiny(front)) tinywindings++;
+ if (Winding_IsTiny(back)) tinywindings++;
+ }
+ }
+ if (splits)
+ {
+ value = splits + 50 * tinywindings;
+ if (value < bestvalue)
+ {
+ bestvalue = value;
+ bestface = face;
+ }
+ }
+ }
+ return bestface;
+}
+
+/*
+=================
+Brush_MakeConvexBrushes
+
+MrE FIXME: this doesn't work because the old
+ Brush_SplitBrushByFace is used
+Turns the brush into a minimal number of convex brushes.
+If the input brush is convex then it will be returned.
+Otherwise the input brush will be freed.
+NOTE: the input brush should have windings for the faces.
+=================
+*/
+brush_t *Brush_MakeConvexBrushes(brush_t *b)
+{
+ brush_t *front, *back, *end;
+ face_t *face;
+
+ b->next = NULL;
+ face = Brush_BestSplitFace(b);
+ if (!face) return b;
+ Brush_SplitBrushByFace(b, face, &front, &back);
+ //this should never happen
+ if (!front && !back) return b;
+ Brush_Free(b);
+ if (!front)
+ return Brush_MakeConvexBrushes(back);
+ b = Brush_MakeConvexBrushes(front);
+ if (back)
+ {
+ for (end = b; end->next; end = end->next);
+ end->next = Brush_MakeConvexBrushes(back);
+ }
+ return b;
+}
+
+/*
+=================
+Brush_Convex
+=================
+*/
+int Brush_Convex(brush_t *b)
+{
+ face_t *face1, *face2;
+
+ for (face1 = b->brush_faces; face1; face1 = face1->next)
+ {
+ if (!face1->face_winding) continue;
+ for (face2 = b->brush_faces; face2; face2 = face2->next)
+ {
+ if (face1 == face2) continue;
+ if (!face2->face_winding) continue;
+ if (Winding_PlanesConcave(face1->face_winding, face2->face_winding,
+ face1->plane.normal, face2->plane.normal,
+ face1->plane.dist, face2->plane.dist))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*
+=================
+Brush_MoveVertexes
+
+- The input brush must be convex
+- The input brush must have face windings.
+- The output brush will be convex.
+- Returns true if the WHOLE vertex movement is performed.
+=================
+*/
+
+// define this to debug the vertex editing mode
+#ifdef _DEBUG
+//#define DBG_VERT
+#endif
+
+#define MAX_MOVE_FACES 64
+
+int Brush_MoveVertex(brush_t *b, vec3_t vertex, vec3_t delta, vec3_t end, bool bSnap)
+{
+ face_t *f, *face, *newface, *lastface, *nextface;
+ face_t *movefaces[MAX_MOVE_FACES];
+ int movefacepoints[MAX_MOVE_FACES];
+ winding_t *w, tmpw;
+ vec3_t start, mid;
+ plane_t plane;
+ int i, j, k, nummovefaces, result, done;
+ float dot, front, back, frac, smallestfrac;
+
+#ifdef DBG_VERT
+ Sys_Printf("Bursh_MoveVertex: %p vertex: %g %g %g delta: %g %g %g end: %g %g %g snap: %s\n", b, vertex[0], vertex[1], vertex[2], delta[0], delta[1], delta[2], end[0], end[1], end[2], bSnap ? "true" : "false" );
+#endif
+
+ result = true;
+ //
+ tmpw.numpoints = 3;
+ tmpw.maxpoints = 3;
+ VectorCopy(vertex, start);
+ VectorAdd(vertex, delta, end);
+ //snap or not?
+ if (bSnap)
+ for (i = 0; i < 3; i++)
+ end[i] = floor(end[i] / g_qeglobals.d_gridsize + 0.1) * g_qeglobals.d_gridsize;
+ //
+ VectorCopy(end, mid);
+ //if the start and end are the same
+ if (Point_Equal(start, end, 0.3f)) return false;
+ //the end point may not be the same as another vertex
+ for (face = b->brush_faces; face; face = face->next)
+ {
+ w = face->face_winding;
+ if (!w) continue;
+ for (i = 0; i < w->numpoints; i++)
+ {
+ if (Point_Equal(w->points[i], end, 0.3f))
+ {
+ VectorCopy(vertex, end);
+ return false;
+ }
+ }
+ }
+ //
+ done = false;
+ while(!done)
+ {
+ //chop off triangles from all brush faces that use the to be moved vertex
+ //store pointers to these chopped off triangles in movefaces[]
+ nummovefaces = 0;
+ for (face = b->brush_faces; face; face = face->next)
+ {
+ w = face->face_winding;
+ if (!w) continue;
+ for (i = 0; i < w->numpoints; i++)
+ {
+ if (Point_Equal(w->points[i], start, 0.2f))
+ {
+ if (face->face_winding->numpoints <= 3)
+ {
+ movefacepoints[nummovefaces] = i;
+ movefaces[nummovefaces++] = face;
+ break;
+ }
+ dot = DotProduct(end, face->plane.normal) - face->plane.dist;
+ //if the end point is in front of the face plane
+ if (dot > 0.1)
+ {
+ //fanout triangle subdivision
+ for (k = i; k < i + w->numpoints-3; k++)
+ {
+ VectorCopy(w->points[i], tmpw.points[0]);
+ VectorCopy(w->points[(k+1) % w->numpoints], tmpw.points[1]);
+ VectorCopy(w->points[(k+2) % w->numpoints], tmpw.points[2]);
+ //
+ newface = Face_Clone(face);
+ //get the original
+ for (f = face; f->original; f = f->original) ;
+ newface->original = f;
+ //store the new winding
+ if (newface->face_winding) Winding_Free(newface->face_winding);
+ newface->face_winding = Winding_Clone(&tmpw);
+ //get the texture information
+ newface->pShader = face->pShader;
+ newface->d_texture = face->d_texture;
+
+ //add the face to the brush
+ newface->next = b->brush_faces;
+ b->brush_faces = newface;
+ //add this new triangle to the move faces
+ movefacepoints[nummovefaces] = 0;
+ movefaces[nummovefaces++] = newface;
+ }
+ //give the original face a new winding
+ VectorCopy(w->points[(i-2+w->numpoints) % w->numpoints], tmpw.points[0]);
+ VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[1]);
+ VectorCopy(w->points[i], tmpw.points[2]);
+ Winding_Free(face->face_winding);
+ face->face_winding = Winding_Clone(&tmpw);
+ //add the original face to the move faces
+ movefacepoints[nummovefaces] = 2;
+ movefaces[nummovefaces++] = face;
+ }
+ else
+ {
+ //chop a triangle off the face
+ VectorCopy(w->points[(i-1+w->numpoints) % w->numpoints], tmpw.points[0]);
+ VectorCopy(w->points[i], tmpw.points[1]);
+ VectorCopy(w->points[(i+1) % w->numpoints], tmpw.points[2]);
+ //remove the point from the face winding
+ Winding_RemovePoint(w, i);
+ //get texture crap right
+ Face_SetColor(b, face, 1.0);
+ for (j = 0; j < w->numpoints; j++)
+ EmitTextureCoordinates(w->points[j], face->d_texture, face);
+ //make a triangle face
+ newface = Face_Clone(face);
+ //get the original
+ for (f = face; f->original; f = f->original) ;
+ newface->original = f;
+ //store the new winding
+ if (newface->face_winding) Winding_Free(newface->face_winding);
+ newface->face_winding = Winding_Clone(&tmpw);
+ //get the texture
+ newface->pShader = face->pShader;
+ newface->d_texture = newface->pShader->getTexture();
+// newface->d_texture = QERApp_Texture_ForName2( newface->texdef.name );
+ //add the face to the brush
+ newface->next = b->brush_faces;
+ b->brush_faces = newface;
+ //
+ movefacepoints[nummovefaces] = 1;
+ movefaces[nummovefaces++] = newface;
+ }
+ break;
+ }
+ }
+ }
+ //now movefaces contains pointers to triangle faces that
+ //contain the to be moved vertex
+ //
+ done = true;
+ VectorCopy(end, mid);
+ smallestfrac = 1;
+ for (face = b->brush_faces; face; face = face->next)
+ {
+ //check if there is a move face that has this face as the original
+ for (i = 0; i < nummovefaces; i++)
+ {
+ if (movefaces[i]->original == face) break;
+ }
+ if (i >= nummovefaces) continue;
+ //check if the original is not a move face itself
+ for (j = 0; j < nummovefaces; j++)
+ {
+ if (face == movefaces[j]) break;
+ }
+ //if the original is not a move face itself
+ if (j >= nummovefaces)
+ {
+ memcpy(&plane, &movefaces[i]->original->plane, sizeof(plane_t));
+ }
+ else
+ {
+ k = movefacepoints[j];
+ w = movefaces[j]->face_winding;
+ VectorCopy(w->points[(k+1)%w->numpoints], tmpw.points[0]);
+ VectorCopy(w->points[(k+2)%w->numpoints], tmpw.points[1]);
+ //
+ k = movefacepoints[i];
+ w = movefaces[i]->face_winding;
+ VectorCopy(w->points[(k+1)%w->numpoints], tmpw.points[2]);
+ if (!Plane_FromPoints(tmpw.points[0], tmpw.points[1], tmpw.points[2], &plane))
+ {
+ VectorCopy(w->points[(k+2)%w->numpoints], tmpw.points[2]);
+ if (!Plane_FromPoints(tmpw.points[0], tmpw.points[1], tmpw.points[2], &plane))
+ //this should never happen otherwise the face merge did a crappy job a previous pass
+ continue;
+ }
+ }
+ //now we've got the plane to check agains
+ front = DotProduct(start, plane.normal) - plane.dist;
+ back = DotProduct(end, plane.normal) - plane.dist;
+ //if the whole move is at one side of the plane
+ if (front < 0.01 && back < 0.01) continue;
+ if (front > -0.01 && back > -0.01) continue;
+ //if there's no movement orthogonal to this plane at all
+ if (fabs(front-back) < 0.001) continue;
+ //ok first only move till the plane is hit
+ frac = front/(front-back);
+ if (frac < smallestfrac)
+ {
+ mid[0] = start[0] + (end[0] - start[0]) * frac;
+ mid[1] = start[1] + (end[1] - start[1]) * frac;
+ mid[2] = start[2] + (end[2] - start[2]) * frac;
+ smallestfrac = frac;
+ }
+ //
+ done = false;
+ }
+
+ //move the vertex
+ for (i = 0; i < nummovefaces; i++)
+ {
+ //move vertex to end position
+ VectorCopy(mid, movefaces[i]->face_winding->points[movefacepoints[i]]);
+ //create new face plane
+ for (j = 0; j < 3; j++)
+ {
+ VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
+ }
+ Face_MakePlane(movefaces[i]);
+ if (VectorLength(movefaces[i]->plane.normal) < 0.1)
+ result = false;
+ }
+ //if the brush is no longer convex
+ if (!result || !Brush_Convex(b))
+ {
+ for (i = 0; i < nummovefaces; i++)
+ {
+ //move the vertex back to the initial position
+ VectorCopy(start, movefaces[i]->face_winding->points[movefacepoints[i]]);
+ //create new face plane
+ for (j = 0; j < 3; j++)
+ {
+ VectorCopy(movefaces[i]->face_winding->points[j], movefaces[i]->planepts[j]);
+ }
+ Face_MakePlane(movefaces[i]);
+ }
+ result = false;
+ VectorCopy(start, end);
+ done = true;
+ }
+ else
+ {
+ VectorCopy(mid, start);
+ }
+ //get texture crap right
+ for (i = 0; i < nummovefaces; i++)
+ {
+ Face_SetColor(b, movefaces[i], 1.0);
+ for (j = 0; j < movefaces[i]->face_winding->numpoints; j++)
+ EmitTextureCoordinates(movefaces[i]->face_winding->points[j], movefaces[i]->d_texture, movefaces[i]);
+ }
+
+ //now try to merge faces with their original faces
+ lastface = NULL;
+ for (face = b->brush_faces; face; face = nextface)
+ {
+ nextface = face->next;
+ if (!face->original)
+ {
+ lastface = face;
+ continue;
+ }
+ if (!Plane_Equal(&face->plane, &face->original->plane, false))
+ {
+ lastface = face;
+ continue;
+ }
+ w = Winding_TryMerge(face->face_winding, face->original->face_winding, face->plane.normal, true);
+ if (!w)
+ {
+ lastface = face;
+ continue;
+ }
+ Winding_Free(face->original->face_winding);
+ face->original->face_winding = w;
+ //get texture crap right
+ Face_SetColor(b, face->original, 1.0);
+ for (j = 0; j < face->original->face_winding->numpoints; j++)
+ EmitTextureCoordinates(face->original->face_winding->points[j], face->original->d_texture, face->original);
+ //remove the face that was merged with the original
+ if (lastface) lastface->next = face->next;
+ else b->brush_faces = face->next;
+ Face_Free(face);
+ }
+ }
+ return result;
+}
+
+/*
+=================
+Brush_InsertVertexBetween
+=================
+*/
+int Brush_InsertVertexBetween(brush_t *b, vec3_t p1, vec3_t p2)
+{
+ face_t *face;
+ winding_t *w, *neww;
+ vec3_t point;
+ int i, insert;
+
+ if (Point_Equal(p1, p2, 0.4f))
+ return false;
+ VectorAdd(p1, p2, point);
+ VectorScale(point, 0.5f, point);
+ insert = false;
+ //the end point may not be the same as another vertex
+ for (face = b->brush_faces; face; face = face->next)
+ {
+ w = face->face_winding;
+ if (!w) continue;
+ neww = NULL;
+ for (i = 0; i < w->numpoints; i++)
+ {
+ if (!Point_Equal(w->points[i], p1, 0.1f))
+ continue;
+ if (Point_Equal(w->points[(i+1) % w->numpoints], p2, 0.1f))
+ {
+ neww = Winding_InsertPoint(w, point, (i+1) % w->numpoints);
+ break;
+ }
+ else if (Point_Equal(w->points[(i-1+w->numpoints) % w->numpoints], p2, 0.3f))
+ {
+ neww = Winding_InsertPoint(w, point, i);
+ break;
+ }
+ }
+ if (neww)
+ {
+ Winding_Free(face->face_winding);
+ face->face_winding = neww;
+ insert = true;
+ }
+ }
+ return insert;
+}
+
+
+/*
+=================
+Brush_ResetFaceOriginals
+=================
+*/
+void Brush_ResetFaceOriginals(brush_t *b)
+{
+ face_t *face;
+
+ for (face = b->brush_faces; face; face = face->next)
+ {
+ face->original = NULL;
+ }
+}
+
+#ifdef ENABLE_GROUPS
+/*
+==============
+Brush_SetEpair
+sets an epair for the given brush
+==============
+*/
+void Brush_SetEpair(brush_t *b, const char *pKey, const char *pValue)
+{
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ if (b->patchBrush)
+ {
+ Patch_SetEpair(b->pPatch, pKey, pValue);
+ }
+ else
+ {
+ SetKeyValue(b->epairs, pKey, pValue);
+ }
+ }
+ else
+ {
+ Sys_Printf("Can only set key/values in Brush primitive mode\n");
+ }
+}
+
+/*
+=================
+Brush_GetKeyValue
+=================
+*/
+const char* Brush_GetKeyValue(brush_t *b, const char *pKey)
+{
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ if (b->patchBrush)
+ {
+ return Patch_GetKeyValue(b->pPatch, pKey);
+ }
+ else
+ {
+ return ValueForKey(b->epairs, pKey);
+ }
+ }
+ else
+ {
+ Sys_Printf("Can only set brush/patch key/values in Brush primitive mode\n");
+ }
+ return "";
+}
+#endif
+/*
+=================
+CheckName
+temporary stuff, detect potential problems when saving the texture name
+=================
+*/
+void CheckName( face_t *fa, char *pname )
+{
+ if (!strlen(fa->texdef.GetName()))
+ {
+#ifdef _DEBUG
+ Sys_Printf("WARNING: unexpected texdef.name is empty in Brush.cpp CheckName\n");
+#endif
+ fa->texdef.SetName(SHADER_NOT_FOUND);
+ strcpy(pname, SHADER_NOT_FOUND);
+ return;
+ }
+
+ // some people manage to get long filename textures (with spaces) in their maps
+ if (strchr( fa->texdef.GetName(), ' ' ))
+ {
+ char Msg1[1024];
+
+ 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.", fa->texdef.GetName() );
+
+ Sys_Printf("%s\n", Msg1 );
+ gtk_MessageBox(g_pParentWnd->m_pWidget, Msg1, "Error saving map", MB_OK );
+ strcpy( pname, SHADER_NOT_FOUND );
+ return;
+ }
+
+ //++timo FIXME: bug #103494 detection attempt
+ // TODO: clean this detection part when bug will have disappeared
+ if (fa->texdef.GetName()[0] == '(')
+ {
+ char *text = "Bug #103494 detected, dropping texture. Please report to timo@qeradiant.com if you have a way to reproduce!\nNOTE: this message may popup several times .. once for each buggy face detected.";
+ Sys_Printf("%s\n", text);
+ gtk_MessageBox(g_pParentWnd->m_pWidget, text, "Error saving map", MB_OK );
+ // need to cleanup this dead face name or we may loop endlessly
+ fa->texdef.SetName(SHADER_NOT_FOUND);
+ strcpy( pname, SHADER_NOT_FOUND );
+ return;
+ }
+ strcpy( pname, fa->texdef.GetName()+9 ); // remove "textures/"
+}
+
+/*
+=============
+Brush_Create
+
+Create non-textured blocks for entities
+The brush is NOT linked to any list
+=============
+*/
+brush_t *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef)
+{
+ int i, j;
+ vec3_t pts[4][2];
+ face_t *f;
+ brush_t *b;
+
+#if DBG_BP
+ // brush primitive mode : convert texdef to brushprimit_texdef ?
+ // most of the time texdef is empty
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ // check texdef is empty .. if there are cases it's not we need to write some conversion code
+ if (texdef->shift[0]!=0 || texdef->shift[1]!=0 || texdef->scale[0]!=0 || texdef->scale[1]!=0 || texdef->rotate!=0)
+ Sys_Printf("Warning : non-zero texdef detected in Brush_Create .. need brush primitive conversion\n");
+ }
+#endif
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (maxs[i] < mins[i])
+ Error ("Brush_InitSolid: backwards");
+ }
+
+ b = Brush_Alloc();
+
+ pts[0][0][0] = mins[0];
+ pts[0][0][1] = mins[1];
+
+ pts[1][0][0] = mins[0];
+ pts[1][0][1] = maxs[1];
+
+ pts[2][0][0] = maxs[0];
+ pts[2][0][1] = maxs[1];
+
+ pts[3][0][0] = maxs[0];
+ pts[3][0][1] = mins[1];
+
+ for (i=0 ; i<4 ; i++)
+ {
+ pts[i][0][2] = mins[2];
+ pts[i][1][0] = pts[i][0][0];
+ pts[i][1][1] = pts[i][0][1];
+ pts[i][1][2] = maxs[2];
+ }
+
+ for (i=0 ; i<4 ; i++)
+ {
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->texdef.flags &= ~SURF_KEEP;
+ f->texdef.contents &= ~CONTENTS_KEEP;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+ j = (i+1)%4;
+
+ VectorCopy (pts[j][1], f->planepts[0]);
+ VectorCopy (pts[i][1], f->planepts[1]);
+ VectorCopy (pts[i][0], f->planepts[2]);
+ }
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->texdef.flags &= ~SURF_KEEP;
+ f->texdef.contents &= ~CONTENTS_KEEP;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorCopy (pts[0][1], f->planepts[0]);
+ VectorCopy (pts[1][1], f->planepts[1]);
+ VectorCopy (pts[2][1], f->planepts[2]);
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->texdef.flags &= ~SURF_KEEP;
+ f->texdef.contents &= ~CONTENTS_KEEP;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorCopy (pts[2][0], f->planepts[0]);
+ VectorCopy (pts[1][0], f->planepts[1]);
+ VectorCopy (pts[0][0], f->planepts[2]);
+
+ return b;
+}
+
+/*
+=============
+Brush_CreatePyramid
+
+Create non-textured pyramid for light entities
+The brush is NOT linked to any list
+=============
+*/
+brush_t *Brush_CreatePyramid (vec3_t mins, vec3_t maxs, texdef_t *texdef)
+{
+ int i;
+
+ //++timo handle new brush primitive ? return here ??
+ return Brush_Create(mins, maxs, texdef);
+
+ for (i=0 ; i<3 ; i++)
+ if (maxs[i] < mins[i])
+ Error ("Brush_InitSolid: backwards");
+
+ brush_t* b = Brush_Alloc();
+
+ vec3_t corners[4];
+
+ float fMid = Rad_rint(mins[2] + (Rad_rint((maxs[2] - mins[2]) / 2)));
+
+ corners[0][0] = mins[0];
+ corners[0][1] = mins[1];
+ corners[0][2] = fMid;
+
+ corners[1][0] = mins[0];
+ corners[1][1] = maxs[1];
+ corners[1][2] = fMid;
+
+ corners[2][0] = maxs[0];
+ corners[2][1] = maxs[1];
+ corners[2][2] = fMid;
+
+ corners[3][0] = maxs[0];
+ corners[3][1] = mins[1];
+ corners[3][2] = fMid;
+
+ vec3_t top, bottom;
+
+ top[0] = Rad_rint(mins[0] + ((maxs[0] - mins[0]) / 2));
+ top[1] = Rad_rint(mins[1] + ((maxs[1] - mins[1]) / 2));
+ top[2] = Rad_rint(maxs[2]);
+
+ VectorCopy(top, bottom);
+ bottom[2] = mins[2];
+
+ // sides
+ for (i = 0; i < 4; i++)
+ {
+ face_t* f = Face_Alloc();
+ f->texdef = *texdef;
+ f->texdef.flags &= ~SURF_KEEP;
+ f->texdef.contents &= ~CONTENTS_KEEP;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+ int j = (i+1)%4;
+
+ VectorCopy (top, f->planepts[0]);
+ VectorCopy (corners[i], f->planepts[1]);
+ VectorCopy(corners[j], f->planepts[2]);
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->texdef.flags &= ~SURF_KEEP;
+ f->texdef.contents &= ~CONTENTS_KEEP;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorCopy (bottom, f->planepts[2]);
+ VectorCopy (corners[i], f->planepts[1]);
+ VectorCopy(corners[j], f->planepts[0]);
+ }
+
+ return b;
+}
+
+
+
+
+/*
+=============
+Brush_MakeSided
+
+Makes the current brush have the given number of 2d sides
+=============
+*/
+void Brush_MakeSided (int sides)
+{
+ int i, axis;
+ vec3_t mins, maxs;
+ brush_t *b;
+ texdef_t *texdef;
+ face_t *f;
+ vec3_t mid;
+ float width;
+ float sv, cv;
+
+ if (sides < 3)
+ {
+ Sys_Status ("Bad sides number", 0);
+ return;
+ }
+
+ if (sides >= MAX_POINTS_ON_WINDING-4)
+ {
+ Sys_Printf("too many sides.\n");
+ return;
+ }
+
+ if (!QE_SingleBrush ())
+ {
+ Sys_Status ("Must have a single brush selected", 0 );
+ return;
+ }
+
+ b = selected_brushes.next;
+ VectorCopy (b->mins, mins);
+ VectorCopy (b->maxs, maxs);
+ texdef = &g_qeglobals.d_texturewin.texdef;
+
+ Brush_Free (b);
+
+ if (g_pParentWnd->ActiveXY())
+ {
+ switch(g_pParentWnd->ActiveXY()->GetViewType())
+ {
+ case XY: axis = 2; break;
+ case XZ: axis = 1; break;
+ case YZ: axis = 0; break;
+ }
+ }
+ else
+ {
+ axis = 2;
+ }
+
+ // find center of brush
+ width = 8;
+ for (i = 0; i < 3; i++)
+ {
+ mid[i] = (maxs[i] + mins[i]) * 0.5;
+ if (i == axis) continue;
+ if ((maxs[i] - mins[i]) * 0.5 > width)
+ width = (maxs[i] - mins[i]) * 0.5;
+ }
+
+ b = Brush_Alloc();
+
+ // create top face
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ f->planepts[2][(axis+1)%3] = mins[(axis+1)%3]; f->planepts[2][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[2][axis] = maxs[axis];
+ f->planepts[1][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[1][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[1][axis] = maxs[axis];
+ f->planepts[0][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[0][(axis+2)%3] = maxs[(axis+2)%3]; f->planepts[0][axis] = maxs[axis];
+
+ // create bottom face
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ f->planepts[0][(axis+1)%3] = mins[(axis+1)%3]; f->planepts[0][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[0][axis] = mins[axis];
+ f->planepts[1][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[1][(axis+2)%3] = mins[(axis+2)%3]; f->planepts[1][axis] = mins[axis];
+ f->planepts[2][(axis+1)%3] = maxs[(axis+1)%3]; f->planepts[2][(axis+2)%3] = maxs[(axis+2)%3]; f->planepts[2][axis] = mins[axis];
+
+ for (i=0 ; i<sides ; i++)
+ {
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ sv = sin (i*3.14159265*2/sides);
+ cv = cos (i*3.14159265*2/sides);
+
+ f->planepts[0][(axis+1)%3] = floor(mid[(axis+1)%3]+width*cv+0.5);
+ f->planepts[0][(axis+2)%3] = floor(mid[(axis+2)%3]+width*sv+0.5);
+ f->planepts[0][axis] = mins[axis];
+
+ f->planepts[1][(axis+1)%3] = f->planepts[0][(axis+1)%3];
+ f->planepts[1][(axis+2)%3] = f->planepts[0][(axis+2)%3];
+ f->planepts[1][axis] = maxs[axis];
+
+ f->planepts[2][(axis+1)%3] = floor(f->planepts[0][(axis+1)%3] - width*sv + 0.5);
+ f->planepts[2][(axis+2)%3] = floor(f->planepts[0][(axis+2)%3] + width*cv + 0.5);
+ f->planepts[2][axis] = maxs[axis];
+ }
+
+ Brush_AddToList (b, &selected_brushes);
+
+ Entity_LinkBrush (world_entity, b);
+
+ Brush_Build( b );
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+
+
+/*
+=============
+Brush_Free
+
+Frees the brush with all of its faces and display list.
+Unlinks the brush from whichever chain it is in.
+Decrements the owner entity's brushcount.
+Removes owner entity if this was the last brush
+unless owner is the world.
+Removes from groups
+=============
+*/
+void Brush_Free (brush_t *b, bool bRemoveNode)
+{
+ face_t *f, *next;
+ epair_t *ep, *enext;
+
+ // remove from group
+ if (bRemoveNode)
+ Group_RemoveBrush(b);
+
+ // free the patch if it's there
+ if (b->patchBrush)
+ {
+ Patch_Delete(b->pPatch);
+ }
+
+ // free faces
+ for (f=b->brush_faces ; f ; f=next)
+ {
+ next = f->next;
+ Face_Free( f );
+ }
+
+ // TTimo : free brush epairs
+ for (ep = b->epairs ; ep ; ep=enext )
+ {
+ enext = ep->next;
+ free (ep->key);
+ free (ep->value);
+ free (ep);
+ }
+
+ // unlink from active/selected list
+ if (b->next)
+ Brush_RemoveFromList (b);
+
+ // unlink from entity list
+ if (b->onext)
+ Entity_UnlinkBrush (b);
+
+ free (b);
+}
+
+/*
+=============
+Face_MemorySize
+=============
+*/
+int Face_MemorySize(face_t *f )
+{
+ int size = 0;
+
+ if (f->face_winding)
+ {
+// size += _msize(f->face_winding);
+ size += sizeof(vec3_t)*f->face_winding->numpoints+2*sizeof(int);
+ }
+// size += _msize(f);
+ size += sizeof(face_t);
+ return size;
+}
+
+/*
+=============
+Brush_MemorySize
+=============
+*/
+int Brush_MemorySize(brush_t *b)
+{
+ face_t *f;
+ epair_t *ep;
+ int size = 0;
+
+ //
+ if (b->patchBrush)
+ {
+ size += Patch_MemorySize(b->pPatch);
+ }
+ //
+ for (f = b->brush_faces; f; f = f->next)
+ {
+ size += Face_MemorySize(f);
+ }
+ //
+ for (ep = b->epairs; ep; ep = ep->next )
+ {
+// size += _msize(ep->key);
+ size += strlen(ep->key);
+// size += _msize(ep->value);
+ size += strlen(ep->value);
+// size += _msize(ep);
+ size += sizeof(epair_t);
+ }
+// size += _msize(b);
+ size += sizeof(brush_t);
+ return size;
+}
+
+
+/*
+============
+Brush_Clone
+
+Does NOT add the new brush to any lists
+============
+*/
+brush_t *Brush_Clone (brush_t *b)
+{
+ brush_t *n = NULL;
+ face_t *f, *nf;
+
+ if (b->patchBrush)
+ {
+ patchMesh_t *p = Patch_Duplicate(b->pPatch);
+ Brush_RemoveFromList(p->pSymbiot);
+ Entity_UnlinkBrush(p->pSymbiot);
+ n = p->pSymbiot;
+ }
+ else
+ {
+ n = Brush_Alloc();
+ n->numberId = g_nBrushId++;
+ n->owner = b->owner;
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ nf = Face_Clone( f );
+ nf->next = n->brush_faces;
+ n->brush_faces = nf;
+ }
+ }
+
+ return n;
+}
+
+/*
+============
+Brush_Clone
+
+Does NOT add the new brush to any lists
+============
+*/
+brush_t *Brush_FullClone(brush_t *b)
+{
+ brush_t *n = NULL;
+ face_t *f, *nf, *f2, *nf2;
+ int j;
+
+ if (b->patchBrush)
+ {
+ patchMesh_t *p = Patch_Duplicate(b->pPatch);
+ Brush_RemoveFromList(p->pSymbiot);
+ Entity_UnlinkBrush(p->pSymbiot);
+ n = p->pSymbiot;
+ n->owner = b->owner;
+ Brush_Build(n);
+ }
+ else
+ {
+ n = Brush_Alloc();
+ n->numberId = g_nBrushId++;
+ n->owner = b->owner;
+ VectorCopy(b->mins, n->mins);
+ VectorCopy(b->maxs, n->maxs);
+ //
+ for (f = b->brush_faces; f; f = f->next)
+ {
+ if (f->original) continue;
+ nf = Face_FullClone(f);
+ nf->next = n->brush_faces;
+ n->brush_faces = nf;
+ //copy all faces that have the original set to this face
+ for (f2 = b->brush_faces; f2; f2 = f2->next)
+ {
+ if (f2->original == f)
+ {
+ nf2 = Face_FullClone(f2);
+ nf2->next = n->brush_faces;
+ n->brush_faces = nf2;
+ //set original
+ nf2->original = nf;
+ }
+ }
+ }
+ for (nf = n->brush_faces; nf; nf = nf->next)
+ {
+ Face_SetColor(n, nf, 1.0);
+ if (nf->face_winding)
+ {
+ if (g_qeglobals.m_bBrushPrimitMode)
+ EmitBrushPrimitTextureCoordinates(nf,nf->face_winding);
+ else
+ {
+ for (j = 0; j < nf->face_winding->numpoints; j++)
+ EmitTextureCoordinates(nf->face_winding->points[j], nf->d_texture, nf);
+ }
+ }
+ }
+ }
+ return n;
+}
+
+ // FIXME - spog - finish this later..
+ /*
+bool Triangle_Ray(vec3_t origin, vec3_t dir, vec3_t p1, vec3_t p2, vec3_t p3)
+{
+ int i;
+ vec3_t v1, v2, normal[3];
+ float d;
+
+ //Sys_Printf("p1: %f %f %f\n",p1[0],p1[1],p1[2]);
+ //Sys_Printf("p2: %f %f %f\n",p2[0],p2[1],p2[2]);
+ //Sys_Printf("p3: %f %f %f\n",p3[0],p3[1],p3[2]);
+ //Sys_Printf("origin: %f %f %f\n",origin[0],origin[1],origin[2]);
+
+ // test ray against triangle
+ // get triangle plane normal
+ //VectorSubtract(p1, p2, v1);
+ //VectorSubtract(p1, p3, v2);
+ //CrossProduct(v1, v2, v1);
+ // check normal against direction
+ //if (DotProduct(dir, v1) >= 0)
+ //{
+ // generate cone normals
+ VectorSubtract(origin, p1, v1);
+ VectorSubtract(origin, p2, v2);
+ CrossProduct(v1, v2, normal[0]);
+ VectorSubtract(origin, p2, v1);
+ VectorSubtract(origin, p3, v2);
+ CrossProduct(v1, v2, normal[1]);
+ VectorSubtract(origin, p3, v1);
+ VectorSubtract(origin, p1, v2);
+ CrossProduct(v1, v2, normal[2]);
+ //}
+ //else
+ //{
+ // flip normals if triangle faces away
+ // Sys_Printf("flipped\n");
+ // VectorSubtract(origin, p1, v1);
+ // VectorSubtract(origin, p3, v2);
+ // CrossProduct(v1, v2, normal[0]);
+ // VectorSubtract(origin, p3, v1);
+ // VectorSubtract(origin, p2, v2);
+ // CrossProduct(v1, v2, normal[1]);
+ // VectorSubtract(origin, p2, v1);
+ // VectorSubtract(origin, p1, v2);
+ // CrossProduct(v1, v2, normal[2]);
+ //}
+
+ for (i=0; i<3; i++)
+ {
+ VectorNormalize(normal[i]);
+ //Sys_Printf("direction: %f %f %f\n",dir[0],dir[1],dir[2]);
+ //Sys_Printf("normal: %f %f %f\n",normal[i][0],normal[i][1],normal[i][2]);
+ d = DotProduct(dir, normal[i]);
+ //Sys_Printf("dotproduct: %f\n",d);
+ if (d < 0)
+ return false;
+ }
+ return true;
+}
+*/
+
+/*
+extern int Triangle_Ray(float orig[3], float dir[3], bool bCullBack,
+ float vert0[3], float vert1[3], float vert2[3],
+ double *t, double *u, double *v);
+
+bool Model_Ray(brush_t *b, vec3_t origin, vec3_t dir, double *t, double *u, double *v)
+{
+ bool bIntersect = false;
+ float tBest = FLT_MAX;
+ int i, j;
+ vec3_t xyz[3];
+ vec3_t vRay[2];
+
+ float angle = FloatForKey (b->owner, "angle"); // FIXME: should be set when this entity key is set
+
+ VectorSubtract (origin, b->owner->origin, vRay[0]);
+ VectorCopy (dir, vRay[1]);
+
+ if (angle > 0)
+ {
+ int i;
+ float s, c;
+ float x, y;
+
+ s = sin (-angle/180*Q_PI);
+ c = cos (-angle/180*Q_PI);
+
+ for (i=0; i<2; i++)
+ {
+ x = vRay[i][0];
+ y = vRay[i][1];
+ vRay[i][0] = (x * c) - (y * s);
+ vRay[i][1] = (x * s) + (y * c);
+ }
+ }
+
+ entitymodel *model = b->owner->md3Class->model;
+
+ while (model != NULL)
+ {
+ for (i = 0; i < model->nTriCount; i++)
+ {
+ for (j = 0; j < 3; j++)
+ VectorCopy(model->pVertList[model->pTriList[i].indexes[j]].v, xyz[j]);
+
+ if (Triangle_Ray(vRay[0], vRay[1], true, xyz[0], xyz[2], xyz[1], t, u, v))
+ {
+ bIntersect = true;
+ if (*t < tBest)
+ tBest = *t;
+ }
+ }
+ model = model->pNext;
+ }
+ if (bIntersect)
+ {
+ *t = tBest;
+ return true;
+ }
+ else
+ {
+ *t = 0;
+ return false;
+ }
+}
+*/
+
+/*
+==============
+Brush_Ray
+
+Itersects a ray with a brush
+Returns the face hit and the distance along the ray the intersection occured at
+Returns NULL and 0 if not hit at all
+
+http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=556
+==============
+*/
+extern bool Patch_Ray(patchMesh_t *patch, vec3_t origin, vec3_t dir, double *t, double *u, double *v);
+face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist, int nFlags)
+{
+ face_t *f, *firstface = NULL;
+ vec3_t p1, p2;
+ float frac, d1, d2;
+ int i;
+
+ if (b->owner->eclass->fixedsize
+ && b->owner->model.pSelect
+ && !(!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY))
+ && g_PrefsDlg.m_nEntityShowState != ENTITY_BOX)
+ {
+ ray_t ray_local;
+ vec_t dist_local = FLT_MAX;
+ ray_construct_for_vec3(&ray_local, origin, dir);
+ if (b->owner->model.pSelect->TestRay(&ray_local, &dist_local))
+ {
+ *dist = dist_local;
+ return b->brush_faces;
+ }
+ else
+ {
+ *dist = 0.0f;
+ return NULL;
+ }
+ }
+
+ VectorCopy (origin, p1);
+ for (i=0 ; i<3 ; i++)
+ p2[i] = p1[i] + dir[i]*2*g_MaxWorldCoord;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+ d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+ if (d1 >= 0 && d2 >= 0)
+ {
+ *dist = 0;
+ return NULL; // ray is on front side of face
+ }
+ if (d1 <=0 && d2 <= 0)
+ continue;
+ // clip the ray to the plane
+ frac = d1 / (d1 - d2);
+ if (d1 > 0)
+ {
+ firstface = f;
+ for (i=0 ; i<3 ; i++)
+ p1[i] = p1[i] + frac *(p2[i] - p1[i]);
+ }
+ else
+ {
+ for (i=0 ; i<3 ; i++)
+ p2[i] = p1[i] + frac *(p2[i] - p1[i]);
+ }
+ }
+
+ // find distance p1 is along dir
+ VectorSubtract (p1, origin, p1);
+ d1 = DotProduct (p1, dir);
+
+ *dist = d1;
+
+ // new test stuff for patches
+ if (!g_PrefsDlg.m_bPatchBBoxSelect && b->patchBrush)
+ {
+ double t, u, v; // t is the distance from origin to point-of-intersection.. er.. i don't know what u and v are
+ if (!Patch_Ray(b->pPatch, origin, dir, &t, &u, &v))
+ {
+ *dist = 0;
+ return NULL;
+ }
+ else
+ {
+ *dist = (float)t;
+ //Sys_Printf("t: %f, u: %f, v: %f\n", t, u, v);
+ }
+ }
+
+ // IMPORTANT NOTE:
+ // modifications to the discarding code here should be matched in the selection code
+ // see Brush_Draw
+
+ // do some last minute filtering
+ if (firstface && nFlags & SF_CAMERA)
+ {
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK)
+ {
+ if (strstr(firstface->texdef.GetName(), "caulk"))
+ {
+ *dist = 0;
+ return NULL;
+ }
+ }
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_BOTCLIP)
+ {
+ if (strstr(firstface->texdef.GetName(), "botclip") || strstr(firstface->texdef.GetName(), "clipmonster"))
+ {
+ *dist = 0;
+ return NULL;
+ }
+ }
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)
+ {
+ if (strstr(firstface->texdef.GetName(), "clip"))
+ {
+ *dist = 0;
+ return NULL;
+ }
+ }
+ }
+
+ return firstface;
+}
+
+//PGM
+face_t *Brush_Point (vec3_t origin, brush_t *b)
+{
+ face_t *f;
+ float d1;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ d1 = DotProduct (origin, f->plane.normal) - f->plane.dist;
+ if (d1 > 0)
+ {
+ return NULL; // point is on front side of face
+ }
+ }
+
+ return b->brush_faces;
+}
+//PGM
+
+
+void Brush_AddToList (brush_t *b, brush_t *blist)
+{
+ if (b->next || b->prev)
+ Error ("Brush_AddToList: already linked");
+
+ if (blist == &selected_brushes || blist == &active_brushes)
+ {
+ if (b->patchBrush && blist == &selected_brushes)
+ {
+ Patch_Select(b->pPatch);
+ }
+ }
+ b->next = blist->next;
+ blist->next->prev = b;
+ blist->next = b;
+ b->prev = blist;
+
+ // TTimo messaging
+ DispatchRadiantMsg( RADIANT_SELECTION );
+}
+
+void Brush_RemoveFromList (brush_t *b)
+{
+ if (!b->next || !b->prev)
+ Error ("Brush_RemoveFromList: not linked");
+
+ if (b->patchBrush)
+ {
+ Patch_Deselect(b->pPatch);
+ }
+ b->next->prev = b->prev;
+ b->prev->next = b->next;
+ b->next = b->prev = NULL;
+}
+
+/*
+===============
+SetFaceTexdef
+
+Doesn't set the curve flags
+
+NOTE : ( TTimo )
+ never trust f->d_texture here, f->texdef and f->d_texture are out of sync when called by Brush_SetTexture
+ use Texture_ForName() to find the right shader
+ FIXME : send the right shader ( qtexture_t * ) in the parameters ?
+
+TTimo: surface plugin, added an IPluginTexdef* parameter
+ if not NULL, get ->Copy() of it into the face ( and remember to hook )
+ if NULL, ask for a default
+
+ TTimo - shader code cleanup
+ added IShader* parameter
+ ===============
+*/
+void SetFaceTexdef2 (brush_t *b, face_t *f, IShader *pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pPlugTexdef) {
+ int oldFlags;
+ int oldContents;
+ face_t *tf;
+
+ oldFlags = f->texdef.flags;
+ oldContents = f->texdef.contents;
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ f->texdef = *texdef;
+ ConvertTexMatWithQTexture( brushprimit_texdef, NULL, &f->brushprimit_texdef, QERApp_Shader_ForName( f->texdef.GetName() )->getTexture() );
+ }
+ else
+ if (bFitScale)
+ {
+ f->texdef = *texdef;
+ // fit the scaling of the texture on the actual plane
+ vec3_t p1,p2,p3; // absolute coordinates
+ // compute absolute coordinates
+ ComputeAbsolute(f,p1,p2,p3);
+ // compute the scale
+ vec3_t vx,vy;
+ VectorSubtract(p2,p1,vx);
+ VectorNormalize(vx, vx);
+ VectorSubtract(p3,p1,vy);
+ VectorNormalize(vy, vy);
+ // assign scale
+ VectorScale(vx,texdef->scale[0],vx);
+ VectorScale(vy,texdef->scale[1],vy);
+ VectorAdd(p1,vx,p2);
+ VectorAdd(p1,vy,p3);
+ // compute back shift scale rot
+ AbsoluteToLocal(f->plane,f,p1,p2,p3);
+ }
+ else
+ f->texdef = *texdef;
+ f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);
+ f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);
+
+ // if this is a curve face, set all other curve faces to the same texdef
+ if (f->texdef.flags & SURF_CURVE)
+ {
+ for (tf = b->brush_faces ; tf ; tf = tf->next)
+ {
+ if (tf->texdef.flags & SURF_CURVE)
+ tf->texdef = f->texdef;
+ }
+ }
+}
+
+/*
+===============
+SetFaceTexdef
+
+Doesn't set the curve flags
+
+NOTE : ( TTimo )
+ never trust f->d_texture here, f->texdef and f->d_texture are out of sync when called by Brush_SetTexture
+ use Texture_ForName() to find the right shader
+ FIXME : send the right shader ( qtexture_t * ) in the parameters ?
+
+ TTimo: surface plugin, added an IPluginTexdef* parameter
+ if not NULL, get ->Copy() of it into the face ( and remember to hook )
+ if NULL, ask for a default
+===============
+*/
+void SetFaceTexdef (face_t *f, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pPlugTexdef) {
+ int oldFlags;
+ int oldContents;
+
+ oldFlags = f->texdef.flags;
+ oldContents = f->texdef.contents;
+
+ if(strcmp(f->texdef.GetName(), texdef->GetName()) != 0) // set shader here instead of Brush_Build
+ Face_SetShader(f, texdef->GetName());
+
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ f->texdef = *texdef;
+ ConvertTexMatWithQTexture( brushprimit_texdef, NULL, &f->brushprimit_texdef, QERApp_Shader_ForName( f->texdef.GetName() )->getTexture() );
+ }
+ else
+ {
+ if (bFitScale)
+ {
+ f->texdef = *texdef;
+ // fit the scaling of the texture on the actual plane
+ vec3_t p1,p2,p3; // absolute coordinates
+ // compute absolute coordinates
+ ComputeAbsolute(f,p1,p2,p3);
+ // compute the scale
+ vec3_t vx,vy;
+ VectorSubtract(p2,p1,vx);
+ VectorNormalize(vx, vx);
+ VectorSubtract(p3,p1,vy);
+ VectorNormalize(vy, vy);
+ // assign scale
+ VectorScale(vx,texdef->scale[0],vx);
+ VectorScale(vy,texdef->scale[1],vy);
+ VectorAdd(p1,vx,p2);
+ VectorAdd(p1,vy,p3);
+ // compute back shift scale rot
+ AbsoluteToLocal(f->plane,f,p1,p2,p3);
+ }
+ else
+ {
+ f->texdef = *texdef;
+ }
+ }
+ f->texdef.flags = (f->texdef.flags & ~SURF_KEEP) | (oldFlags & SURF_KEEP);
+ f->texdef.contents = (f->texdef.contents & ~CONTENTS_KEEP) | (oldContents & CONTENTS_KEEP);
+}
+
+#ifdef _DEBUG
+void Brush_SetTexture2 (brush_t *b, IShader *pShader, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pTexdef)
+{
+ for (face_t* f = b->brush_faces ; f ; f = f->next)
+ SetFaceTexdef2 (b, f, pShader, texdef, brushprimit_texdef, bFitScale, pTexdef);
+ Brush_Build( b );
+ if (b->patchBrush)
+ {
+ Patch_SetTexture(b->pPatch, texdef, pTexdef );
+ b->bFiltered = FilterBrush( b );
+ }
+}
+#endif
+
+void Brush_SetTexture (brush_t *b, texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef* pTexdef)
+{
+ for (face_t* f = b->brush_faces ; f ; f = f->next)
+ SetFaceTexdef (f, texdef, brushprimit_texdef, bFitScale, pTexdef);
+ Brush_Build( b );
+ if (b->patchBrush)
+ {
+ Patch_SetTexture(b->pPatch, texdef, pTexdef );
+ b->bFiltered = FilterBrush( b );
+ }
+}
+
+
+qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f)
+{
+ float d1, d2, fr;
+ int i;
+ float *v;
+
+ d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+ d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+
+ if (d1 >= 0 && d2 >= 0)
+ return false; // totally outside
+ if (d1 <= 0 && d2 <= 0)
+ return true; // totally inside
+
+ fr = d1 / (d1 - d2);
+
+ if (d1 > 0)
+ v = p1;
+ else
+ v = p2;
+
+ for (i=0 ; i<3 ; i++)
+ v[i] = p1[i] + fr*(p2[i] - p1[i]);
+
+ return true;
+}
+
+
+int AddPlanept (float *f)
+{
+ int i;
+
+ for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+ if (g_qeglobals.d_move_points[i] == f)
+ return 0;
+ g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f;
+ return 1;
+}
+
+/*
+==============
+Brush_SelectFaceForDragging
+
+Adds the faces planepts to move_points, and
+rotates and adds the planepts of adjacent face if shear is set
+==============
+*/
+void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear)
+{
+ int i;
+ face_t *f2;
+ winding_t *w;
+ float d;
+ brush_t *b2;
+ int c;
+
+ if (b->owner->eclass->fixedsize)
+ return;
+
+ c = 0;
+ for (i=0 ; i<3 ; i++)
+ c += AddPlanept (f->planepts[i]);
+ if (c == 0)
+ return; // already completely added
+
+ // select all points on this plane in all brushes the selection
+ for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next)
+ {
+ if (b2 == b)
+ continue;
+ for (f2=b2->brush_faces ; f2 ; f2=f2->next)
+ {
+ for (i=0 ; i<3 ; i++)
+ if (fabs(DotProduct(f2->planepts[i], f->plane.normal)
+ -f->plane.dist) > ON_EPSILON)
+ break;
+ if (i==3)
+ { // move this face as well
+ Brush_SelectFaceForDragging (b2, f2, shear);
+ break;
+ }
+ }
+ }
+
+
+ // if shearing, take all the planes adjacent to
+ // selected faces and rotate their points so the
+ // edge clipped by a selcted face has two of the points
+ if (!shear)
+ return;
+
+ for (f2=b->brush_faces ; f2 ; f2=f2->next)
+ {
+ if (f2 == f)
+ continue;
+ w = Brush_MakeFaceWinding (b, f2);
+ if (!w)
+ continue;
+
+ // any points on f will become new control points
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ d = DotProduct (w->points[i], f->plane.normal)
+ - f->plane.dist;
+ if (d > -ON_EPSILON && d < ON_EPSILON)
+ break;
+ }
+
+ //
+ // if none of the points were on the plane,
+ // leave it alone
+ //
+ if (i != w->numpoints)
+ {
+ if (i == 0)
+ { // see if the first clockwise point was the
+ // last point on the winding
+ d = DotProduct (w->points[w->numpoints-1]
+ , f->plane.normal) - f->plane.dist;
+ if (d > -ON_EPSILON && d < ON_EPSILON)
+ i = w->numpoints - 1;
+ }
+
+ AddPlanept (f2->planepts[0]);
+
+ VectorCopy (w->points[i], f2->planepts[0]);
+ if (++i == w->numpoints)
+ i = 0;
+
+ // see if the next point is also on the plane
+ d = DotProduct (w->points[i]
+ , f->plane.normal) - f->plane.dist;
+ if (d > -ON_EPSILON && d < ON_EPSILON)
+ AddPlanept (f2->planepts[1]);
+
+ VectorCopy (w->points[i], f2->planepts[1]);
+ if (++i == w->numpoints)
+ i = 0;
+
+ // the third point is never on the plane
+
+ VectorCopy (w->points[i], f2->planepts[2]);
+ }
+
+ free(w);
+ }
+}
+
+/*
+==============
+Brush_SideSelect
+
+The mouse click did not hit the brush, so grab one or more side
+planes for dragging
+==============
+*/
+void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir
+ , qboolean shear)
+{
+ face_t *f, *f2;
+ vec3_t p1, p2;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ VectorCopy (origin, p1);
+ VectorMA (origin, 2*g_MaxWorldCoord, dir, p2);
+
+ for (f2=b->brush_faces ; f2 ; f2=f2->next)
+ {
+ if (f2 == f)
+ continue;
+ ClipLineToFace (p1, p2, f2);
+ }
+
+ if (f2)
+ continue;
+
+ if (VectorCompare (p1, origin))
+ continue;
+ if (ClipLineToFace (p1, p2, f))
+ continue;
+
+ Brush_SelectFaceForDragging (b, f, shear);
+ }
+}
+
+bool g_bBuildWindingsNoTexBuild = false;
+
+void Brush_SetBuildWindingsNoTexBuild(bool bBuild)
+{
+ g_bBuildWindingsNoTexBuild = bBuild;
+}
+
+// TTimo: don't rebuild pShader and d_texture if it doesn't seem necessary
+// saves quite a lot of time, but on the other hand we've gotta make sure we clean the d_texture in some cases
+// ie when we want to update a shader
+// default will make Radiant rebuild the texture, but it can be turned off by setting the flag g_bBuildWindingsNoTexBuild
+void Brush_BuildWindings( brush_t *b, bool bSnap )
+{
+ winding_t *w;
+ face_t *face;
+ vec_t v;
+
+ if (bSnap)
+ Brush_SnapPlanepts( b );
+
+ // clear the mins/maxs bounds
+ b->mins[0] = b->mins[1] = b->mins[2] = 99999;
+ b->maxs[0] = b->maxs[1] = b->maxs[2] = -99999;
+
+ Brush_MakeFacePlanes (b);
+
+ face = b->brush_faces;
+
+ float fCurveColor = 1.0;
+
+ for ( ; face ; face=face->next)
+ {
+ int i, j;
+ free(face->face_winding);
+ w = face->face_winding = Brush_MakeFaceWinding (b, face);
+
+ if (!g_bBuildWindingsNoTexBuild || !face->d_texture)
+ {
+#ifdef _DEBUG
+ // if there's no d_texture, then we expect pShader to be empty
+ if (!face->d_texture && face->pShader)
+ Sys_FPrintf(SYS_ERR, "ERROR: unexpected face->pShader != NULL with face->d_texture == NULL in Brush_BuildWindings\n");
+#endif
+ if ((!face->d_texture && !face->pShader) || !face->pShader)
+ {
+ // NOTE TTimo
+ // patch 84 for bug 253 doesn't dec ref the potential face->pShader
+ // add a debug check to make sure this is actually not necessary
+#ifdef _DEBUG
+ if (face->pShader)
+ {
+ Sys_FPrintf(SYS_ERR, "ERROR: face->pShader != NULL in Brush_BuildWindings\n");
+ }
+#endif
+ face->pShader = QERApp_Shader_ForName( face->texdef.GetName() );
+ face->pShader->IncRef();
+ face->d_texture = face->pShader->getTexture();
+ }
+ }
+
+ if (!w)
+ continue;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ // add to bounding box
+ for (j=0 ; j<3 ; j++)
+ {
+ v = w->points[i][j];
+ if (v > b->maxs[j])
+ b->maxs[j] = v;
+ if (v < b->mins[j])
+ b->mins[j] = v;
+ }
+ }
+ Face_SetColor (b, face, fCurveColor);
+
+ fCurveColor -= .10f;
+ if (fCurveColor <= 0)
+ fCurveColor = 1.0f;
+
+ // computing ST coordinates for the windings
+ if (g_qeglobals.m_bBrushPrimitMode)
+ {
+ if (g_qeglobals.bNeedConvert)
+ {
+ // we have parsed old brushes format and need conversion
+ // convert old brush texture representation to new format
+ FaceToBrushPrimitFace(face);
+#ifdef _DEBUG
+ // use old texture coordinates code to check against
+ for (i=0 ; i<w->numpoints ; i++)
+ EmitTextureCoordinates( w->points[i], face->d_texture, face);
+#endif
+ }
+ // use new texture representation to compute texture coordinates
+ // in debug mode we will check against old code and warn if there are differences
+ EmitBrushPrimitTextureCoordinates(face,w);
+ }
+ else
+ {
+ if (g_qeglobals.bNeedConvert)
+ {
+ BrushPrimitFaceToFace(face);
+/*
+ // we have parsed brush primitives and need conversion back to standard format
+ // NOTE: converting back is a quick hack, there's some information lost and we can't do anything about it
+ // FIXME: if we normalize the texture matrix to a standard 2x2 size, we end up with wrong scaling
+ // I tried various tweaks, no luck .. seems shifting is lost
+ brushprimit_texdef_t aux;
+ ConvertTexMatWithQTexture( &face->brushprimit_texdef, face->d_texture, &aux, NULL );
+ TexMatToFakeTexCoords( aux.coords, face->texdef.shift, &face->texdef.rotate, face->texdef.scale );
+ face->texdef.scale[0]/=2.0;
+ face->texdef.scale[1]/=2.0;
+*/
+ }
+ for (i=0 ; i<w->numpoints ; i++)
+ EmitTextureCoordinates( w->points[i], face->d_texture, face);
+ }
+ }
+}
+
+/*
+==================
+Brush_RemoveEmptyFaces
+
+Frees any overconstraining faces
+==================
+*/
+void Brush_RemoveEmptyFaces ( brush_t *b )
+{
+ face_t *f, *next;
+
+ f = b->brush_faces;
+ b->brush_faces = NULL;
+
+ for ( ; f ; f=next)
+ {
+ next = f->next;
+ if (!f->face_winding)
+ Face_Free (f);
+ else
+ {
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+ }
+
+ }
+}
+
+void Brush_SnapToGrid(brush_t *pb)
+{
+ face_t *f;
+ vec3_t temp;
+ vec3_t diff[2];
+ int mult[3];
+ int i, j, n;
+ // TTimo: some brushes are "special" and should not be snapped
+ // specially fixed-size entity ones
+ if (pb->owner->eclass->fixedsize)
+ {
+ // save current origin
+ VectorCopy (pb->owner->origin, temp);
+ // snap the origin
+ VectorFSnap(pb->owner->origin, g_qeglobals.d_gridsize);
+ // return if amount is zero
+ if (VectorCompare (pb->owner->origin, temp))
+ return;
+ // transform brush faces same amount
+ VectorSubtract (pb->owner->origin, temp, temp);
+ for (f = pb->brush_faces; f; f = f->next)
+ {
+ for (i=0 ; i<3 ; i++)
+ VectorAdd (f->planepts[i], temp, f->planepts[i]);
+ }
+ }
+ else
+ {
+ for (f = pb->brush_faces ; f; f = f->next)
+ {
+ for (j=0; j<2; j++)
+ {
+ // spog - move planepts apart just far enough to avoid snapping two together
+ VectorSubtract (f->planepts[j+1], f->planepts[j], diff[j]);
+ for (i=0; i<3; i++)
+ {
+ if (diff[j][i] == 0.0f)
+ mult[i] = 2; // next value up from 1
+ else // multiplier = gridsize / component difference, rounded up
+ mult[i] = (int)ceil(fabs(g_qeglobals.d_gridsize / diff[j][i]));
+ }
+
+ if (mult[0] > 1 && mult[1] > 1 && mult[2] > 1) // if all multipliers are greater than 1
+ {
+ n = (mult[0] >= mult[1] && mult[0] >= mult[2]) ? 0 : (mult[1] >= mult[0] && mult[1] >= mult[2]) ? 1 : 2;
+ for (i=0; i<3; i++)
+ diff[j][i] *= mult[n]; // multiply difference by multiplier of smallest component
+ }
+ VectorAdd (f->planepts[j], diff[j], f->planepts[j+1]);
+ }
+
+ for (i=0; i<3; i++)
+ VectorFSnap(f->planepts[i], g_qeglobals.d_gridsize);
+
+ }
+ }
+ Brush_Build(pb,true,true,false,false); // don't filter
+}
+
+void Brush_Rotate(brush_t *b, vec3_t vAngle, vec3_t vOrigin, bool bBuild)
+{
+ for (face_t* f=b->brush_faces ; f ; f=f->next)
+ {
+ for (int i=0 ; i<3 ; i++)
+ {
+ VectorRotateOrigin (f->planepts[i], vAngle, vOrigin, f->planepts[i]);
+ }
+ }
+ if (bBuild)
+ {
+ Brush_Build(b,false,false,false,false); // don't filter
+ }
+}
+
+void Brush_Center(brush_t *b, vec3_t vNewCenter)
+{
+ vec3_t vMid;
+ // get center of the brush
+ for (int j = 0; j < 3; j++)
+ {
+ vMid[j] = b->mins[j] + fabs((b->maxs[j] - b->mins[j]) * 0.5);
+ }
+ // calc distance between centers
+ VectorSubtract(vNewCenter, vMid, vMid);
+ Brush_Move(b, vMid, true);
+
+}
+
+void Brush_Resize(brush_t *b, vec3_t vMin, vec3_t vMax)
+{
+ face_t *f;
+ texdef_t texdef;
+ int i;
+ short box[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } };
+
+ for (i=0 ; i<3 ; i++)
+ if (vMax[i] < vMin[i])
+ Error ("Brush_Resize: invalid input");
+
+ if(b->brush_faces != NULL)
+ texdef = b->brush_faces->texdef;
+ else
+ texdef = g_qeglobals.d_texturewin.texdef;
+
+ while (b->brush_faces != NULL)
+ {
+ f = b->brush_faces->next;
+ Face_Free(b->brush_faces);
+ b->brush_faces = f;
+ }
+
+ for(i=0; i<3; i++)
+ {
+ f = b->brush_faces;
+ b->brush_faces = Face_Alloc();
+ b->brush_faces->next = f;
+ f = b->brush_faces;
+ f->texdef = texdef;
+ VectorCopy(vMax, f->planepts[0]);
+ VectorCopy(vMax, f->planepts[1]);
+ VectorCopy(vMax, f->planepts[2]);
+ f->planepts[2][box[i][0]] = vMin[box[i][0]];
+ f->planepts[1][box[i][1]] = vMin[box[i][1]];
+ }
+ for(i=0; i<3; i++)
+ {
+ f = b->brush_faces;
+ b->brush_faces = Face_Alloc();
+ b->brush_faces->next = f;
+ f = b->brush_faces;
+ f->texdef = texdef;
+ VectorCopy(vMin, f->planepts[0]);
+ VectorCopy(vMin, f->planepts[1]);
+ VectorCopy(vMin, f->planepts[2]);
+ f->planepts[1][box[i][0]] = vMax[box[i][0]];
+ f->planepts[2][box[i][1]] = vMax[box[i][1]];
+ }
+}
+
+void FacingVectors (entity_t *e, vec3_t forward, vec3_t right, vec3_t up)
+{
+ int angleVal;
+ vec3_t angles;
+
+ angleVal = IntForKey(e, "angle");
+ if (angleVal == -1) // up
+ {
+ VectorSet(angles, 270, 0, 0);
+ }
+ else if(angleVal == -2) // down
+ {
+ VectorSet(angles, 90, 0, 0);
+ }
+ else
+ {
+ VectorSet(angles, 0, angleVal, 0);
+ }
+
+ AngleVectors(angles, forward, right, up);
+}
+
+void Brush_DrawFacingAngle (brush_t *b, entity_t *e)
+{
+ vec3_t forward, right, up;
+ vec3_t endpoint, tip1, tip2;
+ vec3_t start;
+ float dist;
+
+ VectorAdd(e->brushes.onext->mins, e->brushes.onext->maxs, start);
+ VectorScale(start, 0.5, start);
+ dist = (b->maxs[0] - start[0]) * 2.5;
+
+ FacingVectors (e, forward, right, up);
+ VectorMA (start, dist, forward, endpoint);
+
+ dist = (b->maxs[0] - start[0]) * 0.5;
+ VectorMA (endpoint, -dist, forward, tip1);
+ VectorMA (tip1, -dist, up, tip1);
+ VectorMA (tip1, 2*dist, up, tip2);
+
+ qglColor4f (1, 1, 1, 1);
+ qglLineWidth (4);
+ qglBegin (GL_LINES);
+ qglVertex3fv (start);
+ qglVertex3fv (endpoint);
+ qglVertex3fv (endpoint);
+ qglVertex3fv (tip1);
+ qglVertex3fv (endpoint);
+ qglVertex3fv (tip2);
+ qglEnd ();
+ qglLineWidth (1);
+}
+
+void Brush_FaceDraw(face_t *face, int nGLState)
+{
+ const winding_t *w = face->face_winding;
+ if (w == NULL) return;
+ if (nGLState & DRAW_GL_LIGHTING && g_PrefsDlg.m_bGLLighting)
+ qglNormal3fv(face->plane.normal);
+ /*
+ if (mode & DRAW_GL_TEXTURE_2D)
+ qglTexCoordPointer(2, GL_FLOAT, 5, &w->points[3]);
+ qglVertexPointer(3, GL_FLOAT, 5, w->points);
+
+ if (mode & DRAW_GL_FILL)
+ qglDrawArrays(GL_TRIANGLE_FAN, 0, w->numpoints);
+ else
+ qglDrawArrays(GL_POLYGON, 0, w->numpoints);
+ */
+
+ if (nGLState & DRAW_GL_FILL)
+ qglBegin(GL_TRIANGLE_FAN);
+ else
+ qglBegin(GL_POLYGON);
+
+ for (int i=0 ; i<w->numpoints ; i++)
+ {
+ if (nGLState & DRAW_GL_TEXTURE_2D)
+ qglTexCoord2fv( &w->points[i][3] );
+ qglVertex3fv(w->points[i]);
+ }
+ qglEnd();
+}
+
+void Brush_Draw(brush_t *b)
+{
+ face_t *face;
+ int order;
+ qtexture_t *prev = 0;
+ winding_t *w;
+
+ int nDrawMode = g_pParentWnd->GetCamWnd()->Camera()->draw_mode;
+ int nGLState = g_pParentWnd->GetCamWnd()->Camera()->draw_glstate;
+
+ GLfloat material[4], identity[4];
+ VectorSet(identity, 0.8f, 0.8f, 0.8f);
+ IShader *pShader;
+ qglPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
+ qglDisableClientState(GL_NORMAL_ARRAY);
+
+ // guarantee the texture will be set first
+ bool bTrans;
+ prev = NULL;
+ for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+ {
+ w = face->face_winding;
+ if (!w)
+ {
+ continue; // freed face
+ }
+
+ bTrans = (face->pShader->getFlags() & QER_TRANS);
+
+ if (bTrans && !(nGLState & DRAW_GL_BLEND))
+ continue;
+ if (!bTrans && nGLState & DRAW_GL_BLEND)
+ continue;
+
+ // IMPORTANT NOTE:
+ // modifications to the discarding code here should be matched in the selection code
+ // see Brush_Ray
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK)
+ {
+ if (strstr(face->texdef.GetName(), "caulk"))
+ continue;
+ }
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_BOTCLIP)
+ {
+ if (strstr(face->texdef.GetName(), "botclip") || strstr(face->texdef.GetName(), "clipmonster"))
+ continue;
+ }
+
+ if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)
+ {
+ if (strstr(face->texdef.GetName(), "clip"))
+ continue;
+ }
+
+ if (nGLState & DRAW_GL_TEXTURE_2D && face->d_texture->name[0] == '(')
+ {
+ prev = NULL;
+ qglDisable(GL_TEXTURE_2D);
+ }
+ else if (nGLState & DRAW_GL_TEXTURE_2D && (nDrawMode == cd_texture || nDrawMode == cd_light) && face->d_texture != prev)
+ {
+ // set the texture for this face
+ prev = face->d_texture;
+ qglBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );
+ }
+
+ if (nGLState & DRAW_GL_LIGHTING && !g_PrefsDlg.m_bGLLighting)
+ {
+ if (!b->owner->eclass->fixedsize)
+ material[3] = face->pShader->getTrans();
+ else
+ material[3] = 1;
+ VectorCopy(face->d_color, material);
+
+ if (nGLState & DRAW_GL_TEXTURE_2D)
+ qglColor4f(face->d_shade, face->d_shade, face->d_shade, material[3]);
+ else
+ qglColor4fv(material);
+ }
+ else if (!b->owner->eclass->fixedsize)
+ {
+ pShader = face->pShader;
+ VectorCopy(pShader->getTexture()->color, material);
+ material[3] = identity[3] = pShader->getTrans();
+
+ if (nGLState & DRAW_GL_TEXTURE_2D)
+ qglColor4fv(identity);
+ else
+ qglColor4fv(material);
+ }
+
+ // draw the polygon
+
+ Brush_FaceDraw(face, nGLState);
+ }
+ qglPopClientAttrib();
+}
+
+void Face_Draw( face_t *f )
+{
+ int i;
+
+ if ( f->face_winding == 0 )
+ return;
+ qglBegin(GL_POLYGON);
+ for ( i = 0 ; i < f->face_winding->numpoints; i++)
+ qglVertex3fv( f->face_winding->points[i] );
+ qglEnd();
+}
+
+entity_t *FindEntity(const char *pszKey, const char *pszValue)
+{
+ entity_t *pe;
+
+ pe = entities.next;
+
+ for (; pe != NULL && pe != &entities ; pe = pe->next)
+ {
+ if (!strcmp(ValueForKey(pe, pszKey), pszValue))
+ return pe;
+ }
+
+ return NULL;
+}
+
+void Brush_DrawXY(brush_t *b, int nViewType)
+{
+ face_t *face;
+ int order;
+ winding_t *w;
+ int i;
+
+ if (b->patchBrush)
+ {
+ Patch_DrawXY(b->pPatch);
+ if (!g_bPatchShowBounds)
+ return;
+ }
+
+ if (b->owner->eclass->fixedsize)
+ {
+ if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT))
+ {
+#if 1 // requires vertex arrays enabled
+ DrawLight(b->owner, DRAW_GL_WIRE, (IsBrushSelected(b)) ? g_PrefsDlg.m_nLightRadiuses : 0, nViewType);
+#else
+ vec3_t vCorners[4];
+ float fMid = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2;
+
+ vCorners[0][0] = b->mins[0];
+ vCorners[0][1] = b->mins[1];
+ vCorners[0][2] = fMid;
+
+ vCorners[1][0] = b->mins[0];
+ vCorners[1][1] = b->maxs[1];
+ vCorners[1][2] = fMid;
+
+ vCorners[2][0] = b->maxs[0];
+ vCorners[2][1] = b->maxs[1];
+ vCorners[2][2] = fMid;
+
+ vCorners[3][0] = b->maxs[0];
+ vCorners[3][1] = b->mins[1];
+ vCorners[3][2] = fMid;
+
+ vec3_t vTop, vBottom;
+
+ vTop[0] = b->mins[0] + ((b->maxs[0] - b->mins[0]) / 2);
+ vTop[1] = b->mins[1] + ((b->maxs[1] - b->mins[1]) / 2);
+ vTop[2] = b->maxs[2];
+
+ VectorCopy(vTop, vBottom);
+ vBottom[2] = b->mins[2];
+
+ qglBegin(GL_LINES);
+ qglVertex3fv(vTop);
+ qglVertex3fv(vCorners[0]);
+ qglVertex3fv(vTop);
+ qglVertex3fv(vCorners[1]);
+ qglVertex3fv(vTop);
+ qglVertex3fv(vCorners[2]);
+ qglVertex3fv(vTop);
+ qglVertex3fv(vCorners[3]);
+ qglEnd();
+
+ qglBegin(GL_LINES);
+ qglVertex3fv(vBottom);
+ qglVertex3fv(vCorners[0]);
+ qglVertex3fv(vBottom);
+ qglVertex3fv(vCorners[1]);
+ qglVertex3fv(vBottom);
+ qglVertex3fv(vCorners[2]);
+ qglVertex3fv(vBottom);
+ qglVertex3fv(vCorners[3]);
+ qglEnd();
+
+ qglBegin(GL_LINE_LOOP);
+ qglVertex3fv(vCorners[0]);
+ qglVertex3fv(vCorners[1]);
+ qglVertex3fv(vCorners[2]);
+ qglVertex3fv(vCorners[3]);
+ qglEnd();
+#endif
+ DrawBrushEntityName (b);
+ return;
+ }
+ else if (b->owner->model.pRender && !(!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY)))
+ {
+ qglPushAttrib(GL_CURRENT_BIT); // save brush colour
+ qglColor3fv(b->owner->eclass->color);
+ if( g_PrefsDlg.m_nEntityShowState != ENTITY_BOX )
+ b->owner->model.pRender->Draw(DRAW_GL_WIRE, DRAW_RF_XY);
+ aabb_draw(b->owner->model.pRender->GetAABB(), DRAW_GL_WIRE);
+ qglPopAttrib();
+ return;
+ }
+ //}
+ }
+
+ for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+ {
+ // moved so check occurs earlier
+ w = face->face_winding;
+ if (!w)
+ continue;
+ // only draw polygons facing in a direction we care about
+ if (nViewType == XY)
+ {
+ if (face->plane.normal[2] <= 0)
+ continue;
+ }
+ else
+ {
+ if (nViewType == XZ)
+ {
+ if (face->plane.normal[1] >= 0) // stop axes being mirrored
+ continue;
+ }
+ else
+ {
+ if (face->plane.normal[0] <= 0)
+ continue;
+ }
+ }
+
+ // draw the polygon
+ qglBegin(GL_LINE_LOOP);
+ for (i=0 ; i<w->numpoints ; i++)
+ qglVertex3fv(w->points[i]);
+ qglEnd();
+ }
+
+ DrawBrushEntityName (b);
+
+}
+
+/*
+============
+Brush_Move
+============
+*/
+void Brush_Move (brush_t *b, const vec3_t move, bool bSnap)
+{
+ int i;
+ face_t *f;
+
+ for (f=b->brush_faces ; f ; f=f->next)
+ for (i=0 ; i<3 ; i++)
+ VectorAdd (f->planepts[i], move, f->planepts[i]);
+
+ if (g_PrefsDlg.m_bTextureLock && !b->owner->eclass->fixedsize)
+ {
+ for (f=b->brush_faces ; f ; f=f->next)
+ {
+ vec3_t vTemp;
+ VectorCopy(move, vTemp);
+ Face_MoveTexture(f, vTemp);
+ }
+ }
+
+ Brush_Build( b, bSnap,true,false,false); // don't filter
+
+
+ if (b->patchBrush)
+ {
+ //Patch_Move(b->nPatchID, move);
+ Patch_Move(b->pPatch, move);
+ }
+
+
+ // PGM - keep the origin vector up to date on fixed size entities.
+ if(b->owner->eclass->fixedsize)
+ {
+ char text[64];
+ VectorAdd(b->owner->origin, move, b->owner->origin);
+ sprintf (text, "%i %i %i",
+ (int)b->owner->origin[0], (int)b->owner->origin[1], (int)b->owner->origin[2]);
+ SetKeyValue(b->owner, "origin", text);
+ //VectorAdd(b->maxs, b->mins, b->owner->origin);
+ //VectorScale(b->owner->origin, 0.5, b->owner->origin);
+ }
+}
+
+
+
+void Brush_Print(brush_t* b)
+{
+ int nFace = 0;
+ for (face_t* f = b->brush_faces ; f ; f=f->next)
+ {
+ Sys_Printf("Face %i\n", nFace++);
+ Sys_Printf("%f %f %f\n", f->planepts[0][0], f->planepts[0][1], f->planepts[0][2]);
+ Sys_Printf("%f %f %f\n", f->planepts[1][0], f->planepts[1][1], f->planepts[1][2]);
+ Sys_Printf("%f %f %f\n", f->planepts[2][0], f->planepts[2][1], f->planepts[2][2]);
+ }
+ }
+
+
+
+/*
+=============
+Brush_MakeSided
+
+Makes the current brushhave the given number of 2d sides and turns it into a cone
+=============
+*/
+void Brush_MakeSidedCone(int sides)
+{
+ int i;
+ vec3_t mins, maxs;
+ brush_t *b;
+ texdef_t *texdef;
+ face_t *f;
+ vec3_t mid;
+ float width;
+ float sv, cv;
+
+ if (sides < 3 || sides > 32)
+ {
+ Sys_Status ("Bad sides number", 0);
+ return;
+ }
+
+ if (!QE_SingleBrush ())
+ {
+ Sys_Status ("Must have a single brush selected", 0 );
+ return;
+ }
+
+ b = selected_brushes.next;
+ VectorCopy (b->mins, mins);
+ VectorCopy (b->maxs, maxs);
+ texdef = &g_qeglobals.d_texturewin.texdef;
+
+ Brush_Free (b);
+
+ // find center of brush
+ width = 8;
+ for (i=0 ; i<2 ; i++)
+ {
+ mid[i] = (maxs[i] + mins[i])*0.5;
+ if (maxs[i] - mins[i] > width)
+ width = maxs[i] - mins[i];
+ }
+ width /= 2;
+
+ b = Brush_Alloc();
+
+ // create bottom face
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2];
+ f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2];
+ f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2];
+
+ for (i=0 ; i<sides ; i++)
+ {
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ sv = sin (i*3.14159265*2/sides);
+ cv = cos (i*3.14159265*2/sides);
+
+
+ f->planepts[0][0] = floor(mid[0]+width*cv+0.5);
+ f->planepts[0][1] = floor(mid[1]+width*sv+0.5);
+ f->planepts[0][2] = mins[2];
+
+ f->planepts[1][0] = mid[0];
+ f->planepts[1][1] = mid[1];
+ f->planepts[1][2] = maxs[2];
+
+ f->planepts[2][0] = floor(f->planepts[0][0] - width * sv + 0.5);
+ f->planepts[2][1] = floor(f->planepts[0][1] + width * cv + 0.5);
+ f->planepts[2][2] = maxs[2];
+
+ }
+
+ Brush_AddToList (b, &selected_brushes);
+
+ Entity_LinkBrush (world_entity, b);
+
+ Brush_Build( b );
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+/*
+=============
+Brush_MakeSided
+
+Makes the current brushhave the given number of 2d sides and turns it into a sphere
+=============
+
+*/
+void Brush_MakeSidedSphere(int sides)
+{
+ int i,j;
+ vec3_t mins, maxs;
+ brush_t *b;
+ texdef_t *texdef;
+ face_t *f;
+ vec3_t mid;
+
+ if (sides < 4 || sides > 32)
+ {
+ Sys_Status ("Bad sides number", 0);
+ return;
+ }
+
+ if (!QE_SingleBrush ())
+ {
+ Sys_Status ("Must have a single brush selected", 0 );
+ return;
+ }
+
+ b = selected_brushes.next;
+ VectorCopy (b->mins, mins);
+ VectorCopy (b->maxs, maxs);
+ texdef = &g_qeglobals.d_texturewin.texdef;
+
+ Brush_Free (b);
+
+ // find center of brush
+ float radius = 8;
+ for (i=0 ; i<2 ; i++)
+ {
+ mid[i] = (maxs[i] + mins[i])*0.5;
+ if (maxs[i] - mins[i] > radius)
+ radius = maxs[i] - mins[i];
+ }
+ radius /= 2;
+
+ b = Brush_Alloc();
+
+ float dt = float(2 * Q_PI / sides);
+ float dp = float(Q_PI / sides);
+ float t,p;
+ for(i=0; i <= sides-1; i++)
+ {
+ for(j=0;j <= sides-2; j++)
+ {
+ t = i * dt;
+ p = float(j * dp - Q_PI / 2);
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorPolar(f->planepts[0], radius, t, p);
+ VectorPolar(f->planepts[1], radius, t, p + dp);
+ VectorPolar(f->planepts[2], radius, t + dt, p + dp);
+
+ for (int k = 0; k < 3; k++)
+ VectorAdd(f->planepts[k], mid, f->planepts[k]);
+ }
+ }
+
+ p = float((sides - 1) * dp - Q_PI / 2);
+ for(i = 0; i <= sides-1; i++)
+ {
+ t = i * dt;
+
+ f = Face_Alloc();
+ f->texdef = *texdef;
+ f->next = b->brush_faces;
+ b->brush_faces = f;
+
+ VectorPolar(f->planepts[0], radius, t, p);
+ VectorPolar(f->planepts[1], radius, t + dt, p + dp);
+ VectorPolar(f->planepts[2], radius, t + dt, p);
+
+ for (int k = 0; k < 3; k++)
+ VectorAdd(f->planepts[k], mid, f->planepts[k]);
+ }
+
+ Brush_AddToList (b, &selected_brushes);
+
+ Entity_LinkBrush (world_entity, b);
+
+ Brush_Build( b );
+
+ Sys_UpdateWindows (W_ALL);
+}
+
+void Face_FitTexture( face_t * face, int nHeight, int nWidth )
+{
+ winding_t *w;
+ vec3_t mins,maxs;
+ int i;
+ float width, height, temp;
+ float rot_width, rot_height;
+ float cosv,sinv,ang;
+ float min_t, min_s, max_t, max_s;
+ float s,t;
+ vec3_t vecs[2];
+ vec3_t coords[4];
+ texdef_t *td;
+
+ if (nHeight < 1)
+ nHeight = 1;
+ if (nWidth < 1)
+ nWidth = 1;
+
+ ClearBounds (mins, maxs);
+
+ w = face->face_winding;
+ if (!w)
+ {
+ return;
+ }
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ AddPointToBounds( w->points[i], mins, maxs );
+ }
+
+ if (g_qeglobals.m_bBrushPrimitMode)
+ Face_FitTexture_BrushPrimit( face, mins, maxs, nHeight, nWidth );
+ else
+ {
+
+ td = &face->texdef;
+ //
+ // get the current angle
+ //
+ ang = td->rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+
+ // get natural texture axis
+ TextureAxisFromPlane(&face->plane, vecs[0], vecs[1]);
+
+ min_s = DotProduct( mins, vecs[0] );
+ min_t = DotProduct( mins, vecs[1] );
+ max_s = DotProduct( maxs, vecs[0] );
+ max_t = DotProduct( maxs, vecs[1] );
+ width = max_s - min_s;
+ height = max_t - min_t;
+ coords[0][0] = min_s;
+ coords[0][1] = min_t;
+ coords[1][0] = max_s;
+ coords[1][1] = min_t;
+ coords[2][0] = min_s;
+ coords[2][1] = max_t;
+ coords[3][0] = max_s;
+ coords[3][1] = max_t;
+ min_s = min_t = 99999;
+ max_s = max_t = -99999;
+ for (i=0; i<4; i++)
+ {
+ s = cosv * coords[i][0] - sinv * coords[i][1];
+ t = sinv * coords[i][0] + cosv * coords[i][1];
+ if (i&1)
+ {
+ if (s > max_s)
+ {
+ max_s = s;
+ }
+ }
+ else
+ {
+ if (s < min_s)
+ {
+ min_s = s;
+ }
+ if (i<2)
+ {
+ if (t < min_t)
+ {
+ min_t = t;
+ }
+ }
+ else
+ {
+ if (t > max_t)
+ {
+ max_t = t;
+ }
+ }
+ }
+ }
+ rot_width = (max_s - min_s);
+ rot_height = (max_t - min_t);
+ td->scale[0] = -(rot_width/((float)(face->d_texture->width*nWidth)));
+ td->scale[1] = -(rot_height/((float)(face->d_texture->height*nHeight)));
+
+ td->shift[0] = min_s/td->scale[0];
+ temp = (int)(td->shift[0] / (face->d_texture->width*nWidth));
+ temp = (temp+1)*face->d_texture->width*nWidth;
+ td->shift[0] = (int)(temp - td->shift[0])%(face->d_texture->width*nWidth);
+
+ td->shift[1] = min_t/td->scale[1];
+ temp = (int)(td->shift[1] / (face->d_texture->height*nHeight));
+ temp = (temp+1)*(face->d_texture->height*nHeight);
+ td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight);
+
+ td->shift[1] = min_t/td->scale[1];
+ temp = (int)(td->shift[1] / (face->d_texture->height*nHeight));
+ temp = (temp+1)*(face->d_texture->height*nHeight);
+ td->shift[1] = (int)(temp - td->shift[1])%(face->d_texture->height*nHeight);
+
+ }
+}
+
+void Brush_FitTexture( brush_t *b, int nHeight, int nWidth )
+{
+ face_t *face;
+
+ for (face = b->brush_faces ; face ; face=face->next)
+ {
+ Face_FitTexture( face, nHeight, nWidth );
+ }
+}
+
+void aabb_draw(const aabb_t *aabb, int mode)
+{
+ vec3_t normals[6] = { { 1, 0, 0}, { 0, 1, 0 }, { 0, 0, 1 }, {-1, 0, 0}, { 0,-1, 0 }, { 0, 0,-1 } };
+ vec3_t points[8];
+ vec3_t vMin, vMax;
+ VectorSubtract(aabb->origin, aabb->extents, vMin);
+ VectorAdd(aabb->origin, aabb->extents, vMax);
+ VectorSet(points[0], vMin[0], vMax[1], vMax[2]);
+ VectorSet(points[1], vMax[0], vMax[1], vMax[2]);
+ VectorSet(points[2], vMax[0], vMin[1], vMax[2]);
+ VectorSet(points[3], vMin[0], vMin[1], vMax[2]);
+ VectorSet(points[4], vMin[0], vMax[1], vMin[2]);
+ VectorSet(points[5], vMax[0], vMax[1], vMin[2]);
+ VectorSet(points[6], vMax[0], vMin[1], vMin[2]);
+ VectorSet(points[7], vMin[0], vMin[1], vMin[2]);
+
+ qglBegin(GL_QUADS);
+
+ qglNormal3fv(normals[0]);
+ qglVertex3fv(points[2]);
+ qglVertex3fv(points[1]);
+ qglVertex3fv(points[5]);
+ qglVertex3fv(points[6]);
+
+ qglNormal3fv(normals[1]);
+ qglVertex3fv(points[1]);
+ qglVertex3fv(points[0]);
+ qglVertex3fv(points[4]);
+ qglVertex3fv(points[5]);
+
+ qglNormal3fv(normals[2]);
+ qglVertex3fv(points[0]);
+ qglVertex3fv(points[1]);
+ qglVertex3fv(points[2]);
+ qglVertex3fv(points[3]);
+
+ qglNormal3fv(normals[3]);
+ qglVertex3fv(points[3]);
+ qglVertex3fv(points[7]);
+ qglVertex3fv(points[4]);
+ qglVertex3fv(points[0]);
+
+ qglNormal3fv(normals[4]);
+ qglVertex3fv(points[3]);
+ qglVertex3fv(points[2]);
+ qglVertex3fv(points[6]);
+ qglVertex3fv(points[7]);
+
+ qglNormal3fv(normals[5]);
+ qglVertex3fv(points[7]);
+ qglVertex3fv(points[6]);
+ qglVertex3fv(points[5]);
+ qglVertex3fv(points[4]);
+
+ qglEnd();
+
+/*
+
+
+ vec3_t Coords[8];
+
+ vec3_t vMin, vMax;
+ VectorSubtract(aabb->origin, aabb->extents, vMin);
+ VectorAdd(aabb->origin, aabb->extents, vMax);
+ VectorSet(Coords[0], vMin[0], vMax[1], vMax[2]);
+ VectorSet(Coords[1], vMax[0], vMax[1], vMax[2]);
+ VectorSet(Coords[2], vMax[0], vMin[1], vMax[2]);
+ VectorSet(Coords[3], vMin[0], vMin[1], vMax[2]);
+ VectorSet(Coords[4], vMin[0], vMax[1], vMin[2]);
+ VectorSet(Coords[5], vMax[0], vMax[1], vMin[2]);
+ VectorSet(Coords[6], vMax[0], vMin[1], vMin[2]);
+ VectorSet(Coords[7], vMin[0], vMin[1], vMin[2]);
+
+ vec3_t Normals[8] = { {-1, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 1 },
+ { 0, 0,-1 },
+ { 0, 1, 0 },
+ { 1, 0, 0 },
+ { 0,-1, 0 } };
+
+ unsigned short Indices[24] = { 2, 1, 5, 6,
+ 1, 0, 4, 5,
+ 0, 1, 2, 3,
+ 3, 7, 4, 0,
+ 3, 2, 6, 7,
+ 7, 6, 5, 4 };
+
+ qglVertexPointer(3, GL_FLOAT, 0, Coords); // filling the arrays
+ qglNormalPointer(GL_FLOAT, 0, Normals);
+
+ //glLockArraysEXT(0, count); // extension GL_EXT_compiled_vertex_array
+
+ qglDrawElements(GL_QUADS, 24, GL_UNSIGNED_SHORT, Indices);
+
+ //glUnlockArraysEXT; // extension GL_EXT_compiled_vertex_array
+*/
+}
+
+qboolean IsBrushSelected(brush_t* bSel)
+{
+ for (brush_t* b = selected_brushes.next ;b != NULL && b != &selected_brushes; b = b->next)
+ {
+ if (b == bSel)
+ return true;
+ }
+ return false;
+}
+
+