1
0
Fork 0
mirror of https://github.com/bjornbytes/lovr.git synced 2024-07-26 23:53:33 +00:00
lovr/src/api/graphics.c

1474 lines
41 KiB
C
Raw Normal View History

2017-12-10 20:40:37 +00:00
#include "api.h"
2018-09-27 00:54:12 +00:00
#include "api/data.h"
#include "api/graphics.h"
2018-09-27 18:45:43 +00:00
#include "api/math.h"
2016-11-19 09:28:01 +00:00
#include "graphics/graphics.h"
2017-11-03 06:47:13 +00:00
#include "graphics/animator.h"
2017-12-07 07:50:52 +00:00
#include "graphics/canvas.h"
2017-10-21 21:05:58 +00:00
#include "graphics/material.h"
2017-08-09 05:34:23 +00:00
#include "graphics/mesh.h"
#include "graphics/model.h"
2018-02-11 23:22:04 +00:00
#include "data/modelData.h"
2018-01-22 16:28:33 +00:00
#include "data/rasterizer.h"
2018-02-11 23:22:04 +00:00
#include "data/textureData.h"
2018-02-11 01:27:29 +00:00
#include "data/vertexData.h"
2016-11-19 09:28:01 +00:00
#include "filesystem/filesystem.h"
2018-09-27 18:45:43 +00:00
#include "util.h"
2018-09-28 03:32:45 +00:00
#define _USE_MATH_DEFINES
2016-10-29 22:17:49 +00:00
#include <math.h>
2017-10-31 08:14:09 +00:00
#include <stdbool.h>
2016-08-10 06:28:17 +00:00
2018-07-05 03:11:52 +00:00
const char* ArcModes[] = {
[ARC_MODE_PIE] = "pie",
[ARC_MODE_OPEN] = "open",
[ARC_MODE_CLOSED] = "closed",
NULL
};
const char* AttributeTypes[] = {
[ATTR_FLOAT] = "float",
[ATTR_BYTE] = "byte",
[ATTR_INT] = "int",
NULL
};
const char* BlendAlphaModes[] = {
[BLEND_ALPHA_MULTIPLY] = "alphamultiply",
[BLEND_PREMULTIPLIED] = "premultiplied",
NULL
};
const char* BlendModes[] = {
[BLEND_ALPHA] = "alpha",
[BLEND_ADD] = "add",
[BLEND_SUBTRACT] = "subtract",
[BLEND_MULTIPLY] = "multiply",
[BLEND_LIGHTEN] = "lighten",
[BLEND_DARKEN] = "darken",
[BLEND_SCREEN] = "screen",
NULL
};
2019-01-16 16:52:21 +00:00
const char* BlockTypes[] = {
[BLOCK_UNIFORM] = "uniform",
[BLOCK_COMPUTE] = "compute",
NULL
};
const char* BufferUsages[] = {
[USAGE_STATIC] = "static",
[USAGE_DYNAMIC] = "dynamic",
[USAGE_STREAM] = "stream",
NULL
};
2018-07-05 03:11:52 +00:00
const char* CompareModes[] = {
2018-07-17 21:53:21 +00:00
[COMPARE_NONE] = "always",
2018-07-05 03:11:52 +00:00
[COMPARE_EQUAL] = "equal",
[COMPARE_NEQUAL] = "notequal",
[COMPARE_LESS] = "less",
[COMPARE_LEQUAL] = "lequal",
[COMPARE_GREATER] = "greater",
[COMPARE_GEQUAL] = "gequal",
NULL
};
const char* DrawModes[] = {
[DRAW_POINTS] = "points",
[DRAW_LINES] = "lines",
[DRAW_LINE_STRIP] = "linestrip",
[DRAW_LINE_LOOP] = "lineloop",
[DRAW_TRIANGLE_STRIP] = "strip",
[DRAW_TRIANGLES] = "triangles",
[DRAW_TRIANGLE_FAN] = "fan",
NULL
};
const char* DrawStyles[] = {
[STYLE_FILL] = "fill",
[STYLE_LINE] = "line",
2018-07-05 03:11:52 +00:00
NULL
};
const char* FilterModes[] = {
[FILTER_NEAREST] = "nearest",
[FILTER_BILINEAR] = "bilinear",
[FILTER_TRILINEAR] = "trilinear",
[FILTER_ANISOTROPIC] = "anisotropic",
NULL
};
const char* HorizontalAligns[] = {
[ALIGN_LEFT] = "left",
[ALIGN_CENTER] = "center",
[ALIGN_RIGHT] = "right",
2018-07-05 03:11:52 +00:00
NULL
};
const char* MaterialColors[] = {
[COLOR_DIFFUSE] = "diffuse",
[COLOR_EMISSIVE] = "emissive",
NULL
};
const char* MaterialScalars[] = {
[SCALAR_METALNESS] = "metalness",
[SCALAR_ROUGHNESS] = "roughness",
NULL
};
const char* MaterialTextures[] = {
[TEXTURE_DIFFUSE] = "diffuse",
[TEXTURE_EMISSIVE] = "emissive",
[TEXTURE_METALNESS] = "metalness",
[TEXTURE_ROUGHNESS] = "roughness",
[TEXTURE_OCCLUSION] = "occlusion",
[TEXTURE_NORMAL] = "normal",
[TEXTURE_ENVIRONMENT_MAP] = "environment",
NULL
};
2018-08-07 23:58:14 +00:00
const char* ShaderTypes[] = {
[SHADER_GRAPHICS] = "graphics",
[SHADER_COMPUTE] = "compute",
NULL
};
2018-07-05 03:11:52 +00:00
const char* StencilActions[] = {
[STENCIL_REPLACE] = "replace",
[STENCIL_INCREMENT] = "increment",
[STENCIL_DECREMENT] = "decrement",
[STENCIL_INCREMENT_WRAP] = "incrementwrap",
[STENCIL_DECREMENT_WRAP] = "decrementwrap",
[STENCIL_INVERT] = "invert",
NULL
};
const char* TextureFormats[] = {
[FORMAT_RGB] = "rgb",
[FORMAT_RGBA] = "rgba",
2018-07-21 00:45:25 +00:00
[FORMAT_RGBA4] = "rgba4",
2018-07-05 03:11:52 +00:00
[FORMAT_RGBA16F] = "rgba16f",
[FORMAT_RGBA32F] = "rgba32f",
2018-07-21 00:45:25 +00:00
[FORMAT_R16F] = "r16f",
[FORMAT_R32F] = "r32f",
2018-08-16 21:54:37 +00:00
[FORMAT_RG16F] = "rg16f",
[FORMAT_RG32F] = "rg32f",
2018-07-21 00:45:25 +00:00
[FORMAT_RGB5A1] = "rgb5a1",
2018-12-16 01:35:57 +00:00
[FORMAT_RGB10A2] = "rgb10a2",
2018-07-05 03:11:52 +00:00
[FORMAT_RG11B10F] = "rg11b10f",
[FORMAT_D16] = "d16",
[FORMAT_D32F] = "d32f",
[FORMAT_D24S8] = "d24s8",
2018-07-05 03:11:52 +00:00
[FORMAT_DXT1] = "dxt1",
[FORMAT_DXT3] = "dxt3",
[FORMAT_DXT5] = "dxt5",
NULL
};
const char* TextureTypes[] = {
[TEXTURE_2D] = "2d",
[TEXTURE_ARRAY] = "array",
[TEXTURE_CUBE] = "cube",
[TEXTURE_VOLUME] = "volume",
NULL
};
2018-08-17 22:09:13 +00:00
const char* UniformAccesses[] = {
[ACCESS_READ] = "read",
[ACCESS_WRITE] = "write",
[ACCESS_READ_WRITE] = "readwrite"
};
2018-07-05 03:11:52 +00:00
const char* VerticalAligns[] = {
[ALIGN_TOP] = "top",
[ALIGN_MIDDLE] = "middle",
[ALIGN_BOTTOM] = "bottom",
2018-07-05 03:11:52 +00:00
NULL
};
const char* Windings[] = {
[WINDING_CLOCKWISE] = "clockwise",
[WINDING_COUNTERCLOCKWISE] = "counterclockwise",
NULL
};
const char* WrapModes[] = {
[WRAP_CLAMP] = "clamp",
[WRAP_REPEAT] = "repeat",
[WRAP_MIRRORED_REPEAT] = "mirroredrepeat",
NULL
};
2017-03-11 11:08:07 +00:00
static uint32_t luax_getvertexcount(lua_State* L, int index) {
int type = lua_type(L, index);
if (type == LUA_TTABLE) {
size_t count = lua_objlen(L, index);
lua_rawgeti(L, index, 1);
int tableType = lua_type(L, -1);
lua_pop(L, 1);
return tableType == LUA_TNUMBER ? count / 3 : count;
} else if (type == LUA_TNUMBER) {
return (lua_gettop(L) - index + 1) / 3;
} else {
return lua_gettop(L) - index + 1;
}
}
static void luax_readvertices(lua_State* L, int index, float* vertices, uint32_t count) {
switch (lua_type(L, index)) {
case LUA_TTABLE:
lua_rawgeti(L, index, 1);
if (lua_type(L, -1) == LUA_TNUMBER) {
lua_pop(L, 1);
for (uint32_t i = 0; i < count; i++) {
for (int j = 1; j <= 3; j++) {
lua_rawgeti(L, index, 3 * i + j);
2018-12-07 23:57:45 +00:00
vertices[j] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
2018-12-07 23:57:45 +00:00
vertices += 8;
}
} else {
2018-03-22 05:03:03 +00:00
lua_pop(L, 1);
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, index, i + 1);
2018-12-07 23:57:45 +00:00
vec3_init(vertices, luax_checkmathtype(L, -1, MATH_VEC3, NULL));
lua_pop(L, 1);
2018-12-07 23:57:45 +00:00
vertices += 8;
}
2018-03-22 05:03:03 +00:00
}
break;
2018-03-22 05:03:03 +00:00
case LUA_TNUMBER:
for (uint32_t i = 0; i < count; i++) {
for (int j = 0; j < 3; j++) {
vertices[j] = lua_tonumber(L, index + 3 * i + j);
}
2018-12-07 23:57:45 +00:00
vertices += 8;
}
break;
default:
for (uint32_t i = 0; i < count; i++) {
vec3_init(vertices, luax_checkmathtype(L, index + i, MATH_VEC3, NULL));
2018-12-07 23:57:45 +00:00
vertices += 8;
}
break;
2016-11-08 07:16:33 +00:00
}
}
2017-12-19 03:48:28 +00:00
static void stencilCallback(void* userdata) {
lua_State* L = userdata;
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_call(L, 0, 0);
}
2018-09-04 03:59:12 +00:00
static TextureData* luax_checktexturedata(lua_State* L, int index, bool flip) {
TextureData* textureData = luax_totype(L, index, TextureData);
if (!textureData) {
2018-01-22 16:14:18 +00:00
Blob* blob = luax_readblob(L, index, "Texture");
2018-09-04 03:59:12 +00:00
textureData = lovrTextureDataCreateFromBlob(blob, flip);
2018-02-26 08:59:03 +00:00
lovrRelease(blob);
2018-01-22 16:14:18 +00:00
}
return textureData;
2018-01-22 16:14:18 +00:00
}
2016-11-23 05:16:13 +00:00
// Base
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsPresent(lua_State* L) {
2016-08-10 06:28:17 +00:00
lovrGraphicsPresent();
return 0;
}
static int l_lovrGraphicsCreateWindow(lua_State* L) {
2018-11-16 10:26:56 +00:00
WindowFlags flags = { 0 };
luaL_checktype(L, 1, LUA_TTABLE);
lua_getfield(L, 1, "width");
flags.width = luaL_optinteger(L, -1, 1080);
lua_pop(L, 1);
lua_getfield(L, 1, "height");
flags.height = luaL_optinteger(L, -1, 600);
lua_pop(L, 1);
lua_getfield(L, 1, "fullscreen");
flags.fullscreen = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "msaa");
flags.msaa = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, 1, "title");
flags.title = luaL_optstring(L, -1, "LÖVR");
lua_pop(L, 1);
lua_getfield(L, 1, "icon");
TextureData* textureData = NULL;
if (!lua_isnil(L, -1)) {
textureData = luax_checktexturedata(L, -1, true);
flags.icon.data = textureData->blob.data;
flags.icon.width = textureData->width;
flags.icon.height = textureData->height;
}
lua_pop(L, 1);
lovrGraphicsCreateWindow(&flags);
luax_atexit(L, lovrGraphicsDestroy); // The lua_State that creates the window shall be the one to destroy it
2018-11-16 10:26:56 +00:00
lovrRelease(textureData);
2017-08-10 03:02:02 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetWidth(lua_State* L) {
2018-08-29 06:05:09 +00:00
lua_pushnumber(L, lovrGraphicsGetWidth());
2017-08-10 03:02:02 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetHeight(lua_State* L) {
2018-08-29 06:05:09 +00:00
lua_pushnumber(L, lovrGraphicsGetHeight());
2017-08-10 03:02:02 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetDimensions(lua_State* L) {
2018-08-29 06:05:09 +00:00
lua_pushnumber(L, lovrGraphicsGetWidth());
lua_pushnumber(L, lovrGraphicsGetHeight());
2017-08-10 03:02:02 +00:00
return 2;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetSupported(lua_State* L) {
2018-08-31 13:03:35 +00:00
const GpuFeatures* features = lovrGraphicsGetSupported();
2018-08-03 18:43:15 +00:00
lua_newtable(L);
2019-01-16 16:52:21 +00:00
lua_pushboolean(L, features->compute);
lua_setfield(L, -2, "compute");
2018-08-31 13:03:35 +00:00
lua_pushboolean(L, features->singlepass);
lua_setfield(L, -2, "singlepass");
2018-08-03 18:43:15 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetSystemLimits(lua_State* L) {
2018-08-31 13:03:35 +00:00
const GpuLimits* limits = lovrGraphicsGetLimits();
2018-07-16 00:05:06 +00:00
lua_newtable(L);
2018-08-31 13:03:35 +00:00
lua_pushnumber(L, limits->pointSizes[1]);
2018-07-16 00:05:06 +00:00
lua_setfield(L, -2, "pointsize");
2018-08-31 13:03:35 +00:00
lua_pushinteger(L, limits->textureSize);
2018-07-16 00:05:06 +00:00
lua_setfield(L, -2, "texturesize");
2018-08-31 13:03:35 +00:00
lua_pushinteger(L, limits->textureMSAA);
2018-07-16 00:05:06 +00:00
lua_setfield(L, -2, "texturemsaa");
2018-08-31 13:03:35 +00:00
lua_pushinteger(L, limits->textureAnisotropy);
2018-07-16 00:05:06 +00:00
lua_setfield(L, -2, "anisotropy");
lua_pushinteger(L, limits->blockSize);
lua_setfield(L, -2, "blocksize");
2018-07-16 00:05:06 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetStats(lua_State* L) {
2017-12-10 04:07:32 +00:00
if (lua_gettop(L) > 0) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1);
} else {
lua_createtable(L, 0, 2);
}
2018-08-31 13:03:35 +00:00
const GpuStats* stats = lovrGraphicsGetStats();
lua_pushinteger(L, stats->drawCalls);
2017-12-10 04:07:32 +00:00
lua_setfield(L, 1, "drawcalls");
2018-08-31 13:03:35 +00:00
lua_pushinteger(L, stats->shaderSwitches);
2017-12-10 04:07:32 +00:00
lua_setfield(L, 1, "shaderswitches");
return 1;
}
2016-11-23 05:16:13 +00:00
// State
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsReset(lua_State* L) {
2018-07-16 00:05:06 +00:00
lovrGraphicsReset();
return 0;
}
static int l_lovrGraphicsGetAlphaSampling(lua_State* L) {
lua_pushboolean(L, lovrGraphicsGetAlphaSampling());
return 1;
}
static int l_lovrGraphicsSetAlphaSampling(lua_State* L) {
lovrGraphicsSetAlphaSampling(lua_toboolean(L, 1));
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetBackgroundColor(lua_State* L) {
2017-08-02 08:25:56 +00:00
Color color = lovrGraphicsGetBackgroundColor();
lua_pushnumber(L, color.r);
lua_pushnumber(L, color.g);
lua_pushnumber(L, color.b);
lua_pushnumber(L, color.a);
2016-08-10 06:28:17 +00:00
return 4;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetBackgroundColor(lua_State* L) {
Color color = luax_checkcolor(L, 1);
2017-08-02 08:25:56 +00:00
lovrGraphicsSetBackgroundColor(color);
2016-08-10 06:28:17 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetBlendMode(lua_State* L) {
2017-03-12 11:03:36 +00:00
BlendMode mode;
BlendAlphaMode alphaMode;
lovrGraphicsGetBlendMode(&mode, &alphaMode);
2018-07-05 03:11:52 +00:00
lua_pushstring(L, BlendModes[mode]);
lua_pushstring(L, BlendAlphaModes[alphaMode]);
2017-03-12 11:03:36 +00:00
return 2;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetBlendMode(lua_State* L) {
BlendMode mode = lua_isnoneornil(L, 1) ? BLEND_NONE : luaL_checkoption(L, 1, NULL, BlendModes);
2018-07-05 03:11:52 +00:00
BlendAlphaMode alphaMode = luaL_checkoption(L, 2, "alphamultiply", BlendAlphaModes);
2017-03-12 11:03:36 +00:00
lovrGraphicsSetBlendMode(mode, alphaMode);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetCanvas(lua_State* L) {
Canvas* canvas = lovrGraphicsGetCanvas();
luax_pushobject(L, canvas);
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetCanvas(lua_State* L) {
2018-08-24 01:28:37 +00:00
Canvas* canvas = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Canvas);
lovrGraphicsSetCanvas(canvas);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetColor(lua_State* L) {
2017-08-02 08:25:56 +00:00
Color color = lovrGraphicsGetColor();
lua_pushnumber(L, color.r);
lua_pushnumber(L, color.g);
lua_pushnumber(L, color.b);
lua_pushnumber(L, color.a);
2016-09-29 03:11:58 +00:00
return 4;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetColor(lua_State* L) {
2017-10-21 20:10:07 +00:00
Color color = luax_checkcolor(L, 1);
2017-08-02 08:25:56 +00:00
lovrGraphicsSetColor(color);
2016-09-29 03:11:58 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsIsCullingEnabled(lua_State* L) {
2017-08-02 07:54:33 +00:00
lua_pushboolean(L, lovrGraphicsIsCullingEnabled());
2016-09-14 00:02:23 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetCullingEnabled(lua_State* L) {
2017-08-02 07:54:33 +00:00
lovrGraphicsSetCullingEnabled(lua_toboolean(L, 1));
2016-08-10 06:28:17 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetDefaultFilter(lua_State* L) {
2017-08-02 07:54:33 +00:00
TextureFilter filter = lovrGraphicsGetDefaultFilter();
2018-07-05 03:11:52 +00:00
lua_pushstring(L, FilterModes[filter.mode]);
2017-08-02 07:54:33 +00:00
if (filter.mode == FILTER_ANISOTROPIC) {
lua_pushnumber(L, filter.anisotropy);
return 2;
}
2017-02-03 23:16:30 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetDefaultFilter(lua_State* L) {
2018-07-05 03:11:52 +00:00
FilterMode mode = luaL_checkoption(L, 1, NULL, FilterModes);
float anisotropy = luaL_optnumber(L, 2, 1.f);
2018-07-05 03:11:52 +00:00
lovrGraphicsSetDefaultFilter((TextureFilter) { .mode = mode, .anisotropy = anisotropy });
2017-02-03 23:16:30 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetDepthTest(lua_State* L) {
2018-02-09 05:50:47 +00:00
CompareMode mode;
bool write;
lovrGraphicsGetDepthTest(&mode, &write);
2018-07-05 03:11:52 +00:00
lua_pushstring(L, CompareModes[mode]);
2018-02-09 05:50:47 +00:00
lua_pushboolean(L, write);
return 2;
2017-08-02 07:54:33 +00:00
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetDepthTest(lua_State* L) {
2018-07-05 03:11:52 +00:00
CompareMode mode = lua_isnoneornil(L, 1) ? COMPARE_NONE : luaL_checkoption(L, 1, NULL, CompareModes);
2018-03-24 19:50:09 +00:00
bool write = lua_isnoneornil(L, 2) ? true : lua_toboolean(L, 2);
lovrGraphicsSetDepthTest(mode, write);
2017-08-02 07:54:33 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetFont(lua_State* L) {
2017-08-10 03:02:02 +00:00
Font* font = lovrGraphicsGetFont();
2018-07-25 03:10:30 +00:00
luax_pushobject(L, font);
2017-08-10 03:02:02 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetFont(lua_State* L) {
2017-08-10 03:02:02 +00:00
Font* font = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Font);
lovrGraphicsSetFont(font);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsIsGammaCorrect(lua_State* L) {
bool gammaCorrect = lovrGraphicsIsGammaCorrect();
lua_pushboolean(L, gammaCorrect);
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetLineWidth(lua_State* L) {
2016-10-01 20:48:31 +00:00
lua_pushnumber(L, lovrGraphicsGetLineWidth());
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetLineWidth(lua_State* L) {
uint8_t width = (uint8_t) luaL_optinteger(L, 1, 1);
2016-10-01 20:48:31 +00:00
lovrGraphicsSetLineWidth(width);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetPointSize(lua_State* L) {
2016-11-13 01:38:49 +00:00
lua_pushnumber(L, lovrGraphicsGetPointSize());
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetPointSize(lua_State* L) {
2016-11-13 01:38:49 +00:00
float size = luaL_optnumber(L, 1, 1.f);
lovrGraphicsSetPointSize(size);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetShader(lua_State* L) {
2017-08-02 07:54:33 +00:00
Shader* shader = lovrGraphicsGetShader();
2018-07-25 03:10:30 +00:00
luax_pushobject(L, shader);
2016-11-23 05:07:33 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetShader(lua_State* L) {
2017-08-02 07:54:33 +00:00
Shader* shader = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Shader);
lovrGraphicsSetShader(shader);
2016-11-23 05:07:33 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetStencilTest(lua_State* L) {
CompareMode mode;
int value;
lovrGraphicsGetStencilTest(&mode, &value);
if (mode == COMPARE_NONE) {
lua_pushnil(L);
return 1;
}
2018-07-05 03:11:52 +00:00
lua_pushstring(L, CompareModes[mode]);
lua_pushinteger(L, value);
return 2;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetStencilTest(lua_State* L) {
if (lua_isnoneornil(L, 1)) {
lovrGraphicsSetStencilTest(COMPARE_NONE, 0);
} else {
2018-07-05 03:11:52 +00:00
CompareMode mode = luaL_checkoption(L, 1, NULL, CompareModes);
int value = luaL_checkinteger(L, 2);
lovrGraphicsSetStencilTest(mode, value);
}
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsGetWinding(lua_State* L) {
2018-07-05 03:11:52 +00:00
lua_pushstring(L, Windings[lovrGraphicsGetWinding()]);
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetWinding(lua_State* L) {
2018-07-05 03:11:52 +00:00
lovrGraphicsSetWinding(luaL_checkoption(L, 1, NULL, Windings));
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsIsWireframe(lua_State* L) {
2017-08-10 03:02:02 +00:00
lua_pushboolean(L, lovrGraphicsIsWireframe());
2016-11-23 05:16:13 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSetWireframe(lua_State* L) {
2017-08-10 03:02:02 +00:00
lovrGraphicsSetWireframe(lua_toboolean(L, 1));
return 0;
2016-11-23 05:16:13 +00:00
}
// Transforms
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsPush(lua_State* L) {
lovrGraphicsPush();
2016-09-21 07:55:53 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsPop(lua_State* L) {
lovrGraphicsPop();
2016-09-21 07:55:53 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsOrigin(lua_State* L) {
2016-09-21 22:26:05 +00:00
lovrGraphicsOrigin();
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsTranslate(lua_State* L) {
2018-11-27 23:03:05 +00:00
float translation[3];
luax_readvec3(L, 1, translation, NULL);
lovrGraphicsTranslate(translation);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsRotate(lua_State* L) {
2018-11-27 23:03:05 +00:00
float rotation[4];
luax_readquat(L, 1, rotation, NULL);
lovrGraphicsRotate(rotation);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsScale(lua_State* L) {
2018-11-27 23:03:05 +00:00
float scale[3];
luax_readscale(L, 1, scale, 3, NULL);
lovrGraphicsScale(scale);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsTransform(lua_State* L) {
2017-01-20 03:13:37 +00:00
float transform[16];
2018-11-27 23:03:05 +00:00
luax_readmat4(L, 1, transform, 3, NULL);
2018-03-05 07:06:34 +00:00
lovrGraphicsMatrixTransform(transform);
2017-01-20 03:13:37 +00:00
return 0;
}
static int l_lovrGraphicsSetProjection(lua_State* L) {
2018-11-27 23:03:05 +00:00
float transform[16];
luax_readmat4(L, 1, transform, 3, NULL);
lovrGraphicsSetProjection(transform);
return 0;
}
// Rendering
2018-07-16 00:05:06 +00:00
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsClear(lua_State* L) {
2018-07-16 00:05:06 +00:00
int index = 1;
int top = lua_gettop(L);
bool clearColor = true;
bool clearDepth = true;
bool clearStencil = true;
Color color = lovrGraphicsGetBackgroundColor();
float depth = 1.f;
int stencil = 0;
if (top >= index) {
if (lua_type(L, index) == LUA_TNUMBER) {
color.r = luaL_checknumber(L, index++);
color.g = luaL_checknumber(L, index++);
color.b = luaL_checknumber(L, index++);
color.a = luaL_optnumber(L, index++, 1.f);
2018-07-16 00:05:06 +00:00
} else {
clearColor = lua_toboolean(L, index++);
}
}
if (top >= index) {
if (lua_type(L, index) == LUA_TNUMBER) {
depth = luaL_checknumber(L, index++);
} else {
clearDepth = lua_toboolean(L, index++);
}
}
if (top >= index) {
if (lua_type(L, index) == LUA_TNUMBER) {
stencil = luaL_checkinteger(L, index++);
} else {
clearStencil = lua_toboolean(L, index++);
}
}
lovrGraphicsClear(clearColor ? &color : NULL, clearDepth ? &depth : NULL, clearStencil ? &stencil : NULL);
return 0;
}
2016-11-23 05:16:13 +00:00
2018-10-26 16:14:57 +00:00
static int l_lovrGraphicsDiscard(lua_State* L) {
int top = lua_gettop(L);
bool color = top >= 1 ? lua_toboolean(L, 1) : true;
bool depth = top >= 2 ? lua_toboolean(L, 2) : true;
bool stencil = top >= 3 ? lua_toboolean(L, 3) : true;
lovrGraphicsDiscard(color, depth, stencil);
return 0;
}
2018-12-11 19:13:46 +00:00
static int l_lovrGraphicsFlush(lua_State* L) {
lovrGraphicsFlush();
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsPoints(lua_State* L) {
float* vertices;
uint32_t count = luax_getvertexcount(L, 1);
lovrGraphicsPoints(count, &vertices);
luax_readvertices(L, 1, vertices, count);
2016-11-08 07:16:33 +00:00
return 0;
}
2016-09-30 02:39:25 +00:00
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsLine(lua_State* L) {
float* vertices;
uint32_t count = luax_getvertexcount(L, 1);
lovrGraphicsLine(count, &vertices);
luax_readvertices(L, 1, vertices, count);
2016-09-30 02:39:25 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsTriangle(lua_State* L) {
DrawStyle style = STYLE_FILL;
Material* material = NULL;
if (lua_isuserdata(L, 1)) {
material = luax_checktype(L, 1, Material);
} else {
style = luaL_checkoption(L, 1, NULL, DrawStyles);
}
float* vertices;
uint32_t count = luax_getvertexcount(L, 2);
lovrAssert(count % 3 == 0, "Triangle vertex count must be a multiple of 3");
lovrGraphicsTriangle(style, material, count, &vertices);
luax_readvertices(L, 2, vertices, count);
2016-11-23 04:43:22 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsPlane(lua_State* L) {
DrawStyle style = STYLE_FILL;
Material* material = NULL;
if (lua_isuserdata(L, 1)) {
material = luax_checktype(L, 1, Material);
} else {
style = luaL_checkoption(L, 1, NULL, DrawStyles);
}
float transform[16];
2018-11-27 23:03:05 +00:00
luax_readmat4(L, 2, transform, 2, NULL);
lovrGraphicsPlane(style, material, transform);
2016-10-04 03:56:45 +00:00
return 0;
}
static int luax_rectangularprism(lua_State* L, int scaleComponents) {
DrawStyle style = STYLE_FILL;
Material* material = NULL;
if (lua_isuserdata(L, 1)) {
material = luax_checktype(L, 1, Material);
} else {
style = luaL_checkoption(L, 1, NULL, DrawStyles);
}
float transform[16];
2018-11-27 23:03:05 +00:00
luax_readmat4(L, 2, transform, scaleComponents, NULL);
lovrGraphicsBox(style, material, transform);
2016-09-30 06:18:51 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsCube(lua_State* L) {
return luax_rectangularprism(L, 1);
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsBox(lua_State* L) {
return luax_rectangularprism(L, 3);
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsArc(lua_State* L) {
DrawStyle style = STYLE_FILL;
Material* material = NULL;
if (lua_isuserdata(L, 1)) {
material = luax_checktype(L, 1, Material);
} else {
style = luaL_checkoption(L, 1, NULL, DrawStyles);
}
ArcMode mode = ARC_MODE_PIE;
2017-11-22 19:32:30 +00:00
int index = 2;
if (lua_type(L, index) == LUA_TSTRING) {
mode = luaL_checkoption(L, index++, NULL, ArcModes);
2017-11-22 19:32:30 +00:00
}
float transform[16];
2018-11-27 23:03:05 +00:00
index = luax_readmat4(L, index, transform, 1, NULL);
float r1 = luaL_optnumber(L, index++, 0.f);
float r2 = luaL_optnumber(L, index++, 2.f * M_PI);
int segments = luaL_optinteger(L, index, 64) * (MIN(fabsf(r2 - r1), 2 * M_PI) / (2 * M_PI));
lovrGraphicsArc(style, mode, material, transform, r1, r2, segments);
2017-11-22 19:32:30 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsCircle(lua_State* L) {
DrawStyle style = STYLE_FILL;
Material* material = NULL;
if (lua_isuserdata(L, 1)) {
material = luax_checktype(L, 1, Material);
} else {
style = luaL_checkoption(L, 1, NULL, DrawStyles);
}
2017-11-22 04:53:34 +00:00
float transform[16];
2018-11-27 23:03:05 +00:00
int index = luax_readmat4(L, 2, transform, 1, NULL);
int segments = luaL_optinteger(L, index, 32);
lovrGraphicsCircle(style, material, transform, segments);
2017-11-22 04:53:34 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsCylinder(lua_State* L) {
2019-01-02 23:18:09 +00:00
float transform[16];
int index = 1;
Material* material = lua_isuserdata(L, index) ? luax_checktype(L, index++, Material) : NULL;
2019-01-02 23:18:09 +00:00
index = luax_readmat4(L, index, transform, 1, NULL);
float r1 = luaL_optnumber(L, index++, 1.f);
float r2 = luaL_optnumber(L, index++, 1.f);
bool capped = lua_isnoneornil(L, index) ? true : lua_toboolean(L, index++);
int segments = luaL_optinteger(L, index, (lua_Integer) floorf(16 + 16 * MAX(r1, r2)));
2019-01-02 23:18:09 +00:00
lovrGraphicsCylinder(material, transform, r1, r2, capped, segments);
2017-06-21 03:54:22 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSphere(lua_State* L) {
2017-06-22 06:10:45 +00:00
float transform[16];
int index = 1;
Material* material = lua_isuserdata(L, index) ? luax_checktype(L, index++, Material) : NULL;
2018-11-27 23:03:05 +00:00
index = luax_readmat4(L, index, transform, 1, NULL);
int segments = luaL_optinteger(L, index, 30);
lovrGraphicsSphere(material, transform, segments);
2017-10-15 23:56:00 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsSkybox(lua_State* L) {
Texture* texture = luax_checktexture(L, 1);
float angle = luaL_optnumber(L, 2, 0.f);
float ax = luaL_optnumber(L, 3, 0.f);
float ay = luaL_optnumber(L, 4, 1.f);
float az = luaL_optnumber(L, 5, 0.f);
2017-10-15 23:56:00 +00:00
lovrGraphicsSkybox(texture, angle, ax, ay, az);
2017-06-22 06:10:45 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsPrint(lua_State* L) {
size_t length;
const char* str = luaL_checklstring(L, 1, &length);
2017-06-22 02:44:02 +00:00
float transform[16];
2018-11-27 23:03:05 +00:00
int index = luax_readmat4(L, 2, transform, 1, NULL);
float wrap = luaL_optnumber(L, index++, 0.f);
2018-07-05 03:11:52 +00:00
HorizontalAlign halign = luaL_checkoption(L, index++, "center", HorizontalAligns);
VerticalAlign valign = luaL_checkoption(L, index++, "middle", VerticalAligns);
lovrGraphicsPrint(str, length, transform, wrap, halign, valign);
2017-06-22 02:44:02 +00:00
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsStencil(lua_State* L) {
2017-12-19 03:48:28 +00:00
luaL_checktype(L, 1, LUA_TFUNCTION);
2018-07-05 03:11:52 +00:00
StencilAction action = luaL_checkoption(L, 2, "replace", StencilActions);
2017-12-19 03:48:28 +00:00
int replaceValue = luaL_optinteger(L, 3, 1);
bool keepValues = lua_toboolean(L, 4);
if (!keepValues) {
int clearTo = lua_isnumber(L, 4) ? lua_tonumber(L, 4) : 0;
lovrGraphicsClear(NULL, NULL, &clearTo);
2017-12-19 03:48:28 +00:00
}
lua_settop(L, 1);
lovrGraphicsStencil(action, replaceValue, stencilCallback, L);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsFill(lua_State* L) {
Texture* texture = lua_isnoneornil(L, 1) ? NULL : luax_checktexture(L, 1);
float u = luaL_optnumber(L, 2, 0.f);
float v = luaL_optnumber(L, 3, 0.f);
float w = luaL_optnumber(L, 4, 1.f - u);
float h = luaL_optnumber(L, 5, 1.f - v);
2018-10-01 01:40:51 +00:00
lovrGraphicsFill(texture, u, v, w, h);
return 0;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsCompute(lua_State* L) {
2018-08-09 00:53:59 +00:00
Shader* shader = luax_checktype(L, 1, Shader);
int x = luaL_optinteger(L, 2, 1);
int y = luaL_optinteger(L, 3, 1);
int z = luaL_optinteger(L, 4, 1);
2018-08-09 00:53:59 +00:00
lovrGraphicsCompute(shader, x, y, z);
return 0;
}
2016-11-23 05:16:13 +00:00
// Types
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewAnimator(lua_State* L) {
2017-11-03 06:47:13 +00:00
Model* model = luax_checktype(L, 1, Model);
2018-01-30 04:30:13 +00:00
Animator* animator = lovrAnimatorCreate(model->modelData);
2018-07-25 03:10:30 +00:00
luax_pushobject(L, animator);
lovrRelease(animator);
2017-11-03 06:47:13 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewShaderBlock(lua_State* L) {
vec_uniform_t uniforms;
vec_init(&uniforms);
2018-07-19 23:58:59 +00:00
2019-01-16 16:52:21 +00:00
BlockType type = luaL_checkoption(L, 1, NULL, BlockTypes);
luaL_checktype(L, 2, LUA_TTABLE);
2018-07-19 23:58:59 +00:00
lua_pushnil(L);
2019-01-16 16:52:21 +00:00
while (lua_next(L, 2) != 0) {
Uniform uniform;
2018-07-19 23:58:59 +00:00
// Name
strncpy(uniform.name, luaL_checkstring(L, -2), LOVR_MAX_UNIFORM_LENGTH - 1);
2018-07-19 23:58:59 +00:00
if (lua_type(L, -1) == LUA_TSTRING) {
uniform.count = 1;
luax_checkuniformtype(L, -1, &uniform.type, &uniform.components);
2018-07-19 23:58:59 +00:00
} else {
luaL_checktype(L, -1, LUA_TTABLE);
lua_rawgeti(L, -1, 1);
luax_checkuniformtype(L, -1, &uniform.type, &uniform.components);
lua_pop(L, 1);
lua_rawgeti(L, -1, 2);
uniform.count = luaL_optinteger(L, -1, 1);
lua_pop(L, 1);
2018-07-19 23:58:59 +00:00
}
lovrAssert(uniform.count >= 1, "Uniform count must be positive, got %d for '%s'", uniform.count, uniform.name);
vec_push(&uniforms, uniform);
// Pop the table, leaving the key for lua_next to nom
2018-07-19 23:58:59 +00:00
lua_pop(L, 1);
}
BufferUsage usage = USAGE_DYNAMIC;
bool readable = false;
2019-01-16 16:52:21 +00:00
if (lua_istable(L, 3)) {
lua_getfield(L, 3, "usage");
usage = luaL_checkoption(L, -1, "dynamic", BufferUsages);
lua_pop(L, 1);
2019-01-16 16:52:21 +00:00
lua_getfield(L, 3, "readable");
readable = lua_toboolean(L, -1);
lua_pop(L, 1);
}
2019-01-16 16:52:21 +00:00
lovrAssert(type == BLOCK_UNIFORM || lovrGraphicsGetSupported()->compute, "Compute blocks are not supported on this system");
size_t size = lovrShaderComputeUniformLayout(&uniforms);
2019-01-16 16:52:21 +00:00
Buffer* buffer = lovrBufferCreate(size, NULL, type == BLOCK_COMPUTE ? BUFFER_SHADER_STORAGE : BUFFER_UNIFORM, usage, readable);
ShaderBlock* block = lovrShaderBlockCreate(type, buffer, &uniforms);
2018-07-28 22:46:09 +00:00
luax_pushobject(L, block);
vec_deinit(&uniforms);
lovrRelease(buffer);
lovrRelease(block);
2018-07-19 23:58:59 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewCanvas(lua_State* L) {
2018-08-31 23:37:30 +00:00
Attachment attachments[MAX_CANVAS_ATTACHMENTS];
int attachmentCount = 0;
int width = 0;
int height = 0;
int index;
if (luax_totype(L, 1, Texture)) {
for (index = 1; index <= MAX_CANVAS_ATTACHMENTS; index++) {
Texture* texture = luax_totype(L, index, Texture);
if (!texture) break;
attachments[attachmentCount++] = (Attachment) { texture, 0, 0 };
}
} else if (lua_istable(L, 1)) {
luax_readattachments(L, 1, attachments, &attachmentCount);
index = 2;
} else {
width = luaL_checkinteger(L, 1);
height = luaL_checkinteger(L, 2);
index = 3;
}
CanvasFlags flags = {
.depth = { .enabled = true, .readable = false, .format = FORMAT_D16 },
.stereo = true,
.msaa = 0,
.mipmaps = true
};
2018-09-01 06:24:59 +00:00
TextureFormat format = FORMAT_RGBA;
bool anonymous = attachmentCount == 0;
2018-08-24 21:43:39 +00:00
2018-08-31 23:37:30 +00:00
if (lua_istable(L, index)) {
lua_getfield(L, index, "depth");
2018-08-25 01:38:46 +00:00
switch (lua_type(L, -1)) {
case LUA_TNIL: break;
case LUA_TBOOLEAN: flags.depth.enabled = lua_toboolean(L, -1); break;
case LUA_TSTRING: flags.depth.format = luaL_checkoption(L, -1, NULL, TextureFormats); break;
case LUA_TTABLE:
lua_getfield(L, -1, "readable");
flags.depth.readable = lua_toboolean(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "format");
flags.depth.format = luaL_checkoption(L, -1, NULL, TextureFormats);
lua_pop(L, 1);
break;
default: lovrThrow("Expected boolean, string, or table for Canvas depth flag");
2018-08-25 01:38:46 +00:00
}
lua_pop(L, 1);
2018-08-31 23:37:30 +00:00
lua_getfield(L, index, "stereo");
2018-08-25 01:38:46 +00:00
flags.stereo = lua_isnil(L, -1) ? flags.stereo : lua_toboolean(L, -1);
2018-08-24 21:43:39 +00:00
lua_pop(L, 1);
2018-08-31 23:37:30 +00:00
lua_getfield(L, index, "msaa");
flags.msaa = lua_isnil(L, -1) ? flags.msaa : luaL_checkinteger(L, -1);
lua_pop(L, 1);
2018-08-29 20:34:54 +00:00
2018-08-31 23:37:30 +00:00
lua_getfield(L, index, "mipmaps");
2018-08-29 20:34:54 +00:00
flags.mipmaps = lua_isnil(L, -1) ? flags.mipmaps : lua_toboolean(L, -1);
lua_pop(L, 1);
2018-08-31 23:37:30 +00:00
if (attachmentCount == 0) {
lua_getfield(L, index, "format");
2018-09-01 06:24:59 +00:00
format = luaL_checkoption(L, -1, "rgba", TextureFormats);
anonymous = lua_isnil(L, -1) || lua_toboolean(L, -1);
2018-08-31 23:37:30 +00:00
lua_pop(L, 1);
}
}
2018-09-01 06:24:59 +00:00
if (anonymous) {
Texture* texture = lovrTextureCreate(TEXTURE_2D, NULL, 0, true, flags.mipmaps, flags.msaa);
lovrTextureAllocate(texture, width, height, 1, format);
lovrTextureSetWrap(texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP, .r = WRAP_CLAMP });
2018-09-01 06:24:59 +00:00
attachments[0] = (Attachment) { texture, 0, 0 };
attachmentCount++;
}
2018-08-31 23:37:30 +00:00
if (width == 0 && height == 0 && attachmentCount > 0) {
width = lovrTextureGetWidth(attachments[0].texture, attachments[0].level);
height = lovrTextureGetHeight(attachments[0].texture, attachments[0].level);
2018-08-24 21:43:39 +00:00
}
Canvas* canvas = lovrCanvasCreate(width, height, flags);
2018-08-31 23:37:30 +00:00
if (attachmentCount > 0) {
lovrCanvasSetAttachments(canvas, attachments, attachmentCount);
if (anonymous) {
lovrRelease(attachments[0].texture);
}
}
2018-07-25 03:10:30 +00:00
luax_pushobject(L, canvas);
lovrRelease(canvas);
2017-12-07 07:50:52 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewFont(lua_State* L) {
Rasterizer* rasterizer = luax_totype(L, 1, Rasterizer);
if (!rasterizer) {
2018-01-22 16:46:09 +00:00
Blob* blob = NULL;
float size;
if (lua_type(L, 1) == LUA_TNUMBER || lua_isnoneornil(L, 1)) {
size = luaL_optinteger(L, 1, 32);
2018-01-22 16:46:09 +00:00
} else {
blob = luax_readblob(L, 1, "Font");
size = luaL_optinteger(L, 2, 32);
2018-01-22 16:46:09 +00:00
}
rasterizer = lovrRasterizerCreate(blob, size);
2018-02-26 08:59:03 +00:00
lovrRelease(blob);
2017-03-11 22:13:49 +00:00
}
2018-01-22 16:28:33 +00:00
Font* font = lovrFontCreate(rasterizer);
2018-07-25 03:10:30 +00:00
luax_pushobject(L, font);
lovrRelease(rasterizer);
2018-02-26 08:59:03 +00:00
lovrRelease(font);
2017-03-11 22:13:49 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewMaterial(lua_State* L) {
2018-07-18 07:34:21 +00:00
Material* material = lovrMaterialCreate();
int index = 1;
if (lua_type(L, index) == LUA_TSTRING) {
Blob* blob = luax_readblob(L, index++, "Texture");
2018-09-04 03:59:12 +00:00
TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true);
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, true, 0);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
lovrRelease(blob);
lovrRelease(textureData);
2018-02-26 08:59:03 +00:00
lovrRelease(texture);
} else if (lua_isuserdata(L, index)) {
Texture* texture = luax_checktexture(L, index);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
index++;
}
if (lua_isnumber(L, index)) {
Color color = luax_checkcolor(L, index);
lovrMaterialSetColor(material, COLOR_DIFFUSE, color);
}
2018-07-25 03:10:30 +00:00
luax_pushobject(L, material);
lovrRelease(material);
2017-10-21 21:05:58 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewMesh(lua_State* L) {
uint32_t count;
int dataIndex = 0;
int drawModeIndex = 2;
VertexData* vertexData = NULL;
bool hasFormat = false;
2018-01-26 02:31:56 +00:00
VertexFormat format;
2018-01-27 02:58:36 +00:00
vertexFormatInit(&format);
if (lua_isnumber(L, 1)) {
count = lua_tointeger(L, 1);
} else if (lua_istable(L, 1)) {
if (lua_isnumber(L, 2)) {
2016-11-13 10:42:05 +00:00
drawModeIndex++;
hasFormat = luax_checkvertexformat(L, 1, &format);
count = lua_tointeger(L, 2);
dataIndex = 0;
} else if (lua_istable(L, 2)) {
2016-11-13 10:42:05 +00:00
drawModeIndex++;
hasFormat = luax_checkvertexformat(L, 1, &format);
count = lua_objlen(L, 2);
dataIndex = 2;
} else {
count = lua_objlen(L, 1);
dataIndex = 1;
}
} else if (lua_isuserdata(L, 1)) {
vertexData = luax_checktype(L, 1, VertexData);
format = vertexData->format;
count = vertexData->count;
hasFormat = true;
} else {
luaL_argerror(L, 1, "table or number expected");
2016-11-08 22:15:37 +00:00
return 0;
}
2018-03-21 21:48:46 +00:00
if (!hasFormat) {
vertexFormatAppend(&format, "lovrPosition", ATTR_FLOAT, 3);
vertexFormatAppend(&format, "lovrNormal", ATTR_FLOAT, 3);
vertexFormatAppend(&format, "lovrTexCoord", ATTR_FLOAT, 2);
}
DrawMode mode = luaL_checkoption(L, drawModeIndex, "fan", DrawModes);
BufferUsage usage = luaL_checkoption(L, drawModeIndex + 1, "dynamic", BufferUsages);
2018-12-07 23:57:45 +00:00
bool readable = lua_toboolean(L, drawModeIndex + 2);
size_t bufferSize = count * format.stride;
Buffer* vertexBuffer = lovrBufferCreate(bufferSize, NULL, BUFFER_VERTEX, usage, readable);
2019-01-04 03:43:35 +00:00
Mesh* mesh = lovrMeshCreate(mode, format, vertexBuffer, count);
2018-12-12 06:10:29 +00:00
lovrMeshAttachAttribute(mesh, "lovrDrawID", &(MeshAttribute) {
.buffer = lovrGraphicsGetIdentityBuffer(),
.type = ATTR_BYTE,
.components = 1,
.divisor = 1,
.integer = true,
.enabled = true
});
if (dataIndex) {
VertexPointer vertices = { .raw = lovrBufferMap(vertexBuffer, 0) };
luax_loadvertices(L, dataIndex, &format, vertices);
} else if (vertexData) {
void* vertices = lovrBufferMap(vertexBuffer, 0);
memcpy(vertices, vertexData->blob.data, vertexData->count * vertexData->format.stride);
}
lovrBufferMarkRange(vertexBuffer, 0, count * format.stride);
lovrRelease(vertexBuffer);
2018-07-25 03:10:30 +00:00
luax_pushobject(L, mesh);
2018-02-26 08:59:03 +00:00
lovrRelease(mesh);
2017-02-03 23:16:30 +00:00
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewModel(lua_State* L) {
ModelData* modelData = luax_totype(L, 1, ModelData);
if (!modelData) {
2018-01-23 02:24:39 +00:00
Blob* blob = luax_readblob(L, 1, "Model");
modelData = lovrModelDataCreate(blob);
2018-02-26 08:59:03 +00:00
lovrRelease(blob);
2018-01-23 02:24:39 +00:00
}
Model* model = lovrModelCreate(modelData);
if (lua_gettop(L) >= 2) {
if (lua_type(L, 2) == LUA_TSTRING) {
Blob* blob = luax_readblob(L, 2, "Texture");
2018-09-04 03:59:12 +00:00
TextureData* textureData = lovrTextureDataCreateFromBlob(blob, true);
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, true, 0);
2018-07-18 07:34:21 +00:00
Material* material = lovrMaterialCreate();
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
lovrModelSetMaterial(model, material);
2018-02-26 08:59:03 +00:00
lovrRelease(blob);
lovrRelease(texture);
lovrRelease(material);
} else {
lovrModelSetMaterial(model, luax_checktype(L, 2, Material));
}
}
2018-07-25 03:10:30 +00:00
luax_pushobject(L, model);
lovrRelease(modelData);
2018-02-26 08:59:03 +00:00
lovrRelease(model);
2016-08-10 06:28:17 +00:00
return 1;
}
2018-08-07 23:58:14 +00:00
static void luax_readshadersource(lua_State* L, int index) {
if (lua_isnoneornil(L, index)) {
return;
}
Blob* blob = luax_totype(L, index, Blob);
if (blob) {
lua_pushlstring(L, blob->data, blob->size);
lua_replace(L, index);
return;
}
const char* source = luaL_checkstring(L, index);
if (!lovrFilesystemIsFile(source)) {
return;
}
2018-08-07 23:58:14 +00:00
size_t bytesRead;
char* contents = lovrFilesystemRead(source, &bytesRead);
lovrAssert(bytesRead > 0, "Could not read shader from file '%s'", source);
lua_pushlstring(L, contents, bytesRead);
lua_replace(L, index);
free(contents);
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewShader(lua_State* L) {
2018-08-07 23:58:14 +00:00
luax_readshadersource(L, 1);
luax_readshadersource(L, 2);
const char* vertexSource = lua_tostring(L, 1);
const char* fragmentSource = lua_tostring(L, 2);
Shader* shader = lovrShaderCreateGraphics(vertexSource, fragmentSource);
2018-07-25 03:10:30 +00:00
luax_pushobject(L, shader);
2018-02-26 08:59:03 +00:00
lovrRelease(shader);
2016-08-10 06:28:17 +00:00
return 1;
}
2016-10-16 03:11:54 +00:00
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewComputeShader(lua_State* L) {
2018-08-07 23:58:14 +00:00
luax_readshadersource(L, 1);
const char* source = lua_tostring(L, 1);
Shader* shader = lovrShaderCreateCompute(source);
luax_pushobject(L, shader);
lovrRelease(shader);
return 1;
}
2018-09-27 01:27:38 +00:00
static int l_lovrGraphicsNewTexture(lua_State* L) {
2018-08-21 01:54:57 +00:00
int index = 1;
int width, height, depth;
int argType = lua_type(L, index);
bool blank = argType == LUA_TNUMBER;
TextureType type = TEXTURE_2D;
if (blank) {
width = lua_tointeger(L, index++);
height = luaL_checkinteger(L, index++);
depth = lua_type(L, index) == LUA_TNUMBER ? lua_tonumber(L, index++) : 0;
lovrAssert(width > 0 && height > 0, "A Texture must have a positive width, height, and depth");
2018-08-21 01:54:57 +00:00
} else if (argType != LUA_TTABLE) {
lua_createtable(L, 1, 0);
lua_pushvalue(L, 1);
lua_rawseti(L, -2, 1);
lua_replace(L, 1);
2018-08-21 01:54:57 +00:00
depth = 1;
2018-08-30 04:45:08 +00:00
index++;
2018-08-21 01:54:57 +00:00
} else {
depth = lua_objlen(L, index++);
type = depth > 0 ? TEXTURE_ARRAY : TEXTURE_CUBE;
2017-12-07 07:50:52 +00:00
}
2018-08-21 01:54:57 +00:00
bool hasFlags = lua_istable(L, index);
bool srgb = !blank;
bool mipmaps = true;
2018-08-21 01:54:57 +00:00
TextureFormat format = FORMAT_RGBA;
int msaa = 0;
if (hasFlags) {
2018-08-21 01:54:57 +00:00
lua_getfield(L, index, "linear");
srgb = lua_isnil(L, -1) ? srgb : !lua_toboolean(L, -1);
2017-12-07 07:50:52 +00:00
lua_pop(L, 1);
2018-08-21 01:54:57 +00:00
lua_getfield(L, index, "mipmaps");
mipmaps = lua_isnil(L, -1) ? mipmaps : lua_toboolean(L, -1);
lua_pop(L, 1);
2018-08-21 01:54:57 +00:00
lua_getfield(L, index, "type");
type = lua_isnil(L, -1) ? type : luaL_checkoption(L, -1, NULL, TextureTypes);
lua_pop(L, 1);
2018-08-21 01:54:57 +00:00
lua_getfield(L, index, "format");
format = lua_isnil(L, -1) ? format : luaL_checkoption(L, -1, NULL, TextureFormats);
lua_pop(L, 1);
lua_getfield(L, index, "msaa");
msaa = lua_isnil(L, -1) ? msaa : luaL_checkinteger(L, -1);
lua_pop(L, 1);
}
Texture* texture = lovrTextureCreate(type, NULL, 0, srgb, mipmaps, msaa);
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
2018-08-21 01:54:57 +00:00
if (blank) {
depth = depth ? depth : (type == TEXTURE_CUBE ? 6 : 1);
2018-08-21 01:54:57 +00:00
lovrTextureAllocate(texture, width, height, depth, format);
} else {
if (type == TEXTURE_CUBE && depth == 0) {
depth = 6;
2018-09-04 03:59:12 +00:00
const char* faces[6] = { "right", "left", "top", "bottom", "back", "front" };
2018-08-21 01:54:57 +00:00
for (int i = 0; i < 6; i++) {
lua_pushstring(L, faces[i]);
lua_rawget(L, 1);
lua_rawseti(L, 1, i + 1);
}
}
for (int i = 0; i < depth; i++) {
lua_rawgeti(L, 1, i + 1);
2018-09-04 03:59:12 +00:00
TextureData* textureData = luax_checktexturedata(L, -1, type != TEXTURE_CUBE);
2018-08-21 01:54:57 +00:00
if (i == 0) {
lovrTextureAllocate(texture, textureData->width, textureData->height, depth, textureData->format);
}
lovrTextureReplacePixels(texture, textureData, 0, 0, i, 0);
lovrRelease(textureData);
lua_pop(L, 1);
}
2017-12-07 07:50:52 +00:00
}
2017-10-15 23:56:00 +00:00
2018-07-25 03:10:30 +00:00
luax_pushobject(L, texture);
2018-02-26 08:59:03 +00:00
lovrRelease(texture);
2016-11-08 11:14:33 +00:00
return 1;
}
2017-03-11 11:08:07 +00:00
2018-09-27 01:27:38 +00:00
static const luaL_Reg lovrGraphics[] = {
2018-07-16 00:05:06 +00:00
// Base
2017-03-11 11:08:07 +00:00
{ "present", l_lovrGraphicsPresent },
{ "createWindow", l_lovrGraphicsCreateWindow },
2017-08-10 03:02:02 +00:00
{ "getWidth", l_lovrGraphicsGetWidth },
{ "getHeight", l_lovrGraphicsGetHeight },
{ "getDimensions", l_lovrGraphicsGetDimensions },
2018-08-03 18:43:15 +00:00
{ "getSupported", l_lovrGraphicsGetSupported },
2018-07-16 00:05:06 +00:00
{ "getSystemLimits", l_lovrGraphicsGetSystemLimits },
2017-12-10 04:07:32 +00:00
{ "getStats", l_lovrGraphicsGetStats },
2018-07-16 00:05:06 +00:00
// State
{ "reset", l_lovrGraphicsReset },
{ "getAlphaSampling", l_lovrGraphicsGetAlphaSampling },
{ "setAlphaSampling", l_lovrGraphicsSetAlphaSampling },
2017-03-11 11:08:07 +00:00
{ "getBackgroundColor", l_lovrGraphicsGetBackgroundColor },
{ "setBackgroundColor", l_lovrGraphicsSetBackgroundColor },
2017-03-12 11:03:36 +00:00
{ "getBlendMode", l_lovrGraphicsGetBlendMode },
{ "setBlendMode", l_lovrGraphicsSetBlendMode },
{ "getCanvas", l_lovrGraphicsGetCanvas },
{ "setCanvas", l_lovrGraphicsSetCanvas },
2017-03-11 11:08:07 +00:00
{ "getColor", l_lovrGraphicsGetColor },
{ "setColor", l_lovrGraphicsSetColor },
{ "isCullingEnabled", l_lovrGraphicsIsCullingEnabled },
{ "setCullingEnabled", l_lovrGraphicsSetCullingEnabled },
{ "getDefaultFilter", l_lovrGraphicsGetDefaultFilter },
{ "setDefaultFilter", l_lovrGraphicsSetDefaultFilter },
2017-08-28 05:59:51 +00:00
{ "getDepthTest", l_lovrGraphicsGetDepthTest },
{ "setDepthTest", l_lovrGraphicsSetDepthTest },
2017-08-10 03:02:02 +00:00
{ "getFont", l_lovrGraphicsGetFont },
{ "setFont", l_lovrGraphicsSetFont },
{ "isGammaCorrect", l_lovrGraphicsIsGammaCorrect },
2017-08-02 07:54:33 +00:00
{ "getLineWidth", l_lovrGraphicsGetLineWidth },
{ "setLineWidth", l_lovrGraphicsSetLineWidth },
{ "getPointSize", l_lovrGraphicsGetPointSize },
{ "setPointSize", l_lovrGraphicsSetPointSize },
2017-08-10 03:02:02 +00:00
{ "getShader", l_lovrGraphicsGetShader },
{ "setShader", l_lovrGraphicsSetShader },
{ "getStencilTest", l_lovrGraphicsGetStencilTest },
{ "setStencilTest", l_lovrGraphicsSetStencilTest },
2017-08-02 07:54:33 +00:00
{ "getWinding", l_lovrGraphicsGetWinding },
{ "setWinding", l_lovrGraphicsSetWinding },
{ "isWireframe", l_lovrGraphicsIsWireframe },
{ "setWireframe", l_lovrGraphicsSetWireframe },
2018-07-16 00:05:06 +00:00
// Transforms
2017-03-11 11:08:07 +00:00
{ "push", l_lovrGraphicsPush },
{ "pop", l_lovrGraphicsPop },
{ "origin", l_lovrGraphicsOrigin },
{ "translate", l_lovrGraphicsTranslate },
{ "rotate", l_lovrGraphicsRotate },
{ "scale", l_lovrGraphicsScale },
{ "transform", l_lovrGraphicsTransform },
{ "setProjection", l_lovrGraphicsSetProjection },
2018-07-16 00:05:06 +00:00
// Rendering
2018-07-16 00:05:06 +00:00
{ "clear", l_lovrGraphicsClear },
2018-10-26 16:14:57 +00:00
{ "discard", l_lovrGraphicsDiscard },
2018-12-11 19:13:46 +00:00
{ "flush", l_lovrGraphicsFlush },
2017-03-11 11:08:07 +00:00
{ "points", l_lovrGraphicsPoints },
{ "line", l_lovrGraphicsLine },
{ "triangle", l_lovrGraphicsTriangle },
{ "plane", l_lovrGraphicsPlane },
{ "cube", l_lovrGraphicsCube },
{ "box", l_lovrGraphicsBox },
2017-11-22 19:32:30 +00:00
{ "arc", l_lovrGraphicsArc },
2017-11-22 04:53:34 +00:00
{ "circle", l_lovrGraphicsCircle },
2017-06-21 03:54:22 +00:00
{ "cylinder", l_lovrGraphicsCylinder },
2017-06-22 06:10:45 +00:00
{ "sphere", l_lovrGraphicsSphere },
2017-10-15 23:56:00 +00:00
{ "skybox", l_lovrGraphicsSkybox },
2017-06-22 02:44:02 +00:00
{ "print", l_lovrGraphicsPrint },
2017-12-19 03:48:28 +00:00
{ "stencil", l_lovrGraphicsStencil },
2018-03-19 19:12:34 +00:00
{ "fill", l_lovrGraphicsFill },
2018-08-09 00:53:59 +00:00
{ "compute", l_lovrGraphicsCompute },
2018-07-16 00:05:06 +00:00
// Types
2017-11-03 06:47:13 +00:00
{ "newAnimator", l_lovrGraphicsNewAnimator },
2017-12-07 07:50:52 +00:00
{ "newCanvas", l_lovrGraphicsNewCanvas },
2017-03-11 11:08:07 +00:00
{ "newFont", l_lovrGraphicsNewFont },
2017-10-21 21:05:58 +00:00
{ "newMaterial", l_lovrGraphicsNewMaterial },
2017-03-11 22:13:49 +00:00
{ "newMesh", l_lovrGraphicsNewMesh },
2017-03-11 11:08:07 +00:00
{ "newModel", l_lovrGraphicsNewModel },
{ "newShader", l_lovrGraphicsNewShader },
2018-08-07 23:58:14 +00:00
{ "newComputeShader", l_lovrGraphicsNewComputeShader },
2018-07-19 23:58:59 +00:00
{ "newShaderBlock", l_lovrGraphicsNewShaderBlock },
2017-03-11 11:08:07 +00:00
{ "newTexture", l_lovrGraphicsNewTexture },
2018-07-16 00:05:06 +00:00
2017-03-11 11:08:07 +00:00
{ NULL, NULL }
};
2018-09-27 01:27:38 +00:00
int luaopen_lovr_graphics(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrGraphics);
luax_registertype(L, "Animator", lovrAnimator);
luax_registertype(L, "Font", lovrFont);
luax_registertype(L, "Material", lovrMaterial);
luax_registertype(L, "Mesh", lovrMesh);
luax_registertype(L, "Model", lovrModel);
luax_registertype(L, "Shader", lovrShader);
luax_registertype(L, "ShaderBlock", lovrShaderBlock);
luax_registertype(L, "Texture", lovrTexture);
luax_registertype(L, "Canvas", lovrCanvas);
luax_pushconf(L);
// Gamma correct
lua_getfield(L, -1, "gammacorrect");
bool gammaCorrect = lua_toboolean(L, -1);
lua_pop(L, 1);
lovrGraphicsInit(gammaCorrect);
lua_pushcfunction(L, l_lovrGraphicsCreateWindow);
2018-11-16 10:26:56 +00:00
lua_getfield(L, -2, "window");
lua_call(L, 1, 0);
2018-09-27 01:27:38 +00:00
2018-11-16 10:26:56 +00:00
lua_pop(L, 1); // conf
2018-09-27 01:27:38 +00:00
return 1;
}