/* by korenkonder GitHub/GitLab: korenkonder */ #include "sprite.hpp" #include "../KKdLib/prj/shared_ptr.hpp" #include "../KKdLib/prj/stack_allocator.hpp" #include "../KKdLib/farc.hpp" #include "../KKdLib/txp.hpp" #include "GL/array_buffer.hpp" #include "GL/element_array_buffer.hpp" #include "data.hpp" #include "file_handler.hpp" #include "gl.hpp" #include "gl_state.hpp" #include "render_context.hpp" #include "shader_ft.hpp" #include "texture.hpp" class SprSet { public: int32_t flag; uint32_t index; ::spr_set* spr_set; texture** textures; int32_t load_count; p_file_handler file_handler; bool ready; prj::shared_ptr alloc_handler; std::string file; std::string name; std::vector sprite_ids; uint32_t hash; SprSet(uint32_t index); virtual ~SprSet(); const char* GetName(spr_info info); rectangle GetRectangle(spr_info info); SCREEN_MODE GetResolutionMode(spr_info info); texture* GetTexture(spr_info info); bool LoadData(); bool LoadDataModern(const void* data, size_t size); bool LoadFile(); bool LoadFileModern(sprite_database* spr_db); bool LoadTexture(); bool LoadTextureModern(const void* data, size_t size); void ReadFile(const char* file, std::string& mdata_dir, void* data); void ReadFileModern(uint32_t set_hash, void* data); void Unload(); void UnloadModern(sprite_database* spr_db); bool UnloadTexture(); }; struct sprite_vertex_data_uv0 { vec3 pos; color4u8 color; }; struct sprite_vertex_data_uv1 { vec3 pos; color4u8 color; vec2 uv; }; struct sprite_vertex_data_uv2 { vec3 pos; color4u8 color; vec2 uv[2]; }; struct sprite_draw_param_attrib_member { uint32_t primitive : 4; uint32_t enable_blend : 1; uint32_t blend : 3; uint32_t sampler : 2; uint32_t vao : 2; }; union sprite_draw_param_attrib { sprite_draw_param_attrib_member m; uint32_t w; }; struct sprite_draw_param { union { GLint first; struct { GLuint start; GLuint end; }; }; GLsizei count; GLintptr offset; const texture* texs[2]; int32_t shader_name; sprite_draw_param_attrib attrib; vec3 vtx[4]; }; namespace spr { struct SprArgsDraw { int32_t num_texture; int32_t blend; const texture* texs[4]; int32_t shader_name; spr::SprKind kind; }; struct TexCoord { struct UV { float_t u; float_t v; inline UV() : u(), v() { } inline operator vec2() const { return { u, v }; } }; UV uv[4]; inline TexCoord() { } }; struct TexParam { const texture* tex; TexCoord texcoord; //int32_t pad[2]; inline TexParam() : tex()/*, pad()*/ { } }; struct SpriteManager { struct RenderData { std::vector draw_param_buffer; std::vector vertex_buffer; std::vector index_buffer; GLuint vao[3]; GL::ArrayBuffer vbo; size_t vbo_vertex_count; GL::ElementArrayBuffer ebo; size_t ebo_index_count; RenderData(); ~RenderData(); template size_t AddData(T*& data, size_t nb_quad); void Clear(); void Update(); }; struct ViewPort { SCREEN_MODE screen; rectangle rect; ViewPort(); }; std::map sets; std::list reqlist[SPR_TARGET_MAX][SPR_LAYER_MAX][SPR_PRIO_MAX]; float_t aspect[2]; ViewPort view_list[2]; SprTarget target; mat4 view_projection; mat4 mat; SCREEN_MODE screen_mode_back; uint32_t set_counter; RenderData render_data; SpriteManager(); ~SpriteManager(); uint32_t AddSetModern(); void AddSprSets(const sprite_database* spr_db); void Clear(); void Draw(render_data_context& rend_data_ctx, int32_t index, bool font, texture* overlay_tex, const mat4& vp); SprSet* GetSet(uint32_t index); bool GetSetReady(uint32_t index); uint32_t GetSetSpriteNum(uint32_t index); uint32_t GetSetTextureNum(uint32_t index); const char* GetSprName(spr_info info); rectangle GetSprRectangle(spr_info info); texture* GetSprTexture(spr_info info); bool LoadFile(uint32_t index); bool LoadFileModern(uint32_t index, sprite_database* spr_db); SprArgs* PutSprite(const SprArgs& args, const sprite_database* spr_db); void ReadFile(uint32_t index, const char* file, std::string& mdata_dir, void* data); void ReadFileModern(uint32_t index, uint32_t set_hash, void* data, sprite_database* spr_db); void RemoveSprSets(const sprite_database* spr_db); void ResetTarget(); void ResetReqList(); void ResetResData(); void PostDraw(); void PreDraw(); void UnloadSet(uint32_t index); void UnloadSetModern(uint32_t index, sprite_database* spr_db); }; static void calc_sprite_vertex(spr::SprArgs* args, vec3 vtx[4], mat4* mat, bool font); static int32_t calc_sprite_texture_param(SprArgs* args, spr::TexParam* param, vec3 vtx[4], bool font); static int32_t calc_sprite_vertex_texture_param(SprArgs& args, vec3 vtx[4], spr::TexParam* param, bool font); static void add_draw_sprite(SprArgs* args_array[0x100], const int32_t args_count, bool font, SprArgsDraw& args_draw, SpriteManager::RenderData& render_data); static void draw_sprite(render_data_context& rend_data_ctx, spr::SpriteManager* sprite_manager, const SprArgs& args, const mat4& mat, int32_t x_min, int32_t y_min, int32_t x_max, int32_t y_max, texture* overlay_tex); static void draw_sprite_begin(render_data_context& rend_data_ctx); static void draw_sprite_copy_overlay_texture( render_data_context& rend_data_ctx, const SprArgs& args, const mat4& mat, const vec3 vtx[4], int32_t overlay_x_min, int32_t overlay_y_min, int32_t overlay_x_max, int32_t overlay_y_max); static void draw_sprite_end(render_data_context& rend_data_ctx); static void draw_sprite_scale(spr::SprArgs* args); } spr::SpriteManager* sprite_manager; #if BREAK_SPRITE_VERTEX_LIMIT size_t string_quad_max_count = 0x2000; spr::SprArgs::Quad* string_quad; #else const size_t string_quad_max_count = 0x2000; spr::SprArgs::Quad string_quad[string_quad_max_count]; #endif size_t string_quad_idx; extern render_context* rctx_ptr; static const GLenum spr_blend_param[6][4] = { { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE }, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE }, { GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE }, { GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE }, { GL_ONE, GL_ZERO, GL_ONE, GL_ZERO }, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE }, }; namespace spr { SprArgs::SprArgs() : kind(), attr(), blend(), target(), layer(), prio(), screen_trans(), screen_scale(), tex(), shader_name(), sprite_draw_param_index(), quad(), nb_quad(), flags(), next() { Reset(); } inline SprArgs::Quad* SprArgs::GetVertexArray() { #if BREAK_SPRITE_VERTEX_LIMIT return string_quad + quad; #else return quad; #endif } void SprArgs::Reset() { kind = SPR_KIND_NORMAL; id = {}; color = 0xFFFFFFFF; attr = (SprAttr)0; blend = SPR_BLEND_DEFAULT; target = SPR_TARGET_DEFAULT; layer = SPR_LAYER_DEFAULT; prio = SPR_PRIO_DEFAULT; screen_trans = SCREEN_MODE_HD; screen_scale = SCREEN_MODE_HD; anchor = 0.0f; trans = 0.0f; scale = 1.0f; rot = 0.0f; slant = 0.0f; shear = 0.0f; matrix = mat4_identity; tex = 0; shader_name = SHADER_FT_FFP; sprite_draw_param_index = -1; #if BREAK_SPRITE_VERTEX_LIMIT quad = -1; #else quad = 0; #endif nb_quad = 0; flags = (SprArgs::Flags)0; size = 0.0f; rect.x = 0.0f; rect.y = 0.0f; rect.width = 1.0f; rect.height = 1.0f; next = 0; } void SprArgs::SetRect(float_t x, float_t y, float_t width, float_t height) { if (flags & TEXTURE_POS_SIZE) return; enum_or(flags, TEXTURE_POS_SIZE); rect.x = x; rect.y = y; rect.width = width; rect.height = height; } void SprArgs::SetQuadArgs(SprArgs::Quad* quad_arg, size_t len) { #if BREAK_SPRITE_VERTEX_LIMIT if (string_quad_idx + len >= string_quad_max_count) { while (string_quad_idx + len >= string_quad_max_count) string_quad_max_count *= 2; spr::SprArgs::Quad* _string_quad = new spr::SprArgs::Quad[string_quad_max_count]; memmove(_string_quad, string_quad, sizeof(SprArgs::Quad) * string_quad_idx); delete[] string_quad; string_quad = _string_quad; } #else if (string_quad_idx + len >= string_quad_max_count) return; #endif this->nb_quad = len; #if BREAK_SPRITE_VERTEX_LIMIT this->quad = string_quad_idx; memmove(string_quad + this->quad, quad_arg, sizeof(SprArgs::Quad) * len); #else this->quad = &string_quad[string_quad_idx]; memmove(this->quad, quad_arg, sizeof(SprArgs::Quad) * len); #endif string_quad_idx += len; } void SprArgs::SetSize(vec2 size) { if (flags & SPRITE_SIZE) return; enum_or(flags, SPRITE_SIZE); this->size.x = size.x; this->size.y = size.y; this->size.z = 0.0f; } void SprArgs::SetChild(SprArgs* args, SprArgs* next) { while (args->next) args = args->next; args->next = next; next->kind = SPR_KIND_MULTI; } vec2 proj_sprite_3d_line(vec3 vec, bool offset) { camera* cam = rctx_ptr->camera; mat4_transform_point(&cam->view, &vec, &vec); if (fabsf(vec.z) < 1.0e-10f) return 0.0f; vec2 sc_vec = cam->depth * *(vec2*)&vec.x * (1.0f / vec.z); ScreenParam& render_screen_param = get_render_screen_param(); sc_vec.x = (float_t)render_screen_param.width * 0.5f - sc_vec.x; sc_vec.y = (float_t)render_screen_param.height * 0.5f + sc_vec.y; if (offset) { ScreenParam& screen_param = get_screen_param(); sc_vec.x = (float_t)render_screen_param.xoffset + sc_vec.x; sc_vec.y = (float_t)(screen_param.height - render_screen_param.yoffset - render_screen_param.height) + sc_vec.y; } return sc_vec; } void put_cross(const mat4& mat, color4u8 color_x, color4u8 color_y, color4u8 color_z) { vec3 v5 = { 0.0f, 0.0f, 0.0f }; vec3 v6; mat4_transform_point(&mat, &v5, &v6); vec3 v7 = v6; v5 = { 1.0f, 0.0f, 0.0f }; mat4_transform_point(&mat, &v5, &v6); put_sprite_3d_line(v7, v6, color_x); v5 = { 0.0f, 1.0f, 0.0f }; mat4_transform_point(&mat, &v5, &v6); put_sprite_3d_line(v7, v6, color_y); v5 = { 0.0f, 0.0f, 1.0f }; mat4_transform_point(&mat, &v5, &v6); put_sprite_3d_line(v7, v6, color_z); } void put_rgb_cross(const mat4& mat) { put_cross(mat, color_red, color_green, color_blue); } SprArgs* put_sprite(const SprArgs& args, const sprite_database* spr_db) { return sprite_manager->PutSprite(args, spr_db); } void put_sprite_3d_line(vec3 p1, vec3 p2, color4u8 color) { vec2 sc_p1 = proj_sprite_3d_line(p1, true); vec2 sc_p2 = proj_sprite_3d_line(p2, true); put_sprite_line(sc_p1, sc_p2, SCREEN_MODE_MAX, SPR_PRIO_DW, color, SPR_LAYER_DEFAULT); } void put_sprite_line(vec2 p1, vec2 p2, SCREEN_MODE screen_mode, SprPrio prio, color4u8 color, SprLayer layer) { SprArgs args; args.trans.x = p1.x; args.trans.y = p1.y; args.trans.z = 0.0f; args.layer = layer; args.kind = SPR_KIND_LINE; args.screen_trans = screen_mode; args.screen_scale = screen_mode; args.prio = prio; args.color = color; args.SetSize({ p2.x - p1.x, p2.y - p1.y }); enum_or(args.attr, SPR_ATTR_NOTRANSFORM); sprite_manager->PutSprite(args, 0); } void put_sprite_poly_line(vec2* points, size_t count, SCREEN_MODE screen_mode, SprPrio prio, color4u8 color, SprLayer layer) { SprArgs args; args.kind = SPR_KIND_POLY_LINE; args.layer = layer; args.screen_trans = screen_mode; args.screen_scale = screen_mode; args.prio = prio; args.color = color; std::vector quad; quad.reserve(count); for (size_t i = 0; i < count; i++, points++) { SprArgs::Quad vert = {}; *(vec2*)&vert.pos = *points; quad.push_back(vert); } args.SetQuadArgs(quad.data(), quad.size()); sprite_manager->PutSprite(args, 0); } void put_sprite_line_box(rectangle rect, SCREEN_MODE screen_mode, SprPrio prio, color4u8 color, SprLayer layer) { SprArgs args; args.trans.x = rect.pos.x; args.trans.y = rect.pos.y; args.trans.z = 0.0f; args.layer = layer; args.kind = SPR_KIND_LINE_BOX; args.screen_trans = screen_mode; args.screen_scale = screen_mode; args.prio = prio; args.color = color; args.SetSize(rect.size); enum_or(args.attr, SPR_ATTR_NOTRANSFORM); sprite_manager->PutSprite(args, 0); } void put_sprite_rect(rectangle rect, SCREEN_MODE screen_mode, SprPrio prio, color4u8 color, SprLayer layer) { SprArgs args; args.trans.x = rect.pos.x; args.trans.y = rect.pos.y; args.trans.z = 0.0f; args.layer = layer; args.kind = SPR_KIND_RECT; args.screen_trans = screen_mode; args.screen_scale = screen_mode; args.prio = prio; args.color = color; args.SetSize(rect.size); enum_or(args.attr, SPR_ATTR_NOTRANSFORM); sprite_manager->PutSprite(args, 0); } void put_sprite_strip(SprArgs::Quad* vert, size_t num, SCREEN_MODE screen_mode, SprPrio prio, int32_t spr_id, SprLayer layer, const sprite_database* spr_db) { SprArgs args; args.layer = layer; args.kind = SPR_KIND_STRIP; args.screen_trans = screen_mode; args.screen_scale = screen_mode; args.prio = prio; args.color = vert[0].color; if (spr_id != -1) args.id.info = spr_db->get_spr_by_id(spr_id)->info; enum_or(args.attr, SPR_ATTR_NOTRANSFORM); args.SetQuadArgs(vert, num); sprite_manager->PutSprite(args, spr_db); } } void sprite_manager_init() { if (!sprite_manager) sprite_manager = new spr::SpriteManager; #if BREAK_SPRITE_VERTEX_LIMIT if (!string_quad) string_quad = new spr::SprArgs::Quad[string_quad_max_count]; #endif } void sprite_manager_add_spr_sets(const sprite_database* spr_db) { sprite_manager->AddSprSets(spr_db); } void sprite_manager_clear() { sprite_manager->Clear(); } void sprite_manager_draw(render_data_context& rend_data_ctx, int32_t index, bool font, texture* overlay_tex, const mat4& vp) { sprite_manager->Draw(rend_data_ctx, index, font, overlay_tex, vp); } spr::SprTarget sprite_manager_get_target() { return sprite_manager->target; } size_t sprite_manager_get_reqlist_count(int32_t index) { size_t count = 0; for (const auto& i : sprite_manager->reqlist[index]) for (const auto& j : i) count += j.size(); return count; } ::spr_set* sprite_manager_get_set(uint32_t spr_id, const sprite_database* spr_db) { SprSet* set = sprite_manager->GetSet(spr_db->get_spr_set_by_id(spr_id)->index); if (set) return set->spr_set; return 0; } bool sprite_manager_get_set_ready(uint32_t set_id, const sprite_database* spr_db) { return sprite_manager->GetSetReady(spr_db->get_spr_set_by_id(set_id)->index); } uint32_t sprite_manager_get_set_sprite_num(uint32_t set_id, const sprite_database* spr_db) { return sprite_manager->GetSetSpriteNum(spr_db->get_spr_set_by_id(set_id)->index); } uint32_t sprite_manager_get_set_texture_num(uint32_t set_id, const sprite_database* spr_db) { return sprite_manager->GetSetTextureNum(spr_db->get_spr_set_by_id(set_id)->index); } const char* sprite_manager_get_spr_name(uint32_t spr_id, const sprite_database* spr_db) { return sprite_manager->GetSprName(spr_db->get_spr_by_id(spr_id)->info); } rectangle sprite_manager_get_spr_rectangle(uint32_t spr_id, const sprite_database* spr_db) { return sprite_manager->GetSprRectangle(spr_db->get_spr_by_id(spr_id)->info); } texture* sprite_manager_get_spr_texture(uint32_t spr_id, const sprite_database* spr_db) { return sprite_manager->GetSprTexture(spr_db->get_spr_by_id(spr_id)->info); } bool sprite_manager_load_file(uint32_t set_id, const sprite_database* spr_db) { return sprite_manager->LoadFile(spr_db->get_spr_set_by_id(set_id)->index); } bool sprite_manager_load_file_modern(uint32_t set_hash, sprite_database* spr_db) { return sprite_manager->LoadFileModern(spr_db->get_spr_set_by_id(set_hash)->index, spr_db); } void sprite_manager_post_draw() { sprite_manager->PostDraw(); } void sprite_manager_pre_draw() { sprite_manager->PreDraw(); } void sprite_manager_read_file(uint32_t set_id, std::string& mdata_dir, void* data, const sprite_database* spr_db) { const spr_db_spr_set* spr_set = spr_db->get_spr_set_by_id(set_id); sprite_manager->ReadFile(spr_set->index, spr_set->file_name.c_str(), mdata_dir, data); } void sprite_manager_read_file(uint32_t set_id, std::string&& mdata_dir, void* data, const sprite_database* spr_db) { const spr_db_spr_set* spr_set = spr_db->get_spr_set_by_id(set_id); sprite_manager->ReadFile(spr_set->index, spr_set->file_name.c_str(), mdata_dir, data); } void sprite_manager_read_file_modern(uint32_t set_hash, void* data, sprite_database* spr_db) { const spr_db_spr_set* spr_set = spr_db->get_spr_set_by_id(set_hash); sprite_manager->ReadFileModern(spr_set->index, set_hash, data, spr_db); } void sprite_manager_remove_spr_sets(const sprite_database* spr_db) { sprite_manager->RemoveSprSets(spr_db); } void sprite_manager_reset_req_list() { sprite_manager->ResetReqList(); } void sprite_manager_reset_res_data() { sprite_manager->ResetResData(); } void sprite_manager_set_target(spr::SprTarget value) { sprite_manager->target = value; } void sprite_manager_set_res(double_t aspect, int32_t width, int32_t height) { if (!sprite_manager) return; sprite_manager->aspect[0] = (float_t)aspect; sprite_manager->view_list[0].rect = { 0.0f, 0.0f, (float_t)width, (float_t)height }; sprite_manager->aspect[1] = (float_t)aspect; sprite_manager->view_list[1].rect = { 0.0f, 0.0f, (float_t)width, (float_t)height }; } void sprite_manager_unload_set(uint32_t set_id, const sprite_database* spr_db) { sprite_manager->UnloadSet(spr_db->get_spr_set_by_id(set_id)->index); } void sprite_manager_unload_set_modern(uint32_t set_hash, sprite_database* spr_db) { sprite_manager->UnloadSetModern(spr_db->get_spr_set_by_id(set_hash)->index, spr_db); } void sprite_manager_free() { #if BREAK_SPRITE_VERTEX_LIMIT if (string_quad) { delete[] string_quad; string_quad = 0; } #endif if (sprite_manager) { delete sprite_manager; sprite_manager = 0; } } namespace spr { SpriteManager::RenderData::RenderData() : vao() { vbo_vertex_count = 4096; ebo_index_count = 4096; static const GLsizei buffer_size_uv0 = sizeof(sprite_vertex_data_uv0); static const GLsizei buffer_size_uv1 = sizeof(sprite_vertex_data_uv1); static const GLsizei buffer_size_uv2 = sizeof(sprite_vertex_data_uv2); vbo.Create(gl_state, buffer_size_uv2 * vbo_vertex_count); ebo.Create(gl_state, sizeof(uint32_t) * ebo_index_count); glGenVertexArrays(3, vao); gl_state.bind_vertex_array(vao[0]); gl_state.bind_array_buffer(vbo, true); gl_state.bind_element_array_buffer(ebo, true); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size_uv0, (void*)offsetof(sprite_vertex_data_uv0, pos)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, buffer_size_uv0, (void*)offsetof(sprite_vertex_data_uv0, color)); gl_state.bind_vertex_array(vao[1]); gl_state.bind_array_buffer(vbo, true); gl_state.bind_element_array_buffer(ebo, true); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size_uv1, (void*)offsetof(sprite_vertex_data_uv1, pos)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, buffer_size_uv1, (void*)offsetof(sprite_vertex_data_uv1, color)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size_uv1, (void*)offsetof(sprite_vertex_data_uv1, uv)); gl_state.bind_vertex_array(vao[2]); gl_state.bind_array_buffer(vbo, true); gl_state.bind_element_array_buffer(ebo, true); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, pos)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, color)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, uv[0])); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, uv[1])); gl_state.bind_array_buffer(0); gl_state.bind_vertex_array(0); gl_state.bind_element_array_buffer(0); } SpriteManager::RenderData::~RenderData() { if (vao[0]) { glDeleteVertexArrays(3, vao); vao[0] = 0; } ebo.Destroy(); vbo.Destroy(); } template size_t SpriteManager::RenderData::AddData(T*& data, size_t nb_quad) { size_t size = vertex_buffer.size(); size_t align_offset = (sizeof(T) - size % sizeof(T)) % sizeof(T); size_t vertex_array_size = sizeof(T) * nb_quad; vertex_buffer.resize(size + align_offset + vertex_array_size); T* vtx_data = (T*)(vertex_buffer.data() + size + align_offset); data = vtx_data; return (uint32_t)((size + align_offset) / sizeof(T)); } void SpriteManager::RenderData::Clear() { draw_param_buffer.clear(); vertex_buffer.clear(); index_buffer.clear(); } void SpriteManager::RenderData::Update() { static const GLsizei buffer_size_uv0 = sizeof(sprite_vertex_data_uv0); static const GLsizei buffer_size_uv1 = sizeof(sprite_vertex_data_uv1); static const GLsizei buffer_size_uv2 = sizeof(sprite_vertex_data_uv2); if (vbo_vertex_count < vertex_buffer.size() / sizeof(sprite_vertex_data_uv2)) { while (vbo_vertex_count < vertex_buffer.size() / sizeof(sprite_vertex_data_uv2)) vbo_vertex_count *= 2; vbo.Recreate(gl_state, buffer_size_uv2 * vbo_vertex_count); gl_state.bind_vertex_array(vao[0]); gl_state.bind_array_buffer(vbo, true); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size_uv0, (void*)offsetof(sprite_vertex_data_uv0, pos)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, buffer_size_uv0, (void*)offsetof(sprite_vertex_data_uv0, color)); gl_state.bind_vertex_array(vao[1]); gl_state.bind_array_buffer(vbo, true); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size_uv1, (void*)offsetof(sprite_vertex_data_uv1, pos)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, buffer_size_uv1, (void*)offsetof(sprite_vertex_data_uv1, color)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size_uv1, (void*)offsetof(sprite_vertex_data_uv1, uv)); gl_state.bind_vertex_array(vao[2]); gl_state.bind_array_buffer(vbo, true); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, pos)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, color)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, uv[0])); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, buffer_size_uv2, (void*)offsetof(sprite_vertex_data_uv2, uv[1])); gl_state.bind_array_buffer(0); gl_state.bind_vertex_array(0); } vbo.WriteMemory(gl_state, 0, vertex_buffer.size(), vertex_buffer.data()); if (ebo_index_count < index_buffer.size()) { while (ebo_index_count < index_buffer.size()) ebo_index_count *= 2; ebo.Recreate(gl_state, sizeof(uint32_t) * ebo_index_count); gl_state.bind_vertex_array(vao[0]); gl_state.bind_element_array_buffer(ebo, true); gl_state.bind_vertex_array(vao[1]); gl_state.bind_element_array_buffer(ebo, true); gl_state.bind_vertex_array(vao[2]); gl_state.bind_element_array_buffer(ebo, true); gl_state.bind_vertex_array(0); gl_state.bind_element_array_buffer(0); } ebo.WriteMemory(gl_state, 0, sizeof(uint32_t) * index_buffer.size(), index_buffer.data()); gl_state.bind_array_buffer(0); gl_state.bind_element_array_buffer(0); } SpriteManager::ViewPort::ViewPort() : screen(SCREEN_MODE_MAX) { } SpriteManager::SpriteManager() : aspect(), target(), set_counter() { ResetTarget(); screen_mode_back = SCREEN_MODE_MAX; ScreenParam res = ScreenParam(SCREEN_MODE_HD); aspect[0] = (float_t)res.aspect; view_list[0].screen = res.mode; view_list[0].rect = { 0.0f, 0.0f, (float_t)res.width, (float_t)res.height }; aspect[1] = (float_t)res.aspect; view_list[1].screen = res.mode; view_list[1].rect = { 0.0f, 0.0f, (float_t)res.width, (float_t)res.height }; view_projection = mat4_identity; mat = mat4_identity; } SpriteManager::~SpriteManager() { Clear(); } uint32_t SpriteManager::AddSetModern() { uint32_t index = this->set_counter; //for (; index <= 0x0FFF; index++) { for (; index <= 0x3FFF; index++) { auto elem = sets.find(0x8000 | index); if (elem == sets.end()) break; } //if (!index || index >= 0x0FFF) { if (!index || index > 0x3FFF) { //for (index = 1; index <= 0x0FFF; index++) { for (index = 1; index <= 0x3FFF; index++) { auto elem = sets.find(0x8000 | index); if (elem == sets.end()) break; } //if (!index || index > 0x0FFF) if (!index || index > 0x3FFF) return 0x8000; } sets.insert({ 0x8000 | index, SprSet(0x8000 | index) }); set_counter = index + 1; //if (index + 1 > 0x0FFF) if (index + 1 > 0x3FFF) set_counter = 1; return 0x8000 | index; } void SpriteManager::AddSprSets(const sprite_database* spr_db) { for (auto& i : spr_db->spr_set_indices) sets.insert({ i.first, SprSet(i.first) }); } void SpriteManager::Clear() { for (int32_t i = 0; i < SPR_TARGET_MAX; i++) for (int32_t j = 0; j < 2; j++) for (int32_t k = 0; k < SPR_PRIO_MAX; k++) reqlist[i][j][k].clear(); for (auto i : sets) i.second.Unload(); sets.clear(); render_data.Clear(); } void SpriteManager::Draw(render_data_context& rend_data_ctx, int32_t index, bool font, texture* overlay_tex, const mat4& vp) { render_context* rctx = rctx_ptr; draw_sprite_begin(rend_data_ctx); SCREEN_MODE screen_mode = get_screen_param().mode; if (index == 2 && screen_mode_back != SCREEN_MODE_MAX) get_screen_param().mode = screen_mode_back; gl_rend_state_rect viewport_rect = rend_data_ctx.state.get_viewport(); for (int32_t i = 0; i < 2; i++) { auto reqlist = this->reqlist[index][i]; size_t count = 0; for (uint32_t j = SPR_PRIO_MAX, k = 0; j; j--, k++) count += reqlist[k].size(); if (!count) continue; int32_t x_min; int32_t y_min; int32_t x_max; int32_t y_max; if (index == 0 || index == 3) { ScreenParam& screen_param = get_screen_param(); float_t sprite_half_width = (float_t)screen_param.width * 0.5f; float_t sprite_half_height = (float_t)screen_param.height * 0.5f; float_t aet_depth = rctx->camera->aet_depth; float_t aet_depth_1 = 1.0f / aet_depth; float_t v15a = sprite_half_height * aspect[i] * 0.2f * aet_depth_1; float_t v15b = sprite_half_height * 0.2f * aet_depth_1; mat4 proj; mat4_frustrum(-v15a, v15a, v15b, -v15b, 0.2f, 3000.0f, &proj); vec3 eye = { sprite_half_width, sprite_half_height, aet_depth }; vec3 target = { sprite_half_width, sprite_half_height, 0.0f }; vec3 up = { 0.0f, 1.0f, 0.0f }; mat4 view; mat4_look_at(&eye, &target, &up, &view); mat4_mul(&view, &proj, &view_projection); vec2 min; vec2 max; get_screen_conv_pos(min, screen_mode, view_list[i].rect.pos, view_list[i].screen); get_screen_conv_pos(max, screen_mode, view_list[i].rect.pos + view_list[i].rect.size, view_list[i].screen); x_min = (int32_t)min.x; y_min = (int32_t)min.y; x_max = (int32_t)(max.x - min.x); y_max = (int32_t)(max.y - min.y); rend_data_ctx.state.set_viewport(x_min, y_min, x_max, y_max); } else { view_projection = vp; x_min = viewport_rect.x; y_min = viewport_rect.y; x_max = viewport_rect.width; y_max = viewport_rect.height; } float_t v23 = (float_t)x_max * 0.5f; float_t v24 = (float_t)x_min + v23; float_t v25 = (float_t)y_max * 0.5f; float_t v26 = (float_t)y_min + v25; mat = view_projection; mat.row0.x = v23 * view_projection.row0.x + v24 * view_projection.row0.w; mat.row1.x = v23 * view_projection.row1.x + v24 * view_projection.row1.w; mat.row2.x = v23 * view_projection.row2.x + v24 * view_projection.row2.w; mat.row3.x = v23 * view_projection.row3.x + v24 * view_projection.row3.w; mat.row0.y = v25 * view_projection.row0.y + v26 * view_projection.row0.w; mat.row1.y = v25 * view_projection.row1.y + v26 * view_projection.row1.w; mat.row2.y = v25 * view_projection.row2.y + v26 * view_projection.row2.w; mat.row3.y = v25 * view_projection.row3.y + v26 * view_projection.row3.w; sprite_scene_shader_data shader_data = {}; shader_data.g_framebuffer_size = { 1.0f / (float_t)overlay_tex->width, 1.0f / (float_t)overlay_tex->height, 0.0f, 0.0f }; mat4_transpose(&view_projection, &shader_data.g_transform); rctx->sprite_scene_ubo.WriteMemory(rend_data_ctx.state, shader_data); rend_data_ctx.state.bind_uniform_buffer_base(0, rctx->sprite_scene_ubo); for (uint32_t j = SPR_PRIO_MAX; j; j--, reqlist++) for (SprArgs& k : *reqlist) if (k.sprite_draw_param_index >= 0) draw_sprite(rend_data_ctx, this, k, mat, x_min, y_min, x_max, y_max, overlay_tex); rend_data_ctx.state.bind_vertex_array(0); } if (index == 2 && screen_mode_back != SCREEN_MODE_MAX) get_screen_param().mode = screen_mode; draw_sprite_end(rend_data_ctx); } SprSet* SpriteManager::GetSet(uint32_t index) { auto elem = sets.find(index); if (elem != sets.end()) return &elem->second; return 0; } bool SpriteManager::GetSetReady(uint32_t index) { SprSet* set = GetSet(index); if (set) return set->ready; return false; } uint32_t SpriteManager::GetSetSpriteNum(uint32_t index) { SprSet* set = GetSet(index); if (set) return set->spr_set->num_of_sprite; return 0; } uint32_t SpriteManager::GetSetTextureNum(uint32_t index) { SprSet* set = GetSet(index); if (set) return set->spr_set->num_of_texture; return 0; } const char* SpriteManager::GetSprName(spr_info info) { SprSet* set = GetSet(info.set_index & 0x8FFF); if (set) return set->GetName(info); return "(null)"; } rectangle SpriteManager::GetSprRectangle(spr_info info) { SprSet* set = GetSet(info.set_index & 0x8FFF); if (set) return set->GetRectangle(info); return {}; } texture* SpriteManager::GetSprTexture(spr_info info) { SprSet* set = GetSet(info.set_index & 0x8FFF); if (set) return set->GetTexture(info); return 0; } bool SpriteManager::LoadFile(uint32_t index) { SprSet* set = GetSet(index); if (set) return set->LoadFile(); return false; } bool SpriteManager::LoadFileModern(uint32_t index, sprite_database* spr_db) { SprSet* set = GetSet(index); if (set) return set->LoadFileModern(spr_db); return false; } SprArgs* SpriteManager::PutSprite(const SprArgs& args, const sprite_database* spr_db) { SprTarget target = args.target; if (target == -1) target = this->target; auto reqlist = &this->reqlist[target][args.layer][args.prio]; spr_info info = args.id.info; if (args.kind == SPR_KIND_NORMAL) info = spr_db->get_spr_by_id(args.id.id)->info; if (info.not_null()) { SprSet* set = GetSet(info.set_index & 0x8FFF); if (!set || !set->ready) return 0; reqlist->push_back(args); SprArgs& _args = reqlist->back(); _args.id.info = info; _args.tex = set->GetTexture(info); rectangle rect = set->GetRectangle(info); _args.SetSize({ rect.size.x, rect.size.y }); _args.SetRect(rect.pos.x, rect.pos.y, rect.size.x, rect.size.y); if (_args.screen_scale != SCREEN_MODE_MAX) _args.screen_scale = set->GetResolutionMode(_args.id.info); return &_args; } if (args.tex) { reqlist->push_back(args); SprArgs& _args = reqlist->back(); _args.id.index = -1; _args.SetSize({ (float_t)_args.tex->width, (float_t)_args.tex->height }); _args.SetRect(0.0, 0.0, (float_t)_args.tex->width, (float_t)_args.tex->height); return &_args; } else { reqlist->push_back(args); SprArgs& _args = reqlist->back(); _args.id.index = -1; return &_args; } } void SpriteManager::ReadFile(uint32_t index, const char* file, std::string& mdata_dir, void* data) { SprSet* set = GetSet(index); if (set) set->ReadFile(file, mdata_dir, data); } void SpriteManager::ReadFileModern(uint32_t index, uint32_t set_hash, void* data, sprite_database* spr_db) { if (index == -1) { index = AddSetModern(); spr_db->add_spr_set(set_hash, index); } SprSet* set = GetSet(index); if (set) set->ReadFileModern(set_hash, data); } void SpriteManager::ResetTarget() { target = SPR_TARGET_FRONT; } void SpriteManager::RemoveSprSets(const sprite_database* spr_db) { for (auto& i : spr_db->spr_set_indices) sets.erase(i.first); } void SpriteManager::ResetReqList() { for (auto& i : reqlist) for (auto& j : i) for (auto& k : j) k.clear(); ResetTarget(); string_quad_idx = 0; } void SpriteManager::ResetResData() { ResetTarget(); screen_mode_back = SCREEN_MODE_MAX; ScreenParam& screen_param = get_screen_param(); aspect[0] = (float_t)screen_param.aspect; view_list[0].screen = screen_param.mode; view_list[0].rect = { 0.0f, 0.0f, (float_t)screen_param.width, (float_t)screen_param.height }; aspect[1] = (float_t)screen_param.aspect; view_list[1].screen = screen_param.mode; view_list[1].rect = { 0.0f, 0.0f, (float_t)screen_param.width, (float_t)screen_param.height }; view_projection = mat4_identity; mat = mat4_identity; } void SpriteManager::PostDraw() { render_data.Clear(); } void SpriteManager::PreDraw() { auto check_same = [](const SprArgsDraw& left, const SprArgsDraw& right) -> bool { if (left.num_texture != right.num_texture) return false; for (int32_t i = 0; i < left.num_texture; i++) if (left.texs[i] != right.texs[i]) return false; return left.blend == right.blend && left.blend != 5 && right.blend != 5 && left.shader_name == right.shader_name && left.kind == right.kind && left.kind != SPR_KIND_POLY_LINE && right.kind != SPR_KIND_POLY_LINE && left.kind != SPR_KIND_STRIP && right.kind != SPR_KIND_STRIP; }; auto get_args_draw = [](const SprArgs& args, SprArgsDraw& args_draw) { int32_t num_texture = 0; if (args.tex) { const SprArgs* _args = &args; while (_args) { args_draw.texs[num_texture++] = _args->tex; _args = _args->next; } } args_draw.num_texture = num_texture; args_draw.blend = args.blend; args_draw.shader_name = args.shader_name; args_draw.kind = args.kind; }; SprArgsDraw args_draw[2]; SprArgs* args_array[256]; for (int32_t i = 0; i < SPR_TARGET_MAX; i++) for (int32_t j = 0; j < 2; j++) for (int32_t k = 0; k < SPR_PRIO_MAX; k++) { int32_t args_count = 0; int32_t draw_idx = 0; for (SprArgs& args : reqlist[i][j][k]) { if (!args_count) { get_args_draw(args, args_draw[draw_idx]); args_array[args_count++] = &args; draw_idx = 1 - draw_idx; } else if (args_count >= 0x100) { add_draw_sprite(args_array, args_count, true, args_draw[1 - draw_idx], render_data); get_args_draw(args, args_draw[draw_idx]); args_count = 0; args_array[args_count++] = &args; draw_idx = 1 - draw_idx; } else { get_args_draw(args, args_draw[draw_idx]); if (!check_same(args_draw[1 - draw_idx], args_draw[draw_idx])) { add_draw_sprite(args_array, args_count, true, args_draw[1 - draw_idx], render_data); args_count = 0; draw_idx = 1 - draw_idx; } args_array[args_count++] = &args; } } if (args_count) add_draw_sprite(args_array, args_count, true, args_draw[1 - draw_idx], render_data); } render_data.Update(); } void SpriteManager::UnloadSet(uint32_t index) { SprSet* set = GetSet(index); if (set) set->Unload(); } void SpriteManager::UnloadSetModern(uint32_t index, sprite_database* spr_db) { SprSet* set = GetSet(index); if (set) { set->UnloadModern(spr_db); if (set->load_count <= 0) { auto elem = sets.find(index); if (elem != sets.end()) sets.erase(elem); } } } static void calc_sprite_vertex(SprArgs* args, vec3 vtx[4], mat4* mat, bool font) { vtx[0].x = 0.0f; vtx[0].y = 0.0f; vtx[0].z = 0.0f; vtx[1].x = 0.0f; vtx[1].y = args->size.y; vtx[1].z = 0.0f; vtx[2].x = args->size.x; vtx[2].y = args->size.y; vtx[2].z = 0.0f; vtx[3].x = args->size.x; vtx[3].y = 0.0f; vtx[3].z = 0.0f; mat4 m; SprAttr attr = args->attr; if (!(attr & SPR_ATTR_NOTRANSFORM)) { if (attr & SPR_ATTR_CTR_ALL) { if (attr & SPR_ATTR_CTR_LT) args->anchor = 0.0f; else if (attr & SPR_ATTR_CTR_LC) { args->anchor.x = 0.0f; args->anchor.y = args->size.y * 0.5f; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_LB) { args->anchor.x = 0.0f; args->anchor.y = args->size.y; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_CT) { args->anchor.x = args->size.x * 0.5f; args->anchor.y = 0.0f; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_CC) { args->anchor.x = args->size.x * 0.5f; args->anchor.y = args->size.y * 0.5f; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_CB) { args->anchor.x = args->size.x * 0.5f; args->anchor.y = args->size.y; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_RT) { args->anchor.x = args->size.x; args->anchor.y = 0.0f; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_RC) { args->anchor.x = args->size.x; args->anchor.y = args->size.y * 0.5f; args->anchor.z = 0.0f; } else if (attr & SPR_ATTR_CTR_RB) { args->anchor.x = args->size.x; args->anchor.y = args->size.y; args->anchor.z = 0.0f; } } if (font) { if (attr & SPR_ATTR_FLIP_H) { args->scale.x = -args->scale.x; args->rot.z = -args->rot.z; } if (attr & SPR_ATTR_FLIP_V) { args->scale.y = -args->scale.y; args->rot.z = -args->rot.z; } } if (fabsf(args->slant) > 0.000001f) { float_t skew_width = tanf(args->slant) * args->size.y * 0.5f; vtx[0].x = vtx[0].x + skew_width; vtx[1].x = vtx[1].x - skew_width; vtx[2].x = vtx[2].x - skew_width; vtx[3].x = vtx[3].x + skew_width; } if (fabsf(args->shear) > 0.000001f) { float_t skew_height = tanf(args->shear) * args->size.x * 0.5f; vtx[0].y = vtx[0].y - skew_height; vtx[1].y = vtx[1].y - skew_height; vtx[2].y = vtx[2].y + skew_height; vtx[3].y = vtx[3].y + skew_height; } mat4_translate(&args->trans, &m); if (fabsf(args->rot.x) > 0.000001f) mat4_mul_rotate_x(&m, args->rot.x, &m); if (fabsf(args->rot.y) > 0.000001f) mat4_mul_rotate_y(&m, args->rot.y, &m); if (fabsf(args->rot.z) > 0.000001f) mat4_mul_rotate_z(&m, args->rot.z, &m); mat4_scale_rot(&m, &args->scale, &m); const vec3 anchor = -args->anchor; mat4_mul_translate(&m, &anchor, &m); } else { mat4_translate(&args->trans, &m); mat4_scale_rot(&m, &args->scale, &m); } mat4_mul(&args->matrix, &m, &m); mat4_transform_point(&m, &vtx[0], &vtx[0]); mat4_transform_point(&m, &vtx[1], &vtx[1]); mat4_transform_point(&m, &vtx[2], &vtx[2]); mat4_transform_point(&m, &vtx[3], &vtx[3]); if (mat) *mat = m; } static int32_t calc_sprite_texture_param(SprArgs* args, TexParam* param, vec3 vtx[4], bool font) { int32_t tex_param_count = 0; const texture* tex = args->tex; while (args) { param->tex = args->tex; float_t width = (float_t)args->tex->width; float_t height = (float_t)args->tex->height; float_t u_scale = 1.0f / width; float_t v_scale = 1.0f / height; if (!args->nb_quad) { vec2 uv00; vec2 uv01; vec2 uv10; vec2 uv11; if (args->kind == SPR_KIND_MULTI) { vec3 v42[4]; mat4 mat; calc_sprite_vertex(args, v42, &mat, font); mat4_invert(&mat, &mat); mat4_transform_point(&mat, &vtx[0], &v42[0]); mat4_transform_point(&mat, &vtx[1], &v42[1]); mat4_transform_point(&mat, &vtx[2], &v42[2]); mat4_transform_point(&mat, &vtx[3], &v42[3]); uv00 = *(vec2*)&v42[0].x; uv01 = *(vec2*)&v42[1].x; uv10 = *(vec2*)&v42[2].x; uv11 = *(vec2*)&v42[3].x; } else { uv00.x = 0.0f; uv00.y = 0.0f; uv01.x = 0.0f; uv01.y = args->rect.height; uv10.x = args->rect.width; uv10.y = args->rect.height; uv11.x = args->rect.width; uv11.y = 0.0f; } const float_t rect_x = args->rect.x; const float_t rect_y = args->rect.y; param->texcoord.uv[0].u = (uv00.x + rect_x) * u_scale; param->texcoord.uv[0].v = (height - (uv00.y + rect_y)) * v_scale; param->texcoord.uv[1].u = (uv01.x + rect_x) * u_scale; param->texcoord.uv[1].v = (height - (uv01.y + rect_y)) * v_scale; param->texcoord.uv[2].u = (uv10.x + rect_x) * u_scale; param->texcoord.uv[2].v = (height - (uv10.y + rect_y)) * v_scale; param->texcoord.uv[3].u = (uv11.x + rect_x) * u_scale; param->texcoord.uv[3].v = (height - (uv11.y + rect_y)) * v_scale; } else if (font) { SprArgs::Quad* vtx = args->GetVertexArray(); for (size_t i = args->nb_quad; i; i--, vtx++) { vtx->uv.x = vtx->uv.x * u_scale; vtx->uv.y = (height - vtx->uv.y) * v_scale; } } args = args->next; param++; tex_param_count++; } return tex_param_count; } static int32_t calc_sprite_vertex_texture_param(SprArgs& args, vec3 vtx[4], TexParam* param, bool font) { vec3 _vtx[4] = {}; if (args.flags & SprArgs::SPRITE_SIZE) calc_sprite_vertex(&args, _vtx, 0, font); int32_t tex_param_count = 0; if ((args.flags & SprArgs::TEXTURE_POS_SIZE) && args.tex) tex_param_count = calc_sprite_texture_param(&args, param, _vtx, font); vtx[0] = _vtx[0]; vtx[1] = _vtx[1]; vtx[2] = _vtx[2]; vtx[3] = _vtx[3]; return tex_param_count; } static void add_draw_sprite(SprArgs* args_array[0x100], const int32_t args_count, bool font, SprArgsDraw& args_draw, SpriteManager::RenderData& render_data) { SprArgs& args = *args_array[0]; if (args.kind == SPR_KIND_MULTI) return; const color4u8 color = args.color; if (font) draw_sprite_scale(&args); vec3 spr_vtx[4] = {}; TexParam tex_param[4] = {}; calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); std::vector& draw_param_buffer = render_data.draw_param_buffer; std::vector& index_buffer = render_data.index_buffer; draw_param_buffer.push_back({}); sprite_draw_param& draw_param = draw_param_buffer.back(); if (args.attr & SPR_ATTR_IGNORE_ALPHA) { draw_param.attrib.m.enable_blend = 0; draw_param.attrib.m.blend = 0; } else { draw_param.attrib.m.enable_blend = 1; draw_param.attrib.m.blend = args_draw.blend; } draw_param.shader_name = args_draw.shader_name; if (draw_param.shader_name == SHADER_FT_FFP) draw_param.shader_name = SHADER_FT_SPRITE; switch (args_draw.num_texture) { case 0: draw_param.attrib.m.vao = 0; switch (args_draw.kind) { case SPR_KIND_LINE: { sprite_vertex_data_uv0* vtx_data = 0; size_t count = 2ULL * args_count; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_LINES; draw_param.first = (GLint)first; draw_param.count = (GLint)count; vtx_data[0] = { spr_vtx[0], color }; vtx_data[1] = { spr_vtx[2], color }; vtx_data += 2; for (int32_t i = 1; i < args_count; i++) { SprArgs& args = *args_array[i]; const color4u8 color = args.color; if (font) draw_sprite_scale(&args); calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); vtx_data[0] = { spr_vtx[0], color }; vtx_data[1] = { spr_vtx[2], color }; vtx_data += 2; } } break; case SPR_KIND_RECT: { sprite_vertex_data_uv0* vtx_data = 0; size_t vtx_count = 4ULL * args_count; size_t idx_count = 6ULL * args_count; size_t start = render_data.AddData(vtx_data, vtx_count); index_buffer.reserve(idx_count); draw_param.attrib.m.primitive = GL_TRIANGLES; draw_param.start = (GLuint)start; draw_param.end = (GLuint)(start + vtx_count - 1); draw_param.count = (GLsizei)idx_count; draw_param.offset = (GLintptr)(index_buffer.size() * sizeof(uint32_t)); vtx_data[0] = { spr_vtx[0], color }; // LB vtx_data[1] = { spr_vtx[1], color }; // LT vtx_data[2] = { spr_vtx[2], color }; // RT vtx_data[3] = { spr_vtx[3], color }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + 0)); // LB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 3)); // RB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 2)); // RT index_buffer.push_back((uint32_t)(start + 3)); // RB start += 4; for (int32_t i = 1; i < args_count; i++) { SprArgs& args = *args_array[i]; const color4u8 color = args.color; if (font) draw_sprite_scale(&args); calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); vtx_data[0] = { spr_vtx[0], color }; // LB vtx_data[1] = { spr_vtx[1], color }; // LT vtx_data[2] = { spr_vtx[2], color }; // RT vtx_data[3] = { spr_vtx[3], color }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + 0)); // LB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 3)); // RB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 2)); // RT index_buffer.push_back((uint32_t)(start + 3)); // RB start += 4; } } break; case SPR_KIND_LINE_BOX: { sprite_vertex_data_uv0* vtx_data = 0; size_t count = 5; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_LINE_STRIP; draw_param.first = (GLint)first; draw_param.count = (GLsizei)count; vtx_data[0] = { spr_vtx[0], color }; vtx_data[1] = { spr_vtx[1], color }; vtx_data[2] = { spr_vtx[2], color }; vtx_data[3] = { spr_vtx[3], color }; vtx_data[4] = { spr_vtx[0], color }; } break; case SPR_KIND_LINES: { if (args.nb_quad) { sprite_vertex_data_uv0* vtx_data = 0; size_t count = args.nb_quad & ~0x01; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_LINES; draw_param.first = (GLint)first; draw_param.count = (GLsizei)count; SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad / 2; i; i--, vtx += 2, vtx_data += 2) { vtx_data[0] = { vtx[0].pos, color }; vtx_data[1] = { vtx[1].pos, color }; } } else { sprite_vertex_data_uv0* vtx_data = 0; size_t count = 2; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_LINES; draw_param.first = (GLint)first; draw_param.count = (GLsizei)count; vtx_data[0] = { spr_vtx[0], color }; vtx_data[1] = { spr_vtx[2], color }; } for (int32_t i = 1; i < args_count; i++) { SprArgs& args = *args_array[i]; const color4u8 color = args.color; if (font) draw_sprite_scale(&args); calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); if (args.nb_quad) { sprite_vertex_data_uv0* vtx_data = 0; size_t count = args.nb_quad & ~0x01; render_data.AddData(vtx_data, count); draw_param.count += (GLsizei)count; SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad / 2; i; i--, vtx += 2, vtx_data += 2) { vtx_data[0] = { vtx[0].pos, color }; vtx_data[1] = { vtx[1].pos, color }; } } else { sprite_vertex_data_uv0* vtx_data = 0; size_t count = 2; render_data.AddData(vtx_data, count); draw_param.count += (GLsizei)count; vtx_data[0] = { spr_vtx[0], color }; vtx_data[1] = { spr_vtx[2], color }; } } } break; case SPR_KIND_POLY_LINE: { sprite_vertex_data_uv0* vtx_data = 0; size_t count = args.nb_quad; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_LINE_STRIP; draw_param.first = (GLint)first; draw_param.count = (GLsizei)count; SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad; i; i--, vtx++, vtx_data++) *vtx_data = { vtx->pos, color }; } break; case SPR_KIND_STRIP: { sprite_vertex_data_uv0* vtx_data = 0; size_t count = args.nb_quad; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_TRIANGLE_STRIP; draw_param.first = (GLint)first; draw_param.count = (GLsizei)count; SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad; i; i--, vtx++, vtx_data++) *vtx_data = { vtx->pos, vtx->color }; } break; } break; case 1: draw_param.texs[0] = tex_param[0].tex; draw_param.attrib.m.sampler = 0; draw_param.attrib.m.vao = 1; if (args.nb_quad) { if (args_draw.kind == SPR_KIND_STRIP) { draw_param.attrib.m.sampler = 1; sprite_vertex_data_uv1* vtx_data = 0; size_t count = args.nb_quad; size_t first = render_data.AddData(vtx_data, count); draw_param.attrib.m.primitive = GL_TRIANGLE_STRIP; draw_param.first = (GLint)first; draw_param.count = (GLsizei)count; SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad; i; i--, vtx++, vtx_data++) *vtx_data = { vtx->pos, vtx->color, vtx->uv }; } else { sprite_vertex_data_uv1* vtx_data = 0; size_t vtx_count = args.nb_quad; size_t idx_count = args.nb_quad / 4 * 6; size_t start = render_data.AddData(vtx_data, vtx_count); index_buffer.reserve(idx_count); draw_param.attrib.m.primitive = GL_TRIANGLES; draw_param.start = (GLuint)start; draw_param.end = (GLuint)(start + vtx_count - 1); draw_param.count = (GLsizei)idx_count; draw_param.offset = (GLintptr)(index_buffer.size() * sizeof(uint32_t)); SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad / 4, j = 0; i; i--, j += 4, vtx += 4) { vtx_data[0] = { vtx[0].pos, vtx[0].color, vtx[0].uv }; // LB vtx_data[1] = { vtx[1].pos, vtx[1].color, vtx[1].uv }; // LT vtx_data[2] = { vtx[2].pos, vtx[2].color, vtx[2].uv }; // RT vtx_data[3] = { vtx[3].pos, vtx[3].color, vtx[3].uv }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + j + 0)); // LB index_buffer.push_back((uint32_t)(start + j + 1)); // LT index_buffer.push_back((uint32_t)(start + j + 3)); // RB index_buffer.push_back((uint32_t)(start + j + 1)); // LT index_buffer.push_back((uint32_t)(start + j + 2)); // RT index_buffer.push_back((uint32_t)(start + j + 3)); // RB } start += args.nb_quad; for (int32_t i = 1; i < args_count; i++) { SprArgs& args = *args_array[i]; size_t vtx_count = args.nb_quad; size_t idx_count = args.nb_quad / 4 * 6; draw_param.end += (GLuint)vtx_count; draw_param.count += (GLsizei)idx_count; render_data.AddData(vtx_data, vtx_count); index_buffer.reserve(idx_count); if (font) draw_sprite_scale(&args); calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); SprArgs::Quad* vtx = args.GetVertexArray(); for (size_t i = args.nb_quad / 4, j = 0; i; i--, j += 4, vtx += 4) { vtx_data[0] = { vtx[0].pos, vtx[0].color, vtx[0].uv }; // LB vtx_data[1] = { vtx[1].pos, vtx[1].color, vtx[1].uv }; // LT vtx_data[2] = { vtx[2].pos, vtx[2].color, vtx[2].uv }; // RT vtx_data[3] = { vtx[3].pos, vtx[3].color, vtx[3].uv }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + j + 0)); // LB index_buffer.push_back((uint32_t)(start + j + 1)); // LT index_buffer.push_back((uint32_t)(start + j + 3)); // RB index_buffer.push_back((uint32_t)(start + j + 1)); // LT index_buffer.push_back((uint32_t)(start + j + 2)); // RT index_buffer.push_back((uint32_t)(start + j + 3)); // RB } start += args.nb_quad; } } } else { sprite_vertex_data_uv1* vtx_data = 0; size_t vtx_count = 4ULL * args_count; size_t idx_count = 6ULL * args_count; size_t start = render_data.AddData(vtx_data, vtx_count); index_buffer.reserve(idx_count); draw_param.attrib.m.primitive = GL_TRIANGLES; draw_param.start = (GLuint)start; draw_param.end = (GLuint)(start + vtx_count - 1); draw_param.count = (GLsizei)idx_count; draw_param.offset = (GLintptr)(index_buffer.size() * sizeof(uint32_t)); vtx_data[0] = { spr_vtx[0], color, tex_param[0].texcoord.uv[0] }; // LB vtx_data[1] = { spr_vtx[1], color, tex_param[0].texcoord.uv[1] }; // LT vtx_data[2] = { spr_vtx[2], color, tex_param[0].texcoord.uv[2] }; // RT vtx_data[3] = { spr_vtx[3], color, tex_param[0].texcoord.uv[3] }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + 0)); // LB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 3)); // RB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 2)); // RT index_buffer.push_back((uint32_t)(start + 3)); // RB start += 4; for (int32_t i = 1; i < args_count; i++) { SprArgs& args = *args_array[i]; const color4u8 color = args.color; if (font) draw_sprite_scale(&args); calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); vtx_data[0] = { spr_vtx[0], color, tex_param[0].texcoord.uv[0] }; // LB vtx_data[1] = { spr_vtx[1], color, tex_param[0].texcoord.uv[1] }; // LT vtx_data[2] = { spr_vtx[2], color, tex_param[0].texcoord.uv[2] }; // RT vtx_data[3] = { spr_vtx[3], color, tex_param[0].texcoord.uv[3] }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + 0)); // LB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 3)); // RB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 2)); // RT index_buffer.push_back((uint32_t)(start + 3)); // RB start += 4; } } break; case 2: { draw_param.texs[0] = tex_param[0].tex; draw_param.texs[1] = tex_param[1].tex; draw_param.attrib.m.sampler = 2; draw_param.attrib.m.vao = 2; sprite_vertex_data_uv2* vtx_data = 0; size_t vtx_count = 4ULL * args_count; size_t idx_count = 6ULL * args_count; size_t start = render_data.AddData(vtx_data, vtx_count); index_buffer.reserve(idx_count); draw_param.attrib.m.primitive = GL_TRIANGLES; draw_param.start = (GLuint)start; draw_param.end = (GLuint)(start + vtx_count - 1); draw_param.count = (GLsizei)idx_count; draw_param.offset = (GLintptr)(index_buffer.size() * sizeof(uint32_t)); vtx_data[0] = { spr_vtx[0], color, tex_param[0].texcoord.uv[0], tex_param[1].texcoord.uv[0] }; // LB vtx_data[1] = { spr_vtx[1], color, tex_param[0].texcoord.uv[1], tex_param[1].texcoord.uv[1] }; // LT vtx_data[2] = { spr_vtx[2], color, tex_param[0].texcoord.uv[2], tex_param[1].texcoord.uv[2] }; // RT vtx_data[3] = { spr_vtx[3], color, tex_param[0].texcoord.uv[3], tex_param[1].texcoord.uv[3] }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + 0)); // LB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 3)); // RB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 2)); // RT index_buffer.push_back((uint32_t)(start + 3)); // RB start += 4; for (int32_t i = 1; i < args_count; i++) { SprArgs& args = *args_array[i]; const color4u8 color = args.color; if (font) draw_sprite_scale(&args); calc_sprite_vertex_texture_param(args, spr_vtx, tex_param, font); vtx_data[0] = { spr_vtx[0], color, tex_param[0].texcoord.uv[0], tex_param[1].texcoord.uv[0] }; // LB vtx_data[1] = { spr_vtx[1], color, tex_param[0].texcoord.uv[1], tex_param[1].texcoord.uv[1] }; // LT vtx_data[2] = { spr_vtx[2], color, tex_param[0].texcoord.uv[2], tex_param[1].texcoord.uv[2] }; // RT vtx_data[3] = { spr_vtx[3], color, tex_param[0].texcoord.uv[3], tex_param[1].texcoord.uv[3] }; // RB vtx_data += 4; index_buffer.push_back((uint32_t)(start + 0)); // LB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 3)); // RB index_buffer.push_back((uint32_t)(start + 1)); // LT index_buffer.push_back((uint32_t)(start + 2)); // RT index_buffer.push_back((uint32_t)(start + 3)); // RB start += 4; } } break; } if (!draw_param.count) { draw_param_buffer.pop_back(); args.sprite_draw_param_index = -1; } else { if (args_draw.blend == 5) { draw_param.vtx[0] = spr_vtx[0]; draw_param.vtx[1] = spr_vtx[1]; draw_param.vtx[2] = spr_vtx[2]; draw_param.vtx[3] = spr_vtx[3]; } args.sprite_draw_param_index = (int32_t)(draw_param_buffer.size() - 1); } for (int32_t i = 1; i < args_count; i++) args_array[i]->sprite_draw_param_index = -1; } static void draw_sprite(render_data_context& rend_data_ctx, SpriteManager* sprite_manager, const SprArgs& args, const mat4& mat, int32_t x_min, int32_t y_min, int32_t x_max, int32_t y_max, texture* overlay_tex) { sprite_draw_param& draw_param = sprite_manager->render_data.draw_param_buffer.data()[args.sprite_draw_param_index]; int32_t combiner = 0; if (draw_param.attrib.m.enable_blend) { if (draw_param.attrib.m.blend == 5) { rend_data_ctx.state.active_bind_texture_2d(7, overlay_tex->glid); draw_sprite_copy_overlay_texture(rend_data_ctx, args, mat, draw_param.vtx, x_min, y_min, x_max, y_max); combiner = 2; } else combiner = draw_param.attrib.m.blend == 3 ? 1 : 0; const GLenum* blend = spr_blend_param[draw_param.attrib.m.blend]; rend_data_ctx.state.enable_blend(); rend_data_ctx.state.set_blend_func_separate(blend[0], blend[1], blend[2], blend[3]); } else rend_data_ctx.state.disable_blend(); if (draw_param.shader_name == SHADER_FT_SPRITE) { int32_t tex_0_type = 0; int32_t tex_1_type = 0; if (draw_param.texs[0]) { if (draw_param.texs[0]->internal_format == GL_COMPRESSED_RED_RGTC1) tex_0_type = 3; else if (draw_param.texs[0]->internal_format == GL_COMPRESSED_RG_RGTC2) tex_0_type = 2; else tex_0_type = 1; if (draw_param.texs[1]) { if (draw_param.texs[1]->internal_format == GL_COMPRESSED_RED_RGTC1) tex_1_type = 3; else if (draw_param.texs[1]->internal_format == GL_COMPRESSED_RG_RGTC2) tex_1_type = 2; else tex_1_type = 1; } } rend_data_ctx.shader_flags.arr[U_TEX_0_TYPE] = tex_0_type; rend_data_ctx.shader_flags.arr[U_TEX_1_TYPE] = tex_1_type; rend_data_ctx.shader_flags.arr[U_COMBINER] = combiner; } shaders_ft.set(rend_data_ctx.state, rend_data_ctx.shader_flags, draw_param.shader_name); if (draw_param.texs[0]) { rend_data_ctx.state.active_bind_texture_2d(0, draw_param.texs[0]->glid); rend_data_ctx.state.bind_sampler(0, rctx_ptr->sprite_samplers[draw_param.attrib.m.sampler]); if (draw_param.texs[1]) { rend_data_ctx.state.active_bind_texture_2d(1, draw_param.texs[1]->glid); rend_data_ctx.state.bind_sampler(1, rctx_ptr->sprite_samplers[draw_param.attrib.m.sampler]); } else rend_data_ctx.state.active_bind_texture_2d(1, rctx_ptr->empty_texture_2d->glid); } else { rend_data_ctx.state.active_bind_texture_2d(0, rctx_ptr->empty_texture_2d->glid); rend_data_ctx.state.active_bind_texture_2d(1, rctx_ptr->empty_texture_2d->glid); } rend_data_ctx.state.bind_vertex_array(sprite_manager->render_data.vao[draw_param.attrib.m.vao]); if (draw_param.attrib.m.primitive != GL_TRIANGLES) rend_data_ctx.state.draw_arrays( draw_param.attrib.m.primitive, draw_param.first, draw_param.count); else rend_data_ctx.state.draw_range_elements( draw_param.attrib.m.primitive, draw_param.start, draw_param.end, draw_param.count, GL_UNSIGNED_INT, (void*)draw_param.offset); } static void draw_sprite_begin(render_data_context& rend_data_ctx) { rend_data_ctx.state.disable_blend(); rend_data_ctx.state.active_bind_texture_2d(0, rctx_ptr->empty_texture_2d->glid); rend_data_ctx.state.bind_sampler(0, 0); rend_data_ctx.state.active_bind_texture_2d(1, rctx_ptr->empty_texture_2d->glid); rend_data_ctx.state.bind_sampler(1, 0); rend_data_ctx.state.bind_sampler(7, 0); rend_data_ctx.state.set_blend_func_separate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); } static void draw_sprite_copy_overlay_texture( render_data_context& rend_data_ctx, const SprArgs& args, const mat4& mat, const vec3 vtx[4], int32_t overlay_x_min, int32_t overlay_y_min, int32_t overlay_x_max, int32_t overlay_y_max) { if (args.nb_quad || args.kind != SPR_KIND_NORMAL) return; mat4 mat_t; mat4_transpose(&mat, &mat_t); float_t x_min_coord = 0.0f; float_t y_min_coord = 0.0f; float_t x_max_coord = 0.0f; float_t y_max_coord = 0.0f; for (int32_t i = 0, j = 0; i < 4; i++) { vec4 v; *(vec3*)&v = vtx[i]; v.w = 1.0f; const float_t w = vec4::dot(mat_t.row3, v); if (w * w <= 0.0001f) continue; const float_t x_coord = vec4::dot(mat_t.row0, v) * (1.0f / w); const float_t y_coord = vec4::dot(mat_t.row1, v) * (1.0f / w); if (j) { if (x_min_coord > x_coord) x_min_coord = x_coord; else if (x_max_coord < x_coord) x_max_coord = x_coord; if (y_min_coord > y_coord) y_min_coord = y_coord; else if (y_max_coord < y_coord) y_max_coord = y_coord; } else { x_min_coord = x_coord; x_max_coord = x_coord; y_min_coord = y_coord; y_max_coord = y_coord; } j++; } int32_t x_max = (int32_t)x_max_coord + 1; int32_t y_max = (int32_t)y_max_coord + 1; int32_t x_min = (int32_t)x_min_coord - 1; int32_t y_min = (int32_t)y_min_coord - 1; x_max = clamp_def(x_max, overlay_x_min, overlay_x_min + overlay_x_max - 1); y_max = clamp_def(y_max, overlay_y_min, overlay_y_min + overlay_y_max - 1); x_min = clamp_def(x_min, overlay_x_min, overlay_x_min + overlay_x_max - 1); y_min = clamp_def(y_min, overlay_y_min, overlay_y_min + overlay_y_max - 1); const int32_t width = x_max - x_min + 1; const int32_t height = y_max - y_min + 1; if (width * height > 0) { rend_data_ctx.state.active_texture(7); rend_data_ctx.state.copy_tex_sub_image_2d(GL_TEXTURE_2D, 0, x_min, y_min, x_min, y_min, width, height); } } static void draw_sprite_end(render_data_context& rend_data_ctx) { rend_data_ctx.state.disable_blend(); rend_data_ctx.state.active_bind_texture_2d(0, 0); rend_data_ctx.state.bind_sampler(0, 0); rend_data_ctx.state.active_bind_texture_2d(1, 0); rend_data_ctx.state.bind_sampler(1, 0); rend_data_ctx.state.active_bind_texture_2d(7, 0); rend_data_ctx.state.bind_sampler(7, 0); rend_data_ctx.state.set_blend_func_separate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); shader::unbind(rend_data_ctx.state); } static void draw_sprite_scale(SprArgs* args) { SprTarget target = args->target; SCREEN_MODE screen_mode = get_screen_param().mode; while (args) { screen_mode_scale_data data(args->screen_trans, screen_mode); if (args->screen_trans != SCREEN_MODE_MAX && args->screen_trans != screen_mode && (target < SPR_TARGET_FRONT_3D_SURF || target > SPR_TARGET_BACK)) { float_t scale_x = data.scale.x; float_t scale_y = data.scale.y; float_t src_res_x = data.src_res.x; float_t src_res_y = data.src_res.y; float_t dst_res_x = data.dst_res.x; float_t dst_res_y = data.dst_res.y; args->trans.x = (args->trans.x - src_res_x) * scale_x + src_res_x; args->trans.y = (args->trans.y - src_res_y) * scale_y + src_res_y; args->trans.z = args->trans.z * scale_y; SprArgs::Quad* vtx = args->GetVertexArray(); for (size_t i = args->nb_quad; i; i--, vtx++) { vtx->pos.x = (vtx->pos.x - src_res_x) * scale_x + dst_res_x; vtx->pos.y = (vtx->pos.y - src_res_y) * scale_y + dst_res_y; } } if (args->screen_scale != SCREEN_MODE_MAX) { vec2 scale = get_screen_conv_scale(screen_mode, args->screen_scale); args->scale.x = scale.x * args->scale.x; args->scale.y = scale.y * args->scale.y; } args = args->next; } } } SprSet::SprSet(uint32_t index) : flag(), spr_set(), textures(), load_count(), ready() { this->index = index; hash = hash_murmurhash_empty; } SprSet::~SprSet() { } const char* SprSet::GetName(spr_info info) { //if (info.set_index & 0x1000) if (info.set_index & 0x4000) return spr_set->texname[info.index]; else return spr_set->sprname[info.index]; } rectangle SprSet::GetRectangle(spr_info info) { //if (info.set_index & 0x1000) { if (info.set_index & 0x4000) { texture* tex = textures[info.index]; return { 0.0f, 0.0f, (float_t)tex->width, (float_t)tex->height }; } spr::SprInfo* sprinfo = &spr_set->sprinfo[info.index]; return { (float_t)(int32_t)sprinfo->px, (float_t)(int32_t)sprinfo->py, (float_t)(int32_t)sprinfo->width, (float_t)(int32_t)sprinfo->height }; } SCREEN_MODE SprSet::GetResolutionMode(spr_info info) { //if (info.set_index & 0x1000) if (info.set_index & 0x4000) return SCREEN_MODE_HD; else return spr_set->sprdata[info.index].screen_mode; } texture* SprSet::GetTexture(spr_info info) { //if (info.set_index & 0x1000) if (info.set_index & 0x4000) return textures[info.index]; else return textures[spr_set->sprinfo[info.index].texid]; } bool SprSet::LoadData() { const void* data = file_handler.get_data(); size_t size = file_handler.get_size(); if (!data || !size) return false; prj::shared_ptr& alloc = alloc_handler; alloc = prj::shared_ptr(new prj::stack_allocator); ::spr_set* set = alloc->allocate<::spr_set>(); this->spr_set = set; set->unpack_file(alloc, data, size, false); return true; } bool SprSet::LoadDataModern(const void* data, size_t size) { if (!data || !size) return false; prj::shared_ptr& alloc = alloc_handler; alloc = prj::shared_ptr(new prj::stack_allocator); ::spr_set* set = alloc->allocate<::spr_set>(); this->spr_set = set; set->unpack_file(alloc, data, size, true); file_handler.reset(); return true; } bool SprSet::LoadFile() { if (ready) return false; else if (file_handler.check_not_ready()) return true; else if (!LoadData() || !LoadTexture()) return false; file_handler.reset(); ready = true; return true; } bool SprSet::LoadFileModern(sprite_database* spr_db) { if (ready) return false; else if (file_handler.check_not_ready()) return true; const void* data = file_handler.get_data(); size_t size = file_handler.get_size(); if (!data || !size) return false; farc f; f.read(data, size, true); std::string& file = file_handler.ptr->file; size_t file_len = file.size(); if (file_len >= 0x100 - 4) return false; const char* t = strrchr(file.c_str(), '.'); if (t) file_len = t - file.c_str(); char buf[0x100]; memcpy(buf, file.c_str(), file_len); char* ext = buf + file_len; size_t ext_len = sizeof(buf) - file_len; memcpy_s(ext, ext_len, ".spr", 5); farc_file* spr = f.read_file(buf); if (!spr) return false; memcpy_s(ext, ext_len, ".spi", 5); farc_file* spi = f.read_file(buf); if (!spi) return false; sprite_database_file spr_db_file; spr_db_file.read(spi->data, spi->size, true); spr_db_spr_set_file* spr_set_file = 0; if (spr_db_file.ready) for (spr_db_spr_set_file& m : spr_db_file.sprite_set) if (m.id == hash) { spr_set_file = &m; break; } if (!spr_set_file) return false; if (spr_db_file.ready) spr_db->parse(spr_set_file, name, sprite_ids); if (!LoadDataModern(spr->data, spr->size) || !LoadTextureModern(spr->data, spr->size)) return false; file_handler.reset(); ready = true; return true; } bool SprSet::LoadTexture() { size_t data = (size_t)file_handler.get_data(); if (!data) return false; std::vector ids(spr_set->num_of_texture); //uint32_t set_index = (uint32_t)(0x1000 | index & 0x0FFF); uint32_t set_index = (uint32_t)(0x4000 | index & 0x3FFF); uint32_t index = 0; for (uint32_t& i : ids) i = (set_index << 16) | index++; texture_txp_set_load(spr_set->txp, &textures, ids.data()); return !!textures; } bool SprSet::LoadTextureModern(const void* data, size_t size) { if (!data || !size) return false; std::vector ids(spr_set->num_of_texture); //uint32_t set_index = (uint32_t)(0x8000 | 0x1000 | index & 0x0FFF); uint32_t set_index = (uint32_t)(0x8000 | 0x4000 | index & 0x3FFF); uint32_t index = 0; for (uint32_t& i : ids) i = (set_index << 16) | index++; texture_txp_set_load(spr_set->txp, &textures, ids.data()); return !!textures; } void SprSet::ReadFile(const char* file, std::string& mdata_dir, void* data) { if (load_count > 1) { load_count++; return; } load_count = 1; this->file.assign(file); std::string farc_file(file); size_t off = farc_file.rfind(".bin"); if (off != -1 && off == farc_file.size() - 4) farc_file.replace(off, 4, ".farc"); const char* dir = "./rom/2d/"; if (mdata_dir.size() && ((data_struct*)data)->check_directory_exists(mdata_dir.c_str())) dir = mdata_dir.c_str(); if (((data_struct*)data)->check_file_exists(dir, farc_file.c_str())) file_handler.read_file(data, dir, farc_file.c_str(), file, prj::MemCSystem, false); ready = false; } void SprSet::ReadFileModern(uint32_t set_hash, void* data) { if (load_count > 1) { load_count++; return; } std::string file; if (((data_struct*)data)->get_file("root+/2d/", set_hash, ".farc", file)) file_handler.read_file(data, "root+/2d/", file.c_str(), prj::MemCSystem); this->hash = set_hash; ready = false; } void SprSet::Unload() { if (load_count > 1) { load_count--; return; } file_handler.reset(); UnloadTexture(); alloc_handler.reset(); spr_set = 0; file.clear(); ready = false; load_count = 0; } void SprSet::UnloadModern(sprite_database* spr_db) { if (load_count > 1) { load_count--; return; } spr_db->remove_spr_set(hash, index, name.c_str(), sprite_ids); file_handler.reset(); UnloadTexture(); alloc_handler.reset(); spr_set = 0; file.clear(); ready = false; load_count = 0; hash = hash_murmurhash_empty; name.clear(); sprite_ids.clear(); } bool SprSet::UnloadTexture() { if (spr_set && spr_set->txp) { delete spr_set->txp; spr_set->txp = 0; } if (textures) { texture_array_free(textures); textures = 0; return true; } return false; }