/* ================== FixTriangleAgainstHash Potentially splits a triangle into a list of triangles based on tjunctions ================== */ static mapTri_t *FixTriangleAgainstHash(const mapTri_t *tri) { mapTri_t *fixed; mapTri_t *a; mapTri_t *test, *next; int blocks[2][3]; int i, j, k; hashVert_t *hv; // if this triangle is degenerate after point snapping, // do nothing (this shouldn't happen, because they should // be removed as they are hashed) if (tri->hashVert[0] == tri->hashVert[1] || tri->hashVert[0] == tri->hashVert[2] || tri->hashVert[1] == tri->hashVert[2]) { return NULL; } fixed = CopyMapTri(tri); fixed->next = NULL; HashBlocksForTri(tri, blocks); for (i = blocks[0][0] ; i <= blocks[1][0] ; i++) { for (j = blocks[0][1] ; j <= blocks[1][1] ; j++) { for (k = blocks[0][2] ; k <= blocks[1][2] ; k++) { for (hv = hashVerts[i][j][k] ; hv ; hv = hv->next) { // fix all triangles in the list against this point test = fixed; fixed = NULL; for (; test ; test = next) { next = test->next; a = FixTriangleAgainstHashVert(test, hv); if (a) { // cut into two triangles a->next->next = fixed; fixed = a; FreeTri(test); } else { test->next = fixed; fixed = test; } } } } } } return fixed; }
/* ================== AddMapTriToAreas Used for curves and inlined models ================== */ void AddMapTriToAreas( mapTri_t* tri, uEntity_t* e ) { int area; idWinding* w; // skip degenerate triangles from pinched curves if( MapTriArea( tri ) <= 0 ) { return; } if( dmapGlobals.fullCarve ) { // always fragment into areas w = WindingForTri( tri ); ClipTriIntoTree_r( w, tri, e, e->tree->headnode ); return; } w = WindingForTri( tri ); area = CheckWindingInAreas_r( w, e->tree->headnode ); delete w; if( area == -1 ) { return; } if( area >= 0 ) { mapTri_t* newTri; idPlane plane; int planeNum; textureVectors_t texVec; // put in single area newTri = CopyMapTri( tri ); newTri->next = NULL; PlaneForTri( tri, plane ); planeNum = FindFloatPlane( plane ); TexVecForTri( &texVec, newTri ); AddTriListToArea( e, newTri, planeNum, area, &texVec ); } else { // fragment into areas w = WindingForTri( tri ); ClipTriIntoTree_r( w, tri, e, e->tree->headnode ); } }
/* =============== CopyTriList =============== */ mapTri_t *CopyTriList( const mapTri_t *a ) { mapTri_t *testList; const mapTri_t *tri; testList = NULL; for ( tri = a ; tri ; tri = tri->next ) { mapTri_t *copy; copy = CopyMapTri( tri ); copy ->next = testList; testList = copy; } return testList; }
/* =============== RemoveBadTris Return a new list with any zero or negative area triangles removed =============== */ mapTri_t *RemoveBadTris( const mapTri_t *list ) { mapTri_t *newList; mapTri_t *copy; const mapTri_t *tri; newList = NULL; for ( tri = list ; tri ; tri = tri->next ) { if ( MapTriArea( tri ) > 0 ) { copy = CopyMapTri( tri ); copy->next = newList; newList = copy; } } return newList; }
// RB begin int FilterMeshesIntoTree_r( idWinding* w, mapTri_t* originalTri, node_t* node ) { idWinding* front, *back; int c; if( !w ) { return 0; } if( node->planenum == PLANENUM_LEAF ) { // add it to the leaf list if( originalTri->material->GetContentFlags() & CONTENTS_AREAPORTAL ) { mapTri_t* list = CopyMapTri( originalTri ); list->next = NULL; node->areaPortalTris = MergeTriLists( node->areaPortalTris, list ); } const MapPolygonMesh* mapMesh = originalTri->originalMapMesh; // classify the leaf by the structural brush if( mapMesh->IsOpaque() ) { node->opaque = true; } delete w; return 1; } // split it by the node plane w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back ); delete w; c = 0; c += FilterMeshesIntoTree_r( front, originalTri, node->children[0] ); c += FilterMeshesIntoTree_r( back, originalTri, node->children[1] ); return c; }
/* ================== FixTriangleAgainstHashVert Returns a list of two new mapTri if the hashVert is on an edge of the given mapTri, otherwise returns NULL. ================== */ static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) { int i; const idDrawVert *v1, *v2; idDrawVert split; idVec3 dir; float len; float frac; mapTri_t *new1, *new2; idVec3 temp; float d, off; const idVec3 *v; idPlane plane1, plane2; v = &hv->v; // if the triangle already has this hashVert as a vert, // it can't be split by it if ( a->hashVert[0] == hv || a->hashVert[1] == hv || a->hashVert[2] == hv ) { return NULL; } split.Clear(); // we probably should find the edge that the vertex is closest to. // it is possible to be < 1 unit away from multiple // edges, but we only want to split by one of them for ( i = 0 ; i < 3 ; i++ ) { v1 = &a->v[i]; v2 = &a->v[(i+1)%3]; VectorSubtract( v2->xyz, v1->xyz, dir ); len = dir.Normalize(); // if it is close to one of the edge vertexes, skip it VectorSubtract( *v, v1->xyz, temp ); d = DotProduct( temp, dir ); if ( d <= 0 || d >= len ) { continue; } // make sure it is on the line VectorMA( v1->xyz, d, dir, temp ); VectorSubtract( temp, *v, temp ); off = temp.Length(); if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) { continue; } // take the x/y/z from the splitter, // but interpolate everything else from the original tri VectorCopy( *v, split.xyz ); frac = d / len; split.st[0] = v1->st[0] + frac * ( v2->st[0] - v1->st[0] ); split.st[1] = v1->st[1] + frac * ( v2->st[1] - v1->st[1] ); split.normal[0] = v1->normal[0] + frac * ( v2->normal[0] - v1->normal[0] ); split.normal[1] = v1->normal[1] + frac * ( v2->normal[1] - v1->normal[1] ); split.normal[2] = v1->normal[2] + frac * ( v2->normal[2] - v1->normal[2] ); split.normal.Normalize(); // split the tri new1 = CopyMapTri( a ); new1->v[(i+1)%3] = split; new1->hashVert[(i+1)%3] = hv; new1->next = NULL; new2 = CopyMapTri( a ); new2->v[i] = split; new2->hashVert[i] = hv; new2->next = new1; plane1.FromPoints( new1->hashVert[0]->v, new1->hashVert[1]->v, new1->hashVert[2]->v ); plane2.FromPoints( new2->hashVert[0]->v, new2->hashVert[1]->v, new2->hashVert[2]->v ); d = DotProduct( plane1, plane2 ); // if the two split triangle's normals don't face the same way, // it should not be split if ( d <= 0 ) { FreeTriList( new2 ); continue; } return new2; } return NULL; }
/* ================= ClipTriByLight Carves a triangle by the frustom planes of a light, producing a (possibly empty) list of triangles on the inside and outside. The original triangle is not modified. If no clipping is required, the result will be a copy of the original. If clipping was required, the outside fragments will be planar clips, which will benefit from re-optimization. ================= */ static void ClipTriByLight( const mapLight_t *light, const mapTri_t *tri, mapTri_t **in, mapTri_t **out ) { idWinding *inside, *oldInside; idWinding *outside[6]; bool hasOutside; int i; *in = NULL; *out = NULL; // clip this winding to the light inside = WindingForTri( tri ); hasOutside = false; for ( i = 0 ; i < 6 ; i++ ) { oldInside = inside; if ( oldInside ) { oldInside->Split( light->def.frustum[i], 0, &outside[i], &inside ); delete oldInside; } else { outside[i] = NULL; } if ( outside[i] ) { hasOutside = true; } } if ( !inside ) { // the entire winding is outside this light // free the clipped fragments for ( i = 0 ; i < 6 ; i++ ) { if ( outside[i] ) { delete outside[i]; } } *out = CopyMapTri( tri ); (*out)->next = NULL; return; } if ( !hasOutside ) { // the entire winding is inside this light // free the inside copy delete inside; *in = CopyMapTri( tri ); (*in)->next = NULL; return; } // the winding is split *in = WindingToTriList( inside, tri ); delete inside; // combine all the outside fragments for ( i = 0 ; i < 6 ; i++ ) { if ( outside[i] ) { mapTri_t *list; list = WindingToTriList( outside[i], tri ); delete outside[i]; *out = MergeTriLists( *out, list ); } } }