void GClip_LinkEntity( edict_t *ent ) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j, k; int area; int topnode; if( ent->r.area.prev ) GClip_UnlinkEntity( ent ); // unlink from old position if( ent == game.edicts ) return; // don't add the world if( !ent->r.inuse ) return; // set the size VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size ); if( ent->r.solid == SOLID_NOT || ( ent->r.svflags & SVF_PROJECTILE ) ) { ent->s.solid = 0; } else if( ISBRUSHMODEL( ent->s.modelindex ) ) { // the only predicted SOLID_TRIGGER entity is ET_PUSH_TRIGGER if( ent->r.solid != SOLID_TRIGGER || ent->s.type == ET_PUSH_TRIGGER ) ent->s.solid = SOLID_BMODEL; else ent->s.solid = 0; } else // encode the size into the entity_state for client prediction { if( ent->r.solid == SOLID_TRIGGER ) { ent->s.solid = 0; } else { // assume that x/y are equal and symetric i = ent->r.maxs[0]/8; clamp( i, 1, 31 ); // z is not symetric j = ( -ent->r.mins[2] )/8; clamp( j, 1, 31 ); // and z maxs can be negative... k = ( ent->r.maxs[2]+32 )/8; clamp( k, 1, 63 ); ent->s.solid = ( k<<10 ) | ( j<<5 ) | i; } } // set the abs box if( ISBRUSHMODEL( ent->s.modelindex ) && ( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) ) { // expand for rotation float radius; radius = RadiusFromBounds( ent->r.mins, ent->r.maxs ); for( i = 0; i < 3; i++ ) { ent->r.absmin[i] = ent->s.origin[i] - radius; ent->r.absmax[i] = ent->s.origin[i] + radius; } } else // axis aligned { VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin ); VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax ); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->r.absmin[0] -= 1; ent->r.absmin[1] -= 1; ent->r.absmin[2] -= 1; ent->r.absmax[0] += 1; ent->r.absmax[1] += 1; ent->r.absmax[2] += 1; // link to PVS leafs ent->r.num_clusters = 0; ent->r.areanum = ent->r.areanum2 = -1; // get all leafs, including solids num_leafs = trap_CM_BoxLeafnums( ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for( i = 0; i < num_leafs; i++ ) { clusters[i] = trap_CM_LeafCluster( leafs[i] ); area = trap_CM_LeafArea( leafs[i] ); if( area > -1 ) { // doors may legally straggle two areas, // but nothing should ever need more than that if( ent->r.areanum > -1 && ent->r.areanum != area ) { if( ent->r.areanum2 > -1 && ent->r.areanum2 != area ) { if( developer->integer ) G_Printf( "Object %s touching 3 areas at %f %f %f\n", ( ent->classname ? ent->classname : "" ), ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] ); } ent->r.areanum2 = area; } else ent->r.areanum = area; } } if( num_leafs >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; } else { ent->r.num_clusters = 0; for( i = 0; i < num_leafs; 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( ent->r.num_clusters == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; break; } ent->r.clusternums[ent->r.num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) { VectorCopy( ent->s.origin, ent->s.old_origin ); ent->olds = ent->s; } ent->r.linkcount++; ent->linked = qtrue; if( ent->r.solid == SOLID_NOT ) return; // find the first node that the ent's box crosses node = sv_areanodes; while( 1 ) { if( node->axis == -1 ) break; if( ent->r.absmin[node->axis] > node->dist ) node = node->children[0]; else if( ent->r.absmax[node->axis] < node->dist ) node = node->children[1]; else break; // crosses the node } // link it in if( ent->r.solid == SOLID_TRIGGER ) GClip_InsertLinkBefore( &ent->r.area, &node->trigger_edicts, NUM_FOR_EDICT( ent ) ); else GClip_InsertLinkBefore( &ent->r.area, &node->solid_edicts, NUM_FOR_EDICT( ent ) ); }
/* ============ G_MoverPush Objects need to be moved back on a failed push, otherwise riders would continue to slide. If qfalse is returned, *obstacle will be the blocking entity ============ */ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) { int i, e; gentity_t *check; vec3_t mins, maxs; pushed_t *p; int entityList[MAX_GENTITIES]; int listedEntities; vec3_t totalMins, totalMaxs; *obstacle = NULL; // mins/maxs are the bounds at the destination // totalMins / totalMaxs are the bounds for the entire move if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2] || amove[0] || amove[1] || amove[2] ) { float radius; radius = RadiusFromBounds( pusher->r.mins, pusher->r.maxs ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = pusher->r.currentOrigin[i] + move[i] - radius; maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius; totalMins[i] = mins[i] - move[i]; totalMaxs[i] = maxs[i] - move[i]; } } else { for (i=0 ; i<3 ; i++) { mins[i] = pusher->r.absmin[i] + move[i]; maxs[i] = pusher->r.absmax[i] + move[i]; } VectorCopy( pusher->r.absmin, totalMins ); VectorCopy( pusher->r.absmax, totalMaxs ); for (i=0 ; i<3 ; i++) { if ( move[i] > 0 ) { totalMaxs[i] += move[i]; } else { totalMins[i] += move[i]; } } } // unlink the pusher so we don't get it in the entityList trap_UnlinkEntity( pusher ); listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES ); // move the pusher to it's final position VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin ); VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles ); trap_LinkEntity( pusher ); // see if any solid entities are inside the final position for ( e = 0 ; e < listedEntities ; e++ ) { check = &g_entities[ entityList[ e ] ]; // only push items and players if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) { continue; } // if the entity is standing on the pusher, it will definitely be moved if ( check->s.groundEntityNum != pusher->s.number ) { // see if the ent needs to be tested if ( check->r.absmin[0] >= maxs[0] || check->r.absmin[1] >= maxs[1] || check->r.absmin[2] >= maxs[2] || check->r.absmax[0] <= mins[0] || check->r.absmax[1] <= mins[1] || check->r.absmax[2] <= mins[2] ) { continue; } // see if the ent's bbox is inside the pusher's final position // this does allow a fast moving object to pass through a thin entity... if (!G_TestEntityPosition (check)) { continue; } } // the entity needs to be pushed if ( G_TryPushingEntity( check, pusher, move, amove ) ) { continue; } // the move was blocked an entity // bobbing entities are instant-kill and never get blocked if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) { continue; } // save off the obstacle so we can call the block function (crush, etc) *obstacle = check; // 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>=pushed ; p-- ) { VectorCopy (p->origin, p->ent->s.pos.trBase); VectorCopy (p->angles, p->ent->s.apos.trBase); if ( p->ent->client ) { p->ent->client->ps.delta_angles[YAW] = p->deltayaw; VectorCopy (p->origin, p->ent->client->ps.origin); } trap_LinkEntity (p->ent); } return qfalse; } return qtrue; }
/* ================= R_ComputeLOD ================= */ int R_ComputeLOD( trRefEntity_t *ent ) { float radius; float flod, lodscale; float projectedRadius; md3Frame_t *frame; int lod; if ( tr.currentModel->numLods < 2 ) { // model has only 1 LOD level, skip computations and bias lod = 0; } else { // multiple LODs exist, so compute projected bounding sphere // and use that as a criteria for selecting LOD frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); frame += ent->e.frame; radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] ); if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 ) { lodscale = (r_lodscale->value+r_autolodscalevalue->value); if ( lodscale > 20 ) { lodscale = 20; } else if ( lodscale < 0 ) { lodscale = 0; } flod = 1.0f - projectedRadius * lodscale; } else { // object intersects near view plane, e.g. view weapon flod = 0; } flod *= tr.currentModel->numLods; lod = Q_ftol( flod ); if ( lod < 0 ) { lod = 0; } else if ( lod >= tr.currentModel->numLods ) { lod = tr.currentModel->numLods - 1; } } lod += r_lodbias->integer; if ( lod >= tr.currentModel->numLods ) lod = tr.currentModel->numLods - 1; if ( lod < 0 ) lod = 0; return lod; }
/* ================= R_ComputeLOD ================= */ int R_ComputeLOD( trRefEntity_t *ent ) { float radius; float flod, lodscale; float projectedRadius; md3Frame_t *frame; #ifdef RAVENMD4 mdrHeader_t *mdr; mdrFrame_t *mdrframe; #endif int lod; if ( tr.currentModel->numLods < 2 ) { // model has only 1 LOD level, skip computations and bias lod = 0; } else { // multiple LODs exist, so compute projected bounding sphere // and use that as a criteria for selecting LOD #ifdef RAVENMD4 if(tr.currentModel->type == MOD_MDR) { int frameSize; mdr = (mdrHeader_t *) tr.currentModel->modelData; frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]); mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame); radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]); } else #endif { frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); frame += ent->e.frame; radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] ); } if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 ) { lodscale = r_lodscale->value; if (lodscale > 20) lodscale = 20; flod = 1.0f - projectedRadius * lodscale; } else { // object intersects near view plane, e.g. view weapon flod = 0; } flod *= tr.currentModel->numLods; lod = ri.ftol(flod); if ( lod < 0 ) { lod = 0; } else if ( lod >= tr.currentModel->numLods ) { lod = tr.currentModel->numLods - 1; } } lod += r_lodbias->integer; if ( lod >= tr.currentModel->numLods ) lod = tr.currentModel->numLods - 1; if ( lod < 0 ) lod = 0; return lod; }
/* ================= Mod_LoadBrushModel ================= */ void Mod_LoadBrushModel (model_t *mod, void *buffer) { int i; dheader_t *header; dmodel_t *bm; loadmodel->type = mod_brush; header = (dheader_t *)buffer; i = LittleLong (header->version); if (i != BSPVERSION) Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); // swap all the lumps mod_base = (byte *)header; for (i=0 ; i<sizeof(dheader_t)/4 ; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); // load into heap Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES]); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES]); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); Mod_LoadNodes (&header->lumps[LUMP_NODES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // for (i=0 ; i<mod->numsubmodels ; i++) { bm = &mod->submodels[i]; mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); mod->firstnode = bm->headnode[0]; if ((unsigned)mod->firstnode > loadmodel->numnodes) Host_Error ("Inline model %i has bad firstnode", i); mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; sprintf (name, "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } }
/* ================= Mod_LoadBrushModel ================= */ void Mod_LoadBrushModel (model_t *mod, void *buffer) { int i, j, maxhulls; dheader_t *header; mmodel_t *bm; extern cvar_t host_mapname; loadmodel->type = mod_brush; header = (dheader_t *)buffer; #ifdef BSP23TEST mod_bspversion = #endif i = LittleLong (header->version); if ((i != BSPVERSION) && (i != BSPVERSION-1)) // JDH: bsps from v0.8 beta work fine too // Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); #ifdef BSP23TEST if (i != 23) #endif { Con_Printf ("Mod_LoadBrushModel: %s has wrong version number %i (should be %i)\n", mod->name, i, BSPVERSION); mod->numsubmodels = -1; // HACK - incorrect BSP version is no longer fatal return; } loadmodel->isworldmodel = COM_FilenamesEqual (loadmodel->name, va("maps/%s.bsp", host_mapname.string)); if (loadmodel->isworldmodel) mod_oversized = false; // swap all the lumps mod_base = (byte *)header; #ifdef BSP23TEST if (mod_bspversion == 23) { for (i=0 ; i<sizeof(dheader_t)/4 - 8 ; i++) ((int *)header)[i] = LittleLong (((int *)header)[i]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); #ifndef RQM_SV_ONLY Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES-1]); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES-1]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING-1]); // Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES-1]); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES-1]); #endif Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS-1]); Mod_LoadNodes (&header->lumps[LUMP_NODES]); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES-1]); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS-1]); } else #endif { for (i=0 ; i<sizeof(dheader_t)/4 ; i++) ((int *)header)[i] = LittleLong (((int *)header)[i]); // load into heap Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); #ifndef RQM_SV_ONLY Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES]); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES]); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); #endif Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); Mod_LoadNodes (&header->lumps[LUMP_NODES]); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); } Mod_MakeHull0 (); if (loadmodel->isworldmodel && mod_oversized) Mod_RecalcNodeBounds (); #ifndef RQM_SV_ONLY mod->numframes = 2; // regular and alternate animation #endif #ifdef HEXEN2_SUPPORT if (hexen2) maxhulls = MAX_MAP_HULLS_H2; else #endif maxhulls = MAX_MAP_HULLS; // set up the submodels (FIXME: this is confusing) for (i = 0 ; i < mod->numsubmodels ; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j=1 ; j<maxhulls ; j++) { mod->hulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes - 1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); #ifndef RQM_SV_ONLY mod->radius = RadiusFromBounds (mod->mins, mod->maxs); #endif mod->numleafs = bm->visleafs; if (i < mod->numsubmodels - 1) { // duplicate the basic information char name[10]; sprintf (name, "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; Q_strcpy (loadmodel->name, name, sizeof(loadmodel->name)); mod = loadmodel; } } /**********JDH************/ // can't do this in Mod_LoadLeafs, since the "loadmodel" at that time // is not what becomes the worldmodel /*if ( origmod->isworldmodel ) { Mod_DecompressVis( mod ); }*/ /**********JDH************/ }
void SV_LinkEntity(sharedEntity_t *gEnt) { worldSector_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int cluster; int num_leafs; int i, j, k; int area; int lastLeaf; float *origin, *angles; svEntity_t *ent; ent = SV_SvEntityForGentity(gEnt); // sanity check for possible currentOrigin being reset bug if (!gEnt->r.bmodel && VectorCompare(gEnt->r.currentOrigin, vec3_origin)) { Com_DPrintf("WARNING: BBOX entity is being linked at world origin, this is probably a bug\n"); } if (ent->worldSector) { SV_UnlinkEntity(gEnt); // unlink from old position } // encode the size into the entityState_t for client prediction if (gEnt->r.bmodel) { gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value } else if (gEnt->r.contents & (CONTENTS_SOLID|CONTENTS_BODY)) { // assume that x/y are equal and symetric i = gEnt->r.maxs[0]; if (i < 1) { i = 1; } if (i > 255) { i = 255; } // z is not symetric j = (-gEnt->r.mins[2]); if (j < 1) { j = 1; } if (j > 255) { j = 255; } // and z maxs can be negative... k = (gEnt->r.maxs[2] + 32); if (k < 1) { k = 1; } if (k > 255) { k = 255; } gEnt->s.solid = (k << 16)|(j << 8)|i; } else { gEnt->s.solid = 0; } // get the position origin = gEnt->r.currentOrigin; angles = gEnt->r.currentAngles; // set the abs box if (gEnt->r.bmodel && (angles[0] || angles[1] || angles[2])) { // expand for rotation float max; max = RadiusFromBounds(gEnt->r.mins, gEnt->r.maxs); for (i = 0; i < 3; i++) { gEnt->r.absmin[i] = origin[i] - max; gEnt->r.absmax[i] = origin[i] + max; } } else { // normal VectorAdd(origin, gEnt->r.mins, gEnt->r.absmin); VectorAdd(origin, gEnt->r.maxs, gEnt->r.absmax); } // because movement is clipped an epsilon away from an actual edge, we must fully check even when bounding boxes don't quite touch gEnt->r.absmin[0] -= 1; gEnt->r.absmin[1] -= 1; gEnt->r.absmin[2] -= 1; gEnt->r.absmax[0] += 1; gEnt->r.absmax[1] += 1; gEnt->r.absmax[2] += 1; // link to PVS leafs ent->numClusters = 0; ent->lastCluster = 0; ent->areanum = -1; ent->areanum2 = -1; // get all leafs, including solids num_leafs = CM_BoxLeafnums(gEnt->r.absmin, gEnt->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf); // if none of the leafs were inside the map, the entity is outside the world and can be considered unlinked if (!num_leafs) { return; } // set areas, even from clusters that don't fit in the entity array for (i = 0; i < num_leafs; i++) { area = CM_LeafArea(leafs[i]); if (area != -1) { // doors may legally straggle two areas, but nothing should evern need more than that if (ent->areanum != -1 && ent->areanum != area) { if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) { Com_DPrintf("Object %i touching 3 areas at %f %f %f\n", gEnt->s.number, gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]); } ent->areanum2 = area; } else { ent->areanum = area; } } } // store as many explicit clusters as we can ent->numClusters = 0; for (i = 0; i < num_leafs; i++) { cluster = CM_LeafCluster(leafs[i]); if (cluster != -1) { ent->clusternums[ent->numClusters++] = cluster; if (ent->numClusters == MAX_ENT_CLUSTERS) { break; } } } // store off a last cluster if we need to if (i != num_leafs) { ent->lastCluster = CM_LeafCluster(lastLeaf); } gEnt->r.linkcount++; // find the first world sector node that the ent's box crosses node = sv_worldSectors; while (1) { if (node->axis == -1) { break; } if (gEnt->r.absmin[node->axis] > node->dist) { node = node->children[0]; } else if (gEnt->r.absmax[node->axis] < node->dist) { node = node->children[1]; } else { break; // crosses the node } } // link it in ent->worldSector = node; ent->nextEntityInWorldSector = node->entities; node->entities = ent; gEnt->r.linked = qtrue; }
void CreateBrushFaces (void) { int i,j, k; vec_t r; face_t *f, *next; winding_t *w; plane_t clipplane, faceplane; mface_t *mf; vec3_t offset, point; VectorClear(offset); ClearBounds(brush_mins,brush_maxs); brush_faces = NULL; if(!strcmp(ValueForKey(CurrentEntity,"classname"),"area_rotate")) { entity_t *FoundEntity; char *searchstring, text[20]; searchstring = ValueForKey(CurrentEntity,"target"); FoundEntity = FindTargetEntity(searchstring); if(FoundEntity) GetVectorForKey(FoundEntity, "origin", offset); sprintf(text, "%g %g %g", offset[0], offset[1], offset[2]); SetKeyValue(CurrentEntity, "origin", text); } GetVectorForKey(CurrentEntity, "origin", offset); //printf("%i brushfaces at offset %f %f %f\n", numbrushfaces, offset[0], offset[1], offset[2]); for (i = 0;i < numbrushfaces;i++) { mf = &faces[i]; //printf("plane %f %f %f %f\n", mf->plane.normal[0], mf->plane.normal[1], mf->plane.normal[2], mf->plane.dist); faceplane = mf->plane; w = BaseWindingForPlane (&faceplane); //VectorNegate( faceplane.normal, point ); for (j = 0;j < numbrushfaces && w;j++) { clipplane = faces[j].plane; if(j == i) continue; // flip the plane, because we want to keep the back side VectorNegate(clipplane.normal, clipplane.normal); clipplane.dist *= -1; w = ClipWindingEpsilon (w, &clipplane, ON_EPSILON, true); } if (!w) //printf("----- skipped plane -----\n"); continue; // overcontrained plane // this face is a keeper f = AllocFace (); f->winding = w; for (j = 0;j < w->numpoints;j++) { for (k = 0;k < 3;k++) { point[k] = w->points[j][k] - offset[k]; r = Q_rint(point[k]); if ( fabs( point[k] - r ) < ZERO_EPSILON) w->points[j][k] = r; else w->points[j][k] = point[k]; // check for incomplete brushes if( w->points[j][k] >= BOGUS_RANGE || w->points[j][k] <= -BOGUS_RANGE ) break; } // remove this brush if(k < 3) { FreeFace(f); for (f = brush_faces; f; f = next) { next = f->next; FreeFace (f); } brush_faces = NULL; printf("----- skipped brush -----\n"); return; } AddPointToBounds( w->points[j], brush_mins, brush_maxs ); } CheckWinding(w); faceplane.dist -= DotProduct(faceplane.normal, offset); f->texturenum = mf->texinfo; f->planenum = FindPlane (&faceplane, &f->planeside); f->next = brush_faces; brush_faces = f; } // Rotatable objects have to have a bounding box big enough // to account for all its rotations. if (DotProduct(offset, offset)) { vec_t delta; delta = RadiusFromBounds( brush_mins, brush_maxs ); for (k = 0;k < 3;k++) { brush_mins[k] = -delta; brush_maxs[k] = delta; } } //printf("%i : %f %f %f : %f %f %f\n", numbrushfaces, brush_mins[0], brush_mins[1], brush_mins[2], brush_maxs[0], brush_maxs[1], brush_maxs[2]); }
void SP_misc_gamemodel( void ) { char* model; char* skin; vec_t angle; vec3_t angles; vec_t scale; vec3_t vScale; vec3_t org; cg_gamemodel_t* gamemodel; int i; #if 0 // ZTM: Note: Spearmint's game always drops misc_gamemodels. Also, RTCW has targetname set though I'm not sure what, if anything, it's used for. if ( CG_SpawnString( "targetname", "", &model ) || CG_SpawnString( "scriptname", "", &model ) || CG_SpawnString( "spawnflags", "", &model ) ) { // Gordon: this model may not be static, so let the server handle it return; } #endif if ( cg.numMiscGameModels >= MAX_STATIC_GAMEMODELS ) { CG_Error( "^1MAX_STATIC_GAMEMODELS(%i) hit", MAX_STATIC_GAMEMODELS ); } CG_SpawnString( "model", "", &model ); CG_SpawnString( "skin", "", &skin ); CG_SpawnVector( "origin", "0 0 0", org ); if ( !CG_SpawnVector( "angles", "0 0 0", angles ) ) { if ( CG_SpawnFloat( "angle", "0", &angle ) ) { angles[YAW] = angle; } } if ( !CG_SpawnVector( "modelscale_vec", "1 1 1", vScale ) ) { if ( CG_SpawnFloat( "modelscale", "1", &scale ) ) { VectorSet( vScale, scale, scale, scale ); } } gamemodel = &cgs.miscGameModels[cg.numMiscGameModels++]; gamemodel->model = trap_R_RegisterModel( model ); if ( *skin ) { CG_RegisterSkin( skin, &gamemodel->skin, qfalse ); } AnglesToAxis( angles, gamemodel->axes ); for ( i = 0; i < 3; i++ ) { VectorScale( gamemodel->axes[i], vScale[i], gamemodel->axes[i] ); } VectorCopy( org, gamemodel->org ); if ( gamemodel->model ) { vec3_t mins, maxs; trap_R_ModelBounds( gamemodel->model, mins, maxs, 0, 0, 0 ); for ( i = 0; i < 3; i++ ) { mins[i] *= vScale[i]; maxs[i] *= vScale[i]; } gamemodel->radius = RadiusFromBounds( mins, maxs ); } else { gamemodel->radius = 0; } }
/* ================= R_ComputeLOD ================= */ static int R_ComputeLOD(trRefEntity_t *ent) { float radius; float flod, lodscale; float projectedRadius; md3Frame_t *frame; int lod; if (tr.currentModel->numLods < 2) { // model has only 1 LOD level, skip computations and bias lod = 0; } else { // multiple LODs exist, so compute projected bounding sphere // and use that as a criteria for selecting LOD // RF, checked for a forced lowest LOD if (ent->e.reFlags & REFLAG_FORCE_LOD) { return (tr.currentModel->numLods - 1); } frame = ( md3Frame_t * )((( unsigned char * ) tr.currentModel->model.mdc[0]) + tr.currentModel->model.mdc[0]->ofsFrames); frame += ent->e.frame; radius = RadiusFromBounds(frame->bounds[0], frame->bounds[1]); //----(SA) testing if (ent->e.reFlags & REFLAG_ORIENT_LOD) { // right now this is for trees, and pushes the lod distance way in. // this is not the intended purpose, but is helpful for the new // terrain level that has loads of trees // radius = radius/2.0f; } //----(SA) end if ((projectedRadius = ProjectRadius(radius, ent->e.origin)) != 0) { lodscale = r_lodscale->value; if (lodscale > 20) { lodscale = 20; } flod = 1.0f - projectedRadius * lodscale; } else { // object intersects near view plane, e.g. view weapon flod = 0; } flod *= tr.currentModel->numLods; lod = (int)(flod); if (lod < 0) { lod = 0; } else if (lod >= tr.currentModel->numLods) { lod = tr.currentModel->numLods - 1; } } lod += r_lodbias->integer; if (lod >= tr.currentModel->numLods) { lod = tr.currentModel->numLods - 1; } if (lod < 0) { lod = 0; } return lod; }
void SP_misc_gamemodel( void ) { char* model; vec_t angle; vec3_t angles; vec_t scale; vec3_t vScale; vec3_t org; cg_gamemodel_t* gamemodel; int i; if(CG_SpawnString( "targetname", "", &model ) || CG_SpawnString( "scriptname", "", &model ) || CG_SpawnString( "spawnflags", "", &model )) { // Gordon: this model may not be static, so let the server handle it return; } if( cg.numMiscGameModels >= MAX_STATIC_GAMEMODELS ) { CG_Error( "^1MAX_STATIC_GAMEMODELS(%i) hit", MAX_STATIC_GAMEMODELS ); } CG_SpawnString( "model", "", &model ); CG_SpawnVector( "origin", "0 0 0", org ); if(!CG_SpawnVector( "angles", "0 0 0", angles )) { if(CG_SpawnFloat( "angle", "0", &angle )) { angles[YAW] = angle; } } if(!CG_SpawnVector( "modelscale_vec", "1 1 1", vScale )) { if( CG_SpawnFloat( "modelscale", "1", &scale ) ) { VectorSet( vScale, scale, scale, scale ); } } gamemodel = &cgs.miscGameModels[cg.numMiscGameModels++]; gamemodel->model = trap_R_RegisterModel( model ); AnglesToAxis( angles, gamemodel->axes ); for( i = 0; i < 3; i++ ) { VectorScale( gamemodel->axes[i], vScale[i], gamemodel->axes[i] ); } VectorCopy( org, gamemodel->org ); if( gamemodel->model ) { vec3_t mins, maxs; trap_R_ModelBounds( gamemodel->model, mins, maxs ); for( i = 0; i < 3; i++ ) { mins[i] *= vScale[i]; maxs[i] *= vScale[i]; } gamemodel->radius = RadiusFromBounds( mins, maxs ); } else { gamemodel->radius = 0; } }
void GClip_LinkEntity( tvm_relay_t *relay, edict_t *ent ) { int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j; int area; int topnode; if( ent->r.area.prev ) GClip_UnlinkEntity( relay, ent ); // unlink from old position if( ent == ent->relay->edicts ) return; // don't add the world if( !ent->r.inuse ) return; // set the size VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size ); // set the abs box if( ent->s.solid == SOLID_BMODEL && ( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) ) { // expand for rotation float radius; radius = RadiusFromBounds( ent->r.mins, ent->r.maxs ); for( i = 0; i < 3; i++ ) { ent->r.absmin[i] = ent->s.origin[i] - radius; ent->r.absmax[i] = ent->s.origin[i] + radius; } } else { // normal VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin ); VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax ); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->r.absmin[0] -= 1; ent->r.absmin[1] -= 1; ent->r.absmin[2] -= 1; ent->r.absmax[0] += 1; ent->r.absmax[1] += 1; ent->r.absmax[2] += 1; // link to PVS leafs ent->r.num_clusters = 0; ent->r.areanum = ent->r.areanum2 = -1; // get all leafs, including solids num_leafs = trap_CM_BoxLeafnums( relay, ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for( i = 0; i < num_leafs; i++ ) { clusters[i] = trap_CM_LeafCluster( relay, leafs[i] ); area = trap_CM_LeafArea( relay, leafs[i] ); if( area > -1 ) { // doors may legally straggle two areas, // but nothing should ever need more than that if( ent->r.areanum > -1 && ent->r.areanum != area ) { if( ent->r.areanum2 > -1 && ent->r.areanum2 != area ) { TVM_Printf( "Object touching 3 areas at %f %f %f\n", ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] ); } ent->r.areanum2 = area; } else ent->r.areanum = area; } } if( num_leafs >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; } else { ent->r.num_clusters = 0; for( i = 0; i < num_leafs; 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( ent->r.num_clusters == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; break; } ent->r.clusternums[ent->r.num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) { VectorCopy( ent->s.origin, ent->s.old_origin ); //ent->olds = ent->s; } ent->r.linkcount++; }