Add clustering
Using a DBSCAN-based algorithm, this gathers "player clusters" to determine the most packed area in the race.
This commit is contained in:
parent
27ea5021b4
commit
6788db93b4
3 changed files with 257 additions and 0 deletions
|
|
@ -122,6 +122,7 @@ k_bheap.c
|
|||
k_bot.cpp
|
||||
k_botitem.cpp
|
||||
k_botsearch.cpp
|
||||
k_cluster.cpp
|
||||
k_grandprix.c
|
||||
k_boss.c
|
||||
k_hud.c
|
||||
|
|
|
|||
238
src/k_cluster.cpp
Normal file
238
src/k_cluster.cpp
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "d_player.h"
|
||||
#include "m_fixed.h"
|
||||
#include "k_kart.h"
|
||||
#include "r_main.h"
|
||||
#include "g_game.h"
|
||||
|
||||
#include "k_cluster.hpp"
|
||||
#include <vector>
|
||||
|
||||
static fixed_t K_Distance3D(fixed_t x1, fixed_t y1, fixed_t z1, fixed_t x2, fixed_t y2, fixed_t z2)
|
||||
{
|
||||
fixed_t dist_xy = R_PointToDist2(x1,y1,x2,y2);
|
||||
|
||||
return R_PointToDist2(0,z1,dist_xy,z2);
|
||||
}
|
||||
|
||||
static fixed_t K_PlayerDistance3D(player_t *source, player_t *destination)
|
||||
{
|
||||
if ((!source->mo) || (!destination->mo))
|
||||
return INT32_MAX; // Return a garbage value.
|
||||
|
||||
return K_Distance3D(source->mo->x,source->mo->y,source->mo->z,destination->mo->x,destination->mo->y,destination->mo->z);
|
||||
}
|
||||
|
||||
static UINT32 K_GetDTFDifference(player_t *source, player_t *destination)
|
||||
{
|
||||
if ((!source->mo) || (!destination->mo))
|
||||
return INT16_MAX; // Return a garbage value.
|
||||
|
||||
if (destination->distancetofinish > source->distancetofinish)
|
||||
return (destination->distancetofinish - source->distancetofinish);
|
||||
|
||||
return (source->distancetofinish - destination->distancetofinish);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
player_t *closesttocluster;
|
||||
|
||||
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec)
|
||||
{
|
||||
INT64 meanx, meany, meanz;
|
||||
INT32 N;
|
||||
N = 0;
|
||||
vector3_t neighborvector;
|
||||
std::vector<player_t *>clusternodes;
|
||||
std::vector<player_t *> *realvec;
|
||||
|
||||
if (nodevec != nullptr)
|
||||
realvec = static_cast<std::vector<player_t *> *>(nodevec);
|
||||
|
||||
if (!sourcePlayer->mo)
|
||||
return 0;
|
||||
|
||||
meanx = meany = meanz = 0;
|
||||
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *player;
|
||||
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
player = &players[i];
|
||||
|
||||
if (player == sourcePlayer)
|
||||
continue; // Ignore ourselves.
|
||||
|
||||
if (clusterplayer[i])
|
||||
continue; // Ignore players we've scanned already.
|
||||
|
||||
if (player->spectator)
|
||||
continue; // spectator
|
||||
|
||||
if (!player->mo)
|
||||
continue;
|
||||
|
||||
// Scan all points in the database
|
||||
if (K_PlayerDistance3D(sourcePlayer, player) <= eps)
|
||||
{
|
||||
clusterplayer[i] = true;
|
||||
clusternodes.push_back(player);
|
||||
|
||||
if (realvec)
|
||||
realvec->push_back(player);
|
||||
|
||||
// Scan our neighbor for any players close to them.
|
||||
K_CountNeighboringPlayersByDistance(player, eps, &neighborvector, &clusternodes);
|
||||
}
|
||||
}
|
||||
|
||||
N = clusternodes.size();
|
||||
|
||||
if (N != 0)
|
||||
{
|
||||
// Return the average center point of this cluster.
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
if (!clusternodes[i])
|
||||
continue;
|
||||
|
||||
if (!clusternodes[i]->mo)
|
||||
continue;
|
||||
|
||||
meanx += clusternodes[i]->mo->x / FRACUNIT;
|
||||
meany += clusternodes[i]->mo->y / FRACUNIT;
|
||||
meanz += clusternodes[i]->mo->z / FRACUNIT;
|
||||
}
|
||||
|
||||
meanx /= N;
|
||||
meany /= N;
|
||||
meanz /= N;
|
||||
|
||||
//CONS_Printf("mean x: %lld, mean y: %lld, mean z: %lld\n", meanx, meany, meanz);
|
||||
|
||||
out->x = (fixed_t)(meanx) << FRACBITS;
|
||||
out->y = (fixed_t)(meany) << FRACBITS;
|
||||
out->z = (fixed_t)(meanz) << FRACBITS;
|
||||
|
||||
// Get the player closest to the cluster.
|
||||
fixed_t disttocluster, bestdist;
|
||||
bestdist = INT32_MAX;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
disttocluster = K_Distance3D(clusternodes[i]->mo->x,clusternodes[i]->mo->y,clusternodes[i]->mo->z,out->x,out->y,out->z);
|
||||
|
||||
if (disttocluster < bestdist)
|
||||
{
|
||||
closesttocluster = clusternodes[i];
|
||||
bestdist = disttocluster;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out->z = out->y = out->x = 0;
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
static std::vector<player_t *> dtf_vec;
|
||||
static boolean cleardtf = true;
|
||||
|
||||
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
|
||||
{
|
||||
INT64 mean;
|
||||
INT32 N = 0;
|
||||
vector3_t neighborvector;
|
||||
|
||||
if (!sourcePlayer->mo)
|
||||
return 0;
|
||||
|
||||
if (cleardtf)
|
||||
dtf_vec.clear();
|
||||
|
||||
mean = 0;
|
||||
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *player;
|
||||
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
player = &players[i];
|
||||
|
||||
if (player == sourcePlayer)
|
||||
continue; // Ignore ourselves.
|
||||
|
||||
if (clusterplayer[i])
|
||||
continue; // Ignore players we've scanned already.
|
||||
|
||||
if (player->spectator)
|
||||
continue; // spectator
|
||||
|
||||
if (!player->mo)
|
||||
continue;
|
||||
|
||||
// Scan all points in the database
|
||||
if ((fixed_t)K_GetDTFDifference(sourcePlayer, player) <= eps)
|
||||
{
|
||||
//CONS_Printf("%d is less than %d, adding to nodes vector\n", K_GetDTFDifference(sourcePlayer, player), eps);
|
||||
clusterplayer[i] = true;
|
||||
dtf_vec.push_back(player); // Add to result
|
||||
|
||||
cleardtf = false;
|
||||
|
||||
// Scan our neighbor for any players close to them.
|
||||
K_CountNeighboringPlayersByDTF(player, eps, &neighborvector);
|
||||
|
||||
cleardtf = true;
|
||||
}
|
||||
}
|
||||
|
||||
N = dtf_vec.size();
|
||||
|
||||
if (N != 0)
|
||||
{
|
||||
// Return the average center point of this cluster.
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
if (!dtf_vec[i])
|
||||
continue;
|
||||
|
||||
if (!dtf_vec[i]->mo)
|
||||
continue;
|
||||
|
||||
mean += dtf_vec[i]->distancetofinish;
|
||||
}
|
||||
mean /= N;
|
||||
|
||||
out->x = (fixed_t)(mean);
|
||||
out->y = 0;
|
||||
out->z = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
out->z = out->y = out->x = 0;
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
|
||||
{
|
||||
// Dummy vector so the game doesn't SIGSEGV.
|
||||
// There's probably a better solution to this.
|
||||
std::vector<player_t *> dummy = {0};
|
||||
|
||||
return K_CountNeighboringPlayersByDistance(sourcePlayer, eps, out, &dummy);
|
||||
}
|
||||
|
||||
}
|
||||
18
src/k_cluster.hpp
Normal file
18
src/k_cluster.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include "doomdef.h"
|
||||
#include "d_player.h"
|
||||
#include "m_fixed.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern player_t *closesttocluster;
|
||||
|
||||
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec);
|
||||
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out);
|
||||
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, vector3_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Loading…
Reference in a new issue