From 4feeb4d4ab9bcf22093f457302012e515b99f165 Mon Sep 17 00:00:00 2001 From: bjorn Date: Sun, 12 Mar 2017 16:45:24 -0700 Subject: [PATCH] Equirectangular Skybox rough draft; --- src/api/graphics.c | 13 +++- src/graphics/graphics.c | 143 +++++++++++++++++++++++++++------------- src/graphics/skybox.c | 41 +++++++++--- src/graphics/skybox.h | 8 ++- 4 files changed, 146 insertions(+), 59 deletions(-) diff --git a/src/api/graphics.c b/src/api/graphics.c index 0e2461e7..e2d5c14d 100644 --- a/src/api/graphics.c +++ b/src/api/graphics.c @@ -683,8 +683,13 @@ int l_lovrGraphicsNewShader(lua_State* L) { int l_lovrGraphicsNewSkybox(lua_State* L) { void* data[6]; size_t size[6]; + SkyboxType type; - if (lua_istable(L, 1)) { + if (lua_gettop(L) == 1 && lua_type(L, 1) == LUA_TSTRING) { + const char* filename = lua_tostring(L, 1); + data[0] = lovrFilesystemRead(filename, size); + type = SKYBOX_PANORAMA; + } 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"); } @@ -700,14 +705,18 @@ int l_lovrGraphicsNewSkybox(lua_State* L) { data[i] = lovrFilesystemRead(filename, size + i); lua_pop(L, 1); } + + type = SKYBOX_CUBE; } else { for (int i = 0; i < 6; i++) { const char* filename = luaL_checkstring(L, i + 1); data[i] = lovrFilesystemRead(filename, size + i); } + + type = SKYBOX_CUBE; } - Skybox* skybox = lovrSkyboxCreate(data, size); + Skybox* skybox = lovrSkyboxCreate(data, size, type); luax_pushtype(L, Skybox, skybox); lovrRelease(&skybox->ref); diff --git a/src/graphics/graphics.c b/src/graphics/graphics.c index dfa4c51c..22a083c3 100644 --- a/src/graphics/graphics.c +++ b/src/graphics/graphics.c @@ -677,62 +677,113 @@ void lovrGraphicsSkybox(Skybox* skybox, float angle, float ax, float ay, float a lovrGraphicsOrigin(); lovrGraphicsRotate(angle, ax, ay, az); - float cube[] = { - // Front - 1.f, -1.f, -1.f, - 1.f, 1.f, -1.f, - -1.f, -1.f, -1.f, - -1.f, 1.f, -1.f, + if (skybox->type == SKYBOX_CUBE) { + float cube[] = { + // Front + 1.f, -1.f, -1.f, + 1.f, 1.f, -1.f, + -1.f, -1.f, -1.f, + -1.f, 1.f, -1.f, - // Left - -1.f, 1.f, -1.f, - -1.f, 1.f, 1.f, - -1.f, -1.f, -1.f, - -1.f, -1.f, 1.f, + // Left + -1.f, 1.f, -1.f, + -1.f, 1.f, 1.f, + -1.f, -1.f, -1.f, + -1.f, -1.f, 1.f, - // Back - -1.f, -1.f, 1.f, - 1.f, -1.f, 1.f, - -1.f, 1.f, 1.f, - 1.f, 1.f, 1.f, + // Back + -1.f, -1.f, 1.f, + 1.f, -1.f, 1.f, + -1.f, 1.f, 1.f, + 1.f, 1.f, 1.f, - // Right - 1.f, 1.f, 1.f, - 1.f, -1.f, 1.f, - 1.f, 1.f, -1.f, - 1.f, -1.f, -1.f, + // Right + 1.f, 1.f, 1.f, + 1.f, -1.f, 1.f, + 1.f, 1.f, -1.f, + 1.f, -1.f, -1.f, - // Bottom - 1.f, -1.f, -1.f, - 1.f, -1.f, 1.f, - -1.f, -1.f, -1.f, - -1.f, -1.f, 1.f, + // Bottom + 1.f, -1.f, -1.f, + 1.f, -1.f, 1.f, + -1.f, -1.f, -1.f, + -1.f, -1.f, 1.f, - // Adjust - -1.f, -1.f, 1.f, - -1.f, 1.f, -1.f, + // Adjust + -1.f, -1.f, 1.f, + -1.f, 1.f, -1.f, - // Top - -1.f, 1.f, -1.f, - -1.f, 1.f, 1.f, - 1.f, 1.f, -1.f, - 1.f, 1.f, 1.f - }; + // Top + -1.f, 1.f, -1.f, + -1.f, 1.f, 1.f, + 1.f, 1.f, -1.f, + 1.f, 1.f, 1.f + }; - lovrGraphicsSetShapeData(cube, 156); + lovrGraphicsSetShapeData(cube, 156); - glDepthMask(GL_FALSE); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->texture); + glDepthMask(GL_FALSE); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->texture); - int wasCulling = lovrGraphicsIsCullingEnabled(); - lovrGraphicsSetCullingEnabled(0); - lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, NULL, 0, 0, 0); - lovrGraphicsSetCullingEnabled(wasCulling); + int wasCulling = lovrGraphicsIsCullingEnabled(); + lovrGraphicsSetCullingEnabled(0); + lovrGraphicsDrawPrimitive(GL_TRIANGLE_STRIP, NULL, 0, 0, 0); + lovrGraphicsSetCullingEnabled(wasCulling); - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - glActiveTexture(GL_TEXTURE0); - glDepthMask(GL_TRUE); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glActiveTexture(GL_TEXTURE0); + glDepthMask(GL_TRUE); + } else if (skybox->type == SKYBOX_PANORAMA) { + int resolution = 40; + float sphere[8000]; // resolution * resolution * 5 + + for (int i = 0; i < resolution; i++) { + float theta = i * M_PI / resolution; + float sinTheta = sin(theta); + float cosTheta = cos(theta); + + for (int j = 0; j < resolution; j++) { + float phi = j * 2 * M_PI / resolution; + float sinPhi = sin(phi); + float cosPhi = cos(phi); + + float x = sinPhi * sinTheta; + float y = cosTheta; + float z = -cosPhi * sinTheta; + float u = j / resolution; + float v = i / resolution; + + sphere[i * resolution + j * 5 + 0] = x; + sphere[i * resolution + j * 5 + 1] = y; + sphere[i * resolution + j * 5 + 2] = z; + sphere[i * resolution + j * 5 + 3] = u; + sphere[i * resolution + j * 5 + 4] = v; + } + } + + int indices[9600]; // resolution * resolution * 6 + for (int i = 0; i < resolution; i++) { + int offset0 = i * (resolution + 1); + int offset1 = (i + 1) * (resolution + 1); + for (int j = 0; j < resolution; j++) { + int index0 = offset0 + j; + int index1 = offset1 + j; + indices[i * resolution + j * 6 + 0] = index0; + indices[i * resolution + j * 6 + 1] = index1; + indices[i * resolution + j * 6 + 2] = index0 + 1; + indices[i * resolution + j * 6 + 3] = index1; + indices[i * resolution + j * 6 + 4] = index1 + 1; + indices[i * resolution + j * 6 + 5] = index0 + 1; + } + } + + lovrGraphicsSetShapeData(sphere, 8000); + lovrGraphicsSetIndexData(indices, 9600); + glDepthMask(GL_FALSE); + lovrGraphicsDrawPrimitive(GL_TRIANGLES, skybox->texture, 0, 1, 1); + glDepthMask(GL_TRUE); + } lovrGraphicsSetShader(lastShader); lovrRelease(&lastShader->ref); diff --git a/src/graphics/skybox.c b/src/graphics/skybox.c index c07b8a26..fb4073e7 100644 --- a/src/graphics/skybox.c +++ b/src/graphics/skybox.c @@ -2,14 +2,27 @@ #include "lib/stb/stb_image.h" #include -Skybox* lovrSkyboxCreate(void** data, size_t* size) { +Skybox* lovrSkyboxCreate(void** data, size_t* size, SkyboxType type) { Skybox* skybox = lovrAlloc(sizeof(Skybox), lovrSkyboxDestroy); if (!skybox) return NULL; - glGenTextures(1, &skybox->texture); - glBindTexture(GL_TEXTURE_CUBE_MAP, skybox->texture); + skybox->type = type; - for (int i = 0; i < 6; i++) { + 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(data[i], size[i], &width, &height, &channels, 3); @@ -18,15 +31,23 @@ Skybox* lovrSkyboxCreate(void** data, size_t* size) { error("Could not load skybox image %d", i); } - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); + 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(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + 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; } diff --git a/src/graphics/skybox.h b/src/graphics/skybox.h index 4255a464..c323c5ae 100644 --- a/src/graphics/skybox.h +++ b/src/graphics/skybox.h @@ -3,10 +3,16 @@ #pragma once +typedef enum { + SKYBOX_CUBE, + SKYBOX_PANORAMA +} SkyboxType; + typedef struct { Ref ref; + SkyboxType type; GLuint texture; } Skybox; -Skybox* lovrSkyboxCreate(void** data, size_t* size); +Skybox* lovrSkyboxCreate(void** data, size_t* size, int count); void lovrSkyboxDestroy(const Ref* ref);