diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b0cda0..1871ec2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,6 +107,7 @@ if(BUILD_CLIENT) src/lua_bindings/misc.cpp src/lua_bindings/cereal.cpp src/lua_bindings/voxel.cpp + src/lua_bindings/spatial_update_queue.cpp ) add_executable(${CLIENT_EXE_NAME} ${CLIENT_SRCS}) diff --git a/builtin/voxelworld/client_lua/module.lua b/builtin/voxelworld/client_lua/module.lua index 31e645a..9ab7cd6 100644 --- a/builtin/voxelworld/client_lua/module.lua +++ b/builtin/voxelworld/client_lua/module.lua @@ -213,14 +213,6 @@ local function SpatialUpdateQueue() near_weight=near_weight, near_trigger_d=near_trigger_d, far_weight=far_weight, far_trigger_d=far_trigger_d}) end, - -- Put something to be prioritized more the closer it is - put_near_priority = function(self, p, trigger_d, weight, value) - self:put(p, weight, trigger_d, nil, nil, value) - end, - -- Put something to be prioritized more the farther it is - put_far_priority = function(self, p, trigger_d, weight, value) - self:put(p, nil, nil, weight, trigger_d, value) - end, get = function(self) local item = table.remove(self.queue) if not item then return nil end @@ -241,6 +233,9 @@ local function SpatialUpdateQueue() if not item then return nil end return item.fw end, + get_length = function(self) + return #self.queue + end, } return self end @@ -268,7 +263,8 @@ function M.init() M.chunk_size_voxels:mul_components(M.section_size_chunks) end) - local node_update_queue = SpatialUpdateQueue() + --local node_update_queue = SpatialUpdateQueue() + local node_update_queue = buildat.SpatialUpdateQueue() local function update_voxel_geometry(node) local data = node:GetVar("buildat_voxel_data"):GetBuffer() @@ -381,7 +377,7 @@ function M.init() local node_update = node_update_queue:get() local node = replicate.main_scene:GetNode(node_update.node_id) log:verbose("Node update #".. - #node_update_queue.queue.. + node_update_queue:get_length().. " (f="..(math.floor(f*100)/100).."".. ", fw="..(math.floor(fw*100)/100)..")".. ": "..node:GetName()) @@ -394,10 +390,10 @@ function M.init() did_update = true else log:verbose("Poked update #".. - #node_update_queue.queue.. + node_update_queue:get_length().. " (f="..(math.floor((f or -1)*100)/100).."".. ", fw="..(math.floor((fw or -1)*100)/100)..")") - node_update_queue.queue = {} + --node_update_queue.queue = {} -- For testing end -- Check this at the end of the loop so at least one is handled if not did_update or buildat.get_time_us() >= stop_at_us then diff --git a/client/api.lua b/client/api.lua index 0dc3609..0fe53f5 100644 --- a/client/api.lua +++ b/client/api.lua @@ -6,10 +6,53 @@ local log = buildat.Logger("__client/api") buildat.connect_server = __buildat_connect_server buildat.extension_path = __buildat_extension_path buildat.get_time_us = __buildat_get_time_us +buildat.SpatialUpdateQueue = __buildat_SpatialUpdateQueue buildat.safe.disconnect = __buildat_disconnect buildat.safe.get_time_us = __buildat_get_time_us +buildat.safe.SpatialUpdateQueue = function() + local internal = __buildat_SpatialUpdateQueue() + return { + update = function(self, ...) + return internal:update(...) + end, + set_p = function(self, ...) + return internal:set_p(...) + end, + put = function(self, safe_p, near_weight, near_trigger_d, + far_weight, far_trigger_d, value) + if not getmetatable(safe_p) or + getmetatable(safe_p).type_name ~= "Vector3" then + error("p is not a sandboxed Vector3 instance") + end + p = getmetatable(safe_p).unsafe + return internal:put(p, near_weight, near_trigger_d, + far_weight, far_trigger_d, value) + end, + get = function(self, ...) + return internal:get(...) + end, + peek_next_f = function(self, ...) + return internal:peek_next_f(...) + end, + peek_next_fw = function(self, ...) + return internal:peek_next_fw(...) + end, + get_length = function(self, ...) + return internal:get_length(...) + end, + set_p = function(self, safe_p) + if not getmetatable(safe_p) or + getmetatable(safe_p).type_name ~= "Vector3" then + error("p is not a sandboxed Vector3 instance") + end + p = getmetatable(safe_p).unsafe + internal:set_p(p) + end, + } +end + function buildat.safe.set_simple_voxel_model(safe_node, w, h, d, safe_buffer) if not getmetatable(safe_node) or getmetatable(safe_node).type_name ~= "Node" then diff --git a/src/lua_bindings/init.cpp b/src/lua_bindings/init.cpp index d1ef21a..6924edc 100644 --- a/src/lua_bindings/init.cpp +++ b/src/lua_bindings/init.cpp @@ -10,12 +10,14 @@ namespace lua_bindings { extern void init_misc(lua_State *L); extern void init_cereal(lua_State *L); extern void init_voxel(lua_State *L); +extern void init_spatial_update_queue(lua_State *L); void init(lua_State *L) { init_misc(L); init_cereal(L); init_voxel(L); + init_spatial_update_queue(L); } } // namespace lua_bindingss diff --git a/src/lua_bindings/spatial_update_queue.cpp b/src/lua_bindings/spatial_update_queue.cpp new file mode 100644 index 0000000..e5b1cdc --- /dev/null +++ b/src/lua_bindings/spatial_update_queue.cpp @@ -0,0 +1,326 @@ +// http://www.apache.org/licenses/LICENSE-2.0 +// Copyright 2014 Perttu Ahola +#include "lua_bindings/util.h" +#include "core/log.h" +#include +#include +#include +#include +#define MODULE "lua_bindings" + +#define DEF_METHOD(name){\ + lua_pushcfunction(L, l_##name);\ + lua_setfield(L, -2, #name);\ +} + +#define GET_TOLUA_STUFF(result_name, index, type)\ + if(!tolua_isusertype(L, index, #type, 0, &tolua_err)){\ + tolua_error(L, __PRETTY_FUNCTION__, &tolua_err);\ + return 0;\ + }\ + type *result_name = (type*)tolua_tousertype(L, index, 0); +#define TRY_GET_TOLUA_STUFF(result_name, index, type)\ + type *result_name = nullptr;\ + if(tolua_isusertype(L, index, #type, 0, &tolua_err)){\ + result_name = (type*)tolua_tousertype(L, index, 0);\ + } + +// Just do this; Urho3D's stuff doesn't really clash with anything in buildat +using namespace Urho3D; + +namespace lua_bindings { + +struct SpatialUpdateQueue +{ + struct Value { // Describes an update + ss_ type; + uint32_t node_id = -1; + }; + + struct Item { // Queue item + Vector3 p = Vector3(0, 0, 0); + Value value; + float near_weight = -1.0f; + float near_trigger_d = -1.0f; + float far_weight = -1.0f; + float far_trigger_d = -1.0f; + float f = -1.0f; + float fw = -1.0f; + + bool operator>(const Item &other) const; + }; + + Vector3 m_p; + Vector3 m_queue_oldest_p; + size_t m_queue_length = 0; // GCC std::list's size() is O(n) + std::list m_queue; + std::list m_old_queue; + + void update(int max_operations) + { + if(m_old_queue.empty()) + return; + log_d(MODULE, "SpatialUpdateQueue(): Items in old queue: %zu", + m_old_queue.size()); + for(int i=0; i 20){ + m_queue_length = 0; + m_old_queue.swap(m_queue); + m_queue_oldest_p = m_p; + } + } + + void put_item(Item &item) + { + if(item.near_trigger_d == -1.0f && item.far_trigger_d == -1.0f) + throw Exception("Item has neither trigger"); + float d = (item.p - m_p).Length(); + item.f = -1.0f; + item.fw = -1.0f; + if(item.near_trigger_d != -1.0f){ + float f_near = d / item.near_trigger_d; + float fw_near = f_near / item.near_weight; + if(item.fw == -1.0f || (fw_near < item.fw && + (item.f == -1.0f || f_near < item.f))){ + item.f = f_near; + item.fw = fw_near; + } + } + if(item.far_trigger_d != -1.0f){ + float f_far = item.far_trigger_d / d; + float fw_far = f_far / item.far_weight; + if(item.fw == -1.0f || (fw_far < item.fw && + (item.f == -1.0f || f_far < item.f))){ + item.f = f_far; + item.fw = fw_far; + } + } + if(item.f == -1.0f || item.fw == -1.0f) + throw Exception("item.f == -1.0f || item.fw == -1.0f"); + + auto it = std::lower_bound(m_queue.begin(), m_queue.end(), + item, std::greater()); // position in descending order + m_queue.insert(it, item); + m_queue_length++; + } + + void put(const Vector3 &p, float near_weight, float near_trigger_d, + float far_weight, float far_trigger_d, const Value &value) + { + Item item; + item.p = p; + item.near_weight = near_weight; + item.near_trigger_d = near_trigger_d; + item.far_weight = far_weight; + item.far_trigger_d = far_trigger_d; + item.value = value; + put_item(item); + } + + bool empty() + { + return m_queue.empty(); + } + + void pop() + { + m_queue.pop_back(); + m_queue_length--; + } + + Value& get_value() + { + if(m_queue.empty()) + throw Exception("SpatialUpdateQueue::get_value(): Empty"); + return m_queue.back().value; + } + + float get_f() + { + if(m_queue.empty()) + throw Exception("SpatialUpdateQueue::get_f(): Empty"); + return m_queue.back().f; + } + + float get_fw() + { + if(m_queue.empty()) + throw Exception("SpatialUpdateQueue::get_fw(): Empty"); + return m_queue.back().fw; + } + + size_t get_length() + { + return m_queue_length; + } +}; + +bool SpatialUpdateQueue::Item::operator>(const Item &other) const +{ + if(f > 1.0f && other.f <= 1.0f) + return true; + if(f <= 1.0f && other.f > 1.0f) + return false; + return fw > other.fw; +} + +struct LuaSUQ +{ + static constexpr const char *class_name = "SpatialUpdateQueue"; + SpatialUpdateQueue internal; + + static int gc_object(lua_State *L){ + delete *(LuaSUQ**)(lua_touserdata(L, 1)); + return 0; + } + static int l_update(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + int max_operations = luaL_checkinteger(L, 2); + o->internal.update(max_operations); + return 0; + } + static int l_set_p(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + tolua_Error tolua_err; + GET_TOLUA_STUFF(p, 2, Vector3); + o->internal.set_p(*p); + return 0; + } + static int l_put(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + tolua_Error tolua_err; + GET_TOLUA_STUFF(p, 2, Vector3); + float near_weight = (lua_type(L, 3) == LUA_TNIL) ? + -1.0f : luaL_checknumber(L, 3); + float near_trigger_d = (lua_type(L, 4) == LUA_TNIL) ? + -1.0f : luaL_checknumber(L, 4); + float far_weight = (lua_type(L, 5) == LUA_TNIL) ? + -1.0f : luaL_checknumber(L, 5); + float far_trigger_d = (lua_type(L, 6) == LUA_TNIL) ? + -1.0f : luaL_checknumber(L, 6); + luaL_checktype(L, 7, LUA_TTABLE); + SpatialUpdateQueue::Value value; + lua_getfield(L, -1, "type"); + value.type = luaL_checkstring(L, -1); + lua_pop(L, 1); + lua_getfield(L, -1, "node_id"); + value.node_id = luaL_checkinteger(L, -1); + lua_pop(L, 1); + o->internal.put(*p, near_weight, near_trigger_d, + far_weight, far_trigger_d, value); + return 0; + } + static int l_get(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + if(o->internal.empty()) + return 0; + SpatialUpdateQueue::Value &value = o->internal.get_value(); + lua_newtable(L); + lua_pushstring(L, value.type.c_str()); + lua_setfield(L, -2, "type"); + lua_pushinteger(L, value.node_id); + lua_setfield(L, -2, "node_id"); + o->internal.pop(); + return 1; + } + static int l_peek_next_f(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + if(o->internal.empty()) + return 0; + float v = o->internal.get_f(); + lua_pushnumber(L, v); + return 1; + } + static int l_peek_next_fw(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + if(o->internal.empty()) + return 0; + float v = o->internal.get_fw(); + lua_pushnumber(L, v); + return 1; + } + static int l_get_length(lua_State *L){ + LuaSUQ *o = internal_checkobject(L, 1); + int l = o->internal.get_length(); + lua_pushinteger(L, l); + return 1; + } + + static LuaSUQ* internal_checkobject(lua_State *L, int narg){ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, class_name); + if(!ud) luaL_typerror(L, narg, class_name); + return *(LuaSUQ**)ud; + } + static int l_create(lua_State *L){ + LuaSUQ *o = new LuaSUQ(); + *(void**)(lua_newuserdata(L, sizeof(void*))) = o; + luaL_getmetatable(L, class_name); + lua_setmetatable(L, -2); + return 1; + } + static void register_metatable(lua_State *L){ + lua_newtable(L); + int method_table_L = lua_gettop(L); + luaL_newmetatable(L, class_name); + int metatable_L = lua_gettop(L); + + // hide metatable from Lua getmetatable() + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, method_table_L); + lua_settable(L, metatable_L); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, method_table_L); + lua_settable(L, metatable_L); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable_L); + + lua_pop(L, 1); // drop metatable_L + + // fill method_table_L + DEF_METHOD(update); + DEF_METHOD(set_p); + DEF_METHOD(put); + DEF_METHOD(get); + DEF_METHOD(peek_next_f); + DEF_METHOD(peek_next_fw); + DEF_METHOD(get_length); + + // drop method_table_L + lua_pop(L, 1); + } +}; + +static int l_SpatialUpdateQueue(lua_State *L) +{ + return LuaSUQ::l_create(L); +} + +void init_spatial_update_queue(lua_State *L) +{ +#define DEF_BUILDAT_FUNC(name){ \ + lua_pushcfunction(L, l_##name); \ + lua_setglobal(L, "__buildat_" #name); \ +} + LuaSUQ::register_metatable(L); + + DEF_BUILDAT_FUNC(SpatialUpdateQueue); +} + +} // namespace lua_bindingss +// vim: set noet ts=4 sw=4: + diff --git a/src/lua_bindings/util.h b/src/lua_bindings/util.h index f8abbcc..644120a 100644 --- a/src/lua_bindings/util.h +++ b/src/lua_bindings/util.h @@ -4,6 +4,7 @@ #include "core/types.h" extern "C" { #include + #include } namespace lua_bindings