Josh Posted December 5, 2009 Author Share Posted December 5, 2009 Lua script: referencetable={} reference={} referencetable[3]=reference function reference:test() print("hello") end Now how would you call reference:test() from the main program?: lua_getglobal("referencetable") lua_getfield()? //Need to find the table for the value "3". lua_getfield("test") //okay Now we have to push the reference table for the test function to use. How? You cannot get the global variable "reference" because it is just one of many tables the routine will create. lua_getfield uses a string to find the value, so if I use an object as the index it doesn't work: function CreateClass(modelreference) class={} classtable[modelreference]=class end Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Nilium Posted December 5, 2009 Share Posted December 5, 2009 lua_getglobal(L, "referencetable") ' I'm assuming the "reference" table should be a local here, so not in the globals table lua_pushnumber(L, 3) ' push the key lua_gettable(L, -2) ' get the value ' or with an object: lua_getglobal(L, "classtable") ' _G["classtable"] lua_pushbmaxobject(L, modelreference) ' modelreference lua_createtable(L, 0, 0) ' {} lua_settable(L, -3) ' classtable[modelreference]={} lua_pop(L, 1) ' remove the table from classtable from the stack Not sure I see the problem. Maybe you're mistakenly thinking that the only way to get values out of/put values in a table is with lua_get/setfield? Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Josh Posted December 5, 2009 Author Share Posted December 5, 2009 Maybe you're mistakenly thinking that the only way to get values out of/put values in a table is with lua_get/setfield? Yep. I am not sure why lua_createtable() is being called above. Here's how I would like it to work: classtable={} entitytable={} function CreateClass(modelreference) local class={} classtable[modelreference]=class class.instances={} function class:Spawn(model) local entity={} entitytable[model]=entity entity.class=self self.instances[model]=entity function entity:SetKey(key,value) return 1 end return entity end return class end The lua table "entity" corresponds to a BlitzMax model. The lua table "class" corresponds to a BlitzMax modelreference. Just as the BlitzMax modelreference has a list of all instances of that object, the Lua class table has an "instances" table listing all the Lua entities of that class. All shared data that the entities use would get stored in the class table. When all instances of the model are deleted, the engine calls a cleanup function, removing the class for that modelreference from the table. At that time, any sounds or other stuff loaded for the class will be freed. Each "entity" table is also stored in a global table along with the BlitzMax model it corresponds to, so the engine can quickly find the entity table associated with any model. Now let's say I wanted to call the SetKey() function on a specific model from the main program. The Lua tables for the class and entity are already set up, and I am ready to call a function: lua_getglobal(L,"entitytable") if lua_istable(L,-1) lua_pushbmaxobject(L, model) ...? EndIf Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Nilium Posted December 5, 2009 Share Posted December 5, 2009 I'm making a bunch of assumptions about how you're doing this, but I'm going to assume I'm right. ' doing this longhand so exactly what is going in is more apparent (synonymous with lua_getglobal(L, "entitytable")) lua_pushstring(L, "entitytable") ' stack => -1 = "entitytable" lua_gettable(L, LUA_GLOBALSINDEX) ' stack => -1 = entitytable ref lua_pushbmaxobject(L, model) ' stack => -2 = entitytable ref, -1 = model object ' get the "entity" table stored in "entitytable" (similar names make these examples confusing as hell) lua_gettable(L, -2) ' stack => -2 = entitytable ref, -1 = entity ref ' remove the "entitytable" reference since it's unneeded lua_remove(L, -2) ' stack => -1 = entity ref ' longhand (synonymous with lua_getfield(L, "SetKey", -2)) lua_pushstring(L, "SetKey") ' stack => -2 = entity ref, -1 = "SetKey" ' get SetKey method lua_gettable(L, -2) ' stack => -2 = entity ref, -1 = function ' then push the "entity" table to the top of the stack so it will be the 'self' parameter lua_pushvalue(L, -2) ' stack => -3 = entity ref, -2 = function, -1 = entity ref (self argument) ' remove entity ref at -3 lua_remove(L, -3) ' stack => -2 = function, -1 = entity ref (self argument) ' push a key and value ' by now the stack should be something like -4 = function, -3 = entity ref (self argument), -2 = key argument, -1 = value argument lua_pcall(L, 3, 0, 0) ' stack => either nothing left or -1 = error string if there was an error The code highlighting on this forum desperately needs a 'turn the hell off' option. Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Josh Posted December 5, 2009 Author Share Posted December 5, 2009 Yeah, the highlighting is for C and I haven't found a way to change it yet. This makes more sense now. Still confusing, but if I follow it carefully it should work. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted December 6, 2009 Author Share Posted December 6, 2009 I'm getting some good results now. -The only special function name is the the class creation function, i.e. light_directional_CreateClass(). -I started using "object" instead of "entity" to avoid confusing Lua tables with engine entities. -You can call functions like object.super:Kill() in the object:Kill() method. -The commands are all OO and contained in the class table. -Objects can call each others' commands like player.enemy.weapon:shoot() Here's a look at the light_directional script: require("scripts/class") function light_directional_CreateClass(modelreference) local class=CreateClass(modelreference) function class:InitDialog(grid) self.super:InitDialog(grid) group=grid:AddGroup("Light") group:AddProperty("Resolution",PROPERTY_CHOICE,"256,512,1024,2048") group:AddProperty("linearoffset",PROPERTY_VEC3,"0,1,2","Linear offset" ) group:AddProperty("shadowdistance",PROPERTY_VEC3,"","Shadow distance" ) group:AddProperty("Range",PROPERTY_FLOAT) group:Expand(1) end function class:Spawn(model) local object=self.super:Spawn(model) object.model:SetKey("resolution","2") object.light=CreateDirectionalLight(object.model) function object:SetKey(key,value) if key=="resolution" then if value=="0" then self.light:SetShadowmapSize(256) elseif value=="1" then self.light:SetShadowmapSize(512) elseif value=="2" then self.light:SetShadowmapSize(1024) elseif value=="3" then self.light:SetShadowmapSize(2048) end else return self.super:SetKey(key,value) end end function object:GetKey(key,value) if key=="linearoffset" then return self.light:GetShadowOffset(0,0)..","..self.light:GetShadowOffset(0,1)..","..self.light:GetShadowOffset(0,2) elseif key=="shadowdistance" then return self.light:GetShadowDistance(0)..","..self.light:GetShadowDistance(1)..","..self.light:GetShadowDistance(2) else return self.super:GetKey(key,value) end end function object:Kill(model) if self.light~=nil then self.light:Free() self.light=nil end self.super:Kill() end return object end return class end Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
TylerH Posted December 6, 2009 Share Posted December 6, 2009 Right on the money Josh Quote nVidia 530M Intel Core i7 - 2.3Ghz 8GB DDR3 RAM Windows 7 Ultimate (64x)----- Visual Studio 2010 Ultimate Google Chrome Creative Suite 5 FL Studio 10 Office 15 ----- Expert Professional Expert BMX Programmer ----- Link to comment Share on other sites More sharing options...
Rick Posted December 6, 2009 Share Posted December 6, 2009 Is there any way that can be cleaned up somehow? 3 nested functions SetKey() and everything needs to be in a nested function Create() just seems kind of messy. Even if this is how it has to be, there might be a way to hide some of this so it looks more straight forward. Quote Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 Is there any way that can be cleaned up somehow? 3 nested functions SetKey() and everything needs to be in a nested function Create() just seems kind of messy. Even if this is how it has to be, there might be a way to hide some of this so it looks more straight forward. What would your solution be? Personally, I see nothing wrong with this, aside from a bit of unnecessary code. E.g., in SetKey, all you'd need is this: function object:SetKey(key,value) if key=="resolution" then local value = tonumber(value) if 0 <= power and power <= 3 then object.light:SetShadowmapSize(256 * 2^power) end else return self.super:SetKey(key,value) end end This probably comes down to coding style, however, but I like having fewer lines. Far as I'm concerned, nested functions in Lua are fine. You wouldn't have to put everything in Create, for one - you could store it in a local and reference that in the function, like so: require "scripts/class" do local function table_merge(table, into) for k,v in next, table, nil do into[k]=v end end -- object methods local object_methods = {} function object_methods:SetKey(key,value) if key=="resolution" then local power = tonumber(value) if 0 <= power and power <= 3 then object.light:SetShadowmapSize(256 * 2^power) end else return self.super:SetKey(key,value) end end function object_methods:GetKey(key,value) if key=="linearoffset" then return object.light:GetShadowOffset(0,0)..","..object.light:GetShadowOffset(0,1)..","..object.light:GetShadowOffset(0,2) elseif key=="shadowdistance" then return object.light:GetShadowDistance(0)..","..object.light:GetShadowDistance(1)..","..object.light:GetShadowDistance(2) else return self.super:GetKey(key,value) end end function object_methods:Kill(model) if self.light then self.light:Free() self.light = nil end self.super:Kill() end -- class methods local class_methods = {} function class_methods:InitDialog(grid) self.super:InitDialog(grid) group=grid:AddGroup("Light") group:AddProperty("Resolution",PROPERTY_CHOICE,"256,512,1024,2048") group:AddProperty("linearoffset",PROPERTY_VEC3,"0,1,2","Linear offset" ) group:AddProperty("shadowdistance",PROPERTY_VEC3,"","Shadow distance" ) group:AddProperty("Range",PROPERTY_FLOAT) group:Expand(1) end function class_methods:Spawn(model) local object=self.super:Spawn(model) object.model:SetKey("resolution","2") object.light=CreateDirectionalLight(object.model) table_merge(object_methods, object) return object end -- light_directional_CreateClass function light_directional_CreateClass(modelreference) local class=CreateClass(modelreference) table_merge(class_methods, class) return class end end Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
macklebee Posted December 6, 2009 Share Posted December 6, 2009 eh... personally like Josh's way better... its easier for me trying to follow along... but its like you said a matter of coding style. question: where is 'power' being defined at? should it be: function object:SetKey(key,value) if key=="resolution" then local power = tonumber(value) if 0 <= power and power <= 3 then object.light:SetShadowmapSize(256 * 2^power) end ... etc... Quote Win7 64bit / Intel i7-2600 CPU @ 3.9 GHz / 16 GB DDR3 / NVIDIA GeForce GTX 590 LE / 3DWS / BMX / Hexagon macklebee's channel Link to comment Share on other sites More sharing options...
Rick Posted December 6, 2009 Share Posted December 6, 2009 I agree, fewer lines that I have to type the better. Your example of how to do it however isn't much fewer. I was more thinking of some kind of preprocessor macro stuff to make things look easier. Not sure if lua has that sort of thing. Quote Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 I agree, fewer lines that I have to type the better. Your example of how to do it however isn't much fewer. I was more thinking of some kind of preprocessor macro stuff to make things look easier. Not sure if lua has that sort of thing. A preprocessor sounds like a horrifyingly bad idea. question: where is 'power' being defined at? should it be: Woops, editing stuff in the post window tends to do that. Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Rick Posted December 6, 2009 Share Posted December 6, 2009 I just don't think people will be comfortable with those 2 options above. It's not all that straight forward. So just trying to think of other ideas. Quote Link to comment Share on other sites More sharing options...
Josh Posted December 6, 2009 Author Share Posted December 6, 2009 Well, there's the easy way and the powerful way. The multistate approach is very procedural and easy on the eyes, but the single state approach has to have things contained within the class table. You can't just declare a bunch of variables and not worry about them interfering with other scripts. I think if I tried to move the object functions to another file or another part of the code, it would be more confusing. If I tried to muddle it up and make it prettier I think I would just be obfuscating what is really happening. I'm pretty satisfied because it's object-oriented and still pretty simple. I am pretty optimistic about this design so I will document it extensively. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Rick Posted December 6, 2009 Share Posted December 6, 2009 What is happening there? I assume the editor calls light_directional_CreateClass(). Which defines the other functions in the class table and then returns the class table to the editor, which then can call the functions defined inside light_directional_CreateClass? So I assume the editor will save off the class table per model in the scene so it can call the functions defined in it. It's not the worst just seems the light_directional_CreateClass() function should be more of a class. I'm just used to C++ more than lua, but I get what it's doing. Do we have access to the preprocessor? I might try to make something just for myself and the scripts I create. Quote Link to comment Share on other sites More sharing options...
Josh Posted December 6, 2009 Author Share Posted December 6, 2009 There is no preprocessor. How would light_directional_CreateClass() be a class? If you have any ideas tell me. I could eliminate the big class function and just set the value of modelreference directly from the engine before the script is run. That gets rid of one layer of nesting: require("scripts/class") local class=CreateClass(modelreference) function class:InitDialog(grid) self.super:InitDialog(grid) group=grid:AddGroup("Light") group:AddProperty("Resolution",PROPERTY_CHOICE,"256,512,1024,2048") group:AddProperty("linearoffset",PROPERTY_VEC3,"0,1,2","Linear offset" ) group:AddProperty("shadowdistance",PROPERTY_VEC3,"","Shadow distance" ) group:AddProperty("Range",PROPERTY_FLOAT) group:Expand(1) end function class:Spawn(model) local object=self.super:Spawn(model) object.model:SetKey("resolution","2") object.light=CreateDirectionalLight(object.model) function object:SetKey(key,value) if key=="resolution" then if value=="0" then self.light:SetShadowmapSize(256) elseif value=="1" then self.light:SetShadowmapSize(512) elseif value=="2" then self.light:SetShadowmapSize(1024) elseif value=="3" then self.light:SetShadowmapSize(2048) end else return self.super:SetKey(key,value) end end function object:GetKey(key,value) if key=="linearoffset" then return self.light:GetShadowOffset(0,0)..","..self.light:GetShadowOffset(0,1)..","..self.light:GetShadowOffset(0,2) elseif key=="shadowdistance" then return self.light:GetShadowDistance(0)..","..self.light:GetShadowDistance(1)..","..self.light:GetShadowDistance(2) else return self.super:GetKey(key,value) end end function object:Kill(model) if self.light~=nil then self.light:Free() self.light=nil end self.super:Kill() end return object end class=nil Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 Replace the 'class=nil' bit at the end of that script with 'return class' and use luaL_loadfile like so: lua_pushstring(L, entity_class_name+"_CreateClass") ' e.g., light_directional luaL_loadfile(L, path_to_entity_script) ' loads the script as a Lua function lua_settable(L, LUA_GLOBALSINDEX) ' sets the global light_directional_CreateClass to the function Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Josh Posted December 6, 2009 Author Share Posted December 6, 2009 And the application end is like this? It doesn't look like modelreference is being passed correctly: Local result:Int Local size:Int=modelref.lua.StackSize() lua_pushstring(modelref.lua.L,modelref.prefix+"CreateClass") result=modelref.lua.loadfile(StripExt(modelref.path)+".lua") If result lua_settable(modelref.lua.L,LUA_GLOBALSINDEX) EndIf modelref.lua.SetStackSize(size) If result size=modelref.lua.StackSize() lua_getglobal(modelref.lua.L,modelref.prefix+"CreateClass") If lua_isfunction(modelref.lua.L,-1) modelref.lua.pushobject(modelref) modelref.lua.invoke(1,0) EndIf modelref.lua.SetStackSize(size) EndIf Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Niosop Posted December 6, 2009 Share Posted December 6, 2009 I've never touched Lua before LeadWerks (I'm a python guy myself), but the single-state code you posted was simple enough for even me to get. If it makes communicating between entities easier and adds flexibility, then my vote is to go w/ single-state. Niosop Quote Windows 7 x64 - Q6700 @ 2.66GHz - 4GB RAM - 8800 GTX ZBrush - Blender Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 Forgot about the modelreference bit. Add local modelreference = ... just after the require line. Could also do local class = CreateClass(...). Also, when checking the result of luaL_loadfile, luaL_dofile, etc. you check to see if the result is zero, not if it's non-zero (so, result=0 means no errors), so that's also an error in your code there. Out of curiosity, Josh, do you have a Google Wave account? Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Josh Posted December 6, 2009 Author Share Posted December 6, 2009 My loadfile() method returns 1 if luaL_loadfile() returns 0. I'm now getting "attempt to call a string value". Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 What does the script look like now? Also, what is the string value? Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Josh Posted December 6, 2009 Author Share Posted December 6, 2009 require("scripts/class") local modelreference=... local class = CreateClass(modelreference) function class:InitDialog(grid) self.super:InitDialog(grid) group=grid:AddGroup("Light") group:AddProperty("Resolution",PROPERTY_CHOICE,"256,512,1024,2048") group:AddProperty("linearoffset",PROPERTY_VEC3,"0,1,2","Linear offset" ) group:AddProperty("shadowdistance",PROPERTY_VEC3,"","Shadow distance" ) group:AddProperty("Range",PROPERTY_FLOAT) group:Expand(1) end function class:Spawn(model) local object=self.super:Spawn(model) object.model:SetKey("resolution","2") object.light=CreateDirectionalLight(object.model) function object:SetKey(key,value) if key=="resolution" then if value=="0" then self.light:SetShadowmapSize(256) elseif value=="1" then self.light:SetShadowmapSize(512) elseif value=="2" then self.light:SetShadowmapSize(1024) elseif value=="3" then self.light:SetShadowmapSize(2048) end elseif key=="range" then self.light:SetRange(value) elseif key=="shadowdistance" then local offset=string.Explode(value,",") x=tonumber(offset[1]) y=tonumber(offset[2]) z=tonumber(offset[3]) if x==nil then x=0 end if y==nil then y=0 end if z==nil then z=0 end self.light:SetShadowDistance(x,0) self.light:SetShadowDistance(y,1) self.light:SetShadowDistance(z,2) elseif key=="linearoffset" then local offset=string.Explode(value,",") x=tonumber(offset[1]) y=tonumber(offset[2]) z=tonumber(offset[3]) if x==nil then x=0 end if y==nil then y=0 end if z==nil then z=0 end self.light:SetShadowOffset(x,1.0,0) self.light:SetShadowOffset(y,1.0,1) self.light:SetShadowOffset(z,1.0,2) else return self.super:SetKey(key,value) end end function object:GetKey(key,value) if key=="linearoffset" then return self.light:GetShadowOffset(0,0)..","..self.light:GetShadowOffset(0,1)..","..self.light:GetShadowOffset(0,2) elseif key=="shadowdistance" then return self.light:GetShadowDistance(0)..","..self.light:GetShadowDistance(1)..","..self.light:GetShadowDistance(2) elseif key=="range" then return self.light:GetRange() elseif key=="shadowresolution" then resolution=self.light:GetShadowmapSize() if resolution==256 then return 0 elseif resolution==512 then return 1 elseif resolution==1024 then return 2 elseif resolution==2048 then return 3 else return -1 end else return self.super:GetKey(key,value) end end function object:Kill(model) if self.light~=nil then self.light:Free() self.light=nil end self.super:Kill() end return object end return class Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 I don't see anything in the script that would cause that error, and assuming your Lua type's methods are essentially one-to-one wrappers around the existing API, I don't know that there's a problem with that. Probably an error in another script. Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Nilium Posted December 6, 2009 Share Posted December 6, 2009 No, I have it pretty well isolated. It occurs when I try to do anything with modelreference after it is set to ... I'll keep messing around with it. Try adding error("<" .. type(modelreference) .. "> " .. tostring(modelreference), 1) right after you set modelreference. I'd be interested in knowing exactly what shows up as the error message there. Quote MacBook Pro 15.4", Intel Core 2 Duo T9600 2.80GHz, 4GB 1066MHz DDR3, GeForce 9400M 256MB, GeForce 9600M GT 512MB - Mac OS 10.6.2, Vista32 Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.