/* ================ R_PlaneForMirror Get transformed mirrorplane and entity matrix ================ */ void R_PlaneForMirror( msurface_t *surf, mplane_t *out, matrix4x4 m ) { cl_entity_t *ent; ASSERT( out != NULL ); ent = RI.currententity; // setup mirror plane *out = *surf->plane; if( surf->flags & SURF_PLANEBACK ) { VectorNegate( out->normal, out->normal ); out->dist = -out->dist; } if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles )) { mplane_t tmp; if( !VectorIsNull( ent->angles )) Matrix4x4_CreateFromEntity( m, ent->angles, ent->origin, 1.0f ); else Matrix4x4_CreateFromEntity( m, vec3_origin, ent->origin, 1.0f ); tmp = *out; // transform mirror plane by entity matrix Matrix4x4_TransformPositivePlane( m, tmp.normal, tmp.dist, out->normal, &out->dist ); } else Matrix4x4_LoadIdentity( m ); }
/* ================== PM_TraceTexture find the face where the traceline hit assume physentity is valid ================== */ msurface_t *PM_TraceSurface( physent_t *pe, vec3_t start, vec3_t end ) { matrix4x4 matrix; model_t *bmodel; hull_t *hull; vec3_t start_l, end_l; vec3_t offset; bmodel = pe->model; if( !bmodel || bmodel->type != mod_brush ) return NULL; hull = &pe->model->hulls[0]; VectorSubtract( hull->clip_mins, vec3_origin, offset ); VectorAdd( offset, pe->origin, offset ); VectorSubtract( start, offset, start_l ); VectorSubtract( end, offset, end_l ); // rotate start and end into the models frame of reference if( !VectorIsNull( pe->angles )) { Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, start, start_l ); Matrix4x4_VectorITransform( matrix, end, end_l ); } return PM_RecursiveSurfCheck( bmodel, &bmodel->nodes[hull->firstclipnode], start_l, end_l ); }
/** * @brief Applies any configuration and tag alignment, populating the model-view * matrix for the entity in the process. */ void R_SetMatrixForEntity(r_entity_t *e) { if (e->parent) { vec3_t forward; if (!IS_MESH_MODEL(e->model)) { Com_Warn("Invalid model for linked entity\n"); return; } const r_entity_t *p = e->parent; while (p->parent) { p = p->parent; } AngleVectors(p->angles, forward, NULL, NULL); VectorClear(e->origin); VectorClear(e->angles); Matrix4x4_CreateFromEntity(&e->matrix, e->origin, e->angles, e->scale); R_ApplyMeshModelTag(e); R_ApplyMeshModelConfig(e); Matrix4x4_Invert_Simple(&e->inverse_matrix, &e->matrix); Matrix4x4_Transform(&e->matrix, vec3_origin, e->origin); Matrix4x4_Transform(&e->matrix, vec3_forward, forward); VectorAngles(forward, e->angles); return; } Matrix4x4_CreateFromEntity(&e->matrix, e->origin, e->angles, e->scale); if (IS_MESH_MODEL(e->model)) { R_ApplyMeshModelConfig(e); } Matrix4x4_Invert_Simple(&e->inverse_matrix, &e->matrix); }
/* ============= CL_TruePointContents ============= */ int CL_TruePointContents( const vec3_t p ) { int i, contents; int oldhull; hull_t *hull; vec3_t test, offset; physent_t *pe; // sanity check if( !p ) return CONTENTS_NONE; oldhull = clgame.pmove->usehull; // get base contents from world contents = PM_HullPointContents( &cl.worldmodel->hulls[0], 0, p ); for( i = 0; i < clgame.pmove->nummoveent; i++ ) { pe = &clgame.pmove->moveents[i]; if( pe->solid != SOLID_NOT ) // disabled ? continue; // only brushes can have special contents if( !pe->model || pe->model->type != mod_brush ) continue; // check water brushes accuracy clgame.pmove->usehull = 2; hull = PM_HullForBsp( pe, clgame.pmove, offset ); clgame.pmove->usehull = oldhull; // offset the test point appropriately for this hull. VectorSubtract( p, offset, test ); if( (pe->model->flags & MODEL_HAS_ORIGIN) && !VectorIsNull( pe->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, p, test ); }; // test hull for intersection with this model if( PM_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY ) continue; // compare contents ranking if( RankForContents( pe->skin ) > RankForContents( contents )) contents = pe->skin; // new content has more priority }; return contents; }
/** * @brief */ static void Cl_ParseBaseline(void) { static entity_state_t null_state; const uint16_t number = Net_ReadShort(&net_message); const uint16_t bits = Net_ReadShort(&net_message); cl_entity_t *ent = &cl.entities[number]; Net_ReadDeltaEntity(&net_message, &null_state, &ent->baseline, number, bits); // initialize clipping matrices if (ent->baseline.solid) { if (ent->baseline.solid == SOLID_BSP) { Matrix4x4_CreateFromEntity(&ent->matrix, ent->baseline.origin, ent->baseline.angles, 1.0); Matrix4x4_Invert_Simple(&ent->inverse_matrix, &ent->matrix); } else { // bounding-box entities Matrix4x4_CreateFromEntity(&ent->matrix, ent->baseline.origin, vec3_origin, 1.0); Matrix4x4_Invert_Simple(&ent->inverse_matrix, &ent->matrix); } } }
/* ============= CL_WaterEntity ============= */ int CL_WaterEntity( const float *rgflPos ) { physent_t *pe; hull_t *hull; vec3_t test, offset; int i, oldhull; if( !rgflPos ) return -1; oldhull = clgame.pmove->usehull; for( i = 0; i < clgame.pmove->nummoveent; i++ ) { pe = &clgame.pmove->moveents[i]; if( pe->solid != SOLID_NOT ) // disabled ? continue; // only brushes can have special contents if( !pe->model || pe->model->type != mod_brush ) continue; // check water brushes accuracy clgame.pmove->usehull = 2; hull = PM_HullForBsp( pe, clgame.pmove, offset ); clgame.pmove->usehull = oldhull; // offset the test point appropriately for this hull. VectorSubtract( rgflPos, offset, test ); if( (pe->model->flags & MODEL_HAS_ORIGIN) && !VectorIsNull( pe->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, rgflPos, test ); }; // test hull for intersection with this model if( PM_HullPointContents( hull, hull->firstclipnode, test ) == CONTENTS_EMPTY ) continue; // found water entity return pe->info; } return -1; }
static float pfnTraceModel( physent_t *pe, float *start, float *end, trace_t *trace ) { int old_usehull; vec3_t start_l, end_l; vec3_t offset, temp; qboolean rotated; matrix4x4 matrix; hull_t *hull; old_usehull = clgame.pmove->usehull; clgame.pmove->usehull = 2; hull = PM_HullForBsp( pe, clgame.pmove, offset ); clgame.pmove->usehull = old_usehull; if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) rotated = true; else rotated = false; if( rotated ) { Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); Matrix4x4_VectorITransform( matrix, start, start_l ); Matrix4x4_VectorITransform( matrix, end, end_l ); } else { VectorSubtract( start, offset, start_l ); VectorSubtract( end, offset, end_l ); } SV_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, trace ); trace->ent = NULL; if( rotated ) { VectorCopy( trace->plane.normal, temp ); Matrix4x4_TransformPositivePlane( matrix, temp, trace->plane.dist, trace->plane.normal, &trace->plane.dist ); } VectorLerp( start, trace->fraction, end, trace->endpos ); return trace->fraction; }
/* ============= R_TranslateForEntity ============= */ void R_TranslateForEntity( cl_entity_t *e ) { float scale = 1.0f; if( e == clgame.entities || R_StaticEntity( e )) { R_LoadIdentity(); return; } if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) scale = e->curstate.scale; Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, scale ); Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); pglMatrixMode( GL_MODELVIEW ); GL_LoadMatrix( RI.modelviewMatrix ); tr.modelviewIdentity = false; }
// Shoots a decal onto the surface of the BSP. position is the center of the decal in world coords void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, vec3_t saxis, float scale ) { decalinfo_t decalInfo; hull_t *hull; cl_entity_t *ent = NULL; model_t *model = NULL; int width, height; if( textureIndex <= 0 || textureIndex >= MAX_TEXTURES ) { MsgDev( D_ERROR, "Decal has invalid texture!\n" ); return; } if( entityIndex > 0 ) { ent = CL_GetEntityByIndex( entityIndex ); if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); else if( ent != NULL ) model = Mod_Handle( ent->curstate.modelindex ); else return; } else if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); else model = cl.worldmodel; if( !model ) return; if( model->type != mod_brush ) { MsgDev( D_ERROR, "Decals must hit mod_brush!\n" ); return; } decalInfo.m_pModel = model; hull = &model->hulls[0]; // always use #0 hull if( ent && !( flags & FDECAL_LOCAL_SPACE )) { vec3_t pos_l; // transform decal position in local bmodel space if( !VectorIsNull( ent->angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, ent->angles, ent->origin, 1.0f ); Matrix4x4_VectorITransform( matrix, pos, pos_l ); } else { VectorSubtract( pos, ent->origin, pos_l ); } VectorCopy( pos_l, decalInfo.m_Position ); flags |= FDECAL_LOCAL_SPACE; // decal position moved into local space } else { // pass position in global VectorCopy( pos, decalInfo.m_Position ); } // deal with the s axis if one was passed in if( saxis ) { flags |= FDECAL_USESAXIS; VectorCopy( saxis, decalInfo.m_SAxis ); } // this decal must use landmark for correct transition if(!( model->flags & MODEL_HAS_ORIGIN )) { flags |= FDECAL_USE_LANDMARK; } // more state used by R_DecalNode() decalInfo.m_iTexture = textureIndex; decalInfo.m_Entity = entityIndex; decalInfo.m_Flags = flags; R_GetDecalDimensions( textureIndex, &width, &height ); decalInfo.m_Size = width >> 1; if(( height >> 1 ) > decalInfo.m_Size ) decalInfo.m_Size = height >> 1; decalInfo.m_scale = bound( MIN_DECAL_SCALE, scale, MAX_DECAL_SCALE ); // compute the decal dimensions in world space decalInfo.m_decalWidth = width / decalInfo.m_scale; decalInfo.m_decalHeight = height / decalInfo.m_scale; R_DecalNode( model, &model->nodes[hull->firstclipnode], &decalInfo ); }
/* ================= R_FindBmodelMirrors Check all bmodel surfaces and make personal mirror chain ================= */ void R_FindBmodelMirrors( cl_entity_t *e, qboolean static_entity ) { mextrasurf_t *extrasurf; vec3_t mins, maxs; msurface_t *psurf; model_t *clmodel; qboolean rotated; int i, clipFlags; clmodel = e->model; if( static_entity ) { Matrix4x4_LoadIdentity( RI.objectMatrix ); if( R_CullBox( clmodel->mins, clmodel->maxs, RI.clipFlags )) return; VectorCopy( RI.cullorigin, tr.modelorg ); clipFlags = RI.clipFlags; } else { if( !VectorIsNull( e->angles )) { for( i = 0; i < 3; i++ ) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } rotated = true; } else { VectorAdd( e->origin, clmodel->mins, mins ); VectorAdd( e->origin, clmodel->maxs, maxs ); rotated = false; } if( R_CullBox( mins, maxs, RI.clipFlags )) return; if( !VectorIsNull( e->origin ) || !VectorIsNull( e->angles )) { if( rotated ) Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f ); else Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, 1.0f ); } else Matrix4x4_LoadIdentity( RI.objectMatrix ); e->visframe = tr.framecount; // visible if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg ); else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg ); clipFlags = 0; } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) { if(!( psurf->flags & SURF_REFLECT )) continue; if( R_CullSurface( psurf, clipFlags )) continue; extrasurf = SURF_INFO( psurf, RI.currentmodel ); extrasurf->mirrorchain = tr.mirror_entities[tr.num_mirror_entities].chain; tr.mirror_entities[tr.num_mirror_entities].chain = extrasurf; } // store new mirror entity if( !static_entity && tr.mirror_entities[tr.num_mirror_entities].chain != NULL ) { tr.mirror_entities[tr.num_mirror_entities].ent = RI.currententity; tr.num_mirror_entities++; } }
/** * @brief Draws bounding boxes for all non-linked entities in `ents`. */ static void R_DrawEntityBounds(const r_entities_t *ents, const vec4_t color) { if (!r_draw_entity_bounds->value) { return; } if (ents->count == 0) { return; } R_BindDiffuseTexture(r_image_state.null->texnum); R_EnableColorArray(true); R_BindAttributeInterleaveBuffer(&r_model_state.bound_vertice_buffer, R_ARRAY_MASK_ALL); R_BindAttributeBuffer(R_ARRAY_ELEMENTS, &r_model_state.bound_element_buffer); u8vec4_t bc; ColorDecompose(color, bc); for (int32_t i = 0; i < 8; ++i) { Vector4Set(r_model_state.bound_vertices[i].color, bc[0], bc[1], bc[2], bc[3]); } static matrix4x4_t mat, modelview; R_GetMatrix(R_MATRIX_MODELVIEW, &modelview); for (size_t i = 0; i < ents->count; i++) { const r_entity_t *e = ents->entities[i]; if (e->parent || (e->effects & EF_WEAPON) || !IS_MESH_MODEL(e->model)) { continue; } VectorSet(r_model_state.bound_vertices[0].position, e->mins[0], e->mins[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[1].position, e->maxs[0], e->mins[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[2].position, e->maxs[0], e->maxs[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[3].position, e->mins[0], e->maxs[1], e->mins[2]); VectorSet(r_model_state.bound_vertices[4].position, e->mins[0], e->mins[1], e->maxs[2]); VectorSet(r_model_state.bound_vertices[5].position, e->maxs[0], e->mins[1], e->maxs[2]); VectorSet(r_model_state.bound_vertices[6].position, e->maxs[0], e->maxs[1], e->maxs[2]); VectorSet(r_model_state.bound_vertices[7].position, e->mins[0], e->maxs[1], e->maxs[2]); R_UploadToBuffer(&r_model_state.bound_vertice_buffer, sizeof(r_bound_interleave_vertex_t) * 8, r_model_state.bound_vertices); // draw box const vec_t *origin; if (e->effects & EF_BOB) { origin = e->termination; } else { origin = e->origin; } Matrix4x4_CreateFromEntity(&mat, origin, vec3_origin, e->scale); Matrix4x4_Concat(&mat, &modelview, &mat); R_SetMatrix(R_MATRIX_MODELVIEW, &mat); R_DrawArrays(GL_LINES, 0, (GLint) r_model_state.bound_element_count - 6); // draw origin Matrix4x4_CreateFromEntity(&mat, origin, e->angles, e->scale); Matrix4x4_Concat(&mat, &modelview, &mat); R_SetMatrix(R_MATRIX_MODELVIEW, &mat); R_DrawArrays(GL_LINES, (GLint) r_model_state.bound_element_count - 6, 6); } R_SetMatrix(R_MATRIX_MODELVIEW, &modelview); R_UnbindAttributeBuffer(R_ARRAY_ELEMENTS); R_EnableColorArray(false); R_Color(NULL); }
/* ============ SV_PushRotate ============ */ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) { int i, e, block, oldsolid; matrix4x4 start_l, end_l; vec3_t lmove, amove; sv_pushed_t *p, *pushed_p; vec3_t org, org2, temp; edict_t *check; if( svgame.globals->changelevel || VectorIsNull( pusher->v.avelocity )) { pusher->v.ltime += movetime; return NULL; } for( i = 0; i < 3; i++ ) amove[i] = pusher->v.avelocity[i] * movetime; // create pusher initial position Matrix4x4_CreateFromEntity( start_l, pusher->v.angles, pusher->v.origin, 1.0f ); pushed_p = svgame.pushed; // save the pusher's original position pushed_p->ent = pusher; VectorCopy( pusher->v.origin, pushed_p->origin ); VectorCopy( pusher->v.angles, pushed_p->angles ); pushed_p++; // move the pusher to it's final position SV_AngularMove( pusher, movetime, pusher->v.friction ); SV_LinkEdict( pusher, false ); pusher->v.ltime += movetime; oldsolid = pusher->v.solid; // non-solid pushers can't push anything if( pusher->v.solid == SOLID_NOT ) return NULL; // create pusher final position Matrix4x4_CreateFromEntity( end_l, pusher->v.angles, pusher->v.origin, 1.0f ); // see if any solid entities are inside the final position for( e = 1; e < svgame.numEntities; e++ ) { check = EDICT_NUM( e ); if( !SV_IsValidEdict( check )) continue; // filter movetypes to collide with if( !SV_CanPushed( check )) continue; pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition( check, pusher ); pusher->v.solid = oldsolid; if( block ) continue; // if the entity is standing on the pusher, it will definately be moved if( !(( check->v.flags & FL_ONGROUND ) && check->v.groundentity == pusher )) { if( check->v.absmin[0] >= pusher->v.absmax[0] || check->v.absmin[1] >= pusher->v.absmax[1] || check->v.absmin[2] >= pusher->v.absmax[2] || check->v.absmax[0] <= pusher->v.absmin[0] || check->v.absmax[1] <= pusher->v.absmin[1] || check->v.absmax[2] <= pusher->v.absmin[2] ) continue; // see if the ent's bbox is inside the pusher's final position if( !SV_TestEntityPosition( check, NULL )) continue; } // save original position of contacted entity pushed_p->ent = check; VectorCopy( check->v.origin, pushed_p->origin ); VectorCopy( check->v.angles, pushed_p->angles ); pushed_p->fixangle = check->v.fixangle; pushed_p++; // calculate destination position if( check->v.movetype == MOVETYPE_PUSHSTEP || check->v.movetype == MOVETYPE_STEP ) VectorAverage( check->v.absmin, check->v.absmax, org ); else VectorCopy( check->v.origin, org ); Matrix4x4_VectorITransform( start_l, org, temp ); Matrix4x4_VectorTransform( end_l, temp, org2 ); VectorSubtract( org2, org, lmove ); // i can't clear FL_ONGROUND in all cases because many bad things may be happen if( check->v.movetype != MOVETYPE_WALK ) { if( lmove[2] != 0.0f ) check->v.flags &= ~FL_ONGROUND; if( lmove[2] < 0.0f && !pusher->v.dmg ) lmove[2] = 0.0f; // let's the free falling } // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity( check, lmove, amove, &block ); pusher->v.solid = oldsolid; // pushed entity blocked by wall if( block && check->v.movetype != MOVETYPE_WALK ) check->v.flags &= ~FL_ONGROUND; // if it is still inside the pusher, block if( SV_TestEntityPosition( check, NULL ) && block ) { if( !SV_CanBlock( check )) continue; pusher->v.ltime -= movetime; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for( p = pushed_p - 1; p >= svgame.pushed; p-- ) { VectorCopy( p->origin, p->ent->v.origin ); VectorCopy( p->angles, p->ent->v.angles ); SV_LinkEdict( p->ent, (p->ent == check) ? true : false ); p->ent->v.fixangle = p->fixangle; } return check; } } return NULL; }
/* ============= SV_Physics_Compound a glue two entities together ============= */ void SV_Physics_Compound( edict_t *ent ) { edict_t *parent; // regular thinking if( !SV_RunThink( ent )) return; parent = ent->v.aiment; if( !SV_IsValidEdict( parent )) { MsgDev( D_ERROR, "%s have MOVETYPE_COMPOUND with no corresponding ent!", SV_ClassName( ent )); ent->v.movetype = MOVETYPE_NONE; return; } if( ent->v.solid != SOLID_TRIGGER ) ent->v.solid = SOLID_NOT; switch( parent->v.movetype ) { case MOVETYPE_PUSH: case MOVETYPE_PUSHSTEP: break; default: return; } // not initialized ? if( ent->v.ltime == 0.0f ) { VectorCopy( parent->v.origin, ent->v.oldorigin ); VectorCopy( parent->v.angles, ent->v.avelocity ); ent->v.ltime = host.frametime; return; } if( !VectorCompare( parent->v.origin, ent->v.oldorigin ) || !VectorCompare( parent->v.angles, ent->v.avelocity )) { matrix4x4 start_l, end_l, temp_l, child; // create parent old position Matrix4x4_CreateFromEntity( temp_l, ent->v.avelocity, ent->v.oldorigin, 1.0f ); Matrix4x4_Invert_Simple( start_l, temp_l ); // create parent actual position Matrix4x4_CreateFromEntity( end_l, parent->v.angles, parent->v.origin, 1.0f ); // stupid quake bug!!! if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) ent->v.angles[PITCH] = -ent->v.angles[PITCH]; // create child actual position Matrix4x4_CreateFromEntity( child, ent->v.angles, ent->v.origin, 1.0f ); // transform child from start to end Matrix4x4_ConcatTransforms( temp_l, start_l, child ); Matrix4x4_ConcatTransforms( child, end_l, temp_l ); // create child final position Matrix4x4_ConvertToEntity( child, ent->v.angles, ent->v.origin ); // stupid quake bug!!! if( !( host.features & ENGINE_COMPENSATE_QUAKE_BUG )) ent->v.angles[PITCH] = -ent->v.angles[PITCH]; } // notsolid ents never touch triggers SV_LinkEdict( ent, (ent->v.solid == SOLID_NOT) ? false : true ); // shuffle states VectorCopy( parent->v.origin, ent->v.oldorigin ); VectorCopy( parent->v.angles, ent->v.avelocity ); }
/** * @brief Called whenever an entity changes origin, mins, maxs, or solid to add it to * the clipping hull. */ void Sv_LinkEntity(g_entity_t *ent) { int32_t leafs[MAX_ENT_LEAFS]; int32_t clusters[MAX_ENT_LEAFS]; size_t i, j; int32_t top_node; if (ent == svs.game->entities) { // never bother with the world return; } // remove it from its current sector Sv_UnlinkEntity(ent); if (!ent->in_use) { // and if its free, we're done return; } // set the size VectorSubtract(ent->maxs, ent->mins, ent->size); // encode the size into the entity state for client prediction ent->s.solid = ent->solid; switch (ent->s.solid) { case SOLID_TRIGGER: case SOLID_PROJECTILE: case SOLID_DEAD: case SOLID_BOX: PackBounds(ent->mins, ent->maxs, &ent->s.bounds); break; default: PackBounds(vec3_origin, vec3_origin, &ent->s.bounds); break; } // set the absolute bounding box; ensure it is symmetrical Cm_EntityBounds(ent->solid, ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent->abs_mins, ent->abs_maxs); sv_entity_t *sent = &sv.entities[NUM_FOR_ENTITY(ent)]; // link to PVS leafs sent->num_clusters = 0; sent->areas[0] = sent->areas[1] = 0; // get all leafs, including solids const size_t len = Cm_BoxLeafnums(ent->abs_mins, ent->abs_maxs, leafs, lengthof(leafs), &top_node, 0); // set areas, allowing entities (doors) to occupy up to two for (i = 0; i < len; i++) { clusters[i] = Cm_LeafCluster(leafs[i]); const int32_t area = Cm_LeafArea(leafs[i]); if (area) { if (sent->areas[0] && sent->areas[0] != area) { if (sent->areas[1] && sent->areas[1] != area && sv.state == SV_LOADING) { Com_Warn("Object touching 3 areas at %s\n", vtos(ent->abs_mins)); } sent->areas[1] = area; } else { sent->areas[0] = area; } } } if (len == MAX_ENT_LEAFS) { // use top_node sent->num_clusters = -1; sent->top_node = top_node; } else { sent->num_clusters = 0; for (i = 0; i < len; i++) { if (clusters[i] == -1) { continue; // not a visible leaf } for (j = 0; j < i; j++) if (clusters[j] == clusters[i]) { break; } if (j == i) { if (sent->num_clusters == MAX_ENT_CLUSTERS) { // use top_node Com_Debug(DEBUG_SERVER, "%s exceeds MAX_ENT_CLUSTERS\n", etos(ent)); sent->num_clusters = -1; sent->top_node = top_node; break; } sent->clusters[sent->num_clusters++] = clusters[i]; } } } if (ent->solid == SOLID_NOT) { return; } // find the first sector that the ent's box crosses sv_sector_t *sector = sv_world.sectors; while (true) { if (sector->axis == -1) { break; } if (ent->abs_mins[sector->axis] > sector->dist) { sector = sector->children[0]; } else if (ent->abs_maxs[sector->axis] < sector->dist) { sector = sector->children[1]; } else { break; // crosses the node } } // add it to the sector sent->sector = sector; sector->entities = g_list_prepend(sector->entities, ent); // and update its clipping matrices const vec_t *angles = ent->solid == SOLID_BSP ? ent->s.angles : vec3_origin; Matrix4x4_CreateFromEntity(&sent->matrix, ent->s.origin, angles, 1.0); Matrix4x4_Invert_Simple(&sent->inverse_matrix, &sent->matrix); }