Lots of progress on octrees
This commit is contained in:
1260
luprex/core/cpp/bytell-hash-map.hpp
Normal file
1260
luprex/core/cpp/bytell-hash-map.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1496
luprex/core/cpp/flat-hash-map.hpp
Normal file
1496
luprex/core/cpp/flat-hash-map.hpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -76,21 +76,94 @@
|
||||
#include "wrap-vector.hpp"
|
||||
#include "wrap-map.hpp"
|
||||
#include "wrap-string.hpp"
|
||||
|
||||
#include "wrap-bytell-hash-map.hpp"
|
||||
#include "util.hpp"
|
||||
#include "luastack.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
class PlaneMap;
|
||||
class PlaneTree;
|
||||
|
||||
class PlaneItem : public eng::nevernew {
|
||||
class PlaneScan : public eng::nevernew {
|
||||
public:
|
||||
friend class PlaneMap;
|
||||
friend class PlaneTree;
|
||||
enum Shape { BOX, CYLINDER, SPHEROID };
|
||||
private:
|
||||
// The plane to scan.
|
||||
eng::string plane_;
|
||||
|
||||
// The bounding box of the scan.
|
||||
util::XYZ lo_, hi_, center_;
|
||||
|
||||
// If you scan a cylinder or spheroid, it actually
|
||||
// scans the bounding box first, then clips out
|
||||
// the parts that aren't correct.
|
||||
Shape shape_;
|
||||
|
||||
// When true, the items are sorted by ID.
|
||||
// WARNING: setting this to false can create
|
||||
// nondeterminism. Scans by lua should always be sorted.
|
||||
bool sorted_;
|
||||
|
||||
// The special ID, if nonzero, is either PREPENDED to the
|
||||
// results, or OMITTED from the results, depending on omit_special.
|
||||
int64_t special_;
|
||||
bool omit_special_;
|
||||
|
||||
// If this is true, items on the nowhere plane are not scanned.
|
||||
bool omit_nowhere_;
|
||||
|
||||
private:
|
||||
PlaneMap *pmap_;
|
||||
// Derived variables. These are populated by PlaneTree::scan.
|
||||
uint64_t bblo_;
|
||||
uint64_t bbhi_;
|
||||
|
||||
public:
|
||||
PlaneScan() : plane_(""), shape_(BOX), sorted_(true), special_(0), omit_special_(0), omit_nowhere_(false) {}
|
||||
|
||||
// Convert a lua table into a scan configuration.
|
||||
void configure(LuaStack &LS, LuaSlot slot);
|
||||
|
||||
// Set the bounding box given two corners.
|
||||
void set_bbox_given_two_corners(const util::XYZ &a, const util::XYZ &b) {
|
||||
lo_.x = std::min(a.x, b.x);
|
||||
lo_.y = std::min(a.y, b.y);
|
||||
lo_.z = std::min(a.z, b.z);
|
||||
hi_.x = std::max(a.x, b.x);
|
||||
hi_.y = std::max(a.y, b.y);
|
||||
hi_.z = std::max(a.z, b.z);
|
||||
center_ = (lo_ + hi_) * 0.5;
|
||||
}
|
||||
|
||||
// Set the bounding box given a center and a radius.
|
||||
void set_bbox_given_center_radius(const util::XYZ ¢er, float r) {
|
||||
util::XYZ offset(r, r, r);
|
||||
lo_ = center - offset;
|
||||
hi_ = center + offset;
|
||||
center_ = center;
|
||||
}
|
||||
|
||||
void set_plane(std::string_view p) { plane_ = p; }
|
||||
void set_shape(Shape s) { shape_ = s; }
|
||||
void set_sorted(bool s) { sorted_ = s; }
|
||||
void set_special(int64_t id, bool omit) { special_ = id; omit_special_ = omit; }
|
||||
void set_omit_nowhere(bool b) { omit_nowhere_ = b; }
|
||||
};
|
||||
|
||||
class PlaneItem : public eng::nevernew {
|
||||
friend class PlaneTree;
|
||||
friend class PlaneMap;
|
||||
private:
|
||||
PlaneTree *tree_;
|
||||
PlaneItem *prev_;
|
||||
PlaneItem *next_;
|
||||
int64_t id_;
|
||||
eng::string plane_;
|
||||
float x_, y_, z_;
|
||||
int64_t id_;
|
||||
|
||||
public:
|
||||
PlaneItem();
|
||||
@@ -105,43 +178,49 @@ public:
|
||||
const float y() const { return y_; }
|
||||
const float z() const { return z_; }
|
||||
|
||||
void untrack();
|
||||
void track(PlaneMap *pmap);
|
||||
void untrack() { track(nullptr); }
|
||||
|
||||
void set_pos(const eng::string &plane, float x, float y, float z);
|
||||
void set_xyz(float x, float y, float z) { set_pos(plane_, x, y, z); }
|
||||
void set_xyz(float x, float y, float z);
|
||||
};
|
||||
|
||||
class PlaneMap : public eng::nevernew {
|
||||
friend class PlaneItem;
|
||||
private:
|
||||
using EltVec = eng::vector<PlaneItem *>;
|
||||
using Plane = eng::map<int64_t, EltVec>;
|
||||
eng::map<eng::string, Plane> planes_;
|
||||
void remove(const eng::string &plane, int64_t cell, PlaneItem *client);
|
||||
void insert(const eng::string &plane, int64_t cell, PlaneItem *client);
|
||||
|
||||
friend class PlaneTree;
|
||||
public:
|
||||
using IdVector = util::IdVector;
|
||||
|
||||
private:
|
||||
float default_radius_;
|
||||
eng::map<eng::string, std::unique_ptr<PlaneTree>> planes_;
|
||||
|
||||
public:
|
||||
// No special code is needed for construction or destruction.
|
||||
PlaneMap();
|
||||
~PlaneMap();
|
||||
|
||||
// Caution: scan_radius is not deterministically ordered unless sort=true.
|
||||
//
|
||||
// exclude_nowhere - if true, and plane="nowhere", nothing is scanned.
|
||||
// special - an ID that is considered special. If zero, there is no special ID.
|
||||
// omit - if true, remove the special ID from the list.
|
||||
// if false, prepend the special ID to the head of the list.
|
||||
//
|
||||
IdVector scan_radius(const eng::string &plane, float x, float y, float radius, bool exclude_nowhere, int64_t special, bool omit) const;
|
||||
IdVector scan_radius_unsorted(const eng::string &plane, float x, float y, float radius, bool exclude_nowhere, int64_t special, bool omit) const;
|
||||
// The 'insert' and 'remove' operators are inside class
|
||||
// PlaneItem. See PlaneItem::track and PlaneItem::untrack.
|
||||
|
||||
// Scan the PlaneMap for items, return their IDs.
|
||||
IdVector scan(const PlaneScan &s) const;
|
||||
|
||||
// Set the default radius for all planes.
|
||||
// Maybe we'll make it adaptive some day.
|
||||
void set_default_radius(float r) { default_radius_ = r; }
|
||||
|
||||
// Return a debug string for the specified plane.
|
||||
// This is for unit testing.
|
||||
eng::string tree_debug_string(const eng::string &plane) const;
|
||||
eng::string outliers_debug_string(const eng::string &plane) const;
|
||||
|
||||
// Untrack all planeitems. This is for unit testing.
|
||||
void untrack_all();
|
||||
|
||||
private:
|
||||
// unit testing stuff.
|
||||
friend int lfn_unittests_planemap(lua_State *L);
|
||||
EltVec get_cell(const eng::string &plane, int64_t cell) const;
|
||||
int total_cells() const;
|
||||
void clear() { planes_.clear(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -262,6 +262,16 @@ eng::string hash_to_hex(const HashValue &hash);
|
||||
// This is a good hash, but not cryptographically good.
|
||||
uint64_t hash_ints(uint64_t n1, uint64_t n2, uint64_t n3, uint64_t n4);
|
||||
|
||||
// Hash a single 64-bit integer.
|
||||
// This is a good hash, but not cryptographically good.
|
||||
// Published by David Stafford in his article 'Better Bit Mixing'.
|
||||
inline uint64_t hash_int(uint64_t x) {
|
||||
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
|
||||
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
|
||||
x = x ^ (x >> 31);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Convert a 64-bit hash value into a floating point number between 0 and 1.
|
||||
double hash_to_double(uint64_t hash);
|
||||
|
||||
@@ -319,6 +329,9 @@ struct XYZ {
|
||||
XYZ(float ix, float iy, float iz) { x=ix; y=iy; z=iz; }
|
||||
bool operator ==(const XYZ &o) const { return x==o.x && y == o.y && z==o.z; }
|
||||
bool operator !=(const XYZ &o) const { return x!=o.x || y != o.y || z!=o.z; }
|
||||
XYZ operator -(const XYZ &o) const { return XYZ(x-o.x, y-o.y, z-o.z); }
|
||||
XYZ operator +(const XYZ &o) const { return XYZ(x+o.x, y+o.y, z+o.z); }
|
||||
XYZ operator *(float scale) const { return XYZ(x*scale, y*scale, z*scale); }
|
||||
eng::string debug_string() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -265,11 +265,17 @@ LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self",
|
||||
LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
Tangible *tan = w->tangible_get(LS, ltan);
|
||||
double radius = LS.cknumber(lradius);
|
||||
bool omit_nowhere = LS.ckboolean(lomit_nowhere);
|
||||
bool omit_self = LS.ckboolean(lomit_self);
|
||||
const AnimStep &aqback = tan->anim_queue_.back();
|
||||
util::IdVector idv = w->plane_map_.scan_radius(aqback.plane(), aqback.xyz().x, aqback.xyz().y, radius, omit_nowhere, tan->id(), omit_self);
|
||||
|
||||
PlaneScan scan;
|
||||
scan.set_plane(aqback.plane());
|
||||
scan.set_bbox_given_center_radius(aqback.xyz(), LS.cknumber(lradius));
|
||||
scan.set_shape(PlaneScan::SPHEROID);
|
||||
scan.set_sorted(true);
|
||||
scan.set_special(tan->id(), LS.ckboolean(lomit_self));
|
||||
scan.set_omit_nowhere(LS.ckboolean(lomit_nowhere));
|
||||
|
||||
util::IdVector idv = w->plane_map_.scan(scan);
|
||||
tangible_getall(LS, list, idv);
|
||||
return LS.result();
|
||||
}
|
||||
@@ -282,12 +288,15 @@ LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
|
||||
LuaRet list;
|
||||
LuaStack LS(L, lplane, lx, ly, lradius, lomit_nowhere, list);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
eng::string plane = LS.ckstring(lplane);
|
||||
double x = LS.cknumber(lx);
|
||||
double y = LS.cknumber(ly);
|
||||
double radius = LS.cknumber(lradius);
|
||||
bool omit_nowhere = LS.ckboolean(lomit_nowhere);
|
||||
util::IdVector idv = w->plane_map_.scan_radius(plane, x, y, radius, omit_nowhere, 0, false);
|
||||
|
||||
PlaneScan scan;
|
||||
scan.set_plane(LS.ckstring(lplane));
|
||||
scan.set_bbox_given_center_radius(util::XYZ(LS.cknumber(lx), LS.cknumber(ly), 0), LS.cknumber(lradius));
|
||||
scan.set_shape(PlaneScan::SPHEROID);
|
||||
scan.set_sorted(true);
|
||||
scan.set_omit_nowhere(LS.ckboolean(lomit_nowhere));
|
||||
|
||||
util::IdVector idv = w->plane_map_.scan(scan);
|
||||
tangible_getall(LS, list, idv);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -220,26 +220,23 @@ void World::tangible_delete(int64_t id) {
|
||||
LS.result();
|
||||
}
|
||||
|
||||
util::IdVector World::get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const {
|
||||
util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted) const {
|
||||
const Tangible *player = tangible_get(player_id);
|
||||
if (player == nullptr) {
|
||||
return IdVector();
|
||||
}
|
||||
|
||||
// Find out where's the center of the world.
|
||||
// Find out where the player is.
|
||||
const AnimStep &aqback = player->anim_queue_.back();
|
||||
return plane_map_.scan_radius_unsorted(aqback.plane(), aqback.xyz().x, aqback.xyz().y, radius, exclude_nowhere, player_id, omit_player);
|
||||
}
|
||||
|
||||
util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const {
|
||||
const Tangible *player = tangible_get(player_id);
|
||||
if (player == nullptr) {
|
||||
return IdVector();
|
||||
}
|
||||
|
||||
// Find out where's the center of the world.
|
||||
const AnimStep &aqback = player->anim_queue_.back();
|
||||
return plane_map_.scan_radius(aqback.plane(), aqback.xyz().x, aqback.xyz().y, radius, exclude_nowhere, player_id, omit_player);
|
||||
PlaneScan scan;
|
||||
scan.set_plane(aqback.plane());
|
||||
scan.set_bbox_given_center_radius(aqback.xyz(), radius);
|
||||
scan.set_shape(PlaneScan::SPHEROID);
|
||||
scan.set_sorted(sorted);
|
||||
scan.set_omit_nowhere(exclude_nowhere);
|
||||
scan.set_special(player_id, omit_player);
|
||||
return plane_map_.scan(scan);
|
||||
}
|
||||
|
||||
World::Redirects World::fetch_redirects() {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
|
||||
return util::sort_union_id_vectors(
|
||||
master->get_near_unsorted(actor_id, RadiusVisibility, true, false),
|
||||
get_near_unsorted(actor_id, RadiusVisibility, true, false));
|
||||
master->get_near(actor_id, RadiusVisibility, true, false, false),
|
||||
get_near(actor_id, RadiusVisibility, true, false, false));
|
||||
}
|
||||
|
||||
int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
@@ -169,7 +169,7 @@ void World::patch_luatabs(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
int64_t actor_id = sb->read_int64();
|
||||
util::HashValue closehash = sb->read_hashvalue();
|
||||
int ncreate = sb->read_int32();
|
||||
util::IdVector closetans = get_near(actor_id, RadiusClose, true, false);
|
||||
util::IdVector closetans = get_near(actor_id, RadiusClose, true, false, true);
|
||||
assert(closehash == util::hash_id_vector(closetans));
|
||||
number_lua_tables(closetans);
|
||||
create_new_tables(ncreate);
|
||||
@@ -183,8 +183,8 @@ void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) {
|
||||
StreamBuffer tsb;
|
||||
|
||||
// Calculate the set of close tangibles.
|
||||
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false);
|
||||
assert(get_near(actor_id, RadiusClose, true, false) == closetans);
|
||||
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false, true);
|
||||
assert(get_near(actor_id, RadiusClose, true, false, true) == closetans);
|
||||
util::HashValue closehash = util::hash_id_vector(closetans);
|
||||
|
||||
// Number and pair tables in the synchronous and master model.
|
||||
@@ -250,7 +250,7 @@ void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) {
|
||||
|
||||
// Calculate the set of close tangibles.
|
||||
// TODO: we've already calculated this in an earlier function. This is wasteful.
|
||||
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false);
|
||||
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false, true);
|
||||
|
||||
tsb.write_int32(0);
|
||||
int write_count_after = tsb.total_writes();
|
||||
|
||||
@@ -39,7 +39,7 @@ eng::string World::tangible_ids_debug_string() const {
|
||||
|
||||
eng::string World::tangibles_near_debug_string(int64_t actor, int64_t distance) {
|
||||
eng::ostringstream result;
|
||||
for (int64_t id : get_near(actor, distance, true, false)) {
|
||||
for (int64_t id : get_near(actor, distance, true, false, true)) {
|
||||
const Tangible *tan = tangible_get(id);
|
||||
const AnimStep &aqback = tan->anim_queue_.back();
|
||||
result << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl;
|
||||
|
||||
@@ -122,10 +122,10 @@ public:
|
||||
//
|
||||
// Get a list of the tangibles that are near the player. If 'exclude_nowhere' is
|
||||
// true, exclude any tangibles on the nowhere plane (but still include the player himself).
|
||||
// The unsorted version returns the tangibles in an unpredictable order.
|
||||
// The unsorted version returns the tangibles in an unpredictable order. If sorted
|
||||
// is false, return them in an unpredictable order.
|
||||
//
|
||||
IdVector get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const;
|
||||
IdVector get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const;
|
||||
IdVector get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted) const;
|
||||
|
||||
// Make a tangible.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user