/** * @note This is only working for one z-level. But our models should be * split for each level anyway. * @param ent The edict to fill the forbidden list for */ static void G_BuildForbiddenListForEntity (edict_t *ent) { pos3_t mins, maxs, origin; vec3_t center, shiftedMins, shiftedMaxs; int xDelta, yDelta, size, i, j; VectorAdd(ent->absmin, ent->origin, shiftedMins); VectorAdd(ent->absmax, ent->origin, shiftedMaxs); VectorCenterFromMinsMaxs(shiftedMins, shiftedMaxs, center); VecToPos(shiftedMins, mins); VecToPos(shiftedMaxs, maxs); VecToPos(center, origin); xDelta = std::max(1, maxs[0] - mins[0]); yDelta = std::max(1, maxs[1] - mins[1]); size = xDelta * yDelta; ent->forbiddenListPos = (pos3_t *)G_TagMalloc(size * sizeof(pos3_t), TAG_LEVEL); ent->forbiddenListSize = size; for (i = 0; i < xDelta; i++) { for (j = 0; j < yDelta; j++) { const pos_t x = mins[0] + i; const pos_t y = mins[1] + j; const pos_t z = origin[2]; VectorSet(ent->forbiddenListPos[i], x, y, z); } } }
/** * @brief Fills in s->stmins[] and s->stmaxs[] */ static void R_SetSurfaceExtents (mBspSurface_t *surf, const model_t* mod) { vec3_t mins, maxs; vec2_t stmins, stmaxs; int i, j; const mBspTexInfo_t *tex; VectorSet(mins, 999999, 999999, 999999); VectorSet(maxs, -999999, -999999, -999999); Vector2Set(stmins, 999999, 999999); Vector2Set(stmaxs, -999999, -999999); tex = surf->texinfo; for (i = 0; i < surf->numedges; i++) { const int e = mod->bsp.surfedges[surf->firstedge + i]; const mBspVertex_t *v; vec3_t position; if (e >= 0) v = &mod->bsp.vertexes[mod->bsp.edges[e].v[0]]; else v = &mod->bsp.vertexes[mod->bsp.edges[-e].v[1]]; VectorCopy(v->position, position); for (j = 0; j < 3; j++) { /* calculate mins, maxs */ position[j] += (float)shift[j]; if (position[j] > maxs[j]) maxs[j] = position[j]; if (position[j] < mins[j]) mins[j] = position[j]; } { /* calculate stmins, stmaxs */ const float valS = DotProduct(v->position, tex->uv) + tex->u_offset; const float valT = DotProduct(v->position, tex->vv) + tex->v_offset; stmins[0] = min(valS, stmins[0]); stmaxs[0] = max(valS, stmaxs[0]); stmins[1] = min(valT, stmins[1]); stmaxs[1] = max(valT, stmaxs[1]); } } VectorCopy(mins, surf->mins); VectorCopy(maxs, surf->maxs); VectorCenterFromMinsMaxs(surf->mins, surf->maxs, surf->center); for (i = 0; i < 2; i++) { const int bmins = floor(stmins[i] / surf->lightmap_scale); const int bmaxs = ceil(stmaxs[i] / surf->lightmap_scale); surf->stmins[i] = bmins * surf->lightmap_scale; surf->stmaxs[i] = bmaxs * surf->lightmap_scale; surf->stcenter[i] = (surf->stmaxs[i] + surf->stmins[i]) / 2.0; surf->stextents[i] = (bmaxs - bmins) * surf->lightmap_scale; } }
static bool Destroy_Breakable (edict_t *self) { vec3_t origin; const char *model = self->model; VectorCenterFromMinsMaxs(self->absmin, self->absmax, origin); /* the HP value is used to decide whether this was a triggered call or a * call during a fight - a triggered call will be handled differently in * terms of timing and the related particle effects in the client code */ if (self->HP == 0) G_EventModelExplodeTriggered(self); else G_EventModelExplode(self); if (self->particle) G_SpawnParticle(origin, self->spawnflags, self->particle); switch (self->material) { case MAT_GLASS: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakglass+"); break; case MAT_METAL: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakmetal+"); break; case MAT_ELECTRICAL: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakelectric+"); break; case MAT_WOOD: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakwood+"); break; case MAT_MAX: break; } G_TouchEdicts(self, 10.0f); /* destroy the door trigger */ if (self->child) G_FreeEdict(self->child); /* now we can destroy the edict completely */ G_FreeEdict(self); AABB oldAABB(vec3_origin, vec3_origin); gi.GetInlineModelAABB(model, oldAABB); GridBox rerouteOldBox(oldAABB); G_RecalcRouting(model, rerouteOldBox); return true; }
/** * @brief Calculates the bounding box for the given bsp model * @param[in] model The model to calculate the bbox for * @param[out] mins The maxs of the bbox * @param[out] maxs The mins of the bbox */ static void CM_CalculateBoundingBox (const cBspModel_t* model, vec3_t mins, vec3_t maxs) { /* Quickly calculate the bounds of this model to see if they can overlap. */ VectorAdd(model->origin, model->mins, mins); VectorAdd(model->origin, model->maxs, maxs); if (VectorNotEmpty(model->angles)) { vec3_t acenter, aoffset; const float offset = std::max(std::max(fabs(mins[0] - maxs[0]), fabs(mins[1] - maxs[1])), fabs(mins[2] - maxs[2])) / 2.0; VectorCenterFromMinsMaxs(mins, maxs, acenter); VectorSet(aoffset, offset, offset, offset); VectorAdd(acenter, aoffset, maxs); VectorSubtract(acenter, aoffset, mins); } }
/** * @brief Compute scale and center for a model info data structure * @param[in] boxSize The size the model should fit into * @param[in,out] mi The model info that contains the model that should be scaled * @param[out] scale The scale vector * @param[out] center The center of the model (center of the model's bounding box) * @note The scale and center vectors are parameters here because the @c modelInfo_t * struct only holds pointers to the vectors. * @todo Take the rotation info from @c modelInfo_t into account */ void R_ModelAutoScale (const vec2_t boxSize, modelInfo_t *mi, vec3_t scale, vec3_t center) { const float width = mi->model->maxs[0] - mi->model->mins[0]; const float height = mi->model->maxs[2] - mi->model->mins[2]; const float factorX = boxSize[0] / width; const float factorY = boxSize[1] / height; const float size = std::min(factorX, factorY); /* get center */ VectorCenterFromMinsMaxs(mi->model->mins, mi->model->maxs, center); VectorNegate(center, center); VectorSet(scale, size, size, size); mi->center = center; mi->scale = scale; }
/** * @brief Spawns a sound (that will be spatialized on the client side) * @param ent The edict that is causing the sound * @param origin The origin of the sound * @param sound The sound file, path relative to sounds/. If there is a + at the end the client will * choose a random sound. See the event function for more information. * of the path, a random sound will be taken. */ void G_EventSpawnSound (unsigned int playerMask, bool instant, const edict_t* ent, const vec3_t origin, const char *sound) { G_EventAdd(playerMask, EV_SOUND | (instant ? EVENT_INSTANTLY : 0), ent->number); /* use the entity origin unless it is a bmodel or explicitly specified */ if (!origin) { if (ent->solid == SOLID_BSP) { vec3_t origin_v; VectorCenterFromMinsMaxs(ent->mins, ent->maxs, origin_v); VectorAdd(ent->origin, origin_v, origin_v); gi.WritePos(origin); } else { gi.WritePos(vec3_origin); } } else { gi.WritePos(origin); } gi.WriteString(sound); G_EventEnd(); }
void CalculateMinsMaxs (const vec3_t angles, const vec3_t mins, const vec3_t maxs, const vec3_t origin, vec3_t absmin, vec3_t absmax) { /* expand for rotation */ if (VectorNotEmpty(angles)) { vec3_t minVec, maxVec, tmpMinVec, tmpMaxVec; vec3_t centerVec, halfVec, newCenterVec, newHalfVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(mins, maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(angles, m); VectorRotate(m, centerVec, newCenterVec); VectorRotate(m, halfVec, newHalfVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, newHalfVec, tmpMinVec); VectorAdd(newCenterVec, newHalfVec, tmpMaxVec); /* rotation may have changed min and max of the box, so adjust it */ minVec[0] = min(tmpMinVec[0], tmpMaxVec[0]); minVec[1] = min(tmpMinVec[1], tmpMaxVec[1]); minVec[2] = min(tmpMinVec[2], tmpMaxVec[2]); maxVec[0] = max(tmpMinVec[0], tmpMaxVec[0]); maxVec[1] = max(tmpMinVec[1], tmpMaxVec[1]); maxVec[2] = max(tmpMinVec[2], tmpMaxVec[2]); /* Adjust the absolute mins/maxs */ VectorAdd(origin, minVec, absmin); VectorAdd(origin, maxVec, absmax); } else { /* normal */ VectorAdd(origin, mins, absmin); VectorAdd(origin, maxs, absmax); } }
/** * @brief If origin is NULL, the origin is determined from the entity origin or the midpoint of the entity box for bmodels. */ void SV_StartSound (int mask, const vec3_t origin, const edict_t *entity, const char *sound) { vec3_t origin_v; struct dbuffer *msg; /* use the entity origin unless it is a bmodel or explicitly specified */ if (!origin) { origin = origin_v; if (entity->solid == SOLID_BSP) { VectorCenterFromMinsMaxs(entity->mins, entity->maxs, origin_v); VectorAdd(entity->origin, origin_v, origin_v); } else { VectorCopy(entity->origin, origin_v); } } msg = new_dbuffer(); NET_WriteByte(msg, svc_sound); NET_WriteString(msg, sound); NET_WritePos(msg, origin); SV_Multicast(mask, msg); }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] routing The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] box The box around the inline model (alternative to name) * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t *mapTiles, Routing &routing, const char *name, const GridBox &box, const char **list) { if (box.isZero()) { pos3_t min, max; vec3_t absmin, absmax; const cBspModel_t *model; unsigned int i; /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } #if 1 /* An attempt to fix the 'doors starting opened' bug (# 3456). * The main difference is the (missing) rotation of the halfVec. * The results are better, but do not fix the problem. */ CalculateMinsMaxs(model->angles, model->mins, model->maxs, model->origin, absmin, absmax); #else /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(minVec, model->origin, absmin); VectorAdd(maxVec, model->origin, absmax); } else { /* normal */ /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(model->mins, model->origin, absmin); VectorAdd(model->maxs, model->origin, absmax); } #endif VecToPos(absmin, min); VecToPos(absmax, max); /* fit min/max into the world size */ max[0] = std::min(max[0], (pos_t)(PATHFINDING_WIDTH - 1)); max[1] = std::min(max[1], (pos_t)(PATHFINDING_WIDTH - 1)); max[2] = std::min(max[2], (pos_t)(PATHFINDING_HEIGHT - 1)); for (i = 0; i < 3; i++) min[i] = std::max(min[i], (pos_t)0); /* We now have the dimensions, call the generic rerouting function. */ GridBox rerouteBox(min, max); Grid_RecalcBoxRouting(mapTiles, routing, rerouteBox, list); } else { /* use the passed box */ Grid_RecalcBoxRouting(mapTiles, routing, box, list); } }
/** * @brief Deals splash damage to a target and its surroundings. * @param[in] ent The shooting actor * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is. * @param[in] impact The impact vector where the grenade is exploding * @param[in,out] mock pseudo shooting - only for calculating mock values - NULL for real shots * @param[in] tr The trace where the grenade hits something (or not) */ static void G_SplashDamage (edict_t *ent, const fireDef_t *fd, vec3_t impact, shot_mock_t *mock, const trace_t* tr) { edict_t *check = NULL; vec3_t center; float dist; int damage; const bool shock = (fd->obj->dmgtype == gi.csi->damShock); assert(fd->splrad > 0.0); while ((check = G_EdictsGetNextInUse(check))) { /* If we use a blinding weapon we skip the target if it's looking * away from the impact location. */ if (shock && !G_FrustumVis(check, impact)) continue; if (G_IsBrushModel(check) && G_IsBreakable(check)) VectorCenterFromMinsMaxs(check->absmin, check->absmax, center); else if (G_IsLivingActor(check) || G_IsBreakable(check)) VectorCopy(check->origin, center); else continue; /* check for distance */ dist = VectorDist(impact, center); dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0; if (dist > fd->splrad) continue; if (fd->irgoggles) { if (G_IsActor(check)) { /* check whether this actor (check) is in the field of view of the 'shooter' (ent) */ if (G_FrustumVis(ent, check->origin)) { if (!mock) { const unsigned int playerMask = G_TeamToPM(ent->team) ^ G_VisToPM(check->visflags); G_AppearPerishEvent(playerMask, true, check, ent); G_VisFlagsAdd(check, G_PMToVis(playerMask)); } } } continue; } /* check for walls */ if (G_IsLivingActor(check) && !G_ActorVis(impact, ent, check, false)) continue; /* do damage */ if (shock) damage = 0; else damage = fd->spldmg[0] * (1.0 - dist / fd->splrad); if (mock) mock->allow_self = true; G_Damage(check, fd, damage, ent, mock, NULL); if (mock) mock->allow_self = false; } /** @todo splash might also hit other surfaces and the trace doesn't handle that */ if (tr && G_FireAffectedSurface(tr->surface, fd)) { /* move a little away from the impact vector */ VectorMA(impact, 1, tr->plane.normal, impact); G_SpawnParticle(impact, tr->contentFlags >> 8, "burning"); }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] map The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t *mapTiles, routing_t *map, const char *name, const char **list) { const cBspModel_t *model; pos3_t min, max; unsigned int i; double start, end; start = time(NULL); /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } Com_DPrintf(DEBUG_PATHING, "Model:%s origin(%f,%f,%f) angles(%f,%f,%f) mins(%f,%f,%f) maxs(%f,%f,%f)\n", name, model->origin[0], model->origin[1], model->origin[2], model->angles[0], model->angles[1], model->angles[2], model->mins[0], model->mins[1], model->mins[2], model->maxs[0], model->maxs[1], model->maxs[2]); /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(minVec, model->origin, minVec); VecToPos(minVec, min); VectorAdd(maxVec, model->origin, maxVec); VecToPos(maxVec, max); } else { /* normal */ vec3_t temp; /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(model->mins, model->origin, temp); VecToPos(temp, min); VectorAdd(model->maxs, model->origin, temp); VecToPos(temp, max); } /* fit min/max into the world size */ max[0] = min(max[0] + 1, PATHFINDING_WIDTH - 1); max[1] = min(max[1] + 1, PATHFINDING_WIDTH - 1); max[2] = min(max[2] + 1, PATHFINDING_HEIGHT - 1); for (i = 0; i < 3; i++) min[i] = max(min[i] - 1, 0); /* We now have the dimensions, call the generic rerouting function. */ Grid_RecalcBoxRouting(mapTiles, map, min, max, list); end = time(NULL); Com_DPrintf(DEBUG_ROUTING, "Retracing for model %s between (%i, %i, %i) and (%i, %i %i) in %5.1fs\n", name, min[0], min[1], min[2], max[0], max[1], max[2], end - start); }
static void CL_CenterCameraIntoMap_f (void) { VectorCenterFromMinsMaxs(cl.mapData->mapMin, cl.mapData->mapMax, cl.cam.origin); }