a Lua instance addon
Copyright (c) Dan Wilcox 2011,2015
BSD Simplified License.
For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "LICENSE.txt," in this distribution.
See https://github.com/danomatika/ofxLua and the Openframeworks Forum post for documentation
ofxLua is an Open Frameworks addon for running a Lua embedded scripting interpreter within an OpenFrameworks application. Using the SWIG (Simple Wrapper and Interface Generator) tool, C++ functions and classes can be bound to the Lua api allowing them to be called within a Lua script. This is useful in separating the upper level logic from the lower level application and is utilized in numerous video games and applications.
In addition, ofxLua provides bindings for the OpenFrameworks API.
Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode for a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping.
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. It is used to generate the C++ bindings which wrap the OpenFrameworks API for Lua.
(Optional) LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language. It implements the Lua API but is optimized for performance over the standard Lua distribution. It is recommended to use LuaJIT when speed is a concern and it is enabled on embedded Linux in addon_config.mk
for this reason.
OpenFrameworks is a cross platform open source toolkit for creative coding in C++
To use ofxLua, first you need to download and install OpenFrameworks. The examples are developed against the latest release version of OpenFrameworks on <openframeworks.cc/download>.
Currently, ofxLua is being developed on Mac OSX and has been tested on OSX, iOS, & Linux. Windows and Android should work but have not been tested.
Place ofxLua within a folder in the apps folder of the OF dir tree:
openframeworks/addons/ofxLua
If you are using a stable version (0062, 007, ...) of OpenFrameworks then you want to use a git tag of ofxLua for that version. You can select the tag in the Github "Current Branch" menu or clone and check it out using git.
For example, the following commands will clone ofxLua and switch to the OF 0062 tagged version:
git clone git://github.com/danomatika/ofxLua.git cd ofxLua git checkout 0062
The master branch of ofxLua will work with the current stable version of OpenFrameworks and can be considered relatively stable.
You'll need to checkout the swig-openframeworks submodule as well using:
git submodule init
git submodule update
For embedded Linux (arm, Raspberry Pi, etc), LuaJIT is used for better performance. Make sure you have the luajit-5.1 development package installed.
The example projects are in the luaExample
& luaExampleIOS
folders.
Project files for the examples are not included so you will need to generate the project files for your operating system and development environment using the OF ProjectGenerator which is included with the OpenFrameworks distribution.
Point the ProjectGenerator to addons/ofxLua
, change the project name to the exact name of the example you want to generate (ie luaExample
), and make sure to choose ofxLua
from the addons. Hitting "Generate Project" will populate that example with the project files you will need to build it.
Open the Xcode project, select the "luaExample Debug" scheme, and hit "Run".
Open the Code::Blocks .cbp and hit F9 to build. Optionally, you can build the example with the Makefile.
To build and run it on the terminal:
make make run
Simply select ofxLua from the available addons in the ProjectGenerator before generating a new project.
To develop your own project based on ofxLua, simply copy an example project and rename it. You probably want to put it in your apps folder, for example, after copying:
openFrameworks/addons/ofxLua/example/ => openFrameworks/apps/myApps/example/
It must be 3 levels down in the openframeworks folder structure.
Then after renaming:
openFrameworks/apps/myApps/myLuaProject/
On Mac, rename the project in Xcode (do not rename the .xcodeproj file in Finder!): Long click on the project name in the project tree.
Select ofxLua and other addons used by your project from the available addons in the ProjectGenerator, select the parent folder of your project, and set the exact name of the existing project in the text box. This will overwrite the existing project files with new ones that now include ofxLua.
Note: you will lose any custom settings you've added manually to your project.
If you want to add ofxLua to another project, you need to make sure you include the following src files:
openFrameworks/addons/ofxLua/src/ofxLua.h openFrameworks/addons/ofxLua/src/ofxLua.cpp openFrameworks/addons/ofxLua/src/ofxLuaFileWriter.h openFrameworks/addons/ofxLua/src/ofxLuaFileWriter.cpp
and optionally
openFrameworks/addons/ofxLua/src/bindings/ofxLuaBindings.h openFrameworks/addons/ofxLua/src/bindings/YOURPLATFORM/ofxLuaBindings.cpp
On older Mac OSXs (pre 10.8), a header file which is included with the OS contains some macros which conflict with several lua macros. They can be renamed by setting this CFLAG:
-D__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES=0
Include these src files:
openFrameworks/addons/ofxLua/src/ofxLua.h openFrameworks/addons/ofxLua/src/ofxLua.cpp openFrameworks/addons/ofxLua/src/ofxLuaFileWriter.h openFrameworks/addons/ofxLua/src/ofxLuaFileWriter.cpp openFrameworks/addons/ofxLua/src/bindings/ofxLuaBindings.h
You also need to include the platform specific OF API bindings:
# mac os openFrameworks/addons/ofxLua/src/bindings/desktop/ofxLuaBindings.cpp # or ios openFrameworks/addons/ofxLua/src/bindings/ios/ofxLuaBindings.cpp
Finally you need to include the header and library search paths required by luadbind. The provided static library xcode project includes the /usr/local/lib
and /usr/local/lib
search paths (as used by the Homebrew package manager) to the luabind static lib target. You'll need to change these if Boost is installed to a different dir.
Instructions:
- right click and create a new group "ofxLua"
- drag ofxLua/src into "ofxLua"
- remove bindings files that do not match your platform aka remove
src/bindings/desktop
for iOS
SWIG generated bindings for the OF API can be found in src/bindings
. Currently it covers most of the api while leaving out base classes. More specific documentation may come at a future date, but for now check the example scripts on usage.
There is a main "of" module and functions, classes, constants, & enums are renamed:
- function: ofBackground -> of.background
- class: ofColor -> of.Color
- constant: OF_LOG_VERBOSE -> of.LOG_VERBOSE
- enum: ofShader::POSITION_ATTRIBUTE -> of.Shader.POSITION_ATTRIBUTE
- "begin" & ""end": these are lua keywords, so functions with this name are renamed in the following classes:
- ofFbo: beginFbo() & endFbo()
- ofCamera: beginCamera() & endCamera()
- ofMaterial: beginMaterial() & endMaterial()
- ofShader: beginShader() & endShader()
Base classes, deprecations, variable arguments (...), ofThread, ofPtr, ofMutex, & ofScopedLock are ignored for now.
Functions that return a std::vector return a wrapped std::vector in Lua. As with Lua tables, indexes start at 1.
To see the detailed differences with the OF C++ API run the following:
grep DIFF swig/openFrameworks.i
To see work to be done on the bindings run:
grep TODO swig/openFrameworks.i
Simple Lua class support is provided by the class() function from the Lua Users wiki. This implementation allows for inheritance and usage is as follows:
-- class declaration
MyClass = class()
-- constructor & attributes
function MyClass:__init(x, y)
self.x = x
self.y = y
self.bBeingDragged = false
self.bOver = false
self.radius = 4
end
-- create instance & access attribute
myclass = MyClass(10, 10)
myclass.x = 100
-- inherit first class and add an attribute
MyClass2 = class(MyClass)
function MyClass2:__init(x, y, z)
self.z = z
end
-- create instance of derived class & access attributes
myclass2 = MyClass2(10, 10, 5)
myclass2.x = 100
myclass2.z = 100
Create a SWIG interface file (*.i) with includes headers for the functions and classes which you want to bind. You then run SWIG with this file to generate the *.cpp wrapper.
It could be as simple as the following:.
MyCode.h, your custom code:
#pragma once
#include "ofMain.h"
float myFunction(float aNumber) {
return aNumber+1;
}
class MyCoolClass {
public:
MyCoolClass() {
myFloat = 0;
myInt = 0;
aString = "hello world";
}
MyCoolClass(float aNumber) {
myFloat = aNumber;
myInt = (int) aNumber;
aString = "foo bar";
}
// member function
void doSomething() {
ofLog() << "did something: " << aString;
}
// getter/setter
void setInt(int newInt) {myInt = newInt;}
int getInt() {return myInt;}
// member variable, read/write
string aString;
protected:
// some protected member variables, won't be wrapped
float myFloat;
int myInt;
};
MyBindings.i, your custom SWIG interface file:
%module my
%{
// include any needed headers here
#include "MyCode.h"
%}
// include support for some common C++ STL types you might be using,
// see http://swig.org/Doc3.0/Library.html#Library_stl_cpp_library
%include <stl.i>
%include <std_string.i>
%include <std_vector.i>
%include <std_map.i>
// if you're using "string" instead of "std::string" (like the openFrameworks source code),
// you'll need this so swig will know that a "string" can be considered a "std::string"
typedef std::string string;
// include custom code you want to be wrapped,
// note the '%' instead of '#' as '%include' is a SWIG
// command to wrap code in the given header
%include "MyCode.h"
That's it, swig will handle the rest! Of course this is a simple example but there are lots more options for specific bindings settings such as generating properties from getters & setters, etc.
Now call SWIG to generate your .cpp wrapper:
swig -c++ -lua -fcompact -fvirtual -I../../../libs/openFrameworks MyBindings.i
Make sure to add search paths to headers used by your code (aka the "-I" line in the command above).
If all went well, SWIG will have generated the MyBindings_wrap.cxx
C++ file. Put this into your project's source directory and build it with your project.
You will need to open your new Lua library provided by the SWIG-generated .cpp file in order to use it in your lua state.
SWIG creates a "luaopen" C function using your module name which, in this case, will be "luaopen_my". This function needs to be defined in C++ in order to be used, so add it to the top of the .cpp file where you initialize your ofxLua object:
// declare the module bindings
extern "C" {
int luaopen_my(lua_State* L);
}
Then call this function after initing ofxLua:
lua.init(true);
luaopen_my(lua); // open bindings
If everything is working, you should be able to call your bindings in Lua using your new "my" module:
aNumber = my.myFunction(3.45)
-- class
coolClass = my.MyCoolClass() -- constructor
coolClass = my.MyCoolClass(1.23) -- constructor with float
-- note: use ':' not '.' to call class instance member functions
coolClass:doSomething()
-- call the setter function
coolClass:setInt(4)
-- properties are accessed with a '.'
coolClass.aString = "hello world"
See the SWIG interface file in swig
and the SWIG and Lua documentation for more information. SWIG will handle most genral cases for you, but there are plenty of details to get into if you want greater customization.
If you end up having lots of custom code to bind, it's recommended to create multiple SWIG interface files which are included into a single *.i using the %include command. Do not create seaprate files with the same module name, only set the module in the main file as SWIG is designed for 1 module per main interface.
Do not open issues or bug reports if the problem is in writing your own bindings as this is all handled by SWIG. Be sure to search online for similar errors with "swig" as part of your search. More likely than not, it's an issue with your bindings and not with ofxLua.
Lua's require
command will import scripts but doesn't know about the OF data path, so it will fail when using a relative path since the working directory for an OF app is the executable directory by default:
YourApp/bin/data/scriptA.lua
YourApp/bin/data/scriptB.lua
scriptA.lua:
require "scriptB"
require will fail since the current working directory is not YourApp/bin/data
.
The easiest fix for this is to change the current working directory of the app to the directory of the script which is calling require. Setting the changeDir argument to true when calling ofxLua::doScript() will change the directory for you:
lua.doScript("scriptA.lua"); // doesn't change path, require will fail
lua.doScript("scriptA.lua", true); // changes path to script's parent dir, require should work
This will not effect the OF data path.
You can help develop ofxLua on GitHub: https://github.com/danomatika/ofxLua
Create an account, clone or fork the repo, then request a push/merge.
If you find any bugs or suggestions please log them to GitHub as well.
If you get the following error after generating an iOS app:
ld: -pie can only be used when targeting iOS 4.2 or later
change the deployment target in your project settings under the General Tab to something higher than 4.2, say 7.0.