/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "stdafx.h" #include #include "qe3.h" #include "winding.h" // globals int g_nBrushId = 0; 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; } brush_t *Brush_Alloc() { brush_t *b = (brush_t*)qmalloc(sizeof(brush_t)); return b; } void PrintWinding (winding_t *w) { int i; printf ("-------------\n"); for (i=0 ; inumpoints ; i++) 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) { 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) { 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 (dot > best) { best = dot; bestaxis = i; } } VectorCopy (baseaxis[bestaxis*3+1], xv); VectorCopy (baseaxis[bestaxis*3+2], yv); } float lightaxis[3] = {0.6, 0.8, 1.0}; /* ================ SetShadeForPlane Light different planes differently to improve recognition ================ */ float SetShadeForPlane (plane_t *p) { 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 ) ); if (g_qeglobals.bSurfacePropertiesPlugin) f->pData = static_cast( g_SurfaceTable.m_pfnTexdefAlloc( 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; } if (g_qeglobals.bSurfacePropertiesPlugin) { #ifdef _DEBUG if ( !f->pData ) { Sys_Printf("WARNING: unexpected IPluginTexdef is NULL in Face_Free\n"); } else #endif GETPLUGINTEXDEF(f)->DecRef(); } 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; memcpy (n->planepts, f->planepts, sizeof(n->planepts)); // all other fields are derived, and will be set by Brush_Build 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; 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->d_texture = Texture_ForName( n->texdef.name ); return n; } /* ================ Clamp ================ */ void Clamp(float& f, int nClamp) { float fFrac = f - static_cast(f); f = static_cast(f) % nClamp; f += fFrac; } /* ================ Face_MoveTexture ================ */ void Face_MoveTexture(face_t *f, vec3_t delta) { vec3_t vX, vY; /* #ifdef _DEBUG if (g_PrefsDlg.m_bBrushPrimitMode) Sys_Printf("Warning : Face_MoveTexture not done in brush primitive mode\n"); #endif */ if (g_qeglobals.m_bBrushPrimitMode) Face_MoveTexture_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] = 1; if (!f->texdef.scale[1]) f->texdef.scale[1] = 1; 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 ================ */ void Face_SetColor (brush_t *b, face_t *f, float fCurveColor) { float shade; qtexture_t *q; q = f->d_texture; // set shading for face shade = SetShadeForPlane (&f->plane); if (g_pParentWnd->GetCamera()->Camera().draw_mode == cd_texture && !b->owner->eclass->fixedsize) { //if (b->curveBrush) // shade = fCurveColor; f->d_color[0] = f->d_color[1] = f->d_color[2] = shade; } else { f->d_color[0] = shade*q->color[0]; f->d_color[1] = shade*q->color[1]; f->d_color[2] = shade*q->color[2]; } } /* ================ Face_TextureVectors TTimo: NOTE: this is never to get called while in brush primitives mode ================ */ 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 //++timo when playing with patches, this sometimes get called and the Warning is displayed // find some way out .. 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_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1; if (!td->scale[1]) td->scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? 0.5 : 1; // 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)) printf ("WARNING: brush plane with no normal\n"); VectorNormalize (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) { 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 // MERGEME #if 0 if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES)) { // draw the angle pointer a = FloatForKey (b->owner, "angle"); if (a) { 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 (); } } #endif 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); qglCallLists (strlen(name), GL_UNSIGNED_BYTE, 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) printf ("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; 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 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) { bLocalConvert = true; g_qeglobals.bNeedConvert = true; } /* ** build the windings and generate the bounding box */ Brush_BuildWindings(b, bSnap); Patch_BuildPoints (b); /* ** 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 == NULL) Group_AddToProperGroup(b); if (bMarkMap) { Sys_MarkMapModified(); } if (bLocalConvert) g_qeglobals.bNeedConvert = false; } /* ============== 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) { brush_t *b; face_t *nf; vec3_t temp; b = Brush_Clone (in); nf = Face_Clone (f); nf->texdef = b->brush_faces->texdef; 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; 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.1, &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_old1 - The input brush must have face windings. - The input brush must be a brush with faces that do not intersect. - The input brush does not have to be convex. - The vertex will not be moved if the movement either causes the brush to have faces that intersect or causes the brush to be flipped inside out. (For instance a tetrahedron can easily be flipped inside out without having faces that intersect.) - The created brush does not have to be convex. - Returns true if the vertex movement is performed. ================= */ #define MAX_MOVE_FACES 64 #define INTERSECT_EPSILON 0.1 #define POINT_EPSILON 0.3 int Brush_MoveVertex_old1(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; int i, j, k, nummovefaces, result; float dot; result = false; // tmpw.numpoints = 3; tmpw.maxpoints = 3; 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.5) * g_qeglobals.d_gridsize; //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], vertex, POINT_EPSILON)) { 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 newface->d_texture = Texture_ForName( newface->texdef.name ); //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->d_texture = Texture_ForName( 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 //check if the move is valid int l; vec3_t p1, p2; winding_t *w2; plane_t plane; face = NULL; VectorCopy(vertex, tmpw.points[1]); VectorCopy(end, tmpw.points[2]); for (face = b->brush_faces; face; face = face->next) { for (i = 0; i < nummovefaces; i++) { if (face == movefaces[i]) break; } if (i < nummovefaces) continue; //the delta vector may not intersect with any of the not move faces if (Winding_VectorIntersect(face->face_winding, &face->plane, vertex, end, INTERSECT_EPSILON)) break; //if the end point of the to be moved vertex is near this not move face if (abs(DotProduct(face->plane.normal, end) - face->plane.dist) < 0.5) { //the end point may not be inside or very close to the not move face winding if (Winding_PointInside(face->face_winding, &face->plane, end, 0.5)) break; } for (i = 0; i < nummovefaces; i++) { w = movefaces[i]->face_winding; j = movefacepoints[i]; for (k = -1; k <= 1; k += 2) { //check if the new edge will not intersect with the not move face VectorCopy(w->points[(j + k + w->numpoints) % w->numpoints], tmpw.points[0]); if (Winding_VectorIntersect(face->face_winding, &face->plane, tmpw.points[0], end, INTERSECT_EPSILON)) { //ok the new edge instersects with the not move face //we can't perform the vertex movement //break; } //check if the not move face intersects the "movement winding" Winding_Plane(&tmpw, plane.normal, &plane.dist); w2 = face->face_winding; for (l = 0; l < w2->numpoints; l++) { VectorCopy(w2->points[l], p1); if (Point_Equal(p1, tmpw.points[0], POINT_EPSILON)) continue; VectorCopy(w2->points[(l+1) % w2->numpoints], p2); if (Point_Equal(p2, tmpw.points[0], POINT_EPSILON)) continue; if (Winding_VectorIntersect(&tmpw, &plane, p1, p2, INTERSECT_EPSILON)) break; } if (l < w2->numpoints) { //ok this not move face intersects the "movement winding" //we can't perform the vertex movement break; } } if (k <= 1) break; } if (i < nummovefaces) break; } if (!face) { //ok the move was valid //now move all the vertexes of the movefaces for (i = 0; i < nummovefaces; i++) { VectorCopy(end, 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 = true; } //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_MoveVertexes_old2 - The input brush must be convex - The input brush must have face windings. - The output brush will be convex. - Returns true if the vertex movement is performed. ================= */ #define MAX_MOVE_FACES 64 #define INTERSECT_EPSILON 0.1 #define POINT_EPSILON 0.3 int Brush_MoveVertex_old2(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; int i, j, k, nummovefaces, result; float dot; result = true; // tmpw.numpoints = 3; tmpw.maxpoints = 3; 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.5) * g_qeglobals.d_gridsize; //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], vertex, POINT_EPSILON)) { 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 newface->d_texture = Texture_ForName( newface->texdef.name ); //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->d_texture = Texture_ForName( 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 //move the vertex for (i = 0; i < nummovefaces; i++) { //move vertex to end position VectorCopy(end, 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 the brush is no longer convex if (!Brush_Convex(b)) { for (i = 0; i < nummovefaces; i++) { //move the vertex back to the initial position VectorCopy(vertex, 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; } //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_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 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; 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.5) * g_qeglobals.d_gridsize; // VectorCopy(end, mid); //if the start and end are the same if (Point_Equal(start, end, 0.3)) 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.3)) { 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.2)) { 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 newface->d_texture = Texture_ForName( newface->texdef.name ); //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->d_texture = Texture_ForName( 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.4)) return false; VectorAdd(p1, p2, point); VectorScale(point, 0.5, 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.1)) continue; if (Point_Equal(w->points[(i+1) % w->numpoints], p2, 0.1)) { neww = Winding_InsertPoint(w, point, (i+1) % w->numpoints); break; } else if (Point_Equal(w->points[(i-1+w->numpoints) % w->numpoints], p2, 0.3)) { 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; } } /* ================= Brush_Parse The brush is NOT linked to any list ================= */ //++timo FIXME: when using old brush primitives, the test loop for "Brush" and "patchDef2" "patchDef3" is ran // before each face parsing. It works, but it's a performance hit brush_t *Brush_Parse (void) { brush_t *b; face_t *f; int i,j; g_qeglobals.d_parsed_brushes++; b = Brush_Alloc(); do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; // handle "Brush" primitive if (strcmpi(token, "brushDef") == 0) { // Timo parsing new brush format g_qeglobals.bPrimitBrushes=true; // check the map is not mixing the two kinds of brushes if (g_qeglobals.m_bBrushPrimitMode) { if (g_qeglobals.bOldBrushes) Sys_Printf("Warning : old brushes and brush primitive in the same file are not allowed ( Brush_Parse )\n"); } //++Timo write new brush primitive -> old conversion code for Q3->Q2 conversions ? else Sys_Printf("Warning : conversion code from brush primitive not done ( Brush_Parse )\n"); BrushPrimit_Parse(b); if (b == NULL) { Warning ("parsing brush primitive"); return NULL; } else { continue; } } if ( strcmpi( token, "terrainDef" ) == 0 ) { free (b); b = Terrain_Parse(); if (b == NULL) { Warning ("parsing terrain/brush"); return NULL; } else { continue; } } if (strcmpi(token, "patchDef2") == 0 || strcmpi(token, "patchDef3") == 0) { free (b); // double string compare but will go away soon b = Patch_Parse(strcmpi(token, "patchDef2") == 0); if (b == NULL) { Warning ("parsing patch/brush"); return NULL; } else { continue; } // handle inline patch } else { // Timo parsing old brush format g_qeglobals.bOldBrushes=true; if (g_qeglobals.m_bBrushPrimitMode) { // check the map is not mixing the two kinds of brushes if (g_qeglobals.bPrimitBrushes) Sys_Printf("Warning : old brushes and brush primitive in the same file are not allowed ( Brush_Parse )\n"); // set the "need" conversion flag g_qeglobals.bNeedConvert=true; } f = Face_Alloc(); // add the brush to the end of the chain, so // loading and saving a map doesn't reverse the order f->next = NULL; if (!b->brush_faces) { b->brush_faces = f; } else { face_t *scan; for (scan=b->brush_faces ; scan->next ; scan=scan->next) ; scan->next = f; } // read the three point plane definition for (i=0 ; i<3 ; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) { Warning ("parsing brush"); return NULL; } for (j=0 ; j<3 ; j++) { GetToken (false); f->planepts[i][j] = atof(token); } GetToken (false); if (strcmp (token, ")") ) { Warning ("parsing brush"); return NULL; } } } // Timo // if we have a surface plugin, we'll call the plugin parsing if (g_qeglobals.bSurfacePropertiesPlugin) { GETPLUGINTEXDEF(f)->ParseTexdef(); } else { // read the texturedef GetToken (false); f->texdef.SetName(token); if (token[0] == '(') { int i = 32; } GetToken (false); f->texdef.shift[0] = atoi(token); GetToken (false); f->texdef.shift[1] = atoi(token); GetToken (false); f->texdef.rotate = atoi(token); GetToken (false); f->texdef.scale[0] = atof(token); GetToken (false); f->texdef.scale[1] = atof(token); // the flags and value field aren't necessarily present f->d_texture = Texture_ForName( f->texdef.name ); f->texdef.flags = f->d_texture->flags; f->texdef.value = f->d_texture->value; f->texdef.contents = f->d_texture->contents; if (TokenAvailable ()) { GetToken (false); f->texdef.contents = atoi(token); GetToken (false); f->texdef.flags = atoi(token); GetToken (false); f->texdef.value = atoi(token); } } } while (1); return b; } /* ================= QERApp_MapPrintf_FILE callback for surface properties plugin must fit a PFN_QERAPP_MAPPRINTF ( see isurfaceplugin.h ) ================= */ // carefully initialize ! FILE * g_File; void WINAPI QERApp_MapPrintf_FILE( char *text, ... ) { va_list argptr; char buf[32768]; va_start (argptr,text); vsprintf (buf, text,argptr); va_end (argptr); fprintf( g_File, buf ); } /* ============== 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 if (b->terrainBrush) { Terrain_SetEpair(b->pTerrain, 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 if (b->terrainBrush) { return Terrain_GetKeyValue(b->pTerrain, pKey); } else { return ValueForKey(b->epairs, pKey); } } else { Sys_Printf("Can only set brush/patch key/values in Brush primitive mode\n"); } return ""; } /* ================= Brush_Write save all brushes as Brush primitive format ================= */ void Brush_Write (brush_t *b, FILE *f) { epair_t *ep; face_t *fa; char *pname; int i; if (b->patchBrush) { Patch_Write(b->pPatch, f); return; } if ( b->pTerrain ) { Terrain_Write(b->pTerrain, f); return; } if (g_qeglobals.m_bBrushPrimitMode) { // save brush primitive format fprintf (f, "{\nbrushDef\n{\n"); // brush epairs if (b->epairs) for (ep = b->epairs ; ep ; ep=ep->next) fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value); for (fa=b->brush_faces ; fa ; fa=fa->next) { // save planepts for (i=0 ; i<3 ; i++) { fprintf(f, "( "); for (int j = 0; j < 3; j++) if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) fprintf(f, "%i ", static_cast(fa->planepts[i][j])); else fprintf(f, "%f ", fa->planepts[i][j]); fprintf(f, ") "); } // save texture coordinates fprintf(f,"( ( "); for (i=0 ; i<3 ; i++) if (fa->brushprimit_texdef.coords[0][i] == static_cast(fa->brushprimit_texdef.coords[0][i])) fprintf(f,"%i ",static_cast(fa->brushprimit_texdef.coords[0][i])); else fprintf(f,"%f ",fa->brushprimit_texdef.coords[0][i]); fprintf(f,") ( "); for (i=0 ; i<3 ; i++) if (fa->brushprimit_texdef.coords[1][i] == static_cast(fa->brushprimit_texdef.coords[1][i])) fprintf(f,"%i ",static_cast(fa->brushprimit_texdef.coords[1][i])); else fprintf(f,"%f ",fa->brushprimit_texdef.coords[1][i]); fprintf(f,") ) "); // save texture attribs //++timo surface properties plugin not implemented for brush primitives if (g_qeglobals.bSurfacePropertiesPlugin) Sys_Printf("WARNING: surface properties plugin not supported with brush primitives (yet)\n"); char *pName = strlen(fa->texdef.name) > 0 ? fa->texdef.name : "unnamed"; fprintf(f, "%s ", pName ); fprintf(f, "%i %i %i\n", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); } fprintf (f, "}\n}\n"); } else { fprintf (f, "{\n"); for (fa=b->brush_faces ; fa ; fa=fa->next) { for (i=0 ; i<3 ; i++) { fprintf(f, "( "); for (int j = 0; j < 3; j++) { if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) fprintf(f, "%i ", static_cast(fa->planepts[i][j])); else fprintf(f, "%f ", fa->planepts[i][j]); } fprintf(f, ") "); } if (g_qeglobals.bSurfacePropertiesPlugin) { g_File = f; #ifdef _DEBUG if (!fa->pData) Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in Brush_Write\n"); else #endif GETPLUGINTEXDEF(fa)->WriteTexdef( QERApp_MapPrintf_FILE ); } else { pname = fa->texdef.name; if (pname[0] == 0) pname = "unnamed"; fprintf (f, "%s %i %i %i ", pname, (int)fa->texdef.shift[0], (int)fa->texdef.shift[1], (int)fa->texdef.rotate); if (fa->texdef.scale[0] == (int)fa->texdef.scale[0]) fprintf (f, "%i ", (int)fa->texdef.scale[0]); else fprintf (f, "%f ", (float)fa->texdef.scale[0]); if (fa->texdef.scale[1] == (int)fa->texdef.scale[1]) fprintf (f, "%i", (int)fa->texdef.scale[1]); else fprintf (f, "%f", (float)fa->texdef.scale[1]); fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); } fprintf (f, "\n"); } fprintf (f, "}\n"); } } /* ================= QERApp_MapPrintf_MEMFILE callback for surface properties plugin must fit a PFN_QERAPP_MAPPRINTF ( see isurfaceplugin.h ) ================= */ // carefully initialize ! CMemFile * g_pMemFile; void WINAPI QERApp_MapPrintf_MEMFILE( char *text, ... ) { va_list argptr; char buf[32768]; va_start (argptr,text); vsprintf (buf, text,argptr); va_end (argptr); MemFile_fprintf( g_pMemFile, buf ); } /* ================= Brush_Write to a CMemFile* save all brushes as Brush primitive format ================= */ void Brush_Write (brush_t *b, CMemFile *pMemFile) { epair_t *ep; face_t *fa; char *pname; int i; if (b->patchBrush) { Patch_Write(b->pPatch, pMemFile); return; } if (b->terrainBrush) { Terrain_Write(b->pTerrain, pMemFile); return; } //++timo NOTE: it's not very difficult to add since the surface properties plugin // writes throught a printf-style function prototype if (g_qeglobals.bSurfacePropertiesPlugin) { Sys_Printf("WARNING: Brush_Write to a CMemFile and Surface Properties plugin not done\n"); } if (g_qeglobals.m_bBrushPrimitMode) { // brush primitive format MemFile_fprintf (pMemFile, "{\nBrushDef\n{\n"); // brush epairs if (b->epairs) for( ep = b->epairs ; ep ; ep=ep->next ) MemFile_fprintf (pMemFile, "\"%s\" \"%s\"\n", ep->key, ep->value ); for (fa=b->brush_faces ; fa ; fa=fa->next) { // save planepts for (i=0 ; i<3 ; i++) { MemFile_fprintf(pMemFile, "( "); for (int j = 0; j < 3; j++) if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) MemFile_fprintf(pMemFile, "%i ", static_cast(fa->planepts[i][j])); else MemFile_fprintf(pMemFile, "%f ", fa->planepts[i][j]); MemFile_fprintf(pMemFile, ") "); } // save texture coordinates MemFile_fprintf(pMemFile,"( ( "); for (i=0 ; i<3 ; i++) if (fa->brushprimit_texdef.coords[0][i] == static_cast(fa->brushprimit_texdef.coords[0][i])) MemFile_fprintf(pMemFile,"%i ",static_cast(fa->brushprimit_texdef.coords[0][i])); else MemFile_fprintf(pMemFile,"%f ",fa->brushprimit_texdef.coords[0][i]); MemFile_fprintf(pMemFile,") ( "); for (i=0 ; i<3 ; i++) if (fa->brushprimit_texdef.coords[1][i] == static_cast(fa->brushprimit_texdef.coords[1][i])) MemFile_fprintf(pMemFile,"%i ",static_cast(fa->brushprimit_texdef.coords[1][i])); else MemFile_fprintf(pMemFile,"%f ",fa->brushprimit_texdef.coords[1][i]); MemFile_fprintf(pMemFile,") ) "); // save texture attribs char *pName = strlen(fa->texdef.name) > 0 ? fa->texdef.name : "unnamed"; MemFile_fprintf(pMemFile, "%s ", pName); MemFile_fprintf(pMemFile, "%i %i %i\n", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); } MemFile_fprintf (pMemFile, "}\n}\n"); } else { // old brushes format // also handle surface properties plugin MemFile_fprintf (pMemFile, "{\n"); for (fa=b->brush_faces ; fa ; fa=fa->next) { for (i=0 ; i<3 ; i++) { MemFile_fprintf(pMemFile, "( "); for (int j = 0; j < 3; j++) { if (fa->planepts[i][j] == static_cast(fa->planepts[i][j])) MemFile_fprintf(pMemFile, "%i ", static_cast(fa->planepts[i][j])); else MemFile_fprintf(pMemFile, "%f ", fa->planepts[i][j]); } MemFile_fprintf(pMemFile, ") "); } if (g_qeglobals.bSurfacePropertiesPlugin) { g_pMemFile = pMemFile; #ifdef _DEBUG if (!fa->pData) Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in Brush_Write\n"); else #endif GETPLUGINTEXDEF(fa)->WriteTexdef( QERApp_MapPrintf_MEMFILE ); } else { pname = fa->texdef.name; if (pname[0] == 0) pname = "unnamed"; MemFile_fprintf (pMemFile, "%s %i %i %i ", pname, (int)fa->texdef.shift[0], (int)fa->texdef.shift[1], (int)fa->texdef.rotate); if (fa->texdef.scale[0] == (int)fa->texdef.scale[0]) MemFile_fprintf (pMemFile, "%i ", (int)fa->texdef.scale[0]); else MemFile_fprintf (pMemFile, "%f ", (float)fa->texdef.scale[0]); if (fa->texdef.scale[1] == (int)fa->texdef.scale[1]) MemFile_fprintf (pMemFile, "%i", (int)fa->texdef.scale[1]); else MemFile_fprintf (pMemFile, "%f", (float)fa->texdef.scale[1]); MemFile_fprintf (pMemFile, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); } MemFile_fprintf (pMemFile, "\n"); } MemFile_fprintf (pMemFile, "}\n"); } } /* ============= 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; // 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"); } 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) { //++timo handle new brush primitive ? return here ?? return Brush_Create(mins, maxs, texdef); for (int 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 = Q_rint(mins[2] + (Q_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] = Q_rint(mins[0] + ((maxs[0] - mins[0]) / 2)); top[1] = Q_rint(mins[1] + ((maxs[1] - mins[1]) / 2)); top[2] = Q_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 ; itexdef = *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); } if( b->terrainBrush ) { Terrain_Delete( b->pTerrain ); } // free faces for (f=b->brush_faces ; f ; f=next) { next = f->next; Face_Free( f ); } //Timo : 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); } //f->texdef.~texdef_t();; size += _msize(f); 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); } if (b->terrainBrush) { size += Terrain_MemorySize(b->pTerrain); } // 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 += _msize(ep->value); size += _msize(ep); } size += _msize(b); 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 if (b->terrainBrush) { terrainMesh_t *p = Terrain_Duplicate(b->pTerrain); 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 if (b->terrainBrush) { terrainMesh_t *p = Terrain_Duplicate(b->pTerrain); 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; } /* ============== 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 ============== */ face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist) { face_t *f, *firstface; vec3_t p1, p2; float frac, d1, d2; int i; VectorCopy (origin, p1); for (i=0 ; i<3 ; i++) p2[i] = p1[i] + dir[i]*16384; 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; 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 *list) { if (b->next || b->prev) Error ("Brush_AddToList: allready linked"); if (list == &selected_brushes || list == &active_brushes) { if (b->patchBrush && list == &selected_brushes) { Patch_Select(b->pPatch); } if (b->terrainBrush && list == &selected_brushes) { Terrain_Select(b->pTerrain); } } b->next = list->next; list->next->prev = b; list->next = b; b->prev = list; // 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); //Patch_Deselect(b->nPatchID); } if (b->terrainBrush) { Terrain_Deselect(b->pTerrain); } 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 =============== */ void SetFaceTexdef (brush_t *b, face_t *f, 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, Texture_ForName( f->texdef.name ) ); } 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); VectorSubtract(p3,p1,vy); VectorNormalize(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); // surface plugin if (g_qeglobals.bSurfacePropertiesPlugin) { #ifdef _DEBUG if (!f->pData) Sys_Printf("ERROR: unexpected IPluginTexdef* is NULL in SetFaceTexdef\n"); else #endif GETPLUGINTEXDEF(f)->DecRef(); IPluginTexdef *pTexdef = NULL; if ( pPlugTexdef ) { pTexdef = pPlugTexdef->Copy(); pTexdef->Hook( f ); } else pTexdef = g_SurfaceTable.m_pfnTexdefAlloc( f ); f->pData = pTexdef; } // 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; } } } 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 (b, f, texdef, brushprimit_texdef, bFitScale, pTexdef); } Brush_Build( b ); if (b->patchBrush) { //++timo clean // Sys_Printf("WARNING: Brush_SetTexture needs surface plugin code for patches\n"); Patch_SetTexture(b->pPatch, texdef, pTexdef ); } if (b->terrainBrush) { Terrain_SetTexture(b->pTerrain, texdef); } } 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 ; iowner->eclass->fixedsize) return; c = 0; for (i=0 ; i<3 ; i++) c += AddPlanept (f->planepts[i]); if (c == 0) return; // allready 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 ; inumpoints ; 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; //if (b->patchBrush) // return; //Patch_SideSelect(b->nPatchID, origin, dir); for (f=b->brush_faces ; f ; f=f->next) { VectorCopy (origin, p1); VectorMA (origin, 16384, 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); } } 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); face->d_texture = Texture_ForName( face->texdef.name ); if (!w) continue; for (i=0 ; inumpoints ; 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; } } // setup s and t vectors, and set color //if (!g_PrefsDlg.m_bGLLighting) //{ Face_SetColor (b, face, fCurveColor); //} fCurveColor -= .10; if (fCurveColor <= 0) fCurveColor = 1.0; // 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 ; inumpoints ; 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 { for (i=0 ; inumpoints ; 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) { for (face_t *f = pb->brush_faces ; f; f = f->next) { for (int i = 0 ;i < 3 ;i++) { for (int j = 0 ;j < 3 ; j++) { f->planepts[i][j] = floor (f->planepts[i][j] / g_qeglobals.d_gridsize + 0.5) * g_qeglobals.d_gridsize; } } } Brush_Build(pb); } 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++) { VectorRotate(f->planepts[i], vAngle, vOrigin, f->planepts[i]); } } if (bBuild) { Brush_Build(b, false, false); } } 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] + abs((b->maxs[j] - b->mins[j]) * 0.5); } // calc distance between centers VectorSubtract(vNewCenter, vMid, vMid); Brush_Move(b, vMid, true); } // only designed for fixed size entity brushes void Brush_Resize(brush_t *b, vec3_t vMin, vec3_t vMax) { brush_t *b2 = Brush_Create(vMin, vMax, &b->brush_faces->texdef); face_t *next; for (face_t *f=b->brush_faces ; f ; f=next) { next = f->next; Face_Free( f ); } b->brush_faces = b2->brush_faces; // unlink from active/selected list if (b2->next) Brush_RemoveFromList (b2); free(b2); Brush_Build(b, true); } eclass_t* HasModel(brush_t *b) { vec3_t vMin, vMax; vMin[0] = vMin[1] = vMin[2] = 9999; vMax[0] = vMax[1] = vMax[2] = -9999; if (b->owner->md3Class != NULL) { return b->owner->md3Class; } if (Eclass_hasModel(b->owner->eclass, vMin, vMax)) { return b->owner->eclass; } eclass_t *e = NULL; // FIXME: entity needs to track whether a cache hit failed and not ask again if (b->owner->eclass->nShowFlags & ECLASS_MISCMODEL) { char *pModel = ValueForKey(b->owner, "model"); if (pModel != NULL && strlen(pModel) > 0) { e = GetCachedModel(b->owner, pModel, vMin, vMax); if (e != NULL) { // we need to scale the brush to the proper size based on the model load // recreate brush just like in load/save VectorAdd (vMin, b->owner->origin, vMin); VectorAdd (vMax, b->owner->origin, vMax); Brush_Resize(b, vMin, vMax); /* // vec3_t vTemp, vTemp2; VectorSubtract(b->maxs, b->mins, vTemp); VectorSubtract(vMax, vMin, vTemp2); for (int i = 0; i < 3; i++) { if (vTemp[i] != 0) { vTemp2[i] /= vTemp[i]; } } vec3_t vMid, vMid2; vMid[0] = vMid[1] = vMid[2] = 0.0; vMid2[0] = vMid2[1] = vMid2[2] = 0.0; for (int j = 0; j < 3; j++) { vMid2[j] = b->mins[j] + abs((b->maxs[j] - b->mins[j]) * 0.5); } //VectorSubtract(vMid2, vMid, vMid2); for (face_t* f=b->brush_faces ; f ; f=f->next) { for (int i=0 ; i<3 ; i++) { // scale VectorSubtract(f->planepts[i], vMid2, f->planepts[i]); f->planepts[i][0] *= vTemp2[0]; f->planepts[i][1] *= vTemp2[1]; f->planepts[i][2] *= vTemp2[2]; VectorAdd(f->planepts[i], vMid2, f->planepts[i]); } } //Brush_Center(b, b->owner->origin); //Brush_SnapToGrid(b); /* float a = FloatForKey (b->owner, "angle"); if (a) { vec3_t vAngle; vAngle[0] = vAngle[1] = 0; vAngle[2] = a; Brush_Rotate(b, vAngle, b->owner->origin); } else { Brush_Build(b, true); */ // } b->bModelFailed = false; } else { b->bModelFailed = true; } } } return e; } static bool g_bInPaintedModel = false; static bool g_bDoIt = false; bool PaintedModel(brush_t *b, bool bOkToTexture) { if (g_bInPaintedModel) { return true; } if (g_PrefsDlg.m_nEntityShowState == ENTITY_BOX || b->bModelFailed) { return false; } else if (!IsBrushSelected(b) && (g_PrefsDlg.m_nEntityShowState & ENTITY_SELECTED_ONLY)) { return false; } g_bInPaintedModel = true; bool bReturn = false; eclass_t *pEclass = HasModel(b); if (pEclass) { qglPushAttrib(GL_ALL_ATTRIB_BITS); entitymodel *model = pEclass->model; float a = FloatForKey (b->owner, "angle"); while (model != NULL) { if (bOkToTexture == false || g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME || model->nTextureBind == -1) // skinned { qglDisable( GL_CULL_FACE ); qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); qglDisable(GL_TEXTURE_2D); qglColor3fv(pEclass->color); } else { qglColor3f(1, 1, 1); qglEnable(GL_TEXTURE_2D); qglBindTexture( GL_TEXTURE_2D, model->nTextureBind ); } vec3_t v; int i,j; VectorAdd(b->maxs, b->mins, v); VectorScale(v, 0.5, v); VectorCopy(b->owner->origin, v); //for (i = 0; i < 3; i++) //{ // v[i] -= (pEclass->mins[i] - b->mins[i]); //} //if (model->nModelPosition) //{ //v[2] = b->mins[2] - (pEclass->mins[2]); //} float s, c; if (a) { s = sin (a/180*Q_PI); c = cos (a/180*Q_PI); } vec3_t vSin; vec3_t vCos; VectorClear(vSin); VectorClear(vCos); for ( j = 0; j < 3; j++) { if (b->owner->vRotation[j]) { vSin[j] = sin(b->owner->vRotation[j]/180*Q_PI); vCos[j] = cos(b->owner->vRotation[j]/180*Q_PI); } } qglBegin (GL_TRIANGLES); vec5_t vTest[3]; for (i = 0; i < model->nTriCount; i++) { for (j = 0; j < 3; j++) { #if 1 float x = model->pTriList[i].v[j][0] + v[0]; float y = model->pTriList[i].v[j][1] + v[1]; if (a) { float x2 = (((x - v[0]) * c) - ((y - v[1]) * s)) + v[0]; float y2 = (((x - v[0]) * s) + ((y - v[1]) * c)) + v[1]; x = x2; y = y2; } //qglTexCoord2f (pEclass->pTriList[i].st[j][0] / pEclass->nSkinWidth, pEclass->pTriList[i].st[j][1] / pEclass->nSkinHeight); qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]); qglVertex3f(x, y, model->pTriList[i].v[j][2] + v[2]); #else float x = model->pTriList[i].v[j][0] + v[0]; float y = model->pTriList[i].v[j][1] + v[1]; float z = model->pTriList[i].v[j][2] + v[2]; if (b->owner->vRotation[0]) { float y2 = (((y - v[1]) * vCos[0]) - ((z - v[2]) * vSin[0])) + v[1]; float z2 = (((y - v[1]) * vSin[0]) + ((z - v[2]) * vCos[0])) + v[2]; y = y2; z = z2; } if (b->owner->vRotation[1]) { float z2 = (((z - v[2]) * vCos[1]) - ((x - v[0]) * vSin[1])) + v[2]; float x2 = (((z - v[2]) * vSin[1]) + ((x - v[0]) * vCos[1])) + v[0]; x = x2; z = z2; } if (b->owner->vRotation[2]) { float x2 = (((x - v[0]) * vCos[2]) - ((y - v[1]) * vSin[2])) + v[0]; float y2 = (((x - v[0]) * vSin[2]) + ((y - v[1]) * vCos[2])) + v[1]; x = x2; y = y2; } qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]); qglVertex3f(x, y, z); #endif if (g_bDoIt) { vTest[j][0] = x; vTest[j][1] = y; vTest[j][2] = model->pTriList[i].v[j][2] + v[2]; vTest[j][3] = model->pTriList[i].st[j][0]; vTest[j][4] = model->pTriList[i].st[j][1]; } } if (g_bDoIt) { Patch_FromTriangle(vTest[0], vTest[1], vTest[2]); } } qglEnd(); if (g_PrefsDlg.m_nEntityShowState & ENTITY_WIREFRAME) // skinned { qglEnable(GL_CULL_FACE ); qglEnable(GL_TEXTURE_2D); qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); } else { qglDisable(GL_TEXTURE_2D); } model = model->pNext; } if (g_bDoIt) { g_bDoIt = false; } vec3_t vColor; VectorScale(pEclass->color, 0.50, vColor); vec3_t vCenter, vMin, vMax; VectorCopy(b->owner->origin, vCenter); qglColor3fv(vColor); qglPointSize(4); qglBegin(GL_POINTS); qglVertex3fv(b->owner->origin); qglEnd(); qglBegin(GL_LINES); vCenter[0] -= 8; qglVertex3fv(vCenter); vCenter[0] += 16; qglVertex3fv(vCenter); vCenter[0] -= 8; vCenter[1] -= 8; qglVertex3fv(vCenter); vCenter[1] += 16; qglVertex3fv(vCenter); vCenter[1] -= 8; vCenter[2] -= 8; qglVertex3fv(vCenter); vCenter[2] += 16; qglVertex3fv(vCenter); vCenter[2] -= 8; qglEnd(); VectorCopy(vCenter, vMin); VectorCopy(vCenter, vMax); vMin[0] -= 4; vMin[1] -= 4; vMin[2] -= 4; vMax[0] += 4; vMax[1] += 4; vMax[2] += 4; qglBegin(GL_LINE_LOOP); qglVertex3f(vMin[0],vMin[1],vMin[2]); qglVertex3f(vMax[0],vMin[1],vMin[2]); qglVertex3f(vMax[0],vMax[1],vMin[2]); qglVertex3f(vMin[0],vMax[1],vMin[2]); qglEnd(); qglBegin(GL_LINE_LOOP); qglVertex3f(vMin[0],vMin[1],vMax[2]); qglVertex3f(vMax[0],vMin[1],vMax[2]); qglVertex3f(vMax[0],vMax[1],vMax[2]); qglVertex3f(vMin[0],vMax[1],vMax[2]); qglEnd(); qglBegin(GL_LINES); qglVertex3f(vMin[0],vMin[1],vMin[2]); qglVertex3f(vMin[0],vMin[1],vMax[2]); qglVertex3f(vMin[0],vMax[1],vMax[2]); qglVertex3f(vMin[0],vMax[1],vMin[2]); qglVertex3f(vMax[0],vMin[1],vMin[2]); qglVertex3f(vMax[0],vMin[1],vMax[2]); qglVertex3f(vMax[0],vMax[1],vMax[2]); qglVertex3f(vMax[0],vMax[1],vMin[2]); qglEnd(); if (g_PrefsDlg.m_nEntityShowState & ENTITY_BOXED) { qglColor3fv(pEclass->color); vec3_t mins, maxs; VectorCopy(b->mins, mins); VectorCopy(b->maxs, maxs); /* if (a) { vec3_t vAngle; vAngle[0] = vAngle[1] = 0; vAngle[2] = a; VectorRotate(mins, vAngle, b->owner->origin, mins); VectorRotate(maxs, vAngle, b->owner->origin, maxs); } */ qglBegin(GL_LINE_LOOP); qglVertex3f(mins[0],mins[1],mins[2]); qglVertex3f(maxs[0],mins[1],mins[2]); qglVertex3f(maxs[0],maxs[1],mins[2]); qglVertex3f(mins[0],maxs[1],mins[2]); qglEnd(); qglBegin(GL_LINE_LOOP); qglVertex3f(mins[0],mins[1],maxs[2]); qglVertex3f(maxs[0],mins[1],maxs[2]); qglVertex3f(maxs[0],maxs[1],maxs[2]); qglVertex3f(mins[0],maxs[1],maxs[2]); qglEnd(); qglBegin(GL_LINES); qglVertex3f(mins[0],mins[1],mins[2]); qglVertex3f(mins[0],mins[1],maxs[2]); qglVertex3f(mins[0],maxs[1],maxs[2]); qglVertex3f(mins[0],maxs[1],mins[2]); qglVertex3f(maxs[0],mins[1],mins[2]); qglVertex3f(maxs[0],mins[1],maxs[2]); qglVertex3f(maxs[0],maxs[1],maxs[2]); qglVertex3f(maxs[0],maxs[1],mins[2]); qglEnd(); } qglPopAttrib(); bReturn = true; } else { b->bModelFailed = true; } g_bInPaintedModel = false; return bReturn; } /* //++timo moved out to mahlib.h //++timo remove void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; static float sr, sp, sy, cr, cp, cy; // static to help MS compiler fp bugs angle = angles[YAW] * Q_PI / 180; sy = sin(angle); cy = cos(angle); angle = angles[PITCH] * Q_PI / 180; sp = sin(angle); cp = cos(angle); angle = angles[ROLL] * Q_PI / 180; sr = sin(angle); cr = cos(angle); if (forward) { forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; } if (right) { right[0] = (-1*sr*sp*cy+-1*cr*-sy); right[1] = (-1*sr*sp*sy+-1*cr*cy); right[2] = -1*sr*cp; } if (up) { up[0] = (cr*sp*cy+-sr*-sy); up[1] = (cr*sp*sy+-sr*cy); up[2] = cr*cp; } } */ 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 DrawLight(brush_t *b) { vec3_t vTriColor; bool bTriPaint = false; vTriColor[0] = vTriColor[2] = 1.0; vTriColor[1] = 1.0; bTriPaint = true; CString strColor = ValueForKey(b->owner, "_color"); if (strColor.GetLength() > 0) { float fR, fG, fB; int n = sscanf(strColor,"%f %f %f", &fR, &fG, &fB); if (n == 3) { vTriColor[0] = fR; vTriColor[1] = fG; vTriColor[2] = fB; } } qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); 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]; vec3_t vSave; VectorCopy(vTriColor, vSave); qglBegin(GL_TRIANGLE_FAN); qglVertex3fv(vTop); for (int i = 0; i <= 3; i++) { vTriColor[0] *= 0.95; vTriColor[1] *= 0.95; vTriColor[2] *= 0.95; qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); qglVertex3fv(vCorners[i]); } qglVertex3fv(vCorners[0]); qglEnd(); VectorCopy(vSave, vTriColor); vTriColor[0] *= 0.95; vTriColor[1] *= 0.95; vTriColor[2] *= 0.95; qglBegin(GL_TRIANGLE_FAN); qglVertex3fv(vBottom); qglVertex3fv(vCorners[0]); for (i = 3; i >= 0; i--) { vTriColor[0] *= 0.95; vTriColor[1] *= 0.95; vTriColor[2] *= 0.95; qglColor3f(vTriColor[0], vTriColor[1], vTriColor[2]); qglVertex3fv(vCorners[i]); } qglEnd(); // check for DOOM lights CString str = ValueForKey(b->owner, "light_right"); if (str.GetLength() > 0) { vec3_t vRight, vUp, vTarget, vTemp; GetVectorForKey (b->owner, "light_right", vRight); GetVectorForKey (b->owner, "light_up", vUp); GetVectorForKey (b->owner, "light_target", vTarget); qglColor3f(0, 1, 0); qglBegin(GL_LINE_LOOP); VectorAdd(vTarget, b->owner->origin, vTemp); VectorAdd(vTemp, vRight, vTemp); VectorAdd(vTemp, vUp, vTemp); qglVertex3fv(b->owner->origin); qglVertex3fv(vTemp); VectorAdd(vTarget, b->owner->origin, vTemp); VectorAdd(vTemp, vUp, vTemp); VectorSubtract(vTemp, vRight, vTemp); qglVertex3fv(b->owner->origin); qglVertex3fv(vTemp); VectorAdd(vTarget, b->owner->origin, vTemp); VectorAdd(vTemp, vRight, vTemp); VectorSubtract(vTemp, vUp, vTemp); qglVertex3fv(b->owner->origin); qglVertex3fv(vTemp); VectorAdd(vTarget, b->owner->origin, vTemp); VectorSubtract(vTemp, vUp, vTemp); VectorSubtract(vTemp, vRight, vTemp); qglVertex3fv(b->owner->origin); qglVertex3fv(vTemp); qglEnd(); } } void Brush_Draw( brush_t *b ) { face_t *face; int i, order; qtexture_t *prev = 0; winding_t *w; if ( b->owner && ( b->owner->eclass->nShowFlags & ECLASS_PLUGINENTITY ) ) { b->owner->pPlugEnt->CamRender(); return; } // (TTimo) NOTE: added by build 173, I check after pPlugEnt so it doesn't interfere ? if (b->hiddenBrush) { return; } if (b->patchBrush) { //Patch_DrawCam(b->nPatchID); Patch_DrawCam(b->pPatch); //if (!g_bPatchShowBounds) return; } if (b->terrainBrush) { Terrain_DrawCam(b->pTerrain); return; } int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; if (b->owner->eclass->fixedsize) { if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) && (b->owner->eclass->nShowFlags & ECLASS_ANGLE)) { Brush_DrawFacingAngle(b, b->owner); } if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT)) { DrawLight(b); return; } if (nDrawMode == cd_texture || nDrawMode == cd_light) qglDisable (GL_TEXTURE_2D); // if we are wireframing models bool bp = (b->bModelFailed) ? false : PaintedModel(b, true); if (nDrawMode == cd_texture || nDrawMode == cd_light) qglEnable (GL_TEXTURE_2D); if (bp) return; } // guarantee the texture will be set first prev = NULL; for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++) { w = face->face_winding; if (!w) { continue; // freed face } if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) { if (strstr(face->texdef.name, "caulk")) { continue; } } #if 0 if (b->alphaBrush) { if (!(face->texdef.flags & SURF_ALPHA)) continue; //--qglPushAttrib(GL_ALL_ATTRIB_BITS); qglDisable(GL_CULL_FACE); //--qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //--qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //--qglDisable(GL_DEPTH_TEST); //--qglBlendFunc (GL_SRC_ALPHA, GL_DST_ALPHA); //--qglEnable (GL_BLEND); } #endif if ((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 (!b->patchBrush) { if (face->texdef.flags & SURF_TRANS33) qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.33 ); else if ( face->texdef.flags & SURF_TRANS66) qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.66 ); else qglColor3fv( face->d_color ); } else { qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], 0.13 ); } // shader drawing stuff if (face->d_texture->bFromShader) { // setup shader drawing qglColor4f ( face->d_color[0], face->d_color[1], face->d_color[2], face->d_texture->fTrans ); } // draw the polygon //if (nDrawMode == cd_light) //{ if (g_PrefsDlg.m_bGLLighting) { qglNormal3fv(face->plane.normal); } //} qglBegin(GL_POLYGON); //if (nDrawMode == cd_light) for (i=0 ; inumpoints ; i++) { if (nDrawMode == cd_texture || nDrawMode == cd_light) qglTexCoord2fv( &w->points[i][3] ); qglVertex3fv(w->points[i]); } qglEnd(); } #if 0 if (b->alphaBrush) { //--qglPopAttrib(); qglEnable(GL_CULL_FACE); //--qglDisable (GL_BLEND); //--qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } #endif if (b->owner->eclass->fixedsize && (nDrawMode == cd_texture || nDrawMode == cd_light)) qglEnable (GL_TEXTURE_2D); qglBindTexture( GL_TEXTURE_2D, 0 ); } 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(); } void Brush_DrawXY(brush_t *b, int nViewType) { face_t *face; int order; winding_t *w; int i; if (b->hiddenBrush) { return; } if (b->patchBrush) { //Patch_DrawXY(b->nPatchID); Patch_DrawXY(b->pPatch); if (!g_bPatchShowBounds) return; } if (b->terrainBrush) { Terrain_DrawXY(b->pTerrain, b->owner); } if (b->owner->eclass->fixedsize) { if (g_PrefsDlg.m_bNewLightDraw && (b->owner->eclass->nShowFlags & ECLASS_LIGHT)) { 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]; qglPolygonMode (GL_FRONT_AND_BACK, GL_LINE); qglBegin(GL_TRIANGLE_FAN); qglVertex3fv(vTop); qglVertex3fv(vCorners[0]); qglVertex3fv(vCorners[1]); qglVertex3fv(vCorners[2]); qglVertex3fv(vCorners[3]); qglVertex3fv(vCorners[0]); qglEnd(); qglBegin(GL_TRIANGLE_FAN); qglVertex3fv(vBottom); qglVertex3fv(vCorners[0]); qglVertex3fv(vCorners[3]); qglVertex3fv(vCorners[2]); qglVertex3fv(vCorners[1]); qglVertex3fv(vCorners[0]); qglEnd(); DrawBrushEntityName (b); return; } else if (b->owner->eclass->nShowFlags & ECLASS_MISCMODEL) { if (PaintedModel(b, false)) return; } } for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++) { // 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) continue; } else { if (face->plane.normal[0] <= 0) continue; } } w = face->face_winding; if (!w) continue; //if (b->alphaBrush && !(face->texdef.flags & SURF_ALPHA)) // continue; // draw the polygon qglBegin(GL_LINE_LOOP); for (i=0 ; inumpoints ; 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) { vec3_t vTemp; VectorCopy(move, vTemp); if (g_PrefsDlg.m_bTextureLock) Face_MoveTexture(f, vTemp); for (i=0 ; i<3 ; i++) VectorAdd (f->planepts[i], move, f->planepts[i]); } Brush_Build( b, bSnap ); if (b->patchBrush) { //Patch_Move(b->nPatchID, move); Patch_Move(b->pPatch, move); } if (b->terrainBrush) { Terrain_Move(b->pTerrain, move); } // PGM - keep the origin vector up to date on fixed size entities. if(b->owner->eclass->fixedsize) { VectorAdd(b->owner->origin, move, b->owner->origin); //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) { 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 ; itexdef = *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) { 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); td = &face->texdef; w = face->face_winding; if (!w) { return; } for (i=0 ; inumpoints ; i++) { AddPointToBounds( w->points[i], mins, maxs ); } // // 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); } 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 ); } }