/* by korenkonder GitHub/GitLab: korenkonder */ #include "effect.hpp" #include "../KKdLib/prj/algorithm.hpp" #include "../KKdLib/hash.hpp" #include "../KKdLib/mat.hpp" #include "GL/array_buffer.hpp" #include "GL/element_array_buffer.hpp" #include "GL/shader_storage_buffer.hpp" #include "GL/uniform_buffer.hpp" #include "rob/rob.hpp" #include "auth_3d.hpp" #include "data.hpp" #include "gl_state.hpp" #include "random.hpp" #include "render_texture.hpp" #include "render_context.hpp" #include "render_manager.hpp" #include "shader_ft.hpp" #include "stage.hpp" #include "stage_param.hpp" #include "task.hpp" #include "texture.hpp" struct particle_init_data { float_t field_0; float_t field_4; float_t field_8; vec3 trans; float_t scale_y; }; class TaskEffect : public app::Task { public: TaskEffect(); virtual ~TaskEffect() override; virtual void pre_init(int32_t stage_index); virtual void set_stage_hashes(const std::vector& stage_hashes, void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data); // Added virtual void set_stage_indices(const std::vector& stage_indices); virtual void set_frame(int32_t value); virtual void field_48(); virtual void set_enable(bool value); virtual void set_current_stage_hash(uint32_t value); // Added virtual void set_current_stage_index(int32_t value); virtual void set_frame_rate_control(FrameRateControl* value = 0); virtual void field_68(); virtual void reset(); virtual void event(int32_t event_type, void* data); virtual void field_80(); virtual void field_88(); virtual void field_90(); virtual void field_98(int32_t a2, int32_t* a3); virtual void field_A0(int32_t a2, int32_t* a3); virtual void field_A8(int32_t a2, int8_t* a3); }; struct struc_621 { uint32_t stage_hash; // Added int32_t stage_index; std::vector auth_3d_ids; struc_621(); ~struc_621(); }; struct EffectAuth3d { size_t count; size_t max_count; auth_3d_id* auth_3d_ids_ptr; auth_3d_id auth_3d_ids[TASK_STAGE_STAGE_COUNT]; EffectAuth3d(); }; class TaskEffectAuth3D : public TaskEffect { public: EffectAuth3d stage; bool enable; std::vector field_120; uint32_t current_stage_hash; // Added int32_t current_stage_index; int32_t field_13C; std::vector stage_hashes; std::vector stage_indices; int8_t field_158; int8_t field_159; int32_t field_15C; float_t frame; TaskEffectAuth3D(); virtual ~TaskEffectAuth3D() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_hashes(const std::vector& stage_hashes, void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data) override; // Added virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_frame(int32_t value) override; virtual void set_enable(bool value) override; virtual void set_current_stage_hash(uint32_t value) override; // Added virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; void reset_data(); void set_visibility(bool value); }; struct EffectFogAnim { bool field_0; int32_t field_4; bool field_8; float_t field_C[3]; float_t field_18[3]; float_t field_24; int32_t field_28; int32_t field_2C; int32_t field_30; vec4 field_34; EffectFogAnim(); ~EffectFogAnim(); void ctrl(); void reset(); }; class TaskEffectFogAnim : public TaskEffect { public: EffectFogAnim data; TaskEffectFogAnim(); virtual ~TaskEffectFogAnim() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void reset() override; }; struct fog_ring_data { vec3 position; vec3 direction; vec3 field_18; float_t size; float_t density; fog_ring_data(); }; struct struc_371 { int32_t field_0; vec3 position; float_t field_10; float_t field_14; vec3 direction; float_t field_24; struc_371(); }; struct struc_573 { int32_t chara_index; rob_bone_index bone_index; vec3 position; struc_573(); }; struct EffectFogRing { bool enable; float_t delta_frame; bool field_8; float_t ring_size; vec3 wind_dir; int32_t tex_id; vec4 color; float_t ptcl_size; int32_t max_ptcls; int32_t num_ptcls; float_t density; float_t density_offset; fog_ring_data* ptcl_data; int32_t num_vtx; struc_573 field_5C[2][5]; int32_t field_124; struc_371 field_128[10]; int8_t field_2B8; int8_t field_2B9; bool display; int32_t current_stage_index; std::vector stage_indices; FrameRateControl* frame_rate_control; GL::ShaderStorageBuffer ssbo; EffectFogRing(); ~EffectFogRing(); void calc_ptcl(float_t delta_time); void calc_vert(); void ctrl(); void ctrl_inner(float_t delta_time); void dest(); void disp(); void draw(); void init_particle_data(); void restart_effect(); void set_stage_indices(const std::vector& stage_indices); static void draw_static(void* data); static float_t ptcl_random(float_t value); void sub_140347B40(float_t delta_time); static void sub_140347860(fog_ring_data* a1, int32_t a2, struc_371* a3, float_t a4); }; class TaskEffectFogRing : public TaskEffect { public: EffectFogRing data; TaskEffectFogRing(); virtual ~TaskEffectFogRing() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; class TaskEffectLeaf : public TaskEffect { public: FrameRateControl* frame_rate_control; int32_t current_stage_index; std::vector stage_indices; int32_t wait_frames; TaskEffectLeaf(); virtual ~TaskEffectLeaf() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; class TaskEffectLitproj : public TaskEffect { public: int32_t current_stage_index; std::vector stage_indices; int32_t wait_frames; vec4 diffuse; vec4 specular; int32_t frame; TaskEffectLitproj(); virtual ~TaskEffectLitproj() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void reset() override; }; class TaskEffectParticle : public TaskEffect { public: FrameRateControl* frame_rate_control; int32_t current_stage_index; TaskEffectParticle(); virtual ~TaskEffectParticle() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_enable(bool value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; virtual void event(int32_t event_type, void* data) override; }; class TaskEffectRain : public TaskEffect { public: FrameRateControl* frame_rate_control; int32_t current_stage_index; std::vector stage_indices; TaskEffectRain(); virtual ~TaskEffectRain() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; struct ripple_struct { int32_t ripple_uniform; int32_t ripple_emit_uniform; int32_t count; vec3* position; color4u8* color; float_t size; int32_t field_24; }; struct ripple_emit_draw_data { ripple_struct data; vec3 position[16]; color4u8 color[16]; ripple_emit_draw_data(); }; struct struc_192 { int32_t index; vec3 trans; struc_192(); }; struct struc_207 { struc_192 field_0[18]; struc_207(); }; struct ripple_emit_params { float_t wake_attn; float_t speed; float_t field_8; float_t field_C; ripple_emit_params(); }; struct EffectRipple { float_t delta_frame; bool update; int32_t rain_ripple_num; float_t rain_ripple_min_value; float_t rain_ripple_max_value; int32_t field_14; float_t ground_y; float_t emit_pos_scale; float_t emit_pos_ofs_x; float_t emit_pos_ofs_z; int32_t ripple_tex_id; bool use_float_ripplemap; int32_t field_30; float_t rob_emitter_size; size_t emitter_num; const vec3* emitter_list; float_t emitter_size; int32_t field_4C; ripple_emit_draw_data field_50; ripple_emit_draw_data field_178; ripple_emit_draw_data field_2A0; ripple_emit_draw_data field_3C8; int32_t field_4F0; struc_207 field_4F4[6]; int32_t field_BB4; RenderTexture field_BB8; int32_t counter; int8_t field_BEC; ripple_emit_params params; bool stage_set; int32_t current_stage_index; std::vector stage_indices; EffectRipple(); ~EffectRipple(); void clear_tex(); void ctrl(); void dest(); void disp(); void disp_particles(ripple_struct* data); void draw(); void reset(); void set_stage_index(int32_t stage_index); void set_stage_indices(const std::vector& stage_indices); void set_stage_param(stage_param_ripple* ripple); static void draw_static(void* data); void sub_1403584A0(RenderTexture* rt); void sub_140358690(); void sub_1403587C0(const vec3 a2, const vec3 a3, float_t a4, ripple_struct& a5, ripple_struct& a6); void sub_14035AAE0(); void sub_14035AED0(); }; class TaskEffectRipple : public TaskEffect { public: int64_t field_68; FrameRateControl* frame_rate_control; EffectRipple* emit; TaskEffectRipple(); virtual ~TaskEffectRipple() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; class TaskEffectSnow : public TaskEffect { public: FrameRateControl* frame_rate_control; int32_t current_stage_index; std::vector stage_indices; TaskEffectSnow(); virtual ~TaskEffectSnow() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void basic() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; struct splash_particle_data { vec3 position; vec3 direction; float_t size; float_t life_time; float_t field_20; int32_t index; uint32_t flags; float_t alpha; splash_particle_data(); }; struct splash_particle { int32_t count; int32_t alive; std::vector data; int32_t dead; std::vector available; splash_particle(); ~splash_particle(); void ctrl(float_t delta_time); splash_particle_data* emit(); void free(); void init(int32_t count); void kill(int32_t index); void reset(); void restart(); }; struct ParticleEmitter { splash_particle* splash; vec3 trans; int32_t field_1C; float_t field_20; int field_24; float_t field_28; float_t particle_size; ParticleEmitter(); virtual ~ParticleEmitter(); virtual void ctrl(float_t delta_time); virtual void restart(); void reset_data(); }; struct ParticleEmitterRob : ParticleEmitter { int32_t chara_id; int32_t bone_index; vec3 prev_trans; vec3 velocity; vec3 prev_velocity; int32_t emit_num; float_t field_60; float_t emission_ratio_attn; float_t emission_velocity_scale; bool in_water; bool init_trans; ParticleEmitterRob(); virtual ~ParticleEmitterRob() override; virtual void ctrl(float_t delta_time) override; virtual void restart() override; void get_velocity(float_t delta_time); void get_trans(); void reset_data(); void reset_data_base(); void set_chara(int32_t chara_id, int32_t bone_index, bool in_water); void set_splash(splash_particle* value); }; struct water_particle_vertex_data { vec3 position; float_t size; vec4 color; water_particle_vertex_data(); }; struct water_particle { splash_particle* splash; vec4 color; float_t particle_size; int32_t splash_count; std::vector ptcl_data; int32_t count; int32_t splash_tex_id; bool blink; std::vector position_data; std::vector color_data; ripple_struct ripple; float_t ripple_emission; GL::ShaderStorageBuffer ssbo; water_particle(); ~water_particle(); void ctrl(); void disp(); void draw(mat4* mat); void free(); void reset(); void set(splash_particle* splash, int32_t splash_tex_id); }; struct ParticleDispObj { splash_particle* splash; object_info obj_info; std::vector instances_mat; ParticleDispObj(); virtual ~ParticleDispObj(); virtual void disp(); void init(splash_particle* splash, object_info obj_info); void reset(); }; struct EffectSplashParticle { int32_t splash_count; splash_particle* splash; int32_t emitter_rob_count; ParticleEmitterRob* emitter_rob; int32_t water_ptcl_count; water_particle* water_ptcl; bool has_splash_object; splash_particle splash_object; int32_t object_emitter_rob_count; ParticleEmitterRob* object_emitter_rob; ParticleDispObj particle_disp_obj; vec4 color; float_t particle_size; int32_t emit_num; float_t ripple_emission; float_t emission_ratio_attn; float_t emission_velocity_scale; int32_t splash_tex_id; object_info splash_obj_id; bool in_water; bool blink; int64_t rob_ctrl_time; int64_t ptcl_ctrl_time; int64_t ptcl_disp_time; EffectSplashParticle(); ~EffectSplashParticle(); void ctrl(float_t delta_time); void dest(); void disp(); bool init(int32_t splash_tex_id, object_info splash_obj_id, bool in_water, bool blink); void reset(); void restart(); void set_color(const vec4& value); void set_emission_ratio_attn(float_t value); void set_emission_velocity_scale(float_t value); void set_emit_num(int32_t value); void set_particle_size(float_t value); void set_ripple_emission(float_t value); }; struct EffectSplash { bool restart; int32_t stage_index; bool field_8; EffectSplashParticle particle; bool field_110; int32_t current_stage_index; std::vector stage_indices; FrameRateControl* frame_rate_control; int64_t field_138; EffectSplash(); ~EffectSplash(); void ctrl(float_t delta_time); void dest(); void disp(); void reset(); void restart_effect(); void set_current_stage_index(int32_t value); void set_stage_indices(const std::vector& stage_indices); }; class TaskEffectSplash : public TaskEffect { public: bool enable; EffectSplash data; TaskEffectSplash(); virtual ~TaskEffectSplash() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void pre_init(int32_t stage_index) override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; class TaskEffectStar : public TaskEffect { public: FrameRateControl* frame_rate_control; int32_t current_stage_index; float_t delta_frame; std::vector stage_indices; TaskEffectStar(); virtual ~TaskEffectStar() override; virtual bool init() override; virtual bool ctrl() override; virtual bool dest() override; virtual void disp() override; virtual void set_stage_indices(const std::vector& stage_indices) override; virtual void set_enable(bool value) override; virtual void set_current_stage_index(int32_t value) override; virtual void set_frame_rate_control(FrameRateControl* value = 0) override; virtual void reset() override; }; struct for_ring_vertex_data { vec2 position; vec4 color; float_t size; }; 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_rot_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_scene_shader_data { vec4 g_transform[4]; vec4 g_view_pos; vec4 g_light_env_chara_diffuse; vec4 g_light_env_chara_specular; }; struct particle_vertex_data { vec3 position; vec3 normal; vec4 color; vec2 texcoord; }; struct particle_data { vec3 position; float_t size; float_t alpha; int32_t life_time; vec3 velocity; vec3 direction; 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 ripple_batch_shader_data { vec4 g_params; }; struct ripple_scene_shader_data { vec4 g_transform; vec4 g_texcoord; }; struct ripple_emit_scene_shader_data { vec4 g_size_in_projection; vec4 g_transform; vec4 g_framebuffer_size; }; struct snow_particle_vertex_data { vec3 position; float_t size; float_t alpha; }; struct snow_particle_gpu_vertex_data { vec3 position; float_t size; }; struct snow_particle_scene_shader_data { vec4 g_transform[4]; vec4 g_view_world_row2; vec4 g_size_in_projection; vec4 g_state_point_attenuation; vec4 g_range_scale; vec4 g_range_offset; vec4 g_framebuffer_size; vec4 g_near_far; }; struct snow_particle_batch_shader_data { vec4 g_pos_offset; vec4 g_color; vec4i start_vertex_location; }; struct star_catalog_scene_shader_data { vec4 g_transform[4]; }; struct star_catalog_batch_shader_data { vec4 g_size_in_projection; vec4 g_modifiers; vec4 g_thresholds; }; struct star_catalog_milky_way { GL::ArrayBuffer vbo; GL::ElementArrayBuffer ebo; GLuint vao; GLuint sampler; uint16_t restart_index; int32_t idx_count; int32_t longitude_degs_10; int32_t latitude_degs_10; float_t longitude_offset_degs_10; float_t latitude_offset_degs_10; float_t latitude; float_t longitude; float_t uv_rec_scale_u; float_t uv_rec_scale_v; star_catalog_milky_way(); ~star_catalog_milky_way(); void create_buffers(int32_t subdivs, float_t uv_rec_scale_u, float_t uv_rec_scale_v, int32_t longitude_degs_10, int32_t latitude_degs_10, float_t longitude_offset_degs_10, float_t latitude_offset_degs_10); void create_default_sphere(); void delete_buffers(); void draw(const mat4& vp, const mat4& model, texture* tex, GL::UniformBuffer& scene_ubo); void reset(); }; struct star_catalog_vertex { vec3 position; vec2 texcoord; star_catalog_vertex(); }; struct stars_buffer_data { vec3 position; float_t size; vec4 color; stars_buffer_data(); }; struct star_catalog { bool random; bool enable; stage_param_star* stage_param_data_ptr; stage_param_star stage_param_data; star_catalog_milky_way milky_way; int32_t star_count; int32_t star_b_count; p_file_handler file_handler; uint32_t star_tex; uint32_t star_b_tex; uint32_t milky_way_tex_id; GL::ShaderStorageBuffer stars_ssbo; GL::UniformBuffer scene_ubo; GL::UniformBuffer batch_ubo; star_catalog(); ~star_catalog(); void draw(); void free(); bool init(); void set_stage_param_data(stage_param_star* value); static void parse_data(std::vector& vec, size_t data); }; struct water_particle_scene_shader_data { vec4 g_transform[4]; vec4 g_view_world_row2; vec4 g_size_in_projection; vec4 g_state_point_attenuation; }; struct struc_608 { const stage_effects* stage_effects; const stage_effects_modern* stage_effects_modern; prj::vector_pair field_10; struc_608(); struc_608(const ::stage_effects* stage_effects); struc_608(const ::stage_effects_modern* stage_effects_modern); ~struc_608(); }; class EffectManager { public: 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; prj::vector_pair effects; prj::vector_pair field_50; prj::vector_pair 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 EffectManager(); virtual ~EffectManager(); std::pair add_effect(EffectType type); int32_t check_effect_loaded(EffectType type); void event(EffectType type, int32_t event_type, void* data); void dest(); bool load(); void reset(); void reset_data(); void set_current_stage_hash(uint32_t stage_hash); // Added void set_current_stage_index(int32_t stage_index); void set_enable(bool value); void set_frame(int32_t value); void set_frame_rate_control(FrameRateControl* value); void set_stage_hashes(const std::vector& stage_hashes); // Added void set_stage_indices(const std::vector& stage_indices); bool unload(); }; static const uint32_t flt_1409E59C0[] = { 0x3ED7136A, 0x3F3C6690, 0x3E9974E6, 0x3EB68B1A, 0x3E508701, 0x3F39A21F, 0x3D641B33, 0x3F1DB37D, 0x3F0B4D6A, 0x3F51450F, 0x3F21450F, 0x3F21AB4B, 0x3F012F6F, 0x3ED136A4, 0x3F6AE925, 0x3F4EB702, 0x3DE872B0, 0x3E11F601, 0x3F0CDDD7, 0x3E69691A, 0x3EA8201D, 0x3DEA2B17, 0x3F058256, 0x3ED16DB1, 0x3EA9EECC, 0x3F253C36, 0x3F406AD3, 0x3EA9AFE2, 0x3F6F4FB5, 0x3F65729B, 0x3F481ADF, 0x3F575CD1, 0x3DFDF3B6, 0x3C804966, 0x3F293E81, 0x3F407011, 0x3E54A4D3, 0x3EC78FEF, 0x3DEF2F98, 0x3F2D1ACA, 0x3EDA9A80, 0x3F3A7137, 0x3F339EAE, 0x3E476C8B, 0x3D8EA4A9, 0x3EDB1EE2, 0x3D8B2420, 0x3DC902DE, 0x3ECF290B, 0x3EDB5200, 0x3F3EF9DB, 0x3F7DE4A4, 0x3E215769, 0x3F3B4C1B, 0x3F2A53B9, 0x3F4D6E05, 0x3B80C73B, 0x3F439192, 0x3E52DA12, 0x3F0421C0, 0x3F15C9C5, 0x3CB851EC, 0x3EAC8F32, 0x3E60980B, 0x3EB07358, 0x3EF26D48, 0x3F167DFE, 0x3F4C4E51, 0x3EA1244A, 0x3EACDB38, 0x3F1EA359, 0x3EB00150, 0x3F253F7D, 0x3EDA6613, 0x3F262C13, 0x3B42A455, 0x3F68850A, 0x3BA771C9, 0x3F255715, 0x3F682FD7, 0x3F2AEB1C, 0x3DF6E2EB, 0x3F1DC098, 0x3E3BA5E3, 0x3F75551D, 0x3F79081C, 0x3F360568, 0x3F268D11, 0x3F25104D, 0x3DE63737, 0x3F0BFC65, 0x3F6339C1, 0x3F06DFA4, 0x3F47E282, 0x3EF4039B, 0x3F54E50C, 0x3EDDAE3E, 0x3F41EECC, 0x3E561BB0, 0x3EC760BF, 0x3F50A1E0, 0x3F4ABB45, 0x3E9758E2, 0x3DBC5AC4, 0x3EE8B6D8, 0x3F7CB1EE, 0x3ED7C30D, 0x3EF4AF4F, 0x3F1A6762, 0x3EF4CAD5, 0x3F1C3B4F, 0x3EA1D14E, 0x3DE5CE5B, 0x3CC2A455, 0x3E817050, 0x3F128A1E, 0x3F2C039B, 0x3F1644FA, 0x3F159E84, 0x3EF1A75D, 0x3EEC6690, 0x3F280150, 0x3F6DEA89, 0x3E48FB01, 0x3EA1EB85, 0x3E30E022, 0x3F55E9E2, 0x3F385879, 0x3F0BD3C3, 0x3F5BC750, 0x3F06B463, 0x3E508462, 0x3DA1E4F7, 0x3DD3458D, 0x3F477C46, 0x3F36872B, 0x3F643611, 0x3F795183, 0x3F2A34EC, 0x3E7DF8F4, 0x3C804966, 0x3F6154CA, 0x3F6C73AC, 0x3E194318, 0x3F1EAB36, 0x3E8B1855, 0x3F6A862F, 0x3EEE718B, 0x3F511149, 0x3F1D2935, 0x3DEC3223, 0x3ECF3776, 0x3E905BC0, 0x3F1ECFEA, 0x3F71FA98, 0x3C8476F3, 0x3EBB76B4, 0x3F0068DC, 0x3F44764B, 0x3EAFA6DF, 0x3EE41355, 0x3F3D197A, 0x3EB4B5DD, 0x3E6CEC42, 0x3EC07747, 0x3F516B12, 0x3EE60807, 0x3EAE2C13, 0x3E2C9D9D, 0x3D0769EC, 0x3F3921AB, 0x3F06F7E4, 0x3DDF2BAA, 0x3F42708F, 0x3D4BDBA1, 0x3E0A86D7, 0x3F0DE892, 0x3C77B9E0, 0x3F2E5FD9, 0x3F0C3BF7, 0x3EFD6777, 0x3F0B8C54, 0x3EF7DE94, 0x3E457A78, 0x3F56D289, 0x3EF0EA9E, 0x3F44EB9A, 0x3DCA8C15, 0x3F26A4A9, 0x3F759E84, 0x3E8C710D, 0x3F5B1D93, 0x3EDA5F85, 0x3F44A2DB, 0x3F6D729B, 0x3F0584F5, 0x3F1B3EFF, 0x3EE72474, 0x3EB169C2, 0x3DFCD899, 0x3E290ABB, 0x3ED5753A, 0x3ED65E89, 0x3EEB9CB7, 0x3F0D5CFB, 0x3EFC764B, 0x3EC90D5A, 0x3F7EA0BA, 0x3F26147B, 0x3F4B3C60, 0x3EC46B27, 0x3F00D5A6, 0x3F06E979, 0x3F2A8049, 0x3E19ED7C, 0x3F6CE9A3, 0x3F56DD05, 0x3F76E4E2, 0x3F27A637, 0x3F636849, 0x3CE78184, 0x3F38CFC0, 0x3F631A4C, 0x3F6BCC8E, 0x3E42EDBB, 0x3F1D6D5D, 0x3E3D1783, 0x3F5F6A94, 0x3EB48E8A, 0x3F69B670, 0x3DD7B741, 0x3C3851EC, 0x3F2FBDCF, 0x3EF89DF1, 0x3EB9B90F, 0x3EFF41F2, 0x3EE9DB23, 0x3E64FA05, 0x3F018B2F, 0x3ECDEBD9, 0x3F7A7914, }; static const uint32_t flt_1409E5D90[] = { 0xBE23B257, 0x3EF19A41, 0xBECD1633, 0xBE92E9CD, 0xBF17BC7F, 0x3EE6887B, 0xBF637C9A, 0x3E6D9E84, 0x3DB4D6A1, 0x3F228A1E, 0x3E85143C, 0x3E86AD2E, 0x3C17B741, 0xBE3B22D1, 0x3F55D24A, 0x3F1D6E05, 0xBF45E354, 0xBF3704FF, 0x3DCDDD6E, 0xBF0B4C1B, 0xBEAFBE77, 0xBF45753A, 0x3D30403A, 0xBE3A4BDC, 0xBEAC2268, 0x3E94F0D8, 0x3F00D5A6, 0xBEACA03C, 0x3F5EA012, 0x3F4AE5DE, 0x3F103516, 0x3F2EB9A1, 0xBF408312, 0xBF77FB6A, 0x3EA4F8B6, 0x3F00E022, 0xBF15AD97, 0xBE61BDA5, 0xBF44341A, 0x3EB46C76, 0xBE1595FF, 0x3EE9C4DB, 0x3ECE7968, 0xBF1C4913, 0xBF5C577E, 0xBE138716, 0xBF5D3650, 0xBF4DBF48, 0xBE435936, 0xBE12B7FE, 0x3EFBE76D, 0x3F7BC9EF, 0xBF2F544C, 0x3EED31BA, 0x3EA94D94, 0x3F1ADCB1, 0xBF7DFD8B, 0x3F0723CD, 0xBF1692F7, 0x3D042D8C, 0x3E2E50C6, 0xBF747AE1, 0xBEA6E19C, 0xBF0FB353, 0xBE9F1AA0, 0xBD592B80, 0x3E33EFF2, 0x3F189D49, 0xBEBDB8BB, 0xBEA64990, 0x3E751D69, 0xBE9FFEB0, 0x3E94FCA4, 0xBE1667B6, 0x3E98B04B, 0xBF7E7A10, 0x3F51096C, 0xBF7D6191, 0x3E955B03, 0x3F505FAF, 0x3EABAC71, 0xBF424745, 0x3E6E075F, 0xBF222DB6, 0x3F6AAA3B, 0x3F7210E0, 0x3ED815A0, 0x3E9A3593, 0x3E944135, 0xBF46718B, 0x3DBFC116, 0x3F467382, 0x3D5BF488, 0x3F0FC5AC, 0xBD3FC654, 0x3F29CAC1, 0xBE0949A5, 0x3F03DD98, 0xBF14F228, 0xBE627FA2, 0x3F2143BF, 0x3F15768A, 0xBED14F8B, 0xBF50E8A7, 0xBDBA493D, 0x3F796484, 0xBE20F66A, 0xBD350B0F, 0x3E533B10, 0xBD3352A8, 0x3E61DA7B, 0xBEBC5D64, 0xBF468C69, 0xBF73D5BB, 0xBEFD1F60, 0x3E1450F0, 0x3EB00D1B, 0x3E322A6F, 0x3E2CF41F, 0xBD658A33, 0xBD9CC63F, 0x3EA0053E, 0x3F5BD46B, 0xBF1B81D8, 0xBEBC2A45, 0xBF278FEF, 0x3F2BD46B, 0x3EE161E5, 0x3DBD3C36, 0x3F378F47, 0x3D568C69, 0xBF17BE77, 0xBF57876A, 0xBF4B2F45, 0x3F0EF88C, 0x3EDA1DFC, 0x3F486C22, 0x3F72A25E, 0x3EA8D25F, 0xBF010386, 0xBF77FB6A, 0x3F42A8EB, 0x3F58E758, 0xBF335F1C, 0x3E755C53, 0xBEE9D0A6, 0x3F550C5F, 0xBD8C73AC, 0x3F2221EA, 0x3E6949A5, 0xBF44F2D0, 0xBE432229, 0xBEDF4880, 0x3E7681ED, 0x3F63F5D8, 0xBF77B7E9, 0xBE891149, 0x3B525EDD, 0x3F08EC96, 0xBEA0B242, 0xBDDF6556, 0x3EF46499, 0xBE969446, 0xBF0989DF, 0xBE7E22E6, 0x3F22D624, 0xBDCFBFC6, 0xBEA3A68B, 0xBF29B131, 0xBF6F12C2, 0x3EE4855E, 0x3D5EFC7A, 0xBF483516, 0x3F04E11E, 0xBF6683E4, 0xBF3ABC94, 0x3DDE8E61, 0xBF784231, 0x3EB97F63, 0x3DC3BA34, 0xBC26223E, 0x3DB8C005, 0xBD022142, 0xBF1D42C4, 0x3F2DA5B9, 0xBD716095, 0x3F09D7DC, 0xBF4D5CFB, 0x3E9A93F3, 0x3F6B3C60, 0xBEE71DE7, 0x3F363B25, 0xBE16848C, 0x3F0945B7, 0x3F5AE536, 0x3D30A915, 0x3E59F7F9, 0xBDC6DC5D, 0xBE9D2C7C, 0xBF40CA82, 0xBF2B7AA2, 0xBE2A2878, 0xBE26887B, 0xBDA31A4C, 0x3DD5CFAB, 0xBC629739, 0xBE5BC7F7, 0x3F7D4174, 0x3E9851EC, 0x3F1678C0, 0xBE6E5365, 0x3BD5F99C, 0x3D5D2F1B, 0x3EAA0275, 0xBF3309EA, 0x3F59D346, 0x3F2DBA0A, 0x3F6DC91D, 0x3E9E98DD, 0x3F46D139, 0xBF7187E8, 0x3EE33DB0, 0x3F4633F0, 0x3F57991C, 0xBF1E8922, 0x3E6B6AE8, 0xBF2174E6, 0x3F3ED528, 0xBE96E2EB, 0x3F536CDF, 0xBF4A1230, 0xBF7A3CC9, 0x3EBEF73C, 0xBCEC2CE4, 0xBE8C8C93, 0xBB3EB5B3, 0xBDB126E9, 0xBF0D8256, 0x3C45974E, 0xBE48533B, 0x3F74F228, }; 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 EffectManager* effect_manager; static EffectFogAnim* effect_fog_anim; static EffectFogRing* effect_fog_ring; static EffectSplash* effect_splash; static stage_param_leaf* stage_param_data_leaf_current; static bool stage_param_data_leaf_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 GL::ArrayBuffer leaf_ptcl_vbo; static GL::ElementArrayBuffer leaf_ptcl_ebo; static GL::UniformBuffer leaf_particle_scene_ubo; static const size_t leaf_ptcl_count = 0x800; static stage_param_litproj* stage_param_data_litproj_current; static bool stage_param_data_litproj_set; static bool light_proj_enable; static RenderTexture litproj_shadow[2]; static RenderTexture 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_rot_data* ptcl_data; static GLuint ptcl_vao; static GL::ArrayBuffer ptcl_vbo; static GL::UniformBuffer particle_scene_ubo; static const size_t ptcl_count = 0x400; static stage_param_rain* stage_param_data_rain_current; static bool stage_param_data_rain_set; static bool rain_particle_enable; static float_t rain_particle_delta_frame; static uint32_t rain_particle_tex_id; static particle_data rain_ptcl_data[8]; static GL::ShaderStorageBuffer rain_ssbo; static GL::UniformBuffer rain_particle_scene_ubo; static GL::UniformBuffer rain_particle_batch_ubo; static const size_t rain_ptcl_count = 0x8000; static GL::UniformBuffer ripple_batch_ubo; static GL::UniformBuffer ripple_scene_ubo; static EffectRipple* effect_ripple; static GL::ShaderStorageBuffer ripple_emit_ssbo; static GL::UniformBuffer ripple_emit_scene_ubo; static const size_t ripple_emit_count = 5000; static stage_param_snow* stage_param_data_snow_current; static bool stage_param_data_snow_set; static bool snow_particle_enable; static float_t snow_particle_delta_frame; static float_t snow_particle_size_min; static float_t snow_particle_size_mid; static float_t snow_particle_size_max; static int32_t snow_particle_fallen_index; static int32_t snow_particle_fallen_count; static uint32_t snow_particle_tex_id; static particle_data* snow_ptcl_data; static particle_data snow_ptcl_gpu[4]; static particle_data* snow_ptcl_fallen_data; static GL::ShaderStorageBuffer snow_ssbo; static GL::ShaderStorageBuffer snow_gpu_ssbo; static GL::ShaderStorageBuffer snow_fallen_ssbo; static GL::UniformBuffer snow_particle_scene_ubo; static GL::UniformBuffer snow_particle_batch_ubo; static const size_t snow_ptcl_count = 0x8000; static const size_t snow_ptcl_fallen_count = 0x2000; static star_catalog star_catalog_data; static GL::UniformBuffer water_particle_scene_ubo; static float_t flt_140C9A588 = 2.0f; static int32_t dword_141195E48 = 0; static int32_t dword_141195E4C = 0; static TaskEffect** task_effect_data_array[] = { (TaskEffect**)&task_effect_auth_3d, 0, (TaskEffect**)&task_effect_leaf, 0, (TaskEffect**)&task_effect_snow, 0, (TaskEffect**)&task_effect_ripple, (TaskEffect**)&task_effect_rain, 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, (TaskEffect**)&task_effect_star, }; static const char* 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; static TaskEffect* effect_array_get(EffectType type, const char** name); static std::string effect_array_get_stage_param_file_path( EffectType type, int32_t stage_index, bool dev_ram, bool a4); static bool effect_array_parse_stage_param_data_fog_ring(stage_param_fog_ring* fog_ring, int32_t stage_index); static bool effect_array_parse_stage_param_data_leaf(stage_param_leaf* leaf, int32_t stage_index); static bool effect_array_parse_stage_param_data_litproj(stage_param_litproj* litproj, int32_t stage_index); static bool effect_array_parse_stage_param_data_rain(stage_param_rain* rain, int32_t stage_index); static bool effect_array_parse_stage_param_data_ripple(stage_param_ripple* ripple, int32_t stage_index); static bool effect_array_parse_stage_param_data_snow(stage_param_snow* snow, int32_t stage_index); static bool effect_array_parse_stage_param_data_splash(stage_param_splash* splash, int32_t stage_index); static bool effect_array_parse_stage_param_data_star(stage_param_star* star, int32_t stage_index); static void draw_fog_particle(EffectFogRing* data, mat4* mat); static void draw_ripple_particles(ripple_struct* data, mat4* mat); static void draw_water_particle(water_particle* data, mat4* mat); 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_rot_data* data, int32_t count); static particle_rot_data* particle_emit(); static void particle_event(particle_event_data* event_data); static void particle_kill(particle_rot_data* data); static void particle_free(); static void rain_particle_init(bool change_stage = false); static void rain_particle_ctrl(); static void rain_particle_free(); static void ripple_emit_init(); static void ripple_emit_free(); static void snow_particle_init(bool change_stage = false); static void snow_particle_ctrl(); static void snow_particle_data_init(); static void snow_particle_data_emit_fallen(particle_data* data); static void snow_particle_data_kill_fallen(particle_data* data, bool kill); static void snow_particle_data_reset(particle_data* data); static void snow_particle_data_free(); static particle_data* snow_particle_emit_fallen(); static vec3 snow_particle_get_random_velocity(); static void snow_particle_free(); static void water_particle_init(); static void water_particle_free(); static void sub_1403B6ED0(RenderTexture* a1, RenderTexture* a2, RenderTexture* a3, ripple_emit_params& params); static void sub_1403B6F60(GLuint a1, GLuint a2, GLuint a3, ripple_emit_params& params); particle_event_data::particle_event_data() : type(), count(), size(), force() { } 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_manager_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.WriteMemory(shader_data); gl_state_active_bind_texture_2d(0, tex->glid); 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_manager_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.WriteMemory(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->glid); 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, (int32_t)rain_ptcl_count) / 2 / 4 * 6; vec4 color = rain->color; float_t color_a = color.w; rain_particle_scene_ubo.Bind(0); rain_particle_batch_ubo.Bind(1); rain_ssbo.Bind(0); gl_state_bind_vertex_array(rctx_ptr->common_vao); for (int32_t i = 0; i < 8; i++, first += count) { 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.WriteMemory(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 = (particle_vertex_data*)ptcl_vbo.MapMemory(); if (!vtx_data) return; int32_t count = particle_disp(vtx_data, ptcl_data, ptcl_count); ptcl_vbo.UnmapMemory(); if (!count) return; const light_data& light_chara = rctx_ptr->light_set[LIGHT_SET_MAIN].lights[LIGHT_CHARA]; 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); 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); shaders_ft.set(SHADER_FT_PARTICL); particle_scene_ubo.Bind(0); gl_state_bind_vertex_array(ptcl_vao); shaders_ft.draw_arrays(GL_TRIANGLES, 0, count); gl_state_bind_vertex_array(0); } void snow_particle_draw() { if (!stage_param_data_snow_current || !snow_particle_enable || !stage_param_data_snow_set) return; texture* tex = texture_manager_get_texture(snow_particle_tex_id); if (!tex) return; stage_param_snow* snow = stage_param_data_snow_current; draw_pass_set_camera(); 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); float_t point_attenuation = powf(tanf((float_t)rctx_ptr->camera->get_fov() * 0.5f * DEG_TO_RAD_FLOAT) * 3.4f, 2.0f) * 0.1f; snow_particle_scene_shader_data snow_scene = {}; mat4 temp; mat4_transpose(&rctx_ptr->vp_mat, &temp); snow_scene.g_transform[0] = temp.row0; snow_scene.g_transform[1] = temp.row1; snow_scene.g_transform[2] = temp.row2; snow_scene.g_transform[3] = temp.row3; mat4_transpose(&rctx_ptr->view_mat, &temp); snow_scene.g_view_world_row2 = temp.row2; snow_scene.g_size_in_projection.x = 1.0f / (float_t)rctx_ptr->render.render_width[0]; snow_scene.g_size_in_projection.y = 1.0f / (float_t)rctx_ptr->render.render_height[0]; snow_scene.g_size_in_projection.z = snow_particle_size_min; snow_scene.g_size_in_projection.w = snow_particle_size_max; snow_scene.g_state_point_attenuation = { 0.0f, 0.0f, point_attenuation, 0.0f }; snow_scene.g_range_scale.x = snow->range_gpu.x; snow_scene.g_range_scale.y = snow->range_gpu.y; snow_scene.g_range_scale.z = snow->range_gpu.z; snow_scene.g_range_offset.x = snow->offset_gpu.x - snow->range_gpu.x * 0.5f; snow_scene.g_range_offset.y = snow->offset_gpu.y; snow_scene.g_range_offset.z = snow->offset_gpu.z - snow->range_gpu.z * 0.5f; snow_particle_scene_ubo.WriteMemory(snow_scene); snow_particle_batch_shader_data snow_batch = {}; snow_batch.g_color = snow->color; snow_batch.start_vertex_location.x = 0; snow_particle_batch_ubo.WriteMemory(snow_batch); gl_state_active_bind_texture_2d(0, tex->glid); gl_state_active_bind_texture_2d(1, rctx_ptr->render.rend_texture[0].GetDepthTex()); gl_state_bind_vertex_array(rctx_ptr->common_vao); uniform_value[U_SNOW_PARTICLE] = 0; shaders_ft.set(SHADER_FT_SNOW_PT); snow_particle_scene_ubo.Bind(0); snow_particle_batch_ubo.Bind(1); snow_ssbo.Bind(0); shaders_ft.draw_arrays(GL_TRIANGLES, 0, snow->num_snow * 6); snow_fallen_ssbo.Bind(0); shaders_ft.draw_arrays(GL_TRIANGLES, 0, (GLsizei)(snow_ptcl_fallen_count * 6)); uniform_value[U_SNOW_PARTICLE] = 1; shaders_ft.set(SHADER_FT_SNOW_PT); snow_particle_scene_ubo.Bind(0); snow_particle_batch_ubo.Bind(1); point_attenuation = powf(tanf((float_t)rctx_ptr->camera->get_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 }; snow_particle_scene_ubo.WriteMemory(snow_scene); snow_gpu_ssbo.Bind(0); int32_t count = snow->num_snow_gpu / 4; count = min_def(count, (int32_t)snow_ptcl_count / 4) * 6; int32_t first = 0; for (particle_data& i : snow_ptcl_gpu) { vec3 pos_offset = i.position / snow->range_gpu; vec4 color = snow->color; color.w *= i.alpha; snow_batch = {}; snow_batch.g_pos_offset = { pos_offset.x, pos_offset.y, pos_offset.z, 0.0f }; snow_batch.g_color = color; snow_batch.start_vertex_location.x = first; snow_particle_batch_ubo.WriteMemory(snow_batch); shaders_ft.draw_arrays(GL_TRIANGLES, 0, count); first += count; } gl_state_bind_vertex_array(0); gl_state_active_bind_texture_2d(1, 0); gl_state_active_bind_texture_2d(0, 0); gl_state_disable_depth_test(); gl_state_set_depth_mask(GL_TRUE); gl_state_disable_blend(); } void star_catalog_draw() { star_catalog_data.draw(); } void 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 (!effect_manager) effect_manager = new EffectManager; if (!effect_ripple) effect_ripple = new EffectRipple; effect_fog_anim = 0; effect_fog_ring = 0; effect_splash = 0; } void 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 (effect_manager) { delete effect_manager; effect_manager = 0; } if (effect_ripple) { delete effect_ripple; effect_ripple = 0; } effect_fog_anim = 0; effect_fog_ring = 0; effect_splash = 0; } void effect_fog_ring_data_reset() { if (effect_fog_ring) effect_fog_ring->restart_effect(); } void effect_splash_data_reset() { if (effect_splash) effect_splash->restart_effect(); } void effect_manager_event(EffectType type, int32_t event_type, void* data) { effect_manager->event(type, event_type, data); } void effect_manager_dest() { effect_manager->dest(); } bool effect_manager_load() { return effect_manager->load(); } void effect_manager_reset() { effect_manager->reset(); } void effect_manager_set_current_stage_hash(uint32_t stage_hash) { effect_manager->set_current_stage_hash(stage_hash); } void effect_manager_set_current_stage_index(int32_t stage_index) { effect_manager->set_current_stage_index(stage_index); } void effect_manager_set_data(void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data) { effect_manager->data = data; effect_manager->obj_db = obj_db; effect_manager->tex_db = tex_db; effect_manager->stage_data = stage_data; } void effect_manager_set_enable(bool value) { effect_manager->set_enable(value); } void effect_manager_set_frame(int32_t value) { effect_manager->set_frame(value); } void effect_manager_set_frame_rate_control(FrameRateControl* value) { effect_manager->set_frame_rate_control(value); } void effect_manager_set_stage_hashes(const std::vector& stage_hashes) { effect_manager->set_stage_hashes(stage_hashes); } void effect_manager_set_stage_indices(const std::vector& stage_indices) { effect_manager->set_stage_indices(stage_indices); } bool effect_manager_unload() { return effect_manager->unload(); } TaskEffect::TaskEffect() { } TaskEffect::~TaskEffect() { } void TaskEffect::pre_init(int32_t stage_index) { } void TaskEffect::set_stage_hashes(const std::vector& stage_hashes, void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data) { } void TaskEffect::set_stage_indices(const std::vector& stage_indices) { pre_init(stage_indices[0]); } void TaskEffect::set_frame(int32_t value) { } void TaskEffect::field_48() { } void TaskEffect::set_enable(bool value) { } void TaskEffect::set_current_stage_hash(uint32_t value) { } void TaskEffect::set_current_stage_index(int32_t value) { } void TaskEffect::set_frame_rate_control(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() { } EffectAuth3d::EffectAuth3d() : 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()) { del(); 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_mul_rotate_x(&mat, sinf((float_t)((double_t)((float_t)(frame_int % 512) * (float_t)(2.0 / 512.0)) * M_PI)) * 0.015f, &mat); mat4_mul_rotate_z(&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); reset_data(); return true; } void TaskEffectAuth3D::disp() { } void TaskEffectAuth3D::pre_init(int32_t stage_index) { } void TaskEffectAuth3D::set_stage_hashes(const 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_id(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::set_stage_indices(const 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_id(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::set_frame(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::set_current_stage_hash(uint32_t value) { if (current_stage_hash == value) return; set_visibility(false); current_stage_hash = value; set_visibility(enable); } void TaskEffectAuth3D::set_current_stage_index(int32_t value) { if (current_stage_index == value) return; set_visibility(false); current_stage_index = value; set_visibility(enable); } void TaskEffectAuth3D::set_frame_rate_control(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::reset_data() { for (int32_t i = 0; i < stage.count; i++) { auth_3d_id& id = stage.auth_3d_ids_ptr[i]; id.unload(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::set_visibility(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::set_enable(bool value) { enable = value; set_visibility(value); } EffectFogAnim::EffectFogAnim() : field_0(), field_4(), field_8(), field_C(), field_18(), field_24(), field_28(), field_2C(), field_30(), field_34() { reset(); } EffectFogAnim::~EffectFogAnim() { } void EffectFogAnim::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 EffectFogAnim::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(); effect_fog_anim = 0; //sub_1400DE640("TaskEffectFogAnim::dest()\n"); return true; } void TaskEffectFogAnim::disp() { } void TaskEffectFogAnim::pre_init(int32_t) { data.reset(); effect_fog_anim = &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() { } struc_371::struc_371() : field_0(), field_10(), field_14(), field_24() { } struc_573::struc_573() : chara_index(), bone_index() { } EffectFogRing::EffectFogRing() : 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(), display(), frame_rate_control() { current_stage_index = -1; } EffectFogRing::~EffectFogRing() { } void EffectFogRing::calc_ptcl(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 EffectFogRing::calc_vert() { float_t density = this->density; fog_ring_data* ptcl_data = this->ptcl_data; for_ring_vertex_data* ptcl_vtx_data = (for_ring_vertex_data*)ssbo.MapMemory(); if (!ptcl_vtx_data) { num_vtx = 0; return; } vec4 color = this->color; for (int32_t i = num_ptcls; i > 0; i--, ptcl_data++, ptcl_vtx_data++) { float_t size = ptcl_data->size * (float_t)(1.0 / 256.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->position = position; ptcl_vtx_data->color = color; ptcl_vtx_data->size = size; } ssbo.UnmapMemory(); num_vtx = (int32_t)(num_ptcls * 6LL); } void EffectFogRing::ctrl() { if (!display) return; ctrl_inner(delta_frame * (float_t)(1.0 / 60.0)); calc_vert(); field_8 = 0; } void EffectFogRing::ctrl_inner(float_t delta_time) { if (fabsf(delta_time) <= 0.000001f) return; sub_140347B40(delta_time); calc_ptcl(delta_time); } void EffectFogRing::dest() { stage_param_data_fog_ring_storage_clear(); if (ptcl_data) { free(ptcl_data); ptcl_data = 0; } ssbo.Destroy(); rctx_ptr->render_manager->set_pass_sw(rndr::RND_PASSID_PRE_PROCESS, false); rctx_ptr->render_manager->clear_pre_process(0); rctx_ptr->draw_state->set_fog_height(false); } void EffectFogRing::disp() { if (enable && display) rctx_ptr->disp_manager->entry_obj_user(&mat4_identity, (mdl::UserArgsFunc)draw_fog_particle, this, mdl::OBJ_TYPE_USER); } void EffectFogRing::draw() { render_context* rctx = rctx_ptr; rctx->draw_state->set_fog_height(false); if (!enable || !display) return; rctx->draw_state->set_fog_height(true); RenderTexture& 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); gl_state_get_error(); } void EffectFogRing::init_particle_data() { 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 = ptcl_random(ring_size); float_t pos_z = ptcl_random(ring_size); float_t dir_x = ptcl_random(0.5f) + wind_dir_x; float_t dir_z = ptcl_random(0.5f) + wind_dir_z; float_t size = ptcl_random(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 EffectFogRing::restart_effect() { 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; } } init_particle_data(); field_8 = true; } void EffectFogRing::set_stage_indices(const std::vector& stage_indices) { rctx_ptr->render_manager->clear_pre_process(0); rctx_ptr->render_manager->set_pass_sw(rndr::RND_PASSID_PRE_PROCESS, false); display = false; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_fog_ring_storage_clear(); for (const int32_t& i : stage_indices) { stage_param_fog_ring fog_ring; if (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 = this->stage_indices.front(); stage_param_fog_ring* fog_ring = stage_param_data_fog_ring_storage_get_value(current_stage_index); if (!fog_ring) return; display = 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; } if (ptcl_data) { free(ptcl_data); ptcl_data = 0; } ssbo.Destroy(); ptcl_data = new (force_malloc(max_ptcls)) fog_ring_data[max_ptcls]; ssbo.Create(sizeof(for_ring_vertex_data) * max_ptcls); init_particle_data(); rctx_ptr->render_manager->set_pass_sw(rndr::RND_PASSID_PRE_PROCESS, true); rctx_ptr->render_manager->add_pre_process(0, EffectFogRing::draw_static, this); } void EffectFogRing::draw_static(void* data) { ((EffectFogRing*)data)->draw(); } float_t EffectFogRing::ptcl_random(float_t value) { return (float_t)(rand_state_array_get_int(4) % 10000) * 0.0001f * value * 2.0f - value; } void EffectFogRing::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_bone_data->get_mats_mat(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 EffectFogRing::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->get_delta_frame(); data.ctrl(); return 0; } bool TaskEffectFogRing::dest() { data.dest(); effect_fog_ring = 0; //sub_1400DE640("TaskEffectFogRing::dest()\n"); return 1; } void TaskEffectFogRing::disp() { data.disp(); } void TaskEffectFogRing::pre_init(int32_t stage_index) { } void TaskEffectFogRing::set_stage_indices(const std::vector& stage_indices) { set_frame_rate_control(); effect_fog_ring = &data; data.set_stage_indices(stage_indices); } void TaskEffectFogRing::set_enable(bool value) { data.enable = value; } void TaskEffectFogRing::set_current_stage_index(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.display = true; return; } } data.display = false; } void TaskEffectFogRing::set_frame_rate_control(FrameRateControl* value) { if (value) data.frame_rate_control = value; else data.frame_rate_control = get_sys_frame_rate(); } void TaskEffectFogRing::reset() { data.restart_effect(); } 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; set_frame_rate_control(); } return true; } bool TaskEffectLeaf::ctrl() { if (!stage_param_data_leaf_current) return false; leaf_particle_delta_frame = frame_rate_control->get_delta_frame(); 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::pre_init(int32_t stage_index) { } void TaskEffectLeaf::set_stage_indices(const 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 (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 = this->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::set_enable(bool value) { leaf_particle_enable = value; } void TaskEffectLeaf::set_current_stage_index(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::set_frame_rate_control(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = get_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 || get_pause()) 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::pre_init(int32_t stage_index) { } void TaskEffectLitproj::set_stage_indices(const 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 (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 (!this->stage_indices.size()) return; int32_t stage_index = this->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::set_enable(bool value) { light_proj_enable = value; } void TaskEffectLitproj::set_current_stage_index(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() { set_frame_rate_control(); return true; } bool TaskEffectParticle::ctrl() { particle_delta_time = frame_rate_control->get_delta_frame() * (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::pre_init(int32_t stage_index) { current_stage_index = stage_index; //field_74 = 0; particle_init(0); } void TaskEffectParticle::set_enable(bool value) { particle_enable = value; } void TaskEffectParticle::set_frame_rate_control(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = get_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(); set_frame_rate_control(); } return true; } bool TaskEffectRain::ctrl() { if (stage_param_data_rain_current) { rain_particle_delta_frame = frame_rate_control->get_delta_frame(); rain_particle_ctrl(); } return false; } bool TaskEffectRain::dest() { if (stage_param_data_rain_current) { rain_particle_free(); 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::pre_init(int32_t stage_index) { } void TaskEffectRain::set_stage_indices(const 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 (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 = this->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::set_enable(bool value) { rain_particle_enable = value; } void TaskEffectRain::set_current_stage_index(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); rain_particle_init(true); } } void TaskEffectRain::set_frame_rate_control(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = get_sys_frame_rate(); } void TaskEffectRain::reset() { if (stage_param_data_rain_current) { rain_particle_init(); set_frame_rate_control(); } } ripple_emit_draw_data::ripple_emit_draw_data() : data() { } struc_192::struc_192() { index = -1; } struc_207::struc_207() { } ripple_emit_params::ripple_emit_params() { wake_attn = 0.56f; speed = 1.0f; field_8 = 0.0005f; field_C = 0.9f; } EffectRipple::EffectRipple() : delta_frame(), update(), rain_ripple_num(), rain_ripple_min_value(), rain_ripple_max_value(), field_14(), emit_pos_scale(), emit_pos_ofs_x(), emit_pos_ofs_z(), ripple_tex_id(), use_float_ripplemap(), field_30(), rob_emitter_size(), emitter_num(), emitter_size(), field_4C(), field_50(), field_178(), field_2A0(), field_3C8(), field_4F0(), field_BB4(), counter(), field_BEC(), stage_set() { ground_y = -1001.0f; emitter_list = 0; current_stage_index = -1; } EffectRipple::~EffectRipple() { ground_y = -1001.0f; } void EffectRipple::clear_tex() { vec4 clear_color; glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); for (int32_t i = 0, j = 5; i < 3; i++, j++) { RenderTexture* rt; float_t v5; if (use_float_ripplemap) { rt = &rctx_ptr->render_manager->get_render_texture(j - 3); v5 = -0.3f; } else { rt = &rctx_ptr->render_manager->get_render_texture(j); v5 = 0.5f; } glClearColor(0.0f, 0.0f, 0.0f, v5); rt->Bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gl_state_bind_framebuffer(0); } glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); } void EffectRipple::ctrl() { if (delta_frame > 0.0f) update = true; } void EffectRipple::dest() { stage_param_data_ripple_storage_clear(); rctx_ptr->render_manager->set_pass_sw(rndr::RND_PASSID_PRE_PROCESS, false); rctx_ptr->render_manager->clear_pre_process(0); this->ground_y = -1001.0; } void EffectRipple::disp() { if (!stage_set) return; if (use_float_ripplemap) { if (field_30 == 60) sub_140358690(); else sub_14035AED0(); if (field_30 > 0) field_30--; } else { sub_14035AED0(); sub_14035AAE0(); } } void EffectRipple::disp_particles(ripple_struct* data) { if (data->count > 0) rctx_ptr->disp_manager->entry_obj_user(&mat4_identity, (mdl::UserArgsFunc)draw_ripple_particles, data, mdl::OBJ_TYPE_USER); } void EffectRipple::draw() { if (!stage_set) return; gl_state_disable_cull_face(); render_context* rctx = rctx_ptr; RenderTexture* rt[3]; for (int32_t i = 0, j = 2; i < 3; i++, j++) rt[i] = &rctx->render_manager->get_render_texture( use_float_ripplemap ? j : (j + 3)); if (update) { int32_t counter = this->counter + 1; if (counter >= 3) counter = 0; rt[(counter + 1) % 3]->Bind(); GLint v43[4]; glGetIntegerv(GL_VIEWPORT, v43); int32_t width = rt[0]->GetWidth(); int32_t height = rt[0]->GetHeight(); glViewport(1, 1, width - 2, height - 2); draw_pass_set_camera(); glClear(GL_DEPTH_BUFFER_BIT); if (rctx->disp_manager->get_obj_count(mdl::OBJ_TYPE_USER)) { gl_state_active_bind_texture_2d(7, rt[counter % 3]->GetColorTex()); rctx->disp_manager->draw(mdl::OBJ_TYPE_USER, 0, true); gl_state_active_bind_texture_2d(7, 0); } gl_state_bind_framebuffer(0); params.field_8 = 0.0005f; params.field_C = 0.97f; if (field_30 > 0) { params.field_8 = 0.00005f; params.field_C = 0.999f; } sub_1403B6ED0(rt[(counter + 2) % 3], rt[(counter + 1) % 3], rt[counter % 3], params); glViewport(v43[0], v43[1], v43[2], v43[3]); sub_1403584A0(rt[(counter + 2) % 3]); this->counter = counter; } int32_t v11 = (counter + 2) % 3 + 2; if (!use_float_ripplemap) v11 += 3; rctx->render_manager->set_effect_texture( rctx->render_manager->get_render_texture(v11).color_texture); update = false; gl_state_enable_cull_face(); } void EffectRipple::reset() { field_4F0 = 18; for (struc_207& i : field_4F4) for (int32_t j = 0; j < field_4F0; j++) i.field_0[j].trans = 0.0f; field_30 = 60; clear_tex(); } void EffectRipple::set_stage_index(int32_t stage_index) { if (current_stage_index == stage_index) return; current_stage_index = stage_index; stage_set = false; if (stage_index == -1) return; for (int32_t& i : stage_indices) { if (i != stage_index) continue; stage_set = true; set_stage_param(stage_param_data_ripple_storage_get_value(i)); clear_tex(); break; } } void EffectRipple::set_stage_indices(const std::vector& stage_indices) { static const int32_t dword_1409E5330[] = { 0, 1, 2, 4, 10, 5, 12, 7, 14, 9, 21, 15, 23, 17, 25, 19, 26, 20 }; stage_set = false; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_ripple_storage_clear(); for (const int32_t& i : stage_indices) { stage_param_ripple ripple; if (effect_array_parse_stage_param_data_ripple(&ripple, i)) { this->stage_indices.push_back(i); stage_param_data_ripple_storage_set_stage_data(i, &ripple); } } if (!this->stage_indices.size()) return; current_stage_index = this->stage_indices.front(); stage_param_ripple* ripple = stage_param_data_ripple_storage_get_value(this->stage_indices.front()); if (!ripple) return; stage_set = true; set_stage_param(ripple); delta_frame = 1.0f; counter = 0; field_BEC = 0; rctx_ptr->render_manager->set_pass_sw(rndr::RND_PASSID_PRE_PROCESS, true); rctx_ptr->render_manager->add_pre_process(0, EffectRipple::draw_static, this); field_4F0 = 18; for (struc_207& i : field_4F4) for (int32_t j = 0; j < field_4F0; j++) { i.field_0[j].index = dword_1409E5330[j]; i.field_0[j].trans = 0.0f; } update = false; field_30 = 60; clear_tex(); } void EffectRipple::set_stage_param(stage_param_ripple* ripple) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; params.wake_attn = ripple->wake_attn; params.speed = ripple->speed; rain_ripple_num = (int32_t)ripple->rain_ripple_num; rain_ripple_min_value = ripple->rain_ripple_min_value; rain_ripple_max_value = ripple->rain_ripple_max_value; ground_y = ripple->ground_y; emit_pos_scale = ripple->emit_pos_scale; emit_pos_ofs_x = ripple->emit_pos_ofs_x; emit_pos_ofs_z = ripple->emit_pos_ofs_z; ripple_tex_id = aft_tex_db->get_texture_id(ripple->ripple_tex_name.c_str()); use_float_ripplemap = ripple->use_float_ripplemap; rob_emitter_size = ripple->rob_emitter_size; emitter_num = ripple->emitter_num; emitter_list = ripple->emitter_list.data(); emitter_size = ripple->emitter_size; } void EffectRipple::draw_static(void* data) { ((EffectRipple*)data)->draw(); } void EffectRipple::sub_1403584A0(RenderTexture* rt) { if (ripple_tex_id == -1) return; texture* ripple_tex = texture_manager_get_texture(ripple_tex_id); if (!ripple_tex) return; field_BB8.SetColorDepthTextures(ripple_tex->glid); field_BB8.Bind(); image_filter_scale(ripple_tex->glid, rt->GetColorTex(), 1.0f); gl_state_bind_framebuffer(0); } void EffectRipple::sub_140358690() { ripple_emit_draw_data& v1 = field_178; v1.data.position = field_50.position; v1.data.color = field_50.color; for (int32_t i = 0; i < 16; i++) { v1.data.position[i].x = rand_state_array_get_float(4) * 1.8f - 0.9f; v1.data.position[i].y = rand_state_array_get_float(4) * 1.8f - 0.9f; rand_state_array_get_int(0x03, 0x07, 4); v1.data.position[i].z = (float_t)rand_state_array_get_int(0x20, 0xA0, 4); } v1.data.count = 16; v1.data.ripple_uniform = 1; v1.data.ripple_emit_uniform = 1; disp_particles(&v1.data); } void EffectRipple::sub_1403587C0(const vec3 a2, const vec3 a3, float_t a4, ripple_struct& a5, ripple_struct& a6) { vec3 v34 = a3 - a2; float_t v17 = ground_y - (a2.y - a4); int32_t v19 = 1 - (int32_t)(min_def(v17, a4) * -30.0f); int32_t v20 = v19 + 3; float_t v21 = sqrtf(v34.x * v34.x + v34.z * v34.z) * 0.8f; if (v21 < 0.03f) v21 = 0.0f; if (a3.y - a4 > ground_y) { v21 += (float_t)(v34.y * 3.0f); v20 += (int32_t)(float_t)(v34.y * 30.0f); } float_t v23 = sqrtf(v34.x * v34.x + v34.z * v34.z); if (v23 >= 0.01f) v34 *= 1.0f / v23; else v34 = 0.0f; float_t v24 = min_def(v21, 0.2f); if (v23 > 0.5f) { v23 = 0.01f; v24 = 0.0f; } int32_t v33 = v20 - (int32_t)(v23 * -20.0f); int32_t v37 = (int32_t)(a4 * 60.0f); int32_t v27 = v33 >= v37 ? v37 : v33; color4u8 v19a = { 0x08, 0x00, 0x00, (uint8_t)v19 }; color4u8 v29 = { (uint8_t)v27, 0x00, 0x00, (uint8_t)(int32_t)(v24 * 1275.0f) }; for (float_t i = 0.0f; i < v23; i += 0.03f) { if (v24 > 0.0f) { int32_t count = a6.count; if (count < 16) { a6.position[count].x = (a2.x - v34.x * 0.2f + i * v34.x) * emit_pos_scale; a6.position[count].y = 0.0; a6.position[count].z = (a2.z - v34.z * 0.2f + i * v34.z) * emit_pos_scale; a6.color[count] = v29; a6.count++; } } int32_t count = a5.count; if (count < 16) { a5.position[count].x = (a2.x + i * v34.x) * emit_pos_scale; a5.position[count].y = 0.0f; a5.position[count].z = (a2.z + i * v34.z) * emit_pos_scale; a6.color[count] = v19a; a5.count++; } } } void EffectRipple::sub_14035AAE0() { ripple_emit_draw_data& v1 = field_3C8; field_3C8.data.count = 0; field_3C8.data.position = field_3C8.position; field_3C8.data.color = field_3C8.color; field_3C8.data.size = emitter_size; if (!update) return; if (rain_ripple_num) { int32_t min_value = (int32_t)(rain_ripple_min_value * 127.0f); int32_t max_value = (int32_t)(rain_ripple_max_value * 127.0f - rain_ripple_min_value * 127.0f); max_value = max_def(max_value, 1); min_value = min_def(min_value, 126); for (int32_t i = 0; i < rain_ripple_num; i++) { v1.data.position[i].x = (float_t)(rand_state_array_get_int(4) % 1000) * 0.001f * 2.0f - 1.0f; v1.data.position[i].y = 0.0f; v1.data.position[i].y = (float_t)(rand_state_array_get_int(4) % 1000) * 0.001f * 2.0f - 1.0f; v1.data.color[i] = { 0x00, 0x00, 0x00, (uint8_t)(0x7F - rand_state_array_get_int(4) % max_value - min_value) }; v1.data.count++; } } for (size_t i = 0; i < emitter_num; i++) { float_t v10 = emitter_list[i].z; v1.data.position[i].x = ((rand_state_array_get_float(4) - 0.5f) * v10 + emitter_list[i].x) * emit_pos_scale; v1.data.position[i].y = 0.0f; v1.data.position[i].z = ((rand_state_array_get_float(4) - 0.5f) * v10 + emitter_list[i].y) * emit_pos_scale; v1.data.color[i] = rand_state_array_get_float(4) < 0.5f ? color4u8(0x00, 0x00, 0x00, 0xFF) : color4u8(0x00); v1.data.count++; } v1.data.ripple_uniform = use_float_ripplemap ? 1 : 0; v1.data.ripple_emit_uniform = 0; disp_particles(&v1.data); } void EffectRipple::sub_14035AED0() { field_178.data.count = 0; field_178.data.position = field_178.position; field_178.data.color = field_178.color; field_178.data.size = rob_emitter_size; field_2A0.data.count = 0; field_2A0.data.position = field_2A0.position; field_2A0.data.color = field_2A0.color; field_2A0.data.size = rob_emitter_size; if (!update) return; ripple_emit_draw_data& v2 = field_178; ripple_emit_draw_data& v3 = field_2A0; int32_t chara_id = 0; for (struc_207& i : field_4F4) { if (!rob_chara_array_check_visibility(chara_id)) { chara_id++; continue; } rob_chara* rob_chr = rob_chara_array_get(chara_id); if (!rob_chr) { chara_id++; continue; } for (int32_t j = 0; j < field_4F0; j++) { struc_192& v4 = i.field_0[j]; vec3 trans = 0.0f; float_t scale = rob_chr->get_trans_scale(v4.index, trans); if (trans.y - ground_y < scale) { if (use_float_ripplemap) sub_1403587C0(trans, v4.trans, scale, v2.data, v3.data); else if (v2.data.count < 16) { v2.data.position[v2.data.count].x = ((rand_state_array_get_float(4) - 0.5f) * 0.02f + trans.x) * emit_pos_scale + emit_pos_ofs_x; v2.data.position[v2.data.count].y = trans.y; v2.data.position[v2.data.count].z = ((rand_state_array_get_float(4) - 0.5f) * 0.02f + trans.z) * emit_pos_scale + emit_pos_ofs_z; v2.data.color[v2.data.count] = { 0x00, 0x00, 0x00, 0x00 }; v2.data.count++; } } v4.trans = trans; } chara_id++; } if (use_float_ripplemap) { v3.data.ripple_uniform = !!use_float_ripplemap; v3.data.ripple_emit_uniform = 1; disp_particles(&v3.data); } v2.data.ripple_uniform = !!use_float_ripplemap; v2.data.ripple_emit_uniform = 0; disp_particles(&v2.data); } TaskEffectRipple::TaskEffectRipple() : field_68(), frame_rate_control(), emit() { } TaskEffectRipple::~TaskEffectRipple() { } bool TaskEffectRipple::init() { ripple_emit_init(); //sub_1400DE640("TaskEffectRipple::init()\n"); return true; } bool TaskEffectRipple::ctrl() { emit->delta_frame = frame_rate_control->get_delta_frame(); emit->ctrl(); return false; } bool TaskEffectRipple::dest() { emit->dest(); //sub_1400DE640("TaskEffectRipple::dest()\n"); ripple_emit_free(); return true; } void TaskEffectRipple::disp() { emit->disp(); } void TaskEffectRipple::pre_init(int32_t stage_index) { } void TaskEffectRipple::set_stage_indices(const std::vector& stage_indices) { set_frame_rate_control(); emit = effect_ripple; emit->set_stage_indices(stage_indices); } void TaskEffectRipple::set_current_stage_index(int32_t value) { emit->set_stage_index(value); } void TaskEffectRipple::set_frame_rate_control(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = get_sys_frame_rate(); } void TaskEffectRipple::reset() { if (!emit->use_float_ripplemap) effect_ripple->clear_tex(); effect_ripple->reset(); } TaskEffectSnow::TaskEffectSnow() : frame_rate_control() { current_stage_index = -1; } TaskEffectSnow::~TaskEffectSnow() { } bool TaskEffectSnow::init() { if (stage_param_data_snow_current) { snow_particle_init(); set_frame_rate_control(); } return true; } bool TaskEffectSnow::ctrl() { if (stage_param_data_snow_current) { snow_particle_delta_frame = frame_rate_control->get_delta_frame(); snow_particle_ctrl(); } return false; } bool TaskEffectSnow::dest() { snow_particle_data_free(); snow_particle_free(); stage_param_data_snow_current = 0; stage_param_data_snow_storage_clear(); stage_param_data_snow_set = 0; current_stage_index = -1; stage_indices.clear(); return 1; } void TaskEffectSnow::disp() { } void TaskEffectSnow::basic() { } void TaskEffectSnow::pre_init(int32_t stage_index) { } void TaskEffectSnow::set_stage_indices(const std::vector& stage_indices) { if (stage_param_data_snow_current) dest(); stage_param_data_snow_set = 0; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_snow_storage_clear(); for (int32_t i : stage_indices) { stage_param_snow snow; if (effect_array_parse_stage_param_data_snow(&snow, i)) { this->stage_indices.push_back(i); stage_param_data_snow_storage_set_stage_data(i, &snow); } } stage_param_data_snow_current = 0; if (!this->stage_indices.size()) return; int32_t stage_index = this->stage_indices.front(); current_stage_index = stage_index; stage_param_snow* snow = stage_param_data_snow_storage_get_value(stage_index); snow_particle_tex_id = -1; if (snow->tex_name.size()) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; snow_particle_tex_id = aft_tex_db->get_texture_id(snow->tex_name.c_str()); } if (snow) { stage_param_data_snow_current = snow; stage_param_data_snow_set = true; } } void TaskEffectSnow::set_enable(bool value) { snow_particle_enable = value; } void TaskEffectSnow::set_current_stage_index(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_snow_set = found; if (found) { stage_param_data_snow_current = stage_param_data_snow_storage_get_value(value); snow_particle_init(true); } } void TaskEffectSnow::set_frame_rate_control(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = get_sys_frame_rate(); } void TaskEffectSnow::reset() { if (stage_param_data_snow_current) { snow_particle_init(); set_frame_rate_control(); } } splash_particle_data::splash_particle_data() : size(), life_time(), field_20(), index(), flags(), alpha() { } splash_particle::splash_particle() : count(), alive(), dead() { reset(); } splash_particle::~splash_particle() { free(); } void splash_particle::ctrl(float_t delta_time) { const float_t gravity = delta_time * -9.8f; if (!data.size() || fabsf(delta_time) <= 0.000001f) return; for (splash_particle_data& i : data) { if (i.life_time <= 0.0f) continue; i.direction.y = gravity + i.direction.y; i.position = i.direction * delta_time + i.position; i.life_time -= 1.0f; if (i.life_time < 1.0f) kill(i.index); } } splash_particle_data* splash_particle::emit() { if (dead < 0) return 0; int32_t index = available.data()[dead--]; splash_particle_data* ptcl = &this->data.data()[index]; ptcl->index = index; alive++; return ptcl; } void splash_particle::free() { available.clear(); dead = 0; data.clear(); count = 0; alive = 0; } void splash_particle::init(int32_t count) { reset(); this->count = count; alive = 0; dead = 0; data.clear(); data.resize(count); available.clear(); available.resize(count); restart(); } void splash_particle::kill(int32_t index) { if (!available.size() || !data.size() || dead >= count - 1 || index < 0 || index >= count) return; available[dead++] = index; data[index].index = -1; data[index].life_time = 0.0; alive--; } void splash_particle::reset() { count = 0; alive = 0; data.clear(); dead = 0; available.clear(); } void splash_particle::restart() { dead = count - 1; int32_t index = 0; for (int32_t& i : available) i = index++; for (splash_particle_data& i : data) { i.position = 0.0f; i.direction = 0.0f; i.size = 1.0f; i.life_time = 0.0f; i.field_20 = 1.0f; i.index = -1; i.flags = 0x00; i.alpha = 1.0f; } alive = 0; } ParticleEmitter::ParticleEmitter() : splash(), field_1C(), field_20(), field_24(), field_28(), particle_size() { reset_data(); } ParticleEmitter::~ParticleEmitter() { } void ParticleEmitter::ctrl(float_t delta_time) { } void ParticleEmitter::restart() { } void ParticleEmitter::reset_data() { splash = 0; trans = 0.0f; field_1C = 0; field_20 = 1.0f; field_24 = 0; particle_size = 1.0f; field_28 = 1.0f; } ParticleEmitterRob::ParticleEmitterRob() : chara_id(), bone_index(), emit_num(), field_60(), emission_ratio_attn(), emission_velocity_scale(), in_water(), init_trans() { reset_data(); } ParticleEmitterRob::~ParticleEmitterRob() { } static inline int32_t rand_a_get() { int32_t val = dword_141195E48 + 1; if (val + 1 > 241) val = 0; dword_141195E48 = val; return val; } static inline float_t rand_a_get_float() { return *(float_t*)&flt_1409E59C0[rand_a_get()]; } static inline int32_t rand_b_get() { int32_t val = dword_141195E4C + 3; if (val + 3 > 241) val = 0; dword_141195E4C = val; return val; } static inline vec3 rand_b_get_float() { return *(vec3*)&flt_1409E5D90[rand_b_get()]; } void ParticleEmitterRob::ctrl(float_t delta_time) { if (fabsf(delta_time) <= 0.000001f || !splash || !rob_chara_array_get(chara_id) || !rob_chara_array_check_visibility(chara_id)) return; get_trans(); get_velocity(delta_time); float_t v8 = vec3::length(velocity); if (v8 > 100.0f) return; if (trans.y >= 0.3f) field_60 = max_def(field_60 - delta_time * emission_ratio_attn, 0.0f); else field_60 = 1.0f; if (v8 < 1.0f) return; float_t v27 = min_def(v8 * 0.05f, 1.0f); int32_t emit_num = (int32_t)((float_t)this->emit_num * (v27 * v27) * field_60); if (emit_num <= 0) return; vec3 prev_velocity = this->prev_velocity * emission_velocity_scale; vec3 velocity = this->velocity * emission_velocity_scale;; vec3 trans_diff = trans - prev_trans; vec3 velocity_diff = velocity - prev_velocity; float_t v41 = flt_140C9A588; for (int32_t i = 0; i < emit_num; i++) { splash_particle_data* ptcl = splash->emit(); if (!ptcl) break; float_t diff_scale = rand_a_get_float(); vec3 rand_vec = rand_b_get_float(); ptcl->position = rand_vec * 0.05f + (trans_diff * diff_scale + prev_trans); ptcl->direction = rand_vec * 0.65f + (velocity_diff * diff_scale + prev_velocity); float_t size_scale = rand_a_get_float();; ptcl->flags = 0x01; ptcl->size = max_def(size_scale * size_scale * particle_size, 1.0f); if (in_water && (trans_diff.y * diff_scale) + prev_trans.y < 0.3f) { ptcl->flags = 0x00; ptcl->size += v41; ptcl->direction.y += 0.8f; } ptcl->life_time = 100.0f; ptcl->field_20 = 1.0f; float_t v56 = min_def(9.0f / ptcl->size, 1.0f); ptcl->alpha = 1.0f - v56 * v56 + 0.3f; } } void ParticleEmitterRob::restart() { velocity = 0.0f; prev_velocity = 0.0f; init_trans = true; field_60 = 0.0f; } void ParticleEmitterRob::get_trans() { vec3 trans; rob_chara_array_get(chara_id)->get_trans_scale(bone_index, trans); if (init_trans) { prev_trans = trans; init_trans = false; } else prev_trans = this->trans; this->trans = trans; } void ParticleEmitterRob::get_velocity(float_t delta_time) { prev_velocity = velocity; velocity = (trans - prev_trans) * (1.0f / delta_time); } void ParticleEmitterRob::reset_data() { bone_index = -1; chara_id = 0; prev_trans = 0.0f; velocity = 0.0f; prev_velocity = 0.0f; emit_num = 0; field_60 = 0.0f; emission_ratio_attn = 0.0f; emission_velocity_scale = 0.0f; in_water = true; init_trans = true; } void ParticleEmitterRob::reset_data_base() { ParticleEmitter::reset_data(); } void ParticleEmitterRob::set_chara(int32_t chara_id, int32_t bone_index, bool in_water) { reset_data_base(); reset_data(); this->chara_id = chara_id; this->bone_index = bone_index; prev_trans = 0.0f; velocity = 0.0f; prev_velocity = 0.0f; emit_num = 0; field_60 = 0.0f; emission_ratio_attn = 0.2f; emission_velocity_scale = 0.08f; this->in_water = in_water; init_trans = true; } void ParticleEmitterRob::set_splash(splash_particle* value) { splash = value; } water_particle_vertex_data::water_particle_vertex_data() : size() { } water_particle::water_particle() { reset(); } water_particle::~water_particle() { free(); } void water_particle::ctrl() { const vec4 color = this->color; count = 0; int32_t count = 0; int32_t ripple_count = 0; const float_t emit_pos_scale = effect_ripple->emit_pos_scale; const vec3 ripple_position = vec3(emit_pos_scale, 0.0f, emit_pos_scale); const splash_particle_data* splash_ptcl = splash->data.data(); water_particle_vertex_data* ptcl = ptcl_data.data(); if (blink) for (int32_t i = splash->count; i > 0; i--, splash_ptcl++) { if (splash_ptcl->life_time <= 0.0f) continue; ptcl->position = splash_ptcl->position; ptcl->size = splash_ptcl->size; ptcl->color = color; float_t val = rand_a_get_float(); ptcl->color.w = (val * val) * (val * val) * color.w * splash_ptcl->alpha; ptcl++; count++; if (splash_ptcl->position.y < 0.0f) { if (splash_ptcl->flags & 0x01) { ripple.position[ripple_count] = ripple_position * splash_ptcl->position; ripple.color[ripple_count] = color4u8(0x00, 0x00, 0x00, (uint8_t)(int32_t)(ripple_emission * 64.0f)); ripple_count++; } splash->kill(splash_ptcl->index); } } else for (int32_t i = splash->count; i > 0; i--, splash_ptcl++) { if (splash_ptcl->life_time <= 0.0f) continue; ptcl->position = splash_ptcl->position; ptcl->size = splash_ptcl->size; ptcl->color = color; ptcl++; count++; if (splash_ptcl->position.y < 0.0f) { if (splash_ptcl->flags & 0x01) { ripple.position[ripple_count] = ripple_position * splash_ptcl->position; ripple.color[ripple_count] = color4u8(0x00, 0x00, 0x00, (uint8_t)(int32_t)(ripple_emission * 64.0f)); ripple_count++; } splash->kill(splash_ptcl->index); } } this->count = count; ripple.count = ripple_count; } void water_particle::disp() { if (!splash) return; rctx_ptr->disp_manager->entry_obj_user(&mat4_identity, (mdl::UserArgsFunc)draw_water_particle, this, mdl::OBJ_TYPE_TRANSLUCENT); if (effect_ripple && !effect_ripple->use_float_ripplemap) { ripple.ripple_uniform = 0; ripple.ripple_emit_uniform = 0; effect_ripple->disp_particles(&ripple); } } void water_particle::draw(mat4* mat) { if (count <= 0) return; ssbo.WriteMemory(0, sizeof(water_particle_vertex_data) * count, ptcl_data.data()); water_particle_scene_shader_data scene_shader_data = {}; mat4 temp; mat4_mul(mat, &rctx_ptr->vp_mat, &temp); mat4_transpose(&temp, &temp); scene_shader_data.g_transform[0] = temp.row0; scene_shader_data.g_transform[1] = temp.row1; scene_shader_data.g_transform[2] = temp.row2; scene_shader_data.g_transform[3] = temp.row3; mat4_mul(mat, &rctx_ptr->view_mat, &temp); mat4_transpose(&temp, &temp); scene_shader_data.g_view_world_row2 = temp.row2; scene_shader_data.g_state_point_attenuation = { 0.7f, 0.4f, 0.0f, 0.0f }; scene_shader_data.g_size_in_projection.x = (float_t)(1.0 / 1280.0); scene_shader_data.g_size_in_projection.y = (float_t)(1.0 / 720.0); scene_shader_data.g_size_in_projection.z = 1.0; scene_shader_data.g_size_in_projection.w = 60.0; water_particle_scene_ubo.WriteMemory(scene_shader_data); gl_state_disable_cull_face(); shaders_ft.set(SHADER_FT_W_PTCL); gl_state_bind_vertex_array(rctx_ptr->common_vao); water_particle_scene_ubo.Bind(4); ssbo.Bind(0); texture* tex = texture_manager_get_texture(splash_tex_id); if (tex) gl_state_active_bind_texture_2d(0, tex->glid); shaders_ft.draw_arrays(GL_TRIANGLES, 0, count * 6); gl_state_bind_vertex_array(0); shader::unbind(); gl_state_enable_cull_face(); } void water_particle::free() { ssbo.Destroy(); color_data.clear(); position_data.clear(); ptcl_data.clear(); ripple.color = 0; ripple.position = 0; splash_count = 0; } void water_particle::reset() { splash = 0; color = 1.0f; particle_size = 1.0f;; splash_count = 0; ptcl_data.clear(); count = 0; splash_tex_id = -1; blink = false; ripple_emission = 0.0f; } void water_particle::set(splash_particle* splash, int32_t splash_tex_id) { reset(); color = 1.0f; particle_size = 1.0f; this->splash = splash; this->splash_tex_id = splash_tex_id; splash_count = splash->count; ptcl_data.clear(); ptcl_data.resize(splash_count); ripple.count = 0; ripple.position = 0; ripple.color = 0; ripple.size = 1.0; position_data.clear(); color_data.clear(); position_data.resize(5000); color_data.resize(5000); ripple.position = position_data.data(); ripple.color = color_data.data(); ssbo.Create(sizeof(water_particle_vertex_data) * splash_count); } ParticleDispObj::ParticleDispObj() : splash() { } ParticleDispObj::~ParticleDispObj() { } void ParticleDispObj::disp() { if (!splash) return; instances_mat.clear(); if (splash->count > 0) for (splash_particle_data& i : splash->data) if (i.life_time > 0.0f) { mat4 mat; mat4_translate(&i.position, &mat); instances_mat.push_back(mat); } rctx_ptr->disp_manager->entry_obj_by_object_info_instanced(obj_info, instances_mat, -1.0f); } void ParticleDispObj::init(splash_particle* splash, object_info obj_info) { if (!splash) return; reset(); this->splash = splash; this->obj_info = obj_info; instances_mat.clear(); instances_mat.reserve(splash->count); } void ParticleDispObj::reset() { splash = 0; obj_info = {}; instances_mat.clear(); } EffectSplashParticle::EffectSplashParticle() : splash_count(), splash(), emitter_rob_count(), emitter_rob(), water_ptcl_count(), water_ptcl(), has_splash_object(), object_emitter_rob_count(), object_emitter_rob(), particle_size(), emit_num(), ripple_emission(), emission_ratio_attn(), emission_velocity_scale(), splash_tex_id(), in_water(), blink(), rob_ctrl_time(), ptcl_ctrl_time(), ptcl_disp_time() { reset(); } EffectSplashParticle::~EffectSplashParticle() { } void EffectSplashParticle::ctrl(float_t delta_time) { for (int32_t i = 0; i < emitter_rob_count; i++) emitter_rob[i].ctrl(delta_time); if (has_splash_object) for (int32_t i = 0; i < object_emitter_rob_count; i++) object_emitter_rob[i].ctrl(delta_time); time_struct ptcl_ctrl_time; for (int32_t i = 0; i < splash_count; i++) splash[i].ctrl(delta_time); if (has_splash_object) splash_object.ctrl(delta_time); for (int32_t i = 0; i < water_ptcl_count; i++) water_ptcl[i].ctrl(); this->ptcl_ctrl_time = ptcl_ctrl_time.calc_time_int(); } void EffectSplashParticle::dest() { if (object_emitter_rob) { delete object_emitter_rob; object_emitter_rob = 0; } if (splash) { delete[] splash; splash = 0; } splash_count = 0; if (emitter_rob) { delete[] emitter_rob; emitter_rob = 0; } emitter_rob_count = 0; if (water_ptcl) { delete[] water_ptcl; water_ptcl = 0; } water_ptcl_count = 0; } void EffectSplashParticle::disp() { time_struct ptcl_disp_time; for (int32_t i = 0; i < water_ptcl_count; i++) water_ptcl[i].disp(); if (has_splash_object) particle_disp_obj.disp(); this->ptcl_disp_time = ptcl_disp_time.calc_time_int(); } bool EffectSplashParticle::init(int32_t splash_tex_id, object_info splash_obj_id, bool in_water, bool blink) { static const int32_t dword_1409E59A8[5] = { 26, 20, 14, 9, 0, }; static const int32_t dword_1409E59A8_size = sizeof(dword_1409E59A8) / sizeof(int32_t); splash_count = 1; splash = new splash_particle[splash_count]; if (!splash) return false; emitter_rob_count = dword_1409E59A8_size * ROB_CHARA_COUNT; emitter_rob = new ParticleEmitterRob[emitter_rob_count]; if (!emitter_rob) return false; water_ptcl_count = 1; water_ptcl = new water_particle[water_ptcl_count]; if (!water_ptcl) return false; for (int32_t i = 0; i < splash_count; i++) splash[i].init(5000); for (int32_t i = 0, j = 0; i < ROB_CHARA_COUNT; i++) for (const int32_t& l : dword_1409E59A8) { ParticleEmitterRob& ptcl_emit_rob = emitter_rob[j++]; ptcl_emit_rob.set_chara(i, l, in_water); ptcl_emit_rob.set_splash(splash); } for (int32_t i = 0; i < splash_count; i++) { water_ptcl[i].set(&splash[i], splash_tex_id); water_ptcl[i].blink = blink; } if (splash_obj_id.not_null()) { splash_object.init(100); object_emitter_rob_count = dword_1409E59A8_size * ROB_CHARA_COUNT; object_emitter_rob = new ParticleEmitterRob[object_emitter_rob_count]; if (!object_emitter_rob) return false; for (int32_t i = 0, j = 0; i < ROB_CHARA_COUNT; i++) for (const int32_t& l : dword_1409E59A8) { ParticleEmitterRob& ptcl_emit_rob = object_emitter_rob[j++]; ptcl_emit_rob.set_chara(i, l, in_water); ptcl_emit_rob.set_splash(&splash_object); ptcl_emit_rob.emit_num = 10; ptcl_emit_rob.particle_size = 1.0f; ptcl_emit_rob.field_28 = 1.0f; } particle_disp_obj.init(&splash_object, splash_obj_id); has_splash_object = true; } else has_splash_object = false; rob_ctrl_time = 0; ptcl_ctrl_time = 0; ptcl_disp_time = 0; set_color({ 15.0f, 15.0f, 15.0f, 1.0f }); set_particle_size(5.0f); set_emit_num(5000); set_ripple_emission(0.25f); this->splash_tex_id = splash_tex_id; this->splash_obj_id = splash_obj_id; this->in_water = in_water; this->blink = blink; return true; } void EffectSplashParticle::reset() { splash_count = 0; splash = 0; emitter_rob_count = 0; emitter_rob = 0; water_ptcl_count = 0; water_ptcl = 0; has_splash_object = false; object_emitter_rob_count = 0; object_emitter_rob = 0; color = 1.0f; particle_size = 1.0f; emit_num = 1; ripple_emission = 0.0f; emission_ratio_attn = 0.0f; emission_velocity_scale = 0.0f; splash_tex_id = -1; splash_obj_id = {}; in_water = false; blink = false; rob_ctrl_time = 0; ptcl_ctrl_time = 0; ptcl_disp_time = 0; } void EffectSplashParticle::restart() { for (int32_t i = 0; i < splash_count; i++) splash[i].restart(); for (int32_t i = 0; i < splash_count; i++) emitter_rob[i].restart(); if (has_splash_object) { splash_object.restart(); for (int32_t i = 0; i < object_emitter_rob_count; i++) object_emitter_rob[i].restart(); } } void EffectSplashParticle::set_color(const vec4& value) { for (int32_t i = 0; i < water_ptcl_count; i++) water_ptcl[i].color = value; } void EffectSplashParticle::set_emission_ratio_attn(float_t value) { for (int32_t i = 0; i < emitter_rob_count; i++) emitter_rob[i].emission_ratio_attn = value; if (has_splash_object) for (int32_t i = 0; i < object_emitter_rob_count; i++) object_emitter_rob[i].emission_ratio_attn = value; emission_ratio_attn = value; } void EffectSplashParticle::set_emission_velocity_scale(float_t value) { for (int32_t i = 0; i < emitter_rob_count; i++) emitter_rob[i].emission_velocity_scale = value; if (has_splash_object) for (int32_t i = 0; i < object_emitter_rob_count; i++) object_emitter_rob[i].emission_velocity_scale = value; emission_velocity_scale = value; } void EffectSplashParticle::set_emit_num(int32_t value) { emit_num = value; for (int32_t i = 0; i < emitter_rob_count; i++) emitter_rob[i].emit_num = value; } void EffectSplashParticle::set_particle_size(float_t value) { particle_size = value; for (int32_t i = 0; i < water_ptcl_count; i++) water_ptcl[i].particle_size = value; for (int32_t i = 0; i < emitter_rob_count; i++) emitter_rob[i].particle_size = value; } void EffectSplashParticle::set_ripple_emission(float_t value) { for (int32_t i = 0; i < water_ptcl_count; i++) water_ptcl[i].ripple_emission = value; ripple_emission = value; } EffectSplash::EffectSplash() : restart(), stage_index(), field_8(), field_110(), current_stage_index(), frame_rate_control(), field_138() { reset(); } EffectSplash::~EffectSplash() { dest(); } void EffectSplash::ctrl(float_t delta_time) { if (restart && field_8 && field_110) particle.ctrl(delta_time); } void EffectSplash::dest() { stage_param_data_splash_storage_clear(); particle.dest(); reset(); } void EffectSplash::disp() { if (restart && field_8 && field_110) particle.disp(); } void EffectSplash::reset() { restart = false; field_8 = true; stage_index = -1; field_110 = false; current_stage_index = -1; } static void sub_140366CE0() { dword_141195E48 = 0; dword_141195E4C = 0; } void EffectSplash::restart_effect() { if (restart) { sub_140366CE0(); particle.restart(); } } void EffectSplash::set_current_stage_index(int32_t value) { if (current_stage_index == value) return; current_stage_index = value; field_110 = false; if (value != -1) for (int32_t i : stage_indices) if (i == value) { field_110 = true; break; } } void EffectSplash::set_stage_indices(const std::vector& stage_indices) { field_110 = false; current_stage_index = -1; this->stage_indices.clear(); stage_param_data_splash_storage_clear(); for (const int32_t& i : stage_indices) { stage_param_splash splash; if (effect_array_parse_stage_param_data_splash(&splash, i)) { this->stage_indices.push_back(i); stage_param_data_splash_storage_set_stage_data(i, &splash); } } if (!this->stage_indices.size()) return; current_stage_index = this->stage_indices.front(); stage_param_splash* splash = stage_param_data_splash_storage_get_value(current_stage_index); if (!splash) return; field_110 = true; stage_index = current_stage_index; field_8 = true; data_struct* aft_data = &data_list[DATA_AFT]; object_database* aft_obj_db = &aft_data->data_ft.obj_db; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; if (!particle.init(aft_tex_db->get_texture_id(splash->splash_tex_name.c_str()), aft_obj_db->get_object_info(splash->splash_obj_name.c_str()), splash->in_water, splash->blink)) return; particle.set_emit_num(splash->emit_num); particle.set_color(splash->color); particle.set_particle_size(splash->particle_size); particle.set_ripple_emission(splash->ripple_emission); particle.set_emission_ratio_attn(splash->emission_ratio_attn); particle.set_emission_velocity_scale(splash->emission_velocity_scale); restart = true; } TaskEffectSplash::TaskEffectSplash() : enable() { } TaskEffectSplash::~TaskEffectSplash() { } bool TaskEffectSplash::init() { water_particle_init(); set_enable(true); //sub_1400DE640("TaskEffectSplash::init()\n"); return true; } bool TaskEffectSplash::ctrl() { if (enable) data.ctrl(data.frame_rate_control->get_delta_frame() * (float_t)(1.0 / 60.0)); return false; } bool TaskEffectSplash::dest() { data.dest(); water_particle_free(); effect_splash = 0; //sub_1400DE640("TaskEffectSplash::dest()\n"); return true; } void TaskEffectSplash::disp() { if (enable) data.disp(); } void TaskEffectSplash::pre_init(int32_t stage_index) { } void TaskEffectSplash::set_stage_indices(const std::vector& stage_indices) { set_frame_rate_control(0); data.set_stage_indices(stage_indices); effect_splash = &data; } void TaskEffectSplash::set_enable(bool value) { enable = value; } void TaskEffectSplash::set_current_stage_index(int32_t value) { data.set_current_stage_index(value); } void TaskEffectSplash::set_frame_rate_control(FrameRateControl* value) { if (value) data.frame_rate_control = value; else data.frame_rate_control = get_sys_frame_rate(); } void TaskEffectSplash::reset() { data.restart_effect(); } TaskEffectStar::TaskEffectStar() : frame_rate_control(), delta_frame() { current_stage_index = -1; } TaskEffectStar::~TaskEffectStar() { } bool TaskEffectStar::init() { if (!star_catalog_data.stage_param_data_ptr) return true; else if (!star_catalog_data.init()) return false; set_frame_rate_control(0); return true; } bool TaskEffectStar::ctrl() { if (star_catalog_data.stage_param_data_ptr) delta_frame = frame_rate_control->get_delta_frame(); return false; } bool TaskEffectStar::dest() { star_catalog_data.free(); stage_param_data_star_storage_clear(); return true; } void TaskEffectStar::disp() { } void TaskEffectStar::set_stage_indices(const std::vector& stage_indices) { if (star_catalog_data.stage_param_data_ptr) dest(); current_stage_index = -1; this->stage_indices.clear(); stage_param_data_star_storage_clear(); delta_frame = 1.0f; for (int32_t i : stage_indices) { stage_param_star star; if (effect_array_parse_stage_param_data_star(&star, i)) { this->stage_indices.push_back(i); stage_param_data_star_storage_set_stage_data(i, &star); } } if (!this->stage_indices.size()) return; current_stage_index = this->stage_indices.front(); stage_param_star* star = stage_param_data_star_storage_get_value(current_stage_index); if (!star) return; const char* dir = "./rom/"; const char* farc_file; const char* file; if (star_catalog_data.random) { farc_file = "star_catalog_random.farc"; file = "random.bin"; } else { farc_file = "star_catalog_megastar2.farc"; file = "megastar2.bin"; } star_catalog_data.file_handler.read_file(&data_list[DATA_AFT], dir, farc_file, file, false); star_catalog_data.set_stage_param_data(star); } void TaskEffectStar::set_enable(bool value) { star_catalog_data.enable = value; } void TaskEffectStar::set_current_stage_index(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; } star_catalog_data.stage_param_data_ptr = 0; star_catalog_data.star_tex = -1; star_catalog_data.star_b_tex = -1; star_catalog_data.milky_way_tex_id = -1; if (found) star_catalog_data.set_stage_param_data(stage_param_data_star_storage_get_value(value)); } void TaskEffectStar::set_frame_rate_control(FrameRateControl* value) { if (value) frame_rate_control = value; else frame_rate_control = get_sys_frame_rate(); } void TaskEffectStar::reset() { } static TaskEffect* effect_array_get(EffectType type, const char** name) { if (type < EFFECT_AUTH_3D || type > EFFECT_STAR) { if (name) *name = 0; return 0; } if (name) *name = effect_name_array[type]; TaskEffect** task = task_effect_data_array[type]; if (task) return *task; return 0; } static std::string effect_array_get_stage_param_file_path( EffectType 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; 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 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 = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_leaf(stage_param_leaf* leaf, int32_t stage_index) { if (!leaf) return false; std::string path = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_litproj(stage_param_litproj* litproj, int32_t stage_index) { if (!litproj) return false; std::string path = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_rain(stage_param_rain* rain, int32_t stage_index) { if (!rain) return false; std::string path = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_ripple(stage_param_ripple* ripple, int32_t stage_index) { if (!ripple) return false; std::string path = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_snow(stage_param_snow* snow, int32_t stage_index) { if (!snow) return false; std::string path = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_splash(stage_param_splash* splash, int32_t stage_index) { if (!splash) return false; std::string path = effect_array_get_stage_param_file_path(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 effect_array_parse_stage_param_data_star(stage_param_star* star, int32_t stage_index) { if (!star) return false; std::string path = effect_array_get_stage_param_file_path(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; } particle_data::particle_data() : size(), alpha(), life_time() { } star_catalog_milky_way::star_catalog_milky_way() : sampler(), restart_index(), idx_count(), longitude_degs_10(), latitude_degs_10(), longitude_offset_degs_10(), latitude_offset_degs_10(), latitude(), longitude(), uv_rec_scale_u(), uv_rec_scale_v() { reset(); } star_catalog_milky_way::~star_catalog_milky_way() { } void star_catalog_milky_way::create_buffers(int32_t subdivs, float_t uv_rec_scale_u, float_t uv_rec_scale_v, int32_t longitude_degs_10, int32_t latitude_degs_10, float_t longitude_offset_degs_10, float_t latitude_offset_degs_10) { delete_buffers(); const int32_t sectors_count = 2 * subdivs + 1; const int32_t vtx_count = (subdivs - 1) * sectors_count + 2; restart_index = (uint16_t)0xFFFFFFFF; gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); gl_state_bind_element_array_buffer(0); const float_t rec_longitude_degs_10 = 1.0f / (float_t)longitude_degs_10; const float_t rec_latitude_degs_10 = 1.0f / (float_t)latitude_degs_10; star_catalog_vertex* vtx_data = force_malloc(vtx_count); vtx_data[0].position = { 0.0f, 1.0f, 0.0f }; vtx_data[0].texcoord.x = 0.5f; vtx_data[0].texcoord.y = (latitude_offset_degs_10 + (1.0f / uv_rec_scale_v) * 90.0f) * rec_latitude_degs_10; size_t vtx = 1; if (subdivs - 1 > 0) { float_t rec_stack_step = 1.0f / (float_t)subdivs; float_t rec_sector_step = 1.0f / (float_t)(2 * subdivs); for (uint32_t i = subdivs - 1, j = 1; i; i--, j++) { float_t stack_angle = (float_t)M_PI_2 - (float_t)j * (float_t)M_PI * rec_stack_step; float_t xz = cosf(stack_angle); float_t y = sinf(stack_angle); float_t texcoord_y = (latitude_offset_degs_10 + stack_angle * (float_t)(1.0 / M_PI) * 180.0f * (float_t)(1.0 / uv_rec_scale_v)) * rec_latitude_degs_10; for (uint32_t k = sectors_count, l = 0; k; k--, l++, vtx++) { float_t sector_angle = (float_t)l * (float_t)(2.0 * M_PI) * rec_sector_step; vtx_data[vtx].position.x = sinf(sector_angle) * xz; vtx_data[vtx].position.y = y; vtx_data[vtx].position.z = cosf(sector_angle) * xz; vtx_data[vtx].texcoord.x = (longitude_offset_degs_10 + (float_t)l * rec_sector_step * 360.0f * (1.0f / uv_rec_scale_u)) * rec_longitude_degs_10; vtx_data[vtx].texcoord.y = texcoord_y; } } } vtx_data[vtx].position = { 0.0f, -1.0f, 0.0f }; vtx_data[vtx].texcoord.x = 1.0f; vtx_data[vtx].texcoord.y = (latitude_offset_degs_10 - (1.0f / uv_rec_scale_v) * 90.0f) * rec_latitude_degs_10; if (!vao) glGenVertexArrays(1, &vao); vbo.Create(sizeof(star_catalog_vertex) * vtx_count, vtx_data); vbo.Bind(); free_def(vtx_data); static const GLsizei buffer_size = sizeof(star_catalog_vertex); gl_state_bind_vertex_array(vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(star_catalog_vertex, position)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, buffer_size, (void*)offsetof(star_catalog_vertex, texcoord)); const uint16_t first_vertex = 0; const uint16_t last_vertex = (uint16_t)(vtx_count - 1); uint16_t restart_index = this->restart_index; const int32_t ebo_count = (subdivs - 2) * (2 * sectors_count + 1) + 8 * sectors_count - 8; idx_count = ebo_count; uint16_t* ebo_data = force_malloc(ebo_count); size_t idx = 0; for (uint32_t i = sectors_count - 1, j = 1; i; i--, j++, idx += 4) { ebo_data[idx + 0] = first_vertex; ebo_data[idx + 1] = (uint16_t)(j + 1); ebo_data[idx + 2] = (uint16_t)j; ebo_data[idx + 3] = restart_index; } uint16_t v37 = 1; for (uint32_t i = subdivs - 2; i; i--, idx++) { for (uint32_t j = sectors_count; j; j--, idx += 2, v37++) { ebo_data[idx + 0] = (uint16_t)(v37 + sectors_count); ebo_data[idx + 1] = (uint16_t)(v37); } ebo_data[idx] = restart_index; } for (uint32_t i = sectors_count - 1; i; i--, idx += 4, v37++) { ebo_data[idx + 0] = last_vertex; ebo_data[idx + 1] = (uint16_t)v37; ebo_data[idx + 2] = (uint16_t)(v37 + 1); ebo_data[idx + 3] = restart_index; } ebo.Create(sizeof(uint16_t) * ebo_count, ebo_data); ebo.Bind(); free_def(ebo_data); if (!sampler) glGenSamplers(1, &sampler); glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_REPEAT); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); gl_state_bind_element_array_buffer(0); } void star_catalog_milky_way::create_default_sphere() { create_buffers(16, uv_rec_scale_u, uv_rec_scale_v, longitude_degs_10, latitude_degs_10, longitude_offset_degs_10, latitude_offset_degs_10); } void star_catalog_milky_way::delete_buffers() { if (sampler) { glDeleteSamplers(1, &sampler); sampler = 0; } vbo.Destroy(); ebo.Destroy(); if (vao) { glDeleteVertexArrays(1, &vao); vao = 0; } } void star_catalog_milky_way::draw(const mat4& vp, const mat4& mat, texture* tex, GL::UniformBuffer& scene_ubo) { if (!vao) return; mat4 latitude_mat; mat4 longitude_mat; mat4_rotate_x(latitude * DEG_TO_RAD_FLOAT, &latitude_mat); mat4_rotate_y(longitude * DEG_TO_RAD_FLOAT, &longitude_mat); mat4 model; mat4_mul(&latitude_mat, &longitude_mat, &model); const float_t pitch_forward = -0.50503153f; const float_t yaw_forward = 4.6496463f; const float_t pitch_up = 0.47347879f; const float_t yaw_up = 3.3660336f; vec3 forward; vec3 up; forward.x = sinf(yaw_forward) * cosf(pitch_forward); forward.y = sinf(pitch_forward); forward.z = cosf(yaw_forward) * cosf(pitch_forward); up.x = sinf(yaw_up) * cosf(pitch_up); up.y = sinf(pitch_up); up.z = cosf(yaw_up) * cosf(pitch_up); mat4 view = mat4_identity; *(vec3*)&view.row0 = vec3::cross(up, forward); *(vec3*)&view.row1 = up; *(vec3*)&view.row2 = forward; mat4_mul(&model, &view, &model); mat4_mul(&model, &mat, &model); star_catalog_scene_shader_data scene_shader_data = {}; mat4 temp; mat4_mul(&model, &vp, &temp); mat4_transpose(&temp, &temp); scene_shader_data.g_transform[0] = temp.row0; scene_shader_data.g_transform[1] = temp.row1; scene_shader_data.g_transform[2] = temp.row2; scene_shader_data.g_transform[3] = temp.row3; scene_ubo.WriteMemory(scene_shader_data); gl_state_enable_cull_face(); gl_state_disable_blend(); gl_state_set_depth_mask(GL_FALSE); gl_state_active_bind_texture_2d(0, tex->glid); gl_state_bind_sampler(0, sampler); gl_state_enable_primitive_restart(); gl_state_set_primitive_restart_index(restart_index); uniform_value[U_STAR] = 1; shaders_ft.set(SHADER_FT_STAR); gl_state_bind_vertex_array(vao); scene_ubo.Bind(0); shaders_ft.draw_elements(GL_TRIANGLE_STRIP, idx_count, GL_UNSIGNED_SHORT, 0); gl_state_bind_vertex_array(0); gl_state_bind_sampler(0, 0); gl_state_active_bind_texture_2d(0, 0); gl_state_disable_primitive_restart(); gl_state_set_depth_mask(GL_TRUE); gl_state_enable_blend(); gl_state_disable_cull_face(); } void star_catalog_milky_way::reset() { longitude_degs_10 = 3600; latitude_degs_10 = 1800; longitude_offset_degs_10 = 1800.9501f; latitude_offset_degs_10 = 900.95001f; latitude = 0.0f; longitude = 0.0f; uv_rec_scale_u = -0.1f; uv_rec_scale_v = 0.1f; } star_catalog_vertex::star_catalog_vertex() { } star_catalog::star_catalog() : stage_param_data_ptr(), star_count(), star_b_count() { random = false; enable = true; star_tex = -1; star_b_tex = -1; milky_way_tex_id = -1; } star_catalog::~star_catalog() { free(); } void star_catalog::draw() { if (!stage_param_data_ptr || !enable) return; texture* star_tex = texture_manager_get_texture(this->star_tex); texture* star_b_tex = texture_manager_get_texture(this->star_b_tex); if (!star_tex || !star_b_tex) return; mat4 model; stage_param_star::get_mat(model, stage_param_data.observer_north_latitude_deg * DEG_TO_RAD_FLOAT, stage_param_data.observer_east_longitude_deg * DEG_TO_RAD_FLOAT, stage_param_data.utc, stage_param_data.rotation_y_deg * DEG_TO_RAD_FLOAT); const mat4& view = rctx_ptr->view_mat; model.row3.x = vec3::dot(-*(vec3*)&view.row0, *(vec3*)&view.row3) + model.row3.x; model.row3.y = vec3::dot(-*(vec3*)&view.row1, *(vec3*)&view.row3) + model.row3.y; model.row3.z = vec3::dot(-*(vec3*)&view.row2, *(vec3*)&view.row3) + model.row3.z; mat4 proj = rctx_ptr->proj_mat; proj.row0.z = proj.row0.w; proj.row1.z = proj.row1.w; proj.row2.z = proj.row2.w; proj.row3.z = proj.row3.w; mat4 vp; mat4_mul(&rctx_ptr->view_mat, &proj, &vp); texture* milky_way_tex = texture_manager_get_texture(milky_way_tex_id); if (milky_way_tex) milky_way.draw(vp, model, milky_way_tex, scene_ubo); star_catalog_scene_shader_data scene_shader_data = {}; mat4 temp; mat4_mul(&model, &vp, &temp); mat4_transpose(&temp, &temp); scene_shader_data.g_transform[0] = temp.row0; scene_shader_data.g_transform[1] = temp.row1; scene_shader_data.g_transform[2] = temp.row2; scene_shader_data.g_transform[3] = temp.row3; scene_ubo.WriteMemory(scene_shader_data); uniform_value[U_STAR] = 0; shaders_ft.set(SHADER_FT_STAR); gl_state_enable_blend(); gl_state_set_blend_func(GL_ONE, GL_ONE); gl_state_enable_depth_test(); gl_state_set_depth_mask(false); gl_state_bind_vertex_array(rctx_ptr->common_vao); for (int32_t i = 0; i < 2; i++) { stage_param_star_modifiers& modifiers = stage_param_data.modifiers[i]; star_catalog_batch_shader_data batch_shader_data = {}; batch_shader_data.g_size_in_projection = { (float_t)(1.0 / 1280.0), (float_t)(1.0 / 720.0), 0.0f, modifiers.size_max }; batch_shader_data.g_modifiers = { modifiers.color_scale, modifiers.color_scale * modifiers.offset_scale, modifiers.pos_scale, modifiers.pos_scale * modifiers.offset_scale }; batch_shader_data.g_thresholds = { modifiers.threshold * modifiers.pos_scale, 0.0f, 0.0f, 0.0f }; batch_ubo.WriteMemory(batch_shader_data); scene_ubo.Bind(0); batch_ubo.Bind(1); stars_ssbo.Bind(0); if (i) { gl_state_active_bind_texture_2d(0, star_b_tex->glid); shaders_ft.draw_arrays(GL_TRIANGLES, 0, star_b_count * 6); } else { gl_state_active_bind_texture_2d(0, star_tex->glid); shaders_ft.draw_arrays(GL_TRIANGLES, 0, star_count * 6); } } gl_state_active_bind_texture_2d(0, 0); gl_state_bind_vertex_array(0); } void star_catalog::free() { stars_ssbo.Destroy(); batch_ubo.Destroy(); scene_ubo.Destroy(); star_catalog_data.file_handler.reset(); star_catalog_data.milky_way.delete_buffers(); star_catalog_data.stage_param_data_ptr = 0; } bool star_catalog::init() { if (file_handler.check_not_ready()) return false; if (file_handler.get_data()) { std::vector vec; file_handler.get_size(); const void* data = file_handler.get_data(); star_catalog::parse_data(vec, (size_t)data); std::sort(vec.begin(), vec.end(), [](stars_buffer_data a, stars_buffer_data b) { return a.size > b.size; }); stars_buffer_data* stars_data = vec.data(); size_t length = vec.size(); size_t temp; while (length > 0) if (stars_data[temp = length / 2].size <= 0.0f) length = temp; else { stars_data += temp + 1; length -= temp + 1; } if (stars_data != vec.data() + vec.size()) vec.resize(stars_data - vec.data()); stars_data = vec.data(); length = vec.size(); while (length > 0) if (stars_data[temp = length / 2].size <= 2.0f) length = temp; else { stars_data += temp + 1; length -= temp + 1; } if (stars_data != vec.data() + vec.size()) star_catalog_data.star_b_count = (int32_t)(stars_data - vec.data()); else star_catalog_data.star_b_count = (int32_t)vec.size(); file_handler.reset(); star_count = (int32_t)vec.size(); stars_ssbo.Create(sizeof(stars_buffer_data) * vec.size(), vec.data()); } milky_way.create_default_sphere(); scene_ubo.Create(sizeof(star_catalog_scene_shader_data)); batch_ubo.Create(sizeof(star_catalog_batch_shader_data)); return true; } void star_catalog::set_stage_param_data(stage_param_star* value) { stage_param_data_ptr = value; if (value) { data_struct* aft_data = &data_list[DATA_AFT]; texture_database* aft_tex_db = &aft_data->data_ft.tex_db; stage_param_data = *value; stage_param_data.utc.get_current_time(); stage_param_data.observer_north_latitude_deg = 43.065f; stage_param_data.observer_east_longitude_deg = 141.35f; star_tex = aft_tex_db->get_texture_id("F_DIVA_EFF00_HR_STAR"); star_b_tex = aft_tex_db->get_texture_id("F_DIVA_EFF00_HR_STAR_B"); milky_way_tex_id = aft_tex_db->get_texture_id(value->milky_way_texture_name.c_str()); } else { star_tex = -1; star_b_tex = -1; milky_way_tex_id = -1; } } void star_catalog::parse_data(std::vector& vec, size_t data) { size_t count = *(int32_t*)data; vec.clear(); vec.resize(count); if (count > 0) memcpy(vec.data(), (stars_buffer_data*)(data + 4), sizeof(stars_buffer_data) * count); } stars_buffer_data::stars_buffer_data() : size() { } 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() { } EffectManager::EffectManager() : state(), enable(), modern(), data(), obj_db(), tex_db(), stage_data() { current_stage_hash = hash_murmurhash_empty; current_stage_index = -1; } EffectManager::~EffectManager() { } std::pair EffectManager::add_effect(EffectType type) { if (check_effect_loaded(type) < 0) { const char* name = 0; TaskEffect* t = effect_array_get(type, &name); if (t) { app::TaskWork::add_task(t, name); return { type, t }; } } return { EFFECT_INVALID, 0 }; } int32_t EffectManager::check_effect_loaded(EffectType type) { for (std::pair& i : effects) if (i.first == type) return 0; return -1; } void EffectManager::event(EffectType type, int32_t event_type, void* data) { for (std::pair& i : effects) if (i.first == type) i.second->event(event_type, data); } void EffectManager::dest() { if (state == 1) state = 4; else if (state >= 2 && state <= 3) { for (std::pair& i : effects) i.second->del(); state = 4; } } static const EffectType dword_1409E3440[16] = { EFFECT_AUTH_3D, EFFECT_PARTICLE, EFFECT_INVALID, }; bool EffectManager::load() { if (state == 1) { if (!modern) { bool wait_load = false; for (uint32_t i : obj_set_ids) if (objset_info_storage_load_obj_set_check_not_read(i)) wait_load |= true; if (wait_load) return true; for (EffectType i : dword_1409E3440) { if (i == EFFECT_INVALID) break; std::pair v57 = add_effect(i); if (v57.second) { v57.second->set_stage_indices(stage_indices); v57.second->set_enable(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 == EFFECT_INVALID) break; std::pair v57 = add_effect((EffectType)j); if (v57.second) { v57.second->set_stage_indices(stage_indices); v57.second->set_enable(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 (objset_info_storage_load_obj_set_check_not_read(i, obj_db, tex_db)) wait_load = true; if (wait_load) return true; for (EffectType i : dword_1409E3440) { if (i == EFFECT_INVALID) break; std::pair v57 = add_effect(i); if (v57.second) { v57.second->set_stage_hashes(stage_hashes, data, obj_db, tex_db, stage_data); v57.second->set_enable(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 == EFFECT_INVALID) break; std::pair v57 = add_effect((EffectType)j); if (v57.second) { v57.second->set_stage_hashes(stage_hashes, data, obj_db, tex_db, stage_data); v57.second->set_enable(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::check_task_ctrl(i.second)) { wait_load = true; break; } if (!wait_load) { for (std::pair& i : effects) i.second->set_enable(enable); state = 3; return false; } return true; } void EffectManager::reset() { for (std::pair& i : effects) i.second->reset(); } void EffectManager::reset_data() { 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 EffectManager::set_current_stage_hash(uint32_t stage_hash) { if (current_stage_hash != stage_hash) { for (std::pair& i : effects) i.second->set_current_stage_hash(stage_hash); current_stage_hash = stage_hash; } } void EffectManager::set_current_stage_index(int32_t stage_index) { if (current_stage_index != stage_index) { for (std::pair& i : effects) i.second->set_current_stage_index(stage_index); current_stage_index = stage_index; } } void EffectManager::set_enable(bool value) { for (std::pair& i : effects) i.second->set_enable(value); } void EffectManager::set_frame(int32_t value) { for (std::pair& i : effects) i.second->set_frame(value); } void EffectManager::set_frame_rate_control(FrameRateControl* value) { for (std::pair& i : effects) i.second->set_frame_rate_control(value); } void EffectManager::set_stage_hashes(const 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_unique(obj_set_ids); for (uint32_t i : obj_set_ids) objset_info_storage_load_set_hash(data, i); state = 1; } void EffectManager::set_stage_indices(const 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; obj_set_ids.push_back(aft_obj_db->get_object_set_id("EFFCMN")); 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_unique(obj_set_ids); for (uint32_t i : obj_set_ids) objset_info_storage_load_set(aft_data, aft_obj_db, i); state = 1; } bool EffectManager::unload() { if (state != 4) { reset_data(); return false; } for (std::pair& i : effects) if (app::TaskWork::check_task_ready(i.second)) return true; for (uint32_t& i : obj_set_ids) objset_info_storage_unload_set(i); reset_data(); return false; } static void draw_fog_particle(EffectFogRing* data, mat4* mat) { if (!data->num_vtx) return; shaders_ft.set(SHADER_FT_FOGPTCL); gl_state_enable_blend(); texture* tex = texture_manager_get_texture(data->tex_id); if (tex) gl_state_active_bind_texture_2d(0, tex->glid); gl_state_bind_vertex_array(rctx_ptr->common_vao); data->ssbo.Bind(0); shaders_ft.draw_arrays(GL_TRIANGLES, 0, data->num_vtx); gl_state_bind_vertex_array(0); gl_state_disable_blend(); shader::unbind(); } static void draw_ripple_particles(ripple_struct* data, mat4* mat) { if (data->count > 5000) return; vec3* vtx_data = (vec3*)ripple_emit_ssbo.MapMemory(); if (!vtx_data) return; vec3* position = data->position; color4u8* color = data->color; for (size_t i = data->count; i; i--, vtx_data++, position++, color++) *vtx_data = { position->x, position->z, (float_t)color->a * (float_t)(1.0 / 255.0) }; ripple_emit_ssbo.UnmapMemory(); int32_t size = (int32_t)(data->size + 0.5f); RenderTexture& rt = rctx_ptr->render_manager->get_render_texture( effect_ripple->use_float_ripplemap ? 2 : 5); int32_t width = rt.GetWidth(); int32_t height = rt.GetHeight(); ripple_emit_scene_shader_data shader_data = {}; shader_data.g_size_in_projection = { (float_t)size / (float_t)width, (float_t)size / (float_t)height, 0.0f, 0.0f }; shader_data.g_transform.x = (float_t)width / (float_t)(width - 2); shader_data.g_transform.y = (float_t)height / (float_t)(height - 2); shader_data.g_transform.z = 0.0079498291f / (float_t)(width - 2); shader_data.g_transform.w = -0.0079498291f / (float_t)(height - 2); shader_data.g_framebuffer_size = { 1.0f / (float_t)width, 1.0f / (float_t)height, 0.0f, 0.0f }; ripple_emit_scene_ubo.WriteMemory(shader_data); uniform_value[U_RIPPLE] = data->ripple_uniform; uniform_value[U_RIPPLE_EMIT] = data->ripple_emit_uniform; gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); gl_state_bind_vertex_array(rctx_ptr->common_vao); shaders_ft.set(SHADER_FT_RIPEMIT); ripple_emit_scene_ubo.Bind(0); ripple_emit_ssbo.Bind(0); shaders_ft.draw_arrays(GL_TRIANGLES, 0, data->count * 6); shader::unbind(); gl_state_bind_vertex_array(0); gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } static void draw_water_particle(water_particle* data, mat4* mat) { data->draw(mat); } 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(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); static const GLsizei buffer_size = sizeof(leaf_particle_vertex_data); leaf_ptcl_vbo.Create(buffer_size * leaf_ptcl_vtx_count); leaf_ptcl_vbo.Bind(); 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(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); } leaf_ptcl_ebo.Create(sizeof(uint32_t) * ebo_count, ebo_data); leaf_ptcl_ebo.Bind(); 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 = leaf->frame_speed_coef * (leaf_particle_delta_frame * (float_t)(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 = (leaf_particle_vertex_data*)leaf_ptcl_vbo.MapMemory(); if (!vtx_data) 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; mat3 mat; mat3_rotate_xyz(&data->rotation, &mat); vec3 t0; vec3 t1; vec3 t2; vec3 t3; mat3_transform_vector(&mat, &position[0], &t0); mat3_transform_vector(&mat, &position[1], &t1); mat3_transform_vector(&mat, &position[2], &t2); mat3_transform_vector(&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_transform_vector(&mat, &normal[0], &t0); mat3_transform_vector(&mat, &normal[1], &t1); mat3_transform_vector(&mat, &normal[2], &t2); mat3_transform_vector(&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; } } leaf_ptcl_vbo.UnmapMemory(); 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; } leaf_ptcl_vbo.Destroy(); leaf_ptcl_ebo.Destroy(); leaf_particle_scene_ubo.Destroy(); } static void particle_init(vec3* offset) { particle_free(); const size_t ptcl_vtx_count = ptcl_count * 0x06; ptcl_data = force_malloc(ptcl_count); particle_rot_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); static const GLsizei buffer_size = sizeof(particle_vertex_data); ptcl_vbo.Create(buffer_size * ptcl_vtx_count); ptcl_vbo.Bind(); 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); particle_scene_ubo.Create(sizeof(particle_scene_shader_data)); } static void particle_ctrl() { if (!ptcl_data) return; vec3 wind = particle_wind; particle_rot_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_rot_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; mat3 mat; mat3_rotate_xyz(&data->rotation, &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_transform_vector(&mat, &t0, &t0); mat3_transform_vector(&mat, &t1, &t1); mat3_transform_vector(&mat, &t2, &t2); t0 += pos; t1 += pos; t2 += pos; vec3 normal = vec3(0.0f, 0.0f, 1.0f); mat3_transform_vector(&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_rot_data* particle_emit() { if (particle_count >= ptcl_count) return 0; for (int32_t i = 0; i < ptcl_count; i++) { particle_rot_data* data = &ptcl_data[particle_index++]; if (particle_index >= ptcl_count) particle_index = 0; if (!data->alive) { *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_rot_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_rot_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_rot_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; } ptcl_vbo.Destroy(); particle_scene_ubo.Destroy(); } 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; for (int32_t i = 0; i < 8; i++) { 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.size = 1.0f; data.alpha = 1.0f; data.life_time = 1; vec3 _velocity; _velocity.x = rand_state_array_get_float(4); _velocity.y = rand_state_array_get_float(4); _velocity.z = rand_state_array_get_float(4); data.velocity = (_velocity - 0.5f) * vel_range + velocity; } if (change_stage) return; rain_particle_free(); vec3* vtx_data = force_malloc(rain_ptcl_count); for (int32_t i = 0; i < rain_ptcl_count; i++) { 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++ = position; } vtx_data -= rain_ptcl_count; rain_ssbo.Create(sizeof(vec3) * rain_ptcl_count, vtx_data); free_def(vtx_data); 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++) { 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() { rain_ssbo.Destroy(); rain_particle_scene_ubo.Destroy(); rain_particle_batch_ubo.Destroy(); } static void ripple_emit_init() { ripple_batch_ubo.Create(sizeof(ripple_batch_shader_data)); ripple_scene_ubo.Create(sizeof(ripple_scene_shader_data)); ripple_emit_ssbo.Create(sizeof(vec3) * ripple_emit_count); ripple_emit_scene_ubo.Create(sizeof(ripple_emit_scene_shader_data)); } static void ripple_emit_free() { ripple_batch_ubo.Destroy(); ripple_scene_ubo.Destroy(); ripple_emit_ssbo.Destroy(); ripple_emit_scene_ubo.Destroy(); } static void snow_particle_init(bool change_stage) { stage_param_snow* snow = stage_param_data_snow_current; float_t colli_ground_y = stage_param_data_snow_current->colli_ground.y; snow_particle_fallen_count = 0; snow_particle_fallen_index = 0; int32_t render_height = rctx_ptr->render.render_height[0]; float_t snow_particle_size = (float_t)render_height * (float_t)(1.0 / 720.0); snow_particle_size_min = snow_particle_size; snow_particle_size_mid = snow_particle_size * 31.0f; snow_particle_size_max = snow_particle_size * 63.0f; snow_particle_data_free(); snow_particle_data_init(); vec3 velocity = snow->velocity; vec3 vel_range = snow->vel_range; vec3 range = snow->range; vec3 offset = snow->offset; range.y = offset.y + range.y - max_def(offset.y, colli_ground_y); if (stage_param_data_snow_current->num_snow > 0) { particle_data* snow_ptcl = snow_ptcl_data; for (int32_t i = ((stage_param_data_snow_current->num_snow - 1) >> 1) + 1; i; i--, snow_ptcl += 2) { vec3 position; position.x = rand_state_array_get_float(4) - 0.5f; position.y = rand_state_array_get_float(4); position.z = rand_state_array_get_float(4) - 0.5f; snow_ptcl[0].position = position * range + offset; snow_ptcl[0].size = rand_state_array_get_float(4) * 0.4f + 0.3f; snow_ptcl[0].alpha = rand_state_array_get_float(4) * 0.5f + 0.4f; snow_ptcl[0].life_time = 1; vec3 _velocity; _velocity.x = rand_state_array_get_float(4); _velocity.y = rand_state_array_get_float(4); _velocity.z = rand_state_array_get_float(4); snow_ptcl[0].velocity = (_velocity - 0.5f) * vel_range + velocity; snow_ptcl[0].direction = 0.0f; snow_ptcl[1].size = ((rand_state_array_get_float(4) * 0.5f) + 0.3f) * snow_ptcl[0].size; snow_ptcl[1].alpha = (rand_state_array_get_float(4) + 1.0f) * 0.3f; _velocity.x = rand_state_array_get_float(4); _velocity.y = rand_state_array_get_float(4); _velocity.z = rand_state_array_get_float(4); snow_ptcl[1].velocity = (_velocity - 0.5f) * 0.035f * (snow_ptcl[1].size + snow_ptcl[0].size); snow_ptcl[0].size = snow_particle_size_mid * snow_ptcl[0].size; snow_ptcl[1].size = snow_particle_size_mid * snow_ptcl[1].size; } } for (int32_t i = 0; i < snow_ptcl_fallen_count; i++) snow_particle_data_kill_fallen(&snow_ptcl_fallen_data[i], false); for (particle_data& i : snow_ptcl_gpu) { i.position.x = 0.0f; i.position.y = (float_t)(&i - snow_ptcl_gpu) * -25.0f - 5.0f; i.position.z = 0.0f; i.alpha = 1.0; i.life_time = 1; vec3 _velocity; _velocity.x = rand_state_array_get_float(4); _velocity.y = rand_state_array_get_float(4); _velocity.z = rand_state_array_get_float(4); i.velocity = (_velocity - 0.5f) * vel_range + velocity; i.direction = 0.0f; } if (change_stage) return; snow_particle_free(); snow_ssbo.Create(sizeof(snow_particle_vertex_data) * snow->num_snow); snow_particle_gpu_vertex_data* vtx_data = force_malloc(snow_ptcl_count); for (int32_t i = 0; i < snow_ptcl_count; i++, vtx_data++) { vtx_data->position.x = rand_state_array_get_float(4); vtx_data->position.y = rand_state_array_get_float(4); vtx_data->position.z = rand_state_array_get_float(4); vtx_data->size = (rand_state_array_get_float(4) * 0.5f + 0.4f) * snow_particle_size_mid; } vtx_data -= snow_ptcl_count; snow_gpu_ssbo.Create(sizeof(snow_particle_gpu_vertex_data) * snow_ptcl_count, vtx_data); free_def(vtx_data); snow_fallen_ssbo.Create(sizeof(snow_particle_vertex_data) * snow_ptcl_fallen_count); snow_particle_scene_ubo.Create(sizeof(snow_particle_scene_shader_data)); snow_particle_batch_ubo.Create(sizeof(snow_particle_batch_shader_data)); } static void snow_particle_ctrl() { if (!stage_param_data_snow_current) return; stage_param_snow* snow = stage_param_data_snow_current; float_t colli_ground_y = snow->colli_ground.y + 0.03f; float_t offset_y = snow->offset.y; float_t delta_time = (float_t)(snow_particle_delta_frame * (1.0 / 60.0)); vec4 _colli_ground_xz = { -snow->colli_ground.min_x, snow->colli_ground.max_x, -snow->colli_ground.min_z, snow->colli_ground.max_z }; __m128 colli_ground_xz = vec4::load_xmm(_colli_ground_xz); if (snow->num_snow > 0) { particle_data* snow_ptcl = snow_ptcl_data; for (int32_t i = ((snow->num_snow - 1) >> 1) + 1; i; i--, snow_ptcl += 2) { vec3 direction; if (--snow_ptcl[0].life_time > 0) direction = snow_ptcl[0].direction; else { snow_ptcl[0].life_time = rand_state_array_get_int(15, 35, 4); direction = snow_particle_get_random_velocity(); snow_ptcl[0].direction = direction; } vec3 velocity = (direction * delta_time + snow_ptcl[0].velocity) * 0.98f; vec3 position = velocity * delta_time + snow_ptcl[0].position; snow_ptcl[0].velocity = velocity; snow_ptcl[0].position = position; if (position.y < offset_y) snow_particle_data_reset(snow_ptcl); else if (colli_ground_y > position.y) { __m128 pos = vec3::load_xmm(position); if (!_mm_movemask_ps(_mm_add_ps(_mm_mul_ps(_mm_shuffle_ps(pos, pos, 0xA0), vec4::load_xmm({ 1.0f, -1.0f, 1.0f, -1.0f })), colli_ground_xz))) { snow_ptcl[0].position.y = colli_ground_y; snow_particle_data_emit_fallen(snow_ptcl); snow_particle_data_reset(snow_ptcl); } } snow_ptcl[1].position = snow_ptcl[0].position + snow_ptcl[1].velocity; } } for (particle_data& i : snow_ptcl_gpu) { vec3 direction; if (--i.life_time > 0) direction = i.direction; else { i.life_time = rand_state_array_get_int(0xFu, 0x23u, 4); direction = snow_particle_get_random_velocity(); i.direction = direction; } vec3 velocity = (direction * delta_time + i.velocity) * 0.98f; vec3 position = velocity * delta_time + i.position; i.velocity = velocity; i.alpha = 1.0f; i.position = position; if (position.y > -1.0f) i.alpha = max_def(-position.y, 0.0f); else if (position.y < -99.0f) { i.alpha = max_def(position.y + 100.0f, 0.0f); if (position.y < -100.0f) i.position = 0.0f; } } particle_data* snow_ptcl_fallen = snow_ptcl_fallen_data; for (size_t i = snow->num_snow; i; i--, snow_ptcl_fallen++) if (snow_ptcl_fallen->size != 0.0f) { snow_ptcl_fallen->alpha *= 0.95f; if (snow_ptcl_fallen->alpha < 0.02f) snow_particle_data_kill_fallen(snow_ptcl_fallen, true); } snow_particle_vertex_data* vtx_data = (snow_particle_vertex_data*)snow_ssbo.MapMemory(); if (vtx_data) { particle_data* snow_ptcl = snow_ptcl_data; for (int32_t i = snow->num_snow; i; i--, snow_ptcl++, vtx_data++) { vtx_data->position = snow_ptcl->position; vtx_data->size = snow_ptcl->size; vtx_data->alpha = snow_ptcl->alpha; } } snow_ssbo.UnmapMemory(); snow_particle_vertex_data* fallen_vtx_data = (snow_particle_vertex_data*)snow_fallen_ssbo.MapMemory(); if (fallen_vtx_data) { particle_data* snow_ptcl_fallen = snow_ptcl_fallen_data; for (size_t i = snow_ptcl_fallen_count; i; i--, snow_ptcl_fallen++, fallen_vtx_data++) { fallen_vtx_data->position = snow_ptcl_fallen->position; fallen_vtx_data->size = snow_ptcl_fallen->size; fallen_vtx_data->alpha = snow_ptcl_fallen->alpha; } } snow_fallen_ssbo.UnmapMemory(); } static void snow_particle_data_init() { if (!snow_ptcl_data) snow_ptcl_data = force_malloc(stage_param_data_snow_current->num_snow); if (!snow_ptcl_fallen_data) snow_ptcl_fallen_data = force_malloc(snow_ptcl_fallen_count); } static void snow_particle_data_emit_fallen(particle_data* data) { for (int32_t i = 0; i < 6; i++) { particle_data* d = snow_particle_emit_fallen(); if (!d) break; d->position.x = (rand_state_array_get_float(4) - 0.5f) * 0.05f + data->position.x; d->position.y = data->position.y; d->position.z = (rand_state_array_get_float(4) - 0.5f) * 0.05f + data->position.z; d->size = (rand_state_array_get_float(4) * 0.5f + 0.2f) * data->size; d->velocity = 0.0f; d->alpha = data->alpha * 0.60f; } } static void snow_particle_data_kill_fallen(particle_data* data, bool kill) { if (kill) --snow_particle_fallen_count; data->position = { 0.0f, -10.0f, 0.0f }; data->size = 0.0f; data->alpha = 1.0f; } static void snow_particle_data_reset(particle_data* data) { stage_param_snow* snow = stage_param_data_snow_current; vec3 position; position.x = rand_state_array_get_float(4) - 0.5f; position.y = 1.0f; position.z = rand_state_array_get_float(4) - 0.5f; data->position = position * snow->range + snow->offset; data->life_time = 1; vec3 velocity; velocity.x = rand_state_array_get_float(4); velocity.y = rand_state_array_get_float(4); velocity.z = rand_state_array_get_float(4); data->velocity = (velocity - 0.5f) * snow->vel_range + snow->velocity; data->direction = 0.0f; } static void snow_particle_data_free() { if (snow_ptcl_data) { free(snow_ptcl_data); snow_ptcl_data = 0; } if (snow_ptcl_fallen_data) { free(snow_ptcl_fallen_data); snow_ptcl_fallen_data = 0; } } static particle_data* snow_particle_emit_fallen() { if (snow_particle_fallen_count >= snow_ptcl_fallen_count) return 0; for (int32_t i = 0; i < snow_ptcl_fallen_count; i++) { particle_data* data = &snow_ptcl_fallen_data[snow_particle_fallen_index++]; if (snow_particle_fallen_index >= snow_ptcl_fallen_count) snow_particle_fallen_index = 0; if (data->size == 0.0f) { snow_particle_fallen_count++; return data; } } return 0; } static vec3 snow_particle_get_random_velocity() { stage_param_snow* snow = stage_param_data_snow_current; vec3 velocity; velocity.x = rand_state_array_get_float(4); velocity.y = rand_state_array_get_float(4); velocity.z = rand_state_array_get_float(4); return (velocity - 0.5f) * snow->vel_range + snow->velocity; } static void snow_particle_free() { snow_ssbo.Destroy(); snow_gpu_ssbo.Destroy(); snow_fallen_ssbo.Destroy(); snow_particle_scene_ubo.Destroy(); snow_particle_batch_ubo.Destroy(); } static void water_particle_init() { water_particle_scene_ubo.Create(sizeof(water_particle_scene_shader_data)); } static void water_particle_free() { water_particle_scene_ubo.Destroy(); } static void sub_1403B6ED0(RenderTexture* a1, RenderTexture* a2, RenderTexture* a3, ripple_emit_params& params) { a1->Bind(); if (a1->color_texture->internal_format == GL_RGBA32F || a1->color_texture->internal_format == GL_RGBA16F) uniform_value[U_RIPPLE] = 1; else uniform_value[U_RIPPLE] = 0; sub_1403B6F60(a1->GetColorTex(), a2->GetColorTex(), a3->GetColorTex(), params); gl_state_bind_framebuffer(0); } static void sub_1403B6F60(GLuint a1, GLuint a2, GLuint a3, ripple_emit_params& params) { if (!a1 || !a2 || !a3) return; texture_param tex_params[2]; texture_params_get(0, 0, a1, &tex_params[0], a2, &tex_params[1]); int32_t width = tex_params[1].width; int32_t height = tex_params[1].height; GLint v43[4]; glGetIntegerv(GL_VIEWPORT, v43); glViewport(1, 1, width - 2, height - 2); ripple_scene_shader_data ripple_scene = {}; ripple_scene.g_transform = { params.speed / (float_t)width, params.speed / (float_t)height, (float_t)width / (float_t)(width - 2), (float_t)height / (float_t)(height - 2) }; ripple_scene.g_texcoord = { 1.0f, 0.0f, 0.0f, 0.0f }; ripple_scene_ubo.WriteMemory(ripple_scene); ripple_batch_shader_data ripple_batch = {}; ripple_batch.g_params = { params.wake_attn, params.speed, params.field_8, params.field_C }; ripple_batch_ubo.WriteMemory(ripple_batch); gl_state_bind_vertex_array(rctx_ptr->common_vao); shaders_ft.set(SHADER_FT_RIPPLE); ripple_scene_ubo.Bind(0); ripple_batch_ubo.Bind(1); gl_state_active_bind_texture_2d(0, a2); gl_state_active_bind_texture_2d(1, a3); shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, 0, 4); gl_state_active_bind_texture_2d(1, 0); gl_state_active_bind_texture_2d(0, 0); gl_state_bind_vertex_array(0); glViewport(v43[0], v43[1], v43[2], v43[3]); texture_params_restore(&tex_params[0], &tex_params[1]); }