mirror of https://github.com/bjornbytes/lovr.git
Add support for 2d array textures; Improve mipmaps;
lovr.graphics.newTexture has been changed.
This commit is contained in:
parent
5493a980ff
commit
f75530b9e1
|
@ -86,6 +86,7 @@ extern map_int_t MeshUsages;
|
||||||
extern map_int_t PolygonWindings;
|
extern map_int_t PolygonWindings;
|
||||||
extern map_int_t ShapeTypes;
|
extern map_int_t ShapeTypes;
|
||||||
extern map_int_t TextureFormats;
|
extern map_int_t TextureFormats;
|
||||||
|
extern map_int_t TextureTypes;
|
||||||
extern map_int_t TimeUnits;
|
extern map_int_t TimeUnits;
|
||||||
extern map_int_t VerticalAligns;
|
extern map_int_t VerticalAligns;
|
||||||
extern map_int_t WrapModes;
|
extern map_int_t WrapModes;
|
||||||
|
|
|
@ -29,6 +29,7 @@ map_int_t MeshDrawModes;
|
||||||
map_int_t MeshUsages;
|
map_int_t MeshUsages;
|
||||||
map_int_t StencilActions;
|
map_int_t StencilActions;
|
||||||
map_int_t TextureFormats;
|
map_int_t TextureFormats;
|
||||||
|
map_int_t TextureTypes;
|
||||||
map_int_t VerticalAligns;
|
map_int_t VerticalAligns;
|
||||||
map_int_t Windings;
|
map_int_t Windings;
|
||||||
map_int_t WrapModes;
|
map_int_t WrapModes;
|
||||||
|
@ -204,6 +205,11 @@ int l_lovrGraphicsInit(lua_State* L) {
|
||||||
map_set(&TextureFormats, "dxt3", FORMAT_DXT3);
|
map_set(&TextureFormats, "dxt3", FORMAT_DXT3);
|
||||||
map_set(&TextureFormats, "dxt5", FORMAT_DXT5);
|
map_set(&TextureFormats, "dxt5", FORMAT_DXT5);
|
||||||
|
|
||||||
|
map_init(&TextureTypes);
|
||||||
|
map_set(&TextureTypes, "2d", TEXTURE_2D);
|
||||||
|
map_set(&TextureTypes, "array", TEXTURE_ARRAY);
|
||||||
|
map_set(&TextureTypes, "cube", TEXTURE_CUBE);
|
||||||
|
|
||||||
map_init(&VerticalAligns);
|
map_init(&VerticalAligns);
|
||||||
map_set(&VerticalAligns, "top", ALIGN_TOP);
|
map_set(&VerticalAligns, "top", ALIGN_TOP);
|
||||||
map_set(&VerticalAligns, "bottom", ALIGN_BOTTOM);
|
map_set(&VerticalAligns, "bottom", ALIGN_BOTTOM);
|
||||||
|
@ -918,7 +924,7 @@ int l_lovrGraphicsNewMaterial(lua_State* L) {
|
||||||
Blob* blob = luax_readblob(L, index++, "Texture");
|
Blob* blob = luax_readblob(L, index++, "Texture");
|
||||||
TextureData* textureData = lovrTextureDataFromBlob(blob);
|
TextureData* textureData = lovrTextureDataFromBlob(blob);
|
||||||
lovrRelease(&blob->ref);
|
lovrRelease(&blob->ref);
|
||||||
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true);
|
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, true);
|
||||||
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
||||||
lovrRelease(&texture->ref);
|
lovrRelease(&texture->ref);
|
||||||
} else if (lua_isuserdata(L, index)) {
|
} else if (lua_isuserdata(L, index)) {
|
||||||
|
@ -1020,7 +1026,7 @@ int l_lovrGraphicsNewModel(lua_State* L) {
|
||||||
if (lua_type(L, 2) == LUA_TSTRING) {
|
if (lua_type(L, 2) == LUA_TSTRING) {
|
||||||
Blob* blob = luax_readblob(L, 2, "Texture");
|
Blob* blob = luax_readblob(L, 2, "Texture");
|
||||||
TextureData* textureData = lovrTextureDataFromBlob(blob);
|
TextureData* textureData = lovrTextureDataFromBlob(blob);
|
||||||
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true);
|
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, true);
|
||||||
Material* material = lovrMaterialCreate(false);
|
Material* material = lovrMaterialCreate(false);
|
||||||
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
||||||
lovrModelSetMaterial(model, material);
|
lovrModelSetMaterial(model, material);
|
||||||
|
@ -1061,34 +1067,57 @@ int l_lovrGraphicsNewShader(lua_State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int l_lovrGraphicsNewTexture(lua_State* L) {
|
int l_lovrGraphicsNewTexture(lua_State* L) {
|
||||||
int top = lua_gettop(L);
|
|
||||||
bool isTable = lua_istable(L, 1);
|
bool isTable = lua_istable(L, 1);
|
||||||
bool hasFlags = isTable ? lua_istable(L, 2) : lua_istable(L, top);
|
|
||||||
int count = isTable ? lua_objlen(L, 1) : (top - (hasFlags ? 1 : 0));
|
|
||||||
lovrAssert(count == 1 || count == 6, "Expected 1 image for a 2d texture or 6 images for a cubemap, got %d", count);
|
|
||||||
|
|
||||||
TextureData* slices[6];
|
if (!isTable) {
|
||||||
if (isTable) {
|
lua_newtable(L);
|
||||||
for (int i = 0; i < count; i++) {
|
lua_pushvalue(L, 1);
|
||||||
lua_rawgeti(L, 1, i + 1);
|
lua_rawseti(L, -2, 1);
|
||||||
slices[i] = luax_checktexturedata(L, -1);
|
lua_replace(L, 1);
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
slices[i] = luax_checktexturedata(L, i + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sliceCount = lua_objlen(L, 1);
|
||||||
|
TextureType type = isTable ? (sliceCount > 0 ? TEXTURE_ARRAY : TEXTURE_CUBE) : TEXTURE_2D;
|
||||||
|
|
||||||
|
bool hasFlags = lua_istable(L, 2);
|
||||||
bool srgb = true;
|
bool srgb = true;
|
||||||
|
bool mipmaps = true;
|
||||||
|
|
||||||
if (hasFlags) {
|
if (hasFlags) {
|
||||||
lua_getfield(L, top, "linear");
|
lua_getfield(L, 2, "linear");
|
||||||
srgb = !lua_toboolean(L, -1);
|
srgb = !lua_toboolean(L, -1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, 2, "mipmaps");
|
||||||
|
mipmaps = lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, 2, "type");
|
||||||
|
if (!lua_isnil(L, -1)) {
|
||||||
|
type = *(TextureType*) luax_checkenum(L, -1, &TextureTypes, "texture type");
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == TEXTURE_CUBE && sliceCount == 0) {
|
||||||
|
sliceCount = 6;
|
||||||
|
const char* faces[6] = { "left", "right", "top", "bottom", "front", "back" };
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
lua_pushstring(L, faces[i]);
|
||||||
|
lua_rawget(L, 1);
|
||||||
|
lua_rawseti(L, 1, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture* texture = lovrTextureCreate(type, NULL, sliceCount, srgb, mipmaps);
|
||||||
|
|
||||||
|
for (int i = 0; i < sliceCount; i++) {
|
||||||
|
lua_rawgeti(L, 1, i + 1);
|
||||||
|
TextureData* textureData = luax_checktexturedata(L, -1);
|
||||||
|
lovrTextureReplacePixels(texture, textureData, i);
|
||||||
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureType type = (count == 1) ? TEXTURE_2D : TEXTURE_CUBE;
|
|
||||||
Texture* texture = lovrTextureCreate(type, slices, count, srgb);
|
|
||||||
luax_pushtype(L, Texture, texture);
|
luax_pushtype(L, Texture, texture);
|
||||||
lovrRelease(&texture->ref);
|
lovrRelease(&texture->ref);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -136,7 +136,6 @@ TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, Textu
|
||||||
textureData->data = memset(malloc(size), value, size);
|
textureData->data = memset(malloc(size), value, size);
|
||||||
textureData->blob = NULL;
|
textureData->blob = NULL;
|
||||||
vec_init(&textureData->mipmaps);
|
vec_init(&textureData->mipmaps);
|
||||||
textureData->generateMipmaps = false;
|
|
||||||
return textureData;
|
return textureData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +150,6 @@ TextureData* lovrTextureDataGetEmpty(int width, int height, TextureFormat format
|
||||||
textureData->data = NULL;
|
textureData->data = NULL;
|
||||||
textureData->blob = NULL;
|
textureData->blob = NULL;
|
||||||
vec_init(&textureData->mipmaps);
|
vec_init(&textureData->mipmaps);
|
||||||
textureData->generateMipmaps = false;
|
|
||||||
return textureData;
|
return textureData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +169,6 @@ TextureData* lovrTextureDataFromBlob(Blob* blob) {
|
||||||
textureData->format = FORMAT_RGBA;
|
textureData->format = FORMAT_RGBA;
|
||||||
textureData->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
|
textureData->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
|
||||||
textureData->blob = NULL;
|
textureData->blob = NULL;
|
||||||
textureData->generateMipmaps = true;
|
|
||||||
|
|
||||||
if (!textureData->data) {
|
if (!textureData->data) {
|
||||||
lovrThrow("Could not load texture data from '%s'", blob->name);
|
lovrThrow("Could not load texture data from '%s'", blob->name);
|
||||||
|
|
|
@ -40,7 +40,6 @@ typedef struct {
|
||||||
void* data;
|
void* data;
|
||||||
Blob* blob;
|
Blob* blob;
|
||||||
TextureFormat format;
|
TextureFormat format;
|
||||||
bool generateMipmaps;
|
|
||||||
vec_mipmap_t mipmaps;
|
vec_mipmap_t mipmaps;
|
||||||
} TextureData;
|
} TextureData;
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ bool lovrCanvasSupportsFormat(TextureFormat format) {
|
||||||
|
|
||||||
Canvas* lovrCanvasCreate(int width, int height, TextureFormat format, int msaa, bool depth, bool stencil) {
|
Canvas* lovrCanvasCreate(int width, int height, TextureFormat format, int msaa, bool depth, bool stencil) {
|
||||||
TextureData* textureData = lovrTextureDataGetEmpty(width, height, format);
|
TextureData* textureData = lovrTextureDataGetEmpty(width, height, format);
|
||||||
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true);
|
Texture* texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, true);
|
||||||
if (!texture) return NULL;
|
if (!texture) return NULL;
|
||||||
|
|
||||||
Canvas* canvas = lovrAlloc(sizeof(Canvas), lovrCanvasDestroy);
|
Canvas* canvas = lovrAlloc(sizeof(Canvas), lovrCanvasDestroy);
|
||||||
|
|
|
@ -324,7 +324,7 @@ void lovrFontCreateTexture(Font* font) {
|
||||||
|
|
||||||
TextureData* textureData = lovrTextureDataGetBlank(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB);
|
TextureData* textureData = lovrTextureDataGetBlank(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB);
|
||||||
TextureFilter filter = { .mode = FILTER_BILINEAR };
|
TextureFilter filter = { .mode = FILTER_BILINEAR };
|
||||||
font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false);
|
font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false, false);
|
||||||
lovrTextureSetFilter(font->texture, filter);
|
lovrTextureSetFilter(font->texture, filter);
|
||||||
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
|
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1136,6 +1136,8 @@ void lovrGraphicsSkybox(Texture* texture, float angle, float ax, float ay, float
|
||||||
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, texture);
|
||||||
lovrGraphicsSphere(material, NULL, 30);
|
lovrGraphicsSphere(material, NULL, 30);
|
||||||
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, NULL);
|
lovrMaterialSetTexture(material, TEXTURE_DIFFUSE, NULL);
|
||||||
|
} else {
|
||||||
|
lovrThrow("lovr.graphics.skybox only accepts 2d and cube Textures");
|
||||||
}
|
}
|
||||||
|
|
||||||
lovrGraphicsSetDepthTest(mode, write);
|
lovrGraphicsSetDepthTest(mode, write);
|
||||||
|
@ -1234,7 +1236,7 @@ void lovrGraphicsBindTexture(Texture* texture, TextureType type, int slot) {
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
if (!state.defaultTexture) {
|
if (!state.defaultTexture) {
|
||||||
TextureData* textureData = lovrTextureDataGetBlank(1, 1, 0xff, FORMAT_RGBA);
|
TextureData* textureData = lovrTextureDataGetBlank(1, 1, 0xff, FORMAT_RGBA);
|
||||||
state.defaultTexture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true);
|
state.defaultTexture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
texture = state.defaultTexture;
|
texture = state.defaultTexture;
|
||||||
|
|
|
@ -70,7 +70,7 @@ Model* lovrModelCreate(ModelData* modelData) {
|
||||||
model->textures = malloc(modelData->textures.length * sizeof(Texture*));
|
model->textures = malloc(modelData->textures.length * sizeof(Texture*));
|
||||||
for (int i = 0; i < modelData->textures.length; i++) {
|
for (int i = 0; i < modelData->textures.length; i++) {
|
||||||
if (modelData->textures.data[i]) {
|
if (modelData->textures.data[i]) {
|
||||||
model->textures[i] = lovrTextureCreate(TEXTURE_2D, (TextureData**) &modelData->textures.data[i], 1, true);
|
model->textures[i] = lovrTextureCreate(TEXTURE_2D, (TextureData**) &modelData->textures.data[i], 1, true, true);
|
||||||
} else {
|
} else {
|
||||||
model->textures[i] = NULL;
|
model->textures[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ static UniformType getUniformType(GLenum type, const char* debug) {
|
||||||
|
|
||||||
case GL_SAMPLER_2D:
|
case GL_SAMPLER_2D:
|
||||||
case GL_SAMPLER_CUBE:
|
case GL_SAMPLER_CUBE:
|
||||||
|
case GL_SAMPLER_2D_ARRAY:
|
||||||
return UNIFORM_SAMPLER;
|
return UNIFORM_SAMPLER;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -40,6 +41,7 @@ static int getUniformComponents(GLenum type) {
|
||||||
case GL_INT:
|
case GL_INT:
|
||||||
case GL_SAMPLER_2D:
|
case GL_SAMPLER_2D:
|
||||||
case GL_SAMPLER_CUBE:
|
case GL_SAMPLER_CUBE:
|
||||||
|
case GL_SAMPLER_2D_ARRAY:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case GL_FLOAT_VEC2:
|
case GL_FLOAT_VEC2:
|
||||||
|
@ -303,7 +305,12 @@ void lovrShaderBind(Shader* shader) {
|
||||||
|
|
||||||
case UNIFORM_SAMPLER:
|
case UNIFORM_SAMPLER:
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
TextureType type = uniform->glType == GL_SAMPLER_2D ? TEXTURE_2D : TEXTURE_CUBE;
|
TextureType type;
|
||||||
|
switch (uniform->glType) {
|
||||||
|
case GL_SAMPLER_2D: type = TEXTURE_2D; break;
|
||||||
|
case GL_SAMPLER_CUBE: type = TEXTURE_CUBE; break;
|
||||||
|
case GL_SAMPLER_2D_ARRAY: type = TEXTURE_ARRAY; break;
|
||||||
|
}
|
||||||
lovrGraphicsBindTexture(uniform->value.textures[i], type, uniform->baseTextureSlot + i);
|
lovrGraphicsBindTexture(uniform->value.textures[i], type, uniform->baseTextureSlot + i);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -31,113 +31,66 @@ GLenum lovrTextureFormatGetGLInternalFormat(TextureFormat format, bool srgb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lovrTextureFormatIsCompressed(TextureFormat format) {
|
bool lovrTextureFormatIsCompressed(TextureFormat format) {
|
||||||
switch (format) {
|
return format == FORMAT_DXT1 || format == FORMAT_DXT3 || format == FORMAT_DXT5;
|
||||||
case FORMAT_DXT1:
|
|
||||||
case FORMAT_DXT3:
|
|
||||||
case FORMAT_DXT5:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lovrTextureUpload(Texture* texture) {
|
static void lovrTextureAllocate(Texture* texture, TextureData* textureData) {
|
||||||
TextureData* textureData = texture->slices[0];
|
texture->allocated = true;
|
||||||
texture->width = textureData->width;
|
texture->width = textureData->width;
|
||||||
texture->height = textureData->height;
|
texture->height = textureData->height;
|
||||||
|
|
||||||
|
if (lovrTextureFormatIsCompressed(textureData->format)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = textureData->width;
|
||||||
|
int h = textureData->height;
|
||||||
|
int mipmapCount = log2(MAX(w, h)) + 1;
|
||||||
bool srgb = lovrGraphicsIsGammaCorrect() && texture->srgb;
|
bool srgb = lovrGraphicsIsGammaCorrect() && texture->srgb;
|
||||||
|
GLenum glFormat = lovrTextureFormatGetGLFormat(textureData->format);
|
||||||
// Allocate storage
|
GLenum internalFormat = lovrTextureFormatGetGLInternalFormat(textureData->format, srgb);
|
||||||
if (!lovrTextureFormatIsCompressed(textureData->format) && texture->type != TEXTURE_CUBE) {
|
|
||||||
int w = textureData->width;
|
|
||||||
int h = textureData->height;
|
|
||||||
int mipmapCount = log2(MAX(w, h)) + 1;
|
|
||||||
GLenum glFormat = lovrTextureFormatGetGLFormat(textureData->format);
|
|
||||||
GLenum internalFormat = lovrTextureFormatGetGLInternalFormat(textureData->format, srgb);
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
if (GLAD_GL_ARB_texture_storage) {
|
if (GLAD_GL_ARB_texture_storage) {
|
||||||
#endif
|
#endif
|
||||||
glTexStorage2D(GL_TEXTURE_2D, mipmapCount, internalFormat, w, h);
|
glTexStorage2D(texture->type, mipmapCount, internalFormat, w, h);
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
} else {
|
|
||||||
for (int i = 0; i < mipmapCount; i++) {
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, i, internalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
|
||||||
w = MAX(w >> 1, 1);
|
|
||||||
h = MAX(h >> 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload data
|
|
||||||
for (int i = 0; i < texture->sliceCount; i++) {
|
|
||||||
TextureData* textureData = texture->slices[i];
|
|
||||||
GLenum glFormat = lovrTextureFormatGetGLFormat(textureData->format);
|
|
||||||
GLenum glInternalFormat = lovrTextureFormatGetGLInternalFormat(textureData->format, srgb);
|
|
||||||
GLenum binding = (texture->type == TEXTURE_CUBE) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + i : GL_TEXTURE_2D;
|
|
||||||
|
|
||||||
if (lovrTextureFormatIsCompressed(textureData->format)) {
|
|
||||||
Mipmap m; int i;
|
|
||||||
vec_foreach(&textureData->mipmaps, m, i) {
|
|
||||||
glCompressedTexImage2D(binding, i, glInternalFormat, m.width, m.height, 0, m.size, m.data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int w = textureData->width;
|
|
||||||
int h = textureData->height;
|
|
||||||
|
|
||||||
if (texture->type == TEXTURE_CUBE) {
|
|
||||||
glTexImage2D(binding, 0, glInternalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, textureData->data);
|
|
||||||
} else if (textureData->data) {
|
|
||||||
glTexSubImage2D(binding, 0, 0, 0, w, h, glFormat, GL_UNSIGNED_BYTE, textureData->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textureData->generateMipmaps) {
|
|
||||||
glGenerateMipmap(texture->type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void validateSlices(TextureType type, TextureData* slices[6], int sliceCount) {
|
|
||||||
lovrAssert(sliceCount > 0, "At least one layer must be provided to create a texture");
|
|
||||||
int maxSize = lovrGraphicsGetLimits().textureSize;
|
|
||||||
int width = slices[0]->width;
|
|
||||||
int height = slices[0]->height;
|
|
||||||
bool oversized = width > maxSize || height > maxSize;
|
|
||||||
lovrAssert(!oversized, "Texture is too big, max size is %d x %d", maxSize, maxSize);
|
|
||||||
|
|
||||||
if (type == TEXTURE_CUBE) {
|
|
||||||
lovrAssert(sliceCount == 6, "Cube textures must have 6 images");
|
|
||||||
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 {
|
} else {
|
||||||
lovrThrow("Unknown texture type");
|
for (int i = 0; i < mipmapCount; i++) {
|
||||||
|
if (texture->type == TEXTURE_CUBE) {
|
||||||
|
for (int face = 0; face < 6; face++) {
|
||||||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glTexImage2D(texture->type, i, internalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
}
|
||||||
|
w = MAX(w >> 1, 1);
|
||||||
|
h = MAX(h >> 1, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* lovrTextureCreate(TextureType type, TextureData* slices[6], int sliceCount, bool srgb) {
|
Texture* lovrTextureCreate(TextureType type, TextureData** slices, int sliceCount, bool srgb, bool mipmaps) {
|
||||||
Texture* texture = lovrAlloc(sizeof(Texture), lovrTextureDestroy);
|
Texture* texture = lovrAlloc(sizeof(Texture), lovrTextureDestroy);
|
||||||
if (!texture) return NULL;
|
if (!texture) return NULL;
|
||||||
|
|
||||||
texture->type = type;
|
texture->type = type;
|
||||||
validateSlices(type, slices, sliceCount);
|
texture->slices = calloc(sliceCount, sizeof(TextureData**));
|
||||||
texture->sliceCount = sliceCount;
|
texture->sliceCount = sliceCount;
|
||||||
memcpy(texture->slices, slices, sliceCount * sizeof(TextureData*));
|
|
||||||
texture->srgb = srgb;
|
texture->srgb = srgb;
|
||||||
glGenTextures(1, &texture->id);
|
texture->mipmaps = mipmaps;
|
||||||
lovrGraphicsBindTexture(texture, type, 0);
|
texture->allocated = false;
|
||||||
lovrTextureUpload(texture);
|
|
||||||
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
|
|
||||||
WrapMode wrapMode = (type == TEXTURE_CUBE) ? WRAP_CLAMP : WRAP_REPEAT;
|
|
||||||
lovrTextureSetWrap(texture, (TextureWrap) { .s = wrapMode, .t = wrapMode, .r = wrapMode });
|
|
||||||
|
|
||||||
for (int i = 0; i < sliceCount; i++) {
|
WrapMode wrap = type == TEXTURE_CUBE ? WRAP_CLAMP : WRAP_REPEAT;
|
||||||
lovrRetain(&slices[i]->ref);
|
glGenTextures(1, &texture->id);
|
||||||
|
lovrGraphicsBindTexture(texture, texture->type, 0);
|
||||||
|
lovrTextureSetFilter(texture, lovrGraphicsGetDefaultFilter());
|
||||||
|
lovrTextureSetWrap(texture, (TextureWrap) { .s = wrap, .t = wrap, .r = wrap });
|
||||||
|
|
||||||
|
if (slices) {
|
||||||
|
for (int i = 0; i < sliceCount; i++) {
|
||||||
|
lovrTextureReplacePixels(texture, slices[i], i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
|
@ -146,17 +99,44 @@ Texture* lovrTextureCreate(TextureType type, TextureData* slices[6], int sliceCo
|
||||||
void lovrTextureDestroy(const Ref* ref) {
|
void lovrTextureDestroy(const Ref* ref) {
|
||||||
Texture* texture = containerof(ref, Texture);
|
Texture* texture = containerof(ref, Texture);
|
||||||
for (int i = 0; i < texture->sliceCount; i++) {
|
for (int i = 0; i < texture->sliceCount; i++) {
|
||||||
lovrRelease(&texture->slices[i]->ref);
|
if (&texture->slices[i]) {
|
||||||
|
lovrRelease(&texture->slices[i]->ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
glDeleteTextures(1, &texture->id);
|
glDeleteTextures(1, &texture->id);
|
||||||
|
free(texture->slices);
|
||||||
free(texture);
|
free(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int slice) {
|
void lovrTextureReplacePixels(Texture* texture, TextureData* textureData, int slice) {
|
||||||
lovrAssert(slice >= 0 && slice < texture->sliceCount, "Invalid texture slice to replace: %d", slice);
|
lovrRetain(&textureData->ref);
|
||||||
lovrAssert(textureData->width == texture->width && textureData->height == texture->height, "Texture dimensions must match");
|
if (texture->slices[slice]) {
|
||||||
lovrAssert(textureData->format == texture->slices[0]->format, "Texture formats must match");
|
lovrRelease(&texture->slices[slice]->ref);
|
||||||
lovrTextureUpload(texture);
|
}
|
||||||
|
|
||||||
|
texture->slices[slice] = textureData;
|
||||||
|
|
||||||
|
if (!texture->allocated) {
|
||||||
|
lovrTextureAllocate(texture, textureData);
|
||||||
|
} else {
|
||||||
|
// validation
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum glFormat = lovrTextureFormatGetGLFormat(textureData->format);
|
||||||
|
GLenum glInternalFormat = lovrTextureFormatGetGLInternalFormat(textureData->format, texture->srgb);
|
||||||
|
GLenum binding = (texture->type == TEXTURE_CUBE) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + slice : texture->type;
|
||||||
|
|
||||||
|
if (lovrTextureFormatIsCompressed(textureData->format)) {
|
||||||
|
Mipmap m; int i;
|
||||||
|
vec_foreach(&textureData->mipmaps, m, i) {
|
||||||
|
glCompressedTexImage2D(binding, i, glInternalFormat, m.width, m.height, 0, m.size, m.data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glTexSubImage2D(binding, 0, 0, 0, textureData->width, textureData->height, glFormat, GL_UNSIGNED_BYTE, textureData->data);
|
||||||
|
if (texture->mipmaps) {
|
||||||
|
glGenerateMipmap(texture->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureFilter lovrTextureGetFilter(Texture* texture) {
|
TextureFilter lovrTextureGetFilter(Texture* texture) {
|
||||||
|
@ -164,7 +144,6 @@ TextureFilter lovrTextureGetFilter(Texture* texture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
|
void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
|
||||||
bool hasMipmaps = lovrTextureFormatIsCompressed(texture->slices[0]->format) || texture->slices[0]->generateMipmaps;
|
|
||||||
float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.;
|
float anisotropy = filter.mode == FILTER_ANISOTROPIC ? MAX(filter.anisotropy, 1.) : 1.;
|
||||||
lovrGraphicsBindTexture(texture, texture->type, 0);
|
lovrGraphicsBindTexture(texture, texture->type, 0);
|
||||||
texture->filter = filter;
|
texture->filter = filter;
|
||||||
|
@ -176,7 +155,7 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FILTER_BILINEAR:
|
case FILTER_BILINEAR:
|
||||||
if (hasMipmaps) {
|
if (texture->mipmaps) {
|
||||||
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||||
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
} else {
|
} else {
|
||||||
|
@ -187,7 +166,7 @@ void lovrTextureSetFilter(Texture* texture, TextureFilter filter) {
|
||||||
|
|
||||||
case FILTER_TRILINEAR:
|
case FILTER_TRILINEAR:
|
||||||
case FILTER_ANISOTROPIC:
|
case FILTER_ANISOTROPIC:
|
||||||
if (hasMipmaps) {
|
if (texture->mipmaps) {
|
||||||
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
glTexParameteri(texture->type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(texture->type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TEXTURE_2D = GL_TEXTURE_2D,
|
TEXTURE_2D = GL_TEXTURE_2D,
|
||||||
|
TEXTURE_ARRAY = GL_TEXTURE_2D_ARRAY,
|
||||||
TEXTURE_CUBE = GL_TEXTURE_CUBE_MAP
|
TEXTURE_CUBE = GL_TEXTURE_CUBE_MAP
|
||||||
} TextureType;
|
} TextureType;
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Ref ref;
|
Ref ref;
|
||||||
TextureType type;
|
TextureType type;
|
||||||
TextureData* slices[6];
|
TextureData** slices;
|
||||||
int sliceCount;
|
int sliceCount;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
@ -44,13 +45,15 @@ typedef struct {
|
||||||
TextureFilter filter;
|
TextureFilter filter;
|
||||||
TextureWrap wrap;
|
TextureWrap wrap;
|
||||||
bool srgb;
|
bool srgb;
|
||||||
|
bool mipmaps;
|
||||||
|
bool allocated;
|
||||||
} Texture;
|
} Texture;
|
||||||
|
|
||||||
GLenum lovrTextureFormatGetGLFormat(TextureFormat format);
|
GLenum lovrTextureFormatGetGLFormat(TextureFormat format);
|
||||||
GLenum lovrTextureFormatGetGLInternalFormat(TextureFormat format, bool srgb);
|
GLenum lovrTextureFormatGetGLInternalFormat(TextureFormat format, bool srgb);
|
||||||
bool lovrTextureFormatIsCompressed(TextureFormat format);
|
bool lovrTextureFormatIsCompressed(TextureFormat format);
|
||||||
|
|
||||||
Texture* lovrTextureCreate(TextureType type, TextureData* data[6], int count, bool srgb);
|
Texture* lovrTextureCreate(TextureType type, TextureData** slices, int count, bool srgb, bool mipmaps);
|
||||||
void lovrTextureDestroy(const Ref* ref);
|
void lovrTextureDestroy(const Ref* ref);
|
||||||
void lovrTextureReplacePixels(Texture* texture, TextureData* data, int slice);
|
void lovrTextureReplacePixels(Texture* texture, TextureData* data, int slice);
|
||||||
TextureFilter lovrTextureGetFilter(Texture* texture);
|
TextureFilter lovrTextureGetFilter(Texture* texture);
|
||||||
|
|
|
@ -713,7 +713,6 @@ static ModelData* openvrControllerNewModelData(Controller* controller) {
|
||||||
textureData->format = FORMAT_RGBA;
|
textureData->format = FORMAT_RGBA;
|
||||||
textureData->data = memcpy(malloc(size), vrTexture->rubTextureMapData, size);
|
textureData->data = memcpy(malloc(size), vrTexture->rubTextureMapData, size);
|
||||||
textureData->blob = NULL;
|
textureData->blob = NULL;
|
||||||
textureData->generateMipmaps = true;
|
|
||||||
vec_init(&textureData->mipmaps);
|
vec_init(&textureData->mipmaps);
|
||||||
|
|
||||||
modelData->materials[0].diffuseColor = (Color) { 1, 1, 1, 1 };
|
modelData->materials[0].diffuseColor = (Color) { 1, 1, 1, 1 };
|
||||||
|
|
|
@ -106,6 +106,7 @@ const char* lovrSkyboxVertexShader = ""
|
||||||
"out vec3 texturePosition; \n"
|
"out vec3 texturePosition; \n"
|
||||||
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
|
"vec4 position(mat4 projection, mat4 transform, vec4 vertex) { \n"
|
||||||
" texturePosition = vertex.xyz; \n"
|
" texturePosition = vertex.xyz; \n"
|
||||||
|
" texturePosition.y *= -1; \n"
|
||||||
" return projection * transform * vertex; \n"
|
" return projection * transform * vertex; \n"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue