/* * AI_MoveToShortRangeGoalEntity * A.K.A Item pick magnet */ qboolean AI_MoveToShortRangeGoalEntity( edict_t *self, usercmd_t *ucmd ) { if( !self->movetarget || !self->r.client ) return qfalse; if( self->ai.goalEnt && ( self->ai.goalEnt->ent == self->movetarget ) && ( AI_GetNodeFlags( self->ai.goal_node ) & NODEFLAGS_ENTITYREACH ) ) { // wait VectorSubtract( self->movetarget->s.origin, self->s.origin, self->ai.move_vector ); if( VectorLength( self->ai.move_vector ) < 72 ) ucmd->buttons |= BUTTON_WALK; if( BoundsIntersect( self->movetarget->r.absmin, self->movetarget->r.absmax, self->r.absmin, self->r.absmax ) ) { ucmd->forwardmove = 0; ucmd->sidemove = 0; ucmd->upmove = 0; self->ai.node_timeout = 0; return qtrue; } } if( self->movetarget->r.solid == SOLID_NOT || DistanceFast( self->movetarget->s.origin, self->s.origin ) > AI_GOAL_SR_RADIUS + 72 ) { self->movetarget = NULL; self->ai.shortRangeGoalTimeout = level.time; return qfalse; } // Force movement direction to reach the goal entity VectorSubtract( self->movetarget->s.origin, self->s.origin, self->ai.move_vector ); return qtrue; }
/* * R_SurfaceShadowBits */ static unsigned int R_SurfaceShadowBits( const msurface_t *surf, unsigned int checkShadowBits ) { unsigned int i, bit; shadowGroup_t *grp; unsigned int surfShadowBits = 0; if( !R_SurfPotentiallyShadowed( surf ) ) { return 0; } for( i = 0; i < rsc.numShadowGroups; i++ ) { if( !checkShadowBits ) { break; } grp = rsc.shadowGroups + i; bit = grp->bit; if( checkShadowBits & bit ) { switch( surf->facetype ) { case FACETYPE_PLANAR: if ( BoundsIntersect( surf->mins, surf->maxs, grp->visMins, grp->visMaxs ) ) { float dist = PlaneDiff( grp->visOrigin, surf->plane ); if ( dist > -grp->visRadius && dist <= grp->visRadius ) { // crossed by plane surfShadowBits |= bit; } } break; case FACETYPE_PATCH: case FACETYPE_TRISURF: case FACETYPE_FOLIAGE: if( BoundsIntersect( surf->mins, surf->maxs, grp->visMins, grp->visMaxs ) ) { surfShadowBits |= bit; } break; } checkShadowBits &= ~bit; } } return surfShadowBits; }
/* ===================== RE_AddPolyBufferToScene ===================== */ void RE_AddPolyBufferToScene( polyBuffer_t *pPolyBuffer ) { srfPolyBuffer_t *pPolySurf; int fogIndex; fog_t *fog; vec3_t bounds[ 2 ]; int i; if ( !r_drawpolies->integer ) { return; } if ( r_numPolybuffers >= r_maxPolyVerts->integer ) { return; } pPolySurf = &backEndData[ tr.smpFrame ]->polybuffers[ r_numPolybuffers ]; r_numPolybuffers++; pPolySurf->surfaceType = surfaceType_t::SF_POLYBUFFER; pPolySurf->pPolyBuffer = pPolyBuffer; VectorCopy( pPolyBuffer->xyz[ 0 ], bounds[ 0 ] ); VectorCopy( pPolyBuffer->xyz[ 0 ], bounds[ 1 ] ); for ( i = 1; i < pPolyBuffer->numVerts; i++ ) { AddPointToBounds( pPolyBuffer->xyz[ i ], bounds[ 0 ], bounds[ 1 ] ); } for ( fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++ ) { fog = &tr.world->fogs[ fogIndex ]; if ( BoundsIntersect( bounds[ 0 ], bounds[ 1 ], fog->bounds[ 0 ], fog->bounds[ 1 ] ) ) { break; } } if ( fogIndex == tr.world->numFogs ) { fogIndex = 0; } pPolySurf->fogIndex = fogIndex; }
/* ===================== RE_AddPolyBufferToScene ===================== */ void RE_AddPolyBufferToScene(polyBuffer_t *pPolyBuffer) { srfPolyBuffer_t *pPolySurf; int fogIndex; fog_t *fog; vec3_t bounds[2]; int i; if (!r_drawpolies->integer) { return; } if (r_numPolybuffers >= MAX_POLYBUFFERS) { Ren_Warning("WARNING RE_AddPolyBufferToScene: MAX_POLYBUFFERS (%d) reached\n", MAX_POLYBUFFERS); return; } pPolySurf = &backEndData->polybuffers[r_numPolybuffers]; r_numPolybuffers++; pPolySurf->surfaceType = SF_POLYBUFFER; pPolySurf->pPolyBuffer = pPolyBuffer; VectorCopy(pPolyBuffer->xyz[0], bounds[0]); VectorCopy(pPolyBuffer->xyz[0], bounds[1]); for (i = 1; i < pPolyBuffer->numVerts; i++) { AddPointToBounds(pPolyBuffer->xyz[i], bounds[0], bounds[1]); } for (fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++) { fog = &tr.world->fogs[fogIndex]; if (BoundsIntersect(bounds[0], bounds[1], fog->bounds[0], fog->bounds[1])) { break; } } if (fogIndex == tr.world->numFogs) { fogIndex = 0; } pPolySurf->fogIndex = fogIndex; }
///////////////////////////////////// // Purpose: find collision with the given // model inside the map. // NOTE: testT is the starting // 't' value (starting minimum) // usu. set as 't = 1' // set mins & maxs to 0 to test // as point // Output: pTrace filled // Return: true if collided ///////////////////////////////////// u8 IgfxQBSP::ModelCollision(u32 modelInd, const Vec3D *mins, const Vec3D *maxs, const Vec3D &pt1, const Vec3D &pt2, gfxTrace *pTrace, f32 testT) { memset(pTrace, 0, sizeof(gfxTrace)); pTrace->t = testT; if(TESTFLAGS(m_models[modelInd].status, QBSP_MODEL_FLAG_DISABLE)) return FALSE; Vec3D vec(pt2-pt1); //determine if we are colliding with bbox or pt. u8 bIsPt = (mins && maxs) ? FALSE : TRUE; Vec3D bbMin, bbMax; RayModelGetBBoxMin(modelInd, &bbMin); RayModelGetBBoxMax(modelInd, &bbMax); // build a bounding box of the entire move Vec3D trace_absmins, trace_absmaxs; Vec3D point; BoundsClear(trace_absmins, trace_absmaxs); point = mins ? pt1 + (*mins) : pt1; BoundsAddPoint(point, trace_absmins, trace_absmaxs); point = maxs ? pt1 + (*maxs) : pt1; BoundsAddPoint(point, trace_absmins, trace_absmaxs); point = mins ? pt2 + (*mins) : pt2; BoundsAddPoint(point, trace_absmins, trace_absmaxs); point = maxs ? pt2 + (*maxs) : pt2; BoundsAddPoint(point, trace_absmins, trace_absmaxs); if(BoundsIntersect(bbMin, bbMax, trace_absmins, trace_absmaxs)) { // Loop through and check all of the brushes in this model for(s32 i = 0; i < m_models[modelInd].numOfBrushes; i++) { _mdlCollBrush(&m_models[modelInd].brushes[i], bIsPt, mins, maxs, pt1, vec, pTrace); } } if(pTrace->t < testT) return TRUE; return FALSE; }
/* ==================== SV_AddLaddersToPmove ==================== */ void SV_AddLaddersToPmove( areanode_t *node, const vec3_t pmove_mins, const vec3_t pmove_maxs ) { link_t *l, *next; edict_t *check; physent_t *pe; // get water edicts for( l = node->water_edicts.next; l != &node->water_edicts; l = next ) { next = l->next; check = EDICT_FROM_AREA( l ); if( check->v.solid != SOLID_NOT ) // disabled ? continue; // only brushes can have special contents if( Mod_GetType( check->v.modelindex ) != mod_brush ) continue; if( !BoundsIntersect( pmove_mins, pmove_maxs, check->v.absmin, check->v.absmax )) continue; if( svgame.pmove->nummoveent == MAX_MOVEENTS ) return; pe = &svgame.pmove->moveents[svgame.pmove->nummoveent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->nummoveent++; } // recurse down both sides if( node->axis == -1 ) return; if( pmove_maxs[node->axis] > node->dist ) SV_AddLaddersToPmove( node->children[0], pmove_mins, pmove_maxs ); if( pmove_mins[node->axis] < node->dist ) SV_AddLaddersToPmove( node->children[1], pmove_mins, pmove_maxs ); }
/* * GClip_EntityContact */ static qboolean GClip_EntityContact( vec3_t mins, vec3_t maxs, edict_t *ent ) { trace_t tr; struct cmodel_s *model; if( !mins ) mins = vec3_origin; if( !maxs ) maxs = vec3_origin; if( ISBRUSHMODEL( ent->s.modelindex ) ) { model = trap_CM_InlineModel( ent->s.modelindex ); if( !model ) G_Error( "MOVETYPE_PUSH with a non bsp model" ); trap_CM_TransformedBoxTrace( &tr, vec3_origin, vec3_origin, mins, maxs, model, MASK_ALL, ent->s.origin, ent->s.angles ); return tr.startsolid || tr.allsolid; } return ( BoundsIntersect( mins, maxs, ent->r.absmin, ent->r.absmax ) ); }
/* * GClip_EntitiesInBox_AreaGrid */ static int GClip_EntitiesInBox_AreaGrid( areagrid_t *areagrid, const vec3_t mins, const vec3_t maxs, int *list, int maxcount, int areatype, int timeDelta ) { int numlist; link_t *grid; link_t *l; c4clipedict_t *clipEnt; vec3_t paddedmins, paddedmaxs; int igrid[3], igridmins[3], igridmaxs[3]; // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters // being too close to info_teleport_destination) //VectorSet( paddedmins, mins[0] - 1.0f, mins[1] - 1.0f, mins[2] - 1.0f ); //VectorSet( paddedmaxs, maxs[0] + 1.0f, maxs[1] + 1.0f, maxs[2] + 1.0f ); VectorCopy( mins, paddedmins ); VectorCopy( maxs, paddedmaxs ); // FIXME: if areagrid_marknumber wraps, all entities need their // ent->priv.server->areagridmarknumber reset areagrid->marknumber++; igridmins[0] = (int) floor( (paddedmins[0] + areagrid->bias[0]) * areagrid->scale[0] ); igridmins[1] = (int) floor( (paddedmins[1] + areagrid->bias[1]) * areagrid->scale[1] ); //igridmins[2] = (int) ( (paddedmins[2] + areagrid->bias[2]) * areagrid->scale[2] ); igridmaxs[0] = (int) floor( (paddedmaxs[0] + areagrid->bias[0]) * areagrid->scale[0] ) + 1; igridmaxs[1] = (int) floor( (paddedmaxs[1] + areagrid->bias[1]) * areagrid->scale[1] ) + 1; //igridmaxs[2] = (int) ( (paddedmaxs[2] + areagrid->bias[2]) * areagrid->scale[2] ) + 1; igridmins[0] = max( 0, igridmins[0] ); igridmins[1] = max( 0, igridmins[1] ); //igridmins[2] = max( 0, igridmins[2] ); igridmaxs[0] = min( AREA_GRID, igridmaxs[0] ); igridmaxs[1] = min( AREA_GRID, igridmaxs[1] ); //igridmaxs[2] = min( AREA_GRID, igridmaxs[2] ); // paranoid debugging //VectorSet( igridmins, 0, 0, 0 );VectorSet( igridmaxs, AREA_GRID, AREA_GRID, AREA_GRID ); numlist = 0; // add entities not linked into areagrid because they are too big or // outside the grid bounds if( areagrid->outside.next ) { grid = &areagrid->outside; for( l = grid->next; l != grid; l = l->next ) { clipEnt = GClip_GetClipEdictForDeltaTime( l->entNum, timeDelta ); if( areagrid->entmarknumber[l->entNum] == areagrid->marknumber ) { continue; } areagrid->entmarknumber[l->entNum] = areagrid->marknumber; if( !clipEnt->r.inuse ) { continue; // deactivated } if( areatype == AREA_TRIGGERS && clipEnt->r.solid != SOLID_TRIGGER ) { continue; } if( areatype == AREA_SOLID && ( clipEnt->r.solid == SOLID_TRIGGER || clipEnt->r.solid == SOLID_NOT ) ) { continue; } if( BoundsIntersect( paddedmins, paddedmaxs, clipEnt->r.absmin, clipEnt->r.absmax )) { if( numlist < maxcount ) { list[numlist] = l->entNum; } numlist++; } } } // add grid linked entities for( igrid[1] = igridmins[1]; igrid[1] < igridmaxs[1]; igrid[1]++ ) { grid = areagrid->grid + igrid[1] * AREA_GRID + igridmins[0]; for( igrid[0] = igridmins[0]; igrid[0] < igridmaxs[0]; igrid[0]++, grid++ ) { if( !grid->next ) { continue; } for( l = grid->next; l != grid; l = l->next ) { clipEnt = GClip_GetClipEdictForDeltaTime( l->entNum, timeDelta ); if( areagrid->entmarknumber[l->entNum] == areagrid->marknumber ) { continue; } areagrid->entmarknumber[l->entNum] = areagrid->marknumber; if( !clipEnt->r.inuse ) { continue; // deactivated } if( areatype == AREA_TRIGGERS && clipEnt->r.solid != SOLID_TRIGGER ) { continue; } if( areatype == AREA_SOLID && ( clipEnt->r.solid == SOLID_TRIGGER || clipEnt->r.solid == SOLID_NOT ) ) { continue; } if( BoundsIntersect( paddedmins, paddedmaxs, clipEnt->r.absmin, clipEnt->r.absmax )) { if( numlist < maxcount ) { list[numlist] = l->entNum; } numlist++; } } } } return numlist; }
/* ================= R_AddMD5Interactions ================= */ void R_AddMD5Interactions(trRefEntity_t * ent, trRefLight_t * light) { int i; md5Model_t *model; md5Surface_t *surface; shader_t *shader = 0; qboolean personalModel; byte cubeSideBits = CUBESIDE_CLIPALL; interactionType_t iaType = IA_DEFAULT; // cull the entire model if merged bounding box of both frames // is outside the view frustum and we don't care about proper shadowing if(ent->cull == CULL_OUT) { if(r_shadows->integer <= SHADOWING_BLOB || light->l.noShadows) return; else iaType = IA_SHADOWONLY; } // avoid drawing of certain objects #if defined(USE_REFENTITY_NOSHADOWID) if(light->l.inverseShadows) { if(iaType != IA_LIGHTONLY && (light->l.noShadowID && (light->l.noShadowID != ent->e.noShadowID))) return; } else { if(iaType != IA_LIGHTONLY && (light->l.noShadowID && (light->l.noShadowID == ent->e.noShadowID))) return; } #endif // don't add third_person objects if not in a portal personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; model = tr.currentModel->md5; // do a quick AABB cull if(!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], ent->worldBounds[0], ent->worldBounds[1])) { tr.pc.c_dlightSurfacesCulled += model->numSurfaces; return; } // do a more expensive and precise light frustum cull if(!r_noLightFrustums->integer) { if(R_CullLightWorldBounds(light, ent->worldBounds) == CULL_OUT) { tr.pc.c_dlightSurfacesCulled += model->numSurfaces; return; } } cubeSideBits = R_CalcLightCubeSideBits(light, ent->worldBounds); if(!r_vboModels->integer || !model->numVBOSurfaces || (!glConfig2.vboVertexSkinningAvailable && ent->e.skeleton.type == SK_ABSOLUTE)) { // generate interactions with all surfaces for(i = 0, surface = model->surfaces; i < model->numSurfaces; i++, surface++) { if(ent->e.customShader) { shader = R_GetShaderByHandle(ent->e.customShader); } else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) { skin_t *skin; skin = R_GetSkinByHandle(ent->e.customSkin); // match the surface name to something in the skin file shader = tr.defaultShader; // FIXME: replace MD3_MAX_SURFACES for skin_t::surfaces if(i >= 0 && i < skin->numSurfaces && skin->surfaces[i]) { shader = skin->surfaces[i]->shader; } if(shader == tr.defaultShader) { ri.Printf(PRINT_DEVELOPER, "WARNING: no shader for surface %i in skin %s\n", i, skin->name); } else if(shader->defaultShader) { ri.Printf(PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); } } else { shader = R_GetShaderByHandle(surface->shaderIndex); } // skip all surfaces that don't matter for lighting only pass if(shader->isSky || (!shader->interactLight && shader->noShadows)) continue; // we will add shadows even if the main object isn't visible in the view // don't add third_person objects if not viewing through a portal if(!personalModel) { R_AddLightInteraction(light, (void *)surface, shader, cubeSideBits, iaType); tr.pc.c_dlightSurfaces++; } } } else { int i; srfVBOMD5Mesh_t *vboSurface; shader_t *shader; for(i = 0; i < model->numVBOSurfaces; i++) { vboSurface = model->vboSurfaces[i]; if(ent->e.customShader) { shader = R_GetShaderByHandle(ent->e.customShader); } else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) { skin_t *skin; skin = R_GetSkinByHandle(ent->e.customSkin); // match the surface name to something in the skin file shader = tr.defaultShader; // FIXME: replace MD3_MAX_SURFACES for skin_t::surfaces if(i >= 0 && i < skin->numSurfaces && skin->surfaces[i]) { shader = skin->surfaces[i]->shader; } if(shader == tr.defaultShader) { ri.Printf(PRINT_DEVELOPER, "WARNING: no shader for surface %i in skin %s\n", i, skin->name); } else if(shader->defaultShader) { ri.Printf(PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); } } else { shader = vboSurface->shader; } // skip all surfaces that don't matter for lighting only pass if(shader->isSky || (!shader->interactLight && shader->noShadows)) continue; // don't add third_person objects if not viewing through a portal if(!personalModel) { R_AddLightInteraction(light, (void *)vboSurface, shader, cubeSideBits, iaType); tr.pc.c_dlightSurfaces++; } } } }
/* * R_AddBrushModelToDrawList */ qboolean R_AddBrushModelToDrawList( const entity_t *e ) { unsigned int i; vec3_t origin; vec3_t bmins, bmaxs; qboolean rotated; model_t *model = e->model; mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; msurface_t *surf; mfog_t *fog; float radius, distance; unsigned int bit, fullBits; unsigned int dlightBits, shadowBits; if( bmodel->nummodelsurfaces == 0 ) { return qfalse; } radius = R_BrushModelBBox( e, bmins, bmaxs, &rotated ); if( R_CullModelEntity( e, bmins, bmaxs, radius, rotated ) ) { return qfalse; } // never render weapon models or non-occluders into shadowmaps if( rn.renderFlags & RF_SHADOWMAPVIEW ) { if( rsc.entShadowGroups[R_ENT2NUM(e)] != rn.shadowGroup->id ) { return qtrue; } } VectorAdd( e->model->mins, e->model->maxs, origin ); VectorMA( e->origin, 0.5, origin, origin ); distance = Distance( origin, rn.refdef.vieworg ); fog = R_FogForBounds( bmins, bmaxs ); R_TransformPointToModelSpace( e, rotated, rn.refdef.vieworg, modelOrg ); // check dynamic lights that matter in the instance against the model dlightBits = 0; for( i = 0, fullBits = rn.dlightBits, bit = 1; fullBits; i++, fullBits &= ~bit, bit <<= 1 ) { if( !( fullBits & bit ) ) { continue; } if( !BoundsAndSphereIntersect( bmins, bmaxs, rsc.dlights[i].origin, rsc.dlights[i].intensity ) ) { continue; } dlightBits |= bit; } // check shadowmaps that matter in the instance against the model shadowBits = 0; for( i = 0, fullBits = rn.shadowBits; fullBits; i++, fullBits &= ~bit ) { shadowGroup_t *grp = rsc.shadowGroups + i; bit = grp->bit; if( !( fullBits & bit ) ) { continue; } if( !BoundsIntersect( bmins, bmaxs, grp->visMins, grp->visMaxs ) ) { continue; } shadowBits |= bit; } for( i = 0, surf = bmodel->firstmodelsurface; i < bmodel->nummodelsurfaces; i++, surf++ ) { if( !surf->drawSurf ) { continue; } if( surf->visFrame != rf.frameCount ) { surf->visFrame = rf.frameCount; R_AddSurfaceToDrawList( e, surf, fog, 0, dlightBits, shadowBits, distance ); } } return qtrue; }
/* ===================== R_AddPolysToScene ===================== */ static void R_AddPolysToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; int i, j; int fogIndex; fog_t *fog; vec3_t bounds[ 2 ]; if ( !tr.registered ) { return; } if ( !r_drawpolies->integer ) { return; } if ( !hShader ) { Log::Warn("RE_AddPolyToScene: NULL poly shader" ); return; } for ( j = 0; j < numPolys; j++ ) { if ( r_numPolyVerts + numVerts >= r_maxPolyVerts->integer || r_numPolys >= r_maxPolys->integer ) { /* NOTE TTimo this was initially Log::Warn but it happens a lot with high fighting scenes and particles since we don't plan on changing the const and making for room for those effects simply cut this message to developer only */ Log::Debug("RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n" ); return; } poly = &backEndData[ tr.smpFrame ]->polys[ r_numPolys ]; poly->surfaceType = surfaceType_t::SF_POLY; poly->hShader = hShader; poly->numVerts = numVerts; poly->verts = &backEndData[ tr.smpFrame ]->polyVerts[ r_numPolyVerts ]; Com_Memcpy( poly->verts, &verts[ numVerts * j ], numVerts * sizeof( *verts ) ); // done. r_numPolys++; r_numPolyVerts += numVerts; // if no world is loaded if ( tr.world == nullptr ) { fogIndex = 0; } // see if it is in a fog volume else if ( tr.world->numFogs == 1 ) { fogIndex = 0; } else { // find which fog volume the poly is in VectorCopy( poly->verts[ 0 ].xyz, bounds[ 0 ] ); VectorCopy( poly->verts[ 0 ].xyz, bounds[ 1 ] ); for ( i = 1; i < poly->numVerts; i++ ) { AddPointToBounds( poly->verts[ i ].xyz, bounds[ 0 ], bounds[ 1 ] ); } for ( fogIndex = 1; fogIndex < tr.world->numFogs; fogIndex++ ) { fog = &tr.world->fogs[ fogIndex ]; if ( BoundsIntersect( bounds[ 0 ], bounds[ 1 ], fog->bounds[ 0 ], fog->bounds[ 1 ] ) ) { break; } } if ( fogIndex == tr.world->numFogs ) { fogIndex = 0; } } poly->fogIndex = fogIndex; } }
/* ================= R_TraceLine ================= */ msurface_t *R_TransformedTraceLine( trace_t *tr, const vec3_t start, const vec3_t end, ref_entity_t *test, int umask ) { ref_model_t *model; r_fragmentframecount++; // for multi-check avoidance // fill in a default trace Mem_Set( tr, 0, sizeof( trace_t )); trace_surface = NULL; trace_umask = umask; trace_fraction = 1; VectorCopy( end, trace_impact ); Mem_Set( &trace_plane, 0, sizeof( trace_plane )); ClearBounds( trace_absmins, trace_absmaxs ); AddPointToBounds( start, trace_absmins, trace_absmaxs ); AddPointToBounds( end, trace_absmins, trace_absmaxs ); model = test->model; if( model ) { if( model->type == mod_world || model->type == mod_brush ) { mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; vec3_t temp, start_l, end_l, axis[3]; bool rotated = !Matrix3x3_Compare( test->axis, matrix3x3_identity ); // transform VectorSubtract( start, test->origin, start_l ); VectorSubtract( end, test->origin, end_l ); if( rotated ) { VectorCopy( start_l, temp ); Matrix3x3_Transform( test->axis, temp, start_l ); VectorCopy( end_l, temp ); Matrix3x3_Transform( test->axis, temp, end_l ); } VectorCopy( start_l, trace_start ); VectorCopy( end_l, trace_end ); // world uses a recursive approach using BSP tree, submodels // just walk the list of surfaces linearly if( test->model->type == mod_world ) R_RecursiveHullCheck( bmodel->nodes, start_l, end_l ); else if( BoundsIntersect( model->mins, model->maxs, trace_absmins, trace_absmaxs ) ) R_TraceAgainstBmodel( bmodel ); // transform back if( rotated && trace_fraction != 1 ) { Matrix3x3_Transpose( axis, test->axis ); VectorCopy( tr->vecPlaneNormal, temp ); Matrix3x3_Transform( axis, temp, trace_plane.normal ); } } } // calculate the impact plane, if any if( trace_fraction < 1.0f ) { VectorNormalize( trace_plane.normal ); trace_plane.dist = DotProduct( trace_plane.normal, trace_impact ); CategorizePlane( &trace_plane ); tr->flPlaneDist = trace_plane.dist; VectorCopy( trace_plane.normal, tr->vecPlaneNormal ); tr->iContents = trace_surface->contents; tr->pHit = (edict_t *)test; } tr->flFraction = trace_fraction; VectorCopy( trace_impact, tr->vecEndPos ); return trace_surface; }
/* ================= R_AddIQMInteractions ================= */ void R_AddIQMInteractions( trRefEntity_t *ent, trRefLight_t *light, interactionType_t iaType ) { int i; IQModel_t *model; srfIQModel_t *surface; shader_t *shader = 0; bool personalModel; byte cubeSideBits = CUBESIDE_CLIPALL; // cull the entire model if merged bounding box of both frames // is outside the view frustum and we don't care about proper shadowing if ( ent->cull == cullResult_t::CULL_OUT ) { iaType = (interactionType_t) (iaType & ~IA_LIGHT); if( !iaType ) { return; } } // avoid drawing of certain objects #if defined( USE_REFENTITY_NOSHADOWID ) if ( light->l.inverseShadows ) { if ( (iaType & IA_SHADOW) && ( light->l.noShadowID && ( light->l.noShadowID != ent->e.noShadowID ) ) ) { return; } } else { if ( (iaType & IA_SHADOW) && ( light->l.noShadowID && ( light->l.noShadowID == ent->e.noShadowID ) ) ) { return; } } #endif // don't add third_person objects if not in a portal personalModel = ( ent->e.renderfx & RF_THIRD_PERSON ) && !tr.viewParms.isPortal; model = tr.currentModel->iqm; // do a quick AABB cull if ( !BoundsIntersect( light->worldBounds[ 0 ], light->worldBounds[ 1 ], ent->worldBounds[ 0 ], ent->worldBounds[ 1 ] ) ) { tr.pc.c_dlightSurfacesCulled += model->num_surfaces; return; } // do a more expensive and precise light frustum cull if ( !r_noLightFrustums->integer ) { if ( R_CullLightWorldBounds( light, ent->worldBounds ) == cullResult_t::CULL_OUT ) { tr.pc.c_dlightSurfacesCulled += model->num_surfaces; return; } } cubeSideBits = R_CalcLightCubeSideBits( light, ent->worldBounds ); // generate interactions with all surfaces for ( i = 0, surface = model->surfaces; i < model->num_surfaces; i++, surface++ ) { if ( ent->e.customShader ) { shader = R_GetShaderByHandle( ent->e.customShader ); } else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) { skin_t *skin; skin = R_GetSkinByHandle( ent->e.customSkin ); // match the surface name to something in the skin file shader = tr.defaultShader; // FIXME: replace MD3_MAX_SURFACES for skin_t::surfaces if ( i >= 0 && i < skin->numSurfaces && skin->surfaces[ i ] ) { shader = skin->surfaces[ i ]->shader; } if ( shader == tr.defaultShader ) { Log::Warn("no shader for surface %i in skin %s", i, skin->name ); } else if ( shader->defaultShader ) { Log::Warn("shader %s in skin %s not found", shader->name, skin->name ); } } else { shader = R_GetShaderByHandle( surface->shader->index ); } // skip all surfaces that don't matter for lighting only pass if ( shader->isSky || ( !shader->interactLight && shader->noShadows ) ) { continue; } // we will add shadows even if the main object isn't visible in the view // don't add third_person objects if not viewing through a portal if ( !personalModel ) { R_AddLightInteraction( light, ( surfaceType_t * ) surface, shader, cubeSideBits, iaType ); tr.pc.c_dlightSurfaces++; } } }
/* ==================== SV_AddLinksToPmove collect solid entities ==================== */ void SV_AddLinksToPmove( areanode_t *node, const vec3_t pmove_mins, const vec3_t pmove_maxs ) { link_t *l, *next; edict_t *check, *pl; vec3_t mins, maxs; physent_t *pe; pl = EDICT_NUM( svgame.pmove->player_index + 1 ); //ASSERT( SV_IsValidEdict( pl )); if( !SV_IsValidEdict( pl ) ) { MsgDev( D_ERROR, "SV_AddLinksToPmove: you have broken clients!\n"); return; } // touch linked edicts for( l = node->solid_edicts.next; l != &node->solid_edicts; l = next ) { next = l->next; check = EDICT_FROM_AREA( l ); if( check->v.groupinfo != 0 ) { if(( !svs.groupop && (check->v.groupinfo & pl->v.groupinfo ) == 0) || ( svs.groupop == 1 && ( check->v.groupinfo & pl->v.groupinfo ) != 0 )) continue; } if( ( ( check->v.owner > 0) && check->v.owner == pl ) || check->v.solid == SOLID_TRIGGER ) continue; // player or player's own missile if( svgame.pmove->numvisent < MAX_PHYSENTS ) { pe = &svgame.pmove->visents[svgame.pmove->numvisent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->numvisent++; } if( check->v.solid == SOLID_NOT && ( check->v.skin == CONTENTS_NONE || check->v.modelindex == 0 )) continue; // ignore monsterclip brushes if(( check->v.flags & FL_MONSTERCLIP ) && check->v.solid == SOLID_BSP ) continue; if( check == pl ) continue; // himself if( !sv_corpse_solid->value ) { if((( check->v.flags & FL_CLIENT ) && check->v.health <= 0 ) || check->v.deadflag == DEAD_DEAD ) continue; // dead body } if( VectorIsNull( check->v.size )) continue; VectorCopy( check->v.absmin, mins ); VectorCopy( check->v.absmax, maxs ); if( check->v.flags & FL_CLIENT ) { // trying to get interpolated values if( svs.currentPlayer ) SV_GetTrueMinMax( svs.currentPlayer, ( NUM_FOR_EDICT( check ) - 1), mins, maxs ); } if( !BoundsIntersect( pmove_mins, pmove_maxs, mins, maxs )) continue; if( svgame.pmove->numphysent < MAX_PHYSENTS ) { pe = &svgame.pmove->physents[svgame.pmove->numphysent]; if( SV_CopyEdictToPhysEnt( pe, check )) svgame.pmove->numphysent++; } } // recurse down both sides if( node->axis == -1 ) return; if( pmove_maxs[node->axis] > node->dist ) SV_AddLinksToPmove( node->children[0], pmove_mins, pmove_maxs ); if( pmove_mins[node->axis] < node->dist ) SV_AddLinksToPmove( node->children[1], pmove_mins, pmove_maxs ); }
/** * @brief R_AddMDVInteractions * @param[in] ent * @param[in] light */ void R_AddMDVInteractions(trRefEntity_t *ent, trRefLight_t *light) { int i; mdvModel_t *model = 0; mdvSurface_t *mdvSurface = 0; shader_t *shader = 0; int lod; qboolean personalModel; byte cubeSideBits; interactionType_t iaType = IA_DEFAULT; // cull the entire model if merged bounding box of both frames // is outside the view frustum and we don't care about proper shadowing if (ent->cull == CULL_OUT) { if (r_shadows->integer <= SHADOWING_BLOB || light->l.noShadows) { return; } else { iaType = IA_SHADOWONLY; } } // avoid drawing of certain objects #if defined(USE_REFENTITY_NOSHADOWID) if (light->l.inverseShadows) { if (iaType != IA_LIGHTONLY && (light->l.noShadowID && (light->l.noShadowID != ent->e.noShadowID))) { return; } } else { if (iaType != IA_LIGHTONLY && (light->l.noShadowID && (light->l.noShadowID == ent->e.noShadowID))) { return; } } #endif // don't add third_person objects if not in a portal personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; // compute LOD lod = R_ComputeLOD(ent); model = tr.currentModel->mdv[lod]; // do a quick AABB cull if (!BoundsIntersect(light->worldBounds[0], light->worldBounds[1], ent->worldBounds[0], ent->worldBounds[1])) { tr.pc.c_dlightSurfacesCulled += model->numSurfaces; return; } // do a more expensive and precise light frustum cull if (!r_noLightFrustums->integer) { if (R_CullLightWorldBounds(light, ent->worldBounds) == CULL_OUT) { tr.pc.c_dlightSurfacesCulled += model->numSurfaces; return; } } cubeSideBits = R_CalcLightCubeSideBits(light, ent->worldBounds); // generate interactions with all surfaces if (r_vboModels->integer && model->numVBOSurfaces) { // new brute force method: just render everthing with static VBOs int i; srfVBOMDVMesh_t *vboSurface; shader_t *shader; // static VBOs are fine for lighting and shadow mapping for (i = 0; i < model->numVBOSurfaces; i++) { vboSurface = model->vboSurfaces[i]; mdvSurface = vboSurface->mdvSurface; shader = GetMDVSurfaceShader(ent, mdvSurface); // skip all surfaces that don't matter for lighting only pass if (shader->isSky || (!shader->interactLight && shader->noShadows)) { continue; } // we will add shadows even if the main object isn't visible in the view // don't add third_person objects if not viewing through a portal if (!personalModel) { R_AddLightInteraction(light, (surfaceType_t *)vboSurface, shader, cubeSideBits, iaType); tr.pc.c_dlightSurfaces++; } } } else { for (i = 0, mdvSurface = model->surfaces; i < model->numSurfaces; i++, mdvSurface++) { shader = GetMDVSurfaceShader(ent, mdvSurface); // skip all surfaces that don't matter for lighting only pass if (shader->isSky || (!shader->interactLight && shader->noShadows)) { continue; } // we will add shadows even if the main object isn't visible in the view // don't add third_person objects if not viewing through a portal if (!personalModel) { R_AddLightInteraction(light, (surfaceType_t *)mdvSurface, shader, cubeSideBits, iaType); tr.pc.c_dlightSurfaces++; } } } }
/* * GClip_AreaEdicts * fills in a table of edict ids with edicts that have bounding boxes * that intersect the given area. It is possible for a non-axial bmodel * to be returned that doesn't actually intersect the area on an exact * test. * returns the number of pointers filled in * ??? does this always return the world? */ static int GClip_AreaEdicts( vec3_t mins, vec3_t maxs, int *list, int maxcount, int areatype, int timeDelta ) { link_t *l, *start; c4clipedict_t *clipEnt; int stackdepth = 0, count = 0; areanode_t *localstack[AREA_NODES], *node = sv_areanodes; while( 1 ) { // touch linked edicts if( areatype == AREA_SOLID ) start = &node->solid_edicts; else start = &node->trigger_edicts; for( l = start->next; l != start; l = l->next ) { clipEnt = GClip_GetClipEdictForDeltaTime( l->entNum, timeDelta ); if( clipEnt->r.solid == SOLID_NOT ) continue; // deactivated if( !BoundsIntersect( clipEnt->r.absmin, clipEnt->r.absmax, mins, maxs ) ) continue; // not touching if( count == maxcount ) { G_Printf( "G_AreaEdicts: MAXCOUNT\n" ); return count; } list[count++] = l->entNum; } if( node->axis == -1 ) goto checkstack; // terminal node // recurse down both sides if( maxs[node->axis] > node->dist ) { if( mins[node->axis] < node->dist ) { localstack[stackdepth++] = node->children[0]; node = node->children[1]; continue; } node = node->children[0]; continue; } if( mins[node->axis] < node->dist ) { node = node->children[1]; continue; } checkstack: if( !stackdepth ) return count; node = localstack[--stackdepth]; } return count; }
/* * R_TraceLine */ static msurface_t *R_TransformedTraceLine( rtrace_t *tr, const vec3_t start, const vec3_t end, entity_t *test, int surfumask ) { model_t *model; r_traceframecount++; // for multi-check avoidance // fill in a default trace memset( tr, 0, sizeof( *tr ) ); trace_surface = NULL; trace_umask = surfumask; trace_fraction = 1; VectorCopy( end, trace_impact ); memset( &trace_plane, 0, sizeof( trace_plane ) ); ClearBounds( trace_absmins, trace_absmaxs ); AddPointToBounds( start, trace_absmins, trace_absmaxs ); AddPointToBounds( end, trace_absmins, trace_absmaxs ); model = test->model; if( model ) { if( model->type == mod_brush ) { mbrushmodel_t *bmodel = ( mbrushmodel_t * )model->extradata; vec3_t temp, start_l, end_l; mat3_t axis; bool rotated = !Matrix3_Compare( test->axis, axis_identity ); // transform VectorSubtract( start, test->origin, start_l ); VectorSubtract( end, test->origin, end_l ); if( rotated ) { VectorCopy( start_l, temp ); Matrix3_TransformVector( test->axis, temp, start_l ); VectorCopy( end_l, temp ); Matrix3_TransformVector( test->axis, temp, end_l ); } VectorCopy( start_l, trace_start ); VectorCopy( end_l, trace_end ); // world uses a recursive approach using BSP tree, submodels // just walk the list of surfaces linearly if( test->model == rsh.worldModel ) { R_RecursiveHullCheck( bmodel->nodes, start_l, end_l ); } else if( BoundsIntersect( model->mins, model->maxs, trace_absmins, trace_absmaxs ) ) { R_TraceAgainstBmodel( bmodel ); } // transform back if( rotated && trace_fraction != 1 ) { Matrix3_Transpose( test->axis, axis ); VectorCopy( tr->plane.normal, temp ); Matrix3_TransformVector( axis, temp, trace_plane.normal ); } } } // calculate the impact plane, if any if( trace_fraction < 1 && trace_surface != NULL ) { VectorNormalize( trace_plane.normal ); trace_plane.dist = DotProduct( trace_plane.normal, trace_impact ); CategorizePlane( &trace_plane ); tr->shader = trace_surface->shader; tr->plane = trace_plane; tr->surfFlags = trace_surface->flags; tr->ent = R_ENT2NUM( test ); } tr->fraction = trace_fraction; VectorCopy( trace_impact, tr->endpos ); return trace_surface; }
///////////////////////////////////// // Name: LightGetAllNearest // Purpose: add lights that are near // given world AABB to given list // Output: lights added to given list // Return: none ///////////////////////////////////// void LightGetAllNearest(const Vec3D & mins, const Vec3D &maxs, hLIGHT *pLights) { //the point is at the center of both mins/maxs Vec3D pt((mins+maxs)*0.5f); //clear out given list first... memset(pLights, 0, sizeof(hLIGHT)*MAXLIGHT); //don't bother doing anything for no lights if(!TESTFLAGS(g_FLAGS, GFX_LIGHTENABLE)) return; Vec3D v; if(g_lights && g_lightInds && g_numLight > 0) { u32 i, ind; for(i = 0; i < g_numAvail; i++) { ind = g_lightInds[i]; if(g_lights[ind]->lDat.Type == GFXLIGHT_POINT || g_lights[ind]->lDat.Type == GFXLIGHT_SPOT) { v = pt - g_lights[ind]->lDat.Position; g_lights[ind]->dist = Vec3DDot(&v, &v); //make sure it is in range /*if(g_lights[ind]->dist <= g_lights[ind]->lDat.Range*g_lights[ind]->lDat.Range) { }*/ } else //we prioritize directional lights, so they are always added { g_lights[ind]->dist = -((f32)i); } } //sort qsort(g_lightInds, g_numAvail, sizeof(u32), _LightCompare); //get the first 3 or less lights f32 r; ind = 0; Vec3D lMin, lMax; u32 max = MAXLIGHT < g_numAvail ? MAXLIGHT : g_numAvail; for(i = 0; i < max; i++) { lMin.x = g_lights[g_lightInds[i]]->lDat.Position.x - g_lights[g_lightInds[i]]->lDat.Range; lMin.y = g_lights[g_lightInds[i]]->lDat.Position.y - g_lights[g_lightInds[i]]->lDat.Range; lMin.z = g_lights[g_lightInds[i]]->lDat.Position.z - g_lights[g_lightInds[i]]->lDat.Range; lMax.x = g_lights[g_lightInds[i]]->lDat.Position.x + g_lights[g_lightInds[i]]->lDat.Range; lMax.y = g_lights[g_lightInds[i]]->lDat.Position.y + g_lights[g_lightInds[i]]->lDat.Range; lMax.z = g_lights[g_lightInds[i]]->lDat.Position.z + g_lights[g_lightInds[i]]->lDat.Range; //r = g_lights[g_lightInds[i]]->lDat.Range; r *= r; //if(g_lights[g_lightInds[i]]->dist <= r) if(g_lights[g_lightInds[i]]->lDat.Type == GFXLIGHT_DIRECTIONAL || BoundsIntersect(mins, maxs, lMin, lMax)) { pLights[ind] = g_lights[g_lightInds[i]]; ind++; } } } }