From 802f992c4a835cd956c360d4ef70f483e6c8bb9a Mon Sep 17 00:00:00 2001 From: korenkonder Date: Sun, 5 Jan 2025 20:42:54 +0300 Subject: [PATCH] Synced changes for X Pack specific stuff between repos --- res/ft_shaders.farc | Bin 101856 -> 100736 bytes src/DivaGL/DivaGL.vcxproj | 2 + src/DivaGL/Glitter/counter.cpp | 2 +- src/DivaGL/Glitter/effect_group.cpp | 1 - src/DivaGL/Glitter/effect_inst.cpp | 4 +- src/DivaGL/Glitter/emitter_inst.cpp | 10 +- src/DivaGL/Glitter/file_reader.cpp | 2 +- src/DivaGL/Glitter/glitter.cpp | 22 +- src/DivaGL/Glitter/glitter.hpp | 4 +- src/DivaGL/Glitter/locus_history.cpp | 1 - src/DivaGL/Glitter/render_group.cpp | 12 +- src/DivaGL/Glitter/render_scene.cpp | 48 +- src/DivaGL/auth_2d.cpp | 2 +- src/DivaGL/auth_3d.cpp | 90 +++- src/DivaGL/auth_3d.hpp | 1 + src/DivaGL/camera.cpp | 38 +- src/DivaGL/camera.hpp | 2 +- src/DivaGL/data_test/rob_osage_test.cpp | 26 +- src/DivaGL/dllmain.cpp | 1 + src/DivaGL/effect.cpp | 26 +- src/DivaGL/graphics/color_change_dw.cpp | 4 +- src/DivaGL/hook.cpp | 10 +- src/DivaGL/mdl/disp_manager.cpp | 625 ++++++++++++++++++------ src/DivaGL/mdl/disp_manager.hpp | 72 +-- src/DivaGL/mdl/draw_object.cpp | 8 +- src/DivaGL/object.cpp | 178 ++++++- src/DivaGL/object.hpp | 17 +- src/DivaGL/print_work.cpp | 4 +- src/DivaGL/pv_game/firstread.cpp | 313 ++++++++++++ src/DivaGL/pv_game/firstread.hpp | 153 ++++++ src/DivaGL/pv_game/pv_game.cpp | 138 ++++-- src/DivaGL/render.cpp | 26 +- src/DivaGL/render_context.cpp | 6 +- src/DivaGL/render_context.hpp | 1 + src/DivaGL/render_manager.cpp | 521 ++++++++++++++++++-- src/DivaGL/render_manager.hpp | 5 +- src/DivaGL/renderer/dof.cpp | 17 +- src/DivaGL/rob/rob.cpp | 22 +- src/DivaGL/rob/rob.hpp | 2 +- src/DivaGL/shadow.cpp | 6 +- src/DivaGL/sprite.cpp | 30 +- src/DivaGL/sprite.hpp | 6 +- src/DivaGL/stage.cpp | 77 ++- src/DivaGL/types.hpp | 60 +++ 44 files changed, 2132 insertions(+), 463 deletions(-) create mode 100644 src/DivaGL/pv_game/firstread.cpp create mode 100644 src/DivaGL/pv_game/firstread.hpp diff --git a/res/ft_shaders.farc b/res/ft_shaders.farc index c97a9a1c8d0706e42ea6f4f7a3714e3da753d1a7..43dd6f93022c3ad289c02d26e7b762031f4353d2 100644 GIT binary patch delta 37084 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 delta 38241 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

;fFA~~woZI$g2(CoEhH}-zIva~Kh@=A7W%>^* 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 diff --git a/src/DivaGL/DivaGL.vcxproj b/src/DivaGL/DivaGL.vcxproj index 1864e27..2151428 100644 --- a/src/DivaGL/DivaGL.vcxproj +++ b/src/DivaGL/DivaGL.vcxproj @@ -166,6 +166,7 @@ del "$(SolutionDir)bin\$(TargetName).lib" + @@ -225,6 +226,7 @@ del "$(SolutionDir)bin\$(TargetName).lib" + diff --git a/src/DivaGL/Glitter/counter.cpp b/src/DivaGL/Glitter/counter.cpp index 4911611..2138a14 100644 --- a/src/DivaGL/Glitter/counter.cpp +++ b/src/DivaGL/Glitter/counter.cpp @@ -6,7 +6,7 @@ #include "glitter.hpp" namespace Glitter { - Counter* counter = (Counter*)0x0000000141199CB0; + Counter& counter = *(Counter*)0x0000000141199CB0; void Counter::Increment() { value++; diff --git a/src/DivaGL/Glitter/effect_group.cpp b/src/DivaGL/Glitter/effect_group.cpp index 28f0ffc..7674db5 100644 --- a/src/DivaGL/Glitter/effect_group.cpp +++ b/src/DivaGL/Glitter/effect_group.cpp @@ -4,7 +4,6 @@ */ #include "glitter.hpp" -#include "../../KKdLib/prj/algorithm.hpp" #include "../object.hpp" #include "../texture.hpp" diff --git a/src/DivaGL/Glitter/effect_inst.cpp b/src/DivaGL/Glitter/effect_inst.cpp index 50eb068..a41f055 100644 --- a/src/DivaGL/Glitter/effect_inst.cpp +++ b/src/DivaGL/Glitter/effect_inst.cpp @@ -63,7 +63,7 @@ namespace Glitter { ext_anim = 0; render_scene = {}; - Glitter::counter->Increment(); + Glitter::counter.Increment(); if (load_flags & 0x01) enum_or(flags, EFFECT_INST_FLAG_23); @@ -71,7 +71,7 @@ namespace Glitter { if (data.flags & EFFECT_USE_SEED) random = data.seed; else - random = Glitter::counter->GetValue(); + random = Glitter::counter.GetValue(); random_shared.value = random; if (!(eff->data.flags & EFFECT_LOCAL) && data.ext_anim) { diff --git a/src/DivaGL/Glitter/emitter_inst.cpp b/src/DivaGL/Glitter/emitter_inst.cpp index 2d40af5..58b54bd 100644 --- a/src/DivaGL/Glitter/emitter_inst.cpp +++ b/src/DivaGL/Glitter/emitter_inst.cpp @@ -168,7 +168,7 @@ namespace Glitter { switch (data.direction) { case DIRECTION_BILLBOARD: { if (eff_inst->data.flags & EFFECT_LOCAL) { - mat4_transpose(&camera_data->view, &dir_mat); + mat4_transpose(&camera_data.view, &dir_mat); mat4_clear_trans(&dir_mat, &dir_mat); mat4_mul(&dir_mat, &mat, &dir_mat); } @@ -177,7 +177,7 @@ namespace Glitter { mat4 inv_view_mat; mat3 inv_view_mat3; - mat4_transpose(&camera_data->view, &inv_view_mat); + mat4_transpose(&camera_data.view, &inv_view_mat); mat4_to_mat3(&inv_view_mat, &inv_view_mat3); mat3_invert(&inv_view_mat3, &inv_view_mat3); mat4_from_mat3(&inv_view_mat3, &inv_view_mat); @@ -191,7 +191,7 @@ namespace Glitter { mat4_rotate_x((float_t)-M_PI_2, &dir_mat); break; case DIRECTION_BILLBOARD_Y_AXIS: - mat4_rotate_y(camera_data->rotation.y, &dir_mat); + mat4_rotate_y(camera_data.rotation.y, &dir_mat); break; default: mult = false; @@ -287,7 +287,7 @@ namespace Glitter { switch (data.direction) { case DIRECTION_BILLBOARD: { mat3 inv_view_mat3; - mat4_transpose(&camera_data->view, &dir_mat); + mat4_transpose(&camera_data.view, &dir_mat); mat4_to_mat3(&dir_mat, &inv_view_mat3); mat3_invert(&inv_view_mat3, &inv_view_mat3); mat4_from_mat3(&inv_view_mat3, &dir_mat); @@ -301,7 +301,7 @@ namespace Glitter { mat4_rotate_x((float_t)-M_PI_2, &dir_mat); break; case DIRECTION_BILLBOARD_Y_AXIS: - mat4_rotate_y(camera_data->rotation.y, &dir_mat); + mat4_rotate_y(camera_data.rotation.y, &dir_mat); break; default: mult = false; diff --git a/src/DivaGL/Glitter/file_reader.cpp b/src/DivaGL/Glitter/file_reader.cpp index bce33ba..ef7bcec 100644 --- a/src/DivaGL/Glitter/file_reader.cpp +++ b/src/DivaGL/Glitter/file_reader.cpp @@ -662,7 +662,7 @@ namespace Glitter { ids[i] = texture_id(0x2A, GPM_VAL->texture_counter++); txp_set_load(st->data.data(), &eff_group->resources, (uint32_t*)ids); - free_def(ids); + _operator_delete(ids); uint32_t* resource_hashes = this->resource_hashes; diff --git a/src/DivaGL/Glitter/glitter.cpp b/src/DivaGL/Glitter/glitter.cpp index 25f87c9..42fed6e 100644 --- a/src/DivaGL/Glitter/glitter.cpp +++ b/src/DivaGL/Glitter/glitter.cpp @@ -11,21 +11,21 @@ namespace Glitter { const float_t min_emission = 0.01f; - void axis_angle_from_vectors(vec3* axis, float_t* angle, const vec3* vec1, const vec3* vec2) { - *axis = vec3::cross(*vec1, *vec2); - *angle = vec3::length(*axis); + void axis_angle_from_vectors(vec3& axis, float_t& angle, const vec3& vec1, const vec3& vec2) { + axis = vec3::cross(vec1, vec2); + angle = vec3::length(axis); - if (*angle >= 0.000001f) - *angle = asinf(clamp_def(*angle, -1.0f, 1.0f)); + if (angle >= 0.000001f) + angle = asinf(clamp_def(angle, -1.0f, 1.0f)); else { - *angle = 0.0f; - *axis = vec3::cross(vec3(0.0f, 1.0f, 0.0f), *vec1); - if (vec3::length(*axis) < 0.000001f) - *axis = vec3::cross(vec3(0.0f, 0.0f, 1.0f), *vec1); + angle = 0.0f; + axis = vec3::cross(vec3(0.0f, 1.0f, 0.0f), vec1); + if (vec3::length(axis) < 0.000001f) + axis = vec3::cross(vec3(0.0f, 0.0f, 1.0f), vec1); } - if (vec3::dot(*vec1, *vec2) < 0.0f) - *angle = (float_t)M_PI - *angle; + if (vec3::dot(vec1, vec2) < 0.0f) + angle = (float_t)M_PI - angle; } void CreateBuffer(size_t max_count, bool is_quad, diff --git a/src/DivaGL/Glitter/glitter.hpp b/src/DivaGL/Glitter/glitter.hpp index 1ef5a50..695ea95 100644 --- a/src/DivaGL/Glitter/glitter.hpp +++ b/src/DivaGL/Glitter/glitter.hpp @@ -1499,9 +1499,9 @@ namespace Glitter { extern GltParticleManager* glt_particle_manager; extern GltParticleManagerX* glt_particle_manager_x; extern RandomX random_x; - extern Counter* counter; + extern Counter& counter; - extern void axis_angle_from_vectors(vec3* axis, float_t* angle, const vec3* vec0, const vec3* vec1); + extern void axis_angle_from_vectors(vec3& axis, float_t& angle, const vec3& vec0, const vec3& vec1); extern void CreateBuffer(size_t max_count, bool is_quad, Buffer*& buffer, GLuint& vao, GL::ArrayBuffer& vbo, GL::ElementArrayBuffer& ebo); diff --git a/src/DivaGL/Glitter/locus_history.cpp b/src/DivaGL/Glitter/locus_history.cpp index f962b23..6c58c80 100644 --- a/src/DivaGL/Glitter/locus_history.cpp +++ b/src/DivaGL/Glitter/locus_history.cpp @@ -55,5 +55,4 @@ namespace Glitter { hist_data->translation = temp; } } - } diff --git a/src/DivaGL/Glitter/render_group.cpp b/src/DivaGL/Glitter/render_group.cpp index 443bbc8..c9546b2 100644 --- a/src/DivaGL/Glitter/render_group.cpp +++ b/src/DivaGL/Glitter/render_group.cpp @@ -24,7 +24,7 @@ namespace Glitter { vec3 axis; float_t angle; - axis_angle_from_vectors(&axis, &angle, &vec1, &vec2); + axis_angle_from_vectors(axis, angle, vec1, vec2); mat4_mul_rotation(&mat, &axis, angle, &mat); } else @@ -47,7 +47,7 @@ namespace Glitter { vec3 axis; float_t angle; - axis_angle_from_vectors(&axis, &angle, &vec1, &vec2); + axis_angle_from_vectors(axis, angle, vec1, vec2); mat4_mul_rotation(&mat, &axis, angle, &mat); } return mat; @@ -467,7 +467,7 @@ namespace Glitter { vec3 axis; float_t angle; - axis_angle_from_vectors(&axis, &angle, &vec1, &vec2); + axis_angle_from_vectors(axis, angle, vec1, vec2); mat4 mat = mat4_identity; mat4_mul_rotation(&mat, &axis, angle, &mat); @@ -487,7 +487,7 @@ namespace Glitter { vec3 axis; float_t angle; - axis_angle_from_vectors(&axis, &angle, &vec1, &vec2); + axis_angle_from_vectors(axis, angle, vec1, vec2); mat4 mat = mat4_identity; mat4_mul_rotation(&mat, &axis, angle, &mat); @@ -509,7 +509,7 @@ namespace Glitter { vec3 axis; float_t angle; - axis_angle_from_vectors(&axis, &angle, &vec1, &vec2); + axis_angle_from_vectors(axis, angle, vec1, vec2); mat4 mat = mat4_identity; mat4_mul_rotation(&mat, &axis, angle, &mat); @@ -529,7 +529,7 @@ namespace Glitter { vec3 axis; float_t angle; - axis_angle_from_vectors(&axis, &angle, &vec1, &vec2); + axis_angle_from_vectors(axis, angle, vec1, vec2); mat4 mat = mat4_identity; mat4_mul_rotation(&mat, &axis, angle, &mat); diff --git a/src/DivaGL/Glitter/render_scene.cpp b/src/DivaGL/Glitter/render_scene.cpp index f69d9c3..5bae7f6 100644 --- a/src/DivaGL/Glitter/render_scene.cpp +++ b/src/DivaGL/Glitter/render_scene.cpp @@ -9,6 +9,9 @@ #include "../shader_ft.hpp" #include "../wrap.hpp" +extern bool reflect_draw; +extern mat4 reflect_mat; + namespace Glitter { void RenderScene::CalcDisp() { disp_quad = 0; @@ -88,8 +91,8 @@ namespace Glitter { mat4 cam_view; mat4 cam_projection; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->projection, &cam_projection); + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.projection, &cam_projection); mat4 mat; mat4_transpose(&rend_group->mat_draw, &mat); @@ -368,8 +371,8 @@ namespace Glitter { mat4 cam_view; mat4 cam_inv_view; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->inv_view, &cam_inv_view); + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.inv_view, &cam_inv_view); vec3 x_vec = { 1.0f, 0.0f, 0.0f }; if (rend_group->flags & PARTICLE_LOCAL) { @@ -511,8 +514,8 @@ namespace Glitter { mat4 cam_view; mat4 cam_inv_view; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->inv_view, &cam_inv_view); + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.inv_view, &cam_inv_view); mat4 model_mat; mat4 view_mat; @@ -571,7 +574,7 @@ namespace Glitter { mat4_rotate_z((float_t)-M_PI_2, &dir_mat); break; case DIRECTION_BILLBOARD_Y_AXIS: - mat4_rotate_y(camera_data->rotation.y, &dir_mat); + mat4_rotate_y(camera_data.rotation.y, &dir_mat); break; default: dir_mat = mat4_identity; @@ -717,7 +720,7 @@ namespace Glitter { if (fabsf(rend_group->z_offset) > 0.000001f) { use_z_offset = true; mat4_get_translation(model_mat, &dist_to_cam); - dist_to_cam = camera_data->view_point - dist_to_cam; + dist_to_cam = camera_data.view_point - dist_to_cam; if (rend_group->flags & PARTICLE_EMITTER_LOCAL) { mat4_normalize_rotation(model_mat, &z_offset_inv_mat); mat4_invert(&z_offset_inv_mat, &z_offset_inv_mat); @@ -1197,8 +1200,8 @@ namespace Glitter { mat4 cam_view; mat4 cam_inv_view; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->inv_view, &cam_inv_view); + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.inv_view, &cam_inv_view); vec3 x_vec = { 1.0f, 0.0f, 0.0f }; if (rend_group->flags & PARTICLE_LOCAL) { @@ -1338,8 +1341,8 @@ namespace Glitter { mat4 cam_view; mat4 cam_inv_view; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->inv_view, &cam_inv_view); + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.inv_view, &cam_inv_view); mat4 model_mat; mat4 view_mat; @@ -1387,7 +1390,7 @@ namespace Glitter { mat4_rotate_x((float_t)-M_PI_2, &dir_mat); break; case DIRECTION_BILLBOARD_Y_AXIS: - mat4_rotate_y(camera_data->rotation.y, &dir_mat); + mat4_rotate_y(camera_data.rotation.y, &dir_mat); break; default: dir_mat = mat4_identity; @@ -1532,7 +1535,7 @@ namespace Glitter { if (fabsf(rend_group->z_offset) > 0.000001f) { use_z_offset = true; mat4_get_translation(model_mat, &dist_to_cam); - dist_to_cam = camera_data->view_point - dist_to_cam; + dist_to_cam = camera_data.view_point - dist_to_cam; if (rend_group->flags & PARTICLE_EMITTER_LOCAL) { mat4_normalize_rotation(model_mat, &z_offset_inv_mat); mat4_invert(&z_offset_inv_mat, &z_offset_inv_mat); @@ -1804,8 +1807,10 @@ namespace Glitter { mat4 cam_view; mat4 cam_projection; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->projection, &cam_projection); + mat4_transpose(&camera_data.view, &cam_view); + if (reflect_draw) + mat4_mul(&reflect_mat, &cam_view, &cam_view); + mat4_transpose(&camera_data.projection, &cam_projection); mat4 mat; mat4_mul(&rend_group->mat_draw, &cam_view, &mat); @@ -1979,8 +1984,8 @@ namespace Glitter { mat4 cam_view; mat4 cam_inv_view; - mat4_transpose(&camera_data->view, &cam_view); - mat4_transpose(&camera_data->inv_view, &cam_inv_view); + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.inv_view, &cam_inv_view); bool has_scale = false; bool emitter_local = false; @@ -2044,7 +2049,7 @@ namespace Glitter { mat4_rotate_x((float_t)-M_PI_2, &dir_mat); break; case DIRECTION_BILLBOARD_Y_AXIS: - mat4_rotate_y(camera_data->rotation.y, &dir_mat); + mat4_rotate_y(camera_data.rotation.y, &dir_mat); break; case DIRECTION_EMITTER_ROTATION: emitter_rotation = true; @@ -2096,6 +2101,8 @@ namespace Glitter { const bool object_culling = disp_manager.object_culling; if (local) disp_manager.object_culling = false; + extern bool reflect_full; + mdl::obj_reflect_enable = !local && reflect_full; disp_manager.set_texture_pattern(0, 0); RenderElementX* elem = rend_group->elements; @@ -2159,7 +2166,7 @@ namespace Glitter { elem->mat_draw = mat; mat4_transpose(&mat, &mat); - if (disp_manager.entry_obj_by_object_info(&mat, + if (disp_manager.entry_obj_by_object_info(mat, rend_group->object, &elem->color, 0, local)) disp++; @@ -2170,6 +2177,7 @@ namespace Glitter { rend_group->disp = disp; if (local) disp_manager.object_culling = object_culling; + mdl::obj_reflect_enable = false; } size_t RenderSceneX::GetCtrlCount(ParticleType type) { diff --git a/src/DivaGL/auth_2d.cpp b/src/DivaGL/auth_2d.cpp index f9ffa9b..352c85c 100644 --- a/src/DivaGL/auth_2d.cpp +++ b/src/DivaGL/auth_2d.cpp @@ -217,7 +217,7 @@ void aet_layout_data::put_sprite(int32_t spr_id, spr::SprAttr attr, args.prio = prio; if (pos) *(vec2*)&args.trans.x += *pos; - spr::put_sprite(&args); + spr::put_sprite(args); } void aet_layout_data::set_args(const aet_layout_data* layout, spr::SprArgs* args) { diff --git a/src/DivaGL/auth_3d.cpp b/src/DivaGL/auth_3d.cpp index 046a7df..393b327 100644 --- a/src/DivaGL/auth_3d.cpp +++ b/src/DivaGL/auth_3d.cpp @@ -6,7 +6,9 @@ #include "auth_3d.hpp" #include "../KKdLib/hash.hpp" #include "light_param/fog.hpp" +#include "light_param/light.hpp" #include "mdl/disp_manager.hpp" +#include "pv_game/firstread.hpp" #include "rob/rob.hpp" #include "canonical_properties.hpp" #include "file_handler.hpp" @@ -451,6 +453,7 @@ struct auth_3d { mat4* bone_mats; bool shadow; bool chara_item; + bool reflect; chara_index src_chara; chara_index dst_chara; int32_t pos; @@ -877,6 +880,14 @@ void auth_3d_id::set_pos(int32_t value) { } } +void auth_3d_id::set_reflect(bool value) { + if (id >= 0 && ((id & 0x7FFF) < AUTH_3D_DATA_COUNT)) { + auth_3d* auth = &auth_3d_data->data[id & 0x7FFF]; + if (auth->id == id) + auth->reflect = value; + } +} + void auth_3d_id::set_repeat(bool value) { if (id >= 0 && ((id & 0x7FFF) < AUTH_3D_DATA_COUNT)) { auth_3d* auth = &auth_3d_data->data[id & 0x7FFF]; @@ -966,6 +977,59 @@ int32_t auth_3d_data_get_auth_3d_id(uint32_t uid, object_info obj_info, return -1; } +HOOK(void, FASTCALL, auth_3d_parse, 0x00000001401C15B0, auth_3d* auth) { + originalauth_3d_parse(auth); + + extern bool reflect_full; + extern const firstread* firstread_ptr; + if (!reflect_full || !firstread_ptr || !firstread_ptr->auth_3d_array) + return; + + const firstread_auth_3d_array* auth_3d_array = firstread_ptr->auth_3d_array; + + for (uint32_t i = 0; i < auth_3d_array->num_auth_3d; i++) { + if (auth_3d_array->auth_3d_array[i].uid != auth->uid) + continue; + + const firstread_auth_3d* frg_auth_3d = &auth_3d_array->auth_3d_array[i]; + + auto obj_begin = auth->object.begin(); + auto obj_end = auth->object.end(); + const uint32_t num_object = frg_auth_3d->num_object; + for (uint32_t j = 0; j < num_object; j++) { + const uint64_t name_hash = hash_utf8_xxh3_64bits(frg_auth_3d->object_array[j].name); + for (auth_3d_object& k : auth->object) { + if (name_hash != hash_utf8_xxh3_64bits(k.name.c_str())) + continue; + + const firstread_auth_3d_object* frg_a3d_obj = &frg_auth_3d->object_array[j]; + k.uid_name.assign(frg_a3d_obj->uid_name); + k.object_info = object_database_get_object_info(frg_a3d_obj->uid_name); + + k.reflect = k.uid_name.find("_REFLECT") != -1; + k.refract = k.uid_name.find("_REFRACT") != -1; + break; + } + } + + auto obj_list_begin = auth->object_list.begin(); + auto obj_list_end = auth->object_list.end(); + const uint32_t num_object_list = frg_auth_3d->num_object_list; + for (uint32_t j = 0; j < num_object_list; j++) { + const uint64_t name_hash = hash_utf8_xxh3_64bits(frg_auth_3d->object_list_array[j].name); + for (auto obj_list = obj_list_begin; obj_list != obj_list_end; ) + if (name_hash == hash_utf8_xxh3_64bits((*obj_list)->name.c_str())) { + auth->object_list.erase(obj_list); + obj_list_end = auth->object_list.end(); + break; + } + else + obj_list++; + } + break; + } +} + HOOK(void, FASTCALL, auth_3d_curve_array_free, 0x00000001401C2E30, prj::vector* vec, auth_3d_curve* begin, auth_3d_curve* end) { while (begin != end) { @@ -1037,7 +1101,7 @@ HOOK(void, FASTCALL, auth_3d_m_object_hrc_disp, 0x00000001401D0760, auth_3d_m_ob if (i.mats.size()) disp_manager.entry_obj_by_object_info_object_skin( - i.object_info, 0, 0, auth->alpha, i.mats.data(), 0, 0, &i.model_transform.mat); + i.object_info, 0, 0, auth->alpha, i.mats.data(), 0, 0, i.model_transform.mat); } disp_manager.set_obj_flags(); @@ -1098,7 +1162,7 @@ HOOK(void, FASTCALL, auth_3d_object_hrc_disp, 0x00000001401D04A0, auth_3d_object if (oh->node_bone_mats.size()) disp_manager.entry_obj_by_object_info_object_skin( - oh->object_info, 0, 0, auth->alpha, oh->node_bone_mats.data(), 0, 0, &mat); + oh->object_info, 0, 0, auth->alpha, oh->node_bone_mats.data(), 0, 0, mat); disp_manager.set_obj_flags(); disp_manager.set_shadow_type(SHADOW_CHARA); @@ -1118,7 +1182,7 @@ HOOK(void, FASTCALL, auth_3d_object_disp, 0x00000001401D0970, auth_3d_object* o, if (!auth->visible) { if (auth->pos) - spr::put_rgb_cross(&o->model_transform.mat); + spr::put_rgb_cross(o->model_transform.mat); for (auth_3d_object*& i : o->children_object) implOfauth_3d_object_disp(i, auth); @@ -1196,7 +1260,7 @@ HOOK(void, FASTCALL, auth_3d_object_disp, 0x00000001401D0970, auth_3d_object* o, int32_t uid_name_length = (int32_t)o->uid_name.size(); if (uid_name_length <= 3) - disp_manager.entry_obj_by_object_info(&mat, o->object_info); + disp_manager.entry_obj_by_object_info(mat, o->object_info); else if (o->morph.curve) { float_t morph = o->morph.value; int32_t morph_int = (int32_t)morph; @@ -1211,9 +1275,9 @@ HOOK(void, FASTCALL, auth_3d_object_disp, 0x00000001401D0970, auth_3d_object* o, sprintf_s(buf, sizeof(buf), "%.*s%03d", uid_name_length - 3, uid_name, morph_int); object_info obj_info = object_database_get_object_info(buf); if (auth->alpha < 0.999f) - disp_manager.entry_obj_by_object_info(&mat, obj_info, auth->alpha); + disp_manager.entry_obj_by_object_info(mat, obj_info, auth->alpha); else - disp_manager.entry_obj_by_object_info(&mat, obj_info); + disp_manager.entry_obj_by_object_info(mat, obj_info); disp_manager.set_morph({}, 0.0f); } else { @@ -1224,24 +1288,24 @@ HOOK(void, FASTCALL, auth_3d_object_disp, 0x00000001401D0970, auth_3d_object* o, object_info obj_info = object_database_get_object_info(buf); if (obj_info.is_null()) obj_info = o->object_info; - disp_manager.entry_obj_by_object_info(&mat, obj_info); + disp_manager.entry_obj_by_object_info(mat, obj_info); } } else if (o->pattern.curve) { sprintf_s(buf, sizeof(buf), "%.*s%03d", uid_name_length - 3, uid_name, (int32_t)prj::roundf(o->pattern.value)); object_info obj_info = object_database_get_object_info(buf); - disp_manager.entry_obj_by_object_info(&mat, obj_info); + disp_manager.entry_obj_by_object_info(mat, obj_info); } else - disp_manager.entry_obj_by_object_info(&mat, o->object_info); + disp_manager.entry_obj_by_object_info(mat, o->object_info); disp_manager.set_texture_transform(); disp_manager.set_texture_pattern(); disp_manager.set_obj_flags(); if (auth->pos) - spr::put_rgb_cross(&o->model_transform.mat); + spr::put_rgb_cross(o->model_transform.mat); for (auth_3d_object*& i : o->children_object) implOfauth_3d_object_disp(i, auth); @@ -1254,7 +1318,10 @@ HOOK(void, FASTCALL, auth_3d__disp, 0x00000001401D1230, auth_3d* auth) { return; auth_3d_set_material_list(auth); + extern bool reflect_full; + mdl::obj_reflect_enable = reflect_full && auth->reflect; originalauth_3d__disp(auth); + mdl::obj_reflect_enable = false; disp_manager->set_material_list(); } @@ -1298,9 +1365,10 @@ void auth_3d_patch() { // Move both auth_3d_curve's value and type WRITE_MEMORY_STRING(0x00000001401B316A, "\x48\x8B\x43\x68\x48\x89" "\x47\x68\x48\x8B\xC7\x48\x8B\x5C\x24\x48\x48\x83\xC4\x30\x5F\xC3", 0x16); - // Null both auth_3d's shadow and chara_item + // Null auth_3d's shadow, chara_item and reflect WRITE_MEMORY_STRING(0x00000001401D7A78, "\x44\x89\xBF\x98\x00\x00\x00", 0x07); + INSTALL_HOOK(auth_3d_parse); INSTALL_HOOK(auth_3d_curve_array_free); INSTALL_HOOK(auth_3d_m_object_hrc_disp); INSTALL_HOOK(auth_3d_object_hrc_disp); diff --git a/src/DivaGL/auth_3d.hpp b/src/DivaGL/auth_3d.hpp index 975abd8..481f47a 100644 --- a/src/DivaGL/auth_3d.hpp +++ b/src/DivaGL/auth_3d.hpp @@ -59,6 +59,7 @@ struct auth_3d_id { void set_max_frame(float_t value); void set_paused(bool value); void set_pos(int32_t value); + void set_reflect(bool value); void set_repeat(bool value); void set_req_frame(float_t value); void set_shadow(bool value); diff --git a/src/DivaGL/camera.cpp b/src/DivaGL/camera.cpp index de69254..c5e6f9e 100644 --- a/src/DivaGL/camera.cpp +++ b/src/DivaGL/camera.cpp @@ -8,10 +8,10 @@ #include "resolution_mode.hpp" #include -camera_struct* camera_data = (camera_struct*)0x0000000140FBC2C0; +camera_struct& camera_data = *(camera_struct*)0x0000000140FBC2C0; -static void (FASTCALL* camera_struct__update_view)(camera_struct* cam) - = (void (FASTCALL*)(camera_struct * cam))0x00000001401F7690; +static void (FASTCALL* camera_struct__update_view)(camera_struct& cam) + = (void (FASTCALL*)(camera_struct & cam))0x00000001401F7690; HOOK(void, FASTCALL, camera_data_update_projection, 0x00000001401F8E90) { resolution_struct* res_wind = res_window_get(); @@ -22,15 +22,15 @@ HOOK(void, FASTCALL, camera_data_update_projection, 0x00000001401F8E90) { float_t render_half_width = (float_t)res_wind_int->width * 0.5f; float_t render_half_height = (float_t)res_wind_int->height * 0.5f; - float_t aet_depth = camera_data->aet_depth; + float_t aet_depth = camera_data.aet_depth; - float_t spr_2d_range = camera_data->min_distance / aet_depth; + float_t spr_2d_range = camera_data.min_distance / aet_depth; float_t spr_2d_range_x = spr_2d_range * sprite_half_width; float_t spr_2d_range_y = spr_2d_range * sprite_half_height; mat4 spr_2d_proj; mat4_frustrum(-spr_2d_range_x, spr_2d_range_x, spr_2d_range_y, -spr_2d_range_y, - camera_data->min_distance, 3000.0f, &spr_2d_proj); + camera_data.min_distance, 3000.0f, &spr_2d_proj); vec3 spr_2d_viewpoint = { sprite_half_width, sprite_half_height, aet_depth }; vec3 spr_2d_interest = { sprite_half_width, sprite_half_height, 0.0f }; @@ -39,17 +39,17 @@ HOOK(void, FASTCALL, camera_data_update_projection, 0x00000001401F8E90) { mat4_look_at(&spr_2d_viewpoint, &spr_2d_interest, &spr_2d_up, &spr_2d_view); mat4_mul(&spr_2d_view, &spr_2d_proj, &spr_2d_proj); - mat4_transpose(&spr_2d_proj, &camera_data->view_projection_aet_2d); + mat4_transpose(&spr_2d_proj, &camera_data.view_projection_aet_2d); - float_t aet_3d_depth = render_half_height / tanf(camera_data->aet_fov * 0.5f * DEG_TO_RAD_FLOAT); + float_t aet_3d_depth = render_half_height / tanf(camera_data.aet_fov * 0.5f * DEG_TO_RAD_FLOAT); - float_t spr_3d_range = camera_data->min_distance / aet_3d_depth; + float_t spr_3d_range = camera_data.min_distance / aet_3d_depth; float_t spr_3d_range_x = spr_3d_range * render_half_width; float_t spr_3d_range_y = spr_3d_range * render_half_height; mat4 spr_3d_proj; mat4_frustrum(-spr_3d_range_x, spr_3d_range_x, spr_3d_range_y, -spr_3d_range_y, - camera_data->min_distance, 3000.0f, &spr_3d_proj); + camera_data.min_distance, 3000.0f, &spr_3d_proj); vec3 spr_3d_viewpoint = { render_half_width, render_half_height, aet_3d_depth }; vec3 spr_3d_interest = { render_half_width, render_half_height, 0.0f }; @@ -58,21 +58,21 @@ HOOK(void, FASTCALL, camera_data_update_projection, 0x00000001401F8E90) { mat4_look_at(&spr_3d_viewpoint, &spr_3d_interest, &spr_3d_up, &spr_3d_view); mat4_mul(&spr_3d_view, &spr_3d_proj, &spr_3d_proj); - mat4_transpose(&spr_3d_proj, &camera_data->view_projection_aet_3d); + mat4_transpose(&spr_3d_proj, &camera_data.view_projection_aet_3d); - render_get()->calc_projection_matrix(&camera_data->projection, camera_data->fov, - (float_t)camera_data->aspect, camera_data->min_distance, camera_data->max_distance, - camera_data->proj_left_offset, camera_data->proj_right_offset, - camera_data->proj_bottom_offset, camera_data->proj_top_offset); - camera_data->fov_horizontal_rad = tanf(camera_data->fov * 0.5f * DEG_TO_RAD_FLOAT); + render_get()->calc_projection_matrix(&camera_data.projection, camera_data.fov, + (float_t)camera_data.aspect, camera_data.min_distance, camera_data.max_distance, + camera_data.proj_left_offset, camera_data.proj_right_offset, + camera_data.proj_bottom_offset, camera_data.proj_top_offset); + camera_data.fov_horizontal_rad = tanf(camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT); } HOOK(void, FASTCALL, camera_update, 0x00000001401F8970) { implOfcamera_data_update_projection(); camera_struct__update_view(camera_data); - camera_data->fast_change_hist1 = camera_data->fast_change_hist0; - camera_data->fast_change_hist0 = camera_data->fast_change; - camera_data->fast_change = false; + camera_data.fast_change_hist1 = camera_data.fast_change_hist0; + camera_data.fast_change_hist0 = camera_data.fast_change; + camera_data.fast_change = false; } void camera_struct::get_view_point(vec3& value) { diff --git a/src/DivaGL/camera.hpp b/src/DivaGL/camera.hpp index fa8ca7e..75428b6 100644 --- a/src/DivaGL/camera.hpp +++ b/src/DivaGL/camera.hpp @@ -59,6 +59,6 @@ struct camera_struct { static void update_data(); }; -extern camera_struct* camera_data; +extern camera_struct& camera_data; extern void camera_patch(); diff --git a/src/DivaGL/data_test/rob_osage_test.cpp b/src/DivaGL/data_test/rob_osage_test.cpp index 5edc2ce..ac28ae1 100644 --- a/src/DivaGL/data_test/rob_osage_test.cpp +++ b/src/DivaGL/data_test/rob_osage_test.cpp @@ -998,7 +998,7 @@ void RobOsageTest::disp_coli() { etc.data.capsule.wire = false; etc.data.capsule.pos[0] = j[-1].pos; etc.data.capsule.pos[1] = j[ 0].pos; - disp_manager->entry_obj_etc(&mat4_identity, &etc); + disp_manager->entry_obj_etc(mat4_identity, etc); } } else if (cls && cls->rob.nodes.size() >= cls->rob.root_count) { @@ -1022,7 +1022,7 @@ void RobOsageTest::disp_coli() { mat4_translate(&pos, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } } } @@ -1068,7 +1068,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision mat4_translate(&pos, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } break; case SkinParam::CollisionTypeCapsule: { mdl::EtcObj etc(mdl::ETC_OBJ_CAPSULE); @@ -1086,7 +1086,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision mat4_transpose(&transform[i.node_idx[1]], &mat); mat4_transform_point(&mat, &i.pos[1], &etc.data.capsule.pos[1]); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat4_identity, &etc); + disp_manager->entry_obj_etc(mat4_identity, etc); } break; case SkinParam::CollisionTypePlane: { mdl::EtcObj etc(mdl::ETC_OBJ_PLANE); @@ -1105,7 +1105,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision const vec3 up = { 0.0f, 1.0f, 0.0f }; vec3 axis; float_t angle; - Glitter::axis_angle_from_vectors(&axis, &angle, &up, &dir); + Glitter::axis_angle_from_vectors(axis, angle, up, dir); mat4 mat_rot = mat4_identity; mat4_mul_rotation(&mat_rot, &axis, angle, &mat_rot); @@ -1114,7 +1114,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision mat4_mul(&mat_rot, &mat, &mat); mat4_scale_rot(&mat, i.radius, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } break; case SkinParam::CollisionTypeEllipse: { mdl::EtcObj etc(mdl::ETC_OBJ_SPHERE); @@ -1140,7 +1140,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision const vec3 up = { 0.0f, 1.0f, 0.0f }; vec3 axis; float_t angle; - Glitter::axis_angle_from_vectors(&axis, &angle, &up, &dir); + Glitter::axis_angle_from_vectors(axis, angle, up, dir); mat4 m = mat4_identity; mat4_mul_rotation(&m, &axis, angle, &m); @@ -1151,7 +1151,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision mat4_scale_rot(&mat, i.radius, scale_y, i.radius, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } break; case SkinParam::CollisionTypeAABB: { mdl::EtcObj etc(mdl::ETC_OBJ_CUBE); @@ -1168,7 +1168,7 @@ void RobOsageTest::disp_coli_cls_list(ExNodeBlock* ex_node, SkinParam::Collision mat4_translate(&pos, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } break; } } @@ -1193,9 +1193,9 @@ void RobOsageTest::disp_line() { mat4_scale_rot(&mat, 0.05f, &mat); mat4_transpose(&mat, &mat); if (i == ex_node) - spr::put_cross(&mat, color_red, color_green, color_blue); + spr::put_cross(mat, color_red, color_green, color_blue); else - spr::put_cross(&mat, color_dark_red, color_dark_green, color_dark_blue); + spr::put_cross(mat, color_dark_red, color_dark_green, color_dark_blue); const color4u8 line_color = i == ex_node ? color_white : color_dark_cyan; const color4u8 rect_color = i == ex_node ? color_red : color_dark_red; @@ -1212,7 +1212,7 @@ void RobOsageTest::disp_line() { mat4_transpose(i->rob.end_node.bone_node_mat, &mat); mat4_scale_rot(&mat, 0.05f, &mat); mat4_transpose(&mat, &mat); - spr::put_cross(&mat, color_dark_red, color_dark_green, color_dark_blue); + spr::put_cross(mat, color_dark_red, color_dark_green, color_dark_blue); } for (ExClothBlock*& i : itm_eq_obj->cloth_blocks) { @@ -1285,7 +1285,7 @@ void RobOsageTest::disp_line_cls_param(const mat4& transform, const vec3& pos) { mat4_transpose(&transform, &mat); mat4_scale_rot(&mat, 0.1f, &mat); mat4_transpose(&mat, &mat); - spr::put_cross(&mat, color_red, color_green, color_blue); + spr::put_cross(mat, color_red, color_green, color_blue); vec3 p; mat4_transform_point(&transform, &pos, &p); diff --git a/src/DivaGL/dllmain.cpp b/src/DivaGL/dllmain.cpp index 42cf64e..9c8a19f 100644 --- a/src/DivaGL/dllmain.cpp +++ b/src/DivaGL/dllmain.cpp @@ -18,6 +18,7 @@ size_t opengl32_handle; size_t dll_handle; bool cpu_caps_aes_ni; bool cpu_caps_f16c; +bool config_reflect_full = true; void FASTCALL divagl_main() { int32_t cpuid_data[4] = {}; diff --git a/src/DivaGL/effect.cpp b/src/DivaGL/effect.cpp index 6f36f30..688c6b9 100644 --- a/src/DivaGL/effect.cpp +++ b/src/DivaGL/effect.cpp @@ -554,7 +554,7 @@ void leaf_particle_draw() { shader_data.g_transform[1] = temp.row1; shader_data.g_transform[2] = temp.row2; shader_data.g_transform[3] = temp.row3; - camera_data->get_view_point(shader_data.g_view_pos); + camera_data.get_view_point(shader_data.g_view_pos); shader_data.g_color = stage_param_data_leaf_current->color; rctx->get_scene_light(&shader_data.g_light_env_stage_diffuse, &shader_data.g_light_env_stage_specular, @@ -662,7 +662,7 @@ void particle_draw() { shader_data.g_transform[1] = temp.row1; shader_data.g_transform[2] = temp.row2; shader_data.g_transform[3] = temp.row3; - camera_data->get_view_point(shader_data.g_view_pos); + camera_data.get_view_point(shader_data.g_view_pos); light_chara.get_diffuse(shader_data.g_light_env_chara_diffuse); light_chara.get_specular(shader_data.g_light_env_chara_specular); particle_scene_ubo.WriteMemory(shader_data); @@ -690,7 +690,7 @@ void snow_particle_draw() { gl_state_enable_depth_test(); gl_state_set_depth_mask(GL_FALSE); - float_t point_attenuation = powf(tanf((float_t)camera_data->fov + float_t point_attenuation = powf(tanf((float_t)camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT) * 3.4f, 2.0f) * 0.1f; snow_particle_scene_shader_data snow_scene = {}; @@ -741,7 +741,7 @@ void snow_particle_draw() { snow_particle_scene_ubo.Bind(0); snow_particle_batch_ubo.Bind(1); - point_attenuation = powf(tanf((float_t)camera_data->fov + point_attenuation = powf(tanf((float_t)camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT) * 3.4f, 2.0f) * 0.06f; snow_scene.g_state_point_attenuation = { 0.0f, 0.0f, point_attenuation, 0.0f }; @@ -794,7 +794,7 @@ HOOK(void, FASTCALL, EffectFogRing__dest, 0x0000000140348090, EffectFogRing* fog HOOK(void, FASTCALL, EffectFogRing__disp, 0x0000000140348120, EffectFogRing* fog_ring) { if (fog_ring->enable && fog_ring->display) - disp_manager->entry_obj_user(&mat4_identity, + disp_manager->entry_obj_user(mat4_identity, (mdl::UserArgsFunc)draw_fog_particle, fog_ring, mdl::OBJ_TYPE_USER); } @@ -1074,7 +1074,7 @@ HOOK(void, FASTCALL, EffectRipple__draw_static, 0x000000014035AA50, void* data) HOOK(void, FASTCALL, EffectRipple__disp_particles, 0x000000014035AD80, EffectRipple* effect_ripple, ripple_struct* data) { if (data->count > 0) - disp_manager->entry_obj_user(&mat4_identity, + disp_manager->entry_obj_user(mat4_identity, (mdl::UserArgsFunc)draw_ripple_particles, data, mdl::OBJ_TYPE_USER); } @@ -1329,7 +1329,7 @@ HOOK(void, FASTCALL, water_particle__disp, 0x0000000140364090, water_particle* w if (!water_ptcl->splash) return; - disp_manager->entry_obj_user(&mat4_identity, + disp_manager->entry_obj_user(mat4_identity, (mdl::UserArgsFunc)draw_water_particle, water_ptcl, mdl::OBJ_TYPE_TRANSLUCENT); EffectRipple* effect_ripple = effect_ripple_data_get(); @@ -1406,7 +1406,7 @@ void EffectFogRing::draw() { return; draw_state.set_fog_height(true); - RenderTexture& rt = render_manager->get_render_texture(8); + RenderTexture& rt = render_manager.get_render_texture(8); rt.Bind(); gl_state_set_viewport(0, 0, rt.color_texture->get_width_align_mip_level(), @@ -1416,7 +1416,7 @@ void EffectFogRing::draw() { if (disp_manager->get_obj_count(mdl::OBJ_TYPE_USER)) disp_manager->draw(mdl::OBJ_TYPE_USER); gl_state_bind_framebuffer(0); - render_manager->set_effect_texture(rt.color_texture); + render_manager.set_effect_texture(rt.color_texture); gl_state_get_error(); } @@ -1475,7 +1475,7 @@ void EffectRipple::draw() { RenderTexture* rt[3]; for (int32_t i = 0, j = 2; i < 3; i++, j++) - rt[i] = &render_manager->get_render_texture( + rt[i] = &render_manager.get_render_texture( use_float_ripplemap ? j : (j + 3)); if (update) { @@ -1522,8 +1522,8 @@ void EffectRipple::draw() { if (!use_float_ripplemap) v11 += 3; - render_manager->set_effect_texture( - render_manager->get_render_texture(v11).color_texture); + render_manager.set_effect_texture( + render_manager.get_render_texture(v11).color_texture); update = false; @@ -2016,7 +2016,7 @@ static void draw_ripple_particles(ripple_struct* data, mat4* mat) { int32_t size = (int32_t)(data->size + 0.5f); - RenderTexture& rt = render_manager->get_render_texture( + RenderTexture& rt = render_manager.get_render_texture( effect_ripple_data_get()->use_float_ripplemap ? 2 : 5); int32_t width = rt.GetWidth(); int32_t height = rt.GetHeight(); diff --git a/src/DivaGL/graphics/color_change_dw.cpp b/src/DivaGL/graphics/color_change_dw.cpp index e70cb41..e6651e5 100644 --- a/src/DivaGL/graphics/color_change_dw.cpp +++ b/src/DivaGL/graphics/color_change_dw.cpp @@ -145,7 +145,7 @@ HOOK(void, FASTCALL, ColorChangeDw__Draw, 0x00000001402C2170, ColorChangeDw* Thi args.texture = org_tex; if (This->auto_resize) args.SetSpriteSize({ 256.0f, 256.0f }); - spr::put_sprite(&args); + spr::put_sprite(args); } if (chg_tex) { @@ -156,7 +156,7 @@ HOOK(void, FASTCALL, ColorChangeDw__Draw, 0x00000001402C2170, ColorChangeDw* Thi args.SetSpriteSize({ 256.0f, 256.0f }); args.trans.x = 266.0f; } - spr::put_sprite(&args); + spr::put_sprite(args); } } diff --git a/src/DivaGL/hook.cpp b/src/DivaGL/hook.cpp index aed5f39..40f5d35 100644 --- a/src/DivaGL/hook.cpp +++ b/src/DivaGL/hook.cpp @@ -75,6 +75,7 @@ HOOK(int32_t, FASTCALL, data_init, 0x0000000140192FF0) { Glitter::Patch(); rctx = new render_context; + reflect_full_init(); int32_t ret = originaldata_init(); extern size_t diva_handle; @@ -98,6 +99,7 @@ HOOK(int32_t, FASTCALL, data_free, 0x000000140192490) { rctx->free(); int32_t ret = originaldata_free(); + reflect_full_free(); delete rctx; rctx = 0; return ret; @@ -156,20 +158,20 @@ HOOK(void, FASTCALL, rndr__Render__movie_texture_free, 0x00000001404B18F0, HOOK(void, FASTCALL, rndr__RenderManager__rndpass_pre_proc, 0x0000000140502C90) { gl_state_begin_event("rndpass_pre_proc"); - render_manager->render->pre_proc(); + render_manager.render->pre_proc(); Glitter::glt_particle_manager->CalcDisp(); Glitter::glt_particle_manager_x->CalcDisp(); gl_state_end_event(); } HOOK(void, FASTCALL, rndr__RenderManager__render_all, 0x0000000140502CA0) { - render_manager->render_all(); + render_manager.render_all(); } HOOK(void, FASTCALL, rndr__RenderManager__rndpass_post_proc, 0x0000000140502C70) { gl_state_begin_event("rndpass_post_proc"); - render_manager->render->post_proc(); - render_manager->field_31C = false; + render_manager.render->post_proc(); + render_manager.field_31C = false; gl_state_end_event(); } diff --git a/src/DivaGL/mdl/disp_manager.cpp b/src/DivaGL/mdl/disp_manager.cpp index 96bb126..f69ad0d 100644 --- a/src/DivaGL/mdl/disp_manager.cpp +++ b/src/DivaGL/mdl/disp_manager.cpp @@ -45,6 +45,9 @@ static void object_data_get_vertex_attrib_buffer_bindings(const mdl::ObjSubMeshA static void sub_140436760(mat4* view); +extern bool reflect_draw; +extern mat4 reflect_mat; + material_list_struct::material_list_struct() : blend_color(), has_blend_color(), emission(), has_emission() { hash = (uint64_t)-1; } @@ -79,6 +82,9 @@ namespace mdl { int32_t material_list_count; material_list_struct material_list_array[MATERIAL_LIST_COUNT]; + + bool obj_reflect_enable; + ObjSubMeshArgs::ObjSubMeshArgs() : sub_mesh(), mesh(), material(), textures(), mat_count(), mats(), vertex_buffer(), vertex_buffer_offset(), index_buffer(), set_blend_color(), chara_color(), self_shadow(), shadow(), morph_vertex_buffer(), morph_vertex_buffer_offset(), morph_weight(), texture_pattern_count(), @@ -213,23 +219,22 @@ namespace mdl { } - void ObjData::init_etc(const mat4* mat, mdl::EtcObj* etc) { + void ObjData::init_etc(const mat4& mat, const mdl::EtcObj& etc) { kind = mdl::OBJ_KIND_ETC; - mat4_transpose(mat, &this->mat); - args.etc = *etc; + mat4_transpose(&mat, &this->mat); + args.etc = etc; disp_manager->add_vertex_array(&args.etc, this->mat); } - void ObjData::init_sub_mesh(const mat4* mat, float_t radius, - const obj_sub_mesh* sub_mesh, const obj_mesh* mesh, const obj_material_data* material, - const prj::vector* textures, int32_t mat_count, const mat4* mats, - GLuint vertex_buffer, size_t vertex_buffer_offset, GLuint index_buffer, - const vec4& blend_color, const vec4& emission, GLuint morph_vertex_buffer, - size_t morph_vertex_buffer_offset, int32_t instances_count, mat4* instances_mat, - void(FASTCALL* func)(const ObjSubMeshArgs*), const ObjSubMeshArgs* func_data) { + void ObjData::init_sub_mesh(const mat4& mat, float_t radius, const obj_sub_mesh* sub_mesh, + const obj_mesh* mesh, const obj_material_data* material, const prj::vector* textures, + int32_t mat_count, const mat4* mats, GLuint vertex_buffer, size_t vertex_buffer_offset, + GLuint index_buffer, const vec4& blend_color, const vec4& emission, GLuint morph_vertex_buffer, + size_t morph_vertex_buffer_offset, + int32_t instances_count, const mat4* instances_mat, draw_func func, const ObjSubMeshArgs* func_data) { kind = mdl::OBJ_KIND_NORMAL; - mat4_transpose(mat, &this->mat); + mat4_transpose(&mat, &this->mat); this->radius = radius; mdl::ObjSubMeshArgs* args = &this->args.sub_mesh; @@ -284,15 +289,15 @@ namespace mdl { disp_manager->add_vertex_array(args); } - void ObjData::init_translucent(const mat4* mat, ObjTranslucentArgs* translucent) { + void ObjData::init_translucent(const mat4& mat, const ObjTranslucentArgs& translucent) { kind = mdl::OBJ_KIND_TRANSLUCENT; - mat4_transpose(mat, &this->mat); - args.translucent = *translucent; + mat4_transpose(&mat, &this->mat); + args.translucent = translucent; } - void ObjData::init_user(const mat4* mat, UserArgsFunc func, void* data) { + void ObjData::init_user(const mat4& mat, UserArgsFunc func, void* data) { kind = OBJ_KIND_USER; - mat4_transpose(mat, &this->mat); + mat4_transpose(&mat, &this->mat); args.user.func = func; args.user.data = data; } @@ -1549,7 +1554,7 @@ namespace mdl { const vec3 up = { 0.0f, 1.0f, 0.0f }; vec3 axis; float_t angle; - Glitter::axis_angle_from_vectors(&axis, &angle, &up, &dir); + Glitter::axis_angle_from_vectors(axis, angle, up, dir); mat4 m = mat4_identity; mat4_mul_rotation(&m, &axis, angle, &m); @@ -1570,7 +1575,7 @@ namespace mdl { const vec3 up = { 0.0f, 1.0f, 0.0f }; vec3 axis; float_t angle; - Glitter::axis_angle_from_vectors(&axis, &angle, &up, &dir); + Glitter::axis_angle_from_vectors(axis, angle, up, dir); mat4 m = mat4_identity; mat4_mul_rotation(&m, &axis, angle, &m); @@ -1923,7 +1928,7 @@ namespace mdl { mat4 mat; mat4_translate(&i, &mat); mat4_transpose(&mat, &mat); - entry_obj_etc(&mat, &etc); + entry_obj_etc(mat, etc); } if (show_mat_center && type == OBJ_TYPE_TRANSLUCENT) @@ -1938,7 +1943,7 @@ namespace mdl { mat4 mat; mat4_translate(&i, &mat); mat4_transpose(&mat, &mat); - entry_obj_etc(&mat, &etc); + entry_obj_etc(mat, etc); } } @@ -1972,7 +1977,7 @@ namespace mdl { } void DispManager::draw(ObjType type, int32_t depth_mask, bool reflect_texture_mask, int32_t alpha) { - if (type < 0 || type >= mdl::OBJ_TYPE_LOCAL_MAX || get_obj_count(type) < 1) + if (type < 0 || type >= OBJ_TYPE_REFLECT_MAX || get_obj_count(type) < 1) return; int32_t alpha_test = 0; @@ -1997,6 +2002,7 @@ namespace mdl { func = draw_sub_mesh_translucent; else gl_state_set_depth_mask(GL_FALSE); + alpha_test = 1; min_alpha = 0.0f; alpha_threshold = 0.0f; @@ -2022,48 +2028,79 @@ namespace mdl { func = draw_sub_mesh_translucent; gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); draw_state.shader_index = SHADER_FT_SIL; + alpha_test = 1; min_alpha = 0.0f; alpha_threshold = 0.99999994f; break; case OBJ_TYPE_REFLECT_CHARA_OPAQUE: gl_state_set_cull_face_mode(GL_FRONT); - if (reflect) - func = draw_sub_mesh_reflect; - else if (render_manager->reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) - func = draw_sub_mesh_reflect_reflect_map; + + if (!reflect_draw) { + if (reflect) + func = draw_sub_mesh_reflect; + else if (render_manager.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) + func = draw_sub_mesh_reflect_reflect_map; + } break; case OBJ_TYPE_REFLECT_CHARA_TRANSLUCENT: gl_state_set_cull_face_mode(GL_FRONT); - if (reflect) - func = draw_sub_mesh_reflect; - else if (render_manager->reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) - func = draw_sub_mesh_reflect_reflect_map; + min_alpha = 0.0f; + + if (!reflect_draw) { + if (reflect) + func = draw_sub_mesh_reflect; + else if (render_manager.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) + func = draw_sub_mesh_reflect_reflect_map; + } break; case OBJ_TYPE_REFLECT_CHARA_TRANSPARENT: gl_state_set_cull_face_mode(GL_FRONT); - if (reflect) - func = draw_sub_mesh_reflect; - else if (render_manager->reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) - func = draw_sub_mesh_reflect_reflect_map; + alpha_test = 1; min_alpha = 0.1f; alpha_threshold = 0.5f; + + if (!reflect_draw) { + if (reflect) + func = draw_sub_mesh_reflect; + else if (render_manager.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) + func = draw_sub_mesh_reflect_reflect_map; + } break; case OBJ_TYPE_REFLECT_OPAQUE: alpha_test = 1; alpha_threshold = 0.5f; - if (!reflect_texture_mask) + + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + else if (!reflect_texture_mask) func = draw_sub_mesh_reflect_reflect_map; break; case OBJ_TYPE_REFLECT_TRANSLUCENT: + gl_state_set_depth_mask(GL_FALSE); + + min_alpha = 0.0f; + alpha_threshold = 0.0f; + + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + break; + case OBJ_TYPE_REFLECT_TRANSPARENT: + alpha_test = 1; + min_alpha = 0.1f; + alpha_threshold = 0.5f; + + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + break; case OBJ_TYPE_REFRACT_TRANSLUCENT: gl_state_set_depth_mask(GL_FALSE); + min_alpha = 0.0f; alpha_threshold = 0.0f; break; - case OBJ_TYPE_REFLECT_TRANSPARENT: case OBJ_TYPE_REFRACT_TRANSPARENT: alpha_test = 1; min_alpha = 0.1f; @@ -2071,9 +2108,9 @@ namespace mdl { break; case OBJ_TYPE_SSS: func = draw_sub_mesh_sss; - break; - case OBJ_TYPE_USER: - func = draw_sub_mesh_translucent; + + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); break; case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1: case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2: @@ -2088,11 +2125,25 @@ namespace mdl { case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3: case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL: gl_state_set_depth_mask(GL_FALSE); + alpha_test = 1; min_alpha = 0.0f; alpha_threshold = 0.0f; break; - default: + case OBJ_TYPE_USER: + func = draw_sub_mesh_translucent; + break; + case OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS: + if (depth_mask) + func = draw_sub_mesh_translucent; + else + gl_state_set_depth_mask(GL_FALSE); + + alpha_test = 1; + min_alpha = 0.0f; + alpha_threshold = 0.0f; + + gl_state_set_cull_face_mode(GL_FRONT); break; } rctx->set_batch_alpha_threshold(alpha_threshold); @@ -2141,8 +2192,6 @@ namespace mdl { switch (type) { case OBJ_TYPE_TRANSLUCENT: case OBJ_TYPE_TRANSLUCENT_SORT_BY_RADIUS: - case OBJ_TYPE_REFLECT_TRANSLUCENT: - case OBJ_TYPE_REFRACT_TRANSLUCENT: if (!depth_mask) gl_state_set_depth_mask(GL_TRUE); break; @@ -2156,16 +2205,36 @@ namespace mdl { case OBJ_TYPE_REFLECT_CHARA_TRANSPARENT: gl_state_set_cull_face_mode(GL_BACK); break; - case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1: - case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2: - case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3: - gl_state_set_cull_face_mode(GL_BACK); + case OBJ_TYPE_REFLECT_OPAQUE: + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + break; + case OBJ_TYPE_REFLECT_TRANSLUCENT: + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + gl_state_set_depth_mask(GL_TRUE); + break; + case OBJ_TYPE_REFLECT_TRANSPARENT: + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + break; + case OBJ_TYPE_REFRACT_TRANSLUCENT: + if (reflect_draw) + gl_state_set_cull_face_mode(GL_FRONT); + gl_state_set_depth_mask(GL_TRUE); + break; + case OBJ_TYPE_SSS: + if (reflect_draw) + gl_state_set_cull_face_mode(GL_BACK); break; case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1: case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2: case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3: gl_state_set_depth_mask(GL_TRUE); break; + case OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS: + gl_state_set_cull_face_mode(GL_BACK); + break; } uniform_value_reset(); @@ -2211,7 +2280,7 @@ namespace mdl { } static int32_t obj_axis_aligned_bounding_box_check_visibility( - obj_axis_aligned_bounding_box* aabb, const mat4* mat) { + obj_axis_aligned_bounding_box* aabb, const mat4& mat) { vec3 points[8]; points[0] = aabb->center + (aabb->size ^ vec3( 0.0f, 0.0f, 0.0f)); points[1] = aabb->center + (aabb->size ^ vec3(-0.0f, -0.0f, -0.0f)); @@ -2223,7 +2292,7 @@ namespace mdl { points[7] = aabb->center + (aabb->size ^ vec3(-0.0f, -0.0f, 0.0f)); mat4 view_mat; - mat4_mul(&camera_data->view, mat, &view_mat); + mat4_mul(&camera_data.view, &mat, &view_mat); mat4_transpose(&view_mat, &view_mat); for (int32_t i = 0; i < 8; i++) @@ -2231,17 +2300,17 @@ namespace mdl { vec4 v2[6]; *(vec3*)&v2[0] = { 0.0f, 0.0f, -1.0f }; - v2[0].w = -camera_data->min_distance; - *(vec3*)&v2[1] = camera_data->field_1E4; + v2[0].w = -camera_data.min_distance; + *(vec3*)&v2[1] = camera_data.field_1E4; v2[1].w = 0.0f; - *(vec3*)&v2[2] = camera_data->field_1F0; + *(vec3*)&v2[2] = camera_data.field_1F0; v2[2].w = 0.0f; - *(vec3*)&v2[3] = camera_data->field_1FC; + *(vec3*)&v2[3] = camera_data.field_1FC; v2[3].w = 0.0f; - *(vec3*)&v2[4] = camera_data->field_208; + *(vec3*)&v2[4] = camera_data.field_208; v2[4].w = 0.0f; *(vec3*)&v2[5] = { 0.0f, 0.0f, 1.0f }; - v2[5].w = camera_data->max_distance; + v2[5].w = camera_data.max_distance; for (int32_t i = 0; i < 6; i++) for (int32_t j = 0; j < 8; j++) { @@ -2255,82 +2324,90 @@ namespace mdl { return 1; } - static int32_t obj_bounding_sphere_check_visibility(obj_bounding_sphere* sphere, - CullingCheck* culling, const mat4* mat) { + static int32_t obj_bounding_sphere_check_visibility(const obj_bounding_sphere& sphere, + CullingCheck* culling, const mat4& mat) { if (disp_manager->culling_func) - return disp_manager->culling_func(sphere); + return disp_manager->culling_func(&sphere); vec3 center; mat4 _mat; - mat4_transpose(mat, &_mat); - mat4_transform_point(&_mat, &sphere->center, ¢er); - float_t radius = mat4_get_max_scale(&_mat) * sphere->radius; + mat4_transpose(&mat, &_mat); + mat4_transform_point(&_mat, &sphere.center, ¢er); + float_t radius = mat4_get_max_scale(&_mat) * sphere.radius; - mat4_transpose(&camera_data->view, &_mat); + mat4_transpose(&camera_data.view, &_mat); mat4_transform_point(&_mat, ¢er, ¢er); double_t min_depth = (double_t)center.z - (double_t)radius; double_t max_depth = (double_t)center.z + (double_t)radius; - if (-camera_data->min_distance < min_depth || -camera_data->max_distance > max_depth) + if (-camera_data.min_distance < min_depth || -camera_data.max_distance > max_depth) return 0; - float_t v5 = vec3::dot(camera_data->field_1E4, center); + float_t v5 = vec3::dot(camera_data.field_1E4, center); if (v5 < -radius) return 0; - float_t v6 = vec3::dot(camera_data->field_1F0, center); + float_t v6 = vec3::dot(camera_data.field_1F0, center); if (v6 < -radius) return 0; - float_t v7 = vec3::dot(camera_data->field_1FC, center); + float_t v7 = vec3::dot(camera_data.field_1FC, center); if (v7 < -radius) return 0; - float_t v8 = vec3::dot(camera_data->field_208, center); + float_t v8 = vec3::dot(camera_data.field_208, center); if (v8 < -radius) return 0; - if (-camera_data->min_distance >= max_depth && -camera_data->max_distance <= min_depth + if (-camera_data.min_distance >= max_depth && -camera_data.max_distance <= min_depth && v5 >= radius && v6 >= radius && v7 >= radius && v8 >= radius) return 1; return 2; } - bool DispManager::entry_obj(::obj* obj, obj_mesh_vertex_buffer* obj_vertex_buf, - obj_mesh_index_buffer* obj_index_buf, const mat4* mat, - prj::vector* textures, vec4* blend_color, mat4* bone_mat, ::obj* object_morph, - obj_mesh_vertex_buffer* obj_morph_vertex_buf, int32_t instances_count, mat4* instances_mat, - void(*func)(const mdl::ObjSubMeshArgs*), const ObjSubMeshArgs* func_data, - bool enable_bone_mat, bool local) { - if (!obj_vertex_buf || !obj_index_buf) + bool DispManager::entry_obj(const ::obj* obj, const mat4& mat, obj_mesh_vertex_buffer* obj_vert_buf, + obj_mesh_index_buffer* obj_index_buf, const prj::vector* textures, const vec4* blend_color, + const mat4* bone_mat, const ::obj* obj_morph, obj_mesh_vertex_buffer* obj_morph_vert_buf, + int32_t instances_count, const mat4* instances_mat, + draw_func func, const ObjSubMeshArgs* func_data, bool enable_bone_mat, bool local) { + if (!obj_vert_buf || !obj_index_buf) return false; camera_struct::update_data(); + mat4 _mat; + if (mdl::obj_reflect_enable) { + mat4_transpose(&mat, &_mat); + mat4_mul(&_mat, &reflect_mat, &_mat); + mat4_transpose(&_mat, &_mat); + } + if (!local && object_culling && !instances_count && !bone_mat && (!obj || !obj_bounding_sphere_check_visibility( - &obj->bounding_sphere, &culling, mat))) { + obj->bounding_sphere, &culling, mat) && !(mdl::obj_reflect_enable + && obj_bounding_sphere_check_visibility( + obj->bounding_sphere, &culling, _mat)))) { culling.culled.objects++; return false; } culling.passed.objects++; for (int32_t i = 0; i < obj->num_mesh; i++) { - obj_mesh* mesh = &obj->mesh_array[i]; - obj_mesh* mesh_morph = 0; - if (obj_vertex_buf && obj_morph_vertex_buf) { - if (obj_vertex_buf[i].get_size() != obj_morph_vertex_buf[i].get_size()) + const obj_mesh* mesh = &obj->mesh_array[i]; + const obj_mesh* mesh_morph = 0; + if (obj_vert_buf && obj_morph_vert_buf) { + if (obj_vert_buf[i].get_size() != obj_morph_vert_buf[i].get_size()) continue; - if (object_morph && i < object_morph->num_mesh) - mesh_morph = &object_morph->mesh_array[i]; + if (obj_morph && i < obj_morph->num_mesh) + mesh_morph = &obj_morph->mesh_array[i]; } if (!local && object_culling && !instances_count && !bone_mat && !obj_bounding_sphere_check_visibility( - &mesh->bounding_sphere, &culling, mat) + mesh->bounding_sphere, &culling, mat) && (!mesh_morph || !obj_bounding_sphere_check_visibility( - &mesh_morph->bounding_sphere, &culling, mat))) { + mesh_morph->bounding_sphere, &culling, mat))) { culling.culled.meshes++; continue; } @@ -2340,15 +2417,16 @@ namespace mdl { int32_t translucent_priority_count = 0; for (int32_t j = 0; j < mesh->num_submesh; j++) { - obj_sub_mesh* sub_mesh = &mesh->submesh_array[j]; - obj_sub_mesh* sub_mesh_morph = 0; + const obj_sub_mesh* sub_mesh = &mesh->submesh_array[j]; + const obj_sub_mesh* sub_mesh_morph = 0; if (sub_mesh->attrib.m.cloth) continue; if (!local && object_culling && !instances_count && !bone_mat) { int32_t v32 = obj_bounding_sphere_check_visibility( - &sub_mesh->bounding_sphere, &culling, mat); - if (v32 != 2 || (!mesh->attrib.m.billboard && !mesh->attrib.m.billboard_y_axis)) { + sub_mesh->bounding_sphere, &culling, mat); + if (v32 != 2 || (!mesh->attrib.m.billboard && !mesh->attrib.m.billboard_y_axis + && !mesh->attrib.m.disable_aabb_culling)) { if (v32 == 2) { if (disp_manager->culling_func) v32 = 1; @@ -2359,18 +2437,18 @@ namespace mdl { if (!v32) { if (!mesh_morph || j >= mesh_morph->num_submesh) { - culling.culled.submesh_array++; + culling.culled.sub_meshes++; continue; } sub_mesh_morph = &mesh_morph->submesh_array[j]; if (!sub_mesh_morph) { - culling.culled.submesh_array++; + culling.culled.sub_meshes++; continue; } v32 = obj_bounding_sphere_check_visibility( - &sub_mesh_morph->bounding_sphere, &culling, mat); + sub_mesh_morph->bounding_sphere, &culling, mat); if (v32 == 2) { if (disp_manager->culling_func) v32 = 1; @@ -2380,16 +2458,16 @@ namespace mdl { } if (!v32) { - culling.culled.submesh_array++; + culling.culled.sub_meshes++; continue; } } } } - culling.passed.submesh_array++; + culling.passed.sub_meshes++; int32_t num_bone_index = sub_mesh->num_bone_index; - uint16_t* bone_index = sub_mesh->bone_index_array; + const uint16_t* bone_index = sub_mesh->bone_index_array; mat4* mats; if (num_bone_index && enable_bone_mat) { @@ -2413,9 +2491,9 @@ namespace mdl { GLuint morph_vertex_buffer = 0; size_t morph_vertex_buffer_offset = 0; - if (obj_morph_vertex_buf) { - morph_vertex_buffer = obj_morph_vertex_buf[i].get_buffer(); - morph_vertex_buffer_offset = obj_morph_vertex_buf[i].get_offset(); + if (obj_morph_vert_buf) { + morph_vertex_buffer = obj_morph_vert_buf[i].get_buffer(); + morph_vertex_buffer_offset = obj_morph_vert_buf[i].get_offset(); } GLuint index_buffer = 0; @@ -2424,15 +2502,15 @@ namespace mdl { GLuint vertex_buffer = 0; size_t vertex_buffer_offset = 0; - if (obj_vertex_buf) { - vertex_buffer = obj_vertex_buf[i].get_buffer(); - vertex_buffer_offset = obj_vertex_buf[i].get_offset(); + if (obj_vert_buf) { + vertex_buffer = obj_vert_buf[i].get_buffer(); + vertex_buffer_offset = obj_vert_buf[i].get_offset(); } - if (!vertex_buffer || !index_buffer || obj_morph_vertex_buf && !morph_vertex_buffer) + if (!vertex_buffer || !index_buffer || obj_morph_vert_buf && !morph_vertex_buffer) continue; - uint64_t material_hash = hash_utf8_fnv1a64m(material->material.name); + const uint64_t material_hash = hash_utf8_fnv1a64m(material->material.name); material_list_struct* mat_list = 0; for (int32_t k = 0; k < material_list_count; k++) @@ -2515,13 +2593,13 @@ namespace mdl { continue; } - obj_material_attrib_member attrib = material->material.attrib.m; + const obj_material_attrib_member attrib = material->material.attrib.m; if (obj_flags & (mdl::OBJ_ALPHA_ORDER_1 | mdl::OBJ_ALPHA_ORDER_2 | mdl::OBJ_ALPHA_ORDER_3) && data->args.sub_mesh.blend_color.w < 1.0f) { if (!(obj_flags & mdl::OBJ_NO_TRANSLUCENCY)) { if (attrib.flag_28 || (attrib.punch_through || !(attrib.alpha_texture | attrib.alpha_material)) - && !sub_mesh->attrib.m.transparent) { + && !sub_mesh->attrib.m.translucent) { if (!attrib.punch_through) { if (obj_flags & mdl::OBJ_ALPHA_ORDER_1) entry_list(OBJ_TYPE_OPAQUE_ALPHA_ORDER_1, data); @@ -2557,7 +2635,7 @@ namespace mdl { else { if (attrib.flag_28 || data->args.sub_mesh.blend_color.w >= 1.0f && (attrib.punch_through || !(attrib.alpha_texture | attrib.alpha_material)) - && !sub_mesh->attrib.m.transparent) { + && !sub_mesh->attrib.m.translucent) { if (obj_flags & mdl::OBJ_SHADOW) entry_list((ObjType)(OBJ_TYPE_SHADOW_CHARA + shadow_type), data); @@ -2623,7 +2701,7 @@ namespace mdl { if (obj_flags & mdl::OBJ_CHARA_REFLECT) entry_list(OBJ_TYPE_REFLECT_CHARA_OPAQUE, data); if (obj_flags & mdl::OBJ_REFLECT) { - if (render_manager->reflect_type != STAGE_DATA_REFLECT_REFLECT_MAP) + if (render_manager.reflect_type != STAGE_DATA_REFLECT_REFLECT_MAP) entry_list(OBJ_TYPE_REFLECT_OPAQUE, data); else entry_list(OBJ_TYPE_REFLECT_TRANSLUCENT, data); @@ -2653,7 +2731,7 @@ namespace mdl { if (!data) continue; - data->init_translucent(mat, &translucent_args); + data->init_translucent(mat, translucent_args); if (obj_flags & mdl::OBJ_ALPHA_ORDER_1) entry_list(OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1, data); else if (obj_flags & mdl::OBJ_ALPHA_ORDER_2) @@ -2665,27 +2743,289 @@ namespace mdl { entry_list(local ? OBJ_TYPE_TRANSLUCENT_LOCAL : OBJ_TYPE_TRANSLUCENT, data); } + + if (local || (obj_flags & mdl::OBJ_SHADOW_OBJECT) + || (obj_flags & (mdl::OBJ_ALPHA_ORDER_1 | mdl::OBJ_ALPHA_ORDER_2 | mdl::OBJ_ALPHA_ORDER_3) + && blend_color && blend_color->w < 1.0f)) + return true; + + mat4_transpose(&mat, &_mat); + mat4_mul(&_mat, &reflect_mat, &_mat); + mat4_transpose(&_mat, &_mat); + + bool camera_front = vec3::normalize(camera_data.view_point - camera_data.interest).z >= 0.0f; + + for (int32_t i = 0; i < obj->num_mesh; i++) { + const obj_mesh* mesh = &obj->mesh_array[i]; + const obj_mesh* mesh_morph = 0; + if (!mdl::obj_reflect_enable && !mesh->attrib.m.reflect || mesh->attrib.m.no_reflect + || camera_front && mesh->attrib.m.reflect_cam_back) + continue; + + if (obj_vert_buf && obj_morph_vert_buf) { + if (obj_vert_buf[i].get_size() != obj_morph_vert_buf[i].get_size()) + continue; + + if (obj_morph && i < obj_morph->num_mesh) + mesh_morph = &obj_morph->mesh_array[i]; + } + + if (object_culling && !instances_count && !bone_mat + && !obj_bounding_sphere_check_visibility( + mesh->bounding_sphere, &culling, _mat) + && (!mesh_morph || !obj_bounding_sphere_check_visibility( + mesh_morph->bounding_sphere, &culling, _mat))) { + culling.culled.meshes++; + continue; + } + culling.passed.meshes++; + + ObjSubMeshArgs* translucent_priority[40]; + int32_t translucent_priority_count = 0; + + for (int32_t j = 0; j < mesh->num_submesh; j++) { + const obj_sub_mesh* sub_mesh = &mesh->submesh_array[j]; + const obj_sub_mesh* sub_mesh_morph = 0; + if (sub_mesh->attrib.m.cloth || sub_mesh->attrib.m.no_reflect + || !mdl::obj_reflect_enable && !sub_mesh->attrib.m.reflect + || camera_front && sub_mesh->attrib.m.reflect_cam_back) + continue; + + if (object_culling && !instances_count && !bone_mat) { + int32_t v32 = obj_bounding_sphere_check_visibility( + sub_mesh->bounding_sphere, &culling, _mat); + if (v32 != 2 || (!mesh->attrib.m.billboard && !mesh->attrib.m.billboard_y_axis + && !mesh->attrib.m.disable_aabb_culling)) { + if (v32 == 2) { + if (culling_func) + v32 = 1; + else { + obj_axis_aligned_bounding_box aabb = *sub_mesh->axis_aligned_bounding_box; + mat4_transform_point(&reflect_mat, &aabb.center, &aabb.center); + mat4_transform_vector(&reflect_mat, &aabb.size, &aabb.size); + v32 = obj_axis_aligned_bounding_box_check_visibility(&aabb, mat); + } + } + + if (!v32) { + if (!mesh_morph || j >= mesh_morph->num_submesh) { + culling.culled.sub_meshes++; + continue; + } + + sub_mesh_morph = &mesh_morph->submesh_array[j]; + if (!sub_mesh_morph) { + culling.culled.sub_meshes++; + continue; + } + + int32_t v32 = obj_bounding_sphere_check_visibility( + sub_mesh_morph->bounding_sphere, &culling, _mat); + if (v32 == 2) { + if (culling_func) + v32 = 1; + else { + obj_axis_aligned_bounding_box aabb = *sub_mesh_morph->axis_aligned_bounding_box; + mat4_transform_point(&reflect_mat, &aabb.center, &aabb.center); + mat4_transform_vector(&reflect_mat, &aabb.size, &aabb.size); + v32 = obj_axis_aligned_bounding_box_check_visibility(&aabb, mat); + } + } + + if (!v32) { + culling.culled.sub_meshes++; + continue; + } + } + } + } + culling.passed.sub_meshes++; + + int32_t num_bone_index = sub_mesh->num_bone_index; + const uint16_t* bone_index = sub_mesh->bone_index_array; + + mat4* mats; + if (num_bone_index && enable_bone_mat) { + mats = alloc_mat4_array(num_bone_index); + if (bone_mat) + for (int32_t k = 0; k < num_bone_index; k++, bone_index++) + mat4_transpose(&bone_mat[*bone_index], &mats[k]); + else + for (int32_t k = 0; k < num_bone_index; k++) + mats[k] = mat4_identity; + } + else { + mats = 0; + num_bone_index = 0; + } + + obj_material_data* material = &obj->material_array[sub_mesh->material_index]; + ObjData* data = alloc_obj_data(OBJ_KIND_NORMAL); + if (!data) + continue; + + GLuint morph_vertex_buffer = 0; + size_t morph_vertex_buffer_offset = 0; + if (obj_morph_vert_buf) { + morph_vertex_buffer = obj_morph_vert_buf[i].get_buffer(); + morph_vertex_buffer_offset = obj_morph_vert_buf[i].get_offset(); + } + + GLuint index_buffer = 0; + if (obj_index_buf) + index_buffer = obj_index_buf[i].buffer; + + GLuint vertex_buffer = 0; + size_t vertex_buffer_offset = 0; + if (obj_vert_buf) { + vertex_buffer = obj_vert_buf[i].get_buffer(); + vertex_buffer_offset = obj_vert_buf[i].get_offset(); + } + + if (!vertex_buffer || !index_buffer || obj_morph_vert_buf && !morph_vertex_buffer) + continue; + + const uint64_t material_hash = hash_utf8_fnv1a64m(material->material.name); + + const material_list_struct* mat_list = 0; + for (int32_t k = 0; k < material_list_count; k++) + if (material_list_array[k].hash == material_hash) { + mat_list = &material_list_array[k]; + break; + } + + vec4 _blend_color = 1.0f; + vec4 _emission = { 0.0f, 0.0f, 0.0f, 1.0f }; + + if (mat_list) { + bool has_blend_color = false; + if (mat_list->has_blend_color.x) { + _blend_color.x = mat_list->blend_color.x; + has_blend_color = true; + } + + if (mat_list->has_blend_color.y) { + _blend_color.y = mat_list->blend_color.y; + has_blend_color = true; + } + + if (mat_list->has_blend_color.z) { + _blend_color.z = mat_list->blend_color.z; + has_blend_color = true; + } + + if (mat_list->has_blend_color.w) { + _blend_color.w = mat_list->blend_color.w; + has_blend_color = true; + } + + if (blend_color) { + if (!has_blend_color) + _blend_color = *blend_color; + else + _blend_color *= *blend_color; + } + + bool has_emission = false; + if (mat_list->has_emission.x) { + _emission.x = mat_list->emission.x; + has_emission = true; + } + + if (mat_list->has_emission.y) { + _emission.y = mat_list->emission.y; + has_emission = true; + } + + if (mat_list->has_emission.z) { + _emission.z = mat_list->emission.z; + has_emission = true; + } + + if (mat_list->has_emission.w) { + _emission.w = mat_list->emission.w; + has_emission = true; + } + + if (!has_emission) + _emission = material->material.color.emission; + } + else { + if (blend_color) + _blend_color = *blend_color; + _emission = material->material.color.emission; + } + + data->init_sub_mesh(mat, obj->bounding_sphere.radius, sub_mesh, mesh, material, textures, + num_bone_index, mats, vertex_buffer, vertex_buffer_offset, index_buffer, _blend_color, _emission, + morph_vertex_buffer, morph_vertex_buffer_offset, instances_count, instances_mat, func, func_data); + + const obj_material_attrib_member attrib = material->material.attrib.m; + if (!attrib.flag_28 && (data->args.sub_mesh.blend_color.w < 1.0f + || !attrib.punch_through && attrib.alpha_texture | attrib.alpha_material + || sub_mesh->attrib.m.translucent)) { + if (!attrib.translucent_priority) + if (mesh->attrib.m.translucent_sort_by_radius + || obj_flags & mdl::OBJ_TRANSLUCENT_SORT_BY_RADIUS) { + DispManager::entry_list(OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS, data); + } + else + DispManager::entry_list(OBJ_TYPE_REFLECT_TRANSLUCENT, data); + else if (translucent_priority_count < 40) + translucent_priority[translucent_priority_count++] = &data->args.sub_mesh; + } + else { + if (!(obj_flags & mdl::OBJ_NO_TRANSLUCENCY)) + if (attrib.punch_through) + DispManager::entry_list(OBJ_TYPE_REFLECT_TRANSPARENT, data); + else + DispManager::entry_list(OBJ_TYPE_REFLECT_OPAQUE, data); + continue; + } + } + + if (translucent_priority_count <= 0) + continue; + + ObjTranslucentArgs translucent_args; + translucent_args.count = 0; + for (int32_t j = 62; j; j--) + for (int32_t k = 0; k < translucent_priority_count; k++) { + ObjSubMeshArgs* sub_mesh = translucent_priority[k]; + if (sub_mesh->material->material.attrib.m.translucent_priority != j) + continue; + + translucent_args.sub_mesh[translucent_args.count] = sub_mesh; + translucent_args.count++; + } + + ObjData* data = DispManager::alloc_obj_data(mdl::OBJ_KIND_TRANSLUCENT); + if (data) { + data->init_translucent(mat, translucent_args); + DispManager::entry_list(OBJ_TYPE_TRANSLUCENT, data); + } + } + return true; } - void DispManager::entry_obj_by_obj(const mat4* mat, - ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, + void DispManager::entry_obj_by_obj(const mat4& mat, + const ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha) { if (!obj) return; - vec4 blend_color = { 1.0f, 1.0f, 1.0f, alpha }; - vec4* blend_color_ptr = alpha < 1.0f ? &blend_color : 0; + const vec4 blend_color(1.0f, 1.0f, 1.0f, alpha); - entry_obj(obj, obj_vert_buf, obj_index_buf, mat, textures, blend_color_ptr, - bone_mat, 0, 0, 0, 0, 0, 0, !!bone_mat); + entry_obj(obj, mat, obj_vert_buf, obj_index_buf, textures, + alpha < 1.0f ? &blend_color : 0, bone_mat, 0, 0, 0, 0, 0, 0, !!bone_mat); } #if SHARED_OBJECT_BUFFER #pragma warning(push) #pragma warning(disable: 6386) - void DispManager::entry_obj_by_obj(const mat4* mat, - ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer_aft* obj_vert_buf, + void DispManager::entry_obj_by_obj(const mat4& mat, + const ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer_aft* obj_vert_buf, obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha) { if (!obj) return; @@ -2709,23 +3049,23 @@ namespace mdl { blend_color.w = alpha; vec4* blend_color_ptr = alpha < 1.0f ? &blend_color : 0; - disp_manager->entry_obj(obj, _obj_vert_buf, obj_index_buf, mat, + entry_obj(obj, mat, _obj_vert_buf, obj_index_buf, textures, blend_color_ptr, bone_mat, 0, 0, 0, 0, 0, 0, 0, !!bone_mat); _operator_delete(_obj_vert_buf); } #pragma warning(pop) #endif - bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info) { + bool DispManager::entry_obj_by_object_info(const mat4& mat, object_info obj_info) { return entry_obj_by_object_info(mat, obj_info, 0, 0, 0, 0, 0, 0, false); } - bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, mat4* bone_mat) { + bool DispManager::entry_obj_by_object_info(const mat4& mat, object_info obj_info, mat4* bone_mat) { vec4 blend_color = 1.0f; return entry_obj_by_object_info(mat, obj_info, &blend_color, bone_mat, 0, 0, 0, 0, true); } - bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, + bool DispManager::entry_obj_by_object_info(const mat4& mat, object_info obj_info, vec4* blend_color, mat4* bone_mat, int32_t instances_count, mat4* instances_mat, void(*func)(const ObjSubMeshArgs*), const ObjSubMeshArgs* func_data, bool enable_bone_mat, bool local) { @@ -2737,42 +3077,42 @@ namespace mdl { return false; prj::vector* textures = objset_info_storage_get_set_gentex(obj_info.set_id); - obj_mesh_vertex_buffer* obj_vertex_buffer = objset_info_storage_get_obj_mesh_vertex_buffer(obj_info, 0); + obj_mesh_vertex_buffer* obj_vert_buffer = objset_info_storage_get_obj_mesh_vertex_buffer(obj_info, 0); obj_mesh_index_buffer* obj_index_buffer = objset_info_storage_get_obj_mesh_index_buffer(obj_info, 0); ::obj* obj_morph = 0; - obj_mesh_vertex_buffer* obj_morph_vertex_buffer = 0; + obj_mesh_vertex_buffer* obj_morph_vert_buffer = 0; if (morph.object.set_id != (uint16_t)-1) { obj_morph = objset_info_storage_get_obj(morph.object); - obj_morph_vertex_buffer = objset_info_storage_get_obj_mesh_vertex_buffer(morph.object, 0); + obj_morph_vert_buffer = objset_info_storage_get_obj_mesh_vertex_buffer(morph.object, 0); } - return entry_obj(obj, obj_vertex_buffer, obj_index_buffer, - mat, textures, blend_color, bone_mat, obj_morph, obj_morph_vertex_buffer, + return entry_obj(obj, mat, obj_vert_buffer, obj_index_buffer, + textures, blend_color, bone_mat, obj_morph, obj_morph_vert_buffer, instances_count, instances_mat, func, func_data, enable_bone_mat, local); } - bool DispManager::entry_obj_by_object_info(const mat4* mat, + bool DispManager::entry_obj_by_object_info(const mat4& mat, object_info obj_info, float_t alpha, mat4* bone_mat) { vec4 blend_color = 1.0f; blend_color.w = alpha; return entry_obj_by_object_info(mat, obj_info, &blend_color, bone_mat, 0, 0, 0, 0, true); } - bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, + bool DispManager::entry_obj_by_object_info(const mat4& mat, object_info obj_info, float_t r, float_t g, float_t b, float_t a, mat4* bone_mat, bool local) { vec4 blend_color = { r, g, b, a }; return entry_obj_by_object_info(mat, obj_info, &blend_color, bone_mat, 0, 0, 0, 0, true, local); } - bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, + bool DispManager::entry_obj_by_object_info(const mat4& mat, object_info obj_info, vec4* blend_color, mat4* bone_mat, bool local) { return entry_obj_by_object_info(mat, obj_info, blend_color, 0, 0, 0, 0, 0, false, local); } void DispManager::entry_obj_by_object_info_object_skin(object_info obj_info, prj::vector* texture_pattern, texture_data_struct* texture_data, float_t alpha, - mat4* matrices, mat4* ex_data_matrices, const mat4* mat, const mat4* global_mat) { + mat4* matrices, mat4* ex_data_matrices, const mat4* mat, const mat4& global_mat) { obj_skin* skin = objset_info_storage_get_obj_skin(obj_info); if (!skin) return; @@ -2833,13 +3173,13 @@ namespace mdl { } } - void DispManager::entry_obj_etc(const mat4* mat, EtcObj* etc, bool local) { + void DispManager::entry_obj_etc(const mat4& mat, const EtcObj& etc, bool local) { ObjData* data = alloc_obj_data(mdl::OBJ_KIND_ETC); if (!data) return; data->init_etc(mat, etc); - if (etc->color.a == 0xFF) { + if (etc.color.a == 0xFF) { if (!local && (obj_flags & OBJ_SHADOW)) entry_list((mdl::ObjType)(OBJ_TYPE_SHADOW_CHARA + shadow_type), data); entry_list(local ? OBJ_TYPE_OPAQUE_LOCAL : OBJ_TYPE_OPAQUE, data); @@ -2848,7 +3188,7 @@ namespace mdl { entry_list(local ? OBJ_TYPE_TRANSLUCENT_LOCAL : OBJ_TYPE_TRANSLUCENT, data); } - void DispManager::entry_obj_user(const mat4* mat, UserArgsFunc func, void* data, ObjType type) { + void DispManager::entry_obj_user(const mat4& mat, UserArgsFunc func, void* data, ObjType type) { ObjData* _data = alloc_obj_data(OBJ_KIND_USER); if (_data) { _data->init_user(mat, func, data); @@ -2992,7 +3332,11 @@ namespace mdl { } mdl::ObjList& DispManager::get_obj_list(mdl::ObjType type) { - if (type >= OBJ_TYPE_MAX) + if (type >= OBJ_TYPE_LOCAL_MAX) +#pragma warning(suppress: 33010) +#pragma warning(suppress: 33011) + return rctx->obj_reflect[type - OBJ_TYPE_LOCAL_MAX]; + else if (type >= OBJ_TYPE_MAX) #pragma warning(suppress: 33010) #pragma warning(suppress: 33011) return rctx->obj_local[type - OBJ_TYPE_MAX]; @@ -3121,6 +3465,9 @@ namespace mdl { for (prj::list& i : rctx->obj_local) i.clear(); + for (prj::list& i : rctx->obj_reflect) + i.clear(); + buffer_reset(); } @@ -3128,7 +3475,7 @@ namespace mdl { chara_color = value; } - void DispManager::set_culling_finc(bool(FASTCALL* func)(obj_bounding_sphere*)) { + void DispManager::set_culling_func(bool(FASTCALL* func)(const obj_bounding_sphere*)) { culling_func = func; } @@ -3224,7 +3571,7 @@ namespace mdl { vec3 pos = 0.0f; sub_1405E8A20(shadow_ptr_get(), 0, &pos); disp_manager->set_shadow_type(SHADOW_CHARA); - disp_manager->entry_obj_by_object_info(&mat, object_info((uint16_t)id, (uint16_t)set_id)); + disp_manager->entry_obj_by_object_info(mat, object_info((uint16_t)id, (uint16_t)set_id)); disp_manager->set_obj_flags((mdl::ObjFlags)0); } @@ -3237,7 +3584,7 @@ namespace mdl { mat4 mat; mat4_translate(&aabb->center, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } HOOK(void, FASTCALL, MeshDw__DrawObjBoundingSphere, 0x000000014031AFE0, obj_bounding_sphere* bounding_sphere, color4u8_bgra color) { @@ -3252,7 +3599,7 @@ namespace mdl { mat4 mat; mat4_translate(&bounding_sphere->center, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } HOOK(void, FASTCALL, DispManager__entry_obj, 0x00000001404379E0, @@ -3264,20 +3611,20 @@ namespace mdl { abort(); } - HOOK(void, FASTCALL, DispManager__entry_obj_etc, 0x00000001404395C0, mdl::EtcObj* etc) { - disp_manager->entry_obj_etc(&mat4_identity, etc); + HOOK(void, FASTCALL, DispManager__entry_obj_etc, 0x00000001404395C0, const mdl::EtcObj& etc) { + disp_manager->entry_obj_etc(mat4_identity, etc); } #if SHARED_OBJECT_BUFFER HOOK(void, FASTCALL, DispManager__entry_obj_by_obj, 0x0000000140439A20, obj* obj_data, prj::vector* textures, obj_mesh_vertex_buffer_aft* obj_vert_buf, obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha) { - disp_manager->entry_obj_by_obj(rob_chara_item_equip_mat, obj_data, textures, obj_vert_buf, obj_index_buf, bone_mat, alpha); + disp_manager->entry_obj_by_obj(*rob_chara_item_equip_mat, obj_data, textures, obj_vert_buf, obj_index_buf, bone_mat, alpha); } #else HOOK(void, FASTCALL, DispManager__entry_obj_by_obj, 0x0000000140439A20, obj* obj_data, prj::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha) { - disp_manager->entry_obj_by_obj(rob_chara_item_equip_mat, obj_data, textures, obj_vert_buf, obj_index_buf, bone_mat, alpha); + disp_manager->entry_obj_by_obj(*rob_chara_item_equip_mat, obj_data, textures, obj_vert_buf, obj_index_buf, bone_mat, alpha); } #endif @@ -3294,7 +3641,7 @@ namespace mdl { mat4 mat; mat4_translate(pos, &mat); mat4_transpose(&mat, &mat); - disp_manager->entry_obj_etc(&mat, &etc); + disp_manager->entry_obj_etc(mat, etc); } void disp_manager_patch() { diff --git a/src/DivaGL/mdl/disp_manager.hpp b/src/DivaGL/mdl/disp_manager.hpp index d8c3e78..9f7acfb 100644 --- a/src/DivaGL/mdl/disp_manager.hpp +++ b/src/DivaGL/mdl/disp_manager.hpp @@ -115,6 +115,8 @@ namespace mdl { OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2_LOCAL, OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL, OBJ_TYPE_LOCAL_MAX, + OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS = OBJ_TYPE_LOCAL_MAX, + OBJ_TYPE_REFLECT_MAX, }; } @@ -168,6 +170,10 @@ struct texture_data_struct { static_assert(sizeof(texture_data_struct) == 0x34, "\"texture_pattern_struct\" struct should have a size of 0x34"); namespace mdl { + struct ObjSubMeshArgs; + + typedef void(FASTCALL* draw_func)(const ObjSubMeshArgs* args); + struct ObjSubMeshArgs { const obj_sub_mesh* sub_mesh; const obj_mesh* mesh; @@ -199,7 +205,7 @@ namespace mdl { int32_t field_7C8; int32_t instances_count; const mat4* instances_mat; - void(FASTCALL* func)(const ObjSubMeshArgs*); + draw_func func; const ObjSubMeshArgs* func_data; ObjSubMeshArgs(); @@ -349,16 +355,15 @@ namespace mdl { ObjData(); ~ObjData(); - void init_etc(const mat4* mat, mdl::EtcObj* etc); - void init_sub_mesh(const mat4* mat, float_t radius, - const obj_sub_mesh* sub_mesh, const obj_mesh* mesh, const obj_material_data* material, - const prj::vector* textures, int32_t mat_count, const mat4* mats, - GLuint vertex_buffer, size_t vertex_buffer_offset, GLuint index_buffer, - const vec4& blend_color, const vec4& emission, GLuint morph_vertex_buffer, - size_t morph_vertex_buffer_offset, int32_t instances_count, mat4* instances_mat, - void(FASTCALL* func)(const ObjSubMeshArgs*), const ObjSubMeshArgs* func_data); - void init_translucent(const mat4* mat, ObjTranslucentArgs* translucent); - void init_user(const mat4* mat, UserArgsFunc func, void* data); + void init_etc(const mat4& mat, const mdl::EtcObj& etc); + void init_sub_mesh(const mat4& mat, float_t radius, const obj_sub_mesh* sub_mesh, + const obj_mesh* mesh, const obj_material_data* material, const prj::vector* textures, + int32_t mat_count, const mat4* mats, GLuint vertex_buffer, size_t vertex_buffer_offset, + GLuint index_buffer, const vec4& blend_color, const vec4& emission, GLuint morph_vertex_buffer, + size_t morph_vertex_buffer_offset, + int32_t instances_count, const mat4* instances_mat, draw_func func, const ObjSubMeshArgs* func_data); + void init_translucent(const mat4& mat, const ObjTranslucentArgs& translucent); + void init_user(const mat4& mat, UserArgsFunc func, void* data); }; typedef prj::list ObjList; @@ -367,7 +372,7 @@ namespace mdl { struct Info { int32_t objects; int32_t meshes; - int32_t submesh_array; + int32_t sub_meshes; int pad; }; @@ -439,7 +444,7 @@ namespace mdl { float_t wet_param; int32_t texture_transform_count; texture_transform_struct texture_transform_array[TEXTURE_TRANSFORM_COUNT]; - bool(FASTCALL* culling_func)(obj_bounding_sphere*); + bool(FASTCALL* culling_func)(const obj_bounding_sphere*); void add_vertex_array(ObjSubMeshArgs* args); void add_vertex_array(EtcObj* etc, mat4& mat); @@ -454,35 +459,34 @@ namespace mdl { void draw(mdl::ObjType type, int32_t depth_mask = 0, bool reflect_texture_mask = true, int32_t alpha = -1); /*void draw_show_vector(mdl::ObjType type, int32_t show_vector);*/ void entry_list(ObjType type, ObjData* data); - bool entry_obj(::obj* object, obj_mesh_vertex_buffer* obj_vertex_buf, - obj_mesh_index_buffer* obj_index_buf, const mat4* mat, - prj::vector* textures, vec4* blend_color, mat4* bone_mat, ::obj* object_morph, - obj_mesh_vertex_buffer* obj_morph_vertex_buf, int32_t instances_count, mat4* instances_mat, - void(*func)(const mdl::ObjSubMeshArgs*), const ObjSubMeshArgs* func_data, - bool enable_bone_mat, bool local = false); - void entry_obj_by_obj(const mat4* mat, - ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, + bool entry_obj(const ::obj* obj, const mat4& mat, obj_mesh_vertex_buffer* obj_vert_buf, + obj_mesh_index_buffer* obj_index_buf, const prj::vector* textures, const vec4* blend_color, + const mat4* bone_mat, const ::obj* obj_morph, obj_mesh_vertex_buffer* obj_morph_vert_buf, + int32_t instances_count, const mat4* instances_mat, + draw_func func, const ObjSubMeshArgs* func_data, bool enable_bone_mat, bool local = false); + void entry_obj_by_obj(const mat4& mat, + const ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha); #if SHARED_OBJECT_BUFFER - void entry_obj_by_obj(const mat4* mat, - ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer_aft* obj_vert_buf, + void entry_obj_by_obj(const mat4& mat, + const ::obj* obj, prj::vector* textures, obj_mesh_vertex_buffer_aft* obj_vert_buf, obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha); #endif - bool entry_obj_by_object_info(const mat4* mat, object_info obj_info); - bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, mat4* bone_mat); - bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, + bool entry_obj_by_object_info(const mat4& mat, object_info obj_info); + bool entry_obj_by_object_info(const mat4& mat, object_info obj_info, mat4* bone_mat); + bool entry_obj_by_object_info(const mat4& mat, object_info obj_info, vec4* blend_color, mat4* bone_mat, int32_t instances_count, mat4* instances_mat, void(*func)(const ObjSubMeshArgs*), const ObjSubMeshArgs* func_data, bool enable_bone_mat, bool local = false); - bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, float_t alpha, mat4* bone_mat = 0); - bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, + bool entry_obj_by_object_info(const mat4& mat, object_info obj_info, float_t alpha, mat4* bone_mat = 0); + bool entry_obj_by_object_info(const mat4& mat, object_info obj_info, float_t r, float_t g, float_t b, float_t a, mat4* bone_mat = 0, bool local = false); - bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, + bool entry_obj_by_object_info(const mat4& mat, object_info obj_info, vec4* blend_color, mat4* bone_mat = 0, bool local = false); void entry_obj_by_object_info_object_skin(object_info obj_info, prj::vector* texture_pattern, texture_data_struct* texture_data, float_t alpha, - mat4* matrices, mat4* ex_data_matrices, const mat4* mat, const mat4* global_mat); - void entry_obj_etc(const mat4* mat, EtcObj* etc, bool local = false); - void entry_obj_user(const mat4* mat, UserArgsFunc func, void* data, ObjType type); + mat4* matrices, mat4* ex_data_matrices, const mat4* mat, const mat4& global_mat); + void entry_obj_etc(const mat4& mat, const EtcObj& etc, bool local = false); + void entry_obj_user(const mat4& mat, UserArgsFunc func, void* data, ObjType type); GLuint get_vertex_array(const ObjSubMeshArgs* args); GLuint get_vertex_array(const mdl::EtcObj* etc); bool get_chara_color(); @@ -502,7 +506,7 @@ namespace mdl { void obj_sort(mat4* view, ObjType type, int32_t compare_func, bool a3 = false); void refresh(); void set_chara_color(bool value = false); - void set_culling_finc(bool(FASTCALL* func)(obj_bounding_sphere*) = 0); + void set_culling_func(bool(FASTCALL* func)(const obj_bounding_sphere*) = 0); void set_obj_flags(ObjFlags flags = (ObjFlags)0); void set_material_list(int32_t count = 0, const material_list_struct* value = 0); void set_morph(object_info object = {}, float_t weight = 0.0f); @@ -518,6 +522,8 @@ namespace mdl { static_assert(sizeof(mdl::DispManager) == 0x9D0, "\"mdl::DispManager\" struct should have a size of 0x9D0"); + extern bool obj_reflect_enable; + extern void disp_manager_patch(); } diff --git a/src/DivaGL/mdl/draw_object.cpp b/src/DivaGL/mdl/draw_object.cpp index 5159ae2..03c8656 100644 --- a/src/DivaGL/mdl/draw_object.cpp +++ b/src/DivaGL/mdl/draw_object.cpp @@ -295,7 +295,7 @@ namespace mdl { obj_material_attrib_member attrib = args->material->material.attrib.m; uniform->arr[U_ALPHA_TEST] = (!attrib.flag_28 && (args->blend_color.w < 1.0f || (attrib.alpha_texture || attrib.alpha_material) && !attrib.punch_through - || args->sub_mesh->attrib.m.transparent) + || args->sub_mesh->attrib.m.translucent) || attrib.punch_through) ? 1 : 0; uniform->arr[U_NPR_NORMAL] = 1; @@ -303,12 +303,12 @@ namespace mdl { const obj_material_data* material = args->material; switch (material->material.shader.index) { case SHADER_FT_CLOTH: - if (!render_manager->npr_param && material->material.color.ambient.w < 1.0f + if (!render_manager.npr_param && material->material.color.ambient.w < 1.0f && material->material.shader_info.m.aniso_direction == OBJ_MATERIAL_ANISO_DIRECTION_NORMAL) chara = true; break; case SHADER_FT_TIGHTS: - if (!render_manager->npr_param) + if (!render_manager.npr_param) chara = true; break; case SHADER_FT_GLASEYE: @@ -382,7 +382,7 @@ namespace mdl { obj_material_attrib_member attrib = args->material->material.attrib.m; if (!attrib.flag_28 && (args->blend_color.w < 1.0f || (attrib.alpha_texture || attrib.alpha_material) && !attrib.punch_through - || args->sub_mesh->attrib.m.transparent) + || args->sub_mesh->attrib.m.translucent) || attrib.punch_through) { uniform->arr[U_ALPHA_TEST] = 1; rctx->set_batch_alpha_threshold(0.5f); diff --git a/src/DivaGL/object.cpp b/src/DivaGL/object.cpp index 0be64e7..87f68bf 100644 --- a/src/DivaGL/object.cpp +++ b/src/DivaGL/object.cpp @@ -4,7 +4,9 @@ */ #include "object.hpp" +#include "../KKdLib/hash.hpp" #include "mdl/disp_manager.hpp" +#include "pv_game/firstread.hpp" #include "file_handler.hpp" #include "gl_state.hpp" #include "wrap.hpp" @@ -88,6 +90,8 @@ bool (FASTCALL* objset_info_storage_load_obj_set_check_not_read)(int32_t set) static BufObjMgr& bufobj_mgr = *(BufObjMgr*)0x00000001411A34D0; +static int32_t load_set_id; + static GLuint create_index_buffer(size_t size, const void* data); static GLuint create_vertex_buffer(size_t size, const void* data, bool dynamic = false); static void free_index_buffer(GLuint buffer); @@ -939,7 +943,7 @@ inline int32_t obj_material_texture_type_get_texture_index( } void obj_skin_set_matrix_buffer(obj_skin* s, mat4* matrices, - mat4* ex_data_matrices, mat4* matrix_buffer, const mat4* mat, const mat4* global_mat) { + mat4* ex_data_matrices, mat4* matrix_buffer, const mat4* mat, const mat4& global_mat) { if (!s->num_bone) return; @@ -953,7 +957,7 @@ void obj_skin_set_matrix_buffer(obj_skin* s, mat4* matrices, else mat4_mul(&matrices[*bone_id], mat, &temp); - mat4_mul(global_mat, &temp, &temp); + mat4_mul(&global_mat, &temp, &temp); mat4_mul(&temp, bone_matrix, matrix_buffer); } else @@ -964,7 +968,7 @@ void obj_skin_set_matrix_buffer(obj_skin* s, mat4* matrices, else temp = matrices[*bone_id]; - mat4_mul(global_mat, &temp, &temp); + mat4_mul(&global_mat, &temp, &temp); mat4_mul(&temp, bone_matrix, matrix_buffer); } } @@ -1041,6 +1045,172 @@ HOOK(obj_mesh_vertex_buffer*, FASTCALL, objset_info_storage_get_obj_mesh_vertex_ return 0; } +HOOK(obj_set*, FASTCALL, obj_set_parse_file, 0x000000014045CBC0, + prj::shared_ptr& alloc, __int64 set_file, int32_t& result) { + obj_set* set = originalobj_set_parse_file(alloc, set_file, result); + + extern bool reflect_full; + extern const firstread* firstread_ptr; + if (reflect_full && firstread_ptr && firstread_ptr->objset_array && load_set_id != -1) { + const firstread_objset_array* objset_array = firstread_ptr->objset_array; + if (objset_array->objset_id_array && objset_array->objset_array) { + const uint32_t num_objset = objset_array->num_objset; + for (uint32_t i = 0; i < num_objset; i++) { + if (objset_array->objset_id_array[i] != load_set_id || !objset_array->objset_array[i].data) + continue; + + const firstread_objset* frb_set = firstread_objset::read(alloc, + objset_array->objset_array[i].data, objset_array->objset_array[i].size); + if (!frb_set || !set->obj_data || !frb_set->obj_data) + break; + + const int32_t frb_obj_num = frb_set->obj_num; + for (int32_t j = 0; j < frb_obj_num; j++) { + const int32_t obj_num = set->obj_num; + for (int32_t k = 0; k < obj_num; k++) { + if (frb_set->obj_data[j].id != set->obj_id_data[k]) + continue; + + obj* obj = set->obj_data[k]; + const firstread_obj* frb_obj = &frb_set->obj_data[j]; + if (obj->mesh_array && frb_obj->mesh_array) { + const int32_t frb_num_mesh = frb_obj->num_mesh; + int32_t index_match = -1; + for (int32_t l = 0; l < frb_num_mesh; l++) { + const uint64_t name_hash = hash_utf8_xxh3_64bits(frb_obj->mesh_array[l].name); + const int32_t num_mesh = obj->num_mesh; + for (int32_t m = 0; m < num_mesh; m++) { + if (name_hash != hash_utf8_xxh3_64bits(obj->mesh_array[m].name)) + continue; + else if (frb_obj->mesh_array[l].index != (uint8_t)-1) { + if (++index_match != frb_obj->mesh_array[l].index) + continue; + } + + obj_mesh* mesh = &obj->mesh_array[m]; + const firstread_obj_mesh* frb_mesh = &frb_obj->mesh_array[l]; + if (frb_mesh->remove) { + mesh->flags = 0; + mesh->bounding_sphere = {}; + mesh->num_submesh = 0; + mesh->submesh_array = 0; + mesh->vertex_format = (obj_vertex_format)0; + mesh->size_vertex = 0; + mesh->num_vertex = 0; + for (int32_t n = 0; n < 20; n++) + mesh->vertex_array_ptr[n] = 0; + mesh->attrib = {}; + mesh->vertex_flags = 0; + break; + } + + if (mesh->submesh_array && mesh->num_submesh) { + const int32_t num_submesh = mesh->num_submesh; + for (int32_t n = 0; n < num_submesh; n++) { + obj_sub_mesh* submesh = &mesh->submesh_array[n]; + submesh->attrib.m.no_reflect = frb_mesh->attrib.m.no_reflect; + submesh->attrib.m.reflect = frb_mesh->attrib.m.reflect; + submesh->attrib.m.reflect_cam_back = frb_mesh->attrib.m.reflect_cam_back; + } + } + + if (mesh->submesh_array && frb_mesh->submesh_array + && mesh->num_submesh && mesh->num_submesh == frb_mesh->num_submesh) { + const int32_t num_submesh = mesh->num_submesh; + int32_t num_submesh_split = num_submesh; + for (int32_t n = 0; n < num_submesh; n++) { + const firstread_obj_sub_mesh* frb_submesh = &frb_mesh->submesh_array[n]; + if (frb_submesh->split) + num_submesh_split++; + } + + if (num_submesh < num_submesh_split) { + obj_sub_mesh* submesh_array = mesh->submesh_array; + mesh->num_submesh = num_submesh_split; + mesh->submesh_array = alloc->allocate(num_submesh_split); + for (int32_t n = 0, o = 0; n < num_submesh; n++, o++) { + const firstread_obj_sub_mesh* frb_submesh = &frb_mesh->submesh_array[n]; + mesh->submesh_array[o] = submesh_array[n]; + if (frb_submesh->split) + mesh->submesh_array[++o] = submesh_array[n]; + } + } + + for (int32_t n = 0, o = 0; n < num_submesh; n++, o++) { + obj_sub_mesh* submesh = &mesh->submesh_array[o]; + const firstread_obj_sub_mesh* frb_submesh = &frb_mesh->submesh_array[n]; + + if (frb_submesh->split) { + firstread_obj_sub_mesh_split* split = frb_submesh->split; + if (split->material_index[0] != -1) + submesh->material_index = split->material_index[0]; + if (split->num_index[0] && split->index_array[0]) { + submesh->bounding_sphere = split->bounding_sphere[0]; + submesh->index_format = split->index_format; + submesh->num_index = split->num_index[0]; + submesh->index_array = (void*)split->index_array[0]; + } + submesh->attrib = split->attrib[0]; + + obj_sub_mesh* submesh_before = &mesh->submesh_array[++o]; + if (split->material_index[1] != -1) + submesh_before->material_index = split->material_index[1]; + if (split->num_index[1] && split->index_array[1]) { + submesh_before->bounding_sphere = split->bounding_sphere[1]; + submesh_before->index_format = split->index_format; + submesh_before->num_index = split->num_index[1]; + submesh_before->index_array = (void*)split->index_array[1]; + } + submesh_before->attrib = split->attrib[1]; + continue; + } + else if (frb_submesh->num_index && frb_submesh->index_array) { + submesh->bounding_sphere = frb_submesh->bounding_sphere; + submesh->index_format = frb_submesh->index_format; + submesh->num_index = frb_submesh->num_index; + submesh->index_array = (void*)frb_submesh->index_array; + } + + submesh->attrib = frb_submesh->attrib; + } + } + + if (frb_mesh->vertex_format && frb_mesh->num_vertex) { + mesh->vertex_format = frb_mesh->vertex_format; + mesh->size_vertex = frb_mesh->size_vertex; + mesh->num_vertex = frb_mesh->num_vertex; + for (int32_t n = 0; n < 20; n++) + mesh->vertex_array_ptr[n] = frb_mesh->vertex_array_ptr[n]; + } + + mesh->attrib = frb_mesh->attrib; + break; + } + } + } + + if (frb_obj->num_material && frb_obj->material_array) { + obj->num_material = frb_obj->num_material; + obj->material_array = (obj_material_data*)frb_obj->material_array; + } + break; + } + } + break; + } + } + } + + return set; +} + +HOOK(bool, FASTCALL, _objset_info_storage_load_obj_set_check_not_read, 0x000000014045DA60, int32_t set) { + load_set_id = set; + bool ret = original_objset_info_storage_load_obj_set_check_not_read(set); + load_set_id = -1; + return ret; +} + HOOK(void, FASTCALL, obj_mesh_vertex_buffer__unload, 0x0000000140461870, obj_mesh_vertex_buffer* objvb) { objvb->unload(); } @@ -1057,6 +1227,8 @@ void object_patch() { INSTALL_HOOK(ObjsetInfo__vertex_buffer_free); INSTALL_HOOK(objset_info_storage_get_obj_mesh_index_buffer); INSTALL_HOOK(objset_info_storage_get_obj_mesh_vertex_buffer); + INSTALL_HOOK(obj_set_parse_file); + INSTALL_HOOK(_objset_info_storage_load_obj_set_check_not_read); INSTALL_HOOK(obj_mesh_vertex_buffer__unload); } diff --git a/src/DivaGL/object.hpp b/src/DivaGL/object.hpp index 82f7695..c987651 100644 --- a/src/DivaGL/object.hpp +++ b/src/DivaGL/object.hpp @@ -288,9 +288,12 @@ struct obj_material_data { struct obj_sub_mesh_attrib_member { uint32_t recieve_shadow : 1; uint32_t cast_shadow : 1; - uint32_t transparent : 1; + uint32_t translucent : 1; uint32_t cloth : 1; - uint32_t dummy : 28; + uint32_t no_reflect : 1; // Own stuff + uint32_t reflect : 1; // Own stuff + uint32_t reflect_cam_back : 1; // Own stuff + uint32_t dummy : 25; // 28 }; union obj_sub_mesh_attrib { @@ -309,7 +312,7 @@ struct obj_sub_mesh { obj_primitive_type primitive_type; obj_index_format index_format; int32_t num_index; - uint16_t* index_array; + void* index_array; obj_sub_mesh_attrib attrib; uint32_t reserved[4]; obj_axis_aligned_bounding_box* axis_aligned_bounding_box; @@ -325,7 +328,11 @@ struct obj_mesh_attrib_member { uint32_t billboard_y_axis : 1; uint32_t translucent_sort_by_radius : 1; uint32_t billboard : 1; - uint32_t dummy : 26; // 28 + uint32_t no_reflect : 1; // Own stuff + uint32_t reflect : 1; // Own stuff + uint32_t reflect_cam_back : 1; // Own stuff + uint32_t disable_aabb_culling : 1; // Own stuff + uint32_t dummy : 22; // 28 uint32_t compression : 2; // Own stuff }; @@ -611,6 +618,6 @@ extern int32_t obj_material_texture_type_get_texture_index( obj_material_texture_type type, int32_t base_index); extern void obj_skin_set_matrix_buffer(obj_skin* s, mat4* matrices, - mat4* ex_data_matrices, mat4* matrix_buffer, const mat4* mat, const mat4* global_mat); + mat4* ex_data_matrices, mat4* matrix_buffer, const mat4* mat, const mat4& global_mat); extern void object_patch(); diff --git a/src/DivaGL/print_work.cpp b/src/DivaGL/print_work.cpp index d947b9e..fc281d1 100644 --- a/src/DivaGL/print_work.cpp +++ b/src/DivaGL/print_work.cpp @@ -169,7 +169,7 @@ void PrintWork::DrawTextMesh(app::text_flags flags, sprite_text_mesh& mesh) { } args.SetVertexArray(mesh.vertices.data(), mesh.vertices.size()); args.id.index = mesh.sprite_id; - spr::put_sprite(&args); + spr::put_sprite(args); for (spr::SpriteVertex& i : mesh.vertices) { i.color.r = color.r; @@ -184,7 +184,7 @@ void PrintWork::DrawTextMesh(app::text_flags flags, sprite_text_mesh& mesh) { if (flags & app::TEXT_FLAG_FONT) args.shader = SHADER_FT_FONT; args.id.index = mesh.sprite_id; - spr::put_sprite(&args); + spr::put_sprite(args); } void PrintWork::FillRectangle(rectangle rect) { diff --git a/src/DivaGL/pv_game/firstread.cpp b/src/DivaGL/pv_game/firstread.cpp new file mode 100644 index 0000000..4b48d45 --- /dev/null +++ b/src/DivaGL/pv_game/firstread.cpp @@ -0,0 +1,313 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "../../KKdLib/f2/header.hpp" +#include "../../KKdLib/aes.hpp" +#include "../../KKdLib/deflate.hpp" +#include "firstread.hpp" + +enum { + FIRSTREAD_STR_ARRAY = 0, + FIRSTREAD_AUTH_3D, + FIRSTREAD_OBJSET, + FIRSTREAD_STAGE_DATA, + FIRSTREAD_MAX, +}; + +const firstread_str_array* firstread_str_array::read(const void* data, size_t size) { + if (!data || !size) + return 0; + + size_t d = (size_t)data; + firstread_str_array* str_array = (firstread_str_array*)data; + if (str_array->signature != reverse_endianness_uint32_t('strx')) + return 0; + + if (str_array->lang_array) { + str_array->lang_array = (firstread_str_array_lang*)(d + (size_t)str_array->lang_array); + + const uint32_t num_lang = str_array->num_lang; + for (uint32_t i = 0; i < num_lang; i++) { + firstread_str_array_lang* lang = (firstread_str_array_lang*)&str_array->lang_array[i]; + + if (lang->string_array) { + lang->string_array = (firstread_str_array_string*)(d + (size_t)lang->string_array); + + const uint32_t num_string = lang->num_string; + for (uint32_t j = 0; j < num_string; j++) { + firstread_str_array_string* string = (firstread_str_array_string*)&lang->string_array[j]; + + if (string->str) + string->str = (const char*)(d + (size_t)string->str); + } + } + } + } + return str_array; +} + +const firstread_auth_3d_array* firstread_auth_3d_array::read(const void* data, size_t size) { + if (!data || !size) + return 0; + + size_t d = (size_t)data; + firstread_auth_3d_array* auth_3d_array = (firstread_auth_3d_array*)data; + if (auth_3d_array->signature != reverse_endianness_uint32_t('a3dx')) + return 0; + + if (auth_3d_array->auth_3d_array) { + auth_3d_array->auth_3d_array = (firstread_auth_3d*)(d + (size_t)auth_3d_array->auth_3d_array); + + const uint32_t num_auth_3d = auth_3d_array->num_auth_3d; + for (uint32_t i = 0; i < num_auth_3d; i++) { + firstread_auth_3d* a3d = (firstread_auth_3d*)&auth_3d_array->auth_3d_array[i]; + + if (a3d->object_array) { + a3d->object_array = (firstread_auth_3d_object*)(d + (size_t)a3d->object_array); + + const uint32_t num_object = a3d->num_object; + for (uint32_t j = 0; j < num_object; j++) { + firstread_auth_3d_object* obj = (firstread_auth_3d_object*)&a3d->object_array[j]; + + if (obj->name) + obj->name = (const char*)(d + (size_t)obj->name); + + if (obj->uid_name) + obj->uid_name = (const char*)(d + (size_t)obj->uid_name); + } + } + + if (a3d->object_list_array) { + a3d->object_list_array = (firstread_auth_3d_object_list*)(d + (size_t)a3d->object_list_array); + + const uint32_t num_object_list = a3d->num_object_list; + for (uint32_t j = 0; j < num_object_list; j++) { + firstread_auth_3d_object_list* obj_list = (firstread_auth_3d_object_list*)&a3d->object_list_array[j]; + + if (obj_list->name) + obj_list->name = (const char*)(d + (size_t)obj_list->name); + } + } + } + } + return auth_3d_array; +} + +const firstread_objset* firstread_objset::read( + prj::shared_ptr& alloc, const void* data, size_t size) { + if (!data || !size) + return 0; + + f2_header* head = (f2_header*)alloc->allocate(size); + if (!head) + return 0; + + memcpy(head, data, size); + + bool reset_flags = head->attrib.get_gzip() || head->attrib.get_xor_data(); + + if (head->attrib.get_crc()) { + if (!head->validate_crc()) + return 0; + + if (reset_flags) + head->reset_crc(); + } + + if (head->attrib.get_xor_data()) + head->remove_xor(); + + const deflate::allocator allocator = { + (void*)&alloc, + [](void* data, size_t size) { + return (*(prj::shared_ptr*)data)->allocate(size); + }, + [](void* data, void* ptr) { + + } + }; + + if (head->attrib.get_aes()) { + uint8_t* section_data = head->get_section_data(); + uint32_t section_size = head->get_section_size(); + if (section_data) { + static const uint32_t key_iv_size = 0x30; + if (section_size < key_iv_size) + return 0; + + section_size -= key_iv_size; + + if (section_size == align_val(section_size, 0x20)) { + const uint32_t data_size = head->get_data_size(); + uint8_t* key = section_data + data_size - key_iv_size; + uint8_t* iv = section_data + data_size + 0x20 - key_iv_size; + + aes256_ctx aes; + aes256_init_ctx_iv(&aes, key, iv); + aes256_cbc_decrypt_buffer(&aes, section_data, section_size); + head->attrib.set_aes(false); + } + } + } + + void* dec = 0; + if (head->attrib.get_gzip()) { + uint8_t* section_data = head->get_section_data(); + uint32_t section_size = head->get_section_size(); + if (!head->custom || !section_data || !section_size) + return 0; + + dec = 0; + size_t dec_len = head->custom; + if (deflate::decompress(section_data, section_size, dec, dec_len, deflate::MODE_GZIP, allocator) < 0 + || !dec_len || dec_len < head->custom) + return 0; + + if (reset_flags) + head->attrib.set_gzip(false); + } + else { + dec = alloc->allocate(head->custom); + if (dec) { + memset(dec, 0, head->custom); + memcpy(dec, head->get_section_data(), min_def(head->custom, head->get_section_size())); + } + } + + if (!dec) + return 0; + + size_t d = (size_t)dec; + firstread_objset* objset = (firstread_objset*)dec; + if (objset->signature != reverse_endianness_uint32_t('objx')) + return 0; + + if (objset->obj_data) { + objset->obj_data = (firstread_obj*)(d + (size_t)objset->obj_data); + + const uint32_t obj_num = objset->obj_num; + for (uint32_t i = 0; i < obj_num; i++) { + firstread_obj* obj = (firstread_obj*)&objset->obj_data[i]; + + if (obj->mesh_array) { + obj->mesh_array = (firstread_obj_mesh*)(d + (size_t)obj->mesh_array); + + const uint32_t num_mesh = obj->num_mesh; + for (uint32_t j = 0; j < num_mesh; j++) { + firstread_obj_mesh* mesh = (firstread_obj_mesh*)&obj->mesh_array[j]; + + if (mesh->submesh_array) { + mesh->submesh_array = (firstread_obj_sub_mesh*)(d + (size_t)mesh->submesh_array); + + const uint32_t num_submesh = mesh->num_submesh; + for (uint32_t j = 0; j < num_submesh; j++) { + firstread_obj_sub_mesh* sub_mesh = (firstread_obj_sub_mesh*)&mesh->submesh_array[j]; + + if (sub_mesh->index_array) + sub_mesh->index_array = (void*)(d + (size_t)sub_mesh->index_array); + + if (sub_mesh->split) { + sub_mesh->split = (firstread_obj_sub_mesh_split*)(d + (size_t)sub_mesh->split); + + firstread_obj_sub_mesh_split* split = sub_mesh->split; + if (split->index_array[0]) + split->index_array[0] = (void*)(d + (size_t)split->index_array[0]); + if (split->index_array[1]) + split->index_array[1] = (void*)(d + (size_t)split->index_array[1]); + } + } + } + + for (void*& k : mesh->vertex_array_ptr) + if (k) + k = (void*)(d + (size_t)k); + } + } + + if (obj->material_array) + obj->material_array = (obj_material_data*)(d + (size_t)obj->material_array); + } + } + return objset; +} + +const firstread_objset_array* firstread_objset_array::read(const void* data, size_t size) { + if (!data || !size) + return 0; + + size_t d = (size_t)data; + firstread_objset_array* objset_array = (firstread_objset_array*)data; + if (objset_array->signature != reverse_endianness_uint32_t('osdx')) + return 0; + + if (objset_array->objset_id_array) + objset_array->objset_id_array = (int32_t*)(d + (size_t)objset_array->objset_id_array); + + if (objset_array->objset_array) { + objset_array->objset_array = (firstread_objset_data*)(d + (size_t)objset_array->objset_array); + + const uint32_t num_objset = objset_array->num_objset; + for (uint32_t i = 0; i < num_objset; i++) { + firstread_objset_data* objset = (firstread_objset_data*)&objset_array->objset_array[i]; + + if (objset->data) + objset->data = (void*)(d + (size_t)objset->data); + } + } + return objset_array; +} + +const firstread_stage_data_array* firstread_stage_data_array::read(const void* data, size_t size) { + if (!data || !size) + return 0; + + size_t d = (size_t)data; + firstread_stage_data_array* stage_data_array = (firstread_stage_data_array*)data; + if (stage_data_array->signature != reverse_endianness_uint32_t('stgx')) + return 0; + + if (stage_data_array->stage_data_array) { + stage_data_array->stage_data_array = (firstread_stage_data*)(d + (size_t)stage_data_array->stage_data_array); + + const uint32_t num_stage_data = stage_data_array->num_stage_data; + for (uint32_t i = 0; i < num_stage_data; i++) { + firstread_stage_data* stage_data = (firstread_stage_data*)&stage_data_array->stage_data_array[i]; + + if (stage_data->name) + stage_data->name = (const char*)(d + (size_t)stage_data->name); + + if (stage_data->reflect) + stage_data->reflect = (stage_data_reflect*)(d + (size_t)stage_data->reflect); + } + } + return stage_data_array; +} + +const firstread* firstread::read(const void* data, size_t size) { + if (!data || !size) + return 0; + + size_t d = (size_t)data; + firstread* fr = (firstread*)data; + if (fr->signature != reverse_endianness_uint32_t('frbx')) + return 0; + + if (fr->str_array) + fr->str_array = firstread_str_array::read( + (void*)(d + (size_t)fr->str_array), fr->str_array_size); + + if (fr->auth_3d_array) + fr->auth_3d_array = firstread_auth_3d_array::read( + (void*)(d + (size_t)fr->auth_3d_array), fr->auth_3d_array_size); + + if (fr->objset_array) + fr->objset_array = firstread_objset_array::read( + (void*)(d + (size_t)fr->objset_array), fr->objset_array_size); + + if (fr->stage_data_array) + fr->stage_data_array = firstread_stage_data_array::read( + (void*)(d + (size_t)fr->stage_data_array), fr->stage_data_array_size); + return fr; +} diff --git a/src/DivaGL/pv_game/firstread.hpp b/src/DivaGL/pv_game/firstread.hpp new file mode 100644 index 0000000..da5f4a1 --- /dev/null +++ b/src/DivaGL/pv_game/firstread.hpp @@ -0,0 +1,153 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/vec.hpp" +#include "../object.hpp" +#include "../stage.hpp" +#include "../types.hpp" + +struct firstread_str_array_string { + int32_t id; + const char* str; +}; + +struct firstread_str_array_lang { + int32_t lang_id; + uint32_t num_string; + const firstread_str_array_string* string_array; +}; + +struct firstread_str_array { + uint32_t signature; + uint32_t num_lang; + const firstread_str_array_lang* lang_array; + + static const firstread_str_array* read(const void* data, size_t size); +}; + +struct firstread_auth_3d_object { + const char* name; + const char* uid_name; +}; + +struct firstread_auth_3d_object_list { + const char* name; + bool remove; +}; + +struct firstread_auth_3d { + int32_t uid; + uint32_t num_object; + uint32_t num_object_list; + const firstread_auth_3d_object* object_array; + const firstread_auth_3d_object_list* object_list_array; +}; + +struct firstread_auth_3d_array { + uint32_t signature; + uint32_t num_auth_3d; + const firstread_auth_3d* auth_3d_array; + + static const firstread_auth_3d_array* read(const void* data, size_t size); +}; + +struct firstread_obj_sub_mesh_split { + obj_bounding_sphere bounding_sphere[2]; + int32_t material_index[2]; + obj_index_format index_format; + int32_t num_index[2]; + const void* index_array[2]; + obj_sub_mesh_attrib attrib[2]; +}; + +struct firstread_obj_sub_mesh { + obj_bounding_sphere bounding_sphere; + obj_index_format index_format; + int32_t num_index; + const void* index_array; + obj_sub_mesh_attrib attrib; + firstread_obj_sub_mesh_split* split; +}; + +struct firstread_obj_mesh { + firstread_obj_sub_mesh* submesh_array; + int32_t num_submesh; + obj_vertex_format vertex_format; + uint32_t size_vertex; + int32_t num_vertex; + union { + obj_mesh_vertex_array vertex_array; + void* vertex_array_ptr[20]; + }; + obj_mesh_attrib attrib; + bool remove; + uint8_t index; + uint8_t pad[2]; + char name[0x40]; +}; + +struct firstread_obj { + int32_t id; + uint16_t num_mesh; + uint16_t num_material; + firstread_obj_mesh* mesh_array; + const obj_material_data* material_array; +}; + +struct firstread_objset { + uint32_t signature; + int32_t obj_num; + const firstread_obj* obj_data; + + static const firstread_objset* read( + prj::shared_ptr& alloc, const void* data, size_t size); +}; + +struct firstread_objset_data { + const void* data; + size_t size; +}; + +struct firstread_objset_array { + uint32_t signature; + uint32_t num_objset; + const int32_t* objset_id_array; + const firstread_objset_data* objset_array; + + static const firstread_objset_array* read(const void* data, size_t size); +}; + +struct firstread_stage_data { + const char* name; + uint32_t pv_id; + uint8_t reflect_type; + uint8_t reflect_full; + const stage_data_reflect* reflect; +}; + +struct firstread_stage_data_array { + uint32_t signature; + uint32_t num_stage_data; + const firstread_stage_data* stage_data_array; + + static const firstread_stage_data_array* read(const void* data, size_t size); +}; + +struct firstread { + uint32_t signature; + uint32_t pad[0x03]; + const firstread_str_array* str_array; + size_t str_array_size; + const firstread_auth_3d_array* auth_3d_array; + size_t auth_3d_array_size; + const firstread_objset_array* objset_array; + size_t objset_array_size; + const firstread_stage_data_array* stage_data_array; + size_t stage_data_array_size; + + static const firstread* read(const void* data, size_t size); +}; diff --git a/src/DivaGL/pv_game/pv_game.cpp b/src/DivaGL/pv_game/pv_game.cpp index 2b76fbc..ccb40c5 100644 --- a/src/DivaGL/pv_game/pv_game.cpp +++ b/src/DivaGL/pv_game/pv_game.cpp @@ -11,6 +11,7 @@ #include "../auth_3d.hpp" #include "../print_work.hpp" #include "dsc.hpp" +#include "firstread.hpp" #include "pvpp.hpp" #include "pvsr.hpp" #include @@ -620,26 +621,30 @@ public: void stop_current_pv(); }; -struct x_pv_game_str_array { - prj::unordered_map> strings; +struct x_pv_game_firstread { + prj::unordered_map> str_array_strings; p_file_handler file_handler; + const firstread* firstread; int32_t lang_sel; bool init; - x_pv_game_str_array(); - ~x_pv_game_str_array(); + x_pv_game_firstread(); + ~x_pv_game_firstread(); - const char* get_string(int32_t id); + const char* get_str_array_string(int32_t id); void read(); }; TaskPvGame& task_pv_game = *(TaskPvGame*)0x000000014CC95270; TaskPvGameX* task_pv_game_x; -x_pv_game_str_array* x_pv_game_str_array_ptr; +const firstread* firstread_ptr; +x_pv_game_firstread* x_pv_game_firstread_ptr; pv_disp2d& pv_disp2d_data = *(pv_disp2d*)0x0000000140C938E0; size_t pv_game_ptr = 0x0000000140CDD8D0; pv_game_parent& pv_game_parent_data = *(pv_game_parent*)0x0000000140D0B510; +bool reflect_full = false; + bool pv_game_load_state_4_tail_impl() { if (task_pv_game_x->use) { bool wait_load = false; @@ -827,15 +832,15 @@ HOOK(bool, FASTCALL, pv_game_pv_data__load, 0x000000014011B7E0, size_t _this, void pv_game_init() { if (!task_pv_game_x) task_pv_game_x = new (_operator_new(sizeof(TaskPvGameX))) TaskPvGameX; - if (!x_pv_game_str_array_ptr) - x_pv_game_str_array_ptr = new (_operator_new(sizeof(x_pv_game_str_array))) x_pv_game_str_array; + if (!x_pv_game_firstread_ptr) + x_pv_game_firstread_ptr = new (_operator_new(sizeof(x_pv_game_firstread))) x_pv_game_firstread; } void pv_game_free() { - if (x_pv_game_str_array_ptr) { - x_pv_game_str_array_ptr->~x_pv_game_str_array(); - _operator_delete(x_pv_game_str_array_ptr); - x_pv_game_str_array_ptr = 0; + if (x_pv_game_firstread_ptr) { + x_pv_game_firstread_ptr->~x_pv_game_firstread(); + _operator_delete(x_pv_game_firstread_ptr); + x_pv_game_firstread_ptr = 0; } if (task_pv_game_x) { @@ -1715,6 +1720,7 @@ void x_pv_game_effect::set_song_effect(int32_t index, int64_t time) { i.id.set_repeat(false); i.id.set_req_frame(0.0f); i.id.set_visibility(true); + i.id.set_reflect(reflect_full); } for (x_pv_game_song_effect_glitter& i : song_effect.glitter) { @@ -1979,7 +1985,7 @@ void x_pv_aet_disp_string::ctrl(const char* name, aet_obj_data* aet) { } static const char* sub_8133DDF8(int32_t id) { - return x_pv_game_str_array_ptr->get_string(id); + return x_pv_game_firstread_ptr->get_str_array_string(id); } const char* x_pv_str_array_pv::get_song_name(int32_t index) { @@ -3166,6 +3172,7 @@ void x_pv_game_stage::ctrl(float_t delta_time) { id.set_paused(false); id.set_visibility(false); id.set_frame_rate(&bpm_frame_rate_control); + id.set_reflect(reflect_full); eff_auth_3d.id = id; eff_auth_3d.name = stg_eff_auth_3d.name; @@ -3438,6 +3445,7 @@ void x_pv_game_stage::load_change_effect(int32_t curr_stage_effect, int32_t next id.set_paused(false); id.set_visibility(false); id.set_frame_rate(&bpm_frame_rate_control); + id.set_reflect(reflect_full); chg_eff_auth_3d.id = id; chg_eff_auth_3d.name = stg_chg_eff_auth_3d.name; @@ -3560,6 +3568,7 @@ void x_pv_game_stage::set_change_effect_frame_part_2(float_t frame) { id.set_paused(false); id.set_visibility(!!(flags & 0x01)); id.set_frame_rate(&bpm_frame_rate_control); + id.set_reflect(reflect_full); } for (x_pv_game_stage_effect_glitter& i : chg_eff.glitter) { @@ -3649,6 +3658,7 @@ void x_pv_game_stage::set_stage_effect_auth_3d_frame(int32_t stage_effect, float id.set_paused(false); id.set_visibility(true); id.set_frame_rate(&bpm_frame_rate_control); + id.set_reflect(reflect_full); } } @@ -3830,7 +3840,22 @@ TaskPvGameX::~TaskPvGameX() { bool TaskPvGameX::init() { use = true; - x_pv_game_str_array_ptr->read(); + x_pv_game_firstread_ptr->read(); + reflect_full = false; + extern bool config_reflect_full; + if (firstread_ptr && firstread_ptr->stage_data_array && config_reflect_full) { + const firstread_stage_data_array* stage_data_array = firstread_ptr->stage_data_array; + const uint32_t num_stage_data = stage_data_array->num_stage_data; + for (uint32_t i = 0; i < num_stage_data; i++) { + const firstread_stage_data* stage_data = &stage_data_array->stage_data_array[i]; + if (stage_data->pv_id == pv_id) { + if (stage_data->reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP + && stage_data->reflect && stage_data->reflect_full) + reflect_full = true; + break; + } + } + } return true; } @@ -3909,12 +3934,45 @@ bool TaskPvGameX::ctrl() { bool TaskPvGameX::dest() { unload(); + reflect_full = false; use = false; return true; } void TaskPvGameX::disp() { data.disp(); + + static bool reflect_show; + if (reflect_full && reflect_show) { + extern RenderTexture* reflect_tex; + if (reflect_tex) { + const float_t width = 1280.0f; + const float_t height = 720.0f; + + const float_t reflection_quality = 1.0f; + + spr::put_sprite_rect({ 0.0f, 0.0f, width, height }, RESOLUTION_MODE_HD, + spr::SPR_PRIO_DEFAULT, { 0x00, 0x00, 0x00, 0xFF }, 0); + + spr::SprArgs args; + args.resolution_mode_screen = RESOLUTION_MODE_HD; + args.resolution_mode_sprite = RESOLUTION_MODE_HD; + args.SetSpriteSize({ width, height }); + args.SetTexturePosSize(0.0f, + (float_t)reflect_tex->GetHeight() - (float_t)reflect_tex->GetHeight() * reflection_quality, + (float_t)reflect_tex->GetWidth() * reflection_quality, + (float_t)reflect_tex->GetHeight() * reflection_quality); + args.texture = reflect_tex->color_texture; + args.color = color4u8_bgra(0xFF, 0xFF, 0xFF, 0x3F); + args.scale.x = 1.0f; + args.scale.y = -1.0f; + args.center.x = width * 0.5f; + args.center.y = height * 0.5f; + args.trans.x = width * 0.5f; + args.trans.y = height * 0.5f; + spr::put_sprite(args); + } + } } void TaskPvGameX::load(int32_t pv_id, int32_t modules[6]) { @@ -4076,17 +4134,19 @@ void TaskPvGameX::stop_current_pv() { data.stop(); } -x_pv_game_str_array::x_pv_game_str_array() : init() { +x_pv_game_firstread::x_pv_game_firstread() : init(), firstread() { lang_sel = 0; } -x_pv_game_str_array::~x_pv_game_str_array() { - +x_pv_game_firstread::~x_pv_game_firstread() { + firstread = 0; + firstread_ptr = 0; + file_handler.reset(); } -const char* x_pv_game_str_array::get_string(int32_t id) { - auto lang_elem = strings.find(lang_sel); - if (lang_elem != strings.end()) { +const char* x_pv_game_firstread::get_str_array_string(int32_t id) { + auto lang_elem = str_array_strings.find(lang_sel); + if (lang_elem != str_array_strings.end()) { auto elem = lang_elem->second.find(id); if (elem != lang_elem->second.end()) return elem->second; @@ -4094,11 +4154,11 @@ const char* x_pv_game_str_array::get_string(int32_t id) { return 0; } -void x_pv_game_str_array::read() { +void x_pv_game_firstread::read() { if (init) return; - file_handler.read_file_path("rom/str_array_x.bin", prj::HeapCMallocSystem); + file_handler.read_file_path("rom/firstread_x.bin", prj::HeapCMallocSystem); file_handler.read_now(); if (!file_handler.get_data() || !file_handler.get_size()) { @@ -4106,24 +4166,22 @@ void x_pv_game_str_array::read() { return; } - size_t data = (size_t)file_handler.get_data(); - if (*(uint32_t*)data != reverse_endianness_uint32_t('strx')) { - init = true; - return; + firstread = firstread::read(file_handler.get_data(), file_handler.get_size()); + firstread_ptr = firstread; + + if (firstread->str_array) { + const firstread_str_array* str_array = firstread->str_array; + const uint32_t lang_count = str_array->num_lang; + for (uint32_t i = 0; i < lang_count; i++) { + const firstread_str_array_lang* lang_data = &str_array->lang_array[i]; + + prj::unordered_map& strings = str_array_strings[lang_data->lang_id]; + const uint32_t num_string = lang_data->num_string; + for (uint32_t j = 0; j < num_string; j++) { + const firstread_str_array_string* string_data = &lang_data->string_array[j]; + strings[string_data->id] = string_data->str; + } + } } - - uint32_t lang_count = *(uint32_t*)(data + 0x04); - size_t lang_data = data + *(uint32_t*)(data + 0x08); - for (uint32_t i = lang_count; i; i--, lang_data += 0x0C) { - int32_t lang_id = *(int32_t*)lang_data; - uint32_t strings_count = *(uint32_t*)(lang_data + 0x04); - uint32_t strings_offset = *(uint32_t*)(lang_data + 0x08); - - prj::unordered_map& strings = this->strings[lang_id]; - size_t strings_data = data + strings_offset; - for (uint32_t j = strings_count; j; j--, strings_data += 0x08) - strings[*(int32_t*)strings_data] = (const char*)(data + *(uint32_t*)(strings_data + 0x04)); - } - init = true; } diff --git a/src/DivaGL/render.cpp b/src/DivaGL/render.cpp index 857803e..cdced59 100644 --- a/src/DivaGL/render.cpp +++ b/src/DivaGL/render.cpp @@ -227,9 +227,9 @@ namespace rndr { rctx->sun_quad_ubo.Bind(0); mat4 view; - mat4_transpose(&camera_data->view, &view); + mat4_transpose(&camera_data.view, &view); mat4 projection; - mat4_transpose(&camera_data->projection, &projection); + mat4_transpose(&camera_data.projection, &projection); ExposureCharaData* chara_data = exposure_chara_data; int32_t query_index = (this->exposure_query_index + 1) % 3; @@ -339,16 +339,16 @@ namespace rndr { return; mat4 view; - mat4_transpose(&camera_data->view, &view); + mat4_transpose(&camera_data.view, &view); mat4 projection; - mat4_transpose(&camera_data->projection, &projection); + mat4_transpose(&camera_data.projection, &projection); mat4 view_projection; mat4 inverse_view_projection_matrix; - mat4_transpose(&camera_data->view_projection, &view_projection); + mat4_transpose(&camera_data.view_projection, &view_projection); mat4_invert(&view_projection, &inverse_view_projection_matrix); - float_t v5 = tanf(camera_data->fov * 0.5f * DEG_TO_RAD_FLOAT); + float_t v5 = tanf(camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT); light_set* set = &light_set_data[LIGHT_SET_MAIN]; light_data* data = &set->lights[LIGHT_SUN]; @@ -820,16 +820,16 @@ namespace rndr { view_point_prev = view_point; interest_prev = interest; - view_point = camera_data->view_point; - interest = camera_data->interest; + view_point = camera_data.view_point; + interest = camera_data.interest; stage_index_prev = stage_index; stage_index = task_stage_get_current_stage_index(); cam_view_projection_prev = cam_view_projection; - cam_view_projection = camera_data->view_projection; + cam_view_projection = camera_data.view_projection; - reset_exposure = camera_data->fast_change_hist1 && !camera_data->fast_change_hist0; + reset_exposure = camera_data.fast_change_hist1 && !camera_data.fast_change_hist0; if (reset_exposure) { float_t view_point_dist = vec3::distance(view_point, view_point_prev); @@ -1354,8 +1354,8 @@ namespace rndr { mat4 v42; mat4 v43; - mat4_mul(&camera_data->view, &v44, &v42); - mat4_mul(&camera_data->projection, &v42, &v43); + mat4_mul(&camera_data.view, &v44, &v42); + mat4_mul(&camera_data.projection, &v42, &v43); mat4_transpose(&v42, &v42); mat4_transpose(&v43, &v43); @@ -1385,7 +1385,7 @@ namespace rndr { v17 = 0.0f; float_t v18 = (float_t)height / (float_t)width; - float_t v20 = tanf(camera_data->fov * 0.5f * DEG_TO_RAD_FLOAT); + float_t v20 = tanf(camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT); float_t v21 = 0.25f / sqrtf(powf(v20 * 3.4f, 2.0f) * (v41.z * v41.z)); float_t v22; if (v21 < 0.055f) diff --git a/src/DivaGL/render_context.cpp b/src/DivaGL/render_context.cpp index d43584d..7d1c95f 100644 --- a/src/DivaGL/render_context.cpp +++ b/src/DivaGL/render_context.cpp @@ -613,7 +613,7 @@ void render_context::ctrl(bool change_res) { return; if (screen_res_change) - render_manager->resize(screen_width, screen_height); + render_manager.resize(screen_width, screen_height); /*if (render_res_change) litproj_texture->Init(render_width, render_height, 0, GL_RGBA8, GL_DEPTH_COMPONENT32F);*/ @@ -629,7 +629,7 @@ void render_context::ctrl(bool change_res) { }; RenderTexture& render_buffer = render->rend_texture[0]; - RenderTexture& reflect_buffer = render_manager->get_render_texture(0); + RenderTexture& reflect_buffer = render_manager.get_render_texture(0); RenderTexture& shadow_buffer = shadow_ptr_get()->render_textures[1]; init_copy_buffer(reflect_buffer, this->reflect_buffer); init_copy_buffer(render_buffer, this->render_buffer); @@ -683,7 +683,7 @@ void render_context::init() { }; RenderTexture& render_buffer = render_get()->rend_texture[0]; - RenderTexture& reflect_buffer = render_manager->get_render_texture(0); + RenderTexture& reflect_buffer = render_manager.get_render_texture(0); RenderTexture& shadow_buffer = shadow_ptr_get()->render_textures[1]; init_copy_buffer(reflect_buffer, this->reflect_buffer); init_copy_buffer(render_buffer, this->render_buffer); diff --git a/src/DivaGL/render_context.hpp b/src/DivaGL/render_context.hpp index 5aa9930..c4237f8 100644 --- a/src/DivaGL/render_context.hpp +++ b/src/DivaGL/render_context.hpp @@ -451,6 +451,7 @@ struct render_context { int32_t screen_height; mdl::ObjList obj_local[mdl::OBJ_TYPE_LOCAL_MAX - mdl::OBJ_TYPE_MAX]; + mdl::ObjList obj_reflect[mdl::OBJ_TYPE_REFLECT_MAX - mdl::OBJ_TYPE_LOCAL_MAX]; render_context(); ~render_context(); diff --git a/src/DivaGL/render_manager.cpp b/src/DivaGL/render_manager.cpp index 71cb8d7..d4e8dcc 100644 --- a/src/DivaGL/render_manager.cpp +++ b/src/DivaGL/render_manager.cpp @@ -9,6 +9,7 @@ #include "light_param/fog.hpp" #include "light_param/light.hpp" #include "mdl/disp_manager.hpp" +#include "mdl/draw_object.hpp" #include "rob/rob.hpp" #include "clear_color.hpp" #include "effect.hpp" @@ -23,6 +24,29 @@ #include "texture.hpp" #include +struct reflect_full_struct { + RenderTexture reflect_texture; + RenderTexture reflect_buffer_texture; + RenderTexture reflect_contour_texture; + + reflect_full_struct(); + ~reflect_full_struct(); + + void free(); + void init(); +}; + +extern render_context* rctx; + +vec4 reflect_clip_plane; +bool reflect_draw = false; +mat4 reflect_mat; +reflect_full_struct* reflect_full_ptr; + +RenderTexture* litproj_shadow = (RenderTexture*)0x0000000141194DA0; +RenderTexture* litproj_texture = (RenderTexture*)0x0000000141194E00; +rndr::RenderManager& render_manager = *(rndr::RenderManager*)0x00000001411AD320; + static void draw_pass_shadow_begin_make_shadowmap(Shadow* shad, int32_t index, int32_t a3); static void draw_pass_shadow_end_make_shadowmap(Shadow* shad, int32_t index, int32_t a3); static void draw_pass_shadow_filter(RenderTexture* a1, RenderTexture* a2, @@ -36,22 +60,21 @@ static void draw_pass_sss_filter(sss_data* sss); static int32_t draw_pass_3d_get_translucent_count(); static void draw_pass_3d_shadow_reset(); static void draw_pass_3d_shadow_set(Shadow* shad); -extern void draw_pass_3d_translucent(mdl::ObjType opaque, mdl::ObjType transparent, mdl::ObjType translucent); +extern void draw_pass_3d_translucent(mdl::ObjType opaque, + mdl::ObjType transparent, mdl::ObjType translucent); static int32_t draw_pass_3d_translucent_count_layers(int32_t* alpha_array, mdl::ObjType opaque, mdl::ObjType transparent, mdl::ObjType translucent); static void draw_pass_3d_translucent_has_objects(bool* arr, mdl::ObjType type); +static void draw_pass_reflect_full(rndr::RenderManager& render_manager); + static void blur_filter_apply(RenderTexture* dst, RenderTexture* src, blur_filter_mode filter, const vec2 res_scale, const vec4 scale, const vec4 offset); static void render_manager_free_render_textures(); static void render_manager_init_render_textures(int32_t multisample); -extern render_context* rctx; - -RenderTexture* litproj_shadow = (RenderTexture*)0x0000000141194DA0; -RenderTexture* litproj_texture = (RenderTexture*)0x0000000141194E00; -rndr::RenderManager* render_manager = (rndr::RenderManager*)0x00000001411AD320; +static void set_reflect_mat(render_context* rctx); namespace rndr { struct RenderTextureData { @@ -420,6 +443,101 @@ namespace rndr { gl_state_begin_event("pass_ss_sss"); rndr::Render* rend = render; + extern bool reflect_full; + if (reflect_full && pass_sw[rndr::RND_PASSID_REFLECT] + && disp_manager->get_obj_count(mdl::OBJ_TYPE_SSS)) { + gl_state_begin_event("reflect"); + reflect_draw = true; + + rndr::Render* rend = render; + RenderTexture& refl_tex = reflect_full_ptr->reflect_texture; + //if (refl_tex.GetWidth() > 1280.0) + // sss->npr_contour = false; + + //if (sss->npr_contour) { + refl_tex.Bind(); + refl_tex.SetViewport(); + //} + //else { + // sss->textures[0].Bind(); + // gl_state_set_viewport(0, 0, 640, 360); + //} + + glClearColorDLL(sss->param.x, sss->param.y, sss->param.z, 0.0f); + glClearDLL(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + draw_pass_set_camera(); + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + light_set_data[i].data_set((light_set_id)i); + + if (shadow) + draw_pass_3d_shadow_set(shadow_ptr); + else + draw_pass_3d_shadow_reset(); + + set_reflect_mat(rctx); + + draw_state.shader_index = SHADER_FT_SSS_SKIN; + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_LEQUAL); + gl_state_set_depth_mask(GL_TRUE); + disp_manager->draw(mdl::OBJ_TYPE_SSS); + gl_state_disable_depth_test(); + draw_state.shader_index = -1; + draw_pass_3d_shadow_reset(); + uniform->arr[U_NPR] = 0; + + if (npr_param == 1) { + if (sss->enable && sss->npr_contour) { + uniform->arr[U_NPR] = 1; + draw_pass_sss_contour(rend); + } + else if (npr) { + refl_tex.Bind(); + refl_tex.SetViewport(); + glClearDLL(GL_DEPTH_BUFFER_BIT); + draw_state.shader_index = SHADER_FT_SSS_SKIN; + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + disp_manager->draw(mdl::OBJ_TYPE_SSS); + gl_state_disable_depth_test(); + draw_state.shader_index = -1; + gl_state_bind_framebuffer(0); + uniform->arr[U_NPR] = 1; + draw_pass_sss_contour(rend); + } + } + + if (!sss->npr_contour) { + sss->textures[0].Bind(); + gl_state_set_viewport(0, 0, 640, 360); + + filter_scene_shader_data filter_scene = {}; + filter_scene.g_transform = { 1.0f, 1.0f, 0.0f, 0.0f }; + filter_scene.g_texcoord = { 1.0f, 1.0f, 0.0f, 0.0f }; + rctx->filter_scene_ubo.WriteMemory(filter_scene); + + imgfilter_batch_shader_data imgfilter_batch = {}; + imgfilter_batch.g_color_scale = 1.0f; + imgfilter_batch.g_color_offset = 0.0f; + imgfilter_batch.g_texture_lod = 0.0f; + rctx->imgfilter_batch_ubo.WriteMemory(imgfilter_batch); + + uniform->arr[U_IMAGE_FILTER] = 5; + shaders_ft.set(SHADER_FT_IMGFILT); + rctx->filter_scene_ubo.Bind(0); + rctx->imgfilter_batch_ubo.Bind(1); + gl_state_active_bind_texture_2d(0, refl_tex.GetColorTex()); + gl_state_bind_sampler(0, rctx->render_samplers[0]); + gl_state_bind_vertex_array(rctx->common_vao); + shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, 0, 4); + } + + draw_pass_sss_filter(sss); + reflect_draw = false; + gl_state_end_event(); + } + //if (rend->render_width > 1280.0) // sss->npr_contour = false; @@ -507,8 +625,17 @@ namespace rndr { } void RenderManager::pass_reflect() { + extern bool reflect_full; + if (reflect_full) { + draw_pass_reflect_full(*this); + return; + } + + extern RenderTexture* reflect_tex; + reflect_tex = 0; + gl_state_begin_event("pass_reflect"); - RenderTexture& refl_tex = render_manager->get_render_texture(0); + RenderTexture& refl_tex = render_manager.get_render_texture(0); RenderTexture& refl_buf_tex = rctx->reflect_buffer; refl_tex.Bind(); if (disp_manager->get_obj_count(mdl::OBJ_TYPE_REFLECT_OPAQUE) @@ -578,7 +705,7 @@ namespace rndr { void RenderManager::pass_refract() { gl_state_begin_event("pass_refract"); - RenderTexture& refract_texture = render_manager->get_render_texture(1); + RenderTexture& refract_texture = render_manager.get_render_texture(1); refract_texture.Bind(); if (disp_manager->get_obj_count(mdl::OBJ_TYPE_REFRACT_OPAQUE) || disp_manager->get_obj_count(mdl::OBJ_TYPE_REFRACT_TRANSPARENT) @@ -730,7 +857,9 @@ namespace rndr { gl_state_active_bind_texture_2d(14, rctx->empty_texture_2d->glid); if (pass_sw[RND_PASSID_REFLECT] && reflect) { - RenderTexture& refl_tex = get_render_texture(0); + extern bool reflect_full; + RenderTexture& refl_tex = reflect_full && reflect_full_ptr + ? reflect_full_ptr->reflect_texture : get_render_texture(0); gl_state_active_bind_texture_2d(15, refl_tex.GetColorTex()); uniform->arr[U_WATER_REFLECT] = 1; } @@ -773,7 +902,7 @@ namespace rndr { gl_state_set_depth_mask(GL_TRUE); if (draw_pass_3d[DRAW_PASS_3D_TRANSPARENT]) disp_manager->draw(mdl::OBJ_TYPE_TRANSPARENT); - if (render_manager->field_120) + if (render_manager.field_120) disp_manager->draw(mdl::OBJ_TYPE_TYPE_7); render->calc_exposure_chara_data(); @@ -844,8 +973,8 @@ namespace rndr { if (Glitter::glt_particle_manager_x->CheckHasLocalEffect()) { // X gl_state_begin_event("local"); - float_t fov = camera_data->fov; - camera_data->fov = 32.2673416137695f; + float_t fov = camera_data.fov; + camera_data.fov = 32.2673416137695f; draw_pass_set_camera(); disp_manager->draw(mdl::OBJ_TYPE_OPAQUE_LOCAL); @@ -862,7 +991,7 @@ namespace rndr { gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); Glitter::glt_particle_manager_x->DispScenes(Glitter::DISP_LOCAL); gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - camera_data->fov = fov; + camera_data.fov = fov; gl_state_end_event(); @@ -968,8 +1097,16 @@ namespace rndr { } void RenderManager::pass_3d_contour() { - RenderTexture* rt = &render->rend_texture[0]; - RenderTexture* contour_rt = render->sss_contour_texture; + RenderTexture* rt; + RenderTexture* contour_rt; + if (reflect_draw) { + rt = &reflect_full_ptr->reflect_texture; + contour_rt = &reflect_full_ptr->reflect_buffer_texture; + } + else { + rt = &render->rend_texture[0]; + contour_rt = render->sss_contour_texture; + } gl_state_begin_event("`anonymous-namespace'::draw_npr_frame"); gl_state_enable_blend(); @@ -1059,15 +1196,15 @@ void image_filter_scale(RenderTexture* dst, texture* src, const vec4& scale) { void draw_pass_set_camera() { camera_struct::update_data(); - mat4_transpose(&camera_data->view, &rctx->view_mat); - mat4_transpose(&camera_data->projection, &rctx->proj_mat); - mat4_transpose(&camera_data->view_projection, &rctx->vp_mat); + mat4_transpose(&camera_data.view, &rctx->view_mat); + mat4_transpose(&camera_data.projection, &rctx->proj_mat); + mat4_transpose(&camera_data.view_projection, &rctx->vp_mat); vec3 view_point; - camera_data->get_view_point(view_point); + camera_data.get_view_point(view_point); rctx->set_scene_projection_view(rctx->view_mat, rctx->proj_mat, view_point); - float_t max_distance = camera_data->max_distance; - float_t min_distance = camera_data->min_distance; + float_t max_distance = camera_data.max_distance; + float_t min_distance = camera_data.min_distance; rctx->g_near_far = { (float_t)(max_distance / (max_distance - min_distance)), (float_t)(-(max_distance * min_distance) / (max_distance - min_distance)), @@ -1080,8 +1217,10 @@ HOOK(void, FASTCALL, render_manager_init_render_textures, 0x0000000140502560, in } HOOK(void, FASTCALL, render_manager_free_data, 0x0000000140502770) { + if (reflect_full_ptr) + reflect_full_ptr->free(); render_manager_free_render_textures(); - render_manager->render->free(); + render_manager.render->free(); shadow_ptr_free(); @@ -1093,8 +1232,6 @@ HOOK(void, FASTCALL, render_manager_free_render_textures, 0x00000001405027A0) { } HOOK(void, FASTCALL, render_manager_init_data, 0x0000000140502A2A, int32_t ssaa, int32_t hd_res, int32_t ss_alpha_mask, bool npr) { - rndr::RenderManager& render_manager = *::render_manager; - render_manager.reset(); render_manager.npr = npr; render_texture_counter_reset(); @@ -1119,9 +1256,25 @@ HOOK(void, FASTCALL, render_manager_init_data, 0x0000000140502A2A, int32_t ssaa, void(FASTCALL * sub_1403B6CC0)() = (void(FASTCALL*)())0x00000001403B6CC0; sub_1403B6CC0(); + extern bool config_reflect_full; + if (reflect_full_ptr && config_reflect_full) + reflect_full_ptr->init(); + glGetErrorDLL(); } +void reflect_full_init() { + if (!reflect_full_ptr) + reflect_full_ptr = new reflect_full_struct; +} + +void reflect_full_free() { + if (reflect_full_ptr) { + delete reflect_full_ptr; + reflect_full_ptr = 0; + } +} + void render_manager_patch() { WRITE_NOP_6(0x0000000140502A24); WRITE_MEMORY(0x0000000140502A3C, uint8_t, 0xC3); @@ -1132,6 +1285,66 @@ void render_manager_patch() { INSTALL_HOOK(render_manager_init_data); } +reflect_full_struct::reflect_full_struct() { + +} + +reflect_full_struct::~reflect_full_struct() { + free(); +} + +void reflect_full_struct::free() { + reflect_contour_texture.Free(); + + reflect_buffer_texture.Free(); + reflect_texture.Free(); +} + +void reflect_full_struct::init() { + rndr::Render* render = render_manager.render; + + reflect_texture.Init(render->render_width[0], render->render_height[0], + 0, GL_RGBA16F, GL_DEPTH_COMPONENT24); + reflect_buffer_texture.Init(render->render_width[0], render->render_height[0], + 0, GL_RGBA16F, GL_DEPTH_COMPONENT24); + + reflect_contour_texture.SetColorDepthTextures(reflect_texture.GetColorTex()); +} + +void get_reflect_mat() { + vec4 reflect_clip_plane; + const light_set* set = &light_set_data[LIGHT_SET_MAIN]; + const light_data* light_reflect = &set->lights[LIGHT_REFLECT]; + if (light_reflect->type == LIGHT_SPOT) { + const vec3 spot_direction = -light_reflect->spot_direction; + const vec3 position = *(vec3*)&light_reflect->position; + + float_t length = vec3::length(spot_direction); + if (length > 0.000001f) + *(vec3*)&reflect_clip_plane = spot_direction * (1.0f / length); + else + *(vec3*)&reflect_clip_plane = { 0.0f, 1.0f, 0.0f }; + + reflect_clip_plane.w = -vec3::dot(position, *(vec3*)&reflect_clip_plane); + } + else if (light_reflect->type == LIGHT_POINT) + reflect_clip_plane = { 0.0f, -1.0f, 0.0f, 9999.0f }; + else + reflect_clip_plane = { 0.0f, -1.0f, 0.0f, 0.0f }; + + const vec4 temp = 2.0f * reflect_clip_plane; + + reflect_mat = mat4_identity; + *(vec3*)&reflect_mat.row0 -= temp.x * *(vec3*)&reflect_clip_plane; + *(vec3*)&reflect_mat.row1 -= temp.y * *(vec3*)&reflect_clip_plane; + *(vec3*)&reflect_mat.row2 -= temp.z * *(vec3*)&reflect_clip_plane; + *(vec3*)&reflect_mat.row3 -= temp.w * *(vec3*)&reflect_clip_plane; + reflect_mat.row0.w = 0.0f; + reflect_mat.row1.w = 0.0f; + reflect_mat.row2.w = 0.0f; + reflect_mat.row3.w = 1.0f; +} + static void draw_pass_shadow_begin_make_shadowmap(Shadow* shad, int32_t index, int32_t a3) { shad->curr_render_textures[0]->Bind(); shad->curr_render_textures[0]->SetViewport(); @@ -1434,21 +1647,34 @@ static bool draw_pass_shadow_litproj_set_mat(bool set_mat) { } static void draw_pass_sss_contour(rndr::Render* rend) { - rend->sss_contour_texture->Bind(); - rend->sss_contour_texture->SetViewport(); + if (reflect_draw) + reflect_full_ptr->reflect_buffer_texture.Bind(); + else + rend->sss_contour_texture->Bind(); + gl_state_enable_depth_test(); gl_state_set_depth_func(GL_ALWAYS); gl_state_set_depth_mask(GL_TRUE); - gl_state_set_viewport(0, 0, rend->render_width[0], rend->render_height[0]); - gl_state_active_bind_texture_2d(0, rend->rend_texture[0].GetColorTex()); - gl_state_bind_sampler(0, rctx->render_samplers[1]); - gl_state_active_bind_texture_2d(1, rend->rend_texture[0].GetDepthTex()); - gl_state_bind_sampler(1, rctx->render_samplers[1]); + if (reflect_draw) { + RenderTexture& refl_tex = reflect_full_ptr->reflect_texture; + gl_state_set_viewport(0, 0, refl_tex.GetWidth(), refl_tex.GetHeight()); + gl_state_active_bind_texture_2d(0, refl_tex.GetColorTex()); + gl_state_bind_sampler(0, rctx->render_samplers[1]); + gl_state_active_bind_texture_2d(1, refl_tex.GetDepthTex()); + gl_state_bind_sampler(1, rctx->render_samplers[1]); + } + else { + gl_state_set_viewport(0, 0, rend->render_width[0], rend->render_height[0]); + gl_state_active_bind_texture_2d(0, rend->rend_texture[0].GetColorTex()); + gl_state_bind_sampler(0, rctx->render_samplers[1]); + gl_state_active_bind_texture_2d(1, rend->rend_texture[0].GetDepthTex()); + gl_state_bind_sampler(1, rctx->render_samplers[1]); + } gl_state_active_texture(0); - float_t v3 = 1.0f / tanf(camera_data->fov * 0.5f * DEG_TO_RAD_FLOAT); + float_t v3 = 1.0f / tanf(camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT); - vec3 direction = camera_data->interest - camera_data->view_point; + vec3 direction = camera_data.interest - camera_data.view_point; float_t length = vec3::length(direction); float_t v7 = direction.y; if (length != 0.0f) @@ -1465,10 +1691,17 @@ static void draw_pass_sss_contour(rndr::Render* rend) { shaders_ft.set(SHADER_FT_CONTOUR); rctx->contour_coef_ubo.Bind(2); - render_get()->draw_quad( - rend->render_post_width[0], rend->render_post_height[0], - rend->render_post_width_scale, rend->render_post_height_scale, - 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + + if (reflect_draw) { + RenderTexture& refl_tex = reflect_full_ptr->reflect_texture; + render_get()->draw_quad(refl_tex.GetWidth(), refl_tex.GetHeight(), 1.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + } + else + render_get()->draw_quad( + rend->render_post_width[0], rend->render_post_height[0], + rend->render_post_width_scale, rend->render_post_height_scale, + 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); } static void draw_pass_sss_filter_calc_coef(double_t step, int32_t size, double_t a3, int32_t iterations, @@ -1511,8 +1744,8 @@ static void draw_pass_sss_filter_calc_coef(double_t step, int32_t size, double_t static void draw_pass_sss_filter(sss_data* sss) { const int32_t sss_count = 6; - vec3 interest = camera_data->interest; - vec3 view_point = camera_data->view_point; + vec3 interest = camera_data.interest; + vec3 view_point = camera_data.view_point; vec3 chara_position[2]; chara_position[0] = 0.0f; @@ -1543,7 +1776,7 @@ static void draw_pass_sss_filter(sss_data* sss) { interest = chara_position[0]; float_t v29 = max_def(vec3::distance(view_point, interest), 0.25f); - float_t v31 = max_def(tanf(camera_data->fov * 0.5f * DEG_TO_RAD_FLOAT) * 5.0f, 0.25f); + float_t v31 = max_def(tanf(camera_data.fov * 0.5f * DEG_TO_RAD_FLOAT) * 5.0f, 0.25f); float_t v33 = 0.6f; float_t v34 = (float_t)(1.0 / clamp_def(v31 * v29, 0.25f, 100.0f)); if (v34 < 0.145f) @@ -1557,7 +1790,9 @@ static void draw_pass_sss_filter(sss_data* sss) { rndr::Render* rend = render_get(); uniform->arr[U_REDUCE] = 0; shaders_ft.set(SHADER_FT_REDUCE); - RenderTexture& rt = rend->rend_texture[0]; + RenderTexture& rt = reflect_draw + ? reflect_full_ptr->reflect_texture + : rend->rend_texture[0]; gl_state_bind_texture_2d(rt.GetColorTex()); gl_state_bind_sampler(0, rctx->render_samplers[0]); render_get()->draw_quad(rt.GetWidth(), rt.GetHeight(), 1.0f, 1.0f, @@ -1582,7 +1817,7 @@ static void draw_pass_sss_filter(sss_data* sss) { draw_pass_sss_filter_calc_coef(1.0, sss_count, v34, 3, a5, a6, a7, a8, shader_data.g_coef); rctx->sss_filter_gaussian_coef_ubo.WriteMemory(shader_data); - sss->textures[1].Bind(); + sss->textures[reflect_draw ? 3 : 1].Bind(); gl_state_set_viewport(0, 0, 320, 180); uniform->arr[U_SSS_FILTER] = 3; shaders_ft.set(SHADER_FT_SSS_FILT); @@ -1717,7 +1952,8 @@ static void draw_pass_3d_shadow_set(Shadow* shad) { gl_state_bind_sampler(7, 0); } -static void draw_pass_3d_translucent(mdl::ObjType opaque, mdl::ObjType transparent, mdl::ObjType translucent) { +static void draw_pass_3d_translucent(mdl::ObjType opaque, + mdl::ObjType transparent, mdl::ObjType translucent) { if (disp_manager->get_obj_count(opaque) < 1 && disp_manager->get_obj_count(transparent) < 1 && disp_manager->get_obj_count(translucent) < 1) @@ -1731,11 +1967,11 @@ static void draw_pass_3d_translucent(mdl::ObjType opaque, mdl::ObjType transpare for (int32_t i = 0; i < count; i++) { int32_t alpha = alpha_array[i]; rend->transparency_copy(); - if (render_manager->draw_pass_3d[DRAW_PASS_3D_OPAQUE] && disp_manager->get_obj_count(opaque)) + if (render_manager.draw_pass_3d[DRAW_PASS_3D_OPAQUE] && disp_manager->get_obj_count(opaque)) disp_manager->draw(opaque, 0, true, alpha); - if (render_manager->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT] && disp_manager->get_obj_count(transparent)) + if (render_manager.draw_pass_3d[DRAW_PASS_3D_TRANSPARENT] && disp_manager->get_obj_count(transparent)) disp_manager->draw(transparent, 0, true, alpha); - if (render_manager->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT] && disp_manager->get_obj_count(translucent)) { + if (render_manager.draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT] && disp_manager->get_obj_count(translucent)) { gl_state_enable_blend(); disp_manager->draw(translucent, 0, true, alpha); gl_state_disable_blend(); @@ -1780,6 +2016,167 @@ static void draw_pass_3d_translucent_has_objects(bool* arr, mdl::ObjType type) { } } +RenderTexture* reflect_tex; + +static void draw_pass_reflect_full(rndr::RenderManager& render_manager) { + gl_state_begin_event("pass_reflect"); + RenderTexture& refl_tex = reflect_full_ptr->reflect_texture; + RenderTexture& refl_buf_tex = reflect_full_ptr->reflect_buffer_texture; + refl_tex.Bind(); + extern bool reflect_full; + if (disp_manager->get_obj_count(mdl::OBJ_TYPE_REFLECT_OPAQUE) + || disp_manager->get_obj_count(mdl::OBJ_TYPE_REFLECT_TRANSPARENT) + || disp_manager->get_obj_count(mdl::OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS) + || disp_manager->get_obj_count(mdl::OBJ_TYPE_REFLECT_TRANSLUCENT)) { + reflect_tex = &refl_tex; + + refl_tex.SetViewport(); + + draw_pass_set_camera(); + if (!sss_data_get()->enable || !sss_data_get()->npr_contour) + glClearDLL(GL_DEPTH_BUFFER_BIT); + + gl_state_set_depth_func(GL_LEQUAL); + + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + light_set_data[i].data_set((light_set_id)i); + for (int32_t i = FOG_DEPTH; i < FOG_MAX; i++) + fog_data[i].data_set((fog_id)i); + + glClearColorDLL(0.0f, 0.0f, 0.0f, 0.0f); + if (disp_manager->get_obj_count(mdl::OBJ_TYPE_SSS)) + glClearDLL(GL_COLOR_BUFFER_BIT); + else + glClearDLL(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_LEQUAL); + gl_state_set_depth_mask(GL_TRUE); + gl_state_set_cull_face_mode(GL_FRONT); + + reflect_draw = true; + draw_state.shader_index = -1; + + int32_t render_width = refl_tex.GetWidth(); + int32_t render_height = refl_tex.GetHeight(); + rctx->set_scene_framebuffer_size(render_width, render_height, render_width, render_height); + + if (render_manager.shadow) + draw_pass_3d_shadow_set(render_manager.shadow_ptr); + else + draw_pass_3d_shadow_reset(); + + if (render_manager.effect_texture) + gl_state_active_bind_texture_2d(14, render_manager.effect_texture->glid); + else + gl_state_active_bind_texture_2d(14, rctx->empty_texture_2d->glid); + + uniform->arr[U_WATER_REFLECT] = 0; + + sss_data_get()->set_texture(3); + + gl_state_bind_sampler(14, rctx->render_samplers[0]); + gl_state_bind_sampler(15, rctx->render_samplers[0]); + gl_state_bind_sampler(16, rctx->render_samplers[0]); + + uniform->arr[U_STAGE_AMBIENT] = render_manager.light_stage_ambient ? 1 : 0; + + set_reflect_mat(rctx); + + if (render_manager.alpha_z_sort) { + disp_manager->obj_sort(&rctx->view_mat, + mdl::OBJ_TYPE_REFLECT_TRANSLUCENT, 1, render_manager.field_31F); + disp_manager->obj_sort(&rctx->view_mat, + mdl::OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS, 2); + } + + if (render_manager.opaque_z_sort) + disp_manager->obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_REFLECT_OPAQUE, 0); + + uniform->arr[U_REFLECT] = 1; + if (render_manager.draw_pass_3d[DRAW_PASS_3D_OPAQUE]) { + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + disp_manager->draw(mdl::OBJ_TYPE_REFLECT_OPAQUE); + gl_state_disable_depth_test(); + } + + Glitter::glt_particle_manager_x->DispScenes(Glitter::DISP_OPAQUE); + + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + if (render_manager.draw_pass_3d[DRAW_PASS_3D_TRANSPARENT]) + disp_manager->draw(mdl::OBJ_TYPE_REFLECT_TRANSPARENT); + + if (render_manager.npr_param == 1) + render_manager.pass_3d_contour(); + + gl_state_disable_depth_test(); + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + Glitter::glt_particle_manager_x->DispScenes(Glitter::DISP_ALPHA); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + /*if (npr_param == 1) { + gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + }*/ + + gl_state_enable_depth_test(); + + if (render_manager.draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT]) { + gl_state_enable_blend(); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_set_depth_mask(GL_FALSE); + disp_manager->draw(mdl::OBJ_TYPE_REFLECT_TRANSLUCENT_SORT_BY_RADIUS); + disp_manager->draw(mdl::OBJ_TYPE_REFLECT_TRANSLUCENT); + gl_state_disable_blend(); + } + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); // X + Glitter::glt_particle_manager_x->DispScenes(Glitter::DISP_TYPE_2); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + gl_state_disable_depth_test(); + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + Glitter::glt_particle_manager_x->DispScenes(Glitter::DISP_NORMAL); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + gl_state_active_bind_texture_2d(14, 0); + gl_state_active_bind_texture_2d(15, 0); + + if (render_manager.shadow) + draw_pass_3d_shadow_reset(); + + rndr::Render* render = render_manager.render; + rctx->set_scene_framebuffer_size(render->render_width[0], + render->render_height[0], render->render_width[0], render->render_height[0]); + reflect_draw = false; + + gl_state_set_cull_face_mode(GL_BACK); + gl_state_disable_depth_test(); + uniform->arr[U_REFLECT] = 0; + + for (int32_t i = render_manager.reflect_blur_num, j = 0; i > 0; i--, j++) { + blur_filter_apply(&refl_buf_tex, &refl_tex, + render_manager.reflect_blur_filter, 1.0f, 1.0f, 0.0f); + image_filter_scale(&refl_tex, refl_buf_tex.color_texture); + } + } + else { + vec4 clear_color; + glGetFloatvDLL(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); + glClearColorDLL(0.0f, 0.0f, 0.0f, 0.0f); + glClearDLL(GL_COLOR_BUFFER_BIT); + glClearColorDLL(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + } + shader::unbind(); + gl_state_bind_framebuffer(0); + gl_state_end_event(); +} + static void blur_filter_apply(RenderTexture* dst, RenderTexture* src, blur_filter_mode filter, const vec2 res_scale, const vec4 scale, const vec4 offset) { if (!dst || !src) @@ -1818,8 +2215,6 @@ static void blur_filter_apply(RenderTexture* dst, RenderTexture* src, } static void render_manager_free_render_textures() { - rndr::RenderManager& render_manager = *::render_manager; - if (render_manager.multisample_renderbuffer) { glDeleteRenderbuffers(1, &render_manager.multisample_renderbuffer); render_manager.multisample_renderbuffer = 0; @@ -1835,8 +2230,6 @@ static void render_manager_free_render_textures() { } static void render_manager_init_render_textures(int32_t multisample) { - rndr::RenderManager& render_manager = *::render_manager; - if (multisample) { glGenFramebuffers(1, &render_manager.multisample_framebuffer); glGenRenderbuffers(1, &render_manager.multisample_renderbuffer); @@ -1874,3 +2267,31 @@ static void render_manager_init_render_textures(int32_t multisample) { } gl_state_bind_framebuffer(0); } + +static void set_reflect_mat(render_context* rctx) { + const vec3 clip_plane = *(vec3*)&rctx->data.buffer_scene_data.g_clip_plane; + const vec4 temp = 2.0f * rctx->data.buffer_scene_data.g_clip_plane; + + reflect_mat = mat4_identity; + *(vec3*)&reflect_mat.row0 -= temp.x * clip_plane; + *(vec3*)&reflect_mat.row1 -= temp.y * clip_plane; + *(vec3*)&reflect_mat.row2 -= temp.z * clip_plane; + *(vec3*)&reflect_mat.row3 -= temp.w * clip_plane; + reflect_mat.row0.w = 0.0f; + reflect_mat.row1.w = 0.0f; + reflect_mat.row2.w = 0.0f; + reflect_mat.row3.w = 1.0f; + + mat4 cam_view; + mat4 cam_projection; + mat4_transpose(&camera_data.view, &cam_view); + mat4_transpose(&camera_data.projection, &cam_projection); + + mat4_mul(&reflect_mat, &cam_view, &rctx->view_mat); + rctx->proj_mat = cam_projection; + mat4_mul(&rctx->view_mat, &rctx->proj_mat, &rctx->vp_mat); + + vec3 view_point = camera_data.view_point; + mat4_transform_point(&reflect_mat, &view_point, &view_point); + rctx->set_scene_projection_view(rctx->view_mat, rctx->proj_mat, view_point); +} diff --git a/src/DivaGL/render_manager.hpp b/src/DivaGL/render_manager.hpp index 10443ad..6097c18 100644 --- a/src/DivaGL/render_manager.hpp +++ b/src/DivaGL/render_manager.hpp @@ -135,10 +135,13 @@ namespace rndr { extern RenderTexture* litproj_shadow; extern RenderTexture* litproj_texture; -extern rndr::RenderManager* render_manager; +extern rndr::RenderManager& render_manager; extern void image_filter_scale(RenderTexture* dst, texture* src, const vec4& scale = 1.0f); extern void draw_pass_set_camera(); +extern void reflect_full_init(); +extern void reflect_full_free(); + extern void render_manager_patch(); diff --git a/src/DivaGL/renderer/dof.cpp b/src/DivaGL/renderer/dof.cpp index 01b359c..57bf332 100644 --- a/src/DivaGL/renderer/dof.cpp +++ b/src/DivaGL/renderer/dof.cpp @@ -45,7 +45,6 @@ namespace renderer { if (dof_debug_data->flags & DOF_DEBUG_AUTO_FOCUS) { size_t rob_chara_smth = get_rob_chara_smth(); - size_t rob_chr = 0; for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { rob_chara* rob_chr = rob_chara_array_get(rob_chara_smth, i); if (!rob_chr || !rob_chr->is_visible()) @@ -59,24 +58,24 @@ namespace renderer { mat4_get_translation(&mat, &chara_trans); mat4 view; - mat4_transpose(&camera_data->view, &view); + mat4_transpose(&camera_data.view, &view); focus = -vec3::dot(*(vec3*)&view.row2, chara_trans) - view.row2.w - 0.1f; break; } } - focus = max_def(focus, camera_data->min_distance); + focus = max_def(focus, camera_data.min_distance); apply_physical(rt, rt->GetColorTex(), rt->GetDepthTex(), - camera_data->min_distance, camera_data->max_distance, + camera_data.min_distance, camera_data.max_distance, focus, dof_debug_data->focal_length, - camera_data->fov * DEG_TO_RAD_FLOAT, + camera_data.fov * DEG_TO_RAD_FLOAT, dof_debug_data->f_number); } else { float_t fuzzing_range = max_def(dof_debug_data->f2.fuzzing_range, 0.01f); apply_f2(rt, rt->GetColorTex(), rt->GetDepthTex(), - camera_data->min_distance, camera_data->max_distance, - camera_data->fov * DEG_TO_RAD_FLOAT, + camera_data.min_distance, camera_data.max_distance, + camera_data.fov * DEG_TO_RAD_FLOAT, dof_debug_data->f2.focus, dof_debug_data->f2.focus_range, fuzzing_range, dof_debug_data->f2.ratio); } @@ -86,8 +85,8 @@ namespace renderer { float_t fuzzing_range = max_def(dof_pv_data->f2.fuzzing_range, 0.01f); if (dof_pv_data->f2.fuzzing_range >= 0.0099999998) apply_f2(rt, rt->GetColorTex(), rt->GetDepthTex(), - camera_data->min_distance, camera_data->max_distance, - camera_data->fov * DEG_TO_RAD_FLOAT, + camera_data.min_distance, camera_data.max_distance, + camera_data.fov * DEG_TO_RAD_FLOAT, dof_pv_data->f2.focus, dof_pv_data->f2.focus_range, fuzzing_range, dof_pv_data->f2.ratio); enum_or(dof_debug_data->flags, DOF_DEBUG_ENABLE_DOF); diff --git a/src/DivaGL/rob/rob.cpp b/src/DivaGL/rob/rob.cpp index 33c7946..248d216 100644 --- a/src/DivaGL/rob/rob.cpp +++ b/src/DivaGL/rob/rob.cpp @@ -262,7 +262,7 @@ void RobOsage::SetRot(float_t rot_y, float_t rot_z) { skin_param_ptr->rot.z = rot_z * DEG_TO_RAD_FLOAT; } -void rob_chara_item_equip_object::disp(const mat4* mat) { +void rob_chara_item_equip_object::disp(const mat4& mat) { if (obj_info.is_null()) return; @@ -277,7 +277,7 @@ void rob_chara_item_equip_object::disp(const mat4* mat) { disp_manager->entry_obj_by_object_info_object_skin(obj_info, &texture_pattern, &texture_data, alpha, mats, ex_data_bone_mats.data(), 0, mat); - rob_chara_item_equip_mat = mat; + rob_chara_item_equip_mat = &mat; for (ExNodeBlock*& i : node_blocks) i->__vftable->Disp(i); @@ -615,7 +615,7 @@ static void sub_140512C20(rob_chara_item_equip* rob_itm_equip) { int32_t tex_pat_count = (int32_t)rob_itm_equip->texture_pattern.size(); if (tex_pat_count) disp_manager->set_texture_pattern(tex_pat_count, rob_itm_equip->texture_pattern.data()); - disp_manager->entry_obj_by_object_info(&mat, rob_itm_equip->field_D0); + disp_manager->entry_obj_by_object_info(mat, rob_itm_equip->field_D0); if (tex_pat_count) disp_manager->set_texture_pattern(); } @@ -633,6 +633,8 @@ static float_t sub_140512F60(rob_chara_item_equip* rob_itm_equip) { HOOK(void, FASTCALL, rob_chara_item_equip_disp, 0x0000000140512950, rob_chara_item_equip* rob_item_equip, int32_t chara_id) { + extern bool reflect_full; + mdl::obj_reflect_enable = reflect_full; mdl::ObjFlags flags = (mdl::ObjFlags)0; if (chara_reflect) @@ -668,13 +670,13 @@ HOOK(void, FASTCALL, rob_chara_item_equip_disp, 0x0000000140512950, disp_manager.set_texture_color_coefficients(rob_item_equip->texture_color_coefficients); disp_manager.set_wet_param(rob_item_equip->wet); - render_manager->field_31C |= rob_item_equip->npr_flag; + render_manager.field_31C |= rob_item_equip->npr_flag; sub_140512C20(rob_item_equip); rob_chara_age_age_array_disp(chara_id, chara_reflect, rob_item_equip->chara_color); if (rob_item_equip->item_equip_range) for (int32_t i = rob_item_equip->first_item_equip_object; i < rob_item_equip->max_item_equip_object; i++) - rob_item_equip->item_equip_object[i].disp(&rob_item_equip->mat); + rob_item_equip->item_equip_object[i].disp(rob_item_equip->mat); else { for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) { mdl::ObjFlags chara_flags = (mdl::ObjFlags)0; @@ -693,7 +695,7 @@ HOOK(void, FASTCALL, rob_chara_item_equip_disp, 0x0000000140512950, enum_and(flags, ~mdl::OBJ_SHADOW); disp_manager.set_obj_flags((mdl::ObjFlags)(chara_flags | flags | mdl::OBJ_SSS)); - rob_item_equip->item_equip_object[i].disp(&rob_item_equip->mat); + rob_item_equip->item_equip_object[i].disp(rob_item_equip->mat); } } disp_manager.set_texture_color_coefficients(temp_texture_color_coeff); @@ -701,6 +703,8 @@ HOOK(void, FASTCALL, rob_chara_item_equip_disp, 0x0000000140512950, disp_manager.set_chara_color(); disp_manager.set_obj_flags(); disp_manager.set_shadow_type(); + + mdl::obj_reflect_enable = false; } HOOK(void, FASTCALL, rob_chara_set_chara_size, 0x0000000140516810, rob_chara* rob_chr, float_t value) { @@ -956,7 +960,7 @@ void rob_chara_age_age_object::disp(size_t chara_index, static prj::vector* (FASTCALL * rob_chara_age_age_object__get_obj_set_texture)(rob_chara_age_age_object * rob_age_age_obj) = (prj::vector*(FASTCALL*)(rob_chara_age_age_object * rob_age_age_obj))0x000000014045A8E0; - disp_manager->entry_obj_by_obj(&mat4_identity, &obj, + disp_manager->entry_obj_by_obj(mat4_identity, &obj, rob_chara_age_age_object__get_obj_set_texture(this), &obj_vert_buf, &obj_index_buf, 0, 1.0f); } @@ -992,8 +996,8 @@ void rob_chara_arm_adjust_x::reset() { static void rob_chara_age_age_disp(rob_chara_age_age* arr, int32_t chara_id, bool reflect, bool chara_color) { - bool npr = !!render_manager->npr_param; - mat4& view = camera_data->view; + bool npr = !!render_manager.npr_param; + mat4& view = camera_data.view; vec3 v11 = *(vec3*)&view.row2; arr[chara_id * 3 + 0].disp(chara_id, npr, reflect, v11, chara_color); arr[chara_id * 3 + 1].disp(chara_id, npr, reflect, v11, chara_color); diff --git a/src/DivaGL/rob/rob.hpp b/src/DivaGL/rob/rob.hpp index d1a3ac7..eff2c1d 100644 --- a/src/DivaGL/rob/rob.hpp +++ b/src/DivaGL/rob/rob.hpp @@ -1368,7 +1368,7 @@ struct rob_chara_item_equip_object { obj_skin* skin; rob_chara_item_equip* item_equip; - void disp(const mat4* mat); + void disp(const mat4& mat); int32_t get_bone_index(const char* name); bone_node* get_bone_node(int32_t bone_index); bone_node* get_bone_node(const char* name); diff --git a/src/DivaGL/shadow.cpp b/src/DivaGL/shadow.cpp index ba1af5b..04435af 100644 --- a/src/DivaGL/shadow.cpp +++ b/src/DivaGL/shadow.cpp @@ -19,9 +19,9 @@ void Shadow::ctrl() { for (int32_t i = 0; i < 2; i++) shadow_enable[i] = false; - if (render_manager->shadow) { - view_mat[0] = camera_data->view; - view_mat[1] = camera_data->inv_view; + if (render_manager.shadow) { + view_mat[0] = camera_data.view; + view_mat[1] = camera_data.inv_view; light_data& light_chara = light_set_data[LIGHT_SET_MAIN].lights[LIGHT_CHARA]; diff --git a/src/DivaGL/sprite.cpp b/src/DivaGL/sprite.cpp index e6454e1..cd68dbb 100644 --- a/src/DivaGL/sprite.cpp +++ b/src/DivaGL/sprite.cpp @@ -147,11 +147,11 @@ static const GLenum spr_blend_param[6][4] = { }; namespace spr { - void(FASTCALL* put_cross)(const mat4* mat, color4u8 color_x, color4u8 color_y, color4u8 color_z) - = (void(FASTCALL*)(const mat4 * mat, color4u8 color_x, color4u8 color_y, color4u8 color_z))0x00000001404BC920; - void(FASTCALL* put_rgb_cross)(const mat4* mat) = (void(FASTCALL*)(const mat4 * mat))0x00000001404BC8F0; - spr::SprArgs* (FASTCALL* put_sprite)(const spr::SprArgs* args) - = (spr::SprArgs * (FASTCALL*)(const spr::SprArgs * args))0x0000000140640740; + void(FASTCALL* put_cross)(const mat4& mat, color4u8 color_x, color4u8 color_y, color4u8 color_z) + = (void(FASTCALL*)(const mat4 & mat, color4u8 color_x, color4u8 color_y, color4u8 color_z))0x00000001404BC920; + void(FASTCALL* put_rgb_cross)(const mat4& mat) = (void(FASTCALL*)(const mat4 & mat))0x00000001404BC8F0; + spr::SprArgs* (FASTCALL* put_sprite)(const spr::SprArgs& args) + = (spr::SprArgs * (FASTCALL*)(const spr::SprArgs & args))0x0000000140640740; SprArgs::SprArgs() : kind(), attr(), blend(), index(), layer(), prio(), resolution_mode_screen(), resolution_mode_sprite(), texture(), @@ -235,13 +235,13 @@ namespace spr { vec2 proj_sprite_3d_line(vec3 vec, bool offset) { mat4 view; - mat4_transpose(&camera_data->view, &view); + mat4_transpose(&camera_data.view, &view); mat4_transform_point(&view, &vec, &vec); if (fabsf(vec.z) < 1.0e-10f) return 0.0f; - vec2 sc_vec = camera_data->depth * *(vec2*)&vec.x * (1.0f / vec.z); + vec2 sc_vec = camera_data.depth * *(vec2*)&vec.x * (1.0f / vec.z); resolution_struct* res_wind_int = res_window_internal_get(); sc_vec.x = (float_t)res_wind_int->width * 0.5f - sc_vec.x; @@ -274,7 +274,7 @@ namespace spr { args.color = color; args.SetSpriteSize({ p2.x - p1.x, p2.y - p1.y }); enum_or(args.attr, SPR_ATTR_MATRIX); - spr::put_sprite(&args); + spr::put_sprite(args); } void put_sprite_line_list(vec2* points, size_t count, resolution_mode mode, @@ -297,7 +297,7 @@ namespace spr { } args.SetVertexArray(vertex_array.data(), vertex_array.size()); - spr::put_sprite(&args); + spr::put_sprite(args); } void put_sprite_multi(rectangle rect, resolution_mode mode, spr::SprPrio prio, color4u8 color, int32_t layer) { @@ -313,7 +313,7 @@ namespace spr { args.color = color; args.SetSpriteSize(rect.size); enum_or(args.attr, SPR_ATTR_MATRIX); - spr::put_sprite(&args); + spr::put_sprite(args); } void put_sprite_rect(rectangle rect, resolution_mode mode, spr::SprPrio prio, color4u8 color, int32_t layer) { @@ -329,7 +329,7 @@ namespace spr { args.color = color; args.SetSpriteSize(rect.size); enum_or(args.attr, SPR_ATTR_MATRIX); - spr::put_sprite(&args); + spr::put_sprite(args); } void put_sprite_triangles(SpriteVertex* vert, size_t num, resolution_mode mode, @@ -345,7 +345,7 @@ namespace spr { args.id.info = sprite_database_get_spr_by_id(spr_id)->info; enum_or(args.attr, SPR_ATTR_MATRIX); args.SetVertexArray(vert, num); - spr::put_sprite(&args); + spr::put_sprite(args); } SpriteManager::RenderData::RenderData() { @@ -481,7 +481,7 @@ namespace spr { float_t sprite_half_width = (float_t)res_window_get()->width * 0.5f; float_t sprite_half_height = (float_t)res_window_get()->height * 0.5f; - float_t aet_depth = camera_data->aet_depth; + float_t aet_depth = camera_data.aet_depth; float_t aet_depth_1 = 1.0f / aet_depth; float_t v15a = sprite_half_height * aspect[i] * 0.2f * aet_depth_1; @@ -1441,8 +1441,8 @@ void sprite_manager_set_res(double_t aspect, int32_t width, int32_t height) { void sprite_manager_set_view_projection(bool aet_3d) { view_projection_aet = aet_3d - ? camera_data->view_projection_aet_3d - : camera_data->view_projection_aet_2d; + ? camera_data.view_projection_aet_3d + : camera_data.view_projection_aet_2d; } void sprite_manager_free() { diff --git a/src/DivaGL/sprite.hpp b/src/DivaGL/sprite.hpp index b11efaf..4996738 100644 --- a/src/DivaGL/sprite.hpp +++ b/src/DivaGL/sprite.hpp @@ -263,9 +263,9 @@ namespace spr { static_assert(sizeof(spr::SprArgs) == 0xE8, "\"spr::SprArgs\" struct should have a size of 0xE8"); - extern void(FASTCALL* put_cross)(const mat4* mat, color4u8 color_x, color4u8 color_y, color4u8 color_z); - extern void(FASTCALL* put_rgb_cross)(const mat4* mat); - extern spr::SprArgs* (FASTCALL* put_sprite)(const spr::SprArgs* args); + extern void(FASTCALL* put_cross)(const mat4& mat, color4u8 color_x, color4u8 color_y, color4u8 color_z); + extern void(FASTCALL* put_rgb_cross)(const mat4& mat); + extern spr::SprArgs* (FASTCALL* put_sprite)(const spr::SprArgs& args); vec2 proj_sprite_3d_line(vec3 vec, bool offset); diff --git a/src/DivaGL/stage.cpp b/src/DivaGL/stage.cpp index 49f381a..fb0564e 100644 --- a/src/DivaGL/stage.cpp +++ b/src/DivaGL/stage.cpp @@ -4,8 +4,9 @@ */ #include "stage.hpp" +#include "../KKdLib/hash.hpp" #include "../KKdLib/mat.hpp" -#include "../KKdLib/vec.hpp" +#include "pv_game/firstread.hpp" #include "object.hpp" #include "render_context.hpp" #include @@ -80,13 +81,54 @@ static_assert(sizeof(stage) == 0x68, "\"stage\" struct should have a size of 0x6 extern size_t(FASTCALL* stage_data_handler_get_stage_data_count)() = (size_t(FASTCALL*)())0x000000014064AFC0; static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, mat4* mat); -static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere); -static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere); +static bool object_bounding_sphere_check_visibility_shadow_chara(const obj_bounding_sphere* sphere); +static bool object_bounding_sphere_check_visibility_shadow_stage(const obj_bounding_sphere* sphere); + +bool& chara_reflect = *(bool*)0x00000001411ADAFC; + +HOOK(void, FASTCALL, stage__set, 0x0000000140647710, stage* prev, stage* curr) { + originalstage__set(prev, curr); + + extern bool reflect_full; + extern const firstread* firstread_ptr; + if (reflect_full && firstread_ptr && firstread_ptr->stage_data_array && (!curr || prev != curr) && curr) { + const firstread_stage_data_array* stage_data_array = firstread_ptr->stage_data_array; + + ::stage_data* stage_data = curr->stage_data; + const uint64_t name_hash = hash_utf8_xxh3_64bits(stage_data->name); + const uint32_t num_stage_data = stage_data_array->num_stage_data; + for (uint32_t i = 0; i < num_stage_data; i++) { + if (hash_utf8_xxh3_64bits(stage_data_array->stage_data_array[i].name) != name_hash) + continue; + + const firstread_stage_data* frg_stage_data = &stage_data_array->stage_data_array[i]; + + chara_reflect = false; + render_manager.reflect_type = (stage_data_reflect_type)frg_stage_data->reflect_type; + + if (frg_stage_data->reflect) { + const stage_data_reflect* reflect = frg_stage_data->reflect; + render_manager.pass_sw[rndr::RND_PASSID_REFLECT] = true; + render_manager.reflect_blur_num = reflect->blur_num; + render_manager.reflect_blur_filter = (blur_filter_mode)reflect->blur_filter; + render_manager.reflect = true; + reflect_refract_resolution_mode mode = REFLECT_REFRACT_RESOLUTION_512x512; + if (frg_stage_data->reflect_type != STAGE_DATA_REFLECT_REFLECT_MAP) + mode = reflect->mode; + render_manager.tex_index[0] = mode; + } + break; + } + } +} HOOK(void, FASTCALL, stage__disp, 0x0000000140649560, stage* s) { if (s->state != 6 || !s->stage_display) return; + extern bool reflect_full; + mdl::obj_reflect_enable = reflect_full; + mdl::DispManager& disp_manager = *::disp_manager; mat4 mat; @@ -94,27 +136,27 @@ HOOK(void, FASTCALL, stage__disp, 0x0000000140649560, stage* s) { mat4_transpose(&mat, &mat); if (s->stage_data->object_ground.not_null() && s->ground) - disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_ground); + disp_manager.entry_obj_by_object_info(mat, s->stage_data->object_ground); if (s->stage_data->object_ring.not_null() && s->ring) - disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_ring); + disp_manager.entry_obj_by_object_info(mat, s->stage_data->object_ring); if (s->stage_data->object_reflect.not_null()) { disp_manager.set_obj_flags((mdl::ObjFlags)(mdl::OBJ_NO_TRANSLUCENCY | mdl::OBJ_REFLECT)); - disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_reflect); + disp_manager.entry_obj_by_object_info(mat, s->stage_data->object_reflect); disp_manager.set_obj_flags(); } if (s->stage_data->object_refract.not_null()) { disp_manager.set_obj_flags((mdl::ObjFlags)(mdl::OBJ_NO_TRANSLUCENCY | mdl::OBJ_REFRACT)); - disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_refract); + disp_manager.entry_obj_by_object_info(mat, s->stage_data->object_refract); disp_manager.set_obj_flags(); } if (s->stage_data->object_sky.not_null() && s->sky) { mat4 t = s->mat; mat4_mul(&mat, &t, &t); - disp_manager.entry_obj_by_object_info(&t, s->stage_data->object_sky); + disp_manager.entry_obj_by_object_info(t, s->stage_data->object_sky); } if (s->stage_data->lens_flare_texture != -1 && s->lens_flare) { @@ -131,14 +173,16 @@ HOOK(void, FASTCALL, stage__disp, 0x0000000140649560, stage* s) { rend->lens_shaft_inv_scale = s->stage_data->lens_shaft_inv_scale; } } + + mdl::obj_reflect_enable = false; } -static void stage__disp_shadow_object(object_info object, mat4* mat) { +static void stage__disp_shadow_object(object_info object, const mat4& mat) { mdl::DispManager& disp_manager = *::disp_manager; for (int32_t i = SHADOW_CHARA; i < SHADOW_MAX; i++) { disp_manager.set_shadow_type((shadow_type_enum)i); - disp_manager.set_culling_finc(i == SHADOW_CHARA + disp_manager.set_culling_func(i == SHADOW_CHARA ? object_bounding_sphere_check_visibility_shadow_chara : object_bounding_sphere_check_visibility_shadow_stage); disp_manager.set_obj_flags((mdl::ObjFlags)(mdl::OBJ_NO_TRANSLUCENCY | mdl::OBJ_SHADOW_OBJECT)); @@ -146,7 +190,7 @@ static void stage__disp_shadow_object(object_info object, mat4* mat) { } disp_manager.set_obj_flags(); - disp_manager.set_culling_finc(); + disp_manager.set_culling_func(); disp_manager.set_shadow_type(); } @@ -159,17 +203,18 @@ HOOK(void, FASTCALL, stage__disp_shadow, 0x00000001406497C0, stage* s) { mat4_rotate_y(s->rot_y, &mat); mat4_transpose(&mat, &mat); if (s->stage_data->object_shadow.not_null()) - stage__disp_shadow_object(s->stage_data->object_shadow, &mat); + stage__disp_shadow_object(s->stage_data->object_shadow, mat); } void stage_patch() { + INSTALL_HOOK(stage__set); INSTALL_HOOK(stage__disp); INSTALL_HOOK(stage__disp_shadow); } -static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, mat4* mat) { +static bool object_bounding_sphere_check_visibility_shadow(const obj_bounding_sphere* sphere, mat4* mat) { mat4 view; - mat4_transpose(&camera_data->view, &view); + mat4_transpose(&camera_data.view, &view); vec3 center; mat4_transform_point(mat, &sphere->center, ¢er); @@ -188,14 +233,14 @@ static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* return true; } -static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere) { +static bool object_bounding_sphere_check_visibility_shadow_chara(const obj_bounding_sphere* sphere) { mat4 mat; Shadow* shad = shadow_ptr_get(); mat4_look_at(&shad->view_point[0], &shad->interest[0], &mat); return object_bounding_sphere_check_visibility_shadow(sphere, &mat); } -static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere) { +static bool object_bounding_sphere_check_visibility_shadow_stage(const obj_bounding_sphere* sphere) { mat4 mat; Shadow* shad = shadow_ptr_get(); mat4_look_at(&shad->view_point[1], &shad->interest[1], &mat); diff --git a/src/DivaGL/types.hpp b/src/DivaGL/types.hpp index 2b08417..f9a704c 100644 --- a/src/DivaGL/types.hpp +++ b/src/DivaGL/types.hpp @@ -116,6 +116,11 @@ namespace prj { template class ptr_base { + public: + T* get() const { + return ptr; + } + private: T* ptr; ref_count* ref; @@ -127,6 +132,61 @@ namespace prj { template class shared_ptr : public ptr_base { public: + T* operator->() const { + return this->get(); + } + + operator bool() const { + return !!this->get(); + } + }; + + struct stack_allocator { + size_t size; + uint8_t* begin; + uint8_t* end; + uint8_t* capacity_end; + + inline void* allocate(size_t size) { + static void* (*prj__stack_allocator__allocate)(prj::stack_allocator * This, size_t size) + = (void* (*)(prj::stack_allocator * This, size_t size))0x000000014009B110; + return prj__stack_allocator__allocate(this, size); + } + + template + inline T* allocate() { + return new((T*)allocate(sizeof(T))) T; + } + + template + inline T* allocate(size_t size) { + if (!size) + return 0; + + T* arr = (T*)allocate(sizeof(T) * size); + for (size_t i = 0; i < size; i++) + new(&arr[i]) T(); + return arr; + } + + template + inline T* allocate(const T* src) { + if (!src) + return 0; + + return new((T*)allocate(sizeof(T))) T(*src); + } + + template + inline T* allocate(const T* src, size_t size) { + if (!src || !size) + return 0; + + T* dst = (T*)allocate(sizeof(T) * size); + for (size_t i = 0; i < size; i++) + new(&dst[i]) T(src[i]); + return dst; + } }; template