Merge branch 'master' into dev

This commit is contained in:
bjorn 2022-03-14 13:19:59 -07:00
commit fdfcb5539f
22 changed files with 247 additions and 119 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2021 Bjorn Swenson and other LÖVR contributors
Copyright (c) 2022 Bjorn Swenson and other LÖVR contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -313,21 +313,24 @@ int luaopen_lovr_audio(lua_State* L) {
const char *spatializer = NULL;
uint32_t sampleRate = 48000; // Set default here
luax_pushconf(L);
lua_getfield(L, -1, "audio");
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "spatializer");
spatializer = lua_tostring(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "audio");
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "spatializer");
spatializer = lua_tostring(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "samplerate");
sampleRate = lua_isnil(L, -1) ? sampleRate : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "samplerate");
sampleRate = lua_isnil(L, -1) ? sampleRate : luax_checku32(L, -1);
lua_pop(L, 1);
lua_getfield(L, -1, "start");
start = lua_isnil(L, -1) || lua_toboolean(L, -1);
lua_getfield(L, -1, "start");
start = lua_isnil(L, -1) || lua_toboolean(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lua_pop(L, 2);
lua_pop(L, 1);
if (lovrAudioInit(spatializer, sampleRate)) {
luax_atexit(L, lovrAudioDestroy);

View File

@ -53,6 +53,31 @@ static int l_lovrDataNewBlob(lua_State* L) {
return 1;
}
static int l_lovrDataNewImage(lua_State* L) {
Image* image = NULL;
if (lua_type(L, 1) == LUA_TNUMBER) {
uint32_t width = luax_checku32(L, 1);
uint32_t height = luax_checku32(L, 2);
TextureFormat format = luax_checkenum(L, 3, TextureFormat, "rgba");
Blob* blob = lua_isnoneornil(L, 4) ? NULL : luax_checktype(L, 4, Blob);
image = lovrImageCreate(width, height, blob, 0x0, format);
} else {
Image* source = luax_totype(L, 1, Image);
if (source) {
image = lovrImageCreate(source->width, source->height, source->blob, 0x0, source->format);
} else {
Blob* blob = luax_readblob(L, 1, "Texture");
bool flip = lua_isnoneornil(L, 2) ? true : lua_toboolean(L, 2);
image = lovrImageCreateFromBlob(blob, flip);
lovrRelease(blob, lovrBlobDestroy);
}
}
luax_pushtype(L, Image, image);
lovrRelease(image, lovrImageDestroy);
return 1;
}
static int l_lovrDataNewModelData(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Model");
ModelData* modelData = lovrModelDataCreate(blob, luax_readfile);
@ -109,31 +134,6 @@ static int l_lovrDataNewSound(lua_State* L) {
return 1;
}
static int l_lovrDataNewImage(lua_State* L) {
Image* image = NULL;
if (lua_type(L, 1) == LUA_TNUMBER) {
uint32_t width = luax_checku32(L, 1);
uint32_t height = luax_checku32(L, 2);
TextureFormat format = luax_checkenum(L, 3, TextureFormat, "rgba");
Blob* blob = lua_isnoneornil(L, 4) ? NULL : luax_checktype(L, 4, Blob);
image = lovrImageCreate(width, height, blob, 0x0, format);
} else {
Image* source = luax_totype(L, 1, Image);
if (source) {
image = lovrImageCreate(source->width, source->height, source->blob, 0x0, source->format);
} else {
Blob* blob = luax_readblob(L, 1, "Texture");
bool flip = lua_isnoneornil(L, 2) ? true : lua_toboolean(L, 2);
image = lovrImageCreateFromBlob(blob, flip);
lovrRelease(blob, lovrBlobDestroy);
}
}
luax_pushtype(L, Image, image);
lovrRelease(image, lovrImageDestroy);
return 1;
}
static const luaL_Reg lovrData[] = {
{ "newBlob", l_lovrDataNewBlob },
{ "newImage", l_lovrDataNewImage },

View File

@ -153,7 +153,7 @@ static int l_lovrSoundGetFrames(lua_State* L) {
Sound* other = luax_totype(L, 2, Sound);
Blob* blob = luax_totype(L, 2, Blob);
if (blob) {
lovrAssert(dstOffset + count * stride <= blob->size, "Tried to write samples past the end of the Blob");
lovrAssert(dstOffset + count * stride <= blob->size, "This Blob can hold %d bytes, which is not enough space to hold %d bytes of audio data at the requested offset (%d)", blob->size, count * stride, dstOffset);
char* data = (char*) blob->data + dstOffset;
uint32_t frames = 0;
while (frames < count) {

View File

@ -127,9 +127,15 @@ static int l_lovrFilesystemGetDirectoryItems(lua_State* L) {
}
lua_getglobal(L, "table");
lua_getfield(L, -1, "sort");
lua_pushvalue(L, 2);
lua_call(L, 1, 0);
if (lua_type(L, -1) == LUA_TTABLE) {
lua_getfield(L, -1, "sort");
if (lua_type(L, -1) == LUA_TFUNCTION) {
lua_pushvalue(L, 2);
lua_call(L, 1, 0);
} else {
lua_pop(L, 1);
}
}
lua_pop(L, 1);
return 1;
}

View File

@ -132,6 +132,7 @@ StringEntry lovrMaterialColor[] = {
StringEntry lovrMaterialScalar[] = {
[SCALAR_METALNESS] = ENTRY("metalness"),
[SCALAR_ROUGHNESS] = ENTRY("roughness"),
[SCALAR_ALPHA_CUTOFF] = ENTRY("alphacutoff"),
{ 0 }
};
@ -1246,8 +1247,8 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
padding = luaL_optinteger(L, 2, padding);
spread = luaL_optnumber(L, 3, spread);
}
Font* font = lovrFontCreate(rasterizer, padding, spread);
TextureFilter defaultFilter = lovrGraphicsGetDefaultFilter();
Font* font = lovrFontCreate(rasterizer, padding, spread, defaultFilter.mode);
luax_pushtype(L, Font, font);
lovrRelease(rasterizer, lovrRasterizerDestroy);
lovrRelease(font, lovrFontDestroy);
@ -1844,22 +1845,26 @@ int luaopen_lovr_graphics(lua_State* L) {
luax_registertype(L, ShaderBlock);
luax_registertype(L, Texture);
luax_pushconf(L);
bool debug = false;
lua_getfield(L, -1, "graphics");
luax_pushconf(L);
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "debug");
debug = lua_toboolean(L, -1);
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);
}
lua_pop(L, 1);
lovrGraphicsInit(debug);
lua_pushcfunction(L, l_lovrGraphicsCreateWindow);
lua_getfield(L, -2, "window");
lua_call(L, 1, 0);
if (lua_istable(L, -1)) {
lua_pushcfunction(L, l_lovrGraphicsCreateWindow);
lua_getfield(L, -2, "window");
lua_call(L, 1, 0);
}
lua_pop(L, 1);
return 1;
}

View File

@ -56,6 +56,7 @@ StringEntry lovrDevice[] = {
StringEntry lovrDeviceButton[] = {
[BUTTON_TRIGGER] = ENTRY("trigger"),
[BUTTON_THUMBSTICK] = ENTRY("thumbstick"),
[BUTTON_THUMBREST] = ENTRY("thumbrest"),
[BUTTON_TOUCHPAD] = ENTRY("touchpad"),
[BUTTON_GRIP] = ENTRY("grip"),
[BUTTON_MENU] = ENTRY("menu"),
@ -427,8 +428,7 @@ static int l_lovrHeadsetIsDown(lua_State* L) {
return 1;
}
}
lua_pushboolean(L, false);
return 1;
return 0;
}
static int l_lovrHeadsetWasPressed(lua_State* L) {
@ -469,8 +469,7 @@ static int l_lovrHeadsetIsTouched(lua_State* L) {
return 1;
}
}
lua_pushboolean(L, 1);
return false;
return 0;
}
static const int axisCounts[MAX_AXES] = {
@ -719,9 +718,6 @@ int luaopen_lovr_headset(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrHeadset);
luax_pushconf(L);
lua_getfield(L, -1, "headset");
size_t driverCount = 0;
HeadsetDriver drivers[8];
float supersample = 1.f;
@ -729,49 +725,52 @@ int luaopen_lovr_headset(lua_State* L) {
int msaa = 4;
bool overlay = false;
luax_pushconf(L);
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "headset");
if (lua_istable(L, -1)) {
// Drivers
lua_getfield(L, -1, "drivers");
int n = luax_len(L, -1);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
drivers[driverCount++] = luax_checkenum(L, -1, HeadsetDriver, NULL);
lovrAssert(driverCount < sizeof(drivers) / sizeof(drivers[0]), "Too many headset drivers specified in conf.lua");
// Drivers
lua_getfield(L, -1, "drivers");
int n = luax_len(L, -1);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
drivers[driverCount++] = luax_checkenum(L, -1, HeadsetDriver, NULL);
lovrAssert(driverCount < sizeof(drivers) / sizeof(drivers[0]), "Too many headset drivers specified in conf.lua");
lua_pop(L, 1);
}
lua_pop(L, 1);
// Supersample
lua_getfield(L, -1, "supersample");
if (lua_type(L, -1) == LUA_TBOOLEAN) {
supersample = lua_toboolean(L, -1) ? 2.f : 1.f;
} else {
supersample = luax_optfloat(L, -1, 1.f);
}
lua_pop(L, 1);
// Offset
lua_getfield(L, -1, "offset");
offset = luax_optfloat(L, -1, 1.7f);
lua_pop(L, 1);
// MSAA
lua_getfield(L, -1, "msaa");
msaa = luaL_optinteger(L, -1, 4);
lua_pop(L, 1);
// Overlay
lua_getfield(L, -1, "overlay");
overlay = lua_toboolean(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
// Supersample
lua_getfield(L, -1, "supersample");
if (lua_type(L, -1) == LUA_TBOOLEAN) {
supersample = lua_toboolean(L, -1) ? 2.f : 1.f;
} else {
supersample = luax_optfloat(L, -1, 1.f);
}
lua_pop(L, 1);
// Offset
lua_getfield(L, -1, "offset");
offset = luax_optfloat(L, -1, 1.7f);
lua_pop(L, 1);
// MSAA
lua_getfield(L, -1, "msaa");
msaa = luaL_optinteger(L, -1, 4);
lua_pop(L, 1);
// Overlay
lua_getfield(L, -1, "overlay");
overlay = lua_toboolean(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
if (lovrHeadsetInit(drivers, driverCount, supersample, offset, msaa, overlay)) {
luax_atexit(L, lovrHeadsetDestroy);
}
lua_pop(L, 2);
luax_atexit(L, lovrHeadsetDestroy);
lovrHeadsetInit(drivers, driverCount, supersample, offset, msaa, overlay);
headsetRenderData.ref = LUA_NOREF;
return 1;

View File

@ -191,9 +191,10 @@ static int l_lovrMathGammaToLinear(lua_State* L) {
if (lua_istable(L, 1)) {
for (int i = 0; i < 3; i++) {
lua_rawgeti(L, 1, i + 1);
lua_pushnumber(L, lovrMathGammaToLinear(luax_checkfloat(L, -1)));
float component = luax_checkfloat(L, -1);
lua_pop(L, 1);
lua_pushnumber(L, lovrMathGammaToLinear(component));
}
lua_pop(L, 3);
return 3;
} else {
int n = CLAMP(lua_gettop(L), 1, 3);
@ -208,9 +209,10 @@ static int l_lovrMathLinearToGamma(lua_State* L) {
if (lua_istable(L, 1)) {
for (int i = 0; i < 3; i++) {
lua_rawgeti(L, 1, i + 1);
lua_pushnumber(L, lovrMathLinearToGamma(luax_checkfloat(L, -1)));
float component = luax_checkfloat(L, -1);
lua_pop(L, 1);
lua_pushnumber(L, lovrMathLinearToGamma(component));
}
lua_pop(L, 3);
return 3;
} else {
int n = CLAMP(lua_gettop(L), 1, 3);

View File

@ -252,7 +252,7 @@ static int l_lovrVec2Distance(lua_State* L) {
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC2, NULL);
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
}
float dx = v[0] - u[0];
float dy = v[1] - u[1];
@ -269,7 +269,7 @@ static int l_lovrVec2Dot(lua_State* L) {
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC2, NULL);
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
}
lua_pushnumber(L, v[0] * u[0] + v[1] * u[1]);
return 1;
@ -286,7 +286,7 @@ static int l_lovrVec2Lerp(lua_State* L) {
u = uvec;
t = luax_checkfloat(L, 4);
} else {
u = luax_checkvector(L, 2, V_VEC2, NULL);
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
t = luax_checkfloat(L, 3);
}
v[0] = v[0] + (u[0] - v[0]) * t;
@ -295,6 +295,29 @@ static int l_lovrVec2Lerp(lua_State* L) {
return 1;
}
static int l_lovrVec2Angle(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u;
float uvec[2];
float dot, length_v, length_u;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
}
length_v = sqrtf(v[0] * v[0] + v[1] * v[1]);
length_u = sqrtf(u[0] * u[0] + u[1] * u[1]);
if ((length_v == 0.f) || (length_u == 0.f)) {
lua_pushnumber(L, (float) M_PI / 2);
} else {
dot = v[0] * u[0] + v[1] * u[1];
lua_pushnumber(L, acosf(dot / (length_v * length_u)));
}
return 1;
}
static int l_lovrVec2__add(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC2);
if (lua_type(L, 1) == LUA_TNUMBER) {
@ -496,6 +519,7 @@ const luaL_Reg lovrVec2[] = {
{ "distance", l_lovrVec2Distance },
{ "dot", l_lovrVec2Dot },
{ "lerp", l_lovrVec2Lerp },
{ "angle", l_lovrVec2Angle },
{ "__add", l_lovrVec2__add },
{ "__sub", l_lovrVec2__sub },
{ "__mul", l_lovrVec2__mul },
@ -686,6 +710,22 @@ static int l_lovrVec3Lerp(lua_State* L) {
return 1;
}
static int l_lovrVec3Angle(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
}
lua_pushnumber(L, vec3_angle(v, u));
return 1;
}
static int l_lovrVec3__add(lua_State* L) {
vec3 out = luax_newtempvector(L, V_VEC3);
if (lua_type(L, 1) == LUA_TNUMBER) {
@ -890,6 +930,7 @@ const luaL_Reg lovrVec3[] = {
{ "dot", l_lovrVec3Dot },
{ "cross", l_lovrVec3Cross },
{ "lerp", l_lovrVec3Lerp },
{ "angle", l_lovrVec3Angle },
{ "__add", l_lovrVec3__add },
{ "__sub", l_lovrVec3__sub },
{ "__mul", l_lovrVec3__mul },
@ -1038,7 +1079,7 @@ static int l_lovrVec4Distance(lua_State* L) {
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC4, NULL);
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
}
float dx = v[0] - u[0];
float dy = v[1] - u[1];
@ -1059,7 +1100,7 @@ static int l_lovrVec4Dot(lua_State* L) {
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC4, NULL);
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
}
lua_pushnumber(L, v[0] * u[0] + v[1] * u[1] + v[2] * u[2] + v[3] * u[3]);
return 1;
@ -1078,7 +1119,7 @@ static int l_lovrVec4Lerp(lua_State* L) {
u = uvec;
t = luax_checkfloat(L, 6);
} else {
u = luax_checkvector(L, 2, V_VEC4, NULL);
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
t = luax_checkfloat(L, 3);
}
v[0] = v[0] + (u[0] - v[0]) * t;
@ -1089,6 +1130,31 @@ static int l_lovrVec4Lerp(lua_State* L) {
return 1;
}
static int l_lovrVec4Angle(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u;
float uvec[4];
float dot, length_v, length_u;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
}
length_v = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
length_u = sqrtf(u[0] * u[0] + u[1] * u[1] + u[2] * u[2] + u[3] * u[3]);
if ((length_v == 0.f) || (length_u == 0.f)) {
lua_pushnumber(L, (float) M_PI / 2);
} else {
dot = v[0] * u[0] + v[1] * u[1] + v[2] * u[2] + v[3] * u[3];
lua_pushnumber(L, acosf(dot / (length_v * length_u)));
}
return 1;
}
static int l_lovrVec4__add(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC4);
if (lua_type(L, 1) == LUA_TNUMBER) {
@ -1328,6 +1394,7 @@ const luaL_Reg lovrVec4[] = {
{ "distance", l_lovrVec4Distance },
{ "dot", l_lovrVec4Dot },
{ "lerp", l_lovrVec4Lerp },
{ "angle", l_lovrVec4Angle },
{ "__add", l_lovrVec4__add },
{ "__sub", l_lovrVec4__sub },
{ "__mul", l_lovrVec4__mul },
@ -1798,7 +1865,7 @@ static int l_lovrMat4__mul(lua_State* L) {
} else if (type == V_VEC3) {
vec3 out = luax_newtempvector(L, V_VEC3);
vec3_init(out, n);
mat4_transform(m, n);
mat4_transform(m, out);
} else if (type == V_VEC4) {
float* out = luax_newtempvector(L, V_VEC4);
memcpy(out, n, 4 * sizeof(float));

View File

@ -122,6 +122,15 @@ MAF vec3 vec3_max(vec3 v, const vec3 u) {
return v;
}
MAF float vec3_angle(const vec3 v, const vec3 u) {
float denom = vec3_length(v) * vec3_length(u);
if (denom == 0.f) {
return (float) M_PI / 2;
} else {
return acosf(vec3_dot(v, u) / denom);
}
}
// quat
MAF quat quat_set(quat q, float x, float y, float z, float w) {
@ -546,7 +555,7 @@ MAF void mat4_getAngleAxis(mat4 m, float* angle, float* ax, float* ay, float* az
if (fabsf(cosangle) < 1.f - FLT_EPSILON) {
*angle = acosf(cosangle);
} else {
*angle = (float) M_PI;
*angle = cosangle > 0.f ? 0.f : (float) M_PI;
}
*ax = axis[0];
*ay = axis[1];

View File

@ -58,6 +58,7 @@ typedef struct {
typedef enum {
SCALAR_METALNESS,
SCALAR_ROUGHNESS,
SCALAR_ALPHA_CUTOFF,
MAX_MATERIAL_SCALARS
} MaterialScalar;

View File

@ -689,6 +689,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
for (int i = (token++)->size; i > 0; i--, material++) {
material->scalars[SCALAR_METALNESS] = 1.f;
material->scalars[SCALAR_ROUGHNESS] = 1.f;
material->scalars[SCALAR_ALPHA_CUTOFF] = 0.f;
material->colors[COLOR_DIFFUSE] = (Color) { 1.f, 1.f, 1.f, 1.f };
material->colors[COLOR_EMISSIVE] = (Color) { 0.f, 0.f, 0.f, 0.f };
memset(material->images, 0xff, MAX_MATERIAL_TEXTURES * sizeof(uint32_t));
@ -730,6 +731,8 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
material->colors[COLOR_EMISSIVE].r = NOM_FLOAT(json, token);
material->colors[COLOR_EMISSIVE].g = NOM_FLOAT(json, token);
material->colors[COLOR_EMISSIVE].b = NOM_FLOAT(json, token);
} else if (STR_EQ(key, "alphaCutoff")) {
material->scalars[SCALAR_ALPHA_CUTOFF] = NOM_FLOAT(json, token);
} else if (STR_EQ(key, "name")) {
gltfString name = NOM_STR(json, token);
map_set(&model->materialMap, hash64(name.data, name.length), model->materialCount - i);

View File

@ -27,6 +27,7 @@ struct Font {
uint32_t padding;
float lineHeight;
float pixelDensity;
FilterMode filterMode;
bool flip;
};
@ -49,7 +50,7 @@ static void lovrFontAddGlyph(Font* font, Glyph* glyph);
static void lovrFontExpandTexture(Font* font);
static void lovrFontCreateTexture(Font* font);
Font* lovrFontCreate(Rasterizer* rasterizer, uint32_t padding, double spread) {
Font* lovrFontCreate(Rasterizer* rasterizer, uint32_t padding, double spread, FilterMode filterMode) {
Font* font = calloc(1, sizeof(Font));
lovrAssert(font, "Out of memory");
font->ref = 1;
@ -60,6 +61,7 @@ Font* lovrFontCreate(Rasterizer* rasterizer, uint32_t padding, double spread) {
font->spread = spread;
font->lineHeight = 1.f;
font->pixelDensity = (float) lovrRasterizerGetHeight(rasterizer);
font->filterMode = filterMode;
map_init(&font->kerning, 0);
// Atlas
@ -355,7 +357,7 @@ static void lovrFontCreateTexture(Font* font) {
lovrRelease(font->texture, lovrTextureDestroy);
Image* image = lovrImageCreate(font->atlas.width, font->atlas.height, NULL, 0x0, FORMAT_RGBA16F);
font->texture = lovrTextureCreate(TEXTURE_2D, &image, 1, false, false, 0);
lovrTextureSetFilter(font->texture, (TextureFilter) { .mode = FILTER_BILINEAR });
lovrTextureSetFilter(font->texture, (TextureFilter) { .mode = font->filterMode });
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
lovrRelease(image, lovrImageDestroy);
}

View File

@ -1,6 +1,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "data/modelData.h"
#pragma once
@ -20,7 +21,7 @@ typedef enum {
} VerticalAlign;
typedef struct Font Font;
Font* lovrFontCreate(struct Rasterizer* rasterizer, uint32_t padding, double spread);
Font* lovrFontCreate(struct Rasterizer* rasterizer, uint32_t padding, double spread, FilterMode filterMode);
void lovrFontDestroy(void* ref);
struct Rasterizer* lovrFontGetRasterizer(Font* font);
struct Texture* lovrFontGetTexture(Font* font);

View File

@ -483,7 +483,7 @@ Font* lovrGraphicsGetFont() {
if (!state.font) {
if (!state.defaultFont) {
Rasterizer* rasterizer = lovrRasterizerCreate(NULL, 32);
state.defaultFont = lovrFontCreate(rasterizer, 1, 3.);
state.defaultFont = lovrFontCreate(rasterizer, 1, 3., state.defaultFilter.mode);
lovrRelease(rasterizer, lovrRasterizerDestroy);
}

View File

@ -21,7 +21,7 @@ Material* lovrMaterialCreate() {
material->ref = 1;
for (int i = 0; i < MAX_MATERIAL_SCALARS; i++) {
material->scalars[i] = 1.f;
material->scalars[i] = i == SCALAR_ALPHA_CUTOFF ? 0.f : 1.f;
}
for (int i = 0; i < MAX_MATERIAL_COLORS; i++) {

View File

@ -1763,8 +1763,8 @@ void lovrTextureAllocate(Texture* texture, uint32_t width, uint32_t height, uint
lovrAssert(texture->type != TEXTURE_CUBE || width == height, "Cubemap images must be square");
lovrAssert(texture->type != TEXTURE_CUBE || depth == 6, "6 images are required for a cube texture");
lovrAssert(texture->type != TEXTURE_2D || depth == 1, "2D textures can only contain a single image");
lovrAssert(width < maxSize, "Texture width %d exceeds max of %d", width, maxSize);
lovrAssert(height < maxSize, "Texture height %d exceeds max of %d", height, maxSize);
lovrAssert(width <= maxSize, "Texture width %d exceeds max of %d", width, maxSize);
lovrAssert(height <= maxSize, "Texture height %d exceeds max of %d", height, maxSize);
lovrAssert(!texture->msaa || texture->type == TEXTURE_2D, "Only 2D textures can be created with MSAA");
texture->allocated = true;
@ -2132,8 +2132,9 @@ Image* lovrCanvasNewImage(Canvas* canvas, uint32_t index) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, canvas->resolveBuffer);
}
#ifndef LOVR_WEBGL
Texture* texture = canvas->attachments[index].texture;
#ifndef LOVR_WEBGL
if ((texture->incoherent >> BARRIER_TEXTURE) & 1) {
lovrGpuSync(1 << BARRIER_TEXTURE);
}
@ -2143,8 +2144,10 @@ Image* lovrCanvasNewImage(Canvas* canvas, uint32_t index) {
glReadBuffer(index);
}
Image* image = lovrImageCreate(canvas->width, canvas->height, NULL, 0x0, FORMAT_RGBA);
glReadPixels(0, 0, canvas->width, canvas->height, GL_RGBA, GL_UNSIGNED_BYTE, image->blob->data);
Image* image = lovrImageCreate(canvas->width, canvas->height, NULL, 0x0, texture->format);
GLenum glFormat = convertTextureFormat(texture->format);
GLenum glFormatType = convertTextureFormatType(texture->format);
glReadPixels(0, 0, canvas->width, canvas->height, glFormat, glFormatType, image->blob->data);
if (index != 0) {
glReadBuffer(0);

View File

@ -55,6 +55,7 @@ typedef enum {
typedef enum {
BUTTON_TRIGGER,
BUTTON_THUMBSTICK,
BUTTON_THUMBREST,
BUTTON_TOUCHPAD,
BUTTON_GRIP,
BUTTON_MENU,

View File

@ -11,6 +11,7 @@
#if defined(_WIN32)
#define XR_USE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include <unknwn.h>
#include <windows.h>
#elif defined(__ANDROID__)
#define XR_USE_PLATFORM_ANDROID
@ -200,7 +201,7 @@ static bool openxr_init(float supersample, float offset, uint32_t msaa, bool ove
XrExtensionProperties* extensions = calloc(extensionCount, sizeof(*extensions));
lovrAssert(extensions, "Out of memory");
for (uint32_t i = 0; i < extensionCount; i++) extensions[i].type = XR_TYPE_EXTENSION_PROPERTIES;
xrEnumerateInstanceExtensionProperties(NULL, 32, &extensionCount, extensions);
xrEnumerateInstanceExtensionProperties(NULL, extensionCount, &extensionCount, extensions);
const char* enabledExtensionNames[5];
uint32_t enabledExtensionCount = 0;
@ -726,6 +727,7 @@ static bool getButtonState(Device device, DeviceButton button, bool* value, bool
switch (button) {
case BUTTON_TRIGGER: info.action = state.actions[ACTION_TRIGGER_DOWN + touch]; break;
case BUTTON_THUMBREST: info.action = state.actions[ACTION_THUMBREST_DOWN + touch]; break;
case BUTTON_TOUCHPAD: info.action = state.actions[ACTION_TRACKPAD_DOWN + touch]; break;
case BUTTON_MENU: info.action = state.actions[ACTION_MENU_DOWN + touch]; break;
case BUTTON_GRIP: info.action = state.actions[ACTION_GRIP_DOWN + touch]; break;

View File

@ -273,6 +273,14 @@ static bool vrapi_isDown(Device device, DeviceButton button, bool* down, bool* c
return false;
}
if (device == DEVICE_HAND_LEFT && (button == BUTTON_A || button == BUTTON_B)) {
return false;
}
if (device == DEVICE_HAND_RIGHT && (button == BUTTON_X || button == BUTTON_Y)) {
return false;
}
if (state.hands[device - DEVICE_HAND_LEFT].Type != ovrControllerType_TrackedRemote) {
return false;
}
@ -301,6 +309,14 @@ static bool vrapi_isTouched(Device device, DeviceButton button, bool* touched) {
return false;
}
if (device == DEVICE_HAND_LEFT && (button == BUTTON_A || button == BUTTON_B)) {
return false;
}
if (device == DEVICE_HAND_RIGHT && (button == BUTTON_X || button == BUTTON_Y)) {
return false;
}
if (state.hands[device - DEVICE_HAND_LEFT].Type != ovrControllerType_TrackedRemote) {
return false;
}
@ -310,6 +326,7 @@ static bool vrapi_isTouched(Device device, DeviceButton button, bool* touched) {
switch (button) {
case BUTTON_TRIGGER: *touched = input->Touches & ovrTouch_IndexTrigger; return true;
case BUTTON_THUMBSTICK: *touched = input->Touches & ovrTouch_Joystick; return true;
case BUTTON_THUMBREST: *touched = input->Touches & ovrTouch_ThumbRest; return true;
case BUTTON_A: *touched = input->Touches & ovrTouch_A; return true;
case BUTTON_B: *touched = input->Touches & ovrTouch_B; return true;
case BUTTON_X: *touched = input->Touches & ovrTouch_X; return true;

View File

@ -27,6 +27,8 @@ enum {
ACTION_GRIP_DOWN,
ACTION_GRIP_TOUCH,
ACTION_GRIP_AXIS,
ACTION_THUMBREST_DOWN,
ACTION_THUMBREST_TOUCH,
ACTION_VIBRATE,
MAX_ACTIONS
};
@ -58,6 +60,7 @@ static XrActionCreateInfo actionCreateInfo[] = {
[ACTION_GRIP_DOWN] = action("grip_down", "Grip Down", XR_ACTION_TYPE_BOOLEAN_INPUT),
[ACTION_GRIP_TOUCH] = action("grip_touch", "Grip Touch", XR_ACTION_TYPE_BOOLEAN_INPUT),
[ACTION_GRIP_AXIS] = action("grip_axis", "Grip Axis", XR_ACTION_TYPE_FLOAT_INPUT),
[ACTION_THUMBREST_TOUCH] = action("thumbrest_touch", "Thumbrest Touch", XR_ACTION_TYPE_BOOLEAN_INPUT),
[ACTION_VIBRATE] = action("vibrate", "Vibrate", XR_ACTION_TYPE_VIBRATION_OUTPUT),
};
#undef action
@ -120,6 +123,8 @@ static const char* bindings[MAX_PROFILES][MAX_ACTIONS][2] = {
[ACTION_GRIP_DOWN][1] = "/user/hand/right/input/squeeze/value",
[ACTION_GRIP_AXIS][0] = "/user/hand/left/input/squeeze/value",
[ACTION_GRIP_AXIS][1] = "/user/hand/right/input/squeeze/value",
[ACTION_THUMBREST_TOUCH][0] = "/user/hand/left/input/thumbrest/touch",
[ACTION_THUMBREST_TOUCH][1] = "/user/hand/right/input/thumbrest/touch",
[ACTION_VIBRATE][0] = "/user/hand/left/output/haptic",
[ACTION_VIBRATE][1] = "/user/hand/right/output/haptic"
},

View File

@ -87,6 +87,7 @@ const char* lovrShaderFragmentPrefix = ""
"out vec4 lovrCanvas[gl_MaxDrawBuffers]; \n"
"uniform float lovrMetalness; \n"
"uniform float lovrRoughness; \n"
"uniform float lovrAlphaCutoff; \n"
"uniform vec4 lovrDiffuseColor; \n"
"uniform vec4 lovrEmissiveColor; \n"
"uniform sampler2D lovrDiffuseTexture; \n"
@ -124,7 +125,7 @@ const char* lovrShaderFragmentSuffix = ""
"#else \n"
" lovrCanvas[0] = color(lovrGraphicsColor, lovrDiffuseTexture, lovrTexCoord); \n"
"#ifdef FLAG_alphaCutoff \n"
" if (lovrCanvas[0].a < FLAG_alphaCutoff) { \n"
" if (lovrCanvas[0].a < lovrAlphaCutoff) { \n"
" discard; \n"
" } \n"
"#endif \n"
@ -372,7 +373,8 @@ const char* lovrFillVertexShader = ""
const char* lovrShaderScalarUniforms[] = {
"lovrMetalness",
"lovrRoughness"
"lovrRoughness",
"lovrAlphaCutoff"
};
const char* lovrShaderColorUniforms[] = {