Merge branch 'master' into fake-headset

This commit is contained in:
Ben Campbell 2017-10-23 09:32:32 +13:00
commit a22a9ed1f4
51 changed files with 2001 additions and 1526 deletions

View File

@ -27,8 +27,9 @@ set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "")
set(ASSIMP_NO_EXPORT ON OFF CACHE BOOL "")
set(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT OFF CACHE BOOL "")
set(ASSIMP_BUILD_COLLADA_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_FBX_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_GLTF_IMPORTER ON CACHE BOOL "")
set(ASSIMP_BUILD_OBJ_IMPORTER ON CACHE BOOL "")
if(EMSCRIPTEN)
set(ZLIB_FOUND 1)
set(ZLIB_LIBRARIES "-s USE_ZLIB=1")
@ -197,11 +198,11 @@ set(PHYSFS_BUILD_WX_TEST FALSE CACHE BOOL "")
if(EMSCRIPTEN)
option(PHYSFS_ARCHIVE_ZIP OFF)
add_subdirectory(deps/physfs physfs)
include_directories(deps/physfs)
include_directories(deps/physfs/src)
set(LOVR_PHYSFS physfs)
elseif(WIN32)
add_subdirectory(deps/physfs physfs)
include_directories(deps/physfs)
include_directories(deps/physfs/src)
set(LOVR_PHYSFS physfs)
else()
find_package(PhysFS REQUIRED)
@ -224,12 +225,12 @@ set(LOVR_SRC
src/api/types/controller.c
src/api/types/font.c
src/api/types/joints.c
src/api/types/material.c
src/api/types/mesh.c
src/api/types/model.c
src/api/types/randomGenerator.c
src/api/types/shader.c
src/api/types/shapes.c
src/api/types/skybox.c
src/api/types/source.c
src/api/types/texture.c
src/api/types/transform.c
@ -238,13 +239,15 @@ set(LOVR_SRC
src/audio/source.c
src/event/event.c
src/filesystem/blob.c
src/filesystem/file.c
src/filesystem/filesystem.c
src/graphics/font.c
src/graphics/graphics.c
src/graphics/material.c
src/graphics/mesh.c
src/graphics/model.c
src/graphics/shader.c
src/graphics/skybox.c
src/graphics/shaders.c
src/graphics/texture.c
src/headset/fake.c
src/headset/headset.c
@ -259,6 +262,7 @@ set(LOVR_SRC
src/lib/stb/stb_vorbis.c
src/lib/vec/vec.c
src/loaders/font.c
src/loaders/material.c
src/loaders/model.c
src/loaders/source.c
src/loaders/texture.c
@ -306,6 +310,10 @@ target_link_libraries(lovr
)
if(WIN32)
set_target_properties(lovr PROPERTIES COMPILE_FLAGS "/wd4244")
set_target_properties(lovr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(lovr PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
function(move_dll ARG_TARGET)
add_custom_command(TARGET lovr POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy

View File

@ -75,12 +75,20 @@ lovr /path/to/myGame
Linux
---
On Arch Linux, first install necessary dependencies:
First, install the dependencies using your package manager of choice.
#### Arch Linux
```sh
pacman -S assimp glfw-x11 luajit physfs freetype2 openal ode
```
#### Debian/Ubuntu
```sh
sudo apt-get install build-essential cmake libassimp-dev libglfw3-dev libluajit-5.1-dev libphysfs-dev libfreetype6-dev libopenal-dev libode-dev
```
Then, build with CMake:
```sh

2
deps/physfs vendored

@ -1 +1 @@
Subproject commit abb9a4c8b6d16389ca5a519c18b25f34249c3426
Subproject commit 68beaa110991cb5002c8dc3a3d61718845046e79

View File

@ -23,11 +23,12 @@ Blob* luax_readblob(lua_State* L, int index, const char* debug) {
}
}
static void pushDirectoryItem(void* userdata, const char* path, const char* filename) {
static int pushDirectoryItem(void* userdata, const char* path, const char* filename) {
lua_State* L = userdata;
int n = lua_objlen(L, -1);
lua_pushstring(L, filename);
lua_rawseti(L, -2, n + 1);
return 1;
}
// Loader to help Lua's require understand PhysFS.
@ -122,12 +123,6 @@ int l_lovrFilesystemCreateDirectory(lua_State* L) {
return 1;
}
int l_lovrFilesystemExists(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushboolean(L, lovrFilesystemExists(path));
return 1;
}
int l_lovrFilesystemGetAppdataDirectory(lua_State* L) {
char buffer[1024];
@ -195,7 +190,11 @@ int l_lovrFilesystemGetSaveDirectory(lua_State* L) {
int l_lovrFilesystemGetSize(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
lua_pushinteger(L, lovrFilesystemGetSize(path));
size_t size = lovrFilesystemGetSize(path);
if ((int) size == -1) {
return luaL_error(L, "File does not exist");
}
lua_pushinteger(L, size);
return 1;
}
@ -322,7 +321,6 @@ int l_lovrFilesystemWrite(lua_State* L) {
const luaL_Reg lovrFilesystem[] = {
{ "append", l_lovrFilesystemAppend },
{ "createDirectory", l_lovrFilesystemCreateDirectory },
{ "exists", l_lovrFilesystemExists },
{ "getAppdataDirectory", l_lovrFilesystemGetAppdataDirectory },
{ "getDirectoryItems", l_lovrFilesystemGetDirectoryItems },
{ "getExecutablePath", l_lovrFilesystemGetExecutablePath },

View File

@ -1,8 +1,10 @@
#include "api/lovr.h"
#include "graphics/graphics.h"
#include "graphics/material.h"
#include "graphics/mesh.h"
#include "graphics/model.h"
#include "loaders/font.h"
#include "loaders/material.h"
#include "loaders/model.h"
#include "loaders/texture.h"
#include "filesystem/filesystem.h"
@ -14,6 +16,8 @@ map_int_t CompareModes;
map_int_t DrawModes;
map_int_t FilterModes;
map_int_t HorizontalAligns;
map_int_t MaterialColors;
map_int_t MaterialTextures;
map_int_t MatrixTypes;
map_int_t MeshAttributeTypes;
map_int_t MeshDrawModes;
@ -63,24 +67,16 @@ static void luax_readvertices(lua_State* L, int index, vec_float_t* points) {
}
}
static Texture* luax_readtexture(lua_State* L, int index) {
Blob* blob = luax_readblob(L, index, "Texture");
TextureData* textureData = lovrTextureDataFromBlob(blob);
Texture* texture = lovrTextureCreate(textureData);
lovrRelease(&blob->ref);
return texture;
}
// Base
int l_lovrGraphicsInit(lua_State* L) {
lua_newtable(L);
luaL_register(L, NULL, lovrGraphics);
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, "Skybox", lovrSkybox);
luax_registertype(L, "Texture", lovrTexture);
map_init(&BlendAlphaModes);
@ -120,6 +116,13 @@ int l_lovrGraphicsInit(lua_State* L) {
map_set(&HorizontalAligns, "right", ALIGN_RIGHT);
map_set(&HorizontalAligns, "center", ALIGN_CENTER);
map_init(&MaterialColors);
map_set(&MaterialColors, "diffuse", COLOR_DIFFUSE);
map_init(&MaterialTextures);
map_set(&MaterialTextures, "diffuse", TEXTURE_DIFFUSE);
map_set(&MaterialTextures, "environment", TEXTURE_ENVIRONMENT_MAP);
map_init(&MatrixTypes);
map_set(&MatrixTypes, "model", MATRIX_MODEL);
map_set(&MatrixTypes, "view", MATRIX_VIEW);
@ -253,26 +256,7 @@ int l_lovrGraphicsGetColor(lua_State* L) {
}
int l_lovrGraphicsSetColor(lua_State* L) {
Color color = { 0xff, 0xff, 0xff, 0xff };
if (lua_istable(L, 1)) {
for (int i = 1; i <= 4; i++) {
lua_rawgeti(L, 1, i);
}
color.r = luaL_checknumber(L, -4);
color.g = luaL_checknumber(L, -3);
color.b = luaL_checknumber(L, -2);
color.a = luaL_optnumber(L, -1, 255);
lua_pop(L, 4);
} else if (lua_gettop(L) >= 3) {
color.r = lua_tointeger(L, 1);
color.g = lua_tointeger(L, 2);
color.b = lua_tointeger(L, 3);
color.a = lua_isnoneornil(L, 4) ? 255 : lua_tointeger(L, 4);
} else {
return luaL_error(L, "Invalid color, expected 3 numbers, 4 numbers, or a table");
}
Color color = luax_checkcolor(L, 1);
lovrGraphicsSetColor(color);
return 0;
}
@ -357,6 +341,22 @@ int l_lovrGraphicsSetLineWidth(lua_State* L) {
return 0;
}
int l_lovrGraphicsGetMaterial(lua_State* L) {
Material* material = lovrGraphicsGetMaterial();
if (material && !material->isDefault) {
luax_pushtype(L, Material, material);
} else {
lua_pushnil(L);
}
return 1;
}
int l_lovrGraphicsSetMaterial(lua_State* L) {
Material* material = lua_isnoneornil(L, 1) ? NULL : luax_checktype(L, 1, Material);
lovrGraphicsSetMaterial(material);
return 0;
}
int l_lovrGraphicsGetPointSize(lua_State* L) {
lua_pushnumber(L, lovrGraphicsGetPointSize());
return 1;
@ -493,36 +493,24 @@ int l_lovrGraphicsTriangle(lua_State* L) {
}
int l_lovrGraphicsPlane(lua_State* L) {
Texture* texture = NULL;
DrawMode drawMode;
if (lua_isstring(L, 1)) {
drawMode = *(DrawMode*) luax_checkenum(L, 1, &DrawModes, "draw mode");
} else {
drawMode = DRAW_MODE_FILL;
texture = luax_checktype(L, 1, Texture);
if (lua_gettop(L) == 1) {
lovrGraphicsPlaneFullscreen(texture);
return 0;
}
if (lua_isuserdata(L, 1)) {
Texture* texture = luax_checktype(L, 1, Texture);
lovrGraphicsPlaneFullscreen(texture);
return 0;
}
DrawMode drawMode = *(DrawMode*) luax_checkenum(L, 1, &DrawModes, "draw mode");
float transform[16];
luax_readtransform(L, 2, transform, 1);
lovrGraphicsPlane(drawMode, texture, transform);
lovrGraphicsPlane(drawMode, transform);
return 0;
}
static int luax_rectangularprism(lua_State* L, int uniformScale) {
Texture* texture = NULL;
DrawMode drawMode;
if (lua_isstring(L, 1)) {
drawMode = *(DrawMode*) luax_checkenum(L, 1, &DrawModes, "draw mode");
} else {
drawMode = DRAW_MODE_FILL;
texture = luax_checktype(L, 1, Texture);
}
DrawMode drawMode = *(DrawMode*) luax_checkenum(L, 1, &DrawModes, "draw mode");
float transform[16];
luax_readtransform(L, 2, transform, uniformScale);
lovrGraphicsBox(drawMode, texture, transform);
lovrGraphicsBox(drawMode, transform);
return 0;
}
@ -550,15 +538,21 @@ int l_lovrGraphicsCylinder(lua_State* L) {
}
int l_lovrGraphicsSphere(lua_State* L) {
Texture* texture = NULL;
float transform[16];
int index = 1;
if (lua_isuserdata(L, 1) && (lua_isuserdata(L, 2) || lua_isnumber(L, 2))) {
texture = luax_checktype(L, index++, Texture);
}
index = luax_readtransform(L, index, transform, 1);
int segments = luaL_optnumber(L, index, 30);
lovrGraphicsSphere(texture, transform, segments, NULL);
lovrGraphicsSphere(transform, segments);
return 0;
}
int l_lovrGraphicsSkybox(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
float angle = luaL_optnumber(L, 2, 0);
float ax = luaL_optnumber(L, 3, 0);
float ay = luaL_optnumber(L, 4, 1);
float az = luaL_optnumber(L, 5, 0);
lovrGraphicsSkybox(texture, angle, ax, ay, az);
return 0;
}
@ -598,6 +592,13 @@ int l_lovrGraphicsNewFont(lua_State* L) {
return 1;
}
int l_lovrGraphicsNewMaterial(lua_State* L) {
MaterialData* materialData = lovrMaterialDataCreateEmpty();
Material* material = lovrMaterialCreate(materialData, 0);
luax_pushtype(L, Material, material);
return 1;
}
int l_lovrGraphicsNewMesh(lua_State* L) {
int size;
int dataIndex = 0;
@ -671,13 +672,6 @@ int l_lovrGraphicsNewModel(lua_State* L) {
Blob* blob = luax_readblob(L, 1, "Model");
ModelData* modelData = lovrModelDataCreate(blob);
Model* model = lovrModelCreate(modelData);
if (lua_gettop(L) >= 2) {
Texture* texture = luax_readtexture(L, 2);
lovrModelSetTexture(model, texture);
lovrRelease(&texture->ref);
}
luax_pushtype(L, Model, model);
lovrRelease(&model->ref);
lovrRelease(&blob->ref);
@ -707,51 +701,6 @@ int l_lovrGraphicsNewShader(lua_State* L) {
return 1;
}
int l_lovrGraphicsNewSkybox(lua_State* L) {
Blob* blobs[6] = { NULL };
SkyboxType type;
if (lua_gettop(L) == 1 && lua_type(L, 1) == LUA_TSTRING) {
type = SKYBOX_PANORAMA;
blobs[0] = luax_readblob(L, 1, "Skybox");
} else if (lua_istable(L, 1)) {
if (lua_objlen(L, 1) != 6) {
return luaL_argerror(L, 1, "Expected 6 strings or a table containing 6 strings");
}
for (int i = 0; i < 6; i++) {
lua_rawgeti(L, 1, i + 1);
if (!lua_isstring(L, -1)) {
return luaL_argerror(L, 1, "Expected 6 strings or a table containing 6 strings");
}
blobs[i] = luax_readblob(L, -1, "Skybox");
lua_pop(L, 1);
}
type = SKYBOX_CUBE;
} else {
for (int i = 0; i < 6; i++) {
blobs[i] = luax_readblob(L, i + 1, "Skybox");
}
type = SKYBOX_CUBE;
}
Skybox* skybox = lovrSkyboxCreate(blobs, type);
luax_pushtype(L, Skybox, skybox);
lovrRelease(&skybox->ref);
for (int i = 0; i < 6; i++) {
if (blobs[i]) {
lovrRelease(&blobs[i]->ref);
}
}
return 1;
}
int l_lovrGraphicsNewTexture(lua_State* L) {
Texture* texture;
@ -763,7 +712,34 @@ int l_lovrGraphicsNewTexture(lua_State* L) {
TextureData* textureData = lovrTextureDataGetEmpty(width, height, FORMAT_RGBA);
texture = lovrTextureCreateWithFramebuffer(textureData, *projection, msaa);
} else {
texture = luax_readtexture(L, 1);
Blob* blobs[6];
int isTable = lua_istable(L, 1);
int count = isTable ? lua_objlen(L, 1) : lua_gettop(L);
if (count != 1 && count != 6) {
return luaL_error(L, "Expected 1 image for a 2D texture or 6 images for a cube texture, got %d", count);
}
if (isTable) {
for (int i = 0; i < count; i++) {
lua_rawgeti(L, -1, i + 1);
blobs[i] = luax_readblob(L, -1, "Texture");
lua_pop(L, 1);
}
} else {
for (int i = 0; i < count; i++) {
blobs[i] = luax_readblob(L, i + 1, "Texture");
}
}
TextureData* slices[6];
for (int i = 0; i < count; i++) {
slices[i] = lovrTextureDataFromBlob(blobs[i]);
lovrRelease(&blobs[i]->ref);
}
TextureType type = (count == 1) ? TEXTURE_2D : TEXTURE_CUBE;
texture = lovrTextureCreate(type, slices, count);
}
luax_pushtype(L, Texture, texture);
@ -796,6 +772,8 @@ const luaL_Reg lovrGraphics[] = {
{ "getSystemLimits", l_lovrGraphicsGetSystemLimits },
{ "getLineWidth", l_lovrGraphicsGetLineWidth },
{ "setLineWidth", l_lovrGraphicsSetLineWidth },
{ "getMaterial", l_lovrGraphicsGetMaterial },
{ "setMaterial", l_lovrGraphicsSetMaterial },
{ "getPointSize", l_lovrGraphicsGetPointSize },
{ "setPointSize", l_lovrGraphicsSetPointSize },
{ "getShader", l_lovrGraphicsGetShader },
@ -819,12 +797,13 @@ const luaL_Reg lovrGraphics[] = {
{ "box", l_lovrGraphicsBox },
{ "cylinder", l_lovrGraphicsCylinder },
{ "sphere", l_lovrGraphicsSphere },
{ "skybox", l_lovrGraphicsSkybox },
{ "print", l_lovrGraphicsPrint },
{ "newFont", l_lovrGraphicsNewFont },
{ "newMaterial", l_lovrGraphicsNewMaterial },
{ "newMesh", l_lovrGraphicsNewMesh },
{ "newModel", l_lovrGraphicsNewModel },
{ "newShader", l_lovrGraphicsNewShader },
{ "newSkybox", l_lovrGraphicsNewSkybox },
{ "newTexture", l_lovrGraphicsNewTexture },
{ NULL, NULL }
};

View File

@ -31,6 +31,7 @@ extern const luaL_Reg lovrGraphics[];
extern const luaL_Reg lovrHeadset[];
extern const luaL_Reg lovrHingeJoint[];
extern const luaL_Reg lovrJoint[];
extern const luaL_Reg lovrMaterial[];
extern const luaL_Reg lovrMath[];
extern const luaL_Reg lovrMesh[];
extern const luaL_Reg lovrModel[];
@ -38,7 +39,6 @@ extern const luaL_Reg lovrPhysics[];
extern const luaL_Reg lovrRandomGenerator[];
extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrShape[];
extern const luaL_Reg lovrSkybox[];
extern const luaL_Reg lovrSliderJoint[];
extern const luaL_Reg lovrSource[];
extern const luaL_Reg lovrSphereShape[];
@ -61,6 +61,8 @@ extern map_int_t HeadsetOrigins;
extern map_int_t HeadsetTypes;
extern map_int_t HorizontalAligns;
extern map_int_t JointTypes;
extern map_int_t MaterialColors;
extern map_int_t MaterialTextures;
extern map_int_t MatrixTypes;
extern map_int_t MeshAttributeTypes;
extern map_int_t MeshDrawModes;

View File

@ -70,14 +70,8 @@ int l_lovrControllerVibrate(lua_State* L) {
int l_lovrControllerNewModel(lua_State* L) {
Controller* controller = luax_checktype(L, 1, Controller);
ModelData* modelData = lovrHeadsetControllerNewModelData(controller);
TextureData* textureData = lovrHeadsetControllerNewTextureData(controller);
if (modelData) {
Model* model = lovrModelCreate(modelData);
if (textureData) {
Texture* texture = lovrTextureCreate(textureData);
lovrModelSetTexture(model, texture);
lovrRelease(&texture->ref);
}
luax_pushtype(L, Model, model);
lovrRelease(&model->ref);
} else {

55
src/api/types/material.c Normal file
View File

@ -0,0 +1,55 @@
#include "api/lovr.h"
#include "graphics/material.h"
int l_lovrMaterialGetColor(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);
MaterialColor colorType = *(MaterialColor*) luax_optenum(L, 2, "diffuse", &MaterialColors, "color");
Color color = lovrMaterialGetColor(material, colorType);
lua_pushinteger(L, color.r);
lua_pushinteger(L, color.g);
lua_pushinteger(L, color.b);
lua_pushinteger(L, color.a);
return 4;
}
int l_lovrMaterialSetColor(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);
MaterialColor colorType = COLOR_DIFFUSE;
int index = 2;
if (lua_type(L, index) == LUA_TSTRING) {
colorType = *(MaterialColor*) luax_checkenum(L, index, &MaterialColors, "color");
index++;
}
Color color = luax_checkcolor(L, index);
lovrMaterialSetColor(material, colorType, color);
return 0;
}
int l_lovrMaterialGetTexture(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);
MaterialTexture textureType = *(MaterialTexture*) luax_optenum(L, 2, "diffuse", &MaterialTextures, "texture");
Texture* texture = lovrMaterialGetTexture(material, textureType);
luax_pushtype(L, Texture, texture);
return 1;
}
int l_lovrMaterialSetTexture(lua_State* L) {
Material* material = luax_checktype(L, 1, Material);
MaterialTexture textureType = TEXTURE_DIFFUSE;
int index = 2;
if (lua_type(L, index) == LUA_TSTRING) {
textureType = *(MaterialTexture*) luax_checkenum(L, index, &MaterialTextures, "texture");
index++;
}
Texture* texture = lua_isnoneornil(L, index) ? NULL : luax_checktype(L, index, Texture);
lovrMaterialSetTexture(material, textureType, texture);
return 0;
}
const luaL_Reg lovrMaterial[] = {
{ "getColor", l_lovrMaterialGetColor },
{ "setColor", l_lovrMaterialSetColor },
{ "getTexture", l_lovrMaterialGetTexture },
{ "setTexture", l_lovrMaterialSetTexture },
{ NULL, NULL }
};

View File

@ -273,27 +273,30 @@ int l_lovrMeshSetVertexMap(lua_State* L) {
luaL_checktype(L, 2, LUA_TTABLE);
int count = lua_objlen(L, 2);
unsigned int* indices = malloc(count * sizeof(unsigned int));
int indexSize = mesh->indexSize;
void* indices = realloc(lovrMeshGetVertexMap(mesh, NULL), indexSize * count);
for (int i = 0; i < count; i++) {
lua_rawgeti(L, 2, i + 1);
if (!lua_isnumber(L, -1)) {
free(indices);
return luaL_error(L, "Mesh vertex map index #%d must be numeric", i);
}
int index = lua_tointeger(L, -1);
if (index > lovrMeshGetVertexCount(mesh) || index < 1) {
free(indices);
return luaL_error(L, "Invalid vertex map value: %d", index);
}
indices[i] = index - 1;
if (indexSize == sizeof(uint16_t)) {
*(((uint16_t*) indices) + i) = index - 1;
} else if (indexSize == sizeof(uint32_t)) {
*(((uint32_t*) indices) + i) = index - 1;
}
lua_pop(L, 1);
}
lovrMeshSetVertexMap(mesh, indices, count);
free(indices);
return 0;
}
@ -336,30 +339,7 @@ int l_lovrMeshSetDrawRange(lua_State* L) {
lovrMeshSetRangeEnabled(mesh, 1);
int rangeStart = luaL_checkinteger(L, 2) - 1;
int rangeCount = luaL_checkinteger(L, 3);
if (lovrMeshSetDrawRange(mesh, rangeStart, rangeCount)) {
return luaL_error(L, "Invalid mesh draw range (%d, %d)", rangeStart + 1, rangeCount);
}
return 0;
}
int l_lovrMeshGetTexture(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Texture* texture = lovrMeshGetTexture(mesh);
if (texture) {
luax_pushtype(L, Texture, texture);
} else {
lua_pushnil(L);
}
return 1;
}
int l_lovrMeshSetTexture(lua_State* L) {
Mesh* mesh = luax_checktype(L, 1, Mesh);
Texture* texture = lua_isnoneornil(L, 2) ? NULL : luax_checktype(L, 2, Texture);
lovrMeshSetTexture(mesh, texture);
lovrMeshSetDrawRange(mesh, rangeStart, rangeCount);
return 0;
}
@ -380,7 +360,5 @@ const luaL_Reg lovrMesh[] = {
{ "setDrawMode", l_lovrMeshSetDrawMode },
{ "getDrawRange", l_lovrMeshGetDrawRange },
{ "setDrawRange", l_lovrMeshSetDrawRange },
{ "getTexture", l_lovrMeshGetTexture },
{ "setTexture", l_lovrMeshSetTexture },
{ NULL, NULL }
};

View File

@ -9,33 +9,15 @@ int l_lovrModelDraw(lua_State* L) {
return 0;
}
int l_lovrModelGetTexture(lua_State* L) {
int l_lovrModelGetMesh(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Texture* texture = lovrModelGetTexture(model);
luax_pushtype(L, Texture, texture);
Mesh* mesh = lovrModelGetMesh(model);
luax_pushtype(L, Mesh, mesh);
return 1;
}
int l_lovrModelSetTexture(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Texture* texture = luax_checktype(L, 2, Texture);
lovrModelSetTexture(model, texture);
return 0;
}
int l_lovrModelGetAABB(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float* aabb = lovrModelGetAABB(model);
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, aabb[i]);
}
return 6;
}
const luaL_Reg lovrModel[] = {
{ "draw", l_lovrModelDraw },
{ "getTexture", l_lovrModelGetTexture },
{ "setTexture", l_lovrModelSetTexture },
{ "getAABB", l_lovrModelGetAABB },
{ "getMesh", l_lovrModelGetMesh },
{ NULL, NULL }
};

View File

@ -3,163 +3,134 @@
#include "graphics/shader.h"
#include "math/transform.h"
struct TempData {
void* data;
size_t size;
};
static struct TempData tempData;
int l_lovrShaderSend(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name = luaL_checkstring(L, 2);
lua_settop(L, 3);
int id = lovrShaderGetUniformId(shader, name);
if (id == -1) {
Uniform* uniform = lovrShaderGetUniform(shader, name);
if (!uniform) {
return luaL_error(L, "Unknown shader variable '%s'", name);
}
GLenum type;
int size;
lovrShaderGetUniformType(shader, name, &type, &size);
lovrGraphicsBindProgram(shader->id);
float data[16];
int n;
vec_float_t values;
vec_init(&values);
switch (type) {
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
case GL_INT:
lovrShaderSendInt(shader, id, luaL_checkinteger(L, 3));
break;
case GL_FLOAT:
lovrShaderSendFloat(shader, id, luaL_checknumber(L, 3));
break;
case GL_FLOAT_VEC2:
luaL_checktype(L, 3, LUA_TTABLE);
lua_rawgeti(L, 3, 1);
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
} else {
lua_pop(L, 1);
}
n = lua_objlen(L, -1);
if (n < size) {
return luaL_error(L, "Expected %d vec3s, got %d", size, n);
}
for (int i = 0; i < size; i++) {
lua_rawgeti(L, -1, i + 1);
for (int j = 0; j < 2; j++) {
lua_rawgeti(L, -1, j + 1);
vec_push(&values, lua_tonumber(L, -1));
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lovrShaderSendFloatVec2(shader, id, size, values.data);
break;
case GL_FLOAT_VEC3:
luaL_checktype(L, 3, LUA_TTABLE);
lua_rawgeti(L, 3, 1);
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
} else {
lua_pop(L, 1);
}
n = lua_objlen(L, -1);
if (n < size) {
return luaL_error(L, "Expected %d vec3s, got %d", size, n);
}
for (int i = 0; i < size; i++) {
lua_rawgeti(L, -1, i + 1);
for (int j = 0; j < 3; j++) {
lua_rawgeti(L, -1, j + 1);
vec_push(&values, lua_tonumber(L, -1));
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lovrShaderSendFloatVec3(shader, id, size, values.data);
break;
case GL_FLOAT_VEC4:
luaL_checktype(L, 3, LUA_TTABLE);
lua_rawgeti(L, 3, 1);
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
} else {
lua_pop(L, 1);
}
n = lua_objlen(L, -1);
if (n < size) {
return luaL_error(L, "Expected %d vec3s, got %d", size, n);
}
for (int i = 0; i < size; i++) {
lua_rawgeti(L, -1, i + 1);
for (int j = 0; j < 4; j++) {
lua_rawgeti(L, -1, j + 1);
vec_push(&values, lua_tonumber(L, -1));
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lovrShaderSendFloatVec4(shader, id, size, values.data);
break;
case GL_FLOAT_MAT2:
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < 4; i++) {
lua_rawgeti(L, 3, i + 1);
data[i] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
lovrShaderSendFloatMat2(shader, id, data);
break;
case GL_FLOAT_MAT3:
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < 9; i++) {
lua_rawgeti(L, 3, i + 1);
data[i] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
lovrShaderSendFloatMat3(shader, id, data);
break;
case GL_FLOAT_MAT4:
if (lua_isuserdata(L, 3)) {
Transform* transform = luax_checktype(L, 3, Transform);
memcpy(data, transform->matrix, 16 * sizeof(float));
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < 16; i++) {
lua_rawgeti(L, 3, i + 1);
data[i] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
}
lovrShaderSendFloatMat4(shader, id, data);
break;
default:
return luaL_error(L, "Unknown uniform type %d", type);
if (!tempData.data) {
tempData.data = malloc(uniform->size);
} else if (tempData.size < uniform->size) {
tempData.size = uniform->size;
tempData.data = realloc(tempData.data, tempData.size);
}
vec_deinit(&values);
int* ints = (int*) tempData.data;
float* floats = (float*) tempData.data;
Texture** textures = (Texture**) tempData.data;
int n = 1;
int components = uniform->components;
if (components > 1) {
luaL_checktype(L, 3, LUA_TTABLE);
lua_rawgeti(L, 3, 1);
if (!lua_istable(L, -1)) {
lua_newtable(L);
lua_pushvalue(L, 3);
lua_rawseti(L, -2, 1);
} else {
lua_pop(L, 1);
}
n = lua_objlen(L, -1);
if (n != uniform->count) {
const char* elements = uniform->count == 1 ? "element" : "elements";
return luaL_error(L, "Expected %d %s for array '%s', got %d", uniform->count, elements, uniform->name, n);
}
}
switch (uniform->type) {
case UNIFORM_FLOAT:
if (components == 1) {
floats[0] = luaL_checkinteger(L, 3);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) {
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, uniform->name, lua_objlen(L, -1));
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
floats[i * components + j] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetFloat(shader, name, floats, n);
break;
case UNIFORM_INT:
if (components == 1) {
ints[0] = luaL_checkinteger(L, 3);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
if (lua_type(L, -1) != LUA_TTABLE || (int) lua_objlen(L, -1) != components) {
return luaL_error(L, "Expected %d components for uniform '%s' #%d, got %d", components, uniform->name, lua_objlen(L, -1));
}
for (int j = 0; j < components; j++) {
lua_rawgeti(L, -1, j + 1);
ints[i * components + j] = luaL_checkinteger(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetInt(shader, name, ints, n);
break;
case UNIFORM_MATRIX:
if (components == 4 && lua_isuserdata(L, 3)) {
Transform* transform = luax_checktype(L, 3, Transform);
memcpy(floats, transform->matrix, 16 * sizeof(float));
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
for (int j = 0; j < components * components; j++) {
lua_rawgeti(L, -1, j + 1);
floats[i * components * components + j] = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
lovrShaderSetMatrix(shader, name, floats, n);
break;
case UNIFORM_SAMPLER:
if (components == 1) {
textures[0] = luax_checktype(L, 3, Texture);
} else {
luaL_checktype(L, 3, LUA_TTABLE);
for (int i = 0; i < n; i++) {
lua_rawgeti(L, -1, i + 1);
textures[i] = luax_checktype(L, -1, Texture);
lua_pop(L, 1);
}
}
lovrShaderSetTexture(shader, name, textures, n);
break;
default: break;
}
return 0;
}

View File

@ -1,17 +0,0 @@
#include "api/lovr.h"
#include "graphics/graphics.h"
int l_lovrSkyboxDraw(lua_State* L) {
Skybox* skybox = luax_checktype(L, 1, Skybox);
float angle = luaL_optnumber(L, 2, 0.f);
float ax = luaL_optnumber(L, 3, 0.f);
float ay = luaL_optnumber(L, 4, 0.f);
float az = luaL_optnumber(L, 5, 0.f);
lovrGraphicsSkybox(skybox, angle, ax, ay, az);
return 0;
}
const luaL_Reg lovrSkybox[] = {
{ "draw", l_lovrSkyboxDraw },
{ NULL, NULL }
};

View File

@ -3,8 +3,8 @@
int l_lovrTextureGetDimensions(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, lovrTextureGetWidth(texture));
lua_pushnumber(L, lovrTextureGetHeight(texture));
lua_pushnumber(L, texture->width);
lua_pushnumber(L, texture->height);
return 2;
}
@ -21,33 +21,36 @@ int l_lovrTextureGetFilter(lua_State* L) {
int l_lovrTextureGetHeight(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, lovrTextureGetHeight(texture));
lua_pushnumber(L, texture->height);
return 1;
}
int l_lovrTextureGetWidth(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lua_pushnumber(L, lovrTextureGetWidth(texture));
lua_pushnumber(L, texture->width);
return 1;
}
int l_lovrTextureGetWrap(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
WrapMode horizontal, vertical;
lovrTextureGetWrap(texture, &horizontal, &vertical);
luax_pushenum(L, &WrapModes, horizontal);
luax_pushenum(L, &WrapModes, vertical);
TextureWrap wrap = lovrTextureGetWrap(texture);
luax_pushenum(L, &WrapModes, wrap.s);
luax_pushenum(L, &WrapModes, wrap.t);
if (texture->type == TEXTURE_CUBE) {
luax_pushenum(L, &WrapModes, wrap.r);
return 3;
}
return 2;
}
int l_lovrTextureRenderTo(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
lovrGraphicsPushCanvas();
lovrGraphicsPushView();
lovrTextureBindFramebuffer(texture);
lua_settop(L, 2);
lua_call(L, 0, 0);
lovrTextureResolveMSAA(texture);
lovrGraphicsPopCanvas();
lovrGraphicsPopView();
return 0;
}
@ -62,9 +65,11 @@ int l_lovrTextureSetFilter(lua_State* L) {
int l_lovrTextureSetWrap(lua_State* L) {
Texture* texture = luax_checktype(L, 1, Texture);
WrapMode* horizontal = (WrapMode*) luax_checkenum(L, 2, &WrapModes, "wrap mode");
WrapMode* vertical = (WrapMode*) luax_optenum(L, 3, luaL_checkstring(L, 2), &WrapModes, "wrap mode");
lovrTextureSetWrap(texture, *horizontal, *vertical);
TextureWrap wrap;
wrap.s = *(WrapMode*) luax_checkenum(L, 2, &WrapModes, "wrap mode");
wrap.t = *(WrapMode*) luax_optenum(L, 3, luaL_checkstring(L, 2), &WrapModes, "wrap mode");
wrap.r = *(WrapMode*) luax_optenum(L, 4, luaL_checkstring(L, 2), &WrapModes, "wrap mode");
lovrTextureSetWrap(texture, wrap);
return 0;
}

View File

@ -78,7 +78,8 @@ if not lovr.filesystem.getSource() or not runnable then
local logo, controllers
function lovr.load()
logo = lovr.graphics.newTexture(lovr.filesystem.newBlob(lovr._logo, 'logo.png'))
logo = lovr.graphics.newMaterial()
logo:setTexture(lovr.graphics.newTexture(lovr.filesystem.newBlob(lovr._logo, 'logo.png')))
lovr.graphics.setBackgroundColor(245, 252, 255)
refreshControllers()
end
@ -97,7 +98,9 @@ if not lovr.filesystem.getSource() or not runnable then
local titlePosition = 1.3 - padding
local subtitlePosition = titlePosition - font:getHeight() * .25 - padding
lovr.graphics.plane(logo, 0, 1.8, -3, 1, 0, 0, 1)
lovr.graphics.setMaterial(logo)
lovr.graphics.plane('fill', 0, 1.8, -3, 1, 0, 0, 1)
lovr.graphics.setMaterial()
lovr.graphics.setColor(15, 15, 15)
lovr.graphics.print('LÖVR', -.01, titlePosition, -3, .25, 0, 0, 1, 0, nil, 'center', 'top')
lovr.graphics.setColor(15, 15, 15, fade)

View File

@ -174,353 +174,363 @@ unsigned char boot_lua[] = {
0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x6f, 0x67, 0x6f, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77,
0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x28, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x6f, 0x67, 0x6f, 0x3a, 0x73, 0x65, 0x74, 0x54,
0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x6e, 0x65, 0x77,
0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e,
0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x62, 0x28, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x5f, 0x6c, 0x6f, 0x67, 0x6f, 0x2c, 0x20, 0x27, 0x6c, 0x6f, 0x67,
0x6f, 0x2e, 0x70, 0x6e, 0x67, 0x27, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69,
0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x32, 0x34,
0x35, 0x2c, 0x20, 0x32, 0x35, 0x32, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x28,
0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x32,
0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35,
0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x6d,
0x6f, 0x64, 0x65, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72,
0x73, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x78, 0x2c, 0x20, 0x79, 0x2c, 0x20,
0x7a, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x3a, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69,
0x6f, 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d,
0x6f, 0x64, 0x65, 0x6c, 0x3a, 0x64, 0x72, 0x61, 0x77, 0x28, 0x78, 0x2c,
0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x3a, 0x67, 0x65, 0x74,
0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28,
0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61,
0x64, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x2e, 0x31, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f, 0x6e,
0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61,
0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x46, 0x6f, 0x6e,
0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x20, 0x66, 0x61, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x38, 0x30, 0x20,
0x2b, 0x20, 0x31, 0x35, 0x30, 0x20, 0x2a, 0x20, 0x6d, 0x61, 0x74, 0x68,
0x2e, 0x61, 0x62, 0x73, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x73, 0x69,
0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72,
0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x28, 0x29, 0x20, 0x2a,
0x20, 0x32, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x33, 0x20, 0x2d,
0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x62, 0x74, 0x69,
0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x3d, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x3a, 0x67,
0x65, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x20, 0x2a,
0x20, 0x2e, 0x32, 0x35, 0x20, 0x2d, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69,
0x6e, 0x67, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x6c,
0x61, 0x6e, 0x65, 0x28, 0x6c, 0x6f, 0x67, 0x6f, 0x2c, 0x20, 0x30, 0x2c,
0x20, 0x31, 0x2e, 0x38, 0x2c, 0x20, 0x2d, 0x33, 0x2c, 0x20, 0x31, 0x2c,
0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x29, 0x0a, 0x20, 0x20,
0x6f, 0x2e, 0x70, 0x6e, 0x67, 0x27, 0x29, 0x29, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68,
0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
0x28, 0x31, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72,
0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x28, 0x27, 0x4c, 0xc3, 0x96, 0x56, 0x52, 0x27, 0x2c, 0x20, 0x2d, 0x2e,
0x30, 0x31, 0x2c, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x2d, 0x33, 0x2c, 0x20, 0x2e,
0x32, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c,
0x20, 0x30, 0x2c, 0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x27, 0x63, 0x65,
0x6e, 0x74, 0x65, 0x72, 0x27, 0x2c, 0x20, 0x27, 0x74, 0x6f, 0x70, 0x27,
0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67,
0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43,
0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x31, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x2c,
0x20, 0x31, 0x35, 0x2c, 0x20, 0x66, 0x61, 0x64, 0x65, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70,
0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x27,
0x4e, 0x6f, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x20, 0x3a, 0x28, 0x27, 0x2c,
0x20, 0x2d, 0x2e, 0x30, 0x31, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x74, 0x69,
0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c,
0x20, 0x2d, 0x33, 0x2c, 0x20, 0x2e, 0x31, 0x35, 0x2c, 0x20, 0x30, 0x2c,
0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x6e, 0x69,
0x6c, 0x2c, 0x20, 0x27, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x27, 0x2c,
0x20, 0x27, 0x74, 0x6f, 0x70, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e,
0x64, 0x0a, 0x0a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x28, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x6e, 0x64,
0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x5f, 0x2c,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x20,
0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65,
0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73,
0x28, 0x29, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73,
0x5b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5d,
0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x3a, 0x6e, 0x65, 0x77, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x28, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64, 0x64,
0x65, 0x64, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x0a,
0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64,
0x20, 0x3d, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x0a, 0x65, 0x6e,
0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x63,
0x63, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20,
0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x2c, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x66, 0x27, 0x29, 0x0a, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72,
0x0a, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e,
0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x73, 0x75, 0x63,
0x63, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72,
0x72, 0x20, 0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x2c, 0x20, 0x63, 0x6f, 0x6e,
0x66, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e,
0x73, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x28,
0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x29, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x6f,
0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x20, 0x27, 0x61,
0x75, 0x64, 0x69, 0x6f, 0x27, 0x2c, 0x20, 0x27, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x27, 0x2c, 0x20, 0x27, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
0x73, 0x27, 0x2c, 0x20, 0x27, 0x6d, 0x61, 0x74, 0x68, 0x27, 0x2c, 0x20,
0x27, 0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x73, 0x27, 0x2c, 0x20, 0x27,
0x74, 0x69, 0x6d, 0x65, 0x72, 0x27, 0x20, 0x7d, 0x0a, 0x66, 0x6f, 0x72,
0x20, 0x5f, 0x2c, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69,
0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x6d, 0x6f, 0x64,
0x75, 0x6c, 0x65, 0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x69,
0x66, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
0x65, 0x73, 0x5b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5d, 0x20, 0x74,
0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x5b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x72,
0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x27, 0x20, 0x2e, 0x2e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
0x0a, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61,
0x70, 0x68, 0x69, 0x63, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f,
0x6e, 0x66, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x77,
0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x77, 0x69, 0x6e, 0x64,
0x6f, 0x77, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72,
0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x28, 0x77, 0x2e, 0x77, 0x69,
0x64, 0x74, 0x68, 0x2c, 0x20, 0x77, 0x2e, 0x68, 0x65, 0x69, 0x67, 0x68,
0x74, 0x2c, 0x20, 0x77, 0x2e, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72,
0x65, 0x65, 0x6e, 0x2c, 0x20, 0x77, 0x2e, 0x6d, 0x73, 0x61, 0x61, 0x2c,
0x20, 0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x2c, 0x20, 0x77, 0x2e,
0x69, 0x63, 0x6f, 0x6e, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d,
0x2d, 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x79, 0x2c, 0x20, 0x74, 0x65,
0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x20, 0x68, 0x61, 0x63, 0x6b,
0x20, 0x2d, 0x20, 0x66, 0x61, 0x6b, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64,
0x73, 0x65, 0x74, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73,
0x20, 0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x77, 0x69, 0x6e,
0x64, 0x6f, 0x77, 0x20, 0x54, 0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x66, 0x69,
0x78, 0x21, 0x0a, 0x69, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6d,
0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x5b, 0x27, 0x68, 0x65, 0x61, 0x64,
0x73, 0x65, 0x74, 0x27, 0x5d, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x5b, 0x27, 0x68, 0x65, 0x61, 0x64, 0x73,
0x65, 0x74, 0x27, 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69,
0x72, 0x65, 0x28, 0x27, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61,
0x64, 0x73, 0x65, 0x74, 0x27, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a,
0x2d, 0x2d, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x61, 0x66, 0x74,
0x65, 0x72, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x69, 0x73,
0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x0a, 0x69, 0x66, 0x20,
0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6e,
0x0a, 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6e,
0x66, 0x65, 0x72, 0x72, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69,
0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x73, 0x65, 0x74,
0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x28, 0x63, 0x6f, 0x6e,
0x66, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e,
0x64, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
0x65, 0x74, 0x2e, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x61, 0x6e,
0x64, 0x6c, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x6d,
0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x7b, 0x0a, 0x20,
0x20, 0x71, 0x75, 0x69, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
0x20, 0x20, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x66, 0x6f,
0x63, 0x75, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x28, 0x66, 0x29, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64,
0x64, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64, 0x64, 0x65, 0x64, 0x28,
0x63, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64,
0x2c, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x3d, 0x20,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65,
0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x28, 0x63, 0x29,
0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a,
0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x2c, 0x20, 0x62, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x70,
0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x28, 0x63,
0x2c, 0x20, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65,
0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64,
0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28,
0x63, 0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6c,
0x65, 0x61, 0x73, 0x65, 0x64, 0x28, 0x63, 0x2c, 0x20, 0x62, 0x29, 0x20,
0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x7d, 0x2c,
0x20, 0x7b, 0x0a, 0x20, 0x20, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28,
0x73, 0x65, 0x6c, 0x66, 0x2c, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x27,
0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x3a, 0x20, 0x27, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x29,
0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x7d, 0x29, 0x0a, 0x0a, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x20, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x52, 0x65, 0x6e,
0x64, 0x65, 0x72, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28,
0x29, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x4f,
0x72, 0x69, 0x67, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x28, 0x29, 0x20,
0x3d, 0x3d, 0x20, 0x27, 0x68, 0x65, 0x61, 0x64, 0x27, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
0x48, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65,
0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a,
0x65, 0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x28, 0x29,
0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x66,
0x6f, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x61, 0x2c, 0x20,
0x62, 0x2c, 0x20, 0x63, 0x2c, 0x20, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x6f,
0x6c, 0x6c, 0x28, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x69, 0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27,
0x71, 0x75, 0x69, 0x74, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x28, 0x6e,
0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x71, 0x75, 0x69, 0x74,
0x20, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x29, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6e, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x61,
0x6e, 0x64, 0x6c, 0x65, 0x72, 0x73, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d,
0x28, 0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x63, 0x2c, 0x20, 0x64, 0x29,
0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x20, 0x64, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x28,
0x29, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x61, 0x75, 0x64, 0x69, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e,
0x69, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x64, 0x74, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x2e, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x69, 0x73, 0x50, 0x72,
0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x61, 0x75, 0x64, 0x69, 0x6f, 0x2e, 0x73, 0x65, 0x74, 0x4f, 0x72, 0x69,
0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65,
0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x32,
0x34, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x32, 0x2c, 0x20, 0x32, 0x35, 0x35,
0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73,
0x68, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73,
0x28, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x20,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69,
0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35, 0x35, 0x2c, 0x20, 0x32, 0x35,
0x35, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2c, 0x20,
0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69,
0x72, 0x73, 0x28, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x78, 0x2c, 0x20, 0x79, 0x2c,
0x20, 0x7a, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x3a, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74,
0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x3a, 0x64, 0x72, 0x61, 0x77, 0x28, 0x78,
0x2c, 0x20, 0x79, 0x2c, 0x20, 0x7a, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x3a, 0x67, 0x65,
0x74, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x2e, 0x73, 0x65, 0x74,
0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65,
0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x61, 0x75, 0x64, 0x69, 0x6f, 0x2e, 0x73, 0x65, 0x74, 0x56, 0x65, 0x6c,
0x6f, 0x63, 0x69, 0x74, 0x79, 0x28, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x56, 0x65,
0x6c, 0x6f, 0x63, 0x69, 0x74, 0x79, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x64, 0x74,
0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e,
0x63, 0x6c, 0x65, 0x61, 0x72, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63,
0x73, 0x2e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x64,
0x72, 0x61, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e,
0x69, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70,
0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x2e, 0x31, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x6f,
0x6e, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72,
0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x67, 0x65, 0x74, 0x46, 0x6f,
0x6e, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x20, 0x66, 0x61, 0x64, 0x65, 0x20, 0x3d, 0x20, 0x38, 0x30,
0x20, 0x2b, 0x20, 0x31, 0x35, 0x30, 0x20, 0x2a, 0x20, 0x6d, 0x61, 0x74,
0x68, 0x2e, 0x61, 0x62, 0x73, 0x28, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x73,
0x69, 0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x74, 0x69, 0x6d, 0x65,
0x72, 0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x28, 0x29, 0x20,
0x2a, 0x20, 0x32, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x33, 0x20,
0x2d, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x62, 0x74,
0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x3d, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x2d, 0x20, 0x66, 0x6f, 0x6e, 0x74, 0x3a,
0x67, 0x65, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x28, 0x29, 0x20,
0x2a, 0x20, 0x2e, 0x32, 0x35, 0x20, 0x2d, 0x20, 0x70, 0x61, 0x64, 0x64,
0x69, 0x6e, 0x67, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73,
0x65, 0x74, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x28, 0x6c,
0x6f, 0x67, 0x6f, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70,
0x6c, 0x61, 0x6e, 0x65, 0x28, 0x27, 0x66, 0x69, 0x6c, 0x6c, 0x27, 0x2c,
0x20, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x38, 0x2c, 0x20, 0x2d, 0x33, 0x2c,
0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72,
0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x4d, 0x61,
0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69,
0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x28,
0x31, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61,
0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28,
0x27, 0x4c, 0xc3, 0x96, 0x56, 0x52, 0x27, 0x2c, 0x20, 0x2d, 0x2e, 0x30,
0x31, 0x2c, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x2d, 0x33, 0x2c, 0x20, 0x2e, 0x32,
0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20,
0x30, 0x2c, 0x20, 0x6e, 0x69, 0x6c, 0x2c, 0x20, 0x27, 0x63, 0x65, 0x6e,
0x74, 0x65, 0x72, 0x27, 0x2c, 0x20, 0x27, 0x74, 0x6f, 0x70, 0x27, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72,
0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x43, 0x6f,
0x6c, 0x6f, 0x72, 0x28, 0x31, 0x35, 0x2c, 0x20, 0x31, 0x35, 0x2c, 0x20,
0x31, 0x35, 0x2c, 0x20, 0x66, 0x61, 0x64, 0x65, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68,
0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x28, 0x27, 0x4e,
0x6f, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x20, 0x3a, 0x28, 0x27, 0x2c, 0x20,
0x2d, 0x2e, 0x30, 0x31, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x74, 0x69, 0x74,
0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20,
0x2d, 0x33, 0x2c, 0x20, 0x2e, 0x31, 0x35, 0x2c, 0x20, 0x30, 0x2c, 0x20,
0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x30, 0x2c, 0x20, 0x6e, 0x69, 0x6c,
0x2c, 0x20, 0x27, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x27, 0x2c, 0x20,
0x27, 0x74, 0x6f, 0x70, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64,
0x0a, 0x0a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x74,
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x28, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6e,
0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x5f, 0x2c, 0x20,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x69,
0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74,
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x28,
0x29, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x5b,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x5d, 0x20,
0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
0x3a, 0x6e, 0x65, 0x77, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x28, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e,
0x64, 0x0a, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64, 0x64, 0x65,
0x64, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x0a, 0x20,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20,
0x3d, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x0a, 0x65, 0x6e, 0x64,
0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x63, 0x63,
0x65, 0x73, 0x73, 0x2c, 0x20, 0x65, 0x72, 0x72, 0x20, 0x3d, 0x20, 0x70,
0x63, 0x61, 0x6c, 0x6c, 0x28, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
0x2c, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x66, 0x27, 0x29, 0x0a, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72, 0x0a,
0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63,
0x65, 0x73, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72,
0x20, 0x3d, 0x20, 0x70, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x66,
0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x73,
0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x28, 0x63,
0x6f, 0x6e, 0x66, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x29, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x6f, 0x64,
0x75, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x20, 0x27, 0x61, 0x75,
0x64, 0x69, 0x6f, 0x27, 0x2c, 0x20, 0x27, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x27, 0x2c, 0x20, 0x27, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x27, 0x2c, 0x20, 0x27, 0x6d, 0x61, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x27,
0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x73, 0x27, 0x2c, 0x20, 0x27, 0x74,
0x69, 0x6d, 0x65, 0x72, 0x27, 0x20, 0x7d, 0x0a, 0x66, 0x6f, 0x72, 0x20,
0x5f, 0x2c, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x69, 0x6e,
0x20, 0x69, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28, 0x6d, 0x6f, 0x64, 0x75,
0x6c, 0x65, 0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x69, 0x66,
0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
0x73, 0x5b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5d, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x5b,
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x65,
0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x27, 0x20, 0x2e, 0x2e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x29,
0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a,
0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70,
0x68, 0x69, 0x63, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x6e,
0x66, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x77, 0x20,
0x3d, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x77, 0x69, 0x6e, 0x64, 0x6f,
0x77, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61,
0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x28, 0x77, 0x2e, 0x77, 0x69, 0x64,
0x74, 0x68, 0x2c, 0x20, 0x77, 0x2e, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
0x2c, 0x20, 0x77, 0x2e, 0x66, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65,
0x65, 0x6e, 0x2c, 0x20, 0x77, 0x2e, 0x6d, 0x73, 0x61, 0x61, 0x2c, 0x20,
0x77, 0x2e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x2c, 0x20, 0x77, 0x2e, 0x69,
0x63, 0x6f, 0x6e, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d,
0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x79, 0x2c, 0x20, 0x74, 0x65, 0x6d,
0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x20, 0x68, 0x61, 0x63, 0x6b, 0x20,
0x2d, 0x20, 0x66, 0x61, 0x6b, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x73,
0x65, 0x74, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73, 0x20,
0x61, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x77, 0x69, 0x6e, 0x64,
0x6f, 0x77, 0x20, 0x54, 0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x66, 0x69, 0x78,
0x21, 0x0a, 0x69, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x6d, 0x6f,
0x64, 0x75, 0x6c, 0x65, 0x73, 0x5b, 0x27, 0x68, 0x65, 0x61, 0x64, 0x73,
0x65, 0x74, 0x27, 0x5d, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x5b, 0x27, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65,
0x74, 0x27, 0x5d, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x28, 0x27, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64,
0x73, 0x65, 0x74, 0x27, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d,
0x2d, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x61, 0x66, 0x74, 0x65,
0x72, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x20, 0x69, 0x73, 0x20,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x0a, 0x69, 0x66, 0x20, 0x63,
0x6f, 0x6e, 0x66, 0x65, 0x72, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x63, 0x6f, 0x6e, 0x66,
0x65, 0x72, 0x72, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65,
0x74, 0x2e, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x6f, 0x28, 0x68,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72,
0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x65,
0x61, 0x64, 0x73, 0x65, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x28,
0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72,
0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e,
0x64, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x74, 0x69, 0x6d,
0x65, 0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x2e, 0x30, 0x30,
0x31, 0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x72, 0x75,
0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x29, 0x20,
0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20,
0x74, 0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x69, 0x74, 0x20, 0x3d,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x28, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x65, 0x78, 0x69, 0x74,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x20, 0x65, 0x78, 0x69, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20,
0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73,
0x74, 0x65, 0x6d, 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x28, 0x27,
0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x6c, 0x75, 0x61, 0x27, 0x29, 0x20, 0x74,
0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x28, 0x27, 0x6d, 0x61, 0x69, 0x6e, 0x27, 0x29, 0x0a, 0x65, 0x6e,
0x64, 0x0a
0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x73, 0x65, 0x74, 0x4d,
0x69, 0x72, 0x72, 0x6f, 0x72, 0x65, 0x64, 0x28, 0x63, 0x6f, 0x6e, 0x66,
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64,
0x20, 0x63, 0x6f, 0x6e, 0x66, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65,
0x74, 0x2e, 0x6d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x20, 0x65, 0x6e,
0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x61, 0x6e, 0x64,
0x6c, 0x65, 0x72, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x74, 0x6d, 0x65,
0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x28, 0x7b, 0x0a, 0x20, 0x20,
0x71, 0x75, 0x69, 0x74, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20,
0x20, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x66, 0x6f, 0x63,
0x75, 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x28, 0x66, 0x29, 0x20, 0x65, 0x6e,
0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20, 0x20, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64, 0x64,
0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x61, 0x64, 0x64, 0x65, 0x64, 0x28, 0x63,
0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c,
0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20,
0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
0x72, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x28, 0x63, 0x29, 0x20,
0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x2c, 0x0a, 0x20,
0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x70,
0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x75, 0x6e,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x2c, 0x20, 0x62, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x70, 0x72,
0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c,
0x65, 0x72, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x28, 0x63, 0x2c,
0x20, 0x62, 0x29, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e,
0x64, 0x2c, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20,
0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63,
0x2c, 0x20, 0x62, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x72, 0x65, 0x6c, 0x65,
0x61, 0x73, 0x65, 0x64, 0x28, 0x63, 0x2c, 0x20, 0x62, 0x29, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x7d, 0x2c, 0x20,
0x7b, 0x0a, 0x20, 0x20, 0x5f, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20,
0x3d, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x73,
0x65, 0x6c, 0x66, 0x2c, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x27, 0x55,
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x3a, 0x20, 0x27, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x28, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x29, 0x29, 0x0a,
0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x7d, 0x29, 0x0a, 0x0a, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x52, 0x65, 0x6e, 0x64,
0x65, 0x72, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x29,
0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x4f, 0x72,
0x69, 0x67, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x28, 0x29, 0x20, 0x3d,
0x3d, 0x20, 0x27, 0x68, 0x65, 0x61, 0x64, 0x27, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x48,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74,
0x28, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, 0x65,
0x6e, 0x64, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x28, 0x29, 0x0a,
0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x2e, 0x70, 0x75, 0x6d, 0x70, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x66, 0x6f,
0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x61, 0x2c, 0x20, 0x62,
0x2c, 0x20, 0x63, 0x2c, 0x20, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x6f, 0x6c,
0x6c, 0x28, 0x29, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69,
0x66, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x71,
0x75, 0x69, 0x74, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x28, 0x6e, 0x6f,
0x74, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x71, 0x75, 0x69, 0x74, 0x20,
0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x71, 0x75, 0x69, 0x74, 0x28, 0x29, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
0x6e, 0x20, 0x61, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x61, 0x6e,
0x64, 0x6c, 0x65, 0x72, 0x73, 0x5b, 0x6e, 0x61, 0x6d, 0x65, 0x5d, 0x28,
0x61, 0x2c, 0x20, 0x62, 0x2c, 0x20, 0x63, 0x2c, 0x20, 0x64, 0x29, 0x0a,
0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x20, 0x64, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x74, 0x69, 0x6d, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x28, 0x29,
0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x61,
0x75, 0x64, 0x69, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65,
0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x69,
0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20, 0x74,
0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x64, 0x74, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x2e, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66,
0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65,
0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68,
0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x69, 0x73, 0x50, 0x72, 0x65,
0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x61,
0x75, 0x64, 0x69, 0x6f, 0x2e, 0x73, 0x65, 0x74, 0x4f, 0x72, 0x69, 0x65,
0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74,
0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28,
0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x2e, 0x73, 0x65, 0x74, 0x50,
0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74,
0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x61,
0x75, 0x64, 0x69, 0x6f, 0x2e, 0x73, 0x65, 0x74, 0x56, 0x65, 0x6c, 0x6f,
0x63, 0x69, 0x74, 0x79, 0x28, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65,
0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x56, 0x65, 0x6c,
0x6f, 0x63, 0x69, 0x74, 0x79, 0x28, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20,
0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x64, 0x74, 0x29,
0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x63,
0x6c, 0x65, 0x61, 0x72, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
0x2e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x64, 0x72,
0x61, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65,
0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c, 0x6f,
0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x2e, 0x69,
0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x20, 0x74,
0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74,
0x2e, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x54, 0x6f, 0x28, 0x68, 0x65,
0x61, 0x64, 0x73, 0x65, 0x74, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43,
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x65, 0x61,
0x64, 0x73, 0x65, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x28, 0x29,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76,
0x72, 0x2e, 0x64, 0x72, 0x61, 0x77, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e,
0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x65,
0x73, 0x65, 0x6e, 0x74, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x65, 0x6e, 0x64,
0x0a, 0x20, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x74, 0x69, 0x6d, 0x65,
0x72, 0x2e, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x28, 0x2e, 0x30, 0x30, 0x31,
0x29, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x72, 0x75, 0x6e,
0x28, 0x29, 0x0a, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6c, 0x6f, 0x76, 0x72,
0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x29, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x20, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74,
0x72, 0x75, 0x65, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x65, 0x78, 0x69, 0x74, 0x20, 0x3d, 0x20,
0x6c, 0x6f, 0x76, 0x72, 0x2e, 0x73, 0x74, 0x65, 0x70, 0x28, 0x29, 0x0a,
0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x65, 0x78, 0x69, 0x74, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
0x65, 0x78, 0x69, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6c,
0x6f, 0x76, 0x72, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x79, 0x73, 0x74,
0x65, 0x6d, 0x2e, 0x69, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x28, 0x27, 0x6d,
0x61, 0x69, 0x6e, 0x2e, 0x6c, 0x75, 0x61, 0x27, 0x29, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65,
0x28, 0x27, 0x6d, 0x61, 0x69, 0x6e, 0x27, 0x29, 0x0a, 0x65, 0x6e, 0x64,
0x0a
};
unsigned int boot_lua_len = 6266;
unsigned int boot_lua_len = 6385;

View File

@ -7,6 +7,7 @@ Blob* lovrBlobCreate(void* data, size_t size, const char* name) {
blob->data = data;
blob->size = size;
blob->name = name;
blob->seek = 0;
return blob;
}

View File

@ -7,6 +7,7 @@ typedef struct {
void* data;
size_t size;
const char* name;
size_t seek;
} Blob;
Blob* lovrBlobCreate(void* data, size_t size, const char* name);

64
src/filesystem/file.c Normal file
View File

@ -0,0 +1,64 @@
#include "filesystem/file.h"
#include <physfs.h>
File* lovrFileCreate(const char* path) {
File* file = lovrAlloc(sizeof(File), lovrFileDestroy);
if (!file) return NULL;
file->path = path;
file->handle = NULL;
return file;
}
void lovrFileDestroy(const Ref* ref) {
File* file = containerof(ref, File);
if (file->handle) {
PHYSFS_close(file->handle);
}
free(file);
}
int lovrFileOpen(File* file, FileMode mode) {
lovrAssert(!file->handle, "File is already open");
file->mode = mode;
switch (mode) {
case OPEN_READ: file->handle = PHYSFS_openRead(file->path); break;
case OPEN_WRITE: file->handle = PHYSFS_openWrite(file->path); break;
case OPEN_APPEND: file->handle = PHYSFS_openAppend(file->path); break;
}
return file->handle == NULL;
}
void lovrFileClose(File* file) {
lovrAssert(file->handle, "File must be open to close it");
PHYSFS_close(file->handle);
file->handle = NULL;
}
size_t lovrFileRead(File* file, void* data, size_t size, size_t count) {
lovrAssert(file->handle && file->mode == OPEN_READ, "File must be open for reading");
return PHYSFS_read(file->handle, data, size, count);
}
size_t lovrFileWrite(File* file, void* data, size_t size, size_t count) {
lovrAssert(file->handle && (file->mode == OPEN_READ || file->mode == OPEN_WRITE), "File must be open for writing");
return PHYSFS_write(file->handle, data, size, count);
}
size_t lovrFileGetSize(File* file) {
lovrAssert(file->handle, "File must be open to get its size");
return PHYSFS_fileLength(file->handle);
}
int lovrFileSeek(File* file, size_t position) {
lovrAssert(file->handle, "File must be open to seek");
return !PHYSFS_seek(file->handle, position);
}
size_t lovrFileTell(File* file) {
lovrAssert(file->handle, "File must be open to tell");
return PHYSFS_tell(file->handle);
}

26
src/filesystem/file.h Normal file
View File

@ -0,0 +1,26 @@
#include "util.h"
#pragma once
typedef enum {
OPEN_READ,
OPEN_WRITE,
OPEN_APPEND
} FileMode;
typedef struct {
Ref ref;
const char* path;
void* handle;
FileMode mode;
} File;
File* lovrFileCreate(const char* filename);
void lovrFileDestroy(const Ref* ref);
int lovrFileOpen(File* file, FileMode mode);
void lovrFileClose(File* file);
size_t lovrFileRead(File* file, void* data, size_t size, size_t count);
size_t lovrFileWrite(File* file, void* data, size_t size, size_t count);
size_t lovrFileGetSize(File* file);
int lovrFileSeek(File* file, size_t position);
size_t lovrFileTell(File* file);

View File

@ -1,9 +1,9 @@
#include "filesystem/filesystem.h"
#include "filesystem/file.h"
#include "util.h"
#include <physfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
@ -22,7 +22,7 @@ static FilesystemState state;
void lovrFilesystemInit(const char* arg0, const char* arg1) {
if (!PHYSFS_init(arg0)) {
lovrThrow("Could not initialize filesystem: %s", PHYSFS_getLastError());
lovrThrow("Could not initialize filesystem: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
}
state.source = malloc(LOVR_PATH_MAX * sizeof(char));
@ -60,10 +60,6 @@ int lovrFilesystemCreateDirectory(const char* path) {
return !PHYSFS_mkdir(path);
}
int lovrFilesystemExists(const char* path) {
return PHYSFS_exists(path);
}
int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size) {
#ifdef __APPLE__
const char* home;
@ -97,7 +93,7 @@ int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size) {
}
void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata) {
PHYSFS_enumerateFilesCallback(path, callback, userdata);
PHYSFS_enumerate(path, callback, userdata);
}
int lovrFilesystemGetExecutablePath(char* dest, unsigned int size) {
@ -126,7 +122,8 @@ const char* lovrFilesystemGetIdentity() {
}
long lovrFilesystemGetLastModified(const char* path) {
return PHYSFS_getLastModTime(path);
PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.modtime : -1;
}
const char* lovrFilesystemGetRealDirectory(const char* path) {
@ -137,15 +134,9 @@ const char* lovrFilesystemGetSaveDirectory() {
return state.savePathFull;
}
int lovrFilesystemGetSize(const char* path) {
PHYSFS_file* handle = PHYSFS_openRead(path);
if (!handle) {
return -1;
}
int length = PHYSFS_fileLength(handle);
PHYSFS_close(handle);
return length;
size_t lovrFilesystemGetSize(const char* path) {
PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.filesize : -1;
}
const char* lovrFilesystemGetSource() {
@ -153,15 +144,29 @@ const char* lovrFilesystemGetSource() {
}
const char* lovrFilesystemGetUserDirectory() {
return PHYSFS_getUserDir();
#if defined(__APPLE__) || defined(__linux__)
const char* home;
if ((home = getenv("HOME")) == NULL) {
home = getpwuid(getuid())->pw_dir;
}
return home;
#elif _WIN32
return getenv("USERPROFILE");
#elif EMSCRIPTEN
return "/home/web_user";
#else
#error "This platform is missing an implementation for lovrFilesystemGetUserDirectory"
#endif
}
int lovrFilesystemIsDirectory(const char* path) {
return PHYSFS_isDirectory(path);
PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.filetype == PHYSFS_FILETYPE_DIRECTORY : 0;
}
int lovrFilesystemIsFile(const char* path) {
return lovrFilesystemExists(path) && !lovrFilesystemIsDirectory(path);
PHYSFS_Stat stat;
return PHYSFS_stat(path, &stat) ? stat.filetype == PHYSFS_FILETYPE_REGULAR : 0;
}
int lovrFilesystemIsFused() {
@ -174,15 +179,20 @@ int lovrFilesystemMount(const char* path, const char* mountpoint, int append) {
void* lovrFilesystemRead(const char* path, size_t* bytesRead) {
// Open file
PHYSFS_file* handle = PHYSFS_openRead(path);
if (!handle) {
// Create file
File* file = lovrFileCreate(path);
if (!file) {
return NULL;
}
// Open it
if (lovrFileOpen(file, OPEN_READ)) {
return NULL;
}
// Get file size
int size = PHYSFS_fileLength(handle);
if (size < 0) {
size_t size = lovrFileGetSize(file);
if (size == (unsigned int) -1) {
return NULL;
}
@ -193,8 +203,8 @@ void* lovrFilesystemRead(const char* path, size_t* bytesRead) {
}
// Perform read
*bytesRead = PHYSFS_read(handle, data, 1, size);
PHYSFS_close(handle);
*bytesRead = lovrFileRead(file, data, 1, size);
lovrFileClose(file);
// Make sure we got everything
if (*bytesRead != (size_t) size) {
@ -214,7 +224,7 @@ int lovrFilesystemSetIdentity(const char* identity) {
// Unmount old write directory
if (state.savePathFull && state.savePathRelative) {
PHYSFS_removeFromSearchPath(state.savePathRelative);
PHYSFS_unmount(state.savePathRelative);
} else {
state.savePathRelative = malloc(LOVR_PATH_MAX);
state.savePathFull = malloc(LOVR_PATH_MAX);
@ -231,7 +241,8 @@ int lovrFilesystemSetIdentity(const char* identity) {
strncpy(state.savePathFull, fullPathBuffer, LOVR_PATH_MAX);
PHYSFS_mkdir(state.savePathRelative);
if (!PHYSFS_setWriteDir(state.savePathFull)) {
lovrThrow("Could not set write directory: %s (%s)", PHYSFS_getLastError(), state.savePathRelative);
const char* error = PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
lovrThrow("Could not set write directory: %s (%s)", error, state.savePathRelative);
}
PHYSFS_mount(state.savePathFull, NULL, 0);
@ -240,16 +251,18 @@ int lovrFilesystemSetIdentity(const char* identity) {
}
int lovrFilesystemUnmount(const char* path) {
return !PHYSFS_removeFromSearchPath(path);
return !PHYSFS_unmount(path);
}
int lovrFilesystemWrite(const char* path, const char* content, size_t size, int append) {
PHYSFS_file* handle = append ? PHYSFS_openAppend(path) : PHYSFS_openWrite(path);
if (!handle) {
size_t lovrFilesystemWrite(const char* path, const char* content, size_t size, int append) {
File* file = lovrFileCreate(path);
if (!file) {
return 0;
}
int bytesWritten = PHYSFS_write(handle, content, 1, size);
PHYSFS_close(handle);
lovrFileOpen(file, append ? OPEN_APPEND : OPEN_WRITE);
size_t bytesWritten = lovrFileWrite(file, (void*) content, 1, size);
lovrFileClose(file);
lovrRelease(&file->ref);
return bytesWritten;
}

View File

@ -4,7 +4,7 @@
#define LOVR_PATH_MAX 1024
typedef void getDirectoryItemsCallback(void* userdata, const char* dir, const char* file);
typedef int getDirectoryItemsCallback(void* userdata, const char* dir, const char* file);
typedef struct {
char* source;
@ -17,7 +17,6 @@ typedef struct {
void lovrFilesystemInit(const char* arg0, const char* arg1);
void lovrFilesystemDestroy();
int lovrFilesystemCreateDirectory(const char* path);
int lovrFilesystemExists(const char* path);
int lovrFilesystemGetAppdataDirectory(char* dest, unsigned int size);
void lovrFilesystemGetDirectoryItems(const char* path, getDirectoryItemsCallback callback, void* userdata);
int lovrFilesystemGetExecutablePath(char* dest, unsigned int size);
@ -25,7 +24,7 @@ const char* lovrFilesystemGetIdentity();
long lovrFilesystemGetLastModified(const char* path);
const char* lovrFilesystemGetRealDirectory(const char* path);
const char* lovrFilesystemGetSaveDirectory();
int lovrFilesystemGetSize(const char* path);
size_t lovrFilesystemGetSize(const char* path);
const char* lovrFilesystemGetSource();
const char* lovrFilesystemGetUserDirectory();
int lovrFilesystemIsDirectory(const char* path);
@ -37,4 +36,4 @@ int lovrFilesystemRemove(const char* path);
int lovrFilesystemSetIdentity(const char* identity);
int lovrFilesystemSetSource(const char* source);
int lovrFilesystemUnmount(const char* path);
int lovrFilesystemWrite(const char* path, const char* content, size_t size, int append);
size_t lovrFilesystemWrite(const char* path, const char* content, size_t size, int append);

View File

@ -49,9 +49,9 @@ Font* lovrFontCreate(FontData* fontData) {
// Texture
TextureData* textureData = lovrTextureDataGetBlank(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB);
TextureFilter filter = { .mode = FILTER_BILINEAR };
font->texture = lovrTextureCreate(textureData);
font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1);
lovrTextureSetFilter(font->texture, filter);
lovrTextureSetWrap(font->texture, WRAP_CLAMP, WRAP_CLAMP);
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
return font;
}
@ -277,7 +277,7 @@ void lovrFontAddGlyph(Font* font, Glyph* glyph) {
glyph->y = atlas->y;
// Paste glyph into texture
lovrGraphicsBindTexture(font->texture);
lovrGraphicsBindTexture(font->texture, TEXTURE_2D, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, atlas->x, atlas->y, glyph->tw, glyph->th, FORMAT_RGB.glFormat, GL_UNSIGNED_BYTE, glyph->data);
// Advance atlas cursor
@ -299,7 +299,7 @@ void lovrFontExpandTexture(Font* font) {
}
// Resize the texture storage
lovrTextureDataResize(font->texture->textureData, atlas->width, atlas->height, 0x0);
lovrTextureDataResize(font->texture->slices[0], atlas->width, atlas->height, 0x0);
lovrTextureRefresh(font->texture);
// Reset the cursor

View File

@ -1,4 +1,5 @@
#include "graphics/graphics.h"
#include "graphics/shaders.h"
#include "loaders/texture.h"
#include "loaders/font.h"
#include "event/event.h"
@ -35,6 +36,7 @@ void lovrGraphicsDestroy() {
for (int i = 0; i < DEFAULT_SHADER_COUNT; i++) {
if (state.defaultShaders[i]) lovrRelease(&state.defaultShaders[i]->ref);
}
if (state.defaultMaterial) lovrRelease(&state.defaultMaterial->ref);
if (state.defaultFont) lovrRelease(&state.defaultFont->ref);
if (state.defaultTexture) lovrRelease(&state.defaultTexture->ref);
glDeleteVertexArrays(1, &state.streamVAO);
@ -49,7 +51,7 @@ void lovrGraphicsReset() {
int h = lovrGraphicsGetHeight();
float projection[16];
state.transform = 0;
state.canvas = 0;
state.view = 0;
state.defaultShader = -1;
lovrGraphicsSetBackgroundColor((Color) { 0, 0, 0, 255 });
lovrGraphicsSetBlendMode(BLEND_ALPHA, BLEND_ALPHA_MULTIPLY);
@ -58,6 +60,7 @@ void lovrGraphicsReset() {
lovrGraphicsSetDefaultFilter((TextureFilter) { .mode = FILTER_TRILINEAR });
lovrGraphicsSetDepthTest(COMPARE_LEQUAL);
lovrGraphicsSetFont(NULL);
lovrGraphicsSetMaterial(NULL);
lovrGraphicsSetLineWidth(1);
lovrGraphicsSetPointSize(1);
lovrGraphicsSetShader(NULL);
@ -86,9 +89,51 @@ void lovrGraphicsPrepare() {
mat4 model = state.transforms[state.transform][MATRIX_MODEL];
mat4 view = state.transforms[state.transform][MATRIX_VIEW];
mat4 projection = state.canvases[state.canvas].projection;
mat4 projection = state.views[state.view].projection;
lovrShaderSetMatrix(shader, "lovrModel", model, 16);
lovrShaderSetMatrix(shader, "lovrView", view, 16);
lovrShaderSetMatrix(shader, "lovrProjection", projection, 16);
float transform[16];
mat4_multiply(mat4_set(transform, view), model);
lovrShaderSetMatrix(shader, "lovrTransform", transform, 16);
if (lovrShaderGetUniform(shader, "lovrNormalMatrix")) {
if (mat4_invert(transform)) {
mat4_transpose(transform);
} else {
mat4_identity(transform);
}
float normalMatrix[9] = {
transform[0], transform[1], transform[2],
transform[4], transform[5], transform[6],
transform[8], transform[9], transform[10]
};
lovrShaderSetMatrix(shader, "lovrNormalMatrix", normalMatrix, 9);
}
// Color
float color[4] = { state.color.r / 255., state.color.g / 255., state.color.b / 255., state.color.a / 255. };
lovrShaderSetFloat(shader, "lovrColor", color, 4);
// Material
Material* material = lovrGraphicsGetMaterial();
for (int i = 0; i < MAX_MATERIAL_COLORS; i++) {
Color color = lovrMaterialGetColor(material, i);
float data[4] = { color.r / 255., color.g / 255., color.b / 255., color.a / 255. };
lovrShaderSetFloat(shader, lovrShaderColorUniforms[i], data, 4);
}
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
Texture* texture = lovrMaterialGetTexture(material, i);
lovrShaderSetTexture(shader, lovrShaderTextureUniforms[i], &texture, 1);
}
lovrGraphicsBindProgram(shader->id);
lovrShaderBind(shader, model, view, projection, state.color, 0);
lovrShaderBind(shader);
}
void lovrGraphicsCreateWindow(int w, int h, int fullscreen, int msaa, const char* title, const char* icon) {
@ -327,6 +372,31 @@ void lovrGraphicsSetLineWidth(float width) {
glLineWidth(width);
}
Material* lovrGraphicsGetMaterial() {
if (!state.material) {
if (!state.defaultMaterial) {
MaterialData* materialData = lovrMaterialDataCreateEmpty();
state.defaultMaterial = lovrMaterialCreate(materialData, 1);
}
lovrGraphicsSetMaterial(state.defaultMaterial);
}
return state.material;
}
void lovrGraphicsSetMaterial(Material* material) {
if (state.material) {
lovrRelease(&state.material->ref);
}
state.material = material;
if (material) {
lovrRetain(&material->ref);
}
}
float lovrGraphicsGetPointSize() {
return state.pointSize;
}
@ -417,7 +487,7 @@ void lovrGraphicsMatrixTransform(MatrixType type, mat4 transform) {
// Primitives
static void lovrGraphicsSetShapeData(float* data, int length) {
static void lovrGraphicsSetStreamData(float* data, int length) {
vec_clear(&state.streamData);
vec_pusharr(&state.streamData, data, length);
}
@ -465,25 +535,22 @@ static void lovrGraphicsDrawPrimitive(GLenum mode, int hasNormals, int hasTexCoo
}
void lovrGraphicsPoints(float* points, int count) {
lovrGraphicsBindTexture(NULL);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsSetShapeData(points, count);
lovrGraphicsSetStreamData(points, count);
lovrGraphicsDrawPrimitive(GL_POINTS, 0, 0, 0);
}
void lovrGraphicsLine(float* points, int count) {
lovrGraphicsBindTexture(NULL);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsSetShapeData(points, count);
lovrGraphicsSetStreamData(points, count);
lovrGraphicsDrawPrimitive(GL_LINE_STRIP, 0, 0, 0);
}
void lovrGraphicsTriangle(DrawMode mode, float* points) {
lovrGraphicsBindTexture(NULL);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
if (mode == DRAW_MODE_LINE) {
lovrGraphicsSetShapeData(points, 9);
lovrGraphicsSetStreamData(points, 9);
lovrGraphicsDrawPrimitive(GL_LINE_LOOP, 0, 0, 0);
} else {
float normal[3];
@ -495,12 +562,12 @@ void lovrGraphicsTriangle(DrawMode mode, float* points) {
points[6], points[7], points[8], normal[0], normal[1], normal[2]
};
lovrGraphicsSetShapeData(data, 18);
lovrGraphicsSetStreamData(data, 18);
lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, 1, 0, 0);
}
}
void lovrGraphicsPlane(DrawMode mode, Texture* texture, mat4 transform) {
void lovrGraphicsPlane(DrawMode mode, mat4 transform) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
@ -512,9 +579,8 @@ void lovrGraphicsPlane(DrawMode mode, Texture* texture, mat4 transform) {
-.5, -.5, 0
};
lovrGraphicsBindTexture(NULL);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsSetShapeData(points, 12);
lovrGraphicsSetStreamData(points, 12);
lovrGraphicsDrawPrimitive(GL_LINE_LOOP, 0, 0, 0);
} else if (mode == DRAW_MODE_FILL) {
float data[] = {
@ -524,9 +590,8 @@ void lovrGraphicsPlane(DrawMode mode, Texture* texture, mat4 transform) {
.5, -.5, 0, 0, 0, -1, 1, 1
};
lovrGraphicsBindTexture(texture);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsSetShapeData(data, 32);
lovrGraphicsSetStreamData(data, 32);
lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, 1, 1, 0);
}
@ -541,13 +606,16 @@ void lovrGraphicsPlaneFullscreen(Texture* texture) {
1, -1, 0, 1, 0
};
lovrGraphicsBindTexture(texture);
lovrGraphicsSetDefaultShader(SHADER_FULLSCREEN);
lovrGraphicsSetShapeData(data, 20);
Material* material = lovrGraphicsGetMaterial();
Texture* lastTexture = lovrMaterialGetTexture(material, TEXTURE_DIFFUSE);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
lovrGraphicsSetStreamData(data, 20);
lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, 0, 1, 0);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, lastTexture);
}
void lovrGraphicsBox(DrawMode mode, Texture* texture, mat4 transform) {
void lovrGraphicsBox(DrawMode mode, mat4 transform) {
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
@ -573,9 +641,8 @@ void lovrGraphicsBox(DrawMode mode, Texture* texture, mat4 transform) {
0, 4, 1, 5, 2, 6, 3, 7 // Connections
};
lovrGraphicsBindTexture(NULL);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsSetShapeData(points, 24);
lovrGraphicsSetStreamData(points, 24);
lovrGraphicsSetIndexData(indices, 24);
lovrGraphicsDrawPrimitive(GL_LINES, 0, 0, 1);
} else {
@ -621,9 +688,8 @@ void lovrGraphicsBox(DrawMode mode, Texture* texture, mat4 transform) {
.5, .5, .5, 0, 1, 0, 1, 0
};
lovrGraphicsBindTexture(texture);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsSetShapeData(data, 208);
lovrGraphicsSetStreamData(data, 208);
lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, 1, 1, 0);
}
@ -729,14 +795,13 @@ void lovrGraphicsCylinder(float x1, float y1, float z1, float x2, float y2, floa
}
}
lovrGraphicsBindTexture(NULL);
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsDrawPrimitive(GL_TRIANGLES, 1, 0, 1);
#undef PUSH_CYLINDER_VERTEX
#undef PUSH_CYLINDER_TRIANGLE
}
void lovrGraphicsSphere(Texture* texture, mat4 transform, int segments, Skybox* skybox) {
void lovrGraphicsSphere(mat4 transform, int segments) {
vec_clear(&state.streamData);
vec_clear(&state.streamIndices);
@ -753,11 +818,9 @@ void lovrGraphicsSphere(Texture* texture, mat4 transform, int segments, Skybox*
vec_push(&state.streamData, y);
vec_push(&state.streamData, z);
if (!skybox) {
vec_push(&state.streamData, x);
vec_push(&state.streamData, y);
vec_push(&state.streamData, z);
}
vec_push(&state.streamData, x);
vec_push(&state.streamData, y);
vec_push(&state.streamData, z);
vec_push(&state.streamData, u);
vec_push(&state.streamData, v);
@ -779,23 +842,20 @@ void lovrGraphicsSphere(Texture* texture, mat4 transform, int segments, Skybox*
}
}
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
if (skybox) {
Texture* oldTexture = lovrGraphicsGetTexture();
glBindTexture(GL_TEXTURE_2D, skybox->texture);
lovrGraphicsDrawPrimitive(GL_TRIANGLES, 0, 1, 1);
glBindTexture(GL_TEXTURE_2D, oldTexture->id);
} else {
lovrGraphicsBindTexture(texture);
if (transform) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
lovrGraphicsDrawPrimitive(GL_TRIANGLES, 1, 1, 1);
}
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsDrawPrimitive(GL_TRIANGLES, 1, 1, 1);
if (transform) {
lovrGraphicsPop();
}
}
void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float az) {
void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float az) {
lovrGraphicsPush();
lovrGraphicsOrigin();
lovrGraphicsRotate(MATRIX_MODEL, angle, ax, ay, az);
@ -803,7 +863,7 @@ void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float a
int wasCulling = lovrGraphicsIsCullingEnabled();
lovrGraphicsSetCullingEnabled(0);
if (skybox->type == SKYBOX_CUBE) {
if (texture->type == TEXTURE_CUBE) {
float cube[] = {
// Front
1.f, -1.f, -1.f,
@ -846,15 +906,19 @@ void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float a
1.f, 1.f, 1.f
};
lovrGraphicsSetShapeData(cube, 78);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->texture);
lovrGraphicsSetStreamData(cube, 78);
lovrGraphicsSetDefaultShader(SHADER_SKYBOX);
Material* material = lovrGraphicsGetMaterial();
Texture* lastTexture = lovrMaterialGetTexture(material, TEXTURE_ENVIRONMENT_MAP);
lovrMaterialSetTexture(material, TEXTURE_ENVIRONMENT_MAP, texture);
lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, 0, 0, 0);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glActiveTexture(GL_TEXTURE0);
} else if (skybox->type == SKYBOX_PANORAMA) {
lovrGraphicsSphere(NULL, NULL, 30, skybox);
lovrMaterialSetTexture(material, TEXTURE_ENVIRONMENT_MAP, lastTexture);
} else if (texture->type == TEXTURE_2D) {
Material* material = lovrGraphicsGetMaterial();
Texture* lastTexture = lovrMaterialGetTexture(material, TEXTURE_DIFFUSE);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
lovrGraphicsSphere(NULL, 30);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, lastTexture);
}
lovrGraphicsSetCullingEnabled(wasCulling);
@ -872,68 +936,80 @@ void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAl
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
lovrGraphicsScale(MATRIX_MODEL, scale, scale, scale);
lovrGraphicsTranslate(MATRIX_MODEL, 0, offsety, 0);
lovrGraphicsBindTexture(font->texture);
lovrGraphicsSetDefaultShader(SHADER_FONT);
Material* material = lovrGraphicsGetMaterial();
Texture* lastTexture = lovrMaterialGetTexture(material, TEXTURE_DIFFUSE);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, font->texture);
glDepthMask(GL_FALSE);
lovrGraphicsDrawPrimitive(GL_TRIANGLES, 0, 1, 0);
glDepthMask(GL_TRUE);
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, lastTexture);
lovrGraphicsPop();
}
// Internal State
void lovrGraphicsPushCanvas() {
if (++state.canvas >= MAX_CANVASES) {
lovrThrow("Canvas overflow");
void lovrGraphicsPushView() {
if (++state.view >= MAX_VIEWS) {
lovrThrow("View overflow");
}
memcpy(&state.canvases[state.canvas], &state.canvases[state.canvas - 1], sizeof(CanvasState));
memcpy(&state.views[state.view], &state.views[state.view - 1], sizeof(View));
}
void lovrGraphicsPopCanvas() {
if (--state.canvas < 0) {
lovrThrow("Canvas underflow");
void lovrGraphicsPopView() {
if (--state.view < 0) {
lovrThrow("View underflow");
}
int* viewport = state.canvases[state.canvas].viewport;
int* viewport = state.views[state.view].viewport;
lovrGraphicsSetViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
lovrGraphicsBindFramebuffer(state.canvases[state.canvas].framebuffer);
lovrGraphicsBindFramebuffer(state.views[state.view].framebuffer);
}
mat4 lovrGraphicsGetProjection() {
return state.canvases[state.canvas].projection;
return state.views[state.view].projection;
}
void lovrGraphicsSetProjection(mat4 projection) {
memcpy(state.canvases[state.canvas].projection, projection, 16 * sizeof(float));
memcpy(state.views[state.view].projection, projection, 16 * sizeof(float));
}
void lovrGraphicsSetViewport(int x, int y, int w, int h) {
state.canvases[state.canvas].viewport[0] = x;
state.canvases[state.canvas].viewport[1] = y;
state.canvases[state.canvas].viewport[2] = w;
state.canvases[state.canvas].viewport[3] = h;
state.views[state.view].viewport[0] = x;
state.views[state.view].viewport[1] = y;
state.views[state.view].viewport[2] = w;
state.views[state.view].viewport[3] = h;
glViewport(x, y, w, h);
}
void lovrGraphicsBindFramebuffer(int framebuffer) {
state.canvases[state.canvas].framebuffer = framebuffer;
state.views[state.view].framebuffer = framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
Texture* lovrGraphicsGetTexture() {
return state.texture;
Texture* lovrGraphicsGetTexture(int slot) {
return state.textures[slot];
}
void lovrGraphicsBindTexture(Texture* texture) {
void lovrGraphicsBindTexture(Texture* texture, TextureType type, int slot) {
if (!texture) {
if (!state.defaultTexture) {
state.defaultTexture = lovrTextureCreate(lovrTextureDataGetBlank(1, 1, 0xff, FORMAT_RGBA));
TextureData* textureData = lovrTextureDataGetBlank(1, 1, 0xff, FORMAT_RGBA);
state.defaultTexture = lovrTextureCreate(TEXTURE_2D, &textureData, 1);
}
texture = state.defaultTexture;
}
if (texture != state.texture) {
state.texture = texture;
glBindTexture(GL_TEXTURE_2D, texture->id);
if (texture != state.textures[slot]) {
if (state.textures[slot]) {
lovrRelease(&state.textures[slot]->ref);
}
state.textures[slot] = texture;
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(type, texture->id);
lovrRetain(&texture->ref);
}
}

View File

@ -1,16 +1,17 @@
#include "graphics/font.h"
#include "graphics/material.h"
#include "graphics/shader.h"
#include "graphics/skybox.h"
#include "graphics/texture.h"
#include "math/math.h"
#include "lib/glfw.h"
#pragma once
#define MAX_CANVASES 4
#define MAX_VIEWS 4
#define MAX_TRANSFORMS 60
#define INTERNAL_TRANSFORMS 4
#define DEFAULT_SHADER_COUNT 4
#define MAX_TEXTURES 16
typedef enum {
BLEND_ALPHA,
@ -48,6 +49,11 @@ typedef enum {
COMPARE_GREATER = GL_GREATER
} CompareMode;
typedef enum {
MATRIX_MODEL,
MATRIX_VIEW
} MatrixType;
typedef struct {
int initialized;
float pointSizes[2];
@ -60,17 +66,13 @@ typedef struct {
int framebuffer;
float projection[16];
int viewport[4];
} CanvasState;
typedef enum {
MATRIX_MODEL,
MATRIX_VIEW
} MatrixType;
} View;
typedef struct {
GLFWwindow* window;
Shader* defaultShaders[DEFAULT_SHADER_COUNT];
DefaultShader defaultShader;
Material* defaultMaterial;
Font* defaultFont;
Texture* defaultTexture;
float transforms[MAX_TRANSFORMS + INTERNAL_TRANSFORMS][2][16];
@ -85,6 +87,7 @@ typedef struct {
Font* font;
GraphicsLimits limits;
float lineWidth;
Material* material;
float pointSize;
Shader* shader;
Winding winding;
@ -94,9 +97,9 @@ typedef struct {
uint32_t streamIBO;
vec_float_t streamData;
vec_uint_t streamIndices;
CanvasState canvases[MAX_CANVASES];
int canvas;
Texture* texture;
View views[MAX_VIEWS];
int view;
Texture* textures[MAX_TEXTURES];
uint32_t program;
uint32_t vertexArray;
uint32_t vertexBuffer;
@ -132,6 +135,8 @@ void lovrGraphicsSetFont(Font* font);
GraphicsLimits lovrGraphicsGetLimits();
float lovrGraphicsGetLineWidth();
void lovrGraphicsSetLineWidth(float width);
Material* lovrGraphicsGetMaterial();
void lovrGraphicsSetMaterial(Material* material);
float lovrGraphicsGetPointSize();
void lovrGraphicsSetPointSize(float size);
Shader* lovrGraphicsGetShader();
@ -154,23 +159,23 @@ void lovrGraphicsMatrixTransform(MatrixType type, mat4 transform);
void lovrGraphicsPoints(float* points, int count);
void lovrGraphicsLine(float* points, int count);
void lovrGraphicsTriangle(DrawMode mode, float* points);
void lovrGraphicsPlane(DrawMode mode, Texture* texture, mat4 transform);
void lovrGraphicsPlane(DrawMode mode, mat4 transform);
void lovrGraphicsPlaneFullscreen(Texture* texture);
void lovrGraphicsBox(DrawMode mode, Texture* texture, mat4 transform);
void lovrGraphicsBox(DrawMode mode, mat4 transform);
void lovrGraphicsCylinder(float x1, float y1, float z1, float x2, float y2, float z2, float r1, float r2, int capped, int segments);
void lovrGraphicsSphere(Texture* texture, mat4 transform, int segments, Skybox* skybox);
void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float az);
void lovrGraphicsSphere(mat4 transform, int segments);
void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float az);
void lovrGraphicsPrint(const char* str, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
// Internal State
void lovrGraphicsPushCanvas();
void lovrGraphicsPopCanvas();
void lovrGraphicsPushView();
void lovrGraphicsPopView();
mat4 lovrGraphicsGetProjection();
void lovrGraphicsSetProjection(mat4 projection);
void lovrGraphicsSetViewport(int x, int y, int w, int h);
void lovrGraphicsBindFramebuffer(int framebuffer);
Texture* lovrGraphicsGetTexture();
void lovrGraphicsBindTexture(Texture* texture);
void lovrGraphicsBindTexture(Texture* texture, TextureType type, int slot);
void lovrGraphicsSetDefaultShader(DefaultShader defaultShader);
Shader* lovrGraphicsGetActiveShader();
void lovrGraphicsBindProgram(uint32_t program);

46
src/graphics/material.c Normal file
View File

@ -0,0 +1,46 @@
#include "graphics/graphics.h"
#include "graphics/material.h"
Material* lovrMaterialCreate(MaterialData* materialData, int isDefault) {
Material* material = lovrAlloc(sizeof(Material), lovrMaterialDestroy);
if (!material) return NULL;
material->materialData = materialData;
material->isDefault = isDefault;
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
if (materialData->textures[i]) {
material->textures[i] = lovrTextureCreate(TEXTURE_2D, &materialData->textures[i], 1);
} else {
material->textures[i] = NULL;
}
}
return material;
}
void lovrMaterialDestroy(const Ref* ref) {
Material* material = containerof(ref, Material);
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
if (material->textures[i]) {
lovrRelease(&material->textures[i]->ref);
}
}
free(material);
}
Color lovrMaterialGetColor(Material* material, MaterialColor colorType) {
return material->materialData->colors[colorType];
}
void lovrMaterialSetColor(Material* material, MaterialColor colorType, Color color) {
material->materialData->colors[colorType] = color;
}
Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType) {
return material->textures[textureType];
}
void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture) {
material->textures[textureType] = texture;
}

19
src/graphics/material.h Normal file
View File

@ -0,0 +1,19 @@
#include "util.h"
#include "graphics/texture.h"
#include "loaders/material.h"
#pragma once
typedef struct {
Ref ref;
MaterialData* materialData;
Texture* textures[MAX_MATERIAL_TEXTURES];
int isDefault;
} Material;
Material* lovrMaterialCreate(MaterialData* materialData, int isDefault);
void lovrMaterialDestroy(const Ref* ref);
Color lovrMaterialGetColor(Material* material, MaterialColor colorType);
void lovrMaterialSetColor(Material* material, MaterialColor colorType, Color color);
Texture* lovrMaterialGetTexture(Material* material, MaterialTexture textureType);
void lovrMaterialSetTexture(Material* material, MaterialTexture textureType, Texture* texture);

View File

@ -1,5 +1,6 @@
#include "graphics/mesh.h"
#include "graphics/graphics.h"
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
@ -43,7 +44,6 @@ Mesh* lovrMeshCreate(size_t count, MeshFormat* format, MeshDrawMode drawMode, Me
Mesh* mesh = lovrAlloc(sizeof(Mesh), lovrMeshDestroy);
if (!mesh) return NULL;
vec_init(&mesh->map);
vec_init(&mesh->format);
if (format) {
@ -79,10 +79,12 @@ Mesh* lovrMeshCreate(size_t count, MeshFormat* format, MeshDrawMode drawMode, Me
mesh->vao = 0;
mesh->vbo = 0;
mesh->ibo = 0;
mesh->indices = NULL;
mesh->indexCount = 0;
mesh->indexSize = count > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
mesh->isRangeEnabled = 0;
mesh->rangeStart = 0;
mesh->rangeCount = mesh->count;
mesh->texture = NULL;
mesh->lastShader = NULL;
glGenBuffers(1, &mesh->vbo);
@ -100,14 +102,11 @@ Mesh* lovrMeshCreate(size_t count, MeshFormat* format, MeshDrawMode drawMode, Me
void lovrMeshDestroy(const Ref* ref) {
Mesh* mesh = containerof(ref, Mesh);
if (mesh->texture) {
lovrRelease(&mesh->texture->ref);
}
glDeleteBuffers(1, &mesh->vbo);
glDeleteBuffers(1, &mesh->ibo);
glDeleteVertexArrays(1, &mesh->vao);
vec_deinit(&mesh->map);
vec_deinit(&mesh->format);
free(mesh->indices);
#ifdef EMSCRIPTEN
free(mesh->data);
#endif
@ -119,21 +118,28 @@ void lovrMeshDraw(Mesh* mesh, mat4 transform) {
lovrMeshUnmap(mesh);
}
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
lovrGraphicsBindTexture(mesh->texture);
if (transform) {
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
}
lovrGraphicsSetDefaultShader(SHADER_DEFAULT);
lovrGraphicsPrepare();
lovrGraphicsBindVertexArray(mesh->vao);
lovrMeshBindAttributes(mesh);
size_t start = mesh->rangeStart;
size_t count = mesh->rangeCount;
if (mesh->map.length > 0) {
glDrawElements(mesh->drawMode, mesh->map.length, GL_UNSIGNED_INT, (GLvoid*) start);
if (mesh->indexCount > 0) {
count = mesh->isRangeEnabled ? mesh->rangeCount : mesh->indexCount;
GLenum indexType = mesh->indexSize == sizeof(uint16_t) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
glDrawElements(mesh->drawMode, count, indexType, (GLvoid*) (start * mesh->indexSize));
} else {
glDrawArrays(mesh->drawMode, start, count);
}
lovrGraphicsPop();
if (transform) {
lovrGraphicsPop();
}
}
MeshFormat lovrMeshGetVertexFormat(Mesh* mesh) {
@ -144,9 +150,8 @@ MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh) {
return mesh->drawMode;
}
int lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode) {
void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode) {
mesh->drawMode = drawMode;
return 0;
}
int lovrMeshGetVertexCount(Mesh* mesh) {
@ -157,21 +162,22 @@ int lovrMeshGetVertexSize(Mesh* mesh) {
return mesh->stride;
}
unsigned int* lovrMeshGetVertexMap(Mesh* mesh, size_t* count) {
*count = mesh->map.length;
return mesh->map.data;
void* lovrMeshGetVertexMap(Mesh* mesh, size_t* count) {
*count = mesh->indexCount;
return mesh->indices;
}
void lovrMeshSetVertexMap(Mesh* mesh, unsigned int* map, size_t count) {
if (count == 0 || !map) {
vec_clear(&mesh->map);
} else {
vec_clear(&mesh->map);
vec_pusharr(&mesh->map, map, count);
lovrGraphicsBindVertexArray(mesh->vao);
lovrGraphicsBindIndexBuffer(mesh->ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(unsigned int), mesh->map.data, GL_STATIC_DRAW);
void lovrMeshSetVertexMap(Mesh* mesh, void* data, size_t count) {
if (!data || count == 0) {
mesh->indexCount = 0;
return;
}
mesh->indices = data;
mesh->indexCount = count;
lovrGraphicsBindVertexArray(mesh->vao);
lovrGraphicsBindIndexBuffer(mesh->ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->indexCount * mesh->indexSize, mesh->indices, GL_STATIC_DRAW);
}
int lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) {
@ -223,32 +229,12 @@ void lovrMeshGetDrawRange(Mesh* mesh, int* start, int* count) {
*count = mesh->rangeCount;
}
int lovrMeshSetDrawRange(Mesh* mesh, int start, int count) {
if (start < 0 || count < 0 || (size_t) start + count > mesh->count) {
return 1;
}
void lovrMeshSetDrawRange(Mesh* mesh, int start, int count) {
size_t limit = mesh->indexCount > 0 ? mesh->indexCount : mesh->count;
int isValidRange = start >= 0 && count >= 0 && (size_t) start + count <= limit;
lovrAssert(isValidRange, "Invalid mesh draw range [%d, %d]", start + 1, start + count + 1);
mesh->rangeStart = start;
mesh->rangeCount = count;
return 0;
}
Texture* lovrMeshGetTexture(Mesh* mesh) {
return mesh->texture;
}
void lovrMeshSetTexture(Mesh* mesh, Texture* texture) {
if (mesh->texture != texture) {
if (mesh->texture) {
lovrRelease(&mesh->texture->ref);
}
mesh->texture = texture;
if (mesh->texture) {
lovrRetain(&mesh->texture->ref);
}
}
}
void* lovrMeshMap(Mesh* mesh, int start, size_t count, int read, int write) {

View File

@ -49,11 +49,12 @@ typedef struct {
GLuint vao;
GLuint vbo;
GLuint ibo;
vec_uint_t map;
void* indices;
size_t indexCount;
size_t indexSize;
int isRangeEnabled;
int rangeStart;
int rangeCount;
Texture* texture;
Shader* lastShader;
} Mesh;
@ -62,17 +63,17 @@ void lovrMeshDestroy(const Ref* ref);
void lovrMeshDraw(Mesh* mesh, mat4 transform);
MeshFormat lovrMeshGetVertexFormat(Mesh* mesh);
MeshDrawMode lovrMeshGetDrawMode(Mesh* mesh);
int lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode);
void lovrMeshSetDrawMode(Mesh* mesh, MeshDrawMode drawMode);
int lovrMeshGetVertexCount(Mesh* mesh);
int lovrMeshGetVertexSize(Mesh* mesh);
unsigned int* lovrMeshGetVertexMap(Mesh* mesh, size_t* count);
void lovrMeshSetVertexMap(Mesh* mesh, unsigned int* map, size_t count);
void* lovrMeshGetVertexMap(Mesh* mesh, size_t* count);
void lovrMeshSetVertexMap(Mesh* mesh, void* data, size_t count);
int lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name);
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, int enabled);
int lovrMeshIsRangeEnabled(Mesh* mesh);
void lovrMeshSetRangeEnabled(Mesh* mesh, char isEnabled);
void lovrMeshGetDrawRange(Mesh* mesh, int* start, int* count);
int lovrMeshSetDrawRange(Mesh* mesh, int start, int count);
void lovrMeshSetDrawRange(Mesh* mesh, int start, int count);
Texture* lovrMeshGetTexture(Mesh* mesh);
void lovrMeshSetTexture(Mesh* mesh, Texture* texture);
void* lovrMeshMap(Mesh* mesh, int start, size_t count, int read, int write);

View File

@ -3,65 +3,31 @@
#include "math/mat4.h"
#include "math/vec3.h"
#include <stdlib.h>
#include <float.h>
static void visitNode(Model* model, ModelData* modelData, ModelNode* node, mat4 transform, vec_float_t* vertices, vec_uint_t* indices) {
float newTransform[16];
static void renderNode(Model* model, int nodeIndex) {
ModelNode* node = &model->modelData->nodes[nodeIndex];
Material* currentMaterial = lovrGraphicsGetMaterial();
int useMaterials = currentMaterial->isDefault;
if (transform) {
mat4_set(newTransform, transform);
} else {
mat4_identity(newTransform);
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, node->transform);
for (int i = 0; i < node->primitives.length; i++) {
ModelPrimitive* primitive = &model->modelData->primitives[node->primitives.data[i]];
if (useMaterials) {
lovrGraphicsSetMaterial(model->materials[primitive->material]);
}
lovrMeshSetDrawRange(model->mesh, primitive->drawStart, primitive->drawCount);
lovrMeshDraw(model->mesh, NULL);
}
mat4_multiply(newTransform, node->transform);
int indexOffset = vertices->length / 3;
// Meshes
for (int m = 0; m < node->meshes.length; m++) {
ModelMesh* mesh = modelData->meshes.data[node->meshes.data[m]];
// Transformed vertices
for (int v = 0; v < mesh->vertices.length; v++) {
ModelVertex vertex = mesh->vertices.data[v];
float vec[3] = { vertex.x, vertex.y, vertex.z };
mat4_transform(newTransform, vec);
vec_pusharr(vertices, vec, 3);
model->aabb[0] = MIN(model->aabb[0], vec[0]);
model->aabb[1] = MAX(model->aabb[1], vec[0]);
model->aabb[2] = MIN(model->aabb[2], vec[1]);
model->aabb[3] = MAX(model->aabb[3], vec[1]);
model->aabb[4] = MIN(model->aabb[4], vec[2]);
model->aabb[5] = MAX(model->aabb[5], vec[2]);
if (modelData->hasNormals) {
ModelVertex normal = mesh->normals.data[v];
vec_push(vertices, normal.x);
vec_push(vertices, normal.y);
vec_push(vertices, normal.z);
}
if (modelData->hasTexCoords) {
ModelVertex texCoord = mesh->texCoords.data[v];
vec_push(vertices, texCoord.x);
vec_push(vertices, texCoord.y);
}
}
// Face vertex indices
for (int f = 0; f < mesh->faces.length; f++) {
ModelFace face = mesh->faces.data[f];
vec_push(indices, face.indices[0] + indexOffset);
vec_push(indices, face.indices[1] + indexOffset);
vec_push(indices, face.indices[2] + indexOffset);
}
for (int i = 0; i < node->children.length; i++) {
renderNode(model, node->children.data[i]);
}
for (int c = 0; c < node->children.length; c++) {
visitNode(model, modelData, node->children.data[c], newTransform, vertices, indices);
lovrGraphicsPop();
if (useMaterials) {
lovrGraphicsSetMaterial(currentMaterial);
}
}
@ -70,85 +36,61 @@ Model* lovrModelCreate(ModelData* modelData) {
if (!model) return NULL;
model->modelData = modelData;
model->aabb[0] = FLT_MAX;
model->aabb[1] = FLT_MIN;
model->aabb[2] = FLT_MAX;
model->aabb[3] = FLT_MIN;
model->aabb[4] = FLT_MAX;
model->aabb[5] = FLT_MIN;
vec_float_t vertices;
vec_init(&vertices);
vec_uint_t indices;
vec_init(&indices);
visitNode(model, modelData, modelData->root, NULL, &vertices, &indices);
MeshFormat format;
vec_init(&format);
int components = 3;
MeshAttribute position = { .name = "lovrPosition", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, position);
MeshAttribute attribute = { .name = "lovrPosition", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, attribute);
if (modelData->hasNormals) {
MeshAttribute normal = { .name = "lovrNormal", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, normal);
components += 3;
MeshAttribute attribute = { .name = "lovrNormal", .type = MESH_FLOAT, .count = 3 };
vec_push(&format, attribute);
}
if (modelData->hasTexCoords) {
MeshAttribute texCoord = { .name = "lovrTexCoord", .type = MESH_FLOAT, .count = 2 };
vec_push(&format, texCoord);
components += 2;
if (modelData->hasUVs) {
MeshAttribute attribute = { .name = "lovrTexCoord", .type = MESH_FLOAT, .count = 2 };
vec_push(&format, attribute);
}
model->mesh = lovrMeshCreate(vertices.length / components, &format, MESH_TRIANGLES, MESH_STATIC);
void* data = lovrMeshMap(model->mesh, 0, vertices.length / components, 0, 1);
memcpy(data, vertices.data, vertices.length * sizeof(float));
model->mesh = lovrMeshCreate(modelData->vertexCount, &format, MESH_TRIANGLES, MESH_STATIC);
void* data = lovrMeshMap(model->mesh, 0, modelData->vertexCount, 0, 1);
memcpy(data, modelData->vertices, modelData->vertexCount * modelData->vertexSize * sizeof(float));
lovrMeshUnmap(model->mesh);
lovrMeshSetVertexMap(model->mesh, indices.data, indices.length);
lovrMeshSetVertexMap(model->mesh, modelData->indices, modelData->indexCount);
lovrMeshSetRangeEnabled(model->mesh, 1);
model->texture = NULL;
model->materials = malloc(modelData->materialCount * sizeof(Material*));
for (int i = 0; i < modelData->materialCount; i++) {
model->materials[i] = lovrMaterialCreate(&modelData->materials[i], 0);
}
vec_deinit(&format);
vec_deinit(&vertices);
vec_deinit(&indices);
return model;
}
void lovrModelDestroy(const Ref* ref) {
Model* model = containerof(ref, Model);
if (model->texture) {
lovrRelease(&model->texture->ref);
for (int i = 0; i < model->modelData->materialCount; i++) {
lovrRelease(&model->materials[i]->ref);
}
free(model->materials);
lovrModelDataDestroy(model->modelData);
lovrRelease(&model->mesh->ref);
free(model);
}
void lovrModelDraw(Model* model, mat4 transform) {
lovrMeshDraw(model->mesh, transform);
}
Texture* lovrModelGetTexture(Model* model) {
return model->texture;
}
void lovrModelSetTexture(Model* model, Texture* texture) {
if (model->texture) {
lovrRelease(&model->texture->ref);
if (model->modelData->nodeCount == 0) {
return;
}
model->texture = texture;
lovrMeshSetTexture(model->mesh, model->texture);
if (model->texture) {
lovrRetain(&model->texture->ref);
}
lovrGraphicsPush();
lovrGraphicsMatrixTransform(MATRIX_MODEL, transform);
renderNode(model, 0);
lovrGraphicsPop();
}
float* lovrModelGetAABB(Model* model) {
return model->aabb;
Mesh* lovrModelGetMesh(Model* model) {
return model->mesh;
}

View File

@ -1,4 +1,5 @@
#include "loaders/model.h"
#include "graphics/material.h"
#include "graphics/mesh.h"
#include "graphics/texture.h"
#include "math/math.h"
@ -11,13 +12,10 @@ typedef struct {
Ref ref;
ModelData* modelData;
Mesh* mesh;
Texture* texture;
float aabb[6];
Material** materials;
} Model;
Model* lovrModelCreate(ModelData* modelData);
void lovrModelDestroy(const Ref* ref);
void lovrModelDraw(Model* model, mat4 transform);
Texture* lovrModelGetTexture(Model* model);
void lovrModelSetTexture(Model* model, Texture* texture);
float* lovrModelGetAABB(Model* model);
Mesh* lovrModelGetMesh(Model* model);

View File

@ -1,90 +1,66 @@
#include "graphics/shader.h"
#include "graphics/shaders.h"
#include "graphics/graphics.h"
#include "math/mat4.h"
#include <stdio.h>
#include <stdlib.h>
static const char* lovrShaderVertexPrefix = ""
#ifdef EMSCRIPTEN
"#version 300 es \n"
"precision mediump float; \n"
#else
"#version 150 \n"
#endif
"in vec3 lovrPosition; \n"
"in vec3 lovrNormal; \n"
"in vec2 lovrTexCoord; \n"
"out vec2 texCoord; \n"
"uniform mat4 lovrModel; \n"
"uniform mat4 lovrView; \n"
"uniform mat4 lovrProjection; \n"
"uniform mat4 lovrTransform; \n"
"uniform mat3 lovrNormalMatrix; \n";
static UniformType getUniformType(GLenum type, const char* debug) {
switch (type) {
case GL_FLOAT:
case GL_FLOAT_VEC2:
case GL_FLOAT_VEC3:
case GL_FLOAT_VEC4:
return UNIFORM_FLOAT;
static const char* lovrShaderFragmentPrefix = ""
#ifdef EMSCRIPTEN
"#version 300 es \n"
"precision mediump float; \n"
#else
"#version 150 \n"
"in vec4 gl_FragCoord; \n"
#endif
"in vec2 texCoord; \n"
"out vec4 lovrFragColor; \n"
"uniform vec4 lovrColor; \n"
"uniform sampler2D lovrTexture; \n";
case GL_INT:
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
return UNIFORM_INT;
static const char* lovrShaderVertexSuffix = ""
"void main() { \n"
" texCoord = lovrTexCoord; \n"
" gl_Position = position(lovrProjection, lovrTransform, vec4(lovrPosition, 1.0)); \n"
"}";
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
return UNIFORM_MATRIX;
static const char* lovrShaderFragmentSuffix = ""
"void main() { \n"
" lovrFragColor = color(lovrColor, lovrTexture, texCoord); \n"
"}";
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
return UNIFORM_SAMPLER;
static const char* lovrDefaultVertexShader = ""
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
" return projection * transform * vertex; \n"
"}";
default:
lovrThrow("Unsupported type for uniform '%s'", debug);
return UNIFORM_FLOAT;
}
}
static const char* lovrDefaultFragmentShader = ""
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
" return graphicsColor * texture(image, uv); \n"
"}";
static int getUniformComponents(GLenum type) {
switch (type) {
case GL_FLOAT:
case GL_INT:
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
return 1;
static const char* lovrSkyboxVertexShader = ""
"out vec3 texturePosition; \n"
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
" texturePosition = vertex.xyz; \n"
" return projection * transform * vertex; \n"
"}";
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
case GL_FLOAT_MAT2:
return 2;
static const char* lovrSkyboxFragmentShader = ""
"in vec3 texturePosition; \n"
"uniform samplerCube cube; \n"
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
" return graphicsColor * texture(cube, texturePosition); \n"
"}";
case GL_FLOAT_VEC3:
case GL_INT_VEC3:
case GL_FLOAT_MAT3:
return 3;
static const char* lovrFontFragmentShader = ""
"float median(float r, float g, float b) { \n"
" return max(min(r, g), min(max(r, g), b)); \n"
"} \n"
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
" vec3 col = texture(image, uv).rgb; \n"
" float sdf = median(col.r, col.g, col.b); \n"
" float w = fwidth(sdf); \n"
" float alpha = smoothstep(.5 - w, .5 + w, sdf); \n"
" return vec4(graphicsColor.rgb, graphicsColor.a * alpha); \n"
"}";
case GL_FLOAT_VEC4:
case GL_INT_VEC4:
case GL_FLOAT_MAT4:
return 4;
static const char* lovrNoopVertexShader = ""
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
" return vertex; \n"
"}";
default:
return 1;
}
}
static GLuint compileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
@ -160,46 +136,103 @@ Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource) {
// Link
GLuint id = linkShaders(vertexShader, fragmentShader);
shader->id = id;
// Compute information about uniforms
lovrGraphicsBindProgram(id);
// Uniform introspection
GLint uniformCount;
int textureSlot = 0;
GLsizei bufferSize = LOVR_MAX_UNIFORM_LENGTH / sizeof(GLchar);
map_init(&shader->uniforms);
glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount);
for (int i = 0; i < uniformCount; i++) {
Uniform uniform;
glGetActiveUniform(id, i, bufferSize, NULL, &uniform.count, &uniform.type, uniform.name);
glGetActiveUniform(id, i, bufferSize, NULL, &uniform.count, &uniform.glType, uniform.name);
char* subscript = strchr(uniform.name, '[');
if (subscript) {
*subscript = '\0';
}
uniform.location = glGetUniformLocation(id, uniform.name);
uniform.index = i;
uniform.location = glGetUniformLocation(id, uniform.name);
uniform.type = getUniformType(uniform.glType, uniform.name);
uniform.components = getUniformComponents(uniform.glType);
uniform.baseTextureSlot = (uniform.type == UNIFORM_SAMPLER) ? textureSlot : -1;
switch (uniform.type) {
case UNIFORM_FLOAT:
uniform.size = uniform.components * uniform.count * sizeof(float);
uniform.value.data = malloc(uniform.size);
break;
case UNIFORM_INT:
uniform.size = uniform.components * uniform.count * sizeof(int);
uniform.value.data = malloc(uniform.size);
break;
case UNIFORM_MATRIX:
uniform.size = uniform.components * uniform.components * uniform.count * sizeof(int);
uniform.value.data = malloc(uniform.size);
break;
case UNIFORM_SAMPLER:
uniform.size = uniform.components * uniform.count * MAX(sizeof(Texture*), sizeof(int));
uniform.value.data = malloc(uniform.size);
// Use the value for ints to bind texture slots, but use the value for textures afterwards.
for (int i = 0; i < uniform.count; i++) {
uniform.value.ints[i] = uniform.baseTextureSlot + i;
}
glUniform1iv(uniform.location, uniform.count, uniform.value.ints);
break;
}
memset(uniform.value.data, 0, uniform.size);
size_t offset = 0;
for (int j = 0; j < uniform.count; j++) {
int location = uniform.location;
if (uniform.count > 1) {
char name[LOVR_MAX_UNIFORM_LENGTH];
snprintf(name, LOVR_MAX_UNIFORM_LENGTH, "%s[%d]", uniform.name, j);
location = glGetUniformLocation(id, name);
}
switch (uniform.type) {
case UNIFORM_FLOAT:
glGetUniformfv(id, location, &uniform.value.floats[offset]);
offset += uniform.components;
break;
case UNIFORM_INT:
glGetUniformiv(id, location, &uniform.value.ints[offset]);
offset += uniform.components;
break;
case UNIFORM_MATRIX:
glGetUniformfv(id, location, &uniform.value.floats[offset]);
offset += uniform.components * uniform.components;
break;
default:
break;
}
}
map_set(&shader->uniforms, uniform.name, uniform);
textureSlot += (uniform.type == UNIFORM_SAMPLER) ? uniform.count : 0;
}
// Initial state
shader->id = id;
mat4_identity(shader->model);
mat4_identity(shader->view);
mat4_identity(shader->projection);
shader->color = (Color) { 0, 0, 0, 0 };
// Send initial uniform values to shader
lovrGraphicsBindProgram(id);
lovrShaderBind(shader, shader->model, shader->view, shader->projection, shader->color, 1);
return shader;
}
Shader* lovrShaderCreateDefault(DefaultShader type) {
switch (type) {
case SHADER_DEFAULT: return lovrShaderCreate(NULL, NULL);
case SHADER_SKYBOX: {
Shader* shader = lovrShaderCreate(lovrSkyboxVertexShader, lovrSkyboxFragmentShader);
lovrShaderSendInt(shader, lovrShaderGetUniformId(shader, "cube"), 1);
return shader;
}
case SHADER_SKYBOX: return lovrShaderCreate(lovrSkyboxVertexShader, lovrSkyboxFragmentShader); break;
case SHADER_FONT: return lovrShaderCreate(NULL, lovrFontFragmentShader);
case SHADER_FULLSCREEN: return lovrShaderCreate(lovrNoopVertexShader, NULL);
default: lovrThrow("Unknown default shader type");
@ -213,64 +246,54 @@ void lovrShaderDestroy(const Ref* ref) {
free(shader);
}
void lovrShaderBind(Shader* shader, mat4 model, mat4 view, mat4 projection, Color color, int force) {
int dirtyModel = force || memcmp(shader->model, model, 16 * sizeof(float));
int dirtyView = force || memcmp(shader->view, view, 16 * sizeof(float));
int dirtyTransform = dirtyModel || dirtyView;
int dirtyProjection = force || memcmp(shader->projection, projection, 16 * sizeof(float));
int dirtyColor = force || memcmp(&shader->color, &color, sizeof(Color));
void lovrShaderBind(Shader* shader) {
map_iter_t iter = map_iter(&shader->uniforms);
const char* key;
while ((key = map_next(&shader->uniforms, &iter)) != NULL) {
Uniform* uniform = map_get(&shader->uniforms, key);
if (dirtyModel) {
int uniformId = lovrShaderGetUniformId(shader, "lovrModel");
lovrShaderSendFloatMat4(shader, uniformId, model);
memcpy(shader->model, model, 16 * sizeof(float));
}
if (dirtyView) {
int uniformId = lovrShaderGetUniformId(shader, "lovrView");
lovrShaderSendFloatMat4(shader, uniformId, view);
memcpy(shader->view, view, 16 * sizeof(float));
}
if (dirtyTransform) {
float matrix[16];
int uniformId = lovrShaderGetUniformId(shader, "lovrTransform");
mat4_multiply(mat4_set(matrix, view), model);
if (uniformId > -1) {
lovrShaderSendFloatMat4(shader, uniformId, matrix);
if (uniform->type != UNIFORM_SAMPLER && !uniform->dirty) {
continue;
}
if (mat4_invert(matrix)) {
mat4_transpose(matrix);
} else {
mat4_identity(matrix);
uniform->dirty = 0;
int count = uniform->count;
void* data = uniform->value.data;
switch (uniform->type) {
case UNIFORM_FLOAT:
switch (uniform->components) {
case 1: glUniform1fv(uniform->location, count, data); break;
case 2: glUniform2fv(uniform->location, count, data); break;
case 3: glUniform3fv(uniform->location, count, data); break;
case 4: glUniform4fv(uniform->location, count, data); break;
}
break;
case UNIFORM_INT:
switch (uniform->components) {
case 1: glUniform1iv(uniform->location, count, data); break;
case 2: glUniform2iv(uniform->location, count, data); break;
case 3: glUniform3iv(uniform->location, count, data); break;
case 4: glUniform4iv(uniform->location, count, data); break;
}
break;
case UNIFORM_MATRIX:
switch (uniform->components) {
case 2: glUniformMatrix2fv(uniform->location, count, GL_FALSE, data); break;
case 3: glUniformMatrix3fv(uniform->location, count, GL_FALSE, data); break;
case 4: glUniformMatrix4fv(uniform->location, count, GL_FALSE, data); break;
}
break;
case UNIFORM_SAMPLER:
for (int i = 0; i < count; i++) {
TextureType type = uniform->glType == GL_SAMPLER_2D ? TEXTURE_2D : TEXTURE_CUBE;
lovrGraphicsBindTexture(uniform->value.textures[i], type, uniform->baseTextureSlot + i);
}
break;
}
float normalMatrix[9] = {
matrix[0], matrix[1], matrix[2],
matrix[4], matrix[5], matrix[6],
matrix[8], matrix[9], matrix[10]
};
uniformId = lovrShaderGetUniformId(shader, "lovrNormalMatrix");
if (uniformId > -1) {
lovrShaderSendFloatMat3(shader, uniformId, normalMatrix);
}
}
if (dirtyProjection) {
int uniformId = lovrShaderGetUniformId(shader, "lovrProjection");
lovrShaderSendFloatMat4(shader, uniformId, projection);
memcpy(shader->projection, projection, 16 * sizeof(float));
}
if (dirtyColor) {
int uniformId = lovrShaderGetUniformId(shader, "lovrColor");
float c[4] = { color.r / 255., color.g / 255., color.b / 255., color.a / 255. };
lovrShaderSendFloatVec4(shader, uniformId, 1, c);
shader->color = color;
}
}
@ -282,51 +305,39 @@ int lovrShaderGetAttributeId(Shader* shader, const char* name) {
return glGetAttribLocation(shader->id, name);
}
int lovrShaderGetUniformId(Shader* shader, const char* name) {
Uniform* uniform = map_get(&shader->uniforms, name);
return uniform ? uniform->location : -1;
Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
return map_get(&shader->uniforms, name);
}
int lovrShaderGetUniformType(Shader* shader, const char* name, GLenum* type, int* count) {
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int count, size_t size, const char* debug) {
Uniform* uniform = map_get(&shader->uniforms, name);
if (!uniform) {
return 1;
return;
}
*type = uniform->type;
*count = uniform->count;
return 0;
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, uniform->name);
lovrAssert(count * size == uniform->size, "Expected %d %ss for uniform %s, got %d", uniform->count, debug, uniform->name, count);
if (!uniform->dirty && !memcmp(uniform->value.data, data, count * size)) {
return;
}
memcpy(uniform->value.data, data, count * size);
uniform->dirty = 1;
}
void lovrShaderSendInt(Shader* shader, int id, int value) {
glUniform1i(id, value);
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_FLOAT, data, count, sizeof(float), "float");
}
void lovrShaderSendFloat(Shader* shader, int id, float value) {
glUniform1f(id, value);
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_INT, data, count, sizeof(int), "int");
}
void lovrShaderSendFloatVec2(Shader* shader, int id, int count, float* vector) {
glUniform2fv(id, count, vector);
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_MATRIX, data, count, sizeof(float), "float");
}
void lovrShaderSendFloatVec3(Shader* shader, int id, int count, float* vector) {
glUniform3fv(id, count, vector);
}
void lovrShaderSendFloatVec4(Shader* shader, int id, int count, float* vector) {
glUniform4fv(id, count, vector);
}
void lovrShaderSendFloatMat2(Shader* shader, int id, float* matrix) {
glUniformMatrix2fv(id, 1, GL_FALSE, matrix);
}
void lovrShaderSendFloatMat3(Shader* shader, int id, float* matrix) {
glUniformMatrix3fv(id, 1, GL_FALSE, matrix);
}
void lovrShaderSendFloatMat4(Shader* shader, int id, float* matrix) {
glUniformMatrix4fv(id, 1, GL_FALSE, matrix);
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count) {
lovrShaderSetUniform(shader, name, UNIFORM_SAMPLER, data, count, sizeof(Texture*), "texture");
}

View File

@ -1,3 +1,4 @@
#include "graphics/texture.h"
#include "math/math.h"
#include "lib/map/map.h"
#include "lib/glfw.h"
@ -10,6 +11,20 @@
#define LOVR_SHADER_TEX_COORD 2
#define LOVR_MAX_UNIFORM_LENGTH 256
typedef enum {
UNIFORM_FLOAT,
UNIFORM_MATRIX,
UNIFORM_INT,
UNIFORM_SAMPLER
} UniformType;
typedef union {
void* data;
int* ints;
float* floats;
Texture** textures;
} UniformValue;
typedef enum {
SHADER_DEFAULT,
SHADER_SKYBOX,
@ -19,10 +34,16 @@ typedef enum {
typedef struct {
GLchar name[LOVR_MAX_UNIFORM_LENGTH];
GLenum glType;
int index;
int location;
GLenum type;
int count;
int components;
size_t size;
UniformType type;
UniformValue value;
int baseTextureSlot;
int dirty;
} Uniform;
typedef map_t(Uniform) map_uniform_t;
@ -40,15 +61,10 @@ typedef struct {
Shader* lovrShaderCreate(const char* vertexSource, const char* fragmentSource);
Shader* lovrShaderCreateDefault(DefaultShader type);
void lovrShaderDestroy(const Ref* ref);
void lovrShaderBind(Shader* shader, mat4 model, mat4 view, mat4 projection, Color color, int force);
void lovrShaderBind(Shader* shader);
int lovrShaderGetAttributeId(Shader* shader, const char* name);
int lovrShaderGetUniformId(Shader* shader, const char* name);
int lovrShaderGetUniformType(Shader* shader, const char* name, GLenum* type, int* count);
void lovrShaderSendInt(Shader* shader, int id, int value);
void lovrShaderSendFloat(Shader* shader, int id, float value);
void lovrShaderSendFloatVec2(Shader* shader, int id, int count, float* vector);
void lovrShaderSendFloatVec3(Shader* shader, int id, int count,float* vector);
void lovrShaderSendFloatVec4(Shader* shader, int id, int count, float* vector);
void lovrShaderSendFloatMat2(Shader* shader, int id, float* matrix);
void lovrShaderSendFloatMat3(Shader* shader, int id, float* matrix);
void lovrShaderSendFloatMat4(Shader* shader, int id, float* matrix);
Uniform* lovrShaderGetUniform(Shader* shader, const char* name);
void lovrShaderSetFloat(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetInt(Shader* shader, const char* name, int* data, int count);
void lovrShaderSetMatrix(Shader* shader, const char* name, float* data, int count);
void lovrShaderSetTexture(Shader* shader, const char* name, Texture** data, int count);

93
src/graphics/shaders.c Normal file
View File

@ -0,0 +1,93 @@
#include "graphics/shaders.h"
const char* lovrShaderColorUniforms[] = {
"lovrDiffuseColor"
};
const char* lovrShaderTextureUniforms[] = {
"lovrDiffuseTexture",
"lovrEnvironmentTexture"
};
const char* lovrShaderVertexPrefix = ""
#ifdef EMSCRIPTEN
"#version 300 es \n"
"precision mediump float; \n"
#else
"#version 150 \n"
#endif
"in vec3 lovrPosition; \n"
"in vec3 lovrNormal; \n"
"in vec2 lovrTexCoord; \n"
"out vec2 texCoord; \n"
"uniform mat4 lovrModel; \n"
"uniform mat4 lovrView; \n"
"uniform mat4 lovrProjection; \n"
"uniform mat4 lovrTransform; \n"
"uniform mat3 lovrNormalMatrix; \n";
const char* lovrShaderFragmentPrefix = ""
#ifdef EMSCRIPTEN
"#version 300 es \n"
"precision mediump float; \n"
#else
"#version 150 \n"
"in vec4 gl_FragCoord; \n"
#endif
"in vec2 texCoord; \n"
"out vec4 lovrFragColor; \n"
"uniform vec4 lovrColor; \n"
"uniform vec4 lovrDiffuseColor; \n"
"uniform sampler2D lovrDiffuseTexture; \n"
"uniform samplerCube lovrEnvironmentTexture; \n";
const char* lovrShaderVertexSuffix = ""
"void main() { \n"
" texCoord = lovrTexCoord; \n"
" gl_Position = position(lovrProjection, lovrTransform, vec4(lovrPosition, 1.0)); \n"
"}";
const char* lovrShaderFragmentSuffix = ""
"void main() { \n"
" lovrFragColor = color(lovrColor, lovrDiffuseTexture, texCoord); \n"
"}";
const char* lovrDefaultVertexShader = ""
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
" return projection * transform * vertex; \n"
"}";
const char* lovrDefaultFragmentShader = ""
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
" return graphicsColor * lovrDiffuseColor * texture(image, uv); \n"
"}";
const char* lovrSkyboxVertexShader = ""
"out vec3 texturePosition; \n"
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
" texturePosition = vertex.xyz; \n"
" return projection * transform * vertex; \n"
"}";
const char* lovrSkyboxFragmentShader = ""
"in vec3 texturePosition; \n"
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
" return graphicsColor * texture(lovrEnvironmentTexture, texturePosition); \n"
"}";
const char* lovrFontFragmentShader = ""
"float median(float r, float g, float b) { \n"
" return max(min(r, g), min(max(r, g), b)); \n"
"} \n"
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
" vec3 col = texture(image, uv).rgb; \n"
" float sdf = median(col.r, col.g, col.b); \n"
" float w = fwidth(sdf); \n"
" float alpha = smoothstep(.5 - w, .5 + w, sdf); \n"
" return vec4(graphicsColor.rgb, graphicsColor.a * alpha); \n"
"}";
const char* lovrNoopVertexShader = ""
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
" return vertex; \n"
"}";

14
src/graphics/shaders.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
extern const char* lovrShaderColorUniforms[];
extern const char* lovrShaderTextureUniforms[];
extern const char* lovrShaderVertexPrefix;
extern const char* lovrShaderVertexSuffix;
extern const char* lovrShaderFragmentPrefix;
extern const char* lovrShaderFragmentSuffix;
extern const char* lovrDefaultVertexShader;
extern const char* lovrDefaultFragmentShader;
extern const char* lovrSkyboxVertexShader;
extern const char* lovrSkyboxFragmentShader;
extern const char* lovrFontFragmentShader;
extern const char* lovrNoopVertexShader;

View File

@ -1,56 +0,0 @@
#include "graphics/skybox.h"
#include "lib/stb/stb_image.h"
#include <stdlib.h>
Skybox* lovrSkyboxCreate(Blob** blobs, SkyboxType type) {
Skybox* skybox = lovrAlloc(sizeof(Skybox), lovrSkyboxDestroy);
if (!skybox) return NULL;
skybox->type = type;
GLenum binding;
int count;
if (type == SKYBOX_CUBE) {
binding = GL_TEXTURE_CUBE_MAP;
count = 6;
} else {
binding = GL_TEXTURE_2D;
count = 1;
}
glGenTextures(1, &skybox->texture);
glBindTexture(binding, skybox->texture);
for (int i = 0; i < count; i++) {
int width, height, channels;
stbi_set_flip_vertically_on_load(0);
unsigned char* image = stbi_load_from_memory(blobs[i]->data, blobs[i]->size, &width, &height, &channels, 3);
lovrAssert(image, "Could not load skybox image %d", i);
if (type == SKYBOX_CUBE) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
}
free(image);
}
glTexParameteri(binding, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(binding, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(binding, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(binding, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (type == SKYBOX_CUBE) {
glTexParameteri(binding, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
return skybox;
}
void lovrSkyboxDestroy(const Ref* ref) {
Skybox* skybox = containerof(ref, Skybox);
glDeleteTextures(1, &skybox->texture);
free(skybox);
}

View File

@ -1,19 +0,0 @@
#include "filesystem/blob.h"
#include "lib/glad/glad.h"
#include "util.h"
#pragma once
typedef enum {
SKYBOX_CUBE,
SKYBOX_PANORAMA
} SkyboxType;
typedef struct {
Ref ref;
SkyboxType type;
GLuint texture;
} Skybox;
Skybox* lovrSkyboxCreate(Blob** blobs, SkyboxType type);
void lovrSkyboxDestroy(const Ref* ref);

View File

@ -6,9 +6,9 @@
#include <stdio.h>
static void lovrTextureCreateStorage(Texture* texture) {
TextureData* textureData = texture->textureData;
TextureData* textureData = texture->slices[0];
if (textureData->format.compressed || !textureData->mipmaps.generated) {
if (textureData->format.compressed || !textureData->mipmaps.generated || texture->type == TEXTURE_CUBE) {
return;
}
@ -32,30 +32,50 @@ static void lovrTextureCreateStorage(Texture* texture) {
#endif
}
Texture* lovrTextureCreate(TextureData* textureData) {
static void validateSlices(TextureType type, TextureData* slices[6], int sliceCount) {
if (type == TEXTURE_CUBE) {
lovrAssert(sliceCount == 6, "Cube textures must have 6 images");
int width = slices[0]->width;
int height = slices[0]->height;
lovrAssert(width == height, "Cube textures must be square");
for (int i = 1; i < sliceCount; i++) {
int hasSameDimensions = slices[i]->width == width && slices[i]->height == height;
lovrAssert(hasSameDimensions, "All textures in a cube texture must have the same dimensions");
}
} else if (type == TEXTURE_2D) {
lovrAssert(sliceCount == 1, "2D textures can only contain a single image");
} else {
lovrThrow("Unknown texture type");
}
}
Texture* lovrTextureCreate(TextureType type, TextureData* slices[6], int sliceCount) {
Texture* texture = lovrAlloc(sizeof(Texture), lovrTextureDestroy);
if (!texture) return NULL;
texture->type = type;
validateSlices(type, slices, sliceCount);
texture->sliceCount = sliceCount;
memcpy(texture->slices, slices, sliceCount * sizeof(TextureData*));
texture->framebuffer = 0;
texture->depthBuffer = 0;
texture->textureData = textureData;
glGenTextures(1, &texture->id);
lovrGraphicsBindTexture(texture);
lovrGraphicsBindTexture(texture, type, 0);
lovrTextureCreateStorage(texture);
lovrTextureRefresh(texture);
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
lovrTextureSetWrap(texture, WRAP_REPEAT, WRAP_REPEAT);
WrapMode wrapMode = (type == TEXTURE_CUBE) ? WRAP_CLAMP : WRAP_REPEAT;
lovrTextureSetWrap(texture, (TextureWrap) { .s = wrapMode, .t = wrapMode, .r = wrapMode });
return texture;
}
Texture* lovrTextureCreateWithFramebuffer(TextureData* textureData, TextureProjection projection, int msaa) {
Texture* texture = lovrTextureCreate(textureData);
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1);
if (!texture) return NULL;
int width = texture->textureData->width;
int height = texture->textureData->height;
int width = texture->width;
int height = texture->height;
texture->projection = projection;
texture->msaa = msaa;
@ -102,7 +122,9 @@ Texture* lovrTextureCreateWithFramebuffer(TextureData* textureData, TextureProje
void lovrTextureDestroy(const Ref* ref) {
Texture* texture = containerof(ref, Texture);
lovrTextureDataDestroy(texture->textureData);
for (int i = 0; i < texture->sliceCount; i++) {
lovrTextureDataDestroy(texture->slices[i]);
}
if (texture->framebuffer) {
glDeleteFramebuffers(1, &texture->framebuffer);
}
@ -112,23 +134,19 @@ void lovrTextureDestroy(const Ref* ref) {
void lovrTextureBindFramebuffer(Texture* texture) {
lovrAssert(texture->framebuffer, "Texture cannot be used as a canvas");
int w = texture->textureData->width;
int h = texture->textureData->height;
lovrGraphicsBindFramebuffer(texture->framebuffer);
lovrGraphicsSetViewport(0, 0, w, h);
lovrGraphicsSetViewport(0, 0, texture->width, texture->height);
if (texture->projection == PROJECTION_ORTHOGRAPHIC) {
float projection[16];
mat4_orthographic(projection, 0, w, 0, h, -1, 1);
mat4_orthographic(projection, 0, texture->width, 0, texture->height, -1, 1);
lovrGraphicsSetProjection(projection);
} else if (texture->projection == PROJECTION_PERSPECTIVE) {
mat4 projection = lovrGraphicsGetProjection();
float b = projection[5];
float c = projection[10];
float d = projection[14];
float aspect = (float) w / h;
float aspect = (float) texture->width / texture->height;
float k = (c - 1.f) / (c + 1.f);
float near = (d * (1.f - k)) / (2.f * k);
float far = k * near;
@ -145,53 +163,52 @@ void lovrTextureResolveMSAA(Texture* texture) {
return;
}
int w = texture->textureData->width;
int h = texture->textureData->height;
int width = texture->width;
int height = texture->height;
glBindFramebuffer(GL_READ_FRAMEBUFFER, texture->framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, texture->resolveFramebuffer);
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
void lovrTextureRefresh(Texture* texture) {
TextureData* textureData = texture->textureData;
GLenum glInternalFormat = textureData->format.glInternalFormat;
GLenum glFormat = textureData->format.glFormat;
lovrGraphicsBindTexture(texture);
lovrGraphicsBindTexture(texture, texture->type, 0);
if (textureData->format.compressed) {
Mipmap m; int i;
vec_foreach(&textureData->mipmaps.list, m, i) {
glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, m.width, m.height, 0, m.size, m.data);
}
} else {
int w = textureData->width;
int h = textureData->height;
glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, textureData->data);
if (textureData->mipmaps.generated) {
glGenerateMipmap(GL_TEXTURE_2D);
validateSlices(texture->type, texture->slices, texture->sliceCount);
texture->width = texture->slices[0]->width;
texture->height = texture->slices[0]->height;
for (int i = 0; i < texture->sliceCount; i++) {
TextureData* textureData = texture->slices[i];
GLenum glInternalFormat = textureData->format.glInternalFormat;
GLenum glFormat = textureData->format.glFormat;
GLenum binding = (texture->type == TEXTURE_CUBE) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + i : GL_TEXTURE_2D;
if (textureData->format.compressed) {
Mipmap m; int i;
vec_foreach(&textureData->mipmaps.list, m, i) {
glCompressedTexImage2D(binding, i, glInternalFormat, m.width, m.height, 0, m.size, m.data);
}
} else {
int w = textureData->width;
int h = textureData->height;
glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, textureData->data);
if (textureData->mipmaps.generated) {
glGenerateMipmap(GL_TEXTURE_2D); // TODO
}
}
}
}
int lovrTextureGetHeight(Texture* texture) {
return texture->textureData->height;
}
int lovrTextureGetWidth(Texture* texture) {
return texture->textureData->width;
}
TextureFilter lovrTextureGetFilter(Texture* texture) {
return texture->filter;
}
void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
int hasMipmaps = texture->textureData->format.compressed || texture->textureData->mipmaps.generated;
int hasMipmaps = texture->slices[0]->format.compressed || texture->slices[0]->mipmaps.generated;
float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.;
lovrGraphicsBindTexture(texture);
lovrGraphicsBindTexture(texture, texture->type, 0);
texture->filter = filter;
switch (filter.mode) {
@ -225,15 +242,16 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
void lovrTextureGetWrap(Texture* texture, WrapMode* horizontal, WrapMode* vertical) {
*horizontal = texture->wrapHorizontal;
*vertical = texture->wrapVertical;
TextureWrap lovrTextureGetWrap(Texture* texture) {
return texture->wrap;
}
void lovrTextureSetWrap(Texture* texture, WrapMode horizontal, WrapMode vertical) {
texture->wrapHorizontal = horizontal;
texture->wrapVertical = vertical;
lovrGraphicsBindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, horizontal);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vertical);
void lovrTextureSetWrap(Texture* texture, TextureWrap wrap) {
texture->wrap = wrap;
lovrGraphicsBindTexture(texture, texture->type, 0);
glTexParameteri(texture->type, GL_TEXTURE_WRAP_S, wrap.s);
glTexParameteri(texture->type, GL_TEXTURE_WRAP_T, wrap.t);
if (texture->type == TEXTURE_CUBE) {
glTexParameteri(texture->type, GL_TEXTURE_WRAP_R, wrap.r);
}
}

View File

@ -4,6 +4,11 @@
#pragma once
typedef enum {
TEXTURE_2D = GL_TEXTURE_2D,
TEXTURE_CUBE = GL_TEXTURE_CUBE_MAP
} TextureType;
typedef enum {
FILTER_NEAREST,
FILTER_BILINEAR,
@ -22,6 +27,12 @@ typedef enum {
WRAP_MIRRORED_REPEAT = GL_MIRRORED_REPEAT
} WrapMode;
typedef struct {
WrapMode s;
WrapMode t;
WrapMode r;
} TextureWrap;
typedef enum {
PROJECTION_ORTHOGRAPHIC,
PROJECTION_PERSPECTIVE
@ -29,7 +40,11 @@ typedef enum {
typedef struct {
Ref ref;
TextureData* textureData;
TextureType type;
TextureData* slices[6];
int sliceCount;
int width;
int height;
GLuint id;
GLuint msaaId;
GLuint framebuffer;
@ -37,22 +52,19 @@ typedef struct {
GLuint depthBuffer;
TextureProjection projection;
TextureFilter filter;
WrapMode wrapHorizontal;
WrapMode wrapVertical;
TextureWrap wrap;
int msaa;
} Texture;
GLenum lovrTextureGetGLFormat(TextureFormat format);
Texture* lovrTextureCreate(TextureData* textureData);
Texture* lovrTextureCreate(TextureType type, TextureData* data[6], int count);
Texture* lovrTextureCreateWithFramebuffer(TextureData* textureData, TextureProjection projection, int msaa);
void lovrTextureDestroy(const Ref* ref);
void lovrTextureBindFramebuffer(Texture* texture);
void lovrTextureResolveMSAA(Texture* texture);
void lovrTextureRefresh(Texture* texture);
int lovrTextureGetHeight(Texture* texture);
int lovrTextureGetWidth(Texture* texture);
TextureFilter lovrTextureGetFilter(Texture* texture);
void lovrTextureSetFilter(Texture* texture, TextureFilter filter);
void lovrTextureGetWrap(Texture* texture, WrapMode* horizontal, WrapMode* vertical);
void lovrTextureSetWrap(Texture* texture, WrapMode horizontal, WrapMode vertical);
TextureWrap lovrTextureGetWrap(Texture* texture);
void lovrTextureSetWrap(Texture* texture, TextureWrap wrap);

View File

@ -383,10 +383,6 @@ static ModelData* fakeControllerNewModelData(Controller* controller) {
return NULL;
}
static TextureData* fakeControllerNewTextureData(Controller* controller) {
return NULL;
}
static void fakeRenderTo(headsetRenderCallback callback, void* userdata) {
// float head[16], transform[16], projection[16];
@ -491,7 +487,6 @@ HeadsetImpl lovrHeadsetFakeDriver = {
fakeControllerIsTouched,
fakeControllerVibrate,
fakeControllerNewModelData,
fakeControllerNewTextureData,
fakeRenderTo,
fakeUpdate,
};

View File

@ -229,13 +229,6 @@ ModelData* lovrHeadsetControllerNewModelData(Controller* controller)
}
}
TextureData* lovrHeadsetControllerNewTextureData(Controller* controller) {
if( headset && controller) {
return headset->controllerNewTextureData(controller);
} else {
return NULL;
}
}
void lovrHeadsetRenderTo(headsetRenderCallback callback, void* userdata) {
if (headset) {

View File

@ -90,7 +90,6 @@ typedef struct {
int (*controllerIsTouched)(Controller* controller, ControllerButton button);
void (*controllerVibrate)(Controller* controller, float duration, float power);
ModelData* (*controllerNewModelData)(Controller* controller);
TextureData* (*controllerNewTextureData)(Controller* controller);
void (*renderTo)(headsetRenderCallback callback, void* userdata);
void (*update)(float dt);
} HeadsetImpl;
@ -130,7 +129,6 @@ int lovrHeadsetControllerIsDown(Controller* controller, ControllerButton button)
int lovrHeadsetControllerIsTouched(Controller* controller, ControllerButton button);
void lovrHeadsetControllerVibrate(Controller* controller, float duration, float power);
ModelData* lovrHeadsetControllerNewModelData(Controller* controller);
TextureData* lovrHeadsetControllerNewTextureData(Controller* controller);
void lovrHeadsetRenderTo(headsetRenderCallback callback, void* userdata);
void lovrHeadsetUpdate(float dt);

View File

@ -656,77 +656,64 @@ static ModelData* openvrControllerNewModelData(Controller* controller) {
}
}
RenderModel_t* vrModel = state.deviceModels[id];
ModelData* modelData = malloc(sizeof(ModelData));
if (!modelData) return NULL;
ModelMesh* mesh = malloc(sizeof(ModelMesh));
vec_init(&modelData->meshes);
vec_push(&modelData->meshes, mesh);
vec_init(&mesh->faces);
for (uint32_t i = 0; i < vrModel->unTriangleCount; i++) {
ModelFace face;
face.indices[0] = vrModel->rIndexData[3 * i + 0];
face.indices[1] = vrModel->rIndexData[3 * i + 1];
face.indices[2] = vrModel->rIndexData[3 * i + 2];
vec_push(&mesh->faces, face);
}
vec_init(&mesh->vertices);
vec_init(&mesh->normals);
vec_init(&mesh->texCoords);
for (size_t i = 0; i < vrModel->unVertexCount; i++) {
float* position = vrModel->rVertexData[i].vPosition.v;
float* normal = vrModel->rVertexData[i].vNormal.v;
float* texCoords = vrModel->rVertexData[i].rfTextureCoord;
ModelVertex v;
v.x = position[0];
v.y = position[1];
v.z = position[2];
vec_push(&mesh->vertices, v);
v.x = normal[0];
v.y = normal[1];
v.z = normal[2];
vec_push(&mesh->normals, v);
v.x = texCoords[0];
v.y = texCoords[1];
v.z = 0.f;
vec_push(&mesh->texCoords, v);
}
ModelNode* root = malloc(sizeof(ModelNode));
vec_init(&root->meshes);
vec_push(&root->meshes, 0);
vec_init(&root->children);
mat4_identity(root->transform);
modelData->root = root;
modelData->hasNormals = 1;
modelData->hasTexCoords = 1;
return modelData;
}
static TextureData* openvrControllerNewTextureData(Controller* controller) {
if (!state.isInitialized || !controller) return NULL;
int id = controller->id;
if (!state.deviceModels[id]) {
openvrControllerNewModelData(controller);
}
// Load texture
if (!state.deviceTextures[id]) {
while (state.renderModels->LoadTexture_Async(state.deviceModels[id]->diffuseTextureId, &state.deviceTextures[id]) == EVRRenderModelError_VRRenderModelError_Loading) {
lovrSleep(.001);
}
}
RenderModel_t* vrModel = state.deviceModels[id];
ModelData* modelData = malloc(sizeof(ModelData));
if (!modelData) return NULL;
modelData->indexCount = vrModel->unTriangleCount;
modelData->indices = malloc(modelData->indexCount * sizeof(unsigned int));
memcpy(modelData->indices, vrModel->rIndexData, modelData->indexCount * sizeof(unsigned int));
modelData->vertexCount = vrModel->unVertexCount;
modelData->vertexSize = 8;
modelData->vertices = malloc(modelData->vertexCount * modelData->vertexSize * sizeof(float));
int vertex = 0;
for (size_t i = 0; i < vrModel->unVertexCount; i++) {
float* position = vrModel->rVertexData[i].vPosition.v;
float* normal = vrModel->rVertexData[i].vNormal.v;
float* texCoords = vrModel->rVertexData[i].rfTextureCoord;
modelData->vertices[vertex++] = position[0];
modelData->vertices[vertex++] = position[1];
modelData->vertices[vertex++] = position[2];
modelData->vertices[vertex++] = normal[0];
modelData->vertices[vertex++] = normal[1];
modelData->vertices[vertex++] = normal[2];
modelData->vertices[vertex++] = texCoords[0];
modelData->vertices[vertex++] = texCoords[1];
}
modelData->nodeCount = 1;
modelData->primitiveCount = 1;
modelData->materialCount = 1;
modelData->nodes = malloc(1 * sizeof(ModelNode));
modelData->primitives = malloc(1 * sizeof(ModelPrimitive));
modelData->materials = malloc(1 * sizeof(MaterialData));
// Geometry
ModelNode* root = &modelData->nodes[0];
root->parent = -1;
mat4_identity(root->transform);
vec_init(&root->children);
vec_init(&root->primitives);
vec_push(&root->primitives, 0);
modelData->primitives[0].material = 0;
modelData->primitives[0].drawStart = 0;
modelData->primitives[0].drawCount = modelData->vertexCount;
// Material
RenderModel_TextureMap_t* vrTexture = state.deviceTextures[id];
TextureData* textureData = malloc(sizeof(TextureData));
@ -743,7 +730,14 @@ static TextureData* openvrControllerNewTextureData(Controller* controller) {
textureData->data = memcpy(malloc(size), vrTexture->rubTextureMapData, size);;
textureData->mipmaps.generated = 1;
textureData->blob = NULL;
return textureData;
modelData->materials[0] = *lovrMaterialDataCreateEmpty();
modelData->materials[0].textures[TEXTURE_DIFFUSE] = textureData;
modelData->hasNormals = 1;
modelData->hasUVs = 1;
return modelData;
}
static void openvrRenderTo(headsetRenderCallback callback, void* userdata) {
@ -758,7 +752,7 @@ static void openvrRenderTo(headsetRenderCallback callback, void* userdata) {
float head[16], transform[16], projection[16];
float (*matrix)[4];
lovrGraphicsPushCanvas();
lovrGraphicsPushView();
state.isRendering = 1;
state.compositor->WaitGetPoses(state.renderPoses, 16, NULL, 0);
@ -789,7 +783,8 @@ static void openvrRenderTo(headsetRenderCallback callback, void* userdata) {
lovrTextureResolveMSAA(state.texture);
// OpenVR changes the OpenGL texture binding, so we reset it after rendering
Texture* oldTexture = lovrGraphicsGetTexture();
glActiveTexture(GL_TEXTURE0);
Texture* oldTexture = lovrGraphicsGetTexture(0);
// Submit
uintptr_t texture = (uintptr_t) state.texture->id;
@ -803,7 +798,7 @@ static void openvrRenderTo(headsetRenderCallback callback, void* userdata) {
}
state.isRendering = 0;
lovrGraphicsPopCanvas();
lovrGraphicsPopView();
if (state.isMirrored) {
Color oldColor = lovrGraphicsGetColor();
@ -861,7 +856,6 @@ HeadsetImpl lovrHeadsetOpenVRDriver = {
openvrControllerIsTouched,
openvrControllerVibrate,
openvrControllerNewModelData,
openvrControllerNewTextureData,
openvrRenderTo,
openvrUpdate,
};

View File

@ -249,10 +249,6 @@ static ModelData* webvrControllerNewModelData(Controller* controller) {
return NULL;
}
static TextureData* webvrControllerNewTextureData(Controller* controller) {
return NULL;
}
static void webvrRenderTo(headsetRenderCallback callback, void* userdata) {
state.renderCallback = callback;
emscripten_vr_set_render_callback(onRequestAnimationFrame, userdata);
@ -293,7 +289,6 @@ HeadsetImpl lovrHeadsetWebVRDriver = {
webvrControllerIsTouched,
webvrControllerVibrate,
webvrControllerNewModelData,
webvrControllerNewTextureData,
webvrRenderTo,
webvrUpdate,
};

19
src/loaders/material.c Normal file
View File

@ -0,0 +1,19 @@
#include "loaders/material.h"
MaterialData* lovrMaterialDataCreateEmpty() {
MaterialData* materialData = malloc(sizeof(MaterialData));
for (int i = 0; i < MAX_MATERIAL_COLORS; i++) {
materialData->colors[i] = (Color) { 0xff, 0xff, 0xff, 0xff };
}
for (int i = 0; i < MAX_MATERIAL_TEXTURES; i++) {
materialData->textures[i] = NULL;
}
return materialData;
}
void lovrMaterialDataDestroy(MaterialData* materialData) {
free(materialData);
}

22
src/loaders/material.h Normal file
View File

@ -0,0 +1,22 @@
#include "loaders/texture.h"
#pragma once
typedef enum {
COLOR_DIFFUSE,
MAX_MATERIAL_COLORS
} MaterialColor;
typedef enum {
TEXTURE_DIFFUSE,
TEXTURE_ENVIRONMENT_MAP,
MAX_MATERIAL_TEXTURES
} MaterialTexture;
typedef struct {
Color colors[MAX_MATERIAL_COLORS];
TextureData* textures[MAX_MATERIAL_TEXTURES];
} MaterialData;
MaterialData* lovrMaterialDataCreateEmpty();
void lovrMaterialDataDestroy(MaterialData* materialData);

View File

@ -1,30 +1,171 @@
#include "loaders/model.h"
#include "filesystem/filesystem.h"
#include "filesystem/file.h"
#include "math/math.h"
#include "math/mat4.h"
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/config.h>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/matrix4x4.h>
#include <assimp/vector3.h>
#include <assimp/postprocess.h>
static void assimpNodeTraversal(ModelNode* node, struct aiNode* assimpNode) {
static void normalizePath(const char* path, char* dst, size_t size) {
if (path[0] == '/') {
strncpy(dst, path, size);
return;
}
memset(dst, 0, size);
while (*path != '\0') {
if (*path == '/') {
path++;
continue;
}
if (*path == '.') {
if (path[1] == '\0' || path[1] == '/') {
path++;
continue;
}
if (path[1] == '.' && (path[2] == '\0' || path[2] == '/')) {
path += 2;
while ((--dst)[-1] != '/');
continue;
}
}
while (*path != '\0' && *path != '/') {
*dst++ = *path++;
}
*dst++ = '/';
}
*--dst = '\0';
}
// Blob IO (to avoid reading data twice)
static unsigned long assimpBlobRead(struct aiFile* assimpFile, char* buffer, size_t size, size_t count) {
Blob* blob = (Blob*) assimpFile->UserData;
char* data = blob->data;
size_t bytes = MIN(count * size * sizeof(char), blob->size - blob->seek);
memcpy(buffer, data + blob->seek, bytes);
return bytes;
}
static size_t assimpBlobGetSize(struct aiFile* assimpFile) {
Blob* blob = (Blob*) assimpFile->UserData;
return blob->size;
}
static aiReturn assimpBlobSeek(struct aiFile* assimpFile, unsigned long position, enum aiOrigin origin) {
Blob* blob = (Blob*) assimpFile->UserData;
switch (origin) {
case aiOrigin_SET: blob->seek = position; break;
case aiOrigin_CUR: blob->seek += position; break;
case aiOrigin_END: blob->seek = blob->size - position; break;
default: return aiReturn_FAILURE;
}
return blob->seek < blob->size ? aiReturn_SUCCESS : aiReturn_FAILURE;
}
static unsigned long assimpBlobTell(struct aiFile* assimpFile) {
Blob* blob = (Blob*) assimpFile->UserData;
return blob->seek;
}
// File IO (for reading referenced materials/textures)
static unsigned long assimpFileRead(struct aiFile* assimpFile, char* buffer, size_t size, size_t count) {
File* file = (File*) assimpFile->UserData;
unsigned long bytes = lovrFileRead(file, buffer, size, count);
return bytes;
}
static size_t assimpFileGetSize(struct aiFile* assimpFile) {
File* file = (File*) assimpFile->UserData;
return lovrFileGetSize(file);
}
static aiReturn assimpFileSeek(struct aiFile* assimpFile, unsigned long position, enum aiOrigin origin) {
File* file = (File*) assimpFile->UserData;
return lovrFileSeek(file, position) ? aiReturn_FAILURE : aiReturn_SUCCESS;
}
static unsigned long assimpFileTell(struct aiFile* assimpFile) {
File* file = (File*) assimpFile->UserData;
return lovrFileTell(file);
}
static struct aiFile* assimpFileOpen(struct aiFileIO* io, const char* path, const char* mode) {
struct aiFile* assimpFile = malloc(sizeof(struct aiFile));
Blob* blob = (Blob*) io->UserData;
if (!strcmp(blob->name, path)) {
assimpFile->ReadProc = assimpBlobRead;
assimpFile->FileSizeProc = assimpBlobGetSize;
assimpFile->SeekProc = assimpBlobSeek;
assimpFile->TellProc = assimpBlobTell;
assimpFile->UserData = (void*) blob;
} else {
char normalizedPath[LOVR_PATH_MAX];
normalizePath(path, normalizedPath, LOVR_PATH_MAX);
File* file = lovrFileCreate(path);
if (lovrFileOpen(file, OPEN_READ)) {
return NULL;
}
assimpFile->ReadProc = assimpFileRead;
assimpFile->FileSizeProc = assimpFileGetSize;
assimpFile->SeekProc = assimpFileSeek;
assimpFile->TellProc = assimpFileTell;
assimpFile->UserData = (void*) file;
}
return assimpFile;
}
static void assimpFileClose(struct aiFileIO* io, struct aiFile* assimpFile) {
void* blob = io->UserData;
if (assimpFile->UserData != blob) {
File* file = (File*) assimpFile->UserData;
lovrFileClose(file);
lovrRelease(&file->ref);
}
free(assimpFile);
}
static void assimpSumChildren(struct aiNode* assimpNode, int* totalChildren) {
(*totalChildren)++;
for (unsigned int i = 0; i < assimpNode->mNumChildren; i++) {
assimpSumChildren(assimpNode->mChildren[i], totalChildren);
}
}
static void assimpNodeTraversal(ModelData* modelData, struct aiNode* assimpNode, int* nodeId) {
int currentIndex = *nodeId;
ModelNode* node = &modelData->nodes[currentIndex];
// Transform
struct aiMatrix4x4 m = assimpNode->mTransformation;
aiTransposeMatrix4(&m);
mat4_set(node->transform, (float*) &m);
// Meshes
vec_init(&node->meshes);
vec_pusharr(&node->meshes, assimpNode->mMeshes, assimpNode->mNumMeshes);
// Primitives
vec_init(&node->primitives);
vec_pusharr(&node->primitives, assimpNode->mMeshes, assimpNode->mNumMeshes);
// Children
vec_init(&node->children);
for (unsigned int n = 0; n < assimpNode->mNumChildren; n++) {
ModelNode* child = malloc(sizeof(ModelNode));
assimpNodeTraversal(child, assimpNode->mChildren[n]);
vec_push(&node->children, child);
(*nodeId)++;
vec_push(&node->children, *nodeId);
ModelNode* child = &modelData->nodes[*nodeId];
child->parent = currentIndex;
assimpNodeTraversal(modelData, assimpNode->mChildren[n], nodeId);
}
}
@ -32,104 +173,160 @@ ModelData* lovrModelDataCreate(Blob* blob) {
ModelData* modelData = malloc(sizeof(ModelData));
if (!modelData) return NULL;
modelData->hasNormals = 0;
modelData->hasTexCoords = 0;
struct aiFileIO assimpIO;
assimpIO.OpenProc = assimpFileOpen;
assimpIO.CloseProc = assimpFileClose;
assimpIO.UserData = (void*) blob;
struct aiPropertyStore* propertyStore = aiCreatePropertyStore();
aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_OptimizeGraph | aiProcess_FlipUVs;
const struct aiScene* scene = aiImportFileFromMemory(blob->data, blob->size, flags, NULL);
const struct aiScene* scene = aiImportFileExWithProperties(blob->name, flags, &assimpIO, propertyStore);
aiReleasePropertyStore(propertyStore);
if (!scene) {
lovrThrow("Unable to load model from '%s': %s\n", blob->name, aiGetErrorString());
}
modelData->nodeCount = 0;
modelData->vertexCount = 0;
modelData->indexCount = 0;
modelData->hasNormals = 0;
modelData->hasUVs = 0;
// Meshes
vec_init(&modelData->meshes);
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
struct aiMesh* assimpMesh = scene->mMeshes[m];
ModelMesh* mesh = malloc(sizeof(ModelMesh));
vec_push(&modelData->meshes, mesh);
modelData->vertexCount += assimpMesh->mNumVertices;
modelData->indexCount += assimpMesh->mNumFaces * 3;
modelData->hasNormals |= assimpMesh->mNormals != NULL;
modelData->hasUVs |= assimpMesh->mTextureCoords[0] != NULL;
}
// Faces
vec_init(&mesh->faces);
// Allocate
int indexSize = modelData->vertexCount > USHRT_MAX ? sizeof(uint32_t) : sizeof(uint16_t);
modelData->primitiveCount = scene->mNumMeshes;
modelData->primitives = malloc(modelData->primitiveCount * sizeof(ModelPrimitive));
modelData->vertexSize = 3 + (modelData->hasNormals ? 3 : 0) + (modelData->hasUVs ? 2 : 0);
modelData->vertices = malloc(modelData->vertexSize * modelData->vertexCount * sizeof(float));
modelData->indices = malloc(modelData->indexCount * indexSize);
// Load
int vertex = 0;
int index = 0;
for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
struct aiMesh* assimpMesh = scene->mMeshes[m];
modelData->primitives[m].material = assimpMesh->mMaterialIndex;
modelData->primitives[m].drawStart = index;
modelData->primitives[m].drawCount = 0;
// Indices
for (unsigned int f = 0; f < assimpMesh->mNumFaces; f++) {
struct aiFace assimpFace = assimpMesh->mFaces[f];
lovrAssert(assimpFace.mNumIndices == 3, "Only triangular faces are supported");
// Skip lines and points, polygons are triangulated
if (assimpFace.mNumIndices != 3) {
continue;
}
modelData->primitives[m].drawCount += assimpFace.mNumIndices;
ModelFace face;
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
face.indices[i] = assimpFace.mIndices[i];
if (indexSize == sizeof(uint16_t)) {
uint16_t* indices = modelData->indices;
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
indices[index++] = (vertex / modelData->vertexSize) + assimpFace.mIndices[i];
}
} else if (indexSize == sizeof(uint32_t)) {
uint32_t* indices = modelData->indices;
for (unsigned int i = 0; i < assimpFace.mNumIndices; i++) {
indices[index++] = (vertex / modelData->vertexSize) + assimpFace.mIndices[i];
}
}
vec_push(&mesh->faces, face);
}
// Vertices
vec_init(&mesh->vertices);
for (unsigned int v = 0; v < assimpMesh->mNumVertices; v++) {
ModelVertex vertex;
vertex.x = assimpMesh->mVertices[v].x;
vertex.y = assimpMesh->mVertices[v].y;
vertex.z = assimpMesh->mVertices[v].z;
vec_push(&mesh->vertices, vertex);
}
modelData->vertices[vertex++] = assimpMesh->mVertices[v].x;
modelData->vertices[vertex++] = assimpMesh->mVertices[v].y;
modelData->vertices[vertex++] = assimpMesh->mVertices[v].z;
// Normals
lovrAssert(assimpMesh->mNormals, "Model must have normals");
if (modelData->hasNormals) {
if (assimpMesh->mNormals) {
modelData->vertices[vertex++] = assimpMesh->mNormals[v].x;
modelData->vertices[vertex++] = assimpMesh->mNormals[v].y;
modelData->vertices[vertex++] = assimpMesh->mNormals[v].z;
} else {
modelData->vertices[vertex++] = 0;
modelData->vertices[vertex++] = 0;
modelData->vertices[vertex++] = 0;
}
}
modelData->hasNormals = 1;
vec_init(&mesh->normals);
for (unsigned int n = 0; n < assimpMesh->mNumVertices; n++) {
ModelVertex normal;
normal.x = assimpMesh->mNormals[n].x;
normal.y = assimpMesh->mNormals[n].y;
normal.z = assimpMesh->mNormals[n].z;
vec_push(&mesh->normals, normal);
}
modelData->hasTexCoords = modelData->hasTexCoords || assimpMesh->mTextureCoords[0] != NULL;
if (assimpMesh->mTextureCoords[0]) {
vec_init(&mesh->texCoords);
for (unsigned int i = 0; i < assimpMesh->mNumVertices; i++) {
ModelVertex texCoord;
texCoord.x = assimpMesh->mTextureCoords[0][i].x;
texCoord.y = assimpMesh->mTextureCoords[0][i].y;
vec_push(&mesh->texCoords, texCoord);
if (modelData->hasUVs) {
if (assimpMesh->mTextureCoords[0]) {
modelData->vertices[vertex++] = assimpMesh->mTextureCoords[0][v].x;
modelData->vertices[vertex++] = assimpMesh->mTextureCoords[0][v].y;
} else {
modelData->vertices[vertex++] = 0;
modelData->vertices[vertex++] = 0;
}
}
}
}
// Materials
modelData->materialCount = scene->mNumMaterials;
modelData->materials = malloc(modelData->materialCount * sizeof(MaterialData));
for (unsigned int m = 0; m < scene->mNumMaterials; m++) {
MaterialData* materialData = lovrMaterialDataCreateEmpty();
struct aiMaterial* material = scene->mMaterials[m];
struct aiColor4D color;
struct aiString str;
if (aiGetMaterialColor(material, AI_MATKEY_COLOR_DIFFUSE, &color) == aiReturn_SUCCESS) {
materialData->colors[COLOR_DIFFUSE].r = color.r * 255;
materialData->colors[COLOR_DIFFUSE].g = color.g * 255;
materialData->colors[COLOR_DIFFUSE].b = color.b * 255;
materialData->colors[COLOR_DIFFUSE].a = color.a * 255;
}
if (aiGetMaterialTexture(material, aiTextureType_DIFFUSE, 0, &str, NULL, NULL, NULL, NULL, NULL, NULL) == aiReturn_SUCCESS) {
char* path = str.data;
char normalizedPath[LOVR_PATH_MAX];
normalizePath(path, normalizedPath, LOVR_PATH_MAX);
size_t size;
void* data = lovrFilesystemRead(path, &size);
if (data) {
Blob* blob = lovrBlobCreate(data, size, path);
materialData->textures[TEXTURE_DIFFUSE] = lovrTextureDataFromBlob(blob);
}
}
modelData->materials[m] = *materialData;
}
// Nodes
modelData->root = malloc(sizeof(ModelNode));
assimpNodeTraversal(modelData->root, scene->mRootNode);
modelData->nodeCount = 0;
assimpSumChildren(scene->mRootNode, &modelData->nodeCount);
modelData->nodes = malloc(modelData->nodeCount * sizeof(ModelNode));
modelData->nodes[0].parent = -1;
int nodeIndex = 0;
assimpNodeTraversal(modelData, scene->mRootNode, &nodeIndex);
aiReleaseImport(scene);
return modelData;
}
void lovrModelDataDestroy(ModelData* modelData) {
for (int i = 0; i < modelData->meshes.length; i++) {
ModelMesh* mesh = modelData->meshes.data[i];
vec_deinit(&mesh->faces);
vec_deinit(&mesh->vertices);
vec_deinit(&mesh->normals);
if (modelData->hasTexCoords) {
vec_deinit(&mesh->texCoords);
}
free(mesh);
for (int i = 0; i < modelData->nodeCount; i++) {
vec_deinit(&modelData->nodes[i].children);
vec_deinit(&modelData->nodes[i].primitives);
}
vec_void_t nodes;
vec_init(&nodes);
vec_push(&nodes, modelData->root);
while (nodes.length > 0) {
ModelNode* node = vec_first(&nodes);
vec_extend(&nodes, &node->children);
vec_deinit(&node->meshes);
vec_deinit(&node->children);
vec_splice(&nodes, 0, 1);
free(node);
for (int i = 0; i < modelData->materialCount; i++) {
lovrMaterialDataDestroy(&modelData->materials[i]);
}
vec_deinit(&modelData->meshes);
vec_deinit(&nodes);
free(modelData->nodes);
free(modelData->primitives);
free(modelData->materials);
free(modelData->vertices);
free(modelData->indices);
free(modelData);
}

View File

@ -1,43 +1,37 @@
#include "filesystem/blob.h"
#include "loaders/material.h"
#include "util.h"
#include "lib/vec/vec.h"
#pragma once
typedef struct {
float x;
float y;
float z;
} ModelVertex;
int material;
int drawStart;
int drawCount;
} ModelPrimitive;
typedef vec_t(ModelVertex) vec_model_vertex_t;
typedef struct {
unsigned int indices[3];
} ModelFace;
typedef vec_t(ModelFace) vec_model_face_t;
typedef struct {
vec_model_face_t faces;
vec_model_vertex_t vertices;
vec_model_vertex_t normals;
vec_model_vertex_t texCoords;
} ModelMesh;
typedef vec_t(ModelMesh*) vec_model_mesh_t;
typedef struct {
typedef struct ModelNode {
float transform[16];
vec_uint_t meshes;
vec_void_t children;
int parent;
vec_uint_t children;
vec_uint_t primitives;
} ModelNode;
typedef struct {
ModelNode* root;
vec_model_mesh_t meshes;
ModelNode* nodes;
ModelPrimitive* primitives;
MaterialData* materials;
float* vertices;
void* indices;
int nodeCount;
int primitiveCount;
int materialCount;
int vertexCount;
int vertexSize;
int indexCount;
int hasNormals;
int hasTexCoords;
int hasUVs;
} ModelData;
ModelData* lovrModelDataCreate(Blob* blob);

View File

@ -1,6 +1,5 @@
#include "filesystem/blob.h"
#include "lib/glad/glad.h"
#include <stddef.h>
#include "lib/glfw.h"
#include <stdint.h>
#pragma once

View File

@ -148,3 +148,27 @@ void* luax_optenum(lua_State* L, int index, const char* fallback, map_int_t* map
return value;
}
Color luax_checkcolor(lua_State* L, int index) {
Color color = { 0xff, 0xff, 0xff, 0xff };
if (lua_istable(L, 1)) {
for (int i = 1; i <= 4; i++) {
lua_rawgeti(L, 1, i);
}
color.r = luaL_checknumber(L, -4);
color.g = luaL_checknumber(L, -3);
color.b = luaL_checknumber(L, -2);
color.a = luaL_optnumber(L, -1, 255);
lua_pop(L, 4);
} else if (lua_gettop(L) >= index + 2) {
color.r = lua_tointeger(L, index);
color.g = lua_tointeger(L, index + 1);
color.b = lua_tointeger(L, index + 2);
color.a = lua_isnoneornil(L, index + 3) ? 255 : lua_tointeger(L, index + 3);
} else {
luaL_error(L, "Invalid color, expected 3 numbers, 4 numbers, or a table");
}
return color;
}

View File

@ -2,6 +2,7 @@
#include <lauxlib.h>
#include <lualib.h>
#include "lib/map/map.h"
#include "util.h"
#pragma once
@ -35,3 +36,4 @@ void luax_registerobject(lua_State* L, void* object);
void luax_pushenum(lua_State* L, map_int_t* map, int value);
void* luax_checkenum(lua_State* L, int index, map_int_t* map, const char* typeName);
void* luax_optenum(lua_State* L, int index, const char* fallback, map_int_t* map, const char* typeName);
Color luax_checkcolor(lua_State* L, int index);

View File

@ -2,6 +2,7 @@
#include "math/quat.h"
#include "math/vec3.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
// m0 m4 m8 m12