Skip to content
Snippets Groups Projects
Select Git revision
  • 126fba4ddbe0e8c724dec690cb43315b276da90b
  • next default protected
  • expose-touching-sectorlist-to-lua
  • expose-texture-width-and-height
  • mobj-hit-floor-hook
  • wad-start-and-end-from-srb2classic
  • fix-emerald-hunt
  • woof-midi-port
  • fix-1425
  • expose-path-traverse
  • can-hurt-self-score-fix
  • post-mobj-thinker
  • hms-useragent
  • gitlab-ci
  • nightshoopsanity
  • fullscreen-toggle
  • next-test
  • bbox
  • cmake-enable-cxx
  • master
  • more-use-afters-frees
  • SRB2_release_2.2.10
  • SRB2_release_2.2.9
  • SRB2_release_2.2.8
  • SRB2_release_2.2.7
  • SRB2_release_2.2.6
  • SRB2_release_2.2.5
  • SRB2_release_2.2.4
  • SRB2_release_2.2.3
  • SRB2_release_2.2.2
  • SRB2_release_2.2.1
  • SRB2_release_2.2.0
  • SRB2_release_2.1.25
  • SRB2_release_2.1.24
  • SRB2_release_2.1.23
  • SRB2_release_2.1.22
  • SRB2_release_2.1.21
  • SRB2_release_2.1.20
  • SRB2_release_2.1.19
  • SRB2_release_2.1.18
  • td-release-v1.0.0
41 results

lua_colorlib.c

Blame
  • Forked from STJr / SRB2
    2595 commits behind the upstream repository.
    lua_colorlib.c 15.08 KiB
    // SONIC ROBO BLAST 2
    //-----------------------------------------------------------------------------
    // Copyright (C) 2021-2022 by "Lactozilla".
    // Copyright (C) 2014-2023 by Sonic Team Junior.
    //
    // This program is free software distributed under the
    // terms of the GNU General Public License, version 2.
    // See the 'LICENSE' file for more details.
    //-----------------------------------------------------------------------------
    /// \file  lua_colorlib.c
    /// \brief color and colormap libraries for Lua scripting
    
    #include "doomdef.h"
    #include "fastcmp.h"
    #include "r_data.h"
    #include "v_video.h"
    
    #include "lua_script.h"
    #include "lua_libs.h"
    
    #define COLORLIB_USE_LOOKUP
    
    #ifdef COLORLIB_USE_LOOKUP
    	static colorlookup_t colormix_lut;
    	#define GetNearestColor(r, g, b) GetColorLUT(&colormix_lut, r, g, b)
    #else
    	#define GetNearestColor(r, g, b) NearestPaletteColor(r, g, b, pMasterPalette)
    #endif
    
    ////////////////
    // Color library
    ////////////////
    
    static int lib_colorPaletteToRgb(lua_State *L)
    {
    	RGBA_t color = V_GetMasterColor((UINT8)luaL_checkinteger(L, 1));
    	lua_pushinteger(L, color.s.red);
    	lua_pushinteger(L, color.s.green);
    	lua_pushinteger(L, color.s.blue);
    	return 3;
    }
    
    #define IS_HEX_CHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F'))
    #define ARE_HEX_CHARS(str, i) IS_HEX_CHAR(str[i]) && IS_HEX_CHAR(str[i + 1])
    
    static UINT32 hex2int(char x)
    {
    	if (x >= '0' && x <= '9')
    		return x - '0';
    	else if (x >= 'a' && x <= 'f')
    		return x - 'a' + 10;
    	else if (x >= 'A' && x <= 'F')
    		return x - 'A' + 10;
    
    	return 0;
    }
    
    static UINT8 GetHTMLColorLength(const char *str)
    {
    	if (str[0] == '#')
    		str++;
    	return strlen(str) >= 8 ? 4 : 3;
    }
    
    static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc)
    {
    	const char *hex = str;
    
    	if (hex[0] == '#')
    		hex++;
    	else if (!IS_HEX_CHAR(hex[0]))
    		return 0;
    
    	size_t len = strlen(hex);
    
    	if (len == 3)
    	{
    		// Shorthand like #09C
    		for (unsigned i = 0; i < 3; i++)
    		{
    			if (!IS_HEX_CHAR(hex[i]))
    				return 0;
    
    			UINT32 hx = hex2int(hex[i]);
    			*rgba++ = (hx * 16) + hx;
    		}
    
    		return 3;
    	}
    	else if (len == 6 || len == 8)
    	{
    		if (numc != 4)
    			len = 6;
    
    		// A triplet like #0099CC
    		for (unsigned i = 0; i < len; i += 2)
    		{
    			if (!ARE_HEX_CHARS(hex, i))
    				return false;
    
    			*rgba++ = (hex2int(hex[i]) * 16) + hex2int(hex[i + 1]);
    		}
    
    		return len;
    	}
    
    	return 0;
    }
    
    static UINT8 GetPackedRGBA(UINT32 colors, UINT8 *rgba)
    {
    	if (colors > 0xFFFFFF)
    	{
    		rgba[0] = (colors >> 24) & 0xFF;
    		rgba[1] = (colors >> 16) & 0xFF;
    		rgba[2] = (colors >> 8) & 0xFF;
    		rgba[3] = colors & 0xFF;
    		return 4;
    	}
    	else
    	{
    		rgba[0] = (colors >> 16) & 0xFF;
    		rgba[1] = (colors >> 8) & 0xFF;
    		rgba[2] = colors & 0xFF;
    		rgba[3] = 0xFF;
    		return 3;
    	}
    }
    
    static UINT8 GetArgsRGBA(lua_State *L, UINT8 index, INT32 *r, INT32 *g, INT32 *b, INT32 *a)
    {
    	UINT8 rgba[4] = { 0, 0, 0, 255 };
    	UINT8 num = 0;
    
    	if (lua_gettop(L) == 1 && lua_type(L, index) == LUA_TNUMBER)
    	{
    		num = GetPackedRGBA(luaL_checkinteger(L, 1), rgba);
    
    		*r = rgba[0];
    		*g = rgba[1];
    		*b = rgba[2];
    		if (a)
    			*a = rgba[3];
    	}
    	else if (lua_type(L, index) == LUA_TSTRING)
    	{
    		const char *str = lua_tostring(L, index);
    		UINT8 parsed = ParseHTMLColor(str, rgba, GetHTMLColorLength(str));
    		if (!parsed)
    			luaL_error(L, "Malformed HTML color '%s'", str);
    
    		num = parsed == 8 ? 4 : 3;
    
    		*r = rgba[0];
    		*g = rgba[1];
    		*b = rgba[2];
    		if (a)
    			*a = rgba[3];
    	}
    	else
    	{
    		INT32 temp;
    
    #define CHECKINT(i) luaL_checkinteger(L, i)
    #define GETCOLOR(c, i, desc) { \
    			temp = CHECKINT(i); \
    			if (temp < 0 || temp > 255) \
    				luaL_error(L, desc " channel %d out of range (0 - 255)", temp); \
    			c = temp; \
    			num++; \
    		}
    
    		GETCOLOR(*r, index + 0, "red color");
    		GETCOLOR(*g, index + 1, "green color");
    		GETCOLOR(*b, index + 2, "blue color");
    #undef CHECKINT
    #define CHECKINT(i) luaL_optinteger(L, i, 255)
    		if (a)
    			GETCOLOR(*a, index + 3, "alpha");
    #undef CHECKINT
    #undef GETCOLOR
    
    		num = 3 + (lua_type(L, index + 3) == LUA_TNUMBER);
    	}
    
    	return num;
    }
    
    static int lib_colorRgbToPalette(lua_State *L)
    {
    	INT32 r, g, b;
    	GetArgsRGBA(L, 1, &r, &g, &b, NULL);
    
    #ifdef COLORLIB_USE_LOOKUP
    	InitColorLUT(&colormix_lut, pMasterPalette, false);
    #endif
    
    	lua_pushinteger(L, GetNearestColor(r, g, b));
    	return 1;
    }
    
    static void GetHSLFromTable(lua_State *L, UINT32 index, UINT8 *hsl)
    {
    	lua_pushnil(L);
    
    	while (lua_next(L, index)) {
    		lua_Integer i = 0;
    		const char *field = NULL;
    		if (lua_isnumber(L, -2))
    			i = lua_tointeger(L, -2);
    		else
    			field = luaL_checkstring(L, -2);
    
    #define CHECKFIELD(p, c) (i == p || (field && fastcmp(field, c)))
    #define HSLSET(p, c) { \
    			INT32 val = luaL_checkinteger(L, -1); \
    			if (val < 0 || val > 255) \
    				luaL_error(L, c " %d out of range (0 - 255)", val); \
    			hsl[p] = (UINT8)val; \
    		}
    
    		if (CHECKFIELD(1, "h")) {
    			HSLSET(0, "hue");
    		} else if (CHECKFIELD(2, "s")) {
    			HSLSET(1, "saturation");
    		} else if (CHECKFIELD(3, "l")) {
    			HSLSET(2, "lightness");
    		}
    
    #undef CHECKFIELD
    #undef HSLSET
    
    		lua_pop(L, 1);
    	}
    }
    
    #define SCALE_UINT8_TO_FIXED(val) FixedDiv(val * FRACUNIT, 255 * FRACUNIT)
    #define SCALE_FIXED_TO_UINT8(val) FixedRound(FixedMul(val, 255 * FRACUNIT)) / FRACUNIT
    
    static fixed_t hue2rgb(fixed_t p, fixed_t q, fixed_t t)
    {
    	if (t < 0)
    		t += FRACUNIT;
    	if (t > FRACUNIT)
    		t -= FRACUNIT;
    
    	fixed_t out;
    
    	if (t < FRACUNIT / 6)
    		out = p + FixedMul(FixedMul(q - p, 6 * FRACUNIT), t);
    	else if (t < FRACUNIT / 2)
    		out = q;
    	else if (t < 2 * FRACUNIT / 3)
    		out = p + FixedMul(FixedMul(q - p, 2 * FRACUNIT / 3 - t), 6 * FRACUNIT);
    	else
    		out = p;
    
    	return out;
    }
    
    static int lib_colorHslToRgb(lua_State *L)
    {
    	fixed_t h, s, l;
    
    	if (lua_istable(L, 1))
    	{
    		UINT8 hsl[3] = { 0, 0, 0 };
    		GetHSLFromTable(L, 1, hsl);
    		h = hsl[0];
    		s = hsl[1];
    		l = hsl[2];
    	}
    	else
    	{
    #define GETHSL(c, i, desc) \
    		c = luaL_checkinteger(L, i); \
    		if (c < 0 || c > 255) \
    			luaL_error(L, desc " %d out of range (0 - 255)", c)
    
    		GETHSL(h, 1, "hue");
    		GETHSL(s, 2, "saturation");
    		GETHSL(l, 3, "value");
    #undef GETHSL
    	}
    
    	if (!s)
    	{
    		lua_pushinteger(L, l);
    		lua_pushinteger(L, l);
    		lua_pushinteger(L, l);
    	}
    	else
    	{
    		h = SCALE_UINT8_TO_FIXED(h);
    		s = SCALE_UINT8_TO_FIXED(s);
    		l = SCALE_UINT8_TO_FIXED(l);
    
    		fixed_t q, p;
    
    		if (l < FRACUNIT/2)
    			q = FixedMul(l, FRACUNIT + s);
    		else
    			q = l + s - FixedMul(l, s);
    
    		p = l * 2 - q;
    
    		lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h + FRACUNIT/3)));
    		lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h)));
    		lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h - FRACUNIT/3)));
    	}
    
    	return 3;
    }
    
    static int lib_colorRgbToHsl(lua_State *L)
    {
    	INT32 ir, ig, ib;
    	GetArgsRGBA(L, 1, &ir, &ig, &ib, NULL);
    
    	fixed_t r = SCALE_UINT8_TO_FIXED(ir);
    	fixed_t g = SCALE_UINT8_TO_FIXED(ig);
    	fixed_t b = SCALE_UINT8_TO_FIXED(ib);
    
    	fixed_t cmin = min(min(r, g), b);
    	fixed_t cmax = max(max(r, g), b);
    
    	fixed_t h, s, l = (cmax + cmin) / 2;
    	fixed_t delta = cmax - cmin;
    
    	if (!delta)
    		h = s = 0;
    	else
    	{
    		if (l > FRACUNIT / 2)
    			s = FixedDiv(delta, (FRACUNIT * 2) - cmax - cmin);
    		else
    			s = FixedDiv(delta, cmax + cmin);
    
    		if (r > g && r > b)
    		{
    			h = FixedDiv(g - b, delta);
    
    			if (g < b)
    				h += FRACUNIT * 6;
    		}
    		else
    		{
    			h = FixedDiv(r - g, delta);
    
    			if (g > b)
    				h += FRACUNIT * 2;
    			else
    				h += FRACUNIT * 4;
    		}
    
    		h = FixedDiv(h, FRACUNIT * 6);
    	}
    
    	lua_pushinteger(L, SCALE_FIXED_TO_UINT8(h));
    	lua_pushinteger(L, SCALE_FIXED_TO_UINT8(s));
    	lua_pushinteger(L, SCALE_FIXED_TO_UINT8(l));
    
    	return 3;
    }
    
    static int lib_colorHexToRgb(lua_State *L)
    {
    	UINT8 rgba[4] = { 0, 0, 0, 255 };
    
    	const char *str = luaL_checkstring(L, 1);
    	UINT8 parsed = ParseHTMLColor(str, rgba, 4), num = 3;
    	if (!parsed)
    		luaL_error(L, "Malformed HTML color '%s'", str);
    	else if (parsed == 8)
    		num++;
    
    	lua_pushinteger(L, rgba[0]);
    	lua_pushinteger(L, rgba[1]);
    	lua_pushinteger(L, rgba[2]);
    	if (num == 4)
    		lua_pushinteger(L, rgba[3]);
    
    	return num;
    }
    
    static int lib_colorRgbToHex(lua_State *L)
    {
    	INT32 r, g, b, a;
    	UINT8 num = GetArgsRGBA(L, 1, &r, &g, &b, &a);
    
    	char buffer[10];
    	if (num >= 4)
    		snprintf(buffer, sizeof buffer, "#%02X%02X%02X%02X", r, g, b, a);
    	else
    		snprintf(buffer, sizeof buffer, "#%02X%02X%02X", r, g, b);
    
    	lua_pushstring(L, buffer);
    	return 1;
    }
    
    static int lib_colorPackRgb(lua_State *L)
    {
    	INT32 r, g, b;
    
    	GetArgsRGBA(L, 1, &r, &g, &b, NULL);
    
    	UINT32 packed = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
    
    	lua_pushinteger(L, packed);
    	return 1;
    }
    
    static int lib_colorPackRgba(lua_State *L)
    {
    	INT32 r, g, b, a;
    
    	GetArgsRGBA(L, 1, &r, &g, &b, &a);
    
    	UINT32 packed = ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF);
    
    	lua_pushinteger(L, packed);
    	return 1;
    }
    
    static int lib_colorUnpackRgba(lua_State *L)
    {
    	UINT8 rgba[4];
    
    	UINT8 num = GetPackedRGBA(lua_tointeger(L, 1), rgba);
    
    	for (UINT8 i = 0; i < num; i++)
    	{
    		lua_pushinteger(L, rgba[i]);
    	}
    
    	return num;
    }
    
    static luaL_Reg color_lib[] = {
    	{"paletteToRgb", lib_colorPaletteToRgb},
    	{"rgbToPalette", lib_colorRgbToPalette},
    	{"hslToRgb", lib_colorHslToRgb},
    	{"rgbToHsl", lib_colorRgbToHsl},
    	{"hexToRgb", lib_colorHexToRgb},
    	{"rgbToHex", lib_colorRgbToHex},
    	{"packRgb", lib_colorPackRgb},
    	{"packRgba", lib_colorPackRgba},
    	{"unpackRgba", lib_colorUnpackRgba},
    	{NULL, NULL}
    };
    
    /////////////////////////
    // extracolormap userdata
    /////////////////////////
    
    enum extracolormap_e {
    	extracolormap_red = 0,
    	extracolormap_green,
    	extracolormap_blue,
    	extracolormap_alpha,
    	extracolormap_color,
    	extracolormap_fade_red,
    	extracolormap_fade_green,
    	extracolormap_fade_blue,
    	extracolormap_fade_alpha,
    	extracolormap_fade_color,
    	extracolormap_fade_start,
    	extracolormap_fade_end,
    	extracolormap_colormap
    };
    
    static const char *const extracolormap_opt[] = {
    	"red",
    	"green",
    	"blue",
    	"alpha",
    	"color",
    	"fade_red",
    	"fade_green",
    	"fade_blue",
    	"fade_alpha",
    	"fade_color",
    	"fade_start",
    	"fade_end",
    	"colormap",
    	NULL};
    
    static int extracolormap_get(lua_State *L)
    {
    	extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
    	enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt);
    
    	switch (field)
    	{
    	case extracolormap_red:
    		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
    		break;
    	case extracolormap_green:
    		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
    		break;
    	case extracolormap_blue:
    		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
    		break;
    	case extracolormap_alpha:
    		lua_pushinteger(L, R_GetRgbaA(exc->rgba));
    		break;
    	case extracolormap_color:
    		lua_pushinteger(L, R_GetRgbaR(exc->rgba));
    		lua_pushinteger(L, R_GetRgbaG(exc->rgba));
    		lua_pushinteger(L, R_GetRgbaB(exc->rgba));
    		lua_pushinteger(L, R_GetRgbaA(exc->rgba));
    		return 4;
    	case extracolormap_fade_red:
    		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
    		break;
    	case extracolormap_fade_green:
    		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
    		break;
    	case extracolormap_fade_blue:
    		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
    		break;
    	case extracolormap_fade_alpha:
    		lua_pushinteger(L, R_GetRgbaA(exc->fadergba));
    		break;
    	case extracolormap_fade_color:
    		lua_pushinteger(L, R_GetRgbaR(exc->fadergba));
    		lua_pushinteger(L, R_GetRgbaG(exc->fadergba));
    		lua_pushinteger(L, R_GetRgbaB(exc->fadergba));
    		lua_pushinteger(L, R_GetRgbaA(exc->fadergba));
    		return 4;
    	case extracolormap_fade_start:
    		lua_pushinteger(L, exc->fadestart);
    		break;
    	case extracolormap_fade_end:
    		lua_pushinteger(L, exc->fadeend);
    		break;
    	case extracolormap_colormap:
    		LUA_PushUserdata(L, exc->colormap, META_LIGHTTABLE);
    		break;
    	}
    	return 1;
    }
    
    static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba, int arg)
    {
    	if (lua_type(L, arg) == LUA_TSTRING)
    	{
    		const char *str = lua_tostring(L, arg);
    		UINT8 parsed = ParseHTMLColor(str, rgba, 4);
    		if (!parsed)
    			luaL_error(L, "Malformed HTML color '%s'", str);
    	}
    	else
    	{
    		GetPackedRGBA(lua_tointeger(L, arg), rgba);
    	}
    }
    
    static int extracolormap_set(lua_State *L)
    {
    	extracolormap_t *exc = *((extracolormap_t **)luaL_checkudata(L, 1, META_EXTRACOLORMAP));
    	enum extracolormap_e field = luaL_checkoption(L, 2, NULL, extracolormap_opt);
    
    	UINT8 r = R_GetRgbaR(exc->rgba);
    	UINT8 g = R_GetRgbaG(exc->rgba);
    	UINT8 b = R_GetRgbaB(exc->rgba);
    	UINT8 a = R_GetRgbaA(exc->rgba);
    
    	UINT8 fr = R_GetRgbaR(exc->fadergba);
    	UINT8 fg = R_GetRgbaG(exc->fadergba);
    	UINT8 fb = R_GetRgbaB(exc->fadergba);
    	UINT8 fa = R_GetRgbaA(exc->fadergba);
    
    	UINT8 rgba[4];
    
    	INT32 old_rgba = exc->rgba, old_fade_rgba = exc->fadergba; // It's not unsigned?
    	UINT8 old_fade_start = exc->fadestart, old_fade_end = exc->fadeend;
    
    #define val luaL_checkinteger(L, 3)
    
    	switch(field)
    	{
    	case extracolormap_red:
    		exc->rgba = R_PutRgbaRGBA(val, g, b, a);
    		break;
    	case extracolormap_green:
    		exc->rgba = R_PutRgbaRGBA(r, val, b, a);
    		break;
    	case extracolormap_blue:
    		exc->rgba = R_PutRgbaRGBA(r, g, val, a);
    		break;
    	case extracolormap_alpha:
    		exc->rgba = R_PutRgbaRGBA(r, g, b, val);
    		break;
    	case extracolormap_color:
    		rgba[0] = r;
    		rgba[1] = g;
    		rgba[2] = b;
    		rgba[3] = a;
    		GetExtraColormapRGBA(L, rgba, 3);
    		exc->rgba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
    		break;
    	case extracolormap_fade_red:
    		exc->fadergba = R_PutRgbaRGBA(val, fg, fb, fa);
    		break;
    	case extracolormap_fade_green:
    		exc->fadergba = R_PutRgbaRGBA(fr, val, fb, fa);
    		break;
    	case extracolormap_fade_blue:
    		exc->fadergba = R_PutRgbaRGBA(fr, fg, val, fa);
    		break;
    	case extracolormap_fade_alpha:
    		exc->fadergba = R_PutRgbaRGBA(fr, fg, fb, val);
    		break;
    	case extracolormap_fade_color:
    		rgba[0] = fr;
    		rgba[1] = fg;
    		rgba[2] = fb;
    		rgba[3] = fa;
    		GetExtraColormapRGBA(L, rgba, 3);
    		exc->fadergba = R_PutRgbaRGBA(rgba[0], rgba[1], rgba[2], rgba[3]);
    		break;
    	case extracolormap_fade_start:
    		if (val > 31)
    			return luaL_error(L, "fade start %d out of range (0 - 31)", val);
    		exc->fadestart = val;
    		break;
    	case extracolormap_fade_end:
    		if (val > 31)
    			return luaL_error(L, "fade end %d out of range (0 - 31)", val);
    		exc->fadeend = val;
    		break;
    	case extracolormap_colormap:
    		return luaL_error(L, LUA_QL("extracolormap_t") " field " LUA_QS " should not be set directly.", extracolormap_opt[field]);
    	}
    
    #undef val
    
    	if (exc->rgba != old_rgba
    		|| exc->fadergba != old_fade_rgba
    		|| exc->fadestart != old_fade_start
    		|| exc->fadeend != old_fade_end)
    	R_GenerateLightTable(exc, true);
    
    	return 0;
    }
    
    static int lighttable_get(lua_State *L)
    {
    	void **userdata;
    
    	lighttable_t *table = *((lighttable_t **)luaL_checkudata(L, 1, META_LIGHTTABLE));
    	UINT32 row = luaL_checkinteger(L, 2);
    	if (row < 1 || row > 34)
    		return luaL_error(L, "lighttable row %d out of range (1 - %d)", row, 34);
    
    	userdata = lua_newuserdata(L, sizeof(void *));
    	*userdata = &table[256 * (row - 1)];
    	luaL_getmetatable(L, META_COLORMAP);
    	lua_setmetatable(L, -2);
    
    	return 1;
    }
    
    static int lighttable_len(lua_State *L)
    {
    	lua_pushinteger(L, NUM_PALETTE_ENTRIES);
    	return 1;
    }
    
    int LUA_ColorLib(lua_State *L)
    {
    	luaL_newmetatable(L, META_EXTRACOLORMAP);
    		lua_pushcfunction(L, extracolormap_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, extracolormap_set);
    		lua_setfield(L, -2, "__newindex");
    	lua_pop(L, 1);
    
    	luaL_newmetatable(L, META_LIGHTTABLE);
    		lua_pushcfunction(L, lighttable_get);
    		lua_setfield(L, -2, "__index");
    
    		lua_pushcfunction(L, lighttable_len);
    		lua_setfield(L, -2, "__len");
    	lua_pop(L, 1);
    
    	luaL_register(L, "color", color_lib);
    
    	return 0;
    }