/* ================= CheckWinding ================= */ void CheckWinding( winding_t *w ) { int i, j; vec_t *p1, *p2; vec_t d, edgedist; vec3_t dir, edgenormal, facenormal; vec_t area; vec_t facedist; if ( w->numpoints < 3 ) { Com_Error( ERR_DROP, "CheckWinding: %i points",w->numpoints ); } area = WindingArea( w ); if ( area < 1 ) { Com_Error( ERR_DROP, "CheckWinding: %f area", area ); } WindingPlane( w, facenormal, &facedist ); for ( i = 0 ; i < w->numpoints ; i++ ) { p1 = w->p[i]; for ( j = 0 ; j < 3 ; j++ ) if ( p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE ) { Com_Error( ERR_DROP, "CheckFace: BUGUS_RANGE: %f",p1[j] ); } j = i + 1 == w->numpoints ? 0 : i + 1; // check the point is on the face plane d = DotProduct( p1, facenormal ) - facedist; if ( d < -ON_EPSILON || d > ON_EPSILON ) { Com_Error( ERR_DROP, "CheckWinding: point off plane" ); } // check the edge isnt degenerate p2 = w->p[j]; VectorSubtract( p2, p1, dir ); if ( VectorLength( dir ) < ON_EPSILON ) { Com_Error( ERR_DROP, "CheckWinding: degenerate edge" ); } CrossProduct( facenormal, dir, edgenormal ); VectorNormalize2( edgenormal, edgenormal ); edgedist = DotProduct( p1, edgenormal ); edgedist += ON_EPSILON; // all other points must be on front side for ( j = 0 ; j < w->numpoints ; j++ ) { if ( j == i ) { continue; } d = DotProduct( w->p[j], edgenormal ); if ( d > edgedist ) { Com_Error( ERR_DROP, "CheckWinding: non-convex" ); } } } }
/* ============= SubdividePatch ============= */ void SubdividePatch (patch_t *patch) { winding_t *w, *o1, *o2; vec3_t total; vec3_t split; vec_t dist; vec_t widest = -1; int i, j, widest_axis = -1; int subdivide_it = 0; vec_t v; patch_t *newp; w = patch->winding; VectorSubtract (patch->maxs, patch->mins, total); for (i=0 ; i<3 ; i++) { if ( total[i] > widest ) { widest_axis = i; widest = total[i]; } if ( total[i] > patch->chop || (patch->face_maxs[i] == patch->maxs[i] || patch->face_mins[i] == patch->mins[i] ) && total[i] > minchop ) { subdivide_it = 1; } } if ( subdivide_it ) { // // split the winding // VectorCopy (vec3_origin, split); split[widest_axis] = 1; dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f; ClipWinding (w, split, dist, &o1, &o2); // // create a new patch // if (num_patches == MAX_PATCHES) Error ("MAX_PATCHES"); newp = &patches[num_patches]; newp->next = patch->next; patch->next = newp; patch->winding = o1; newp->winding = o2; VectorCopy( patch->face_mins, newp->face_mins ); VectorCopy( patch->face_maxs, newp->face_maxs ); VectorCopy( patch->baselight, newp->baselight ); VectorCopy( patch->directlight, newp->directlight ); VectorCopy( patch->totallight, newp->totallight ); VectorCopy( patch->reflectivity, newp->reflectivity ); newp->plane = patch->plane; newp->sky = patch->sky; newp->chop = patch->chop; newp->faceNumber = patch->faceNumber; num_patches++; patch->area = WindingArea (patch->winding); newp->area = WindingArea (newp->winding); WindingCenter (patch->winding, patch->origin); WindingCenter (newp->winding, newp->origin); #ifdef PHONG_NORMAL_PATCHES // This seems to be a bad idea for some reason. Leave it turned off for now. // Set (Copy or Calculate) the synthetic normal for these new patches VectorAdd (patch->origin, patch->plane->normal, patch->origin); VectorAdd (newp->origin, newp->plane->normal, newp->origin); GetPhongNormal( patch->faceNumber, patch->origin, patch->normal ); GetPhongNormal( newp->faceNumber, newp->origin, newp->normal ); VectorSubtract( patch->origin, patch->plane->normal, patch->origin); VectorSubtract( newp->origin, newp->plane->normal, newp->origin); #else VectorCopy( patch->plane->normal, patch->normal ); VectorCopy( newp->plane->normal, newp->normal ); #endif VectorAdd( patch->origin, patch->normal, patch->origin ); VectorAdd( newp->origin, newp->normal, newp->origin ); WindingBounds(patch->winding, patch->mins, patch->maxs); WindingBounds(newp->winding, newp->mins, newp->maxs); // Subdivide patch even more if on the edge of the face; this is a hack! VectorSubtract (patch->maxs, patch->mins, total); if ( total[0] < patch->chop && total[1] < patch->chop && total[2] < patch->chop ) for ( i=0; i<3; i++ ) if ( (patch->face_maxs[i] == patch->maxs[i] || patch->face_mins[i] == patch->mins[i] ) && total[i] > minchop ) { patch->chop = max( minchop, patch->chop / 2 ); break; } SubdividePatch (patch); // Subdivide patch even more if on the edge of the face; this is a hack! VectorSubtract (newp->maxs, newp->mins, total); if ( total[0] < newp->chop && total[1] < newp->chop && total[2] < newp->chop ) for ( i=0; i<3; i++ ) if ( (newp->face_maxs[i] == newp->maxs[i] || newp->face_mins[i] == newp->mins[i] ) && total[i] > minchop ) { newp->chop = max( minchop, newp->chop / 2 ); break; } SubdividePatch (newp); } }
void Sin_FixTextureReferences( void ) { int i, j, k, we; sin_dbrushside_t *brushside; sin_dbrush_t *brush; sin_dface_t *face; winding_t *w; memset( sin_dbrushsidetextured, false, SIN_MAX_MAP_BRUSHSIDES ); //go over all the brushes for ( i = 0; i < sin_numbrushes; i++ ) { brush = &sin_dbrushes[i]; //hint brushes are not textured if ( Sin_HintSkipBrush( brush ) ) { continue; } //go over all the sides of the brush for ( j = 0; j < brush->numsides; j++ ) { brushside = &sin_dbrushsides[brush->firstside + j]; // w = Sin_BrushSideWinding( brush, brushside ); if ( !w ) { sin_dbrushsidetextured[brush->firstside + j] = true; continue; } //end if else { //RemoveEqualPoints(w, 0.2); if ( WindingIsTiny( w ) ) { FreeWinding( w ); sin_dbrushsidetextured[brush->firstside + j] = true; continue; } //end if else { we = WindingError( w ); if ( we == WE_NOTENOUGHPOINTS || we == WE_SMALLAREA || we == WE_POINTBOGUSRANGE // || we == WE_NONCONVEX ) { FreeWinding( w ); sin_dbrushsidetextured[brush->firstside + j] = true; continue; } //end if } //end else } //end else if ( WindingArea( w ) < 20 ) { sin_dbrushsidetextured[brush->firstside + j] = true; } //end if //find a face for texturing this brush for ( k = 0; k < sin_numfaces; k++ ) { face = &sin_dfaces[k]; //if the face is in the same plane as the brush side if ( ( face->planenum & ~1 ) != ( brushside->planenum & ~1 ) ) { continue; } //if the face is partly or totally on the brush side if ( Sin_FaceOnWinding( face, w ) ) { brushside->texinfo = face->texinfo; sin_dbrushsidetextured[brush->firstside + j] = true; break; } //end if } //end for FreeWinding( w ); } //end for } //end for } //end of the function Sin_FixTextureReferences*/
void Q3_FindVisibleBrushSides(void) { int i, j, k, we, numtextured, numsides; float dot; dplane_t *plane; dbrushside_t *brushside; dbrush_t *brush; dsurface_t *surface; winding_t *w; memset(dbrushsidetextured, false, MAX_MAP_BRUSHSIDES); // numsides = 0; //create planes for the planar surfaces Q3_CreatePlanarSurfacePlanes(); Log_Print("searching visible brush sides...\n"); Log_Print("%6d brush sides", numsides); //go over all the brushes for (i = 0; i < q3_numbrushes; i++) { brush = &dbrushes[i]; //go over all the sides of the brush for (j = 0; j < brush->numSides; j++) { qprintf("\r%6d", numsides++); brushside = &dbrushsides[brush->firstSide + j]; // w = Q3_BrushSideWinding(brush, brushside); if (!w) { dbrushsidetextured[brush->firstSide + j] = true; continue; } //end if else { //RemoveEqualPoints(w, 0.2); if (WindingIsTiny(w)) { FreeWinding(w); dbrushsidetextured[brush->firstSide + j] = true; continue; } //end if else { we = WindingError(w); if (we == WE_NOTENOUGHPOINTS || we == WE_SMALLAREA || we == WE_POINTBOGUSRANGE // || we == WE_NONCONVEX ) { FreeWinding(w); dbrushsidetextured[brush->firstSide + j] = true; continue; } //end if } //end else } //end else if (WindingArea(w) < 20) { dbrushsidetextured[brush->firstSide + j] = true; continue; } //end if //find a face for texturing this brush for (k = 0; k < q3_numDrawSurfaces; k++) { surface = &drawSurfaces[k]; if (surface->surfaceType != MST_PLANAR) continue; // //Q3_SurfacePlane(surface, plane.normal, &plane.dist); plane = &q3_surfaceplanes[k]; //the surface plane and the brush side plane should be pretty much the same if (fabs(fabs(plane->dist) - fabs(dplanes[brushside->planeNum].dist)) > 5) continue; dot = DotProduct(plane->normal, dplanes[brushside->planeNum].normal); if (dot > -0.9 && dot < 0.9) continue; //if the face is partly or totally on the brush side if (Q3_FaceOnWinding(surface, w)) { dbrushsidetextured[brush->firstSide + j] = true; //Log_Write("Q3_FaceOnWinding"); break; } //end if } //end for FreeWinding(w); } //end for } //end for qprintf("\r%6d brush sides\n", numsides); numtextured = 0; for (i = 0; i < q3_numbrushsides; i++) { if (forcesidesvisible) dbrushsidetextured[i] = true; if (dbrushsidetextured[i]) numtextured++; } //end for Log_Print("%d brush sides textured out of %d\n", numtextured, q3_numbrushsides); } //end of the function Q3_FindVisibleBrushSides
void MakePatchForFace (int fn, winding_t *w) { dface_t *f = dfaces + fn; // No patches at all for the sky! if ( !IsSky(f) ) { float area; patch_t *patch; vec3_t light; vec3_t centroid = {0,0,0}; int i, j; texinfo_t *tx = &texinfo[f->texinfo]; area = WindingArea (w); totalarea += area; patch = &patches[num_patches]; if (num_patches == MAX_PATCHES) Error ("num_patches == MAX_PATCHES"); patch->next = face_patches[fn]; face_patches[fn] = patch; if ( texscale ) { // Compute the texture "scale" in s,t for( i=0; i<2; i++ ) { patch->scale[i] = 0.0; for( j=0; j<3; j++ ) patch->scale[i] += tx->vecs[i][j] * tx->vecs[i][j]; patch->scale[i] = sqrt( patch->scale[i] ); } } else patch->scale[0] = patch->scale[1] = 1.0; patch->area = area; patch->chop = maxchop / (int)((patch->scale[0]+patch->scale[1])/2); patch->sky = FALSE; patch->winding = w; if (f->side) patch->plane = &backplanes[f->planenum]; else patch->plane = &dplanes[f->planenum]; for (j=0 ; j<f->numedges ; j++) { int edge = dsurfedges[ f->firstedge + j ]; int edge2 = dsurfedges[ j==f->numedges-1 ? f->firstedge : f->firstedge + j + 1 ]; if (edge > 0) { VectorAdd( dvertexes[dedges[edge].v[0]].point, centroid, centroid ); VectorAdd( dvertexes[dedges[edge].v[1]].point, centroid, centroid ); } else { VectorAdd( dvertexes[dedges[-edge].v[1]].point, centroid, centroid ); VectorAdd( dvertexes[dedges[-edge].v[0]].point, centroid, centroid ); } } VectorScale( centroid, 1.0 / (f->numedges * 2), centroid ); VectorCopy( centroid, face_centroids[fn] ); // Save them for generating the patch normals later. patch->faceNumber = fn; WindingCenter (w, patch->origin); #ifdef PHONG_NORMAL_PATCHES // This seems to be a bad idea for some reason. Leave it turned off for now. VectorAdd (patch->origin, patch->plane->normal, patch->origin); GetPhongNormal( fn, patch->origin, patch->normal ); VectorSubtract (patch->origin, patch->plane->normal, patch->origin); if ( !VectorCompare( patch->plane->normal, patch->normal ) ) patch->chop = 16; // Chop it fine! #else VectorCopy( patch->plane->normal, patch->normal ); #endif VectorAdd (patch->origin, patch->normal, patch->origin); WindingBounds (w, patch->face_mins, patch->face_maxs); VectorCopy( patch->face_mins, patch->mins ); VectorCopy( patch->face_maxs, patch->maxs ); BaseLightForFace( f, light, patch->reflectivity ); VectorCopy( light, patch->totallight ); VectorCopy( light, patch->baselight ); // Chop all texlights very fine. if ( !VectorCompare( light, vec3_origin ) ) patch->chop = extra ? minchop / 2 : minchop; num_patches++; } }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveTinyAreas( void ) { //RF, disabled, can cause problems, should do checks on minsidelength instead, and also this should be a post-clustering // process, as in "-reachableonly" return; #if 0 int side, gside, nummerges; tmp_area_t *tmparea; tmp_face_t *face, *gface; vec_t windingArea; if ( !mingroundarea ) { return; } nummerges = 0; Log_Write( "AAS_RemoveTinyAreas\r\n" ); qprintf( "%6d tiny areas removed", 1 ); // for ( tmparea = tmpaasworld.areas; tmparea; tmparea = tmparea->l_next ) { //if the area is invalid if ( tmparea->invalid ) { continue; } //end if // // never remove liquid areas if ( tmparea->contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { continue; } // windingArea = 0; // for ( face = tmparea->tmpfaces; face; face = face->next[side] ) { side = ( face->frontarea != tmparea ); // // never remove ladder areas if ( face->faceflags & FACE_LADDER ) { break; } // //if this face touches a grounded face, and there is no area on the other side, then dont remove it if ( !( face->faceflags & FACE_GROUND ) ) { // does this face share an edge with a ground face? for ( gface = tmparea->tmpfaces; gface; gface = gface->next[gside] ) { gside = ( gface->frontarea != tmparea ); // if ( gface == face ) { continue; } if ( !( gface->faceflags & FACE_GROUND ) ) { continue; } // if ( AAS_FacesTouching( face, gface ) ) { break; } } // if this face touches a grounded face, and there is no area on the other side, then this area must stay if ( gface && ( !face->frontarea || face->frontarea->invalid || !face->backarea || face->backarea->invalid || ( face->faceflags & FACE_SOLID ) ) ) { break; } } // if ( face->faceflags & FACE_GROUND ) { // get the area of this face windingArea += WindingArea( face->winding ); } } //end for // // if this area has a windingArea low enough, then remove it if ( !face && windingArea && windingArea < mingroundarea ) { qprintf( "\r%6d", ++nummerges ); tmparea->invalid = 1; } } //end for qprintf( "\n" ); Log_Write( "%6d tiny areas removed\r\n", nummerges ); #endif } //end of the function AAS_MergeAreas
static void SelectSplitPlaneNum( node_t *node, face_t *list, int *splitPlaneNum, int *compileFlags ){ face_t *split; face_t *check; face_t *bestSplit; int splits, facing, front, back; int side; plane_t *plane; int value, bestValue; int i; vec3_t normal; float dist; int planenum; float sizeBias; /* ydnar: set some defaults */ *splitPlaneNum = -1; /* leaf */ *compileFlags = 0; /* ydnar 2002-06-24: changed this to split on z-axis as well */ /* ydnar 2002-09-21: changed blocksize to be a vector, so mappers can specify a 3 element value */ /* if it is crossing a block boundary, force a split */ for ( i = 0; i < 3; i++ ) { if ( blockSize[ i ] <= 0 ) { continue; } dist = blockSize[ i ] * ( floor( node->mins[ i ] / blockSize[ i ] ) + 1 ); if ( node->maxs[ i ] > dist ) { VectorClear( normal ); normal[ i ] = 1; planenum = FindFloatPlane( normal, dist, 0, NULL ); *splitPlaneNum = planenum; return; } } /* pick one of the face planes */ bestValue = -99999; bestSplit = list; // div0: this check causes detail/structural mixes //for( split = list; split; split = split->next ) // split->checked = qfalse; for ( split = list; split; split = split->next ) { //if ( split->checked ) // continue; plane = &mapplanes[ split->planenum ]; splits = 0; facing = 0; front = 0; back = 0; for ( check = list ; check ; check = check->next ) { if ( check->planenum == split->planenum ) { facing++; //check->checked = qtrue; // won't need to test this plane again continue; } side = WindingOnPlaneSide( check->w, plane->normal, plane->dist ); if ( side == SIDE_CROSS ) { splits++; } else if ( side == SIDE_FRONT ) { front++; } else if ( side == SIDE_BACK ) { back++; } } if ( bspAlternateSplitWeights ) { // from 27 //Bigger is better sizeBias = WindingArea( split->w ); //Base score = 20000 perfectly balanced value = 20000 - ( abs( front - back ) ); value -= plane->counter; // If we've already used this plane sometime in the past try not to use it again value -= facing ; // if we're going to have alot of other surfs use this plane, we want to get it in quickly. value -= splits * 5; //more splits = bad value += sizeBias * 10; //We want a huge score bias based on plane size } else { value = 5 * facing - 5 * splits; // - abs(front-back); if ( plane->type < 3 ) { value += 5; // axial is better } } value += split->priority; // prioritize hints higher if ( value > bestValue ) { bestValue = value; bestSplit = split; } } /* nothing, we have a leaf */ if ( bestValue == -99999 ) { return; } /* set best split data */ *splitPlaneNum = bestSplit->planenum; *compileFlags = bestSplit->compileFlags; if ( *splitPlaneNum > -1 ) { mapplanes[ *splitPlaneNum ].counter++; } }
/* =========== MakeHullFaces =========== */ void MakeHullFaces (brush_t *b, brushhull_t *h) { bface_t *f, *f2; winding_t *w; plane_t *p; int i, j; vec_t v; vec_t area; restart: h->mins[0] = h->mins[1] = h->mins[2] = 9999; h->maxs[0] = h->maxs[1] = h->maxs[2] = -9999; for (f = h->faces ; f ; f=f->next) { // w = BaseWindingForIPlane (f->plane); w = BaseWindingForPlane (f->plane->normal, f->plane->dist); for (f2 = h->faces ; f2 && w ; f2=f2->next) { if (f == f2) continue; p = &mapplanes[f2->planenum ^ 1]; w = ChopWinding (w, p->normal, p->dist); } area = w ? WindingArea(w) : 0; if (area < 0.1) { qprintf ("Entity %i, Brush %i: plane with area %4.2f\n" , b->entitynum, b->brushnum, area); // remove the face and regenerate the hull if (h->faces == f) h->faces = f->next; else { for (f2=h->faces ; f2->next != f ; f2=f2->next) ; f2->next = f->next; } goto restart; } f->w = w; f->contents = CONTENTS_EMPTY; if (w) { for (i=0 ; i<w->numpoints ; i++) { for (j=0 ; j<3 ; j++) { v = w->p[i][j]; // w->p[i][j] = floor (v+0.5); // round to int if (v<h->mins[j]) h->mins[j] = v; if (v>h->maxs[j]) h->maxs[j] = v; } } } } for (i=0 ; i<3 ; i++) { if (h->mins[i] < -BOGUS_RANGE/2 || h->maxs[i] > BOGUS_RANGE/2) { vec3_t eorigin = { 0, 0, 0}; char *pszClass = "Unknown Class"; if ( b->entitynum ) { entity_t *e = entities + b->entitynum; pszClass = ValueForKey(e, "classname" ); GetVectorForKey( e, "origin", eorigin ); } printf( "Entity %i, Brush %i: A '%s' @(%.0f,%.0f,%.0f)\n", b->entitynum, b->brushnum, pszClass, eorigin[0], eorigin[1], eorigin[2] ); printf( "\toutside world(+/-%d): (%.0f, %.0f, %.0f)-(%.0f,%.0f,%.0f)\n", BOGUS_RANGE/2, h->mins[0], h->mins[1], h->mins[2], h->maxs[0], h->maxs[1], h->maxs[2] ); break; } } }
void GetBestSurfaceTriangleMatchForBrushside(side_t * buildSide, bspDrawVert_t * bestVert[3]) { bspDrawSurface_t *s; int i; int t; vec_t best = 0; vec_t thisarea; vec3_t normdiff; vec3_t v1v0, v2v0, norm; bspDrawVert_t *vert[3]; winding_t *polygon; plane_t *buildPlane = &mapplanes[buildSide->planenum]; int matches = 0; // first, start out with NULLs bestVert[0] = bestVert[1] = bestVert[2] = NULL; // brute force through all surfaces for(s = bspDrawSurfaces; s != bspDrawSurfaces + numBSPDrawSurfaces; ++s) { if(s->surfaceType != MST_PLANAR && s->surfaceType != MST_TRIANGLE_SOUP) continue; if(strcmp(buildSide->shaderInfo->shader, bspShaders[s->shaderNum].shader)) continue; for(t = 0; t + 3 <= s->numIndexes; t += 3) { vert[0] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 0]]; vert[1] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 1]]; vert[2] = &bspDrawVerts[s->firstVert + bspDrawIndexes[s->firstIndex + t + 2]]; if(s->surfaceType == MST_PLANAR) { VectorSubtract(vert[0]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue; VectorSubtract(vert[1]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue; VectorSubtract(vert[2]->normal, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue; } else { // this is more prone to roundoff errors, but with embedded // models, there is no better way VectorSubtract(vert[1]->xyz, vert[0]->xyz, v1v0); VectorSubtract(vert[2]->xyz, vert[0]->xyz, v2v0); CrossProduct(v2v0, v1v0, norm); VectorNormalize(norm); VectorSubtract(norm, buildPlane->normal, normdiff); if(VectorLength(normdiff) >= normalEpsilon) continue; } if(abs(DotProduct(vert[0]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue; if(abs(DotProduct(vert[1]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue; if(abs(DotProduct(vert[2]->xyz, buildPlane->normal) - buildPlane->dist) >= distanceEpsilon) continue; // Okay. Correct surface type, correct shader, correct plane. Let's start with the business... polygon = CopyWinding(buildSide->winding); for(i = 0; i < 3; ++i) { // 0: 1, 2 // 1: 2, 0 // 2; 0, 1 vec3_t *v1 = &vert[(i + 1) % 3]->xyz; vec3_t *v2 = &vert[(i + 2) % 3]->xyz; vec3_t triNormal; vec_t triDist; vec3_t sideDirection; // we now need to generate triNormal and triDist so that they represent the plane spanned by normal and (v2 - v1). VectorSubtract(*v2, *v1, sideDirection); CrossProduct(sideDirection, buildPlane->normal, triNormal); triDist = DotProduct(*v1, triNormal); ChopWindingInPlace(&polygon, triNormal, triDist, distanceEpsilon); if(!polygon) goto exwinding; } thisarea = WindingArea(polygon); if(thisarea > 0) ++matches; if(thisarea > best) { best = thisarea; bestVert[0] = vert[0]; bestVert[1] = vert[1]; bestVert[2] = vert[2]; } FreeWinding(polygon); exwinding: ; } } //if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16)) // fprintf(stderr, "brushside with %s: %d matches (%f area)\n", buildSide->shaderInfo->shader, matches, best); }
/* =========== CSGBrush =========== */ void CSGBrush (int brushnum) { int hull; brush_t *b1, *b2; brushhull_t *bh1, *bh2; int bn; qboolean overwrite; int i; bface_t *f, *f2, *next, *fcopy; bface_t *outside, *oldoutside; entity_t *e; vec_t area; SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL); b1 = &mapbrushes[brushnum]; e = &entities[b1->entitynum]; for (hull = 0 ; hull<NUM_HULLS ; hull++) { bh1 = &b1->hulls[hull]; // set outside to a copy of the brush's faces outside = CopyFacesToOutside (bh1); overwrite = false; for (bn=0 ; bn<e->numbrushes ; bn++) { // see if b2 needs to clip a chunk out of b1 if (bn==brushnum) { overwrite = true; // later brushes now overwrite continue; } b2 = &mapbrushes[e->firstbrush + bn]; bh2 = &b2->hulls[hull]; if (!bh2->faces) continue; // brush isn't in this hull // check brush bounding box first for (i=0 ; i<3 ; i++) if (bh1->mins[i] > bh2->maxs[i] || bh1->maxs[i] < bh2->mins[i]) break; if (i<3) continue; // divide faces by the planes of the b2 to find which // fragments are inside f = outside; outside = NULL; for ( ; f ; f=next) { next = f->next; // check face bounding box first for (i=0 ; i<3 ; i++) if (bh2->mins[i] > f->maxs[i] || bh2->maxs[i] < f->mins[i]) break; if (i<3) { // this face doesn't intersect brush2's bbox f->next = outside; outside = f; continue; } oldoutside = outside; fcopy = CopyFace (f); // save to avoid fake splits // throw pieces on the front sides of the planes // into the outside list, return the remains on the inside for (f2=bh2->faces ; f2 && f ; f2=f2->next) f = ClipFace (b1, f, &outside, f2->planenum, overwrite); area = f ? WindingArea (f->w) : 0; if (f && area < 1.0) { qprintf ("Entity %i, Brush %i: tiny penetration\n" , b1->entitynum, b1->brushnum); c_tiny_clip++; FreeFace (f); f = NULL; } if (f) { // there is one convex fragment of the original // face left inside brush2 FreeFace (fcopy); if (b1->contents > b2->contents) { // inside a water brush f->contents = b2->contents; f->next = outside; outside = f; } else // inside a solid brush FreeFace (f); // throw it away } else { // the entire thing was on the outside, even // though the bounding boxes intersected, // which will never happen with axial planes // free the fragments chopped to the outside while (outside != oldoutside) { f2 = outside->next; FreeFace (outside); outside = f2; } // revert to the original face to avoid // unneeded false cuts fcopy->next = outside; outside = fcopy; } } } // all of the faces left in outside are real surface faces SaveOutside (b1, hull, outside, b1->contents); } }
void MakePatchForFace( int fn, winding_t *w ){ dface_t *f; float area; patch_t *patch; dplane_t *pl; int i; vec3_t color; dleaf_t *leaf; f = &dfaces[fn]; area = WindingArea( w ); totalarea += area; patch = &patches[num_patches]; if ( num_patches == MAX_PATCHES ) { Error( "num_patches == MAX_PATCHES" ); } patch->next = face_patches[fn]; face_patches[fn] = patch; patch->winding = w; if ( f->side ) { patch->plane = &backplanes[f->planenum]; } else{ patch->plane = &dplanes[f->planenum]; } if ( face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] ) { // origin offset faces must create new planes if ( numplanes + fakeplanes >= MAX_MAP_PLANES ) { Error( "numplanes + fakeplanes >= MAX_MAP_PLANES" ); } pl = &dplanes[numplanes + fakeplanes]; fakeplanes++; *pl = *( patch->plane ); pl->dist += DotProduct( face_offset[fn], pl->normal ); patch->plane = pl; } WindingCenter( w, patch->origin ); VectorAdd( patch->origin, patch->plane->normal, patch->origin ); leaf = Rad_PointInLeaf( patch->origin ); patch->cluster = leaf->cluster; if ( patch->cluster == -1 ) { Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" ); } patch->area = area; if ( patch->area <= 1 ) { patch->area = 1; } patch->sky = IsSky( f ); VectorCopy( texture_reflectivity[f->texinfo], patch->reflectivity ); // non-bmodel patches can emit light if ( fn < dmodels[0].numfaces ) { BaseLightForFace( f, patch->baselight ); ColorNormalize( patch->reflectivity, color ); for ( i = 0 ; i < 3 ; i++ ) patch->baselight[i] *= color[i]; VectorCopy( patch->baselight, patch->totallight ); } num_patches++; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WindingError(winding_t *w) { int i, j; vec_t *p1, *p2; vec_t d, edgedist; vec3_t dir, edgenormal, facenormal; vec_t area; vec_t facedist; if (w->numpoints < 3) { sprintf(windingerror, "winding %i points", w->numpoints); return WE_NOTENOUGHPOINTS; } //end if area = WindingArea(w); if (area < 1) { sprintf(windingerror, "winding %f area", area); return WE_SMALLAREA; } //end if WindingPlane (w, facenormal, &facedist); for (i=0 ; i<w->numpoints ; i++) { p1 = w->p[i]; for (j=0 ; j<3 ; j++) { if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE) { sprintf(windingerror, "winding point %d BUGUS_RANGE \'%f %f %f\'", j, p1[0], p1[1], p1[2]); return WE_POINTBOGUSRANGE; } //end if } //end for j = i+1 == w->numpoints ? 0 : i+1; // check the point is on the face plane d = DotProduct (p1, facenormal) - facedist; if (d < -ON_EPSILON || d > ON_EPSILON) { sprintf(windingerror, "winding point %d off plane", i); return WE_POINTOFFPLANE; } //end if // check the edge isnt degenerate p2 = w->p[j]; VectorSubtract (p2, p1, dir); if (VectorLength (dir) < ON_EPSILON) { sprintf(windingerror, "winding degenerate edge %d-%d", i, j); return WE_DEGENERATEEDGE; } //end if CrossProduct (facenormal, dir, edgenormal); VectorNormalize (edgenormal); edgedist = DotProduct (p1, edgenormal); edgedist += ON_EPSILON; // all other points must be on front side for (j=0 ; j<w->numpoints ; j++) { if (j == i) continue; d = DotProduct (w->p[j], edgenormal); if (d > edgedist) { sprintf(windingerror, "winding non-convex"); return WE_NONCONVEX; } //end if } //end for } //end for return WE_NONE; } //end of the function WindingError
static void SelectSplitPlaneNum(node_t * node, face_t * list, int *splitPlaneNum, int *compileFlags) { face_t *split; face_t *check; face_t *bestSplit; int splits, facing, front, back; int side; plane_t *plane; int value, bestValue; int i; vec3_t normal; float dist; int planenum; //float sizeBias; //int checks; int frontC, backC, splitsC, facingC; /* ydnar: set some defaults */ *splitPlaneNum = -1; /* leaf */ *compileFlags = 0; /* ydnar 2002-06-24: changed this to split on z-axis as well */ /* ydnar 2002-09-21: changed blocksize to be a vector, so mappers can specify a 3 element value */ /* if it is crossing a block boundary, force a split */ #if 1 for(i = 0; i < 3; i++) { if(blockSize[i] <= 0) continue; dist = blockSize[i] * (floor(node->mins[i] / blockSize[i]) + 1); if(node->maxs[i] > dist) { VectorClear(normal); normal[i] = 1; planenum = FindFloatPlane(normal, dist, 0, NULL); *splitPlaneNum = planenum; return; } } #endif /* pick one of the face planes */ bestValue = -99999; bestSplit = list; //checks = 0; #if 1 // div0: this check causes detail/structural mixes //for(split = list; split; split = split->next) // split->checked = qfalse; #if defined(DEBUG_SPLITS) Sys_FPrintf(SYS_VRB, "split scores: ["); #endif for(split = list; split; split = split->next) { //if(split->checked) // continue; //checks++; plane = &mapplanes[split->planenum]; splits = 0; facing = 0; front = 0; back = 0; for(check = list; check; check = check->next) { if(check->planenum == split->planenum) { facing++; //check->checked = qtrue; // won't need to test this plane again continue; } side = WindingOnPlaneSide(check->w, plane->normal, plane->dist); if(side == SIDE_CROSS) { splits++; } else if(side == SIDE_FRONT) { front++; } else if(side == SIDE_BACK) { back++; } } if(bspAlternateSplitWeights) { //Base score = 20000 perfectly balanced value = 0;//20000; value -= abs(front - back); // prefer centered planes value -= plane->counter; // if we've already used this plane sometime in the past try not to use it again value += facing * 5; // if we're going to have alot of other surfs use this plane, we want to get it in quickly. value -= splits * 5; // more splits = bad //value += sizeBias * 10; // we want a huge score bias based on plane size if(plane->type < 3) { value += 5; // axial is better } // we want a huge score bias based on plane size #if 0 { winding_t *w; node_t *n; plane_t *plane; vec3_t normal; vec_t dist; // create temporary winding to draw the split plane w = CopyWinding(split->w); // clip by all the parents for(n = node->parent; n && w;) { plane = &mapplanes[n->planenum]; if(n->children[0] == node) { // take front ChopWindingInPlace(&w, plane->normal, plane->dist, 0.001); // BASE_WINDING_EPSILON } else { // take back VectorNegate(plane->normal, normal); dist = -plane->dist; ChopWindingInPlace(&w, normal, dist, 0.001); // BASE_WINDING_EPSILON } node = n; n = n->parent; } // clip by node AABB if(w != NULL) ChopWindingByBounds(&w, node->mins, node->maxs, CLIP_EPSILON); if(w != NULL) value += WindingArea(w); } #endif } else { value = 5 * facing - 5 * splits; // - abs(front-back); if(plane->type < 3) { value += 5; // axial is better } } value += split->priority; // prioritize hints higher #if defined(DEBUG_SPLITS) Sys_FPrintf(SYS_VRB, " %d", value); #endif if(value > bestValue) { bestValue = value; bestSplit = split; frontC = front; backC = back; splitsC = splits; facingC = facing; } } #if defined(DEBUG_SPLITS) Sys_FPrintf(SYS_VRB, "]\n"); #endif #endif /* nothing, we have a leaf */ if(bestValue == -99999) return; //Sys_FPrintf(SYS_VRB, "F: %9d B:%9d S:%9d FA:%9d\n", frontC, backC, splitsC, facingC); /* set best split data */ *splitPlaneNum = bestSplit->planenum; *compileFlags = bestSplit->compileFlags; #if defined(DEBUG_SPLITS) if(bestSplit->compileFlags & C_DETAIL) for(split = list; split; split = split->next) if(!(split->compileFlags & C_DETAIL)) Sys_FPrintf(SYS_ERR, "DON'T DO SUCH SPLITS (1)\n"); if((node->compileFlags & C_DETAIL) && !(bestSplit->compileFlags & C_DETAIL)) Sys_FPrintf(SYS_ERR, "DON'T DO SUCH SPLITS (2)\n"); #endif if(*splitPlaneNum > -1) mapplanes[*splitPlaneNum].counter++; }
/* ================== EmitFace ================== */ void EmitFace( face_t *f, qboolean onNode ) { dface_t *df; int i; int e; // void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack // SubdivideFaceBySubdivSize( f ); // set initial output number f->outputnumber = -1; // degenerated if( f->numpoints < 3 ) return; // not a final face if( f->merged || f->split[0] || f->split[1] ) return; // don't emit NODRAW faces for runtime if ( texinfo[f->texinfo].flags & SURF_NODRAW ) { // keep NODRAW terrain surfaces though if ( f->dispinfo == -1 ) return; Warning("NODRAW on terrain surface!\n"); } // save output number so leaffaces can use f->outputnumber = numfaces; // // get the next available .bsp face slot // if (numfaces >= MAX_MAP_FACES) Error( "Too many faces in map, max = %d", MAX_MAP_FACES ); df = &dfaces[numfaces]; // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id dfaceids.AddToTail(); dfaceids[numfaces].hammerfaceid = f->originalface->id; numfaces++; // // plane info - planenum is used by qlight, but not quake // df->planenum = f->planenum; df->onNode = onNode; df->side = f->planenum & 1; // // material info // df->texinfo = f->texinfo; df->dispinfo = f->dispinfo; df->smoothingGroups = f->smoothingGroups; // save the original "side"/face data df->origFace = FindOrCreateOrigFace( f ); df->surfaceFogVolumeID = -1; dfacenodes[numfaces-1] = f->fogVolumeLeaf; if ( f->fogVolumeLeaf ) { Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF ); } // // edge info // df->firstedge = numsurfedges; df->numedges = f->numpoints; // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary if ( f->w ) { df->area = WindingArea( f->w ); } else { df->area = 0; } df->firstPrimID = f->firstPrimID; df->SetNumPrims( f->numPrims ); df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled ); // // save off points -- as edges // for( i = 0; i < f->numpoints; i++ ) { //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f); e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f); if (numsurfedges >= MAX_MAP_SURFEDGES) Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" ); dsurfedges[numsurfedges] = e; numsurfedges++; } // Create overlay face lists. side_t *pSide = f->originalface; if ( pSide ) { int nOverlayCount = pSide->aOverlayIds.Count(); if ( nOverlayCount > 0 ) { Overlay_AddFaceToLists( ( numfaces - 1 ), pSide ); } nOverlayCount = pSide->aWaterOverlayIds.Count(); if ( nOverlayCount > 0 ) { OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide ); } } }