mirror of https://github.com/bjornbytes/lovr.git
1867 lines
54 KiB
C
1867 lines
54 KiB
C
#include "api.h"
|
|
#include "graphics/graphics.h"
|
|
#include "graphics/buffer.h"
|
|
#include "graphics/canvas.h"
|
|
#include "graphics/material.h"
|
|
#include "graphics/mesh.h"
|
|
#include "graphics/model.h"
|
|
#include "graphics/shader.h"
|
|
#include "data/blob.h"
|
|
#include "data/modelData.h"
|
|
#include "data/rasterizer.h"
|
|
#include "data/image.h"
|
|
#include "core/os.h"
|
|
#include "core/util.h"
|
|
#include <lua.h>
|
|
#include <lauxlib.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
StringEntry lovrArcMode[] = {
|
|
[ARC_MODE_PIE] = ENTRY("pie"),
|
|
[ARC_MODE_OPEN] = ENTRY("open"),
|
|
[ARC_MODE_CLOSED] = ENTRY("closed"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrAttributeType[] = {
|
|
[I8] = ENTRY("byte"),
|
|
[U8] = ENTRY("ubyte"),
|
|
[I16] = ENTRY("short"),
|
|
[U16] = ENTRY("ushort"),
|
|
[I32] = ENTRY("int"),
|
|
[U32] = ENTRY("uint"),
|
|
[F32] = ENTRY("float"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrBlendAlphaMode[] = {
|
|
[BLEND_ALPHA_MULTIPLY] = ENTRY("alphamultiply"),
|
|
[BLEND_PREMULTIPLIED] = ENTRY("premultiplied"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrBlendMode[] = {
|
|
[BLEND_ALPHA] = ENTRY("alpha"),
|
|
[BLEND_ADD] = ENTRY("add"),
|
|
[BLEND_SUBTRACT] = ENTRY("subtract"),
|
|
[BLEND_MULTIPLY] = ENTRY("multiply"),
|
|
[BLEND_LIGHTEN] = ENTRY("lighten"),
|
|
[BLEND_DARKEN] = ENTRY("darken"),
|
|
[BLEND_SCREEN] = ENTRY("screen"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrBlockType[] = {
|
|
[BLOCK_UNIFORM] = ENTRY("uniform"),
|
|
[BLOCK_COMPUTE] = ENTRY("compute"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrBufferUsage[] = {
|
|
[USAGE_STATIC] = ENTRY("static"),
|
|
[USAGE_DYNAMIC] = ENTRY("dynamic"),
|
|
[USAGE_STREAM] = ENTRY("stream"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrCompareMode[] = {
|
|
[COMPARE_EQUAL] = ENTRY("equal"),
|
|
[COMPARE_NEQUAL] = ENTRY("notequal"),
|
|
[COMPARE_LESS] = ENTRY("less"),
|
|
[COMPARE_LEQUAL] = ENTRY("lequal"),
|
|
[COMPARE_GREATER] = ENTRY("greater"),
|
|
[COMPARE_GEQUAL] = ENTRY("gequal"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrCoordinateSpace[] = {
|
|
[SPACE_LOCAL] = ENTRY("local"),
|
|
[SPACE_GLOBAL] = ENTRY("global"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrDefaultShader[] = {
|
|
[SHADER_UNLIT] = ENTRY("unlit"),
|
|
[SHADER_STANDARD] = ENTRY("standard"),
|
|
[SHADER_CUBE] = ENTRY("cube"),
|
|
[SHADER_PANO] = ENTRY("pano"),
|
|
[SHADER_FONT] = ENTRY("font"),
|
|
[SHADER_FILL] = ENTRY("screenspace"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrDrawMode[] = {
|
|
[DRAW_POINTS] = ENTRY("points"),
|
|
[DRAW_LINES] = ENTRY("lines"),
|
|
[DRAW_LINE_STRIP] = ENTRY("linestrip"),
|
|
[DRAW_LINE_LOOP] = ENTRY("lineloop"),
|
|
[DRAW_TRIANGLE_STRIP] = ENTRY("strip"),
|
|
[DRAW_TRIANGLES] = ENTRY("triangles"),
|
|
[DRAW_TRIANGLE_FAN] = ENTRY("fan"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrDrawStyle[] = {
|
|
[STYLE_FILL] = ENTRY("fill"),
|
|
[STYLE_LINE] = ENTRY("line"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrFilterMode[] = {
|
|
[FILTER_NEAREST] = ENTRY("nearest"),
|
|
[FILTER_BILINEAR] = ENTRY("bilinear"),
|
|
[FILTER_TRILINEAR] = ENTRY("trilinear"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrHorizontalAlign[] = {
|
|
[ALIGN_LEFT] = ENTRY("left"),
|
|
[ALIGN_CENTER] = ENTRY("center"),
|
|
[ALIGN_RIGHT] = ENTRY("right"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrMaterialColor[] = {
|
|
[COLOR_DIFFUSE] = ENTRY("diffuse"),
|
|
[COLOR_EMISSIVE] = ENTRY("emissive"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrMaterialScalar[] = {
|
|
[SCALAR_METALNESS] = ENTRY("metalness"),
|
|
[SCALAR_ROUGHNESS] = ENTRY("roughness"),
|
|
[SCALAR_ALPHA_CUTOFF] = ENTRY("alphacutoff"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrMaterialTexture[] = {
|
|
[TEXTURE_DIFFUSE] = ENTRY("diffuse"),
|
|
[TEXTURE_EMISSIVE] = ENTRY("emissive"),
|
|
[TEXTURE_METALNESS] = ENTRY("metalness"),
|
|
[TEXTURE_ROUGHNESS] = ENTRY("roughness"),
|
|
[TEXTURE_OCCLUSION] = ENTRY("occlusion"),
|
|
[TEXTURE_NORMAL] = ENTRY("normal"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrShaderType[] = {
|
|
[SHADER_GRAPHICS] = ENTRY("graphics"),
|
|
[SHADER_COMPUTE] = ENTRY("compute"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrStencilAction[] = {
|
|
[STENCIL_REPLACE] = ENTRY("replace"),
|
|
[STENCIL_INCREMENT] = ENTRY("increment"),
|
|
[STENCIL_DECREMENT] = ENTRY("decrement"),
|
|
[STENCIL_INCREMENT_WRAP] = ENTRY("incrementwrap"),
|
|
[STENCIL_DECREMENT_WRAP] = ENTRY("decrementwrap"),
|
|
[STENCIL_INVERT] = ENTRY("invert"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrTextureFormat[] = {
|
|
[FORMAT_RGB] = ENTRY("rgb"),
|
|
[FORMAT_RGBA] = ENTRY("rgba"),
|
|
[FORMAT_RGBA4] = ENTRY("rgba4"),
|
|
[FORMAT_R16] = ENTRY("r16"),
|
|
[FORMAT_RG16] = ENTRY("rg16"),
|
|
[FORMAT_RGBA16] = ENTRY("rgba16"),
|
|
[FORMAT_RGBA16F] = ENTRY("rgba16f"),
|
|
[FORMAT_RGBA32F] = ENTRY("rgba32f"),
|
|
[FORMAT_R16F] = ENTRY("r16f"),
|
|
[FORMAT_R32F] = ENTRY("r32f"),
|
|
[FORMAT_RG16F] = ENTRY("rg16f"),
|
|
[FORMAT_RG32F] = ENTRY("rg32f"),
|
|
[FORMAT_RGB5A1] = ENTRY("rgb5a1"),
|
|
[FORMAT_RGB10A2] = ENTRY("rgb10a2"),
|
|
[FORMAT_RG11B10F] = ENTRY("rg11b10f"),
|
|
[FORMAT_D16] = ENTRY("d16"),
|
|
[FORMAT_D32F] = ENTRY("d32f"),
|
|
[FORMAT_D24S8] = ENTRY("d24s8"),
|
|
[FORMAT_DXT1] = ENTRY("dxt1"),
|
|
[FORMAT_DXT3] = ENTRY("dxt3"),
|
|
[FORMAT_DXT5] = ENTRY("dxt5"),
|
|
[FORMAT_ASTC_4x4] = ENTRY("astc4x4"),
|
|
[FORMAT_ASTC_5x4] = ENTRY("astc5x4"),
|
|
[FORMAT_ASTC_5x5] = ENTRY("astc5x5"),
|
|
[FORMAT_ASTC_6x5] = ENTRY("astc6x5"),
|
|
[FORMAT_ASTC_6x6] = ENTRY("astc6x6"),
|
|
[FORMAT_ASTC_8x5] = ENTRY("astc8x5"),
|
|
[FORMAT_ASTC_8x6] = ENTRY("astc8x6"),
|
|
[FORMAT_ASTC_8x8] = ENTRY("astc8x8"),
|
|
[FORMAT_ASTC_10x5] = ENTRY("astc10x5"),
|
|
[FORMAT_ASTC_10x6] = ENTRY("astc10x6"),
|
|
[FORMAT_ASTC_10x8] = ENTRY("astc10x8"),
|
|
[FORMAT_ASTC_10x10] = ENTRY("astc10x10"),
|
|
[FORMAT_ASTC_12x10] = ENTRY("astc12x10"),
|
|
[FORMAT_ASTC_12x12] = ENTRY("astc12x12"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrTextureType[] = {
|
|
[TEXTURE_2D] = ENTRY("2d"),
|
|
[TEXTURE_ARRAY] = ENTRY("array"),
|
|
[TEXTURE_CUBE] = ENTRY("cube"),
|
|
[TEXTURE_VOLUME] = ENTRY("volume"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrUniformAccess[] = {
|
|
[ACCESS_READ] = ENTRY("read"),
|
|
[ACCESS_WRITE] = ENTRY("write"),
|
|
[ACCESS_READ_WRITE] = ENTRY("readwrite"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrVerticalAlign[] = {
|
|
[ALIGN_TOP] = ENTRY("top"),
|
|
[ALIGN_MIDDLE] = ENTRY("middle"),
|
|
[ALIGN_BOTTOM] = ENTRY("bottom"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrWinding[] = {
|
|
[WINDING_CLOCKWISE] = ENTRY("clockwise"),
|
|
[WINDING_COUNTERCLOCKWISE] = ENTRY("counterclockwise"),
|
|
{ 0 }
|
|
};
|
|
|
|
StringEntry lovrWrapMode[] = {
|
|
[WRAP_CLAMP] = ENTRY("clamp"),
|
|
[WRAP_REPEAT] = ENTRY("repeat"),
|
|
[WRAP_MIRRORED_REPEAT] = ENTRY("mirroredrepeat"),
|
|
{ 0 }
|
|
};
|
|
|
|
static uint32_t luax_getvertexcount(lua_State* L, int index) {
|
|
int type = lua_type(L, index);
|
|
if (type == LUA_TTABLE) {
|
|
int count = luax_len(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 if (type == LUA_TNONE || type == LUA_TNIL) {
|
|
return 0;
|
|
} else { // vec3
|
|
return lua_gettop(L) - index + 1;
|
|
}
|
|
}
|
|
|
|
static void luax_readvertices(lua_State* L, int index, float* v, 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 = 0; j < 3; j++) {
|
|
lua_rawgeti(L, index, 3 * i + j + 1);
|
|
v[j] = lua_tonumber(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
v[3] = v[4] = v[5] = v[6] = v[7] = 0.f;
|
|
v += 8;
|
|
}
|
|
} else {
|
|
lua_pop(L, 1);
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
lua_rawgeti(L, index, i + 1);
|
|
vec3_init(v, luax_checkvector(L, -1, V_VEC3, NULL));
|
|
lua_pop(L, 1);
|
|
v[3] = v[4] = v[5] = v[6] = v[7] = 0.f;
|
|
v += 8;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LUA_TNUMBER:
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
for (int j = 0; j < 3; j++) {
|
|
v[j] = lua_tonumber(L, index + 3 * i + j);
|
|
}
|
|
v[3] = v[4] = v[5] = v[6] = v[7] = 0.f;
|
|
v += 8;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
vec3_init(v, luax_checkvector(L, index + i, V_VEC3, NULL));
|
|
v[3] = v[4] = v[5] = v[6] = v[7] = 0.f;
|
|
v += 8;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void stencilCallback(void* userdata) {
|
|
lua_State* L = userdata;
|
|
luaL_checktype(L, -1, LUA_TFUNCTION);
|
|
lua_call(L, 0, 0);
|
|
}
|
|
|
|
// Must be released when done
|
|
static Image* luax_checkimage(lua_State* L, int index, bool flip) {
|
|
Image* image = luax_totype(L, index, Image);
|
|
|
|
if (image) {
|
|
lovrRetain(image);
|
|
} else {
|
|
Blob* blob = luax_readblob(L, index, "Texture");
|
|
image = lovrImageCreateFromBlob(blob, flip);
|
|
lovrRelease(blob, lovrBlobDestroy);
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
// Base
|
|
|
|
static int l_lovrGraphicsPresent(lua_State* L) {
|
|
lovrGraphicsPresent();
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsCreateWindow(lua_State* L) {
|
|
WindowFlags flags;
|
|
memset(&flags, 0, sizeof(flags));
|
|
|
|
if (!lua_toboolean(L, 1)) {
|
|
return 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, "resizable");
|
|
flags.resizable = 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");
|
|
Image* image = NULL;
|
|
if (!lua_isnil(L, -1)) {
|
|
image = luax_checkimage(L, -1, false);
|
|
flags.icon.data = image->blob->data;
|
|
flags.icon.width = image->width;
|
|
flags.icon.height = image->height;
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, 1, "vsync");
|
|
flags.vsync = lua_tointeger(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
lovrGraphicsCreateWindow(&flags);
|
|
luax_atexit(L, lovrGraphicsDestroy); // The lua_State that creates the window shall be the one to destroy it
|
|
lovrRelease(image, lovrImageDestroy);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetWidth(lua_State* L) {
|
|
lua_pushnumber(L, lovrGraphicsGetWidth());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetHeight(lua_State* L) {
|
|
lua_pushnumber(L, lovrGraphicsGetHeight());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetDimensions(lua_State* L) {
|
|
lua_pushnumber(L, lovrGraphicsGetWidth());
|
|
lua_pushnumber(L, lovrGraphicsGetHeight());
|
|
return 2;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetPixelDensity(lua_State* L) {
|
|
lua_pushnumber(L, lovrGraphicsGetPixelDensity());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsHasWindow(lua_State *L) {
|
|
bool window = os_window_is_open();
|
|
lua_pushboolean(L, window);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetViewPose(lua_State* L) {
|
|
uint32_t view = luaL_checkinteger(L, 1) - 1;
|
|
lovrAssert(view < 2, "Invalid view index %d", view + 1);
|
|
if (lua_gettop(L) > 1) {
|
|
float* matrix = luax_checkvector(L, 2, V_MAT4, NULL);
|
|
bool invert = lua_toboolean(L, 3);
|
|
lovrGraphicsGetViewMatrix(view, matrix);
|
|
if (!invert) mat4_invert(matrix);
|
|
lua_settop(L, 2);
|
|
return 1;
|
|
} else {
|
|
float matrix[16], angle, ax, ay, az;
|
|
lovrGraphicsGetViewMatrix(view, matrix);
|
|
mat4_invert(matrix);
|
|
mat4_getAngleAxis(matrix, &angle, &ax, &ay, &az);
|
|
lua_pushnumber(L, matrix[12]);
|
|
lua_pushnumber(L, matrix[13]);
|
|
lua_pushnumber(L, matrix[14]);
|
|
lua_pushnumber(L, angle);
|
|
lua_pushnumber(L, ax);
|
|
lua_pushnumber(L, ay);
|
|
lua_pushnumber(L, az);
|
|
return 7;
|
|
}
|
|
}
|
|
|
|
static int l_lovrGraphicsSetViewPose(lua_State* L) {
|
|
uint32_t view = luaL_checkinteger(L, 1) - 1;
|
|
lovrAssert(view < 2, "Invalid view index %d", view + 1);
|
|
VectorType t;
|
|
float* m = luax_tovector(L, 2, &t);
|
|
if (m && t == V_MAT4) {
|
|
float matrix[16];
|
|
mat4_init(matrix, m);
|
|
bool inverted = lua_toboolean(L, 3);
|
|
if (!inverted) mat4_invert(matrix);
|
|
lovrGraphicsSetViewMatrix(view, matrix);
|
|
} else {
|
|
int index = 2;
|
|
float position[4], orientation[4], matrix[16];
|
|
index = luax_readvec3(L, index, position, "vec3, number, or mat4");
|
|
index = luax_readquat(L, index, orientation, NULL);
|
|
mat4_fromQuat(matrix, orientation);
|
|
memcpy(matrix + 12, position, 3 * sizeof(float));
|
|
mat4_invert(matrix);
|
|
lovrGraphicsSetViewMatrix(view, matrix);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetProjection(lua_State* L) {
|
|
uint32_t view = luaL_checkinteger(L, 1) - 1;
|
|
lovrAssert(view < 2, "Invalid view index %d", view + 1);
|
|
if (lua_gettop(L) > 1) {
|
|
float* matrix = luax_checkvector(L, 2, V_MAT4, NULL);
|
|
lovrGraphicsGetProjection(view, matrix);
|
|
lua_settop(L, 2);
|
|
return 1;
|
|
} else {
|
|
float matrix[16], left, right, up, down;
|
|
lovrGraphicsGetProjection(view, matrix);
|
|
mat4_getFov(matrix, &left, &right, &up, &down);
|
|
lua_pushnumber(L, left);
|
|
lua_pushnumber(L, right);
|
|
lua_pushnumber(L, up);
|
|
lua_pushnumber(L, down);
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
static int l_lovrGraphicsSetProjection(lua_State* L) {
|
|
uint32_t view = luaL_checkinteger(L, 1) - 1;
|
|
lovrAssert(view < 2, "Invalid view index %d", view + 1);
|
|
if (lua_type(L, 2) == LUA_TNUMBER) {
|
|
float left = luax_checkfloat(L, 2);
|
|
float right = luax_checkfloat(L, 3);
|
|
float up = luax_checkfloat(L, 4);
|
|
float down = luax_checkfloat(L, 5);
|
|
float clipNear = luax_optfloat(L, 6, .1f);
|
|
float clipFar = luax_optfloat(L, 7, 100.f);
|
|
float matrix[16];
|
|
mat4_fov(matrix, left, right, up, down, clipNear, clipFar);
|
|
lovrGraphicsSetProjection(view, matrix);
|
|
} else {
|
|
float* m = luax_checkvector(L, 2, V_MAT4, "mat4 or number");
|
|
lovrGraphicsSetProjection(view, m);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsTick(lua_State* L) {
|
|
const char* label = luaL_checkstring(L, 1);
|
|
lovrGraphicsTick(label);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsTock(lua_State* L) {
|
|
lovrGraphicsFlush();
|
|
const char* label = luaL_checkstring(L, 1);
|
|
lua_pushnumber(L, lovrGraphicsTock(label));
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetFeatures(lua_State* L) {
|
|
const GpuFeatures* features = lovrGraphicsGetFeatures();
|
|
lua_newtable(L);
|
|
lua_pushboolean(L, features->astc);
|
|
lua_setfield(L, -2, "astc");
|
|
lua_pushboolean(L, features->compute);
|
|
lua_setfield(L, -2, "compute");
|
|
lua_pushboolean(L, features->dxt);
|
|
lua_setfield(L, -2, "dxt");
|
|
lua_pushboolean(L, features->instancedStereo);
|
|
lua_setfield(L, -2, "instancedstereo");
|
|
lua_pushboolean(L, features->multiview);
|
|
lua_setfield(L, -2, "multiview");
|
|
lua_pushboolean(L, features->timers);
|
|
lua_setfield(L, -2, "timers");
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetLimits(lua_State* L) {
|
|
const GpuLimits* limits = lovrGraphicsGetLimits();
|
|
lua_newtable(L);
|
|
lua_pushnumber(L, limits->pointSizes[1]);
|
|
lua_setfield(L, -2, "pointsize");
|
|
lua_pushinteger(L, limits->textureSize);
|
|
lua_setfield(L, -2, "texturesize");
|
|
lua_pushinteger(L, limits->textureMSAA);
|
|
lua_setfield(L, -2, "texturemsaa");
|
|
lua_pushinteger(L, limits->textureAnisotropy);
|
|
lua_setfield(L, -2, "anisotropy");
|
|
lua_pushinteger(L, limits->blockSize);
|
|
lua_setfield(L, -2, "blocksize");
|
|
lua_createtable(L, 3, 0);
|
|
lua_pushinteger(L, limits->compute[0]);
|
|
lua_rawseti(L, -2, 1);
|
|
lua_pushinteger(L, limits->compute[1]);
|
|
lua_rawseti(L, -2, 2);
|
|
lua_pushinteger(L, limits->compute[2]);
|
|
lua_rawseti(L, -2, 3);
|
|
lua_setfield(L, -2, "compute");
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetStats(lua_State* L) {
|
|
if (lua_gettop(L) > 0) {
|
|
luaL_checktype(L, 1, LUA_TTABLE);
|
|
lua_settop(L, 1);
|
|
} else {
|
|
lua_createtable(L, 0, 7);
|
|
}
|
|
|
|
lovrGraphicsFlush();
|
|
const GpuStats* stats = lovrGraphicsGetStats();
|
|
lua_pushinteger(L, stats->shaderSwitches);
|
|
lua_setfield(L, 1, "shaderswitches");
|
|
lua_pushinteger(L, stats->renderPasses);
|
|
lua_setfield(L, 1, "renderpasses");
|
|
lua_pushinteger(L, stats->drawCalls);
|
|
lua_setfield(L, 1, "drawcalls");
|
|
lua_pushinteger(L, stats->bufferCount);
|
|
lua_setfield(L, 1, "buffers");
|
|
lua_pushinteger(L, stats->textureCount);
|
|
lua_setfield(L, 1, "textures");
|
|
lua_pushinteger(L, stats->bufferMemory);
|
|
lua_setfield(L, 1, "buffermemory");
|
|
lua_pushinteger(L, stats->textureMemory);
|
|
lua_setfield(L, 1, "texturememory");
|
|
return 1;
|
|
}
|
|
|
|
// State
|
|
|
|
static int l_lovrGraphicsReset(lua_State* L) {
|
|
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;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetBackgroundColor(lua_State* L) {
|
|
Color color = lovrGraphicsGetBackgroundColor();
|
|
lua_pushnumber(L, color.r);
|
|
lua_pushnumber(L, color.g);
|
|
lua_pushnumber(L, color.b);
|
|
lua_pushnumber(L, color.a);
|
|
return 4;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetBackgroundColor(lua_State* L) {
|
|
Color color;
|
|
luax_readcolor(L, 1, &color);
|
|
lovrGraphicsSetBackgroundColor(color);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetBlendMode(lua_State* L) {
|
|
BlendMode mode;
|
|
BlendAlphaMode alphaMode;
|
|
lovrGraphicsGetBlendMode(&mode, &alphaMode);
|
|
if (mode == BLEND_NONE) {
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
luax_pushenum(L, BlendMode, mode);
|
|
luax_pushenum(L, BlendAlphaMode, alphaMode);
|
|
return 2;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetBlendMode(lua_State* L) {
|
|
BlendMode mode = lua_isnoneornil(L, 1) ? BLEND_NONE : luax_checkenum(L, 1, BlendMode, NULL);
|
|
BlendAlphaMode alphaMode = luax_checkenum(L, 2, BlendAlphaMode, "alphamultiply");
|
|
lovrGraphicsSetBlendMode(mode, alphaMode);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetCanvas(lua_State* L) {
|
|
Canvas* canvas = lovrGraphicsGetCanvas();
|
|
luax_pushtype(L, Canvas, canvas);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetCanvas(lua_State* L) {
|
|
Canvas* canvas = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Canvas);
|
|
lovrGraphicsSetCanvas(canvas);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetColor(lua_State* L) {
|
|
Color color = lovrGraphicsGetColor();
|
|
lua_pushnumber(L, color.r);
|
|
lua_pushnumber(L, color.g);
|
|
lua_pushnumber(L, color.b);
|
|
lua_pushnumber(L, color.a);
|
|
return 4;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetColor(lua_State* L) {
|
|
Color color;
|
|
luax_readcolor(L, 1, &color);
|
|
lovrGraphicsSetColor(color);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetColorMask(lua_State* L) {
|
|
bool r, b, g, a;
|
|
lovrGraphicsGetColorMask(&r, &g, &b, &a);
|
|
lua_pushboolean(L, r);
|
|
lua_pushboolean(L, g);
|
|
lua_pushboolean(L, b);
|
|
lua_pushboolean(L, a);
|
|
return 4;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetColorMask(lua_State* L) {
|
|
bool r = lua_toboolean(L, 1);
|
|
bool g = lua_toboolean(L, 2);
|
|
bool b = lua_toboolean(L, 3);
|
|
bool a = lua_toboolean(L, 4);
|
|
lovrGraphicsSetColorMask(r, g, b, a);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsIsCullingEnabled(lua_State* L) {
|
|
lua_pushboolean(L, lovrGraphicsIsCullingEnabled());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetCullingEnabled(lua_State* L) {
|
|
lovrGraphicsSetCullingEnabled(lua_toboolean(L, 1));
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetDefaultFilter(lua_State* L) {
|
|
TextureFilter filter = lovrGraphicsGetDefaultFilter();
|
|
luax_pushenum(L, FilterMode, filter.mode);
|
|
lua_pushnumber(L, filter.anisotropy);
|
|
return 2;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetDefaultFilter(lua_State* L) {
|
|
FilterMode mode = luax_checkenum(L, 1, FilterMode, NULL);
|
|
float anisotropy = luax_optfloat(L, 2, 1.f);
|
|
lovrGraphicsSetDefaultFilter((TextureFilter) { .mode = mode, .anisotropy = anisotropy });
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetDepthTest(lua_State* L) {
|
|
CompareMode mode;
|
|
bool write;
|
|
lovrGraphicsGetDepthTest(&mode, &write);
|
|
luax_pushenum(L, CompareMode, mode);
|
|
lua_pushboolean(L, write);
|
|
return 2;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetDepthTest(lua_State* L) {
|
|
CompareMode mode = lua_isnoneornil(L, 1) ? COMPARE_NONE : luax_checkenum(L, 1, CompareMode, NULL);
|
|
bool write = lua_isnoneornil(L, 2) ? true : lua_toboolean(L, 2);
|
|
lovrGraphicsSetDepthTest(mode, write);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetFont(lua_State* L) {
|
|
Font* font = lovrGraphicsGetFont();
|
|
luax_pushtype(L, Font, font);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetFont(lua_State* L) {
|
|
Font* font = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Font);
|
|
lovrGraphicsSetFont(font);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetLineWidth(lua_State* L) {
|
|
lua_pushnumber(L, lovrGraphicsGetLineWidth());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetLineWidth(lua_State* L) {
|
|
uint8_t width = (uint8_t) luaL_optinteger(L, 1, 1);
|
|
lovrGraphicsSetLineWidth(width);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetPointSize(lua_State* L) {
|
|
lua_pushnumber(L, lovrGraphicsGetPointSize());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetPointSize(lua_State* L) {
|
|
float size = luax_optfloat(L, 1, 1.f);
|
|
lovrGraphicsSetPointSize(size);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetShader(lua_State* L) {
|
|
Shader* shader = lovrGraphicsGetShader();
|
|
luax_pushtype(L, Shader, shader);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetShader(lua_State* L) {
|
|
Shader* shader = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Shader);
|
|
lovrGraphicsSetShader(shader);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetStencilTest(lua_State* L) {
|
|
CompareMode mode;
|
|
int value;
|
|
lovrGraphicsGetStencilTest(&mode, &value);
|
|
luax_pushenum(L, CompareMode, mode);
|
|
lua_pushinteger(L, value);
|
|
return 2;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetStencilTest(lua_State* L) {
|
|
if (lua_isnoneornil(L, 1)) {
|
|
lovrGraphicsSetStencilTest(COMPARE_NONE, 0);
|
|
} else {
|
|
CompareMode mode = luax_checkenum(L, 1, CompareMode, NULL);
|
|
int value = luaL_checkinteger(L, 2);
|
|
lovrGraphicsSetStencilTest(mode, value);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsGetWinding(lua_State* L) {
|
|
luax_pushenum(L, Winding, lovrGraphicsGetWinding());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetWinding(lua_State* L) {
|
|
Winding winding = luax_checkenum(L, 1, Winding, NULL);
|
|
lovrGraphicsSetWinding(winding);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsIsWireframe(lua_State* L) {
|
|
lua_pushboolean(L, lovrGraphicsIsWireframe());
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsSetWireframe(lua_State* L) {
|
|
lovrGraphicsSetWireframe(lua_toboolean(L, 1));
|
|
return 0;
|
|
}
|
|
|
|
// Transforms
|
|
|
|
static int l_lovrGraphicsPush(lua_State* L) {
|
|
lovrGraphicsPush();
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsPop(lua_State* L) {
|
|
lovrGraphicsPop();
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsOrigin(lua_State* L) {
|
|
lovrGraphicsOrigin();
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsTranslate(lua_State* L) {
|
|
float translation[4];
|
|
luax_readvec3(L, 1, translation, NULL);
|
|
lovrGraphicsTranslate(translation);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsRotate(lua_State* L) {
|
|
float rotation[4];
|
|
luax_readquat(L, 1, rotation, NULL);
|
|
lovrGraphicsRotate(rotation);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsScale(lua_State* L) {
|
|
float scale[4];
|
|
luax_readscale(L, 1, scale, 3, NULL);
|
|
lovrGraphicsScale(scale);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsTransform(lua_State* L) {
|
|
float transform[16];
|
|
luax_readmat4(L, 1, transform, 3);
|
|
lovrGraphicsMatrixTransform(transform);
|
|
return 0;
|
|
}
|
|
|
|
// Rendering
|
|
|
|
static int l_lovrGraphicsClear(lua_State* L) {
|
|
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 == 1) {
|
|
luax_readcolor(L, index, &color);
|
|
lovrGraphicsClear(&color, NULL, NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (top >= index) {
|
|
if (lua_type(L, index) == LUA_TNUMBER) {
|
|
color.r = luax_checkfloat(L, index++);
|
|
color.g = luax_checkfloat(L, index++);
|
|
color.b = luax_checkfloat(L, index++);
|
|
color.a = luax_optfloat(L, index++, 1.f);
|
|
} else {
|
|
clearColor = lua_toboolean(L, index++);
|
|
}
|
|
}
|
|
|
|
if (top >= index) {
|
|
if (lua_type(L, index) == LUA_TNUMBER) {
|
|
depth = luax_checkfloat(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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int l_lovrGraphicsFlush(lua_State* L) {
|
|
lovrGraphicsFlush();
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsLine(lua_State* L) {
|
|
float* vertices;
|
|
uint32_t count = luax_getvertexcount(L, 1);
|
|
lovrAssert(count >= 2, "Need at least 2 points to draw a line");
|
|
lovrGraphicsLine(count, &vertices);
|
|
luax_readvertices(L, 1, vertices, count);
|
|
return 0;
|
|
}
|
|
|
|
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 = luax_checkenum(L, 1, DrawStyle, NULL);
|
|
}
|
|
float transform[16];
|
|
int index = luax_readmat4(L, 2, transform, 2);
|
|
float u = luax_optfloat(L, index++, 0.f);
|
|
float v = luax_optfloat(L, index++, 0.f);
|
|
float w = luax_optfloat(L, index++, 1.f - u);
|
|
float h = luax_optfloat(L, index++, 1.f - v);
|
|
lovrGraphicsPlane(style, material, transform, u, v, w, h);
|
|
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 = luax_checkenum(L, 1, DrawStyle, NULL);
|
|
}
|
|
float transform[16];
|
|
luax_readmat4(L, 2, transform, scaleComponents);
|
|
lovrGraphicsBox(style, material, transform);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsCube(lua_State* L) {
|
|
return luax_rectangularprism(L, 1);
|
|
}
|
|
|
|
static int l_lovrGraphicsBox(lua_State* L) {
|
|
return luax_rectangularprism(L, 3);
|
|
}
|
|
|
|
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 = luax_checkenum(L, 1, DrawStyle, NULL);
|
|
}
|
|
ArcMode mode = ARC_MODE_PIE;
|
|
int index = 2;
|
|
if (lua_type(L, index) == LUA_TSTRING) {
|
|
mode = luax_checkenum(L, index++, ArcMode, NULL);
|
|
}
|
|
float transform[16];
|
|
index = luax_readmat4(L, index, transform, 1);
|
|
float r1 = luax_optfloat(L, index++, 0.f);
|
|
float r2 = luax_optfloat(L, index++, 2.f * (float) M_PI);
|
|
int segments = luaL_optinteger(L, index, 64) * (MIN(fabsf(r2 - r1), 2.f * (float) M_PI) / (2.f * (float) M_PI));
|
|
lovrGraphicsArc(style, mode, material, transform, r1, r2, segments);
|
|
return 0;
|
|
}
|
|
|
|
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 = luax_checkenum(L, 1, DrawStyle, NULL);
|
|
}
|
|
float transform[16];
|
|
int index = luax_readmat4(L, 2, transform, 1);
|
|
int segments = luaL_optinteger(L, index, 32);
|
|
lovrGraphicsCircle(style, material, transform, segments);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsCylinder(lua_State* L) {
|
|
float transform[16];
|
|
Material* material = luax_totype(L, 1, Material);
|
|
int index = material ? 2 : 1;
|
|
index = luax_readmat4(L, index, transform, 1);
|
|
float r1 = luax_optfloat(L, index++, 1.f);
|
|
float r2 = luax_optfloat(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)));
|
|
lovrGraphicsCylinder(material, transform, r1, r2, capped, segments);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsSphere(lua_State* L) {
|
|
float transform[16];
|
|
Material* material = luax_totype(L, 1, Material);
|
|
int index = material ? 2 : 1;
|
|
index = luax_readmat4(L, index, transform, 1);
|
|
int segments = luaL_optinteger(L, index, 30);
|
|
lovrGraphicsSphere(material, transform, segments);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsSkybox(lua_State* L) {
|
|
Texture* texture = luax_checktype(L, 1, Texture);
|
|
lovrGraphicsSkybox(texture);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsPrint(lua_State* L) {
|
|
size_t length;
|
|
const char* str = luaL_checklstring(L, 1, &length);
|
|
float transform[16];
|
|
int index = luax_readmat4(L, 2, transform, 1);
|
|
float wrap = luax_optfloat(L, index++, 0.f);
|
|
HorizontalAlign halign = luax_checkenum(L, index++, HorizontalAlign, "center");
|
|
VerticalAlign valign = luax_checkenum(L, index++, VerticalAlign, "middle");
|
|
lovrGraphicsPrint(str, length, transform, wrap, halign, valign);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsStencil(lua_State* L) {
|
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
|
StencilAction action = luax_checkenum(L, 2, StencilAction, "replace");
|
|
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);
|
|
}
|
|
lua_settop(L, 1);
|
|
bool r, g, b, a;
|
|
lovrGraphicsGetColorMask(&r, &g, &b, &a);
|
|
lovrGraphicsSetColorMask(false, false, false, false);
|
|
lovrGraphicsStencil(action, replaceValue, stencilCallback, L);
|
|
lovrGraphicsSetColorMask(r, g, b, a);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsFill(lua_State* L) {
|
|
Texture* texture = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Texture);
|
|
float u = luax_optfloat(L, 2, 0.f);
|
|
float v = luax_optfloat(L, 3, 0.f);
|
|
float w = luax_optfloat(L, 4, 1.f - u);
|
|
float h = luax_optfloat(L, 5, 1.f - v);
|
|
lovrGraphicsFill(texture, u, v, w, h);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrGraphicsCompute(lua_State* L) {
|
|
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);
|
|
lovrGraphicsCompute(shader, x, y, z);
|
|
return 0;
|
|
}
|
|
|
|
// Types
|
|
|
|
static void luax_checkuniformtype(lua_State* L, int index, UniformType* baseType, int* components) {
|
|
size_t length;
|
|
lovrAssert(lua_type(L, index) == LUA_TSTRING, "Uniform types must be strings, got %s", lua_typename(L, index));
|
|
const char* type = lua_tolstring(L, index, &length);
|
|
|
|
if (!strcmp(type, "float")) {
|
|
*baseType = UNIFORM_FLOAT;
|
|
*components = 1;
|
|
} else if (!strcmp(type, "int")) {
|
|
*baseType = UNIFORM_INT;
|
|
*components = 1;
|
|
} else {
|
|
int n = type[length - 1] - '0';
|
|
lovrAssert(n >= 2 && n <= 4, "Unknown uniform type '%s'", type);
|
|
if (type[0] == 'v' && type[1] == 'e' && type[2] == 'c' && length == 4) {
|
|
*baseType = UNIFORM_FLOAT;
|
|
*components = n;
|
|
} else if (type[0] == 'i' && type[1] == 'v' && type[2] == 'e' && type[3] == 'c' && length == 5) {
|
|
*baseType = UNIFORM_INT;
|
|
*components = n;
|
|
} else if (type[0] == 'm' && type[1] == 'a' && type[2] == 't' && length == 4) {
|
|
*baseType = UNIFORM_MATRIX;
|
|
*components = n;
|
|
} else {
|
|
lovrThrow("Unknown uniform type '%s'", type);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int l_lovrGraphicsNewCanvas(lua_State* L) {
|
|
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;
|
|
}
|
|
|
|
TextureFormat format = FORMAT_RGBA;
|
|
bool anonymous = attachmentCount == 0;
|
|
|
|
CanvasFlags flags = {
|
|
.depth = { .enabled = true, .readable = false, .format = FORMAT_D16 },
|
|
.stereo = anonymous,
|
|
.msaa = 0,
|
|
.mipmaps = true
|
|
};
|
|
|
|
if (lua_istable(L, index)) {
|
|
lua_getfield(L, index, "depth");
|
|
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 = luax_checkenum(L, -1, TextureFormat, NULL); 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 = luax_checkenum(L, -1, TextureFormat, NULL);
|
|
lua_pop(L, 1);
|
|
break;
|
|
default: lovrThrow("Expected boolean, string, or table for Canvas depth flag");
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, index, "stereo");
|
|
flags.stereo = lua_isnil(L, -1) ? flags.stereo : lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, index, "msaa");
|
|
flags.msaa = lua_isnil(L, -1) ? flags.msaa : luaL_checkinteger(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, index, "mipmaps");
|
|
flags.mipmaps = lua_isnil(L, -1) ? flags.mipmaps : lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
if (attachmentCount == 0) {
|
|
lua_getfield(L, index, "format");
|
|
format = luax_checkenum(L, -1, TextureFormat, "rgba");
|
|
anonymous = lua_isnil(L, -1) || lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
if (width == 0 && height == 0 && attachmentCount > 0) {
|
|
width = lovrTextureGetWidth(attachments[0].texture, attachments[0].level);
|
|
height = lovrTextureGetHeight(attachments[0].texture, attachments[0].level);
|
|
}
|
|
|
|
Canvas* canvas = lovrCanvasCreate(width, height, flags);
|
|
|
|
if (anonymous) {
|
|
bool multiview = flags.stereo && lovrGraphicsGetFeatures()->multiview;
|
|
TextureType textureType = multiview ? TEXTURE_ARRAY : TEXTURE_2D;
|
|
uint32_t depth = multiview ? 2 : 1;
|
|
Texture* texture = lovrTextureCreate(textureType, NULL, 0, true, flags.mipmaps, flags.msaa);
|
|
lovrTextureAllocate(texture, lovrCanvasGetWidth(canvas), lovrCanvasGetHeight(canvas), depth, format);
|
|
lovrTextureSetWrap(texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP, .r = WRAP_CLAMP });
|
|
attachments[0] = (Attachment) { texture, 0, 0 };
|
|
attachmentCount++;
|
|
}
|
|
|
|
if (attachmentCount > 0) {
|
|
lovrCanvasSetAttachments(canvas, attachments, attachmentCount);
|
|
if (anonymous) {
|
|
lovrRelease(attachments[0].texture, lovrTextureDestroy);
|
|
}
|
|
}
|
|
|
|
luax_pushtype(L, Canvas, canvas);
|
|
lovrRelease(canvas, lovrCanvasDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewFont(lua_State* L) {
|
|
Rasterizer* rasterizer = luax_totype(L, 1, Rasterizer);
|
|
uint32_t padding = 2;
|
|
double spread = 4.;
|
|
|
|
if (!rasterizer) {
|
|
Blob* blob = NULL;
|
|
float size;
|
|
|
|
if (lua_type(L, 1) == LUA_TNUMBER || lua_isnoneornil(L, 1)) {
|
|
size = luaL_optinteger(L, 1, 32);
|
|
padding = luaL_optinteger(L, 2, padding);
|
|
spread = luaL_optnumber(L, 3, spread);
|
|
} else {
|
|
blob = luax_readblob(L, 1, "Font");
|
|
size = luaL_optinteger(L, 2, 32);
|
|
padding = luaL_optinteger(L, 3, padding);
|
|
spread = luaL_optnumber(L, 4, spread);
|
|
}
|
|
|
|
rasterizer = lovrRasterizerCreate(blob, size);
|
|
lovrRelease(blob, lovrBlobDestroy);
|
|
} else {
|
|
padding = luaL_optinteger(L, 2, padding);
|
|
spread = luaL_optnumber(L, 3, spread);
|
|
}
|
|
|
|
Font* font = lovrFontCreate(rasterizer, padding, spread);
|
|
luax_pushtype(L, Font, font);
|
|
lovrRelease(rasterizer, lovrRasterizerDestroy);
|
|
lovrRelease(font, lovrFontDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewMaterial(lua_State* L) {
|
|
Material* material = lovrMaterialCreate();
|
|
|
|
int index = 1;
|
|
|
|
if (lua_type(L, index) == LUA_TSTRING) {
|
|
Blob* blob = luax_readblob(L, index++, "Texture");
|
|
Image* image = lovrImageCreateFromBlob(blob, true);
|
|
Texture* texture = lovrTextureCreate(TEXTURE_2D, &image, 1, true, true, 0);
|
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
|
lovrRelease(blob, lovrBlobDestroy);
|
|
lovrRelease(image, lovrImageDestroy);
|
|
lovrRelease(texture, lovrTextureDestroy);
|
|
} else if (lua_isuserdata(L, index)) {
|
|
Texture* texture = luax_checktype(L, index, Texture);
|
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
|
index++;
|
|
}
|
|
|
|
if (lua_isnumber(L, index)) {
|
|
Color color;
|
|
luax_readcolor(L, index, &color);
|
|
lovrMaterialSetColor(material, COLOR_DIFFUSE, color);
|
|
}
|
|
|
|
luax_pushtype(L, Material, material);
|
|
lovrRelease(material, lovrMaterialDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewMesh(lua_State* L) {
|
|
uint32_t count;
|
|
int dataIndex = 0;
|
|
int formatIndex = 0;
|
|
int drawModeIndex = 2;
|
|
Blob* blob = NULL;
|
|
|
|
if (lua_isnumber(L, 1)) {
|
|
count = lua_tointeger(L, 1);
|
|
} else if (lua_istable(L, 1)) {
|
|
if (lua_isnumber(L, 2)) {
|
|
drawModeIndex++;
|
|
formatIndex = 1;
|
|
count = lua_tointeger(L, 2);
|
|
dataIndex = 0;
|
|
} else if (lua_istable(L, 2)) {
|
|
drawModeIndex++;
|
|
formatIndex = 1;
|
|
count = luax_len(L, 2);
|
|
dataIndex = 2;
|
|
} else if ((blob = luax_totype(L, 2, Blob)) != NULL) {
|
|
drawModeIndex++;
|
|
formatIndex = 1;
|
|
dataIndex = 2;
|
|
} else {
|
|
count = luax_len(L, 1);
|
|
dataIndex = 1;
|
|
}
|
|
} else {
|
|
luaL_argerror(L, 1, "table or number expected");
|
|
return 0;
|
|
}
|
|
|
|
MeshAttribute attributes[MAX_ATTRIBUTES];
|
|
const char* attributeNames[MAX_ATTRIBUTES];
|
|
int attributeCount = 0;
|
|
size_t stride = 0;
|
|
|
|
if (formatIndex == 0) {
|
|
stride = 32;
|
|
attributeCount = 3;
|
|
attributes[0] = (MeshAttribute) { .offset = 0, .stride = stride, .type = F32, .components = 3 };
|
|
attributes[1] = (MeshAttribute) { .offset = 12, .stride = stride, .type = F32, .components = 3 };
|
|
attributes[2] = (MeshAttribute) { .offset = 24, .stride = stride, .type = F32, .components = 2 };
|
|
attributeNames[0] = "lovrPosition";
|
|
attributeNames[1] = "lovrNormal";
|
|
attributeNames[2] = "lovrTexCoord";
|
|
} else {
|
|
attributeCount = luax_len(L, formatIndex);
|
|
lovrAssert(attributeCount >= 0 && attributeCount <= MAX_ATTRIBUTES, "Attribute count must be between 0 and %d", MAX_ATTRIBUTES);
|
|
for (int i = 0; i < attributeCount; i++) {
|
|
lua_rawgeti(L, formatIndex, i + 1);
|
|
lovrAssert(lua_type(L, -1) == LUA_TTABLE, "Attribute definitions must be tables containing name, type, and component count");
|
|
lua_rawgeti(L, -1, 3);
|
|
lua_rawgeti(L, -2, 2);
|
|
lua_rawgeti(L, -3, 1);
|
|
|
|
attributeNames[i] = lua_tostring(L, -1);
|
|
attributes[i].offset = (uint32_t) stride;
|
|
attributes[i].type = luax_checkenum(L, -2, AttributeType, "float");
|
|
attributes[i].components = luaL_optinteger(L, -3, 1);
|
|
|
|
switch (attributes[i].type) {
|
|
case I8: case U8: stride += 1 * attributes[i].components; break;
|
|
case I16: case U16: stride += 2 * attributes[i].components; break;
|
|
case I32: case U32: case F32: stride += 4 * attributes[i].components; break;
|
|
}
|
|
lua_pop(L, 4);
|
|
}
|
|
}
|
|
|
|
if (blob) {
|
|
lovrAssert(blob->size / stride < UINT32_MAX, "Too many vertices in Blob");
|
|
count = (uint32_t) (blob->size / stride);
|
|
}
|
|
|
|
DrawMode mode = luax_checkenum(L, drawModeIndex, DrawMode, "fan");
|
|
BufferUsage usage = luax_checkenum(L, drawModeIndex + 1, BufferUsage, "dynamic");
|
|
bool readable = lua_toboolean(L, drawModeIndex + 2);
|
|
Buffer* vertexBuffer = lovrBufferCreate(count * stride, NULL, BUFFER_VERTEX, usage, readable);
|
|
Mesh* mesh = lovrMeshCreate(mode, vertexBuffer, count);
|
|
|
|
for (int i = 0; i < attributeCount; i++) {
|
|
lovrMeshAttachAttribute(mesh, attributeNames[i], &(MeshAttribute) {
|
|
.buffer = vertexBuffer,
|
|
.offset = attributes[i].offset,
|
|
.stride = stride,
|
|
.type = attributes[i].type,
|
|
.components = attributes[i].components,
|
|
.normalized = attributes[i].type == I8 || attributes[i].type == U8
|
|
});
|
|
}
|
|
|
|
lovrMeshAttachAttribute(mesh, "lovrDrawID", &(MeshAttribute) {
|
|
.buffer = lovrGraphicsGetIdentityBuffer(),
|
|
.type = U8,
|
|
.components = 1,
|
|
.divisor = 1
|
|
});
|
|
|
|
if (dataIndex) {
|
|
AttributeData data = { .raw = lovrBufferMap(vertexBuffer, 0, false) };
|
|
|
|
if (blob) {
|
|
memcpy(data.raw, blob->data, count * stride);
|
|
} else {
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
lua_rawgeti(L, dataIndex, i + 1);
|
|
lovrAssert(lua_istable(L, -1), "Vertices should be specified as a table of tables");
|
|
|
|
int component = 0;
|
|
for (int j = 0; j < attributeCount; j++) {
|
|
MeshAttribute* attribute = &attributes[j];
|
|
for (unsigned k = 0; k < attribute->components; k++) {
|
|
lua_rawgeti(L, -1, ++component);
|
|
switch (attribute->type) {
|
|
case I8: *data.i8++ = luaL_optinteger(L, -1, 0); break;
|
|
case U8: *data.u8++ = luaL_optinteger(L, -1, 0); break;
|
|
case I16: *data.i16++ = luaL_optinteger(L, -1, 0); break;
|
|
case U16: *data.u16++ = luaL_optinteger(L, -1, 0); break;
|
|
case I32: *data.i32++ = luaL_optinteger(L, -1, 0); break;
|
|
case U32: *data.u32++ = luaL_optinteger(L, -1, 0); break;
|
|
case F32: *data.f32++ = luaL_optnumber(L, -1, 0.); break;
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
lovrBufferFlush(vertexBuffer, 0, count * stride);
|
|
lovrBufferUnmap(vertexBuffer);
|
|
}
|
|
|
|
lovrRelease(vertexBuffer, lovrBufferDestroy);
|
|
|
|
luax_pushtype(L, Mesh, mesh);
|
|
lovrRelease(mesh, lovrMeshDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewModel(lua_State* L) {
|
|
ModelData* modelData = luax_totype(L, 1, ModelData);
|
|
|
|
if (!modelData) {
|
|
Blob* blob = luax_readblob(L, 1, "Model");
|
|
modelData = lovrModelDataCreate(blob, luax_readfile);
|
|
lovrRelease(blob, lovrBlobDestroy);
|
|
} else {
|
|
lovrRetain(modelData);
|
|
}
|
|
|
|
Model* model = lovrModelCreate(modelData);
|
|
luax_pushtype(L, Model, model);
|
|
lovrRelease(modelData, lovrModelDataDestroy);
|
|
lovrRelease(model, lovrModelDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static const char* luax_readshadersource(lua_State* L, int index, int *outLength) {
|
|
if (lua_isnoneornil(L, index)) {
|
|
return NULL;
|
|
}
|
|
|
|
Blob* blob = luax_totype(L, index, Blob);
|
|
if (blob) {
|
|
*outLength = (int) blob->size;
|
|
return blob->data;
|
|
}
|
|
|
|
size_t length;
|
|
const char* source = luaL_checklstring(L, index, &length);
|
|
if (memchr(source, '\n', MIN(1024, length))) {
|
|
*outLength = (int) length;
|
|
return source;
|
|
} else {
|
|
void* contents = luax_readfile(source, &length);
|
|
lovrAssert(contents, "Could not read shader from file '%s'", source);
|
|
*outLength = (int) length;
|
|
return contents;
|
|
}
|
|
}
|
|
|
|
#define MAX_SHADER_FLAGS 32
|
|
static void luax_parseshaderflags(lua_State* L, int index, ShaderFlag flags[MAX_SHADER_FLAGS], uint32_t* count) {
|
|
if (lua_isnil(L, -1)) {
|
|
*count = 0;
|
|
return;
|
|
}
|
|
|
|
lovrAssert(lua_istable(L, -1), "Shader flags must be a table");
|
|
lua_pushnil(L);
|
|
while (lua_next(L, -2) != 0) {
|
|
ShaderFlag* flag = &flags[(*count)++];
|
|
|
|
lovrAssert(*count <= MAX_SHADER_FLAGS, "Too many shader flags (max is %d)", MAX_SHADER_FLAGS);
|
|
|
|
if (lua_type(L, -2) == LUA_TSTRING) {
|
|
flag->name = lua_tostring(L, -2);
|
|
} else if (lua_isnumber(L, -2)) {
|
|
flag->index = lua_tointeger(L, -2);
|
|
} else {
|
|
lovrThrow("Shader flag names must be strings or numbers");
|
|
}
|
|
|
|
switch (lua_type(L, -1)) {
|
|
case LUA_TBOOLEAN:
|
|
flag->type = FLAG_BOOL;
|
|
flag->value.b32 = lua_toboolean(L, -1);
|
|
break;
|
|
case LUA_TNUMBER:
|
|
flag->type = FLAG_INT;
|
|
flag->value.i32 = lua_tointeger(L, -1);
|
|
break;
|
|
default:
|
|
lovrThrow("Shader flag values must be booleans or integers");
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
static int l_lovrGraphicsNewShader(lua_State* L) {
|
|
ShaderFlag flags[MAX_SHADER_FLAGS];
|
|
uint32_t flagCount = 0;
|
|
bool multiview = true;
|
|
Shader* shader;
|
|
|
|
if (lua_isstring(L, 1) && (lua_istable(L, 2) || lua_gettop(L) == 1)) {
|
|
DefaultShader shaderType = luax_checkenum(L, 1, DefaultShader, NULL);
|
|
|
|
if (lua_istable(L, 2)) {
|
|
lua_getfield(L, 2, "flags");
|
|
luax_parseshaderflags(L, -1, flags, &flagCount);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, 2, "stereo");
|
|
multiview = lua_isnil(L, -1) ? multiview : lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
shader = lovrShaderCreateDefault(shaderType, flags, flagCount, multiview);
|
|
|
|
// Builtin uniforms
|
|
if (shaderType == SHADER_STANDARD) {
|
|
lovrShaderSetFloats(shader, "lovrExposure", (float[1]) { 1.f }, 0, 1);
|
|
lovrShaderSetFloats(shader, "lovrLightDirection", (float[3]) { -1.f, -1.f, -1.f }, 0, 3);
|
|
lovrShaderSetFloats(shader, "lovrLightColor", (float[4]) { 1.f, 1.f, 1.f, 1.f }, 0, 4);
|
|
}
|
|
} else {
|
|
int vertexSourceLength;
|
|
const char* vertexSource = luax_readshadersource(L, 1, &vertexSourceLength);
|
|
int fragmentSourceLength;
|
|
const char* fragmentSource = luax_readshadersource(L, 2, &fragmentSourceLength);
|
|
|
|
if (lua_istable(L, 3)) {
|
|
lua_getfield(L, 3, "flags");
|
|
luax_parseshaderflags(L, -1, flags, &flagCount);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, 3, "stereo");
|
|
multiview = lua_isnil(L, -1) ? multiview : lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
shader = lovrShaderCreateGraphics(vertexSource, vertexSourceLength, fragmentSource, fragmentSourceLength, flags, flagCount, multiview);
|
|
}
|
|
|
|
luax_pushtype(L, Shader, shader);
|
|
lovrRelease(shader, lovrShaderDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewComputeShader(lua_State* L) {
|
|
int sourceLength;
|
|
const char* source = luax_readshadersource(L, 1, &sourceLength);
|
|
luaL_argcheck(L, source, 1, "string or Blob expected");
|
|
ShaderFlag flags[MAX_SHADER_FLAGS];
|
|
uint32_t flagCount = 0;
|
|
|
|
if (lua_istable(L, 2)) {
|
|
lua_getfield(L, 2, "flags");
|
|
luax_parseshaderflags(L, -1, flags, &flagCount);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
Shader* shader = lovrShaderCreateCompute(source, sourceLength, flags, flagCount);
|
|
luax_pushtype(L, Shader, shader);
|
|
lovrRelease(shader, lovrShaderDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewShaderBlock(lua_State* L) {
|
|
arr_uniform_t uniforms;
|
|
arr_init(&uniforms, realloc);
|
|
|
|
BlockType type = luax_checkenum(L, 1, BlockType, NULL);
|
|
|
|
luaL_checktype(L, 2, LUA_TTABLE);
|
|
lua_pushnil(L);
|
|
while (lua_next(L, 2) != 0) {
|
|
Uniform uniform;
|
|
|
|
// Name
|
|
strncpy(uniform.name, luaL_checkstring(L, -2), LOVR_MAX_UNIFORM_LENGTH - 1);
|
|
|
|
if (lua_type(L, -1) == LUA_TSTRING) {
|
|
uniform.count = 1;
|
|
luax_checkuniformtype(L, -1, &uniform.type, &uniform.components);
|
|
} 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);
|
|
}
|
|
|
|
lovrAssert(uniform.count >= 1, "Uniform count must be positive, got %d for '%s'", uniform.count, uniform.name);
|
|
arr_push(&uniforms, uniform);
|
|
|
|
// Pop the table, leaving the key for lua_next to nom
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
BufferUsage usage = USAGE_DYNAMIC;
|
|
bool readable = false;
|
|
|
|
if (lua_istable(L, 3)) {
|
|
lua_getfield(L, 3, "usage");
|
|
usage = luax_checkenum(L, -1, BufferUsage, "dynamic");
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, 3, "readable");
|
|
readable = lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
lovrAssert(type == BLOCK_UNIFORM || lovrGraphicsGetFeatures()->compute, "Compute blocks are not supported on this system");
|
|
size_t size = lovrShaderComputeUniformLayout(&uniforms);
|
|
Buffer* buffer = lovrBufferCreate(size, NULL, type == BLOCK_COMPUTE ? BUFFER_SHADER_STORAGE : BUFFER_UNIFORM, usage, readable);
|
|
ShaderBlock* block = lovrShaderBlockCreate(type, buffer, &uniforms);
|
|
luax_pushtype(L, ShaderBlock, block);
|
|
arr_free(&uniforms);
|
|
lovrRelease(buffer, lovrBufferDestroy);
|
|
lovrRelease(block, lovrShaderBlockDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrGraphicsNewTexture(lua_State* L) {
|
|
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");
|
|
} else if (argType != LUA_TTABLE) {
|
|
luaL_checkany(L, 1);
|
|
lua_createtable(L, 1, 0);
|
|
lua_pushvalue(L, 1);
|
|
lua_rawseti(L, -2, 1);
|
|
lua_replace(L, 1);
|
|
depth = 1;
|
|
index++;
|
|
} else {
|
|
depth = luax_len(L, index++);
|
|
type = depth > 0 ? TEXTURE_ARRAY : TEXTURE_CUBE;
|
|
}
|
|
|
|
bool hasFlags = lua_istable(L, index);
|
|
bool srgb = !blank;
|
|
bool mipmaps = true;
|
|
TextureFormat format = FORMAT_RGBA;
|
|
int msaa = 0;
|
|
|
|
if (hasFlags) {
|
|
lua_getfield(L, index, "linear");
|
|
srgb = lua_isnil(L, -1) ? srgb : !lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, index, "mipmaps");
|
|
mipmaps = lua_isnil(L, -1) ? mipmaps : lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, index, "type");
|
|
type = lua_isnil(L, -1) ? type : (TextureType) luax_checkenum(L, -1, TextureType, NULL);
|
|
lua_pop(L, 1);
|
|
|
|
lua_getfield(L, index, "format");
|
|
format = lua_isnil(L, -1) ? format : (TextureFormat) luax_checkenum(L, -1, TextureFormat, NULL);
|
|
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());
|
|
|
|
if (blank) {
|
|
depth = depth ? depth : (type == TEXTURE_CUBE ? 6 : 1);
|
|
lovrTextureAllocate(texture, width, height, depth, format);
|
|
} else {
|
|
if (type == TEXTURE_CUBE && depth == 0) {
|
|
depth = 6;
|
|
const char* faces[6] = { "right", "left", "top", "bottom", "back", "front" };
|
|
for (int i = 0; i < 6; i++) {
|
|
lua_pushstring(L, faces[i]);
|
|
lua_rawget(L, 1);
|
|
lovrAssert(!lua_isnil(L, -1), "Could not load cubemap texture: missing '%s' face", faces[i]);
|
|
lua_rawseti(L, 1, i + 1);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < depth; i++) {
|
|
lua_rawgeti(L, 1, i + 1);
|
|
Image* image = luax_checkimage(L, -1, type != TEXTURE_CUBE);
|
|
if (i == 0) {
|
|
lovrTextureAllocate(texture, image->width, image->height, depth, image->format);
|
|
}
|
|
lovrTextureReplacePixels(texture, image, 0, 0, i, 0);
|
|
lovrRelease(image, lovrImageDestroy);
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
luax_pushtype(L, Texture, texture);
|
|
lovrRelease(texture, lovrTextureDestroy);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg lovrGraphics[] = {
|
|
|
|
// Base
|
|
{ "present", l_lovrGraphicsPresent },
|
|
{ "createWindow", l_lovrGraphicsCreateWindow },
|
|
{ "getWidth", l_lovrGraphicsGetWidth },
|
|
{ "getHeight", l_lovrGraphicsGetHeight },
|
|
{ "getDimensions", l_lovrGraphicsGetDimensions },
|
|
{ "getPixelDensity", l_lovrGraphicsGetPixelDensity },
|
|
{ "hasWindow", l_lovrGraphicsHasWindow },
|
|
{ "getViewPose", l_lovrGraphicsGetViewPose },
|
|
{ "setViewPose", l_lovrGraphicsSetViewPose },
|
|
{ "getProjection", l_lovrGraphicsGetProjection },
|
|
{ "setProjection", l_lovrGraphicsSetProjection },
|
|
{ "tick", l_lovrGraphicsTick },
|
|
{ "tock", l_lovrGraphicsTock },
|
|
{ "getFeatures", l_lovrGraphicsGetFeatures },
|
|
{ "getLimits", l_lovrGraphicsGetLimits },
|
|
{ "getStats", l_lovrGraphicsGetStats },
|
|
|
|
// State
|
|
{ "reset", l_lovrGraphicsReset },
|
|
{ "getAlphaSampling", l_lovrGraphicsGetAlphaSampling },
|
|
{ "setAlphaSampling", l_lovrGraphicsSetAlphaSampling },
|
|
{ "getBackgroundColor", l_lovrGraphicsGetBackgroundColor },
|
|
{ "setBackgroundColor", l_lovrGraphicsSetBackgroundColor },
|
|
{ "getBlendMode", l_lovrGraphicsGetBlendMode },
|
|
{ "setBlendMode", l_lovrGraphicsSetBlendMode },
|
|
{ "getCanvas", l_lovrGraphicsGetCanvas },
|
|
{ "setCanvas", l_lovrGraphicsSetCanvas },
|
|
{ "getColor", l_lovrGraphicsGetColor },
|
|
{ "setColor", l_lovrGraphicsSetColor },
|
|
{ "getColorMask", l_lovrGraphicsGetColorMask },
|
|
{ "setColorMask", l_lovrGraphicsSetColorMask },
|
|
{ "isCullingEnabled", l_lovrGraphicsIsCullingEnabled },
|
|
{ "setCullingEnabled", l_lovrGraphicsSetCullingEnabled },
|
|
{ "getDefaultFilter", l_lovrGraphicsGetDefaultFilter },
|
|
{ "setDefaultFilter", l_lovrGraphicsSetDefaultFilter },
|
|
{ "getDepthTest", l_lovrGraphicsGetDepthTest },
|
|
{ "setDepthTest", l_lovrGraphicsSetDepthTest },
|
|
{ "getFont", l_lovrGraphicsGetFont },
|
|
{ "setFont", l_lovrGraphicsSetFont },
|
|
{ "getLineWidth", l_lovrGraphicsGetLineWidth },
|
|
{ "setLineWidth", l_lovrGraphicsSetLineWidth },
|
|
{ "getPointSize", l_lovrGraphicsGetPointSize },
|
|
{ "setPointSize", l_lovrGraphicsSetPointSize },
|
|
{ "getShader", l_lovrGraphicsGetShader },
|
|
{ "setShader", l_lovrGraphicsSetShader },
|
|
{ "getStencilTest", l_lovrGraphicsGetStencilTest },
|
|
{ "setStencilTest", l_lovrGraphicsSetStencilTest },
|
|
{ "getWinding", l_lovrGraphicsGetWinding },
|
|
{ "setWinding", l_lovrGraphicsSetWinding },
|
|
{ "isWireframe", l_lovrGraphicsIsWireframe },
|
|
{ "setWireframe", l_lovrGraphicsSetWireframe },
|
|
|
|
// Transforms
|
|
{ "push", l_lovrGraphicsPush },
|
|
{ "pop", l_lovrGraphicsPop },
|
|
{ "origin", l_lovrGraphicsOrigin },
|
|
{ "translate", l_lovrGraphicsTranslate },
|
|
{ "rotate", l_lovrGraphicsRotate },
|
|
{ "scale", l_lovrGraphicsScale },
|
|
{ "transform", l_lovrGraphicsTransform },
|
|
|
|
// Rendering
|
|
{ "clear", l_lovrGraphicsClear },
|
|
{ "discard", l_lovrGraphicsDiscard },
|
|
{ "flush", l_lovrGraphicsFlush },
|
|
{ "points", l_lovrGraphicsPoints },
|
|
{ "line", l_lovrGraphicsLine },
|
|
{ "plane", l_lovrGraphicsPlane },
|
|
{ "cube", l_lovrGraphicsCube },
|
|
{ "box", l_lovrGraphicsBox },
|
|
{ "arc", l_lovrGraphicsArc },
|
|
{ "circle", l_lovrGraphicsCircle },
|
|
{ "cylinder", l_lovrGraphicsCylinder },
|
|
{ "sphere", l_lovrGraphicsSphere },
|
|
{ "skybox", l_lovrGraphicsSkybox },
|
|
{ "print", l_lovrGraphicsPrint },
|
|
{ "stencil", l_lovrGraphicsStencil },
|
|
{ "fill", l_lovrGraphicsFill },
|
|
{ "compute", l_lovrGraphicsCompute },
|
|
|
|
// Types
|
|
{ "newCanvas", l_lovrGraphicsNewCanvas },
|
|
{ "newFont", l_lovrGraphicsNewFont },
|
|
{ "newMaterial", l_lovrGraphicsNewMaterial },
|
|
{ "newMesh", l_lovrGraphicsNewMesh },
|
|
{ "newModel", l_lovrGraphicsNewModel },
|
|
{ "newShader", l_lovrGraphicsNewShader },
|
|
{ "newComputeShader", l_lovrGraphicsNewComputeShader },
|
|
{ "newShaderBlock", l_lovrGraphicsNewShaderBlock },
|
|
{ "newTexture", l_lovrGraphicsNewTexture },
|
|
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
extern const luaL_Reg lovrCanvas[];
|
|
extern const luaL_Reg lovrFont[];
|
|
extern const luaL_Reg lovrMaterial[];
|
|
extern const luaL_Reg lovrMesh[];
|
|
extern const luaL_Reg lovrModel[];
|
|
extern const luaL_Reg lovrShader[];
|
|
extern const luaL_Reg lovrShaderBlock[];
|
|
extern const luaL_Reg lovrTexture[];
|
|
|
|
int luaopen_lovr_graphics(lua_State* L) {
|
|
lua_newtable(L);
|
|
luax_register(L, lovrGraphics);
|
|
luax_registertype(L, Canvas);
|
|
luax_registertype(L, Font);
|
|
luax_registertype(L, Material);
|
|
luax_registertype(L, Mesh);
|
|
luax_registertype(L, Model);
|
|
luax_registertype(L, Shader);
|
|
luax_registertype(L, ShaderBlock);
|
|
luax_registertype(L, Texture);
|
|
|
|
luax_pushconf(L);
|
|
|
|
bool debug = false;
|
|
lua_getfield(L, -1, "graphics");
|
|
if (lua_istable(L, -1)) {
|
|
lua_getfield(L, -1, "debug");
|
|
debug = lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
lovrGraphicsInit(debug);
|
|
|
|
lua_pushcfunction(L, l_lovrGraphicsCreateWindow);
|
|
lua_getfield(L, -2, "window");
|
|
lua_call(L, 1, 0);
|
|
lua_pop(L, 1);
|
|
return 1;
|
|
}
|