Lots of progress on octrees

This commit is contained in:
2022-07-11 02:32:12 -04:00
parent 227de3666d
commit d54d6e4433
11 changed files with 3691 additions and 309 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -76,21 +76,94 @@
#include "wrap-vector.hpp" #include "wrap-vector.hpp"
#include "wrap-map.hpp" #include "wrap-map.hpp"
#include "wrap-string.hpp" #include "wrap-string.hpp"
#include "wrap-bytell-hash-map.hpp"
#include "util.hpp" #include "util.hpp"
#include "luastack.hpp"
#include <cstdint> #include <cstdint>
#include <memory>
#include <string_view>
class PlaneMap; class PlaneMap;
class PlaneTree;
class PlaneItem : public eng::nevernew { class PlaneScan : public eng::nevernew {
public:
friend class PlaneMap; 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: 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 &center, 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_; eng::string plane_;
float x_, y_, z_; float x_, y_, z_;
int64_t id_;
public: public:
PlaneItem(); PlaneItem();
@@ -105,43 +178,49 @@ public:
const float y() const { return y_; } const float y() const { return y_; }
const float z() const { return z_; } const float z() const { return z_; }
void untrack();
void track(PlaneMap *pmap); void track(PlaneMap *pmap);
void untrack() { track(nullptr); }
void set_pos(const eng::string &plane, float x, float y, float z); 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 { class PlaneMap : public eng::nevernew {
friend class PlaneItem; friend class PlaneItem;
private: friend class PlaneTree;
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);
public: public:
using IdVector = util::IdVector; 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();
~PlaneMap(); ~PlaneMap();
// Caution: scan_radius is not deterministically ordered unless sort=true. // The 'insert' and 'remove' operators are inside class
// // PlaneItem. See PlaneItem::track and PlaneItem::untrack.
// 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;
// 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: private:
// unit testing stuff. // unit testing stuff.
friend int lfn_unittests_planemap(lua_State *L); 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(); }
}; };

View File

@@ -262,6 +262,16 @@ eng::string hash_to_hex(const HashValue &hash);
// This is a good hash, but not cryptographically good. // 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); 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. // Convert a 64-bit hash value into a floating point number between 0 and 1.
double hash_to_double(uint64_t hash); 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; } 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; }
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; eng::string debug_string() const;
}; };

View File

@@ -265,11 +265,17 @@ LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self",
LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list); LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list);
World *w = World::fetch_global_pointer(L); World *w = World::fetch_global_pointer(L);
Tangible *tan = w->tangible_get(LS, ltan); 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(); 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); tangible_getall(LS, list, idv);
return LS.result(); return LS.result();
} }
@@ -282,12 +288,15 @@ LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere",
LuaRet list; LuaRet list;
LuaStack LS(L, lplane, lx, ly, lradius, lomit_nowhere, list); LuaStack LS(L, lplane, lx, ly, lradius, lomit_nowhere, list);
World *w = World::fetch_global_pointer(L); World *w = World::fetch_global_pointer(L);
eng::string plane = LS.ckstring(lplane);
double x = LS.cknumber(lx); PlaneScan scan;
double y = LS.cknumber(ly); scan.set_plane(LS.ckstring(lplane));
double radius = LS.cknumber(lradius); scan.set_bbox_given_center_radius(util::XYZ(LS.cknumber(lx), LS.cknumber(ly), 0), LS.cknumber(lradius));
bool omit_nowhere = LS.ckboolean(lomit_nowhere); scan.set_shape(PlaneScan::SPHEROID);
util::IdVector idv = w->plane_map_.scan_radius(plane, x, y, radius, omit_nowhere, 0, false); 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); tangible_getall(LS, list, idv);
return LS.result(); return LS.result();
} }

View File

@@ -220,26 +220,23 @@ void World::tangible_delete(int64_t id) {
LS.result(); 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); const Tangible *player = tangible_get(player_id);
if (player == nullptr) { if (player == nullptr) {
return IdVector(); return IdVector();
} }
// Find out where's the center of the world. // Find out where the player is.
const AnimStep &aqback = player->anim_queue_.back(); 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 { PlaneScan scan;
const Tangible *player = tangible_get(player_id); scan.set_plane(aqback.plane());
if (player == nullptr) { scan.set_bbox_given_center_radius(aqback.xyz(), radius);
return IdVector(); scan.set_shape(PlaneScan::SPHEROID);
} scan.set_sorted(sorted);
scan.set_omit_nowhere(exclude_nowhere);
// Find out where's the center of the world. scan.set_special(player_id, omit_player);
const AnimStep &aqback = player->anim_queue_.back(); return plane_map_.scan(scan);
return plane_map_.scan_radius(aqback.plane(), aqback.xyz().x, aqback.xyz().y, radius, exclude_nowhere, player_id, omit_player);
} }
World::Redirects World::fetch_redirects() { World::Redirects World::fetch_redirects() {

View File

@@ -3,8 +3,8 @@
util::IdVector World::get_visible_union(int64_t actor_id, World *master) { util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
return util::sort_union_id_vectors( return util::sort_union_id_vectors(
master->get_near_unsorted(actor_id, RadiusVisibility, true, false), master->get_near(actor_id, RadiusVisibility, true, false, false),
get_near_unsorted(actor_id, RadiusVisibility, true, false)); get_near(actor_id, RadiusVisibility, true, false, false));
} }
int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) { 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(); int64_t actor_id = sb->read_int64();
util::HashValue closehash = sb->read_hashvalue(); util::HashValue closehash = sb->read_hashvalue();
int ncreate = sb->read_int32(); 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)); assert(closehash == util::hash_id_vector(closetans));
number_lua_tables(closetans); number_lua_tables(closetans);
create_new_tables(ncreate); create_new_tables(ncreate);
@@ -183,8 +183,8 @@ void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) {
StreamBuffer tsb; StreamBuffer tsb;
// Calculate the set of close tangibles. // Calculate the set of close tangibles.
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false); util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false, true);
assert(get_near(actor_id, RadiusClose, true, false) == closetans); assert(get_near(actor_id, RadiusClose, true, false, true) == closetans);
util::HashValue closehash = util::hash_id_vector(closetans); util::HashValue closehash = util::hash_id_vector(closetans);
// Number and pair tables in the synchronous and master model. // 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. // Calculate the set of close tangibles.
// TODO: we've already calculated this in an earlier function. This is wasteful. // 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); tsb.write_int32(0);
int write_count_after = tsb.total_writes(); int write_count_after = tsb.total_writes();

View File

@@ -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::string World::tangibles_near_debug_string(int64_t actor, int64_t distance) {
eng::ostringstream result; 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 Tangible *tan = tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back(); const AnimStep &aqback = tan->anim_queue_.back();
result << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl; result << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl;

View File

@@ -122,10 +122,10 @@ public:
// //
// Get a list of the tangibles that are near the player. If 'exclude_nowhere' is // 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). // 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(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted) const;
IdVector get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const;
// Make a tangible. // Make a tangible.
// //

View File

@@ -0,0 +1,14 @@
#ifndef WRAP_BYTELL_HASH_MAP_HPP
#define WRAP_BYTELL_HASH_MAP_HPP
#include "eng-malloc.hpp"
#include "bytell-hash-map.hpp"
namespace eng {
template<class K, class V, class H=std::hash<K>, class E=std::equal_to<K>>
class bytell_hash_map : public ska::bytell_hash_map<K, V, H, E, eng::allocator<std::pair<const K, V>>>, public eng::opnew {
using ska::bytell_hash_map<K, V, H, E, eng::allocator<std::pair<const K, V>>>::bytell_hash_map;
};
} // namespace eng
#endif // WRAP_BYTELL_HASH_MAP_HPP