#include "stdafx.h" #include #include "qe3.h" #include "oddbits.h" #include "groupnames.h" #define MAX_POINTS_ON_WINDING 64 face_t *Face_Alloc( void ); winding_t *NewWinding (int points); void FreeWinding (winding_t *w); winding_t *Winding_Clone( winding_t *w ); winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon); qboolean qbShowFaces = false; 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]); } 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; } //============================================================================ /* ================== NewWinding ================== */ winding_t *NewWinding (int points) { winding_t *w; int size; if (points > MAX_POINTS_ON_WINDING) Error ("NewWinding: %i points", points); size = (int)((winding_t *)0)->points[points]; w = (winding_t*) malloc (size); memset (w, 0, size); w->maxpoints = points; return w; } void FreeWinding (winding_t *w) { free (w); } /* ================== Winding_Clone ================== */ winding_t *Winding_Clone(winding_t *w) { int size; winding_t *c; size = (int)((winding_t *)0)->points[w->numpoints]; c = (winding_t*)qmalloc (size); memcpy (c, w, size); return c; } /* ================== ClipWinding Clips the winding to the plane, returning the new winding on the positive side Frees the input winding. If keepon is true, an exactly on-plane winding will be saved, otherwise it will be clipped away. ================== */ winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon) { vec_t dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; vec_t dot; int i, j; vec_t *p1, *p2; vec3_t mid; winding_t *neww; int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; inumpoints ; i++) { dot = DotProduct (in->points[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > ON_EPSILON) sides[i] = SIDE_FRONT; else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } sides[i] = sides[0]; dists[i] = dists[0]; if (keepon && !counts[0] && !counts[1]) return in; if (!counts[0]) { FreeWinding (in); return NULL; } if (!counts[1]) return in; maxpts = in->numpoints+4; // can't use counts[0]+2 because // of fp grouping errors neww = NewWinding (maxpts); for (i=0 ; inumpoints ; i++) { p1 = in->points[i]; if (sides[i] == SIDE_ON) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; continue; } if (sides[i] == SIDE_FRONT) { VectorCopy (p1, neww->points[neww->numpoints]); neww->numpoints++; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point p2 = in->points[(i+1)%in->numpoints]; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[j] = split->dist; else if (split->normal[j] == -1) mid[j] = -split->dist; else mid[j] = p1[j] + dot*(p2[j]-p1[j]); } VectorCopy (mid, neww->points[neww->numpoints]); neww->numpoints++; } if (neww->numpoints > maxpts) Error ("ClipWinding: points exceeded estimate"); // free the original winding FreeWinding (in); return neww; } /* ============================================================================= 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 + 0.0001)) { 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]; /* ================ SetFaceColor ================ */ void SetFaceColor (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]; } } /* ================ FaceTextureVectors ================ */ void FaceTextureVectors (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; td = &f->texdef; q = f->d_texture; memset (STfromXYZ, 0, 8*sizeof(float)); if (!td->scale[0]) td->scale[0] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 1; if (!td->scale[1]) td->scale[1] = (g_PrefsDlg.m_bHiColorTextures) ? fTEXTURE_SCALE : 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; } } void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f) { float STfromXYZ[2][4]; FaceTextureVectors (f, STfromXYZ); xyzst[3] = DotProduct (xyzst, STfromXYZ[0]) + STfromXYZ[0][3]; xyzst[4] = DotProduct (xyzst, STfromXYZ[1]) + STfromXYZ[1][3]; } //========================================================================== /* ================= BasePolyForPlane ================= */ winding_t *BasePolyForPlane (plane_t *p) { int i, x; vec_t max, v; vec3_t org, vright, vup; winding_t *w; // find the major axis max = MIN_WORLD_COORD; x = -1; for (i=0 ; i<3; i++) { v = fabs(p->normal[i]); if (v > max) { x = i; max = v; } } if (x==-1) Error ("BasePolyForPlane: no axis found"); VectorCopy (vec3_origin, vup); switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct (vup, p->normal); VectorMA (vup, -v, p->normal, vup); VectorNormalize (vup); VectorScale (p->normal, p->dist, org); CrossProduct (vup, p->normal, vright); VectorScale (vup, MAX_WORLD_COORD, vup); VectorScale (vright, MAX_WORLD_COORD, vright); // project a really big axis aligned box onto the plane w = NewWinding (4); VectorSubtract (org, vright, w->points[0]); VectorAdd (w->points[0], vup, w->points[0]); VectorAdd (org, vright, w->points[1]); VectorAdd (w->points[1], vup, w->points[1]); VectorAdd (org, vright, w->points[2]); VectorSubtract (w->points[2], vup, w->points[2]); VectorSubtract (org, vright, w->points[3]); VectorSubtract (w->points[3], vup, w->points[3]); w->numpoints = 4; return w; } void Brush_MakeFacePlane (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); } void Brush_MakeFacePlanes (brush_t *b) { face_t *f; for (f=b->brush_faces ; f ; f=f->next) { Brush_MakeFacePlane (f); } } 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 // slightly odd code for SHOW_GROUPNAMES I know... // if (g_qeglobals.d_savedinfo.show_names) { name = ValueForKey (b->owner, "classname"); if (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES) { char *psGroupName = ValueForKey (b->owner, sKEYFIELD_GROUPNAME); if (strlen(psGroupName)) { name = va("%s ( %s )",name,psGroupName); } } qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4); qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name); #ifdef QUAKE3 if (strnicmp(name,"waypoint",8)==0) { name = ValueForKey (b->owner, "targetname"); if (name && strlen(name)) // just being safe.... { CString name2; name2.Format("( %s )",name); qglRasterPos3f (b->mins[0]+4, b->mins[1]+4-10, b->mins[2]+4); qglCallLists (strlen(name2), GL_UNSIGNED_BYTE, name2); } } #endif } else { // (this is deliberately not subservient to the above bool) // if (g_qeglobals.d_savedinfo.exclude & SHOW_GROUPNAMES) { name = ValueForKey (b->owner, sKEYFIELD_GROUPNAME); if (strlen(name)) { name = va("( Group: \"%s\" )",name); qglRasterPos3f (b->mins[0]+4, b->mins[1]+4, b->mins[2]+4); qglCallLists (strlen(name), GL_UNSIGNED_BYTE, name); } } } } /* ================= MakeFaceWinding returns the visible polygon on a face ================= */ winding_t *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 = BasePolyForPlane (&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 = ClipWinding (w, &plane, false); if (!w) return w; } if (w->numpoints < 3) { free(w); w = NULL; } if (!w) printf ("unused plane\n"); return w; } 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 */ #define ZERO_EPSILON 0.001 void Brush_Build( brush_t *b, bool bSnap, bool bMarkMap ) { // int order; // face_t *face; // winding_t *w; /* ** build the windings and generate the bounding box */ Brush_BuildWindings(b, bSnap); Patch_BuildPoints (b); /* b->alphaBrush = false; for (face_t *f=b->brush_faces ; f; f=f->next) { if (f->texdef.flags & SURF_ALPHA) { b->alphaBrush = true; break; } } */ /* ** 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 (bMarkMap) { Sys_MarkMapModified(); } } /* ================= Brush_Parse The brush is NOT linked to any list ================= */ brush_t *Brush_Parse (void) { brush_t *b; face_t *f; int i,j; g_qeglobals.d_parsed_brushes++; b = (brush_t*)qmalloc(sizeof(brush_t)); do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; 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 { 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; } } } // read the texturedef GetToken (false); strcpy(f->texdef.name, token); 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 (but all new maps saved in this version *do* write them) 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); } #ifdef SOF // optional fields here depending on flags... // if(f->texdef.flags & 0x400) // tall wall, read lighting values { if (TokenAvailable()) { GetToken (false); f->texdef.lighting[0] = atof(token); // red GetToken (false); f->texdef.lighting[1] = atof(token); // green GetToken (false); f->texdef.lighting[2] = atof(token); // blue GetToken (false); f->texdef.lighting[3] = atof(token); // alpha } else { Error ("Brush_Parse: Expecting tall-wall lighting values, but none found!"); } } #endif } while (1); return b; } /* ================= Brush_Write ================= */ void Brush_Write (brush_t *b, FILE *f) { face_t *fa; char *pname; int i; if (b->patchBrush) { Patch_Write(b->nPatchID, f); return; } fprintf (f, "{\n"); for (fa=b->brush_faces ; fa ; fa=fa->next) { if (g_PrefsDlg.m_bNoClamp) { 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, ") "); } } else { for (i=0 ; i<3 ; i++) { fprintf (f, "( %i %i %i ) ", (int)fa->planepts[i][0] , (int)fa->planepts[i][1], (int)fa->planepts[i][2]); } } pname = fa->texdef.name; if (pname[0] == 0) pname = "unnamed"; fprintf (f, "%s %i %i %i ", #ifdef QUAKE3 strlwr #endif (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]); // only output flags and value if not default // KPRadiant (and any tga setup always needs to write them unless the build tools read the ini file #if 1 //if (!(g_pParentWnd->GetPlugInMgr().GetTextureInfo() && g_pParentWnd->GetPlugInMgr().GetTextureInfo()->m_bHalfLife)) //{ fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); //} #else bool bFlagsOut = false; if (!fa->d_texture || fa->texdef.value != fa->d_texture->value || fa->texdef.flags != fa->d_texture->flags || fa->texdef.contents != fa->d_texture->contents) { fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); bFlagsOut = true; } #endif #ifdef SOF if(fa->texdef.flags & 0x400) // tall wall, write lighting values { fprintf (f, " %f %f %f %f", fa->texdef.lighting[0], fa->texdef.lighting[1], fa->texdef.lighting[2], fa->texdef.lighting[3]); } #endif fprintf (f, "\n"); } fprintf (f, "}\n"); } /* ================= Brush_Write to a CMemFile* ================= */ void Brush_Write (brush_t *b, CMemFile *pMemFile) { face_t *fa; char *pname; int i; if (b->patchBrush) { Patch_Write(b->nPatchID, pMemFile); return; } MemFile_fprintf (pMemFile, "{\n"); for (fa=b->brush_faces ; fa ; fa=fa->next) { if (g_PrefsDlg.m_bNoClamp) { 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, ") "); } } else { for (i=0 ; i<3 ; i++) { MemFile_fprintf (pMemFile, "( %i %i %i ) ", (int)fa->planepts[i][0] , (int)fa->planepts[i][1], (int)fa->planepts[i][2]); } } 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]); // only output flags and value if not default bool bFlagsOut = false; if (fa->texdef.value != fa->d_texture->value || fa->texdef.flags != fa->d_texture->flags || fa->texdef.contents != fa->d_texture->contents) { MemFile_fprintf (pMemFile, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value); bFlagsOut = true; } #ifdef SOF if(fa->texdef.flags & 0x400) // tall wall, write lighting values { MemFile_fprintf (pMemFile, " %f %f %f %f", fa->texdef.lighting[0], fa->texdef.lighting[1], fa->texdef.lighting[2], fa->texdef.lighting[3]); } #endif 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; for (i=0 ; i<3 ; i++) if (maxs[i] < mins[i]) Error ("Brush_InitSolid: backwards"); b = (brush_t*)qmalloc (sizeof(brush_t)); 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) { 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_t*)qmalloc (sizeof(brush_t)); 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 brushhave the given number of 2d sides ============= */ void Brush_MakeSided (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_t*)qmalloc (sizeof(brush_t)); // create top face f = Face_Alloc(); f->texdef = *texdef; f->next = b->brush_faces; b->brush_faces = f; f->planepts[2][0] = mins[0];f->planepts[2][1] = mins[1];f->planepts[2][2] = maxs[2]; f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = maxs[2]; f->planepts[0][0] = maxs[0];f->planepts[0][1] = maxs[1];f->planepts[0][2] = maxs[2]; // 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] = f->planepts[0][0]; f->planepts[1][1] = f->planepts[0][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_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. ============= */ void Brush_Free (brush_t *b) { face_t *f, *next; // free faces for (f=b->brush_faces ; f ; f=next) { next = f->next; Face_Free( f ); } // unlink from active/selected list if (b->next) Brush_RemoveFromList (b); // unlink from entity list if (b->onext) Entity_UnlinkBrush (b); free (b); } /* ============ Brush_Clone Does NOT add the new brush to any lists ============ */ brush_t *Brush_Clone (brush_t *b) { brush_t *n; face_t *f, *nf; n = (brush_t*)qmalloc(sizeof(brush_t)); 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_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; double frac, d1, d2; float scaleValue; bool scale; int i; VectorCopy (origin, p1); // Need to clamp the p1 - p2 vector to compelety inside the valid range // This is horrid.. =) scale = true; scaleValue = WORLD_SIZE; while(scale) { VectorMA(p1, scaleValue, dir, p2); scale = false; for(i = 0; i < 3; i++) { if(p2[i] > MAX_WORLD_COORD) { scale = true; } if(p2[i] < MIN_WORLD_COORD) { scale = true; } } scaleValue *= 0.8f; } scaleValue /= 0.8f; VectorMA(p1, scaleValue, dir, p2); // ..end of nastiness for (f=b->brush_faces ; f ; f=f->next) { d1 = (double) DotProduct (p1, f->plane.normal) - f->plane.dist; d2 = (double) 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] = (double)p1[i] + frac * ((double)p2[i] - (double)p1[i]); } else { for (i=0 ; i<3 ; i++) p2[i] = (double)p1[i] + frac * ((double)p2[i] - (double)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, bool bAddToBackOfList /* = false */) { if (b->next || b->prev) Error ("Brush_AddToList: already linked"); if (b->patchBrush && list == &selected_brushes) { Patch_Select(b->nPatchID); } if (bAddToBackOfList) { // lists in radiant are circular, so no need to traverse to list end, just link to list-head previous... // b->prev = list->prev; list->prev->next = b; list->prev = b; b->next = list; } else { b->next = list->next; list->next->prev = b; list->next = b; b->prev = list; } } void Brush_RemoveFromList (brush_t *b) { if (!b->next || !b->prev) Error ("Brush_RemoveFromList: not linked"); if (b->patchBrush) { Patch_Deselect(b->nPatchID); } b->next->prev = b->prev; b->prev->next = b->next; b->next = b->prev = NULL; } /* =============== SetFaceTexdef Doesn't set the curve flags =============== */ void SetFaceTexdef (brush_t *b, face_t *f, texdef_t *texdef, bool bFitScale, bool bNoSystemTextureOverwrite) { int oldFlags; int oldContents; face_t *tf; if (bNoSystemTextureOverwrite && !strnicmp(f->texdef.name,"system/",7)) { Sys_Printf("( System-shader protecion: Refusing to overwrite face using \"%s\" )\n",f->texdef.name); return; } oldFlags = f->texdef.flags; oldContents = f->texdef.contents; 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); // 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, bool bFitScale/*=false*/, bool bNoSystemTextureOverwrite/*=false*/) { for (face_t* f = b->brush_faces ; f ; f = f->next) { SetFaceTexdef (b, f, texdef, bFitScale, bNoSystemTextureOverwrite); } Brush_Build( b ); if (b->patchBrush) { Patch_SetTexture(b->nPatchID, 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; static overflow = 0; if (D_MAXPOINTS == g_qeglobals.d_num_move_points) //jfm: stop this overflow { overflow++; Sys_Printf ("Trying to drag too many points!! %d\n",D_MAXPOINTS + overflow); return 0; } overflow = 0; 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 = 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, WORLD_SIZE, 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] = MAX_WORLD_COORD; b->maxs[0] = b->maxs[1] = b->maxs[2] = MIN_WORLD_COORD; 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 = 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 SetFaceColor (b, face, fCurveColor); fCurveColor -= .10; if (fCurveColor <= 0) fCurveColor = 1.0; //BeginTexturingFace( b, face, face->d_texture); 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_Scale2 (eclass_t *e,brush_t *b, float scale, vec3_t vOrigin, bool bBuild) { vec3_t pts[4][2]; int i,j; face_t* f; pts[0][0][0] = e->mins[0]; pts[0][0][1] = e->mins[1]; pts[1][0][0] = e->mins[0]; pts[1][0][1] = e->maxs[1]; pts[2][0][0] = e->maxs[0]; pts[2][0][1] = e->maxs[1]; pts[3][0][0] = e->maxs[0]; pts[3][0][1] = e->mins[1]; for (i=0 ; i<4 ; i++) { pts[i][0][2] = e->mins[2]; pts[i][1][0] = pts[i][0][0]; pts[i][1][1] = pts[i][0][1]; pts[i][1][2] = e->maxs[2]; } f=b->brush_faces; for (i=0;i<4; i++,f=f->next) { 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]); } VectorCopy (pts[0][1], f->planepts[0]); VectorCopy (pts[1][1], f->planepts[1]); VectorCopy (pts[2][1], f->planepts[2]); f=f->next; VectorCopy (pts[2][0], f->planepts[0]); VectorCopy (pts[1][0], f->planepts[1]); VectorCopy (pts[0][0], f->planepts[2]); for (f=b->brush_faces ; f ; f=f->next) { for (int i=0 ; i<3 ; i++) { VectorScale(f->planepts[i], scale, f->planepts[i]); VectorAdd(vOrigin,f->planepts[i],f->planepts[i]); } } if (bBuild) { Brush_Build(b, false, false); } } // 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] = MAX_WORLD_COORD; vMax[0] = vMax[1] = vMax[2] = MIN_WORLD_COORD; 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 (strnicmp(b->owner->eclass->name, "misc_model",10) == 0) { 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); /* // we need to scale the brush to the proper size based on the model load 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; vMid[0] = vMid[1] = vMid[2] = 0.0; for (int j=0 ; j<3 ; j++) { vMid[j] = (b->mins[j] + (vTemp[j] / 2)); } for (face_t* f=b->brush_faces ; f ; f=f->next) { for (int i=0 ; i<3 ; i++) { //scale VectorSubtract(f->planepts[i], vMid, 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], vMid, f->planepts[i]); } } //Brush_SnapToGrid(b); Brush_Build(b, true); */ b->bModelFailed = false; } else { b->bModelFailed = true; } } } return e; } static bool g_bInPaintedModel = false; bool PaintedModel(brush_t *b, bool bOkToTexture, bool bIsGhost) { if (g_bInPaintedModel) { return true; } if (g_PrefsDlg.m_nEntityShowState == ENTITY_BOX) { 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"); //============== #ifdef QUAKE3 bool bUseAnglesCode = false; vec3_t v3Angles; if (bOkToTexture) { vec3_t v3; if (GetVectorForKey (b->owner, "angles", v3)) { // for some reason this program's own maths routines expect entity axis orders differently, so... // v3Angles[0] = v3[2]; v3Angles[1] = v3[0]; v3Angles[2] = v3[1]; bUseAnglesCode = true; } } #endif //============== 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); // this is dumb, 3rd line just overwrites the other two -slc if (bIsGhost) { qglColor4fv(v4GhostColor); qglLineStipple( 8, 0xAAAA); qglEnable(GL_LINE_STIPPLE); } //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); } qglBegin (GL_TRIANGLES); for (i = 0; i < model->nTriCount; i++) { for (j = 0; j < 3; j++) { //============== #ifdef QUAKE3 if (bUseAnglesCode) { vec3_t v3; VectorSet(v3, model->pTriList[i].v[j][0] + v[0], model->pTriList[i].v[j][1] + v[1], model->pTriList[i].v[j][2] + v[2] ); VectorRotate(v3, v3Angles, b->owner->origin, v3); qglTexCoord2f (model->pTriList[i].st[j][0], model->pTriList[i].st[j][1]); qglVertex3f(v3[0], v3[1], v3[2]); } else #endif //============== { 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]); } } } 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); } if (bIsGhost) { qglDisable(GL_LINE_STIPPLE); } model = model->pNext; } if (g_PrefsDlg.m_nEntityShowState & ENTITY_BOXED) { if (bIsGhost) { qglColor4fv(v4GhostColor); qglLineStipple( 8, 0xAAAA); qglEnable(GL_LINE_STIPPLE); } else { qglColor3fv(pEclass->color); } qglBegin(GL_LINE_LOOP); qglVertex3f(b->mins[0],b->mins[1],b->mins[2]); qglVertex3f(b->maxs[0],b->mins[1],b->mins[2]); qglVertex3f(b->maxs[0],b->maxs[1],b->mins[2]); qglVertex3f(b->mins[0],b->maxs[1],b->mins[2]); qglEnd(); qglBegin(GL_LINE_LOOP); qglVertex3f(b->mins[0],b->mins[1],b->maxs[2]); qglVertex3f(b->maxs[0],b->mins[1],b->maxs[2]); qglVertex3f(b->maxs[0],b->maxs[1],b->maxs[2]); qglVertex3f(b->mins[0],b->maxs[1],b->maxs[2]); qglEnd(); qglBegin(GL_LINES); qglVertex3f(b->mins[0],b->mins[1],b->mins[2]); qglVertex3f(b->mins[0],b->mins[1],b->maxs[2]); qglVertex3f(b->mins[0],b->maxs[1],b->maxs[2]); qglVertex3f(b->mins[0],b->maxs[1],b->mins[2]); qglVertex3f(b->maxs[0],b->mins[1],b->mins[2]); qglVertex3f(b->maxs[0],b->mins[1],b->maxs[2]); qglVertex3f(b->maxs[0],b->maxs[1],b->maxs[2]); qglVertex3f(b->maxs[0],b->maxs[1],b->mins[2]); qglEnd(); if (bIsGhost) { qglDisable(GL_LINE_STIPPLE); } } qglPopAttrib(); bReturn = true; } else { b->bModelFailed = true; } g_bInPaintedModel = false; return bReturn; } 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(); } extern qboolean qbCheckShowFaces; void Brush_Draw( brush_t *b, bool bIsGhost ) { face_t *face; int i, order; qtexture_t *prev = 0; winding_t *w; if (b->hiddenBrush) { return; } if (b->patchBrush) { Patch_DrawCam(b->nPatchID, bIsGhost); // if (!g_bPatchShowBounds) return; } int nDrawMode = g_pParentWnd->GetCamera()->Camera().draw_mode; if (b->owner->eclass->fixedsize) { /* MERGEME if(!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_ANGLES) && !strnicmp(b->owner->eclass->name, "info_player", 11)) { Brush_DrawFacingAngle(b, b->owner); } */ if (g_PrefsDlg.m_bNewLightDraw && strcmpi(b->owner->eclass->name, "light") == 0) { 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, bIsGhost); 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 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 ); } if (bIsGhost) { qglColor4fv( v4GhostColor ); qglDisable (GL_TEXTURE_2D); } // draw the polygon qglBegin(GL_POLYGON); if (nDrawMode == cd_light) qglNormal3fv(face->plane.normal); for (i=0 ; inumpoints ; i++) { if (nDrawMode == cd_texture || nDrawMode == cd_light) qglTexCoord2fv( &w->points[i][3] ); qglVertex3fv(w->points[i]); } qglEnd(); // draw face lines over top of faces (artist/designer request)... // drawing them as tris helps show how much overlap.waste there is because of diagonal lines // if (qbCheckShowFaces && qbShowFaces) { qglDisable (GL_TEXTURE_2D); if (bIsGhost) { qglColor4fv( v4GhostColor ); } else { qglColor3f(255,255,255); } for (i=2 ; inumpoints ; i++) { qglBegin(GL_LINE_STRIP); qglVertex3fv (w->points[0]); qglVertex3fv (w->points[i-1]); qglVertex3fv (w->points[i]); qglVertex3fv (w->points[0]); qglEnd (); } if (nDrawMode == cd_texture || nDrawMode == cd_light) qglEnable (GL_TEXTURE_2D); } if (bIsGhost) { qglEnable (GL_TEXTURE_2D); } } #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(); } bool gbSelectedLightBrushesAlwaysDrawRadii = false; //#define DEGTORAD 0.01745329252 //#define RADTODEG 57.295779513 void Brush_DrawXY(brush_t *b, int nViewType, bool bIsGhost) { face_t *face; int order; winding_t *w; int i; if (b->hiddenBrush) { return; } if (b->patchBrush) { Patch_DrawXY(b->nPatchID, bIsGhost); if (!g_bPatchShowBounds) return; } if (b->owner->eclass->fixedsize) { if (g_PrefsDlg.m_bNewLightDraw && strcmpi(b->owner->eclass->name, "light") == 0) { 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); // now draw lighting radius stuff... // if (!(g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS_RADII) || (gbSelectedLightBrushesAlwaysDrawRadii && IsBrushSelected(b)) ) { vec3_t v3Origin; v3Origin[0] = b->mins[0] + (b->maxs[0] - b->mins[0]) / 2; v3Origin[1] = b->mins[1] + (b->maxs[1] - b->mins[1]) / 2; v3Origin[2] = b->mins[2] + (b->maxs[2] - b->mins[2]) / 2; char *p = ValueForKey(b->owner, "light"); if (p[0]==0) { p = "300"; } float f = atof(p); float fLight = f; float f45Len = 0.707; vec3_t v3TargetOrigin; bool bDrawSpotlightArc = false; LPCSTR psTargetName = ValueForKey(b->owner, "target"); bool bIsSpotLight = !!psTargetName[0]; if (bIsSpotLight) { char *p = ValueForKey(b->owner, "radius"); if (p[0]==0) { p="64"; } f = atof(p); // find the origin of the target... // entity_t *e = FindEntity("targetname", psTargetName); if (e) { v3TargetOrigin[0] = e->brushes.mins[0] + (e->brushes.maxs[0] - e->brushes.mins[0]) / 2; v3TargetOrigin[1] = e->brushes.mins[1] + (e->brushes.maxs[1] - e->brushes.mins[1]) / 2; v3TargetOrigin[2] = e->brushes.mins[2] + (e->brushes.maxs[2] - e->brushes.mins[2]) / 2; bDrawSpotlightArc = true; } } for (int iPass=0; iPass<2; iPass++) { if (iPass) { float _f = f; #define iFLAGS_LINEAR 0x01 #define iFLAGS_NOINCIDENCE 0x02 if (bIsSpotLight) { if (bDrawSpotlightArc) { // I give up on this, it's beyond me } } else { int iFlags = atoi( ValueForKey(b->owner,"spawnflags") ); if (iFlags & iFLAGS_NOINCIDENCE) { f *= 0.9; } else { f/=4; } p = ValueForKey(b->owner, "scale"); if (p[0]==0) { p="1"; } float fScale = atof(p); f *= fScale; // clamp inner dotted circle so it never exceeds outer... // if (f>_f) f=_f; } qglLineStipple( 8, 0xAAAA); qglEnable(GL_LINE_STIPPLE); } qglBegin(GL_LINE_LOOP); { if (bIsSpotLight) { if (bDrawSpotlightArc) { // I give up on this, it's beyond me /* vec3_t v3Temp; VectorSubtract(v3TargetOrigin,v3Origin,v3Temp); float fAdjacent = VectorLength(v3Temp); float fOpposite = f/2; float f2 = atan2(fOpposite,fAdjacent); f2 *= 180*Q_PI; // radtodeg float x, y, a; switch (g_pParentWnd->ActiveXY()->GetViewType()) { case XY: x = v3Origin[0]; y = v3Origin[1]; a = f2; break; case XZ: x = v3Origin[0]; y = v3Origin[2]; a = f2; break; case YZ: x = v3Origin[1]; y = v3Origin[2]; a = f2; break; } // findmeste qglVertex3f (x+48*cos(a+Q_PI/(360.0f/a)), y+48*sin(a+Q_PI/(360.0f/a)), 0); qglVertex3f (x, y, 0); qglVertex3f (x+48*cos(a-Q_PI/(360.0f/a)), y+48*sin(a-Q_PI/(360.0f/a)), 0); qglEnd (); */ } } else { switch (g_pParentWnd->ActiveXY()->GetViewType()) { case XY: qglVertex3f( v3Origin[0]+(f*0), v3Origin[1]+(f*1), v3Origin[2] ); qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1]+(f*f45Len), v3Origin[2] ); qglVertex3f( v3Origin[0]+(f*1), v3Origin[1]+(f*0), v3Origin[2] ); qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1]+(-f*f45Len), v3Origin[2] ); qglVertex3f( v3Origin[0]+(f*0), v3Origin[1]+(-f*1), v3Origin[2] ); qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1]+(-f*f45Len), v3Origin[2] ); qglVertex3f( v3Origin[0]+(-f*1), v3Origin[1]+(f*0), v3Origin[2] ); qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1]+(f*f45Len), v3Origin[2] ); break; case XZ: qglVertex3f( v3Origin[0]+(f*0), v3Origin[1], v3Origin[2]+(f*1) ); qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1], v3Origin[2]+(f*f45Len) ); qglVertex3f( v3Origin[0]+(f*1), v3Origin[1], v3Origin[2]+(f*0) ); qglVertex3f( v3Origin[0]+(f*f45Len), v3Origin[1], v3Origin[2]+(-f*f45Len) ); qglVertex3f( v3Origin[0]+(f*0), v3Origin[1], v3Origin[2]+(-f*1) ); qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1], v3Origin[2]+(-f*f45Len) ); qglVertex3f( v3Origin[0]+(-f*1), v3Origin[1], v3Origin[2]+(f*0) ); qglVertex3f( v3Origin[0]+(-f*f45Len), v3Origin[1], v3Origin[2]+(f*f45Len) ); break; case YZ: qglVertex3f( v3Origin[0], v3Origin[1]+(f*0), v3Origin[2]+(f*1) ); qglVertex3f( v3Origin[0], v3Origin[1]+(f*f45Len), v3Origin[2]+(f*f45Len) ); qglVertex3f( v3Origin[0], v3Origin[1]+(f*1), v3Origin[2]+(f*0) ); qglVertex3f( v3Origin[0], v3Origin[1]+(f*f45Len), v3Origin[2]+(-f*f45Len) ); qglVertex3f( v3Origin[0], v3Origin[1]+(f*0), v3Origin[2]+(-f*1) ); qglVertex3f( v3Origin[0], v3Origin[1]+(-f*f45Len), v3Origin[2]+(-f*f45Len) ); qglVertex3f( v3Origin[0], v3Origin[1]+(-f*1), v3Origin[2]+(f*0) ); qglVertex3f( v3Origin[0], v3Origin[1]+(-f*f45Len), v3Origin[2]+(f*f45Len) ); break; } } } qglEnd(); } qglDisable(GL_LINE_STIPPLE); } return; } else if (strnicmp(b->owner->eclass->name, "misc_model",10) == 0) { if (PaintedModel(b, false, bIsGhost)) 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; //xxxxxxxxxxxxxxxxxxxxxxxxxxxxx // draw the polygon qglBegin(GL_LINE_LOOP); for (i=0 ; inumpoints ; i++) qglVertex3fv(w->points[i]); qglEnd(); // draw angle arrows, but only if viewing from top down, and it's an ent brush with an "angle" field... // if (nViewType == XY && b->owner && (b->owner != world_entity) && strlen(ValueForKey(b->owner,"angle"))) { // draw directional arrow... // // (work out size first) // float fMaxX = MIN_WORLD_COORD; float fMinX = MAX_WORLD_COORD; float fMaxY = MIN_WORLD_COORD; float fMinY = MAX_WORLD_COORD; float fMaxZ = MIN_WORLD_COORD; float fMinZ = MAX_WORLD_COORD; for (i=0 ; inumpoints ; i++) { fMaxX = max(fMaxX, w->points[i][0]); fMinX = min(fMinX, w->points[i][0]); fMaxY = max(fMaxY, w->points[i][1]); fMinY = min(fMinY, w->points[i][1]); fMaxZ = max(fMaxZ, w->points[i][2]); // Z stuff not really important, but useful for coords later so WTF? fMinZ = min(fMinZ, w->points[i][2]); } float fSmallest= min( (fMaxX-fMinX) , (fMaxY-fMinY) ); // do NOT use Z for smallest calc! fSmallest*= 0.8f; vec3_t v3Centre = { fMinX + ((fMaxX-fMinX)/2), fMinY + ((fMaxY-fMinY)/2), fMinZ + ((fMaxZ-fMinZ)/2), }; vec3_t v3Top; VectorCopy(v3Centre,v3Top); v3Top[1] += fSmallest/2; vec3_t v3LTArrowEdge = {v3Centre[0] - fSmallest/4, v3Centre[1] - fSmallest/4, v3Top[2]}; vec3_t v3RTArrowEdge = {v3Centre[0] + fSmallest/4, v3Centre[1] - fSmallest/4, v3Top[2]}; vec3_t v3Angle = {0,0, FloatForKey (b->owner, "angle")-90}; VectorRotate(v3Top, v3Angle, v3Centre, v3Top); VectorRotate(v3LTArrowEdge, v3Angle, v3Centre, v3LTArrowEdge); VectorRotate(v3RTArrowEdge, v3Angle, v3Centre, v3RTArrowEdge); qglBegin(GL_LINE_LOOP); qglVertex3fv(v3Top); qglVertex3fv(v3LTArrowEdge); qglVertex3fv(v3Centre); qglVertex3fv(v3RTArrowEdge); qglEnd(); } } DrawBrushEntityName (b); } face_t *Face_Alloc( void ) { face_t *f = (face_t*)qmalloc( sizeof( *f ) ); return f; } void Face_Free( face_t *f ) { assert( f != 0 ); if ( f->face_winding ) { free( f->face_winding ); f->face_winding = 0; } free( f ); } void Clamp(float& f, int nClamp) { float fFrac = f - static_cast(f); f = static_cast(f) % nClamp; f += fFrac; } void Face_MoveTexture(face_t *f, vec3_t delta) { vec3_t vX, vY; 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; // fTEXTURE_SCALE? if (!f->texdef.scale[1]) f->texdef.scale[1] = 1; // fTEXTURE_SCALE? f->texdef.shift[0] -= vShift[0] / f->texdef.scale[0]; f->texdef.shift[1] -= vShift[1] / f->texdef.scale[1]; // clamp the shifts //f->texdef.shift[0] = static_cast(f->texdef.shift[0]) % f->d_texture->width; //f->texdef.shift[1] = static_cast(f->texdef.shift[1]) % f->d_texture->height; Clamp(f->texdef.shift[0], f->d_texture->width); Clamp(f->texdef.shift[1], f->d_texture->height); } /* ============ 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); } // 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_t*)qmalloc (sizeof(brush_t)); // 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_t*)qmalloc (sizeof(brush_t)); 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 Brush_FitTexture( brush_t *b, int nHeight, int nWidth ) { face_t *face; for (face = b->brush_faces ; face ; face=face->next) { Face_FitTexture( face, nHeight, nWidth ); } } void ClearBounds (vec3_t mins, vec3_t maxs) { mins[0] = mins[1] = mins[2] = MAX_WORLD_COORD; maxs[0] = maxs[1] = maxs[2] = MIN_WORLD_COORD; } void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs) { int i; vec_t val; for (i=0 ; i<3 ; i++) { val = v[i]; if (val < mins[i]) mins[i] = val; if (val > maxs[i]) maxs[i] = val; } } 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 ); } //fitcode // // get the current angle // // hack, swap sides if 90 or 270 degrees, fixes a non-fit bug... // if (fabs(td->rotate - 90) < 0.001f || fabs(td->rotate - 270) < 0.001f) { int iTemp = nHeight; nHeight = nWidth; nWidth = iTemp; } 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 = MAX_WORLD_COORD; max_s = max_t = MIN_WORLD_COORD; 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 (s > max_s) { max_s = s; } if (s < min_s) { min_s = s; } if (t < min_t) { min_t = t; } if (t > max_t) { max_t = t; } //////////// 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); }