/* ================ idPhysics_StaticMulti::GetBounds ================ */ const idBounds &idPhysics_StaticMulti::GetBounds( int id ) const { int i; static idBounds bounds; if ( id >= 0 && id < clipModels.Num() ) { if ( clipModels[id] ) { return clipModels[id]->GetBounds(); } } if ( id == -1 ) { bounds.Clear(); for ( i = 0; i < clipModels.Num(); i++ ) { if ( clipModels[i] ) { bounds.AddBounds( clipModels[i]->GetAbsBounds() ); } } for ( i = 0; i < clipModels.Num(); i++ ) { if ( clipModels[i] ) { bounds[0] -= clipModels[i]->GetOrigin(); bounds[1] -= clipModels[i]->GetOrigin(); break; } } return bounds; } return bounds_zero; }
/* ==================== idMD5Mesh::CalculateBounds ==================== */ void idMD5Mesh::CalculateBounds( const idJointMat * entJoints, idBounds & bounds ) const { __m128 minX = vector_float_posInfinity; __m128 minY = vector_float_posInfinity; __m128 minZ = vector_float_posInfinity; __m128 maxX = vector_float_negInfinity; __m128 maxY = vector_float_negInfinity; __m128 maxZ = vector_float_negInfinity; for ( int i = 0; i < numMeshJoints; i++ ) { const idJointMat & joint = entJoints[meshJoints[i]]; __m128 x = _mm_load_ps( joint.ToFloatPtr() + 0 * 4 ); __m128 y = _mm_load_ps( joint.ToFloatPtr() + 1 * 4 ); __m128 z = _mm_load_ps( joint.ToFloatPtr() + 2 * 4 ); minX = _mm_min_ps( minX, x ); minY = _mm_min_ps( minY, y ); minZ = _mm_min_ps( minZ, z ); maxX = _mm_max_ps( maxX, x ); maxY = _mm_max_ps( maxY, y ); maxZ = _mm_max_ps( maxZ, z ); } __m128 expand = _mm_splat_ps( _mm_load_ss( & maxJointVertDist ), 0 ); minX = _mm_sub_ps( minX, expand ); minY = _mm_sub_ps( minY, expand ); minZ = _mm_sub_ps( minZ, expand ); maxX = _mm_add_ps( maxX, expand ); maxY = _mm_add_ps( maxY, expand ); maxZ = _mm_add_ps( maxZ, expand ); _mm_store_ss( bounds.ToFloatPtr() + 0, _mm_splat_ps( minX, 3 ) ); _mm_store_ss( bounds.ToFloatPtr() + 1, _mm_splat_ps( minY, 3 ) ); _mm_store_ss( bounds.ToFloatPtr() + 2, _mm_splat_ps( minZ, 3 ) ); _mm_store_ss( bounds.ToFloatPtr() + 3, _mm_splat_ps( maxX, 3 ) ); _mm_store_ss( bounds.ToFloatPtr() + 4, _mm_splat_ps( maxY, 3 ) ); _mm_store_ss( bounds.ToFloatPtr() + 5, _mm_splat_ps( maxZ, 3 ) ); }
/* ================ BoundTriList ================ */ void BoundTriList( const mapTri_t *list, idBounds &b ) { b.Clear(); for ( ; list ; list = list->next ) { b.AddPoint( list->v[0].xyz ); b.AddPoint( list->v[1].xyz ); b.AddPoint( list->v[2].xyz ); } }
/* ================= HashTriangles Removes triangles that are degenerated or flipped backwards ================= */ void HashTriangles(optimizeGroup_t *groupList) { mapTri_t *a; int vert; int i; optimizeGroup_t *group; // clear the hash tables memset(hashVerts, 0, sizeof(hashVerts)); numHashVerts = 0; numTotalVerts = 0; // bound all the triangles to determine the bucket size hashBounds.Clear(); for (group = groupList ; group ; group = group->nextGroup) { for (a = group->triList ; a ; a = a->next) { hashBounds.AddPoint(a->v[0].xyz); hashBounds.AddPoint(a->v[1].xyz); hashBounds.AddPoint(a->v[2].xyz); } } // spread the bounds so it will never have a zero size for (i = 0 ; i < 3 ; i++) { hashBounds[0][i] = floor(hashBounds[0][i] - 1); hashBounds[1][i] = ceil(hashBounds[1][i] + 1); hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS; hashScale[i] = (hashBounds[1][i] - hashBounds[0][i]) / HASH_BINS; hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS; if (hashIntScale[i] < 1) { hashIntScale[i] = 1; } } // add all the points to the hash buckets for (group = groupList ; group ; group = group->nextGroup) { // don't create tjunctions against discrete surfaces (blood decals, etc) if (group->material != NULL && group->material->IsDiscrete()) { continue; } for (a = group->triList ; a ; a = a->next) { for (vert = 0 ; vert < 3 ; vert++) { a->hashVert[vert] = GetHashVert(a->v[vert].xyz); } } } }
/* ================ RB_DrawBounds ================ */ void RB_DrawBounds( const idBounds &bounds ) { if ( bounds.IsCleared() ) { return; } qglBegin( GL_LINE_LOOP ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[0][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[0][2] ); qglEnd(); qglBegin( GL_LINE_LOOP ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[1][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[1][2] ); qglEnd(); qglBegin( GL_LINES ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[0][2] ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[1][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[1][2] ); qglEnd(); }
/* ================= AddOriginalEdges ================= */ static void AddOriginalEdges( optimizeGroup_t *opt ) { mapTri_t *tri; optVertex_t *v[3]; int numTris; if ( dmapGlobals.verbose ) { common->Printf( "----\n" ); common->Printf( "%6i original tris\n", CountTriList( opt->triList ) ); } optBounds.Clear(); // allocate space for max possible edges numTris = CountTriList( opt->triList ); originalEdges = (originalEdges_t *)Mem_Alloc( numTris * 3 * sizeof( *originalEdges ), TAG_DMAP ); numOriginalEdges = 0; // add all unique triangle edges numOptVerts = 0; numOptEdges = 0; for ( tri = opt->triList ; tri ; tri = tri->next ) { v[0] = tri->optVert[0] = FindOptVertex( &tri->v[0], opt ); v[1] = tri->optVert[1] = FindOptVertex( &tri->v[1], opt ); v[2] = tri->optVert[2] = FindOptVertex( &tri->v[2], opt ); AddOriginalTriangle( v ); } }
/* ================ FindOptVertex ================ */ optVertex_t *FindOptVertex(idDrawVert *v, optimizeGroup_t *opt) { int i; float x, y; optVertex_t *vert; // deal with everything strictly as 2D x = v->xyz * opt->axis[0]; y = v->xyz * opt->axis[1]; // should we match based on the t-junction fixing hash verts? for (i = 0 ; i < numOptVerts ; i++) { if (optVerts[i].pv[0] == x && optVerts[i].pv[1] == y) { return &optVerts[i]; } } if (numOptVerts >= MAX_OPT_VERTEXES) { common->Error("MAX_OPT_VERTEXES"); return NULL; } numOptVerts++; vert = &optVerts[i]; memset(vert, 0, sizeof(*vert)); vert->v = *v; vert->pv[0] = x; vert->pv[1] = y; vert->pv[2] = 0; optBounds.AddPoint(vert->pv); return vert; }
/* ============ idAASFileLocal::BoundsReachableAreaNum_r ============ */ int idAASFileLocal::BoundsReachableAreaNum_r( int nodeNum, const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const { int res; const aasNode_t *node; while( nodeNum ) { if ( nodeNum < 0 ) { if ( ( areas[-nodeNum].flags & areaFlags ) && ( ( areas[-nodeNum].travelFlags & excludeTravelFlags ) == 0 ) ) { return -nodeNum; } return 0; } node = &nodes[nodeNum]; res = bounds.PlaneSide( planeList[node->planeNum] ); if ( res == PLANESIDE_BACK ) { nodeNum = node->children[1]; } else if ( res == PLANESIDE_FRONT ) { nodeNum = node->children[0]; } else { nodeNum = BoundsReachableAreaNum_r( node->children[1], bounds, areaFlags, excludeTravelFlags ); if ( nodeNum ) { return nodeNum; } nodeNum = node->children[0]; } } return 0; }
/* ==================== idMD5Anim::GetBounds ==================== */ void idMD5Anim::GetBounds( idBounds &bnds, int time, int cyclecount ) const { frameBlend_t frame; idVec3 offset; ConvertTimeToFrame( time, cyclecount, frame ); bnds = bounds[ frame.frame1 ]; bnds.AddBounds( bounds[ frame.frame2 ] ); // origin position offset = baseFrame[ 0 ].t; if ( jointInfo[ 0 ].animBits & ( ANIM_TX | ANIM_TY | ANIM_TZ ) ) { const float *componentPtr1 = &componentFrames[ numAnimatedComponents * frame.frame1 + jointInfo[ 0 ].firstComponent ]; const float *componentPtr2 = &componentFrames[ numAnimatedComponents * frame.frame2 + jointInfo[ 0 ].firstComponent ]; if ( jointInfo[ 0 ].animBits & ANIM_TX ) { offset.x = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; componentPtr1++; componentPtr2++; } if ( jointInfo[ 0 ].animBits & ANIM_TY ) { offset.y = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; componentPtr1++; componentPtr2++; } if ( jointInfo[ 0 ].animBits & ANIM_TZ ) { offset.z = *componentPtr1 * frame.frontlerp + *componentPtr2 * frame.backlerp; } } bnds[ 0 ] -= offset; bnds[ 1 ] -= offset; }
/* ==================== idSurface_Polytope::SetupTetrahedron ==================== */ void idSurface_Polytope::SetupTetrahedron( const idBounds &bounds ) { idVec3 center, scale; float c1, c2, c3; c1 = 0.4714045207f; c2 = 0.8164965809f; c3 = -0.3333333333f; center = bounds.GetCenter(); scale = bounds[1] - center; verts.SetNum( 4 ); verts[0].xyz = center + idVec3( 0.0f, 0.0f, scale.z ); verts[1].xyz = center + idVec3( 2.0f * c1 * scale.x, 0.0f, c3 * scale.z ); verts[2].xyz = center + idVec3( -c1 * scale.x, c2 * scale.y, c3 * scale.z ); verts[3].xyz = center + idVec3( -c1 * scale.x, -c2 * scale.y, c3 * scale.z ); indexes.SetNum( 4*3 ); indexes[0*3+0] = 0; indexes[0*3+1] = 1; indexes[0*3+2] = 2; indexes[1*3+0] = 0; indexes[1*3+1] = 2; indexes[1*3+2] = 3; indexes[2*3+0] = 0; indexes[2*3+1] = 3; indexes[2*3+2] = 1; indexes[3*3+0] = 1; indexes[3*3+1] = 3; indexes[3*3+2] = 2; GenerateEdgeIndexes(); }
/* ==================== idSurface_Polytope::SetupHexahedron ==================== */ void idSurface_Polytope::SetupHexahedron(const idBounds &bounds) { idVec3 center, scale; center = bounds.GetCenter(); scale = bounds[1] - center; verts.SetNum(8); verts[0].xyz = center + idVec3(-scale.x, -scale.y, -scale.z); verts[1].xyz = center + idVec3(scale.x, -scale.y, -scale.z); verts[2].xyz = center + idVec3(scale.x, scale.y, -scale.z); verts[3].xyz = center + idVec3(-scale.x, scale.y, -scale.z); verts[4].xyz = center + idVec3(-scale.x, -scale.y, scale.z); verts[5].xyz = center + idVec3(scale.x, -scale.y, scale.z); verts[6].xyz = center + idVec3(scale.x, scale.y, scale.z); verts[7].xyz = center + idVec3(-scale.x, scale.y, scale.z); indexes.SetNum(12*3); indexes[ 0*3+0] = 0; indexes[ 0*3+1] = 3; indexes[ 0*3+2] = 2; indexes[ 1*3+0] = 0; indexes[ 1*3+1] = 2; indexes[ 1*3+2] = 1; indexes[ 2*3+0] = 0; indexes[ 2*3+1] = 1; indexes[ 2*3+2] = 5; indexes[ 3*3+0] = 0; indexes[ 3*3+1] = 5; indexes[ 3*3+2] = 4; indexes[ 4*3+0] = 0; indexes[ 4*3+1] = 4; indexes[ 4*3+2] = 7; indexes[ 5*3+0] = 0; indexes[ 5*3+1] = 7; indexes[ 5*3+2] = 3; indexes[ 6*3+0] = 6; indexes[ 6*3+1] = 5; indexes[ 6*3+2] = 1; indexes[ 7*3+0] = 6; indexes[ 7*3+1] = 1; indexes[ 7*3+2] = 2; indexes[ 8*3+0] = 6; indexes[ 8*3+1] = 2; indexes[ 8*3+2] = 3; indexes[ 9*3+0] = 6; indexes[ 9*3+1] = 3; indexes[ 9*3+2] = 7; indexes[10*3+0] = 6; indexes[10*3+1] = 7; indexes[10*3+2] = 4; indexes[11*3+0] = 6; indexes[11*3+1] = 4; indexes[11*3+2] = 5; GenerateEdgeIndexes(); }
/* ==================== idSurface_Polytope::SetupOctahedron ==================== */ void idSurface_Polytope::SetupOctahedron(const idBounds &bounds) { idVec3 center, scale; center = bounds.GetCenter(); scale = bounds[1] - center; verts.SetNum(6); verts[0].xyz = center + idVec3(scale.x, 0.0f, 0.0f); verts[1].xyz = center + idVec3(-scale.x, 0.0f, 0.0f); verts[2].xyz = center + idVec3(0.0f, scale.y, 0.0f); verts[3].xyz = center + idVec3(0.0f, -scale.y, 0.0f); verts[4].xyz = center + idVec3(0.0f, 0.0f, scale.z); verts[5].xyz = center + idVec3(0.0f, 0.0f, -scale.z); indexes.SetNum(8*3); indexes[0*3+0] = 4; indexes[0*3+1] = 0; indexes[0*3+2] = 2; indexes[1*3+0] = 4; indexes[1*3+1] = 2; indexes[1*3+2] = 1; indexes[2*3+0] = 4; indexes[2*3+1] = 1; indexes[2*3+2] = 3; indexes[3*3+0] = 4; indexes[3*3+1] = 3; indexes[3*3+2] = 0; indexes[4*3+0] = 5; indexes[4*3+1] = 2; indexes[4*3+2] = 0; indexes[5*3+0] = 5; indexes[5*3+1] = 1; indexes[5*3+2] = 2; indexes[6*3+0] = 5; indexes[6*3+1] = 3; indexes[6*3+2] = 1; indexes[7*3+0] = 5; indexes[7*3+1] = 0; indexes[7*3+2] = 3; GenerateEdgeIndexes(); }
/* ========================= R_PreciseCullSurface Check the surface for visibility on a per-triangle basis for cases when it is going to be VERY expensive to draw (subviews) If not culled, also returns the bounding box of the surface in Normalized Device Coordinates, so it can be used to crop the scissor rect. OPTIMIZE: we could also take exact portal passing into consideration ========================= */ bool R_PreciseCullSurface( const drawSurf_t *drawSurf, idBounds &ndcBounds ) { const srfTriangles_t *tri; int numTriangles; idPlane clip, eye; int i, j; unsigned int pointOr; unsigned int pointAnd; idVec3 localView; idFixedWinding w; tri = drawSurf->geo; pointOr = 0; pointAnd = (unsigned int)~0; // get an exact bounds of the triangles for scissor cropping ndcBounds.Clear(); for ( i = 0; i < tri->numVerts; i++ ) { int j; unsigned int pointFlags; R_TransformModelToClip( tri->verts[i].xyz, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); pointFlags = 0; for ( j = 0; j < 3; j++ ) { if ( clip[j] >= clip[3] ) { pointFlags |= (1 << (j*2)); } else if ( clip[j] <= -clip[3] ) { pointFlags |= ( 1 << (j*2+1)); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if ( pointAnd ) { return true; } // backface and frustum cull numTriangles = tri->numIndexes / 3; R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView ); for ( i = 0; i < tri->numIndexes; i += 3 ) { idVec3 dir, normal; float dot; idVec3 d1, d2; const idVec3 &v1 = tri->verts[tri->indexes[i]].xyz; const idVec3 &v2 = tri->verts[tri->indexes[i+1]].xyz; const idVec3 &v3 = tri->verts[tri->indexes[i+2]].xyz; // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized // axis that we get from the gui view transform. It doesn't hurt anything, because // we know that all gui generated surfaces are front facing if ( tr.guiRecursionLevel == 0 ) { // we don't care that it isn't normalized, // all we want is the sign d1 = v2 - v1; d2 = v3 - v1; normal = d2.Cross( d1 ); dir = v1 - localView; dot = normal * dir; if ( dot >= 0.0f ) { return true; } } // now find the exact screen bounds of the clipped triangle w.SetNumPoints( 3 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() ); w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f; for ( j = 0; j < 4; j++ ) { if ( !w.ClipInPlace( -tr.viewDef->frustum[j], 0.1f ) ) { break; } } for ( j = 0; j < w.GetNumPoints(); j++ ) { idVec3 screen; R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen ); ndcBounds.AddPoint( screen ); } } // if we don't enclose any area, return if ( ndcBounds.IsCleared() ) { return true; } return false; }
/* ================== FixGlobalTjunctions ================== */ void FixGlobalTjunctions( uEntity_t *e ) { mapTri_t *a; int vert; int i; optimizeGroup_t *group; int areaNum; common->Printf( "----- FixGlobalTjunctions -----\n" ); // clear the hash tables memset( hashVerts, 0, sizeof( hashVerts ) ); numHashVerts = 0; numTotalVerts = 0; // bound all the triangles to determine the bucket size hashBounds.Clear(); for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) { for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) { for ( a = group->triList ; a ; a = a->next ) { hashBounds.AddPoint( a->v[0].xyz ); hashBounds.AddPoint( a->v[1].xyz ); hashBounds.AddPoint( a->v[2].xyz ); } } } // spread the bounds so it will never have a zero size for ( i = 0 ; i < 3 ; i++ ) { hashBounds[0][i] = floor( hashBounds[0][i] - 1 ); hashBounds[1][i] = ceil( hashBounds[1][i] + 1 ); hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS; hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS; hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS; if ( hashIntScale[i] < 1 ) { hashIntScale[i] = 1; } } // add all the points to the hash buckets for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) { for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) { // don't touch discrete surfaces if ( group->material != NULL && group->material->IsDiscrete() ) { continue; } for ( a = group->triList ; a ; a = a->next ) { for ( vert = 0 ; vert < 3 ; vert++ ) { a->hashVert[vert] = GetHashVert( a->v[vert].xyz ); } } } } // add all the func_static model vertexes to the hash buckets // optionally inline some of the func_static models if ( dmapGlobals.entityNum == 0 ) { for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) { uEntity_t *entity = &dmapGlobals.uEntities[eNum]; const char *className = entity->mapEntity->epairs.GetString( "classname" ); if ( idStr::Icmp( className, "func_static" ) ) { continue; } const char *modelName = entity->mapEntity->epairs.GetString( "model" ); if ( !modelName ) { continue; } if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) { continue; } idRenderModel *model = renderModelManager->FindModel( modelName ); // common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) ); idMat3 axis; // get the rotation matrix in either full form, or single angle form if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) { float angle = entity->mapEntity->epairs.GetFloat( "angle" ); if ( angle != 0.0f ) { axis = idAngles( 0.0f, angle, 0.0f ).ToMat3(); } else { axis.Identity(); } } idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" ); for ( i = 0 ; i < model->NumSurfaces() ; i++ ) { const modelSurface_t *surface = model->Surface( i ); const srfTriangles_t *tri = surface->geometry; mapTri_t mapTri; memset( &mapTri, 0, sizeof( mapTri ) ); mapTri.material = surface->shader; // don't let discretes (autosprites, etc) merge together if ( mapTri.material->IsDiscrete() ) { mapTri.mergeGroup = (void *)surface; } for ( int j = 0 ; j < tri->numVerts ; j += 3 ) { idVec3 v = tri->verts[j].xyz * axis + origin; GetHashVert( v ); } } } } // now fix each area for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) { for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) { // don't touch discrete surfaces if ( group->material != NULL && group->material->IsDiscrete() ) { continue; } mapTri_t *newList = NULL; for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) { mapTri_t *fixed = FixTriangleAgainstHash( tri ); newList = MergeTriLists( newList, fixed ); } FreeTriList( group->triList ); group->triList = newList; } } // done FreeTJunctionHash(); }
/* ========================= R_PreciseCullSurface Check the surface for visibility on a per-triangle basis for cases when it is going to be VERY expensive to draw (subviews) If not culled, also returns the bounding box of the surface in Normalized Device Coordinates, so it can be used to crop the scissor rect. OPTIMIZE: we could also take exact portal passing into consideration ========================= */ bool R_PreciseCullSurface( const drawSurf_t* drawSurf, idBounds& ndcBounds ) { const srfTriangles_t* tri = drawSurf->frontEndGeo; unsigned int pointOr = 0; unsigned int pointAnd = ( unsigned int )~0; // get an exact bounds of the triangles for scissor cropping ndcBounds.Clear(); const idJointMat* joints = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? tri->staticModelWithJoints->jointsInverted : NULL; for( int i = 0; i < tri->numVerts; i++ ) { const idVec3 vXYZ = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[i], joints ); idPlane eye, clip; R_TransformModelToClip( vXYZ, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); unsigned int pointFlags = 0; for( int j = 0; j < 3; j++ ) { if( clip[j] >= clip[3] ) { pointFlags |= ( 1 << ( j * 2 + 0 ) ); } else if( clip[j] <= -clip[3] ) // FIXME: the D3D near clip plane is at zero instead of -1 { pointFlags |= ( 1 << ( j * 2 + 1 ) ); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if( pointAnd != 0 ) { return true; } // backface and frustum cull idVec3 localViewOrigin; R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin ); for( int i = 0; i < tri->numIndexes; i += 3 ) { const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 0 ] ], joints ); const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 1 ] ], joints ); const idVec3 v3 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 2 ] ], joints ); // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized // axis that we get from the gui view transform. It doesn't hurt anything, because // we know that all gui generated surfaces are front facing if( tr.guiRecursionLevel == 0 ) { // we don't care that it isn't normalized, // all we want is the sign const idVec3 d1 = v2 - v1; const idVec3 d2 = v3 - v1; const idVec3 normal = d2.Cross( d1 ); const idVec3 dir = v1 - localViewOrigin; const float dot = normal * dir; if( dot >= 0.0f ) { return true; } } // now find the exact screen bounds of the clipped triangle idFixedWinding w; w.SetNumPoints( 3 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() ); w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f; for( int j = 0; j < 4; j++ ) { if( !w.ClipInPlace( -tr.viewDef->frustums[FRUSTUM_PRIMARY][j], 0.1f ) ) { break; } } for( int j = 0; j < w.GetNumPoints(); j++ ) { idVec3 screen; R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen ); ndcBounds.AddPoint( screen ); } } // if we don't enclose any area, return if( ndcBounds.IsCleared() ) { return true; } return false; }
/* ============ GetObstacles ============ */ int GetObstacles( const idPhysics *physics, const idAAS *aas, const idEntity *ignore, int areaNum, const idVec3 &startPos, const idVec3 &seekPos, obstacle_t *obstacles, int maxObstacles, idBounds &clipBounds ) { int i, j, numListedClipModels, numObstacles, numVerts, clipMask, blockingObstacle, blockingEdgeNum; int wallEdges[MAX_AAS_WALL_EDGES], numWallEdges, verts[2], lastVerts[2], nextVerts[2]; float stepHeight, headHeight, blockingScale, min, max; idVec3 seekDelta, silVerts[32], start, end, nextStart, nextEnd; idVec2 expBounds[2], edgeDir, edgeNormal, nextEdgeDir, nextEdgeNormal, lastEdgeNormal; idVec2 obDelta; idPhysics *obPhys; idBox box; idEntity *obEnt; idClipModel *clipModel; idClipModel *clipModelList[ MAX_GENTITIES ]; numObstacles = 0; seekDelta = seekPos - startPos; expBounds[0] = physics->GetBounds()[0].ToVec2() - idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON ); expBounds[1] = physics->GetBounds()[1].ToVec2() + idVec2( CM_BOX_EPSILON, CM_BOX_EPSILON ); physics->GetAbsBounds().AxisProjection( -physics->GetGravityNormal(), stepHeight, headHeight ); stepHeight += aas->GetSettings()->maxStepHeight; // clip bounds for the obstacle search space clipBounds[0] = clipBounds[1] = startPos; clipBounds.AddPoint( seekPos ); clipBounds.ExpandSelf( MAX_OBSTACLE_RADIUS ); clipMask = physics->GetClipMask(); // find all obstacles touching the clip bounds numListedClipModels = gameLocal.clip.ClipModelsTouchingBounds( clipBounds, clipMask, clipModelList, MAX_GENTITIES ); for ( i = 0; i < numListedClipModels && numObstacles < MAX_OBSTACLES; i++ ) { clipModel = clipModelList[i]; obEnt = clipModel->GetEntity(); if ( !clipModel->IsTraceModel() ) { continue; } if ( obEnt->IsType( idActor::Type ) ) { obPhys = obEnt->GetPhysics(); // ignore myself, my enemy, and dead bodies if ( ( obPhys == physics ) || ( obEnt == ignore ) || ( obEnt->health <= 0 ) ) { continue; } // if the actor is moving idVec3 v1 = obPhys->GetLinearVelocity(); if ( v1.LengthSqr() > Square( 10.0f ) ) { idVec3 v2 = physics->GetLinearVelocity(); if ( v2.LengthSqr() > Square( 10.0f ) ) { // if moving in about the same direction if ( v1 * v2 > 0.0f ) { continue; } } } } else if ( obEnt->IsType( idMoveable::Type ) ) { // moveables are considered obstacles } else { // ignore everything else continue; } // check if we can step over the object clipModel->GetAbsBounds().AxisProjection( -physics->GetGravityNormal(), min, max ); if ( max < stepHeight || min > headHeight ) { // can step over this one continue; } // project a box containing the obstacle onto the floor plane box = idBox( clipModel->GetBounds(), clipModel->GetOrigin(), clipModel->GetAxis() ); numVerts = box.GetParallelProjectionSilhouetteVerts( physics->GetGravityNormal(), silVerts ); // create a 2D winding for the obstacle; obstacle_t &obstacle = obstacles[numObstacles++]; obstacle.winding.Clear(); for ( j = 0; j < numVerts; j++ ) { obstacle.winding.AddPoint( silVerts[j].ToVec2() ); } if ( ai_showObstacleAvoidance.GetBool() ) { for ( j = 0; j < numVerts; j++ ) { silVerts[j].z = startPos.z; } for ( j = 0; j < numVerts; j++ ) { gameRenderWorld->DebugArrow( colorWhite, silVerts[j], silVerts[(j+1)%numVerts], 4 ); } } // expand the 2D winding for collision with a 2D box obstacle.winding.ExpandForAxialBox( expBounds ); obstacle.winding.GetBounds( obstacle.bounds ); obstacle.entity = obEnt; } // if there are no dynamic obstacles the path should be through valid AAS space if ( numObstacles == 0 ) { return 0; } // if the current path doesn't intersect any dynamic obstacles the path should be through valid AAS space if ( PointInsideObstacle( obstacles, numObstacles, startPos.ToVec2() ) == -1 ) { if ( !GetFirstBlockingObstacle( obstacles, numObstacles, -1, startPos.ToVec2(), seekDelta.ToVec2(), blockingScale, blockingObstacle, blockingEdgeNum ) ) { return 0; } } // create obstacles for AAS walls if ( aas ) { float halfBoundsSize = ( expBounds[ 1 ].x - expBounds[ 0 ].x ) * 0.5f; numWallEdges = aas->GetWallEdges( areaNum, clipBounds, TFL_WALK, wallEdges, MAX_AAS_WALL_EDGES ); aas->SortWallEdges( wallEdges, numWallEdges ); lastVerts[0] = lastVerts[1] = 0; lastEdgeNormal.Zero(); nextVerts[0] = nextVerts[1] = 0; for ( i = 0; i < numWallEdges && numObstacles < MAX_OBSTACLES; i++ ) { aas->GetEdge( wallEdges[i], start, end ); aas->GetEdgeVertexNumbers( wallEdges[i], verts ); edgeDir = end.ToVec2() - start.ToVec2(); edgeDir.Normalize(); edgeNormal.x = edgeDir.y; edgeNormal.y = -edgeDir.x; if ( i < numWallEdges-1 ) { aas->GetEdge( wallEdges[i+1], nextStart, nextEnd ); aas->GetEdgeVertexNumbers( wallEdges[i+1], nextVerts ); nextEdgeDir = nextEnd.ToVec2() - nextStart.ToVec2(); nextEdgeDir.Normalize(); nextEdgeNormal.x = nextEdgeDir.y; nextEdgeNormal.y = -nextEdgeDir.x; } obstacle_t &obstacle = obstacles[numObstacles++]; obstacle.winding.Clear(); obstacle.winding.AddPoint( end.ToVec2() ); obstacle.winding.AddPoint( start.ToVec2() ); obstacle.winding.AddPoint( start.ToVec2() - edgeDir - edgeNormal * halfBoundsSize ); obstacle.winding.AddPoint( end.ToVec2() + edgeDir - edgeNormal * halfBoundsSize ); if ( lastVerts[1] == verts[0] ) { obstacle.winding[2] -= lastEdgeNormal * halfBoundsSize; } else { obstacle.winding[1] -= edgeDir; } if ( verts[1] == nextVerts[0] ) { obstacle.winding[3] -= nextEdgeNormal * halfBoundsSize; } else { obstacle.winding[0] += edgeDir; } obstacle.winding.GetBounds( obstacle.bounds ); obstacle.entity = NULL; memcpy( lastVerts, verts, sizeof( lastVerts ) ); lastEdgeNormal = edgeNormal; } } // show obstacles if ( ai_showObstacleAvoidance.GetBool() ) { for ( i = 0; i < numObstacles; i++ ) { obstacle_t &obstacle = obstacles[i]; for ( j = 0; j < obstacle.winding.GetNumPoints(); j++ ) { silVerts[j].ToVec2() = obstacle.winding[j]; silVerts[j].z = startPos.z; } for ( j = 0; j < obstacle.winding.GetNumPoints(); j++ ) { gameRenderWorld->DebugArrow( colorGreen, silVerts[j], silVerts[(j+1)%obstacle.winding.GetNumPoints()], 4 ); } } } return numObstacles; }
/* ============ idFrustum::ProjectionBounds ============ */ bool idFrustum::ProjectionBounds( const idBox &box, idBounds &projectionBounds ) const { int i, p1, p2, pointCull[8], culled, outside; float scale1, scale2; idFrustum localFrustum; idVec3 points[8], localOrigin; idMat3 localAxis, localScaled; idBounds bounds( -box.GetExtents(), box.GetExtents() ); // if the frustum origin is inside the bounds if( bounds.ContainsPoint( ( origin - box.GetCenter() ) * box.GetAxis().Transpose() ) ) { // bounds that cover the whole frustum float boxMin, boxMax, base; base = origin * axis[0]; box.AxisProjection( axis[0], boxMin, boxMax ); projectionBounds[0].x = boxMin - base; projectionBounds[1].x = boxMax - base; projectionBounds[0].y = projectionBounds[0].z = -1.0f; projectionBounds[1].y = projectionBounds[1].z = 1.0f; return true; } projectionBounds.Clear(); // transform the bounds into the space of this frustum localOrigin = ( box.GetCenter() - origin ) * axis.Transpose(); localAxis = box.GetAxis() * axis.Transpose(); BoxToPoints( localOrigin, box.GetExtents(), localAxis, points ); // test outer four edges of the bounds culled = -1; outside = 0; for( i = 0; i < 4; i++ ) { p1 = i; p2 = 4 + i; AddLocalLineToProjectionBoundsSetCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds ); culled &= pointCull[p1] & pointCull[p2]; outside |= pointCull[p1] | pointCull[p2]; } // if the bounds are completely outside this frustum if( culled ) { return false; } // if the bounds are completely inside this frustum if( !outside ) { return true; } // test the remaining edges of the bounds for( i = 0; i < 4; i++ ) { p1 = i; p2 = ( i + 1 ) & 3; AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds ); } for( i = 0; i < 4; i++ ) { p1 = 4 + i; p2 = 4 + ( ( i + 1 ) & 3 ); AddLocalLineToProjectionBoundsUseCull( points[p1], points[p2], pointCull[p1], pointCull[p2], projectionBounds ); } // if the bounds extend beyond two or more boundaries of this frustum if( outside != 1 && outside != 2 && outside != 4 && outside != 8 ) { localOrigin = ( origin - box.GetCenter() ) * box.GetAxis().Transpose(); localScaled = axis * box.GetAxis().Transpose(); localScaled[0] *= dFar; localScaled[1] *= dLeft; localScaled[2] *= dUp; // test the outer edges of this frustum for intersection with the bounds if( ( outside & 2 ) && ( outside & 8 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] - localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, -1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, -1.0f ) ); } } if( ( outside & 2 ) && ( outside & 4 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] - localScaled[1] + localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, -1.0f, 1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, -1.0f, 1.0f ) ); } } if( ( outside & 1 ) && ( outside & 8 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] - localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, -1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, -1.0f ) ); } } if( ( outside & 1 ) && ( outside & 2 ) ) { BoundsRayIntersection( bounds, localOrigin, localScaled[0] + localScaled[1] + localScaled[2], scale1, scale2 ); if( scale1 <= scale2 && scale1 >= 0.0f ) { projectionBounds.AddPoint( idVec3( scale1 * dFar, 1.0f, 1.0f ) ); projectionBounds.AddPoint( idVec3( scale2 * dFar, 1.0f, 1.0f ) ); } } } return true; }
/* ============ idAASLocal::TestIfBarrierIsolatesReachability ============ */ bool idAASLocal::TestIfBarrierIsolatesReachability ( idReachability* p_reachability, int areaIndex, idBounds barrierBounds ) const { /* * Test params */ if ( p_reachability == NULL) { return false; } // Test the paths from the reachability to all other reachabilities leaving // the area. If a reachability has no path to another reachability that does not // intersect the barrier, then return true. Also if there are no other reachbilities // return true. Otherwise return false; // Iterate the other reachabilities bool b_hadPath = false; bool b_foundClearPath = false; idReachability* p_reach2 = GetAreaFirstReachability(areaIndex); while (p_reach2 != NULL) { if (p_reach2 != p_reachability) { b_hadPath = true; // Test if path between the reachabilities is blocked by the barrier bounds if (barrierBounds.LineIntersection (p_reachability->start, p_reach2->start)) { // Blocked return true; } /* // Its not blocked b_foundClearPath = true; } */ } // Not same reachability // Is it blocked? if (b_foundClearPath) { // End iteration early if we already found a clear path p_reach2 = NULL; } else { p_reach2 = p_reach2->next; } } // Next other reachability on same area return false; /* // Return result of test if ( (b_hadPath) && (!b_foundClearPath) ) { // Its isolated by the bounds given return true; } else { // Its not isolated by the bounds given return false; } */ }
/* ============ idAASLocal::GetWallEdges ============ */ int idAASLocal::GetWallEdges( int areaNum, const idBounds &bounds, int travelFlags, int *edges, int maxEdges ) const { int i, j, k, l, face1Num, face2Num, edge1Num, edge2Num, numEdges, absEdge1Num; int *areaQueue, curArea, queueStart, queueEnd; byte *areasVisited; const aasArea_t *area; const aasFace_t *face1, *face2; idReachability *reach; if( !file ) { return 0; } numEdges = 0; areasVisited = ( byte * ) _alloca16( file->GetNumAreas() ); memset( areasVisited, 0, file->GetNumAreas() * sizeof( byte ) ); areaQueue = ( int * ) _alloca16( file->GetNumAreas() * sizeof( int ) ); queueStart = -1; queueEnd = 0; areaQueue[0] = areaNum; areasVisited[areaNum] = true; for( curArea = areaNum; queueStart < queueEnd; curArea = areaQueue[++queueStart] ) { area = &file->GetArea( curArea ); for( i = 0; i < area->numFaces; i++ ) { face1Num = file->GetFaceIndex( area->firstFace + i ); face1 = &file->GetFace( abs( face1Num ) ); if( !( face1->flags & FACE_FLOOR ) ) { continue; } for( j = 0; j < face1->numEdges; j++ ) { edge1Num = file->GetEdgeIndex( face1->firstEdge + j ); absEdge1Num = abs( edge1Num ); // test if the edge is shared by another floor face of this area for( k = 0; k < area->numFaces; k++ ) { if( k == i ) { continue; } face2Num = file->GetFaceIndex( area->firstFace + k ); face2 = &file->GetFace( abs( face2Num ) ); if( !( face2->flags & FACE_FLOOR ) ) { continue; } for( l = 0; l < face2->numEdges; l++ ) { edge2Num = abs( file->GetEdgeIndex( face2->firstEdge + l ) ); if( edge2Num == absEdge1Num ) { break; } } if( l < face2->numEdges ) { break; } } if( k < area->numFaces ) { continue; } // test if the edge is used by a reachability for( reach = area->reach; reach; reach = reach->next ) { if( reach->travelType & travelFlags ) { if( reach->edgeNum == absEdge1Num ) { break; } } } if( reach ) { continue; } // test if the edge is already in the list for( k = 0; k < numEdges; k++ ) { if( edge1Num == edges[k] ) { break; } } if( k < numEdges ) { continue; } // add the edge to the list edges[numEdges++] = edge1Num; if( numEdges >= maxEdges ) { return numEdges; } } } // add new areas to the queue for( reach = area->reach; reach; reach = reach->next ) { if( reach->travelType & travelFlags ) { // if the area the reachability leads to hasn't been visited yet and the area bounds touch the search bounds if( !areasVisited[reach->toAreaNum] && bounds.IntersectsBounds( file->GetArea( reach->toAreaNum ).bounds ) ) { areaQueue[queueEnd++] = reach->toAreaNum; areasVisited[reach->toAreaNum] = true; } } } } return numEdges; }