/* =============== FinishBrush Produces a final brush based on the buildBrush->sides array and links it to the current entity =============== */ static uBrush_t *FinishBrush( void ) { uBrush_t *b; primitive_t *prim; // create windings for sides and bounds for brush if( !CreateBrushWindings( buildBrush ) ) { // don't keep this brush FreeBuildBrush(); return NULL; } if( buildBrush->contents & CONTENTS_AREAPORTAL ) { if( dmapGlobals.num_entities != 1 ) { common->Printf( "Entity %i, Brush %i: areaportals only allowed in world\n" , dmapGlobals.num_entities - 1, entityPrimitive ); FreeBuildBrush(); return NULL; } } // keep it b = CopyBrush( buildBrush ); FreeBuildBrush(); b->entitynum = dmapGlobals.num_entities - 1; b->brushnum = entityPrimitive; b->original = b; prim = ( primitive_t * )Mem_Alloc( sizeof( *prim ) ); memset( prim, 0, sizeof( *prim ) ); prim->next = uEntity->primitives; uEntity->primitives = prim; prim->brush = b; return b; }
/* ================== BrushFromBounds Creates a new axial brush ================== */ bspbrush_t *BrushFromBounds (Vector& mins, Vector& maxs) { bspbrush_t *b; int i; Vector normal; vec_t dist; b = AllocBrush (6); b->numsides = 6; for (i=0 ; i<3 ; i++) { VectorClear (normal); normal[i] = 1; dist = maxs[i]; b->sides[i].planenum = g_MainMap->FindFloatPlane (normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3+i].planenum = g_MainMap->FindFloatPlane (normal, dist); } CreateBrushWindings (b); return b; }
/* ================= AdjustBrushesForOrigin ================= */ void AdjustBrushesForOrigin( entity_t *ent ) { int i; side_t *s; vec_t newdist; brush_t *b; parseMesh_t *p; for( b = ent->brushes; b != NULL; b = b->next ) { for( i = 0; i < b->numsides; i++) { s = &b->sides[i]; newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->origin ); s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL ); } // rebuild brush windings (just offsetting the winding above should be fine) CreateBrushWindings( b ); } for( p = ent->patches; p != NULL; p = p->next ) { for( i = 0; i < (p->mesh.width * p->mesh.height); i++ ) VectorSubtract( p->mesh.verts[i].xyz, ent->origin, p->mesh.verts[i].xyz ); } }
static void AdjustEntityForOrigin( uEntity_t *ent ) { primitive_t *prim; uBrush_t *b; int i; side_t *s; for ( prim = ent->primitives ; prim ; prim = prim->next ) { b = prim->brush; if ( !b ) { continue; } for ( i = 0; i < b->numsides; i++ ) { idPlane plane; s = &b->sides[i]; plane = dmapGlobals.mapPlanes[s->planenum]; plane[3] += plane.Normal() * ent->origin; s->planenum = FindFloatPlane( plane ); s->texVec.v[0][3] += DotProduct( ent->origin, s->texVec.v[0] ); s->texVec.v[1][3] += DotProduct( ent->origin, s->texVec.v[1] ); // remove any integral shift s->texVec.v[0][3] -= floor( s->texVec.v[0][3] ); s->texVec.v[1][3] -= floor( s->texVec.v[1][3] ); } CreateBrushWindings(b); } }
//=========================================================================== // Creates a new axial brush // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs) { bspbrush_t *b; int i; vec3_t normal; vec_t dist; b = AllocBrush (6); b->numsides = 6; for (i=0 ; i<3 ; i++) { VectorClear (normal); normal[i] = 1; dist = maxs[i]; b->sides[i].planenum = FindFloatPlane (normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3+i].planenum = FindFloatPlane (normal, dist); } CreateBrushWindings (b); return b; } //end of the function BrushFromBounds
/* ================== BrushFromBounds Creates a new axial brush ================== */ brush_t *BrushFromBounds (float minx, float miny, float minz, float maxx, float maxy, float maxz, shaderInfo_t *si) { brush_t *b; vec3_t mins, maxs; vec3_t normal; vec_t dist; int i; b = AllocBrush (6); b->entityNum = mapEntityNum; b->original = b; b->contentShader = si; b->compileFlags = si->compileFlags; b->contentFlags = si->contentFlags; b->opaque = qtrue; b->detail = qfalse; b->numsides = 6; VectorSet(mins, minx, miny, minz); VectorSet(maxs, maxx, maxy, maxz); for (i=0 ; i<3 ; i++) { VectorClear (normal); normal[i] = 1; dist = maxs[i]; b->sides[i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &maxs ); b->sides[i].shaderInfo = si; b->sides[i].surfaceFlags = si->surfaceFlags; b->sides[i].contentFlags = si->contentFlags; b->sides[i].compileFlags = si->compileFlags; b->sides[i].value = si->value; normal[i] = -1; dist = -mins[i]; b->sides[3+i].planenum = FindFloatPlane (normal, dist, 1, (vec3_t*) &mins ); b->sides[3+i].shaderInfo = si; b->sides[3+i].surfaceFlags = si->surfaceFlags; b->sides[3+i].contentFlags = si->contentFlags; b->sides[3+i].compileFlags = si->compileFlags; b->sides[3+i].value = si->value; } CreateBrushWindings (b); return b; }
/* ================== BrushFromBounds Creates a new axial brush ================== */ uBrush_t *BrushFromBounds( const idBounds &bounds ) { uBrush_t *b; int i; idPlane plane; b = AllocBrush (6); b->numsides = 6; for (i=0 ; i<3 ; i++) { plane[0] = plane[1] = plane[2] = 0; plane[i] = 1; plane[3] = -bounds[1][i]; b->sides[i].planenum = FindFloatPlane( plane ); plane[i] = -1; plane[3] = bounds[0][i]; b->sides[3+i].planenum = FindFloatPlane( plane ); } CreateBrushWindings (b); return b; }
/** * @brief Creates a new axial brush */ static brush_t *BrushFromBounds(vec3_t mins, vec3_t maxs) { brush_t *b; int32_t i; vec3_t normal; vec_t dist; b = AllocBrush(6); b->num_sides = 6; for (i = 0; i < 3; i++) { VectorClear(normal); normal[i] = 1; dist = maxs[i]; b->sides[i].plane_num = FindPlane(normal, dist); normal[i] = -1; dist = -mins[i]; b->sides[3 + i].plane_num = FindPlane(normal, dist); } CreateBrushWindings(b); return b; }
/* ================= FinishBrush produces a final brush based on the buildBrush->sides array and links it to the current entity ================= */ brush_t *FinishBrush( void ) { brush_t *b; // create windings for sides and bounds for brush if( !CreateBrushWindings( buildBrush )) return NULL; // origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity. // after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush if( buildBrush->compileFlags & C_ORIGIN ) { char string[32]; vec3_t size, movedir, origin; if( numEntities == 1 ) { Msg( "Entity %i, Brush %i: origin brushes not allowed in world\n", mapEnt->mapEntityNum, entitySourceBrushes ); return NULL; } // calcualte movedir (Xash 0.4 style) VectorAverage( buildBrush->mins, buildBrush->maxs, origin ); VectorSubtract( buildBrush->maxs, buildBrush->mins, size ); if( size[2] > size[0] && size[2] > size[1] ) VectorSet( movedir, 0.0f, 1.0f, 0.0f ); // x-rotate else if( size[1] > size[2] && size[1] > size[0] ) VectorSet( movedir, 1.0f, 0.0f, 0.0f ); // y-rotate else if( size[0] > size[2] && size[0] > size[1] ) VectorSet( movedir, 0.0f, 0.0f, 1.0f ); // z-rotate else VectorClear( movedir ); // custom movedir #if 0 if( !VectorIsNull( movedir )) { com.snprintf( string, sizeof( string ), "%i %i %i", (int)movedir[0], (int)movedir[1], (int)movedir[2] ); SetKeyValue( &entities[numEntities - 1], "movedir", string ); } #endif if(!VectorIsNull( origin )) { com.snprintf( string, sizeof( string ), "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] ); SetKeyValue( &entities[numEntities - 1], "origin", string ); VectorCopy( origin, entities[numEntities - 1].origin ); } // don't keep this brush return NULL; } // determine if the brush is an area portal if( buildBrush->compileFlags & C_AREAPORTAL ) { if( numEntities != 1 ) { Msg( "Entity %i, Brush %i: areaportals only allowed in world\n", mapEnt->mapEntityNum, entitySourceBrushes ); return NULL; } } AddBrushBevels(); b = CopyBrush( buildBrush ); b->entityNum = mapEnt->mapEntityNum; b->brushNum = entitySourceBrushes; b->original = b; // link opaque brushes to head of list, translucent brushes to end if( b->opaque || mapEnt->lastBrush == NULL ) { b->next = mapEnt->brushes; mapEnt->brushes = b; if( mapEnt->lastBrush == NULL ) mapEnt->lastBrush = b; } else { b->next = NULL; mapEnt->lastBrush->next = b; mapEnt->lastBrush = b; } // link colorMod volume brushes to the entity directly if( b->contentShader != NULL && b->contentShader->colorMod != NULL && b->contentShader->colorMod->type == CM_VOLUME ) { b->nextColorModBrush = mapEnt->colorModBrushes; mapEnt->colorModBrushes = b; } return b; }
static void ConvertBrush(FILE * f, int num, bspBrush_t * brush, vec3_t origin) { int i, j; bspBrushSide_t *side; side_t *buildSide; bspShader_t *shader; char *texture; bspPlane_t *plane; plane_t *buildPlane; vec3_t pts[3]; bspDrawVert_t *vert[3]; int valid; /* start brush */ fprintf(f, "\t// brush %d\n", num); fprintf(f, "\t{\n"); fprintf(f, "\tbrushDef\n"); fprintf(f, "\t{\n"); /* clear out build brush */ for(i = 0; i < buildBrush->numsides; i++) { buildSide = &buildBrush->sides[i]; if(buildSide->winding != NULL) { FreeWinding(buildSide->winding); buildSide->winding = NULL; } } buildBrush->numsides = 0; /* iterate through bsp brush sides */ for(i = 0; i < brush->numSides; i++) { /* get side */ side = &bspBrushSides[brush->firstSide + i]; /* get shader */ if(side->shaderNum < 0 || side->shaderNum >= numBSPShaders) continue; shader = &bspShaders[side->shaderNum]; if(!Q_stricmp(shader->shader, "default") || !Q_stricmp(shader->shader, "noshader")) continue; /* get plane */ plane = &bspPlanes[side->planeNum]; /* add build side */ buildSide = &buildBrush->sides[buildBrush->numsides]; buildBrush->numsides++; /* tag it */ buildSide->shaderInfo = ShaderInfoForShader(shader->shader); buildSide->planenum = side->planeNum; buildSide->winding = NULL; } /* make brush windings */ if(!CreateBrushWindings(buildBrush)) return; /* iterate through build brush sides */ for(i = 0; i < buildBrush->numsides; i++) { /* get build side */ buildSide = &buildBrush->sides[i]; /* get plane */ buildPlane = &mapplanes[buildSide->planenum]; /* dummy check */ if(buildSide->shaderInfo == NULL || buildSide->winding == NULL) continue; // st-texcoords -> texMat block // start out with dummy VectorSet(buildSide->texMat[0], 1 / 32.0, 0, 0); VectorSet(buildSide->texMat[1], 0, 1 / 32.0, 0); // find surface for this side (by brute force) // surface format: // - meshverts point in pairs of three into verts // - (triangles) // - find the triangle that has most in common with our side GetBestSurfaceTriangleMatchForBrushside(buildSide, vert); valid = 0; if(vert[0] && vert[1] && vert[2]) { int i; vec3_t texX, texY; vec3_t xy1I, xy1J, xy1K; vec2_t stI, stJ, stK; vec_t D, D0, D1, D2; ComputeAxisBase(buildPlane->normal, texX, texY); VectorSet(xy1I, DotProduct(vert[0]->xyz, texX), DotProduct(vert[0]->xyz, texY), 1); VectorSet(xy1J, DotProduct(vert[1]->xyz, texX), DotProduct(vert[1]->xyz, texY), 1); VectorSet(xy1K, DotProduct(vert[2]->xyz, texX), DotProduct(vert[2]->xyz, texY), 1); stI[0] = vert[0]->st[0]; stI[1] = vert[0]->st[1]; stJ[0] = vert[1]->st[0]; stJ[1] = vert[1]->st[1]; stK[0] = vert[2]->st[0]; stK[1] = vert[2]->st[1]; // - solve linear equations: // - (x, y) := xyz . (texX, texY) // - st[i] = texMat[i][0]*x + texMat[i][1]*y + texMat[i][2] // (for three vertices) D = Det3x3(xy1I[0], xy1I[1], 1, xy1J[0], xy1J[1], 1, xy1K[0], xy1K[1], 1); if(D != 0) { for(i = 0; i < 2; ++i) { D0 = Det3x3(stI[i], xy1I[1], 1, stJ[i], xy1J[1], 1, stK[i], xy1K[1], 1); D1 = Det3x3(xy1I[0], stI[i], 1, xy1J[0], stJ[i], 1, xy1K[0], stK[i], 1); D2 = Det3x3(xy1I[0], xy1I[1], stI[i], xy1J[0], xy1J[1], stJ[i], xy1K[0], xy1K[1], stK[i]); VectorSet(buildSide->texMat[i], D0 / D, D1 / D, D2 / D); valid = 1; } } else fprintf(stderr, "degenerate triangle found when solving texMat equations for\n(%f %f %f) (%f %f %f) (%f %f %f)\n( %f %f %f )\n( %f %f %f ) -> ( %f %f )\n( %f %f %f ) -> ( %f %f )\n( %f %f %f ) -> ( %f %f )\n", buildPlane->normal[0], buildPlane->normal[1], buildPlane->normal[2], vert[0]->normal[0], vert[0]->normal[1], vert[0]->normal[2], texX[0], texX[1], texX[2], texY[0], texY[1], texY[2], vert[0]->xyz[0], vert[0]->xyz[1], vert[0]->xyz[2], xy1I[0], xy1I[1], vert[1]->xyz[0], vert[1]->xyz[1], vert[1]->xyz[2], xy1J[0], xy1J[1], vert[2]->xyz[0], vert[2]->xyz[1], vert[2]->xyz[2], xy1K[0], xy1K[1]); } else if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16)) fprintf(stderr, "no matching triangle for brushside using %s (hopefully nobody can see this side anyway)\n", buildSide->shaderInfo->shader); /* get texture name */ if(!Q_strncasecmp(buildSide->shaderInfo->shader, "textures/", 9)) texture = buildSide->shaderInfo->shader + 9; else texture = buildSide->shaderInfo->shader; /* get plane points and offset by origin */ for(j = 0; j < 3; j++) { VectorAdd(buildSide->winding->p[j], origin, pts[j]); //% pts[ j ][ 0 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 0 ] * SNAP_FLOAT_TO_INT + 0.5f ); //% pts[ j ][ 1 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 1 ] * SNAP_FLOAT_TO_INT + 0.5f ); //% pts[ j ][ 2 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 2 ] * SNAP_FLOAT_TO_INT + 0.5f ); } /* print brush side */ /* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */ fprintf(f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( ( %.8f %.8f %.8f ) ( %.8f %.8f %.8f ) ) %s %d 0 0\n", pts[0][0], pts[0][1], pts[0][2], pts[1][0], pts[1][1], pts[1][2], pts[2][0], pts[2][1], pts[2][2], buildSide->texMat[0][0], buildSide->texMat[0][1], buildSide->texMat[0][2], buildSide->texMat[1][0], buildSide->texMat[1][1], buildSide->texMat[1][2], texture, // DEBUG: valid ? 0 : C_DETAIL 0); // TODO write brush primitives format here } /* end brush */ fprintf(f, "\t}\n"); fprintf(f, "\t}\n\n"); }
void InsertModel(char *name, int frame, matrix_t transform, matrix_t nTransform, remap_t * remap, shaderInfo_t * celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle) { int i, j, k, s, numSurfaces; matrix_t identity; picoModel_t *model; picoShader_t *shader; picoSurface_t *surface; shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; char *picoShaderName; char shaderName[MAX_QPATH]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; remap_t *rm, *glob; double normalEpsilon_save; double distanceEpsilon_save; /* get model */ model = LoadModel(name, frame); if(model == NULL) return; /* handle null matrix */ if(transform == NULL) { MatrixIdentity(identity); transform = identity; } /* create transform matrix for normals */ #if 0 MatrixCopy(transform, nTransform); if(MatrixInverse(nTransform)) { Sys_FPrintf(SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n"); MatrixTranspose(transform, nTransform); } #endif /* fix bogus lightmap scale */ if(lightmapScale <= 0.0f) lightmapScale = 1.0f; /* fix bogus shade angle */ if(shadeAngle <= 0.0f) shadeAngle = 0.0f; /* each surface on the model will become a new map drawsurface */ numSurfaces = PicoGetModelNumSurfaces(model); //% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces ); for(s = 0; s < numSurfaces; s++) { /* get surface */ surface = PicoGetModelSurface(model, s); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* fix the surface's normals */ PicoFixSurfaceNormals(surface); /* allocate a surface (ydnar: gs mods) */ ds = AllocDrawSurface(SURFACE_TRIANGLES); ds->entityNum = eNum; ds->castShadows = castShadows; ds->recvShadows = recvShadows; /* get shader name */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) picoShaderName = ""; else picoShaderName = PicoGetShaderName(shader); /* handle shader remapping */ glob = NULL; for(rm = remap; rm != NULL; rm = rm->next) { if(rm->from[0] == '*' && rm->from[1] == '\0') glob = rm; else if(!Q_stricmp(picoShaderName, rm->from)) { Sys_FPrintf(SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to); picoShaderName = rm->to; glob = NULL; break; } } if(glob != NULL) { Sys_FPrintf(SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to); picoShaderName = glob->to; } /* shader renaming for sof2 */ if(renameModelShaders) { strcpy(shaderName, picoShaderName); StripExtension(shaderName); if(spawnFlags & 1) strcat(shaderName, "_RMG_BSP"); else strcat(shaderName, "_BSP"); si = ShaderInfoForShader(shaderName); } else { si = ShaderInfoForShader(picoShaderName); // Tr3B: HACK to support the messy Doom 3 materials provided by .ASE files if(!si->explicitDef) { picoShaderName = PicoGetShaderMapName(shader); Q_strncpyz(shaderName, picoShaderName, sizeof(shaderName)); StripExtension(shaderName); i = 0; while(shaderName[i]) { if(shaderName[i] == '\\') shaderName[i] = '/'; i++; } if(strstr(shaderName, "base/")) { si = ShaderInfoForShader(strstr(shaderName, "base/") + strlen("base/")); Sys_FPrintf(SYS_WRN, "WARNING: Applied .ASE material loader HACK to '%s' -> '%s'\n", picoShaderName, si->shader); } } } /* set shader */ ds->shaderInfo = si; /* force to meta? */ if((si != NULL && si->forceMeta) || (spawnFlags & 4)) /* 3rd bit */ ds->type = SURFACE_FORCED_META; /* fix the surface's normals (jal: conditioned by shader info) */ //if(!(spawnFlags & 64) && (shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META)) // PicoFixSurfaceNormals(surface); /* set sample size */ if(lightmapSampleSize > 0.0f) ds->sampleSize = lightmapSampleSize; /* set lightmap scale */ if(lightmapScale > 0.0f) ds->lightmapScale = lightmapScale; /* set shading angle */ if(shadeAngle > 0.0f) ds->shadeAngleDegrees = shadeAngle; /* set particulars */ ds->numVerts = PicoGetSurfaceNumVertexes(surface); ds->verts = safe_malloc(ds->numVerts * sizeof(ds->verts[0])); memset(ds->verts, 0, ds->numVerts * sizeof(ds->verts[0])); ds->numIndexes = PicoGetSurfaceNumIndexes(surface); ds->indexes = safe_malloc(ds->numIndexes * sizeof(ds->indexes[0])); memset(ds->indexes, 0, ds->numIndexes * sizeof(ds->indexes[0])); /* copy vertexes */ for(i = 0; i < ds->numVerts; i++) { /* get vertex */ dv = &ds->verts[i]; /* xyz and normal */ xyz = PicoGetSurfaceXYZ(surface, i); VectorCopy(xyz, dv->xyz); MatrixTransformPoint2(transform, dv->xyz); normal = PicoGetSurfaceNormal(surface, i); VectorCopy(normal, dv->normal); MatrixTransformNormal2(nTransform, dv->normal); VectorNormalize2(dv->normal, dv->normal); /* ydnar: tek-fu celshading support for flat shaded shit */ if(flat) { dv->st[0] = si->stFlat[0]; dv->st[1] = si->stFlat[1]; } /* ydnar: gs mods: added support for explicit shader texcoord generation */ else if(si->tcGen) { /* project the texture */ dv->st[0] = DotProduct(si->vecs[0], dv->xyz); dv->st[1] = DotProduct(si->vecs[1], dv->xyz); } /* normal texture coordinates */ else { st = PicoGetSurfaceST(surface, 0, i); dv->st[0] = st[0]; dv->st[1] = st[1]; } /* set lightmap/color bits */ color = PicoGetSurfaceColor(surface, 0, i); dv->paintColor[0] = color[0] / 255.0f; dv->paintColor[1] = color[1] / 255.0f; dv->paintColor[2] = color[2] / 255.0f; dv->paintColor[3] = color[3] / 255.0f; for(j = 0; j < MAX_LIGHTMAPS; j++) { dv->lightmap[j][0] = 0.0f; dv->lightmap[j][1] = 0.0f; dv->lightColor[j][0] = 255; dv->lightColor[j][1] = 255; dv->lightColor[j][2] = 255; dv->lightColor[j][3] = 255; } } /* copy indexes */ indexes = PicoGetSurfaceIndexes(surface, 0); for(i = 0; i < ds->numIndexes; i++) ds->indexes[i] = indexes[i]; /* set cel shader */ ds->celShader = celShader; /* ydnar: giant hack land: generate clipping brushes for model triangles */ if(si->clipModel || (spawnFlags & 2)) /* 2nd bit */ { vec3_t points[4], backs[3]; vec4_t plane, reverse, pa, pb, pc; /* temp hack */ if(!si->clipModel && (((si->compileFlags & C_TRANSLUCENT) && !(si->compileFlags & C_COLLISION)) || !(si->compileFlags & C_SOLID))) continue; /* walk triangle list */ for(i = 0; i < ds->numIndexes; i += 3) { /* overflow hack */ AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes + 64) << 1, allocatedmapplanes, 1024); /* make points and back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; /* copy xyz */ VectorCopy(dv->xyz, points[j]); VectorCopy(dv->xyz, backs[j]); /* find nearest axial to normal and push back points opposite */ /* note: this doesn't work as well as simply using the plane of the triangle, below */ for(k = 0; k < 3; k++) { if(fabs(dv->normal[k]) >= fabs(dv->normal[(k + 1) % 3]) && fabs(dv->normal[k]) >= fabs(dv->normal[(k + 2) % 3])) { backs[j][k] += dv->normal[k] < 0.0f ? 64.0f : -64.0f; break; } } } VectorCopy(points[0], points[3]); // for cyclic usage /* make plane for triangle */ // div0: add some extra spawnflags: // 0: snap normals to axial planes for extrusion // 8: extrude with the original normals // 16: extrude only with up/down normals (ideal for terrain) // 24: extrude by distance zero (may need engine changes) if(PlaneFromPoints(plane, points[0], points[1], points[2], qtrue)) { vec3_t bestNormal; float backPlaneDistance = 2; if(spawnFlags & 8) // use a DOWN normal { if(spawnFlags & 16) { // 24: normal as is, and zero width (broken) VectorCopy(plane, bestNormal); } else { // 8: normal as is VectorCopy(plane, bestNormal); } } else { if(spawnFlags & 16) { // 16: UP/DOWN normal VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } else { // 0: axial normal if(fabs(plane[0]) > fabs(plane[1])) // x>y if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // x>y, z>=y if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // z>=x, x>y VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); else // y>=x if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0); else // z>=y, y>=x VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } } /* build a brush */ buildBrush = AllocBrush(48); buildBrush->entityNum = mapEntityNum; buildBrush->original = buildBrush; buildBrush->contentShader = si; buildBrush->compileFlags = si->compileFlags; buildBrush->contentFlags = si->contentFlags; buildBrush->generatedClipBrush = qtrue; normalEpsilon_save = normalEpsilon; distanceEpsilon_save = distanceEpsilon; if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here { buildBrush->detail = qfalse; // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model) if(normalEpsilon > 0) normalEpsilon = 0; if(distanceEpsilon > 0) distanceEpsilon = 0; } else buildBrush->detail = qtrue; /* regenerate back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; // shift by some units VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit } /* make back plane */ VectorScale(plane, -1.0f, reverse); reverse[3] = -plane[3]; if((spawnFlags & 24) != 24) reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance; // that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane) if(PlaneFromPoints(pa, points[2], points[1], backs[1], qtrue) && PlaneFromPoints(pb, points[1], points[0], backs[0], qtrue) && PlaneFromPoints(pc, points[0], points[2], backs[2], qtrue)) { /* set up brush sides */ buildBrush->numsides = 5; buildBrush->sides[0].shaderInfo = si; for(j = 1; j < buildBrush->numsides; j++) buildBrush->sides[j].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works buildBrush->sides[0].planenum = FindFloatPlane(plane, plane[3], 3, points); buildBrush->sides[1].planenum = FindFloatPlane(pa, pa[3], 2, &points[1]); // pa contains points[1] and points[2] buildBrush->sides[2].planenum = FindFloatPlane(pb, pb[3], 2, &points[0]); // pb contains points[0] and points[1] buildBrush->sides[3].planenum = FindFloatPlane(pc, pc[3], 2, &points[2]); // pc contains points[2] and points[0] (copied to points[3] buildBrush->sides[4].planenum = FindFloatPlane(reverse, reverse[3], 3, backs); } else { free(buildBrush); continue; } normalEpsilon = normalEpsilon_save; distanceEpsilon = distanceEpsilon_save; /* add to entity */ if(CreateBrushWindings(buildBrush)) { AddBrushBevels(); //% EmitBrushes( buildBrush, NULL, NULL ); buildBrush->next = entities[mapEntityNum].brushes; entities[mapEntityNum].brushes = buildBrush; entities[mapEntityNum].numBrushes++; } else free(buildBrush); } } } } }