Ejemplo n.º 1
0
/**
 * @brief Resolve the visibility data for the bounding box around the client. The
 * bounding box provides some leniency because the client's actual view origin
 * is likely slightly different than what we think it is.
 */
static void Sv_ClientVisibility(const vec3_t org, byte *pvs, byte *phs) {
	int32_t leafs[MAX_ENT_LEAFS];
	int32_t clusters[MAX_ENT_CLUSTERS];
	size_t num_clusters = 0;
	vec3_t mins, maxs;

	// spread the bounds to account for view offset
	for (int32_t i = 0; i < 3; i++) {
		mins[i] = org[i] - 16.0;
		maxs[i] = org[i] + 16.0;
	}

	const size_t len = Cm_BoxLeafnums(mins, maxs, leafs, lengthof(leafs), NULL, 0);
	if (len == 0) {
		Com_Error(ERROR_DROP, "Bad leaf count @ %s\n", vtos(org));
	}

	memset(pvs, 0, MAX_BSP_LEAFS >> 3);
	memset(phs, 0, MAX_BSP_LEAFS >> 3);

	// convert leafs to clusters and combine their visibility data
	for (size_t i = 0; i < len; i++) {

		const int32_t cluster = Cm_LeafCluster(leafs[i]);

		size_t j;
		for (j = 0; j < num_clusters; j++) {
			if (clusters[j] == cluster) {
				break;
			}
		}

		if (j < num_clusters) { // already got it
			continue;
		}

		clusters[num_clusters++] = cluster;

		byte cluster_pvs[MAX_BSP_LEAFS >> 3];
		byte cluster_phs[MAX_BSP_LEAFS >> 3];

		Cm_ClusterPVS(cluster, cluster_pvs);
		Cm_ClusterPHS(cluster, cluster_phs);

		for (size_t n = 0; n < sizeof(cluster_pvs); n++) {
			pvs[n] |= cluster_pvs[n];
			phs[n] |= cluster_phs[n];
		}

		if (num_clusters == lengthof(clusters)) {
			Com_Warn("MAX_ENT_CLUSTERS %s\n", vtos(org));
			break;
		}
	}
}
Ejemplo n.º 2
0
/*
 * Sv_LinkEdict
 *
 * Called whenever an entity changes origin, mins, maxs, or solid to add it to
 * the clipping hull.
 */
void Sv_LinkEdict(g_edict_t *ent) {
	sv_area_node_t *node;
	int leafs[MAX_TOTAL_ENT_LEAFS];
	int clusters[MAX_TOTAL_ENT_LEAFS];
	int num_leafs;
	int i, j, k;
	int area;
	int top_node;

	if (ent == svs.game->edicts) // never bother with the world
		return;

	if (ent->area.prev) // unlink from its previous area
		Sv_UnlinkEdict(ent);

	if (!ent->in_use) // and if its free, we're done
		return;

	// set the size
	VectorSubtract(ent->maxs, ent->mins, ent->size);

	// encode the size into the entity_state for client prediction
	if (ent->solid == SOLID_BOX) { // assume that x/y are equal and symetric
		i = ent->maxs[0] / 8;
		if (i < 1)
			i = 1;
		if (i > 31)
			i = 31;

		// z is not symmetric
		j = (-ent->mins[2]) / 8;
		if (j < 1)
			j = 1;
		if (j > 31)
			j = 31;

		// and z maxs can be negative...
		k = (ent->maxs[2] + 32) / 8;
		if (k < 1)
			k = 1;
		if (k > 63)
			k = 63;

		ent->s.solid = (k << 10) | (j << 5) | i;
	} else if (ent->solid == SOLID_BSP) {
		ent->s.solid = 31; // a solid_bbox will never create this value
	} else
		ent->s.solid = 0;

	// set the absolute bounding box
	if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1]
			|| ent->s.angles[2])) { // expand for rotation
		float max, v;
		int i;

		max = 0;
		for (i = 0; i < 3; i++) {
			v = fabsf(ent->mins[i]);
			if (v > max)
				max = v;
			v = fabsf(ent->maxs[i]);
			if (v > max)
				max = v;
		}
		for (i = 0; i < 3; i++) {
			ent->abs_mins[i] = ent->s.origin[i] - max;
			ent->abs_maxs[i] = ent->s.origin[i] + max;
		}
	} else { // normal
		VectorAdd(ent->s.origin, ent->mins, ent->abs_mins);
		VectorAdd(ent->s.origin, ent->maxs, ent->abs_maxs);
	}

	// because movement is clipped an epsilon away from an actual edge,
	// we must fully check even when bounding boxes don't quite touch
	ent->abs_mins[0] -= 1;
	ent->abs_mins[1] -= 1;
	ent->abs_mins[2] -= 1;
	ent->abs_maxs[0] += 1;
	ent->abs_maxs[1] += 1;
	ent->abs_maxs[2] += 1;

	// link to PVS leafs
	ent->num_clusters = 0;
	ent->area_num = 0;
	ent->area_num2 = 0;

	// get all leafs, including solids
	num_leafs = Cm_BoxLeafnums(ent->abs_mins, ent->abs_maxs, leafs,
			MAX_TOTAL_ENT_LEAFS, &top_node);

	// set areas
	for (i = 0; i < num_leafs; i++) {
		clusters[i] = Cm_LeafCluster(leafs[i]);
		area = Cm_LeafArea(leafs[i]);
		if (area) { // doors may legally occupy two areas,
			// but nothing should ever need more than that
			if (ent->area_num && ent->area_num != area) {
				if (ent->area_num2 && ent->area_num2 != area && sv.state
						== SV_LOADING) {
					Com_Debug("Object touching 3 areas at %f %f %f\n",
							ent->abs_mins[0], ent->abs_mins[1],
							ent->abs_mins[2]);
				}
				ent->area_num2 = area;
			} else
				ent->area_num = area;
		}
	}

	if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by head_node
		ent->num_clusters = -1;
		ent->head_node = top_node;
	} else {
		ent->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->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by head_node
					ent->num_clusters = -1;
					ent->head_node = top_node;
					break;
				}

				ent->cluster_nums[ent->num_clusters++] = clusters[i];
			}
		}
	}

	// if first time, make sure old_origin is valid
	if (!ent->link_count) {
		VectorCopy(ent->s.origin, ent->s.old_origin);
	}
	ent->link_count++;

	if (ent->solid == SOLID_NOT)
		return;

	// find the first node that the ent's box crosses
	node = sv_world.area_nodes;
	while (true) {

		if (node->axis == -1)
			break;

		if (ent->abs_mins[node->axis] > node->dist)
			node = node->children[0];
		else if (ent->abs_maxs[node->axis] < node->dist)
			node = node->children[1];
		else
			break; // crosses the node
	}

	// link it in
	if (ent->solid == SOLID_TRIGGER)
		Sv_InsertLink(&ent->area, &node->trigger_edicts);
	else
		Sv_InsertLink(&ent->area, &node->solid_edicts);
}
Ejemplo n.º 3
0
/**
 * @brief Called whenever an entity changes origin, mins, maxs, or solid to add it to
 * the clipping hull.
 */
void Sv_LinkEntity(g_entity_t *ent) {
	int32_t leafs[MAX_ENT_LEAFS];
	int32_t clusters[MAX_ENT_LEAFS];
	size_t i, j;
	int32_t top_node;

	if (ent == svs.game->entities) { // never bother with the world
		return;
	}

	// remove it from its current sector
	Sv_UnlinkEntity(ent);

	if (!ent->in_use) { // and if its free, we're done
		return;
	}

	// set the size
	VectorSubtract(ent->maxs, ent->mins, ent->size);

	// encode the size into the entity state for client prediction
	ent->s.solid = ent->solid;
	switch (ent->s.solid) {
		case SOLID_TRIGGER:
		case SOLID_PROJECTILE:
		case SOLID_DEAD:
		case SOLID_BOX:
			PackBounds(ent->mins, ent->maxs, &ent->s.bounds);
			break;
		default:
			PackBounds(vec3_origin, vec3_origin, &ent->s.bounds);
			break;
	}

	// set the absolute bounding box; ensure it is symmetrical
	Cm_EntityBounds(ent->solid, ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent->abs_mins, ent->abs_maxs);

	sv_entity_t *sent = &sv.entities[NUM_FOR_ENTITY(ent)];

	// link to PVS leafs
	sent->num_clusters = 0;
	sent->areas[0] = sent->areas[1] = 0;

	// get all leafs, including solids
	const size_t len = Cm_BoxLeafnums(ent->abs_mins, ent->abs_maxs, leafs, lengthof(leafs),
	                                  &top_node, 0);

	// set areas, allowing entities (doors) to occupy up to two
	for (i = 0; i < len; i++) {
		clusters[i] = Cm_LeafCluster(leafs[i]);
		const int32_t area = Cm_LeafArea(leafs[i]);
		if (area) {
			if (sent->areas[0] && sent->areas[0] != area) {
				if (sent->areas[1] && sent->areas[1] != area && sv.state == SV_LOADING) {
					Com_Warn("Object touching 3 areas at %s\n", vtos(ent->abs_mins));
				}
				sent->areas[1] = area;
			} else {
				sent->areas[0] = area;
			}
		}
	}

	if (len == MAX_ENT_LEAFS) { // use top_node
		sent->num_clusters = -1;
		sent->top_node = top_node;
	} else {
		sent->num_clusters = 0;
		for (i = 0; i < len; i++) {

			if (clusters[i] == -1) {
				continue;    // not a visible leaf
			}

			for (j = 0; j < i; j++)
				if (clusters[j] == clusters[i]) {
					break;
				}

			if (j == i) {
				if (sent->num_clusters == MAX_ENT_CLUSTERS) { // use top_node
					Com_Debug(DEBUG_SERVER, "%s exceeds MAX_ENT_CLUSTERS\n", etos(ent));
					sent->num_clusters = -1;
					sent->top_node = top_node;
					break;
				}

				sent->clusters[sent->num_clusters++] = clusters[i];
			}
		}
	}

	if (ent->solid == SOLID_NOT) {
		return;
	}

	// find the first sector that the ent's box crosses
	sv_sector_t *sector = sv_world.sectors;
	while (true) {

		if (sector->axis == -1) {
			break;
		}

		if (ent->abs_mins[sector->axis] > sector->dist) {
			sector = sector->children[0];
		} else if (ent->abs_maxs[sector->axis] < sector->dist) {
			sector = sector->children[1];
		} else {
			break;    // crosses the node
		}
	}

	// add it to the sector
	sent->sector = sector;
	sector->entities = g_list_prepend(sector->entities, ent);

	// and update its clipping matrices
	const vec_t *angles = ent->solid == SOLID_BSP ? ent->s.angles : vec3_origin;

	Matrix4x4_CreateFromEntity(&sent->matrix, ent->s.origin, angles, 1.0);
	Matrix4x4_Invert_Simple(&sent->inverse_matrix, &sent->matrix);
}