// 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 { 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; }; // Set of values with iterators for fast access of items, so that items // can be removed quickly by value struct ValueSet { struct Entry { Value value; std::list::iterator it; Entry(const Value &value, std::list::iterator it = std::list::iterator()): value(value), it(it) {} bool operator>(const Entry &other) const { if(value.node_id > other.value.node_id) return true; if(value.node_id < other.value.node_id) return false; return value.type > other.value.type; } }; // sorted list in descending node_id and type order std::list m_set; void clear(){ m_set.clear(); } void insert(const Value &value, std::list::iterator queue_it){ Entry entry(value, queue_it); auto it = std::lower_bound(m_set.begin(), m_set.end(), entry, std::greater()); if(it == m_set.end()) m_set.insert(it, entry); else if(it->value.node_id != value.node_id || it->value.type != value.type) m_set.insert(it, entry); else *it = entry; } void remove(const Value &value){ Entry entry(value); auto it = std::lower_bound(m_set.begin(), m_set.end(), entry, std::greater()); if(it == m_set.end()) return; m_set.erase(it); } std::list::iterator*find(const Value &value){ Entry entry(value); auto it = std::lower_bound(m_set.begin(), m_set.end(), entry, std::greater()); if(it == m_set.end()) return nullptr; if(it->value.node_id != value.node_id || it->value.type != value.type) return nullptr; return &(it->it); } }; 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; ValueSet m_value_set; 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_value_set.clear(); 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"); // Find old entry; if the old entry is more important, discard the new // one; if the old entry is less important, remove the old entry std::list::iterator *itp = m_value_set.find(item.value); if(itp != nullptr){ Item &old_item = **itp; if(old_item.fw < item.fw){ // Old item is more important return; } else { // New item is more important m_queue.erase(*itp); m_queue_length--; m_value_set.remove(item.value); } } auto it = std::lower_bound(m_queue.begin(), m_queue.end(), item, std::greater()); // position in descending order auto inserted_it = m_queue.insert(it, item); m_queue_length++; m_value_set.insert(item.value, inserted_it); } 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() { if(m_queue.empty()) throw Exception("SpatialUpdateQueue::pop(): Empty"); Item &item = m_queue.back(); m_value_set.remove(item.value); 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: