Browse Source

Add support for Lua in world, and create the Lua Pattern (pattern can be defined with a lua function)

Godzil 4 years ago
parent
commit
aab9df0802
6 changed files with 199 additions and 3 deletions
  1. 5 0
      CMakeLists.txt
  2. 11 2
      source/CMakeLists.txt
  3. 10 0
      source/include/world.h
  4. 107 0
      source/pattern/luapattern.h
  5. 13 0
      source/world.cpp
  6. 53 1
      tests/pattern_test.cpp

+ 5 - 0
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()

+ 11 - 2
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()

+ 10 - 0
source/include/world.h

@@ -16,6 +16,12 @@
 #include <ray.h>
 #include <stdio.h>
 
+#ifdef ENABLE_LUA_SUPPORT
+extern "C" {
+#include <lua.h>
+}
+#endif
+
 class World
 {
 public:
@@ -29,6 +35,10 @@ private:
     Light* *lightList;
     Shape* *objectList;
 
+#ifdef ENABLE_LUA_SUPPORT
+    lua_State *L;
+#endif
+
 public:
     World();
     ~World();

+ 107 - 0
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 <pattern.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef ENABLE_LUA_SUPPORT
+#error Cannot use the Lua Pattern generator with no Lua support disabled!
+#endif
+
+extern "C" {
+#include <lua.h>
+}
+
+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 */

+ 13 - 0
source/world.cpp

@@ -12,6 +12,14 @@
 #include <stdio.h>
 #include <string.h>
 
+#ifdef ENABLE_LUA_SUPPORT
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+}
+#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()

+ 53 - 1
tests/pattern_test.cpp

@@ -19,6 +19,15 @@
 #include <gtest/gtest.h>
 #include <material.h>
 
+#ifdef ENABLE_LUA_SUPPORT
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+}
+#include <luapattern.h>
+#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);
-}
+}
+
+#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