In a 3D scene, I have an Object that has a position that I would like to move using Lua.
eg. box.position.x = 10
box has a metatable ("Object") and so has position ("Vec"). Object has __newindex
and __index
set to call C functions NewIndexObject
and IndexObject
respectively. Same with Vec (NewIndexVec
and IndexVec
).
Object has an id so it can be identified in a list that is stored in the scene and when box.position
is accessed all is fine, the C function IndexObject is called and I can extract the id from the stack, it's just when box.position.x = 10
is executed 'NewIndexVec' is called and the only thing on the stack is {table, x, 10} so no way of identifying the object to change its x position.
Is there anyway of pushing values onto a local state? Help!
UPDATE: thank you for getting back to me quickly, below I have distilled the code as much as possible. If you run this code it'll appear to work but I have comments where I'm stuck, it's just getting the first object in the array but I need to choose it by it's ID, Thanks in advance
struct Obj
{
std::string id;
int x,y,z;
Obj()
{
x = 10; y = 20; z = 30;
id = "12345";
}
};
//array of external objects
std::vector<Obj> objects;
int NewObject(lua_State * L)
{
Obj obj;
objects.push_back(obj);
lua_newtable(L);
luaL_getmetatable(L, "MT_Object");
lua_setmetatable(L, -2);
lua_pushstring(L, "id");
lua_pushstring(L, obj.id.c_str());
lua_settable(L, 1);
lua_newtable(L);
luaL_getmetatable(L, "MT_Vec");
lua_setmetatable(L, -2);
lua_pushinteger(L, obj.x);
lua_setfield(L, -2, "x");
lua_pushinteger(L, obj.y);
lua_setfield(L, -2, "y");
lua_pushinteger(L, obj.z);
lua_setfield(L, -2, "z");
lua_setfield(L, -2, "position");
return 1;
}
int IndexVec(lua_State * L)
{
// How do I get the correct object so I can pass its value back
Obj &dunnoObj = objects[0];
std::string key = luaL_checkstring(L,-1);
if(key == "x")
lua_pushinteger(L,dunnoObj.x);
else if(key == "y")
lua_pushinteger(L,dunnoObj.y);
else if(key == "z")
lua_pushinteger(L,dunnoObj.z);
return 1;
}
int NewIndexVec(lua_State * L)
{
// How do I know which object's value to update
Obj &dunnoObj = objects[0];
std::string key = luaL_checkstring(L,-2);
int value = luaL_checkinteger(L,-1);
if(key == "x")
dunnoObj.x = value;
else if(key == "y")
dunnoObj.y = value;
else if(key == "z")
dunnoObj.z = value;
return 0;
}
int main()
{
lua_State * L = luaL_newstate();
luaL_openlibs(L);
luaL_Reg objreg[] =
{
{ "new", NewObject },
{ NULL, NULL }
};
luaL_newmetatable(L, "MT_Object");
luaL_register(L, 0, objreg);
lua_setglobal(L, "Object");
luaL_Reg reg[] =
{
{ "__index", IndexVec },
{ "__newindex", NewIndexVec },
{ NULL, NULL }
};
luaL_newmetatable(L, "MT_Vec");
luaL_register(L, 0, reg);
lua_setglobal(L, "Vec");
int res = luaL_dostring(L, "box = Object.new() box.position.x = 1000 print(box.id .. \" , \" ..box.position.x .. \" , \" .. box.position.y .. \" , \" .. box.position.z)");
if(res)
printf("Error: %s\n", lua_tostring(L, -1));
lua_close(L);
return 0;
}
If I understand you correctly, you don't have to do anything. Tables are tracked by reference, so NewIndexVec
doesn't need to know anything about box
if its first argument is box.position
.
If this answer can't work for some reason, then I'd need more information about your data structure to understand your problem.
Basically, box.position
needs to return some obj
for which obj.x = 10
is a valid operation and changes exactly what you want it to change.
The problem is that you're trying to keep the same data in two separate places. Keep all the data in the C++ struct, then have NewObject
return a userdata that pretends to be a table. Both the Object and the position
field should be the same Obj*
, but they may have different metatables to simulate different sets of fields.