2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-01-07 12:35:33 +03:00
rehlds/rehlds/engine/world.cpp
2016-02-04 01:26:04 +03:00

1522 lines
39 KiB
C++

/*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
hull_t box_hull;
hull_t beam_hull;
box_clipnodes_t box_clipnodes;
box_planes_t box_planes;
beam_planes_t beam_planes;
areanode_t sv_areanodes[32];
int sv_numareanodes;
cvar_t sv_force_ent_intersection = { "sv_force_ent_intersection", "0", 0, 0.0f, NULL };
/* <ca50b> ../engine/world.c:48 */
void ClearLink(link_t *l)
{
l->next = l;
l->prev = l;
}
/* <ca41e> ../engine/world.c:53 */
void RemoveLink(link_t *l)
{
l->next->prev = l->prev;
l->prev->next = l->next;
}
/* <ca452> ../engine/world.c:59 */
void InsertLinkBefore(link_t *l, link_t *before)
{
l->next = before;
l->prev = before->prev;
l->next->prev = l;
l->prev->next = l;
}
/* <ca56a> ../engine/world.c:66 */
NOXREF void InsertLinkAfter(link_t *l, link_t *after)
{
l->prev = after;
l->next = after->next;
after->next = l;
l->next->prev = l;
}
/* <ca59d> ../engine/world.c:94 */
void SV_InitBoxHull(void)
{
box_hull.clipnodes = &box_clipnodes[0];
box_hull.planes = &box_planes[0];
box_hull.firstclipnode = 0;
box_hull.lastclipnode = 5;
Q_memcpy(&beam_hull, &box_hull, sizeof(beam_hull));
beam_hull.planes = &beam_planes[0];
for (int i = 0; i < 6; i++)
{
int side = i & 1;
box_clipnodes[i].planenum = i;
box_clipnodes[i].children[side] = -1;
box_clipnodes[i].children[side ^ 1] = (i != 5) ? i + 1 : CONTENTS_SOLID;
box_planes[i].type = i >> 1;
beam_planes[i].type = 5;
box_planes[i].normal[i >> 1] = 1.0f;
}
}
/* <ca3c6> ../engine/world.c:135 */
hull_t *SV_HullForBox(const vec_t *mins, const vec_t *maxs)
{
box_planes[0].dist = maxs[0];
box_planes[1].dist = mins[0];
box_planes[2].dist = maxs[1];
box_planes[3].dist = mins[1];
box_planes[4].dist = maxs[2];
box_planes[5].dist = mins[2];
return &box_hull;
}
/* <ca60c> ../engine/world.c:148 */
NOXREF hull_t *SV_HullForBeam(const vec_t *start, const vec_t *end, const vec_t *size)
{
vec3_t tmp = { 0, 0, 0 };
beam_planes[0].normal[0] = end[0] - start[0];
beam_planes[0].normal[1] = end[1] - start[1];
beam_planes[0].normal[2] = end[2] - start[2];
VectorNormalize(beam_planes[0].normal);
beam_planes[1].normal[0] = beam_planes[0].normal[0];
beam_planes[1].normal[1] = beam_planes[0].normal[1];
beam_planes[1].normal[2] = beam_planes[0].normal[2];
beam_planes[0].dist = _DotProduct((vec_t *)end, beam_planes[0].normal);
beam_planes[1].dist = _DotProduct((vec_t *)start, beam_planes[0].normal);
if (fabs(beam_planes[0].normal[2]) < 0.9f)
tmp[2] = 1.0f;
else
tmp[0] = 1.0f;
CrossProduct(beam_planes[0].normal, tmp, beam_planes[2].normal);
VectorNormalize(beam_planes[2].normal);
beam_planes[3].normal[0] = beam_planes[2].normal[0];
beam_planes[3].normal[1] = beam_planes[2].normal[1];
beam_planes[3].normal[2] = beam_planes[2].normal[2];
beam_planes[2].dist = (start[0] + beam_planes[2].normal[0]) * beam_planes[2].normal[0] + (start[1] + beam_planes[2].normal[1]) * beam_planes[2].normal[1] + (start[2] + beam_planes[2].normal[2]) * beam_planes[2].normal[2];
tmp[0] = start[0] - beam_planes[2].normal[0];
tmp[1] = start[1] - beam_planes[2].normal[1];
tmp[2] = start[2] - beam_planes[2].normal[2];
beam_planes[3].dist = _DotProduct(tmp, beam_planes[2].normal);
CrossProduct(beam_planes[2].normal, beam_planes[0].normal, beam_planes[4].normal);
VectorNormalize(beam_planes[4].normal);
beam_planes[5].normal[0] = beam_planes[4].normal[0];
beam_planes[5].normal[1] = beam_planes[4].normal[1];
beam_planes[5].normal[2] = beam_planes[4].normal[2];
beam_planes[4].dist = _DotProduct((vec_t *)start, beam_planes[4].normal);
beam_planes[5].dist = (start[0] - beam_planes[4].normal[0]) * beam_planes[4].normal[0] + (start[1] - beam_planes[4].normal[1]) * beam_planes[4].normal[1] + (start[2] - beam_planes[4].normal[2]) * beam_planes[4].normal[2];
beam_planes[0].dist += fabs(beam_planes[0].normal[0] * size[0]) + fabs(beam_planes[0].normal[1] * size[1]) + fabs(beam_planes[0].normal[2] * size[2]);
beam_planes[1].dist -= (fabs(beam_planes[1].normal[0] * size[0]) + fabs(beam_planes[1].normal[1] * size[1]) + fabs(beam_planes[1].normal[2] * size[2]));
beam_planes[2].dist += fabs(beam_planes[2].normal[0] * size[0]) + fabs(beam_planes[2].normal[1] * size[1]) + fabs(beam_planes[2].normal[2] * size[2]);
beam_planes[3].dist -= (fabs(beam_planes[3].normal[0] * size[0]) + fabs(beam_planes[3].normal[1] * size[1]) + fabs(beam_planes[3].normal[2] * size[2]));
beam_planes[4].dist += fabs(beam_planes[4].normal[0] * size[0]) + fabs(beam_planes[4].normal[1] * size[1]) + fabs(beam_planes[4].normal[2] * size[2]);
beam_planes[5].dist -= (fabs(beam_planes[4].normal[0] * size[0]) + fabs(beam_planes[4].normal[1] * size[1]) + fabs(beam_planes[4].normal[2] * size[2]));
return &beam_hull;
}
/* <ca662> ../engine/world.c:201 */
struct hull_s *SV_HullForBsp(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset)
{
model_t *model;
hull_t *hull;
model = g_psv.models[ent->v.modelindex];
if (!model)
Sys_Error("Hit a %s with no model (%s)", &pr_strings[ent->v.classname], &pr_strings[ent->v.model]);
if (model->type)
Sys_Error("Hit a %s with no model (%s)", &pr_strings[ent->v.classname], &pr_strings[ent->v.model]);
float xSize = maxs[0] - mins[0];
if (xSize > 8.0f)
{
if (xSize > 36.0f)
{
hull = &model->hulls[2];
}
else
{
float zSize = maxs[2] - mins[2];
if (zSize > 36.0f)
hull = &model->hulls[1];
else
hull = &model->hulls[3];
}
offset[0] = hull->clip_mins[0] - mins[0];
offset[1] = hull->clip_mins[1] - mins[1];
offset[2] = hull->clip_mins[2] - mins[2];
}
else
{
hull = &model->hulls[0];
offset[0] = model->hulls[0].clip_mins[0];
offset[1] = model->hulls[0].clip_mins[1];
offset[2] = model->hulls[0].clip_mins[2];
}
offset[0] = ent->v.origin[0] + offset[0];
offset[1] = ent->v.origin[1] + offset[1];
offset[2] = ent->v.origin[2] + offset[2];
return hull;
}
/* <ca6f1> ../engine/world.c:251 */
hull_t *SV_HullForEntity(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec_t *offset)
{
vec3_t hullmins;
vec3_t hullmaxs;
if (ent->v.solid == SOLID_BSP)
{
if (ent->v.movetype != MOVETYPE_PUSH && ent->v.movetype != MOVETYPE_PUSHSTEP)
Sys_Error("SOLID_BSP without MOVETYPE_PUSH");
return SV_HullForBsp(ent, mins, maxs, offset);
}
hullmins[0] = ent->v.mins[0] - maxs[0];
hullmins[1] = ent->v.mins[1] - maxs[1];
hullmins[2] = ent->v.mins[2] - maxs[2];
hullmaxs[0] = ent->v.maxs[0] - mins[0];
hullmaxs[1] = ent->v.maxs[1] - mins[1];
hullmaxs[2] = ent->v.maxs[2] - mins[2];
offset[0] = ent->v.origin[0];
offset[1] = ent->v.origin[1];
offset[2] = ent->v.origin[2];
return SV_HullForBox(hullmins, hullmaxs);
}
/* <ca793> ../engine/world.c:308 */
areanode_t *SV_CreateAreaNode(int depth, vec_t *mins, vec_t *maxs)
{
areanode_t *anode;
vec3_t mins1;
vec3_t maxs2;
vec3_t size;
vec3_t maxs1;
vec3_t mins2;
float fmid;
anode = &sv_areanodes[sv_numareanodes++];
ClearLink(&anode->trigger_edicts);
ClearLink(&anode->solid_edicts);
if (depth == 4)
{
anode->axis = -1;
anode->children[0] = NULL;
anode->children[1] = NULL;
return anode;
}
size[0] = maxs[0] - mins[0];
size[1] = maxs[1] - mins[1];
anode->axis = (size[0] <= size[1]) ? 1 : 0;
mins1[0] = mins[0];
mins1[1] = mins[1];
mins1[2] = mins[2];
mins2[0] = mins[0];
mins2[1] = mins[1];
mins2[2] = mins[2];
maxs1[0] = maxs[0];
maxs1[1] = maxs[1];
maxs1[2] = maxs[2];
maxs2[0] = maxs[0];
maxs2[1] = maxs[1];
maxs2[2] = maxs[2];
fmid = 0.5f * (mins[anode->axis] + maxs[anode->axis]);
mins2[anode->axis] = fmid;
maxs1[anode->axis] = fmid;
anode->dist = fmid;
anode->children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2);
anode->children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1);
return anode;
}
/* <ca877> ../engine/world.c:353 */
void SV_ClearWorld(void)
{
SV_InitBoxHull();
Q_memset(sv_areanodes, 0, sizeof(sv_areanodes));
sv_numareanodes = 0;
SV_CreateAreaNode(0, g_psv.worldmodel->mins, g_psv.worldmodel->maxs);
}
/* <ca8bb> ../engine/world.c:369 */
void SV_UnlinkEdict(edict_t *ent)
{
if (ent->area.prev)
{
RemoveLink(&ent->area);
ent->area.next = NULL;
ent->area.prev = NULL;
}
}
/* <ca9f5> ../engine/world.c:383 */
void SV_TouchLinks(edict_t *ent, areanode_t *node)
{
edict_t *touch;
model_t *pModel;
link_t *next;
for (link_t *l = node->trigger_edicts.next; l != &node->trigger_edicts; l = next)
{
next = l->next;
touch = (edict_t *)((char *)l - offsetof(edict_t, area));
if (touch == ent)
continue;
if (ent->v.groupinfo != 0 && touch->v.groupinfo != 0)
{
if (g_groupop)
{
if (g_groupop == GROUP_OP_NAND && (ent->v.groupinfo & touch->v.groupinfo))
continue;
}
else
{
if (!(ent->v.groupinfo & touch->v.groupinfo))
continue;
}
}
if (touch->v.solid == SOLID_TRIGGER
&& ent->v.absmin[0] <= touch->v.absmax[0]
&& ent->v.absmin[1] <= touch->v.absmax[1]
&& ent->v.absmin[2] <= touch->v.absmax[2]
&& ent->v.absmax[0] >= touch->v.absmin[0]
&& ent->v.absmax[1] >= touch->v.absmin[1]
&& ent->v.absmax[2] >= touch->v.absmin[2])
{
pModel = g_psv.models[touch->v.modelindex];
if (pModel && pModel->type == mod_brush)
{
vec3_t offset;
hull_t *hull = SV_HullForBsp(touch, ent->v.mins, ent->v.maxs, offset);
vec3_t localPosition;
localPosition[0] = ent->v.origin[0] - offset[0];
localPosition[1] = ent->v.origin[1] - offset[1];
localPosition[2] = ent->v.origin[2] - offset[2];
int contents = SV_HullPointContents(hull, hull->firstclipnode, localPosition);
if (contents != CONTENTS_SOLID)
continue;
}
gGlobalVariables.time = (float)g_psv.time;
gEntityInterface.pfnTouch(touch, ent);
}
}
if (node->axis != -1)
{
if (ent->v.absmax[node->axis] > node->dist)
SV_TouchLinks(ent, node->children[0]);
if (node->dist > ent->v.absmin[node->axis])
SV_TouchLinks(ent, node->children[1]);
}
}
/* <ca8f2> ../engine/world.c:467 */
#ifndef REHLDS_OPT_PEDANTIC
void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node, int *topnode)
{
mplane_t *splitplane;
int sides;
if (node->contents == CONTENTS_SOLID)
return;
if (node->contents < 0)
{
if (ent->num_leafs < MAX_ENT_LEAFS)
{
mleaf_t *leaf = (mleaf_t *)node;
int leafnum = leaf - g_psv.worldmodel->leafs - 1;
ent->leafnums[ent->num_leafs] = leafnum;
ent->num_leafs++;
}
else
{
ent->num_leafs = MAX_ENT_LEAFS + 1;
}
return;
}
splitplane = node->plane;
if (splitplane->type >= 3)
{
sides = BoxOnPlaneSide(ent->v.absmin, ent->v.absmax, splitplane);
}
else
{
if (splitplane->dist > ent->v.absmin[splitplane->type])
{
if (splitplane->dist < ent->v.absmax[splitplane->type])
{
sides = 3;
}
else
{
sides = 2;
}
}
else
{
sides = 1;
}
}
if (sides == 3)
{
if (*topnode == -1)
*topnode = node - g_psv.worldmodel->nodes;
}
if (sides & 1)
SV_FindTouchedLeafs(ent, node->children[0], topnode);
if (sides & 2)
SV_FindTouchedLeafs(ent, node->children[1], topnode);
}
#else // REHLDS_OPT_PEDANTIC
// unrolled some tail recursion
void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node, int *topnode)
{
mplane_t *splitplane;
int sides;
while (1)
{
if (node->contents == CONTENTS_SOLID)
return;
if (node->contents < 0)
{
if (ent->num_leafs < MAX_ENT_LEAFS)
{
mleaf_t *leaf = (mleaf_t *)node;
int leafnum = leaf - g_psv.worldmodel->leafs - 1;
ent->leafnums[ent->num_leafs] = leafnum;
ent->num_leafs++;
}
else
{
ent->num_leafs = MAX_ENT_LEAFS + 1;
}
return;
}
splitplane = node->plane;
if (splitplane->type >= 3)
{
sides = BoxOnPlaneSide(ent->v.absmin, ent->v.absmax, splitplane);
if (sides == 3)
{
if (*topnode == -1)
*topnode = node - g_psv.worldmodel->nodes;
}
if (sides & 1)
SV_FindTouchedLeafs(ent, node->children[0], topnode);
if (sides & 2)
SV_FindTouchedLeafs(ent, node->children[1], topnode);
}
else
{
if (splitplane->dist > ent->v.absmin[splitplane->type])
{
if (splitplane->dist < ent->v.absmax[splitplane->type])
{
// sides = 3;
// do both children nodes
if (*topnode == -1)
*topnode = node - g_psv.worldmodel->nodes;
SV_FindTouchedLeafs(ent, node->children[0], topnode);
node = node->children[1];
continue;
}
else
{
// sides = 2;
// do only SV_FindTouchedLeafs(ent, node->children[1], topnode);
node = node->children[1];
continue;
}
}
else
{
// sides = 1;
// do only SV_FindTouchedLeafs(ent, node->children[0], topnode);
node = node->children[0];
continue;
}
}
break;
}
}
#endif // REHLDS_OPT_PEDANTIC
/* <caab0> ../engine/world.c:517 */
void SV_LinkEdict(edict_t *ent, qboolean touch_triggers)
{
static int iTouchLinkSemaphore = 0;
areanode_t *node;
int topnode;
if (ent->area.prev)
SV_UnlinkEdict(ent);
if (ent == &g_psv.edicts[0] || ent->free)
return;
gEntityInterface.pfnSetAbsBox(ent);
if (ent->v.movetype == MOVETYPE_FOLLOW && ent->v.aiment)
{
ent->headnode = ent->v.aiment->headnode;
ent->num_leafs = ent->v.aiment->num_leafs;
Q_memcpy(ent->leafnums, ent->v.aiment->leafnums, sizeof(ent->leafnums));
}
else
{
ent->num_leafs = 0;
ent->headnode = -1;
topnode = -1;
if (ent->v.modelindex)
SV_FindTouchedLeafs(ent, g_psv.worldmodel->nodes, &topnode);
if (ent->num_leafs > MAX_ENT_LEAFS)
{
ent->num_leafs = 0;
ent->headnode = (int)topnode;
Q_memset(ent->leafnums, -1, sizeof(ent->leafnums));
}
}
if (ent->v.solid == SOLID_NOT && ent->v.skin >= -1)
return;
if (ent->v.solid != SOLID_BSP || g_psv.models[ent->v.modelindex] || Q_strlen(&pr_strings[ent->v.model]))
{
node = sv_areanodes;
while (1)
{
if (node->axis == -1)
break;
if (ent->v.absmin[node->axis] <= node->dist)
{
if (ent->v.absmax[node->axis] >= node->dist)
break;
node = node->children[1];
}
else
{
node = node->children[0];
}
}
InsertLinkBefore(&ent->area, (ent->v.solid == SOLID_TRIGGER) ? &node->trigger_edicts : &node->solid_edicts);
if (touch_triggers)
{
if (!iTouchLinkSemaphore)
{
iTouchLinkSemaphore = 1;
SV_TouchLinks(ent, sv_areanodes);
iTouchLinkSemaphore = 0;
}
}
}
else
{
Con_DPrintf("Inserted %s with no model\n", &pr_strings[ent->v.classname]);
}
}
/* <ca97c> ../engine/world.c:630 */
int SV_HullPointContents(hull_t *hull, int num, const vec_t *p)
{
dclipnode_t *node;
mplane_t *plane;
float d;
int i = num;
while (i >= 0)
{
if (hull->firstclipnode > i || hull->lastclipnode < i)
Sys_Error(__FUNCTION__ ": bad node number");
node = &hull->clipnodes[i];
plane = &hull->planes[node->planenum];
if (plane->type > 2)
d = _DotProduct(plane->normal, p) - plane->dist;
else
d = p[plane->type] - plane->dist;
i = node->children[(d >= 0.0f) ? 0 : 1];
}
return i;
}
/* <cabb7> ../engine/world.c:663 */
int SV_LinkContents(areanode_t *node, const vec_t *pos)
{
link_t *l;
link_t *next;
edict_t *touch;
model_t *pModel;
hull_t *hull;
vec3_t localPosition;
vec3_t offset;
#ifdef REHLDS_OPT_PEDANTIC
// unroll tail recursion
while (1)
#endif
{
for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next)
{
next = l->next;
touch = (edict_t *)((char *)l - offsetof(edict_t, area));
if (!touch->v.solid)
{
if (touch->v.groupinfo)
{
if (g_groupop)
{
if (g_groupop == GROUP_OP_NAND && (touch->v.groupinfo & g_groupmask))
continue;
}
else
{
if (!(touch->v.groupinfo & g_groupmask))
continue;
}
}
pModel = g_psv.models[touch->v.modelindex];
if (pModel
&& !pModel->type
&& pos[0] <= (double)touch->v.absmax[0]
&& pos[1] <= (double)touch->v.absmax[1]
&& pos[2] <= (double)touch->v.absmax[2]
&& pos[0] >= (double)touch->v.absmin[0]
&& pos[1] >= (double)touch->v.absmin[1]
&& pos[2] >= (double)touch->v.absmin[2])
{
int contents = touch->v.skin;
if (contents < -100 || contents > 100)
Con_DPrintf("Invalid contents on trigger field: %s\n", &pr_strings[touch->v.classname]);
hull = SV_HullForBsp(touch, vec3_origin, vec3_origin, offset);
localPosition[0] = pos[0] - offset[0];
localPosition[1] = pos[1] - offset[1];
localPosition[2] = pos[2] - offset[2];
if (SV_HullPointContents(hull, hull->firstclipnode, localPosition) != -1)
return contents;
}
}
}
if (node->axis == -1)
return -1;
#ifndef REHLDS_OPT_PEDANTIC
if (pos[node->axis] > node->dist)
return SV_LinkContents(node->children[0], pos);
if (pos[node->axis] < node->dist)
return SV_LinkContents(node->children[1], pos);
#else // REHLDS_OPT_PEDANTIC
if (pos[node->axis] > node->dist)
{
node = node->children[0];
continue;
}
if (pos[node->axis] < node->dist)
{
node = node->children[1];
continue;
}
break;
#endif // REHLDS_OPT_PEDANTIC
}
return -1;
}
/* <cac70> ../engine/world.c:738 */
int SV_PointContents(const vec_t *p)
{
int cont;
int entityContents;
cont = SV_HullPointContents(g_psv.worldmodel->hulls, 0, p);
if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN)
{
cont = CONTENTS_WATER;
}
else
{
if (cont == CONTENTS_SOLID)
return CONTENTS_SOLID;
}
entityContents = SV_LinkContents(&sv_areanodes[0], p);
return (entityContents != -1) ? entityContents : cont;
}
/* <cb289> ../engine/world.c:764 */
edict_t *SV_TestEntityPosition(edict_t *ent)
{
trace_t trace;
qboolean monsterClip;
monsterClip = (ent->v.flags & FL_MONSTERCLIP) ? TRUE : FALSE;
trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent, monsterClip);
if (trace.startsolid)
{
SV_SetGlobalTrace(&trace);
return trace.ent;
}
return NULL;
}
#ifndef REHLDS_OPT_PEDANTIC
/* <cacbc> ../engine/world.c:804 */
qboolean SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, const vec_t *p1, const vec_t *p2, trace_t *trace)
{
dclipnode_t *node;
mplane_t *plane;
float t2;
vec3_t mid;
float frac;
float t1;
signed int side;
float midf;
float pdif = p2f - p1f;
float DIST_EPSILON = 0.03125f;
if (num >= 0)
{
if (num < hull->firstclipnode || num > hull->lastclipnode || !hull->planes)
Sys_Error(__FUNCTION__ ": bad node number");
node = &hull->clipnodes[num];
plane = &hull->planes[hull->clipnodes[num].planenum];
if (plane->type >= 3)
{
t1 = _DotProduct(p1, plane->normal) - plane->dist;
t2 = _DotProduct(p2, plane->normal) - plane->dist;
}
else
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
if (t1 >= 0.0f && t2 >= 0.0f)
return SV_RecursiveHullCheck(hull, node->children[0], p1f, p2f, p1, p2, trace);
if (t1 >= 0.0f)
{
midf = t1 - DIST_EPSILON;
}
else
{
if (t2 < 0.0f)
return SV_RecursiveHullCheck(hull, node->children[1], p1f, p2f, p1, p2, trace);
midf = t1 + DIST_EPSILON;
}
midf = midf / (t1 - t2);
if (midf >= 0.0f)
{
if (midf > 1.0f)
midf = 1.0f;
}
else
{
midf = 0.0f;
}
if (!IS_NAN(midf)) // not a number
{
frac = pdif * midf + p1f;
mid[0] = (p2[0] - p1[0]) * midf + p1[0];
mid[1] = (p2[1] - p1[1]) * midf + p1[1];
mid[2] = (p2[2] - p1[2]) * midf + p1[2];
side = (t1 < 0.0f) ? 1 : 0;
if (SV_RecursiveHullCheck(hull, node->children[side], p1f, frac, p1, mid, trace))
{
if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID)
return SV_RecursiveHullCheck(hull, node->children[side ^ 1], frac, p2f, mid, p2, trace);
if (!trace->allsolid)
{
if (side)
{
trace->plane.normal[0] = vec3_origin[0] - plane->normal[0];
trace->plane.normal[1] = vec3_origin[1] - plane->normal[1];
trace->plane.normal[2] = vec3_origin[2] - plane->normal[2];
trace->plane.dist = -plane->dist;
}
else
{
trace->plane.normal[0] = plane->normal[0];
trace->plane.normal[1] = plane->normal[1];
trace->plane.normal[2] = plane->normal[2];
trace->plane.dist = plane->dist;
}
while (1)
{
if (SV_HullPointContents(hull, hull->firstclipnode, mid) != CONTENTS_SOLID)
{
trace->fraction = frac;
trace->endpos[0] = mid[0];
trace->endpos[1] = mid[1];
trace->endpos[2] = mid[2];
return FALSE;
}
midf -= 0.1f;
if (midf < 0.0f)
break;
frac = pdif * midf + p1f;
mid[0] = (p2[0] - p1[1]) * midf + p1[0];
mid[1] = (p2[1] - p1[1]) * midf + p1[1];
mid[2] = (p2[2] - p1[2]) * midf + p1[2];
}
trace->fraction = frac;
trace->endpos[0] = mid[0];
trace->endpos[1] = mid[1];
trace->endpos[2] = mid[2];
Con_DPrintf("backup past 0\n");
return FALSE;
}
}
}
return FALSE;
}
if (num == CONTENTS_SOLID)
{
trace->startsolid = TRUE;
}
else
{
trace->allsolid = FALSE;
if (num == CONTENTS_EMPTY)
{
trace->inopen = TRUE;
return TRUE;
}
if (num != CONTENTS_TRANSLUCENT)
{
trace->inwater = TRUE;
return TRUE;
}
}
return TRUE;
}
#else // REHLDS_OPT_PEDANTIC
// version with unrolled tail recursion
qboolean SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, const vec_t *p1, const vec_t *p2, trace_t *trace)
{
dclipnode_t *node;
mplane_t *plane;
float t2;
vec3_t mid;
float frac;
float t1;
signed int side;
float midf;
float pdif;
vec3_t custom_p1; // for holding custom p1 value
float DIST_EPSILON = 0.03125f;
while (num >= 0)
{
pdif = p2f - p1f;
if (num < hull->firstclipnode || num > hull->lastclipnode || !hull->planes)
Sys_Error(__FUNCTION__ ": bad node number");
node = &hull->clipnodes[num];
plane = &hull->planes[hull->clipnodes[num].planenum];
if (plane->type >= 3)
{
t1 = _DotProduct(p1, plane->normal) - plane->dist;
t2 = _DotProduct(p2, plane->normal) - plane->dist;
}
else
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
}
if (t1 >= 0.0f && t2 >= 0.0f)
{
num = node->children[0];
continue;
}
if (t1 >= 0.0f)
{
midf = t1 - DIST_EPSILON;
}
else
{
if (t2 < 0.0f)
{
num = node->children[1];
continue;
}
midf = t1 + DIST_EPSILON;
}
midf = midf / (t1 - t2);
if (midf >= 0.0f)
{
if (midf > 1.0f)
midf = 1.0f;
}
else
{
midf = 0.0f;
}
if (!IS_NAN(midf)) // not a number
{
frac = pdif * midf + p1f;
mid[0] = (p2[0] - p1[0]) * midf + p1[0];
mid[1] = (p2[1] - p1[1]) * midf + p1[1];
mid[2] = (p2[2] - p1[2]) * midf + p1[2];
side = (t1 < 0.0f) ? 1 : 0;
if (SV_RecursiveHullCheck(hull, node->children[side], p1f, frac, p1, mid, trace))
{
if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID)
{
num = node->children[side ^ 1];
p1f = frac;
p1 = custom_p1;
custom_p1[0] = mid[0];
custom_p1[1] = mid[1];
custom_p1[2] = mid[2];
continue;
}
if (!trace->allsolid)
{
if (side)
{
trace->plane.normal[0] = vec3_origin[0] - plane->normal[0];
trace->plane.normal[1] = vec3_origin[1] - plane->normal[1];
trace->plane.normal[2] = vec3_origin[2] - plane->normal[2];
trace->plane.dist = -plane->dist;
}
else
{
trace->plane.normal[0] = plane->normal[0];
trace->plane.normal[1] = plane->normal[1];
trace->plane.normal[2] = plane->normal[2];
trace->plane.dist = plane->dist;
}
while (1)
{
if (SV_HullPointContents(hull, hull->firstclipnode, mid) != CONTENTS_SOLID)
{
trace->fraction = frac;
trace->endpos[0] = mid[0];
trace->endpos[1] = mid[1];
trace->endpos[2] = mid[2];
return FALSE;
}
midf -= 0.1f;
if (midf < 0.0f)
break;
frac = pdif * midf + p1f;
mid[0] = (p2[0] - p1[1]) * midf + p1[0];
mid[1] = (p2[1] - p1[1]) * midf + p1[1];
mid[2] = (p2[2] - p1[2]) * midf + p1[2];
}
trace->fraction = frac;
trace->endpos[0] = mid[0];
trace->endpos[1] = mid[1];
trace->endpos[2] = mid[2];
Con_DPrintf("backup past 0\n");
return FALSE;
}
}
}
return FALSE;
}
if (num == CONTENTS_SOLID)
{
trace->startsolid = TRUE;
}
else
{
trace->allsolid = FALSE;
if (num == CONTENTS_EMPTY)
{
trace->inopen = TRUE;
return TRUE;
}
if (num != CONTENTS_TRANSLUCENT)
{
trace->inwater = TRUE;
return TRUE;
}
}
return TRUE;
}
#endif // REHLDS_OPT_PEDANTIC
/* <cadd3> ../engine/world.c:948 */
void SV_SingleClipMoveToEntity(edict_t *ent, const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end, trace_t *trace)
{
hull_t *hull;
trace_t testtrace;
vec3_t offset;
int rotated;
int closest;
vec3_t end_l;
vec3_t start_l;
int numhulls;
Q_memset(trace, 0, sizeof(trace_t));
trace->fraction = 1.0f;
trace->allsolid = TRUE;
trace->endpos[0] = end[0];
trace->endpos[1] = end[1];
trace->endpos[2] = end[2];
if (g_psv.models[ent->v.modelindex]->type == mod_studio)
{
hull = SV_HullForStudioModel(ent, mins, maxs, offset, &numhulls);
}
else
{
hull = SV_HullForEntity(ent, mins, maxs, offset);
numhulls = 1;
}
start_l[0] = start[0] - offset[0];
start_l[1] = start[1] - offset[1];
start_l[2] = start[2] - offset[2];
end_l[0] = end[0] - offset[0];
end_l[1] = end[1] - offset[1];
end_l[2] = end[2] - offset[2];
if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] != 0.0f || ent->v.angles[1] != 0.0f || ent->v.angles[2] != 0.0f))
{
vec3_t right;
vec3_t forward;
vec3_t up;
vec3_t temp;
AngleVectors(ent->v.angles, forward, right, up);
temp[0] = start_l[0]; temp[1] = start_l[1]; temp[2] = start_l[2];
start_l[0] = _DotProduct(forward, temp);
start_l[1] = -_DotProduct(right, temp);
start_l[2] = _DotProduct(up, temp);
temp[0] = end_l[0]; temp[1] = end_l[1]; temp[2] = end_l[2];
end_l[0] = _DotProduct(forward, temp);
end_l[1] = -_DotProduct(right, temp);
end_l[2] = _DotProduct(up, temp);
rotated = 1;
}
else
{
rotated = 0;
}
if (numhulls == 1)
{
SV_RecursiveHullCheck(hull, hull->firstclipnode, 0.0f, 1.0f, start_l, end_l, trace);
}
else
{
closest = 0;
for (int i = 0; i < numhulls; i++)
{
Q_memset(&testtrace, 0, sizeof(trace_t));
testtrace.endpos[0] = end[0];
testtrace.endpos[1] = end[1];
testtrace.endpos[2] = end[2];
testtrace.fraction = 1.0f;
testtrace.allsolid = TRUE;
SV_RecursiveHullCheck(&hull[i], hull[i].firstclipnode, 0.0f, 1.0f, start_l, end_l, &testtrace);
if (i == 0 || testtrace.allsolid || testtrace.startsolid || testtrace.fraction < trace->fraction)
{
int isSolid = trace->startsolid;
Q_memcpy(trace, &testtrace, sizeof(trace_t));
if (isSolid)
trace->startsolid = TRUE;
closest = i;
}
}
trace->hitgroup = SV_HitgroupForStudioHull(closest);
}
if (trace->fraction != 1.0f)
{
if (rotated)
{
vec3_t right;
vec3_t up;
vec3_t forward;
vec3_t temp;
AngleVectorsTranspose(ent->v.angles, forward, right, up);
temp[0] = trace->plane.normal[0];
temp[1] = trace->plane.normal[1];
temp[2] = trace->plane.normal[2];
trace->plane.normal[0] = _DotProduct(forward, temp);
trace->plane.normal[1] = _DotProduct(right, temp);
trace->plane.normal[2] = _DotProduct(up, temp);
}
trace->endpos[0] = (end[0] - start[0]) * trace->fraction + start[0];
trace->endpos[1] = (end[1] - start[1]) * trace->fraction + start[1];
trace->endpos[2] = (end[2] - start[2]) * trace->fraction + start[2];
}
if (trace->fraction < 1.0f || trace->startsolid)
trace->ent = ent;
}
/* <caf84> ../engine/world.c:1082 */
trace_t SV_ClipMoveToEntity(edict_t *ent, const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end)
{
trace_t goodtrace;
SV_SingleClipMoveToEntity(ent, start, mins, maxs, end, &goodtrace);
return goodtrace;
}
/* <cb027> ../engine/world.c:1148 */
void SV_ClipToLinks(areanode_t *node, moveclip_t *clip)
{
link_t *l;
link_t *next;
for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next)
{
next = l->next;
edict_t *touch = (edict_t *)((char *)l - offsetof(edict_t, area));
if (touch->v.groupinfo && clip->passedict && clip->passedict->v.groupinfo)
{
if (g_groupop)
{
if (g_groupop == GROUP_OP_NAND && (clip->passedict->v.groupinfo & touch->v.groupinfo))
continue;
}
else
{
if (!(clip->passedict->v.groupinfo & touch->v.groupinfo))
continue;
}
}
if (touch->v.solid == SOLID_NOT)
continue;
if (touch == clip->passedict)
continue;
if (touch->v.solid == SOLID_TRIGGER)
Sys_Error("Trigger in clipping list");
if (gNewDLLFunctions.pfnShouldCollide && !gNewDLLFunctions.pfnShouldCollide(touch, clip->passedict))
#ifdef REHLDS_FIXES
// https://github.com/dreamstalker/rehlds/issues/46
continue;
#else
return;
#endif
if (touch->v.solid == SOLID_BSP)
{
if ((touch->v.flags & FL_MONSTERCLIP) && !clip->monsterClipBrush)
continue;
}
else
{
if (clip->type == 1 && touch->v.movetype != MOVETYPE_PUSHSTEP)
continue;
}
if ((!clip->ignoretrans || !touch->v.rendermode || (touch->v.flags & FL_WORLDBRUSH))
&& clip->boxmins[0] <= touch->v.absmax[0]
&& clip->boxmins[1] <= touch->v.absmax[1]
&& clip->boxmins[2] <= touch->v.absmax[2]
&& clip->boxmaxs[0] >= touch->v.absmin[0]
&& clip->boxmaxs[1] >= touch->v.absmin[1]
&& clip->boxmaxs[2] >= touch->v.absmin[2]
#ifdef REHLDS_FIXES
&& ((touch->v.solid == SOLID_SLIDEBOX && sv_force_ent_intersection.string[0] == '0')
#else // REHLDS_FIXES
&& (touch->v.solid == SOLID_SLIDEBOX
#endif // REHLDS_FIXES
|| SV_CheckSphereIntersection(touch, clip->start, clip->end))
&& (!clip->passedict || clip->passedict->v.size[0] == 0.0f || touch->v.size[0] != 0.0f))
{
if (clip->trace.allsolid)
return;
if (clip->passedict && (touch->v.owner == clip->passedict || clip->passedict->v.owner == touch))
continue;
trace_t trace;
if (touch->v.flags & FL_MONSTER)
trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins2, clip->maxs2, clip->end);
else
trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end);
if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction)
{
int oldStartSolid = clip->trace.startsolid;
trace.ent = touch;
clip->trace = trace;
if (oldStartSolid)
clip->trace.startsolid = TRUE;
}
}
}
if (node->axis != -1)
{
if (clip->boxmaxs[node->axis] > node->dist)
SV_ClipToLinks(node->children[0], clip);
if (node->dist > clip->boxmins[node->axis])
SV_ClipToLinks(node->children[1], clip);
}
}
/* <cb142> ../engine/world.c:1267 */
void SV_ClipToWorldbrush(areanode_t *node, moveclip_t *clip)
{
link_t *l;
link_t *next;
for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next)
{
next = l->next;
edict_t *touch = (edict_t *)((char *)l - offsetof(edict_t, area));
if (touch->v.solid != SOLID_BSP)
continue;
if (!(touch->v.flags & FL_WORLDBRUSH))
continue;
if (clip->boxmins[0] <= touch->v.absmax[0]
&& clip->boxmins[1] <= touch->v.absmax[1]
&& clip->boxmins[2] <= touch->v.absmax[2]
&& clip->boxmaxs[0] >= touch->v.absmin[0]
&& clip->boxmaxs[1] >= touch->v.absmin[1]
&& clip->boxmaxs[2] >= touch->v.absmin[2])
{
if (clip->trace.allsolid)
return;
trace_t trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end);
if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction)
{
int oldSolid = clip->trace.startsolid;
trace.ent = touch;
clip->trace = trace;
if (oldSolid)
clip->trace.startsolid = TRUE;
}
}
}
if (node->axis != -1)
{
if (clip->boxmaxs[node->axis] > node->dist)
SV_ClipToWorldbrush(node->children[0], clip);
if (node->dist > clip->boxmins[node->axis])
SV_ClipToWorldbrush(node->children[1], clip);
}
}
/* <cb206> ../engine/world.c:1330 */
void SV_MoveBounds(const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end, vec_t *boxmins, vec_t *boxmaxs)
{
for (int i = 0; i < 3; i++)
{
if (end[i] > start[i])
{
boxmins[i] = start[i] + mins[i] - 1.0f;
boxmaxs[i] = end[i] + maxs[i] + 1.0f;
}
else
{
boxmins[i] = end[i] + mins[i] - 1.0f;
boxmaxs[i] = start[i] + maxs[i] + 1.0f;
}
}
}
/* <cb391> ../engine/world.c:1364 */
trace_t SV_MoveNoEnts(const vec_t *start, vec_t *mins, vec_t *maxs, const vec_t *end, int type, edict_t *passedict)
{
moveclip_t clip;
vec3_t worldEndPoint;
float worldFraction;
Q_memset(&clip, 0, sizeof(clip));
clip.trace = SV_ClipMoveToEntity(g_psv.edicts, start, mins, maxs, end);
if (clip.trace.fraction != 0.0f)
{
worldEndPoint[0] = clip.trace.endpos[0];
worldEndPoint[2] = clip.trace.endpos[2];
worldEndPoint[1] = clip.trace.endpos[1];
clip.end = worldEndPoint;
clip.ignoretrans = type >> 8;
worldFraction = clip.trace.fraction;
clip.type = type;
clip.passedict = passedict;
clip.mins2[0] = mins[0];
clip.mins2[1] = mins[1];
clip.mins2[2] = mins[2];
clip.maxs2[0] = maxs[0];
clip.maxs2[1] = maxs[1];
clip.maxs2[2] = maxs[2];
clip.trace.fraction = 1.0f;
clip.start = start;
clip.mins = mins;
clip.maxs = maxs;
clip.monsterClipBrush = FALSE;
SV_MoveBounds(start, clip.mins2, clip.maxs2, worldEndPoint, clip.boxmins, clip.boxmaxs);
SV_ClipToWorldbrush(sv_areanodes, &clip);
gGlobalVariables.trace_ent = clip.trace.ent;
clip.trace.fraction = worldFraction * clip.trace.fraction;
}
return clip.trace;
}
/* <cb47e> ../engine/world.c:1415 */
trace_t SV_Move(const vec_t *start, const vec_t *mins, const vec_t *maxs, const vec_t *end, int type, edict_t *passedict, qboolean monsterClipBrush)
{
moveclip_t clip;
vec3_t worldEndPoint;
float worldFraction;
Q_memset(&clip, 0, sizeof(clip));
clip.trace = SV_ClipMoveToEntity(g_psv.edicts, start, mins, maxs, end);
if (clip.trace.fraction != 0.0f)
{
worldEndPoint[0] = clip.trace.endpos[0];
worldEndPoint[1] = clip.trace.endpos[1];
worldEndPoint[2] = clip.trace.endpos[2];
clip.end = worldEndPoint;
worldFraction = clip.trace.fraction;
clip.type = type & 0xFF;
clip.ignoretrans = type >> 8;
clip.trace.fraction = 1.0f;
clip.start = start;
clip.mins = mins;
clip.maxs = maxs;
clip.passedict = passedict;
clip.monsterClipBrush = monsterClipBrush;
if (type == 2)
{
for (int i = 0; i < 3; i++)
{
clip.mins2[i] = -15.0f;
clip.maxs2[i] = +15.0f;
}
}
else
{
clip.mins2[0] = mins[0];
clip.mins2[1] = mins[1];
clip.mins2[2] = mins[2];
clip.maxs2[0] = maxs[0];
clip.maxs2[1] = maxs[1];
clip.maxs2[2] = maxs[2];
}
SV_MoveBounds(start, clip.mins2, clip.maxs2, worldEndPoint, clip.boxmins, clip.boxmaxs);
SV_ClipToLinks(sv_areanodes, &clip);
gGlobalVariables.trace_ent = clip.trace.ent;
clip.trace.fraction = worldFraction * clip.trace.fraction;
}
return clip.trace;
}
#ifdef REHLDS_OPT_PEDANTIC
// Optimized version of SV_Move routines for moving point hull throw world
void SV_SingleClipMoveToPoint(const vec_t *start, const vec_t *end, trace_t *trace)
{
hull_t *hull;
Q_memset(trace, 0, sizeof(trace_t));
trace->fraction = 1.0f;
trace->allsolid = TRUE;
trace->endpos[0] = end[0];
trace->endpos[1] = end[1];
trace->endpos[2] = end[2];
hull = &g_psv.models[1]->hulls[0]; // world point hull
SV_RecursiveHullCheck(hull, hull->firstclipnode, 0.0f, 1.0f, start, end, trace);
if (trace->fraction != 1.0f)
{
trace->endpos[0] = ( end[0] - start[0] ) * trace->fraction + start[0];
trace->endpos[1] = ( end[1] - start[1] ) * trace->fraction + start[1];
trace->endpos[2] = ( end[2] - start[2] ) * trace->fraction + start[2];
}
if (trace->fraction < 1.0f || trace->startsolid)
trace->ent = &g_psv.edicts[0];
}
void SV_MoveBounds_Point(const vec_t *start, const vec_t *end, vec_t *boxmins, vec_t *boxmaxs)
{
for (int i = 0; i < 3; i++)
{
if (end[i] > start[i])
{
boxmins[i] = start[i] - 1.0f;
boxmaxs[i] = end[i] + 1.0f;
}
else
{
boxmins[i] = end[i] - 1.0f;
boxmaxs[i] = start[i] + 1.0f;
}
}
}
trace_t SV_Move_Point(const vec_t *start, const vec_t *end, int type, edict_t *passedict)
{
moveclip_t clip;
vec3_t worldEndPoint;
float worldFraction;
Q_memset(&clip, 0, sizeof(clip));
SV_SingleClipMoveToPoint(start, end, &clip.trace);
if (clip.trace.fraction != 0.0f)
{
worldEndPoint[0] = clip.trace.endpos[0];
worldEndPoint[1] = clip.trace.endpos[1];
worldEndPoint[2] = clip.trace.endpos[2];
clip.end = worldEndPoint;
worldFraction = clip.trace.fraction;
clip.type = type & 0xFF;
clip.ignoretrans = type >> 8;
clip.trace.fraction = 1.0f;
clip.start = start;
clip.mins = vec3_origin;
clip.maxs = vec3_origin;
clip.passedict = passedict;
clip.monsterClipBrush = 0;
if (type == 2)
{
for (int i = 0; i < 3; i++)
{
clip.mins2[i] = -15.0f;
clip.maxs2[i] = +15.0f;
}
}
else
{
clip.mins2[0] = 0.0;
clip.mins2[1] = 0.0;
clip.mins2[2] = 0.0;
clip.maxs2[0] = 0.0;
clip.maxs2[1] = 0.0;
clip.maxs2[2] = 0.0;
}
SV_MoveBounds_Point(start, worldEndPoint, clip.boxmins, clip.boxmaxs);
SV_ClipToLinks(sv_areanodes, &clip);
gGlobalVariables.trace_ent = clip.trace.ent;
clip.trace.fraction = worldFraction * clip.trace.fraction;
}
return clip.trace;
}
#endif // REHLDS_OPT_PEDANTIC