lua_bindings/spatial_update_queue: Initial half-working version

This commit is contained in:
Perttu Ahola 2014-10-14 23:24:11 +03:00
parent a2890529bf
commit abad1e0e2f
6 changed files with 381 additions and 12 deletions

View File

@ -107,6 +107,7 @@ if(BUILD_CLIENT)
src/lua_bindings/misc.cpp src/lua_bindings/misc.cpp
src/lua_bindings/cereal.cpp src/lua_bindings/cereal.cpp
src/lua_bindings/voxel.cpp src/lua_bindings/voxel.cpp
src/lua_bindings/spatial_update_queue.cpp
) )
add_executable(${CLIENT_EXE_NAME} ${CLIENT_SRCS}) add_executable(${CLIENT_EXE_NAME} ${CLIENT_SRCS})

View File

@ -213,14 +213,6 @@ local function SpatialUpdateQueue()
near_weight=near_weight, near_trigger_d=near_trigger_d, near_weight=near_weight, near_trigger_d=near_trigger_d,
far_weight=far_weight, far_trigger_d=far_trigger_d}) far_weight=far_weight, far_trigger_d=far_trigger_d})
end, 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) get = function(self)
local item = table.remove(self.queue) local item = table.remove(self.queue)
if not item then return nil end if not item then return nil end
@ -241,6 +233,9 @@ local function SpatialUpdateQueue()
if not item then return nil end if not item then return nil end
return item.fw return item.fw
end, end,
get_length = function(self)
return #self.queue
end,
} }
return self return self
end end
@ -268,7 +263,8 @@ function M.init()
M.chunk_size_voxels:mul_components(M.section_size_chunks) M.chunk_size_voxels:mul_components(M.section_size_chunks)
end) end)
local node_update_queue = SpatialUpdateQueue() --local node_update_queue = SpatialUpdateQueue()
local node_update_queue = buildat.SpatialUpdateQueue()
local function update_voxel_geometry(node) local function update_voxel_geometry(node)
local data = node:GetVar("buildat_voxel_data"):GetBuffer() local data = node:GetVar("buildat_voxel_data"):GetBuffer()
@ -381,7 +377,7 @@ function M.init()
local node_update = node_update_queue:get() local node_update = node_update_queue:get()
local node = replicate.main_scene:GetNode(node_update.node_id) local node = replicate.main_scene:GetNode(node_update.node_id)
log:verbose("Node update #".. log:verbose("Node update #"..
#node_update_queue.queue.. node_update_queue:get_length()..
" (f="..(math.floor(f*100)/100).."".. " (f="..(math.floor(f*100)/100)..""..
", fw="..(math.floor(fw*100)/100)..")".. ", fw="..(math.floor(fw*100)/100)..")"..
": "..node:GetName()) ": "..node:GetName())
@ -394,10 +390,10 @@ function M.init()
did_update = true did_update = true
else else
log:verbose("Poked update #".. log:verbose("Poked update #"..
#node_update_queue.queue.. node_update_queue:get_length()..
" (f="..(math.floor((f or -1)*100)/100).."".. " (f="..(math.floor((f or -1)*100)/100)..""..
", fw="..(math.floor((fw or -1)*100)/100)..")") ", fw="..(math.floor((fw or -1)*100)/100)..")")
node_update_queue.queue = {} --node_update_queue.queue = {} -- For testing
end end
-- Check this at the end of the loop so at least one is handled -- 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 if not did_update or buildat.get_time_us() >= stop_at_us then

View File

@ -6,10 +6,53 @@ local log = buildat.Logger("__client/api")
buildat.connect_server = __buildat_connect_server buildat.connect_server = __buildat_connect_server
buildat.extension_path = __buildat_extension_path buildat.extension_path = __buildat_extension_path
buildat.get_time_us = __buildat_get_time_us buildat.get_time_us = __buildat_get_time_us
buildat.SpatialUpdateQueue = __buildat_SpatialUpdateQueue
buildat.safe.disconnect = __buildat_disconnect buildat.safe.disconnect = __buildat_disconnect
buildat.safe.get_time_us = __buildat_get_time_us 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) function buildat.safe.set_simple_voxel_model(safe_node, w, h, d, safe_buffer)
if not getmetatable(safe_node) or if not getmetatable(safe_node) or
getmetatable(safe_node).type_name ~= "Node" then getmetatable(safe_node).type_name ~= "Node" then

View File

@ -10,12 +10,14 @@ namespace lua_bindings {
extern void init_misc(lua_State *L); extern void init_misc(lua_State *L);
extern void init_cereal(lua_State *L); extern void init_cereal(lua_State *L);
extern void init_voxel(lua_State *L); extern void init_voxel(lua_State *L);
extern void init_spatial_update_queue(lua_State *L);
void init(lua_State *L) void init(lua_State *L)
{ {
init_misc(L); init_misc(L);
init_cereal(L); init_cereal(L);
init_voxel(L); init_voxel(L);
init_spatial_update_queue(L);
} }
} // namespace lua_bindingss } // namespace lua_bindingss

View File

@ -0,0 +1,326 @@
// http://www.apache.org/licenses/LICENSE-2.0
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#include "lua_bindings/util.h"
#include "core/log.h"
#include <tolua++.h>
#include <Vector3.h>
#include <list>
#include <algorithm>
#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<Item> m_queue;
std::list<Item> 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<max_operations; i++){
if(m_old_queue.empty())
break;
Item &item = m_old_queue.back();
put_item(item);
m_old_queue.pop_back();
}
}
void set_p(const Vector3 &p)
{
m_p = p;
if(m_old_queue.empty() && (m_p - m_queue_oldest_p).Length() > 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<Item>()); // 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:

View File

@ -4,6 +4,7 @@
#include "core/types.h" #include "core/types.h"
extern "C" { extern "C" {
#include <lua.h> #include <lua.h>
#include <lauxlib.h>
} }
namespace lua_bindings namespace lua_bindings