/* by korenkonder GitHub/GitLab: korenkonder */ #include "task_effect.hpp" #include "../KKdLib/prj/algorithm.hpp" #include "../KKdLib/hash.hpp" #include "GL/uniform_buffer.hpp" #include "auth_3d.hpp" #include "data.hpp" #include "random.hpp" #include "render_context.hpp" #include "shader_ft.hpp" #include "stage_param.hpp" #include "texture.hpp" struct for_ring_vertex_data { vec2 position; vec4 color; }; struct leaf_particle_data { vec3 position; vec3 direction; vec3 normal; vec3 rotation; vec3 rotation_add; float_t size; int32_t type; void init(); }; struct leaf_particle_scene_shader_data { vec4 g_transform[4]; vec4 g_view_pos; vec4 g_color; vec4 g_light_env_stage_diffuse; vec4 g_light_env_stage_specular; vec4 g_lit_dir; vec4 g_lit_luce; }; struct leaf_particle_vertex_data { vec3 position; vec3 normal; vec2 texcoord; }; struct particle_data { vec3 position; vec3 direction; vec3 normal; vec3 rotation; vec3 rotation_add; vec4 color; bool alive; float_t type; float_t life_time; float_t size; }; struct particle_vertex_data { vec3 position; vec3 normal; vec4 color; vec2 texcoord; }; struct rain_particle_data { vec3 position; vec2 texcoord; float_t alpha; int32_t type; int32_t field_1C; vec3 velocity; int32_t field_2C; int32_t field_30; int32_t field_34; int32_t field_38; int32_t field_3C; rain_particle_data(); }; struct rain_particle_scene_shader_data { vec4 g_view[4]; vec4 g_proj[4]; vec4 g_range_scale; vec4 g_range_offset; }; struct rain_particle_batch_shader_data { vec4 g_pos_offset; vec4 g_tangent; vec4 g_color; }; struct struc_608 { const stage_effects* stage_effects; const stage_effects_modern* stage_effects_modern; std::vector> field_10; struc_608(); struc_608(const ::stage_effects* stage_effects); struc_608(const ::stage_effects_modern* stage_effects_modern); ~struc_608(); }; struct TaskEffectParent { uint32_t current_stage_hash; // Added int32_t current_stage_index; std::vector stage_hashes; // Added std::vector stage_indices; std::vector obj_set_ids; std::vector> effects; std::vector> field_50; std::vector> field_68; int32_t state; bool enable; bool modern; // Added void* data; // Added object_database* obj_db; // Added texture_database* tex_db; // Added stage_database* stage_data; // Added TaskEffectParent(); virtual ~TaskEffectParent(); std::pair AddTaskEffectTypePtr(TaskEffectType type); int32_t CheckTaskEffectTypeLoaded(TaskEffectType type); void Event(TaskEffectType type, int32_t event_type, void* data); void Dest(); bool Load(); void Reset(); void ResetData(); void SetCurrentStageHash(uint32_t stage_hash); // Added void SetCurrentStageIndex(int32_t stage_index); void SetEnable(bool value); void SetFrame(int32_t value); void SetFrameRateControl(FrameRateControl* value); void SetStageHashes(std::vector& stage_hashes); // Added void SetStageIndices(std::vector& stage_indices); bool Unload(); }; static TaskEffect* task_effect_array_get(TaskEffectType type, const char** name); static std::string task_effect_array_get_stage_param_file_path( TaskEffectType type, int32_t stage_index, bool dev_ram, bool a4); static bool task_effect_array_parse_stage_param_data_fog_ring(stage_param_fog_ring* fog_ring, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_leaf(stage_param_leaf* leaf, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_litproj(stage_param_litproj* litproj, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_rain(stage_param_rain* rain, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_ripple(stage_param_ripple* ripple, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_snow(stage_param_snow* snow, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_splash(stage_param_splash* splash, int32_t stage_index); static bool task_effect_array_parse_stage_param_data_star(stage_param_star* star, int32_t stage_index); static void draw_fog_particle(render_context* rctx, TaskEffectFogRing::Data* data); static void leaf_particle_init(bool change_stage = false); static void leaf_particle_ctrl(); static int32_t leaf_particle_disp(); static void leaf_particle_free(); static void particle_init(vec3* offset); static void particle_ctrl(); static int32_t particle_disp(particle_vertex_data* vtx_data, particle_data* data, int32_t count); static particle_data* particle_emit(); static void particle_event(particle_event_data* event_data); static void particle_kill(particle_data* data); static void particle_free(); static void rain_particle_init(bool change_stage); static void rain_particle_ctrl(); static void rain_particle_free(); static TaskEffectAuth3D* task_effect_auth_3d; static TaskEffectLeaf* task_effect_leaf; static TaskEffectSnow* task_effect_snow; static TaskEffectRipple* task_effect_ripple; static TaskEffectRain* task_effect_rain; static TaskEffectSplash* task_effect_splash; static TaskEffectFogAnim* task_effect_fog_anim; static TaskEffectFogRing* task_effect_fog_ring; static TaskEffectParticle* task_effect_particle; static TaskEffectLitproj* task_effect_litproj; static TaskEffectStar* task_effect_star; static TaskEffectParent* task_effect_parent; static TaskEffectFogAnim::Data* task_effect_fog_anim_data; static TaskEffectFogRing::Data* task_effect_fog_ring_data; static TaskEffectSplash::Data* task_effect_splash_data; static stage_param_leaf* stage_param_data_leaf_current; static stage_param_litproj* stage_param_data_litproj_current; static stage_param_rain* stage_param_data_rain_current; static stage_param_snow* stage_param_data_snow_current; static bool stage_param_data_leaf_set; static bool stage_param_data_litproj_set; static bool stage_param_data_rain_set; static bool stage_param_data_snow_set; static bool leaf_particle_enable; static float_t leaf_particle_delta_frame; static float_t leaf_particle_emit_interval; static float_t leaf_particle_emit_timer; static int32_t leaf_particle_num_ptcls; static uint32_t leaf_particle_tex_id; static leaf_particle_data* leaf_ptcl_data; static GLuint leaf_ptcl_vao; static GLuint leaf_ptcl_vbo; static GLuint leaf_ptcl_ebo; static GL::UniformBuffer leaf_particle_scene_ubo; static const size_t leaf_ptcl_count = 0x800; static bool light_proj_enable; static render_texture litproj_shadow[2]; static render_texture litproj_texture; static bool particle_enable; static float_t particle_delta_time; static int32_t particle_index; static int32_t particle_count; static vec3 particle_wind; static particle_data* ptcl_data; static GLuint ptcl_vao; static GLuint ptcl_vbo; static const size_t ptcl_count = 0x400; static bool rain_particle_enable; static float_t rain_particle_delta_frame; static uint32_t rain_particle_tex_id; static rain_particle_data rain_ptcl_data[8]; static GLuint rain_ptcl_vao; static GLuint rain_ptcl_vbo; static GL::UniformBuffer rain_particle_scene_ubo; static GL::UniformBuffer rain_particle_batch_ubo; static const size_t rain_ptcl_count = 0x8000; static TaskEffect** task_effect_data_array[] = { (TaskEffect**)&task_effect_auth_3d, 0, (TaskEffect**)&task_effect_leaf, 0, 0/*(TaskEffect**)&task_effect_snow*/, 0, 0/*(TaskEffect**)&task_effect_ripple*/, (TaskEffect**)&task_effect_rain, 0, 0, 0, 0, 0/*(TaskEffect**)&task_effect_splash*/, 0, (TaskEffect**)&task_effect_fog_anim, 0, (TaskEffect**)&task_effect_fog_ring, 0, (TaskEffect**)&task_effect_particle, (TaskEffect**)&task_effect_litproj, 0/*(TaskEffect**)task_effect_star*/, }; static const char* task_effect_name_array[] = { "EFFECT_AUTH3D", 0, "EFFECT_LEAF", 0, "EFFECT_SNOW", 0, "EFFECT_RIPPLE", "EFFECT_RAIN", 0, 0, 0, 0, "EFFECT_SPLASH", 0, "EFFECT_FOG_ANIM", 0, "EFFECT_FOG_RING", 0, "EFFECT_PARTICLE", "EFFECT_LITPROJ", "EFFECT_STAR", }; extern render_context* rctx_ptr; extern int32_t width; extern int32_t height; TaskEffect::TaskEffect() { } TaskEffect::~TaskEffect() { } void TaskEffect::PreInit(int32_t stage_index) { } void TaskEffect::SetStageHashes(std::vector& stage_hashes, void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data) { } void TaskEffect::SetStageIndices(std::vector& stage_indices) { PreInit(stage_indices[0]); } void TaskEffect::SetFrame(int32_t value) { } void TaskEffect::Field_48() { } void TaskEffect::SetEnable(bool value) { } void TaskEffect::SetCurrentStageHash(uint32_t value) { } void TaskEffect::SetCurrentStageIndex(int32_t value) { } void TaskEffect::SetFrameRateControl(FrameRateControl* value) { } void TaskEffect::Field_68() { } void TaskEffect::Reset() { } void TaskEffect::Event(int32_t event_type, void* data) { } void TaskEffect::Field_80() { } void TaskEffect::Field_88() { } void TaskEffect::Field_90() { } void TaskEffect::Field_98(int32_t a2, int32_t* a3) { if (a3) *a3 = 0; } void TaskEffect::Field_A0(int32_t a2, int32_t* a3) { if (a3) *a3 = 0; } void TaskEffect::Field_A8(int32_t a2, int8_t* a3) { if (a3) *a3 = 0; } struc_621::struc_621() { stage_hash = hash_murmurhash_empty; stage_index = -1; } struc_621::~struc_621() { } TaskEffectAuth3D::Stage::Stage() : auth_3d_ids() { count = 0; max_count = TASK_STAGE_STAGE_COUNT; for (auth_3d_id& i : auth_3d_ids) i = -1; auth_3d_ids_ptr = auth_3d_ids; } TaskEffectAuth3D::TaskEffectAuth3D() : enable(), field_13C(), field_158(), field_159(), field_15C(), frame() { current_stage_hash = hash_murmurhash_empty; current_stage_index = -1; } TaskEffectAuth3D::~TaskEffectAuth3D() { } bool TaskEffectAuth3D::Init() { if (!task_auth_3d_check_task_ready()) { DelTask(); return true; } if (stage.count) { for (int32_t i = 0; i < stage.count; i++) if (!stage.auth_3d_ids_ptr[i].check_loaded()) return false; } for (int32_t i = 0; i < stage.count; i++) { auth_3d_id& id = stage.auth_3d_ids_ptr[i]; id.set_repeat(true); id.set_paused(false); id.set_enable(true); } //if (field_158) // sub_14036D310(2, (__int64)sub_140345B70, (__int64)this); return true; } bool TaskEffectAuth3D::Ctrl() { if (field_158 && field_15C > 0) field_15C = 0; if (field_159) { int32_t frame_int; if (frame >= 0.0f) { frame_int = (int32_t)frame; frame += get_delta_frame(); if (frame > (float_t)(512 * 360)) frame = 0.0f; } else frame_int = 0; mat4 mat; mat4_translate(0.0f, -0.2f, 0.0f, &mat); mat4_rotate_x_mult(&mat, sinf((float_t)((double_t)((float_t)(frame_int % 512) * (float_t)(2.0 / 512.0)) * M_PI)) * 0.015f, &mat); mat4_rotate_z_mult(&mat, sinf((float_t)((double_t)((float_t)(frame_int % 360) * (float_t)(2.0 / 360.0)) * M_PI)) * 0.015f, &mat); task_stage_set_mat(&mat); } return false; } bool TaskEffectAuth3D::Dest() { //if (field_158) // sub_14036D1E0(2, (__int64)sub_140345B70, (__int64)this); ResetData(); return true; } void TaskEffectAuth3D::Disp() { } void TaskEffectAuth3D::PreInit(int32_t stage_index) { } void TaskEffectAuth3D::SetStageHashes(std::vector& stage_hashes, void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data) { this->stage_hashes.assign(stage_hashes.begin(), stage_hashes.end()); stage_indices.clear(); for (uint32_t i : this->stage_hashes) { const stage_data_modern* stg_data = stage_data->get_stage_data_modern(i); if (!stg_data || !stg_data->auth_3d_ids.size()) continue; struc_621 v30; v30.stage_hash = i; if (stage.count != stage.max_count) for (uint32_t j : stg_data->auth_3d_ids) { if (stage.count == stage.max_count) break; auth_3d_id id = auth_3d_data_load_hash(j, data, obj_db, tex_db); if (!id.check_not_empty()) break; id.read_file_modern(); id.set_visibility(false); if (stage.count + 1 <= stage.max_count) stage.auth_3d_ids_ptr[stage.count++] = id; v30.auth_3d_ids.push_back(id); } field_120.push_back(v30); } } void TaskEffectAuth3D::SetStageIndices(std::vector& stage_indices) { stage_hashes.clear(); this->stage_indices.assign(stage_indices.begin(), stage_indices.end()); for (int32_t i : this->stage_indices) { data_struct* aft_data = &data_list[DATA_AFT]; auth_3d_database* aft_auth_3d_db = &aft_data->data_ft.auth_3d_db; stage_database* aft_stage_data = &aft_data->data_ft.stage_data; const stage_data* stage_data = aft_stage_data->get_stage_data(i); if (!stage_data || !stage_data->auth_3d_ids.size()) continue; struc_621 v30; v30.stage_index = i; if (stage.count != stage.max_count) for (int32_t j : stage_data->auth_3d_ids) { if (stage.count == stage.max_count) break; auth_3d_id id = auth_3d_data_load_uid(j, aft_auth_3d_db); if (!id.check_not_empty()) break; id.read_file(aft_auth_3d_db); id.set_visibility(false); if (stage.count + 1 <= stage.max_count) stage.auth_3d_ids_ptr[stage.count++] = id; v30.auth_3d_ids.push_back(id); } field_120.push_back(v30); } } void TaskEffectAuth3D::SetFrame(int32_t value) { double_t frame = (double_t)value; for (int32_t i = 0; i < stage.count; i++) { auth_3d_id& id = stage.auth_3d_ids_ptr[i]; float_t play_control_size = id.get_play_control_size(); float_t req_frame; if (play_control_size != 0.0f) req_frame = (float_t)fmod(frame, play_control_size); else req_frame = 0.0f; id.set_req_frame(req_frame); } } void TaskEffectAuth3D::SetCurrentStageHash(uint32_t value) { if (current_stage_hash == value) return; SetVisibility(false); current_stage_hash = value; SetVisibility(enable); } void TaskEffectAuth3D::SetCurrentStageIndex(int32_t value) { if (current_stage_index == value) return; SetVisibility(false); current_stage_index = value; SetVisibility(enable); } void TaskEffectAuth3D::SetFrameRateControl(FrameRateControl* value) { for (int32_t i = 0; i < stage.count; i++) { auth_3d_id& id = stage.auth_3d_ids_ptr[i]; id.set_frame_rate(value); } } void TaskEffectAuth3D::Reset() { for (int32_t i = 0; i < stage.count; i++) { auth_3d_id& id = stage.auth_3d_ids_ptr[i]; id.set_enable(true); id.set_paused(false); id.set_req_frame(0.0f); } } void TaskEffectAuth3D::ResetData() { for (int32_t i = 0; i < stage.count; i++) { auth_3d_id& id = stage.auth_3d_ids_ptr[i]; id.unload_id(rctx_ptr); } stage.count = 0; enable = false; field_120.clear(); current_stage_hash = hash_murmurhash_empty; current_stage_index = -1; stage_indices.clear(); field_158 = 0; field_159 = 0; field_15C = 0; frame = -1.0f; } void TaskEffectAuth3D::SetVisibility(bool value) { if (current_stage_index != -1) for (struc_621& i : field_120) { if (i.stage_index != current_stage_index) continue; for (auth_3d_id& j : i.auth_3d_ids) j.set_visibility(value); break; } else if (current_stage_hash != -1 && current_stage_hash != hash_murmurhash_empty && current_stage_hash != hash_murmurhash_null) for (struc_621& i : field_120) { if (i.stage_hash != current_stage_hash) continue; for (auth_3d_id& j : i.auth_3d_ids) j.set_visibility(value); break; } } void TaskEffectAuth3D::SetEnable(bool value) { enable = value; SetVisibility(value); } TaskEffectFogAnim::Data::Data() : field_0(), field_4(), field_8(), field_C(), field_18(), field_24(), field_28(), field_2C(), field_30(), field_34() { Reset(); } void TaskEffectFogAnim::Data::Ctrl() { if (!field_0 || !field_8) return; float_t v4 = field_C[0] * DEG_TO_RAD_FLOAT; float_t v7 = (sinf(v4 - 1.5f) + 1.0f) * 0.5f + field_34.x; v7 = min_def(v7, 1.0f) * field_24; float_t v8 = sinf(v4) * v7; float_t v9 = cosf(v4); vec4 color; color.x = (float_t)((v9 * v7) * 1.4022 + 1.0); color.y = (float_t)(1.0 - v8 * 0.345686 - ((double_t)v9 * v7) * 0.714486); color.z = (float_t)(v8 * 1.771 + 1.0); color.w = 1.0f; *(vec3*)&color = vec3::max(*(vec3*)&color, 0.0f); for (int32_t i = 0; i < 3; i++) { float_t v20 = (field_18[i] * 10.0f) + field_C[i]; if (v20 > 360.0) v20 = fmodf(v20, 360.0); field_C[i] = v20; } fog& fog = rctx_ptr->fog[FOG_DEPTH]; fog.set_color(color); *(vec3*)&color = (*(vec3*)&color + 1.0f) * 0.8f; light_set& light_set = rctx_ptr->light_set[LIGHT_SET_MAIN]; light_set.lights[LIGHT_CHARA].set_diffuse(color); light_set.lights[LIGHT_CHARA].set_specular(color); light_set.lights[LIGHT_STAGE].set_diffuse(color); light_set.lights[LIGHT_STAGE].set_specular(color); } void TaskEffectFogAnim::Data::Reset() { field_0 = 0; field_4 = -1; field_8 = 1; field_C[0] = 0.0f; field_C[1] = 0.0f; field_C[2] = 0.0f; field_18[0] = 0.0f; field_18[1] = 0.0f; field_18[2] = 0.0f; field_24 = 0; field_28 = 0; field_2C = 0; field_30 = 0; field_34 = 1.0f; } TaskEffectFogAnim::TaskEffectFogAnim() { } TaskEffectFogAnim::~TaskEffectFogAnim() { } bool TaskEffectFogAnim::Init() { //sub_1400DE640("TaskEffectFogAnim::init()\n"); return true; } bool TaskEffectFogAnim::Ctrl() { data.Ctrl(); return false; } bool TaskEffectFogAnim::Dest() { data.Reset(); task_effect_fog_anim_data = 0; //sub_1400DE640("TaskEffectFogAnim::dest()\n"); return true; } void TaskEffectFogAnim::Disp() { } void TaskEffectFogAnim::PreInit(int32_t) { data.Reset(); task_effect_fog_anim_data = &data; //sub_1400DE640("TaskEffectFogAnim::pre_init()\n"); } void TaskEffectFogAnim::Reset() { if (data.field_0) { data.field_C[0] = 0.0f; data.field_C[1] = 0.0f; data.field_C[2] = 0.0f; } } fog_ring_data::fog_ring_data() : size(), density() { } particle_event_data::particle_event_data() : type(), count(), size(), force() { } struc_371::struc_371() : field_0(), field_10(), field_14(), field_24() { } struc_573::struc_573() : chara_index(), bone_index() { } TaskEffectFogRing::Data::Data() : enable(), delta_frame(), field_8(), ring_size(), tex_id(), ptcl_size(), max_ptcls(), num_ptcls(), density(), density_offset(), ptcl_data(), num_vtx(), field_124(), field_2B8(), field_2B9(), disp(), frame_rate_control(), vao(), vbo() { current_stage_index = -1; } TaskEffectFogRing::Data::~Data() { } void TaskEffectFogRing::Data::CalcPtcl(float_t delta_time) { float_t delta_time1 = 1.0f * delta_time; float_t delta_time2 = 2.0f * delta_time; float_t delta_time3 = 3.0f * delta_time; fog_ring_data* ptcl_data = this->ptcl_data; float_t ring_size = (this->ring_size * 0.5f) + 1.0f; for (int32_t i = num_ptcls; i > 0; i--, ptcl_data++) { sub_140347860(ptcl_data, field_124, field_128, delta_time); float_t v12 = ptcl_data->field_18.x; float_t v13 = ptcl_data->field_18.z; float_t pos_x = ptcl_data->position.x; float_t pos_z = ptcl_data->position.z; pos_x = (v12 + ptcl_data->direction.x) * delta_time + pos_x; pos_z = (v13 + ptcl_data->direction.z) * delta_time + pos_z; if (pos_x < -ring_size) pos_x = ring_size; else if (pos_x > ring_size) pos_x = -ring_size; if (pos_z < -ring_size) pos_z = ring_size; else if (pos_z > ring_size) pos_z = -ring_size; ptcl_data->position.x = pos_x; ptcl_data->position.z = pos_z; bool v11 = false; if (fabsf(v12) > 0.00001f) { v11 = true; ptcl_data->field_18.x = v12 - v12 * delta_time3; } ptcl_data->field_18.y = 0.0f; if (fabsf(v13) > 0.00001f) { v11 = true; ptcl_data->field_18.z = v13 - v13 * delta_time3; } float_t v17; if (v11) { float_t v14 = ptcl_data->field_18.x; float_t v15 = ptcl_data->field_18.z; v17 = ptcl_data->density - v15 * v15 + v14 * v14 * delta_time1; v17 = max_def(v17, 0.0f); } else { v17 = delta_time2 + ptcl_data->density; v17 = min_def(v17, 1.0f); } ptcl_data->density = v17; } } void TaskEffectFogRing::Data::CalcVert() { float_t density = this->density; fog_ring_data* ptcl_data = this->ptcl_data; for_ring_vertex_data* ptcl_vtx_data; if (GLAD_GL_VERSION_4_5) { ptcl_vtx_data = (for_ring_vertex_data*)glMapNamedBuffer(vbo, GL_WRITE_ONLY); if (!ptcl_vtx_data) { glUnmapNamedBuffer(vbo); num_vtx = 0; return; } } else { gl_state_bind_array_buffer(vbo); ptcl_vtx_data = (for_ring_vertex_data*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (!ptcl_vtx_data) { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); num_vtx = 0; return; } } vec4 color = this->color; for (int32_t i = num_ptcls; i > 0; i--, ptcl_data++, ptcl_vtx_data += 6) { float_t size = ptcl_data->size * (float_t)((1.0 / 128.0 + 1.0 / 256.0) / 2.0); color.w = ptcl_data->density * density; vec2 position; position.x = ptcl_data->position.x * (float_t)(1.0 / 8.0); position.y = ptcl_data->position.z * (float_t)(1.0 / 8.0); ptcl_vtx_data[0].position = position + vec2(-size, size); ptcl_vtx_data[0].color = color; ptcl_vtx_data[1].position = position + vec2(-size, -size); ptcl_vtx_data[1].color = color; ptcl_vtx_data[2].position = position + vec2( size, size); ptcl_vtx_data[2].color = color; ptcl_vtx_data[3].position = position + vec2( size, -size); ptcl_vtx_data[3].color = color; ptcl_vtx_data[4].position = position + vec2( size, size); ptcl_vtx_data[4].color = color; ptcl_vtx_data[5].position = position + vec2(-size, -size); ptcl_vtx_data[5].color = color; } if (GLAD_GL_VERSION_4_5) glUnmapNamedBuffer(vbo); else { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); } num_vtx = (int32_t)(num_ptcls * 6LL); } void TaskEffectFogRing::Data::Ctrl() { if (!disp) return; CtrlInner(delta_frame * (float_t)(1.0 / 60.0)); CalcVert(); field_8 = 0; } void TaskEffectFogRing::Data::CtrlInner(float_t delta_time) { if (fabs(delta_time) <= 0.000001f) return; sub_140347B40(delta_time); CalcPtcl(delta_time); } void TaskEffectFogRing::Data::Dest() { stage_param_data_fog_ring_storage_clear(); if (ptcl_data) { free(ptcl_data); ptcl_data = 0; } if (vao) { glDeleteVertexArrays(1, &vao); vao = 0; } if (vbo) { glDeleteBuffers(1, &vbo); vbo = 0; } rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_USER, false); rctx_ptr->render_manager.clear_user(0); rctx_ptr->draw_state.set_fog_height(false); } void TaskEffectFogRing::Data::Disp() { if (enable && disp) rctx_ptr->disp_manager.entry_obj_user(&mat4_identity, (mdl::UserArgsFunc)draw_fog_particle, this, mdl::OBJ_TYPE_USER); } void TaskEffectFogRing::Data::Draw() { render_context* rctx = rctx_ptr; rctx->draw_state.set_fog_height(false); if (!enable || !disp) return; rctx->draw_state.set_fog_height(true); render_texture& rt = rctx->render_manager.get_render_texture(8); rt.bind(); glViewport(0, 0, rt.color_texture->get_width_align_mip_level(), rt.color_texture->get_height_align_mip_level()); glClearColor(density_offset, density_offset, density_offset, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_USER)) rctx->disp_manager.draw(mdl::OBJ_TYPE_USER); gl_state_bind_framebuffer(0); rctx->render_manager.set_effect_texture(rt.color_texture); glGetError(); } void TaskEffectFogRing::Data::InitParticleData() { fog_ring_data* ptcl_data = this->ptcl_data; if (!ptcl_data) return; float_t ptcl_size = this->ptcl_size; float_t ring_size = this->ring_size * 0.5f; float_t wind_dir_x = wind_dir.x; float_t wind_dir_z = wind_dir.z; for (int32_t i = max_ptcls; i > 0; i--, ptcl_data++) { float_t pos_x = PtclRandom(ring_size); float_t pos_z = PtclRandom(ring_size); float_t dir_x = PtclRandom(0.5f) + wind_dir_x; float_t dir_z = PtclRandom(0.5f) + wind_dir_z; float_t size = PtclRandom(3.0f); ptcl_data->position.x = pos_x; ptcl_data->position.y = 0.5f; ptcl_data->position.z = pos_z; ptcl_data->direction.x = dir_x; ptcl_data->direction.y = 0.0f; ptcl_data->direction.z = dir_z; ptcl_data->size = size + ptcl_size; ptcl_data->density = 1.0f; } } void TaskEffectFogRing::Data::Reset() { struc_573(*v2)[5] = field_5C; for (int32_t i = 0; i < 2; i++, v2++) { struc_573* v3 = *v2; for (int32_t j = 0; j < 5; j++, v3++) { v3->bone_index = (rob_bone_index)0; v3->position = 0.0f; } } InitParticleData(); field_8 = true; } void TaskEffectFogRing::Data::SetStageIndices(std::vector& stage_indices) { rctx_ptr->render_manager.clear_user(0); rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_USER, false); disp = false; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_fog_ring_storage_clear(); for (int32_t& i : stage_indices) { stage_param_fog_ring fog_ring; if (task_effect_array_parse_stage_param_data_fog_ring(&fog_ring, i)) { this->stage_indices.push_back(i); stage_param_data_fog_ring_storage_set_stage_data(i, &fog_ring); } } enable = true; num_vtx = 0; field_2B8 = 0; field_2B9 = 0; delta_frame = 1.0f; field_8 = true; if (!this->stage_indices.size()) return; current_stage_index = stage_indices.front(); stage_param_fog_ring* fog_ring = stage_param_data_fog_ring_storage_get_value(current_stage_index); if (!fog_ring) return; disp = true; enable = true; ring_size = fog_ring->ring_size; wind_dir = fog_ring->wind_dir; tex_id = -1; if (fog_ring->tex_name.size()) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; tex_id = aft_tex_db->get_texture_id(fog_ring->tex_name.c_str()); } color = fog_ring->color; ptcl_size = fog_ring->ptcl_size; max_ptcls = 2000; num_ptcls = fog_ring->num_ptcls; density = fog_ring->density; density_offset = fog_ring->density_offset; struc_573(*v2)[5] = field_5C; for (int32_t i = 0; i < 2; i++, v2++) { struc_573* v3 = *v2; v3[0].chara_index = i; v3[0].bone_index = ROB_BONE_KL_TOE_R_WJ; v3[0].position = 0.0f; v3[1].chara_index = i; v3[1].bone_index = ROB_BONE_KL_TOE_L_WJ; v3[1].position = 0.0f; v3[2].chara_index = i; v3[2].bone_index = ROB_BONE_KL_TE_L_WJ; v3[2].position = 0.0f; v3[3].chara_index = i; v3[3].bone_index = ROB_BONE_KL_TE_R_WJ; v3[3].position = 0.0f; v3[4].chara_index = i; v3[4].bone_index = ROB_BONE_N_HARA_CP; v3[4].position = 0.0f; } const size_t max_ptcls_vtx_count = (size_t)max_ptcls * 0x06; ptcl_data = force_malloc_s(fog_ring_data, max_ptcls); ptcl_data = new (ptcl_data) fog_ring_data[max_ptcls]; if (!vao) glGenVertexArrays(1, &vao); if (!vbo) glGenBuffers(1, &vbo); static const GLsizei buffer_size = sizeof(for_ring_vertex_data); gl_state_bind_array_buffer(vbo, true); if (GLAD_GL_VERSION_4_4) glBufferStorage(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * max_ptcls_vtx_count), 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); else glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * max_ptcls_vtx_count), 0, GL_DYNAMIC_DRAW); gl_state_bind_vertex_array(vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(for_ring_vertex_data, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(for_ring_vertex_data, color)); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); InitParticleData(); rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_USER, true); rctx_ptr->render_manager.add_user(0, TaskEffectFogRing::Data::DrawStatic, this); } void TaskEffectFogRing::Data::DrawStatic(void* data) { ((TaskEffectFogRing::Data*)data)->Draw(); } float_t TaskEffectFogRing::Data::PtclRandom(float_t value) { return (float_t)(rand_state_array_get_int(4) % 10000) * 0.0001f * value * 2.0f - value; } void TaskEffectFogRing::Data::sub_140347B40(float_t delta_time) { field_124 = 0; struc_573(*v5)[5] = field_5C; for (int32_t i = 0; i < 2; i++, v5++) { if (!rob_chara_array_get(i) || !rob_chara_array_check_visibility(i)) continue; rob_chara_bone_data* rob_bone_data = rob_chara_array_get_bone_data(i); if (!rob_bone_data) continue; struc_573* v8 = *v5; for (int32_t j = 0; j < 5; j++, v8++) { mat4* mat = rob_chara_bone_data_get_mats_mat(rob_bone_data, v8->bone_index); if (!mat) continue; vec3 trans; mat4_get_translation(mat, &trans); v8->position = trans; if (field_124 >= 10 || field_8) continue; vec3 v23 = (trans - v8->position) * (1.0f / delta_time); float_t v15 = vec3::length(v23); float_t v18; float_t v19; if (v8->bone_index) { if (v15 < 10.0f) continue; if (v15 != 0.0f) v23 *= 1.0f / v15; v18 = 0.01f * v15; v19 = 0.5f; } else { if (trans.y >= 0.2f || v8->position.y < 0.2f) continue; v18 = 2.2f; v19 = 2.0f; v23 = { 0.0f, 1.0f, 0.0f }; } struc_371& v20 = field_128[field_124++]; v20.field_0 = 1; v20.position = trans; v20.field_10 = v19; v20.field_14 = v19 * v19; v20.direction = v23; v20.field_24 = v18; } } } void TaskEffectFogRing::Data::sub_140347860(fog_ring_data* a1, int32_t a2, struc_371* a3, float_t delta_time) { if (a2 <= 0) return; for (int32_t i = a2; i > 0; i--, a3++) { vec3 v32 = a1->position - a3->position; float_t v9 = a3->field_10 + 1.0f; float_t v10 = vec3::length_squared(v32); if (v10 > v9 * v9) continue; vec3 v33 = 0.0f; switch (a3->field_0) { case 0: v33 = a3->direction * a3->field_24; break; case 1: if (v10 < 0.00001f) v33 = vec3(1.0f, 0.0f, 1.0f) * a3->field_24; else { float_t v16 = sqrtf(v10); if (v16 != 0.0f) v32 *= 1.0f / v16; float_t v17 = 1.0f / v10; v17 = min_def(v17, 1.0f); v33 = v32 * (v17 * a3->field_24); } break; } a1->field_18 = (v33 * delta_time * 60.0f) + a1->field_18; } } TaskEffectFogRing::TaskEffectFogRing() { } TaskEffectFogRing::~TaskEffectFogRing() { } bool TaskEffectFogRing::Init() { //sub_1400DE640("TaskEffectFogRing::init()\n"); return true; } bool TaskEffectFogRing::Ctrl() { data.delta_frame = data.frame_rate_control->GetDeltaFrame(); data.Ctrl(); return 0; } bool TaskEffectFogRing::Dest() { data.Dest(); task_effect_fog_ring_data = 0; //sub_1400DE640("TaskEffectFogRing::dest()\n"); return 1; } void TaskEffectFogRing::Disp() { data.Disp(); } void TaskEffectFogRing::PreInit(int32_t stage_index) { } void TaskEffectFogRing::SetStageIndices(std::vector& stage_indices) { SetFrameRateControl(0); task_effect_fog_ring_data = &data; data.SetStageIndices(stage_indices); } void TaskEffectFogRing::SetEnable(bool value) { data.enable = value; } void TaskEffectFogRing::SetCurrentStageIndex(int32_t value) { if (data.current_stage_index == value) return; data.current_stage_index = value; if (value != -1) { for (int32_t& i : data.stage_indices) if (i == value) { data.disp = true; return; } } data.disp = false; } void TaskEffectFogRing::SetFrameRateControl(FrameRateControl* value) { if (value) data.frame_rate_control = value; else data.frame_rate_control = &sys_frame_rate; } void TaskEffectFogRing::Reset() { //sub_140349C30(&this->data); } TaskEffectLeaf::TaskEffectLeaf() : frame_rate_control(), wait_frames() { current_stage_index = -1; } TaskEffectLeaf:: ~TaskEffectLeaf() { } bool TaskEffectLeaf::Init() { if (stage_param_data_leaf_current) { leaf_particle_init(); wait_frames = 2; SetFrameRateControl(0); } return true; } bool TaskEffectLeaf::Ctrl() { if (!stage_param_data_leaf_current) return false; leaf_particle_delta_frame = frame_rate_control->GetDeltaFrame(); if (wait_frames > 0) { wait_frames--; return false; } leaf_particle_ctrl(); return false; } bool TaskEffectLeaf::Dest() { leaf_particle_free(); stage_param_data_leaf_current = 0; stage_param_data_leaf_storage_clear(); return true; } void TaskEffectLeaf::Disp() { } void TaskEffectLeaf::PreInit(int32_t stage_index) { } void TaskEffectLeaf::SetStageIndices(std::vector& stage_indices) { if (stage_param_data_leaf_current) Dest(); stage_param_data_leaf_set = 0; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_leaf_storage_clear(); for (int32_t i : stage_indices) { stage_param_leaf leaf; if (task_effect_array_parse_stage_param_data_leaf(&leaf, i)) { this->stage_indices.push_back(i); stage_param_data_leaf_storage_set_stage_data(i, &leaf); } } stage_param_data_leaf_current = 0; if (!this->stage_indices.size()) return; int32_t stage_index = stage_indices.front(); current_stage_index = stage_index; stage_param_leaf* leaf = stage_param_data_leaf_storage_get_value(stage_index); leaf_particle_tex_id = -1; if (leaf->tex_name.size()) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; leaf_particle_tex_id = aft_tex_db->get_texture_id(leaf->tex_name.c_str()); } if (leaf) { stage_param_data_leaf_current = leaf; stage_param_data_leaf_set = true; } } void TaskEffectLeaf::SetEnable(bool value) { leaf_particle_enable = value; } void TaskEffectLeaf::SetCurrentStageIndex(int32_t value) { if (current_stage_index == value) return; current_stage_index = value; bool found = false; for (int32_t i : stage_indices) if (i == value) { found = true; break; } stage_param_data_leaf_set = found; if (found) { stage_param_data_leaf_current = stage_param_data_leaf_storage_get_value(value); leaf_particle_init(true); wait_frames = 2; } } void TaskEffectLeaf::SetFrameRateControl(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = &sys_frame_rate; } void TaskEffectLeaf::Reset() { if (stage_param_data_leaf_current) { leaf_particle_init(); wait_frames = 2; } } TaskEffectLitproj::TaskEffectLitproj() : wait_frames(), frame() { current_stage_index = -1; } TaskEffectLitproj:: ~TaskEffectLitproj() { } bool TaskEffectLitproj::Init() { if (stage_param_data_litproj_current) { wait_frames = 10; frame = 0; } return true; } bool TaskEffectLitproj::Ctrl() { if (!stage_param_data_litproj_current) return false; light_data& light = rctx_ptr->light_set[LIGHT_SET_MAIN].lights[LIGHT_PROJECTION]; if (light.get_type() != LIGHT_SPOT) return false; if (wait_frames > 0) { if (!--wait_frames) { light.get_diffuse(diffuse); light.get_specular(specular); } return false; } if (--frame < 0) frame = 255; float_t v8 = (float_t)((float_t)frame * M_PI * (1.0 / 128.0)); float_t v11 = (sinf(sinf(v8 * 27.0f) + v8 * 2.0f) + sinf(v8 * 23.0f)) * 0.5f; light.set_diffuse({ diffuse.x, diffuse.y * (v11 * 0.1f + 1.0f), diffuse.z, diffuse.w }); light.set_specular({ specular.x, specular.y * (v11 * 0.15f + 1.0f), specular.z, specular.w }); return false; } bool TaskEffectLitproj::Dest() { delete rctx_ptr->litproj; rctx_ptr->litproj = 0; stage_param_data_litproj_current = 0; stage_param_data_litproj_storage_clear(); return true; } void TaskEffectLitproj::Disp() { } void TaskEffectLitproj::PreInit(int32_t stage_index) { } void TaskEffectLitproj::SetStageIndices(std::vector& stage_indices) { if (stage_param_data_litproj_current) Dest(); stage_param_data_litproj_set = 0; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_litproj_storage_clear(); for (int32_t i : stage_indices) { stage_param_litproj litproj; if (task_effect_array_parse_stage_param_data_litproj(&litproj, i)) { this->stage_indices.push_back(i); stage_param_data_litproj_storage_set_stage_data(i, &litproj); } } stage_param_data_litproj_current = 0; if (stage_indices.size()) { int32_t stage_index = stage_indices.front(); current_stage_index = stage_index; stage_param_litproj* litproj = stage_param_data_litproj_storage_get_value(stage_index); if (litproj) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; stage_param_data_litproj_current = litproj; light_proj* _litproj = new light_proj(width, height); rctx_ptr->litproj = _litproj; _litproj->enable = true; _litproj->texture_id = aft_tex_db->get_texture_id(litproj->tex_name.c_str()); stage_param_data_litproj_set = true; } } } void TaskEffectLitproj::SetEnable(bool value) { light_proj_enable = value; } void TaskEffectLitproj::SetCurrentStageIndex(int32_t value) { if (current_stage_index == value) return; current_stage_index = value; bool found = false; for (int32_t i : stage_indices) if (i == value) { found = true; break; } stage_param_data_litproj_set = found; if (found) stage_param_data_litproj_current = stage_param_data_litproj_storage_get_value(value); } void TaskEffectLitproj::Reset() { wait_frames = 10; frame = 0; } TaskEffectParticle::TaskEffectParticle() : frame_rate_control() { current_stage_index = -1; } TaskEffectParticle::~TaskEffectParticle() { } bool TaskEffectParticle::Init() { SetFrameRateControl(0); return true; } bool TaskEffectParticle::Ctrl() { particle_delta_time = frame_rate_control->GetDeltaFrame() * (float_t)(1.0 / 60.0); if (particle_count > 0) particle_ctrl(); return false; } bool TaskEffectParticle::Dest() { particle_free(); return true; } void TaskEffectParticle::Disp() { } void TaskEffectParticle::PreInit(int32_t stage_index) { current_stage_index = stage_index; //field_74 = 0; particle_init(0); } void TaskEffectParticle::SetEnable(bool value) { particle_enable = value; } void TaskEffectParticle::SetFrameRateControl(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = &sys_frame_rate; } void TaskEffectParticle::Reset() { if (ptcl_data) particle_init(0); } void TaskEffectParticle::Event(int32_t event_type, void* data) { if (event_type == 1) particle_event((particle_event_data*)data); } TaskEffectRain::TaskEffectRain() : frame_rate_control() { current_stage_index = -1; } TaskEffectRain:: ~TaskEffectRain() { } bool TaskEffectRain::Init() { if (stage_param_data_rain_current) { rain_particle_init(0); SetFrameRateControl(0); } return true; } bool TaskEffectRain::Ctrl() { if (stage_param_data_rain_current) { rain_particle_delta_frame = frame_rate_control->GetDeltaFrame(); rain_particle_ctrl(); } return false; } bool TaskEffectRain::Dest() { if (stage_param_data_rain_current) { rain_particle_free(); rain_ptcl_vbo = 0; stage_param_data_rain_current = 0; stage_param_data_rain_storage_clear(); stage_param_data_rain_set = false; current_stage_index = -1; stage_indices.clear(); } return true; } void TaskEffectRain::Disp() { } void TaskEffectRain::PreInit(int32_t stage_index) { } void TaskEffectRain::SetStageIndices(std::vector& stage_indices) { if (stage_param_data_rain_current) Dest(); stage_param_data_rain_set = false; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_rain_storage_clear(); for (int32_t i : stage_indices) { stage_param_rain rain; if (task_effect_array_parse_stage_param_data_rain(&rain, i)) { this->stage_indices.push_back(i); stage_param_data_rain_storage_set_stage_data(i, &rain); } } stage_param_data_rain_current = 0; if (!this->stage_indices.size()) return; int32_t stage_index = stage_indices.front(); current_stage_index = stage_index; stage_param_rain* rain = stage_param_data_rain_storage_get_value(stage_index); rain_particle_tex_id = -1; if (rain->tex_name.size()) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; rain_particle_tex_id = aft_tex_db->get_texture_id(rain->tex_name.c_str()); } if (rain) { stage_param_data_rain_current = rain; stage_param_data_rain_set = true; } } void TaskEffectRain::SetEnable(bool value) { rain_particle_enable = value; } void TaskEffectRain::SetCurrentStageIndex(int32_t value) { if (current_stage_index == value) return; current_stage_index = value; bool found = false; for (int32_t i : stage_indices) if (i == value) { found = true; break; } stage_param_data_rain_set = found; if (found) stage_param_data_rain_current = stage_param_data_rain_storage_get_value(value); } void TaskEffectRain::SetFrameRateControl(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = &sys_frame_rate; } void TaskEffectRain::Reset() { if (stage_param_data_rain_current) { rain_particle_init(0); SetFrameRateControl(0); } } TaskEffectRipple::TaskEffectRipple() : field_68(), frame_rate_control(), emit() { } TaskEffectRipple::~TaskEffectRipple() { } bool TaskEffectRipple::Init() { //sub_1400DE640("TaskEffectRipple::init()\n"); return true; } bool TaskEffectRipple::Ctrl() { emit->delta_frame = frame_rate_control->GetDeltaFrame(); //emit->ctrl(); return false; } bool TaskEffectRipple::Dest() { //emit->dest(); //sub_1400DE640("TaskEffectRipple::dest()\n"); return true; } void TaskEffectRipple::Disp() { //emit->disp(); } void TaskEffectRipple::PreInit(int32_t stage_index) { } void TaskEffectRipple::SetStageIndices(std::vector& stage_indices) { SetFrameRateControl(0); //emit = &ripple_emit_data; //emit->set_stage_indices(stage_indices); } void TaskEffectRipple::SetCurrentStageIndex(int32_t value) { //emit->set_stage_index(value); } void TaskEffectRipple::SetFrameRateControl(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = &sys_frame_rate; } void TaskEffectRipple::Reset() { //if (!emit->use_float_ripplemap) // ripple_emit_data.clear_tex(); //ripple_emit_data.reset(); } void leaf_particle_draw() { if (!stage_param_data_leaf_current || !leaf_ptcl_data || !leaf_particle_enable || !stage_param_data_leaf_set) return; texture* tex = texture_storage_get_texture(leaf_particle_tex_id); if (!tex) return; int32_t count = leaf_particle_disp(); if (!count) return; const light_data& light_stage = rctx_ptr->light_set[LIGHT_SET_MAIN].lights[LIGHT_STAGE]; leaf_particle_scene_shader_data shader_data = {}; mat4 temp; mat4_transpose(&rctx_ptr->vp_mat, &temp); shader_data.g_transform[0] = temp.row0; shader_data.g_transform[1] = temp.row1; shader_data.g_transform[2] = temp.row2; shader_data.g_transform[3] = temp.row3; rctx_ptr->camera->get_view_point(shader_data.g_view_pos); shader_data.g_color = stage_param_data_leaf_current->color; light_stage.get_diffuse(shader_data.g_light_env_stage_diffuse); light_stage.get_specular(shader_data.g_light_env_stage_specular); shader_data.g_lit_dir = rctx_ptr->obj_scene.g_light_chara_dir; shader_data.g_lit_luce = rctx_ptr->obj_scene.g_light_chara_luce; leaf_particle_scene_ubo.WriteMapMemory(shader_data); gl_state_active_bind_texture_2d(0, tex->tex); shaders_ft.set(SHADER_FT_LEAF_PT); leaf_particle_scene_ubo.Bind(0); gl_state_bind_vertex_array(leaf_ptcl_vao); shaders_ft.draw_elements(GL_TRIANGLES, count / 4 * 6, GL_UNSIGNED_INT, 0); gl_state_bind_vertex_array(0); } void rain_particle_draw() { if (!stage_param_data_rain_current || !rain_particle_enable || !stage_param_data_rain_set) return; texture* tex = texture_storage_get_texture(rain_particle_tex_id); if (!tex) return; stage_param_rain* rain = stage_param_data_rain_current; vec3 range = rain->range; vec3 range_scale = rain->range; vec3 range_offset = rain->offset; range_offset.x -= range.x * 0.5f; range_offset.z -= range.z * 0.5f; rain_particle_scene_shader_data scene_shader_data = {}; mat4 temp; mat4_transpose(&rctx_ptr->view_mat, &temp); scene_shader_data.g_view[0] = temp.row0; scene_shader_data.g_view[1] = temp.row1; scene_shader_data.g_view[2] = temp.row2; scene_shader_data.g_view[3] = temp.row3; mat4_transpose(&rctx_ptr->proj_mat, &temp); scene_shader_data.g_proj[0] = temp.row0; scene_shader_data.g_proj[1] = temp.row1; scene_shader_data.g_proj[2] = temp.row2; scene_shader_data.g_proj[3] = temp.row3; scene_shader_data.g_range_scale = { range_scale.x, range_scale.y, range_scale.z, 0.0f }; scene_shader_data.g_range_offset = { range_offset.x, range_offset.y, range_offset.z, 0.0f }; rain_particle_scene_ubo.WriteMapMemory(scene_shader_data); gl_state_enable_blend(); gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); gl_state_enable_depth_test(); gl_state_set_depth_mask(GL_FALSE); gl_state_active_bind_texture_2d(0, tex->tex); shaders_ft.set(SHADER_FT_RAIN); float_t tangent_sign = -rain->psize.x; float_t tangent_size = -rain->psize.y * (float_t)(1.0 / 30.0); int32_t first = 0; int32_t count = min_def(rain->num_rain, rain_ptcl_count) / 2 / 4 * 6; vec4 color = rain->color; float_t color_a = color.w; gl_state_bind_vertex_array(rain_ptcl_vao); rain_particle_scene_ubo.Bind(0); rain_particle_batch_ubo.Bind(1); for (int32_t i = 0; i < 8; i++, first += count) { rain_particle_data& data = rain_ptcl_data[i]; vec3 pos_offset = data.position / range; vec3 tangent = data.velocity * tangent_size; color.w = color_a * data.alpha; rain_particle_batch_shader_data batch_shader_data = {}; batch_shader_data.g_pos_offset = { pos_offset.x, pos_offset.y, pos_offset.z, 0.075f }; batch_shader_data.g_tangent = { tangent.x, tangent.y, tangent.z, tangent_sign }; batch_shader_data.g_color = color; rain_particle_batch_ubo.WriteMapMemory(batch_shader_data); shaders_ft.draw_arrays(GL_TRIANGLES, first, count); } gl_state_bind_vertex_array(0); gl_state_active_bind_texture_2d(0, 0); gl_state_set_depth_mask(GL_TRUE); gl_state_disable_blend(); } void particle_draw() { if (!ptcl_data) return; particle_vertex_data* vtx_data; if (GLAD_GL_VERSION_4_5) { vtx_data = (particle_vertex_data*)glMapNamedBuffer(ptcl_vbo, GL_WRITE_ONLY); if (!vtx_data) { glUnmapNamedBuffer(ptcl_vbo); return; } } else { gl_state_bind_array_buffer(ptcl_vbo); vtx_data = (particle_vertex_data*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (!vtx_data) { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); return; } } int32_t count = particle_disp(vtx_data, ptcl_data, ptcl_count); if (GLAD_GL_VERSION_4_5) glUnmapNamedBuffer(ptcl_vbo); else { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); } if (!count) return; gl_state_bind_vertex_array(ptcl_vao); shaders_ft.set(SHADER_FT_PARTICL); shaders_ft.draw_arrays(GL_TRIANGLES, 0, count); gl_state_bind_vertex_array(0); } void snow_particle_draw() { } void task_effect_init() { if (!task_effect_auth_3d) task_effect_auth_3d = new TaskEffectAuth3D; if (!task_effect_leaf) task_effect_leaf = new TaskEffectLeaf; /*if (!task_effect_snow) task_effect_snow = new TaskEffectSnow;*/ if (!task_effect_ripple) task_effect_ripple = new TaskEffectRipple; if (!task_effect_rain) task_effect_rain = new TaskEffectRain; /*if (!task_effect_splash) task_effect_splash = new TaskEffectSplash;*/ if (!task_effect_fog_anim) task_effect_fog_anim = new TaskEffectFogAnim; if (!task_effect_fog_ring) task_effect_fog_ring = new TaskEffectFogRing; if (!task_effect_particle) task_effect_particle = new TaskEffectParticle; if (!task_effect_litproj) task_effect_litproj = new TaskEffectLitproj; /*if (!task_effect_star) task_effect_star = new TaskEffectStar;*/ if (!task_effect_parent) task_effect_parent = new TaskEffectParent; task_effect_fog_anim_data = 0; task_effect_fog_ring_data = 0; task_effect_splash_data = 0; } void task_effect_free() { if (task_effect_auth_3d) { delete task_effect_auth_3d; task_effect_auth_3d = 0; } if (task_effect_leaf) { delete task_effect_leaf; task_effect_leaf = 0; } /*if (task_effect_snow) { delete task_effect_snow; task_effect_snow = 0; }*/ if (task_effect_ripple) { delete task_effect_ripple; task_effect_ripple = 0; } if (task_effect_rain) { delete task_effect_rain; task_effect_rain = 0; } /*if (task_effect_splash) { delete task_effect_splash; task_effect_splash = 0; }*/ if (task_effect_fog_anim) { delete task_effect_fog_anim; task_effect_fog_anim = 0; } if (task_effect_fog_ring) { delete task_effect_fog_ring; task_effect_fog_ring = 0; } if (task_effect_particle) { delete task_effect_particle; task_effect_particle = 0; } if (task_effect_litproj) { delete task_effect_litproj; task_effect_litproj = 0; } /*if (task_effect_star) { delete task_effect_star; task_effect_star = 0; }*/ if (task_effect_parent) { delete task_effect_parent; task_effect_parent = 0; } task_effect_fog_anim_data = 0; task_effect_fog_ring_data = 0; task_effect_splash_data = 0; } void task_effect_parent_event(TaskEffectType type, int32_t event_type, void* data) { task_effect_parent->Event(type, event_type, data); } void task_effect_parent_dest() { task_effect_parent->Dest(); } bool task_effect_parent_load() { return task_effect_parent->Load(); } void task_effect_parent_reset() { task_effect_parent->Reset(); } void task_effect_parent_set_current_stage_hash(uint32_t stage_hash) { task_effect_parent->SetCurrentStageHash(stage_hash); } void task_effect_parent_set_current_stage_index(int32_t stage_index) { task_effect_parent->SetCurrentStageIndex(stage_index); } void task_effect_parent_set_data(void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data) { task_effect_parent->data = data; task_effect_parent->obj_db = obj_db; task_effect_parent->tex_db = tex_db; task_effect_parent->stage_data = stage_data; } void task_effect_parent_set_enable(bool value) { task_effect_parent->SetEnable(value); } void task_effect_parent_set_frame(int32_t value) { task_effect_parent->SetFrame(value); } void task_effect_parent_set_frame_rate_control(FrameRateControl* value) { task_effect_parent->SetFrameRateControl(value); } void task_effect_parent_set_stage_hashes(std::vector& stage_hashes) { task_effect_parent->SetStageHashes(stage_hashes); } void task_effect_parent_set_stage_indices(std::vector&stage_indices) { task_effect_parent->SetStageIndices(stage_indices); } bool task_effect_parent_unload() { return task_effect_parent->Unload(); } static TaskEffect* task_effect_array_get(TaskEffectType type, const char** name) { if (type < TASK_EFFECT_AUTH_3D || type > TASK_EFFECT_STAR) { if (name) *name = 0; return 0; } if (name) *name = task_effect_name_array[type]; TaskEffect** task = task_effect_data_array[type]; if (task) return *task; return 0; } static std::string task_effect_array_get_stage_param_file_path( TaskEffectType type, int32_t stage_index, bool dev_ram, bool a4) { std::string path; if (dev_ram) path.assign("dev_ram/stage_param/"); else path.assign("rom/stage_param/"); if (a4) return path; data_struct* aft_data = &data_list[DATA_AFT]; stage_database* aft_stage_data = &aft_data->data_ft.stage_data; const char* stage_name = aft_stage_data->get_stage_name(stage_index); if (!stage_name) return {}; const char* effect_name = 0; task_effect_array_get(type, &effect_name); if (!effect_name) return {}; size_t path_len = path.size(); path.append(effect_name + 7); path.append("_"); path.append(stage_name + 3); size_t path_len_new = path.size(); for (size_t i = path_len; i < path_len_new; i++) { char c = path[i]; if (c >= 'A' && c <= 'Z') path[i] = c + 0x20; } path.append(".txt"); return path; } static bool task_effect_array_parse_stage_param_data_fog_ring(stage_param_fog_ring* fog_ring, int32_t stage_index) { if (!fog_ring) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_FOG_RING, stage_index, 0, 0); data_list[DATA_AFT].load_file(fog_ring, path.c_str(), stage_param_fog_ring::load_file); return fog_ring->ready; } static bool task_effect_array_parse_stage_param_data_leaf(stage_param_leaf* leaf, int32_t stage_index) { if (!leaf) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_LEAF, stage_index, 0, 0); data_list[DATA_AFT].load_file(leaf, path.c_str(), stage_param_leaf::load_file); return leaf->ready; } static bool task_effect_array_parse_stage_param_data_litproj(stage_param_litproj* litproj, int32_t stage_index) { if (!litproj) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_LITPROJ, stage_index, 0, 0); data_list[DATA_AFT].load_file(litproj, path.c_str(), stage_param_litproj::load_file); return litproj->ready; } static bool task_effect_array_parse_stage_param_data_rain(stage_param_rain* rain, int32_t stage_index) { if (!rain) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_RAIN, stage_index, 0, 0); data_list[DATA_AFT].load_file(rain, path.c_str(), stage_param_rain::load_file); return rain->ready; } static bool task_effect_array_parse_stage_param_data_ripple(stage_param_ripple* ripple, int32_t stage_index) { if (!ripple) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_RIPPLE, stage_index, 0, 0); data_list[DATA_AFT].load_file(ripple, path.c_str(), stage_param_ripple::load_file); return ripple->ready; } static bool task_effect_array_parse_stage_param_data_snow(stage_param_snow* snow, int32_t stage_index) { if (!snow) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_SNOW, stage_index, 0, 0); data_list[DATA_AFT].load_file(snow, path.c_str(), stage_param_snow::load_file); return snow->ready; } static bool task_effect_array_parse_stage_param_data_splash(stage_param_splash* splash, int32_t stage_index) { if (!splash) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_SPLASH, stage_index, 0, 0); data_list[DATA_AFT].load_file(splash, path.c_str(), stage_param_splash::load_file); return splash->ready; } static bool task_effect_array_parse_stage_param_data_star(stage_param_star* star, int32_t stage_index) { if (!star) return false; std::string path = task_effect_array_get_stage_param_file_path(TASK_EFFECT_STAR, stage_index, 0, 0); data_list[DATA_AFT].load_file(star, path.c_str(), stage_param_star::load_file); return star->ready; } void leaf_particle_data::init() { stage_param_leaf* leaf = stage_param_data_leaf_current; position.x = rand_state_array_get_float(4); position.y = rand_state_array_get_float(4); position.z = rand_state_array_get_float(4); position = (position * vec3(2.0f, 1.0f, 2.0f) - vec3(1.0f, 0.0f, 1.0f)) * leaf->range + leaf->offset; direction = vec3(0.0f, -1.0f, 0.0f); normal = vec3(0.0f, 0.0f, 1.0f); rotation.x = rand_state_array_get_float(4) * 6.28f; rotation.y = rand_state_array_get_float(4) * 6.28f; rotation.z = 0.0f; rotation_add.x = (rand_state_array_get_float(4) + 1.0f) * 3.0f; rotation_add.y = (rand_state_array_get_float(4) + 1.0f) * 3.0f; rotation_add.z = 10.0f; type = 1; size = leaf->psize; } rain_particle_data::rain_particle_data() : alpha(), type(), field_1C(), field_2C(), field_30(), field_34(), field_38(), field_3C() { } struc_608::struc_608() : stage_effects(), stage_effects_modern() { } struc_608::struc_608(const ::stage_effects* stage_effects) : stage_effects_modern() { this->stage_effects = stage_effects; } struc_608::struc_608(const ::stage_effects_modern* stage_effects_modern) : stage_effects() { this->stage_effects_modern = stage_effects_modern; } struc_608::~struc_608() { } TaskEffectParent::TaskEffectParent() : state(), enable(), modern(), data(), obj_db(), tex_db(), stage_data() { current_stage_hash = hash_murmurhash_empty; current_stage_index = -1; } TaskEffectParent::~TaskEffectParent() { } std::pair TaskEffectParent::AddTaskEffectTypePtr(TaskEffectType type) { if (CheckTaskEffectTypeLoaded(type) < 0) { const char* name = 0; TaskEffect* t = task_effect_array_get(type, &name); if (t) { app::TaskWork::AddTask(t, name); return { type, t }; } } return { TASK_EFFECT_INVALID, 0 }; } int32_t TaskEffectParent::CheckTaskEffectTypeLoaded(TaskEffectType type) { for (std::pair& i : effects) if (i.first == type) return 0; return -1; } void TaskEffectParent::Event(TaskEffectType type, int32_t event_type, void* data) { for (std::pair& i : effects) if (i.first == type) i.second->Event(event_type, data); } void TaskEffectParent::Dest() { if (state == 1) state = 4; else if (state >= 2 && state <= 3) { for (std::pair& i : effects) i.second->DelTask(); state = 4; } } static const TaskEffectType dword_1409E3440[16] = { TASK_EFFECT_AUTH_3D, TASK_EFFECT_PARTICLE, TASK_EFFECT_INVALID, }; bool TaskEffectParent::Load() { if (state == 1) { if (!modern) { bool wait_load = false; for (uint32_t i : obj_set_ids) if (object_storage_load_obj_set_check_not_read(i)) wait_load |= true; if (wait_load) return true; for (TaskEffectType i : dword_1409E3440) { if (i == TASK_EFFECT_INVALID) break; std::pair v57 = AddTaskEffectTypePtr(i); if (v57.second) { v57.second->SetStageIndices(stage_indices); v57.second->SetEnable(enable); effects.push_back(v57); field_50.push_back(v57); } } for (std::pair& i : field_68) { const stage_effects* stage_effects = i.second.stage_effects; for (int32_t j : stage_effects->field_20) { if (j == TASK_EFFECT_INVALID) break; std::pair v57 = AddTaskEffectTypePtr((TaskEffectType)j); if (v57.second) { v57.second->SetStageIndices(stage_indices); v57.second->SetEnable(enable); effects.push_back(v57); i.second.field_10.push_back(v57); } } } } else { void* data = this->data; object_database* obj_db = this->obj_db; texture_database* tex_db = this->tex_db; stage_database* stage_data = this->stage_data; bool wait_load = false; for (uint32_t i : obj_set_ids) if (object_storage_load_obj_set_check_not_read(i, obj_db, tex_db)) wait_load = true; if (wait_load) return true; for (TaskEffectType i : dword_1409E3440) { if (i == TASK_EFFECT_INVALID) break; std::pair v57 = AddTaskEffectTypePtr(i); if (v57.second) { v57.second->SetStageHashes(stage_hashes, data, obj_db, tex_db, stage_data); v57.second->SetEnable(enable); effects.push_back(v57); field_50.push_back(v57); } } for (std::pair& i : field_68) { const stage_effects_modern* stage_effects = i.second.stage_effects_modern; for (int32_t j : stage_effects->field_20) { if (j == TASK_EFFECT_INVALID) break; std::pair v57 = AddTaskEffectTypePtr((TaskEffectType)j); if (v57.second) { v57.second->SetStageHashes(stage_hashes, data, obj_db, tex_db, stage_data); v57.second->SetEnable(enable); effects.push_back(v57); i.second.field_10.push_back(v57); } } } } state = 2; return true; } else if (state != 2) return false; bool wait_load = false; for (std::pair& i : effects) if (!app::TaskWork::CheckTaskCtrl(i.second)) { wait_load = true; break; } if (!wait_load) { for (std::pair& i : effects) i.second->SetEnable(enable); state = 3; return false; } return true; } void TaskEffectParent::Reset() { for (std::pair& i : effects) i.second->Reset(); } void TaskEffectParent::ResetData() { current_stage_hash = hash_murmurhash_empty; current_stage_index = -1; this->current_stage_index = -1; stage_hashes.clear(); stage_indices.clear(); obj_set_ids.clear(); effects.clear(); field_68.clear(); enable = true; state = 0; } void TaskEffectParent::SetCurrentStageHash(uint32_t stage_hash) { if (current_stage_hash != stage_hash) { for (std::pair& i : effects) i.second->SetCurrentStageHash(stage_hash); current_stage_hash = stage_hash; } } void TaskEffectParent::SetCurrentStageIndex(int32_t stage_index) { if (current_stage_index != stage_index) { for (std::pair& i : effects) i.second->SetCurrentStageIndex(stage_index); current_stage_index = stage_index; } } void TaskEffectParent::SetEnable(bool value) { for (std::pair& i : effects) i.second->SetEnable(value); } void TaskEffectParent::SetFrame(int32_t value) { for (std::pair& i : effects) i.second->SetFrame(value); } void TaskEffectParent::SetFrameRateControl(FrameRateControl* value) { for (std::pair& i : effects) i.second->SetFrameRateControl(value); } void TaskEffectParent::SetStageHashes(std::vector& stage_hashes) { this->stage_indices.clear(); this->stage_hashes.assign(stage_hashes.begin(), stage_hashes.end()); obj_set_ids.clear(); modern = true; for (uint32_t i : stage_hashes) { const ::stage_data_modern* stage_data = this->stage_data->get_stage_data_modern(i); if (!stage_data) continue; const stage_effects_modern* effects = stage_data->effects_init ? &stage_data->effects : &stage_effects_modern_default; field_68.push_back({ i, struc_608(effects) }); for (uint32_t j : effects->field_0) if (j == hash_murmurhash_empty || j == hash_murmurhash_null || j == -1) break; else obj_set_ids.push_back(j); } prj::sort(obj_set_ids); prj::unique(obj_set_ids); for (uint32_t i : obj_set_ids) object_storage_load_set_hash(data, i); state = 1; } void TaskEffectParent::SetStageIndices(std::vector& stage_indices) { this->stage_indices.assign(stage_indices.begin(), stage_indices.end()); this->stage_hashes.clear(); obj_set_ids.clear(); modern = false; data_struct* aft_data = &data_list[DATA_AFT]; object_database* aft_obj_db = &aft_data->data_ft.obj_db; stage_database* aft_stage_data = &aft_data->data_ft.stage_data; for (int32_t i : stage_indices) { const ::stage_data* stage_data = aft_stage_data->get_stage_data(i); if (!stage_data) continue; const stage_effects* effects = stage_data->effects_init ? &stage_data->effects : &stage_effects_default; field_68.push_back({ i, struc_608(effects) }); for (int32_t j : effects->field_0) if (j == -1) break; else obj_set_ids.push_back(j); } prj::sort(obj_set_ids); prj::unique(obj_set_ids); for (uint32_t i : obj_set_ids) object_storage_load_set(aft_data, aft_obj_db, i); state = 1; } bool TaskEffectParent::Unload() { if (state != 4) { ResetData(); return false; } for (std::pair& i : effects) if (app::TaskWork::CheckTaskReady(i.second)) return true; for (uint32_t& i : obj_set_ids) object_storage_unload_set(i); ResetData(); return false; } static void draw_fog_particle(render_context* rctx, TaskEffectFogRing::Data* data) { if (!data->num_vtx) return; shaders_ft.set(SHADER_FT_FOGPTCL); gl_state_enable_blend(); texture* tex = texture_storage_get_texture(data->tex_id); if (tex) gl_state_active_bind_texture_2d(0, tex->tex); gl_state_bind_vertex_array(data->vao); shaders_ft.draw_arrays(GL_TRIANGLES, 0, data->num_vtx); gl_state_disable_blend(); } static void leaf_particle_init(bool change_stage) { leaf_particle_emit_timer = 0.0f; leaf_particle_emit_interval = stage_param_data_leaf_current->emit_interval; if (change_stage) return; leaf_particle_free(); const size_t leaf_ptcl_vtx_count = leaf_ptcl_count * 0x08; leaf_ptcl_data = force_malloc_s(leaf_particle_data, leaf_ptcl_count); leaf_particle_num_ptcls = stage_param_data_leaf_current->num_initial_ptcls; leaf_particle_data* data = leaf_ptcl_data; int32_t i = 0; for (; i < leaf_particle_num_ptcls; i++, data++) data->init(); for (; i < leaf_ptcl_count; i++, data++) data->type = 0; if (!leaf_ptcl_vao) glGenVertexArrays(1, &leaf_ptcl_vao); if (!leaf_ptcl_vbo) glGenBuffers(1, &leaf_ptcl_vbo); static const GLsizei buffer_size = sizeof(leaf_particle_vertex_data); gl_state_bind_array_buffer(leaf_ptcl_vbo, true); if (GLAD_GL_VERSION_4_4) glBufferStorage(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * leaf_ptcl_vtx_count), 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); else glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * leaf_ptcl_vtx_count), 0, GL_DYNAMIC_DRAW); gl_state_bind_vertex_array(leaf_ptcl_vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(leaf_particle_vertex_data, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(leaf_particle_vertex_data, texcoord)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(leaf_particle_vertex_data, normal)); size_t ebo_count = leaf_ptcl_vtx_count / 4 * 6; uint32_t* ebo_data = force_malloc_s(uint32_t, ebo_count); for (size_t i = 0, j = 0; i < ebo_count; i += 6, j += 4) { ebo_data[i + 0] = (uint32_t)(j + 0); ebo_data[i + 1] = (uint32_t)(j + 1); ebo_data[i + 2] = (uint32_t)(j + 2); ebo_data[i + 3] = (uint32_t)(j + 0); ebo_data[i + 4] = (uint32_t)(j + 2); ebo_data[i + 5] = (uint32_t)(j + 3); } if (!leaf_ptcl_ebo) glGenBuffers(1, &leaf_ptcl_ebo); gl_state_bind_element_array_buffer(leaf_ptcl_ebo, true); if (GLAD_GL_VERSION_4_4) glBufferStorage(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(sizeof(uint32_t) * ebo_count), ebo_data, 0); else glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(sizeof(uint32_t) * ebo_count), ebo_data, GL_STATIC_DRAW); free_def(ebo_data); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); gl_state_bind_element_array_buffer(0); leaf_particle_scene_ubo.Create(sizeof(leaf_particle_scene_shader_data)); } static float_t sub_14034C960(vec3 x) { float_t t1 = vec3::length_squared(x); if (t1 <= 1.0e-30f) return 0.0f; float_t t2 = 1.0f / sqrtf(t1); return (t2 * 1.5f + t1 * t2 * t2 * t2 * -0.5f) * t1; } static void leaf_particle_ctrl() { if (!leaf_particle_enable || !stage_param_data_leaf_set || !leaf_ptcl_data) return; stage_param_leaf* leaf = stage_param_data_leaf_current; float_t off_y = leaf->offset.y; float_t delta_time = (float_t)(leaf->frame_speed_coef * (leaf_particle_delta_frame * (1.0 / 60.0))); leaf_particle_data* data = leaf_ptcl_data; if (leaf_particle_num_ptcls < 0x800) { int32_t v10 = 0; leaf_particle_emit_timer += delta_time; for (int32_t i = 0; i <= 100 && leaf_particle_emit_timer > leaf_particle_emit_interval; i++) { data[leaf_particle_num_ptcls++].init(); v10++; leaf_particle_emit_interval += leaf->emit_interval * leaf->frame_speed_coef; } } vec3 wind = leaf->wind; stage_param_data_leaf_lie_plane_xz lie_plane_xz = leaf->lie_plane_xz; data = leaf_ptcl_data; for (int32_t i = 0; i < leaf_particle_num_ptcls; i++, data++) { if (data->type != 1) continue; vec3 direction = data->direction; float_t v19 = sub_14034C960(direction); vec3 direction_diff = wind - direction; vec3 v25 = vec3::dot(direction_diff * data->normal, vec3(1.0f)) * data->normal * 35.0f + vec3(0.0f, -9.8f, 0.0f) + direction_diff * sub_14034C960(direction_diff) * 5.0f; direction += v25 * delta_time; data->direction = direction; data->position += direction * delta_time; data->rotation += data->rotation_add * v19 * delta_time; float_t pos_y = data->position.y; if (pos_y < off_y) data->type = 2; else if (pos_y < 0.01f && pos_y > -0.09f && (lie_plane_xz.min_x <= data->position.x && data->position.x <= lie_plane_xz.max_x) && (lie_plane_xz.min_z <= data->position.z && data->position.z <= lie_plane_xz.max_z)) { data->type = 2; data->position.y = 0.01f; data->rotation.x = (float_t)-M_PI_2; data->rotation.y = 0.0f; } } } static std::pair sub_1406427A0(vec3 x, vec3 y) { float_t t1 = vec3::length_squared(x); if (t1 <= 1.0e-30f) return { y, 0.0f }; float_t t2 = 1.0f / sqrtf(t1); float_t t3 = (t2 * 1.5f + t1 * t2 * t2 * t2 * -0.5f); return { t3 * x, t3 * t1 }; } static int32_t leaf_particle_disp() { float_t size = leaf_ptcl_data[0].size; vec3 position[4]; vec3 normal[4]; position[0] = vec3(-0.5f, -0.5f, 0.0f) * size; position[1] = vec3( 0.5f, -0.5f, 0.2f) * size; position[2] = vec3( 0.5f, 0.5f, 0.0f) * size; position[3] = vec3(-0.5f, 0.5f, 0.2f) * size; normal[0] = sub_1406427A0(vec3( 0.0f, 0.0f, 1.0f), vec3(0.0f, 1.0f, 0.0f)).first; normal[1] = sub_1406427A0(vec3(-0.4f, 0.4f, 1.0f), vec3(0.0f, 1.0f, 0.0f)).first; normal[2] = sub_1406427A0(vec3(-0.2f, -0.2f, 1.0f), vec3(0.0f, 1.0f, 0.0f)).first; normal[3] = sub_1406427A0(vec3( 0.4f, -0.4f, 1.0f), vec3(0.0f, 1.0f, 0.0f)).first; leaf_particle_vertex_data* vtx_data; if (GLAD_GL_VERSION_4_5) { vtx_data = (leaf_particle_vertex_data*)glMapNamedBuffer(leaf_ptcl_vbo, GL_WRITE_ONLY); if (!vtx_data) { glUnmapNamedBuffer(leaf_ptcl_vbo); return 0; } } else { gl_state_bind_array_buffer(leaf_ptcl_vbo); vtx_data = (leaf_particle_vertex_data*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (!vtx_data) { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); return 0; } } int32_t vtx_count = 0; leaf_particle_data* data = leaf_ptcl_data; bool split_tex = stage_param_data_leaf_current->split_tex; for (int32_t i = 0; i < leaf_particle_num_ptcls; i++, data++) { if (!data->type) continue; vec3 pos = data->position; vec3 rot = data->rotation; mat3 mat; mat3_rotate_x(rot.x, &mat); mat3_rotate_y_mult(&mat, rot.y, &mat); mat3_rotate_z_mult(&mat, rot.z, &mat); vec3 t0; vec3 t1; vec3 t2; vec3 t3; mat3_mult_vec(&mat, &position[0], &t0); mat3_mult_vec(&mat, &position[1], &t1); mat3_mult_vec(&mat, &position[2], &t2); mat3_mult_vec(&mat, &position[3], &t3); vtx_data[0].position = pos + t0; vtx_data[1].position = pos + t1; vtx_data[2].position = pos + t2; vtx_data[3].position = pos + t3; mat3_mult_vec(&mat, &normal[0], &t0); mat3_mult_vec(&mat, &normal[1], &t1); mat3_mult_vec(&mat, &normal[2], &t2); mat3_mult_vec(&mat, &normal[3], &t3); vtx_data[0].normal = t0; vtx_data[1].normal = t1; vtx_data[2].normal = t2; vtx_data[3].normal = t3; data[0].normal = t0; if (split_tex) { const float_t u = i & 0x01 ? 1.0f : 0.0f; const float_t v = i & 0x02 ? 1.0f : 0.0f; float_t u0 = u * 0.5f; float_t v0 = v * 0.5f; float_t u1 = (u + 1.0f) * 0.5f; float_t v1 = (v + 1.0f) * 0.5f; vtx_data[0].texcoord = vec2(u0, v0); vtx_data[1].texcoord = vec2(u1, v0); vtx_data[2].texcoord = vec2(u1, v1); vtx_data[3].texcoord = vec2(u0, v1); } else { vtx_data[0].texcoord = 0.0f; vtx_data[1].texcoord = vec2(1.0f, 0.0f); vtx_data[2].texcoord = 1.0f; vtx_data[3].texcoord = vec2(0.0f, 1.0f); } if (data->type == 1) { vtx_data[4] = vtx_data[0]; vtx_data[5] = vtx_data[3]; vtx_data[6] = vtx_data[2]; vtx_data[7] = vtx_data[1]; vtx_data[4].normal = -vtx_data[4].normal; vtx_data[5].normal = -vtx_data[5].normal; vtx_data[6].normal = -vtx_data[6].normal; vtx_data[7].normal = -vtx_data[7].normal; vtx_data += 8; vtx_count += 8; } else { vtx_data += 4; vtx_count += 4; } } if (GLAD_GL_VERSION_4_5) glUnmapNamedBuffer(leaf_ptcl_vbo); else { glUnmapBuffer(GL_ARRAY_BUFFER); gl_state_bind_array_buffer(0); } return vtx_count; } static void leaf_particle_free() { if (leaf_ptcl_data) { free(leaf_ptcl_data); leaf_ptcl_data = 0; } if (leaf_ptcl_vao) { glDeleteVertexArrays(1, &leaf_ptcl_vao); leaf_ptcl_vao = 0; } if (leaf_ptcl_vbo) { glDeleteBuffers(1, &leaf_ptcl_vbo); leaf_ptcl_vbo = 0; } if (leaf_ptcl_ebo) { glDeleteBuffers(1, &leaf_ptcl_ebo); leaf_ptcl_ebo = 0; } leaf_particle_scene_ubo.Destroy(); } static void particle_init(vec3* offset) { if (ptcl_data) delete[] ptcl_data; const size_t ptcl_vtx_count = ptcl_count * 0x06; ptcl_data = force_malloc_s(particle_data, ptcl_count); particle_data* data = ptcl_data; for (size_t i = 0; i < ptcl_count; i++, data++) data->alive = false; particle_index = 0; particle_count = 0; if (offset) particle_wind = *offset; else particle_wind = 0.0f; if (!ptcl_vao) glGenVertexArrays(1, &ptcl_vao); if (!ptcl_vbo) glGenBuffers(1, &ptcl_vbo); static const GLsizei buffer_size = sizeof(particle_vertex_data); gl_state_bind_array_buffer(ptcl_vbo, true); if (GLAD_GL_VERSION_4_4) glBufferStorage(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * ptcl_vtx_count), 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); else glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * ptcl_vtx_count), 0, GL_DYNAMIC_DRAW); gl_state_bind_vertex_array(ptcl_vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(particle_vertex_data, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(particle_vertex_data, color)); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(particle_vertex_data, texcoord)); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(particle_vertex_data, normal)); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); } static void particle_ctrl() { if (!ptcl_data) return; vec3 wind = particle_wind; particle_data* data = ptcl_data; for (int32_t i = 0; i < ptcl_count; i++, data++) { if (!data->alive) continue; vec3 v6 = data->direction + (wind - data->direction + vec3(0.0f, -3.0f, 0.0f)) * particle_delta_time; std::pair v7 = sub_1406427A0(v6, vec3(0.0f, 1.0f, 0.0f)); float_t v8 = vec3::dot(v7.first * data->normal, vec3(1.0f)) * v7.second; vec3 v14 = v6 + (v8 * data->normal * -0.345f); data->direction = 0.97f * v14; data->position += v14 * particle_delta_time; data->rotation += data->rotation_add * v7.second * particle_delta_time; data->life_time -= particle_delta_time; if (data->position.y < 0.0f || data->life_time < 0.0f) particle_kill(data); } } static int32_t particle_disp(particle_vertex_data* vtx_data, particle_data* data, int32_t count) { int32_t vtx_count = 0; for (size_t i = 0; i < count; i++, data++) { if (!data->alive) continue; vec3 pos = data->position; vec3 rot = data->rotation; mat3 mat; mat3_rotate_x(rot.x, &mat); mat3_rotate_y_mult(&mat, rot.y, &mat); mat3_rotate_z_mult(&mat, rot.z, &mat); float_t size = data->size; vec3 t0 = vec3( 0.0125f, 0.007216875f, 0.0f) * size; vec3 t1 = vec3(-0.0125f, 0.007216875f, 0.0f) * size; vec3 t2 = vec3( 0.0f , -0.01443375f , 0.0f) * size; mat3_mult_vec(&mat, &t0, &t0); mat3_mult_vec(&mat, &t1, &t1); mat3_mult_vec(&mat, &t2, &t2); t0 += pos; t1 += pos; t2 += pos; vec3 normal = vec3(0.0f, 0.0f, 1.0f); mat3_mult_vec(&mat, &normal, &normal); data->normal = normal; vec4 color = data->color; vtx_data[0].position = t0; vtx_data[1].position = t1; vtx_data[2].position = t2; vtx_data[0].normal = normal; vtx_data[1].normal = normal; vtx_data[2].normal = normal; vtx_data[0].color = color; vtx_data[1].color = color; vtx_data[2].color = color; vtx_data[0].texcoord = vec2(0.0f); vtx_data[1].texcoord = vec2(1.0f, 0.5f); vtx_data[2].texcoord = vec2(0.0f, 1.0f); normal = -normal; vtx_data[3].position = t2; vtx_data[4].position = t1; vtx_data[5].position = t0; vtx_data[3].normal = normal; vtx_data[4].normal = normal; vtx_data[5].normal = normal; vtx_data[3].color = color; vtx_data[4].color = color; vtx_data[5].color = color; vtx_data[3].texcoord = vec2(0.0f, 1.0f); vtx_data[4].texcoord = vec2(1.0f, 0.5f); vtx_data[5].texcoord = vec2(0.0f); vtx_data += 6; vtx_count += 6; } return vtx_count; } static particle_data* particle_emit() { if (particle_count >= ptcl_count) return 0; for (int32_t i = 0; i < ptcl_count; i++) { particle_data* data = &ptcl_data[particle_index++]; if (particle_index >= ptcl_count) particle_index = 0; if (data->alive) continue; *data = {}; data->alive = true; particle_count++; return data; } return 0; } static void particle_event(particle_event_data* event_data) { if (!ptcl_data) return; float_t type = event_data->type; int32_t count = (int32_t)event_data->count; float_t size = event_data->size; vec3 trans = event_data->trans; float_t force = event_data->force; if (type == 1.0f) { vec4 color[3]; color[0] = vec4(1.0f, 0.2f, 0.2f, 1.0f); color[1] = vec4(1.0f, 1.0f, 0.4f, 1.0f); color[2] = vec4(1.0f, 0.6f, 0.0f, 1.0f); for (int32_t i = 0; i < count; i++) { particle_data* data = particle_emit(); if (!data) break; data->position = trans; data->rotation.x = rand_state_array_get_float(4) * 6.28f; data->rotation.y = rand_state_array_get_float(4) * 6.28f; vec3 direction = vec3(cosf(data->rotation.x), 0.0f, sinf(data->rotation.x)); data->direction = direction * (rand_state_array_get_float(4) * force); data->normal = vec3(0.0f, 0.0f, 1.0f); data->rotation_add.x = (rand_state_array_get_float(4) + 1.0f) * 5.0f; data->rotation_add.y = (rand_state_array_get_float(4) + 1.0f) * 2.5f; data->rotation_add.z = 20.0f; int32_t color_rand = rand_state_array_get_int(0, 2, 4); data->color = color[color_rand]; data->type = type; data->life_time = 10.0f; data->size = size; } } else if (type == 2.0f) { float_t v13 = rand_state_array_get_float(4) * (float_t)(M_PI * 2.0); float_t v14 = rand_state_array_get_float(4) * (float_t)(144.0 * DEG_TO_RAD); float_t v15 = sinf(v14); vec3 direction; direction.x = sinf(v13) * v15; direction.y = cosf(v14); direction.z = cosf(v13) * v15; for (int32_t i = 0; i < count; i++) { particle_data* data = particle_emit(); if (!data) break; data->position = trans; data->normal = vec3(0.0f, 0.0f, 1.0f); data->rotation.x = rand_state_array_get_float(4) * 6.28f; data->rotation.y = rand_state_array_get_float(4) * 6.28f; data->direction = direction * ((rand_state_array_get_float(4) + 1.0f) * force); data->rotation_add.x = (rand_state_array_get_float(4) + 1.0f) * 5.0f; data->rotation_add.y = (rand_state_array_get_float(4) + 1.0f) * 2.5f; data->rotation_add.z = 20.0f; data->color.x = 5.0; data->color.y = 4.5; data->color.z = 2.0; data->color.w = 1.0; data->type = type; data->life_time = 8.0f; data->size = size; } } } static void particle_kill(particle_data* data) { data->alive = false; particle_count--; } static void particle_free() { if (ptcl_data) delete[] ptcl_data; ptcl_data = 0; particle_index = 0; particle_count = 0; if (ptcl_vao) { glDeleteVertexArrays(1, &ptcl_vao); ptcl_vao = 0; } if (ptcl_vbo) { glDeleteBuffers(1, &ptcl_vbo); ptcl_vbo = 0; } } static void rain_particle_init(bool change_stage) { vec3 velocity = stage_param_data_rain_current->velocity; vec3 vel_range = stage_param_data_rain_current->vel_range; const size_t rain_ptcl_vtx_count = rain_ptcl_count * 0x06; for (int32_t i = 0; i < 8; i++) { rain_particle_data& data = rain_ptcl_data[i]; data.position.x = 0.0f; data.position.y = ((float_t)i * -12.5f) - 5.0f; data.position.z = 0.0f; data.texcoord.x = 1.0f; data.alpha = 1.0f; data.type = 1; data.velocity.x = rand_state_array_get_float(4); data.velocity.y = rand_state_array_get_float(4); data.velocity.z = rand_state_array_get_float(4); data.velocity = (data.velocity - 0.5f) * vel_range + velocity; data.field_2C = 0; data.field_30 = 0; data.field_34 = 0; data.field_38 = 0; data.field_3C = 0; } if (change_stage) return; if (!rain_ptcl_vao) glGenVertexArrays(1, &rain_ptcl_vao); if (!rain_ptcl_vbo) glGenBuffers(1, &rain_ptcl_vbo); vec3* vtx_data = force_malloc_s(vec3, rain_ptcl_vtx_count); for (int32_t i = 0; i < rain_ptcl_count; i++, vtx_data += 6) { vec3 position; position.x = rand_state_array_get_float(4); position.y = rand_state_array_get_float(4); position.z = rand_state_array_get_float(4); vtx_data[0] = position; vtx_data[1] = position; vtx_data[2] = position; vtx_data[3] = position; vtx_data[4] = position; vtx_data[5] = position; } vtx_data -= rain_ptcl_vtx_count; static const GLsizei buffer_size = sizeof(vec3); gl_state_bind_array_buffer(rain_ptcl_vbo, true); if (GLAD_GL_VERSION_4_4) glBufferStorage(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * rain_ptcl_vtx_count), vtx_data, 0); else glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(buffer_size * rain_ptcl_vtx_count), vtx_data, GL_STATIC_DRAW); free_def(vtx_data); gl_state_bind_vertex_array(rain_ptcl_vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, 0); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); rain_particle_scene_ubo.Create(sizeof(rain_particle_scene_shader_data)); rain_particle_batch_ubo.Create(sizeof(rain_particle_batch_shader_data)); } static void rain_particle_ctrl() { if (!stage_param_data_rain_current) return; float_t delta_time = (float_t)(rain_particle_delta_frame * (1.0 / 60.0)); for (int32_t i = 0; i < 8; i++) { rain_particle_data& data = rain_ptcl_data[i]; data.position += delta_time * data.velocity; data.alpha = 1.0f; float_t pos_y = data.position.y; if (pos_y > -1.0f) data.alpha = max_def(-pos_y, 0.0f); else if (pos_y < -99.0f) { data.alpha = max_def(pos_y + 100.0f, 0.0f); if (pos_y < -100.0f) data.position = 0.0f; } } } static void rain_particle_free() { if (rain_ptcl_vao) { glDeleteVertexArrays(1, &rain_ptcl_vao); rain_ptcl_vao = 0; } if (rain_ptcl_vbo) { glDeleteBuffers(1, &rain_ptcl_vbo); rain_ptcl_vbo = 0; } rain_particle_scene_ubo.Destroy(); rain_particle_batch_ubo.Destroy(); }