static int rbody_ranged_scatter (lua_State *L)
{
TRY_START
        check_args(L, 12);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        std::string mat    = check_path(L, 2);
        GET_UD_MACRO(GfxRangedInstancesPtr, gri, 3, GFXRANGEDINSTANCES_TAG);
        float density       = check_float(L, 4);
        float min_slope     = check_float(L, 5);
        float max_slope     = check_float(L, 6);
        float min_elevation = check_float(L, 7);
        float max_elevation = check_float(L, 8);
        bool no_z           = check_bool(L, 9);
        bool rotate         = check_bool(L, 10);
        bool align_slope    = check_bool(L, 11);
        unsigned seed       = check_t<unsigned>(L, 12);

        SimpleTransform world_trans(self.getPosition(), self.getOrientation());

        self.colMesh->scatter(phys_mats.getMaterial(mat)->id,
                              world_trans, density, min_slope, max_slope, min_elevation,
                              max_elevation, no_z, rotate, align_slope, seed,
                              *gri);

        return 0;
TRY_END
}
static int stringdb_index(lua_State *L)
{
TRY_START
        check_args(L,2);
        GET_UD_MACRO(StringDB,self,1,STRINGDB_TAG);
        if (lua_type(L,2)==LUA_TNUMBER) {
                if (self.size()==0) {
                        my_lua_error(L,"Empty stringdb");
                }
                unsigned short index=check_t<unsigned short>(L,2,1,self.size());
                lua_pushstring(L,self[index-1].c_str());
        } else {
                std::string key  = luaL_checkstring(L,2);
                if (key=="add") {
                        push_cfunction(L,stringdb_add);
                } else if (key=="clear") {
                        push_cfunction(L,stringdb_clear);
                } else if (key=="table") {
                        lua_createtable(L, self.size(), 0);
                        for (unsigned int i=0 ; i<self.size() ; i++) {
                                lua_pushnumber(L,i+1);
                                lua_pushstring(L,self[i].c_str());
                                lua_settable(L,-3);
                        }
                } else {
                        my_lua_error(L,"Not a readable StringDB member: "+key);
                }
        }
        return 1;
TRY_END
}
static void init_cast_blacklist (lua_State *L, int base_line, LuaSweepCallback &lcb)
{
        int blacklist_number = lua_gettop(L) - base_line;
        for (int i=1 ; i<=blacklist_number ; ++i) {
                GET_UD_MACRO(RigidBody, black, base_line+i, RBODY_TAG);
                lcb.blacklist.insert(&black);
        }
}
static int stringdb_len(lua_State *L)
{
TRY_START
        check_args(L,2); // a
        GET_UD_MACRO(StringDB,self,1,STRINGDB_TAG);
        lua_pushnumber(L,self.size());
        return 1;
TRY_END
}
static int gritobj_deactivate (lua_State *L)
{
TRY_START
        check_args(L,1);
        GET_UD_MACRO(GritObjectPtr,self,1,GRITOBJ_TAG);
        self->deactivate(L,self);
        return 0;
TRY_END
}
static int gritobj_reload_disk_resource (lua_State *L)
{
TRY_START
        check_args(L,1);
        GET_UD_MACRO(GritObjectPtr,self,1,GRITOBJ_TAG);
        self->reloadDiskResources();
        return 0;
TRY_END
}
static int stringdb_clear (lua_State *L)
{
TRY_START
        check_args(L,1);
        GET_UD_MACRO(StringDB,self,1,STRINGDB_TAG);
        self.clear();
        return 0;
TRY_END
}
static int rbody_activate (lua_State *L)
{
TRY_START
        check_args(L, 1);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        self.activate();
        return 0;
TRY_END
}
static int stringdb_add (lua_State *L)
{
TRY_START
        check_args(L,2);
        GET_UD_MACRO(StringDB,self,1,STRINGDB_TAG);
        self.push_back(luaL_checkstring(L,2));
        return 0;
TRY_END
}
static int gritobj_destroy (lua_State *L)
{
TRY_START
        check_args(L,1);
        GET_UD_MACRO(GritObjectPtr,self,1,GRITOBJ_TAG);
        object_del(L,self);
        return 0;
TRY_END
}
static int rbody_destroy (lua_State *L)
{
TRY_START
        check_args(L, 1);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        self.destroy(L);
        return 0;
TRY_END
}
static int gritobj_add_disk_resource (lua_State *L)
{
TRY_START
        check_args(L,2);
        GET_UD_MACRO(GritObjectPtr,self,1,GRITOBJ_TAG);
        std::string name = check_path(L,2);
        self->addDiskResource(name);
        return 0;
TRY_END
}
static int rbody_get_part_enabled (lua_State *L)
{
TRY_START
        check_args(L, 2);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        int i = (int)check_int(L, 2, 0, self.getNumElements()-1);
        lua_pushboolean(L, self.getElementEnabled(i));
        return 1;
TRY_END
}
static int rbody_torque (lua_State *L)
{
TRY_START
        check_args(L, 2);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        Vector3 torque = check_v3(L, 2);
        self.torque(torque);
        return 0;
TRY_END
}
static int ifilter_pressed (lua_State *L)
{
TRY_START
    check_args(L, 2);
    GET_UD_MACRO(InputFilter, self, 1, IFILTER_TAG);
    std::string button = check_string(L, 2);
    lua_pushboolean(L, self.isButtonPressed(button));
    return 1;
TRY_END
}
static int ifilter_gc (lua_State *L)
{
TRY_START
    check_args(L, 1);
    GET_UD_MACRO(InputFilter, self, 1, IFILTER_TAG);
    if (self.isAlive()) self.destroy(L);
    delete &self;
    return 0;
TRY_END
}
static int rbody_world_to_local (lua_State *L)
{
TRY_START
        check_args(L, 2);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        Vector3 a = check_v3(L, 2);
        Vector3 result = self.getOrientation().inverse() * (a - self.getPosition());
        push_v3(L, result);
        return 1;
TRY_END
}
static int rbody_set_part_orientation_offset (lua_State *L)
{
TRY_START
        check_args(L, 3);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        int i = (int)check_int(L, 2, 0, self.getNumElements()-1);
        Quaternion q = check_quat(L, 3);
        self.setElementOrientationOffset(i, q);
        return 0;
TRY_END
}
static int rbody_get_part_orientation_offset (lua_State *L)
{
TRY_START
        check_args(L, 2);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        int i = (int)check_int(L, 2, 0, self.getNumElements()-1);
        Quaternion q = self.getElementOrientationOffset(i);
        lua_pushnumber(L, q.w); lua_pushnumber(L, q.x); lua_pushnumber(L, q.y); lua_pushnumber(L, q.z);
        return 4;
TRY_END
}
static int rbody_set_part_position_offset (lua_State *L)
{
TRY_START
        check_args(L, 3);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        int i = (int)check_int(L, 2, 0, self.getNumElements()-1);
        Vector3 v = check_v3(L, 3);
        self.setElementPositionOffset(i, v);
        return 0;
TRY_END
}
static int gritobj_update_sphere (lua_State *L)
{
TRY_START
        check_args(L,3);
        GET_UD_MACRO(GritObjectPtr,self,1,GRITOBJ_TAG);
        Vector3 pos = check_v3(L,2);
        float r = check_float(L,3);
        self->updateSphere(pos, r);
        return 0;
TRY_END
}
static int rbody_set_part_enabled (lua_State *L)
{
TRY_START
        check_args(L, 3);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        int i = (int)check_int(L, 2, 0, self.getNumElements()-1);
        bool b = check_bool(L, 3);
        self.setElementEnabled(i, b);
        return 0;
TRY_END
}
static int ifilter_tostring (lua_State *L)
{
TRY_START
    check_args(L, 1);
    GET_UD_MACRO(InputFilter, self, 1, IFILTER_TAG);
    std::stringstream ss;
    ss << IFILTER_TAG << " " << static_cast<void*>(&self) << " (" << self.order << ") \"" << self.description << "\"";
    lua_pushstring(L, ss.str().c_str());
    return 1;
TRY_END
}
static int rbody_get_part_position_offset (lua_State *L)
{
TRY_START
        check_args(L, 2);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        int i = (int)check_int(L, 2, 0, self.getNumElements()-1);
        Vector3 v = self.getElementPositionOffset(i);
        lua_pushnumber(L, v.x); lua_pushnumber(L, v.y); lua_pushnumber(L, v.z);
        return 3;
TRY_END
}
static int rbody_impulse (lua_State *L)
{
TRY_START
        check_args(L, 3);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        Vector3 impulse = check_v3(L, 2);
        Vector3 wpos = check_v3(L, 3);
        const Vector3 &pos = self.getPosition();
        self.impulse(impulse, wpos-pos);
        return 0;
TRY_END
}
static int plot_newindex(lua_State *L)
{
TRY_START
        check_args(L,3);
        GET_UD_MACRO(Plot,self,1,PLOT_TAG);
        (void) self;
        std::string key  = luaL_checkstring(L,2);
        if (false) {
        } else {
                my_lua_error(L,"Not a writeable Plot member: "+key);
        }
        return 0;
TRY_END
}
static int stringdb_newindex(lua_State *L)
{
TRY_START
        check_args(L,3);
        GET_UD_MACRO(StringDB,self,1,STRINGDB_TAG);
        std::string key  = luaL_checkstring(L,2);
        if (lua_type(L,2)==LUA_TNUMBER) {
                if (self.size()==0) {
                        my_lua_error(L,"Empty stringdb");
                }
                unsigned short index=check_t<unsigned short>(L,2,1,self.size());
                std::string v = luaL_checkstring(L,3);
                self[index-1] = v;
        } else {
                if (key=="value") {
                        GET_UD_MACRO(StringDB,v,3,STRINGDB_TAG);
                        self = v;
                } else {
                        my_lua_error(L,"Not a writeable StringDB member: "+key);
                }
        }
        return 0;
TRY_END
}
static int rbody_local_vel (lua_State *L)
{
TRY_START
        check_args(L, 3);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        Vector3 pos = check_v3(L, 2);
        bool world_space = check_bool(L, 3);
        Vector3 local_pos = pos;
        if (world_space) {
                local_pos -= self.getPosition();
        }
        Vector3 local_vel = self.getLocalVelocity(local_pos);
        push_v3(L, local_vel);
        return 1;
TRY_END
}
static int rbody_scatter (lua_State *L)
{
TRY_START
        check_args(L, 11);
        GET_UD_MACRO(RigidBody, self, 1, RBODY_TAG);
        std::string mat = check_path(L, 2);
        SimpleTransform world_trans(self.getPosition(), self.getOrientation());
        float density       = check_float(L, 3);
        float min_slope     = check_float(L, 4);
        float max_slope     = check_float(L, 5);
        float min_elevation = check_float(L, 6);
        float max_elevation = check_float(L, 7);
        bool no_z           = check_bool(L, 8);
        bool rotate         = check_bool(L, 9);
        bool align_slope    = check_bool(L, 10);
        unsigned seed       = check_t<unsigned>(L, 11);

        std::vector<SimpleTransform> r;
        self.colMesh->scatter(phys_mats.getMaterial(mat)->id,
                              world_trans, density, min_slope, max_slope, min_elevation,
                              max_elevation, no_z, rotate, align_slope, seed,
                              r);

        lua_newtable(L);
        for (size_t j=0 ; j<r.size(); ++j) {
                const Quaternion &q = r[j].quat;
                const Vector3 &p = r[j].pos;
                lua_pushnumber(L, p.x);
                lua_rawseti(L, -2, 7*j+1);
                lua_pushnumber(L, p.y);
                lua_rawseti(L, -2, 7*j+1+1);
                lua_pushnumber(L, p.z);
                lua_rawseti(L, -2, 7*j+2+1);
                lua_pushnumber(L, q.w);
                lua_rawseti(L, -2, 7*j+3+1);
                lua_pushnumber(L, q.x);
                lua_rawseti(L, -2, 7*j+4+1);
                lua_pushnumber(L, q.y);
                lua_rawseti(L, -2, 7*j+5+1);
                lua_pushnumber(L, q.z);
                lua_rawseti(L, -2, 7*j+6+1);
        }

        return 1;
TRY_END
}
static int gritcls_newindex (lua_State *L)
{
TRY_START
        check_args(L,3);
        GET_UD_MACRO(GritClass,self,1,GRITCLS_TAG);
        std::string key = check_string(L,2);

        if (key=="name") {
                my_lua_error(L,"Not a writeable GritClass member: "+key);
        } else if (key=="parent") {
                self.setParent(L);
        } else {
                self.set(L,key);
        }

        return 0;
TRY_END
}