diff --git a/src/DivaGL/render_texture.cpp b/src/DivaGL/render_texture.cpp index 13f003c..0e6764b 100644 --- a/src/DivaGL/render_texture.cpp +++ b/src/DivaGL/render_texture.cpp @@ -6,6 +6,12 @@ #include "render_texture.hpp" #include "gl_state.hpp" +static uint32_t& render_texture_counter = *(uint32_t*)0x00000001411AD648; + +static int32_t render_texture_init_framebuffer(RenderTexture* rt, int32_t max_level); +static int32_t render_texture_set_framebuffer_texture(RenderTexture* rrt, + GLuint color_texture, int32_t level, GLuint depth_texture, bool stencil); + int32_t RenderTexture::Bind(int32_t index) { if (index < 0 || index > max_level) return -1; @@ -19,25 +25,169 @@ int32_t RenderTexture::Bind(int32_t index) { } void RenderTexture::Free() { - static void (FASTCALL * RenderTexture__Free)(RenderTexture * rt) - = (void (FASTCALL*)(RenderTexture * rt))0x00000001405035C0; - RenderTexture__Free(this); + if (depth_texture) { + texture_free(depth_texture); + depth_texture = 0; + } + + if (color_texture) { + texture_free(color_texture); + color_texture = 0; + } + + if (rbo) { + glDeleteRenderbuffers(1, &rbo); + rbo = 0; + } + + if (field_2C) { + glDeleteRenderbuffers(1, &field_2C); + field_2C = 0; + } + + if (fbos) { + glDeleteFramebuffers(max_level + 1, fbos); + free(fbos); + fbos = 0; + } + max_level = 0; } int32_t RenderTexture::Init(int32_t width, int32_t height, int32_t max_level, GLenum color_format, GLenum depth_format) { - static int32_t(FASTCALL * RenderTexture__Init)(RenderTexture * rt, int32_t width, int32_t height, - int32_t max_level, GLenum color_format, GLenum depth_format) - = (int32_t(FASTCALL*)(RenderTexture * rt, int32_t width, int32_t height, - int32_t max_level, GLenum color_format, GLenum depth_format))0x0000000140503420; - return RenderTexture__Init(this, width, height, max_level, color_format, depth_format); + if (max_level < 0) + return -1; + + max_level = min_def(max_level, 15); + if (max_level < 0) + return 0; + Free(); + + GLuint color_texture; + if (color_format) { + this->color_texture = texture_load_tex_2d(texture_id(0x23, render_texture_counter), + color_format, width, height, max_level, 0, 0); + if (!this->color_texture) + return -1; + + render_texture_counter++; + color_texture = GetColorTex(); + gl_state_bind_texture_2d(GetColorTex()); + if (color_format == GL_RGBA32F) { + glTexParameteriDLL(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteriDLL(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glTexParameteriDLL(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteriDLL(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_state_bind_texture_2d(0); + } + else { + this->color_texture = 0; + color_texture = 0; + } + + GLuint depth_texture; + bool stencil; + if (depth_format) { + this->depth_texture = texture_load_tex_2d(texture_id(0x23, render_texture_counter), + depth_format, width, height, 0, 0, 0); + if (!this->depth_texture) + return -1; + + render_texture_counter++; + depth_texture = this->GetDepthTex(); + stencil = depth_format == GL_DEPTH24_STENCIL8; + } + else { + this->depth_texture = 0; + depth_texture = 0; + stencil = false; + } + this->max_level = max_level; + + if (render_texture_init_framebuffer(this, max_level) >= 0) + for (int32_t i = 0; i <= max_level; i++) + if (render_texture_set_framebuffer_texture(this, color_texture, i, depth_texture, stencil) < 0) + return -1; + return 0; } int32_t RenderTexture::SetColorDepthTextures(GLuint color_texture, - int32_t max_level, GLuint depth_texture) { - int32_t(FASTCALL * RenderTexture__SetColorDepthTextures)(RenderTexture * rt, - GLuint color_texture, int32_t level, GLuint depth_texture) - = (int32_t(FASTCALL*)(RenderTexture * rt, - GLuint color_texture, int32_t level, GLuint depth_texture))0x0000000140503130; - return RenderTexture__SetColorDepthTextures(this, color_texture, max_level, depth_texture); + int32_t max_level, GLuint depth_texture, bool stencil) { + int32_t error = 0; + this->max_level = max_level; + if (!fbos) + error = render_texture_init_framebuffer(this, max_level); + render_texture_set_framebuffer_texture(this, color_texture, max_level, depth_texture, stencil); + return error; +} + +void render_texture_counter_reset() { + render_texture_counter = 0; +} + +static int32_t render_texture_init_framebuffer(RenderTexture* rt, int32_t max_level) { + rt->fbos = force_malloc(max_level + 1LL); + glGenFramebuffers(max_level + 1, rt->fbos); + return -(gl_state_get_error() != GL_ZERO); +} + +static int32_t render_texture_set_framebuffer_texture(RenderTexture* rt, + GLuint color_texture, int32_t level, GLuint depth_texture, bool stencil) { + if (level < 0 || level > rt->max_level) + return -1; + + gl_state_bind_framebuffer(rt->fbos[level]); + gl_state_get_error(); + + if (color_texture) { + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, color_texture, level); + gl_state_get_error(); + glDrawBufferDLL(GL_COLOR_ATTACHMENT0); + glReadBufferDLL(GL_COLOR_ATTACHMENT0); + } + else { + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0); + gl_state_get_error(); + glDrawBufferDLL(GL_ZERO); + glReadBufferDLL(GL_ZERO); + } + gl_state_get_error(); + + if (!level) { + if (stencil) { + if (depth_texture) + glFramebufferTexture(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, depth_texture, 0); + else if (rt->rbo) { + glBindRenderbuffer(GL_RENDERBUFFER, rt->rbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rt->rbo); + } + else + glFramebufferTexture(GL_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, 0, 0); + } + else { + if (depth_texture) + glFramebufferTexture(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, depth_texture, 0); + else if (rt->rbo) { + glBindRenderbuffer(GL_RENDERBUFFER, rt->rbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->rbo); + } + else + glFramebufferTexture(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, 0, 0); + } + gl_state_get_error(); + } + + int32_t ret = 0; + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + ret = -1; + gl_state_bind_framebuffer(0); + gl_state_get_error(); + return ret; } diff --git a/src/DivaGL/render_texture.hpp b/src/DivaGL/render_texture.hpp index af29314..6f81319 100644 --- a/src/DivaGL/render_texture.hpp +++ b/src/DivaGL/render_texture.hpp @@ -23,7 +23,7 @@ struct RenderTexture { int32_t Init(int32_t width, int32_t height, int32_t max_level, GLenum color_format, GLenum depth_format); int32_t SetColorDepthTextures(GLuint color_texture, - int32_t max_level = 0, GLuint depth_texture = 0); + int32_t max_level = 0, GLuint depth_texture = 0, bool stencil = false); inline GLuint GetColorTex() { return color_texture->tex; @@ -47,3 +47,5 @@ struct RenderTexture { }; static_assert(sizeof(RenderTexture) == 0x30, "\"RenderTexture\" struct should have a size of 0x30"); + +extern void render_texture_counter_reset(); diff --git a/src/DivaGL/texture.cpp b/src/DivaGL/texture.cpp index f157346..f83520a 100644 --- a/src/DivaGL/texture.cpp +++ b/src/DivaGL/texture.cpp @@ -9,20 +9,8 @@ #include "gl_state.hpp" #include -static void texture_bind(GLenum target, GLuint texture); - -texture* (FASTCALL* _texture_init)(texture_id id) +texture* (FASTCALL* texture_init)(texture_id id) = (texture * (FASTCALL*)(texture_id id))0x000000014069A390; -texture* (FASTCALL* _texture_copy)(texture_id id, texture* org_tex) - = (texture * (FASTCALL*)(texture_id id, texture * org_tex))0x000000014069B550; -texture* (FASTCALL* texture_load_tex_cubemap)(uint32_t id, GLenum internal_format, - int32_t width, int32_t height, int32_t max_mipmap_level, void** data_ptr) - = (texture * (FASTCALL*)(uint32_t id, GLenum internal_format, int32_t width, - int32_t height, int32_t max_mipmap_level, void** data_ptr))0x000000014069B860; -texture* (FASTCALL* texture_load_tex_2d)(texture_id id, GLenum internal_format, - int32_t width, int32_t height, int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy) - = (texture * (FASTCALL*)(texture_id id, GLenum internal_format, int32_t width, int32_t height, - int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy))0x000000014069B8E0; void(FASTCALL* texture_free)(texture* tex) = (void(FASTCALL*)(texture * tex))0x000000014069DA70; int32_t(FASTCALL* texture_info_get_id)(const char* name) @@ -30,6 +18,20 @@ int32_t(FASTCALL* texture_info_get_id)(const char* name) texture* (FASTCALL* texture_handler_get_texture)(uint32_t id) = (texture * (FASTCALL*)(uint32_t id))0x000000014069CD70; +static void texture_bind(GLenum target, GLuint texture); +static void texture_get_format_type_by_internal_format(GLenum internal_format, GLenum* format, GLenum* type); +static uint32_t texture_get_height_mip_level(texture* tex, int32_t mip_level); +static GLuint texture_get_working_internal_format(GLuint internal_format); +static int32_t texture_get_size(GLenum internal_format, int32_t width, int32_t height); +static int32_t texture_get_size_mip_level(texture* tex, int32_t mip_level); +static uint32_t texture_get_width_mip_level(texture* tex, int32_t mip_level); +static int32_t texture_load(GLenum target, GLenum internal_format, + int32_t width, int32_t height, int32_t level, void* data); +static texture* texture_load_tex(texture_id id, GLenum target, + GLenum internal_format, int32_t width, int32_t height, + int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy); +static GLenum texture_txp_get_gl_internal_format(txp* t); + uint32_t texture::get_height_align_mip_level(uint8_t mip_level) { if (flags & TEXTURE_BLOCK_COMPRESSION) return max_def((uint32_t)height >> mip_level, 4u); @@ -44,6 +46,180 @@ uint32_t texture::get_width_align_mip_level(uint8_t mip_level) { return max_def((uint32_t)width >> mip_level, 1u); } +texture* texture_copy(texture_id id, texture* org_tex) { + if (org_tex->target != GL_TEXTURE_2D) + return 0; + + texture_bind(org_tex->target, org_tex->tex); + std::vector vec_data; + for (int32_t i = 0; i <= org_tex->max_mipmap_level; i++) { + void* data = force_malloc(texture_get_size_mip_level(org_tex, i)); + if (!data) + break; + + if (org_tex->flags & TEXTURE_BLOCK_COMPRESSION) { + glGetCompressedTexImage(org_tex->target, i, data); + } + else { + GLenum format; + GLenum type; + texture_get_format_type_by_internal_format(org_tex->internal_format, &format, &type); + glGetTexImageDLL(org_tex->target, i, format, type, data); + } + gl_state_get_error(); + vec_data.push_back(data); + } + texture_bind(org_tex->target, 0); + + texture* tex = texture_load_tex_2d(id, org_tex->internal_format, + org_tex->width, org_tex->height, org_tex->max_mipmap_level, vec_data.data(), true); + + for (void*& i : vec_data) + free_def(i); + + return tex; +} + +texture* texture_load_tex_2d(texture_id id, GLenum internal_format, int32_t width, int32_t height, + int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy) { + return texture_load_tex(id, GL_TEXTURE_2D, internal_format, + width, height, max_mipmap_level, data_ptr, use_high_anisotropy); +} + +texture* texture_load_tex_cube_map(texture_id id, GLenum internal_format, int32_t width, int32_t height, + int32_t max_mipmap_level, void** data_ptr) { + return texture_load_tex(id, GL_TEXTURE_CUBE_MAP, internal_format, + width, height, max_mipmap_level, data_ptr, false); +} + +texture* texture_txp_load(txp* t, texture_id id) { + if (!t || !t->mipmaps.size()) + return 0; + + int32_t count = t->array_size * t->mipmaps_count; + void** data_ptr = force_malloc(count); + for (uint32_t i = 0, k = 0; i < t->array_size; i++) + for (uint32_t j = 0; j < t->mipmaps_count; j++, k++) + data_ptr[k] = t->mipmaps[k].data.data(); + + GLenum internal_format = texture_txp_get_gl_internal_format(t); + int32_t width = t->mipmaps[0].width; + int32_t height = t->mipmaps[0].height; + int32_t max_mipmap_level = t->mipmaps_count - 1; + + texture* tex; + if (t->has_cube_map) + tex = texture_load_tex_cube_map(id, internal_format, width, height, max_mipmap_level, data_ptr); + else + tex = texture_load_tex_2d(id, internal_format, width, height, max_mipmap_level, data_ptr, true); + free_def(data_ptr); + return tex; +} + +void texture_txp_store(texture* tex, txp* t) { + if (tex->target != GL_TEXTURE_2D && tex->target != GL_TEXTURE_CUBE_MAP) + return; + + int32_t max_mipmap_level = tex->max_mipmap_level; + + t->has_cube_map = tex->target == GL_TEXTURE_CUBE_MAP; + t->array_size = t->has_cube_map ? 6 : 1; + t->mipmaps_count = max_mipmap_level + 1; + t->mipmaps.resize((size_t)t->mipmaps_count * t->array_size); + + txp_format format = TXP_RGBA8; + switch (tex->internal_format) { + case GL_ALPHA8: + format = TXP_A8; + break; + case GL_RGB8: + format = TXP_RGB8; + break; + case GL_RGBA8: + format = TXP_RGBA8; + break; + case GL_RGB5: + format = TXP_RGB5; + break; + case GL_RGB5_A1: + format = TXP_RGB5A1; + break; + case GL_RGBA4: + format = TXP_RGBA4; + break; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + format = TXP_BC1; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + format = TXP_BC1a; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + format = TXP_BC2; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + format = TXP_BC3; + break; + case GL_COMPRESSED_RED_RGTC1_EXT: + format = TXP_BC4; + break; + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + format = TXP_BC5; + break; + case GL_LUMINANCE8: + format = TXP_L8; + break; + case GL_LUMINANCE8_ALPHA8: + format = TXP_L8A8; + break; + } + + auto get_tex_mipmap_data = [&](txp_mipmap& mipmap, GLenum target, int32_t mip_level) { + mipmap.width = texture_get_width_mip_level(tex, mip_level); + mipmap.height = texture_get_height_mip_level(tex, mip_level); + mipmap.format = format; + mipmap.size = texture_get_size_mip_level(tex, mip_level); + mipmap.data.resize(mipmap.size); + + if (tex->flags & TEXTURE_BLOCK_COMPRESSION) + glGetCompressedTexImage(target, mip_level, mipmap.data.data()); + else { + GLenum format; + GLenum type; + texture_get_format_type_by_internal_format(tex->internal_format, &format, &type); + glGetTexImageDLL(target, mip_level, format, type, mipmap.data.data()); + } + gl_state_get_error(); + }; + + texture_bind(tex->target, tex->tex); + int32_t size = 0; + if (t->has_cube_map) + for (int32_t i = 0; i < 6; i++) + for (int32_t j = 0; j <= max_mipmap_level; j++) { + static const GLenum target_cube_map_array[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + }; + + get_tex_mipmap_data(t->mipmaps[i * ((int64_t)max_mipmap_level + 1) + j], + target_cube_map_array[i], j); + } + else + for (int32_t i = 0; i <= max_mipmap_level; i++) + get_tex_mipmap_data(t->mipmaps[i], GL_TEXTURE_2D, i); + texture_bind(tex->target, 0); +} + +void texture_array_free(texture** arr) { + if (!arr) + return; + + for (texture** i = arr; *i; i++) + texture_free(*i); + free_def(arr); +} + void texture_set_params(GLenum target, int32_t max_mipmap_level, bool use_high_anisotropy) { glTexParameteriDLL(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteriDLL(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -108,13 +284,404 @@ void texture_params_restore(texture_param* tex_0_param, gl_state_active_bind_texture_2d(i, 0); } +bool texture_txp_set_load(txp_set* t, texture*** texs, uint32_t* ids) { + if (!t || !texs || !ids) + return false; + + size_t count = t->textures.size(); + *texs = force_malloc(count + 1); + texture** tex = *texs; + for (size_t i = 0; i < count; i++) + tex[i] = texture_txp_load(&t->textures[i], texture_id(0, ids[i])); + tex[count] = 0; + return true; +} + +bool texture_txp_set_load(txp_set* t, texture*** texs, texture_id* ids) { + if (!t || !texs || !ids) + return false; + + size_t count = t->textures.size(); + *texs = force_malloc(count + 1); + texture** tex = *texs; + for (size_t i = 0; i < count; i++) + tex[i] = texture_txp_load(&t->textures[i], ids[i]); + tex[count] = 0; + return true; +} + inline static void texture_bind(GLenum target, GLuint texture) { switch (target) { case GL_TEXTURE_2D: - gl_state_bind_texture_2d(texture, true); + gl_state_bind_texture_2d(texture); break; case GL_TEXTURE_CUBE_MAP: - gl_state_bind_texture_cube_map(texture, true); + gl_state_bind_texture_cube_map(texture); break; } } + +static void texture_get_format_type_by_internal_format(GLenum internal_format, GLenum* format, GLenum* type) { + GLenum _format; + GLenum _type; + switch (internal_format) { + case GL_ALPHA8: + _format = GL_ALPHA; + _type = GL_UNSIGNED_BYTE; + break; + case GL_LUMINANCE8: + _format = GL_LUMINANCE; + _type = GL_UNSIGNED_BYTE; + break; + case GL_LUMINANCE8_ALPHA8: + _format = GL_LUMINANCE_ALPHA; + _type = GL_UNSIGNED_BYTE; + break; + case GL_INTENSITY8: + _format = GL_INTENSITY; + _type = GL_UNSIGNED_BYTE; + break; + case GL_RGB5: + _format = GL_RGB; + _type = GL_UNSIGNED_SHORT_5_6_5_REV; + break; + case GL_RGB8: + _format = GL_RGB; + _type = GL_UNSIGNED_BYTE; + break; + case GL_RGBA4: + _format = GL_RGBA; + _type = GL_UNSIGNED_SHORT_4_4_4_4_REV; + break; + case GL_RGB5_A1: + _format = GL_RGBA; + _type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + case GL_RGBA8: + _format = GL_RGBA; + _type = GL_UNSIGNED_BYTE; + break; + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + _format = GL_DEPTH_COMPONENT; + _type = GL_FLOAT; + break; + case GL_RG8: + _format = GL_RG; + _type = GL_UNSIGNED_BYTE; + break; + case GL_R32F: + _format = GL_RED; + _type = GL_FLOAT; + break; + case GL_RG32F: + _format = GL_RG; + _type = GL_FLOAT; + break; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + _format = GL_RGB; + _type = GL_ZERO; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + _format = GL_RGBA; + _type = GL_ZERO; + break; + case GL_RGBA32F: + _format = GL_RGBA; + _type = GL_FLOAT; + break; + case GL_RGBA16F: + _format = GL_RGBA; + _type = GL_HALF_FLOAT; + break; + case GL_DEPTH24_STENCIL8: + _format = GL_DEPTH_STENCIL; + _type = GL_UNSIGNED_INT_24_8; + break; + case GL_R11F_G11F_B10F: + _format = GL_RGB; + _type = GL_UNSIGNED_INT_10F_11F_11F_REV; + break; + case GL_RGB9_E5: + _format = GL_RGB; + _type = GL_UNSIGNED_INT_5_9_9_9_REV; + break; + case GL_DEPTH_COMPONENT32F: + _format = GL_DEPTH_COMPONENT; + _type = GL_FLOAT; + break; + case GL_COMPRESSED_RED_RGTC1: + _format = GL_RED; + _type = GL_ZERO; + break; + case GL_COMPRESSED_RG_RGTC2: + _format = GL_RG; + _type = GL_ZERO; + break; + default: + _format = GL_ZERO; + _type = GL_ZERO; + break; + } + + if (format) + *format = _format; + if (type) + *type = _type; +} + +inline static uint32_t texture_get_height_mip_level(texture* tex, int32_t mip_level) { + return max_def((uint32_t)tex->height >> mip_level, 1u); +} + +inline static GLuint texture_get_working_internal_format(GLuint internal_format) { + switch (internal_format) { + case GL_ALPHA8: + return GL_R8; + case GL_LUMINANCE8: + return GL_R8; + case GL_LUMINANCE8_ALPHA8: + return GL_RG8; + default: + return internal_format; + } +} + +static int32_t texture_get_size(GLenum internal_format, int32_t width, int32_t height) { + int32_t size = width * height; + switch (internal_format) { + case GL_ALPHA8: + case GL_LUMINANCE8: + case GL_INTENSITY8: + case GL_R8: + return size; + case GL_LUMINANCE8_ALPHA8: + case GL_RGB5: + case GL_RGBA4: + case GL_RGB5_A1: + case GL_DEPTH_COMPONENT16: + case GL_RG8: + return size * 2; + case GL_RGB8: + case GL_RGBA8: + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + case GL_R32F: + case GL_RGBA16F: + case GL_DEPTH24_STENCIL8: + case GL_DEPTH_COMPONENT32F: + case GL_R11F_G11F_B10F: + case GL_RGB9_E5: + return size * 4; + case GL_RG32F: + return size * 8; + case GL_RGBA32F: + return size * 16; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RED_RGTC1: + width = align_val(width, 4); + height = align_val(height, 4); + return width * height / 2; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RG_RGTC2: + width = align_val(width, 4); + height = align_val(height, 4); + return width * height; + default: + return 0; + } +} + +inline static int32_t texture_get_size_mip_level(texture* tex, int32_t mip_level) { + return texture_get_size(tex->internal_format, + texture_get_width_mip_level(tex, mip_level), + texture_get_height_mip_level(tex, mip_level)); +} + +inline static uint32_t texture_get_width_mip_level(texture* tex, int32_t mip_level) { + return max_def((uint32_t)tex->width >> mip_level, 1u); +} + +static int32_t texture_load(GLenum target, GLenum internal_format, + int32_t width, int32_t height, int32_t level, void* data) { + gl_state_get_all_gl_errors(); + glPixelStoreiDLL(GL_UNPACK_ALIGNMENT, 1); + switch (internal_format) { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RED_RGTC1_EXT: + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: { + int32_t size = texture_get_size(internal_format, width, height); + glCompressedTexImage2D(target, level, internal_format, width, height, 0, size, data); + } break; + default: { + GLenum format; + GLenum type; + texture_get_format_type_by_internal_format(internal_format, &format, &type); + glTexImage2DDLL(target, level, internal_format, width, height, 0, format, type, data); + } break; + } + return -(gl_state_get_error() != GL_ZERO); +} + +static texture* texture_load_tex(texture_id id, GLenum target, + GLenum internal_format, int32_t width, int32_t height, + int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy) { + texture* tex = texture_init(id); + if (tex->init_count > 1) + return tex; + + glGenTextures(1, &tex->tex); + texture_bind(target, tex->tex); + texture_set_params(target, max_mipmap_level, use_high_anisotropy); + + GLint swizzle[4]; + switch (internal_format) { + case GL_ALPHA8: + swizzle[0] = GL_ZERO; + swizzle[1] = GL_ZERO; + swizzle[2] = GL_ZERO; + swizzle[3] = GL_RED; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + break; + case GL_COMPRESSED_RED_RGTC1_EXT: + swizzle[0] = GL_RED; + swizzle[1] = GL_RED; + swizzle[2] = GL_RED; + swizzle[3] = GL_ONE; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + break; + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + swizzle[0] = GL_RED; + swizzle[1] = GL_GREEN; + swizzle[2] = GL_ZERO; + swizzle[3] = GL_RED; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + break; + case GL_LUMINANCE8: + swizzle[0] = GL_RED; + swizzle[1] = GL_RED; + swizzle[2] = GL_RED; + swizzle[3] = GL_ONE; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + break; + case GL_LUMINANCE8_ALPHA8: + swizzle[0] = GL_RED; + swizzle[1] = GL_RED; + swizzle[2] = GL_RED; + swizzle[3] = GL_GREEN; + glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + break; + } + + GLuint working_internal_format = texture_get_working_internal_format(internal_format); + + int32_t size = 0; + if (target == GL_TEXTURE_CUBE_MAP) + for (int32_t i = 0; i < 6; i++) + for (int32_t j = 0; j <= max_mipmap_level; j++) { + int32_t mip_width = max_def(width >> j, 1); + int32_t mip_height = max_def(height >> j, 1); + void* data; + if (data_ptr) + data = data_ptr[i * (max_mipmap_level + 1) + j]; + else + data = 0; + + static const GLenum target_cube_map_array[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + }; + + if (texture_load(target_cube_map_array[i], + working_internal_format, mip_width, mip_height, j, data) < 0) + goto fail; + size += texture_get_size(internal_format, mip_width, mip_height); + } + else + for (int32_t i = 0; i <= max_mipmap_level; i++) { + int32_t mip_width = max_def(width >> i, 1); + int32_t mip_height = max_def(height >> i, 1); + void* data; + if (data_ptr) + data = data_ptr[i]; + else + data = 0; + + if (texture_load(target, + working_internal_format, mip_width, mip_height, i, data) < 0) + goto fail; + size += texture_get_size(internal_format, mip_width, mip_height); + } + + texture_bind(target, 0); + + tex->target = target; + tex->width = (int16_t)width; + tex->height = (int16_t)height; + tex->size = size; + tex->internal_format = internal_format; + tex->max_mipmap_level = max_mipmap_level; + + switch (internal_format) { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL_COMPRESSED_RED_RGTC1_EXT: + case GL_COMPRESSED_RED_GREEN_RGTC2_EXT: + enum_or(tex->flags, TEXTURE_BLOCK_COMPRESSION); + break; + } + return tex; + +fail: + texture_bind(target, 0); + texture_free(tex); + return 0; +} + +static GLenum texture_txp_get_gl_internal_format(txp* t) { + if (!t || !t->mipmaps.size()) + return GL_ZERO; + + switch (t->mipmaps[0].format) { + case TXP_A8: + return GL_ALPHA8; + case TXP_RGB8: + return GL_RGB8; + case TXP_RGBA8: + return GL_RGBA8; + case TXP_RGB5: + return GL_RGB5; + case TXP_RGB5A1: + return GL_RGB5_A1; + case TXP_RGBA4: + return GL_RGBA4; + case TXP_BC1: + return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case TXP_BC1a: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case TXP_BC2: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case TXP_BC3: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case TXP_BC4: + return GL_COMPRESSED_RED_RGTC1_EXT; + case TXP_BC5: + return GL_COMPRESSED_RED_GREEN_RGTC2_EXT; + case TXP_L8: + return GL_LUMINANCE8; + case TXP_L8A8: + return GL_LUMINANCE8_ALPHA8; + default: + return GL_ZERO; + } +} diff --git a/src/DivaGL/texture.hpp b/src/DivaGL/texture.hpp index 09cb009..f3c0394 100644 --- a/src/DivaGL/texture.hpp +++ b/src/DivaGL/texture.hpp @@ -80,15 +80,18 @@ struct texture_param { static_assert(sizeof(texture_param) == 0x08, "\"texture_param\" struct should have a size of 0x08"); extern texture* (FASTCALL* texture_init)(texture_id id); -extern texture* (FASTCALL* texture_copy)(texture_id id, texture* org_tex); -extern texture* (FASTCALL* texture_load_tex_cubemap)(uint32_t id, GLenum internal_format, - int32_t width, int32_t height, int32_t max_mipmap_level, void** data_ptr); -extern texture* (FASTCALL* texture_load_tex_2d)(texture_id id, GLenum internal_format, - int32_t width, int32_t height, int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy); extern void(FASTCALL* texture_free)(texture* tex); extern int32_t(FASTCALL* texture_info_get_id)(const char* name); extern texture* (FASTCALL* texture_handler_get_texture)(uint32_t id); +extern texture* texture_copy(texture_id id, texture* org_tex); +extern texture* texture_load_tex_2d(texture_id id, GLenum internal_format, int32_t width, int32_t height, + int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy); +extern texture* texture_load_tex_cube_map(texture_id id, GLenum internal_format, int32_t width, int32_t height, + int32_t max_mipmap_level, void** data_ptr); +extern texture* texture_txp_load(txp* t, texture_id id); +extern void texture_txp_store(texture* tex, txp* t); + extern void texture_set_params(GLenum target, int32_t max_mipmap_level, bool use_high_anisotropy); extern void texture_params_get(GLuint tex_0 = 0, texture_param* tex_0_param = 0,