From aab9df08023c96642fee6b3a5ac030eed3c1574f Mon Sep 17 00:00:00 2001 From: Godzil Date: Mon, 2 Mar 2020 16:30:24 +0000 Subject: [PATCH] Add support for Lua in world, and create the Lua Pattern (pattern can be defined with a lua function) --- CMakeLists.txt | 5 ++ source/CMakeLists.txt | 13 ++++- source/include/world.h | 10 ++++ source/pattern/luapattern.h | 107 ++++++++++++++++++++++++++++++++++++ source/world.cpp | 13 +++++ tests/pattern_test.cpp | 54 +++++++++++++++++- 6 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 source/pattern/luapattern.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bdc3275..cfa9ed7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,11 @@ if (SHOW_STATS) add_compile_options(-DRENDER_STATS) endif() +option(USE_LUA "Enable the use of Lua" ON) +if (USE_LUA) + add_compile_options(-DENABLE_LUA_SUPPORT) +endif() + if (ENABLE_COVERAGE AND COVERALLS) message(FATAL_ERROR "You can't enable both ENABLE_COVERAGE and COVERALLS at the same time") endif() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 9740f9f..918d133 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -14,7 +14,13 @@ file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_D ${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp) target_include_directories(rayonnement PUBLIC include pattern) -add_dependencies(rayonnement LuaCore) + +if (USE_LUA) + add_dependencies(rayonnement LuaCore) + target_link_libraries(rayonnement ${LUA_LIBRARIES}) +endif() + +target_include_directories(rayonnement PUBLIC include ${LODEPNG_INCLUDE_FOLDER} ${LUA_INCLUDE_DIR}) target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES}) target_link_libraries(rayonnement LodePNG) @@ -22,11 +28,14 @@ if (USE_OPENMP) target_link_libraries(rayonnement OpenMP::OpenMP_CXX) endif() + +# The main executable add_executable(dorayme main.cpp) + add_dependencies(dorayme LuaCore) -target_include_directories(rayonnement PUBLIC include ${LODEPNG_INCLUDE_FOLDER} ${LUA_INCLUDE_DIR}) target_link_libraries(dorayme rayonnement ${LUA_LIBRARIES}) + if (COVERALLS) set(COVERAGE_SRCS ${RAY_HEADERS} ${RAY_SOURCES} ${COVERAGE_SRCS} PARENT_SCOPE) endif() \ No newline at end of file diff --git a/source/include/world.h b/source/include/world.h index 6562092..040dd5f 100644 --- a/source/include/world.h +++ b/source/include/world.h @@ -16,6 +16,12 @@ #include #include +#ifdef ENABLE_LUA_SUPPORT +extern "C" { +#include +} +#endif + class World { public: @@ -29,6 +35,10 @@ private: Light* *lightList; Shape* *objectList; +#ifdef ENABLE_LUA_SUPPORT + lua_State *L; +#endif + public: World(); ~World(); diff --git a/source/pattern/luapattern.h b/source/pattern/luapattern.h new file mode 100644 index 0000000..e18570d --- /dev/null +++ b/source/pattern/luapattern.h @@ -0,0 +1,107 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Lua based Pattern header + * + * Created by Manoƫl Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_LUAPATTERN_H +#define DORAYME_LUAPATTERN_H + +#include +#include +#include + +#ifndef ENABLE_LUA_SUPPORT +#error Cannot use the Lua Pattern generator with no Lua support disabled! +#endif + +extern "C" { +#include +} + +class LuaPattern : public Pattern +{ +private: + lua_State *L; + char funcName[50]; + +public: + LuaPattern(Colour a, Colour b) : Pattern(a, b), L(nullptr) { }; + + void setLua(lua_State *L) { + this->L = L; + }; + + void setLuaFunctionName(const char *name) { + strncpy(this->funcName, name, 50); + } + + Colour patternAt(Tuple point) + { + int isnum; + double r, g, b; + + lua_getglobal(this->L, this->funcName); + lua_pushnumber(this->L, point.x); + lua_pushnumber(this->L, point.y); + lua_pushnumber(this->L, point.z); + + lua_createtable(L, 3, 0); + lua_pushnumber(L, this->a.x); + lua_setfield(L, -2, "r"); + lua_pushnumber(L, this->a.y); + lua_setfield(L, -2, "g"); + lua_pushnumber(L, this->a.z); + lua_setfield(L, -2, "b"); + + lua_createtable(L, 3, 0); + lua_pushnumber(L, this->b.x); + lua_setfield(L, -2, "r"); + lua_pushnumber(L, this->b.y); + lua_setfield(L, -2, "g"); + lua_pushnumber(L, this->b.z); + lua_setfield(L, -2, "b"); + + if (lua_pcall(this->L, 5, 3, 0) != LUA_OK) + { + printf("Error running the Lua function '%s': %s\n", this->funcName, + lua_tostring(this->L, -1)); + return Colour(0, 0, 0); + } + + r = lua_tonumberx(this->L, -3, &isnum); + if (!isnum) + { + printf("Error: function '%s' must return numbers\n", this->funcName); + lua_pop(this->L, 1); + return Colour(0, 0, 0); + } + g = lua_tonumberx(this->L, -2, &isnum); + if (!isnum) + { + printf("Error: function '%s' must return numbers\n", this->funcName); + lua_pop(this->L, 1); + return Colour(0, 0, 0); + } + b = lua_tonumberx(this->L, -1, &isnum); + if (!isnum) + { + printf("Error: function '%s' must return numbers\n", this->funcName); + lua_pop(this->L, 1); + return Colour(0, 0, 0); + } + + lua_pop(this->L, 1); + return Colour(r, g, b); + } + + void dumpMe(FILE *fp) { + fprintf(fp, "\"Type\": \"Lua\",\n"); + Pattern::dumpMe(fp); + } + +}; + +#endif /* DORAYME_LUAPATTERN_H */ diff --git a/source/world.cpp b/source/world.cpp index 1b743f9..15d5073 100644 --- a/source/world.cpp +++ b/source/world.cpp @@ -12,6 +12,14 @@ #include #include +#ifdef ENABLE_LUA_SUPPORT +extern "C" { +#include +#include +#include +} +#endif + #define MIN_ALLOC (2) World::World() : objectCount(0), lightCount(0) @@ -23,6 +31,11 @@ World::World() : objectCount(0), lightCount(0) this->allocatedObjectCount = MIN_ALLOC; this->objectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC); this->objectCount = 0; + +#ifdef ENABLE_LUA_SUPPORT + this->L = luaL_newstate(); /* opens Lua */ + luaL_openlibs(L); /* opens the basic library */ +#endif }; World::~World() diff --git a/tests/pattern_test.cpp b/tests/pattern_test.cpp index fd20681..57e6315 100644 --- a/tests/pattern_test.cpp +++ b/tests/pattern_test.cpp @@ -19,6 +19,15 @@ #include #include +#ifdef ENABLE_LUA_SUPPORT +extern "C" { +#include +#include +#include +} +#include +#endif + Colour black = Colour(0, 0, 0); Colour white = Colour(1, 1, 1); @@ -208,4 +217,47 @@ TEST(PatternTest, Checkers_should_repeat_in_z) ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white); ASSERT_EQ(pattern.patternAt(Point(0, 0, 0.99)), white); ASSERT_EQ(pattern.patternAt(Point(0, 0, 1.01)), black); -} \ No newline at end of file +} + +#ifdef ENABLE_LUA_SUPPORT +TEST(PatternTest, Simple_test_of_a_lua_pattern) +{ + int error; + LuaPattern pattern = LuaPattern(white, black); + lua_State *L = luaL_newstate(); + luaL_openlibs(L); + + pattern.setLua(L); + + pattern.setLuaFunctionName("pat"); + + error = luaL_loadstring(L, "function pat(x, y, z, a, b)\n" + " local v = math.floor(x) + math.floor(y) + math.floor(z)\n" + " if (v % 2) == 0 then\n" + " return a.r, a.g, a.b\n" + " end\n" + " return b.r, b.g, b.b\n" + "end"); + + if (error) + { + fprintf(stderr, "%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); /* pop error message from the stack */ + } + ASSERT_EQ(error, 0); + + error = lua_pcall(L, 0, 0, 0); + if (error) + { + fprintf(stderr, "%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); /* pop error message from the stack */ + } + ASSERT_EQ(error, 0); + + ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white); + ASSERT_EQ(pattern.patternAt(Point(0, 0.99, 0)), white); + ASSERT_EQ(pattern.patternAt(Point(0, 1.01, 0)), black); + + lua_close(L); +} +#endif \ No newline at end of file