/** * @brief Creates a new axial brush */ bspbrush_t* BrushFromBounds (const vec3_t mins, const vec3_t maxs) { bspbrush_t* b; int i; vec3_t normal; b = AllocBrush(6); b->numsides = 6; for (i = 0; i < 3; i++) { VectorClear(normal); normal[i] = 1; vec_t dist = maxs[i]; b->sides[i].planenum = FindOrCreateFloatPlane(normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3 + i].planenum = FindOrCreateFloatPlane(normal, dist); } CreateBrushWindings(b); return b; }
/** * @brief Builds a plane normal and distance from three points on the plane. * If the normal is nearly axial, it will be snapped to be axial. Looks * up the plane in the unique planes. * @param[in] b The brush that the points belong to. * @param[in] p0 Three points on the plane. (A vector with plane coordinates) * @param[in] p1 Three points on the plane. (A vector with plane coordinates) * @param[in] p2 Three points on the plane. (A vector with plane coordinates) * @return the index of the plane in the planes list. */ static int16_t PlaneFromPoints (const mapbrush_t* b, const vec3_t p0, const vec3_t p1, const vec3_t p2) { vec3_t t1, t2, normal; vec_t dist; VectorSubtract(p0, p1, t1); VectorSubtract(p2, p1, t2); CrossProduct(t1, t2, normal); VectorNormalize(normal); dist = DotProduct(p0, normal); if (!VectorNotEmpty(normal)) Sys_Error("PlaneFromPoints: Bad normal (null) for brush %i", b->brushnum); return FindOrCreateFloatPlane(normal, dist); }
/** * @brief If there was an origin brush, offset all of the planes and texinfo * @note Used for e.g. func_door or func_rotating */ static void AdjustBrushesForOrigin (const entity_t* ent) { for (int i = 0; i < ent->numbrushes; i++) { mapbrush_t* b = &mapbrushes[ent->firstbrush + i]; for (int j = 0; j < b->numsides; j++) { side_t* s = &b->original_sides[j]; const ptrdiff_t index = s - brushsides; const vec_t newdist = mapplanes[s->planenum].dist - DotProduct(mapplanes[s->planenum].normal, ent->origin); s->surfaceFlags |= SURF_ORIGIN; side_brushtextures[index].surfaceFlags |= SURF_ORIGIN; s->planenum = FindOrCreateFloatPlane(mapplanes[s->planenum].normal, newdist); s->texinfo = TexinfoForBrushTexture(&mapplanes[s->planenum], &side_brushtextures[index], ent->origin, s->contentFlags & CONTENTS_TERRAIN); s->brush = b; } /* create windings for sides and bounds for brush */ MakeBrushWindings(b); } }
/** * @brief Writes the brush list to the bsp */ void EmitBrushes (void) { int i, j, bnum, s, x; vec3_t normal; vec_t dist; int planenum; curTile->numbrushsides = 0; curTile->numbrushes = nummapbrushes; /* Clear out curTile->brushes */ OBJZERO(curTile->brushes); for (bnum = 0; bnum < nummapbrushes; bnum++) { const mapbrush_t* b = &mapbrushes[bnum]; dBspBrush_t* db = &curTile->dbrushes[bnum]; cBspBrush_t* cb = &curTile->brushes[bnum]; db->contentFlags = b->contentFlags; db->firstbrushside = curTile->numbrushsides; db->numsides = b->numsides; cb->contentFlags = b->contentFlags; cb->firstbrushside = curTile->numbrushsides; cb->numsides = b->numsides; cb->checkcount = 0; for (j = 0; j < b->numsides; j++) { if (curTile->numbrushsides == MAX_MAP_BRUSHSIDES) { Sys_Error("MAX_MAP_BRUSHSIDES (%i)", curTile->numbrushsides); } else { dBspBrushSide_t* cp = &curTile->brushsides[curTile->numbrushsides]; curTile->numbrushsides++; cp->planenum = b->original_sides[j].planenum; cp->texinfo = b->original_sides[j].texinfo; } } /* add any axis planes not contained in the brush to bevel off corners */ for (x = 0; x < 3; x++) for (s = -1; s <= 1; s += 2) { /* add the plane */ VectorCopy(vec3_origin, normal); normal[x] = (float)s; if (s == -1) dist = -b->mins[x]; else dist = b->maxs[x]; planenum = FindOrCreateFloatPlane(normal, dist); for (i = 0; i < b->numsides; i++) if (b->original_sides[i].planenum == planenum) break; if (i == b->numsides) { if (curTile->numbrushsides >= MAX_MAP_BRUSHSIDES) Sys_Error("MAX_MAP_BRUSHSIDES (%i)", curTile->numbrushsides); curTile->brushsides[curTile->numbrushsides].planenum = planenum; curTile->brushsides[curTile->numbrushsides].texinfo = curTile->brushsides[curTile->numbrushsides - 1].texinfo; curTile->numbrushsides++; db->numsides++; cb->numsides++; } } } }
/** * @brief Adds any additional planes necessary to allow the brush to be expanded * against axial bounding boxes */ static void AddBrushBevels (mapbrush_t* b) { int axis, dir; int i, l, order; vec3_t normal; /* add the axial planes */ order = 0; for (axis = 0; axis < 3; axis++) { for (dir = -1; dir <= 1; dir += 2, order++) { side_t* s; /* see if the plane is already present */ for (i = 0, s = b->original_sides; i < b->numsides; i++, s++) { if (mapplanes[s->planenum].normal[axis] == dir) break; } if (i == b->numsides) { /* add a new side */ float dist; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Sys_Error("MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides); nummapbrushsides++; b->numsides++; VectorClear(normal); normal[axis] = dir; if (dir == 1) dist = b->mbBox.maxs[axis]; else dist = -b->mbBox.mins[axis]; s->planenum = FindOrCreateFloatPlane(normal, dist); s->texinfo = b->original_sides[0].texinfo; s->contentFlags = b->original_sides[0].contentFlags; s->bevel = true; c_boxbevels++; } /* if the plane is not in it canonical order, swap it */ if (i != order) { const ptrdiff_t index = b->original_sides - brushsides; side_t sidetemp = b->original_sides[order]; brush_texture_t tdtemp = side_brushtextures[index + order]; b->original_sides[order] = b->original_sides[i]; b->original_sides[i] = sidetemp; side_brushtextures[index + order] = side_brushtextures[index + i]; side_brushtextures[index + i] = tdtemp; } } } /* add the edge bevels */ if (b->numsides == 6) return; /* pure axial */ /* test the non-axial plane edges */ for (i = 6; i < b->numsides; i++) { side_t* s = b->original_sides + i; winding_t* w = s->winding; if (!w) continue; for (int j = 0; j < w->numpoints; j++) { int k = (j + 1) % w->numpoints; vec3_t vec; VectorSubtract(w->p[j], w->p[k], vec); if (VectorNormalize(vec) < 0.5) continue; SnapVector(vec); for (k = 0; k < 3; k++) if (vec[k] == -1 || vec[k] == 1 || (vec[k] == 0.0f && vec[(k + 1) % 3] == 0.0f)) break; /* axial */ if (k != 3) continue; /* only test non-axial edges */ /* try the six possible slanted axials from this edge */ for (axis = 0; axis < 3; axis++) { for (dir = -1; dir <= 1; dir += 2) { /* construct a plane */ vec3_t vec2 = {0, 0, 0}; float dist; side_t* s2; vec2[axis] = dir; CrossProduct(vec, vec2, normal); if (VectorNormalize(normal) < 0.5) continue; dist = DotProduct(w->p[j], normal); /* if all the points on all the sides are * behind this plane, it is a proper edge bevel */ for (k = 0; k < b->numsides; k++) { winding_t* w2; float minBack; /* @note: This leads to different results on different archs * due to float rounding/precision errors - use the -ffloat-store * feature of gcc to 'fix' this */ /* if this plane has already been used, skip it */ if (PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist)) break; w2 = b->original_sides[k].winding; if (!w2) continue; minBack = 0.0f; for (l = 0; l < w2->numpoints; l++) { const float d = DotProduct(w2->p[l], normal) - dist; if (d > 0.1) break; /* point in front */ if (d < minBack) minBack = d; } /* if some point was at the front */ if (l != w2->numpoints) break; /* if no points at the back then the winding is on the bevel plane */ if (minBack > -0.1f) break; } if (k != b->numsides) continue; /* wasn't part of the outer hull */ /* add this plane */ if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Sys_Error("MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides); nummapbrushsides++; s2 = &b->original_sides[b->numsides]; s2->planenum = FindOrCreateFloatPlane(normal, dist); s2->texinfo = b->original_sides[0].texinfo; s2->contentFlags = b->original_sides[0].contentFlags; s2->bevel = true; c_edgebevels++; b->numsides++; } } } } }