/** * @brief Performs box traces against the world and all inline models, gives the hit position back * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] traceLine The start/stop position of the trace. * @param[in] traceBox The minimum/maximum extents of the collision box that is projected. * @param[in] levelmask A mask of the game levels to trace against. Mask 0x100 filters clips. * @param[in] brushmask Any brush detected must at least have one of these contents. * @param[in] brushreject Any brush detected with any of these contents will be ignored. * @param[in] list The local models list (a local model has a name starting with * followed by the model number) * @return a trace_t with the information of the closest brush intersected. * @sa CM_CompleteBoxTrace * @sa CM_HintedTransformedBoxTrace */ trace_t CM_EntCompleteBoxTrace (mapTiles_t* mapTiles, const Line& traceLine, const AABB* traceBox, int levelmask, int brushmask, int brushreject, const char** list) { AABB lineBox(*traceBox); lineBox.shift(traceLine.start); /* the traceBox in starting position */ AABB lineBoxTemp(*traceBox); lineBoxTemp.shift(traceLine.stop); /* in end position */ lineBox.add(lineBoxTemp); /* bounding box for the whole trace */ /* Now lineBox specifies the whole volume to be traced through. */ /* reconstruct a levelmask */ const vec_t minZ = lineBox.getMinZ(); const vec_t maxZ = lineBox.getMaxZ(); int newLevelMask = 0; if (levelmask & TL_FLAG_ACTORCLIP) /* if the passed levelmask contains the bit for the cliplevels, */ newLevelMask = TL_FLAG_ACTORCLIP; /* preserve it */ for (int i = 0; i < PATHFINDING_HEIGHT; i++) { const vec_t lower = i * UNIT_HEIGHT; /* the height bounds of the level */ const vec_t upper = (i + 1) * UNIT_HEIGHT; if (minZ > upper || maxZ < lower) continue; newLevelMask |= (1 << i); } /* trace against world first */ const trace_t tr = CM_CompleteBoxTrace(mapTiles, traceLine, *traceBox, newLevelMask, brushmask, brushreject); if (!list || tr.fraction == 0.0) return tr; trace_t trace = tr; for (const char** name = list; *name; name++) { /* check whether this is really an inline model */ if (*name[0] != '*') Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s'", *name); const cBspModel_t* model = CM_InlineModel(mapTiles, *name); assert(model); if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6) continue; AABB modelBox; /* Quickly calculate the bounds of this model to see if they can overlap. */ CM_CalculateWidestBoundingBox(model, modelBox); /* If the bounds of the extents box and the line do not overlap, then skip tracing this model. */ if (!lineBox.doesIntersect(modelBox)) continue; const trace_t newtr = CM_HintedTransformedBoxTrace(mapTiles->mapTiles[model->tile], traceLine, *traceBox, model->headnode, brushmask, brushreject, model->origin, model->angles, model->shift, trace.fraction); /* memorize the trace with the minimal fraction */ if (newtr.fraction == 0.0) return newtr; if (newtr.fraction < trace.fraction) trace = newtr; } return trace; }
/** * @brief Performs box traces against the world and all inline models, gives the hit position back * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] start The position to start the trace. * @param[in] end The position where the trace ends. * @param[in] traceBox The minimum/maximum extents of the collision box that is projected. * @param[in] levelmask A mask of the game levels to trace against. Mask 0x100 filters clips. * @param[in] brushmask Any brush detected must at least have one of these contents. * @param[in] brushreject Any brush detected with any of these contents will be ignored. * @param[in] list The local models list (a local model has a name starting with * followed by the model number) * @return a trace_t with the information of the closest brush intersected. * @sa CM_CompleteBoxTrace * @sa CM_HintedTransformedBoxTrace */ trace_t CM_EntCompleteBoxTrace (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const AABB* traceBox, int levelmask, int brushmask, int brushreject, const char **list) { trace_t trace, newtr; const char **name; vec3_t bmins, bmaxs; /* trace against world first */ trace = CM_CompleteBoxTrace(mapTiles, start, end, *traceBox, levelmask, brushmask, brushreject); if (!list || trace.fraction == 0.0) return trace; /* Find the original bounding box for the tracing line. */ VectorSet(bmins, std::min(start[0], end[0]), std::min(start[1], end[1]), std::min(start[2], end[2])); VectorSet(bmaxs, std::max(start[0], end[0]), std::max(start[1], end[1]), std::max(start[2], end[2])); /* Now increase the bounding box by mins and maxs in both directions. */ VectorAdd(bmins, traceBox->mins, bmins); VectorAdd(bmaxs, traceBox->maxs, bmaxs); /* Now bmins and bmaxs specify the whole volume to be traced through. */ for (name = list; *name; name++) { vec3_t amins, amaxs; const cBspModel_t *model; /* check whether this is really an inline model */ if (*name[0] != '*') Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s'", *name); model = CM_InlineModel(mapTiles, *name); assert(model); if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6) continue; /* Quickly calculate the bounds of this model to see if they can overlap. */ CM_CalculateBoundingBox(model, amins, amaxs); /* If the bounds of the extents box and the line do not overlap, then skip tracing this model. */ if (bmins[0] > amaxs[0] || bmins[1] > amaxs[1] || bmins[2] > amaxs[2] || bmaxs[0] < amins[0] || bmaxs[1] < amins[1] || bmaxs[2] < amins[2]) continue; newtr = CM_HintedTransformedBoxTrace(&mapTiles->mapTiles[model->tile], start, end, traceBox->mins, traceBox->maxs, model->headnode, brushmask, brushreject, model->origin, model->angles, model->shift, trace.fraction); /* memorize the trace with the minimal fraction */ if (newtr.fraction == 0.0) return newtr; if (newtr.fraction < trace.fraction) trace = newtr; } return trace; }
/** * @brief Checks traces against the world and all inline models, gives the hit position back * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] start The position to start the trace. * @param[in] stop The position where the trace ends. * @param[out] end The position where the line hits a object or the stop position if nothing is in the line * @param[in] levelmask * @param[in] entlist The local models list * @sa TR_TestLineDM * @sa CM_TransformedBoxTrace */ bool CM_EntTestLineDM (mapTiles_t *mapTiles, const vec3_t start, const vec3_t stop, vec3_t end, const int levelmask, const char **entlist) { trace_t trace; const char **name; bool blocked; float fraction = 2.0f; /* trace against world first */ blocked = TR_TestLineDM(mapTiles, start, stop, end, levelmask); if (!entlist) return blocked; for (name = entlist; *name; name++) { const cBspModel_t *model; /* check whether this is really an inline model */ if (*name[0] != '*') { /* Let's see what the data looks like... */ Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s' (inlines: %p, name: %p)", *name, (void*)entlist, (void*)name); } model = CM_InlineModel(mapTiles, *name); assert(model); if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6) continue; /* check if we can safely exclude that the trace can hit the model */ if (CM_LineMissesModel(start, stop, model)) continue; trace = CM_HintedTransformedBoxTrace(&mapTiles->mapTiles[model->tile], start, end, vec3_origin, vec3_origin, model->headnode, MASK_ALL, 0, model->origin, model->angles, vec3_origin, fraction); /* if we started the trace in a wall */ if (trace.startsolid) { VectorCopy(start, end); return true; } /* trace not finished */ if (trace.fraction < fraction) { blocked = true; fraction = trace.fraction; VectorCopy(trace.endpos, end); } } /* return result */ return blocked; }
/** * @brief Clip against solid entities * @sa CL_Trace * @sa SV_ClipMoveToEntities */ static void CL_ClipMoveToLEs (MoveClipCL* clip) { if (clip->trace.allsolid) return; le_t* le = nullptr; while ((le = LE_GetNextInUse(le))) { int tile = 0; if (!(le->contents & clip->contentmask)) continue; if (le == clip->passle || le == clip->passle2) continue; vec3_t angles, shift; const int32_t headnode = CL_HullForEntity(le, &tile, shift, angles); assert(headnode < MAX_MAP_NODES); vec3_t origin; VectorCopy(le->origin, origin); trace_t trace = CM_HintedTransformedBoxTrace(cl.mapTiles->mapTiles[tile], clip->moveLine, clip->objBox, headnode, clip->contentmask, 0, origin, angles, shift, 1.0); if (trace.fraction < clip->trace.fraction) { /* make sure we keep a startsolid from a previous trace */ const bool oldStart = clip->trace.startsolid; trace.le = le; clip->trace = trace; clip->trace.startsolid |= oldStart; /* if true, plane is not valid */ } else if (trace.allsolid) { trace.le = le; clip->trace = trace; /* if true, the initial point was in a solid area */ } else if (trace.startsolid) { trace.le = le; clip->trace.startsolid = true; } } }
/** * @brief Checks traces against the world and all inline models * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] start The position to start the trace. * @param[in] stop The position where the trace ends. * @param[in] levelmask * @param[in] entlist The local models list * @sa TR_TestLine * @sa CM_InlineModel * @sa CM_TransformedBoxTrace * @return true - hit something * @return false - hit nothing */ bool CM_EntTestLine (mapTiles_t *mapTiles, const vec3_t start, const vec3_t stop, const int levelmask, const char **entlist) { trace_t trace; const char **name; /* trace against world first */ if (TR_TestLine(mapTiles, start, stop, levelmask)) /* We hit the world, so we didn't make it anyway... */ return true; /* no local models, so we made it. */ if (!entlist) return false; for (name = entlist; *name; name++) { const cBspModel_t *model; /* check whether this is really an inline model */ if (*name[0] != '*') Com_Error(ERR_DROP, "name in the inlineList is no inline model: '%s'", *name); model = CM_InlineModel(mapTiles, *name); assert(model); if (model->headnode >= mapTiles->mapTiles[model->tile].numnodes + 6) continue; /* check if we can safely exclude that the trace can hit the model */ if (CM_LineMissesModel(start, stop, model)) continue; trace = CM_HintedTransformedBoxTrace(&mapTiles->mapTiles[model->tile], start, stop, vec3_origin, vec3_origin, model->headnode, MASK_VISIBILILITY, 0, model->origin, model->angles, model->shift, 1.0); /* if we started the trace in a wall */ /* or the trace is not finished */ if (trace.startsolid || trace.fraction < 1.0) return true; } /* not blocked */ return false; }